miden_node_store/accounts/
mod.rs1use std::collections::{BTreeMap, HashMap};
4
5use miden_objects::account::{AccountId, AccountIdPrefix};
6use miden_objects::block::account_tree::{AccountMutationSet, AccountTree};
7use miden_objects::block::{AccountWitness, BlockNumber};
8use miden_objects::crypto::merkle::{
9 EmptySubtreeRoots,
10 LargeSmt,
11 LeafIndex,
12 MemoryStorage,
13 MerkleError,
14 MerklePath,
15 NodeIndex,
16 NodeMutation,
17 SMT_DEPTH,
18 SmtLeaf,
19 SmtStorage,
20 SparseMerklePath,
21};
22use miden_objects::{AccountTreeError, EMPTY_WORD, Word};
23
24#[cfg(test)]
25mod tests;
26
27pub type InMemoryAccountTree = AccountTree<LargeSmt<MemoryStorage>>;
29
30pub trait AccountTreeStorage {
35 fn root(&self) -> Word;
37
38 fn num_accounts(&self) -> usize;
40
41 fn open(&self, account_id: AccountId) -> AccountWitness;
43
44 fn get(&self, account_id: AccountId) -> Word;
46
47 fn compute_mutations(
49 &self,
50 accounts: impl IntoIterator<Item = (AccountId, Word)>,
51 ) -> Result<AccountMutationSet, AccountTreeError>;
52
53 fn apply_mutations_with_reversion(
55 &mut self,
56 mutations: AccountMutationSet,
57 ) -> Result<AccountMutationSet, AccountTreeError>;
58
59 fn contains_account_id_prefix(&self, prefix: AccountIdPrefix) -> bool;
61}
62
63impl<S> AccountTreeStorage for AccountTree<LargeSmt<S>>
64where
65 S: SmtStorage,
66{
67 fn root(&self) -> Word {
68 self.root()
69 }
70
71 fn num_accounts(&self) -> usize {
72 self.num_accounts()
73 }
74
75 fn open(&self, account_id: AccountId) -> AccountWitness {
76 self.open(account_id)
77 }
78
79 fn get(&self, account_id: AccountId) -> Word {
80 self.get(account_id)
81 }
82
83 fn compute_mutations(
84 &self,
85 accounts: impl IntoIterator<Item = (AccountId, Word)>,
86 ) -> Result<AccountMutationSet, AccountTreeError> {
87 self.compute_mutations(accounts)
88 }
89
90 fn apply_mutations_with_reversion(
91 &mut self,
92 mutations: AccountMutationSet,
93 ) -> Result<AccountMutationSet, AccountTreeError> {
94 self.apply_mutations_with_reversion(mutations)
95 }
96
97 fn contains_account_id_prefix(&self, prefix: AccountIdPrefix) -> bool {
98 self.contains_account_id_prefix(prefix)
99 }
100}
101
102#[allow(missing_docs)]
106#[derive(thiserror::Error, Debug)]
107pub enum HistoricalError {
108 #[error(transparent)]
109 MerkleError(#[from] MerkleError),
110 #[error(transparent)]
111 AccountTreeError(#[from] AccountTreeError),
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
118enum HistoricalSelector {
119 Future,
121 At(BlockNumber),
123 Latest,
125 TooAncient,
127}
128
129#[derive(Debug, Clone)]
134struct HistoricalOverlay {
135 block_number: BlockNumber,
136 root: Word,
137 node_mutations: HashMap<NodeIndex, Word>,
138 account_updates: HashMap<LeafIndex<SMT_DEPTH>, (Word, Word)>,
139}
140
141impl HistoricalOverlay {
142 fn new(block_number: BlockNumber, rev_set: AccountMutationSet) -> Self {
143 let root = rev_set.as_mutation_set().root();
144 let mut_set = rev_set.into_mutation_set();
145
146 let node_mutations =
147 HashMap::from_iter(mut_set.node_mutations().iter().map(|(node_index, mutation)| {
148 match mutation {
149 NodeMutation::Addition(inner_node) => (*node_index, inner_node.hash()),
150 NodeMutation::Removal => {
151 let empty_root = *EmptySubtreeRoots::entry(SMT_DEPTH, node_index.depth());
155 (*node_index, empty_root)
156 },
157 }
158 }));
159
160 let account_updates = HashMap::from_iter(
161 mut_set.new_pairs().iter().map(|(&k, &v)| (LeafIndex::from(k), (k, v))),
162 );
163
164 Self {
165 block_number,
166 root,
167 node_mutations,
168 account_updates,
169 }
170 }
171}
172
173#[derive(Debug, Clone)]
182pub struct AccountTreeWithHistory<S>
183where
184 S: AccountTreeStorage,
185{
186 block_number: BlockNumber,
188 latest: S,
190 overlays: BTreeMap<BlockNumber, HistoricalOverlay>,
192}
193
194impl<S> AccountTreeWithHistory<S>
195where
196 S: AccountTreeStorage,
197{
198 pub const MAX_HISTORY: usize = 33;
200
201 pub fn new(account_tree: S, block_number: BlockNumber) -> Self {
206 Self {
207 block_number,
208 latest: account_tree,
209 overlays: BTreeMap::new(),
210 }
211 }
212
213 fn drain_excess(overlays: &mut BTreeMap<BlockNumber, HistoricalOverlay>) {
215 while overlays.len() > Self::MAX_HISTORY {
216 overlays.pop_first();
217 }
218 }
219
220 pub fn block_number_latest(&self) -> BlockNumber {
225 self.block_number
226 }
227
228 pub fn root_latest(&self) -> Word {
230 self.latest.root()
231 }
232
233 pub fn root_at(&self, block_number: BlockNumber) -> Option<Word> {
237 match self.historical_selector(block_number) {
238 HistoricalSelector::Latest => Some(self.latest.root()),
239 HistoricalSelector::At(block_number) => {
240 let overlay = self.overlays.get(&block_number)?;
241 debug_assert_eq!(overlay.block_number, block_number);
242 Some(overlay.root)
243 },
244 HistoricalSelector::Future | HistoricalSelector::TooAncient => None,
245 }
246 }
247
248 pub fn num_accounts_latest(&self) -> usize {
250 self.latest.num_accounts()
251 }
252
253 pub fn history_len(&self) -> usize {
255 self.overlays.len()
256 }
257
258 pub fn open_latest(&self, account_id: AccountId) -> AccountWitness {
260 self.latest.open(account_id)
261 }
262
263 pub fn open_at(
272 &self,
273 account_id: AccountId,
274 block_number: BlockNumber,
275 ) -> Option<AccountWitness> {
276 match self.historical_selector(block_number) {
277 HistoricalSelector::Latest => Some(self.latest.open(account_id)),
278 HistoricalSelector::At(block_number) => {
279 self.overlays.get(&block_number)?;
281 Self::reconstruct_historical_witness(self, account_id, block_number)
282 },
283 HistoricalSelector::Future | HistoricalSelector::TooAncient => None,
284 }
285 }
286
287 pub fn get_latest_commitment(&self, account_id: AccountId) -> Word {
289 self.latest.get(account_id)
290 }
291
292 pub fn contains_account_id_prefix_in_latest(&self, prefix: AccountIdPrefix) -> bool {
294 self.latest.contains_account_id_prefix(prefix)
295 }
296
297 fn historical_selector(&self, desired_block_number: BlockNumber) -> HistoricalSelector {
302 if desired_block_number == self.block_number {
303 return HistoricalSelector::Latest;
304 }
305
306 if self.block_number.checked_sub(desired_block_number.as_u32()).is_none() {
308 return HistoricalSelector::Future;
309 }
310
311 if !self.overlays.contains_key(&desired_block_number) {
313 return HistoricalSelector::TooAncient;
314 }
315
316 HistoricalSelector::At(desired_block_number)
317 }
318
319 fn reconstruct_historical_witness(
321 &self,
322 account_id: AccountId,
323 block_target: BlockNumber,
324 ) -> Option<AccountWitness> {
325 let latest_witness = self.latest.open(account_id);
327 let (latest_path, leaf) = latest_witness.into_proof().into_parts();
328 let path_nodes = Self::initialize_path_nodes(&latest_path);
329
330 let leaf_index = NodeIndex::from(leaf.index());
331
332 let (path, leaf) = Self::apply_reversion_overlays(
336 self.overlays.range(block_target..).rev().map(|(_, overlay)| overlay),
337 path_nodes,
338 leaf_index,
339 leaf,
340 )?;
341
342 let commitment = match leaf {
344 SmtLeaf::Empty(_) => EMPTY_WORD,
345 SmtLeaf::Single((_, value)) => value,
346 SmtLeaf::Multiple(_) => unreachable!("AccountTree uses prefix-free IDs"),
347 };
348
349 AccountWitness::new(account_id, commitment, path).ok()
350 }
351
352 fn initialize_path_nodes(path: &SparseMerklePath) -> [Word; SMT_DEPTH as usize] {
356 let mut path_nodes: [Word; SMT_DEPTH as usize] = MerklePath::from(path.clone())
357 .to_vec()
358 .try_into()
359 .expect("MerklePath should have exactly SMT_DEPTH nodes");
360 path_nodes.reverse();
361 path_nodes
362 }
363
364 fn apply_reversion_overlays<'a>(
369 overlays: impl IntoIterator<Item = &'a HistoricalOverlay>,
370 mut path_nodes: [Word; SMT_DEPTH as usize],
371 leaf_index: NodeIndex,
372 mut leaf: SmtLeaf,
373 ) -> Option<(SparseMerklePath, SmtLeaf)> {
374 for overlay in overlays {
376 for sibling in leaf_index.proof_indices() {
378 let height = sibling
379 .depth()
380 .checked_sub(1) .expect("proof_indices should not include root")
382 as usize;
383
384 if let Some(hash) = overlay.node_mutations.get(&sibling) {
389 path_nodes[height] = *hash;
390 }
391 }
392
393 if let Some(&(key, value)) = overlay.account_updates.get(&leaf.index()) {
395 leaf = if value == EMPTY_WORD {
396 SmtLeaf::new_empty(leaf.index())
397 } else {
398 SmtLeaf::new_single(key, value)
399 };
400 }
401 }
402
403 let dense: Vec<Word> = path_nodes.iter().rev().copied().collect();
406 let path = MerklePath::new(dense);
407 let path = SparseMerklePath::try_from(path).ok()?;
408 Some((path, leaf))
409 }
410
411 pub fn compute_and_apply_mutations(
418 &mut self,
419 account_commitments: impl IntoIterator<Item = (AccountId, Word)>,
420 ) -> Result<(), HistoricalError> {
421 let mutations = self.compute_mutations(account_commitments)?;
422 self.apply_mutations(mutations)
423 }
424
425 pub fn compute_mutations(
427 &self,
428 account_commitments: impl IntoIterator<Item = (AccountId, Word)>,
429 ) -> Result<AccountMutationSet, HistoricalError> {
430 Ok(self.latest.compute_mutations(account_commitments)?)
431 }
432
433 pub fn apply_mutations(
441 &mut self,
442 mutations: AccountMutationSet,
443 ) -> Result<(), HistoricalError> {
444 let rev = self.latest.apply_mutations_with_reversion(mutations)?;
446
447 let block_num = self.block_number;
449 let overlay = HistoricalOverlay::new(block_num, rev);
450 self.overlays.insert(block_num, overlay);
451
452 self.block_number = block_num.child();
454
455 Self::drain_excess(&mut self.overlays);
457
458 Ok(())
459 }
460}