miden_node_store/accounts/
mod.rs1use std::collections::{BTreeMap, HashMap};
4
5#[cfg(feature = "rocksdb")]
6use miden_large_smt_backend_rocksdb::RocksDbStorage;
7use miden_protocol::account::{AccountId, AccountIdPrefix};
8use miden_protocol::block::BlockNumber;
9use miden_protocol::block::account_tree::{AccountMutationSet, AccountTree, AccountWitness};
10use miden_protocol::crypto::merkle::smt::{
11 LargeSmt,
12 LeafIndex,
13 MemoryStorage,
14 NodeMutation,
15 SMT_DEPTH,
16 SmtLeaf,
17 SmtStorage,
18};
19use miden_protocol::crypto::merkle::{
20 EmptySubtreeRoots,
21 MerkleError,
22 MerklePath,
23 NodeIndex,
24 SparseMerklePath,
25};
26use miden_protocol::errors::AccountTreeError;
27use miden_protocol::{EMPTY_WORD, Word};
28use tracing::instrument;
29
30use crate::COMPONENT;
31
32#[cfg(test)]
33mod tests;
34
35pub type InMemoryAccountTree = AccountTree<LargeSmt<MemoryStorage>>;
37
38#[cfg(feature = "rocksdb")]
39pub type PersistentAccountTree = AccountTree<LargeSmt<RocksDbStorage>>;
41
42#[expect(missing_docs)]
46#[derive(thiserror::Error, Debug)]
47pub enum HistoricalError {
48 #[error(transparent)]
49 MerkleError(#[from] MerkleError),
50 #[error(transparent)]
51 AccountTreeError(#[from] AccountTreeError),
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58enum HistoricalSelector {
59 Future,
61 At(BlockNumber),
63 Latest,
65 TooAncient,
67}
68
69#[derive(Debug, Clone)]
74struct HistoricalOverlay {
75 block_number: BlockNumber,
76 root: Word,
77 node_mutations: HashMap<NodeIndex, Word>,
78 account_updates: HashMap<LeafIndex<SMT_DEPTH>, (Word, Word)>,
79}
80
81impl HistoricalOverlay {
82 fn new(block_number: BlockNumber, rev_set: AccountMutationSet) -> Self {
83 let root = rev_set.as_mutation_set().root();
84 let mut_set = rev_set.into_mutation_set();
85
86 let node_mutations =
87 HashMap::from_iter(mut_set.node_mutations().iter().map(|(node_index, mutation)| {
88 match mutation {
89 NodeMutation::Addition(inner_node) => (*node_index, inner_node.hash()),
90 NodeMutation::Removal => {
91 let empty_root = *EmptySubtreeRoots::entry(SMT_DEPTH, node_index.depth());
94 (*node_index, empty_root)
95 },
96 }
97 }));
98
99 let account_updates = HashMap::from_iter(
100 mut_set.new_pairs().iter().map(|(&k, &v)| (LeafIndex::from(k), (k, v))),
101 );
102
103 Self {
104 block_number,
105 root,
106 node_mutations,
107 account_updates,
108 }
109 }
110}
111
112#[derive(Debug)]
121pub struct AccountTreeWithHistory<S: SmtStorage> {
122 block_number: BlockNumber,
124 latest: AccountTree<LargeSmt<S>>,
126 overlays: BTreeMap<BlockNumber, HistoricalOverlay>,
128}
129
130impl<S: SmtStorage> AccountTreeWithHistory<S> {
131 pub const MAX_HISTORY: usize = 50;
133
134 pub fn new(account_tree: AccountTree<LargeSmt<S>>, block_number: BlockNumber) -> Self {
139 Self {
140 block_number,
141 latest: account_tree,
142 overlays: BTreeMap::new(),
143 }
144 }
145
146 fn drain_excess(overlays: &mut BTreeMap<BlockNumber, HistoricalOverlay>) {
148 while overlays.len() > Self::MAX_HISTORY {
149 overlays.pop_first();
150 }
151 }
152
153 pub fn block_number_latest(&self) -> BlockNumber {
158 self.block_number
159 }
160
161 pub fn root_latest(&self) -> Word {
163 self.latest.root()
164 }
165
166 pub fn root_at(&self, block_number: BlockNumber) -> Option<Word> {
170 match self.historical_selector(block_number) {
171 HistoricalSelector::Latest => Some(self.latest.root()),
172 HistoricalSelector::At(block_number) => {
173 let overlay = self.overlays.get(&block_number)?;
174 debug_assert_eq!(overlay.block_number, block_number);
175 Some(overlay.root)
176 },
177 HistoricalSelector::Future | HistoricalSelector::TooAncient => None,
178 }
179 }
180
181 pub fn num_accounts_latest(&self) -> usize {
183 self.latest.num_accounts()
184 }
185
186 pub fn history_len(&self) -> usize {
188 self.overlays.len()
189 }
190
191 #[instrument(target = COMPONENT, skip_all)]
193 pub fn open_latest(&self, account_id: AccountId) -> AccountWitness {
194 self.latest.open(account_id)
195 }
196
197 #[instrument(target = COMPONENT, skip_all)]
206 pub fn open_at(
207 &self,
208 account_id: AccountId,
209 block_number: BlockNumber,
210 ) -> Option<AccountWitness> {
211 match self.historical_selector(block_number) {
212 HistoricalSelector::Latest => Some(self.latest.open(account_id)),
213 HistoricalSelector::At(block_number) => {
214 self.overlays.get(&block_number)?;
216 Self::reconstruct_historical_witness(self, account_id, block_number)
217 },
218 HistoricalSelector::Future | HistoricalSelector::TooAncient => None,
219 }
220 }
221
222 pub fn get_latest_commitment(&self, account_id: AccountId) -> Word {
224 self.latest.get(account_id)
225 }
226
227 pub fn contains_account_id_prefix_in_latest(&self, prefix: AccountIdPrefix) -> bool {
229 self.latest.contains_account_id_prefix(prefix)
230 }
231
232 fn historical_selector(&self, desired_block_number: BlockNumber) -> HistoricalSelector {
237 if desired_block_number == self.block_number {
238 return HistoricalSelector::Latest;
239 }
240
241 if self.block_number.checked_sub(desired_block_number.as_u32()).is_none() {
243 return HistoricalSelector::Future;
244 }
245
246 if !self.overlays.contains_key(&desired_block_number) {
248 return HistoricalSelector::TooAncient;
249 }
250
251 HistoricalSelector::At(desired_block_number)
252 }
253
254 #[instrument(target = COMPONENT, skip_all)]
256 fn reconstruct_historical_witness(
257 &self,
258 account_id: AccountId,
259 block_target: BlockNumber,
260 ) -> Option<AccountWitness> {
261 let latest_witness = self.latest.open(account_id);
263 let (latest_path, leaf) = latest_witness.into_proof().into_parts();
264 let path_nodes = Self::initialize_path_nodes(&latest_path);
265
266 let leaf_index = NodeIndex::from(leaf.index());
267
268 let (path, leaf) = Self::apply_reversion_overlays(
272 self.overlays.range(block_target..).rev().map(|(_, overlay)| overlay),
273 path_nodes,
274 leaf_index,
275 leaf,
276 )?;
277
278 let commitment = match leaf {
280 SmtLeaf::Empty(_) => EMPTY_WORD,
281 SmtLeaf::Single((_, value)) => value,
282 SmtLeaf::Multiple(_) => unreachable!("AccountTree uses prefix-free IDs"),
283 };
284
285 AccountWitness::new(account_id, commitment, path).ok()
286 }
287
288 fn initialize_path_nodes(path: &SparseMerklePath) -> [Word; SMT_DEPTH as usize] {
292 let mut path_nodes: [Word; SMT_DEPTH as usize] = MerklePath::from(path.clone())
293 .to_vec()
294 .try_into()
295 .expect("MerklePath should have exactly SMT_DEPTH nodes");
296 path_nodes.reverse();
297 path_nodes
298 }
299
300 #[instrument(target = COMPONENT, skip_all)]
305 fn apply_reversion_overlays<'a>(
306 overlays: impl IntoIterator<Item = &'a HistoricalOverlay>,
307 mut path_nodes: [Word; SMT_DEPTH as usize],
308 leaf_index: NodeIndex,
309 mut leaf: SmtLeaf,
310 ) -> Option<(SparseMerklePath, SmtLeaf)> {
311 for overlay in overlays {
313 for sibling in leaf_index.proof_indices() {
315 let height = sibling
316 .depth()
317 .checked_sub(1) .expect("proof_indices should not include root")
319 as usize;
320
321 if let Some(hash) = overlay.node_mutations.get(&sibling) {
325 path_nodes[height] = *hash;
326 }
327 }
328
329 if let Some(&(key, value)) = overlay.account_updates.get(&leaf.index()) {
331 leaf = if value == EMPTY_WORD {
332 SmtLeaf::new_empty(leaf.index())
333 } else {
334 SmtLeaf::new_single(key, value)
335 };
336 }
337 }
338
339 let dense: Vec<Word> = path_nodes.iter().rev().copied().collect();
342 let path = MerklePath::new(dense);
343 let path = SparseMerklePath::try_from(path).ok()?;
344 Some((path, leaf))
345 }
346
347 pub fn compute_and_apply_mutations(
354 &mut self,
355 account_commitments: impl IntoIterator<Item = (AccountId, Word)>,
356 ) -> Result<(), HistoricalError> {
357 let mutations = self.compute_mutations(account_commitments)?;
358 self.apply_mutations(mutations)
359 }
360
361 pub fn compute_mutations(
363 &self,
364 account_commitments: impl IntoIterator<Item = (AccountId, Word)>,
365 ) -> Result<AccountMutationSet, HistoricalError> {
366 Ok(self.latest.compute_mutations(account_commitments)?)
367 }
368
369 #[instrument(target = COMPONENT, skip_all)]
377 pub fn apply_mutations(
378 &mut self,
379 mutations: AccountMutationSet,
380 ) -> Result<(), HistoricalError> {
381 let rev = self.latest.apply_mutations_with_reversion(mutations)?;
383
384 let block_num = self.block_number;
386 let overlay = HistoricalOverlay::new(block_num, rev);
387 self.overlays.insert(block_num, overlay);
388
389 self.block_number = block_num.child();
391
392 Self::drain_excess(&mut self.overlays);
394
395 Ok(())
396 }
397}