commonware_storage/mmr/mod.rs
1//! A Merkle Mountain Range (MMR) is an append-only data structure that allows for efficient
2//! verification of the inclusion of an element, or some range of consecutive elements, in a list.
3//!
4//! # Terminology
5//!
6//! An MMR is a list of perfect binary trees (aka "mountains") of strictly decreasing height. The
7//! roots of these trees are called the "peaks" of the MMR. Each "element" stored in the MMR is
8//! represented by some leaf node in one of these perfect trees, storing a positioned hash of the
9//! element. Non-leaf nodes store a positioned hash of their children.
10//!
11//! The "size" of an MMR is the total number of nodes summed over all trees.
12//!
13//! The nodes of the MMR are ordered by a post-order traversal of the MMR trees, starting from the
14//! from tallest tree to shortest. The "position" of a node in the MMR is defined as the 0-based
15//! index of the node in this ordering. This implies the positions of elements, which are always
16//! leaves, may not be contiguous even if they were consecutively added. An element's "number" is
17//! its 0-based index in the order of element insertion. In the example below, the right-most
18//! element has position 18 and number 10.
19//!
20//! As the MMR is an append-only data structure, node positions never change and can be used as
21//! stable identifiers.
22//!
23//! The "height" of a node is 0 for a leaf, 1 for the parent of 2 leaves, and so on.
24//!
25//! The "root digest" (or just "root") of an MMR is the result of hashing together the size of the
26//! MMR and the digests of every peak in decreasing order of height.
27//!
28//! # Examples
29//!
30//! (Borrowed from <https://docs.grin.mw/wiki/chain-state/merkle-mountain-range/>): After adding 11
31//! elements to an MMR, it will have 19 nodes total with 3 peaks corresponding to 3 perfect binary
32//! trees as pictured below, with nodes identified by their positions:
33//!
34//! ```text
35//! Height
36//! 3 14
37//! / \
38//! / \
39//! / \
40//! / \
41//! 2 6 13
42//! / \ / \
43//! 1 2 5 9 12 17
44//! / \ / \ / \ / \ / \
45//! 0 0 1 3 4 7 8 10 11 15 16 18
46//!
47//! Number 0 1 2 3 4 5 6 7 8 9 10
48//! ```
49//!
50//! The root hash in this example is computed as:
51//!
52//! ```text
53//!
54//! Hash(19,
55//! Hash(14, // first peak
56//! Hash(6,
57//! Hash(2, Hash(0, element_0), Hash(1, element_1)),
58//! Hash(5, Hash(3, element_2), Hash(4, element_4))
59//! )
60//! Hash(13,
61//! Hash(9, Hash(7, element_7), Hash(8, element_8)),
62//! Hash(12, Hash(10, element_10), Hash(11, element_11))
63//! )
64//! )
65//! Hash(17, Hash(15, element_15), Hash(16, element_16)) // second peak
66//! Hash(18, element_18) // third peak
67//! )
68//! ```
69
70use crate::mmr::hasher::Hasher;
71use commonware_cryptography::Hasher as CHasher;
72use std::future::Future;
73use thiserror::Error;
74
75pub mod bitmap;
76pub mod hasher;
77pub mod iterator;
78pub mod journaled;
79pub mod mem;
80pub mod storage;
81#[cfg(test)]
82mod tests;
83pub mod verification;
84
85/// A trait for building an MMR and computing the root.
86pub trait Builder<H: CHasher>: Send + Sync {
87 /// Add `element` to the MMR and return its position within it. The element can be an arbitrary
88 /// byte slice, and need not be converted to a digest first.
89 fn add(
90 &mut self,
91 hasher: &mut impl Hasher<H>,
92 element: &[u8],
93 ) -> impl Future<Output = Result<u64, Error>>;
94
95 /// Return the root hash of the MMR.
96 fn root(&self, hasher: &mut impl Hasher<H>) -> H::Digest;
97}
98
99/// Errors that can occur when interacting with an MMR.
100#[derive(Error, Debug)]
101pub enum Error {
102 #[error("an element required for this operation has been pruned: {0}")]
103 ElementPruned(u64),
104 #[error("metadata error: {0}")]
105 MetadataError(#[from] crate::metadata::Error),
106 #[error("journal error: {0}")]
107 JournalError(#[from] crate::journal::Error),
108 #[error("missing node: {0}")]
109 MissingNode(u64),
110 #[error("MMR is empty")]
111 Empty,
112 #[error("invalid update")]
113 InvalidUpdate,
114 #[error("invalid proof length")]
115 InvalidProofLength,
116 #[error("proof missing digest at position: {0}")]
117 MissingDigest(u64),
118 #[error("invalid size: {0}")]
119 InvalidSize(u64),
120 #[error("root mismatch")]
121 RootMismatch,
122 #[error("invalid proof")]
123 InvalidProof,
124 #[error("runtime error: {0}")]
125 Runtime(#[from] commonware_runtime::Error),
126}