1use miden_crypto::merkle::{MerkleError, MutationSet, Smt, SmtLeaf};
2use vm_processor::SMT_DEPTH;
3
4use crate::{
5 Digest, Felt, FieldElement, Word,
6 account::{AccountId, AccountIdPrefix},
7 block::AccountWitness,
8 errors::AccountTreeError,
9};
10
11#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct AccountTree {
24 smt: Smt,
25}
26
27impl AccountTree {
28 pub const DEPTH: u8 = SMT_DEPTH;
33
34 pub(super) const KEY_SUFFIX_IDX: usize = 2;
36 pub(super) const KEY_PREFIX_IDX: usize = 3;
38
39 pub fn new() -> Self {
44 AccountTree { smt: Smt::new() }
45 }
46
47 pub fn with_entries<I>(
59 entries: impl IntoIterator<Item = (AccountId, Digest), IntoIter = I>,
60 ) -> Result<Self, AccountTreeError>
61 where
62 I: ExactSizeIterator<Item = (AccountId, Digest)>,
63 {
64 let entries = entries.into_iter();
65 let num_accounts = entries.len();
66
67 let smt = Smt::with_entries(
68 entries.map(|(id, commitment)| (Self::id_to_smt_key(id), Word::from(commitment))),
69 )
70 .map_err(|err| {
71 let MerkleError::DuplicateValuesForIndex(leaf_idx) = err else {
72 unreachable!("the only error returned by Smt::with_entries is of this type");
73 };
74
75 AccountTreeError::DuplicateStateCommitments {
78 prefix: AccountIdPrefix::new_unchecked(
79 Felt::try_from(leaf_idx).expect("leaf index should be a valid felt"),
80 ),
81 }
82 })?;
83
84 if smt.num_leaves() < num_accounts {
88 for (leaf_idx, leaf) in smt.leaves() {
89 if leaf.num_entries() >= 2 {
90 return Err(AccountTreeError::DuplicateIdPrefix {
94 duplicate_prefix: AccountIdPrefix::new_unchecked(
95 Felt::try_from(leaf_idx.value())
96 .expect("leaf index should be a valid felt"),
97 ),
98 });
99 }
100 }
101 }
102
103 Ok(AccountTree { smt })
104 }
105
106 pub fn open(&self, account_id: AccountId) -> AccountWitness {
114 let key = Self::id_to_smt_key(account_id);
115 let proof = self.smt.open(&key);
116
117 AccountWitness::from_smt_proof(account_id, proof)
118 }
119
120 pub fn get(&self, account_id: AccountId) -> Digest {
122 let key = Self::id_to_smt_key(account_id);
123 Digest::from(self.smt.get_value(&key))
124 }
125
126 pub fn root(&self) -> Digest {
128 self.smt.root()
129 }
130
131 pub fn num_accounts(&self) -> usize {
133 self.smt.num_leaves()
136 }
137
138 pub fn account_commitments(&self) -> impl Iterator<Item = (AccountId, Digest)> {
140 self.smt.leaves().map(|(_leaf_idx, leaf)| {
141 let SmtLeaf::Single((key, commitment)) = leaf else {
145 unreachable!("empty and multiple variant should never be encountered")
146 };
147
148 (
149 AccountId::try_from([key[Self::KEY_PREFIX_IDX], key[Self::KEY_SUFFIX_IDX]])
151 .expect("account tree should only contain valid IDs"),
152 Digest::from(commitment),
153 )
154 })
155 }
156
157 pub fn compute_mutations(
175 &self,
176 account_commitments: impl IntoIterator<Item = (AccountId, Digest)>,
177 ) -> Result<AccountMutationSet, AccountTreeError> {
178 let mutation_set = self.smt.compute_mutations(
179 account_commitments
180 .into_iter()
181 .map(|(id, commitment)| (Self::id_to_smt_key(id), Word::from(commitment))),
182 );
183
184 for id_key in mutation_set.new_pairs().keys() {
185 match self.smt.get_leaf(id_key) {
187 SmtLeaf::Empty(_) => (),
189 SmtLeaf::Single((existing_key, _)) => {
190 if existing_key != *id_key {
193 return Err(AccountTreeError::DuplicateIdPrefix {
194 duplicate_prefix: Self::smt_key_to_id(*id_key).prefix(),
195 });
196 }
197 },
198 SmtLeaf::Multiple(_) => {
199 unreachable!(
200 "account tree should never contain duplicate ID prefixes and therefore never a multiple leaf"
201 )
202 },
203 }
204 }
205
206 Ok(AccountMutationSet::new(mutation_set))
207 }
208
209 pub fn insert(
223 &mut self,
224 account_id: AccountId,
225 state_commitment: Digest,
226 ) -> Result<Digest, AccountTreeError> {
227 let key = Self::id_to_smt_key(account_id);
228 let prev_value = Digest::from(self.smt.insert(key, Word::from(state_commitment)));
229
230 if self.smt.get_leaf(&key).num_entries() >= 2 {
233 return Err(AccountTreeError::DuplicateIdPrefix {
234 duplicate_prefix: account_id.prefix(),
235 });
236 }
237
238 Ok(prev_value)
239 }
240
241 pub fn apply_mutations(
248 &mut self,
249 mutations: AccountMutationSet,
250 ) -> Result<(), AccountTreeError> {
251 self.smt
252 .apply_mutations(mutations.into_mutation_set())
253 .map_err(AccountTreeError::ApplyMutations)
254 }
255
256 pub(super) fn id_to_smt_key(account_id: AccountId) -> Digest {
261 let mut key = [Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::ZERO];
264 key[Self::KEY_SUFFIX_IDX] = account_id.suffix();
265 key[Self::KEY_PREFIX_IDX] = account_id.prefix().as_felt();
266
267 Digest::from(key)
268 }
269
270 pub(super) fn smt_key_to_id(key: Digest) -> AccountId {
278 AccountId::try_from([key[Self::KEY_PREFIX_IDX], key[Self::KEY_SUFFIX_IDX]])
279 .expect("account tree should only contain valid IDs")
280 }
281}
282
283impl Default for AccountTree {
284 fn default() -> Self {
285 Self::new()
286 }
287}
288
289#[derive(Debug, Clone, PartialEq, Eq)]
299pub struct AccountMutationSet {
300 mutation_set: MutationSet<{ AccountTree::DEPTH }, Digest, Word>,
301}
302
303impl AccountMutationSet {
304 fn new(mutation_set: MutationSet<{ AccountTree::DEPTH }, Digest, Word>) -> Self {
309 Self { mutation_set }
310 }
311
312 pub fn as_mutation_set(&self) -> &MutationSet<{ AccountTree::DEPTH }, Digest, Word> {
317 &self.mutation_set
318 }
319
320 pub fn into_mutation_set(self) -> MutationSet<{ AccountTree::DEPTH }, Digest, Word> {
325 self.mutation_set
326 }
327}
328
329#[cfg(test)]
333pub(super) mod tests {
334 use std::vec::Vec;
335
336 use assert_matches::assert_matches;
337 use vm_core::EMPTY_WORD;
338
339 use super::*;
340 use crate::{
341 account::{AccountStorageMode, AccountType},
342 testing::account_id::{AccountIdBuilder, account_id},
343 };
344
345 pub(crate) fn setup_duplicate_prefix_ids() -> [(AccountId, Digest); 2] {
346 let id0 = AccountId::try_from(account_id(
347 AccountType::FungibleFaucet,
348 AccountStorageMode::Public,
349 0xaabb_ccdd,
350 ))
351 .unwrap();
352 let id1 = AccountId::try_from(account_id(
353 AccountType::FungibleFaucet,
354 AccountStorageMode::Public,
355 0xaabb_ccff,
356 ))
357 .unwrap();
358 assert_eq!(id0.prefix(), id1.prefix(), "test requires that these ids have the same prefix");
359
360 let commitment0 = Digest::from([Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::new(42)]);
361 let commitment1 = Digest::from([Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::new(24)]);
362
363 assert_eq!(id0.prefix(), id1.prefix(), "test requires that these ids have the same prefix");
364 [(id0, commitment0), (id1, commitment1)]
365 }
366
367 #[test]
368 fn insert_fails_on_duplicate_prefix() {
369 let mut tree = AccountTree::new();
370 let [(id0, commitment0), (id1, commitment1)] = setup_duplicate_prefix_ids();
371
372 tree.insert(id0, commitment0).unwrap();
373 assert_eq!(tree.get(id0), commitment0);
374
375 let err = tree.insert(id1, commitment1).unwrap_err();
376
377 assert_matches!(err, AccountTreeError::DuplicateIdPrefix {
378 duplicate_prefix
379 } if duplicate_prefix == id0.prefix());
380 }
381
382 #[test]
383 fn with_entries_fails_on_duplicate_prefix() {
384 let entries = setup_duplicate_prefix_ids();
385
386 let err = AccountTree::with_entries(entries.iter().copied()).unwrap_err();
387
388 assert_matches!(err, AccountTreeError::DuplicateIdPrefix {
389 duplicate_prefix
390 } if duplicate_prefix == entries[0].0.prefix());
391 }
392
393 #[test]
394 fn insert_succeeds_on_multiple_updates() {
395 let mut tree = AccountTree::new();
396 let [(id0, commitment0), (_, commitment1)] = setup_duplicate_prefix_ids();
397
398 tree.insert(id0, commitment0).unwrap();
399 tree.insert(id0, commitment1).unwrap();
400 assert_eq!(tree.get(id0), commitment1);
401 }
402
403 #[test]
404 fn apply_mutations() {
405 let id0 = AccountIdBuilder::new().build_with_seed([5; 32]);
406 let id1 = AccountIdBuilder::new().build_with_seed([6; 32]);
407 let id2 = AccountIdBuilder::new().build_with_seed([7; 32]);
408
409 let digest0 = Digest::from([0, 0, 0, 1u32]);
410 let digest1 = Digest::from([0, 0, 0, 2u32]);
411 let digest2 = Digest::from([0, 0, 0, 3u32]);
412 let digest3 = Digest::from([0, 0, 0, 4u32]);
413
414 let mut tree = AccountTree::with_entries([(id0, digest0), (id1, digest1)]).unwrap();
415
416 let mutations = tree
417 .compute_mutations([(id0, digest1), (id1, digest2), (id2, digest3)])
418 .unwrap();
419
420 tree.apply_mutations(mutations).unwrap();
421
422 assert_eq!(tree.num_accounts(), 3);
423 assert_eq!(tree.get(id0), digest1);
424 assert_eq!(tree.get(id1), digest2);
425 assert_eq!(tree.get(id2), digest3);
426 }
427
428 #[test]
429 fn duplicates_in_compute_mutations() {
430 let [pair0, pair1] = setup_duplicate_prefix_ids();
431 let id2 = AccountIdBuilder::new().build_with_seed([5; 32]);
432 let commitment2 = Digest::from([0, 0, 0, 99u32]);
433
434 let tree = AccountTree::with_entries([pair0, (id2, commitment2)]).unwrap();
435
436 let err = tree.compute_mutations([pair1]).unwrap_err();
437
438 assert_matches!(err, AccountTreeError::DuplicateIdPrefix {
439 duplicate_prefix
440 } if duplicate_prefix == pair1.0.prefix());
441 }
442
443 #[test]
444 fn account_commitments() {
445 let id0 = AccountIdBuilder::new().build_with_seed([5; 32]);
446 let id1 = AccountIdBuilder::new().build_with_seed([6; 32]);
447 let id2 = AccountIdBuilder::new().build_with_seed([7; 32]);
448
449 let digest0 = Digest::from([0, 0, 0, 1u32]);
450 let digest1 = Digest::from([0, 0, 0, 2u32]);
451 let digest2 = Digest::from([0, 0, 0, 3u32]);
452 let empty_digest = Digest::from(EMPTY_WORD);
453
454 let mut tree =
455 AccountTree::with_entries([(id0, digest0), (id1, digest1), (id2, digest2)]).unwrap();
456
457 tree.insert(id2, empty_digest).unwrap();
459
460 assert_eq!(tree.num_accounts(), 2);
461
462 let accounts: Vec<_> = tree.account_commitments().collect();
463 assert_eq!(accounts.len(), 2);
464 assert!(accounts.contains(&(id0, digest0)));
465 assert!(accounts.contains(&(id1, digest1)));
466 }
467
468 #[test]
469 fn account_witness() {
470 let id0 = AccountIdBuilder::new().build_with_seed([5; 32]);
471 let id1 = AccountIdBuilder::new().build_with_seed([6; 32]);
472
473 let digest0 = Digest::from([0, 0, 0, 1u32]);
474 let digest1 = Digest::from([0, 0, 0, 2u32]);
475
476 let tree = AccountTree::with_entries([(id0, digest0), (id1, digest1)]).unwrap();
477
478 assert_eq!(tree.num_accounts(), 2);
479
480 for id in [id0, id1] {
481 let (control_path, control_leaf) =
482 tree.smt.open(&AccountTree::id_to_smt_key(id)).into_parts();
483 let witness = tree.open(id);
484
485 assert_eq!(witness.leaf(), control_leaf);
486 assert_eq!(witness.path(), &control_path);
487 }
488 }
489}