miden_objects/block/
account_tree.rs

1use alloc::boxed::Box;
2use alloc::string::ToString;
3use alloc::vec::Vec;
4
5use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable};
6use miden_crypto::merkle::{LeafIndex, MerkleError, MutationSet, Smt, SmtLeaf, SmtProof};
7use miden_processor::{DeserializationError, SMT_DEPTH};
8
9use crate::Word;
10use crate::account::{AccountId, AccountIdPrefix};
11use crate::block::AccountWitness;
12use crate::errors::AccountTreeError;
13
14// FREE HELPER FUNCTIONS
15// ================================================================================================
16// These module-level functions provide conversions between AccountIds and SMT keys.
17// They avoid the need for awkward syntax like account_id_to_smt_key().
18
19const KEY_PREFIX_IDX: usize = 3;
20const KEY_SUFFIX_IDX: usize = 2;
21
22/// Converts an [`AccountId`] to an SMT key for use in account trees.
23///
24/// The key is constructed with the account ID suffix at index 2 and prefix at index 3.
25pub fn account_id_to_smt_key(account_id: AccountId) -> Word {
26    let mut key = Word::empty();
27    key[KEY_SUFFIX_IDX] = account_id.suffix();
28    key[KEY_PREFIX_IDX] = account_id.prefix().as_felt();
29    key
30}
31
32/// Recovers an [`AccountId`] from an SMT key.
33///
34/// # Panics
35///
36/// Panics if the key does not represent a valid account ID. This should never happen
37/// when used with keys from account trees, as the tree only stores valid IDs.
38pub fn smt_key_to_account_id(key: Word) -> AccountId {
39    AccountId::try_from([key[KEY_PREFIX_IDX], key[KEY_SUFFIX_IDX]])
40        .expect("account tree should only contain valid IDs")
41}
42
43// ACCOUNT TREE BACKEND TRAIT
44// ================================================================================================
45
46/// This trait abstracts over different SMT backends (e.g., `Smt` and `LargeSmt`) to allow
47/// the `AccountTree` to work with either implementation transparently.
48///
49/// Implementors must provide `Default` for creating empty instances. Users should
50/// instantiate the backend directly (potentially with entries) and then pass it to
51/// [`AccountTree::new`].
52pub trait AccountTreeBackend: Sized {
53    type Error: core::error::Error + Send + 'static;
54
55    /// Returns the number of leaves in the SMT.
56    fn num_leaves(&self) -> usize;
57
58    /// Returns all leaves in the SMT as an iterator over leaf index and leaf pairs.
59    fn leaves<'a>(&'a self) -> Box<dyn 'a + Iterator<Item = (LeafIndex<SMT_DEPTH>, SmtLeaf)>>;
60
61    /// Opens the leaf at the given key, returning a Merkle proof.
62    fn open(&self, key: &Word) -> SmtProof;
63
64    /// Applies the given mutation set to the SMT.
65    fn apply_mutations(
66        &mut self,
67        set: MutationSet<SMT_DEPTH, Word, Word>,
68    ) -> Result<(), Self::Error>;
69
70    /// Applies the given mutation set to the SMT and returns the reverse mutation set.
71    ///
72    /// The reverse mutation set can be used to revert the changes made by this operation.
73    fn apply_mutations_with_reversion(
74        &mut self,
75        set: MutationSet<SMT_DEPTH, Word, Word>,
76    ) -> Result<MutationSet<SMT_DEPTH, Word, Word>, Self::Error>;
77
78    /// Computes the mutation set required to apply the given updates to the SMT.
79    fn compute_mutations(
80        &self,
81        updates: Vec<(Word, Word)>,
82    ) -> Result<MutationSet<SMT_DEPTH, Word, Word>, Self::Error>;
83
84    /// Inserts a key-value pair into the SMT, returning the previous value at that key.
85    fn insert(&mut self, key: Word, value: Word) -> Result<Word, Self::Error>;
86
87    /// Returns the value associated with the given key.
88    fn get_value(&self, key: &Word) -> Word;
89
90    /// Returns the leaf at the given key.
91    fn get_leaf(&self, key: &Word) -> SmtLeaf;
92
93    /// Returns the root of the SMT.
94    fn root(&self) -> Word;
95}
96
97impl AccountTreeBackend for Smt {
98    type Error = MerkleError;
99
100    fn num_leaves(&self) -> usize {
101        Smt::num_leaves(self)
102    }
103
104    fn leaves<'a>(&'a self) -> Box<dyn 'a + Iterator<Item = (LeafIndex<SMT_DEPTH>, SmtLeaf)>> {
105        Box::new(Smt::leaves(self).map(|(idx, leaf)| (idx, leaf.clone())))
106    }
107
108    fn open(&self, key: &Word) -> SmtProof {
109        Smt::open(self, key)
110    }
111
112    fn apply_mutations(
113        &mut self,
114        set: MutationSet<SMT_DEPTH, Word, Word>,
115    ) -> Result<(), Self::Error> {
116        Smt::apply_mutations(self, set)
117    }
118
119    fn apply_mutations_with_reversion(
120        &mut self,
121        set: MutationSet<SMT_DEPTH, Word, Word>,
122    ) -> Result<MutationSet<SMT_DEPTH, Word, Word>, Self::Error> {
123        Smt::apply_mutations_with_reversion(self, set)
124    }
125
126    fn compute_mutations(
127        &self,
128        updates: Vec<(Word, Word)>,
129    ) -> Result<MutationSet<SMT_DEPTH, Word, Word>, Self::Error> {
130        Smt::compute_mutations(self, updates)
131    }
132
133    fn insert(&mut self, key: Word, value: Word) -> Result<Word, Self::Error> {
134        Smt::insert(self, key, value)
135    }
136
137    fn get_value(&self, key: &Word) -> Word {
138        Smt::get_value(self, key)
139    }
140
141    fn get_leaf(&self, key: &Word) -> SmtLeaf {
142        Smt::get_leaf(self, key)
143    }
144
145    fn root(&self) -> Word {
146        Smt::root(self)
147    }
148}
149
150#[cfg(feature = "std")]
151use miden_crypto::merkle::{LargeSmt, LargeSmtError, SmtStorage};
152#[cfg(feature = "std")]
153fn large_smt_error_to_merkle_error(err: LargeSmtError) -> MerkleError {
154    match err {
155        LargeSmtError::Storage(storage_err) => {
156            panic!("Storage error encountered: {:?}", storage_err)
157        },
158        LargeSmtError::Merkle(merkle_err) => merkle_err,
159    }
160}
161
162#[cfg(feature = "std")]
163impl<Backend> AccountTreeBackend for LargeSmt<Backend>
164where
165    Backend: SmtStorage,
166{
167    type Error = MerkleError;
168
169    fn num_leaves(&self) -> usize {
170        // LargeSmt::num_leaves returns Result<usize, LargeSmtError>
171        // We'll unwrap or return 0 on error
172        LargeSmt::num_leaves(self).map_err(large_smt_error_to_merkle_error).unwrap_or(0)
173    }
174
175    fn leaves<'a>(&'a self) -> Box<dyn 'a + Iterator<Item = (LeafIndex<SMT_DEPTH>, SmtLeaf)>> {
176        Box::new(LargeSmt::leaves(self).expect("Only IO can error out here"))
177    }
178
179    fn open(&self, key: &Word) -> SmtProof {
180        LargeSmt::open(self, key)
181    }
182
183    fn apply_mutations(
184        &mut self,
185        set: MutationSet<SMT_DEPTH, Word, Word>,
186    ) -> Result<(), Self::Error> {
187        LargeSmt::apply_mutations(self, set).map_err(large_smt_error_to_merkle_error)
188    }
189
190    fn apply_mutations_with_reversion(
191        &mut self,
192        set: MutationSet<SMT_DEPTH, Word, Word>,
193    ) -> Result<MutationSet<SMT_DEPTH, Word, Word>, Self::Error> {
194        LargeSmt::apply_mutations_with_reversion(self, set).map_err(large_smt_error_to_merkle_error)
195    }
196
197    fn compute_mutations(
198        &self,
199        updates: Vec<(Word, Word)>,
200    ) -> Result<MutationSet<SMT_DEPTH, Word, Word>, Self::Error> {
201        LargeSmt::compute_mutations(self, updates).map_err(large_smt_error_to_merkle_error)
202    }
203
204    fn insert(&mut self, key: Word, value: Word) -> Result<Word, Self::Error> {
205        LargeSmt::insert(self, key, value)
206    }
207
208    fn get_value(&self, key: &Word) -> Word {
209        LargeSmt::get_value(self, key)
210    }
211
212    fn get_leaf(&self, key: &Word) -> SmtLeaf {
213        LargeSmt::get_leaf(self, key)
214    }
215
216    fn root(&self) -> Word {
217        LargeSmt::root(self).map_err(large_smt_error_to_merkle_error).unwrap()
218    }
219}
220
221/// The sparse merkle tree of all accounts in the blockchain.
222///
223/// The key is the [`AccountId`] while the value is the current state commitment of the account,
224/// i.e. [`Account::commitment`](crate::account::Account::commitment). If the account is new, then
225/// the commitment is the [`EMPTY_WORD`](crate::EMPTY_WORD).
226///
227/// Each account ID occupies exactly one leaf in the tree, which is identified by its
228/// [`AccountId::prefix`]. In other words, account ID prefixes are unique in the blockchain.
229#[derive(Debug, Clone, PartialEq, Eq)]
230pub struct AccountTree<S = Smt> {
231    smt: S,
232}
233
234impl<S> Default for AccountTree<S>
235where
236    S: Default,
237{
238    fn default() -> Self {
239        Self { smt: Default::default() }
240    }
241}
242
243impl<S> AccountTree<S>
244where
245    S: AccountTreeBackend<Error = MerkleError>,
246{
247    // CONSTANTS
248    // --------------------------------------------------------------------------------------------
249
250    /// The depth of the account tree.
251    pub const DEPTH: u8 = SMT_DEPTH;
252
253    /// The index of the account ID suffix in the SMT key.
254    pub(super) const KEY_SUFFIX_IDX: usize = 2;
255    /// The index of the account ID prefix in the SMT key.
256    pub(super) const KEY_PREFIX_IDX: usize = 3;
257
258    // CONSTRUCTORS
259    // --------------------------------------------------------------------------------------------
260
261    /// Creates a new `AccountTree` from its inner representation with validation.
262    ///
263    /// This constructor validates that the provided SMT upholds the guarantees of the
264    /// [`AccountTree`]. The constructor ensures only the uniqueness of the account ID prefix.
265    ///
266    /// # Errors
267    ///
268    /// Returns an error if:
269    /// - The SMT contains duplicate account ID prefixes
270    pub fn new(smt: S) -> Result<Self, AccountTreeError> {
271        for (_leaf_idx, leaf) in smt.leaves() {
272            match leaf {
273                SmtLeaf::Empty(_) => {
274                    // Empty leaves are fine (shouldn't be returned by leaves() but handle anyway)
275                    continue;
276                },
277                SmtLeaf::Single((key, _)) => {
278                    // Single entry is good - verify it's a valid account ID
279                    Self::smt_key_to_id(key);
280                },
281                SmtLeaf::Multiple(entries) => {
282                    // Multiple entries means duplicate prefixes
283                    // Extract one of the keys to identify the duplicate prefix
284                    if let Some((key, _)) = entries.first() {
285                        let account_id = Self::smt_key_to_id(*key);
286                        return Err(AccountTreeError::DuplicateIdPrefix {
287                            duplicate_prefix: account_id.prefix(),
288                        });
289                    }
290                },
291            }
292        }
293
294        Ok(Self::new_unchecked(smt))
295    }
296
297    /// Creates a new `AccountTree` from its inner representation without validation.
298    ///
299    /// # Warning
300    ///
301    /// Assumes the provided SMT upholds the guarantees of the [`AccountTree`]. Specifically:
302    /// - Each account ID prefix must be unique (no duplicate prefixes allowed)
303    /// - The SMT should only contain valid account IDs and their state commitments
304    ///
305    /// See type-level documentation for more details on these invariants. Using this constructor
306    /// with an SMT that violates these guarantees may lead to undefined behavior.
307    pub fn new_unchecked(smt: S) -> Self {
308        AccountTree { smt }
309    }
310
311    // PUBLIC ACCESSORS
312    // --------------------------------------------------------------------------------------------
313
314    /// Returns an opening of the leaf associated with the `account_id`. This is a proof of the
315    /// current state commitment of the given account ID.
316    ///
317    /// Conceptually, an opening is a Merkle path to the leaf, as well as the leaf itself.
318    ///
319    /// # Panics
320    ///
321    /// Panics if the SMT backend fails to open the leaf (only possible with [`LargeSmt`] backend).
322    pub fn open(&self, account_id: AccountId) -> AccountWitness {
323        let key = Self::id_to_smt_key(account_id);
324        let proof = self.smt.open(&key);
325
326        AccountWitness::from_smt_proof(account_id, proof)
327    }
328
329    /// Returns the current state commitment of the given account ID.
330    pub fn get(&self, account_id: AccountId) -> Word {
331        let key = Self::id_to_smt_key(account_id);
332        self.smt.get_value(&key)
333    }
334
335    /// Returns the root of the tree.
336    pub fn root(&self) -> Word {
337        self.smt.root()
338    }
339
340    /// Returns true if the tree contains a leaf for the given account ID prefix.
341    pub fn contains_account_id_prefix(&self, account_id_prefix: AccountIdPrefix) -> bool {
342        let key = Self::id_prefix_to_smt_key(account_id_prefix);
343        let is_empty = matches!(self.smt.get_leaf(&key), SmtLeaf::Empty(_));
344        !is_empty
345    }
346
347    /// Returns the number of account IDs in this tree.
348    pub fn num_accounts(&self) -> usize {
349        // Because each ID's prefix is unique in the tree and occupies a single leaf, the number of
350        // IDs in the tree is equivalent to the number of leaves in the tree.
351        self.smt.num_leaves()
352    }
353
354    /// Returns an iterator over the account ID state commitment pairs in the tree.
355    pub fn account_commitments(&self) -> impl Iterator<Item = (AccountId, Word)> {
356        self.smt.leaves().map(|(_leaf_idx, leaf)| {
357            // SAFETY: By construction no Multiple variant is ever present in the tree.
358            // The Empty variant is not returned by Smt::leaves, because it only returns leaves that
359            // are actually present.
360            let SmtLeaf::Single((key, commitment)) = leaf else {
361                unreachable!("empty and multiple variant should never be encountered")
362            };
363
364            (
365                // SAFETY: By construction, the tree only contains valid IDs.
366                AccountId::try_from([key[Self::KEY_PREFIX_IDX], key[Self::KEY_SUFFIX_IDX]])
367                    .expect("account tree should only contain valid IDs"),
368                commitment,
369            )
370        })
371    }
372
373    /// Computes the necessary changes to insert the specified (account ID, state commitment) pairs
374    /// into this tree, allowing for validation before applying those changes.
375    ///
376    /// [`Self::apply_mutations`] can be used in order to commit these changes to the tree.
377    ///
378    /// If the `concurrent` feature of `miden-crypto` is enabled, this function uses a parallel
379    /// implementation to compute the mutations, otherwise it defaults to the sequential
380    /// implementation.
381    ///
382    /// This is a thin wrapper around [`Smt::compute_mutations`]. See its documentation for more
383    /// details.
384    ///
385    /// # Errors
386    ///
387    /// Returns an error if:
388    /// - an insertion of an account ID would violate the uniqueness of account ID prefixes in the
389    ///   tree.
390    pub fn compute_mutations(
391        &self,
392        account_commitments: impl IntoIterator<Item = (AccountId, Word)>,
393    ) -> Result<AccountMutationSet, AccountTreeError> {
394        let mutation_set = self
395            .smt
396            .compute_mutations(Vec::from_iter(
397                account_commitments
398                    .into_iter()
399                    .map(|(id, commitment)| (Self::id_to_smt_key(id), commitment)),
400            ))
401            .map_err(AccountTreeError::ComputeMutations)?;
402
403        for id_key in mutation_set.new_pairs().keys() {
404            // Check if the insertion would be valid.
405            match self.smt.get_leaf(id_key) {
406                // Inserting into an empty leaf is valid.
407                SmtLeaf::Empty(_) => (),
408                SmtLeaf::Single((existing_key, _)) => {
409                    // If the key matches the existing one, then we're updating the leaf, which is
410                    // valid. If it does not match, then we would insert a duplicate.
411                    if existing_key != *id_key {
412                        return Err(AccountTreeError::DuplicateIdPrefix {
413                            duplicate_prefix: Self::smt_key_to_id(*id_key).prefix(),
414                        });
415                    }
416                },
417                SmtLeaf::Multiple(_) => {
418                    unreachable!(
419                        "account tree should never contain duplicate ID prefixes and therefore never a multiple leaf"
420                    )
421                },
422            }
423        }
424
425        Ok(AccountMutationSet::new(mutation_set))
426    }
427
428    // PUBLIC MUTATORS
429    // --------------------------------------------------------------------------------------------
430
431    /// Inserts the state commitment for the given account ID, returning the previous state
432    /// commitment associated with that ID.
433    ///
434    /// This also recomputes all hashes between the leaf (associated with the key) and the root,
435    /// updating the root itself.
436    ///
437    /// # Errors
438    ///
439    /// Returns an error if:
440    /// - the prefix of the account ID already exists in the tree.
441    pub fn insert(
442        &mut self,
443        account_id: AccountId,
444        state_commitment: Word,
445    ) -> Result<Word, AccountTreeError> {
446        let key = Self::id_to_smt_key(account_id);
447        // SAFETY: account tree should not contain multi-entry leaves and so the maximum number
448        // of entries per leaf should never be exceeded.
449        let prev_value = self.smt.insert(key, state_commitment)
450            .expect("account tree should always have a single value per key, and hence cannot exceed the maximum leaf number");
451
452        // If the leaf of the account ID now has two or more entries, we've inserted a duplicate
453        // prefix.
454        if self.smt.get_leaf(&key).num_entries() >= 2 {
455            return Err(AccountTreeError::DuplicateIdPrefix {
456                duplicate_prefix: account_id.prefix(),
457            });
458        }
459
460        Ok(prev_value)
461    }
462
463    /// Applies the prospective mutations computed with [`Self::compute_mutations`] to this tree.
464    ///
465    /// # Errors
466    ///
467    /// Returns an error if:
468    /// - `mutations` was computed on a tree with a different root than this one.
469    pub fn apply_mutations(
470        &mut self,
471        mutations: AccountMutationSet,
472    ) -> Result<(), AccountTreeError> {
473        self.smt
474            .apply_mutations(mutations.into_mutation_set())
475            .map_err(AccountTreeError::ApplyMutations)
476    }
477
478    /// Applies the prospective mutations computed with [`Self::compute_mutations`] to this tree
479    /// and returns the reverse mutation set.
480    ///
481    /// Applying the reverse mutation sets to the updated tree will revert the changes.
482    ///
483    /// # Errors
484    ///
485    /// Returns an error if:
486    /// - `mutations` was computed on a tree with a different root than this one.
487    pub fn apply_mutations_with_reversion(
488        &mut self,
489        mutations: AccountMutationSet,
490    ) -> Result<AccountMutationSet, AccountTreeError> {
491        let reversion = self
492            .smt
493            .apply_mutations_with_reversion(mutations.into_mutation_set())
494            .map_err(AccountTreeError::ApplyMutations)?;
495        Ok(AccountMutationSet::new(reversion))
496    }
497
498    // HELPERS
499    // --------------------------------------------------------------------------------------------
500
501    /// Returns the SMT key of the given account ID.
502    pub(super) fn id_to_smt_key(account_id: AccountId) -> Word {
503        // We construct this in such a way that we're forced to use the constants, so that when
504        // they're updated, the other usages of the constants are also updated.
505        let mut key = Word::empty();
506        key[Self::KEY_SUFFIX_IDX] = account_id.suffix();
507        key[Self::KEY_PREFIX_IDX] = account_id.prefix().as_felt();
508
509        key
510    }
511
512    /// Returns the SMT key of the given account ID prefix.
513    fn id_prefix_to_smt_key(account_id: AccountIdPrefix) -> Word {
514        // We construct this in such a way that we're forced to use the constants, so that when
515        // they're updated, the other usages of the constants are also updated.
516        let mut key = Word::empty();
517        key[Self::KEY_PREFIX_IDX] = account_id.as_felt();
518
519        key
520    }
521
522    /// Returns the [`AccountId`] recovered from the given SMT key.
523    ///
524    /// # Panics
525    ///
526    /// Panics if:
527    /// - the key is not a valid account ID. This should not happen when used on keys from (partial)
528    ///   account tree.
529    pub(super) fn smt_key_to_id(key: Word) -> AccountId {
530        AccountId::try_from([key[Self::KEY_PREFIX_IDX], key[Self::KEY_SUFFIX_IDX]])
531            .expect("account tree should only contain valid IDs")
532    }
533}
534
535// CONVENIENCE METHODS
536// ================================================================================================
537
538impl AccountTree<Smt> {
539    /// Creates a new [`AccountTree`] with the provided entries.
540    ///
541    /// This is a convenience method for testing that creates an SMT backend with the provided
542    /// entries and wraps it in an AccountTree. It validates that the entries don't contain
543    /// duplicate prefixes.
544    ///
545    /// # Errors
546    ///
547    /// Returns an error if:
548    /// - The provided entries contain duplicate account ID prefixes
549    /// - The backend fails to create the SMT with the entries
550    pub fn with_entries<I>(
551        entries: impl IntoIterator<Item = (AccountId, Word), IntoIter = I>,
552    ) -> Result<Self, AccountTreeError>
553    where
554        I: ExactSizeIterator<Item = (AccountId, Word)>,
555    {
556        // Create the SMT with the entries
557        let smt = Smt::with_entries(
558            entries
559                .into_iter()
560                .map(|(id, commitment)| (account_id_to_smt_key(id), commitment)),
561        )
562        .map_err(|err| {
563            let MerkleError::DuplicateValuesForIndex(leaf_idx) = err else {
564                unreachable!("the only error returned by Smt::with_entries is of this type");
565            };
566
567            // SAFETY: Since we only inserted account IDs into the SMT, it is guaranteed that
568            // the leaf_idx is a valid Felt as well as a valid account ID prefix.
569            AccountTreeError::DuplicateStateCommitments {
570                prefix: AccountIdPrefix::new_unchecked(
571                    crate::Felt::try_from(leaf_idx).expect("leaf index should be a valid felt"),
572                ),
573            }
574        })?;
575
576        AccountTree::new(smt)
577    }
578}
579
580// SERIALIZATION
581// ================================================================================================
582
583impl Serializable for AccountTree {
584    fn write_into<W: ByteWriter>(&self, target: &mut W) {
585        self.account_commitments().collect::<Vec<_>>().write_into(target);
586    }
587}
588
589impl Deserializable for AccountTree {
590    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
591        let entries = Vec::<(AccountId, Word)>::read_from(source)?;
592
593        // Validate uniqueness of account ID prefixes before creating the tree
594        let mut seen_prefixes = alloc::collections::BTreeSet::new();
595        for (id, _) in &entries {
596            if !seen_prefixes.insert(id.prefix()) {
597                return Err(DeserializationError::InvalidValue(format!(
598                    "Duplicate account ID prefix: {}",
599                    id.prefix()
600                )));
601            }
602        }
603
604        // Create the SMT with validated entries
605        let smt =
606            Smt::with_entries(entries.into_iter().map(|(k, v)| (account_id_to_smt_key(k), v)))
607                .map_err(|err| DeserializationError::InvalidValue(err.to_string()))?;
608        Ok(Self::new_unchecked(smt))
609    }
610}
611
612// ACCOUNT MUTATION SET
613// ================================================================================================
614
615/// A newtype wrapper around a [`MutationSet`] for use in the [`AccountTree`].
616///
617/// It guarantees that applying the contained mutations will result in an account tree with unique
618/// account ID prefixes.
619///
620/// It is returned by and used in methods on the [`AccountTree`].
621#[derive(Debug, Clone, PartialEq, Eq)]
622pub struct AccountMutationSet {
623    mutation_set: MutationSet<SMT_DEPTH, Word, Word>,
624}
625
626impl AccountMutationSet {
627    // CONSTRUCTORS
628    // --------------------------------------------------------------------------------------------
629
630    /// Creates a new [`AccountMutationSet`] from the provided raw mutation set.
631    fn new(mutation_set: MutationSet<SMT_DEPTH, Word, Word>) -> Self {
632        Self { mutation_set }
633    }
634
635    // PUBLIC ACCESSORS
636    // --------------------------------------------------------------------------------------------
637
638    /// Returns a reference to the underlying [`MutationSet`].
639    pub fn as_mutation_set(&self) -> &MutationSet<SMT_DEPTH, Word, Word> {
640        &self.mutation_set
641    }
642
643    // PUBLIC MUTATORS
644    // --------------------------------------------------------------------------------------------
645
646    /// Consumes self and returns the underlying [`MutationSet`].
647    pub fn into_mutation_set(self) -> MutationSet<SMT_DEPTH, Word, Word> {
648        self.mutation_set
649    }
650}
651
652// TESTS
653// ================================================================================================
654
655#[cfg(test)]
656pub(super) mod tests {
657    use std::vec::Vec;
658
659    use assert_matches::assert_matches;
660
661    use super::*;
662    use crate::account::{AccountStorageMode, AccountType};
663    use crate::testing::account_id::{AccountIdBuilder, account_id};
664
665    pub(crate) fn setup_duplicate_prefix_ids() -> [(AccountId, Word); 2] {
666        let id0 = AccountId::try_from(account_id(
667            AccountType::FungibleFaucet,
668            AccountStorageMode::Public,
669            0xaabb_ccdd,
670        ))
671        .unwrap();
672        let id1 = AccountId::try_from(account_id(
673            AccountType::FungibleFaucet,
674            AccountStorageMode::Public,
675            0xaabb_ccff,
676        ))
677        .unwrap();
678        assert_eq!(id0.prefix(), id1.prefix(), "test requires that these ids have the same prefix");
679
680        let commitment0 = Word::from([0, 0, 0, 42u32]);
681        let commitment1 = Word::from([0, 0, 0, 24u32]);
682
683        assert_eq!(id0.prefix(), id1.prefix(), "test requires that these ids have the same prefix");
684        [(id0, commitment0), (id1, commitment1)]
685    }
686
687    #[test]
688    fn insert_fails_on_duplicate_prefix() {
689        let mut tree = AccountTree::<Smt>::default();
690        let [(id0, commitment0), (id1, commitment1)] = setup_duplicate_prefix_ids();
691
692        tree.insert(id0, commitment0).unwrap();
693        assert_eq!(tree.get(id0), commitment0);
694
695        let err = tree.insert(id1, commitment1).unwrap_err();
696
697        assert_matches!(err, AccountTreeError::DuplicateIdPrefix {
698          duplicate_prefix
699        } if duplicate_prefix == id0.prefix());
700    }
701
702    #[test]
703    fn insert_succeeds_on_multiple_updates() {
704        let mut tree = AccountTree::<Smt>::default();
705        let [(id0, commitment0), (_, commitment1)] = setup_duplicate_prefix_ids();
706
707        tree.insert(id0, commitment0).unwrap();
708        tree.insert(id0, commitment1).unwrap();
709        assert_eq!(tree.get(id0), commitment1);
710    }
711
712    #[test]
713    fn apply_mutations() {
714        let id0 = AccountIdBuilder::new().build_with_seed([5; 32]);
715        let id1 = AccountIdBuilder::new().build_with_seed([6; 32]);
716        let id2 = AccountIdBuilder::new().build_with_seed([7; 32]);
717
718        let digest0 = Word::from([0, 0, 0, 1u32]);
719        let digest1 = Word::from([0, 0, 0, 2u32]);
720        let digest2 = Word::from([0, 0, 0, 3u32]);
721        let digest3 = Word::from([0, 0, 0, 4u32]);
722
723        let mut tree = AccountTree::with_entries([(id0, digest0), (id1, digest1)]).unwrap();
724
725        let mutations = tree
726            .compute_mutations([(id0, digest1), (id1, digest2), (id2, digest3)])
727            .unwrap();
728
729        tree.apply_mutations(mutations).unwrap();
730
731        assert_eq!(tree.num_accounts(), 3);
732        assert_eq!(tree.get(id0), digest1);
733        assert_eq!(tree.get(id1), digest2);
734        assert_eq!(tree.get(id2), digest3);
735    }
736
737    #[test]
738    fn duplicates_in_compute_mutations() {
739        let [pair0, pair1] = setup_duplicate_prefix_ids();
740        let id2 = AccountIdBuilder::new().build_with_seed([5; 32]);
741        let commitment2 = Word::from([0, 0, 0, 99u32]);
742
743        let tree = AccountTree::with_entries([pair0, (id2, commitment2)]).unwrap();
744        let err = tree.compute_mutations([pair1]).unwrap_err();
745
746        assert_matches!(err, AccountTreeError::DuplicateIdPrefix {
747          duplicate_prefix
748        } if duplicate_prefix == pair1.0.prefix());
749    }
750
751    #[test]
752    fn account_commitments() {
753        let id0 = AccountIdBuilder::new().build_with_seed([5; 32]);
754        let id1 = AccountIdBuilder::new().build_with_seed([6; 32]);
755        let id2 = AccountIdBuilder::new().build_with_seed([7; 32]);
756
757        let digest0 = Word::from([0, 0, 0, 1u32]);
758        let digest1 = Word::from([0, 0, 0, 2u32]);
759        let digest2 = Word::from([0, 0, 0, 3u32]);
760        let empty_digest = Word::empty();
761
762        let mut tree =
763            AccountTree::with_entries([(id0, digest0), (id1, digest1), (id2, digest2)]).unwrap();
764
765        // remove id2
766        tree.insert(id2, empty_digest).unwrap();
767
768        assert_eq!(tree.num_accounts(), 2);
769
770        let accounts: Vec<_> = tree.account_commitments().collect();
771        assert_eq!(accounts.len(), 2);
772        assert!(accounts.contains(&(id0, digest0)));
773        assert!(accounts.contains(&(id1, digest1)));
774    }
775
776    #[test]
777    fn account_witness() {
778        let id0 = AccountIdBuilder::new().build_with_seed([5; 32]);
779        let id1 = AccountIdBuilder::new().build_with_seed([6; 32]);
780
781        let digest0 = Word::from([0, 0, 0, 1u32]);
782        let digest1 = Word::from([0, 0, 0, 2u32]);
783
784        let tree = AccountTree::with_entries([(id0, digest0), (id1, digest1)]).unwrap();
785
786        assert_eq!(tree.num_accounts(), 2);
787
788        for id in [id0, id1] {
789            let proof = tree.smt.open(&account_id_to_smt_key(id));
790            let (control_path, control_leaf) = proof.into_parts();
791            let witness = tree.open(id);
792
793            assert_eq!(witness.leaf(), control_leaf);
794            assert_eq!(witness.path(), &control_path);
795        }
796    }
797
798    #[test]
799    fn contains_account_prefix() {
800        // Create a tree with a single account.
801        let [pair0, pair1] = setup_duplicate_prefix_ids();
802        let tree = AccountTree::with_entries([pair0]).unwrap();
803        assert_eq!(tree.num_accounts(), 1);
804
805        // Validate the leaf for the inserted account exists.
806        assert!(tree.contains_account_id_prefix(pair0.0.prefix()));
807
808        // Validate the leaf for the uninserted account with same prefix exists.
809        assert!(tree.contains_account_id_prefix(pair1.0.prefix()));
810
811        // Validate the unrelated, uninserted account leaf does not exist.
812        let id1 = AccountIdBuilder::new().build_with_seed([7; 32]);
813        assert!(!tree.contains_account_id_prefix(id1.prefix()));
814    }
815
816    #[cfg(feature = "std")]
817    #[test]
818    fn large_smt_backend_basic_operations() {
819        use miden_crypto::merkle::{LargeSmt, MemoryStorage};
820
821        // Create test data
822        let id0 = AccountIdBuilder::new().build_with_seed([5; 32]);
823        let id1 = AccountIdBuilder::new().build_with_seed([6; 32]);
824        let id2 = AccountIdBuilder::new().build_with_seed([7; 32]);
825
826        let digest0 = Word::from([0, 0, 0, 1u32]);
827        let digest1 = Word::from([0, 0, 0, 2u32]);
828        let digest2 = Word::from([0, 0, 0, 3u32]);
829
830        // Create AccountTree with LargeSmt backend
831        let tree = LargeSmt::<MemoryStorage>::with_entries(
832            MemoryStorage::default(),
833            [(account_id_to_smt_key(id0), digest0), (account_id_to_smt_key(id1), digest1)],
834        )
835        .map(AccountTree::new_unchecked)
836        .unwrap();
837
838        // Test basic operations
839        assert_eq!(tree.num_accounts(), 2);
840        assert_eq!(tree.get(id0), digest0);
841        assert_eq!(tree.get(id1), digest1);
842
843        // Test opening
844        let witness0 = tree.open(id0);
845        assert_eq!(witness0.id(), id0);
846
847        // Test mutations
848        let mut tree_mut = LargeSmt::<MemoryStorage>::with_entries(
849            MemoryStorage::default(),
850            [(account_id_to_smt_key(id0), digest0), (account_id_to_smt_key(id1), digest1)],
851        )
852        .map(AccountTree::new_unchecked)
853        .unwrap();
854        tree_mut.insert(id2, digest2).unwrap();
855        assert_eq!(tree_mut.num_accounts(), 3);
856        assert_eq!(tree_mut.get(id2), digest2);
857
858        // Verify original tree unchanged
859        assert_eq!(tree.num_accounts(), 2);
860    }
861
862    #[cfg(feature = "std")]
863    #[test]
864    fn large_smt_backend_duplicate_prefix_check() {
865        use miden_crypto::merkle::{LargeSmt, MemoryStorage};
866
867        let [(id0, commitment0), (id1, commitment1)] = setup_duplicate_prefix_ids();
868
869        let mut tree = AccountTree::new_unchecked(LargeSmt::new(MemoryStorage::default()).unwrap());
870
871        tree.insert(id0, commitment0).unwrap();
872        assert_eq!(tree.get(id0), commitment0);
873
874        let err = tree.insert(id1, commitment1).unwrap_err();
875
876        assert_matches!(
877            err,
878            AccountTreeError::DuplicateIdPrefix { duplicate_prefix }
879            if duplicate_prefix == id0.prefix()
880        );
881    }
882
883    #[cfg(feature = "std")]
884    #[test]
885    fn large_smt_backend_apply_mutations() {
886        use miden_crypto::merkle::{LargeSmt, MemoryStorage};
887
888        let id0 = AccountIdBuilder::new().build_with_seed([5; 32]);
889        let id1 = AccountIdBuilder::new().build_with_seed([6; 32]);
890        let id2 = AccountIdBuilder::new().build_with_seed([7; 32]);
891
892        let digest0 = Word::from([0, 0, 0, 1u32]);
893        let digest1 = Word::from([0, 0, 0, 2u32]);
894        let digest2 = Word::from([0, 0, 0, 3u32]);
895        let digest3 = Word::from([0, 0, 0, 4u32]);
896
897        let mut tree = LargeSmt::with_entries(
898            MemoryStorage::default(),
899            [(account_id_to_smt_key(id0), digest0), (account_id_to_smt_key(id1), digest1)],
900        )
901        .map(AccountTree::new_unchecked)
902        .unwrap();
903
904        let mutations = tree
905            .compute_mutations([(id0, digest1), (id1, digest2), (id2, digest3)])
906            .unwrap();
907
908        tree.apply_mutations(mutations).unwrap();
909
910        assert_eq!(tree.num_accounts(), 3);
911        assert_eq!(tree.get(id0), digest1);
912        assert_eq!(tree.get(id1), digest2);
913        assert_eq!(tree.get(id2), digest3);
914    }
915
916    #[cfg(feature = "std")]
917    #[test]
918    fn large_smt_backend_same_root_as_regular_smt() {
919        use miden_crypto::merkle::{LargeSmt, MemoryStorage};
920
921        let id0 = AccountIdBuilder::new().build_with_seed([5; 32]);
922        let id1 = AccountIdBuilder::new().build_with_seed([6; 32]);
923
924        let digest0 = Word::from([0, 0, 0, 1u32]);
925        let digest1 = Word::from([0, 0, 0, 2u32]);
926
927        // Create tree with LargeSmt backend
928        let large_tree = LargeSmt::with_entries(
929            MemoryStorage::default(),
930            [(account_id_to_smt_key(id0), digest0), (account_id_to_smt_key(id1), digest1)],
931        )
932        .map(AccountTree::new_unchecked)
933        .unwrap();
934
935        // Create tree with regular Smt backend
936        let regular_tree = AccountTree::with_entries([(id0, digest0), (id1, digest1)]).unwrap();
937
938        // Both should have the same root
939        assert_eq!(large_tree.root(), regular_tree.root());
940
941        // Both should have the same account commitments
942        let large_commitments: std::collections::BTreeMap<_, _> =
943            large_tree.account_commitments().collect();
944        let regular_commitments: std::collections::BTreeMap<_, _> =
945            regular_tree.account_commitments().collect();
946
947        assert_eq!(large_commitments, regular_commitments);
948    }
949}