Skip to main content

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}