ab_merkle_tree/
balanced.rs1use crate::{hash_pair, hash_pair_block, hash_pairs};
2use ab_blake3::{BLOCK_LEN, OUT_LEN};
3#[cfg(feature = "alloc")]
4use alloc::boxed::Box;
5use array_reshape::Flatten;
6use core::iter::TrustedLen;
7use core::mem::MaybeUninit;
8use core::num::NonZero;
9
10const BATCH_HASH_NUM_BLOCKS: usize = 16;
12const BATCH_HASH_NUM_LEAVES: usize = BATCH_HASH_NUM_BLOCKS * BLOCK_LEN / OUT_LEN;
14
15const TREE_SIZE_WITHOUT_LEAVES<const N: usize>: usize = {
16 assert!(
18 N.is_power_of_two(),
19 "Balanced Merkle Tree must have a number of leaves that is a power of 2"
20 );
21
22 assert!(
23 N > 1,
24 "This Balanced Merkle Tree must have more than one leaf"
25 );
26
27 N - 1
28};
29pub const PROOF_ELEMENTS<const N: usize>: usize = N.ilog2() as usize;
31const ROOT_ONLY_LARGE_STACK_SIZE<const N: usize>: usize = {
32 if N < BATCH_HASH_NUM_LEAVES {
35 1
36 } else {
37 (N / BATCH_HASH_NUM_LEAVES).ilog2() as usize + 1
38 }
39};
40
41#[derive(Debug)]
59pub struct BalancedMerkleTree<'a, const N: usize> {
60 leaves: &'a [[u8; OUT_LEN]; N],
61 tree: [[u8; OUT_LEN]; TREE_SIZE_WITHOUT_LEAVES::<N>],
63}
64
65impl<'a, const N: usize> BalancedMerkleTree<'a, N>
68where
69 [(); TREE_SIZE_WITHOUT_LEAVES::<N>]:,
70{
71 #[cfg_attr(
78 all(feature = "no-panic", not(target_arch = "riscv64")),
79 no_panic::no_panic
80 )]
81 pub fn new(leaves: &'a [[u8; OUT_LEN]; N]) -> Self {
82 const { TREE_SIZE_WITHOUT_LEAVES::<N> };
85
86 let mut tree = [MaybeUninit::<[u8; OUT_LEN]>::uninit(); _];
87
88 Self::init_internal(leaves, &mut tree);
89
90 Self {
91 leaves,
92 tree: unsafe { tree.transpose().assume_init() },
94 }
95 }
96
97 #[cfg_attr(
101 all(feature = "no-panic", not(target_arch = "riscv64")),
102 no_panic::no_panic
103 )]
104 pub fn new_in<'b>(
105 instance: &'b mut MaybeUninit<Self>,
106 leaves: &'a [[u8; OUT_LEN]; N],
107 ) -> &'b mut Self {
108 const { TREE_SIZE_WITHOUT_LEAVES::<N> };
111
112 let instance_ptr = instance.as_mut_ptr();
113 unsafe {
115 (&raw mut (*instance_ptr).leaves).write(leaves);
116 }
117 let tree = {
118 let tree_ptr = unsafe { &raw mut (*instance_ptr).tree };
120 unsafe {
122 tree_ptr
123 .cast::<[MaybeUninit<[u8; OUT_LEN]>; TREE_SIZE_WITHOUT_LEAVES::<N>]>()
124 .as_mut_unchecked()
125 }
126 };
127
128 Self::init_internal(leaves, tree);
129
130 unsafe { instance.assume_init_mut() }
132 }
133
134 #[cfg(feature = "alloc")]
137 pub fn new_boxed(leaves: &'a [[u8; OUT_LEN]; N]) -> Box<Self> {
138 let mut instance = Box::<Self>::new_uninit();
139
140 Self::new_in(&mut instance, leaves);
141
142 unsafe { instance.assume_init() }
144 }
145
146 #[cfg_attr(
149 all(feature = "no-panic", not(target_arch = "riscv64")),
150 no_panic::no_panic
151 )]
152 fn init_internal(
153 leaves: &[[u8; OUT_LEN]; N],
154 tree: &mut [MaybeUninit<[u8; OUT_LEN]>; TREE_SIZE_WITHOUT_LEAVES::<N>],
155 ) {
156 let mut tree_hashes = tree.as_mut_slice();
157 let mut level_hashes = leaves.as_slice();
158
159 while level_hashes.len() > 1 {
160 let num_pairs = level_hashes.len() / 2;
161 let parent_hashes;
162 (parent_hashes, tree_hashes) = unsafe { tree_hashes.split_at_mut_unchecked(num_pairs) };
165
166 let (parent_hashes_chunks, parent_hashes_remainder) =
167 parent_hashes.as_chunks_mut::<BATCH_HASH_NUM_BLOCKS>();
168 if parent_hashes_remainder.is_empty() {
169 for (pairs, hashes) in level_hashes
170 .as_chunks::<BATCH_HASH_NUM_LEAVES>()
171 .0
172 .iter()
173 .zip(parent_hashes_chunks)
174 {
175 *hashes = MaybeUninit::new(hash_pairs(pairs)).into();
178 }
179 } else {
180 for (pair, parent_hash) in level_hashes
181 .as_chunks::<{ BLOCK_LEN / OUT_LEN }>()
182 .0
183 .iter()
184 .zip(parent_hashes.iter_mut())
185 {
186 parent_hash.write(hash_pair_block(pair.flatten_ref()));
187 }
188 }
189
190 level_hashes = unsafe { parent_hashes.assume_init_ref() };
192 }
193 }
194
195 #[inline]
201 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
202 pub fn compute_root_only(leaves: &[[u8; OUT_LEN]; N]) -> [u8; OUT_LEN] {
203 const { TREE_SIZE_WITHOUT_LEAVES::<N> };
206
207 match N {
209 2 => {
210 let [root] = hash_pairs(leaves);
211
212 return root;
213 }
214 4 => {
215 let hashes = hash_pairs::<2, _>(leaves);
216 let [root] = hash_pairs(&hashes);
217
218 return root;
219 }
220 8 => {
221 let hashes = hash_pairs::<4, _>(leaves);
222 let hashes = hash_pairs::<2, _>(&hashes);
223 let [root] = hash_pairs(&hashes);
224
225 return root;
226 }
227 16 => {
228 let hashes = hash_pairs::<8, _>(leaves);
229 let hashes = hash_pairs::<4, _>(&hashes);
230 let hashes = hash_pairs::<2, _>(&hashes);
231 let [root] = hash_pairs(&hashes);
232
233 return root;
234 }
235 _ => {
236 assert!(N >= BATCH_HASH_NUM_LEAVES);
238 }
239 }
240
241 let mut stack = [[[0u8; OUT_LEN]; BATCH_HASH_NUM_BLOCKS]; ROOT_ONLY_LARGE_STACK_SIZE::<N>];
245
246 let mut parent_current = [[0u8; OUT_LEN]; BATCH_HASH_NUM_LEAVES];
249 for (num_chunks, chunk_leaves) in leaves
250 .as_chunks::<BATCH_HASH_NUM_LEAVES>()
251 .0
252 .iter()
253 .enumerate()
254 {
255 let current_half = &mut parent_current[BATCH_HASH_NUM_BLOCKS..];
256
257 let current = hash_pairs::<BATCH_HASH_NUM_BLOCKS, _>(chunk_leaves);
258 current_half.copy_from_slice(¤t);
259
260 let lowest_active_levels = num_chunks.trailing_ones() as usize;
262 for parent in &mut stack[..lowest_active_levels] {
263 let parent_half = &mut parent_current[..BATCH_HASH_NUM_BLOCKS];
264 parent_half.copy_from_slice(parent);
265
266 let current = hash_pairs::<BATCH_HASH_NUM_BLOCKS, _>(&parent_current);
267
268 let current_half = &mut parent_current[BATCH_HASH_NUM_BLOCKS..];
269 current_half.copy_from_slice(¤t);
270 }
271
272 let current_half = &mut parent_current[BATCH_HASH_NUM_BLOCKS..];
273
274 stack[lowest_active_levels].copy_from_slice(current_half);
276 }
277
278 let hashes = &stack[ROOT_ONLY_LARGE_STACK_SIZE::<N> - 1];
279 let hashes = hash_pairs::<{ BATCH_HASH_NUM_BLOCKS / 2 }, _>(hashes);
280 let hashes = hash_pairs::<{ BATCH_HASH_NUM_BLOCKS / 4 }, _>(&hashes);
281 let hashes = hash_pairs::<{ BATCH_HASH_NUM_BLOCKS / 8 }, _>(&hashes);
282 let [root] = hash_pairs::<{ BATCH_HASH_NUM_BLOCKS / 16 }, _>(&hashes);
283
284 root
285 }
286
287 #[inline]
289 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
290 pub fn root(&self) -> [u8; OUT_LEN] {
291 *self
292 .tree
293 .last()
294 .or(self.leaves.last())
295 .expect("There is always at least one leaf hash; qed")
296 }
297
298 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
300 pub fn all_proofs(&self) -> ProofsIterator<'_, N> {
301 ProofsIterator {
302 leaves: self.leaves,
303 tree: &self.tree,
304 leaf_index: 0,
305 }
306 }
307
308 #[inline]
310 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
311 pub fn verify(
312 root: &[u8; OUT_LEN],
313 proof: &[[u8; OUT_LEN]; PROOF_ELEMENTS::<N>],
314 leaf_index: usize,
315 leaf: [u8; OUT_LEN],
316 ) -> bool {
317 const { TREE_SIZE_WITHOUT_LEAVES::<N> };
320
321 if leaf_index >= N {
322 return false;
323 }
324
325 let mut computed_root = leaf;
326
327 let mut position = leaf_index;
328 for hash in proof {
329 computed_root = if position.is_multiple_of(2) {
330 hash_pair(&computed_root, hash)
331 } else {
332 hash_pair(hash, &computed_root)
333 };
334
335 position /= 2;
336 }
337
338 root == &computed_root
339 }
340}
341
342#[derive(Debug)]
344pub struct ProofsIterator<'a, const N: usize> {
345 leaves: &'a [[u8; OUT_LEN]; N],
346 tree: &'a [[u8; OUT_LEN]; TREE_SIZE_WITHOUT_LEAVES::<N>],
347 leaf_index: usize,
349}
350
351impl<const N: usize> ProofsIterator<'_, N> {
352 #[inline(always)]
353 fn remaining(&self) -> usize {
354 N - self.leaf_index
355 }
356}
357
358impl<const N: usize> Iterator for ProofsIterator<'_, N> {
359 type Item = [[u8; OUT_LEN]; PROOF_ELEMENTS::<N>];
360
361 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
362 fn next(&mut self) -> Option<Self::Item> {
363 let index = self.leaf_index;
364
365 let sibling_index = index ^ 1;
372 let sibling_hash = *self.leaves.get(sibling_index)?;
375 self.leaf_index += 1;
376
377 let mut proof = [MaybeUninit::<[u8; OUT_LEN]>::uninit(); _];
378 proof[0].write(sibling_hash);
379
380 let shared_proof = &mut proof[1..];
382
383 let mut tree_hashes = self.tree.as_slice();
384 let mut parent_position = index / 2;
385 let mut parent_level_size = N / 2;
386
387 for hash in shared_proof {
388 let parent_other_position = parent_position ^ 1;
389
390 let other_hash = tree_hashes.get(parent_other_position).expect(
391 "`parent_position` is within the current level of the tree, whose size is an \
392 even number; qed",
393 );
394 hash.write(*other_hash);
395 tree_hashes = &tree_hashes[parent_level_size..];
396
397 parent_position /= 2;
398 parent_level_size /= 2;
399 }
400
401 Some(unsafe { proof.transpose().assume_init() })
403 }
404
405 #[inline(always)]
406 fn size_hint(&self) -> (usize, Option<usize>) {
407 let remaining = self.remaining();
408 (remaining, Some(remaining))
409 }
410
411 #[inline(always)]
412 fn count(self) -> usize {
413 self.remaining()
414 }
415
416 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
417 fn last(mut self) -> Option<Self::Item> {
418 if self.leaf_index >= N {
419 return None;
420 }
421 self.leaf_index = N - 1;
422 self.next()
423 }
424
425 #[inline(always)]
426 fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
427 let advance = n.min(self.remaining());
428 self.leaf_index += advance;
429 NonZero::new(n - advance).map_or(Ok(()), Err)
430 }
431
432 #[inline(always)]
433 fn nth(&mut self, n: usize) -> Option<Self::Item> {
434 match self.advance_by(n) {
435 Ok(()) => self.next(),
436 Err(_) => None,
437 }
438 }
439}
440
441impl<const N: usize> ExactSizeIterator for ProofsIterator<'_, N> {
442 #[inline(always)]
443 fn len(&self) -> usize {
444 self.remaining()
445 }
446}
447
448unsafe impl<const N: usize> TrustedLen for ProofsIterator<'_, N> {}