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