miden-crypto/src/merkle/mmr/full.rs
src/merkle/mmr/full.rsRaw
1//! A fully materialized Merkle mountain range (MMR).
2//!
3//! A MMR is a forest structure, i.e. it is an ordered set of disjoint rooted trees. The trees are
4//! ordered by size, from the most to least number of leaves. Every tree is a perfect binary tree,
5//! meaning a tree has all its leaves at the same depth, and every inner node has a branch-factor
6//! of 2 with both children set.
7//!
8//! Additionally the structure only supports adding leaves to the right-most tree, the one with the
9//! least number of leaves. The structure preserves the invariant that each tree has different
10//! depths, i.e. as part of adding adding a new element to the forest the trees with same depth are
11//! merged, creating a new tree with depth d+1, this process is continued until the property is
12//! restabilished.
13use super::{
14 super::{InnerNodeInfo, MerklePath, RpoDigest, Vec},
15 bit::TrueBitPositionIterator,
16 MmrPeaks, MmrProof, Rpo256,
17};
18use core::fmt::{Display, Formatter};
19
20#[cfg(feature = "std")]
21use std::error::Error;
22
23// MMR
24// ===============================================================================================
25
26/// A fully materialized Merkle Mountain Range, with every tree in the forest and all their
27/// elements.
28///
29/// Since this is a full representation of the MMR, elements are never removed and the MMR will
30/// grow roughly `O(2n)` in number of leaf elements.
31#[derive(Debug, Clone)]
32#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
33pub struct Mmr {
34 /// Refer to the `forest` method documentation for details of the semantics of this value.
35 pub(super) forest: usize,
36
37 /// Contains every element of the forest.
38 ///
39 /// The trees are in postorder sequential representation. This representation allows for all
40 /// the elements of every tree in the forest to be stored in the same sequential buffer. It
41 /// also means new elements can be added to the forest, and merging of trees is very cheap with
42 /// no need to copy elements.
43 pub(super) nodes: Vec<RpoDigest>,
44}
45
46#[derive(Debug, PartialEq, Eq, Copy, Clone)]
47pub enum MmrError {
48 InvalidPosition(usize),
49}
50
51impl Display for MmrError {
52 fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
53 match self {
54 MmrError::InvalidPosition(pos) => write!(fmt, "Mmr does not contain position {pos}"),
55 }
56 }
57}
58
59#[cfg(feature = "std")]
60impl Error for MmrError {}
61
62impl Default for Mmr {
63 fn default() -> Self {
64 Self::new()
65 }
66}
67
68impl Mmr {
69 // CONSTRUCTORS
70 // ============================================================================================
71
72 /// Constructor for an empty `Mmr`.
73 pub fn new() -> Mmr {
74 Mmr { forest: 0, nodes: Vec::new() }
75 }
76
77 // ACCESSORS
78 // ============================================================================================
79
80 /// Returns the MMR forest representation.
81 ///
82 /// The forest value has the following interpretations:
83 /// - its value is the number of elements in the forest
84 /// - bit count corresponds to the number of trees in the forest
85 /// - each true bit position determines the depth of a tree in the forest
86 pub const fn forest(&self) -> usize {
87 self.forest
88 }
89
90 // FUNCTIONALITY
91 // ============================================================================================
92
93 /// Given a leaf position, returns the Merkle path to its corresponding peak. If the position
94 /// is greater-or-equal than the tree size an error is returned.
95 ///
96 /// Note: The leaf position is the 0-indexed number corresponding to the order the leaves were
97 /// added, this corresponds to the MMR size _prior_ to adding the element. So the 1st element
98 /// has position 0, the second position 1, and so on.
99 pub fn open(&self, pos: usize) -> Result<MmrProof, MmrError> {
100 // find the target tree responsible for the MMR position
101 let tree_bit =
102 leaf_to_corresponding_tree(pos, self.forest).ok_or(MmrError::InvalidPosition(pos))?;
103 let forest_target = 1usize << tree_bit;
104
105 // isolate the trees before the target
106 let forest_before = self.forest & high_bitmask(tree_bit + 1);
107 let index_offset = nodes_in_forest(forest_before);
108
109 // find the root
110 let index = nodes_in_forest(forest_target) - 1;
111
112 // update the value position from global to the target tree
113 let relative_pos = pos - forest_before;
114
115 // collect the path and the final index of the target value
116 let (_, path) =
117 self.collect_merkle_path_and_value(tree_bit, relative_pos, index_offset, index);
118
119 Ok(MmrProof {
120 forest: self.forest,
121 position: pos,
122 merkle_path: MerklePath::new(path),
123 })
124 }
125
126 /// Returns the leaf value at position `pos`.
127 ///
128 /// Note: The leaf position is the 0-indexed number corresponding to the order the leaves were
129 /// added, this corresponds to the MMR size _prior_ to adding the element. So the 1st element
130 /// has position 0, the second position 1, and so on.
131 pub fn get(&self, pos: usize) -> Result<RpoDigest, MmrError> {
132 // find the target tree responsible for the MMR position
133 let tree_bit =
134 leaf_to_corresponding_tree(pos, self.forest).ok_or(MmrError::InvalidPosition(pos))?;
135 let forest_target = 1usize << tree_bit;
136
137 // isolate the trees before the target
138 let forest_before = self.forest & high_bitmask(tree_bit + 1);
139 let index_offset = nodes_in_forest(forest_before);
140
141 // find the root
142 let index = nodes_in_forest(forest_target) - 1;
143
144 // update the value position from global to the target tree
145 let relative_pos = pos - forest_before;
146
147 // collect the path and the final index of the target value
148 let (value, _) =
149 self.collect_merkle_path_and_value(tree_bit, relative_pos, index_offset, index);
150
151 Ok(value)
152 }
153
154 /// Adds a new element to the MMR.
155 pub fn add(&mut self, el: RpoDigest) {
156 // Note: every node is also a tree of size 1, adding an element to the forest creates a new
157 // rooted-tree of size 1. This may temporarily break the invariant that every tree in the
158 // forest has different sizes, the loop below will eagerly merge trees of same size and
159 // restore the invariant.
160 self.nodes.push(el);
161
162 let mut left_offset = self.nodes.len().saturating_sub(2);
163 let mut right = el;
164 let mut left_tree = 1;
165 while self.forest & left_tree != 0 {
166 right = Rpo256::merge(&[self.nodes[left_offset], right]);
167 self.nodes.push(right);
168
169 left_offset = left_offset.saturating_sub(nodes_in_forest(left_tree));
170 left_tree <<= 1;
171 }
172
173 self.forest += 1;
174 }
175
176 /// Returns an accumulator representing the current state of the MMR.
177 pub fn accumulator(&self) -> MmrPeaks {
178 let peaks: Vec<RpoDigest> = TrueBitPositionIterator::new(self.forest)
179 .rev()
180 .map(|bit| nodes_in_forest(1 << bit))
181 .scan(0, |offset, el| {
182 *offset += el;
183 Some(*offset)
184 })
185 .map(|offset| self.nodes[offset - 1])
186 .collect();
187
188 MmrPeaks { num_leaves: self.forest, peaks }
189 }
190
191 /// An iterator over inner nodes in the MMR. The order of iteration is unspecified.
192 pub fn inner_nodes(&self) -> MmrNodes {
193 MmrNodes {
194 mmr: self,
195 forest: 0,
196 last_right: 0,
197 index: 0,
198 }
199 }
200
201 // UTILITIES
202 // ============================================================================================
203
204 /// Internal function used to collect the Merkle path of a value.
205 fn collect_merkle_path_and_value(
206 &self,
207 tree_bit: u32,
208 relative_pos: usize,
209 index_offset: usize,
210 mut index: usize,
211 ) -> (RpoDigest, Vec<RpoDigest>) {
212 // collect the Merkle path
213 let mut tree_depth = tree_bit as usize;
214 let mut path = Vec::with_capacity(tree_depth + 1);
215 while tree_depth > 0 {
216 let bit = relative_pos & tree_depth;
217 let right_offset = index - 1;
218 let left_offset = right_offset - nodes_in_forest(tree_depth);
219
220 // Elements to the right have a higher position because they were
221 // added later. Therefore when the bit is true the node's path is
222 // to the right, and its sibling to the left.
223 let sibling = if bit != 0 {
224 index = right_offset;
225 self.nodes[index_offset + left_offset]
226 } else {
227 index = left_offset;
228 self.nodes[index_offset + right_offset]
229 };
230
231 tree_depth >>= 1;
232 path.push(sibling);
233 }
234
235 // the rest of the codebase has the elements going from leaf to root, adjust it here for
236 // easy of use/consistency sake
237 path.reverse();
238
239 let value = self.nodes[index_offset + index];
240 (value, path)
241 }
242}
243
244impl<T> From<T> for Mmr
245where
246 T: IntoIterator<Item = RpoDigest>,
247{
248 fn from(values: T) -> Self {
249 let mut mmr = Mmr::new();
250 for v in values {
251 mmr.add(v)
252 }
253 mmr
254 }
255}
256
257// ITERATOR
258// ===============================================================================================
259
260/// Yields inner nodes of the [Mmr].
261pub struct MmrNodes<'a> {
262 /// [Mmr] being yielded, when its `forest` value is matched, the iterations is finished.
263 mmr: &'a Mmr,
264 /// Keeps track of the left nodes yielded so far waiting for a right pair, this matches the
265 /// semantics of the [Mmr]'s forest attribute, since that too works as a buffer of left nodes
266 /// waiting for a pair to be hashed together.
267 forest: usize,
268 /// Keeps track of the last right node yielded, after this value is set, the next iteration
269 /// will be its parent with its corresponding left node that has been yield already.
270 last_right: usize,
271 /// The current index in the `nodes` vector.
272 index: usize,
273}
274
275impl<'a> Iterator for MmrNodes<'a> {
276 type Item = InnerNodeInfo;
277
278 fn next(&mut self) -> Option<Self::Item> {
279 debug_assert!(self.last_right.count_ones() <= 1, "last_right tracks zero or one element");
280
281 // only parent nodes are emitted, remove the single node tree from the forest
282 let target = self.mmr.forest & (usize::MAX << 1);
283
284 if self.forest < target {
285 if self.last_right == 0 {
286 // yield the left leaf
287 debug_assert!(self.last_right == 0, "left must be before right");
288 self.forest |= 1;
289 self.index += 1;
290
291 // yield the right leaf
292 debug_assert!((self.forest & 1) == 1, "right must be after left");
293 self.last_right |= 1;
294 self.index += 1;
295 };
296
297 debug_assert!(
298 self.forest & self.last_right != 0,
299 "parent requires both a left and right",
300 );
301
302 // compute the number of nodes in the right tree, this is the offset to the
303 // previous left parent
304 let right_nodes = nodes_in_forest(self.last_right);
305 // the next parent position is one above the position of the pair
306 let parent = self.last_right << 1;
307
308 // the left node has been paired and the current parent yielded, removed it from the forest
309 self.forest ^= self.last_right;
310 if self.forest & parent == 0 {
311 // this iteration yielded the left parent node
312 debug_assert!(self.forest & 1 == 0, "next iteration yields a left leaf");
313 self.last_right = 0;
314 self.forest ^= parent;
315 } else {
316 // the left node of the parent level has been yielded already, this iteration
317 // was the right parent. Next iteration yields their parent.
318 self.last_right = parent;
319 }
320
321 // yields a parent
322 let value = self.mmr.nodes[self.index];
323 let right = self.mmr.nodes[self.index - 1];
324 let left = self.mmr.nodes[self.index - 1 - right_nodes];
325 self.index += 1;
326 let node = InnerNodeInfo { value, left, right };
327
328 Some(node)
329 } else {
330 None
331 }
332 }
333}
334
335// UTILITIES
336// ===============================================================================================
337
338/// Given a 0-indexed leaf position and the current forest, return the tree number responsible for
339/// the position.
340///
341/// Note:
342/// The result is a tree position `p`, it has the following interpretations. $p+1$ is the depth of
343/// the tree, which corresponds to the size of a Merkle proof for that tree. $2^p$ is equal to the
344/// number of leaves in this particular tree. and $2^(p+1)-1$ corresponds to size of the tree.
345pub(crate) const fn leaf_to_corresponding_tree(pos: usize, forest: usize) -> Option<u32> {
346 if pos >= forest {
347 None
348 } else {
349 // - each bit in the forest is a unique tree and the bit position its power-of-two size
350 // - each tree owns a consecutive range of positions equal to its size from left-to-right
351 // - this means the first tree owns from `0` up to the `2^k_0` first positions, where `k_0`
352 // is the highest true bit position, the second tree from `2^k_0 + 1` up to `2^k_1` where
353 // `k_1` is the second higest bit, so on.
354 // - this means the highest bits work as a category marker, and the position is owned by
355 // the first tree which doesn't share a high bit with the position
356 let before = forest & pos;
357 let after = forest ^ before;
358 let tree = after.ilog2();
359
360 Some(tree)
361 }
362}
363
364/// Return a bitmask for the bits including and above the given position.
365pub(crate) const fn high_bitmask(bit: u32) -> usize {
366 if bit > usize::BITS - 1 {
367 0
368 } else {
369 usize::MAX << bit
370 }
371}
372
373/// Return the total number of nodes of a given forest
374///
375/// Panics:
376///
377/// This will panic if the forest has size greater than `usize::MAX / 2`
378pub(crate) const fn nodes_in_forest(forest: usize) -> usize {
379 // - the size of a perfect binary tree is $2^{k+1}-1$ or $2*2^k-1$
380 // - the forest represents the sum of $2^k$ so a single multiplication is necessary
381 // - the number of `-1` is the same as the number of trees, which is the same as the number
382 // bits set
383 let tree_count = forest.count_ones() as usize;
384 forest * 2 - tree_count
385}