Skip to main content

miden_crypto/merkle/mmr/
forest.rs

1use core::{
2    fmt::{Binary, Display},
3    ops::{BitAnd, BitOr, BitXor, BitXorAssign},
4};
5
6use super::{InOrderIndex, MmrError};
7use crate::{
8    Felt,
9    utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
10};
11
12/// A compact representation of trees in a forest. Used in the Merkle forest (MMR).
13///
14/// Each active bit of the stored number represents a disjoint tree with number of leaves
15/// equal to the bit position.
16///
17/// The forest value has the following interpretations:
18/// - its value is the number of leaves in the forest
19/// - the version number (MMR is append only so the number of leaves always increases)
20/// - bit count corresponds to the number of trees (trees) in the forest
21/// - each true bit position determines the depth of a tree in the forest
22///
23/// Examples:
24/// - `Forest(0)` is a forest with no trees.
25/// - `Forest(0b01)` is a forest with a single leaf/node (the smallest tree possible).
26/// - `Forest(0b10)` is a forest with a single binary tree with 2 leaves (3 nodes).
27/// - `Forest(0b11)` is a forest with two trees: one with 1 leaf (1 node), and one with 2 leaves (3
28///   nodes).
29/// - `Forest(0b1010)` is a forest with two trees: one with 8 leaves (15 nodes), one with 2 leaves
30///   (3 nodes).
31/// - `Forest(0b1000)` is a forest with one tree, which has 8 leaves (15 nodes).
32///
33/// Forest sizes are capped at [`Forest::MAX_LEAVES`]. Use [`Forest::new`] or
34/// [`Forest::append_leaf`] to enforce the limit.
35#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize))]
37pub struct Forest(usize);
38
39impl Forest {
40    /// Maximum number of leaves supported by the forest.
41    ///
42    /// Rationale:
43    /// - We require `MAX_LEAVES <= usize::MAX / 2 + 1` so `num_nodes()` stays indexable via
44    ///   `usize`.
45    /// - We choose `usize::MAX / 2` (hard cutoff) rather than `usize::MAX / 2 + 1` so the cap is
46    ///   always of the form `2^k - 1` on all targets.
47    /// - With that shape, bitwise OR/XOR of valid forest values remains within bounds, so OR/XOR
48    ///   does not need additional overflow protection.
49    pub const MAX_LEAVES: usize = if (u32::MAX as usize) < (usize::MAX / 2) {
50        u32::MAX as usize
51    } else {
52        usize::MAX / 2
53    };
54
55    /// Creates an empty forest (no trees).
56    pub const fn empty() -> Self {
57        Self(0)
58    }
59
60    /// Creates a forest with `num_leaves` leaves, returning an error if the value is too large.
61    pub fn new(num_leaves: usize) -> Result<Self, DeserializationError> {
62        if !Self::is_valid_size(num_leaves) {
63            return Err(DeserializationError::InvalidValue(format!(
64                "forest size {} exceeds maximum {}",
65                num_leaves,
66                Self::MAX_LEAVES
67            )));
68        }
69        Ok(Self(num_leaves))
70    }
71
72    /// Creates a forest with a given height.
73    ///
74    /// This is equivalent to creating a forest with `1 << height` leaves.
75    ///
76    /// # Panics
77    ///
78    /// This will panic if `height` is greater than `usize::BITS - 1`.
79    pub fn with_height(height: usize) -> Self {
80        assert!(height < usize::BITS as usize);
81        Self::new(1 << height).expect("forest height exceeds maximum")
82    }
83
84    /// Returns true if `num_leaves` is within the supported bounds.
85    pub const fn is_valid_size(num_leaves: usize) -> bool {
86        num_leaves <= Self::MAX_LEAVES
87    }
88
89    /// Returns true if there are no trees in the forest.
90    pub fn is_empty(self) -> bool {
91        self.0 == 0
92    }
93
94    /// Adds exactly one more leaf to the capacity of this forest.
95    ///
96    /// Some smaller trees might be merged together.
97    pub fn append_leaf(&mut self) -> Result<(), MmrError> {
98        if self.0 >= Self::MAX_LEAVES {
99            return Err(MmrError::ForestSizeExceeded {
100                requested: self.0.saturating_add(1),
101                max: Self::MAX_LEAVES,
102            });
103        }
104        self.0 += 1;
105        Ok(())
106    }
107
108    /// Returns a count of leaves in the entire underlying forest (MMR).
109    pub fn num_leaves(self) -> usize {
110        self.0
111    }
112
113    /// Return the total number of nodes of a given forest.
114    ///
115    /// This relies on the `Forest` invariant that `num_leaves() <= Forest::MAX_LEAVES`.
116    /// The internal assertion is a defensive check and should be unreachable for values created
117    /// through validated constructors/deserializers.
118    pub const fn num_nodes(self) -> usize {
119        assert!(self.0 <= Self::MAX_LEAVES);
120        if self.0 <= usize::MAX / 2 {
121            self.0 * 2 - self.num_trees()
122        } else {
123            // If `self.0 > usize::MAX / 2` then we need 128-bit math to double it.
124            let (inner, num_trees) = (self.0 as u128, self.num_trees() as u128);
125            (inner * 2 - num_trees) as usize
126        }
127    }
128
129    /// Return the total number of trees of a given forest (the number of active bits).
130    pub const fn num_trees(self) -> usize {
131        self.0.count_ones() as usize
132    }
133
134    /// Returns the height (bit position) of the largest tree in the forest.
135    ///
136    /// # Panics
137    ///
138    /// This will panic if the forest is empty.
139    pub fn largest_tree_height_unchecked(self) -> usize {
140        // ilog2 is computed with leading zeros, which itself is computed with the intrinsic ctlz.
141        // [Rust 1.67.0] x86 uses the `bsr` instruction. AArch64 uses the `clz` instruction.
142        self.0.ilog2() as usize
143    }
144
145    /// Returns the height (bit position) of the largest tree in the forest.
146    ///
147    /// If the forest cannot be empty, use [`largest_tree_height_unchecked`] for performance.
148    ///
149    /// [`largest_tree_height_unchecked`]: Self::largest_tree_height_unchecked
150    pub fn largest_tree_height(self) -> Option<usize> {
151        if self.is_empty() {
152            return None;
153        }
154
155        Some(self.largest_tree_height_unchecked())
156    }
157
158    /// Returns a forest with only the largest tree present.
159    ///
160    /// # Panics
161    ///
162    /// This will panic if the forest is empty.
163    pub fn largest_tree_unchecked(self) -> Self {
164        Self::with_height(self.largest_tree_height_unchecked())
165    }
166
167    /// Returns a forest with only the largest tree present.
168    ///
169    /// If forest cannot be empty, use `largest_tree` for better performance.
170    pub fn largest_tree(self) -> Self {
171        if self.is_empty() {
172            return Self::empty();
173        }
174
175        self.largest_tree_unchecked()
176    }
177
178    /// Returns the height (bit position) of the smallest tree in the forest.
179    ///
180    /// # Panics
181    ///
182    /// This will panic if the forest is empty.
183    pub fn smallest_tree_height_unchecked(self) -> usize {
184        // Trailing_zeros is computed with the intrinsic cttz. [Rust 1.67.0] x86 uses the `bsf`
185        // instruction. AArch64 uses the `rbit clz` instructions.
186        self.0.trailing_zeros() as usize
187    }
188
189    /// Returns the height (bit position) of the smallest tree in the forest.
190    ///
191    /// If the forest cannot be empty, use [`smallest_tree_height_unchecked`] for better
192    /// performance.
193    ///
194    /// [`smallest_tree_height_unchecked`]: Self::smallest_tree_height_unchecked
195    pub fn smallest_tree_height(self) -> Option<usize> {
196        if self.is_empty() {
197            return None;
198        }
199
200        Some(self.smallest_tree_height_unchecked())
201    }
202
203    /// Returns a forest with only the smallest tree present.
204    ///
205    /// # Panics
206    ///
207    /// This will panic if the forest is empty.
208    pub fn smallest_tree_unchecked(self) -> Self {
209        Self::with_height(self.smallest_tree_height_unchecked())
210    }
211
212    /// Returns a forest with only the smallest tree present.
213    ///
214    /// If forest cannot be empty, use `smallest_tree` for performance.
215    pub fn smallest_tree(self) -> Self {
216        if self.is_empty() {
217            return Self::empty();
218        }
219        self.smallest_tree_unchecked()
220    }
221
222    /// Keeps only trees larger than the reference tree.
223    ///
224    /// For example, if we start with the bit pattern `0b0101_0110`, and keep only the trees larger
225    /// than tree index 1, that targets this bit:
226    /// ```text
227    /// Forest(0b0101_0110).trees_larger_than(1)
228    ///                        ^
229    /// Becomes:      0b0101_0100
230    ///                        ^
231    /// ```
232    /// And keeps only trees *after* that bit, meaning that the tree at `tree_idx` is also removed,
233    /// resulting in `0b0101_0100`.
234    ///
235    /// ```
236    /// # use miden_crypto::merkle::mmr::Forest;
237    /// let range = Forest::new(0b0101_0110).unwrap();
238    /// assert_eq!(range.trees_larger_than(1), Forest::new(0b0101_0100).unwrap());
239    /// ```
240    pub fn trees_larger_than(self, tree_idx: u32) -> Self {
241        let mask = high_bitmask(tree_idx + 1);
242        Self::new(self.0 & mask).expect("forest size exceeds maximum")
243    }
244
245    /// Creates a new forest with all possible trees smaller than the smallest tree in this
246    /// forest.
247    ///
248    /// This forest must have exactly one tree.
249    ///
250    /// # Panics
251    /// With debug assertions enabled, this function panics if this forest does not have
252    /// exactly one tree.
253    ///
254    /// For a non-panicking version of this function, see [`Forest::all_smaller_trees()`].
255    pub fn all_smaller_trees_unchecked(self) -> Self {
256        debug_assert_eq!(self.num_trees(), 1);
257        Self::new(self.0 - 1).expect("forest size exceeds maximum")
258    }
259
260    /// Creates a new forest with all possible trees smaller than the smallest tree in this
261    /// forest, or returns `None` if this forest has more or less than one tree.
262    ///
263    /// If the forest cannot have more or less than one tree, use
264    /// [`Forest::all_smaller_trees_unchecked()`] for performance.
265    pub fn all_smaller_trees(self) -> Option<Forest> {
266        if self.num_trees() != 1 {
267            return None;
268        }
269        Some(self.all_smaller_trees_unchecked())
270    }
271
272    /// Returns a forest with exactly one tree, one size (depth) larger than the current one.
273    ///
274    /// # Errors
275    /// Returns an error if the resulting forest would exceed [`Forest::MAX_LEAVES`].
276    pub(crate) fn next_larger_tree(self) -> Result<Self, MmrError> {
277        debug_assert_eq!(self.num_trees(), 1);
278        let value = self.0.saturating_mul(2);
279        if value > Self::MAX_LEAVES {
280            return Err(MmrError::ForestSizeExceeded { requested: value, max: Self::MAX_LEAVES });
281        }
282        Ok(Forest(value))
283    }
284
285    /// Returns true if the forest contains a single-node tree.
286    pub fn has_single_leaf_tree(self) -> bool {
287        self.0 & 1 != 0
288    }
289
290    /// Add a single-node tree if not already present in the forest.
291    pub fn with_single_leaf(self) -> Self {
292        // Setting the lowest bit cannot exceed MAX_LEAVES when MAX_LEAVES is 2^k - 1.
293        Self(self.0 | 1)
294    }
295
296    /// Remove the single-node tree if present in the forest.
297    pub fn without_single_leaf(self) -> Self {
298        // Clearing the lowest bit does not add leaves.
299        Self(self.0 & (usize::MAX - 1))
300    }
301
302    /// Returns a new forest that does not have the trees that `other` has.
303    pub fn without_trees(self, other: Forest) -> Self {
304        // Clearing bits does not add leaves.
305        Self(self.0 & !other.0)
306    }
307
308    /// Returns index of the forest tree for a specified leaf index.
309    pub fn tree_index(&self, leaf_idx: usize) -> usize {
310        let root = self
311            .leaf_to_corresponding_tree(leaf_idx)
312            .expect("position must be part of the forest");
313        let smaller_tree_mask =
314            Self::new(2_usize.pow(root) - 1).expect("forest size exceeds maximum");
315        let num_smaller_trees = (*self & smaller_tree_mask).num_trees();
316        self.num_trees() - num_smaller_trees - 1
317    }
318
319    /// Returns the smallest tree's root element as an [InOrderIndex].
320    ///
321    /// This function takes the smallest tree in this forest, "pretends" that it is a subtree of a
322    /// fully balanced binary tree, and returns the the in-order index of that balanced tree's root
323    /// node.
324    pub fn root_in_order_index(&self) -> InOrderIndex {
325        // Count total size of all trees in the forest.
326        let nodes = self.num_nodes();
327
328        // Add the count for the parent nodes that separate each tree. These are allocated but
329        // currently empty, and correspond to the nodes that will be used once the trees are merged.
330        let open_trees = self.num_trees() - 1;
331
332        // Remove the leaf-count of the rightmost subtree. The target tree root index comes before
333        // the subtree, for the in-order tree walk.
334        let right_subtree_count = self.smallest_tree_unchecked().num_leaves() - 1;
335
336        let idx = nodes + open_trees - right_subtree_count;
337
338        InOrderIndex::new(idx.try_into().unwrap())
339    }
340
341    /// Returns the in-order index of the rightmost element (the smallest tree).
342    pub fn rightmost_in_order_index(&self) -> InOrderIndex {
343        // Count total size of all trees in the forest.
344        let nodes = self.num_nodes();
345
346        // Add the count for the parent nodes that separate each tree. These are allocated but
347        // currently empty, and correspond to the nodes that will be used once the trees are merged.
348        let open_trees = self.num_trees() - 1;
349
350        let idx = nodes + open_trees;
351
352        InOrderIndex::new(idx.try_into().unwrap())
353    }
354
355    /// Checks if an in-order index corresponds to a valid node in the forest.
356    ///
357    /// Returns `true` if the index points to an actual node within one of the trees,
358    /// `false` if the index is:
359    /// - Zero (invalid, as `InOrderIndex` is 1-indexed)
360    /// - Beyond the forest bounds
361    /// - A separator position between trees (these positions are reserved for future parent nodes
362    ///   when trees are merged, but don't correspond to actual nodes yet)
363    ///
364    /// # Example
365    /// For a forest with 7 leaves (0b111 = trees of 4, 2, and 1 leaves):
366    /// - Valid indices: 1-7 (first tree), 9-11 (second tree), 13 (third tree)
367    /// - Invalid separator indices: 8 (between first and second), 12 (between second and third)
368    pub fn is_valid_in_order_index(&self, idx: &InOrderIndex) -> bool {
369        // Index 0 is never valid (InOrderIndex is 1-indexed)
370        if idx.inner() == 0 {
371            return false;
372        }
373
374        // Empty forest has no valid indices
375        if self.is_empty() {
376            return false;
377        }
378
379        let idx_val = idx.inner();
380        let mut offset = 0usize;
381
382        // Iterate through trees from largest to smallest
383        for tree in TreeSizeIterator::new(*self).rev() {
384            let tree_nodes = tree.num_nodes();
385            let tree_start = offset + 1;
386            let tree_end = offset + tree_nodes;
387
388            if idx_val >= tree_start && idx_val <= tree_end {
389                return true;
390            }
391
392            // Move offset past this tree and the separator position
393            offset = tree_end + 1;
394        }
395
396        false
397    }
398
399    /// Given a leaf index in the current forest, return the tree number responsible for the
400    /// leaf.
401    ///
402    /// The result is a tree position `p`:
403    /// - `p+1` is the depth of the tree.
404    /// - Because the root element is not part of the proof, `p` is the length of the authentication
405    ///   path.
406    /// - `2^p` is equal to the number of leaves in this particular tree.
407    /// - And `2^(p+1)-1` corresponds to the size of the tree.
408    ///
409    /// For example, given a forest with 6 leaves whose forest is `0b110`:
410    /// ```text
411    ///       __ tree 2 __
412    ///      /            \
413    ///    ____          ____         _ tree 1 _
414    ///   /    \        /    \       /          \
415    ///  0      1      2      3     4            5
416    /// ```
417    ///
418    /// Leaf indices `0..=3` are in the tree at index 2 and leaf indices `4..=5` are in the tree at
419    /// index 1.
420    pub fn leaf_to_corresponding_tree(self, leaf_idx: usize) -> Option<u32> {
421        let forest = self.0;
422
423        if leaf_idx >= forest {
424            None
425        } else {
426            // - each bit in the forest is a unique tree and the bit position is its power-of-two
427            //   size
428            // - each tree is associated to a consecutive range of positions equal to its size from
429            //   left-to-right
430            // - this means the first tree owns from `0` up to the `2^k_0` first positions, where
431            //   `k_0` is the highest set bit position, the second tree from `2^k_0 + 1` up to
432            //   `2^k_1` where `k_1` is the second highest bit, so on.
433            // - this means the highest bits work as a category marker, and the position is owned by
434            //   the first tree which doesn't share a high bit with the position
435            let before = forest & leaf_idx;
436            let after = forest ^ before;
437            let tree_idx = after.ilog2();
438
439            Some(tree_idx)
440        }
441    }
442
443    /// Given a leaf index in the current forest, return the leaf index in the tree to which
444    /// the leaf belongs.
445    pub(super) fn leaf_relative_position(self, leaf_idx: usize) -> Option<usize> {
446        let tree_idx = self.leaf_to_corresponding_tree(leaf_idx)?;
447        let mask = high_bitmask(tree_idx + 1);
448        Some(leaf_idx - (self.0 & mask))
449    }
450}
451
452impl Display for Forest {
453    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
454        write!(f, "{}", self.0)
455    }
456}
457
458impl Binary for Forest {
459    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
460        write!(f, "{:b}", self.0)
461    }
462}
463
464impl BitAnd<Forest> for Forest {
465    type Output = Self;
466
467    fn bitand(self, rhs: Self) -> Self::Output {
468        Self::new(self.0 & rhs.0).expect("forest size exceeds maximum")
469    }
470}
471
472// Compile-time invariant: MAX_LEAVES must be exactly 2^k - 1.
473const _: () =
474    assert!(Forest::MAX_LEAVES != 0 && (Forest::MAX_LEAVES & (Forest::MAX_LEAVES + 1)) == 0);
475
476impl BitOr<Forest> for Forest {
477    type Output = Self;
478
479    fn bitor(self, rhs: Self) -> Self::Output {
480        Self(self.0 | rhs.0)
481    }
482}
483
484impl BitXor<Forest> for Forest {
485    type Output = Self;
486
487    fn bitxor(self, rhs: Self) -> Self::Output {
488        Self(self.0 ^ rhs.0)
489    }
490}
491
492impl BitXorAssign<Forest> for Forest {
493    fn bitxor_assign(&mut self, rhs: Self) {
494        self.0 ^= rhs.0;
495    }
496}
497
498impl TryFrom<Felt> for Forest {
499    type Error = MmrError;
500
501    fn try_from(value: Felt) -> Result<Self, Self::Error> {
502        let value = usize::try_from(value.as_canonical_u64()).map_err(|_| {
503            MmrError::ForestSizeExceeded {
504                requested: usize::MAX,
505                max: Self::MAX_LEAVES,
506            }
507        })?;
508        if value > Self::MAX_LEAVES {
509            return Err(MmrError::ForestSizeExceeded { requested: value, max: Self::MAX_LEAVES });
510        }
511        Ok(Self(value))
512    }
513}
514
515pub(crate) fn largest_tree_from_mask(mask: usize) -> Forest {
516    if mask == 0 {
517        Forest::empty()
518    } else {
519        let bit = mask.ilog2();
520        Forest::new(1usize << bit).expect("forest size exceeds maximum")
521    }
522}
523
524impl From<Forest> for Felt {
525    fn from(value: Forest) -> Self {
526        Felt::new_unchecked(value.0 as u64)
527    }
528}
529
530/// Return a bitmask for the bits including and above the given position.
531pub(crate) fn high_bitmask(bit: u32) -> usize {
532    if bit > usize::BITS - 1 { 0 } else { usize::MAX << bit }
533}
534
535// SERIALIZATION
536// ================================================================================================
537
538impl Serializable for Forest {
539    fn write_into<W: ByteWriter>(&self, target: &mut W) {
540        self.0.write_into(target);
541    }
542}
543
544impl Deserializable for Forest {
545    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
546        let value = source.read_usize()?;
547        Self::new(value)
548    }
549}
550
551#[cfg(feature = "serde")]
552impl<'de> serde::Deserialize<'de> for Forest {
553    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
554    where
555        D: serde::Deserializer<'de>,
556    {
557        let value = usize::deserialize(deserializer)?;
558        Self::new(value).map_err(serde::de::Error::custom)
559    }
560}
561
562// TREE SIZE ITERATOR
563// ================================================================================================
564
565/// Iterate over the trees within this `Forest`, from smallest to largest.
566///
567/// Each item is a "sub-forest", containing only one tree.
568pub struct TreeSizeIterator {
569    inner: Forest,
570}
571
572impl TreeSizeIterator {
573    pub fn new(value: Forest) -> TreeSizeIterator {
574        TreeSizeIterator { inner: value }
575    }
576}
577
578impl Iterator for TreeSizeIterator {
579    type Item = Forest;
580
581    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
582        let tree = self.inner.smallest_tree();
583
584        if tree.is_empty() {
585            None
586        } else {
587            self.inner = self.inner.without_trees(tree);
588            Some(tree)
589        }
590    }
591}
592
593impl DoubleEndedIterator for TreeSizeIterator {
594    fn next_back(&mut self) -> Option<<Self as Iterator>::Item> {
595        let tree = self.inner.largest_tree();
596
597        if tree.is_empty() {
598            None
599        } else {
600            self.inner = self.inner.without_trees(tree);
601            Some(tree)
602        }
603    }
604}