Skip to main content

miden_protocol/block/account_tree/
mod.rs

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