ab_merkle_tree/sparse.rs
1//! Sparse Merkle Tree and related data structures.
2//!
3//! Sparse Merkle Tree is essentially a huge Balanced Merkle Tree, where most of the leaves are
4//! empty. By "empty" here we mean all zeroes. To optimize proofs and their verification, the
5//! hashing function is customized and returns all zeroes when both left and right branch are all
6//! zeroes, otherwise inner nodes are hashed like in a Balanced Merkle Tree.
7
8use crate::MerkleHasher;
9use core::marker::PhantomCovariant;
10use core::num::NonZeroU128;
11
12/// Number of elements in a proof for a tree with `BITS`
13pub const PROOF_ELEMENTS<const BITS: u8>: usize = usize::from(BITS);
14const STACK_SIZE<const BITS: u8>: usize = usize::from(BITS) + 1;
15
16/// Ensuring only supported `NUM_BITS` can be specified for [`SparseMerkleTree`].
17///
18/// This is essentially a workaround for the current Rust type system constraints that do not allow
19/// a nicer way to do the same thing at compile time.
20const ENSURE_SUPPORTED_BITS<const BITS: u8>: usize = {
21 assert!(
22 BITS <= 128,
23 "This Sparse Merkle Tree doesn't support more than 2^128 leaves"
24 );
25
26 assert!(
27 BITS != 0,
28 "This Sparse Merkle Tree must have more than one leaf"
29 );
30
31 0
32};
33
34/// Sparse Merkle Tree Leaf
35#[derive(Debug)]
36pub enum Leaf<'a, H>
37where
38 H: MerkleHasher,
39{
40 // TODO: Batch of leaves for efficiently, especially with SIMD?
41 /// Leaf contains a value
42 Occupied {
43 /// Leaf value
44 leaf: &'a [u8; H::OUT_LEN],
45 },
46 /// Leaf contains a value (owned)
47 OccupiedOwned {
48 /// Leaf value
49 leaf: [u8; H::OUT_LEN],
50 },
51 /// Leaf is empty
52 Empty {
53 /// Number of consecutive empty leaves
54 skip_count: NonZeroU128,
55 },
56}
57
58impl<'a, H> From<&'a [u8; H::OUT_LEN]> for Leaf<'a, H>
59where
60 H: MerkleHasher,
61{
62 #[inline(always)]
63 fn from(leaf: &'a [u8; H::OUT_LEN]) -> Self {
64 Self::Occupied { leaf }
65 }
66}
67
68// TODO: A version that can hold intermediate nodes in memory, efficiently update leaves, etc.
69/// Sparse Merkle Tree variant that has hash-sized leaves, with most leaves being empty
70/// (all zeroes).
71///
72/// In contrast to a proper Balanced Merkle Tree, constant `BITS` here specifies the max number of
73/// leaves hypothetically possible in a tree (2^BITS, often intractable), rather than the number of
74/// non-empty leaves actually present.
75///
76/// Inner nodes are hashed with `H` (except when both children are empty), which is created with
77/// [`Default`] since the number of leaves is always `2^BITS`.
78#[derive(Debug)]
79pub struct SparseMerkleTree<const BITS: u8, H>(PhantomCovariant<H>);
80
81// TODO: Optimize by hashing multiple pairs at once with `MerkleHasher::hash_pairs()`
82impl<const BITS: u8, H> SparseMerkleTree<BITS, H>
83where
84 [(); ENSURE_SUPPORTED_BITS::<BITS>]:,
85 H: MerkleHasher + Default,
86{
87 const ZERO: [u8; H::OUT_LEN] = [0; H::OUT_LEN];
88
89 // TODO: Method that generates not only root, but also proof, like Unbalanced Merkle Tree
90 /// Compute Merkle Tree root.
91 ///
92 /// If provided iterator ends early, it means the rest of the leaves are empty.
93 ///
94 /// There must be no [`Leaf::Occupied`] for empty/unoccupied leaves or else they may result in
95 /// invalid root, [`Leaf::Empty`] must be used instead.
96 ///
97 /// Returns `None` if too many leaves were provided.
98 #[inline]
99 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
100 pub fn compute_root_only<'a, Iter>(leaves: Iter) -> Option<[u8; H::OUT_LEN]>
101 where
102 H: 'a,
103 Iter: IntoIterator<Item = Leaf<'a, H>> + 'a,
104 {
105 // TODO: Technically redundant with the where-clause, which is not enforced in generic code
106 // due to https://github.com/rust-lang/rust/issues/164019, remove once fixed
107 const { ENSURE_SUPPORTED_BITS::<BITS> };
108
109 let hasher = H::default();
110
111 // Stack of intermediate nodes per tree level
112 let mut stack = [[0u8; H::OUT_LEN]; _];
113 let mut processed_some = false;
114 let mut num_leaves = 0u128;
115
116 for leaf in leaves {
117 if u32::from(BITS) < u128::BITS {
118 // How many leaves were processed so far. Should have been
119 // `num_leaves == 2^BITS`, but `>=` helps compiler with panic safety checks.
120 // TODO: Use `==` once https://github.com/rust-lang/rust/issues/162834 is resolved
121 if num_leaves >= 2u128.pow(u32::from(BITS)) {
122 return None;
123 }
124 } else {
125 // For `BITS == u128::BITS` `num_leaves` will wrap around back to zero right at the
126 // very end
127 if processed_some && num_leaves == 0 {
128 return None;
129 }
130 processed_some = true;
131 }
132
133 let leaf = match leaf {
134 Leaf::Occupied { leaf } => *leaf,
135 Leaf::OccupiedOwned { leaf } => leaf,
136 Leaf::Empty { skip_count } => {
137 num_leaves = Self::skip_leaves(
138 &hasher,
139 &mut stack,
140 &mut processed_some,
141 num_leaves,
142 skip_count.get(),
143 )?;
144 continue;
145 }
146 };
147
148 let mut current = leaf;
149
150 // Every bit set to `1` corresponds to an active Merkle Tree level
151 let lowest_active_levels = num_leaves.trailing_ones() as usize;
152 for item in stack.iter().take(lowest_active_levels) {
153 current = hasher.hash_pair(&[*item, current]);
154 }
155
156 // Place the current hash at the first inactive level
157 *stack.get_mut(lowest_active_levels).expect(
158 "Stack has `BITS + 1` levels, enough for `num_leaves < 2^BITS` checked above; qed",
159 ) = current;
160 // Wrapping is needed for `BITS == u128::BITS`, where number of leaves narrowly
161 // doesn't fit into `u128` itself
162 num_leaves = num_leaves.wrapping_add(1);
163 }
164
165 if u32::from(BITS) < u128::BITS {
166 Self::skip_leaves(
167 &hasher,
168 &mut stack,
169 &mut processed_some,
170 num_leaves,
171 2u128.pow(u32::from(BITS)) - num_leaves,
172 )?;
173 } else if processed_some && num_leaves != 0 {
174 // For `BITS == u128::BITS` `num_leaves` will wrap around back to zero right at the
175 // very end, so we reverse the mechanism here
176 Self::skip_leaves(
177 &hasher,
178 &mut stack,
179 &mut processed_some,
180 num_leaves,
181 0u128.wrapping_sub(num_leaves),
182 )?;
183 }
184
185 Some(stack[BITS as usize])
186 }
187
188 /// Returns updated number of leaves
189 #[inline]
190 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
191 fn skip_leaves(
192 hasher: &H,
193 stack: &mut [[u8; H::OUT_LEN]; STACK_SIZE::<BITS>],
194 processed_some: &mut bool,
195 mut num_leaves: u128,
196 mut skip_count: u128,
197 ) -> Option<u128> {
198 if u32::from(BITS) < u128::BITS {
199 // How many leaves were processed so far
200 if num_leaves.checked_add(skip_count)? > 2u128.pow(u32::from(BITS)) {
201 return None;
202 }
203 } else {
204 // For `BITS == u128::BITS` `num_leaves` will wrap around back to zero right at the
205 // very end
206 let (overflow_amount, overflowed) = num_leaves.overflowing_add(skip_count);
207 if *processed_some && overflowed && overflow_amount > 0 {
208 return None;
209 }
210 *processed_some = true;
211 }
212
213 while skip_count > 0 {
214 // Find the largest aligned chunk to skip for the current state of the tree
215 let max_levels_to_skip = skip_count.ilog2().min(num_leaves.trailing_zeros());
216 let chunk_size = 1u128 << max_levels_to_skip;
217
218 let mut level = max_levels_to_skip;
219 let mut current = Self::ZERO;
220 for item in stack.iter().skip(max_levels_to_skip as usize) {
221 // Check the active level for merging up the stack.
222 //
223 // `BITS == u128::BITS` condition is only added for better dead code elimination
224 // since that check is only relevant for 2^128 leaves case and nothing else.
225 if (u32::from(BITS) == u128::BITS && level == u128::BITS)
226 || num_leaves & (1 << level) == 0
227 {
228 // Level wasn't active before, stop here
229 break;
230 }
231
232 // Hash together unless both are zero
233 if !(item == &Self::ZERO && current == Self::ZERO) {
234 current = hasher.hash_pair(&[*item, current]);
235 }
236
237 level += 1;
238 }
239 // SAFETY: Level is limited by the number of leaves, which in turn is checked above to
240 // fit into 2^BITS, while `BITS` generic in turn ensured sufficient stack size
241 *unsafe { stack.get_unchecked_mut(level as usize) } = current;
242
243 // Wrapping is needed for `BITS == u128::BITS`, where number of leaves narrowly
244 // doesn't fit into `u128` itself
245 num_leaves = num_leaves.wrapping_add(chunk_size);
246 skip_count -= chunk_size;
247 }
248
249 Some(num_leaves)
250 }
251
252 /// Verify previously generated proof.
253 ///
254 /// Leaf can either be leaf value for a leaf that is occupied or all zeroes for a leaf that is
255 /// supposed to be empty.
256 #[inline]
257 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
258 pub fn verify(
259 root: &[u8; H::OUT_LEN],
260 proof: &[[u8; H::OUT_LEN]; PROOF_ELEMENTS::<BITS>],
261 leaf_index: u128,
262 leaf: [u8; H::OUT_LEN],
263 ) -> bool {
264 // TODO: Technically redundant with the where-clause, which is not enforced in generic code
265 // due to https://github.com/rust-lang/rust/issues/164019, remove once fixed
266 const { ENSURE_SUPPORTED_BITS::<BITS> };
267
268 // For `BITS == u128::BITS` any index is valid by definition
269 if u32::from(BITS) < u128::BITS && leaf_index >= 2u128.pow(u32::from(BITS)) {
270 return false;
271 }
272
273 let hasher = H::default();
274 let mut computed_root = leaf;
275
276 let mut position = leaf_index;
277 for hash in proof {
278 computed_root = if position.is_multiple_of(2) {
279 hasher.hash_pair(&[computed_root, *hash])
280 } else {
281 hasher.hash_pair(&[*hash, computed_root])
282 };
283
284 position /= 2;
285 }
286
287 root == &computed_root
288 }
289}