1use alloc::collections::BTreeMap;
2use alloc::vec::Vec;
3
4use miden_protocol::account::{
5 AccountId,
6 AccountStorage,
7 StorageMap,
8 StorageMapKey,
9 StorageMapKeyHash,
10 StorageMapWitness,
11 StorageSlotContent,
12};
13use miden_protocol::asset::{Asset, AssetVault, AssetVaultKey, AssetWitness};
14use miden_protocol::crypto::merkle::smt::{SMT_DEPTH, Smt, SmtForest};
15use miden_protocol::crypto::merkle::{EmptySubtreeRoots, MerkleError};
16use miden_protocol::{EMPTY_WORD, Word};
17
18use super::StoreError;
19
20#[derive(Debug, Default, Clone, Eq, PartialEq)]
26pub struct AccountSmtForest {
27 forest: SmtForest,
28 account_roots: BTreeMap<AccountId, Vec<Word>>,
30 pending_old_roots: BTreeMap<AccountId, Vec<Vec<Word>>>,
32 root_refcounts: BTreeMap<Word, usize>,
34}
35
36impl AccountSmtForest {
37 pub fn new() -> Self {
38 Self::default()
39 }
40
41 pub fn get_roots(&self, account_id: &AccountId) -> Option<&Vec<Word>> {
46 self.account_roots.get(account_id)
47 }
48
49 pub fn get_asset_and_witness(
51 &self,
52 vault_root: Word,
53 vault_key: AssetVaultKey,
54 ) -> Result<(Asset, AssetWitness), StoreError> {
55 let vault_key_word = vault_key.into();
56 let proof = self.forest.open(vault_root, vault_key_word)?;
57 let asset_word =
58 proof.get(&vault_key_word).ok_or(MerkleError::UntrackedKey(vault_key_word))?;
59 if asset_word == EMPTY_WORD {
60 return Err(MerkleError::UntrackedKey(vault_key_word).into());
61 }
62
63 let asset = Asset::from_key_value_words(vault_key_word, asset_word)?;
64 let witness = AssetWitness::new(proof)?;
65 Ok((asset, witness))
66 }
67
68 pub fn get_storage_map_item_witness(
70 &self,
71 map_root: Word,
72 key: StorageMapKey,
73 ) -> Result<StorageMapWitness, StoreError> {
74 let hashed_key = key.hash();
75 let proof = self.forest.open(map_root, Word::from(hashed_key)).map_err(StoreError::from)?;
76 Ok(StorageMapWitness::new(proof, [key])?)
77 }
78
79 pub fn stage_roots(&mut self, account_id: AccountId, new_roots: Vec<Word>) {
88 increment_refcounts(&mut self.root_refcounts, &new_roots);
89 if let Some(old_roots) = self.account_roots.insert(account_id, new_roots) {
90 self.pending_old_roots.entry(account_id).or_default().push(old_roots);
91 }
92 }
93
94 pub fn commit_roots(&mut self, account_id: AccountId) {
96 if let Some(old_roots_stack) = self.pending_old_roots.remove(&account_id) {
97 for old_roots in old_roots_stack {
98 let to_pop = decrement_refcounts(&mut self.root_refcounts, &old_roots);
99 self.safe_pop_smts(to_pop);
100 }
101 }
102 }
103
104 pub fn discard_roots(&mut self, account_id: AccountId) {
110 let old_roots = self.pending_old_roots.get_mut(&account_id).and_then(Vec::pop);
111
112 let new_roots = match old_roots {
114 Some(old_roots) => self.account_roots.insert(account_id, old_roots),
115 None => self.account_roots.remove(&account_id),
116 };
117
118 if let Some(new_roots) = new_roots {
119 let to_pop = decrement_refcounts(&mut self.root_refcounts, &new_roots);
120 self.safe_pop_smts(to_pop);
121 }
122
123 if self.pending_old_roots.get(&account_id).is_some_and(Vec::is_empty) {
125 self.pending_old_roots.remove(&account_id);
126 }
127 }
128
129 pub fn replace_roots(&mut self, account_id: AccountId, new_roots: Vec<Word>) {
138 assert!(
139 !self.pending_old_roots.contains_key(&account_id),
140 "cannot replace roots while staged changes are pending for account {account_id}"
141 );
142 increment_refcounts(&mut self.root_refcounts, &new_roots);
143 if let Some(old_roots) = self.account_roots.insert(account_id, new_roots) {
144 let to_pop = decrement_refcounts(&mut self.root_refcounts, &old_roots);
145 self.safe_pop_smts(to_pop);
146 }
147 }
148
149 pub fn update_asset_nodes(
154 &mut self,
155 root: Word,
156 new_assets: impl Iterator<Item = Asset>,
157 removed_vault_keys: impl Iterator<Item = AssetVaultKey>,
158 ) -> Result<Word, StoreError> {
159 let entries: Vec<(Word, Word)> = new_assets
160 .map(|asset| {
161 let key: Word = asset.vault_key().into();
162 let value = asset.to_value_word();
163 (key, value)
164 })
165 .chain(removed_vault_keys.map(|vault_key| (vault_key.into(), EMPTY_WORD)))
166 .collect();
167
168 if entries.is_empty() {
169 return Ok(root);
170 }
171
172 let new_root = self.forest.batch_insert(root, entries).map_err(StoreError::from)?;
173 Ok(new_root)
174 }
175
176 pub fn update_storage_map_nodes(
178 &mut self,
179 root: Word,
180 entries: impl Iterator<Item = (StorageMapKey, Word)>,
181 ) -> Result<Word, StoreError> {
182 let entries: Vec<(StorageMapKeyHash, Word)> =
183 entries.map(|(key, value)| (key.hash(), value)).collect();
184
185 if entries.is_empty() {
186 return Ok(root);
187 }
188
189 let new_root = self
190 .forest
191 .batch_insert(root, entries.into_iter().map(|(key, value)| (Word::from(key), value)))
192 .map_err(StoreError::from)?;
193 Ok(new_root)
194 }
195
196 pub fn insert_asset_nodes(&mut self, vault: &AssetVault) -> Result<(), StoreError> {
198 let smt = Smt::with_entries(vault.assets().map(|asset| {
199 let key: Word = asset.vault_key().into();
200 let value = asset.to_value_word();
201 (key, value)
202 }))
203 .map_err(StoreError::from)?;
204
205 let empty_root = *EmptySubtreeRoots::entry(SMT_DEPTH, 0);
206 let entries: Vec<(Word, Word)> = smt.entries().map(|(k, v)| (*k, *v)).collect();
207 if entries.is_empty() {
208 return Ok(());
209 }
210 let new_root = self.forest.batch_insert(empty_root, entries).map_err(StoreError::from)?;
211 debug_assert_eq!(new_root, smt.root());
212 Ok(())
213 }
214
215 pub fn insert_storage_map_nodes(&mut self, storage: &AccountStorage) -> Result<(), StoreError> {
217 let maps = storage.slots().iter().filter_map(|slot| match slot.content() {
218 StorageSlotContent::Map(map) => Some(map),
219 StorageSlotContent::Value(_) => None,
220 });
221
222 for map in maps {
223 self.insert_storage_map_nodes_for_map(map)?;
224 }
225 Ok(())
226 }
227
228 pub fn insert_account_state(
231 &mut self,
232 vault: &AssetVault,
233 storage: &AccountStorage,
234 ) -> Result<(), StoreError> {
235 self.insert_storage_map_nodes(storage)?;
236 self.insert_asset_nodes(vault)?;
237 Ok(())
238 }
239
240 pub fn insert_and_stage_account_state(
243 &mut self,
244 account_id: AccountId,
245 vault: &AssetVault,
246 storage: &AccountStorage,
247 ) -> Result<(), StoreError> {
248 self.insert_account_state(vault, storage)?;
249 let roots = Self::collect_account_roots(vault, storage);
250 self.stage_roots(account_id, roots);
251 Ok(())
252 }
253
254 pub fn insert_and_register_account_state(
257 &mut self,
258 account_id: AccountId,
259 vault: &AssetVault,
260 storage: &AccountStorage,
261 ) -> Result<(), StoreError> {
262 self.insert_account_state(vault, storage)?;
263 let roots = Self::collect_account_roots(vault, storage);
264 self.replace_roots(account_id, roots);
265 Ok(())
266 }
267
268 pub fn insert_storage_map_nodes_for_map(&mut self, map: &StorageMap) -> Result<(), StoreError> {
270 let empty_root = *EmptySubtreeRoots::entry(SMT_DEPTH, 0);
271 let entries: Vec<(StorageMapKeyHash, Word)> =
272 map.entries().map(|(key, value)| (key.hash(), *value)).collect();
273 if entries.is_empty() {
274 return Ok(());
275 }
276 self.forest
277 .batch_insert(
278 empty_root,
279 entries.into_iter().map(|(key, value)| (Word::from(key), value)),
280 )
281 .map_err(StoreError::from)?;
282 Ok(())
283 }
284
285 fn collect_account_roots(vault: &AssetVault, storage: &AccountStorage) -> Vec<Word> {
290 let mut roots = vec![vault.root()];
291 for slot in storage.slots() {
292 if let StorageSlotContent::Map(map) = slot.content() {
293 roots.push(map.root());
294 }
295 }
296 roots
297 }
298
299 fn safe_pop_smts(&mut self, roots: impl IntoIterator<Item = Word>) {
301 self.forest.pop_smts(roots);
302 }
303}
304
305fn increment_refcounts(refcounts: &mut BTreeMap<Word, usize>, roots: &[Word]) {
306 for root in roots {
307 *refcounts.entry(*root).or_insert(0) += 1;
308 }
309}
310
311fn decrement_refcounts(refcounts: &mut BTreeMap<Word, usize>, roots: &[Word]) -> Vec<Word> {
313 let mut to_pop = Vec::new();
314 for root in roots {
315 if let Some(count) = refcounts.get_mut(root) {
316 *count -= 1;
317 if *count == 0 {
318 refcounts.remove(root);
319 to_pop.push(*root);
320 }
321 }
322 }
323 to_pop
324}
325
326#[cfg(test)]
327mod tests {
328 use miden_protocol::testing::account_id::{
329 ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET,
330 ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET,
331 };
332 use miden_protocol::{ONE, ZERO};
333
334 use super::*;
335
336 fn account_a() -> AccountId {
337 AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET).unwrap()
338 }
339
340 fn account_b() -> AccountId {
341 AccountId::try_from(ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET).unwrap()
342 }
343
344 fn insert_map(forest: &mut AccountSmtForest, key: Word, value: Word) -> Word {
347 let mut map = StorageMap::new();
348 map.insert(StorageMapKey::new(key), value).unwrap();
349 forest.insert_storage_map_nodes_for_map(&map).unwrap();
350 map.root()
351 }
352
353 fn root_is_live(forest: &AccountSmtForest, root: Word, key: Word) -> bool {
355 forest.get_storage_map_item_witness(root, StorageMapKey::new(key)).is_ok()
356 }
357
358 #[test]
359 fn stage_then_commit_releases_old_roots() {
360 let mut forest = AccountSmtForest::new();
361 let id = account_a();
362
363 let key1: Word = [ONE, ZERO, ZERO, ZERO].into();
364 let key2: Word = [ZERO, ONE, ZERO, ZERO].into();
365 let val: Word = [ONE, ONE, ONE, ONE].into();
366
367 let root1 = insert_map(&mut forest, key1, val);
368 let root2 = insert_map(&mut forest, key2, val);
369
370 forest.replace_roots(id, vec![root1]);
372 assert_eq!(forest.get_roots(&id), Some(&vec![root1]));
373
374 forest.stage_roots(id, vec![root2]);
376 assert_eq!(forest.get_roots(&id), Some(&vec![root2]));
377
378 assert!(root_is_live(&forest, root1, key1));
380 assert!(root_is_live(&forest, root2, key2));
381
382 forest.commit_roots(id);
384 assert_eq!(forest.get_roots(&id), Some(&vec![root2]));
385 assert!(!root_is_live(&forest, root1, key1));
386 assert!(root_is_live(&forest, root2, key2));
387 }
388
389 #[test]
390 fn stage_then_discard_restores_old_roots() {
391 let mut forest = AccountSmtForest::new();
392 let id = account_a();
393
394 let key1: Word = [ONE, ZERO, ZERO, ZERO].into();
395 let key2: Word = [ZERO, ONE, ZERO, ZERO].into();
396 let val: Word = [ONE, ONE, ONE, ONE].into();
397
398 let root1 = insert_map(&mut forest, key1, val);
399 let root2 = insert_map(&mut forest, key2, val);
400
401 forest.replace_roots(id, vec![root1]);
402
403 forest.stage_roots(id, vec![root2]);
405 forest.discard_roots(id);
406
407 assert_eq!(forest.get_roots(&id), Some(&vec![root1]));
408 assert!(root_is_live(&forest, root1, key1));
409 assert!(!root_is_live(&forest, root2, key2));
410 }
411
412 #[test]
413 fn shared_root_survives_single_account_replacement() {
414 let mut forest = AccountSmtForest::new();
415 let id1 = account_a();
416 let id2 = account_b();
417
418 let key: Word = [ONE, ZERO, ZERO, ZERO].into();
419 let val: Word = [ONE, ONE, ONE, ONE].into();
420 let shared_root = insert_map(&mut forest, key, val);
421
422 forest.replace_roots(id1, vec![shared_root]);
424 forest.replace_roots(id2, vec![shared_root]);
425
426 let key2: Word = [ZERO, ONE, ZERO, ZERO].into();
428 let other_root = insert_map(&mut forest, key2, val);
429 forest.replace_roots(id1, vec![other_root]);
430
431 assert!(root_is_live(&forest, shared_root, key));
433
434 forest.replace_roots(id2, vec![other_root]);
436 assert!(!root_is_live(&forest, shared_root, key));
437 }
438
439 #[test]
440 fn multiple_stages_discard_one_at_a_time() {
441 let mut forest = AccountSmtForest::new();
442 let id = account_a();
443
444 let key_a: Word = [ONE, ZERO, ZERO, ZERO].into();
445 let key_b: Word = [ZERO, ONE, ZERO, ZERO].into();
446 let key_c: Word = [ZERO, ZERO, ONE, ZERO].into();
447 let val: Word = [ONE, ONE, ONE, ONE].into();
448
449 let root_a = insert_map(&mut forest, key_a, val);
450 let root_b = insert_map(&mut forest, key_b, val);
451 let root_c = insert_map(&mut forest, key_c, val);
452
453 forest.replace_roots(id, vec![root_a]);
455 forest.stage_roots(id, vec![root_b]);
456 forest.stage_roots(id, vec![root_c]);
457 assert_eq!(forest.get_roots(&id), Some(&vec![root_c]));
458
459 forest.discard_roots(id);
461 assert_eq!(forest.get_roots(&id), Some(&vec![root_b]));
462 assert!(!root_is_live(&forest, root_c, key_c));
463 assert!(root_is_live(&forest, root_b, key_b));
464 assert!(root_is_live(&forest, root_a, key_a));
465
466 forest.discard_roots(id);
468 assert_eq!(forest.get_roots(&id), Some(&vec![root_a]));
469 assert!(!root_is_live(&forest, root_b, key_b));
470 assert!(root_is_live(&forest, root_a, key_a));
471 }
472
473 #[test]
474 fn multiple_stages_commit_releases_all_old() {
475 let mut forest = AccountSmtForest::new();
476 let id = account_a();
477
478 let key_a: Word = [ONE, ZERO, ZERO, ZERO].into();
479 let key_b: Word = [ZERO, ONE, ZERO, ZERO].into();
480 let key_c: Word = [ZERO, ZERO, ONE, ZERO].into();
481 let val: Word = [ONE, ONE, ONE, ONE].into();
482
483 let root_a = insert_map(&mut forest, key_a, val);
484 let root_b = insert_map(&mut forest, key_b, val);
485 let root_c = insert_map(&mut forest, key_c, val);
486
487 forest.replace_roots(id, vec![root_a]);
489 forest.stage_roots(id, vec![root_b]);
490 forest.stage_roots(id, vec![root_c]);
491 forest.commit_roots(id);
492
493 assert_eq!(forest.get_roots(&id), Some(&vec![root_c]));
495 assert!(!root_is_live(&forest, root_a, key_a));
496 assert!(!root_is_live(&forest, root_b, key_b));
497 assert!(root_is_live(&forest, root_c, key_c));
498 }
499
500 #[test]
501 fn unchanged_root_survives_stage_commit() {
502 let mut forest = AccountSmtForest::new();
503 let id = account_a();
504
505 let key1: Word = [ONE, ZERO, ZERO, ZERO].into();
506 let key2: Word = [ZERO, ONE, ZERO, ZERO].into();
507 let val: Word = [ONE, ONE, ONE, ONE].into();
508
509 let shared_root = insert_map(&mut forest, key1, val);
510 let changing_root = insert_map(&mut forest, key2, val);
511
512 forest.replace_roots(id, vec![shared_root, changing_root]);
514
515 let key3: Word = [ZERO, ZERO, ONE, ZERO].into();
517 let new_root = insert_map(&mut forest, key3, val);
518 forest.stage_roots(id, vec![shared_root, new_root]);
519 forest.commit_roots(id);
520
521 assert!(root_is_live(&forest, shared_root, key1));
523 assert!(!root_is_live(&forest, changing_root, key2));
525 assert!(root_is_live(&forest, new_root, key3));
527 }
528}