miden_node_store/accounts/
mod.rs1use std::collections::{BTreeMap, HashMap};
4
5use miden_protocol::account::{AccountId, AccountIdPrefix};
6use miden_protocol::block::BlockNumber;
7use miden_protocol::block::account_tree::{AccountMutationSet, AccountTree, AccountWitness};
8use miden_protocol::crypto::merkle::smt::{
9 LargeSmt,
10 LeafIndex,
11 MemoryStorage,
12 NodeMutation,
13 SMT_DEPTH,
14 SmtLeaf,
15 SmtStorage,
16};
17use miden_protocol::crypto::merkle::{
18 EmptySubtreeRoots,
19 MerkleError,
20 MerklePath,
21 NodeIndex,
22 SparseMerklePath,
23};
24use miden_protocol::errors::AccountTreeError;
25use miden_protocol::{EMPTY_WORD, Word};
26
27#[cfg(test)]
28mod tests;
29
30pub type InMemoryAccountTree = AccountTree<LargeSmt<MemoryStorage>>;
32
33#[cfg(feature = "rocksdb")]
34pub type PersistentAccountTree = AccountTree<LargeSmt<miden_crypto::merkle::smt::RocksDbStorage>>;
36
37#[allow(missing_docs)]
41#[derive(thiserror::Error, Debug)]
42pub enum HistoricalError {
43 #[error(transparent)]
44 MerkleError(#[from] MerkleError),
45 #[error(transparent)]
46 AccountTreeError(#[from] AccountTreeError),
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53enum HistoricalSelector {
54 Future,
56 At(BlockNumber),
58 Latest,
60 TooAncient,
62}
63
64#[derive(Debug, Clone)]
69struct HistoricalOverlay {
70 block_number: BlockNumber,
71 root: Word,
72 node_mutations: HashMap<NodeIndex, Word>,
73 account_updates: HashMap<LeafIndex<SMT_DEPTH>, (Word, Word)>,
74}
75
76impl HistoricalOverlay {
77 fn new(block_number: BlockNumber, rev_set: AccountMutationSet) -> Self {
78 let root = rev_set.as_mutation_set().root();
79 let mut_set = rev_set.into_mutation_set();
80
81 let node_mutations =
82 HashMap::from_iter(mut_set.node_mutations().iter().map(|(node_index, mutation)| {
83 match mutation {
84 NodeMutation::Addition(inner_node) => (*node_index, inner_node.hash()),
85 NodeMutation::Removal => {
86 let empty_root = *EmptySubtreeRoots::entry(SMT_DEPTH, node_index.depth());
90 (*node_index, empty_root)
91 },
92 }
93 }));
94
95 let account_updates = HashMap::from_iter(
96 mut_set.new_pairs().iter().map(|(&k, &v)| (LeafIndex::from(k), (k, v))),
97 );
98
99 Self {
100 block_number,
101 root,
102 node_mutations,
103 account_updates,
104 }
105 }
106}
107
108#[derive(Debug)]
117pub struct AccountTreeWithHistory<S: SmtStorage> {
118 block_number: BlockNumber,
120 latest: AccountTree<LargeSmt<S>>,
122 overlays: BTreeMap<BlockNumber, HistoricalOverlay>,
124}
125
126impl<S: SmtStorage> AccountTreeWithHistory<S> {
127 pub const MAX_HISTORY: usize = 50;
129
130 pub fn new(account_tree: AccountTree<LargeSmt<S>>, block_number: BlockNumber) -> Self {
135 Self {
136 block_number,
137 latest: account_tree,
138 overlays: BTreeMap::new(),
139 }
140 }
141
142 fn drain_excess(overlays: &mut BTreeMap<BlockNumber, HistoricalOverlay>) {
144 while overlays.len() > Self::MAX_HISTORY {
145 overlays.pop_first();
146 }
147 }
148
149 pub fn block_number_latest(&self) -> BlockNumber {
154 self.block_number
155 }
156
157 pub fn root_latest(&self) -> Word {
159 self.latest.root()
160 }
161
162 pub fn root_at(&self, block_number: BlockNumber) -> Option<Word> {
166 match self.historical_selector(block_number) {
167 HistoricalSelector::Latest => Some(self.latest.root()),
168 HistoricalSelector::At(block_number) => {
169 let overlay = self.overlays.get(&block_number)?;
170 debug_assert_eq!(overlay.block_number, block_number);
171 Some(overlay.root)
172 },
173 HistoricalSelector::Future | HistoricalSelector::TooAncient => None,
174 }
175 }
176
177 pub fn num_accounts_latest(&self) -> usize {
179 self.latest.num_accounts()
180 }
181
182 pub fn history_len(&self) -> usize {
184 self.overlays.len()
185 }
186
187 pub fn open_latest(&self, account_id: AccountId) -> AccountWitness {
189 self.latest.open(account_id)
190 }
191
192 pub fn open_at(
201 &self,
202 account_id: AccountId,
203 block_number: BlockNumber,
204 ) -> Option<AccountWitness> {
205 match self.historical_selector(block_number) {
206 HistoricalSelector::Latest => Some(self.latest.open(account_id)),
207 HistoricalSelector::At(block_number) => {
208 self.overlays.get(&block_number)?;
210 Self::reconstruct_historical_witness(self, account_id, block_number)
211 },
212 HistoricalSelector::Future | HistoricalSelector::TooAncient => None,
213 }
214 }
215
216 pub fn get_latest_commitment(&self, account_id: AccountId) -> Word {
218 self.latest.get(account_id)
219 }
220
221 pub fn contains_account_id_prefix_in_latest(&self, prefix: AccountIdPrefix) -> bool {
223 self.latest.contains_account_id_prefix(prefix)
224 }
225
226 fn historical_selector(&self, desired_block_number: BlockNumber) -> HistoricalSelector {
231 if desired_block_number == self.block_number {
232 return HistoricalSelector::Latest;
233 }
234
235 if self.block_number.checked_sub(desired_block_number.as_u32()).is_none() {
237 return HistoricalSelector::Future;
238 }
239
240 if !self.overlays.contains_key(&desired_block_number) {
242 return HistoricalSelector::TooAncient;
243 }
244
245 HistoricalSelector::At(desired_block_number)
246 }
247
248 fn reconstruct_historical_witness(
250 &self,
251 account_id: AccountId,
252 block_target: BlockNumber,
253 ) -> Option<AccountWitness> {
254 let latest_witness = self.latest.open(account_id);
256 let (latest_path, leaf) = latest_witness.into_proof().into_parts();
257 let path_nodes = Self::initialize_path_nodes(&latest_path);
258
259 let leaf_index = NodeIndex::from(leaf.index());
260
261 let (path, leaf) = Self::apply_reversion_overlays(
265 self.overlays.range(block_target..).rev().map(|(_, overlay)| overlay),
266 path_nodes,
267 leaf_index,
268 leaf,
269 )?;
270
271 let commitment = match leaf {
273 SmtLeaf::Empty(_) => EMPTY_WORD,
274 SmtLeaf::Single((_, value)) => value,
275 SmtLeaf::Multiple(_) => unreachable!("AccountTree uses prefix-free IDs"),
276 };
277
278 AccountWitness::new(account_id, commitment, path).ok()
279 }
280
281 fn initialize_path_nodes(path: &SparseMerklePath) -> [Word; SMT_DEPTH as usize] {
285 let mut path_nodes: [Word; SMT_DEPTH as usize] = MerklePath::from(path.clone())
286 .to_vec()
287 .try_into()
288 .expect("MerklePath should have exactly SMT_DEPTH nodes");
289 path_nodes.reverse();
290 path_nodes
291 }
292
293 fn apply_reversion_overlays<'a>(
298 overlays: impl IntoIterator<Item = &'a HistoricalOverlay>,
299 mut path_nodes: [Word; SMT_DEPTH as usize],
300 leaf_index: NodeIndex,
301 mut leaf: SmtLeaf,
302 ) -> Option<(SparseMerklePath, SmtLeaf)> {
303 for overlay in overlays {
305 for sibling in leaf_index.proof_indices() {
307 let height = sibling
308 .depth()
309 .checked_sub(1) .expect("proof_indices should not include root")
311 as usize;
312
313 if let Some(hash) = overlay.node_mutations.get(&sibling) {
318 path_nodes[height] = *hash;
319 }
320 }
321
322 if let Some(&(key, value)) = overlay.account_updates.get(&leaf.index()) {
324 leaf = if value == EMPTY_WORD {
325 SmtLeaf::new_empty(leaf.index())
326 } else {
327 SmtLeaf::new_single(key, value)
328 };
329 }
330 }
331
332 let dense: Vec<Word> = path_nodes.iter().rev().copied().collect();
335 let path = MerklePath::new(dense);
336 let path = SparseMerklePath::try_from(path).ok()?;
337 Some((path, leaf))
338 }
339
340 pub fn compute_and_apply_mutations(
347 &mut self,
348 account_commitments: impl IntoIterator<Item = (AccountId, Word)>,
349 ) -> Result<(), HistoricalError> {
350 let mutations = self.compute_mutations(account_commitments)?;
351 self.apply_mutations(mutations)
352 }
353
354 pub fn compute_mutations(
356 &self,
357 account_commitments: impl IntoIterator<Item = (AccountId, Word)>,
358 ) -> Result<AccountMutationSet, HistoricalError> {
359 Ok(self.latest.compute_mutations(account_commitments)?)
360 }
361
362 pub fn apply_mutations(
370 &mut self,
371 mutations: AccountMutationSet,
372 ) -> Result<(), HistoricalError> {
373 let rev = self.latest.apply_mutations_with_reversion(mutations)?;
375
376 let block_num = self.block_number;
378 let overlay = HistoricalOverlay::new(block_num, rev);
379 self.overlays.insert(block_num, overlay);
380
381 self.block_number = block_num.child();
383
384 Self::drain_excess(&mut self.overlays);
386
387 Ok(())
388 }
389}