sui_core/accumulators/
funds_read.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::{collections::BTreeMap, sync::Arc};
5
6use sui_types::{
7    accumulator_root::{AccumulatorObjId, AccumulatorValue, U128},
8    base_types::SequenceNumber,
9    error::{SuiErrorKind, SuiResult, UserInputError},
10    storage::ChildObjectResolver,
11};
12
13pub(crate) trait AccountFundsRead: Send + Sync {
14    fn get_account_amount(
15        &self,
16        account_id: &AccumulatorObjId,
17        // Version of the accumulator root object, used to
18        // bound the version when we look for child account objects.
19        accumulator_version: SequenceNumber,
20    ) -> u128;
21
22    /// Gets latest amount in account, without a version bound on the accumulator root object.
23    /// Only used for signing time checks / RPC reads, not scheduling.
24    fn get_latest_account_amount(&self, account_id: &AccumulatorObjId) -> u128;
25
26    /// Checks if given amounts are available in the latest versions of the referenced acccumulator
27    /// objects. This does un-sequenced reads and can only be used on the signing/voting path
28    /// where deterministic results are not required.
29    fn check_amounts_available(
30        &self,
31        requested_amounts: &BTreeMap<AccumulatorObjId, u64>,
32    ) -> SuiResult {
33        for (object_id, requested_amount) in requested_amounts {
34            let actual_amount = self.get_latest_account_amount(object_id);
35
36            if actual_amount < *requested_amount as u128 {
37                return Err(SuiErrorKind::UserInputError {
38                    error: UserInputError::InvalidWithdrawReservation {
39                        error: format!(
40                            "Available amount in account for object id {} is less than requested: {} < {}",
41                            object_id, actual_amount, requested_amount
42                        ),
43                    },
44                }
45                .into());
46            }
47        }
48
49        Ok(())
50    }
51}
52
53impl AccountFundsRead for Arc<dyn ChildObjectResolver + Send + Sync> {
54    fn get_account_amount(
55        &self,
56        account_id: &AccumulatorObjId,
57        accumulator_version: SequenceNumber,
58    ) -> u128 {
59        // TODO: The implementation currently relies on the fact that we could
60        // load older versions of child objects. This has two problems:
61        // 1. Aggressive pruning might prune old versions of child objects,
62        // 2. Tidehunter might not continue to support this kinds of reads.
63        // To fix this, we could also read the latest version of the accumulator root object,
64        // and see if the provided accumulator version is already settled.
65        let value: U128 =
66            AccumulatorValue::load_by_id(self.as_ref(), Some(accumulator_version), *account_id)
67                // Expect is safe because at this point we should know that we are dealing with a Balance<T>
68                // object
69                .expect("read cannot fail")
70                .unwrap_or(U128 { value: 0 });
71
72        value.value
73    }
74
75    fn get_latest_account_amount(&self, account_id: &AccumulatorObjId) -> u128 {
76        let value = AccumulatorValue::load_by_id(self.as_ref(), None, *account_id)
77            .expect("read cannot fail")
78            .unwrap_or(U128 { value: 0 });
79
80        value.value
81    }
82}