linera_execution/
resources.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! This module tracks the resources used during the execution of a transaction.
5
6use std::{sync::Arc, time::Duration};
7
8use custom_debug_derive::Debug;
9use linera_base::{
10    data_types::{Amount, ArithmeticError, Blob},
11    ensure,
12    identifiers::AccountOwner,
13    ownership::ChainOwnership,
14    vm::VmRuntime,
15};
16use linera_views::{context::Context, ViewError};
17use serde::Serialize;
18
19use crate::{ExecutionError, Message, Operation, ResourceControlPolicy, SystemExecutionStateView};
20
21#[derive(Clone, Debug, Default)]
22pub struct ResourceController<Account = Amount, Tracker = ResourceTracker> {
23    /// The (fixed) policy used to charge fees and control resource usage.
24    policy: Arc<ResourceControlPolicy>,
25    /// How the resources were used so far.
26    pub tracker: Tracker,
27    /// The account paying for the resource usage.
28    pub account: Account,
29}
30
31impl<Account, Tracker> ResourceController<Account, Tracker> {
32    /// Creates a new resource controller with the given policy and account.
33    pub fn new(policy: Arc<ResourceControlPolicy>, tracker: Tracker, account: Account) -> Self {
34        Self {
35            policy,
36            tracker,
37            account,
38        }
39    }
40
41    /// Returns a reference to the policy.
42    pub fn policy(&self) -> &Arc<ResourceControlPolicy> {
43        &self.policy
44    }
45
46    /// Returns a reference to the tracker.
47    pub fn tracker(&self) -> &Tracker {
48        &self.tracker
49    }
50}
51
52/// The runtime size of an `Amount`.
53pub const RUNTIME_AMOUNT_SIZE: u32 = 16;
54
55/// The runtime size of a `ApplicationId`.
56pub const RUNTIME_APPLICATION_ID_SIZE: u32 = 32;
57
58/// The runtime size of a `BlockHeight`.
59pub const RUNTIME_BLOCK_HEIGHT_SIZE: u32 = 8;
60
61/// The runtime size of a `ChainId`.
62pub const RUNTIME_CHAIN_ID_SIZE: u32 = 32;
63
64/// The runtime size of a `Timestamp`.
65pub const RUNTIME_TIMESTAMP_SIZE: u32 = 8;
66
67/// The runtime size of the weight of an owner.
68pub const RUNTIME_OWNER_WEIGHT_SIZE: u32 = 8;
69
70/// The runtime constant part size of the `ChainOwnership`.
71/// It consists of one `u32` and four `TimeDelta` which are the constant part of
72/// the `ChainOwnership`. The way we do it is not optimal:
73/// TODO(#4164): Implement a procedure for computing naive sizes.
74pub const RUNTIME_CONSTANT_CHAIN_OWNERSHIP_SIZE: u32 = 4 + 4 * 8;
75
76#[cfg(test)]
77mod tests {
78    use std::mem::size_of;
79
80    use linera_base::{
81        data_types::{Amount, BlockHeight, Timestamp},
82        identifiers::{ApplicationId, ChainId},
83    };
84
85    use crate::resources::{
86        RUNTIME_AMOUNT_SIZE, RUNTIME_APPLICATION_ID_SIZE, RUNTIME_BLOCK_HEIGHT_SIZE,
87        RUNTIME_CHAIN_ID_SIZE, RUNTIME_OWNER_WEIGHT_SIZE, RUNTIME_TIMESTAMP_SIZE,
88    };
89
90    #[test]
91    fn test_size_of_runtime_operations() {
92        assert_eq!(RUNTIME_AMOUNT_SIZE as usize, size_of::<Amount>());
93        assert_eq!(
94            RUNTIME_APPLICATION_ID_SIZE as usize,
95            size_of::<ApplicationId>()
96        );
97        assert_eq!(RUNTIME_BLOCK_HEIGHT_SIZE as usize, size_of::<BlockHeight>());
98        assert_eq!(RUNTIME_CHAIN_ID_SIZE as usize, size_of::<ChainId>());
99        assert_eq!(RUNTIME_TIMESTAMP_SIZE as usize, size_of::<Timestamp>());
100        assert_eq!(RUNTIME_OWNER_WEIGHT_SIZE as usize, size_of::<u64>());
101    }
102}
103
104/// The resources used so far by an execution process.
105/// Acts as an accumulator for all resources consumed during
106/// a specific execution flow. This could be the execution of a block,
107/// the processing of a single message, or a specific phase within these
108/// broader operations.
109#[derive(Copy, Debug, Clone, Default)]
110pub struct ResourceTracker {
111    /// The total size of the block so far.
112    pub block_size: u64,
113    /// The EVM fuel used so far.
114    pub evm_fuel: u64,
115    /// The Wasm fuel used so far.
116    pub wasm_fuel: u64,
117    /// The number of read operations.
118    pub read_operations: u32,
119    /// The number of write operations.
120    pub write_operations: u32,
121    /// The size of bytes read from runtime.
122    pub bytes_runtime: u32,
123    /// The number of bytes read.
124    pub bytes_read: u64,
125    /// The number of bytes written.
126    pub bytes_written: u64,
127    /// The number of blobs read.
128    pub blobs_read: u32,
129    /// The number of blobs published.
130    pub blobs_published: u32,
131    /// The number of blob bytes read.
132    pub blob_bytes_read: u64,
133    /// The number of blob bytes published.
134    pub blob_bytes_published: u64,
135    /// The number of events read.
136    pub events_read: u32,
137    /// The number of events published.
138    pub events_published: u32,
139    /// The number of event bytes read.
140    pub event_bytes_read: u64,
141    /// The number of event bytes published.
142    pub event_bytes_published: u64,
143    /// The change in the number of bytes being stored by user applications.
144    pub bytes_stored: i32,
145    /// The number of operations executed.
146    pub operations: u32,
147    /// The total size of the arguments of user operations.
148    pub operation_bytes: u64,
149    /// The number of outgoing messages created (system and user).
150    pub messages: u32,
151    /// The total size of the arguments of outgoing user messages.
152    pub message_bytes: u64,
153    /// The number of HTTP requests performed.
154    pub http_requests: u32,
155    /// The number of calls to services as oracles.
156    pub service_oracle_queries: u32,
157    /// The time spent executing services as oracles.
158    pub service_oracle_execution: Duration,
159    /// The amount allocated to message grants.
160    pub grants: Amount,
161}
162
163impl ResourceTracker {
164    fn fuel(&self, vm_runtime: VmRuntime) -> u64 {
165        match vm_runtime {
166            VmRuntime::Wasm => self.wasm_fuel,
167            VmRuntime::Evm => self.evm_fuel,
168        }
169    }
170}
171
172/// How to access the balance of an account.
173pub trait BalanceHolder {
174    fn balance(&self) -> Result<Amount, ArithmeticError>;
175
176    fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError>;
177
178    fn try_sub_assign(&mut self, other: Amount) -> Result<(), ArithmeticError>;
179}
180
181// The main accounting functions for a ResourceController.
182impl<Account, Tracker> ResourceController<Account, Tracker>
183where
184    Account: BalanceHolder,
185    Tracker: AsRef<ResourceTracker> + AsMut<ResourceTracker>,
186{
187    /// Obtains the balance of the account. The only possible error is an arithmetic
188    /// overflow, which should not happen in practice due to final token supply.
189    pub fn balance(&self) -> Result<Amount, ArithmeticError> {
190        self.account.balance()
191    }
192
193    /// Operates a 3-way merge by transferring the difference between `initial`
194    /// and `other` to `self`.
195    pub fn merge_balance(&mut self, initial: Amount, other: Amount) -> Result<(), ExecutionError> {
196        if other <= initial {
197            let sub_amount = initial.try_sub(other).expect("other <= initial");
198            self.account.try_sub_assign(sub_amount).map_err(|_| {
199                ExecutionError::FeesExceedFunding {
200                    fees: sub_amount,
201                    balance: self.balance().unwrap_or(Amount::MAX),
202                }
203            })?;
204        } else {
205            self.account
206                .try_add_assign(other.try_sub(initial).expect("other > initial"))?;
207        }
208        Ok(())
209    }
210
211    /// Subtracts an amount from a balance and reports an error if that is impossible.
212    fn update_balance(&mut self, fees: Amount) -> Result<(), ExecutionError> {
213        self.account
214            .try_sub_assign(fees)
215            .map_err(|_| ExecutionError::FeesExceedFunding {
216                fees,
217                balance: self.balance().unwrap_or(Amount::MAX),
218            })?;
219        Ok(())
220    }
221
222    /// Obtains the amount of fuel that could be spent by consuming the entire balance.
223    pub(crate) fn remaining_fuel(&self, vm_runtime: VmRuntime) -> u64 {
224        let balance = self.balance().unwrap_or(Amount::MAX);
225        let fuel = self.tracker.as_ref().fuel(vm_runtime);
226        let maximum_fuel_per_block = self.policy.maximum_fuel_per_block(vm_runtime);
227        self.policy
228            .remaining_fuel(balance, vm_runtime)
229            .min(maximum_fuel_per_block.saturating_sub(fuel))
230    }
231
232    /// Tracks the allocation of a grant.
233    pub fn track_grant(&mut self, grant: Amount) -> Result<(), ExecutionError> {
234        self.tracker.as_mut().grants.try_add_assign(grant)?;
235        self.update_balance(grant)
236    }
237
238    /// Tracks the execution of an operation in block.
239    pub fn track_operation(&mut self, operation: &Operation) -> Result<(), ExecutionError> {
240        self.tracker.as_mut().operations = self
241            .tracker
242            .as_mut()
243            .operations
244            .checked_add(1)
245            .ok_or(ArithmeticError::Overflow)?;
246        self.update_balance(self.policy.operation)?;
247        match operation {
248            Operation::System(_) => Ok(()),
249            Operation::User { bytes, .. } => {
250                let size = bytes.len();
251                self.tracker.as_mut().operation_bytes = self
252                    .tracker
253                    .as_mut()
254                    .operation_bytes
255                    .checked_add(size as u64)
256                    .ok_or(ArithmeticError::Overflow)?;
257                self.update_balance(self.policy.operation_bytes_price(size as u64)?)?;
258                Ok(())
259            }
260        }
261    }
262
263    /// Tracks the creation of an outgoing message.
264    pub fn track_message(&mut self, message: &Message) -> Result<(), ExecutionError> {
265        self.tracker.as_mut().messages = self
266            .tracker
267            .as_mut()
268            .messages
269            .checked_add(1)
270            .ok_or(ArithmeticError::Overflow)?;
271        self.update_balance(self.policy.message)?;
272        match message {
273            Message::System(_) => Ok(()),
274            Message::User { bytes, .. } => {
275                let size = bytes.len();
276                self.tracker.as_mut().message_bytes = self
277                    .tracker
278                    .as_mut()
279                    .message_bytes
280                    .checked_add(size as u64)
281                    .ok_or(ArithmeticError::Overflow)?;
282                self.update_balance(self.policy.message_bytes_price(size as u64)?)?;
283                Ok(())
284            }
285        }
286    }
287
288    /// Tracks the execution of an HTTP request.
289    pub fn track_http_request(&mut self) -> Result<(), ExecutionError> {
290        self.tracker.as_mut().http_requests = self
291            .tracker
292            .as_ref()
293            .http_requests
294            .checked_add(1)
295            .ok_or(ArithmeticError::Overflow)?;
296        self.update_balance(self.policy.http_request)
297    }
298
299    /// Tracks a number of fuel units used.
300    pub(crate) fn track_fuel(
301        &mut self,
302        fuel: u64,
303        vm_runtime: VmRuntime,
304    ) -> Result<(), ExecutionError> {
305        match vm_runtime {
306            VmRuntime::Wasm => {
307                self.tracker.as_mut().wasm_fuel = self
308                    .tracker
309                    .as_ref()
310                    .wasm_fuel
311                    .checked_add(fuel)
312                    .ok_or(ArithmeticError::Overflow)?;
313                ensure!(
314                    self.tracker.as_ref().wasm_fuel <= self.policy.maximum_wasm_fuel_per_block,
315                    ExecutionError::MaximumFuelExceeded(vm_runtime)
316                );
317            }
318            VmRuntime::Evm => {
319                self.tracker.as_mut().evm_fuel = self
320                    .tracker
321                    .as_ref()
322                    .evm_fuel
323                    .checked_add(fuel)
324                    .ok_or(ArithmeticError::Overflow)?;
325                ensure!(
326                    self.tracker.as_ref().evm_fuel <= self.policy.maximum_evm_fuel_per_block,
327                    ExecutionError::MaximumFuelExceeded(vm_runtime)
328                );
329            }
330        }
331        self.update_balance(self.policy.fuel_price(fuel, vm_runtime)?)
332    }
333
334    /// Tracks runtime reading of `ChainId`
335    pub(crate) fn track_runtime_chain_id(&mut self) -> Result<(), ExecutionError> {
336        self.track_size_runtime_operations(RUNTIME_CHAIN_ID_SIZE)
337    }
338
339    /// Tracks runtime reading of `BlockHeight`
340    pub(crate) fn track_runtime_block_height(&mut self) -> Result<(), ExecutionError> {
341        self.track_size_runtime_operations(RUNTIME_BLOCK_HEIGHT_SIZE)
342    }
343
344    /// Tracks runtime reading of `ApplicationId`
345    pub(crate) fn track_runtime_application_id(&mut self) -> Result<(), ExecutionError> {
346        self.track_size_runtime_operations(RUNTIME_APPLICATION_ID_SIZE)
347    }
348
349    /// Tracks runtime reading of application parameters.
350    pub(crate) fn track_runtime_application_parameters(
351        &mut self,
352        parameters: &[u8],
353    ) -> Result<(), ExecutionError> {
354        let parameters_len = parameters.len() as u32;
355        self.track_size_runtime_operations(parameters_len)
356    }
357
358    /// Tracks runtime reading of `Timestamp`
359    pub(crate) fn track_runtime_timestamp(&mut self) -> Result<(), ExecutionError> {
360        self.track_size_runtime_operations(RUNTIME_TIMESTAMP_SIZE)
361    }
362
363    /// Tracks runtime reading of balance
364    pub(crate) fn track_runtime_balance(&mut self) -> Result<(), ExecutionError> {
365        self.track_size_runtime_operations(RUNTIME_AMOUNT_SIZE)
366    }
367
368    /// Tracks runtime reading of owner balances
369    pub(crate) fn track_runtime_owner_balances(
370        &mut self,
371        owner_balances: &[(AccountOwner, Amount)],
372    ) -> Result<(), ExecutionError> {
373        let mut size = 0;
374        for (account_owner, _) in owner_balances {
375            size += account_owner.size() + RUNTIME_AMOUNT_SIZE;
376        }
377        self.track_size_runtime_operations(size)
378    }
379
380    /// Tracks runtime reading of owners
381    pub(crate) fn track_runtime_owners(
382        &mut self,
383        owners: &[AccountOwner],
384    ) -> Result<(), ExecutionError> {
385        let mut size = 0;
386        for owner in owners {
387            size += owner.size();
388        }
389        self.track_size_runtime_operations(size)
390    }
391
392    /// Tracks runtime reading of owners
393    pub(crate) fn track_runtime_chain_ownership(
394        &mut self,
395        chain_ownership: &ChainOwnership,
396    ) -> Result<(), ExecutionError> {
397        let mut size = 0;
398        for account_owner in &chain_ownership.super_owners {
399            size += account_owner.size();
400        }
401        for account_owner in chain_ownership.owners.keys() {
402            size += account_owner.size() + RUNTIME_OWNER_WEIGHT_SIZE;
403        }
404        size += RUNTIME_CONSTANT_CHAIN_OWNERSHIP_SIZE;
405        self.track_size_runtime_operations(size)
406    }
407
408    /// Tracks runtime operations.
409    fn track_size_runtime_operations(&mut self, size: u32) -> Result<(), ExecutionError> {
410        self.tracker.as_mut().bytes_runtime = self
411            .tracker
412            .as_mut()
413            .bytes_runtime
414            .checked_add(size)
415            .ok_or(ArithmeticError::Overflow)?;
416        self.update_balance(self.policy.bytes_runtime_price(size)?)
417    }
418
419    /// Tracks a read operation.
420    pub(crate) fn track_read_operation(&mut self) -> Result<(), ExecutionError> {
421        self.tracker.as_mut().read_operations = self
422            .tracker
423            .as_mut()
424            .read_operations
425            .checked_add(1)
426            .ok_or(ArithmeticError::Overflow)?;
427        self.update_balance(self.policy.read_operations_price(1)?)
428    }
429
430    /// Tracks a write operation.
431    pub(crate) fn track_write_operations(&mut self, count: u32) -> Result<(), ExecutionError> {
432        self.tracker.as_mut().write_operations = self
433            .tracker
434            .as_mut()
435            .write_operations
436            .checked_add(count)
437            .ok_or(ArithmeticError::Overflow)?;
438        self.update_balance(self.policy.write_operations_price(count)?)
439    }
440
441    /// Tracks a number of bytes read.
442    pub(crate) fn track_bytes_read(&mut self, count: u64) -> Result<(), ExecutionError> {
443        self.tracker.as_mut().bytes_read = self
444            .tracker
445            .as_mut()
446            .bytes_read
447            .checked_add(count)
448            .ok_or(ArithmeticError::Overflow)?;
449        if self.tracker.as_mut().bytes_read >= self.policy.maximum_bytes_read_per_block {
450            return Err(ExecutionError::ExcessiveRead);
451        }
452        self.update_balance(self.policy.bytes_read_price(count)?)?;
453        Ok(())
454    }
455
456    /// Tracks a number of bytes written.
457    pub(crate) fn track_bytes_written(&mut self, count: u64) -> Result<(), ExecutionError> {
458        self.tracker.as_mut().bytes_written = self
459            .tracker
460            .as_mut()
461            .bytes_written
462            .checked_add(count)
463            .ok_or(ArithmeticError::Overflow)?;
464        if self.tracker.as_mut().bytes_written >= self.policy.maximum_bytes_written_per_block {
465            return Err(ExecutionError::ExcessiveWrite);
466        }
467        self.update_balance(self.policy.bytes_written_price(count)?)?;
468        Ok(())
469    }
470
471    /// Tracks a number of blob bytes written.
472    pub(crate) fn track_blob_read(&mut self, count: u64) -> Result<(), ExecutionError> {
473        {
474            let tracker = self.tracker.as_mut();
475            tracker.blob_bytes_read = tracker
476                .blob_bytes_read
477                .checked_add(count)
478                .ok_or(ArithmeticError::Overflow)?;
479            tracker.blobs_read = tracker
480                .blobs_read
481                .checked_add(1)
482                .ok_or(ArithmeticError::Overflow)?;
483        }
484        self.update_balance(self.policy.blob_read_price(count)?)?;
485        Ok(())
486    }
487
488    /// Tracks a number of blob bytes published.
489    pub fn track_blob_published(&mut self, blob: &Blob) -> Result<(), ExecutionError> {
490        self.policy.check_blob_size(blob.content())?;
491        let size = blob.content().bytes().len() as u64;
492        if blob.is_committee_blob() {
493            return Ok(());
494        }
495        {
496            let tracker = self.tracker.as_mut();
497            tracker.blob_bytes_published = tracker
498                .blob_bytes_published
499                .checked_add(size)
500                .ok_or(ArithmeticError::Overflow)?;
501            tracker.blobs_published = tracker
502                .blobs_published
503                .checked_add(1)
504                .ok_or(ArithmeticError::Overflow)?;
505        }
506        self.update_balance(self.policy.blob_published_price(size)?)?;
507        Ok(())
508    }
509
510    /// Tracks a number of event bytes read.
511    pub(crate) fn track_event_read(&mut self, count: u64) -> Result<(), ExecutionError> {
512        {
513            let tracker = self.tracker.as_mut();
514            tracker.event_bytes_read = tracker
515                .event_bytes_read
516                .checked_add(count)
517                .ok_or(ArithmeticError::Overflow)?;
518            tracker.events_read = tracker
519                .events_read
520                .checked_add(1)
521                .ok_or(ArithmeticError::Overflow)?;
522        }
523        self.update_balance(self.policy.blob_read_price(count)?)?;
524        Ok(())
525    }
526
527    /// Tracks a number of event bytes published.
528    pub(crate) fn track_event_published(
529        &mut self,
530        event_bytes: &[u8],
531    ) -> Result<(), ExecutionError> {
532        let size = event_bytes.len() as u64;
533        {
534            let tracker = self.tracker.as_mut();
535            tracker.event_bytes_published = tracker
536                .event_bytes_published
537                .checked_add(size)
538                .ok_or(ArithmeticError::Overflow)?;
539            tracker.events_published = tracker
540                .events_published
541                .checked_add(1)
542                .ok_or(ArithmeticError::Overflow)?;
543        }
544        self.update_balance(self.policy.blob_published_price(size)?)?;
545        Ok(())
546    }
547
548    /// Tracks a change in the number of bytes stored.
549    // TODO(#1536): This is not fully implemented.
550    #[allow(dead_code)]
551    pub(crate) fn track_stored_bytes(&mut self, delta: i32) -> Result<(), ExecutionError> {
552        self.tracker.as_mut().bytes_stored = self
553            .tracker
554            .as_mut()
555            .bytes_stored
556            .checked_add(delta)
557            .ok_or(ArithmeticError::Overflow)?;
558        Ok(())
559    }
560
561    /// Returns the remaining time services can spend executing as oracles.
562    pub(crate) fn remaining_service_oracle_execution_time(
563        &self,
564    ) -> Result<Duration, ExecutionError> {
565        let tracker = self.tracker.as_ref();
566        let spent_execution_time = tracker.service_oracle_execution;
567        let limit = Duration::from_millis(self.policy.maximum_service_oracle_execution_ms);
568
569        limit
570            .checked_sub(spent_execution_time)
571            .ok_or(ExecutionError::MaximumServiceOracleExecutionTimeExceeded)
572    }
573
574    /// Tracks a call to a service to run as an oracle.
575    pub(crate) fn track_service_oracle_call(&mut self) -> Result<(), ExecutionError> {
576        self.tracker.as_mut().service_oracle_queries = self
577            .tracker
578            .as_mut()
579            .service_oracle_queries
580            .checked_add(1)
581            .ok_or(ArithmeticError::Overflow)?;
582        self.update_balance(self.policy.service_as_oracle_query)
583    }
584
585    /// Tracks the time spent executing the service as an oracle.
586    pub(crate) fn track_service_oracle_execution(
587        &mut self,
588        execution_time: Duration,
589    ) -> Result<(), ExecutionError> {
590        let tracker = self.tracker.as_mut();
591        let spent_execution_time = &mut tracker.service_oracle_execution;
592        let limit = Duration::from_millis(self.policy.maximum_service_oracle_execution_ms);
593
594        *spent_execution_time = spent_execution_time.saturating_add(execution_time);
595
596        ensure!(
597            *spent_execution_time < limit,
598            ExecutionError::MaximumServiceOracleExecutionTimeExceeded
599        );
600
601        Ok(())
602    }
603
604    /// Tracks the size of a response produced by an oracle.
605    pub(crate) fn track_service_oracle_response(
606        &mut self,
607        response_bytes: usize,
608    ) -> Result<(), ExecutionError> {
609        ensure!(
610            response_bytes as u64 <= self.policy.maximum_oracle_response_bytes,
611            ExecutionError::ServiceOracleResponseTooLarge
612        );
613
614        Ok(())
615    }
616}
617
618impl<Account, Tracker> ResourceController<Account, Tracker>
619where
620    Tracker: AsMut<ResourceTracker>,
621{
622    /// Tracks the serialized size of a block, or parts of it.
623    pub fn track_block_size_of(&mut self, data: &impl Serialize) -> Result<(), ExecutionError> {
624        self.track_block_size(bcs::serialized_size(data)?)
625    }
626
627    /// Tracks the serialized size of a block, or parts of it.
628    pub fn track_block_size(&mut self, size: usize) -> Result<(), ExecutionError> {
629        let tracker = self.tracker.as_mut();
630        tracker.block_size = u64::try_from(size)
631            .ok()
632            .and_then(|size| tracker.block_size.checked_add(size))
633            .ok_or(ExecutionError::BlockTooLarge)?;
634        ensure!(
635            tracker.block_size <= self.policy.maximum_block_size,
636            ExecutionError::BlockTooLarge
637        );
638        Ok(())
639    }
640}
641
642impl ResourceController<Option<AccountOwner>, ResourceTracker> {
643    /// Provides a reference to the current execution state and obtains a temporary object
644    /// where the accounting functions of [`ResourceController`] are available.
645    pub async fn with_state<'a, C>(
646        &mut self,
647        view: &'a mut SystemExecutionStateView<C>,
648    ) -> Result<ResourceController<Sources<'a>, &mut ResourceTracker>, ViewError>
649    where
650        C: Context + Clone + Send + Sync + 'static,
651    {
652        self.with_state_and_grant(view, None).await
653    }
654
655    /// Provides a reference to the current execution state as well as an optional grant,
656    /// and obtains a temporary object where the accounting functions of
657    /// [`ResourceController`] are available.
658    pub async fn with_state_and_grant<'a, C>(
659        &mut self,
660        view: &'a mut SystemExecutionStateView<C>,
661        grant: Option<&'a mut Amount>,
662    ) -> Result<ResourceController<Sources<'a>, &mut ResourceTracker>, ViewError>
663    where
664        C: Context + Clone + Send + Sync + 'static,
665    {
666        let mut sources = Vec::new();
667        // First, use the grant (e.g. for messages) and otherwise use the chain account
668        // (e.g. for blocks and operations).
669        if let Some(grant) = grant {
670            sources.push(grant);
671        } else {
672            sources.push(view.balance.get_mut());
673        }
674        // Then the local account, if any. Currently, any negative fee (e.g. storage
675        // refund) goes preferably to this account.
676        if let Some(owner) = &self.account {
677            if let Some(balance) = view.balances.get_mut(owner).await? {
678                sources.push(balance);
679            }
680        }
681
682        Ok(ResourceController {
683            policy: self.policy.clone(),
684            tracker: &mut self.tracker,
685            account: Sources { sources },
686        })
687    }
688}
689
690// The simplest `BalanceHolder` is an `Amount`.
691impl BalanceHolder for Amount {
692    fn balance(&self) -> Result<Amount, ArithmeticError> {
693        Ok(*self)
694    }
695
696    fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
697        self.try_add_assign(other)
698    }
699
700    fn try_sub_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
701        self.try_sub_assign(other)
702    }
703}
704
705// This is also needed for the default instantiation `ResourceController<Amount, ResourceTracker>`.
706// See https://doc.rust-lang.org/std/convert/trait.AsMut.html#reflexivity for general context.
707impl AsMut<ResourceTracker> for ResourceTracker {
708    fn as_mut(&mut self) -> &mut Self {
709        self
710    }
711}
712
713impl AsRef<ResourceTracker> for ResourceTracker {
714    fn as_ref(&self) -> &Self {
715        self
716    }
717}
718
719/// A temporary object holding a number of references to funding sources.
720pub struct Sources<'a> {
721    sources: Vec<&'a mut Amount>,
722}
723
724impl BalanceHolder for Sources<'_> {
725    fn balance(&self) -> Result<Amount, ArithmeticError> {
726        let mut amount = Amount::ZERO;
727        for source in &self.sources {
728            amount.try_add_assign(**source)?;
729        }
730        Ok(amount)
731    }
732
733    fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
734        // Try to credit the owner account first.
735        // TODO(#1648): This may need some additional design work.
736        let source = self.sources.last_mut().expect("at least one source");
737        source.try_add_assign(other)
738    }
739
740    fn try_sub_assign(&mut self, mut other: Amount) -> Result<(), ArithmeticError> {
741        for source in &mut self.sources {
742            if source.try_sub_assign(other).is_ok() {
743                return Ok(());
744            }
745            other.try_sub_assign(**source).expect("*source < other");
746            **source = Amount::ZERO;
747        }
748        if other > Amount::ZERO {
749            Err(ArithmeticError::Underflow)
750        } else {
751            Ok(())
752        }
753    }
754}