1use alloc::string::ToString;
2use alloc::vec::Vec;
3
4#[cfg(feature = "std")]
5use miden_crypto::merkle::smt::{LargeSmt, LargeSmtError, SmtStorage};
6
7use crate::Word;
8use crate::account::{AccountId, AccountIdPrefix};
9use crate::block::{SmtBackend, SmtBackendReader};
10use crate::crypto::merkle::MerkleError;
11use crate::crypto::merkle::smt::{MutationSet, SMT_DEPTH, Smt, SmtLeaf};
12use crate::errors::AccountTreeError;
13use crate::utils::serde::{
14 ByteReader,
15 ByteWriter,
16 Deserializable,
17 DeserializationError,
18 Serializable,
19};
20
21mod partial;
22pub use partial::PartialAccountTree;
23
24mod witness;
25pub use witness::AccountWitness;
26
27mod account_id_key;
28pub use account_id_key::AccountIdKey;
29
30#[derive(Debug, Clone, PartialEq, Eq)]
42pub struct AccountTree<S = Smt> {
43 smt: S,
44}
45
46impl<S> Default for AccountTree<S>
47where
48 S: Default,
49{
50 fn default() -> Self {
51 Self { smt: Default::default() }
52 }
53}
54
55impl<S> AccountTree<S>
56where
57 S: SmtBackendReader<Error = MerkleError>,
58{
59 pub const DEPTH: u8 = SMT_DEPTH;
64
65 pub fn new(smt: S) -> Result<Self, AccountTreeError> {
79 for (_leaf_idx, leaf) in smt.leaves() {
80 match leaf {
81 SmtLeaf::Empty(_) => {
82 continue;
84 },
85 SmtLeaf::Single((key, _)) => {
86 AccountIdKey::try_from_word(key).map_err(|err| {
88 AccountTreeError::InvalidAccountIdKey { key, source: err }
89 })?;
90 },
91 SmtLeaf::Multiple(entries) => {
92 if let Some((key, _)) = entries.first() {
95 let key = *key;
96 let account_id = AccountIdKey::try_from_word(key).map_err(|err| {
97 AccountTreeError::InvalidAccountIdKey { key, source: err }
98 })?;
99
100 return Err(AccountTreeError::DuplicateIdPrefix {
101 duplicate_prefix: account_id.prefix(),
102 });
103 }
104 },
105 }
106 }
107
108 Ok(Self::new_unchecked(smt))
109 }
110
111 pub fn new_unchecked(smt: S) -> Self {
122 AccountTree { smt }
123 }
124
125 pub fn open(&self, account_id: AccountId) -> AccountWitness {
137 let key = AccountIdKey::from(account_id).as_word();
138 let proof = self.smt.open(&key);
139
140 AccountWitness::from_smt_proof(account_id, proof)
141 }
142
143 pub fn get(&self, account_id: AccountId) -> Word {
145 let key = AccountIdKey::from(account_id).as_word();
146 self.smt.get_value(&key)
147 }
148
149 pub fn root(&self) -> Word {
151 self.smt.root()
152 }
153
154 pub fn contains_account_id_prefix(&self, account_id_prefix: AccountIdPrefix) -> bool {
156 let key = AccountIdKey::id_prefix_to_smt_key(account_id_prefix);
157 let is_empty = matches!(self.smt.get_leaf(&key), SmtLeaf::Empty(_));
158 !is_empty
159 }
160
161 pub fn num_accounts(&self) -> usize {
163 self.smt.num_leaves()
166 }
167
168 pub fn account_commitments(&self) -> impl Iterator<Item = (AccountId, Word)> {
170 self.smt.leaves().map(|(_leaf_idx, leaf)| {
171 let SmtLeaf::Single((key, commitment)) = leaf else {
175 unreachable!("empty and multiple variant should never be encountered")
176 };
177
178 (
179 AccountIdKey::try_from_word(key)
181 .expect("account tree should only contain valid IDs"),
182 commitment,
183 )
184 })
185 }
186}
187
188impl<S> AccountTree<S>
189where
190 S: SmtBackend<Error = MerkleError>,
191{
192 pub fn compute_mutations(
214 &self,
215 account_commitments: impl IntoIterator<Item = (AccountId, Word)>,
216 ) -> Result<AccountMutationSet, AccountTreeError> {
217 let mutation_set = self
218 .smt
219 .compute_mutations(Vec::from_iter(
220 account_commitments
221 .into_iter()
222 .map(|(id, commitment)| (AccountIdKey::from(id).as_word(), commitment)),
223 ))
224 .map_err(AccountTreeError::ComputeMutations)?;
225
226 for id_key in mutation_set.new_pairs().keys() {
227 match self.smt.get_leaf(id_key) {
229 SmtLeaf::Empty(_) => (),
231 SmtLeaf::Single((existing_key, _)) => {
232 if existing_key != *id_key {
235 return Err(AccountTreeError::DuplicateIdPrefix {
236 duplicate_prefix: AccountIdKey::try_from_word(*id_key)
237 .expect("account tree should only contain valid IDs")
238 .prefix(),
239 });
240 }
241 },
242 SmtLeaf::Multiple(_) => {
243 unreachable!(
244 "account tree should never contain duplicate ID prefixes and therefore never a multiple leaf"
245 )
246 },
247 }
248 }
249
250 Ok(AccountMutationSet::new(mutation_set))
251 }
252
253 pub fn insert(
264 &mut self,
265 account_id: AccountId,
266 state_commitment: Word,
267 ) -> Result<Word, AccountTreeError> {
268 let key = AccountIdKey::from(account_id).as_word();
269 let prev_value = self.smt.insert(key, state_commitment)
272 .expect("account tree should always have a single value per key, and hence cannot exceed the maximum leaf number");
273
274 if self.smt.get_leaf(&key).num_entries() >= 2 {
277 return Err(AccountTreeError::DuplicateIdPrefix {
278 duplicate_prefix: account_id.prefix(),
279 });
280 }
281
282 Ok(prev_value)
283 }
284
285 pub fn apply_mutations(
292 &mut self,
293 mutations: AccountMutationSet,
294 ) -> Result<(), AccountTreeError> {
295 self.smt
296 .apply_mutations(mutations.into_mutation_set())
297 .map_err(AccountTreeError::ApplyMutations)
298 }
299
300 pub fn apply_mutations_with_reversion(
310 &mut self,
311 mutations: AccountMutationSet,
312 ) -> Result<AccountMutationSet, AccountTreeError> {
313 let reversion = self
314 .smt
315 .apply_mutations_with_reversion(mutations.into_mutation_set())
316 .map_err(AccountTreeError::ApplyMutations)?;
317 Ok(AccountMutationSet::new(reversion))
318 }
319}
320
321impl AccountTree<Smt> {
322 pub fn with_entries<I>(
334 entries: impl IntoIterator<Item = (AccountId, Word), IntoIter = I>,
335 ) -> Result<Self, AccountTreeError>
336 where
337 I: ExactSizeIterator<Item = (AccountId, Word)>,
338 {
339 let smt = Smt::with_entries(
341 entries
342 .into_iter()
343 .map(|(id, commitment)| (AccountIdKey::from(id).as_word(), commitment)),
344 )
345 .map_err(duplicate_state_commitment_error)?;
346
347 AccountTree::new(smt)
348 }
349}
350
351#[cfg(feature = "std")]
352impl<Backend> AccountTree<LargeSmt<Backend>>
353where
354 Backend: SmtStorage,
355{
356 pub fn with_storage_from_entries(
370 storage: Backend,
371 entries: impl IntoIterator<Item = (AccountId, Word)>,
372 ) -> Result<Self, AccountTreeError> {
373 use crate::block::smt_backend::large_smt_error_to_merkle_error;
374
375 let leaves = entries
376 .into_iter()
377 .map(|(id, commitment)| (AccountIdKey::from(id).as_word(), commitment));
378
379 let smt = LargeSmt::<Backend>::with_entries(storage, leaves)
380 .map_err(large_smt_error_to_merkle_error)
381 .map_err(duplicate_state_commitment_error)?;
382
383 AccountTree::new(smt)
384 }
385
386 pub fn reader(&self) -> Result<AccountTree<LargeSmt<Backend::Reader>>, LargeSmtError> {
388 Ok(AccountTree::new_unchecked(self.smt.reader()?))
389 }
390}
391
392fn duplicate_state_commitment_error(err: MerkleError) -> AccountTreeError {
397 let MerkleError::DuplicateValuesForIndex(leaf_idx) = err else {
398 unreachable!("the only error returned by the SMT constructors is a duplicate-value error");
399 };
400
401 AccountTreeError::DuplicateStateCommitments {
404 prefix: AccountIdPrefix::new_unchecked(
405 crate::Felt::try_from(leaf_idx).expect("leaf index should be a valid felt"),
406 ),
407 }
408}
409
410impl Serializable for AccountTree {
414 fn write_into<W: ByteWriter>(&self, target: &mut W) {
415 self.account_commitments().collect::<Vec<_>>().write_into(target);
416 }
417}
418
419impl Deserializable for AccountTree {
420 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
421 let entries = Vec::<(AccountId, Word)>::read_from(source)?;
422
423 let mut seen_prefixes = alloc::collections::BTreeSet::new();
425 for (id, _) in &entries {
426 if !seen_prefixes.insert(id.prefix()) {
427 return Err(DeserializationError::InvalidValue(format!(
428 "Duplicate account ID prefix: {}",
429 id.prefix()
430 )));
431 }
432 }
433
434 let smt = Smt::with_entries(
436 entries.into_iter().map(|(k, v)| (AccountIdKey::from(k).as_word(), v)),
437 )
438 .map_err(|err| DeserializationError::InvalidValue(err.to_string()))?;
439 Ok(Self::new_unchecked(smt))
440 }
441}
442
443#[derive(Debug, Clone, PartialEq, Eq)]
453pub struct AccountMutationSet {
454 mutation_set: MutationSet<SMT_DEPTH, Word, Word>,
455}
456
457impl AccountMutationSet {
458 fn new(mutation_set: MutationSet<SMT_DEPTH, Word, Word>) -> Self {
463 Self { mutation_set }
464 }
465
466 pub fn as_mutation_set(&self) -> &MutationSet<SMT_DEPTH, Word, Word> {
471 &self.mutation_set
472 }
473
474 pub fn into_mutation_set(self) -> MutationSet<SMT_DEPTH, Word, Word> {
479 self.mutation_set
480 }
481}
482
483#[cfg(test)]
487pub(super) mod tests {
488 use std::vec::Vec;
489
490 use assert_matches::assert_matches;
491
492 use super::*;
493 use crate::account::{AccountType, AssetCallbackFlag};
494 use crate::testing::account_id::{AccountIdBuilder, account_id};
495
496 pub(crate) fn setup_duplicate_prefix_ids() -> [(AccountId, Word); 2] {
497 let id0 = AccountId::try_from(account_id(
498 AccountType::Public,
499 AssetCallbackFlag::Disabled,
500 0xaabb_ccdd,
501 ))
502 .unwrap();
503 let id1 = AccountId::try_from(account_id(
504 AccountType::Public,
505 AssetCallbackFlag::Disabled,
506 0xaabb_ccff,
507 ))
508 .unwrap();
509 assert_eq!(id0.prefix(), id1.prefix(), "test requires that these ids have the same prefix");
510
511 let commitment0 = Word::from([0, 0, 0, 42u32]);
512 let commitment1 = Word::from([0, 0, 0, 24u32]);
513
514 assert_eq!(id0.prefix(), id1.prefix(), "test requires that these ids have the same prefix");
515 [(id0, commitment0), (id1, commitment1)]
516 }
517
518 #[test]
519 fn insert_fails_on_duplicate_prefix() {
520 let mut tree = AccountTree::<Smt>::default();
521 let [(id0, commitment0), (id1, commitment1)] = setup_duplicate_prefix_ids();
522
523 tree.insert(id0, commitment0).unwrap();
524 assert_eq!(tree.get(id0), commitment0);
525
526 let err = tree.insert(id1, commitment1).unwrap_err();
527
528 assert_matches!(err, AccountTreeError::DuplicateIdPrefix {
529 duplicate_prefix
530 } if duplicate_prefix == id0.prefix());
531 }
532
533 #[test]
534 fn insert_succeeds_on_multiple_updates() {
535 let mut tree = AccountTree::<Smt>::default();
536 let [(id0, commitment0), (_, commitment1)] = setup_duplicate_prefix_ids();
537
538 tree.insert(id0, commitment0).unwrap();
539 tree.insert(id0, commitment1).unwrap();
540 assert_eq!(tree.get(id0), commitment1);
541 }
542
543 #[test]
544 fn apply_mutations() {
545 let id0 = AccountIdBuilder::new().build_with_seed([5; 32]);
546 let id1 = AccountIdBuilder::new().build_with_seed([6; 32]);
547 let id2 = AccountIdBuilder::new().build_with_seed([7; 32]);
548
549 let digest0 = Word::from([0, 0, 0, 1u32]);
550 let digest1 = Word::from([0, 0, 0, 2u32]);
551 let digest2 = Word::from([0, 0, 0, 3u32]);
552 let digest3 = Word::from([0, 0, 0, 4u32]);
553
554 let mut tree = AccountTree::with_entries([(id0, digest0), (id1, digest1)]).unwrap();
555
556 let mutations = tree
557 .compute_mutations([(id0, digest1), (id1, digest2), (id2, digest3)])
558 .unwrap();
559
560 tree.apply_mutations(mutations).unwrap();
561
562 assert_eq!(tree.num_accounts(), 3);
563 assert_eq!(tree.get(id0), digest1);
564 assert_eq!(tree.get(id1), digest2);
565 assert_eq!(tree.get(id2), digest3);
566 }
567
568 #[test]
569 fn duplicates_in_compute_mutations() {
570 let [pair0, pair1] = setup_duplicate_prefix_ids();
571 let id2 = AccountIdBuilder::new().build_with_seed([5; 32]);
572 let commitment2 = Word::from([0, 0, 0, 99u32]);
573
574 let tree = AccountTree::with_entries([pair0, (id2, commitment2)]).unwrap();
575 let err = tree.compute_mutations([pair1]).unwrap_err();
576
577 assert_matches!(err, AccountTreeError::DuplicateIdPrefix {
578 duplicate_prefix
579 } if duplicate_prefix == pair1.0.prefix());
580 }
581
582 #[test]
583 fn account_commitments() {
584 let id0 = AccountIdBuilder::new().build_with_seed([5; 32]);
585 let id1 = AccountIdBuilder::new().build_with_seed([6; 32]);
586 let id2 = AccountIdBuilder::new().build_with_seed([7; 32]);
587
588 let digest0 = Word::from([0, 0, 0, 1u32]);
589 let digest1 = Word::from([0, 0, 0, 2u32]);
590 let digest2 = Word::from([0, 0, 0, 3u32]);
591 let empty_digest = Word::empty();
592
593 let mut tree =
594 AccountTree::with_entries([(id0, digest0), (id1, digest1), (id2, digest2)]).unwrap();
595
596 tree.insert(id2, empty_digest).unwrap();
598
599 assert_eq!(tree.num_accounts(), 2);
600
601 let accounts: Vec<_> = tree.account_commitments().collect();
602 assert_eq!(accounts.len(), 2);
603 assert!(accounts.contains(&(id0, digest0)));
604 assert!(accounts.contains(&(id1, digest1)));
605 }
606
607 #[test]
608 fn account_witness() {
609 let id0 = AccountIdBuilder::new().build_with_seed([5; 32]);
610 let id1 = AccountIdBuilder::new().build_with_seed([6; 32]);
611
612 let digest0 = Word::from([0, 0, 0, 1u32]);
613 let digest1 = Word::from([0, 0, 0, 2u32]);
614
615 let tree = AccountTree::with_entries([(id0, digest0), (id1, digest1)]).unwrap();
616
617 assert_eq!(tree.num_accounts(), 2);
618
619 for id in [id0, id1] {
620 let proof = tree.smt.open(&AccountIdKey::from(id).as_word());
621 let (control_path, control_leaf) = proof.into_parts();
622 let witness = tree.open(id);
623
624 assert_eq!(witness.leaf(), control_leaf);
625 assert_eq!(witness.path(), &control_path);
626 }
627 }
628
629 #[test]
630 fn contains_account_prefix() {
631 let [pair0, pair1] = setup_duplicate_prefix_ids();
633 let tree = AccountTree::with_entries([pair0]).unwrap();
634 assert_eq!(tree.num_accounts(), 1);
635
636 assert!(tree.contains_account_id_prefix(pair0.0.prefix()));
638
639 assert!(tree.contains_account_id_prefix(pair1.0.prefix()));
641
642 let id1 = AccountIdBuilder::new().build_with_seed([7; 32]);
644 assert!(!tree.contains_account_id_prefix(id1.prefix()));
645 }
646
647 #[cfg(feature = "std")]
648 #[test]
649 fn large_smt_backend_basic_operations() {
650 use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage};
651
652 let id0 = AccountIdBuilder::new().build_with_seed([5; 32]);
654 let id1 = AccountIdBuilder::new().build_with_seed([6; 32]);
655 let id2 = AccountIdBuilder::new().build_with_seed([7; 32]);
656
657 let digest0 = Word::from([0, 0, 0, 1u32]);
658 let digest1 = Word::from([0, 0, 0, 2u32]);
659 let digest2 = Word::from([0, 0, 0, 3u32]);
660
661 let tree = LargeSmt::<MemoryStorage>::with_entries(
663 MemoryStorage::default(),
664 [
665 (AccountIdKey::from(id0).as_word(), digest0),
666 (AccountIdKey::from(id1).as_word(), digest1),
667 ],
668 )
669 .map(AccountTree::new_unchecked)
670 .unwrap();
671
672 assert_eq!(tree.num_accounts(), 2);
674 assert_eq!(tree.get(id0), digest0);
675 assert_eq!(tree.get(id1), digest1);
676
677 let witness0 = tree.open(id0);
679 assert_eq!(witness0.id(), id0);
680
681 let mut tree_mut = LargeSmt::<MemoryStorage>::with_entries(
683 MemoryStorage::default(),
684 [
685 (AccountIdKey::from(id0).as_word(), digest0),
686 (AccountIdKey::from(id1).as_word(), digest1),
687 ],
688 )
689 .map(AccountTree::new_unchecked)
690 .unwrap();
691 tree_mut.insert(id2, digest2).unwrap();
692 assert_eq!(tree_mut.num_accounts(), 3);
693 assert_eq!(tree_mut.get(id2), digest2);
694
695 assert_eq!(tree.num_accounts(), 2);
697 }
698
699 #[cfg(feature = "std")]
700 #[test]
701 fn large_smt_backend_duplicate_prefix_check() {
702 use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage};
703
704 let [(id0, commitment0), (id1, commitment1)] = setup_duplicate_prefix_ids();
705
706 let mut tree = AccountTree::new_unchecked(LargeSmt::new(MemoryStorage::default()).unwrap());
707
708 tree.insert(id0, commitment0).unwrap();
709 assert_eq!(tree.get(id0), commitment0);
710
711 let err = tree.insert(id1, commitment1).unwrap_err();
712
713 assert_matches!(
714 err,
715 AccountTreeError::DuplicateIdPrefix { duplicate_prefix }
716 if duplicate_prefix == id0.prefix()
717 );
718 }
719
720 #[cfg(feature = "std")]
721 #[test]
722 fn large_smt_backend_apply_mutations() {
723 use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage};
724
725 let id0 = AccountIdBuilder::new().build_with_seed([5; 32]);
726 let id1 = AccountIdBuilder::new().build_with_seed([6; 32]);
727 let id2 = AccountIdBuilder::new().build_with_seed([7; 32]);
728
729 let digest0 = Word::from([0, 0, 0, 1u32]);
730 let digest1 = Word::from([0, 0, 0, 2u32]);
731 let digest2 = Word::from([0, 0, 0, 3u32]);
732 let digest3 = Word::from([0, 0, 0, 4u32]);
733
734 let mut tree = LargeSmt::with_entries(
735 MemoryStorage::default(),
736 [
737 (AccountIdKey::from(id0).as_word(), digest0),
738 (AccountIdKey::from(id1).as_word(), digest1),
739 ],
740 )
741 .map(AccountTree::new_unchecked)
742 .unwrap();
743
744 let mutations = tree
745 .compute_mutations([(id0, digest1), (id1, digest2), (id2, digest3)])
746 .unwrap();
747
748 tree.apply_mutations(mutations).unwrap();
749
750 assert_eq!(tree.num_accounts(), 3);
751 assert_eq!(tree.get(id0), digest1);
752 assert_eq!(tree.get(id1), digest2);
753 assert_eq!(tree.get(id2), digest3);
754 }
755
756 #[cfg(feature = "std")]
757 #[test]
758 fn large_smt_backend_same_root_as_regular_smt() {
759 use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage};
760
761 let id0 = AccountIdBuilder::new().build_with_seed([5; 32]);
762 let id1 = AccountIdBuilder::new().build_with_seed([6; 32]);
763
764 let digest0 = Word::from([0, 0, 0, 1u32]);
765 let digest1 = Word::from([0, 0, 0, 2u32]);
766
767 let large_tree = LargeSmt::with_entries(
769 MemoryStorage::default(),
770 [
771 (AccountIdKey::from(id0).as_word(), digest0),
772 (AccountIdKey::from(id1).as_word(), digest1),
773 ],
774 )
775 .map(AccountTree::new_unchecked)
776 .unwrap();
777
778 let regular_tree = AccountTree::with_entries([(id0, digest0), (id1, digest1)]).unwrap();
780
781 assert_eq!(large_tree.root(), regular_tree.root());
783
784 let large_commitments: std::collections::BTreeMap<_, _> =
786 large_tree.account_commitments().collect();
787 let regular_commitments: std::collections::BTreeMap<_, _> =
788 regular_tree.account_commitments().collect();
789
790 assert_eq!(large_commitments, regular_commitments);
791 }
792
793 #[cfg(feature = "std")]
794 #[test]
795 fn with_storage_from_entries_rejects_duplicate_state_commitments() {
796 use miden_crypto::merkle::smt::MemoryStorage;
797
798 let id = AccountIdBuilder::new().build_with_seed([5; 32]);
799 let commitment0 = Word::from([0, 0, 0, 1u32]);
800 let commitment1 = Word::from([0, 0, 0, 2u32]);
801
802 let err = AccountTree::with_storage_from_entries(
805 MemoryStorage::default(),
806 [(id, commitment0), (id, commitment1)],
807 )
808 .unwrap_err();
809
810 assert_matches!(
811 err,
812 AccountTreeError::DuplicateStateCommitments { prefix } if prefix == id.prefix()
813 );
814 }
815}