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 change in the number of bytes being stored by user applications.
136    pub bytes_stored: i32,
137    /// The number of operations executed.
138    pub operations: u32,
139    /// The total size of the arguments of user operations.
140    pub operation_bytes: u64,
141    /// The number of outgoing messages created (system and user).
142    pub messages: u32,
143    /// The total size of the arguments of outgoing user messages.
144    pub message_bytes: u64,
145    /// The number of HTTP requests performed.
146    pub http_requests: u32,
147    /// The number of calls to services as oracles.
148    pub service_oracle_queries: u32,
149    /// The time spent executing services as oracles.
150    pub service_oracle_execution: Duration,
151    /// The amount allocated to message grants.
152    pub grants: Amount,
153}
154
155impl ResourceTracker {
156    fn fuel(&self, vm_runtime: VmRuntime) -> u64 {
157        match vm_runtime {
158            VmRuntime::Wasm => self.wasm_fuel,
159            VmRuntime::Evm => self.evm_fuel,
160        }
161    }
162}
163
164/// How to access the balance of an account.
165pub trait BalanceHolder {
166    fn balance(&self) -> Result<Amount, ArithmeticError>;
167
168    fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError>;
169
170    fn try_sub_assign(&mut self, other: Amount) -> Result<(), ArithmeticError>;
171}
172
173// The main accounting functions for a ResourceController.
174impl<Account, Tracker> ResourceController<Account, Tracker>
175where
176    Account: BalanceHolder,
177    Tracker: AsRef<ResourceTracker> + AsMut<ResourceTracker>,
178{
179    /// Obtains the balance of the account. The only possible error is an arithmetic
180    /// overflow, which should not happen in practice due to final token supply.
181    pub fn balance(&self) -> Result<Amount, ArithmeticError> {
182        self.account.balance()
183    }
184
185    /// Operates a 3-way merge by transferring the difference between `initial`
186    /// and `other` to `self`.
187    pub fn merge_balance(&mut self, initial: Amount, other: Amount) -> Result<(), ExecutionError> {
188        if other <= initial {
189            let sub_amount = initial.try_sub(other).expect("other <= initial");
190            self.account.try_sub_assign(sub_amount).map_err(|_| {
191                ExecutionError::FeesExceedFunding {
192                    fees: sub_amount,
193                    balance: self.balance().unwrap_or(Amount::MAX),
194                }
195            })?;
196        } else {
197            self.account
198                .try_add_assign(other.try_sub(initial).expect("other > initial"))?;
199        }
200        Ok(())
201    }
202
203    /// Subtracts an amount from a balance and reports an error if that is impossible.
204    fn update_balance(&mut self, fees: Amount) -> Result<(), ExecutionError> {
205        self.account
206            .try_sub_assign(fees)
207            .map_err(|_| ExecutionError::FeesExceedFunding {
208                fees,
209                balance: self.balance().unwrap_or(Amount::MAX),
210            })?;
211        Ok(())
212    }
213
214    /// Obtains the amount of fuel that could be spent by consuming the entire balance.
215    pub(crate) fn remaining_fuel(&self, vm_runtime: VmRuntime) -> u64 {
216        let balance = self.balance().unwrap_or(Amount::MAX);
217        let fuel = self.tracker.as_ref().fuel(vm_runtime);
218        let maximum_fuel_per_block = self.policy.maximum_fuel_per_block(vm_runtime);
219        self.policy
220            .remaining_fuel(balance, vm_runtime)
221            .min(maximum_fuel_per_block.saturating_sub(fuel))
222    }
223
224    /// Tracks the allocation of a grant.
225    pub fn track_grant(&mut self, grant: Amount) -> Result<(), ExecutionError> {
226        self.tracker.as_mut().grants.try_add_assign(grant)?;
227        self.update_balance(grant)
228    }
229
230    /// Tracks the execution of an operation in block.
231    pub fn track_operation(&mut self, operation: &Operation) -> Result<(), ExecutionError> {
232        self.tracker.as_mut().operations = self
233            .tracker
234            .as_mut()
235            .operations
236            .checked_add(1)
237            .ok_or(ArithmeticError::Overflow)?;
238        self.update_balance(self.policy.operation)?;
239        match operation {
240            Operation::System(_) => Ok(()),
241            Operation::User { bytes, .. } => {
242                let size = bytes.len();
243                self.tracker.as_mut().operation_bytes = self
244                    .tracker
245                    .as_mut()
246                    .operation_bytes
247                    .checked_add(size as u64)
248                    .ok_or(ArithmeticError::Overflow)?;
249                self.update_balance(self.policy.operation_bytes_price(size as u64)?)?;
250                Ok(())
251            }
252        }
253    }
254
255    /// Tracks the creation of an outgoing message.
256    pub fn track_message(&mut self, message: &Message) -> Result<(), ExecutionError> {
257        self.tracker.as_mut().messages = self
258            .tracker
259            .as_mut()
260            .messages
261            .checked_add(1)
262            .ok_or(ArithmeticError::Overflow)?;
263        self.update_balance(self.policy.message)?;
264        match message {
265            Message::System(_) => Ok(()),
266            Message::User { bytes, .. } => {
267                let size = bytes.len();
268                self.tracker.as_mut().message_bytes = self
269                    .tracker
270                    .as_mut()
271                    .message_bytes
272                    .checked_add(size as u64)
273                    .ok_or(ArithmeticError::Overflow)?;
274                self.update_balance(self.policy.message_bytes_price(size as u64)?)?;
275                Ok(())
276            }
277        }
278    }
279
280    /// Tracks the execution of an HTTP request.
281    pub fn track_http_request(&mut self) -> Result<(), ExecutionError> {
282        self.tracker.as_mut().http_requests = self
283            .tracker
284            .as_ref()
285            .http_requests
286            .checked_add(1)
287            .ok_or(ArithmeticError::Overflow)?;
288        self.update_balance(self.policy.http_request)
289    }
290
291    /// Tracks a number of fuel units used.
292    pub(crate) fn track_fuel(
293        &mut self,
294        fuel: u64,
295        vm_runtime: VmRuntime,
296    ) -> Result<(), ExecutionError> {
297        match vm_runtime {
298            VmRuntime::Wasm => {
299                self.tracker.as_mut().wasm_fuel = self
300                    .tracker
301                    .as_ref()
302                    .wasm_fuel
303                    .checked_add(fuel)
304                    .ok_or(ArithmeticError::Overflow)?;
305                ensure!(
306                    self.tracker.as_ref().wasm_fuel <= self.policy.maximum_wasm_fuel_per_block,
307                    ExecutionError::MaximumFuelExceeded(vm_runtime)
308                );
309            }
310            VmRuntime::Evm => {
311                self.tracker.as_mut().evm_fuel = self
312                    .tracker
313                    .as_ref()
314                    .evm_fuel
315                    .checked_add(fuel)
316                    .ok_or(ArithmeticError::Overflow)?;
317                ensure!(
318                    self.tracker.as_ref().evm_fuel <= self.policy.maximum_evm_fuel_per_block,
319                    ExecutionError::MaximumFuelExceeded(vm_runtime)
320                );
321            }
322        }
323        self.update_balance(self.policy.fuel_price(fuel, vm_runtime)?)
324    }
325
326    /// Tracks runtime reading of `ChainId`
327    pub(crate) fn track_runtime_chain_id(&mut self) -> Result<(), ExecutionError> {
328        self.track_size_runtime_operations(RUNTIME_CHAIN_ID_SIZE)
329    }
330
331    /// Tracks runtime reading of `BlockHeight`
332    pub(crate) fn track_runtime_block_height(&mut self) -> Result<(), ExecutionError> {
333        self.track_size_runtime_operations(RUNTIME_BLOCK_HEIGHT_SIZE)
334    }
335
336    /// Tracks runtime reading of `ApplicationId`
337    pub(crate) fn track_runtime_application_id(&mut self) -> Result<(), ExecutionError> {
338        self.track_size_runtime_operations(RUNTIME_APPLICATION_ID_SIZE)
339    }
340
341    /// Tracks runtime reading of application parameters.
342    pub(crate) fn track_runtime_application_parameters(
343        &mut self,
344        parameters: &[u8],
345    ) -> Result<(), ExecutionError> {
346        let parameters_len = parameters.len() as u32;
347        self.track_size_runtime_operations(parameters_len)
348    }
349
350    /// Tracks runtime reading of `Timestamp`
351    pub(crate) fn track_runtime_timestamp(&mut self) -> Result<(), ExecutionError> {
352        self.track_size_runtime_operations(RUNTIME_TIMESTAMP_SIZE)
353    }
354
355    /// Tracks runtime reading of balance
356    pub(crate) fn track_runtime_balance(&mut self) -> Result<(), ExecutionError> {
357        self.track_size_runtime_operations(RUNTIME_AMOUNT_SIZE)
358    }
359
360    /// Tracks runtime reading of owner balances
361    pub(crate) fn track_runtime_owner_balances(
362        &mut self,
363        owner_balances: &[(AccountOwner, Amount)],
364    ) -> Result<(), ExecutionError> {
365        let mut size = 0;
366        for (account_owner, _) in owner_balances {
367            size += account_owner.size() + RUNTIME_AMOUNT_SIZE;
368        }
369        self.track_size_runtime_operations(size)
370    }
371
372    /// Tracks runtime reading of owners
373    pub(crate) fn track_runtime_owners(
374        &mut self,
375        owners: &[AccountOwner],
376    ) -> Result<(), ExecutionError> {
377        let mut size = 0;
378        for owner in owners {
379            size += owner.size();
380        }
381        self.track_size_runtime_operations(size)
382    }
383
384    /// Tracks runtime reading of owners
385    pub(crate) fn track_runtime_chain_ownership(
386        &mut self,
387        chain_ownership: &ChainOwnership,
388    ) -> Result<(), ExecutionError> {
389        let mut size = 0;
390        for account_owner in &chain_ownership.super_owners {
391            size += account_owner.size();
392        }
393        for account_owner in chain_ownership.owners.keys() {
394            size += account_owner.size() + RUNTIME_OWNER_WEIGHT_SIZE;
395        }
396        size += RUNTIME_CONSTANT_CHAIN_OWNERSHIP_SIZE;
397        self.track_size_runtime_operations(size)
398    }
399
400    /// Tracks runtime operations.
401    fn track_size_runtime_operations(&mut self, size: u32) -> Result<(), ExecutionError> {
402        self.tracker.as_mut().bytes_runtime = self
403            .tracker
404            .as_mut()
405            .bytes_runtime
406            .checked_add(size)
407            .ok_or(ArithmeticError::Overflow)?;
408        self.update_balance(self.policy.bytes_runtime_price(size)?)
409    }
410
411    /// Tracks a read operation.
412    pub(crate) fn track_read_operation(&mut self) -> Result<(), ExecutionError> {
413        self.tracker.as_mut().read_operations = self
414            .tracker
415            .as_mut()
416            .read_operations
417            .checked_add(1)
418            .ok_or(ArithmeticError::Overflow)?;
419        self.update_balance(self.policy.read_operations_price(1)?)
420    }
421
422    /// Tracks a write operation.
423    pub(crate) fn track_write_operations(&mut self, count: u32) -> Result<(), ExecutionError> {
424        self.tracker.as_mut().write_operations = self
425            .tracker
426            .as_mut()
427            .write_operations
428            .checked_add(count)
429            .ok_or(ArithmeticError::Overflow)?;
430        self.update_balance(self.policy.write_operations_price(count)?)
431    }
432
433    /// Tracks a number of bytes read.
434    pub(crate) fn track_bytes_read(&mut self, count: u64) -> Result<(), ExecutionError> {
435        self.tracker.as_mut().bytes_read = self
436            .tracker
437            .as_mut()
438            .bytes_read
439            .checked_add(count)
440            .ok_or(ArithmeticError::Overflow)?;
441        if self.tracker.as_mut().bytes_read >= self.policy.maximum_bytes_read_per_block {
442            return Err(ExecutionError::ExcessiveRead);
443        }
444        self.update_balance(self.policy.bytes_read_price(count)?)?;
445        Ok(())
446    }
447
448    /// Tracks a number of bytes written.
449    pub(crate) fn track_bytes_written(&mut self, count: u64) -> Result<(), ExecutionError> {
450        self.tracker.as_mut().bytes_written = self
451            .tracker
452            .as_mut()
453            .bytes_written
454            .checked_add(count)
455            .ok_or(ArithmeticError::Overflow)?;
456        if self.tracker.as_mut().bytes_written >= self.policy.maximum_bytes_written_per_block {
457            return Err(ExecutionError::ExcessiveWrite);
458        }
459        self.update_balance(self.policy.bytes_written_price(count)?)?;
460        Ok(())
461    }
462
463    /// Tracks a number of blob bytes written.
464    pub(crate) fn track_blob_read(&mut self, count: u64) -> Result<(), ExecutionError> {
465        {
466            let tracker = self.tracker.as_mut();
467            tracker.blob_bytes_read = tracker
468                .blob_bytes_read
469                .checked_add(count)
470                .ok_or(ArithmeticError::Overflow)?;
471            tracker.blobs_read = tracker
472                .blobs_read
473                .checked_add(1)
474                .ok_or(ArithmeticError::Overflow)?;
475        }
476        self.update_balance(self.policy.blob_read_price(count)?)?;
477        Ok(())
478    }
479
480    /// Tracks a number of blob bytes published.
481    pub fn track_blob_published(&mut self, blob: &Blob) -> Result<(), ExecutionError> {
482        self.policy.check_blob_size(blob.content())?;
483        let size = blob.content().bytes().len() as u64;
484        if blob.is_committee_blob() {
485            return Ok(());
486        }
487        {
488            let tracker = self.tracker.as_mut();
489            tracker.blob_bytes_published = tracker
490                .blob_bytes_published
491                .checked_add(size)
492                .ok_or(ArithmeticError::Overflow)?;
493            tracker.blobs_published = tracker
494                .blobs_published
495                .checked_add(1)
496                .ok_or(ArithmeticError::Overflow)?;
497        }
498        self.update_balance(self.policy.blob_published_price(size)?)?;
499        Ok(())
500    }
501
502    /// Tracks a change in the number of bytes stored.
503    // TODO(#1536): This is not fully implemented.
504    #[allow(dead_code)]
505    pub(crate) fn track_stored_bytes(&mut self, delta: i32) -> Result<(), ExecutionError> {
506        self.tracker.as_mut().bytes_stored = self
507            .tracker
508            .as_mut()
509            .bytes_stored
510            .checked_add(delta)
511            .ok_or(ArithmeticError::Overflow)?;
512        Ok(())
513    }
514
515    /// Returns the remaining time services can spend executing as oracles.
516    pub(crate) fn remaining_service_oracle_execution_time(
517        &self,
518    ) -> Result<Duration, ExecutionError> {
519        let tracker = self.tracker.as_ref();
520        let spent_execution_time = tracker.service_oracle_execution;
521        let limit = Duration::from_millis(self.policy.maximum_service_oracle_execution_ms);
522
523        limit
524            .checked_sub(spent_execution_time)
525            .ok_or(ExecutionError::MaximumServiceOracleExecutionTimeExceeded)
526    }
527
528    /// Tracks a call to a service to run as an oracle.
529    pub(crate) fn track_service_oracle_call(&mut self) -> Result<(), ExecutionError> {
530        self.tracker.as_mut().service_oracle_queries = self
531            .tracker
532            .as_mut()
533            .service_oracle_queries
534            .checked_add(1)
535            .ok_or(ArithmeticError::Overflow)?;
536        self.update_balance(self.policy.service_as_oracle_query)
537    }
538
539    /// Tracks the time spent executing the service as an oracle.
540    pub(crate) fn track_service_oracle_execution(
541        &mut self,
542        execution_time: Duration,
543    ) -> Result<(), ExecutionError> {
544        let tracker = self.tracker.as_mut();
545        let spent_execution_time = &mut tracker.service_oracle_execution;
546        let limit = Duration::from_millis(self.policy.maximum_service_oracle_execution_ms);
547
548        *spent_execution_time = spent_execution_time.saturating_add(execution_time);
549
550        ensure!(
551            *spent_execution_time < limit,
552            ExecutionError::MaximumServiceOracleExecutionTimeExceeded
553        );
554
555        Ok(())
556    }
557
558    /// Tracks the size of a response produced by an oracle.
559    pub(crate) fn track_service_oracle_response(
560        &mut self,
561        response_bytes: usize,
562    ) -> Result<(), ExecutionError> {
563        ensure!(
564            response_bytes as u64 <= self.policy.maximum_oracle_response_bytes,
565            ExecutionError::ServiceOracleResponseTooLarge
566        );
567
568        Ok(())
569    }
570}
571
572impl<Account, Tracker> ResourceController<Account, Tracker>
573where
574    Tracker: AsMut<ResourceTracker>,
575{
576    /// Tracks the serialized size of a block, or parts of it.
577    pub fn track_block_size_of(&mut self, data: &impl Serialize) -> Result<(), ExecutionError> {
578        self.track_block_size(bcs::serialized_size(data)?)
579    }
580
581    /// Tracks the serialized size of a block, or parts of it.
582    pub fn track_block_size(&mut self, size: usize) -> Result<(), ExecutionError> {
583        let tracker = self.tracker.as_mut();
584        tracker.block_size = u64::try_from(size)
585            .ok()
586            .and_then(|size| tracker.block_size.checked_add(size))
587            .ok_or(ExecutionError::BlockTooLarge)?;
588        ensure!(
589            tracker.block_size <= self.policy.maximum_block_size,
590            ExecutionError::BlockTooLarge
591        );
592        Ok(())
593    }
594}
595
596impl ResourceController<Option<AccountOwner>, ResourceTracker> {
597    /// Provides a reference to the current execution state and obtains a temporary object
598    /// where the accounting functions of [`ResourceController`] are available.
599    pub async fn with_state<'a, C>(
600        &mut self,
601        view: &'a mut SystemExecutionStateView<C>,
602    ) -> Result<ResourceController<Sources<'a>, &mut ResourceTracker>, ViewError>
603    where
604        C: Context + Clone + Send + Sync + 'static,
605    {
606        self.with_state_and_grant(view, None).await
607    }
608
609    /// Provides a reference to the current execution state as well as an optional grant,
610    /// and obtains a temporary object where the accounting functions of
611    /// [`ResourceController`] are available.
612    pub async fn with_state_and_grant<'a, C>(
613        &mut self,
614        view: &'a mut SystemExecutionStateView<C>,
615        grant: Option<&'a mut Amount>,
616    ) -> Result<ResourceController<Sources<'a>, &mut ResourceTracker>, ViewError>
617    where
618        C: Context + Clone + Send + Sync + 'static,
619    {
620        let mut sources = Vec::new();
621        // First, use the grant (e.g. for messages) and otherwise use the chain account
622        // (e.g. for blocks and operations).
623        if let Some(grant) = grant {
624            sources.push(grant);
625        } else {
626            sources.push(view.balance.get_mut());
627        }
628        // Then the local account, if any. Currently, any negative fee (e.g. storage
629        // refund) goes preferably to this account.
630        if let Some(owner) = &self.account {
631            if let Some(balance) = view.balances.get_mut(owner).await? {
632                sources.push(balance);
633            }
634        }
635
636        Ok(ResourceController {
637            policy: self.policy.clone(),
638            tracker: &mut self.tracker,
639            account: Sources { sources },
640        })
641    }
642}
643
644// The simplest `BalanceHolder` is an `Amount`.
645impl BalanceHolder for Amount {
646    fn balance(&self) -> Result<Amount, ArithmeticError> {
647        Ok(*self)
648    }
649
650    fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
651        self.try_add_assign(other)
652    }
653
654    fn try_sub_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
655        self.try_sub_assign(other)
656    }
657}
658
659// This is also needed for the default instantiation `ResourceController<Amount, ResourceTracker>`.
660// See https://doc.rust-lang.org/std/convert/trait.AsMut.html#reflexivity for general context.
661impl AsMut<ResourceTracker> for ResourceTracker {
662    fn as_mut(&mut self) -> &mut Self {
663        self
664    }
665}
666
667impl AsRef<ResourceTracker> for ResourceTracker {
668    fn as_ref(&self) -> &Self {
669        self
670    }
671}
672
673/// A temporary object holding a number of references to funding sources.
674pub struct Sources<'a> {
675    sources: Vec<&'a mut Amount>,
676}
677
678impl BalanceHolder for Sources<'_> {
679    fn balance(&self) -> Result<Amount, ArithmeticError> {
680        let mut amount = Amount::ZERO;
681        for source in self.sources.iter() {
682            amount.try_add_assign(**source)?;
683        }
684        Ok(amount)
685    }
686
687    fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
688        // Try to credit the owner account first.
689        // TODO(#1648): This may need some additional design work.
690        let source = self.sources.last_mut().expect("at least one source");
691        source.try_add_assign(other)
692    }
693
694    fn try_sub_assign(&mut self, mut other: Amount) -> Result<(), ArithmeticError> {
695        for source in self.sources.iter_mut() {
696            if source.try_sub_assign(other).is_ok() {
697                return Ok(());
698            }
699            other.try_sub_assign(**source).expect("*source < other");
700            **source = Amount::ZERO;
701        }
702        if other > Amount::ZERO {
703            Err(ArithmeticError::Underflow)
704        } else {
705            Ok(())
706        }
707    }
708}