Skip to main content

commonware_storage/merkle/
mod.rs

1//! Shared types for Merkle-family data structures (MMR, MMB).
2//!
3//! This module provides generic `Position<F>` and `Location<F>` types parameterized by a
4//! [`Family`] marker trait. Each Merkle family (e.g. MMR, MMB) implements the trait with
5//! its own constants and conversion formulas, while the shared arithmetic, codec, and comparison
6//! logic lives here.
7
8pub mod batch;
9#[cfg(test)]
10pub(crate) mod conformance;
11pub mod hasher;
12#[cfg(feature = "std")]
13mod persisted;
14#[cfg(feature = "std")]
15pub use persisted::{compact, full};
16mod location;
17pub mod mem;
18pub mod mmb;
19pub mod mmr;
20pub mod path;
21mod position;
22mod proof;
23mod read;
24#[cfg(feature = "std")]
25pub mod storage;
26#[cfg(feature = "std")]
27pub mod verification;
28
29use bytes::BufMut;
30use commonware_codec::{Buf, EncodeSize, Read, Write};
31use commonware_cryptography::Digest;
32use core::fmt::Debug;
33pub use location::{Location, LocationRangeExt};
34pub use position::Position;
35pub use proof::{
36    ElementPlan, MAX_PROOF_DIGESTS_PER_ELEMENT, Proof, RangePlan, multi_proof_positions,
37};
38pub use read::Readable;
39use thiserror::Error;
40
41/// Safe upper bound on the number of pinned nodes for any u64-backed Merkle family.
42///
43/// Pinned nodes are produced by [`Family::nodes_to_pin`], whose default
44/// implementation returns the peaks of the sub-structure at the prune
45/// location. A structure with `n` leaves has at most `popcount(n)` peaks, and
46/// since [`Location`] is backed by a `u64`, `n <= u64::MAX` yields at most 64
47/// peaks.
48pub const MAX_PINNED_NODES: usize = 64;
49
50/// Defines the strategy used to fold peaks into the final root digest.
51#[derive(Copy, Clone, Debug, PartialEq, Eq)]
52pub enum Bagging {
53    /// Peaks are bagged forward from oldest to newest, placing the smallest peak at the top.
54    ForwardFold,
55    /// Peaks are bagged backward from newest to oldest, placing the largest peak at the top.
56    BackwardFold,
57}
58
59impl Bagging {
60    /// Domain tag committed by every root folded under this policy.
61    ///
62    /// Tags are unique across policies. Changing one changes every root folded under that policy.
63    pub const fn domain(self) -> u8 {
64        match self {
65            Self::ForwardFold => 0,
66            Self::BackwardFold => 1,
67        }
68    }
69}
70
71/// Marker trait for Merkle-family data structures.
72///
73/// Provides the per-family constants and conversion functions that differentiate
74/// MMR from MMB (or other future Merkle structures). Families capture structural topology
75/// only; bagging is owned by the [`hasher::Hasher`] instance and supplied by the consumer.
76pub trait Family: Copy + Debug + Default + Send + Sync + 'static {
77    /// Domain tag committed by every root of this family.
78    ///
79    /// Tags are unique across families. Changing one changes every root of that family.
80    const DOMAIN: u8;
81
82    /// Maximum valid node count / size.
83    const MAX_NODES: Position<Self>;
84
85    /// Maximum valid leaf count.
86    const MAX_LEAVES: Location<Self>;
87
88    /// Convert a leaf location to its node position, or equivalently, convert a leaf count to the
89    /// corresponding total node count (size).
90    ///
91    /// The public, guaranteed domain is `loc <= MAX_LEAVES`. Some implementations may also accept
92    /// slightly larger temporary values for internal probing (for example, when checking the next
93    /// size boundary), but callers must not rely on that behavior.
94    fn location_to_position(loc: Location<Self>) -> Position<Self>;
95
96    /// Convert a node position to its leaf location, or `None` if the position is not a leaf.
97    /// Equivalently, convert a total node count (size) to the corresponding leaf count, returning
98    /// `None` if the size is not valid.
99    ///
100    /// The caller guarantees `pos <= MAX_NODES`.
101    fn position_to_location(pos: Position<Self>) -> Option<Location<Self>>;
102
103    /// Whether `size` is a valid tree size for this Merkle structure.
104    fn is_valid_size(size: Position<Self>) -> bool;
105
106    /// Returns the largest valid size that is no greater than `size`.
107    fn to_nearest_size(size: Position<Self>) -> Position<Self>;
108
109    /// Return the peaks of a structure with the given `size` as `(position, height)` pairs
110    /// in canonical oldest-to-newest order (suitable for
111    /// [`Hasher::root`](crate::merkle::hasher::Hasher::root)).
112    fn peaks(size: Position<Self>) -> impl Iterator<Item = (Position<Self>, u32)> + Send;
113
114    /// Count the number of oldest peaks that are entirely inactive.
115    ///
116    /// A peak is considered entirely inactive if all of its leaves strictly precede the
117    /// `inactivity_floor`. These peaks can be safely bagged forward into the `grafted_root`.
118    fn inactive_peaks(size: Location<Self>, inactivity_floor: Location<Self>) -> usize {
119        let mut inactive_count = 0;
120        let mut leaf_capacity_sum = 0u64;
121        for (_, height) in Self::peaks(Self::location_to_position(size)) {
122            let capacity = 1u64.checked_shl(height).expect("height excessively large");
123            leaf_capacity_sum = leaf_capacity_sum
124                .checked_add(capacity)
125                .expect("capacity overflow");
126            if leaf_capacity_sum <= *inactivity_floor {
127                inactive_count += 1;
128            } else {
129                break;
130            }
131        }
132        inactive_count
133    }
134
135    /// Compute positions of nodes that must be pinned when pruning to `prune_loc`.
136    ///
137    /// Pinned nodes are the minimal set of pruned digests required to continue growing the
138    /// structure and recomputing its root after pruning. The default implementation returns the
139    /// peaks of the sub-structure at `prune_loc`, which is sufficient for both root computation and
140    /// re-merkleization of retained leaves. Implementations may override this if their family
141    /// requires a different canonical pinned-node set for the pruning boundary.
142    ///
143    /// # Panics
144    ///
145    /// Implementations panic if `prune_loc` is invalid (i.e., exceeds
146    /// [`MAX_LEAVES`](Self::MAX_LEAVES)). Callers must validate inputs before calling.
147    fn nodes_to_pin(prune_loc: Location<Self>) -> impl Iterator<Item = Position<Self>> + Send {
148        let prune_pos = Self::location_to_position(prune_loc);
149        Self::peaks(prune_pos)
150            .filter(move |&(pos, _)| pos < prune_pos)
151            .map(|(pos, _)| pos)
152    }
153
154    /// Return the positions of the left and right children of the node at `pos` with the
155    /// given `height`. The caller guarantees `height > 0` (leaves have no children).
156    fn children(pos: Position<Self>, height: u32) -> (Position<Self>, Position<Self>);
157
158    /// Return the heights of the internal nodes that lie between `size_for(N)` and
159    /// `size_for(N+1)`, where `N` is the given leaf count. These are the nodes created
160    /// when the `N`-th leaf is appended.
161    fn parent_heights(leaves: Location<Self>) -> impl Iterator<Item = u32>;
162
163    /// Return the height of the node at `pos`.
164    ///
165    /// # Panics
166    ///
167    /// Implementations may panic if `pos` does not correspond to a node that exists in some valid
168    /// instance of this family (i.e., it is not a position that would appear in a structure of any
169    /// size).
170    fn pos_to_height(pos: Position<Self>) -> u32;
171}
172
173/// Pending-chunk slot for Merkle families that do not carry a pending chunk (e.g. MMR).
174#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
175pub struct Unused;
176
177impl Write for Unused {
178    #[inline]
179    fn write(&self, _: &mut impl BufMut) {}
180}
181
182impl EncodeSize for Unused {
183    #[inline]
184    fn encode_size(&self) -> usize {
185        0
186    }
187}
188
189impl Read for Unused {
190    type Cfg = ();
191
192    #[inline]
193    fn read_cfg(_: &mut impl Buf, _: &Self::Cfg) -> Result<Self, commonware_codec::Error> {
194        Ok(Self)
195    }
196}
197
198impl<D: Digest> TryFrom<Option<D>> for Unused {
199    type Error = commonware_codec::Error;
200
201    #[inline]
202    fn try_from(value: Option<D>) -> Result<Self, Self::Error> {
203        match value {
204            None => Ok(Self),
205            Some(_) => Err(commonware_codec::Error::Invalid(
206                "PendingChunk",
207                "pending chunk present for family without pending chunks",
208            )),
209        }
210    }
211}
212
213#[cfg(feature = "arbitrary")]
214impl arbitrary::Arbitrary<'_> for Unused {
215    fn arbitrary(_: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
216        Ok(Self)
217    }
218}
219
220/// The pending-chunk slot carried by a [Graftable] family's proof and witness types.
221pub trait PendingChunk<D: Digest>:
222    Clone
223    + Debug
224    + Eq
225    + Write
226    + EncodeSize
227    + Read<Cfg = ()>
228    + Send
229    + Sync
230    + TryFrom<Option<D>, Error: Debug>
231{
232    /// View the slot as an `Option<&D>`.
233    fn as_ref(&self) -> Option<&D> {
234        None
235    }
236}
237
238impl<D: Digest> PendingChunk<D> for Option<D> {
239    #[inline]
240    fn as_ref(&self) -> Option<&D> {
241        Self::as_ref(self)
242    }
243}
244
245impl<D: Digest> PendingChunk<D> for Unused {}
246
247/// Extension of [`Family`] with methods needed for grafting bitmap chunks onto a Merkle structure.
248/// Grafting combines an activity bitmap with an ops Merkle structure by hashing bitmap chunks
249/// together with ops subtree roots. These methods provide the coordinate conversions and
250/// chunk-to-peak mappings required by that process.
251pub trait Graftable: Family {
252    /// The pending-chunk slot type for this family's proofs and witnesses.
253    type PendingChunk<D: Digest>: PendingChunk<D>;
254
255    /// Return the nodes that collectively cover the leaf range of a bitmap chunk in a structure of
256    /// the given `size`.
257    ///
258    /// A chunk at index `chunk_idx` with grafting height `grafting_height` covers leaves
259    /// `[chunk_idx << grafting_height, (chunk_idx + 1) << grafting_height)`. The returned nodes
260    /// partition that range: each node's leaf range is entirely within the chunk, and together they
261    /// cover it exactly.
262    ///
263    /// Results are returned in oldest-to-newest (left-to-right) order.
264    ///
265    /// # Panics
266    ///
267    /// Panics if `size` is not a valid size or if the chunk's leaf range exceeds the structure's
268    /// leaf count.
269    fn chunk_peaks(
270        size: Position<Self>,
271        chunk_idx: u64,
272        grafting_height: u32,
273    ) -> impl Iterator<Item = (Position<Self>, u32)> + Send;
274
275    /// Return the deterministic position of the node at `height` whose leftmost leaf is at
276    /// `leaf_start`.
277    ///
278    /// For some families, this position corresponds to a node that physically exists in any
279    /// structure containing those leaves. For others (e.g. MMB with delayed merging), it may be a
280    /// "virtual" position that no actual node occupies, but is still deterministic and unique for
281    /// the given leaf range and height.
282    ///
283    /// Used by grafting to map grafted-structure positions to ops-structure positions for domain
284    /// separation in hash pre-images.
285    ///
286    /// # Panics
287    ///
288    /// Panics if `height` is excessively large (e.g., `>= 63`), or if the resulting position
289    /// computation overflows the bounds of the underlying numeric types.
290    fn subtree_root_position(leaf_start: Location<Self>, height: u32) -> Position<Self>;
291
292    /// Return the location of the leftmost leaf covered by the node at `pos` with `height`. For a
293    /// leaf (height 0), returns its own location.
294    ///
295    /// # Panics
296    ///
297    /// Panics if `height` is excessively large (e.g., `>= 63`), or if an invalid combination of
298    /// `pos` and `height` results in arithmetic underflow/overflow.
299    fn leftmost_leaf(pos: Position<Self>, height: u32) -> Location<Self>;
300
301    /// Return the minimum leaf count at which the node at `pos` with `height` exists in the
302    /// structure.
303    ///
304    /// For families without delayed merging (e.g. MMR), a node exists as soon as all leaves
305    /// in its span have been appended. For families with delayed merging (e.g. MMB), the
306    /// node is created some number of leaf insertions _after_ its last leaf, so the birth
307    /// size is larger. The MMB override accounts for this delay.
308    ///
309    /// This is used by the grafted-tree pruning logic to determine when a chunk-pair's
310    /// parent has been born in the ops tree, which controls when it is safe to prune the
311    /// pair's individual grafted leaves.
312    ///
313    /// # Panics
314    ///
315    /// Panics if `height` is excessively large (e.g., `>= 63`), or if arithmetic overflows.
316    fn peak_birth_size(pos: Position<Self>, height: u32) -> u64 {
317        let leftmost = *Self::leftmost_leaf(pos, height);
318        let width = 1u64.checked_shl(height).expect("height excessively large");
319        leftmost.checked_add(width).expect("birth size overflow")
320    }
321}
322
323/// Errors that can occur when interacting with a Merkle-family data structure.
324#[derive(Debug, Error)]
325pub enum Error<F: Family> {
326    /// The position does not correspond to a leaf node.
327    #[error("{0} is not a leaf")]
328    NonLeaf(Position<F>),
329
330    /// The position exceeds the valid range.
331    #[error("{0} > MAX_NODES")]
332    PositionOverflow(Position<F>),
333
334    /// The location exceeds the valid range.
335    #[error("{0} > MAX_LEAVES")]
336    LocationOverflow(Location<F>),
337
338    /// The range is empty but must contain at least one element.
339    #[error("range is empty")]
340    Empty,
341
342    /// The end of a range is out of bounds.
343    #[error("range end out of bounds: {0}")]
344    RangeOutOfBounds(Location<F>),
345
346    /// The requested size is invalid.
347    #[error("invalid size: {0}")]
348    InvalidSize(u64),
349
350    /// A requested leaf location exceeds the current leaf count.
351    #[error("leaf location out of bounds: {0}")]
352    LeafOutOfBounds(Location<F>),
353
354    /// A requested leaf location repeats the location before it.
355    #[error("duplicate leaf location: {0}")]
356    DuplicateLocation(Location<F>),
357
358    /// A requested leaf location is less than the location before it.
359    #[error("unsorted leaf location: {0}")]
360    UnsortedLocation(Location<F>),
361
362    /// A required node was not available (e.g. pruned).
363    #[error("element pruned: {0}")]
364    ElementPruned(Position<F>),
365
366    /// A required digest was compressed into a synthetic accumulator (e.g. a backward-fold
367    /// suffix accumulator) when the source proof was constructed, so it is not individually
368    /// available. Unlike [`Error::ElementPruned`], the digest still exists in the source
369    /// structure; the caller can recover by supplying it through an explicit witness slice.
370    #[error("digest hidden behind synthetic accumulator: {0}")]
371    CompressedDigest(Position<F>),
372
373    /// The provided pinned node list does not match the expected pruning boundary.
374    #[error("invalid pinned nodes")]
375    InvalidPinnedNodes,
376
377    /// The structure is at none of the states recorded by the batch's chain, so applying the
378    /// batch would combine it with a different history.
379    #[error("stale batch: no matching chain state (size {actual}, fork size {expected})")]
380    StaleBatch {
381        /// The base size when the batch chain was forked.
382        expected: Position<F>,
383        /// The current structure size.
384        actual: Position<F>,
385    },
386
387    /// An unapplied ancestor batch was dropped before this batch was merkleized, so this batch
388    /// lacks that ancestor's changes. Keep an ancestor alive until it is applied or until the
389    /// descendants that will be applied have been merkleized.
390    #[error("ancestor dropped: expected size {expected}, actual size {actual}")]
391    AncestorDropped {
392        /// The size the structure would have after applying the batch and its ancestors.
393        expected: Position<F>,
394        /// The structure's size when the batch was rejected.
395        actual: Position<F>,
396    },
397
398    /// The proof is invalid.
399    #[error("invalid proof")]
400    InvalidProof,
401
402    /// The requested inactive peak count cannot be represented by the current peak list.
403    #[error("inactive peak count {requested} exceeds peak count {peaks}")]
404    InvalidInactivePeaks {
405        /// Requested inactive peak count.
406        requested: usize,
407        /// Number of available peaks.
408        peaks: usize,
409    },
410
411    /// The root does not match the computed root.
412    #[error("root mismatch")]
413    RootMismatch,
414
415    /// A metadata error occurred.
416    #[cfg(feature = "std")]
417    #[error("metadata error: {0}")]
418    Metadata(#[from] crate::metadata::Error),
419
420    /// A journal error occurred.
421    #[cfg(feature = "std")]
422    #[error("journal error: {0}")]
423    Journal(#[from] crate::journal::Error),
424
425    /// A required node is missing.
426    #[error("missing node: {0}")]
427    MissingNode(Position<F>),
428
429    /// Data is corrupted.
430    #[error("data corrupted: {0}")]
431    DataCorrupted(&'static str),
432
433    /// Bit offset is out of bounds.
434    #[error("bit offset {0} out of bounds (size: {1})")]
435    BitOutOfBounds(u64, u64),
436}