1use alloc::{
46 collections::btree_set::BTreeSet,
47 string::{String, ToString},
48 vec,
49 vec::Vec,
50};
51use bytes::{Buf, BufMut};
52use commonware_codec::{EncodeSize, Read, ReadExt, ReadRangeExt, Write};
53use commonware_cryptography::{Digest, Hasher};
54use commonware_utils::{non_empty_vec, vec::NonEmptyVec};
55use thiserror::Error;
56
57pub const MAX_LEVELS: usize = u8::MAX as usize;
60
61#[derive(Error, Debug)]
63pub enum Error {
64 #[error("invalid position: {0}")]
65 InvalidPosition(u32),
66 #[error("invalid proof: {0} != {1}")]
67 InvalidProof(String, String),
68 #[error("no leaves")]
69 NoLeaves,
70 #[error("unaligned proof")]
71 UnalignedProof,
72 #[error("duplicate position: {0}")]
73 DuplicatePosition(u32),
74}
75
76pub struct Builder<H: Hasher> {
78 hasher: H,
79 leaves: Vec<H::Digest>,
80}
81
82impl<H: Hasher> Builder<H> {
83 pub fn new(leaves: usize) -> Self {
85 Self {
86 hasher: H::new(),
87 leaves: Vec::with_capacity(leaves),
88 }
89 }
90
91 pub fn add(&mut self, leaf: &H::Digest) -> u32 {
95 let position: u32 = self.leaves.len().try_into().expect("too many leaves");
96 self.hasher.update(&position.to_be_bytes());
97 self.hasher.update(leaf);
98 self.leaves.push(self.hasher.finalize());
99 position
100 }
101
102 pub fn build(self) -> Tree<H::Digest> {
107 Tree::new(self.hasher, self.leaves)
108 }
109}
110
111#[derive(Clone, Debug)]
113pub struct Tree<D: Digest> {
114 empty: bool,
116
117 levels: NonEmptyVec<NonEmptyVec<D>>,
119
120 root: D,
126}
127
128impl<D: Digest> Tree<D> {
129 fn new<H: Hasher<Digest = D>>(mut hasher: H, mut leaves: Vec<D>) -> Self {
131 let mut empty = false;
136 let leaf_count = leaves.len() as u32;
137 if leaves.is_empty() {
138 leaves.push(hasher.finalize());
139 empty = true;
140 }
141
142 let mut levels = non_empty_vec![non_empty_vec![@leaves]];
144
145 let mut current_level = levels.last();
147 while !current_level.is_singleton() {
148 let mut next_level = Vec::with_capacity(current_level.len().get().div_ceil(2));
149 for chunk in current_level.chunks(2) {
150 hasher.update(&chunk[0]);
152
153 if chunk.len() == 2 {
155 hasher.update(&chunk[1]);
156 } else {
157 hasher.update(&chunk[0]);
159 };
160
161 next_level.push(hasher.finalize());
163 }
164
165 levels.push(non_empty_vec![@next_level]);
167 current_level = levels.last();
168 }
169
170 let tree_root = levels.last().first();
173 hasher.update(&leaf_count.to_be_bytes());
174 hasher.update(tree_root);
175 let root = hasher.finalize();
176
177 Self {
178 empty,
179 levels,
180 root,
181 }
182 }
183
184 pub const fn root(&self) -> D {
190 self.root
191 }
192
193 pub fn proof(&self, position: u32) -> Result<Proof<D>, Error> {
198 self.multi_proof(core::iter::once(position))
199 }
200
201 pub fn range_proof(&self, start: u32, end: u32) -> Result<Proof<D>, Error> {
208 if self.empty {
210 if start == 0 && end == 0 {
211 return Ok(Proof::default());
212 }
213 return Err(Error::InvalidPosition(start));
214 }
215
216 if start > end {
218 return Err(Error::InvalidPosition(start));
219 }
220 let leaf_count = self.levels.first().len().get() as u32;
221 if start >= leaf_count {
222 return Err(Error::InvalidPosition(start));
223 }
224 if end >= leaf_count {
225 return Err(Error::InvalidPosition(end));
226 }
227
228 let sibling_positions = siblings_required_for_range_proof(leaf_count, start, end)?;
230 let siblings: Vec<D> = sibling_positions
231 .iter()
232 .map(|&(level, index)| self.levels[level][index])
233 .collect();
234
235 Ok(Proof {
236 leaf_count,
237 siblings,
238 })
239 }
240
241 pub fn multi_proof<I, P>(&self, positions: I) -> Result<Proof<D>, Error>
250 where
251 I: IntoIterator<Item = P>,
252 P: core::borrow::Borrow<u32>,
253 {
254 let mut positions = positions.into_iter().peekable();
255
256 let first = *positions.peek().ok_or(Error::NoLeaves)?.borrow();
258
259 if self.empty {
261 return Err(Error::InvalidPosition(first));
262 }
263
264 let leaf_count = self.levels.first().len().get() as u32;
265
266 let sibling_positions =
268 siblings_required_for_multi_proof(leaf_count, positions.map(|p| *p.borrow()))?;
269
270 let siblings: Vec<D> = sibling_positions
272 .iter()
273 .map(|&(level, index)| self.levels[level][index])
274 .collect();
275
276 Ok(Proof {
277 leaf_count,
278 siblings,
279 })
280 }
281}
282
283#[derive(Clone, Debug, Eq, PartialEq)]
293pub struct Proof<D: Digest> {
294 pub leaf_count: u32,
299
300 pub siblings: Vec<D>,
303}
304
305impl<D: Digest> Default for Proof<D> {
306 fn default() -> Self {
307 Self {
308 leaf_count: 0,
309 siblings: Vec::new(),
310 }
311 }
312}
313
314impl<D: Digest> Write for Proof<D> {
315 fn write(&self, writer: &mut impl BufMut) {
316 self.leaf_count.write(writer);
317 self.siblings.write(writer);
318 }
319}
320
321impl<D: Digest> Read for Proof<D> {
322 type Cfg = usize;
326
327 fn read_cfg(
328 reader: &mut impl Buf,
329 max_items: &Self::Cfg,
330 ) -> Result<Self, commonware_codec::Error> {
331 let leaf_count = u32::read(reader)?;
332 let max_siblings = max_items.saturating_mul(MAX_LEVELS);
333 let siblings = Vec::<D>::read_range(reader, ..=max_siblings)?;
334 Ok(Self {
335 leaf_count,
336 siblings,
337 })
338 }
339}
340
341impl<D: Digest> EncodeSize for Proof<D> {
342 fn encode_size(&self) -> usize {
343 self.leaf_count.encode_size() + self.siblings.encode_size()
344 }
345}
346
347#[cfg(feature = "arbitrary")]
348impl<D: Digest> arbitrary::Arbitrary<'_> for Proof<D>
349where
350 D: for<'a> arbitrary::Arbitrary<'a>,
351{
352 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
353 Ok(Self {
354 leaf_count: u.arbitrary()?,
355 siblings: u.arbitrary()?,
356 })
357 }
358}
359
360const fn levels_in_tree(leaf_count: u32) -> usize {
363 (u32::BITS - (leaf_count.saturating_sub(1)).leading_zeros() + 1) as usize
364}
365
366fn siblings_required_for_multi_proof(
371 leaf_count: u32,
372 positions: impl IntoIterator<Item = u32>,
373) -> Result<BTreeSet<(usize, usize)>, Error> {
374 let mut current = BTreeSet::new();
376 for pos in positions {
377 if pos >= leaf_count {
378 return Err(Error::InvalidPosition(pos));
379 }
380 if !current.insert(pos as usize) {
381 return Err(Error::DuplicatePosition(pos));
382 }
383 }
384
385 if current.is_empty() {
386 return Err(Error::NoLeaves);
387 }
388
389 let mut sibling_positions = BTreeSet::new();
392 let levels_count = levels_in_tree(leaf_count);
393 let mut level_size = leaf_count as usize;
394 for level in 0..levels_count - 1 {
395 for &index in ¤t {
396 let sibling_index = if index.is_multiple_of(2) {
397 if index + 1 < level_size {
398 index + 1
399 } else {
400 index
401 }
402 } else {
403 index - 1
404 };
405
406 if sibling_index != index && !current.contains(&sibling_index) {
407 sibling_positions.insert((level, sibling_index));
408 }
409 }
410
411 current = current.iter().map(|idx| idx / 2).collect();
412 level_size = level_size.div_ceil(2);
413 }
414
415 Ok(sibling_positions)
416}
417
418fn siblings_required_for_range_proof(
421 leaf_count: u32,
422 start: u32,
423 end: u32,
424) -> Result<BTreeSet<(usize, usize)>, Error> {
425 if leaf_count == 0 {
426 return Err(Error::NoLeaves);
427 }
428 if start > end {
429 return Err(Error::InvalidPosition(start));
430 }
431 if start >= leaf_count {
432 return Err(Error::InvalidPosition(start));
433 }
434 if end >= leaf_count {
435 return Err(Error::InvalidPosition(end));
436 }
437
438 let mut sibling_positions = BTreeSet::new();
439 let levels_count = levels_in_tree(leaf_count);
440 let mut level_start = start as usize;
441 let mut level_end = end as usize;
442 let mut level_size = leaf_count as usize;
443
444 for level in 0..levels_count - 1 {
445 if !level_start.is_multiple_of(2) {
446 sibling_positions.insert((level, level_start - 1));
447 }
448 if level_end.is_multiple_of(2) {
449 let right = level_end + 1;
450 if right < level_size {
451 sibling_positions.insert((level, right));
452 }
453 }
454
455 level_start /= 2;
456 level_end /= 2;
457 level_size = level_size.div_ceil(2);
458 }
459
460 Ok(sibling_positions)
461}
462
463impl<D: Digest> Proof<D> {
464 pub fn verify_element_inclusion<H: Hasher<Digest = D>>(
475 &self,
476 hasher: &mut H,
477 leaf: &D,
478 mut position: u32,
479 root: &D,
480 ) -> Result<(), Error> {
481 if position >= self.leaf_count {
483 return Err(Error::InvalidPosition(position));
484 }
485
486 hasher.update(&position.to_be_bytes());
488 hasher.update(leaf);
489 let mut computed = hasher.finalize();
490
491 let mut level_size = self.leaf_count as usize;
493 let mut sibling_iter = self.siblings.iter();
494
495 while level_size > 1 {
497 let is_last_odd = position.is_multiple_of(2) && position as usize + 1 >= level_size;
499
500 let (left_node, right_node) = if is_last_odd {
501 (&computed, &computed)
503 } else if position.is_multiple_of(2) {
504 let sibling = sibling_iter.next().ok_or(Error::UnalignedProof)?;
506 (&computed, sibling)
507 } else {
508 let sibling = sibling_iter.next().ok_or(Error::UnalignedProof)?;
510 (sibling, &computed)
511 };
512
513 hasher.update(left_node);
515 hasher.update(right_node);
516 computed = hasher.finalize();
517
518 position /= 2;
520 level_size = level_size.div_ceil(2);
521 }
522
523 if sibling_iter.next().is_some() {
525 return Err(Error::UnalignedProof);
526 }
527
528 hasher.update(&self.leaf_count.to_be_bytes());
531 hasher.update(&computed);
532 let finalized = hasher.finalize();
533
534 if finalized == *root {
535 Ok(())
536 } else {
537 Err(Error::InvalidProof(finalized.to_string(), root.to_string()))
538 }
539 }
540
541 pub fn verify_multi_inclusion<H: Hasher<Digest = D>>(
550 &self,
551 hasher: &mut H,
552 elements: &[(D, u32)],
553 root: &D,
554 ) -> Result<(), Error> {
555 if elements.is_empty() {
557 if self.leaf_count == 0 && self.siblings.is_empty() {
558 let empty_tree_root = hasher.finalize();
560 hasher.update(&0u32.to_be_bytes());
561 hasher.update(&empty_tree_root);
562 let finalized = hasher.finalize();
563 if finalized == *root {
564 return Ok(());
565 } else {
566 return Err(Error::InvalidProof(finalized.to_string(), root.to_string()));
567 }
568 }
569 return Err(Error::NoLeaves);
570 }
571
572 let mut sorted: Vec<(u32, D)> = Vec::with_capacity(elements.len());
574 for (leaf, position) in elements {
575 if *position >= self.leaf_count {
576 return Err(Error::InvalidPosition(*position));
577 }
578 hasher.update(&position.to_be_bytes());
579 hasher.update(leaf);
580 sorted.push((*position, hasher.finalize()));
581 }
582 sorted.sort_unstable_by_key(|(pos, _)| *pos);
583
584 for i in 1..sorted.len() {
586 if sorted[i - 1].0 == sorted[i].0 {
587 return Err(Error::DuplicatePosition(sorted[i].0));
588 }
589 }
590
591 let levels = levels_in_tree(self.leaf_count);
594 let mut level_size = self.leaf_count;
595 let mut sibling_iter = self.siblings.iter();
596 let mut current = sorted;
597 let mut next_level: Vec<(u32, D)> = Vec::with_capacity(current.len());
598
599 for _ in 0..levels - 1 {
600 let mut idx = 0;
601 while idx < current.len() {
602 let (pos, digest) = current[idx];
603 let parent_pos = pos / 2;
604
605 let (left, right) = if pos.is_multiple_of(2) {
607 let left = digest;
609
610 let right = if idx + 1 < current.len() && current[idx + 1].0 == pos + 1 {
612 idx += 1;
613 current[idx].1
614 } else if pos + 1 >= level_size {
615 left
617 } else {
618 *sibling_iter.next().ok_or(Error::UnalignedProof)?
620 };
621 (left, right)
622 } else {
623 let right = digest;
626 let left = *sibling_iter.next().ok_or(Error::UnalignedProof)?;
627 (left, right)
628 };
629
630 hasher.update(&left);
632 hasher.update(&right);
633 next_level.push((parent_pos, hasher.finalize()));
634
635 idx += 1;
636 }
637
638 core::mem::swap(&mut current, &mut next_level);
640 next_level.clear();
641 level_size = level_size.div_ceil(2);
642 }
643
644 if sibling_iter.next().is_some() {
646 return Err(Error::UnalignedProof);
647 }
648
649 if current.len() != 1 {
650 return Err(Error::UnalignedProof);
651 }
652
653 let tree_root = current[0].1;
656 hasher.update(&self.leaf_count.to_be_bytes());
657 hasher.update(&tree_root);
658 let finalized = hasher.finalize();
659
660 if finalized == *root {
661 Ok(())
662 } else {
663 Err(Error::InvalidProof(finalized.to_string(), root.to_string()))
664 }
665 }
666
667 pub fn verify_range_inclusion<H: Hasher<Digest = D>>(
676 &self,
677 hasher: &mut H,
678 position: u32,
679 leaves: &[D],
680 root: &D,
681 ) -> Result<(), Error> {
682 if leaves.is_empty() && position != 0 {
684 return Err(Error::InvalidPosition(position));
685 }
686 if !leaves.is_empty() {
687 let leaves_len =
688 u32::try_from(leaves.len()).map_err(|_| Error::InvalidPosition(position))?;
689 let end = position
690 .checked_add(leaves_len - 1)
691 .ok_or(Error::InvalidPosition(position))?;
692 if end >= self.leaf_count {
693 return Err(Error::InvalidPosition(end));
694 }
695 }
696
697 let elements: Vec<(D, u32)> = leaves
699 .iter()
700 .enumerate()
701 .map(|(i, leaf)| (*leaf, position + i as u32))
702 .collect();
703 self.verify_multi_inclusion(hasher, &elements, root)
704 }
705}
706
707#[cfg(test)]
708mod tests {
709 use super::*;
710 use commonware_codec::{Decode, Encode};
711 use commonware_cryptography::sha256::{Digest, Sha256};
712 use rstest::rstest;
713
714 #[test]
720 fn issue_2837_regression() {
721 let digests: Vec<Digest> = (0..255u32)
723 .map(|i| Sha256::hash(&i.to_be_bytes()))
724 .collect();
725
726 let mut builder = Builder::<Sha256>::new(255);
727 for digest in &digests {
728 builder.add(digest);
729 }
730 let tree = builder.build();
731 let root = tree.root();
732
733 let original_proof = tree.proof(0).unwrap();
735 assert_eq!(original_proof.leaf_count, 255);
736
737 let mut hasher = Sha256::default();
739 assert!(
740 original_proof
741 .verify_element_inclusion(&mut hasher, &digests[0], 0, &root)
742 .is_ok(),
743 "Original proof should verify"
744 );
745
746 let malleated_proof = Proof {
749 leaf_count: 254,
750 siblings: original_proof.siblings.clone(),
751 };
752
753 let result = malleated_proof.verify_element_inclusion(&mut hasher, &digests[0], 0, &root);
756 assert!(
757 result.is_err(),
758 "Malleated proof with wrong leaf_count must fail verification"
759 );
760 }
761
762 #[test]
763 fn test_tampered_proof_no_siblings() {
764 let txs = [b"tx1", b"tx2", b"tx3", b"tx4"];
766 let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(*tx)).collect();
767 let element = &digests[0];
768
769 let mut builder = Builder::<Sha256>::new(txs.len());
771 for digest in &digests {
772 builder.add(digest);
773 }
774 let tree = builder.build();
775 let root = tree.root();
776
777 let mut proof = tree.proof(0).unwrap();
779
780 proof.siblings = Vec::new();
782
783 let mut hasher = Sha256::default();
785 assert!(proof
786 .verify_element_inclusion(&mut hasher, element, 0, &root)
787 .is_err());
788 }
789
790 #[test]
791 fn test_tampered_proof_extra_sibling() {
792 let txs = [b"tx1", b"tx2", b"tx3", b"tx4"];
794 let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(*tx)).collect();
795 let element = &digests[0];
796
797 let mut builder = Builder::<Sha256>::new(txs.len());
799 for digest in &digests {
800 builder.add(digest);
801 }
802 let tree = builder.build();
803 let root = tree.root();
804
805 let mut proof = tree.proof(0).unwrap();
807
808 proof.siblings.push(*element);
810
811 let mut hasher = Sha256::default();
813 assert!(proof
814 .verify_element_inclusion(&mut hasher, element, 0, &root)
815 .is_err());
816 }
817
818 #[test]
819 fn test_invalid_proof_wrong_element() {
820 let txs = [b"tx1", b"tx2", b"tx3", b"tx4"];
822 let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(*tx)).collect();
823
824 let mut builder = Builder::<Sha256>::new(txs.len());
826 for digest in &digests {
827 builder.add(digest);
828 }
829 let tree = builder.build();
830 let root = tree.root();
831
832 let proof = tree.proof(2).unwrap();
834
835 let mut hasher = Sha256::default();
837 let wrong_leaf = Sha256::hash(b"wrong_tx");
838 assert!(proof
839 .verify_element_inclusion(&mut hasher, &wrong_leaf, 2, &root)
840 .is_err());
841 }
842
843 #[test]
844 fn test_invalid_proof_wrong_index() {
845 let txs = [b"tx1", b"tx2", b"tx3", b"tx4"];
847 let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(*tx)).collect();
848
849 let mut builder = Builder::<Sha256>::new(txs.len());
851 for digest in &digests {
852 builder.add(digest);
853 }
854 let tree = builder.build();
855 let root = tree.root();
856
857 let proof = tree.proof(1).unwrap();
859
860 let mut hasher = Sha256::default();
862 assert!(proof
863 .verify_element_inclusion(&mut hasher, &digests[1], 2, &root)
864 .is_err());
865 }
866
867 #[test]
868 fn test_invalid_proof_wrong_root() {
869 let txs = [b"tx1", b"tx2", b"tx3", b"tx4"];
871 let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(*tx)).collect();
872
873 let mut builder = Builder::<Sha256>::new(txs.len());
875 for digest in &digests {
876 builder.add(digest);
877 }
878 let tree = builder.build();
879
880 let proof = tree.proof(0).unwrap();
882
883 let mut hasher = Sha256::default();
885 let wrong_root = Sha256::hash(b"wrong_root");
886 assert!(proof
887 .verify_element_inclusion(&mut hasher, &digests[0], 0, &wrong_root)
888 .is_err());
889 }
890
891 #[test]
892 fn test_invalid_proof_serialization_truncated() {
893 let txs = [b"tx1", b"tx2", b"tx3"];
895 let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(*tx)).collect();
896
897 let mut builder = Builder::<Sha256>::new(txs.len());
899 for digest in &digests {
900 builder.add(digest);
901 }
902 let tree = builder.build();
903
904 let proof = tree.proof(1).unwrap();
906 let mut serialized = proof.encode();
907
908 serialized.truncate(serialized.len() - 1);
910 assert!(Proof::<Digest>::decode_cfg(&mut serialized, &1).is_err());
911 }
912
913 #[test]
914 fn test_invalid_proof_serialization_extra() {
915 let txs = [b"tx1", b"tx2", b"tx3"];
917 let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(*tx)).collect();
918
919 let mut builder = Builder::<Sha256>::new(txs.len());
921 for digest in &digests {
922 builder.add(digest);
923 }
924 let tree = builder.build();
925
926 let proof = tree.proof(1).unwrap();
928 let mut serialized = proof.encode_mut();
929
930 serialized.extend_from_slice(&[0u8]);
932 assert!(Proof::<Digest>::decode_cfg(&mut serialized, &1).is_err());
933 }
934
935 #[test]
936 fn test_invalid_proof_modified_hash() {
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 mut proof = tree.proof(2).unwrap();
951
952 let mut hasher = Sha256::default();
954 proof.siblings[0] = Sha256::hash(b"modified");
955 assert!(proof
956 .verify_element_inclusion(&mut hasher, &digests[2], 2, &root)
957 .is_err());
958 }
959
960 #[test]
961 fn test_odd_tree_duplicate_index_proof() {
962 let txs = [b"tx1", b"tx2", b"tx3"];
964 let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(*tx)).collect();
965
966 let mut builder = Builder::<Sha256>::new(txs.len());
968 for digest in &digests {
969 builder.add(digest);
970 }
971 let tree = builder.build();
972 let root = tree.root();
973
974 let proof = tree.proof(2).unwrap();
976
977 let mut hasher = Sha256::default();
979 assert!(proof
980 .verify_element_inclusion(&mut hasher, &digests[2], 2, &root)
981 .is_ok());
982
983 assert!(tree.proof(3).is_err());
985
986 assert!(proof
989 .verify_element_inclusion(&mut hasher, &digests[2], 3, &root)
990 .is_err());
991 }
992
993 #[test]
994 fn test_range_proof_basic() {
995 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
997
998 let mut builder = Builder::<Sha256>::new(digests.len());
1000 for digest in &digests {
1001 builder.add(digest);
1002 }
1003 let tree = builder.build();
1004 let root = tree.root();
1005
1006 let range_proof = tree.range_proof(2, 5).unwrap();
1008 let mut hasher = Sha256::default();
1009 let range_leaves = &digests[2..6];
1010
1011 assert!(range_proof
1012 .verify_range_inclusion(&mut hasher, 2, range_leaves, &root)
1013 .is_ok());
1014
1015 let mut serialized = range_proof.encode();
1017 let deserialized = Proof::<Digest>::decode_cfg(&mut serialized, &4).unwrap();
1018 assert!(deserialized
1019 .verify_range_inclusion(&mut hasher, 2, range_leaves, &root)
1020 .is_ok());
1021 }
1022
1023 #[test]
1024 fn test_range_proof_single_element() {
1025 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1027
1028 let mut builder = Builder::<Sha256>::new(digests.len());
1030 for digest in &digests {
1031 builder.add(digest);
1032 }
1033 let tree = builder.build();
1034 let root = tree.root();
1035
1036 for (i, digest) in digests.iter().enumerate() {
1038 let range_proof = tree.range_proof(i as u32, i as u32).unwrap();
1039 let mut hasher = Sha256::default();
1040
1041 let result =
1042 range_proof.verify_range_inclusion(&mut hasher, i as u32, &[*digest], &root);
1043 assert!(result.is_ok());
1044 }
1045 }
1046
1047 #[test]
1048 fn test_range_proof_full_tree() {
1049 let digests: Vec<Digest> = (0..7u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1051
1052 let mut builder = Builder::<Sha256>::new(digests.len());
1054 for digest in &digests {
1055 builder.add(digest);
1056 }
1057 let tree = builder.build();
1058 let root = tree.root();
1059
1060 let range_proof = tree.range_proof(0, (digests.len() - 1) as u32).unwrap();
1062 let mut hasher = Sha256::default();
1063 assert!(range_proof
1064 .verify_range_inclusion(&mut hasher, 0, &digests, &root)
1065 .is_ok());
1066 }
1067
1068 #[test]
1069 fn test_range_proof_edge_cases() {
1070 let digests: Vec<Digest> = (0..15u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1072
1073 let mut builder = Builder::<Sha256>::new(digests.len());
1075 for digest in &digests {
1076 builder.add(digest);
1077 }
1078 let tree = builder.build();
1079 let root = tree.root();
1080 let mut hasher = Sha256::default();
1081
1082 let range_proof = tree.range_proof(0, 7).unwrap();
1084 assert!(range_proof
1085 .verify_range_inclusion(&mut hasher, 0, &digests[0..8], &root)
1086 .is_ok());
1087
1088 let range_proof = tree.range_proof(8, 14).unwrap();
1090 assert!(range_proof
1091 .verify_range_inclusion(&mut hasher, 8, &digests[8..15], &root)
1092 .is_ok());
1093
1094 let range_proof = tree.range_proof(13, 14).unwrap();
1096 assert!(range_proof
1097 .verify_range_inclusion(&mut hasher, 13, &digests[13..15], &root)
1098 .is_ok());
1099 }
1100
1101 #[test]
1102 fn test_range_proof_invalid_range() {
1103 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1105
1106 let mut builder = Builder::<Sha256>::new(digests.len());
1108 for digest in &digests {
1109 builder.add(digest);
1110 }
1111 let tree = builder.build();
1112
1113 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()); }
1119
1120 #[test]
1121 fn test_range_proof_tampering() {
1122 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1124
1125 let mut builder = Builder::<Sha256>::new(digests.len());
1127 for digest in &digests {
1128 builder.add(digest);
1129 }
1130 let tree = builder.build();
1131 let root = tree.root();
1132
1133 let range_proof = tree.range_proof(2, 4).unwrap();
1135 let mut hasher = Sha256::default();
1136 let range_leaves = &digests[2..5];
1137
1138 let wrong_leaves = vec![
1140 Sha256::hash(b"wrong1"),
1141 Sha256::hash(b"wrong2"),
1142 Sha256::hash(b"wrong3"),
1143 ];
1144 assert!(range_proof
1145 .verify_range_inclusion(&mut hasher, 2, &wrong_leaves, &root)
1146 .is_err());
1147
1148 assert!(range_proof
1150 .verify_range_inclusion(&mut hasher, 2, &digests[2..4], &root)
1151 .is_err());
1152
1153 let mut tampered_proof = range_proof.clone();
1155 assert!(!tampered_proof.siblings.is_empty());
1156 tampered_proof.siblings[0] = Sha256::hash(b"tampered");
1158 assert!(tampered_proof
1159 .verify_range_inclusion(&mut hasher, 2, range_leaves, &root)
1160 .is_err());
1161
1162 let wrong_root = Sha256::hash(b"wrong_root");
1164 assert!(range_proof
1165 .verify_range_inclusion(&mut hasher, 2, range_leaves, &wrong_root)
1166 .is_err());
1167 }
1168
1169 #[test]
1170 fn test_range_proof_various_sizes() {
1171 for tree_size in [1, 2, 3, 4, 5, 7, 8, 15, 16, 31, 32, 63, 64] {
1173 let digests: Vec<Digest> = (0..tree_size as u32)
1174 .map(|i| Sha256::hash(&i.to_be_bytes()))
1175 .collect();
1176
1177 let mut builder = Builder::<Sha256>::new(digests.len());
1179 for digest in &digests {
1180 builder.add(digest);
1181 }
1182 let tree = builder.build();
1183 let root = tree.root();
1184 let mut hasher = Sha256::default();
1185
1186 for range_size in 1..=tree_size.min(8) {
1188 for start in 0..=(tree_size - range_size) {
1189 let range_proof = tree
1190 .range_proof(start as u32, (start + range_size - 1) as u32)
1191 .unwrap();
1192 let end = start + range_size;
1193 assert!(
1194 range_proof
1195 .verify_range_inclusion(
1196 &mut hasher,
1197 start as u32,
1198 &digests[start..end],
1199 &root
1200 )
1201 .is_ok(),
1202 "Failed for tree_size={tree_size}, start={start}, range_size={range_size}"
1203 );
1204 }
1205 }
1206 }
1207 }
1208
1209 #[test]
1210 fn test_range_proof_malicious_wrong_position() {
1211 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1213
1214 let mut builder = Builder::<Sha256>::new(digests.len());
1216 for digest in &digests {
1217 builder.add(digest);
1218 }
1219 let tree = builder.build();
1220 let root = tree.root();
1221
1222 let range_proof = tree.range_proof(2, 4).unwrap();
1224 let mut hasher = Sha256::default();
1225 let range_leaves = &digests[2..5];
1226
1227 assert!(range_proof
1229 .verify_range_inclusion(&mut hasher, 3, range_leaves, &root)
1230 .is_err());
1231 assert!(range_proof
1232 .verify_range_inclusion(&mut hasher, 1, range_leaves, &root)
1233 .is_err());
1234 }
1235
1236 #[test]
1237 fn test_range_proof_malicious_reordered_leaves() {
1238 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1240
1241 let mut builder = Builder::<Sha256>::new(digests.len());
1243 for digest in &digests {
1244 builder.add(digest);
1245 }
1246 let tree = builder.build();
1247 let root = tree.root();
1248
1249 let range_proof = tree.range_proof(2, 4).unwrap();
1251 let mut hasher = Sha256::default();
1252
1253 let reordered_leaves = vec![digests[3], digests[2], digests[4]];
1255 assert!(range_proof
1256 .verify_range_inclusion(&mut hasher, 2, &reordered_leaves, &root)
1257 .is_err());
1258 }
1259
1260 #[test]
1261 fn test_range_proof_malicious_extra_siblings() {
1262 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1264
1265 let mut builder = Builder::<Sha256>::new(digests.len());
1267 for digest in &digests {
1268 builder.add(digest);
1269 }
1270 let tree = builder.build();
1271 let root = tree.root();
1272
1273 let mut range_proof = tree.range_proof(2, 3).unwrap();
1275 let mut hasher = Sha256::default();
1276 let range_leaves = &digests[2..4];
1277
1278 range_proof.siblings.push(Sha256::hash(b"extra"));
1280 assert!(range_proof
1281 .verify_range_inclusion(&mut hasher, 2, range_leaves, &root)
1282 .is_err());
1283 }
1284
1285 #[test]
1286 fn test_range_proof_malicious_missing_siblings() {
1287 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1289
1290 let mut builder = Builder::<Sha256>::new(digests.len());
1292 for digest in &digests {
1293 builder.add(digest);
1294 }
1295 let tree = builder.build();
1296 let root = tree.root();
1297
1298 let mut range_proof = tree.range_proof(2, 2).unwrap();
1300 let mut hasher = Sha256::default();
1301 let range_leaves = &digests[2..3];
1302
1303 assert!(!range_proof.siblings.is_empty());
1305
1306 range_proof.siblings.pop();
1308 assert!(range_proof
1309 .verify_range_inclusion(&mut hasher, 2, range_leaves, &root)
1310 .is_err());
1311 }
1312
1313 #[test]
1314 fn test_range_proof_integer_overflow_protection() {
1315 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1317
1318 let mut builder = Builder::<Sha256>::new(digests.len());
1320 for digest in &digests {
1321 builder.add(digest);
1322 }
1323 let tree = builder.build();
1324
1325 assert!(tree.range_proof(u32::MAX, u32::MAX).is_err());
1327 assert!(tree.range_proof(u32::MAX - 1, u32::MAX).is_err());
1328 assert!(tree.range_proof(7, u32::MAX).is_err());
1329 }
1330
1331 #[test]
1332 fn test_range_proof_malicious_wrong_tree_structure() {
1333 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1335
1336 let mut builder = Builder::<Sha256>::new(digests.len());
1338 for digest in &digests {
1339 builder.add(digest);
1340 }
1341 let tree = builder.build();
1342 let root = tree.root();
1343
1344 let mut range_proof = tree.range_proof(2, 3).unwrap();
1346 let mut hasher = Sha256::default();
1347 let range_leaves = &digests[2..4];
1348
1349 range_proof.siblings.push(Sha256::hash(b"fake_level"));
1351 assert!(range_proof
1352 .verify_range_inclusion(&mut hasher, 2, range_leaves, &root)
1353 .is_err());
1354
1355 let mut range_proof = tree.range_proof(2, 2).unwrap();
1357 let range_leaves = &digests[2..3];
1358 assert!(!range_proof.siblings.is_empty());
1359 range_proof.siblings.pop();
1360 assert!(range_proof
1361 .verify_range_inclusion(&mut hasher, 2, range_leaves, &root)
1362 .is_err());
1363 }
1364
1365 #[test]
1366 fn test_range_proof_boundary_conditions() {
1367 for tree_size in [1, 2, 4, 8, 16, 32] {
1369 let digests: Vec<Digest> = (0..tree_size as u32)
1370 .map(|i| Sha256::hash(&i.to_be_bytes()))
1371 .collect();
1372
1373 let mut builder = Builder::<Sha256>::new(digests.len());
1375 for digest in &digests {
1376 builder.add(digest);
1377 }
1378 let tree = builder.build();
1379 let root = tree.root();
1380 let mut hasher = Sha256::default();
1381
1382 let proof = tree.range_proof(0, 0).unwrap();
1385 assert!(proof
1386 .verify_range_inclusion(&mut hasher, 0, &digests[0..1], &root)
1387 .is_ok());
1388
1389 let last_idx = tree_size - 1;
1391 let proof = tree.range_proof(last_idx as u32, last_idx as u32).unwrap();
1392 assert!(proof
1393 .verify_range_inclusion(
1394 &mut hasher,
1395 last_idx as u32,
1396 &digests[last_idx..tree_size],
1397 &root
1398 )
1399 .is_ok());
1400
1401 let proof = tree.range_proof(0, (tree_size - 1) as u32).unwrap();
1403 assert!(proof
1404 .verify_range_inclusion(&mut hasher, 0, &digests, &root)
1405 .is_ok());
1406 }
1407 }
1408
1409 #[test]
1410 fn test_empty_tree_proof() {
1411 let builder = Builder::<Sha256>::new(0);
1413 let tree = builder.build();
1414
1415 assert!(tree.proof(0).is_err());
1417 assert!(tree.proof(1).is_err());
1418 assert!(tree.proof(100).is_err());
1419 }
1420
1421 #[test]
1422 fn test_empty_tree_range_proof() {
1423 let builder = Builder::<Sha256>::new(0);
1425 let tree = builder.build();
1426 let root = tree.root();
1427
1428 let range_proof = tree.range_proof(0, 0).unwrap();
1430 assert!(range_proof.siblings.is_empty());
1431 assert_eq!(range_proof, Proof::default());
1432
1433 let invalid_ranges = vec![
1435 (0, 1),
1436 (0, 10),
1437 (1, 1),
1438 (1, 2),
1439 (5, 5),
1440 (10, 10),
1441 (0, u32::MAX),
1442 (u32::MAX, u32::MAX),
1443 ];
1444 for (start, end) in invalid_ranges {
1445 assert!(tree.range_proof(start, end).is_err());
1446 }
1447
1448 let mut hasher = Sha256::default();
1450 let empty_leaves: &[Digest] = &[];
1451 assert!(range_proof
1452 .verify_range_inclusion(&mut hasher, 0, empty_leaves, &root)
1453 .is_ok());
1454
1455 let non_empty_leaves = vec![Sha256::hash(b"leaf")];
1457 assert!(range_proof
1458 .verify_range_inclusion(&mut hasher, 0, &non_empty_leaves, &root)
1459 .is_err());
1460
1461 let wrong_root = Sha256::hash(b"wrong");
1463 assert!(range_proof
1464 .verify_range_inclusion(&mut hasher, 0, empty_leaves, &wrong_root)
1465 .is_err());
1466
1467 assert!(range_proof
1469 .verify_range_inclusion(&mut hasher, 1, empty_leaves, &root)
1470 .is_err());
1471 }
1472
1473 #[test]
1474 fn test_empty_range_proof_serialization() {
1475 let proof = Proof::<Digest>::default();
1476 let mut serialized = proof.encode();
1477 let deserialized = Proof::<Digest>::decode_cfg(&mut serialized, &0).unwrap();
1478 assert_eq!(proof, deserialized);
1479 }
1480
1481 #[test]
1482 fn test_empty_tree_root_consistency() {
1483 let mut roots = Vec::new();
1485 for _ in 0..5 {
1486 let builder = Builder::<Sha256>::new(0);
1487 let tree = builder.build();
1488 roots.push(tree.root());
1489 }
1490
1491 for i in 1..roots.len() {
1493 assert_eq!(roots[0], roots[i]);
1494 }
1495
1496 let mut hasher = Sha256::default();
1498 hasher.update(0u32.to_be_bytes().as_slice());
1499 hasher.update(Sha256::hash(b"").as_ref());
1500 let expected_root = hasher.finalize();
1501 assert_eq!(roots[0], expected_root);
1502 }
1503
1504 #[rstest]
1505 #[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) {
1509 let digests: Vec<Digest> = (0..16u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1511
1512 let mut builder = Builder::<Sha256>::new(digests.len());
1514 for digest in &digests {
1515 builder.add(digest);
1516 }
1517 let tree = builder.build();
1518 let root = tree.root();
1519 let mut hasher = Sha256::default();
1520
1521 let range_proof = tree.range_proof(start, start + count - 1).unwrap();
1522 let end = start as usize + count as usize;
1523
1524 assert!(range_proof
1526 .verify_range_inclusion(&mut hasher, start, &digests[start as usize..end], &root)
1527 .is_ok());
1528
1529 for sibling_idx in 0..range_proof.siblings.len() {
1531 let mut tampered_proof = range_proof.clone();
1532 tampered_proof.siblings[sibling_idx] = Sha256::hash(b"tampered");
1533 assert!(tampered_proof
1534 .verify_range_inclusion(&mut hasher, start, &digests[start as usize..end], &root)
1535 .is_err());
1536 }
1537 }
1538
1539 #[rstest]
1541 fn test_range_proof_duplicate_node_edge_cases(
1542 #[values(3, 5, 7, 9, 11, 13, 15)] tree_size: usize,
1543 ) {
1544 let digests: Vec<Digest> = (0..tree_size as u32)
1545 .map(|i| Sha256::hash(&i.to_be_bytes()))
1546 .collect();
1547
1548 let mut builder = Builder::<Sha256>::new(digests.len());
1550 for digest in &digests {
1551 builder.add(digest);
1552 }
1553 let tree = builder.build();
1554 let root = tree.root();
1555 let mut hasher = Sha256::default();
1556
1557 let start = tree_size - 2;
1559 let proof = tree
1560 .range_proof(start as u32, (tree_size - 1) as u32)
1561 .unwrap();
1562 assert!(proof
1563 .verify_range_inclusion(&mut hasher, start as u32, &digests[start..tree_size], &root)
1564 .is_ok());
1565 }
1566
1567 #[test]
1568 fn test_multi_proof_basic() {
1569 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1571
1572 let mut builder = Builder::<Sha256>::new(digests.len());
1574 for digest in &digests {
1575 builder.add(digest);
1576 }
1577 let tree = builder.build();
1578 let root = tree.root();
1579
1580 let positions = [0, 3, 5];
1582 let multi_proof = tree.multi_proof(positions).unwrap();
1583 let mut hasher = Sha256::default();
1584
1585 let elements: Vec<(Digest, u32)> = positions
1586 .iter()
1587 .map(|&p| (digests[p as usize], p))
1588 .collect();
1589 assert!(multi_proof
1590 .verify_multi_inclusion(&mut hasher, &elements, &root)
1591 .is_ok());
1592 }
1593
1594 #[test]
1595 fn test_multi_proof_single_element() {
1596 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1598
1599 let mut builder = Builder::<Sha256>::new(digests.len());
1601 for digest in &digests {
1602 builder.add(digest);
1603 }
1604 let tree = builder.build();
1605 let root = tree.root();
1606 let mut hasher = Sha256::default();
1607
1608 for (i, digest) in digests.iter().enumerate() {
1610 let multi_proof = tree.multi_proof([i as u32]).unwrap();
1611 let elements = [(*digest, i as u32)];
1612 assert!(
1613 multi_proof
1614 .verify_multi_inclusion(&mut hasher, &elements, &root)
1615 .is_ok(),
1616 "Failed for position {i}"
1617 );
1618 }
1619 }
1620
1621 #[test]
1622 fn test_multi_proof_all_elements() {
1623 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1625
1626 let mut builder = Builder::<Sha256>::new(digests.len());
1628 for digest in &digests {
1629 builder.add(digest);
1630 }
1631 let tree = builder.build();
1632 let root = tree.root();
1633 let mut hasher = Sha256::default();
1634
1635 let positions: Vec<u32> = (0..digests.len() as u32).collect();
1637 let multi_proof = tree.multi_proof(&positions).unwrap();
1638
1639 let elements: Vec<(Digest, u32)> = positions
1640 .iter()
1641 .map(|&p| (digests[p as usize], p))
1642 .collect();
1643 assert!(multi_proof
1644 .verify_multi_inclusion(&mut hasher, &elements, &root)
1645 .is_ok());
1646
1647 assert!(multi_proof.siblings.is_empty());
1649 }
1650
1651 #[test]
1652 fn test_multi_proof_adjacent_elements() {
1653 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1655
1656 let mut builder = Builder::<Sha256>::new(digests.len());
1658 for digest in &digests {
1659 builder.add(digest);
1660 }
1661 let tree = builder.build();
1662 let root = tree.root();
1663 let mut hasher = Sha256::default();
1664
1665 let positions = [2, 3];
1667 let multi_proof = tree.multi_proof(positions).unwrap();
1668
1669 let elements: Vec<(Digest, u32)> = positions
1670 .iter()
1671 .map(|&p| (digests[p as usize], p))
1672 .collect();
1673 assert!(multi_proof
1674 .verify_multi_inclusion(&mut hasher, &elements, &root)
1675 .is_ok());
1676 }
1677
1678 #[test]
1679 fn test_multi_proof_sparse_positions() {
1680 let digests: Vec<Digest> = (0..16u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1682
1683 let mut builder = Builder::<Sha256>::new(digests.len());
1685 for digest in &digests {
1686 builder.add(digest);
1687 }
1688 let tree = builder.build();
1689 let root = tree.root();
1690 let mut hasher = Sha256::default();
1691
1692 let positions = [0, 7, 8, 15];
1694 let multi_proof = tree.multi_proof(positions).unwrap();
1695
1696 let elements: Vec<(Digest, u32)> = positions
1697 .iter()
1698 .map(|&p| (digests[p as usize], p))
1699 .collect();
1700 assert!(multi_proof
1701 .verify_multi_inclusion(&mut hasher, &elements, &root)
1702 .is_ok());
1703 }
1704
1705 #[test]
1706 fn test_multi_proof_empty_tree() {
1707 let builder = Builder::<Sha256>::new(0);
1709 let tree = builder.build();
1710
1711 assert!(matches!(
1714 tree.multi_proof(std::iter::empty::<u32>()),
1715 Err(Error::NoLeaves)
1716 ));
1717
1718 assert!(matches!(
1720 tree.multi_proof([0]),
1721 Err(Error::InvalidPosition(0))
1722 ));
1723 }
1724
1725 #[test]
1726 fn test_multi_proof_empty_positions() {
1727 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1729
1730 let mut builder = Builder::<Sha256>::new(digests.len());
1732 for digest in &digests {
1733 builder.add(digest);
1734 }
1735 let tree = builder.build();
1736
1737 assert!(matches!(
1739 tree.multi_proof(std::iter::empty::<u32>()),
1740 Err(Error::NoLeaves)
1741 ));
1742 }
1743
1744 #[test]
1745 fn test_multi_proof_duplicate_positions_error() {
1746 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1748
1749 let mut builder = Builder::<Sha256>::new(digests.len());
1751 for digest in &digests {
1752 builder.add(digest);
1753 }
1754 let tree = builder.build();
1755
1756 assert!(matches!(
1758 tree.multi_proof([1, 1]),
1759 Err(Error::DuplicatePosition(1))
1760 ));
1761 assert!(matches!(
1762 tree.multi_proof([0, 2, 2, 5]),
1763 Err(Error::DuplicatePosition(2))
1764 ));
1765 }
1766
1767 #[test]
1768 fn test_multi_proof_unsorted_input() {
1769 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1771
1772 let mut builder = Builder::<Sha256>::new(digests.len());
1774 for digest in &digests {
1775 builder.add(digest);
1776 }
1777 let tree = builder.build();
1778 let root = tree.root();
1779 let mut hasher = Sha256::default();
1780
1781 let positions = [5, 0, 3];
1783 let multi_proof = tree.multi_proof(positions).unwrap();
1784
1785 let unsorted_elements = [(digests[5], 5), (digests[0], 0), (digests[3], 3)];
1787 assert!(multi_proof
1788 .verify_multi_inclusion(&mut hasher, &unsorted_elements, &root)
1789 .is_ok());
1790 }
1791
1792 #[test]
1793 fn test_multi_proof_various_sizes() {
1794 for tree_size in [1, 2, 3, 4, 5, 7, 8, 15, 16, 31, 32] {
1796 let digests: Vec<Digest> = (0..tree_size as u32)
1797 .map(|i| Sha256::hash(&i.to_be_bytes()))
1798 .collect();
1799
1800 let mut builder = Builder::<Sha256>::new(digests.len());
1802 for digest in &digests {
1803 builder.add(digest);
1804 }
1805 let tree = builder.build();
1806 let root = tree.root();
1807 let mut hasher = Sha256::default();
1808
1809 if tree_size >= 2 {
1812 let positions = [0, (tree_size - 1) as u32];
1813 let multi_proof = tree.multi_proof(positions).unwrap();
1814 let elements: Vec<(Digest, u32)> = positions
1815 .iter()
1816 .map(|&p| (digests[p as usize], p))
1817 .collect();
1818 assert!(
1819 multi_proof
1820 .verify_multi_inclusion(&mut hasher, &elements, &root)
1821 .is_ok(),
1822 "Failed for tree_size={tree_size}, positions=[0, {}]",
1823 tree_size - 1
1824 );
1825 }
1826
1827 if tree_size >= 4 {
1829 let positions: Vec<u32> = (0..tree_size as u32).step_by(2).collect();
1830 let multi_proof = tree.multi_proof(&positions).unwrap();
1831 let elements: Vec<(Digest, u32)> = positions
1832 .iter()
1833 .map(|&p| (digests[p as usize], p))
1834 .collect();
1835 assert!(
1836 multi_proof
1837 .verify_multi_inclusion(&mut hasher, &elements, &root)
1838 .is_ok(),
1839 "Failed for tree_size={tree_size}, every other element"
1840 );
1841 }
1842 }
1843 }
1844
1845 #[test]
1846 fn test_multi_proof_wrong_elements() {
1847 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1849
1850 let mut builder = Builder::<Sha256>::new(digests.len());
1852 for digest in &digests {
1853 builder.add(digest);
1854 }
1855 let tree = builder.build();
1856 let root = tree.root();
1857 let mut hasher = Sha256::default();
1858
1859 let positions = [0, 3, 5];
1861 let multi_proof = tree.multi_proof(positions).unwrap();
1862
1863 let wrong_elements = [
1865 (Sha256::hash(b"wrong1"), 0),
1866 (digests[3], 3),
1867 (digests[5], 5),
1868 ];
1869 assert!(multi_proof
1870 .verify_multi_inclusion(&mut hasher, &wrong_elements, &root)
1871 .is_err());
1872 }
1873
1874 #[test]
1875 fn test_multi_proof_wrong_positions() {
1876 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).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 let mut hasher = Sha256::default();
1887
1888 let positions = [0, 3, 5];
1890 let multi_proof = tree.multi_proof(positions).unwrap();
1891
1892 let wrong_positions = [
1894 (digests[0], 1), (digests[3], 3),
1896 (digests[5], 5),
1897 ];
1898 assert!(multi_proof
1899 .verify_multi_inclusion(&mut hasher, &wrong_positions, &root)
1900 .is_err());
1901 }
1902
1903 #[test]
1904 fn test_multi_proof_wrong_root() {
1905 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1907
1908 let mut builder = Builder::<Sha256>::new(digests.len());
1910 for digest in &digests {
1911 builder.add(digest);
1912 }
1913 let tree = builder.build();
1914 let mut hasher = Sha256::default();
1915
1916 let positions = [0, 3, 5];
1918 let multi_proof = tree.multi_proof(positions).unwrap();
1919
1920 let elements: Vec<(Digest, u32)> = positions
1921 .iter()
1922 .map(|&p| (digests[p as usize], p))
1923 .collect();
1924
1925 let wrong_root = Sha256::hash(b"wrong_root");
1927 assert!(multi_proof
1928 .verify_multi_inclusion(&mut hasher, &elements, &wrong_root)
1929 .is_err());
1930 }
1931
1932 #[test]
1933 fn test_multi_proof_tampering() {
1934 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1936
1937 let mut builder = Builder::<Sha256>::new(digests.len());
1939 for digest in &digests {
1940 builder.add(digest);
1941 }
1942 let tree = builder.build();
1943 let root = tree.root();
1944 let mut hasher = Sha256::default();
1945
1946 let positions = [0, 5];
1948 let multi_proof = tree.multi_proof(positions).unwrap();
1949
1950 let elements: Vec<(Digest, u32)> = positions
1951 .iter()
1952 .map(|&p| (digests[p as usize], p))
1953 .collect();
1954
1955 assert!(!multi_proof.siblings.is_empty());
1957 let mut modified = multi_proof.clone();
1958 modified.siblings[0] = Sha256::hash(b"tampered");
1959 assert!(modified
1960 .verify_multi_inclusion(&mut hasher, &elements, &root)
1961 .is_err());
1962
1963 let mut extra = multi_proof.clone();
1965 extra.siblings.push(Sha256::hash(b"extra"));
1966 assert!(extra
1967 .verify_multi_inclusion(&mut hasher, &elements, &root)
1968 .is_err());
1969
1970 let mut missing = multi_proof;
1972 missing.siblings.pop();
1973 assert!(missing
1974 .verify_multi_inclusion(&mut hasher, &elements, &root)
1975 .is_err());
1976 }
1977
1978 #[test]
1979 fn test_multi_proof_deduplication() {
1980 let digests: Vec<Digest> = (0..16u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1982
1983 let mut builder = Builder::<Sha256>::new(digests.len());
1985 for digest in &digests {
1986 builder.add(digest);
1987 }
1988 let tree = builder.build();
1989
1990 let individual_siblings: usize = [0u32, 1, 8, 9]
1992 .iter()
1993 .map(|&p| tree.proof(p).unwrap().siblings.len())
1994 .sum();
1995
1996 let multi_proof = tree.multi_proof([0, 1, 8, 9]).unwrap();
1998
1999 assert!(
2001 multi_proof.siblings.len() < individual_siblings,
2002 "Multi-proof ({}) should have fewer siblings than sum of individual proofs ({})",
2003 multi_proof.siblings.len(),
2004 individual_siblings
2005 );
2006 }
2007
2008 #[test]
2009 fn test_multi_proof_serialization() {
2010 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
2012
2013 let mut builder = Builder::<Sha256>::new(digests.len());
2015 for digest in &digests {
2016 builder.add(digest);
2017 }
2018 let tree = builder.build();
2019 let root = tree.root();
2020 let mut hasher = Sha256::default();
2021
2022 let positions = [0, 3, 5];
2024 let multi_proof = tree.multi_proof(positions).unwrap();
2025
2026 let serialized = multi_proof.encode();
2028 let deserialized = Proof::<Digest>::decode_cfg(serialized, &positions.len()).unwrap();
2029
2030 assert_eq!(multi_proof, deserialized);
2031
2032 let elements: Vec<(Digest, u32)> = positions
2034 .iter()
2035 .map(|&p| (digests[p as usize], p))
2036 .collect();
2037 assert!(deserialized
2038 .verify_multi_inclusion(&mut hasher, &elements, &root)
2039 .is_ok());
2040 }
2041
2042 #[test]
2043 fn test_multi_proof_serialization_truncated() {
2044 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
2046
2047 let mut builder = Builder::<Sha256>::new(digests.len());
2049 for digest in &digests {
2050 builder.add(digest);
2051 }
2052 let tree = builder.build();
2053
2054 let positions = [0, 3, 5];
2056 let multi_proof = tree.multi_proof(positions).unwrap();
2057
2058 let mut serialized = multi_proof.encode();
2060 serialized.truncate(serialized.len() - 1);
2061
2062 assert!(Proof::<Digest>::decode_cfg(&mut serialized, &positions.len()).is_err());
2064 }
2065
2066 #[test]
2067 fn test_multi_proof_serialization_extra() {
2068 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
2070
2071 let mut builder = Builder::<Sha256>::new(digests.len());
2073 for digest in &digests {
2074 builder.add(digest);
2075 }
2076 let tree = builder.build();
2077
2078 let positions = [0, 3, 5];
2080 let multi_proof = tree.multi_proof(positions).unwrap();
2081
2082 let mut serialized = multi_proof.encode_mut();
2084 serialized.extend_from_slice(&[0u8]);
2085
2086 assert!(Proof::<Digest>::decode_cfg(&mut serialized, &positions.len()).is_err());
2088 }
2089
2090 #[test]
2091 fn test_multi_proof_decode_insufficient_data() {
2092 let mut serialized = Vec::new();
2093 serialized.extend_from_slice(&8u32.encode()); serialized.extend_from_slice(&1usize.encode()); let err = Proof::<Digest>::decode_cfg(serialized.as_slice(), &1).unwrap_err();
2098 assert!(matches!(err, commonware_codec::Error::EndOfBuffer));
2099 }
2100
2101 #[test]
2102 fn test_multi_proof_invalid_position() {
2103 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
2105
2106 let mut builder = Builder::<Sha256>::new(digests.len());
2108 for digest in &digests {
2109 builder.add(digest);
2110 }
2111 let tree = builder.build();
2112
2113 assert!(matches!(
2115 tree.multi_proof([0, 8]),
2116 Err(Error::InvalidPosition(8))
2117 ));
2118 assert!(matches!(
2119 tree.multi_proof([100]),
2120 Err(Error::InvalidPosition(100))
2121 ));
2122 }
2123
2124 #[test]
2125 fn test_multi_proof_verify_invalid_position() {
2126 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
2128
2129 let mut builder = Builder::<Sha256>::new(digests.len());
2131 for digest in &digests {
2132 builder.add(digest);
2133 }
2134 let tree = builder.build();
2135 let root = tree.root();
2136 let mut hasher = Sha256::default();
2137
2138 let positions = [0, 3];
2140 let multi_proof = tree.multi_proof(positions).unwrap();
2141
2142 let invalid_elements = [(digests[0], 0), (digests[3], 100)];
2144 assert!(multi_proof
2145 .verify_multi_inclusion(&mut hasher, &invalid_elements, &root)
2146 .is_err());
2147 }
2148
2149 #[test]
2150 fn test_multi_proof_odd_tree_sizes() {
2151 for tree_size in [3, 5, 7, 9, 11, 13, 15] {
2153 let digests: Vec<Digest> = (0..tree_size as u32)
2154 .map(|i| Sha256::hash(&i.to_be_bytes()))
2155 .collect();
2156
2157 let mut builder = Builder::<Sha256>::new(digests.len());
2159 for digest in &digests {
2160 builder.add(digest);
2161 }
2162 let tree = builder.build();
2163 let root = tree.root();
2164 let mut hasher = Sha256::default();
2165
2166 let positions = [0, (tree_size - 1) as u32];
2168 let multi_proof = tree.multi_proof(positions).unwrap();
2169
2170 let elements: Vec<(Digest, u32)> = positions
2171 .iter()
2172 .map(|&p| (digests[p as usize], p))
2173 .collect();
2174 assert!(
2175 multi_proof
2176 .verify_multi_inclusion(&mut hasher, &elements, &root)
2177 .is_ok(),
2178 "Failed for tree_size={tree_size}"
2179 );
2180 }
2181 }
2182
2183 #[test]
2184 fn test_multi_proof_verify_empty_elements() {
2185 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
2187
2188 let mut builder = Builder::<Sha256>::new(digests.len());
2189 for digest in &digests {
2190 builder.add(digest);
2191 }
2192 let tree = builder.build();
2193 let root = tree.root();
2194 let mut hasher = Sha256::default();
2195
2196 let positions = [0, 3];
2198 let multi_proof = tree.multi_proof(positions).unwrap();
2199
2200 let empty_elements: &[(Digest, u32)] = &[];
2202 assert!(multi_proof
2203 .verify_multi_inclusion(&mut hasher, empty_elements, &root)
2204 .is_err());
2205 }
2206
2207 #[test]
2208 fn test_multi_proof_default_verify() {
2209 let mut hasher = Sha256::default();
2211 let default_proof = Proof::<Digest>::default();
2212
2213 let empty_elements: &[(Digest, u32)] = &[];
2215
2216 let builder = Builder::<Sha256>::new(0);
2218 let empty_tree = builder.build();
2219 let empty_root = empty_tree.root();
2220
2221 assert!(default_proof
2222 .verify_multi_inclusion(&mut hasher, empty_elements, &empty_root)
2223 .is_ok());
2224
2225 let wrong_root = Sha256::hash(b"not_empty");
2227 assert!(default_proof
2228 .verify_multi_inclusion(&mut hasher, empty_elements, &wrong_root)
2229 .is_err());
2230 }
2231
2232 #[test]
2233 fn test_multi_proof_single_leaf_tree() {
2234 let digest = Sha256::hash(b"only_leaf");
2236
2237 let mut builder = Builder::<Sha256>::new(1);
2239 builder.add(&digest);
2240 let tree = builder.build();
2241 let root = tree.root();
2242 let mut hasher = Sha256::default();
2243
2244 let multi_proof = tree.multi_proof([0]).unwrap();
2246
2247 assert_eq!(multi_proof.leaf_count, 1);
2249
2250 assert!(
2252 multi_proof.siblings.is_empty(),
2253 "Single leaf tree should have no siblings"
2254 );
2255
2256 let elements = [(digest, 0u32)];
2258 assert!(
2259 multi_proof
2260 .verify_multi_inclusion(&mut hasher, &elements, &root)
2261 .is_ok(),
2262 "Single leaf multi-proof verification failed"
2263 );
2264
2265 let wrong_digest = Sha256::hash(b"wrong");
2267 let wrong_elements = [(wrong_digest, 0u32)];
2268 assert!(
2269 multi_proof
2270 .verify_multi_inclusion(&mut hasher, &wrong_elements, &root)
2271 .is_err(),
2272 "Should fail with wrong digest"
2273 );
2274
2275 let wrong_position_elements = [(digest, 1u32)];
2277 assert!(
2278 multi_proof
2279 .verify_multi_inclusion(&mut hasher, &wrong_position_elements, &root)
2280 .is_err(),
2281 "Should fail with invalid position"
2282 );
2283 }
2284
2285 #[test]
2286 fn test_multi_proof_malicious_leaf_count_zero() {
2287 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
2289
2290 let mut builder = Builder::<Sha256>::new(digests.len());
2291 for digest in &digests {
2292 builder.add(digest);
2293 }
2294 let tree = builder.build();
2295 let root = tree.root();
2296 let mut hasher = Sha256::default();
2297
2298 let positions = [0, 3];
2300 let mut multi_proof = tree.multi_proof(positions).unwrap();
2301 multi_proof.leaf_count = 0;
2302
2303 let elements: Vec<(Digest, u32)> = positions
2304 .iter()
2305 .map(|&p| (digests[p as usize], p))
2306 .collect();
2307
2308 assert!(multi_proof
2310 .verify_multi_inclusion(&mut hasher, &elements, &root)
2311 .is_err());
2312 }
2313
2314 #[test]
2315 fn test_multi_proof_malicious_leaf_count_larger() {
2316 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
2318
2319 let mut builder = Builder::<Sha256>::new(digests.len());
2320 for digest in &digests {
2321 builder.add(digest);
2322 }
2323 let tree = builder.build();
2324 let root = tree.root();
2325 let mut hasher = Sha256::default();
2326
2327 let positions = [0, 3];
2329 let mut multi_proof = tree.multi_proof(positions).unwrap();
2330 let original_leaf_count = multi_proof.leaf_count;
2331 multi_proof.leaf_count = 1000;
2332
2333 let elements: Vec<(Digest, u32)> = positions
2334 .iter()
2335 .map(|&p| (digests[p as usize], p))
2336 .collect();
2337
2338 assert!(
2340 multi_proof
2341 .verify_multi_inclusion(&mut hasher, &elements, &root)
2342 .is_err(),
2343 "Should reject proof with inflated leaf_count ({} -> {})",
2344 original_leaf_count,
2345 multi_proof.leaf_count
2346 );
2347 }
2348
2349 #[test]
2350 fn test_multi_proof_malicious_leaf_count_smaller() {
2351 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
2353
2354 let mut builder = Builder::<Sha256>::new(digests.len());
2355 for digest in &digests {
2356 builder.add(digest);
2357 }
2358 let tree = builder.build();
2359 let root = tree.root();
2360 let mut hasher = Sha256::default();
2361
2362 let positions = [0, 3];
2364 let mut multi_proof = tree.multi_proof(positions).unwrap();
2365 multi_proof.leaf_count = 4; let elements: Vec<(Digest, u32)> = positions
2368 .iter()
2369 .map(|&p| (digests[p as usize], p))
2370 .collect();
2371
2372 assert!(
2374 multi_proof
2375 .verify_multi_inclusion(&mut hasher, &elements, &root)
2376 .is_err(),
2377 "Should reject proof with deflated leaf_count"
2378 );
2379 }
2380
2381 #[test]
2382 fn test_multi_proof_mismatched_element_count() {
2383 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
2385
2386 let mut builder = Builder::<Sha256>::new(digests.len());
2387 for digest in &digests {
2388 builder.add(digest);
2389 }
2390 let tree = builder.build();
2391 let root = tree.root();
2392 let mut hasher = Sha256::default();
2393
2394 let positions = [0, 3];
2396 let multi_proof = tree.multi_proof(positions).unwrap();
2397
2398 let too_few = [(digests[0], 0u32)];
2400 assert!(
2401 multi_proof
2402 .verify_multi_inclusion(&mut hasher, &too_few, &root)
2403 .is_err(),
2404 "Should reject when fewer elements provided than proof was generated for"
2405 );
2406
2407 let too_many = [(digests[0], 0u32), (digests[3], 3), (digests[5], 5)];
2409 assert!(
2410 multi_proof
2411 .verify_multi_inclusion(&mut hasher, &too_many, &root)
2412 .is_err(),
2413 "Should reject when more elements provided than proof was generated for"
2414 );
2415 }
2416
2417 #[test]
2418 fn test_multi_proof_swapped_siblings() {
2419 let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
2421
2422 let mut builder = Builder::<Sha256>::new(digests.len());
2423 for digest in &digests {
2424 builder.add(digest);
2425 }
2426 let tree = builder.build();
2427 let root = tree.root();
2428 let mut hasher = Sha256::default();
2429
2430 let positions = [0, 5];
2432 let mut multi_proof = tree.multi_proof(positions).unwrap();
2433
2434 if multi_proof.siblings.len() >= 2 {
2436 multi_proof.siblings.swap(0, 1);
2438
2439 let elements: Vec<(Digest, u32)> = positions
2440 .iter()
2441 .map(|&p| (digests[p as usize], p))
2442 .collect();
2443
2444 assert!(
2445 multi_proof
2446 .verify_multi_inclusion(&mut hasher, &elements, &root)
2447 .is_err(),
2448 "Should reject proof with swapped siblings"
2449 );
2450 }
2451 }
2452
2453 #[test]
2454 fn test_multi_proof_dos_large_leaf_count() {
2455 let digests: Vec<Digest> = (0..4u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
2458
2459 let mut builder = Builder::<Sha256>::new(digests.len());
2460 for digest in &digests {
2461 builder.add(digest);
2462 }
2463 let tree = builder.build();
2464 let root = tree.root();
2465 let mut hasher = Sha256::default();
2466
2467 let positions = [0, 2];
2469 let mut multi_proof = tree.multi_proof(positions).unwrap();
2470
2471 multi_proof.leaf_count = u32::MAX;
2473
2474 let elements: Vec<(Digest, u32)> = positions
2475 .iter()
2476 .map(|&p| (digests[p as usize], p))
2477 .collect();
2478
2479 let result = multi_proof.verify_multi_inclusion(&mut hasher, &elements, &root);
2482 assert!(result.is_err(), "Should reject malicious large leaf_count");
2483 }
2484
2485 #[cfg(feature = "arbitrary")]
2486 mod conformance {
2487 use super::*;
2488 use commonware_codec::conformance::CodecConformance;
2489 use commonware_conformance::Conformance;
2490 use commonware_cryptography::sha256::Digest as Sha256Digest;
2491
2492 fn test_merkle_tree(n: usize) -> Digest {
2493 let mut digests = Vec::with_capacity(n);
2495 let mut builder = Builder::<Sha256>::new(n);
2496 for i in 0..n {
2497 let digest = Sha256::hash(&i.to_be_bytes());
2498 builder.add(&digest);
2499 digests.push(digest);
2500 }
2501 let tree = builder.build();
2502 let root = tree.root();
2503
2504 let mut hasher = Sha256::default();
2506 for (i, leaf) in digests.iter().enumerate() {
2507 let proof = tree.proof(i as u32).unwrap();
2509 assert!(
2510 proof
2511 .verify_element_inclusion(&mut hasher, leaf, i as u32, &root)
2512 .is_ok(),
2513 "correct fail for size={n} leaf={i}"
2514 );
2515
2516 let serialized = proof.encode();
2518 let deserialized = Proof::<Digest>::decode_cfg(serialized, &1).unwrap();
2519 assert!(
2520 deserialized
2521 .verify_element_inclusion(&mut hasher, leaf, i as u32, &root)
2522 .is_ok(),
2523 "deserialize fail for size={n} leaf={i}"
2524 );
2525
2526 if !proof.siblings.is_empty() {
2528 let mut update_tamper = proof.clone();
2529 update_tamper.siblings[0] = Sha256::hash(b"tampered");
2530 assert!(
2531 update_tamper
2532 .verify_element_inclusion(&mut hasher, leaf, i as u32, &root)
2533 .is_err(),
2534 "modify fail for size={n} leaf={i}"
2535 );
2536 }
2537
2538 let mut add_tamper = proof.clone();
2540 add_tamper.siblings.push(Sha256::hash(b"tampered"));
2541 assert!(
2542 add_tamper
2543 .verify_element_inclusion(&mut hasher, leaf, i as u32, &root)
2544 .is_err(),
2545 "add fail for size={n} leaf={i}"
2546 );
2547
2548 if !proof.siblings.is_empty() {
2550 let mut remove_tamper = proof.clone();
2551 remove_tamper.siblings.pop();
2552 assert!(
2553 remove_tamper
2554 .verify_element_inclusion(&mut hasher, leaf, i as u32, &root)
2555 .is_err(),
2556 "remove fail for size={n} leaf={i}"
2557 );
2558 }
2559 }
2560
2561 assert!(tree.proof(n as u32).is_err());
2563
2564 root
2566 }
2567
2568 struct RootConformance;
2569
2570 impl Conformance for RootConformance {
2571 async fn commit(seed: u64) -> Vec<u8> {
2572 let root = test_merkle_tree(seed as usize);
2573 root.to_vec()
2574 }
2575 }
2576
2577 commonware_conformance::conformance_tests! {
2578 CodecConformance<Proof<Sha256Digest>>,
2579 RootConformance => 200
2580 }
2581 }
2582}