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")]
13pub mod journaled;
14mod location;
15pub mod mem;
16pub mod mmb;
17pub mod mmr;
18pub(super) mod path;
19mod position;
20mod proof;
21mod read;
22#[cfg(feature = "std")]
23pub mod storage;
24#[cfg(feature = "std")]
25pub mod verification;
26
27use alloc::vec::Vec;
28use core::fmt::Debug;
29pub use location::{Location, LocationRangeExt};
30pub use position::Position;
31pub use proof::Proof;
32pub use read::Readable;
33use thiserror::Error;
34
35/// Marker trait for Merkle-family data structures.
36///
37/// Provides the per-family constants and conversion functions that differentiate
38/// MMR from MMB (or other future Merkle structures).
39pub trait Family: Copy + Clone + Debug + Default + Send + Sync + 'static {
40    /// Maximum valid node count / size.
41    const MAX_NODES: Position<Self>;
42
43    /// Maximum valid leaf count.
44    const MAX_LEAVES: Location<Self>;
45
46    /// Convert a leaf location to its node position, or equivalently, convert a leaf count to the
47    /// corresponding total node count (size).
48    ///
49    /// The public, guaranteed domain is `loc <= MAX_LEAVES`. Some implementations may also accept
50    /// slightly larger temporary values for internal probing (for example, when checking the next
51    /// size boundary), but callers must not rely on that behavior.
52    fn location_to_position(loc: Location<Self>) -> Position<Self>;
53
54    /// Convert a node position to its leaf location, or `None` if the position is not a leaf.
55    /// Equivalently, convert a total node count (size) to the corresponding leaf count, returning
56    /// `None` if the size is not valid.
57    ///
58    /// The caller guarantees `pos <= MAX_NODES`.
59    fn position_to_location(pos: Position<Self>) -> Option<Location<Self>>;
60
61    /// Whether `size` is a valid tree size for this Merkle structure.
62    fn is_valid_size(size: Position<Self>) -> bool;
63
64    /// Returns the largest valid size that is no greater than `size`.
65    fn to_nearest_size(size: Position<Self>) -> Position<Self>;
66
67    /// Return the peaks of a structure with the given `size` as `(position, height)` pairs
68    /// in canonical oldest-to-newest order (suitable for
69    /// [`Hasher::root`](crate::merkle::hasher::Hasher::root)).
70    fn peaks(size: Position<Self>) -> impl Iterator<Item = (Position<Self>, u32)>;
71
72    /// Compute positions of nodes that must be pinned when pruning to `prune_loc`.
73    ///
74    /// The default implementation returns the peaks of the sub-structure at `prune_loc`,
75    /// which is sufficient for both root computation and re-merkleization of retained leaves.
76    /// Implementations may override to return a conservative superset of the minimally
77    /// required nodes. Callers must therefore treat the result as "safe to retain" rather
78    /// than assuming it is minimal or canonical.
79    ///
80    /// # Panics
81    ///
82    /// Implementations panic if `prune_loc` is invalid (i.e., exceeds
83    /// [`MAX_LEAVES`](Self::MAX_LEAVES)). Callers must validate inputs before calling.
84    fn nodes_to_pin(prune_loc: Location<Self>) -> impl Iterator<Item = Position<Self>> + Send {
85        let prune_pos = Self::location_to_position(prune_loc);
86        Self::peaks(prune_pos)
87            .filter(move |&(pos, _)| pos < prune_pos)
88            .map(|(pos, _)| pos)
89            .collect::<Vec<_>>()
90            .into_iter()
91    }
92
93    /// Return the positions of the left and right children of the node at `pos` with the
94    /// given `height`. The caller guarantees `height > 0` (leaves have no children).
95    fn children(pos: Position<Self>, height: u32) -> (Position<Self>, Position<Self>);
96
97    /// Return the heights of the internal nodes that lie between `size_for(N)` and
98    /// `size_for(N+1)`, where `N` is the given leaf count. These are the nodes created
99    /// when the `N`-th leaf is appended.
100    fn parent_heights(leaves: Location<Self>) -> impl Iterator<Item = u32>;
101
102    /// Return the height of the node at `pos`.
103    ///
104    /// # Panics
105    ///
106    /// Implementations may panic if `pos` does not correspond to a node that exists in some valid
107    /// instance of this family (i.e., it is not a position that would appear in a structure of any
108    /// size).
109    fn pos_to_height(pos: Position<Self>) -> u32;
110}
111
112/// Extension of [`Family`] with methods needed for grafting bitmap chunks onto a Merkle structure.
113/// Grafting combines an activity bitmap with an ops Merkle structure by hashing bitmap chunks
114/// together with ops subtree roots. These methods provide the coordinate conversions and
115/// chunk-to-peak mappings required by that process.
116pub trait Graftable: Family {
117    /// Return the nodes that collectively cover the leaf range of a bitmap chunk in a structure of
118    /// the given `size`.
119    ///
120    /// A chunk at index `chunk_idx` with grafting height `grafting_height` covers leaves
121    /// `[chunk_idx << grafting_height, (chunk_idx + 1) << grafting_height)`. The returned nodes
122    /// partition that range: each node's leaf range is entirely within the chunk, and together they
123    /// cover it exactly.
124    ///
125    /// Results are returned in oldest-to-newest (left-to-right) order.
126    ///
127    /// # Panics
128    ///
129    /// Panics if `size` is not a valid size or if the chunk's leaf range exceeds the structure's
130    /// leaf count.
131    fn chunk_peaks(
132        size: Position<Self>,
133        chunk_idx: u64,
134        grafting_height: u32,
135    ) -> impl Iterator<Item = (Position<Self>, u32)> + Send;
136
137    /// Return the deterministic position of the node at `height` whose leftmost leaf is at
138    /// `leaf_start`.
139    ///
140    /// For some families, this position corresponds to a node that physically exists in any
141    /// structure containing those leaves. For others (e.g. MMB with delayed merging), it may be a
142    /// "virtual" position that no actual node occupies, but is still deterministic and unique for
143    /// the given leaf range and height.
144    ///
145    /// Used by grafting to map grafted-structure positions to ops-structure positions for domain
146    /// separation in hash pre-images.
147    ///
148    /// # Panics
149    ///
150    /// Panics if `height` is excessively large (e.g., `>= 63`), or if the resulting position
151    /// computation overflows the bounds of the underlying numeric types.
152    fn subtree_root_position(leaf_start: Location<Self>, height: u32) -> Position<Self>;
153
154    /// Return the location of the leftmost leaf covered by the node at `pos` with `height`. For a
155    /// leaf (height 0), returns its own location.
156    ///
157    /// # Panics
158    ///
159    /// Panics if `height` is excessively large (e.g., `>= 63`), or if an invalid combination of
160    /// `pos` and `height` results in arithmetic underflow/overflow.
161    fn leftmost_leaf(pos: Position<Self>, height: u32) -> Location<Self>;
162}
163
164/// Errors that can occur when interacting with a Merkle-family data structure.
165#[derive(Debug, Error)]
166pub enum Error<F: Family> {
167    /// The position does not correspond to a leaf node.
168    #[error("{0} is not a leaf")]
169    NonLeaf(Position<F>),
170
171    /// The position exceeds the valid range.
172    #[error("{0} > MAX_NODES")]
173    PositionOverflow(Position<F>),
174
175    /// The location exceeds the valid range.
176    #[error("{0} > MAX_LEAVES")]
177    LocationOverflow(Location<F>),
178
179    /// The range is empty but must contain at least one element.
180    #[error("range is empty")]
181    Empty,
182
183    /// The end of a range is out of bounds.
184    #[error("range end out of bounds: {0}")]
185    RangeOutOfBounds(Location<F>),
186
187    /// The requested size is invalid.
188    #[error("invalid size: {0}")]
189    InvalidSize(u64),
190
191    /// A requested leaf location exceeds the current leaf count.
192    #[error("leaf location out of bounds: {0}")]
193    LeafOutOfBounds(Location<F>),
194
195    /// A required node was not available (e.g. pruned).
196    #[error("element pruned: {0}")]
197    ElementPruned(Position<F>),
198
199    /// The provided pinned node list does not match the expected pruning boundary.
200    #[error("invalid pinned nodes")]
201    InvalidPinnedNodes,
202
203    /// Structure has diverged incompatibly from the batch's ancestor chain.
204    #[error("stale batch: base size {expected}, current size {actual}")]
205    StaleBatch {
206        /// The base size when the batch chain was forked.
207        expected: Position<F>,
208        /// The current structure size.
209        actual: Position<F>,
210    },
211
212    /// An ancestor batch was dropped before this batch was applied, causing
213    /// data loss. All ancestors must be kept alive until descendants are applied.
214    #[error("ancestor dropped: expected size {expected}, actual size {actual}")]
215    AncestorDropped {
216        /// The expected size after applying all ancestors + this batch.
217        expected: Position<F>,
218        /// The actual size (less than expected due to missing ancestor data).
219        actual: Position<F>,
220    },
221
222    /// The proof is invalid.
223    #[error("invalid proof")]
224    InvalidProof,
225
226    /// The root does not match the computed root.
227    #[error("root mismatch")]
228    RootMismatch,
229
230    /// A required digest is missing.
231    #[error("missing digest: {0}")]
232    MissingDigest(Position<F>),
233
234    /// A metadata error occurred.
235    #[cfg(feature = "std")]
236    #[error("metadata error: {0}")]
237    Metadata(#[from] crate::metadata::Error),
238
239    /// A journal error occurred.
240    #[cfg(feature = "std")]
241    #[error("journal error: {0}")]
242    Journal(#[from] crate::journal::Error),
243
244    /// A runtime error occurred.
245    #[cfg(feature = "std")]
246    #[error("runtime error: {0}")]
247    Runtime(#[from] commonware_runtime::Error),
248
249    /// A required node is missing.
250    #[error("missing node: {0}")]
251    MissingNode(Position<F>),
252
253    /// Data is corrupted.
254    #[error("data corrupted: {0}")]
255    DataCorrupted(&'static str),
256
257    /// A required grafted leaf digest is missing.
258    #[error("missing grafted leaf digest: {0}")]
259    MissingGraftedLeaf(Position<F>),
260
261    /// Bit offset is out of bounds.
262    #[error("bit offset {0} out of bounds (size: {1})")]
263    BitOutOfBounds(u64, u64),
264}