1use std::collections::HashMap;
2use std::collections::HashSet;
3
4use itertools::Itertools;
5use num_traits::One;
6
7use crate::data_type::DataType;
8use crate::twenty_first::bfe;
9use crate::twenty_first::prelude::*;
10use crate::twenty_first::util_types::mmr::mmr_accumulator::MmrAccumulator;
11use crate::twenty_first::util_types::mmr::shared_advanced::get_peak_heights;
12use crate::twenty_first::util_types::mmr::shared_basic::leaf_index_to_mt_index_and_peak_index;
13
14const ROOT_MT_INDEX: u64 = 1;
15
16pub(super) fn indexed_leaf_element_type() -> DataType {
17 DataType::Tuple(vec![DataType::U64, DataType::Digest])
18}
19
20#[derive(Debug, Clone)]
23pub struct AuthStructIntegrityProof {
24 pub nd_auth_struct_indices: Vec<u64>,
26 pub nd_sibling_indices: Vec<(u64, u64)>,
27 pub nd_siblings: Vec<(Digest, Digest)>,
28}
29
30#[derive(Debug, Clone)]
34pub struct AuthenticatedMerkleAuthStruct {
35 pub auth_struct: Vec<Digest>,
36 pub indexed_leafs: Vec<(u64, Digest)>,
37 pub witness: AuthStructIntegrityProof,
38}
39
40impl AuthStructIntegrityProof {
41 fn auth_struct_and_nd_indices(
46 num_leafs: u64,
47 leaf_indices: &[u64],
48 ) -> (Vec<u64>, Vec<(u64, u64)>) {
49 let mut node_is_needed = HashSet::new();
53
54 let mut node_can_be_computed = HashSet::new();
59
60 for &leaf_index in leaf_indices {
61 assert!(
62 num_leafs > leaf_index,
63 "Leaf index must be less than number of leafs. Got leaf_index = {leaf_index}; num_leafs = {num_leafs}"
64 );
65
66 let mut node_index = leaf_index + num_leafs;
67 while node_index > ROOT_MT_INDEX {
68 let sibling_index = node_index ^ 1;
69 node_can_be_computed.insert(node_index);
70 node_is_needed.insert(sibling_index);
71 node_index /= 2;
72 }
73 }
74
75 let set_difference = node_is_needed.difference(&node_can_be_computed).copied();
76 let set_union = node_is_needed
77 .union(&node_can_be_computed)
78 .sorted_unstable()
79 .rev();
80
81 let mut set_union = set_union.peekable();
82
83 let mut set_union_as_ordered_pairs = Vec::new();
84 while set_union.peek().is_some() {
85 let right_index = *set_union.next().unwrap();
86
87 let left_index = *set_union.next().unwrap();
90 set_union_as_ordered_pairs.push((left_index, right_index));
91 }
92
93 (
94 set_difference.sorted_unstable().rev().collect(),
95 set_union_as_ordered_pairs,
96 )
97 }
98
99 pub fn root_from_authentication_struct(
103 &self,
104 tree_height: u32,
105 auth_struct: Vec<Digest>,
106 indexed_leafs: Vec<(u64, Digest)>,
107 ) -> Digest {
108 fn digest_to_xfe(digest: Digest, challenge: XFieldElement) -> XFieldElement {
109 let leaf_xfe_lo = XFieldElement::new([digest.0[0], digest.0[1], digest.0[2]]);
110 let leaf_xfe_hi =
111 challenge * XFieldElement::new([digest.0[3], digest.0[4], BFieldElement::one()]);
112
113 leaf_xfe_lo + leaf_xfe_hi
114 }
115
116 fn node_index_to_bfe(node_index: u64) -> BFieldElement {
117 BFieldElement::new(node_index)
118 }
119
120 assert_eq!(
122 self.nd_auth_struct_indices.len(),
123 auth_struct.len(),
124 "Provided auth struct length must match that specified in receiver"
125 );
126
127 let (alpha, beta, gamma) = {
129 let mut sponge = Tip5::init();
130 sponge.pad_and_absorb_all(&indexed_leafs.encode());
131 sponge.pad_and_absorb_all(&auth_struct.encode());
132 let challenges = sponge.sample_scalars(3);
133 (challenges[0], challenges[1], challenges[2])
134 };
135
136 let mut p = XFieldElement::one();
138 for i in (0..indexed_leafs.len()).rev() {
139 let node_index_as_bfe = node_index_to_bfe((1 << tree_height) ^ indexed_leafs[i].0);
140 let leaf_as_xfe = digest_to_xfe(indexed_leafs[i].1, alpha);
141 let fact = leaf_as_xfe - beta + gamma * node_index_as_bfe;
142 p *= fact;
143 }
144
145 let mut prev = 0;
146 for i in (0..auth_struct.len()).rev() {
147 let auth_struct_index = self.nd_auth_struct_indices[i];
148
149 assert!(auth_struct_index > prev);
152 prev = auth_struct_index;
153
154 let auth_struct_index_as_bfe = node_index_to_bfe(auth_struct_index);
155
156 let auth_str_elem_as_xfe = digest_to_xfe(auth_struct[i], alpha);
157 let fact = auth_str_elem_as_xfe - beta + gamma * auth_struct_index_as_bfe;
158 p *= fact;
159 }
160
161 let mut t = indexed_leafs.first().unwrap().1;
163 let mut t_xfe = digest_to_xfe(t, alpha);
164 let mut parent_index_bfe = BFieldElement::one();
165 for ((l, r), (left_index, right_index)) in self
166 .nd_siblings
167 .iter()
168 .zip_eq(self.nd_sibling_indices.clone())
169 {
170 let left_index_bfe = node_index_to_bfe(left_index);
171 let right_index_bfe = node_index_to_bfe(right_index);
172 assert_eq!(left_index + 1, right_index);
173 parent_index_bfe = left_index_bfe / bfe!(2);
174
175 t = Tip5::hash_pair(*l, *r);
176
177 let l_xfe = digest_to_xfe(*l, alpha);
178 let r_xfe = digest_to_xfe(*r, alpha);
179 t_xfe = digest_to_xfe(t, alpha);
180
181 let fact_left = l_xfe - beta + gamma * left_index_bfe;
182 let fact_right = r_xfe - beta + gamma * right_index_bfe;
183 let fact_parent = t_xfe - beta + gamma * parent_index_bfe;
184
185 p *= fact_left.inverse() * fact_right.inverse() * fact_parent;
186 }
187
188 assert_eq!(t_xfe - beta + gamma, p);
189
190 assert!(parent_index_bfe.is_one());
191
192 t
193 }
194
195 pub fn new_from_mmr_membership_proofs(
204 mmra: &MmrAccumulator,
205 indexed_mmr_mps: Vec<(u64, Digest, MmrMembershipProof)>,
206 ) -> HashMap<u32, AuthenticatedMerkleAuthStruct> {
207 #[derive(Clone, Debug)]
208 struct IndexedAuthenticatedMmrLeaf {
209 merkle_tree_node_index: u64,
210 merkle_tree_leaf_index: u64,
211 leaf_digest: Digest,
212 membership_proof: MmrMembershipProof,
213 }
214
215 let num_mmr_leafs = mmra.num_leafs();
218 let mut peak_index_to_indexed_mmr_mp: HashMap<u32, Vec<IndexedAuthenticatedMmrLeaf>> =
219 HashMap::default();
220 let peak_heights = get_peak_heights(num_mmr_leafs);
221 for (mmr_leaf_index, leaf, mmr_mp) in indexed_mmr_mps {
222 let (mt_index, peak_index) =
223 leaf_index_to_mt_index_and_peak_index(mmr_leaf_index, num_mmr_leafs);
224 let peak_index_as_usize: usize = peak_index.try_into().unwrap();
225 let num_leafs_local_mt = 1 << peak_heights[peak_index_as_usize];
226 let mt_leaf_index = mt_index - num_leafs_local_mt;
227 peak_index_to_indexed_mmr_mp
228 .entry(peak_index)
229 .or_default()
230 .push(IndexedAuthenticatedMmrLeaf {
231 merkle_tree_node_index: mt_index,
232 merkle_tree_leaf_index: mt_leaf_index,
233 leaf_digest: leaf,
234 membership_proof: mmr_mp,
235 });
236 }
237
238 let mut peak_index_to_authenticated_auth_struct = HashMap::default();
241 for (peak_index, indexed_mmr_mp_structs) in peak_index_to_indexed_mmr_mp {
242 let peak_index_as_usize: usize = peak_index.try_into().unwrap();
243 let num_leafs_in_local_mt = 1 << peak_heights[peak_index_as_usize];
244 let local_mt_leaf_indices = indexed_mmr_mp_structs
245 .iter()
246 .map(|x| x.merkle_tree_leaf_index)
247 .collect_vec();
248
249 let (nd_auth_struct_indices, nd_sibling_indices) =
250 Self::auth_struct_and_nd_indices(num_leafs_in_local_mt, &local_mt_leaf_indices);
251 let peak = mmra.peaks()[peak_index_as_usize];
252
253 let mut node_digests: HashMap<u64, Digest> = HashMap::default();
254 node_digests.insert(ROOT_MT_INDEX, peak);
255
256 for indexed_mmr_mp in indexed_mmr_mp_structs.iter() {
258 let mut mt_node_index = indexed_mmr_mp.merkle_tree_node_index;
259 let mut node = indexed_mmr_mp.leaf_digest;
260
261 for ap_elem in indexed_mmr_mp.membership_proof.authentication_path.iter() {
263 node_digests.insert(mt_node_index, node);
264 node_digests.insert(mt_node_index ^ 1, *ap_elem);
265 node = if mt_node_index & 1 == 0 {
266 Tip5::hash_pair(node, *ap_elem)
267 } else {
268 Tip5::hash_pair(*ap_elem, node)
269 };
270
271 mt_node_index /= 2;
272 }
273
274 assert_eq!(peak, node, "Derived peak must match provided peak");
276 }
277 let nd_siblings = nd_sibling_indices
278 .iter()
279 .map(|(left_idx, right_idx)| (node_digests[left_idx], node_digests[right_idx]))
280 .collect_vec();
281 let auth_struct = nd_auth_struct_indices
282 .iter()
283 .map(|idx| node_digests[idx])
284 .collect_vec();
285 let indexed_leafs = indexed_mmr_mp_structs
286 .into_iter()
287 .map(|indexed_mmr_mp| {
288 (
289 indexed_mmr_mp.merkle_tree_leaf_index,
290 indexed_mmr_mp.leaf_digest,
291 )
292 })
293 .collect_vec();
294
295 let witness = Self {
296 nd_auth_struct_indices,
297 nd_sibling_indices,
298 nd_siblings,
299 };
300
301 peak_index_to_authenticated_auth_struct.insert(
302 peak_index,
303 AuthenticatedMerkleAuthStruct {
304 auth_struct,
305 indexed_leafs,
306 witness,
307 },
308 );
309 }
310
311 peak_index_to_authenticated_auth_struct
312 }
313
314 pub fn new_from_merkle_tree(
317 tree: &MerkleTree,
318 mut revealed_leaf_indices: Vec<u64>,
319 ) -> AuthenticatedMerkleAuthStruct {
320 revealed_leaf_indices.sort_unstable();
321 revealed_leaf_indices.dedup();
322 revealed_leaf_indices.reverse();
323 let num_leafs: u64 = tree.num_leafs() as u64;
324
325 let (nd_auth_struct_indices, nd_sibling_indices) =
326 Self::auth_struct_and_nd_indices(num_leafs, &revealed_leaf_indices);
327
328 let nd_siblings = nd_sibling_indices
329 .iter()
330 .map(|&(l, r)| {
331 let l: usize = l.try_into().unwrap();
332 let r: usize = r.try_into().unwrap();
333 (tree.node(l).unwrap(), tree.node(r).unwrap())
334 })
335 .collect_vec();
336
337 let revealed_leafs = revealed_leaf_indices
338 .iter()
339 .map(|j| tree.node((*j + num_leafs) as usize).unwrap())
340 .collect_vec();
341 let indexed_leafs = revealed_leaf_indices
342 .clone()
343 .into_iter()
344 .zip_eq(revealed_leafs)
345 .collect_vec();
346
347 let auth_struct = nd_auth_struct_indices
348 .iter()
349 .map(|node_index| tree.node(*node_index as usize).unwrap())
350 .collect_vec();
351
352 let witness = Self {
353 nd_auth_struct_indices,
354 nd_sibling_indices,
355 nd_siblings,
356 };
357
358 AuthenticatedMerkleAuthStruct {
359 auth_struct,
360 indexed_leafs,
361 witness,
362 }
363 }
364}
365
366#[cfg(test)]
367mod tests {
368 use proptest::collection::vec;
369 use proptest::prop_assert_eq;
370 use rand::random;
371 use test_strategy::proptest;
372
373 use super::*;
374 use crate::twenty_first::math::other::random_elements;
375 use crate::twenty_first::util_types::mmr::mmr_accumulator::util::mmra_with_mps;
376
377 #[proptest(cases = 20)]
378 fn root_from_authentication_struct_mmr_prop_test(
379 #[strategy(0..u64::MAX / 2)] mmr_leaf_count: u64,
380 #[strategy(0usize..20)] _num_revealed_leafs: usize,
381 #[strategy(vec(0u64..#mmr_leaf_count, #_num_revealed_leafs))]
382 mmr_revealed_leaf_indices: Vec<u64>,
383 ) {
384 let indexed_leafs_input: Vec<(u64, Digest)> = mmr_revealed_leaf_indices
385 .iter()
386 .map(|idx| (*idx, random()))
387 .collect_vec();
388 let (mmra, mmr_mps) = mmra_with_mps(mmr_leaf_count, indexed_leafs_input.clone());
389 let indexed_mmr_mps = mmr_mps
390 .into_iter()
391 .zip_eq(indexed_leafs_input)
392 .map(|(mmr_mp, (idx, leaf))| (idx, leaf, mmr_mp))
393 .collect_vec();
394
395 let authenticated_auth_structs =
396 AuthStructIntegrityProof::new_from_mmr_membership_proofs(&mmra, indexed_mmr_mps);
397
398 let peak_heights = get_peak_heights(mmr_leaf_count);
399 for (peak_index, authentication_auth_struct) in authenticated_auth_structs {
400 let AuthenticatedMerkleAuthStruct {
401 auth_struct,
402 indexed_leafs,
403 witness,
404 } = &authentication_auth_struct;
405 let tree_height: u32 = peak_heights[peak_index as usize];
406 let computed_root = witness.root_from_authentication_struct(
407 tree_height,
408 auth_struct.to_owned(),
409 indexed_leafs.to_owned(),
410 );
411
412 prop_assert_eq!(mmra.peaks()[peak_index as usize], computed_root);
413 }
414 }
415
416 #[test]
417 fn auth_struct_on_empty_mmr() {
418 let empty_mmra = MmrAccumulator::init(vec![], 0);
419 let authenticated_auth_structs =
420 AuthStructIntegrityProof::new_from_mmr_membership_proofs(&empty_mmra, vec![]);
421 assert!(authenticated_auth_structs.is_empty());
422 }
423
424 #[test]
425 fn auth_struct_non_empty_mmr_empty_leaf_list() {
426 let mmra_10_leafs = MmrAccumulator::new_from_leafs(vec![Digest::default(); 10]);
427 let authenticated_auth_structs =
428 AuthStructIntegrityProof::new_from_mmr_membership_proofs(&mmra_10_leafs, vec![]);
429 assert!(authenticated_auth_structs.is_empty());
430 }
431
432 #[should_panic]
433 #[test]
434 fn panics_on_missing_nd_digests() {
435 let tree_height = 3u32;
436 let num_leafs = 1u64 << tree_height;
437 let leafs: Vec<Digest> = random_elements(num_leafs.try_into().unwrap());
438 let tree = MerkleTree::par_new(&leafs).unwrap();
439
440 let authenticated_auth_struct =
441 AuthStructIntegrityProof::new_from_merkle_tree(&tree, vec![0]);
442 let AuthenticatedMerkleAuthStruct {
443 auth_struct,
444 indexed_leafs,
445 mut witness,
446 } = authenticated_auth_struct;
447 witness.nd_sibling_indices.clear();
448 witness.nd_siblings.clear();
449
450 witness.root_from_authentication_struct(tree_height, auth_struct, indexed_leafs);
451 }
452
453 #[test]
454 fn auth_struct_from_mmr_mps_test_height_5_9_indices() {
455 let local_tree_height = 5;
456 let mmr_leaf_indices = [0, 1, 2, 16, 17, 18, 27, 29, 31];
457 let indexed_leafs_input: Vec<(u64, Digest)> = mmr_leaf_indices
458 .iter()
459 .map(|idx| (*idx, random()))
460 .collect_vec();
461 let (mmra, mmr_mps) = mmra_with_mps(1 << local_tree_height, indexed_leafs_input.clone());
462 let indexed_mmr_mps = mmr_mps
463 .into_iter()
464 .zip_eq(indexed_leafs_input)
465 .map(|(mmr_mp, (idx, leaf))| (idx, leaf, mmr_mp))
466 .collect_vec();
467
468 let authenticity_witnesses =
469 AuthStructIntegrityProof::new_from_mmr_membership_proofs(&mmra, indexed_mmr_mps);
470 assert!(
471 authenticity_witnesses.len().is_one(),
472 "All indices belong to first peak"
473 );
474 let AuthenticatedMerkleAuthStruct {
475 auth_struct,
476 indexed_leafs,
477 witness,
478 } = &authenticity_witnesses[&0];
479
480 let tree_height: u32 = local_tree_height.try_into().unwrap();
481 let computed_root = witness.root_from_authentication_struct(
482 tree_height,
483 auth_struct.to_owned(),
484 indexed_leafs.to_owned(),
485 );
486
487 let peak_index = 0;
488 let expected_root = mmra.peaks()[peak_index];
489 assert_eq!(expected_root, computed_root);
490 }
491
492 #[test]
493 fn auth_struct_from_mmr_mps_test_height_4_2_indices() {
494 let local_tree_height = 4;
495 let mmr_leaf_indices = [0, 1];
496 let indexed_leafs_input: Vec<(u64, Digest)> = mmr_leaf_indices
497 .iter()
498 .map(|idx| (*idx, random()))
499 .collect_vec();
500 let (mmra, mmr_mps) = mmra_with_mps(1 << local_tree_height, indexed_leafs_input.clone());
501 let indexed_mmr_mps = mmr_mps
502 .into_iter()
503 .zip_eq(indexed_leafs_input)
504 .map(|(mmr_mp, (idx, leaf))| (idx, leaf, mmr_mp))
505 .collect_vec();
506
507 let authenticity_witnesses =
508 AuthStructIntegrityProof::new_from_mmr_membership_proofs(&mmra, indexed_mmr_mps);
509 assert!(
510 authenticity_witnesses.len().is_one(),
511 "All indices belong to first peak"
512 );
513 let AuthenticatedMerkleAuthStruct {
514 auth_struct,
515 indexed_leafs,
516 witness,
517 } = &authenticity_witnesses[&0];
518
519 let tree_height: u32 = local_tree_height.try_into().unwrap();
520 let computed_root = witness.root_from_authentication_struct(
521 tree_height,
522 auth_struct.to_owned(),
523 indexed_leafs.to_owned(),
524 );
525
526 let peak_index = 0;
527 let expected_root = mmra.peaks()[peak_index];
528 assert_eq!(expected_root, computed_root);
529 }
530
531 #[proptest(cases = 20)]
532 fn root_from_authentication_struct_prop_test(
533 #[strategy(0..12u64)] tree_height: u64,
534 #[strategy(1usize..100)] _num_revealed_leafs: usize,
535 #[strategy(vec(0u64..1<<#tree_height, #_num_revealed_leafs))] revealed_leaf_indices: Vec<
536 u64,
537 >,
538 ) {
539 let num_leafs = 1u64 << tree_height;
540 let leafs: Vec<Digest> = random_elements(num_leafs.try_into().unwrap());
541 let tree = MerkleTree::par_new(&leafs).unwrap();
542
543 let authenticated_auth_struct =
544 AuthStructIntegrityProof::new_from_merkle_tree(&tree, revealed_leaf_indices);
545 let AuthenticatedMerkleAuthStruct {
546 auth_struct,
547 indexed_leafs,
548 witness,
549 } = authenticated_auth_struct;
550
551 let tree_height: u32 = tree_height.try_into().unwrap();
552 let computed_root =
553 witness.root_from_authentication_struct(tree_height, auth_struct, indexed_leafs);
554 let expected_root = tree.root();
555 prop_assert_eq!(expected_root, computed_root);
556 }
557
558 fn prop_from_merkle_tree(
559 tree_height: usize,
560 leaf_indices: Vec<u64>,
561 nd_auth_struct_indices: Vec<u64>,
562 nd_sibling_indices: Vec<(u64, u64)>,
563 ) {
564 let leafs: Vec<Digest> = random_elements(1 << tree_height);
565 let tree = MerkleTree::par_new(&leafs).unwrap();
566
567 let auth_struct = nd_auth_struct_indices
568 .iter()
569 .map(|i| tree.node(*i as usize).unwrap())
570 .collect_vec();
571 let revealed_leafs = leaf_indices
572 .iter()
573 .map(|i| tree.leaf(*i as usize).unwrap())
574 .collect_vec();
575 let revealed_leafs = leaf_indices
576 .into_iter()
577 .zip_eq(revealed_leafs)
578 .collect_vec();
579 let nd_siblings = nd_sibling_indices
580 .iter()
581 .map(|(left_idx, right_idx)| {
582 (
583 tree.node(*left_idx as usize).unwrap(),
584 tree.node(*right_idx as usize).unwrap(),
585 )
586 })
587 .collect_vec();
588
589 let mmr_auth_struct = AuthStructIntegrityProof {
590 nd_auth_struct_indices,
591 nd_sibling_indices,
592 nd_siblings,
593 };
594 let tree_height: u32 = tree_height.try_into().unwrap();
595 let calculated_root = mmr_auth_struct.root_from_authentication_struct(
596 tree_height,
597 auth_struct,
598 revealed_leafs,
599 );
600 assert_eq!(tree.root(), calculated_root);
601 }
602
603 #[test]
604 fn root_from_authentication_struct_tree_height_0_1_revealed() {
605 let tree_height = 0;
606 let leaf_indices = vec![0];
607 let nd_auth_struct_indices = vec![];
608 let nd_sibling_indices = vec![];
609 prop_from_merkle_tree(
610 tree_height,
611 leaf_indices,
612 nd_auth_struct_indices,
613 nd_sibling_indices,
614 )
615 }
616
617 #[test]
618 fn root_from_authentication_struct_tree_height_1_1_revealed() {
619 let tree_height = 1;
620 let leaf_indices = vec![0u64];
621 let nd_auth_struct_indices = vec![3];
622 let nd_sibling_indices = vec![(2u64, 3u64)];
623 prop_from_merkle_tree(
624 tree_height,
625 leaf_indices,
626 nd_auth_struct_indices,
627 nd_sibling_indices,
628 )
629 }
630
631 #[test]
632 fn root_from_authentication_struct_tree_height_1_2_revealed() {
633 let tree_height = 1;
634 let leaf_indices = vec![0u64, 1];
635 let nd_auth_struct_indices = vec![];
636 let nd_sibling_indices = vec![(2u64, 3u64)];
637 prop_from_merkle_tree(
638 tree_height,
639 leaf_indices,
640 nd_auth_struct_indices,
641 nd_sibling_indices,
642 )
643 }
644
645 #[test]
646 fn root_from_authentication_struct_tree_height_2_2_revealed() {
647 let tree_height = 2;
648 let leaf_indices = vec![0u64, 1];
649 let auth_struct_indices = vec![3];
650 let nd_sibling_indices = vec![(4u64, 5u64), (2, 3)];
651 prop_from_merkle_tree(
652 tree_height,
653 leaf_indices,
654 auth_struct_indices,
655 nd_sibling_indices,
656 )
657 }
658
659 #[test]
660 fn root_from_authentication_struct_tree_height_4_4_revealed() {
661 let tree_height = 4;
662 let leaf_indices = vec![14u64, 12, 10, 8];
663 let num_leafs = 1 << tree_height;
664 let auth_struct_indices = vec![
665 num_leafs + 15,
666 num_leafs + 13,
667 num_leafs + 11,
668 num_leafs + 9,
669 2,
670 ];
671 let nd_sibling_indices_layer_height_0 = [(14u64, 15u64), (12, 13), (10, 11), (8, 9)]
672 .map(|(l, r)| (l + num_leafs, r + num_leafs));
673 let nd_sibling_indices_layer_height_1 = [(14u64, 15u64), (12u64, 13u64)];
674 let nd_sibling_indices_layer_height_2 = [(6u64, 7u64)];
675 let nd_sibling_indices_layer_height_3 = [(2u64, 3u64)];
676 let nd_sibling_indices = [
677 nd_sibling_indices_layer_height_0.to_vec(),
678 nd_sibling_indices_layer_height_1.to_vec(),
679 nd_sibling_indices_layer_height_2.to_vec(),
680 nd_sibling_indices_layer_height_3.to_vec(),
681 ]
682 .concat();
683
684 prop_from_merkle_tree(
685 tree_height,
686 leaf_indices,
687 auth_struct_indices,
688 nd_sibling_indices,
689 )
690 }
691}