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
22const KEY_PREFIX_IDX: usize = 3;
28const KEY_SUFFIX_IDX: usize = 2;
29
30pub 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
40pub 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
51pub fn account_id_to_smt_index(account_id: AccountId) -> LeafIndex<SMT_DEPTH> {
53 account_id_to_smt_key(account_id).into()
54}
55
56#[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 pub const DEPTH: u8 = SMT_DEPTH;
90
91 pub(super) const KEY_SUFFIX_IDX: usize = 2;
93 pub(super) const KEY_PREFIX_IDX: usize = 3;
95
96 pub fn new(smt: S) -> Result<Self, AccountTreeError> {
109 for (_leaf_idx, leaf) in smt.leaves() {
110 match leaf {
111 SmtLeaf::Empty(_) => {
112 continue;
114 },
115 SmtLeaf::Single((key, _)) => {
116 smt_key_to_account_id(key);
118 },
119 SmtLeaf::Multiple(entries) => {
120 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 pub fn new_unchecked(smt: S) -> Self {
146 AccountTree { smt }
147 }
148
149 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 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 pub fn root(&self) -> Word {
175 self.smt.root()
176 }
177
178 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 pub fn num_accounts(&self) -> usize {
187 self.smt.num_leaves()
190 }
191
192 pub fn account_commitments(&self) -> impl Iterator<Item = (AccountId, Word)> {
194 self.smt.leaves().map(|(_leaf_idx, leaf)| {
195 let SmtLeaf::Single((key, commitment)) = leaf else {
199 unreachable!("empty and multiple variant should never be encountered")
200 };
201
202 (
203 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 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 match self.smt.get_leaf(id_key) {
244 SmtLeaf::Empty(_) => (),
246 SmtLeaf::Single((existing_key, _)) => {
247 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 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 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 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 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 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 fn id_prefix_to_smt_key(account_id: AccountIdPrefix) -> Word {
341 let mut key = Word::empty();
344 key[Self::KEY_PREFIX_IDX] = account_id.as_felt();
345
346 key
347 }
348}
349
350impl 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 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 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#[derive(Debug, Clone, PartialEq, Eq)]
392pub struct AccountMutationSet {
393 mutation_set: MutationSet<SMT_DEPTH, Word, Word>,
394}
395
396impl AccountMutationSet {
397 fn new(mutation_set: MutationSet<SMT_DEPTH, Word, Word>) -> Self {
402 Self { mutation_set }
403 }
404
405 pub fn as_mutation_set(&self) -> &MutationSet<SMT_DEPTH, Word, Word> {
410 &self.mutation_set
411 }
412
413 pub fn into_mutation_set(self) -> MutationSet<SMT_DEPTH, Word, Word> {
418 self.mutation_set
419 }
420}
421
422#[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 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 let [pair0, pair1] = setup_duplicate_prefix_ids();
572 let tree = AccountTree::with_entries([pair0]).unwrap();
573 assert_eq!(tree.num_accounts(), 1);
574
575 assert!(tree.contains_account_id_prefix(pair0.0.prefix()));
577
578 assert!(tree.contains_account_id_prefix(pair1.0.prefix()));
580
581 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 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 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 assert_eq!(tree.num_accounts(), 2);
610 assert_eq!(tree.get(id0), digest0);
611 assert_eq!(tree.get(id1), digest1);
612
613 let witness0 = tree.open(id0);
615 assert_eq!(witness0.id(), id0);
616
617 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 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 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 let regular_tree = AccountTree::with_entries([(id0, digest0), (id1, digest1)]).unwrap();
707
708 assert_eq!(large_tree.root(), regular_tree.root());
710
711 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}