ab_merkle_tree/
unbalanced.rs1use crate::hash_pair;
2use ab_blake3::OUT_LEN;
3#[cfg(feature = "alloc")]
4use alloc::boxed::Box;
5#[cfg(feature = "alloc")]
6use alloc::vec::Vec;
7use core::mem::MaybeUninit;
8
9pub const PROOF_ELEMENTS<const MAX_N: u64>: usize = MAX_N.next_power_of_two().ilog2() as usize;
11const STACK_SIZE<const MAX_N: u64>: usize = MAX_N.next_power_of_two().ilog2() as usize + 1;
12const SUPPORTED_LEAVES_ARRAY_SIZE<const N: usize>: usize = {
13 assert!(N != 0, "Number of leaves must be greater than 0");
14 0
15};
16const USIZE_TO_U64<const N: usize>: u64 = N as u64;
17
18#[derive(Debug)]
37pub struct UnbalancedMerkleTree;
38
39impl UnbalancedMerkleTree {
45 #[inline]
53 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
54 pub fn compute_root_only<'a, const MAX_N: u64, Item, Iter>(
55 leaves: Iter,
56 ) -> Option<[u8; OUT_LEN]>
57 where
58 Item: Into<[u8; OUT_LEN]>,
59 Iter: IntoIterator<Item = Item> + 'a,
60 {
61 let mut stack = [[0u8; OUT_LEN]; STACK_SIZE::<MAX_N>];
63 let mut num_leaves = 0u64;
64
65 for hash in leaves {
66 if num_leaves >= MAX_N {
69 return None;
70 }
71
72 let mut current = hash.into();
73
74 let lowest_active_levels = num_leaves.trailing_ones() as usize;
76 for item in stack.iter().take(lowest_active_levels) {
77 current = hash_pair(item, ¤t);
78 }
79
80 stack[lowest_active_levels] = current;
82 num_leaves += 1;
83 }
84
85 if num_leaves == 0 {
86 return None;
88 }
89
90 let mut stack_bits = num_leaves;
91
92 {
93 let lowest_active_level = stack_bits.trailing_zeros() as usize;
94 stack[0] = *unsafe { stack.get_unchecked(lowest_active_level) };
97 stack_bits &= !(1 << lowest_active_level);
99 }
100
101 loop {
103 let lowest_active_level = stack_bits.trailing_zeros() as usize;
104
105 if lowest_active_level == u64::BITS as usize {
106 break;
107 }
108
109 stack_bits &= !(1 << lowest_active_level);
111
112 let lowest_active_level_item = unsafe { stack.get_unchecked(lowest_active_level) };
114
115 stack[0] = hash_pair(lowest_active_level_item, &stack[0]);
116 }
117
118 Some(stack[0])
119 }
120
121 #[inline(always)]
126 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
127 pub fn compute_root_only_array<const N: usize, Item>(leaves: &[Item; N]) -> [u8; OUT_LEN]
128 where
129 Item: Into<[u8; OUT_LEN]> + Copy,
130 [(); SUPPORTED_LEAVES_ARRAY_SIZE::<N>]:,
131 {
132 let maybe_root =
133 Self::compute_root_only::<{ USIZE_TO_U64::<N> }, _, _>(leaves.iter().copied());
134 unsafe { maybe_root.unwrap_unchecked() }
136 }
137
138 #[inline]
145 #[cfg(feature = "alloc")]
146 pub fn compute_root_and_proof<'a, const MAX_N: u64, Item, Iter>(
147 leaves: Iter,
148 target_index: usize,
149 ) -> Option<([u8; OUT_LEN], Vec<[u8; OUT_LEN]>)>
150 where
151 Item: Into<[u8; OUT_LEN]>,
152 Iter: IntoIterator<Item = Item> + 'a,
153 {
154 let mut stack = [[0u8; OUT_LEN]; _];
156 let mut proof =
158 unsafe { Box::<[MaybeUninit<[u8; OUT_LEN]>; _]>::new_uninit().assume_init() };
159
160 let (root, proof_length) = Self::compute_root_and_proof_inner::<MAX_N, _, _>(
161 leaves,
162 target_index,
163 &mut stack,
164 &mut proof,
165 )?;
166
167 let proof_capacity = proof.len();
168 let proof = Box::into_raw(proof);
169 let proof = unsafe {
172 Vec::from_raw_parts(proof.cast::<[u8; OUT_LEN]>(), proof_length, proof_capacity)
173 };
174
175 Some((root, proof))
176 }
177
178 #[inline]
185 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
186 pub fn compute_root_and_proof_in<'a, 'proof, const MAX_N: u64, Item, Iter>(
187 leaves: Iter,
188 target_index: usize,
189 proof: &'proof mut [MaybeUninit<[u8; OUT_LEN]>; PROOF_ELEMENTS::<MAX_N>],
190 ) -> Option<([u8; OUT_LEN], &'proof mut [[u8; OUT_LEN]])>
191 where
192 Item: Into<[u8; OUT_LEN]>,
193 Iter: IntoIterator<Item = Item> + 'a,
194 {
195 let mut stack = [[0u8; OUT_LEN]; _];
197
198 let (root, proof_length) = Self::compute_root_and_proof_inner::<MAX_N, _, _>(
199 leaves,
200 target_index,
201 &mut stack,
202 proof,
203 )?;
204 let proof = unsafe { proof.get_unchecked_mut(..proof_length).assume_init_mut() };
206
207 Some((root, proof))
208 }
209
210 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
211 fn compute_root_and_proof_inner<'a, const MAX_N: u64, Item, Iter>(
212 leaves: Iter,
213 target_index: usize,
214 stack: &mut [[u8; OUT_LEN]; STACK_SIZE::<MAX_N>],
215 proof: &mut [MaybeUninit<[u8; OUT_LEN]>; PROOF_ELEMENTS::<MAX_N>],
216 ) -> Option<([u8; OUT_LEN], usize)>
217 where
218 Item: Into<[u8; OUT_LEN]>,
219 Iter: IntoIterator<Item = Item> + 'a,
220 {
221 let mut proof_length = 0;
222 let mut num_leaves = 0u64;
223
224 let mut current_target_level = None;
225 let mut position = target_index;
226
227 for (current_index, hash) in leaves.into_iter().enumerate() {
228 if num_leaves >= MAX_N {
231 return None;
232 }
233
234 let mut current = hash.into();
235
236 let lowest_active_levels = num_leaves.trailing_ones() as usize;
238
239 if current_index == target_index {
240 for item in stack.iter().take(lowest_active_levels) {
241 unsafe { proof.get_unchecked_mut(proof_length) }.write(*item);
244 proof_length += 1;
245
246 current = hash_pair(item, ¤t);
247
248 position /= 2;
250 }
251
252 current_target_level = Some(lowest_active_levels);
253 } else {
254 for (level, item) in stack.iter().enumerate().take(lowest_active_levels) {
255 if current_target_level == Some(level) {
256 unsafe { proof.get_unchecked_mut(proof_length) }.write(
258 if position.is_multiple_of(2) {
259 current
260 } else {
261 *item
262 },
263 );
264 proof_length += 1;
265
266 current_target_level = Some(level + 1);
267
268 position /= 2;
270 }
271
272 current = hash_pair(item, ¤t);
273 }
274 }
275
276 stack[lowest_active_levels] = current;
278 num_leaves += 1;
279 }
280
281 if target_index >= num_leaves as usize {
283 return None;
285 }
286
287 let Some(current_target_level) = current_target_level else {
288 return None;
290 };
291
292 let mut stack_bits = num_leaves;
293
294 {
295 let lowest_active_level = stack_bits.trailing_zeros() as usize;
296 stack[0] = *unsafe { stack.get_unchecked(lowest_active_level) };
299 stack_bits &= !(1 << lowest_active_level);
301 }
302
303 let mut merged_peaks = false;
306 loop {
307 let lowest_active_level = stack_bits.trailing_zeros() as usize;
308
309 if lowest_active_level == u64::BITS as usize {
310 break;
311 }
312
313 stack_bits &= !(1 << lowest_active_level);
315
316 let lowest_active_level_item = unsafe { stack.get_unchecked(lowest_active_level) };
318
319 if lowest_active_level > current_target_level
320 || (lowest_active_level == current_target_level
321 && !position.is_multiple_of(2)
322 && !merged_peaks)
323 {
324 unsafe { proof.get_unchecked_mut(proof_length) }.write(*lowest_active_level_item);
326 proof_length += 1;
327 merged_peaks = false;
328 } else if lowest_active_level == current_target_level {
329 unsafe { proof.get_unchecked_mut(proof_length) }.write(stack[0]);
331 proof_length += 1;
332 merged_peaks = false;
333 } else {
334 merged_peaks = true;
336 }
337
338 stack[0] = hash_pair(lowest_active_level_item, &stack[0]);
340
341 position /= 2;
342 }
343
344 Some((stack[0], proof_length))
345 }
346
347 #[inline]
349 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
350 pub fn verify(
351 root: &[u8; OUT_LEN],
352 proof: &[[u8; OUT_LEN]],
353 leaf_index: u64,
354 leaf: [u8; OUT_LEN],
355 num_leaves: u64,
356 ) -> bool {
357 if leaf_index >= num_leaves {
358 return false;
359 }
360
361 let mut current = leaf;
362 let mut position = leaf_index;
363 let mut proof_pos = 0;
364 let mut level_size = num_leaves;
365
366 while level_size > 1 {
368 let is_left = position.is_multiple_of(2);
369 let is_last = position == level_size - 1;
370
371 if is_left && !is_last {
372 if proof_pos >= proof.len() {
374 return false;
376 }
377 current = hash_pair(¤t, &proof[proof_pos]);
378 proof_pos += 1;
379 } else if !is_left {
380 if proof_pos >= proof.len() {
382 return false;
384 }
385 current = hash_pair(&proof[proof_pos], ¤t);
386 proof_pos += 1;
387 } else {
388 }
390
391 position /= 2;
392 level_size = level_size.div_ceil(2);
394 }
395
396 proof_pos == proof.len() && current == *root
398 }
399}