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 core::iter::TrustedLen;
6use core::mem;
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]],
61 tree: [[u8; OUT_LEN]; TREE_SIZE_WITHOUT_LEAVES::<N>],
63}
64
65impl<'a, const N: usize> BalancedMerkleTree<'a, N> {
68 #[cfg_attr(
75 all(feature = "no-panic", not(target_arch = "riscv64")),
76 no_panic::no_panic
77 )]
78 pub fn new(leaves: &'a [[u8; OUT_LEN]; N]) -> Self {
79 let mut tree = [MaybeUninit::<[u8; OUT_LEN]>::uninit(); _];
80
81 Self::init_internal(leaves, &mut tree);
82
83 Self {
84 leaves,
85 tree: unsafe { tree.transpose().assume_init() },
87 }
88 }
89
90 #[cfg_attr(
94 all(feature = "no-panic", not(target_arch = "riscv64")),
95 no_panic::no_panic
96 )]
97 pub fn new_in<'b>(
98 instance: &'b mut MaybeUninit<Self>,
99 leaves: &'a [[u8; OUT_LEN]; N],
100 ) -> &'b mut Self {
101 let instance_ptr = instance.as_mut_ptr();
102 unsafe {
104 (&raw mut (*instance_ptr).leaves).write(leaves);
105 }
106 let tree = {
107 let tree_ptr = unsafe { &raw mut (*instance_ptr).tree };
109 unsafe {
111 tree_ptr
112 .cast::<[MaybeUninit<[u8; OUT_LEN]>; TREE_SIZE_WITHOUT_LEAVES::<N>]>()
113 .as_mut_unchecked()
114 }
115 };
116
117 Self::init_internal(leaves, tree);
118
119 unsafe { instance.assume_init_mut() }
121 }
122
123 #[cfg(feature = "alloc")]
126 pub fn new_boxed(leaves: &'a [[u8; OUT_LEN]; N]) -> Box<Self> {
127 let mut instance = Box::<Self>::new_uninit();
128
129 Self::new_in(&mut instance, leaves);
130
131 unsafe { instance.assume_init() }
133 }
134
135 #[cfg_attr(
138 all(feature = "no-panic", not(target_arch = "riscv64")),
139 no_panic::no_panic
140 )]
141 fn init_internal(
142 leaves: &[[u8; OUT_LEN]; N],
143 tree: &mut [MaybeUninit<[u8; OUT_LEN]>; TREE_SIZE_WITHOUT_LEAVES::<N>],
144 ) {
145 let mut tree_hashes = tree.as_mut_slice();
146 let mut level_hashes = leaves.as_slice();
147
148 while level_hashes.len() > 1 {
149 let num_pairs = level_hashes.len() / 2;
150 let parent_hashes;
151 (parent_hashes, tree_hashes) = unsafe { tree_hashes.split_at_mut_unchecked(num_pairs) };
154
155 if parent_hashes.len().is_multiple_of(BATCH_HASH_NUM_BLOCKS) {
156 let parent_hashes_chunks =
158 unsafe { parent_hashes.as_chunks_unchecked_mut::<BATCH_HASH_NUM_BLOCKS>() };
159 for (pairs, hashes) in level_hashes
160 .as_chunks::<BATCH_HASH_NUM_LEAVES>()
161 .0
162 .iter()
163 .zip(parent_hashes_chunks)
164 {
165 let hashes = unsafe {
169 mem::transmute::<
170 &mut [MaybeUninit<[u8; OUT_LEN]>; BATCH_HASH_NUM_BLOCKS],
171 &mut MaybeUninit<[[u8; OUT_LEN]; BATCH_HASH_NUM_BLOCKS]>,
172 >(hashes)
173 };
174
175 hashes.write(hash_pairs(pairs));
178 }
179 } else {
180 for (pair, parent_hash) in level_hashes
181 .as_chunks()
182 .0
183 .iter()
184 .zip(parent_hashes.iter_mut())
185 {
186 let pair = unsafe {
188 mem::transmute::<&[[u8; OUT_LEN]; BLOCK_LEN / OUT_LEN], &[u8; BLOCK_LEN]>(
189 pair,
190 )
191 };
192 parent_hash.write(hash_pair_block(pair));
193 }
194 }
195
196 level_hashes = unsafe { parent_hashes.assume_init_ref() };
198 }
199 }
200
201 #[inline]
207 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
208 pub fn compute_root_only(leaves: &[[u8; OUT_LEN]; N]) -> [u8; OUT_LEN] {
209 match N {
211 2 => {
212 let [root] = hash_pairs(leaves);
213
214 return root;
215 }
216 4 => {
217 let hashes = hash_pairs::<2, _>(leaves);
218 let [root] = hash_pairs(&hashes);
219
220 return root;
221 }
222 8 => {
223 let hashes = hash_pairs::<4, _>(leaves);
224 let hashes = hash_pairs::<2, _>(&hashes);
225 let [root] = hash_pairs(&hashes);
226
227 return root;
228 }
229 16 => {
230 let hashes = hash_pairs::<8, _>(leaves);
231 let hashes = hash_pairs::<4, _>(&hashes);
232 let hashes = hash_pairs::<2, _>(&hashes);
233 let [root] = hash_pairs(&hashes);
234
235 return root;
236 }
237 _ => {
238 assert!(N >= BATCH_HASH_NUM_LEAVES);
240 }
241 }
242
243 let mut stack = [[[0u8; OUT_LEN]; BATCH_HASH_NUM_BLOCKS]; ROOT_ONLY_LARGE_STACK_SIZE::<N>];
247
248 let mut parent_current = [[0u8; OUT_LEN]; BATCH_HASH_NUM_LEAVES];
251 for (num_chunks, chunk_leaves) in leaves
252 .as_chunks::<BATCH_HASH_NUM_LEAVES>()
253 .0
254 .iter()
255 .enumerate()
256 {
257 let current_half = &mut parent_current[BATCH_HASH_NUM_BLOCKS..];
258
259 let current = hash_pairs::<BATCH_HASH_NUM_BLOCKS, _>(chunk_leaves);
260 current_half.copy_from_slice(¤t);
261
262 let lowest_active_levels = num_chunks.trailing_ones() as usize;
264 for parent in &mut stack[..lowest_active_levels] {
265 let parent_half = &mut parent_current[..BATCH_HASH_NUM_BLOCKS];
266 parent_half.copy_from_slice(parent);
267
268 let current = hash_pairs::<BATCH_HASH_NUM_BLOCKS, _>(&parent_current);
269
270 let current_half = &mut parent_current[BATCH_HASH_NUM_BLOCKS..];
271 current_half.copy_from_slice(¤t);
272 }
273
274 let current_half = &mut parent_current[BATCH_HASH_NUM_BLOCKS..];
275
276 stack[lowest_active_levels].copy_from_slice(current_half);
278 }
279
280 let hashes = &stack[ROOT_ONLY_LARGE_STACK_SIZE::<N> - 1];
281 let hashes = hash_pairs::<{ BATCH_HASH_NUM_BLOCKS / 2 }, _>(hashes);
282 let hashes = hash_pairs::<{ BATCH_HASH_NUM_BLOCKS / 4 }, _>(&hashes);
283 let hashes = hash_pairs::<{ BATCH_HASH_NUM_BLOCKS / 8 }, _>(&hashes);
284 let [root] = hash_pairs::<{ BATCH_HASH_NUM_BLOCKS / 16 }, _>(&hashes);
285
286 root
287 }
288
289 #[inline]
291 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
292 pub fn root(&self) -> [u8; OUT_LEN] {
293 *self
294 .tree
295 .last()
296 .or(self.leaves.last())
297 .expect("There is always at least one leaf hash; qed")
298 }
299
300 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
302 pub fn all_proofs(&self) -> ProofsIterator<'_, N> {
303 ProofsIterator {
304 leaves: self.leaves,
305 tree: &self.tree,
306 leaf_index: 0,
307 len: N,
308 }
309 }
310
311 #[inline]
313 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
314 pub fn verify(
315 root: &[u8; OUT_LEN],
316 proof: &[[u8; OUT_LEN]; PROOF_ELEMENTS::<N>],
317 leaf_index: usize,
318 leaf: [u8; OUT_LEN],
319 ) -> bool {
320 if leaf_index >= N {
321 return false;
322 }
323
324 let mut computed_root = leaf;
325
326 let mut position = leaf_index;
327 for hash in proof {
328 computed_root = if position.is_multiple_of(2) {
329 hash_pair(&computed_root, hash)
330 } else {
331 hash_pair(hash, &computed_root)
332 };
333
334 position /= 2;
335 }
336
337 root == &computed_root
338 }
339}
340
341#[derive(Debug)]
343pub struct ProofsIterator<'a, const N: usize> {
344 leaves: &'a [[u8; OUT_LEN]],
345 tree: &'a [[u8; OUT_LEN]; TREE_SIZE_WITHOUT_LEAVES::<N>],
346 leaf_index: usize,
347 len: usize,
348}
349
350impl<'a, const N: usize> Iterator for ProofsIterator<'a, N> {
351 type Item = [[u8; OUT_LEN]; PROOF_ELEMENTS::<N>];
352
353 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
354 fn next(&mut self) -> Option<Self::Item> {
355 if self.len == 0 {
356 return None;
357 }
358 self.len -= 1;
359
360 let index = self.leaf_index;
361 self.leaf_index += 1;
362
363 let sibling_index = index ^ 1;
370 let sibling_hash = *unsafe { self.leaves.get_unchecked(sibling_index) };
372
373 let mut proof = [MaybeUninit::<[u8; OUT_LEN]>::uninit(); _];
374 proof[0].write(sibling_hash);
375
376 let shared_proof = &mut proof[1..];
378
379 let mut tree_hashes = self.tree.as_slice();
380 let mut parent_position = index / 2;
381 let mut parent_level_size = N / 2;
382
383 for hash in shared_proof {
384 let parent_other_position = parent_position ^ 1;
385
386 let other_hash = unsafe { tree_hashes.get_unchecked(parent_other_position) };
388 hash.write(*other_hash);
389 tree_hashes = &tree_hashes[parent_level_size..];
390
391 parent_position /= 2;
392 parent_level_size /= 2;
393 }
394
395 Some(unsafe { proof.transpose().assume_init() })
397 }
398
399 #[inline(always)]
400 fn size_hint(&self) -> (usize, Option<usize>) {
401 (self.len, Some(self.len))
402 }
403
404 #[inline(always)]
405 fn count(self) -> usize {
406 self.len
407 }
408
409 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
410 fn last(mut self) -> Option<Self::Item> {
411 if self.len == 0 {
412 return None;
413 }
414 self.leaf_index = N - 1;
415 self.len = 1;
416 self.next()
417 }
418
419 #[inline(always)]
420 fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
421 let advance = n.min(self.len);
422 self.leaf_index += advance;
423 self.len -= advance;
424 NonZero::new(n - advance).map_or(Ok(()), Err)
425 }
426
427 #[inline(always)]
428 fn nth(&mut self, n: usize) -> Option<Self::Item> {
429 match self.advance_by(n) {
430 Ok(()) => self.next(),
431 Err(_) => None,
432 }
433 }
434}
435
436impl<'a, const N: usize> ExactSizeIterator for ProofsIterator<'a, N> {
437 #[inline(always)]
438 fn len(&self) -> usize {
439 self.len
440 }
441}
442
443unsafe impl<'a, const N: usize> TrustedLen for ProofsIterator<'a, N> {}