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}