miden_crypto/merkle/smt/forest/mod.rs
1use alloc::{collections::BTreeSet, vec::Vec};
2
3use super::{EmptySubtreeRoots, MerkleError, NodeIndex, SmtLeaf, SmtProof, Word};
4use crate::{
5 Map,
6 merkle::smt::{LeafIndex, SMT_DEPTH, SmtLeafError, SmtProofError, forest::store::SmtStore},
7};
8
9mod store;
10
11#[cfg(test)]
12mod tests;
13
14// SPARSE MERKLE TREE FOREST
15// ================================================================================================
16
17/// An in-memory data collection of sparse Merkle trees (SMTs).
18///
19/// Each SMT in the forest is identified by its root hash. The forest stores all leaves of all SMTs
20/// in the forest, as well as all Merkle paths required to prove membership of any leaf in any SMT.
21///
22/// An empty tree root is always present in the forest.
23///
24/// Example usage:
25///
26/// ```rust
27/// use miden_crypto::{
28/// Felt, ONE, WORD_SIZE, Word, ZERO,
29/// merkle::{
30/// EmptySubtreeRoots,
31/// smt::{MAX_LEAF_ENTRIES, SMT_DEPTH, SmtForest},
32/// },
33/// };
34///
35/// // Create a new SMT forest
36/// let mut forest = SmtForest::new();
37///
38/// // Insert a key-value pair into an SMT with an empty root
39/// let empty_tree_root = *EmptySubtreeRoots::entry(SMT_DEPTH, 0);
40/// let key = Word::new([ZERO; WORD_SIZE]);
41/// let value = Word::new([ONE; WORD_SIZE]);
42/// let new_root = forest.insert(empty_tree_root, key, value).unwrap();
43///
44/// // Insert multiple key-value pairs
45/// let mut entries = Vec::new();
46/// for i in 0..MAX_LEAF_ENTRIES {
47/// let key = Word::new([Felt::new(i as u64); WORD_SIZE]);
48/// let value = Word::new([Felt::new((i + 1) as u64); WORD_SIZE]);
49/// entries.push((key, value));
50/// }
51/// let new_root = forest.batch_insert(new_root, entries.into_iter()).unwrap();
52///
53/// // Open a proof for the inserted key
54/// let proof = forest.open(new_root, key).unwrap();
55///
56/// // Prune SMTs to release memory used by their nodes and leaves
57/// forest.pop_smts(vec![new_root]);
58/// ```
59#[derive(Debug, Clone, Eq, PartialEq)]
60pub struct SmtForest {
61 /// Roots of all SMTs in this forest. Any time an SMT in this forest is updated, we add a new
62 /// root to this set.
63 roots: BTreeSet<Word>,
64
65 /// Stores Merkle paths for all SMTs in this forest.
66 store: SmtStore,
67
68 /// Leaves of all SMTs stored in this forest
69 leaves: Map<Word, SmtLeaf>,
70}
71
72impl Default for SmtForest {
73 fn default() -> Self {
74 Self::new()
75 }
76}
77
78impl SmtForest {
79 // CONSTRUCTORS
80 // --------------------------------------------------------------------------------------------
81
82 /// Creates an empty `SmtForest` instance.
83 pub fn new() -> SmtForest {
84 let roots = BTreeSet::new();
85 let store = SmtStore::new();
86 let leaves = Map::new();
87
88 SmtForest { roots, store, leaves }
89 }
90
91 // DATA EXTRACTORS
92 // --------------------------------------------------------------------------------------------
93
94 /// Returns an opening for the specified key in the SMT with the specified root.
95 ///
96 /// Returns an error if an SMT with this root is not in the forest, or if the forest does
97 /// not have sufficient data to provide an opening for the specified key.
98 pub fn open(&self, root: Word, key: Word) -> Result<SmtProof, MerkleError> {
99 if !self.contains_root(root) {
100 return Err(MerkleError::RootNotInStore(root));
101 }
102
103 let leaf_index = NodeIndex::from(LeafIndex::from(key));
104
105 let proof = self.store.get_path(root, leaf_index)?;
106 let path = proof.path.try_into()?;
107 let leaf_hash = proof.value;
108 let leaf = if leaf_hash == crate::EMPTY_WORD {
109 SmtLeaf::new_empty(LeafIndex::from(key))
110 } else {
111 let Some(leaf) = self.leaves.get(&leaf_hash).cloned() else {
112 return Err(MerkleError::UntrackedKey(key));
113 };
114 leaf
115 };
116
117 SmtProof::new(path, leaf).map_err(|error| match error {
118 SmtProofError::InvalidMerklePathLength(depth) => MerkleError::InvalidPathLength(depth),
119 // These variants are only returned by verification methods, not by SmtProof::new()
120 SmtProofError::InvalidKeyForProof
121 | SmtProofError::ValueMismatch { .. }
122 | SmtProofError::ConflictingRoots { .. }
123 | SmtProofError::ValuePresent { .. } => unreachable!(),
124 })
125 }
126
127 // STATE MUTATORS
128 // --------------------------------------------------------------------------------------------
129
130 /// Inserts the specified key-value pair into an SMT with the specified root. This will also
131 /// add a new root to the forest. Returns the new root.
132 ///
133 /// Returns an error if an SMT with the specified root is not in the forest, there is not
134 /// enough data in the forest to perform the insert, or if the insert would create a leaf
135 /// with too many entries.
136 pub fn insert(&mut self, root: Word, key: Word, value: Word) -> Result<Word, MerkleError> {
137 self.batch_insert(root, vec![(key, value)])
138 }
139
140 /// Inserts the specified key-value pairs into an SMT with the specified root. This will also
141 /// add a single new root to the forest for the entire batch of inserts. Returns the new root.
142 ///
143 /// Returns an error if an SMT with the specified root is not in the forest, there is not
144 /// enough data in the forest to perform the insert, or if the insert would create a leaf
145 /// with too many entries.
146 pub fn batch_insert(
147 &mut self,
148 root: Word,
149 entries: impl IntoIterator<Item = (Word, Word)> + Clone,
150 ) -> Result<Word, MerkleError> {
151 if !self.contains_root(root) {
152 return Err(MerkleError::RootNotInStore(root));
153 }
154
155 // Find all affected leaf indices
156 let indices = entries
157 .clone()
158 .into_iter()
159 .map(|(key, _)| LeafIndex::from(key))
160 .collect::<BTreeSet<_>>();
161
162 // Create new SmtLeaf objects for updated key-value pairs
163 let mut new_leaves = Map::new();
164 for index in indices {
165 let node_index = NodeIndex::from(index);
166 let current_hash = self.store.get_node(root, node_index)?;
167
168 let current_leaf = self
169 .leaves
170 .get(¤t_hash)
171 .cloned()
172 .unwrap_or_else(|| SmtLeaf::new_empty(index));
173
174 new_leaves.insert(index, (current_hash, current_leaf));
175 }
176 for (key, value) in entries {
177 let index = LeafIndex::from(key);
178 let (_old_hash, leaf) = new_leaves.get_mut(&index).unwrap();
179 if value == crate::EMPTY_WORD {
180 let _ = leaf.remove(key);
181 } else {
182 leaf.insert(key, value).map_err(to_merkle_error)?;
183 }
184 }
185
186 // Calculate new leaf hashes, skip processing unchanged leaves
187 new_leaves = new_leaves
188 .into_iter()
189 .filter_map(|(key, (old_hash, leaf))| {
190 let new_hash = leaf.hash();
191 if new_hash == old_hash {
192 None
193 } else {
194 Some((key, (new_hash, leaf)))
195 }
196 })
197 .collect();
198
199 // Update SmtStore with new leaf hashes
200 let new_leaf_entries =
201 new_leaves.iter().map(|(index, leaf)| (NodeIndex::from(*index), leaf.0));
202 let new_root = self.store.set_leaves(root, new_leaf_entries)?;
203
204 // Update successful, insert new leaves into the forest
205 for (leaf_hash, leaf) in new_leaves.into_values() {
206 if leaf_hash != crate::EMPTY_WORD {
207 self.leaves.insert(leaf_hash, leaf);
208 }
209 }
210 // Never register the empty tree root in self.roots, as it is always implicitly valid
211 // and the empty hash nodes are pre-populated infrastructure in the SmtStore.
212 // Adding it here would let `pop_smts` walk and destroy those nodes,
213 // corrupting the store for all future operations.
214 if new_root != *EmptySubtreeRoots::entry(SMT_DEPTH, 0) {
215 self.roots.insert(new_root);
216 }
217
218 Ok(new_root)
219 }
220
221 /// Removes the specified SMTs (identified by their roots) from the forest.
222 /// Releases memory used by nodes and leaves that are no longer reachable.
223 /// Roots not in the forest and empty trees are ignored.
224 pub fn pop_smts(&mut self, roots: impl IntoIterator<Item = Word>) {
225 let roots = roots
226 .into_iter()
227 .filter(|root| {
228 // don't use self.contains_root here because we don't remove empty trees
229 self.roots.contains(root)
230 })
231 .collect::<Vec<_>>();
232
233 for root in &roots {
234 self.roots.remove(root);
235 }
236
237 for leaf in self.store.remove_roots(roots) {
238 self.leaves.remove(&leaf);
239 }
240 }
241
242 // HELPER METHODS
243 // --------------------------------------------------------------------------------------------
244
245 /// Checks if the forest contains the specified root or if it is the empty tree root
246 /// (always present in the forest).
247 fn contains_root(&self, root: Word) -> bool {
248 self.roots.contains(&root) || *EmptySubtreeRoots::entry(SMT_DEPTH, 0) == root
249 }
250}
251
252fn to_merkle_error(err: SmtLeafError) -> MerkleError {
253 match err {
254 SmtLeafError::TooManyLeafEntries { actual } => MerkleError::TooManyLeafEntries { actual },
255 _ => unreachable!("other SmtLeafError variants should not be possible here"),
256 }
257}