1use alloc::{
45 string::{String, ToString},
46 vec,
47 vec::Vec,
48};
49use bytes::BufMut;
50use commonware_codec::{
51 Buf, EncodeSize, FixedSize, Read, ReadExt, ReadRangeExt, Write, util::max_items_size,
52};
53use commonware_cryptography::{Digest, Hasher};
54use commonware_parallel::Sequential;
55use commonware_utils::{non_empty_vec, vec::NonEmptyVec};
56use thiserror::Error;
57
58pub const MAX_LEVELS: usize = u32::BITS as usize;
62
63#[derive(Error, Debug)]
65pub enum Error {
66 #[error("invalid position: {0}")]
67 InvalidPosition(u32),
68 #[error("invalid proof: {0} != {1}")]
69 InvalidProof(String, String),
70 #[error("no leaves")]
71 NoLeaves,
72 #[error("unaligned proof")]
73 UnalignedProof,
74 #[error("duplicate position: {0}")]
75 DuplicatePosition(u32),
76 #[error("unsorted position: {0}")]
77 UnsortedPosition(u32),
78}
79
80pub struct Builder<H: Hasher> {
82 leaves: Vec<H::Digest>,
83}
84
85impl<H: Hasher> Builder<H> {
86 pub fn new(leaves: usize) -> Self {
88 Self {
89 leaves: Vec::with_capacity(leaves),
90 }
91 }
92
93 pub fn add(&mut self, leaf: &H::Digest) -> u32 {
101 let position: u32 = self.leaves.len().try_into().expect("too many leaves");
104 assert!(position < u32::MAX, "too many leaves");
105
106 let digest = H::hash(&[&position.to_be_bytes(), leaf.as_ref()]);
107 self.leaves.push(digest);
108 position
109 }
110
111 pub fn build(self) -> Tree<H::Digest> {
116 Tree::new::<H>(self.leaves)
117 }
118}
119
120#[derive(Clone, Debug)]
122pub struct Tree<D: Digest> {
123 empty: bool,
125
126 levels: NonEmptyVec<NonEmptyVec<D>>,
128
129 root: D,
135}
136
137impl<D: Digest> Tree<D> {
138 fn new<H: Hasher<Digest = D>>(mut leaves: Vec<D>) -> Self {
140 let mut empty = false;
145 let leaf_count = leaves.len() as u32;
146 if leaves.is_empty() {
147 leaves.push(H::hash(&[]));
148 empty = true;
149 }
150
151 let mut levels = non_empty_vec![non_empty_vec![@leaves]];
153
154 let mut current_level = levels.last();
156 while !current_level.is_singleton() {
157 let mut next_level = Vec::with_capacity(current_level.len().get().div_ceil(2));
158
159 for group in current_level.chunks(4) {
164 match group {
165 [a, b, c, d] => {
166 let (left, right) = H::hash_pair(
167 &[a.as_ref(), b.as_ref()],
168 &[c.as_ref(), d.as_ref()],
169 &Sequential,
170 );
171 next_level.push(left);
172 next_level.push(right);
173 }
174 [a, b, c] => {
175 let (left, right) = H::hash_pair(
176 &[a.as_ref(), b.as_ref()],
177 &[c.as_ref(), c.as_ref()],
178 &Sequential,
179 );
180 next_level.push(left);
181 next_level.push(right);
182 }
183 [a, b] => next_level.push(H::hash(&[a.as_ref(), b.as_ref()])),
184 [a] => next_level.push(H::hash(&[a.as_ref(), a.as_ref()])),
185 _ => unreachable!("chunks(4) yields at most 4 elements"),
186 }
187 }
188
189 levels.push(non_empty_vec![@next_level]);
191 current_level = levels.last();
192 }
193
194 let tree_root = levels.last().first();
197 let root = H::hash(&[&leaf_count.to_be_bytes(), tree_root.as_ref()]);
198
199 Self {
200 empty,
201 levels,
202 root,
203 }
204 }
205
206 pub const fn root(&self) -> D {
212 self.root
213 }
214
215 pub fn proof(&self, position: u32) -> Result<Proof<D>, Error> {
220 let leaf_count = self.levels.first().len().get() as u32;
223 if self.empty || position >= leaf_count {
224 return Err(Error::InvalidPosition(position));
225 }
226
227 let levels = self.levels.len().get();
233 let mut siblings = Vec::with_capacity(levels - 1);
234 let mut index = position as usize;
235 for level in &self.levels[..levels - 1] {
236 if let Some(sibling) = level.get(index ^ 1) {
237 siblings.push(*sibling);
238 }
239 index /= 2;
240 }
241
242 Ok(Proof {
243 leaf_count,
244 siblings,
245 })
246 }
247
248 pub fn range_proof(&self, start: u32, end: u32) -> Result<Proof<D>, Error> {
255 if self.empty {
257 if start == 0 && end == 0 {
258 return Ok(Proof::default());
259 }
260 return Err(Error::InvalidPosition(start));
261 }
262
263 if start > end {
265 return Err(Error::InvalidPosition(start));
266 }
267 let leaf_count = self.levels.first().len().get() as u32;
268 if start >= leaf_count {
269 return Err(Error::InvalidPosition(start));
270 }
271 if end >= leaf_count {
272 return Err(Error::InvalidPosition(end));
273 }
274
275 let mut siblings = Vec::with_capacity(2 * (levels_in_tree(leaf_count) - 1));
279 siblings_required_for_range_proof(leaf_count, start, end, |level, index| {
280 siblings.push(self.levels[level][index])
281 })?;
282
283 Ok(Proof {
284 leaf_count,
285 siblings,
286 })
287 }
288
289 pub fn multi_proof<I, P>(&self, positions: I) -> Result<Proof<D>, Error>
300 where
301 I: IntoIterator<Item = P>,
302 P: core::borrow::Borrow<u32>,
303 {
304 let mut positions = positions.into_iter().peekable();
305
306 let first = *positions.peek().ok_or(Error::NoLeaves)?.borrow();
308
309 if self.empty {
311 return Err(Error::InvalidPosition(first));
312 }
313
314 let leaf_count = self.levels.first().len().get() as u32;
315
316 let sibling_positions =
318 siblings_required_for_multi_proof(leaf_count, positions.map(|p| *p.borrow()))?;
319
320 let siblings: Vec<D> = sibling_positions
323 .iter()
324 .map(|&(level, index)| self.levels[level][index])
325 .collect();
326
327 Ok(Proof {
328 leaf_count,
329 siblings,
330 })
331 }
332}
333
334#[derive(Clone, Debug, Eq, PartialEq)]
344pub struct Proof<D: Digest> {
345 pub leaf_count: u32,
350
351 pub siblings: Vec<D>,
354}
355
356impl<D: Digest> Default for Proof<D> {
357 fn default() -> Self {
358 Self {
359 leaf_count: 0,
360 siblings: Vec::new(),
361 }
362 }
363}
364
365impl<D: Digest> Write for Proof<D> {
366 fn write(&self, writer: &mut impl BufMut) {
367 self.leaf_count.write(writer);
368 self.siblings.write(writer);
369 }
370}
371
372impl<D: Digest> Read for Proof<D> {
373 type Cfg = usize;
377
378 fn read_cfg(
379 reader: &mut impl Buf,
380 max_items: &Self::Cfg,
381 ) -> Result<Self, commonware_codec::Error> {
382 let leaf_count = u32::read(reader)?;
383 let max_siblings = max_items.saturating_mul(MAX_LEVELS);
384 let siblings = Vec::<D>::read_range(reader, ..=max_siblings)?;
385 Ok(Self {
386 leaf_count,
387 siblings,
388 })
389 }
390}
391
392impl<D: Digest> EncodeSize for Proof<D> {
393 fn encode_size(&self) -> usize {
394 self.leaf_count.encode_size() + self.siblings.encode_size()
395 }
396}
397
398#[cfg(feature = "arbitrary")]
399impl<D: Digest> arbitrary::Arbitrary<'_> for Proof<D>
400where
401 D: for<'a> arbitrary::Arbitrary<'a>,
402{
403 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
404 Ok(Self {
405 leaf_count: u.arbitrary()?,
406 siblings: u.arbitrary()?,
407 })
408 }
409}
410
411const fn levels_in_tree(leaf_count: u32) -> usize {
414 (u32::BITS - (leaf_count.saturating_sub(1)).leading_zeros() + 1) as usize
415}
416
417fn siblings_required_for_multi_proof(
426 leaf_count: u32,
427 positions: impl IntoIterator<Item = u32>,
428) -> Result<Vec<(usize, usize)>, Error> {
429 let positions = positions.into_iter();
437 let capacity = match positions.size_hint() {
438 (lower, Some(upper)) if lower == upper => lower.min(leaf_count as usize),
439 _ => 0,
440 };
441 let mut current: Vec<usize> = Vec::with_capacity(capacity);
442 for pos in positions {
443 if pos >= leaf_count {
444 return Err(Error::InvalidPosition(pos));
445 }
446 match current.last() {
447 Some(&last) if last == pos as usize => return Err(Error::DuplicatePosition(pos)),
448 Some(&last) if last > pos as usize => return Err(Error::UnsortedPosition(pos)),
449 _ => current.push(pos as usize),
450 }
451 }
452 if current.is_empty() {
453 return Err(Error::NoLeaves);
454 }
455
456 let mut next = Vec::with_capacity(current.len());
460 let mut sibling_positions = Vec::with_capacity(current.len());
461 let levels_count = levels_in_tree(leaf_count);
462 let mut level_size = leaf_count as usize;
463 for level in 0..levels_count - 1 {
464 for (offset, &index) in current.iter().enumerate() {
465 let sibling = index ^ 1;
469 let known = if index.is_multiple_of(2) {
470 current.get(offset + 1) == Some(&sibling)
471 } else {
472 offset > 0 && current[offset - 1] == sibling
473 };
474
475 if sibling < level_size && !known {
479 sibling_positions.push((level, sibling));
480 }
481
482 let parent = index / 2;
486 if next.last() != Some(&parent) {
487 next.push(parent);
488 }
489 }
490
491 core::mem::swap(&mut current, &mut next);
493 next.clear();
494 level_size = level_size.div_ceil(2);
495 }
496
497 Ok(sibling_positions)
498}
499
500fn siblings_required_for_range_proof(
507 leaf_count: u32,
508 start: u32,
509 end: u32,
510 mut sibling: impl FnMut(usize, usize),
511) -> Result<(), Error> {
512 if leaf_count == 0 {
513 return Err(Error::NoLeaves);
514 }
515 if start > end {
516 return Err(Error::InvalidPosition(start));
517 }
518 if start >= leaf_count {
519 return Err(Error::InvalidPosition(start));
520 }
521 if end >= leaf_count {
522 return Err(Error::InvalidPosition(end));
523 }
524
525 let levels_count = levels_in_tree(leaf_count);
526 let mut level_start = start as usize;
527 let mut level_end = end as usize;
528 let mut level_size = leaf_count as usize;
529
530 for level in 0..levels_count - 1 {
534 if !level_start.is_multiple_of(2) {
535 sibling(level, level_start - 1);
536 }
537 if level_end.is_multiple_of(2) {
538 let right = level_end + 1;
539 if right < level_size {
540 sibling(level, right);
541 }
542 }
543
544 level_start /= 2;
545 level_end /= 2;
546 level_size = level_size.div_ceil(2);
547 }
548
549 Ok(())
550}
551
552impl<D: Digest> Proof<D> {
553 pub fn max_encode_size(siblings: usize) -> usize {
556 max_items_size(siblings, D::SIZE) + u32::SIZE
557 }
558
559 pub fn verify_element_inclusion<H: Hasher<Digest = D>>(
570 &self,
571 leaf: &D,
572 mut position: u32,
573 root: &D,
574 ) -> Result<(), Error> {
575 if position >= self.leaf_count {
577 return Err(Error::InvalidPosition(position));
578 }
579
580 let mut computed = H::hash(&[&position.to_be_bytes(), leaf.as_ref()]);
582
583 let mut level_size = self.leaf_count as usize;
585 let mut sibling_iter = self.siblings.iter();
586
587 while level_size > 1 {
589 let is_last_odd = position.is_multiple_of(2) && position as usize + 1 >= level_size;
591
592 let (left_node, right_node) = if is_last_odd {
593 (computed, computed)
595 } else if position.is_multiple_of(2) {
596 let sibling = *sibling_iter.next().ok_or(Error::UnalignedProof)?;
598 (computed, sibling)
599 } else {
600 let sibling = *sibling_iter.next().ok_or(Error::UnalignedProof)?;
602 (sibling, computed)
603 };
604
605 computed = H::hash(&[left_node.as_ref(), right_node.as_ref()]);
607
608 position /= 2;
610 level_size = level_size.div_ceil(2);
611 }
612
613 if sibling_iter.next().is_some() {
615 return Err(Error::UnalignedProof);
616 }
617
618 let finalized = H::hash(&[&self.leaf_count.to_be_bytes(), computed.as_ref()]);
621
622 if finalized == *root {
623 Ok(())
624 } else {
625 Err(Error::InvalidProof(finalized.to_string(), root.to_string()))
626 }
627 }
628
629 pub fn verify_multi_inclusion<H: Hasher<Digest = D>>(
639 &self,
640 elements: &[(D, u32)],
641 root: &D,
642 ) -> Result<(), Error> {
643 if elements.is_empty() {
645 if self.leaf_count == 0 && self.siblings.is_empty() {
646 let empty_tree_root = H::hash(&[]);
648 let finalized = H::hash(&[&0u32.to_be_bytes(), empty_tree_root.as_ref()]);
649 if finalized == *root {
650 return Ok(());
651 } else {
652 return Err(Error::InvalidProof(finalized.to_string(), root.to_string()));
653 }
654 }
655 return Err(Error::NoLeaves);
656 }
657
658 let mut previous = None;
660 for &(_, position) in elements {
661 if position >= self.leaf_count {
662 return Err(Error::InvalidPosition(position));
663 }
664 match previous {
665 Some(previous) if position == previous => {
666 return Err(Error::DuplicatePosition(position));
667 }
668 Some(previous) if position < previous => {
669 return Err(Error::UnsortedPosition(position));
670 }
671 _ => previous = Some(position),
672 }
673 }
674
675 let mut current: Vec<(u32, D)> = Vec::with_capacity(elements.len());
677 let (leaf_chunks, leaf_remainder) = elements.as_chunks::<2>();
678 for chunk in leaf_chunks {
679 let (leaf_a, pos_a) = &chunk[0];
680 let (leaf_b, pos_b) = &chunk[1];
681 let (digest_a, digest_b) = H::hash_pair(
682 &[&pos_a.to_be_bytes(), leaf_a.as_ref()],
683 &[&pos_b.to_be_bytes(), leaf_b.as_ref()],
684 &Sequential,
685 );
686 current.push((*pos_a, digest_a));
687 current.push((*pos_b, digest_b));
688 }
689 for (leaf, position) in leaf_remainder {
690 let digest = H::hash(&[&position.to_be_bytes(), leaf.as_ref()]);
691 current.push((*position, digest));
692 }
693
694 let levels = levels_in_tree(self.leaf_count);
697 let mut level_size = self.leaf_count;
698 let mut sibling_iter = self.siblings.iter();
699 let mut next_level: Vec<(u32, D)> = Vec::with_capacity(current.len());
700 let mut parents: Vec<(u32, D, D)> = Vec::with_capacity(current.len());
701
702 for _ in 0..levels - 1 {
703 let mut idx = 0;
706 while idx < current.len() {
707 let (pos, digest) = current[idx];
708 let parent_pos = pos / 2;
709
710 let (left, right) = if pos.is_multiple_of(2) {
712 let left = digest;
714
715 let right = if idx + 1 < current.len() && current[idx + 1].0 == pos + 1 {
717 idx += 1;
718 current[idx].1
719 } else if pos + 1 >= level_size {
720 left
722 } else {
723 *sibling_iter.next().ok_or(Error::UnalignedProof)?
725 };
726 (left, right)
727 } else {
728 let right = digest;
731 let left = *sibling_iter.next().ok_or(Error::UnalignedProof)?;
732 (left, right)
733 };
734
735 parents.push((parent_pos, left, right));
736 idx += 1;
737 }
738
739 let (parent_chunks, parent_remainder) = parents.as_chunks::<2>();
741 for chunk in parent_chunks {
742 let (pos_a, left_a, right_a) = chunk[0];
743 let (pos_b, left_b, right_b) = chunk[1];
744 let (digest_a, digest_b) = H::hash_pair(
745 &[left_a.as_ref(), right_a.as_ref()],
746 &[left_b.as_ref(), right_b.as_ref()],
747 &Sequential,
748 );
749 next_level.push((pos_a, digest_a));
750 next_level.push((pos_b, digest_b));
751 }
752 for &(pos, left, right) in parent_remainder {
753 next_level.push((pos, H::hash(&[left.as_ref(), right.as_ref()])));
754 }
755 parents.clear();
756
757 core::mem::swap(&mut current, &mut next_level);
759 next_level.clear();
760 level_size = level_size.div_ceil(2);
761 }
762
763 if sibling_iter.next().is_some() {
765 return Err(Error::UnalignedProof);
766 }
767
768 if current.len() != 1 {
769 return Err(Error::UnalignedProof);
770 }
771
772 let tree_root = current[0].1;
775 let finalized = H::hash(&[&self.leaf_count.to_be_bytes(), tree_root.as_ref()]);
776
777 if finalized == *root {
778 Ok(())
779 } else {
780 Err(Error::InvalidProof(finalized.to_string(), root.to_string()))
781 }
782 }
783
784 pub fn verify_range_inclusion<H: Hasher<Digest = D>>(
793 &self,
794 position: u32,
795 leaves: &[D],
796 root: &D,
797 ) -> Result<(), Error> {
798 if leaves.is_empty() && position != 0 {
800 return Err(Error::InvalidPosition(position));
801 }
802 if !leaves.is_empty() {
803 let leaves_len =
804 u32::try_from(leaves.len()).map_err(|_| Error::InvalidPosition(position))?;
805 let end = position
806 .checked_add(leaves_len - 1)
807 .ok_or(Error::InvalidPosition(position))?;
808 if end >= self.leaf_count {
809 return Err(Error::InvalidPosition(end));
810 }
811 }
812
813 let elements: Vec<(D, u32)> = leaves
815 .iter()
816 .enumerate()
817 .map(|(i, leaf)| (*leaf, position + i as u32))
818 .collect();
819 self.verify_multi_inclusion::<H>(&elements, root)
820 }
821}
822
823#[cfg(test)]
824mod tests {
825 use super::*;
826 use commonware_codec::{Decode, Encode};
827 use commonware_cryptography::sha256::{Digest, Sha256};
828 use rstest::rstest;
829
830 #[test]
836 fn issue_2837_regression() {
837 let digests: Vec<Digest> = (0..255u32)
839 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
840 .collect();
841
842 let mut builder = Builder::<Sha256>::new(255);
843 for digest in &digests {
844 builder.add(digest);
845 }
846 let tree = builder.build();
847 let root = tree.root();
848
849 let original_proof = tree.proof(0).unwrap();
851 assert_eq!(original_proof.leaf_count, 255);
852
853 assert!(
855 original_proof
856 .verify_element_inclusion::<Sha256>(&digests[0], 0, &root)
857 .is_ok(),
858 "Original proof should verify"
859 );
860
861 let malleated_proof = Proof {
864 leaf_count: 254,
865 siblings: original_proof.siblings,
866 };
867
868 let result = malleated_proof.verify_element_inclusion::<Sha256>(&digests[0], 0, &root);
871 assert!(
872 result.is_err(),
873 "Malleated proof with wrong leaf_count must fail verification"
874 );
875 }
876
877 #[test]
878 fn test_tampered_proof_no_siblings() {
879 let txs = [b"tx1", b"tx2", b"tx3", b"tx4"];
881 let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(&[*tx])).collect();
882 let element = &digests[0];
883
884 let mut builder = Builder::<Sha256>::new(txs.len());
886 for digest in &digests {
887 builder.add(digest);
888 }
889 let tree = builder.build();
890 let root = tree.root();
891
892 let mut proof = tree.proof(0).unwrap();
894
895 proof.siblings = Vec::new();
897
898 assert!(
900 proof
901 .verify_element_inclusion::<Sha256>(element, 0, &root)
902 .is_err()
903 );
904 }
905
906 #[test]
907 fn test_tampered_proof_extra_sibling() {
908 let txs = [b"tx1", b"tx2", b"tx3", b"tx4"];
910 let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(&[*tx])).collect();
911 let element = &digests[0];
912
913 let mut builder = Builder::<Sha256>::new(txs.len());
915 for digest in &digests {
916 builder.add(digest);
917 }
918 let tree = builder.build();
919 let root = tree.root();
920
921 let mut proof = tree.proof(0).unwrap();
923
924 proof.siblings.push(*element);
926
927 assert!(
929 proof
930 .verify_element_inclusion::<Sha256>(element, 0, &root)
931 .is_err()
932 );
933 }
934
935 #[test]
936 fn test_invalid_proof_wrong_element() {
937 let txs = [b"tx1", b"tx2", b"tx3", b"tx4"];
939 let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(&[*tx])).collect();
940
941 let mut builder = Builder::<Sha256>::new(txs.len());
943 for digest in &digests {
944 builder.add(digest);
945 }
946 let tree = builder.build();
947 let root = tree.root();
948
949 let proof = tree.proof(2).unwrap();
951
952 let wrong_leaf = Sha256::hash(&[b"wrong_tx"]);
954 assert!(
955 proof
956 .verify_element_inclusion::<Sha256>(&wrong_leaf, 2, &root)
957 .is_err()
958 );
959 }
960
961 #[test]
962 fn test_invalid_proof_wrong_index() {
963 let txs = [b"tx1", b"tx2", b"tx3", b"tx4"];
965 let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(&[*tx])).collect();
966
967 let mut builder = Builder::<Sha256>::new(txs.len());
969 for digest in &digests {
970 builder.add(digest);
971 }
972 let tree = builder.build();
973 let root = tree.root();
974
975 let proof = tree.proof(1).unwrap();
977
978 assert!(
980 proof
981 .verify_element_inclusion::<Sha256>(&digests[1], 2, &root)
982 .is_err()
983 );
984 }
985
986 #[test]
987 fn test_invalid_proof_wrong_root() {
988 let txs = [b"tx1", b"tx2", b"tx3", b"tx4"];
990 let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(&[*tx])).collect();
991
992 let mut builder = Builder::<Sha256>::new(txs.len());
994 for digest in &digests {
995 builder.add(digest);
996 }
997 let tree = builder.build();
998
999 let proof = tree.proof(0).unwrap();
1001
1002 let wrong_root = Sha256::hash(&[b"wrong_root"]);
1004 assert!(
1005 proof
1006 .verify_element_inclusion::<Sha256>(&digests[0], 0, &wrong_root)
1007 .is_err()
1008 );
1009 }
1010
1011 #[test]
1012 fn test_invalid_proof_serialization_truncated() {
1013 let txs = [b"tx1", b"tx2", b"tx3"];
1015 let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(&[*tx])).collect();
1016
1017 let mut builder = Builder::<Sha256>::new(txs.len());
1019 for digest in &digests {
1020 builder.add(digest);
1021 }
1022 let tree = builder.build();
1023
1024 let proof = tree.proof(1).unwrap();
1026 let mut serialized = proof.encode();
1027
1028 serialized.truncate(serialized.len() - 1);
1030 assert!(Proof::<Digest>::decode_cfg(&mut serialized, &1).is_err());
1031 }
1032
1033 #[test]
1034 fn test_invalid_proof_serialization_extra() {
1035 let txs = [b"tx1", b"tx2", b"tx3"];
1037 let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(&[*tx])).collect();
1038
1039 let mut builder = Builder::<Sha256>::new(txs.len());
1041 for digest in &digests {
1042 builder.add(digest);
1043 }
1044 let tree = builder.build();
1045
1046 let proof = tree.proof(1).unwrap();
1048 let mut serialized = proof.encode_mut();
1049
1050 serialized.extend_from_slice(&[0u8]);
1052 assert!(Proof::<Digest>::decode_cfg(&mut serialized, &1).is_err());
1053 }
1054
1055 #[test]
1056 fn test_invalid_proof_modified_hash() {
1057 let txs = [b"tx1", b"tx2", b"tx3", b"tx4"];
1059 let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(&[*tx])).collect();
1060
1061 let mut builder = Builder::<Sha256>::new(txs.len());
1063 for digest in &digests {
1064 builder.add(digest);
1065 }
1066 let tree = builder.build();
1067 let root = tree.root();
1068
1069 let mut proof = tree.proof(2).unwrap();
1071
1072 proof.siblings[0] = Sha256::hash(&[b"modified"]);
1074 assert!(
1075 proof
1076 .verify_element_inclusion::<Sha256>(&digests[2], 2, &root)
1077 .is_err()
1078 );
1079 }
1080
1081 #[test]
1082 fn test_odd_tree_duplicate_index_proof() {
1083 let txs = [b"tx1", b"tx2", b"tx3"];
1085 let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(&[*tx])).collect();
1086
1087 let mut builder = Builder::<Sha256>::new(txs.len());
1089 for digest in &digests {
1090 builder.add(digest);
1091 }
1092 let tree = builder.build();
1093 let root = tree.root();
1094
1095 let proof = tree.proof(2).unwrap();
1097
1098 assert!(
1100 proof
1101 .verify_element_inclusion::<Sha256>(&digests[2], 2, &root)
1102 .is_ok()
1103 );
1104
1105 assert!(tree.proof(3).is_err());
1107
1108 assert!(
1111 proof
1112 .verify_element_inclusion::<Sha256>(&digests[2], 3, &root)
1113 .is_err()
1114 );
1115 }
1116
1117 #[test]
1118 fn test_range_proof_basic() {
1119 let digests: Vec<Digest> = (0..8u32)
1121 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1122 .collect();
1123
1124 let mut builder = Builder::<Sha256>::new(digests.len());
1126 for digest in &digests {
1127 builder.add(digest);
1128 }
1129 let tree = builder.build();
1130 let root = tree.root();
1131
1132 let range_proof = tree.range_proof(2, 5).unwrap();
1134 let range_leaves = &digests[2..6];
1135
1136 assert!(
1137 range_proof
1138 .verify_range_inclusion::<Sha256>(2, range_leaves, &root)
1139 .is_ok()
1140 );
1141
1142 let mut serialized = range_proof.encode();
1144 let deserialized = Proof::<Digest>::decode_cfg(&mut serialized, &4).unwrap();
1145 assert!(
1146 deserialized
1147 .verify_range_inclusion::<Sha256>(2, range_leaves, &root)
1148 .is_ok()
1149 );
1150 }
1151
1152 #[test]
1153 fn test_range_proof_single_element() {
1154 let digests: Vec<Digest> = (0..8u32)
1156 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1157 .collect();
1158
1159 let mut builder = Builder::<Sha256>::new(digests.len());
1161 for digest in &digests {
1162 builder.add(digest);
1163 }
1164 let tree = builder.build();
1165 let root = tree.root();
1166
1167 for (i, digest) in digests.iter().enumerate() {
1169 let range_proof = tree.range_proof(i as u32, i as u32).unwrap();
1170
1171 let result = range_proof.verify_range_inclusion::<Sha256>(i as u32, &[*digest], &root);
1172 assert!(result.is_ok());
1173 }
1174 }
1175
1176 #[test]
1177 fn test_range_proof_full_tree() {
1178 let digests: Vec<Digest> = (0..7u32)
1180 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1181 .collect();
1182
1183 let mut builder = Builder::<Sha256>::new(digests.len());
1185 for digest in &digests {
1186 builder.add(digest);
1187 }
1188 let tree = builder.build();
1189 let root = tree.root();
1190
1191 let range_proof = tree.range_proof(0, (digests.len() - 1) as u32).unwrap();
1193 assert!(
1194 range_proof
1195 .verify_range_inclusion::<Sha256>(0, &digests, &root)
1196 .is_ok()
1197 );
1198 }
1199
1200 #[test]
1201 fn test_range_proof_edge_cases() {
1202 let digests: Vec<Digest> = (0..15u32)
1204 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1205 .collect();
1206
1207 let mut builder = Builder::<Sha256>::new(digests.len());
1209 for digest in &digests {
1210 builder.add(digest);
1211 }
1212 let tree = builder.build();
1213 let root = tree.root();
1214
1215 let range_proof = tree.range_proof(0, 7).unwrap();
1217 assert!(
1218 range_proof
1219 .verify_range_inclusion::<Sha256>(0, &digests[0..8], &root)
1220 .is_ok()
1221 );
1222
1223 let range_proof = tree.range_proof(8, 14).unwrap();
1225 assert!(
1226 range_proof
1227 .verify_range_inclusion::<Sha256>(8, &digests[8..15], &root)
1228 .is_ok()
1229 );
1230
1231 let range_proof = tree.range_proof(13, 14).unwrap();
1233 assert!(
1234 range_proof
1235 .verify_range_inclusion::<Sha256>(13, &digests[13..15], &root)
1236 .is_ok()
1237 );
1238 }
1239
1240 #[test]
1241 fn test_range_proof_invalid_range() {
1242 let digests: Vec<Digest> = (0..8u32)
1244 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1245 .collect();
1246
1247 let mut builder = Builder::<Sha256>::new(digests.len());
1249 for digest in &digests {
1250 builder.add(digest);
1251 }
1252 let tree = builder.build();
1253
1254 assert!(tree.range_proof(8, 8).is_err()); assert!(tree.range_proof(0, 8).is_err()); assert!(tree.range_proof(5, 8).is_err()); assert!(tree.range_proof(2, 1).is_err()); }
1260
1261 #[test]
1262 fn test_range_proof_tampering() {
1263 let digests: Vec<Digest> = (0..8u32)
1265 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1266 .collect();
1267
1268 let mut builder = Builder::<Sha256>::new(digests.len());
1270 for digest in &digests {
1271 builder.add(digest);
1272 }
1273 let tree = builder.build();
1274 let root = tree.root();
1275
1276 let range_proof = tree.range_proof(2, 4).unwrap();
1278 let range_leaves = &digests[2..5];
1279
1280 let wrong_leaves = vec![
1282 Sha256::hash(&[b"wrong1"]),
1283 Sha256::hash(&[b"wrong2"]),
1284 Sha256::hash(&[b"wrong3"]),
1285 ];
1286 assert!(
1287 range_proof
1288 .verify_range_inclusion::<Sha256>(2, &wrong_leaves, &root)
1289 .is_err()
1290 );
1291
1292 assert!(
1294 range_proof
1295 .verify_range_inclusion::<Sha256>(2, &digests[2..4], &root)
1296 .is_err()
1297 );
1298
1299 let mut tampered_proof = range_proof.clone();
1301 assert!(!tampered_proof.siblings.is_empty());
1302 tampered_proof.siblings[0] = Sha256::hash(&[b"tampered"]);
1304 assert!(
1305 tampered_proof
1306 .verify_range_inclusion::<Sha256>(2, range_leaves, &root)
1307 .is_err()
1308 );
1309
1310 let wrong_root = Sha256::hash(&[b"wrong_root"]);
1312 assert!(
1313 range_proof
1314 .verify_range_inclusion::<Sha256>(2, range_leaves, &wrong_root)
1315 .is_err()
1316 );
1317 }
1318
1319 #[test]
1320 fn test_range_proof_various_sizes() {
1321 for tree_size in [1, 2, 3, 4, 5, 7, 8, 15, 16, 31, 32, 63, 64] {
1323 let digests: Vec<Digest> = (0..tree_size as u32)
1324 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1325 .collect();
1326
1327 let mut builder = Builder::<Sha256>::new(digests.len());
1329 for digest in &digests {
1330 builder.add(digest);
1331 }
1332 let tree = builder.build();
1333 let root = tree.root();
1334
1335 for range_size in 1..=tree_size.min(8) {
1337 for start in 0..=(tree_size - range_size) {
1338 let range_proof = tree
1339 .range_proof(start as u32, (start + range_size - 1) as u32)
1340 .unwrap();
1341 let end = start + range_size;
1342 assert!(
1343 range_proof
1344 .verify_range_inclusion::<Sha256>(
1345 start as u32,
1346 &digests[start..end],
1347 &root
1348 )
1349 .is_ok(),
1350 "Failed for tree_size={tree_size}, start={start}, range_size={range_size}"
1351 );
1352 }
1353 }
1354 }
1355 }
1356
1357 #[test]
1358 fn test_range_proof_malicious_wrong_position() {
1359 let digests: Vec<Digest> = (0..8u32)
1361 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1362 .collect();
1363
1364 let mut builder = Builder::<Sha256>::new(digests.len());
1366 for digest in &digests {
1367 builder.add(digest);
1368 }
1369 let tree = builder.build();
1370 let root = tree.root();
1371
1372 let range_proof = tree.range_proof(2, 4).unwrap();
1374 let range_leaves = &digests[2..5];
1375
1376 assert!(
1378 range_proof
1379 .verify_range_inclusion::<Sha256>(3, range_leaves, &root)
1380 .is_err()
1381 );
1382 assert!(
1383 range_proof
1384 .verify_range_inclusion::<Sha256>(1, range_leaves, &root)
1385 .is_err()
1386 );
1387 }
1388
1389 #[test]
1390 fn test_range_proof_malicious_reordered_leaves() {
1391 let digests: Vec<Digest> = (0..8u32)
1393 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1394 .collect();
1395
1396 let mut builder = Builder::<Sha256>::new(digests.len());
1398 for digest in &digests {
1399 builder.add(digest);
1400 }
1401 let tree = builder.build();
1402 let root = tree.root();
1403
1404 let range_proof = tree.range_proof(2, 4).unwrap();
1406
1407 let reordered_leaves = vec![digests[3], digests[2], digests[4]];
1409 assert!(
1410 range_proof
1411 .verify_range_inclusion::<Sha256>(2, &reordered_leaves, &root)
1412 .is_err()
1413 );
1414 }
1415
1416 #[test]
1417 fn test_range_proof_malicious_extra_siblings() {
1418 let digests: Vec<Digest> = (0..8u32)
1420 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1421 .collect();
1422
1423 let mut builder = Builder::<Sha256>::new(digests.len());
1425 for digest in &digests {
1426 builder.add(digest);
1427 }
1428 let tree = builder.build();
1429 let root = tree.root();
1430
1431 let mut range_proof = tree.range_proof(2, 3).unwrap();
1433 let range_leaves = &digests[2..4];
1434
1435 range_proof.siblings.push(Sha256::hash(&[b"extra"]));
1437 assert!(
1438 range_proof
1439 .verify_range_inclusion::<Sha256>(2, range_leaves, &root)
1440 .is_err()
1441 );
1442 }
1443
1444 #[test]
1445 fn test_range_proof_malicious_missing_siblings() {
1446 let digests: Vec<Digest> = (0..8u32)
1448 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1449 .collect();
1450
1451 let mut builder = Builder::<Sha256>::new(digests.len());
1453 for digest in &digests {
1454 builder.add(digest);
1455 }
1456 let tree = builder.build();
1457 let root = tree.root();
1458
1459 let mut range_proof = tree.range_proof(2, 2).unwrap();
1461 let range_leaves = &digests[2..3];
1462
1463 assert!(!range_proof.siblings.is_empty());
1465
1466 range_proof.siblings.pop();
1468 assert!(
1469 range_proof
1470 .verify_range_inclusion::<Sha256>(2, range_leaves, &root)
1471 .is_err()
1472 );
1473 }
1474
1475 #[test]
1476 fn test_range_proof_integer_overflow_protection() {
1477 let digests: Vec<Digest> = (0..8u32)
1479 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1480 .collect();
1481
1482 let mut builder = Builder::<Sha256>::new(digests.len());
1484 for digest in &digests {
1485 builder.add(digest);
1486 }
1487 let tree = builder.build();
1488
1489 assert!(tree.range_proof(u32::MAX, u32::MAX).is_err());
1491 assert!(tree.range_proof(u32::MAX - 1, u32::MAX).is_err());
1492 assert!(tree.range_proof(7, u32::MAX).is_err());
1493 }
1494
1495 #[test]
1496 fn test_range_proof_malicious_wrong_tree_structure() {
1497 let digests: Vec<Digest> = (0..8u32)
1499 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1500 .collect();
1501
1502 let mut builder = Builder::<Sha256>::new(digests.len());
1504 for digest in &digests {
1505 builder.add(digest);
1506 }
1507 let tree = builder.build();
1508 let root = tree.root();
1509
1510 let mut range_proof = tree.range_proof(2, 3).unwrap();
1512 let range_leaves = &digests[2..4];
1513
1514 range_proof.siblings.push(Sha256::hash(&[b"fake_level"]));
1516 assert!(
1517 range_proof
1518 .verify_range_inclusion::<Sha256>(2, range_leaves, &root)
1519 .is_err()
1520 );
1521
1522 let mut range_proof = tree.range_proof(2, 2).unwrap();
1524 let range_leaves = &digests[2..3];
1525 assert!(!range_proof.siblings.is_empty());
1526 range_proof.siblings.pop();
1527 assert!(
1528 range_proof
1529 .verify_range_inclusion::<Sha256>(2, range_leaves, &root)
1530 .is_err()
1531 );
1532 }
1533
1534 #[test]
1535 fn test_range_proof_boundary_conditions() {
1536 for tree_size in [1, 2, 4, 8, 16, 32] {
1538 let digests: Vec<Digest> = (0..tree_size as u32)
1539 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1540 .collect();
1541
1542 let mut builder = Builder::<Sha256>::new(digests.len());
1544 for digest in &digests {
1545 builder.add(digest);
1546 }
1547 let tree = builder.build();
1548 let root = tree.root();
1549
1550 let proof = tree.range_proof(0, 0).unwrap();
1553 assert!(
1554 proof
1555 .verify_range_inclusion::<Sha256>(0, &digests[0..1], &root)
1556 .is_ok()
1557 );
1558
1559 let last_idx = tree_size - 1;
1561 let proof = tree.range_proof(last_idx as u32, last_idx as u32).unwrap();
1562 assert!(
1563 proof
1564 .verify_range_inclusion::<Sha256>(
1565 last_idx as u32,
1566 &digests[last_idx..tree_size],
1567 &root
1568 )
1569 .is_ok()
1570 );
1571
1572 let proof = tree.range_proof(0, (tree_size - 1) as u32).unwrap();
1574 assert!(
1575 proof
1576 .verify_range_inclusion::<Sha256>(0, &digests, &root)
1577 .is_ok()
1578 );
1579 }
1580 }
1581
1582 #[test]
1583 fn test_empty_tree_proof() {
1584 let builder = Builder::<Sha256>::new(0);
1586 let tree = builder.build();
1587
1588 assert!(tree.proof(0).is_err());
1590 assert!(tree.proof(1).is_err());
1591 assert!(tree.proof(100).is_err());
1592 }
1593
1594 #[test]
1595 fn test_empty_tree_range_proof() {
1596 let builder = Builder::<Sha256>::new(0);
1598 let tree = builder.build();
1599 let root = tree.root();
1600
1601 let range_proof = tree.range_proof(0, 0).unwrap();
1603 assert!(range_proof.siblings.is_empty());
1604 assert_eq!(range_proof, Proof::default());
1605
1606 let invalid_ranges = vec![
1608 (0, 1),
1609 (0, 10),
1610 (1, 1),
1611 (1, 2),
1612 (5, 5),
1613 (10, 10),
1614 (0, u32::MAX),
1615 (u32::MAX, u32::MAX),
1616 ];
1617 for (start, end) in invalid_ranges {
1618 assert!(tree.range_proof(start, end).is_err());
1619 }
1620
1621 let empty_leaves: &[Digest] = &[];
1623 assert!(
1624 range_proof
1625 .verify_range_inclusion::<Sha256>(0, empty_leaves, &root)
1626 .is_ok()
1627 );
1628
1629 let non_empty_leaves = vec![Sha256::hash(&[b"leaf"])];
1631 assert!(
1632 range_proof
1633 .verify_range_inclusion::<Sha256>(0, &non_empty_leaves, &root)
1634 .is_err()
1635 );
1636
1637 let wrong_root = Sha256::hash(&[b"wrong"]);
1639 assert!(
1640 range_proof
1641 .verify_range_inclusion::<Sha256>(0, empty_leaves, &wrong_root)
1642 .is_err()
1643 );
1644
1645 assert!(
1647 range_proof
1648 .verify_range_inclusion::<Sha256>(1, empty_leaves, &root)
1649 .is_err()
1650 );
1651 }
1652
1653 #[test]
1654 fn test_empty_range_proof_serialization() {
1655 let proof = Proof::<Digest>::default();
1656 let mut serialized = proof.encode();
1657 let deserialized = Proof::<Digest>::decode_cfg(&mut serialized, &0).unwrap();
1658 assert_eq!(proof, deserialized);
1659 }
1660
1661 #[test]
1662 fn test_empty_tree_root_consistency() {
1663 let mut roots = Vec::new();
1665 for _ in 0..5 {
1666 let builder = Builder::<Sha256>::new(0);
1667 let tree = builder.build();
1668 roots.push(tree.root());
1669 }
1670
1671 for i in 1..roots.len() {
1673 assert_eq!(roots[0], roots[i]);
1674 }
1675
1676 let empty = Sha256::hash(&[b""]);
1678 let expected_root = Sha256::hash(&[&0u32.to_be_bytes(), &empty]);
1679 assert_eq!(roots[0], expected_root);
1680 }
1681
1682 #[rstest]
1683 #[case::need_left_sibling(1, 2)] #[case::need_right_sibling(4, 4)] #[case::full_tree(0, 16)] fn test_range_proof_siblings_usage(#[case] start: u32, #[case] count: u32) {
1687 let digests: Vec<Digest> = (0..16u32)
1689 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1690 .collect();
1691
1692 let mut builder = Builder::<Sha256>::new(digests.len());
1694 for digest in &digests {
1695 builder.add(digest);
1696 }
1697 let tree = builder.build();
1698 let root = tree.root();
1699
1700 let range_proof = tree.range_proof(start, start + count - 1).unwrap();
1701 let end = start as usize + count as usize;
1702
1703 assert!(
1705 range_proof
1706 .verify_range_inclusion::<Sha256>(start, &digests[start as usize..end], &root)
1707 .is_ok()
1708 );
1709
1710 for sibling_idx in 0..range_proof.siblings.len() {
1712 let mut tampered_proof = range_proof.clone();
1713 tampered_proof.siblings[sibling_idx] = Sha256::hash(&[b"tampered"]);
1714 assert!(
1715 tampered_proof
1716 .verify_range_inclusion::<Sha256>(start, &digests[start as usize..end], &root)
1717 .is_err()
1718 );
1719 }
1720 }
1721
1722 #[rstest]
1724 fn test_range_proof_duplicate_node_edge_cases(
1725 #[values(3, 5, 7, 9, 11, 13, 15)] tree_size: usize,
1726 ) {
1727 let digests: Vec<Digest> = (0..tree_size as u32)
1728 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1729 .collect();
1730
1731 let mut builder = Builder::<Sha256>::new(digests.len());
1733 for digest in &digests {
1734 builder.add(digest);
1735 }
1736 let tree = builder.build();
1737 let root = tree.root();
1738
1739 let start = tree_size - 2;
1741 let proof = tree
1742 .range_proof(start as u32, (tree_size - 1) as u32)
1743 .unwrap();
1744 assert!(
1745 proof
1746 .verify_range_inclusion::<Sha256>(start as u32, &digests[start..tree_size], &root)
1747 .is_ok()
1748 );
1749 }
1750
1751 #[test]
1752 fn test_multi_proof_basic() {
1753 let digests: Vec<Digest> = (0..8u32)
1755 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1756 .collect();
1757
1758 let mut builder = Builder::<Sha256>::new(digests.len());
1760 for digest in &digests {
1761 builder.add(digest);
1762 }
1763 let tree = builder.build();
1764 let root = tree.root();
1765
1766 let positions = [0, 3, 5];
1768 let multi_proof = tree.multi_proof(positions).unwrap();
1769
1770 let elements: Vec<(Digest, u32)> = positions
1771 .iter()
1772 .map(|&p| (digests[p as usize], p))
1773 .collect();
1774 assert!(
1775 multi_proof
1776 .verify_multi_inclusion::<Sha256>(&elements, &root)
1777 .is_ok()
1778 );
1779 }
1780
1781 #[test]
1782 fn test_multi_proof_single_element() {
1783 let digests: Vec<Digest> = (0..8u32)
1785 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1786 .collect();
1787
1788 let mut builder = Builder::<Sha256>::new(digests.len());
1790 for digest in &digests {
1791 builder.add(digest);
1792 }
1793 let tree = builder.build();
1794 let root = tree.root();
1795
1796 for (i, digest) in digests.iter().enumerate() {
1798 let multi_proof = tree.multi_proof([i as u32]).unwrap();
1799 let elements = [(*digest, i as u32)];
1800 assert!(
1801 multi_proof
1802 .verify_multi_inclusion::<Sha256>(&elements, &root)
1803 .is_ok(),
1804 "Failed for position {i}"
1805 );
1806 }
1807 }
1808
1809 #[test]
1810 fn test_multi_proof_all_elements() {
1811 let digests: Vec<Digest> = (0..8u32)
1813 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1814 .collect();
1815
1816 let mut builder = Builder::<Sha256>::new(digests.len());
1818 for digest in &digests {
1819 builder.add(digest);
1820 }
1821 let tree = builder.build();
1822 let root = tree.root();
1823
1824 let positions: Vec<u32> = (0..digests.len() as u32).collect();
1826 let multi_proof = tree.multi_proof(&positions).unwrap();
1827
1828 let elements: Vec<(Digest, u32)> = positions
1829 .iter()
1830 .map(|&p| (digests[p as usize], p))
1831 .collect();
1832 assert!(
1833 multi_proof
1834 .verify_multi_inclusion::<Sha256>(&elements, &root)
1835 .is_ok()
1836 );
1837
1838 assert!(multi_proof.siblings.is_empty());
1840 }
1841
1842 #[test]
1843 fn test_multi_proof_adjacent_elements() {
1844 let digests: Vec<Digest> = (0..8u32)
1846 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1847 .collect();
1848
1849 let mut builder = Builder::<Sha256>::new(digests.len());
1851 for digest in &digests {
1852 builder.add(digest);
1853 }
1854 let tree = builder.build();
1855 let root = tree.root();
1856
1857 let positions = [2, 3];
1859 let multi_proof = tree.multi_proof(positions).unwrap();
1860
1861 let elements: Vec<(Digest, u32)> = positions
1862 .iter()
1863 .map(|&p| (digests[p as usize], p))
1864 .collect();
1865 assert!(
1866 multi_proof
1867 .verify_multi_inclusion::<Sha256>(&elements, &root)
1868 .is_ok()
1869 );
1870 }
1871
1872 #[test]
1873 fn test_multi_proof_sparse_positions() {
1874 let digests: Vec<Digest> = (0..16u32)
1876 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1877 .collect();
1878
1879 let mut builder = Builder::<Sha256>::new(digests.len());
1881 for digest in &digests {
1882 builder.add(digest);
1883 }
1884 let tree = builder.build();
1885 let root = tree.root();
1886
1887 let positions = [0, 7, 8, 15];
1889 let multi_proof = tree.multi_proof(positions).unwrap();
1890
1891 let elements: Vec<(Digest, u32)> = positions
1892 .iter()
1893 .map(|&p| (digests[p as usize], p))
1894 .collect();
1895 assert!(
1896 multi_proof
1897 .verify_multi_inclusion::<Sha256>(&elements, &root)
1898 .is_ok()
1899 );
1900 }
1901
1902 #[test]
1903 fn test_multi_proof_empty_tree() {
1904 let builder = Builder::<Sha256>::new(0);
1906 let tree = builder.build();
1907
1908 assert!(matches!(
1911 tree.multi_proof(std::iter::empty::<u32>()),
1912 Err(Error::NoLeaves)
1913 ));
1914
1915 assert!(matches!(
1917 tree.multi_proof([0]),
1918 Err(Error::InvalidPosition(0))
1919 ));
1920 }
1921
1922 #[test]
1923 fn test_multi_proof_empty_positions() {
1924 let digests: Vec<Digest> = (0..8u32)
1926 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1927 .collect();
1928
1929 let mut builder = Builder::<Sha256>::new(digests.len());
1931 for digest in &digests {
1932 builder.add(digest);
1933 }
1934 let tree = builder.build();
1935
1936 assert!(matches!(
1938 tree.multi_proof(std::iter::empty::<u32>()),
1939 Err(Error::NoLeaves)
1940 ));
1941 }
1942
1943 #[test]
1944 fn test_multi_proof_duplicate_positions_error() {
1945 let digests: Vec<Digest> = (0..8u32)
1947 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1948 .collect();
1949
1950 let mut builder = Builder::<Sha256>::new(digests.len());
1952 for digest in &digests {
1953 builder.add(digest);
1954 }
1955 let tree = builder.build();
1956
1957 assert!(matches!(
1959 tree.multi_proof([1, 1]),
1960 Err(Error::DuplicatePosition(1))
1961 ));
1962 assert!(matches!(
1963 tree.multi_proof([0, 2, 2, 5]),
1964 Err(Error::DuplicatePosition(2))
1965 ));
1966 }
1967
1968 #[test]
1969 fn test_multi_proof_reports_first_invalid_position() {
1970 let digests: Vec<Digest> = (0..4u32)
1972 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1973 .collect();
1974 let mut builder = Builder::<Sha256>::new(digests.len());
1975 for digest in &digests {
1976 builder.add(digest);
1977 }
1978 let tree = builder.build();
1979
1980 let cases = [
1984 (vec![], Error::NoLeaves),
1985 (vec![0, 0, 4], Error::DuplicatePosition(0)),
1986 (vec![4, 0, 0], Error::InvalidPosition(4)),
1987 (vec![3, 4], Error::InvalidPosition(4)),
1988 (vec![3, 1, 1, 3], Error::UnsortedPosition(1)),
1989 (vec![1, 3, 3, 1], Error::DuplicatePosition(3)),
1990 (vec![0, 1, 2, 3, 0, 4], Error::UnsortedPosition(0)),
1991 (vec![0, 1, 2, 3, 4, 0], Error::InvalidPosition(4)),
1992 ];
1993
1994 let proof = tree.multi_proof([0, 3]).unwrap();
1997 let root = tree.root();
1998 for (positions, expected) in cases {
1999 let result = tree.multi_proof(&positions);
2000 assert_eq!(
2001 format!("{:?}", result.err()),
2002 format!("{:?}", Some(&expected)),
2003 "multi_proof({positions:?}) must report the first invalid position"
2004 );
2005
2006 let elements: Vec<_> = positions.iter().map(|&p| (digests[0], p)).collect();
2007 let result = proof.verify_multi_inclusion::<Sha256>(&elements, &root);
2008 assert_eq!(
2009 format!("{:?}", result.err()),
2010 format!("{:?}", Some(&expected)),
2011 "verify_multi_inclusion({positions:?}) must report the first invalid position"
2012 );
2013 }
2014 }
2015
2016 #[test]
2017 fn test_multi_proof_stops_reading_at_first_invalid_position() {
2018 let mut read = 0;
2021 let result =
2022 siblings_required_for_multi_proof(3, core::iter::repeat(0).inspect(|_| read += 1));
2023 assert!(matches!(result, Err(Error::DuplicatePosition(0))));
2024 assert_eq!(read, 2);
2025
2026 let mut read = 0;
2027 let result = siblings_required_for_multi_proof(3, (0..3).cycle().inspect(|_| read += 1));
2028 assert!(matches!(result, Err(Error::UnsortedPosition(0))));
2029 assert_eq!(read, 4);
2030
2031 let mut read = 0;
2032 let result = siblings_required_for_multi_proof(
2033 u32::MAX,
2034 core::iter::repeat(u32::MAX).inspect(|_| read += 1),
2035 );
2036 assert!(matches!(result, Err(Error::InvalidPosition(u32::MAX))));
2037 assert_eq!(read, 1);
2038
2039 let mut builder = Builder::<Sha256>::new(3);
2041 for i in 0..3u32 {
2042 builder.add(&Sha256::hash(&[&i.to_be_bytes()]));
2043 }
2044 let tree = builder.build();
2045 assert!(matches!(
2046 tree.multi_proof(core::iter::repeat(2u32)),
2047 Err(Error::DuplicatePosition(2))
2048 ));
2049 }
2050
2051 #[test]
2052 fn test_multi_proof_unsorted_input() {
2053 let digests: Vec<Digest> = (0..8u32)
2055 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2056 .collect();
2057
2058 let mut builder = Builder::<Sha256>::new(digests.len());
2060 for digest in &digests {
2061 builder.add(digest);
2062 }
2063 let tree = builder.build();
2064 let root = tree.root();
2065
2066 assert!(matches!(
2069 tree.multi_proof([5, 0, 3]),
2070 Err(Error::UnsortedPosition(0))
2071 ));
2072 let multi_proof = tree.multi_proof([0, 3, 5]).unwrap();
2073
2074 let unsorted_elements = [(digests[5], 5), (digests[0], 0), (digests[3], 3)];
2076 assert!(matches!(
2077 multi_proof.verify_multi_inclusion::<Sha256>(&unsorted_elements, &root),
2078 Err(Error::UnsortedPosition(0))
2079 ));
2080 let sorted_elements = [(digests[0], 0), (digests[3], 3), (digests[5], 5)];
2081 assert!(
2082 multi_proof
2083 .verify_multi_inclusion::<Sha256>(&sorted_elements, &root)
2084 .is_ok()
2085 );
2086 }
2087
2088 #[test]
2089 fn test_multi_proof_various_sizes() {
2090 for tree_size in [1, 2, 3, 4, 5, 7, 8, 15, 16, 31, 32] {
2092 let digests: Vec<Digest> = (0..tree_size as u32)
2093 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2094 .collect();
2095
2096 let mut builder = Builder::<Sha256>::new(digests.len());
2098 for digest in &digests {
2099 builder.add(digest);
2100 }
2101 let tree = builder.build();
2102 let root = tree.root();
2103
2104 if tree_size >= 2 {
2107 let positions = [0, (tree_size - 1) as u32];
2108 let multi_proof = tree.multi_proof(positions).unwrap();
2109 let elements: Vec<(Digest, u32)> = positions
2110 .iter()
2111 .map(|&p| (digests[p as usize], p))
2112 .collect();
2113 assert!(
2114 multi_proof
2115 .verify_multi_inclusion::<Sha256>(&elements, &root)
2116 .is_ok(),
2117 "Failed for tree_size={tree_size}, positions=[0, {}]",
2118 tree_size - 1
2119 );
2120 }
2121
2122 if tree_size >= 4 {
2124 let positions: Vec<u32> = (0..tree_size as u32).step_by(2).collect();
2125 let multi_proof = tree.multi_proof(&positions).unwrap();
2126 let elements: Vec<(Digest, u32)> = positions
2127 .iter()
2128 .map(|&p| (digests[p as usize], p))
2129 .collect();
2130 assert!(
2131 multi_proof
2132 .verify_multi_inclusion::<Sha256>(&elements, &root)
2133 .is_ok(),
2134 "Failed for tree_size={tree_size}, every other element"
2135 );
2136 }
2137 }
2138 }
2139
2140 #[test]
2141 fn test_multi_proof_wrong_elements() {
2142 let digests: Vec<Digest> = (0..8u32)
2144 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2145 .collect();
2146
2147 let mut builder = Builder::<Sha256>::new(digests.len());
2149 for digest in &digests {
2150 builder.add(digest);
2151 }
2152 let tree = builder.build();
2153 let root = tree.root();
2154
2155 let positions = [0, 3, 5];
2157 let multi_proof = tree.multi_proof(positions).unwrap();
2158
2159 let wrong_elements = [
2161 (Sha256::hash(&[b"wrong1"]), 0),
2162 (digests[3], 3),
2163 (digests[5], 5),
2164 ];
2165 assert!(
2166 multi_proof
2167 .verify_multi_inclusion::<Sha256>(&wrong_elements, &root)
2168 .is_err()
2169 );
2170 }
2171
2172 #[test]
2173 fn test_multi_proof_wrong_positions() {
2174 let digests: Vec<Digest> = (0..8u32)
2176 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2177 .collect();
2178
2179 let mut builder = Builder::<Sha256>::new(digests.len());
2181 for digest in &digests {
2182 builder.add(digest);
2183 }
2184 let tree = builder.build();
2185 let root = tree.root();
2186
2187 let positions = [0, 3, 5];
2189 let multi_proof = tree.multi_proof(positions).unwrap();
2190
2191 let wrong_positions = [
2193 (digests[0], 1), (digests[3], 3),
2195 (digests[5], 5),
2196 ];
2197 assert!(
2198 multi_proof
2199 .verify_multi_inclusion::<Sha256>(&wrong_positions, &root)
2200 .is_err()
2201 );
2202 }
2203
2204 #[test]
2205 fn test_multi_proof_wrong_root() {
2206 let digests: Vec<Digest> = (0..8u32)
2208 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2209 .collect();
2210
2211 let mut builder = Builder::<Sha256>::new(digests.len());
2213 for digest in &digests {
2214 builder.add(digest);
2215 }
2216 let tree = builder.build();
2217
2218 let positions = [0, 3, 5];
2220 let multi_proof = tree.multi_proof(positions).unwrap();
2221
2222 let elements: Vec<(Digest, u32)> = positions
2223 .iter()
2224 .map(|&p| (digests[p as usize], p))
2225 .collect();
2226
2227 let wrong_root = Sha256::hash(&[b"wrong_root"]);
2229 assert!(
2230 multi_proof
2231 .verify_multi_inclusion::<Sha256>(&elements, &wrong_root)
2232 .is_err()
2233 );
2234 }
2235
2236 #[test]
2237 fn test_multi_proof_tampering() {
2238 let digests: Vec<Digest> = (0..8u32)
2240 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2241 .collect();
2242
2243 let mut builder = Builder::<Sha256>::new(digests.len());
2245 for digest in &digests {
2246 builder.add(digest);
2247 }
2248 let tree = builder.build();
2249 let root = tree.root();
2250
2251 let positions = [0, 5];
2253 let multi_proof = tree.multi_proof(positions).unwrap();
2254
2255 let elements: Vec<(Digest, u32)> = positions
2256 .iter()
2257 .map(|&p| (digests[p as usize], p))
2258 .collect();
2259
2260 assert!(!multi_proof.siblings.is_empty());
2262 let mut modified = multi_proof.clone();
2263 modified.siblings[0] = Sha256::hash(&[b"tampered"]);
2264 assert!(
2265 modified
2266 .verify_multi_inclusion::<Sha256>(&elements, &root)
2267 .is_err()
2268 );
2269
2270 let mut extra = multi_proof.clone();
2272 extra.siblings.push(Sha256::hash(&[b"extra"]));
2273 assert!(
2274 extra
2275 .verify_multi_inclusion::<Sha256>(&elements, &root)
2276 .is_err()
2277 );
2278
2279 let mut missing = multi_proof;
2281 missing.siblings.pop();
2282 assert!(
2283 missing
2284 .verify_multi_inclusion::<Sha256>(&elements, &root)
2285 .is_err()
2286 );
2287 }
2288
2289 #[test]
2290 fn test_multi_proof_deduplication() {
2291 let digests: Vec<Digest> = (0..16u32)
2293 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2294 .collect();
2295
2296 let mut builder = Builder::<Sha256>::new(digests.len());
2298 for digest in &digests {
2299 builder.add(digest);
2300 }
2301 let tree = builder.build();
2302
2303 let individual_siblings: usize = [0u32, 1, 8, 9]
2305 .iter()
2306 .map(|&p| tree.proof(p).unwrap().siblings.len())
2307 .sum();
2308
2309 let multi_proof = tree.multi_proof([0, 1, 8, 9]).unwrap();
2311
2312 assert!(
2314 multi_proof.siblings.len() < individual_siblings,
2315 "Multi-proof ({}) should have fewer siblings than sum of individual proofs ({})",
2316 multi_proof.siblings.len(),
2317 individual_siblings
2318 );
2319 }
2320
2321 #[test]
2322 fn test_multi_proof_serialization() {
2323 let digests: Vec<Digest> = (0..8u32)
2325 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2326 .collect();
2327
2328 let mut builder = Builder::<Sha256>::new(digests.len());
2330 for digest in &digests {
2331 builder.add(digest);
2332 }
2333 let tree = builder.build();
2334 let root = tree.root();
2335
2336 let positions = [0, 3, 5];
2338 let multi_proof = tree.multi_proof(positions).unwrap();
2339
2340 let serialized = multi_proof.encode();
2342 let deserialized = Proof::<Digest>::decode_cfg(serialized, &positions.len()).unwrap();
2343
2344 assert_eq!(multi_proof, deserialized);
2345
2346 let elements: Vec<(Digest, u32)> = positions
2348 .iter()
2349 .map(|&p| (digests[p as usize], p))
2350 .collect();
2351 assert!(
2352 deserialized
2353 .verify_multi_inclusion::<Sha256>(&elements, &root)
2354 .is_ok()
2355 );
2356 }
2357
2358 #[test]
2359 fn test_multi_proof_serialization_truncated() {
2360 let digests: Vec<Digest> = (0..8u32)
2362 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2363 .collect();
2364
2365 let mut builder = Builder::<Sha256>::new(digests.len());
2367 for digest in &digests {
2368 builder.add(digest);
2369 }
2370 let tree = builder.build();
2371
2372 let positions = [0, 3, 5];
2374 let multi_proof = tree.multi_proof(positions).unwrap();
2375
2376 let mut serialized = multi_proof.encode();
2378 serialized.truncate(serialized.len() - 1);
2379
2380 assert!(Proof::<Digest>::decode_cfg(&mut serialized, &positions.len()).is_err());
2382 }
2383
2384 #[test]
2385 fn test_multi_proof_serialization_extra() {
2386 let digests: Vec<Digest> = (0..8u32)
2388 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2389 .collect();
2390
2391 let mut builder = Builder::<Sha256>::new(digests.len());
2393 for digest in &digests {
2394 builder.add(digest);
2395 }
2396 let tree = builder.build();
2397
2398 let positions = [0, 3, 5];
2400 let multi_proof = tree.multi_proof(positions).unwrap();
2401
2402 let mut serialized = multi_proof.encode_mut();
2404 serialized.extend_from_slice(&[0u8]);
2405
2406 assert!(Proof::<Digest>::decode_cfg(&mut serialized, &positions.len()).is_err());
2408 }
2409
2410 #[test]
2411 fn test_multi_proof_decode_insufficient_data() {
2412 let mut serialized = Vec::new();
2413 serialized.extend_from_slice(&8u32.encode()); serialized.extend_from_slice(&1usize.encode()); let err = Proof::<Digest>::decode_cfg(serialized, &1).unwrap_err();
2418 assert!(matches!(err, commonware_codec::Error::EndOfBuffer));
2419 }
2420
2421 #[test]
2422 fn test_multi_proof_invalid_position() {
2423 let digests: Vec<Digest> = (0..8u32)
2425 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2426 .collect();
2427
2428 let mut builder = Builder::<Sha256>::new(digests.len());
2430 for digest in &digests {
2431 builder.add(digest);
2432 }
2433 let tree = builder.build();
2434
2435 assert!(matches!(
2437 tree.multi_proof([0, 8]),
2438 Err(Error::InvalidPosition(8))
2439 ));
2440 assert!(matches!(
2441 tree.multi_proof([100]),
2442 Err(Error::InvalidPosition(100))
2443 ));
2444 }
2445
2446 #[test]
2447 fn test_multi_proof_verify_invalid_position() {
2448 let digests: Vec<Digest> = (0..8u32)
2450 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2451 .collect();
2452
2453 let mut builder = Builder::<Sha256>::new(digests.len());
2455 for digest in &digests {
2456 builder.add(digest);
2457 }
2458 let tree = builder.build();
2459 let root = tree.root();
2460
2461 let positions = [0, 3];
2463 let multi_proof = tree.multi_proof(positions).unwrap();
2464
2465 let invalid_elements = [(digests[0], 0), (digests[3], 100)];
2467 assert!(
2468 multi_proof
2469 .verify_multi_inclusion::<Sha256>(&invalid_elements, &root)
2470 .is_err()
2471 );
2472 }
2473
2474 #[test]
2475 fn test_multi_proof_odd_tree_sizes() {
2476 for tree_size in [3, 5, 7, 9, 11, 13, 15] {
2478 let digests: Vec<Digest> = (0..tree_size as u32)
2479 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2480 .collect();
2481
2482 let mut builder = Builder::<Sha256>::new(digests.len());
2484 for digest in &digests {
2485 builder.add(digest);
2486 }
2487 let tree = builder.build();
2488 let root = tree.root();
2489
2490 let positions = [0, (tree_size - 1) as u32];
2492 let multi_proof = tree.multi_proof(positions).unwrap();
2493
2494 let elements: Vec<(Digest, u32)> = positions
2495 .iter()
2496 .map(|&p| (digests[p as usize], p))
2497 .collect();
2498 assert!(
2499 multi_proof
2500 .verify_multi_inclusion::<Sha256>(&elements, &root)
2501 .is_ok(),
2502 "Failed for tree_size={tree_size}"
2503 );
2504 }
2505 }
2506
2507 #[test]
2508 fn test_multi_proof_verify_empty_elements() {
2509 let digests: Vec<Digest> = (0..8u32)
2511 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2512 .collect();
2513
2514 let mut builder = Builder::<Sha256>::new(digests.len());
2515 for digest in &digests {
2516 builder.add(digest);
2517 }
2518 let tree = builder.build();
2519 let root = tree.root();
2520
2521 let positions = [0, 3];
2523 let multi_proof = tree.multi_proof(positions).unwrap();
2524
2525 let empty_elements: &[(Digest, u32)] = &[];
2527 assert!(
2528 multi_proof
2529 .verify_multi_inclusion::<Sha256>(empty_elements, &root)
2530 .is_err()
2531 );
2532 }
2533
2534 #[test]
2535 fn test_multi_proof_default_verify() {
2536 let default_proof = Proof::<Digest>::default();
2538
2539 let empty_elements: &[(Digest, u32)] = &[];
2541
2542 let builder = Builder::<Sha256>::new(0);
2544 let empty_tree = builder.build();
2545 let empty_root = empty_tree.root();
2546
2547 assert!(
2548 default_proof
2549 .verify_multi_inclusion::<Sha256>(empty_elements, &empty_root)
2550 .is_ok()
2551 );
2552
2553 let wrong_root = Sha256::hash(&[b"not_empty"]);
2555 assert!(
2556 default_proof
2557 .verify_multi_inclusion::<Sha256>(empty_elements, &wrong_root)
2558 .is_err()
2559 );
2560 }
2561
2562 #[test]
2563 fn test_multi_proof_single_leaf_tree() {
2564 let digest = Sha256::hash(&[b"only_leaf"]);
2566
2567 let mut builder = Builder::<Sha256>::new(1);
2569 builder.add(&digest);
2570 let tree = builder.build();
2571 let root = tree.root();
2572
2573 let multi_proof = tree.multi_proof([0]).unwrap();
2575
2576 assert_eq!(multi_proof.leaf_count, 1);
2578
2579 assert!(
2581 multi_proof.siblings.is_empty(),
2582 "Single leaf tree should have no siblings"
2583 );
2584
2585 let elements = [(digest, 0u32)];
2587 assert!(
2588 multi_proof
2589 .verify_multi_inclusion::<Sha256>(&elements, &root)
2590 .is_ok(),
2591 "Single leaf multi-proof verification failed"
2592 );
2593
2594 let wrong_digest = Sha256::hash(&[b"wrong"]);
2596 let wrong_elements = [(wrong_digest, 0u32)];
2597 assert!(
2598 multi_proof
2599 .verify_multi_inclusion::<Sha256>(&wrong_elements, &root)
2600 .is_err(),
2601 "Should fail with wrong digest"
2602 );
2603
2604 let wrong_position_elements = [(digest, 1u32)];
2606 assert!(
2607 multi_proof
2608 .verify_multi_inclusion::<Sha256>(&wrong_position_elements, &root)
2609 .is_err(),
2610 "Should fail with invalid position"
2611 );
2612 }
2613
2614 #[test]
2615 fn test_multi_proof_malicious_leaf_count_zero() {
2616 let digests: Vec<Digest> = (0..8u32)
2618 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2619 .collect();
2620
2621 let mut builder = Builder::<Sha256>::new(digests.len());
2622 for digest in &digests {
2623 builder.add(digest);
2624 }
2625 let tree = builder.build();
2626 let root = tree.root();
2627
2628 let positions = [0, 3];
2630 let mut multi_proof = tree.multi_proof(positions).unwrap();
2631 multi_proof.leaf_count = 0;
2632
2633 let elements: Vec<(Digest, u32)> = positions
2634 .iter()
2635 .map(|&p| (digests[p as usize], p))
2636 .collect();
2637
2638 assert!(
2640 multi_proof
2641 .verify_multi_inclusion::<Sha256>(&elements, &root)
2642 .is_err()
2643 );
2644 }
2645
2646 #[test]
2647 fn test_multi_proof_malicious_leaf_count_larger() {
2648 let digests: Vec<Digest> = (0..8u32)
2650 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2651 .collect();
2652
2653 let mut builder = Builder::<Sha256>::new(digests.len());
2654 for digest in &digests {
2655 builder.add(digest);
2656 }
2657 let tree = builder.build();
2658 let root = tree.root();
2659
2660 let positions = [0, 3];
2662 let mut multi_proof = tree.multi_proof(positions).unwrap();
2663 let original_leaf_count = multi_proof.leaf_count;
2664 multi_proof.leaf_count = 1000;
2665
2666 let elements: Vec<(Digest, u32)> = positions
2667 .iter()
2668 .map(|&p| (digests[p as usize], p))
2669 .collect();
2670
2671 assert!(
2673 multi_proof
2674 .verify_multi_inclusion::<Sha256>(&elements, &root)
2675 .is_err(),
2676 "Should reject proof with inflated leaf_count ({} -> {})",
2677 original_leaf_count,
2678 multi_proof.leaf_count
2679 );
2680 }
2681
2682 #[test]
2683 fn test_multi_proof_malicious_leaf_count_smaller() {
2684 let digests: Vec<Digest> = (0..8u32)
2686 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2687 .collect();
2688
2689 let mut builder = Builder::<Sha256>::new(digests.len());
2690 for digest in &digests {
2691 builder.add(digest);
2692 }
2693 let tree = builder.build();
2694 let root = tree.root();
2695
2696 let positions = [0, 3];
2698 let mut multi_proof = tree.multi_proof(positions).unwrap();
2699 multi_proof.leaf_count = 4; let elements: Vec<(Digest, u32)> = positions
2702 .iter()
2703 .map(|&p| (digests[p as usize], p))
2704 .collect();
2705
2706 assert!(
2708 multi_proof
2709 .verify_multi_inclusion::<Sha256>(&elements, &root)
2710 .is_err(),
2711 "Should reject proof with deflated leaf_count"
2712 );
2713 }
2714
2715 #[test]
2716 fn test_multi_proof_mismatched_element_count() {
2717 let digests: Vec<Digest> = (0..8u32)
2719 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2720 .collect();
2721
2722 let mut builder = Builder::<Sha256>::new(digests.len());
2723 for digest in &digests {
2724 builder.add(digest);
2725 }
2726 let tree = builder.build();
2727 let root = tree.root();
2728
2729 let positions = [0, 3];
2731 let multi_proof = tree.multi_proof(positions).unwrap();
2732
2733 let too_few = [(digests[0], 0u32)];
2735 assert!(
2736 multi_proof
2737 .verify_multi_inclusion::<Sha256>(&too_few, &root)
2738 .is_err(),
2739 "Should reject when fewer elements provided than proof was generated for"
2740 );
2741
2742 let too_many = [(digests[0], 0u32), (digests[3], 3), (digests[5], 5)];
2744 assert!(
2745 multi_proof
2746 .verify_multi_inclusion::<Sha256>(&too_many, &root)
2747 .is_err(),
2748 "Should reject when more elements provided than proof was generated for"
2749 );
2750 }
2751
2752 #[test]
2753 fn test_multi_proof_swapped_siblings() {
2754 let digests: Vec<Digest> = (0..8u32)
2756 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2757 .collect();
2758
2759 let mut builder = Builder::<Sha256>::new(digests.len());
2760 for digest in &digests {
2761 builder.add(digest);
2762 }
2763 let tree = builder.build();
2764 let root = tree.root();
2765
2766 let positions = [0, 5];
2768 let mut multi_proof = tree.multi_proof(positions).unwrap();
2769
2770 if multi_proof.siblings.len() >= 2 {
2772 multi_proof.siblings.swap(0, 1);
2774
2775 let elements: Vec<(Digest, u32)> = positions
2776 .iter()
2777 .map(|&p| (digests[p as usize], p))
2778 .collect();
2779
2780 assert!(
2781 multi_proof
2782 .verify_multi_inclusion::<Sha256>(&elements, &root)
2783 .is_err(),
2784 "Should reject proof with swapped siblings"
2785 );
2786 }
2787 }
2788
2789 #[test]
2790 fn test_multi_proof_dos_large_leaf_count() {
2791 let digests: Vec<Digest> = (0..4u32)
2794 .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2795 .collect();
2796
2797 let mut builder = Builder::<Sha256>::new(digests.len());
2798 for digest in &digests {
2799 builder.add(digest);
2800 }
2801 let tree = builder.build();
2802 let root = tree.root();
2803
2804 let positions = [0, 2];
2806 let mut multi_proof = tree.multi_proof(positions).unwrap();
2807
2808 multi_proof.leaf_count = u32::MAX;
2810
2811 let elements: Vec<(Digest, u32)> = positions
2812 .iter()
2813 .map(|&p| (digests[p as usize], p))
2814 .collect();
2815
2816 let result = multi_proof.verify_multi_inclusion::<Sha256>(&elements, &root);
2819 assert!(result.is_err(), "Should reject malicious large leaf_count");
2820 }
2821
2822 #[cfg(feature = "arbitrary")]
2823 mod conformance {
2824 use super::*;
2825 use commonware_codec::conformance::CodecConformance;
2826 use commonware_conformance::Conformance;
2827 use commonware_cryptography::sha256::Digest as Sha256Digest;
2828
2829 fn test_merkle_tree(n: usize) -> Digest {
2830 let mut digests = Vec::with_capacity(n);
2832 let mut builder = Builder::<Sha256>::new(n);
2833 for i in 0..n {
2834 let digest = Sha256::hash(&[&i.to_be_bytes()]);
2835 builder.add(&digest);
2836 digests.push(digest);
2837 }
2838 let tree = builder.build();
2839 let root = tree.root();
2840
2841 for (i, leaf) in digests.iter().enumerate() {
2843 let proof = tree.proof(i as u32).unwrap();
2845 assert!(
2846 proof
2847 .verify_element_inclusion::<Sha256>(leaf, i as u32, &root)
2848 .is_ok(),
2849 "correct fail for size={n} leaf={i}"
2850 );
2851
2852 let serialized = proof.encode();
2854 let deserialized = Proof::<Digest>::decode_cfg(serialized, &1).unwrap();
2855 assert!(
2856 deserialized
2857 .verify_element_inclusion::<Sha256>(leaf, i as u32, &root)
2858 .is_ok(),
2859 "deserialize fail for size={n} leaf={i}"
2860 );
2861
2862 if !proof.siblings.is_empty() {
2864 let mut update_tamper = proof.clone();
2865 update_tamper.siblings[0] = Sha256::hash(&[b"tampered"]);
2866 assert!(
2867 update_tamper
2868 .verify_element_inclusion::<Sha256>(leaf, i as u32, &root)
2869 .is_err(),
2870 "modify fail for size={n} leaf={i}"
2871 );
2872 }
2873
2874 let mut add_tamper = proof.clone();
2876 add_tamper.siblings.push(Sha256::hash(&[b"tampered"]));
2877 assert!(
2878 add_tamper
2879 .verify_element_inclusion::<Sha256>(leaf, i as u32, &root)
2880 .is_err(),
2881 "add fail for size={n} leaf={i}"
2882 );
2883
2884 if !proof.siblings.is_empty() {
2886 let mut remove_tamper = proof.clone();
2887 remove_tamper.siblings.pop();
2888 assert!(
2889 remove_tamper
2890 .verify_element_inclusion::<Sha256>(leaf, i as u32, &root)
2891 .is_err(),
2892 "remove fail for size={n} leaf={i}"
2893 );
2894 }
2895 }
2896
2897 assert!(tree.proof(n as u32).is_err());
2899
2900 root
2902 }
2903
2904 struct RootConformance;
2905
2906 impl Conformance for RootConformance {
2907 async fn commit(seed: u64) -> Vec<u8> {
2908 let root = test_merkle_tree(seed as usize);
2909 root.to_vec()
2910 }
2911 }
2912
2913 commonware_conformance::conformance_tests! {
2914 CodecConformance<Proof<Sha256Digest>>,
2915 RootConformance => 200
2916 }
2917 }
2918}