Skip to main content

commonware_storage/bmt/
mod.rs

1//! Stateless Binary Merkle Tree (BMT).
2//!
3//! The Binary Merkle Tree is constructed level-by-level. The first level consists of position-hashed leaf digests.
4//! On each additional level, pairs of nodes are hashed from the previous level (if a level contains an odd
5//! number of nodes, the last node is duplicated). The finalized root of the tree incorporates the leaf count
6//! to prevent proof malleability: `root = hash(leaf_count || tree_root)`.
7//!
8//! For example, given three leaves A, B, and C, the tree is constructed as follows:
9//!
10//! ```text
11//!     Level 2 (tree_root):  [hash(hash(hash(0,A),hash(1,B)),hash(hash(2,C),hash(2,C)))]
12//!     Level 1:              [hash(hash(0,A),hash(1,B)),hash(hash(2,C),hash(2,C))]
13//!     Level 0 (leaves):     [hash(0,A),hash(1,B),hash(2,C)]
14//!     Finalized root:       hash(3 || tree_root)
15//! ```
16//!
17//! A proof for one or more leaves is generated by collecting the siblings needed to reconstruct the root.
18//! An external process can then use this proof (with some trusted root) to verify that the leaves
19//! are part of the tree.
20//!
21//! # Example
22//!
23//! ```rust
24//! use commonware_storage::bmt::{Builder, Tree};
25//! use commonware_cryptography::{Sha256, sha256::Digest, Hasher as _};
26//!
27//! // Create transactions and compute their digests
28//! let txs = [b"tx1", b"tx2", b"tx3", b"tx4"];
29//! let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(*tx)).collect();
30//!
31//! // Build a Merkle Tree from the digests
32//! let mut builder = Builder::<Sha256>::new(digests.len());
33//! for digest in &digests {
34//!    builder.add(digest);
35//! }
36//! let tree = builder.build();
37//! let root = tree.root();
38//!
39//! // Generate a proof for leaf at index 1
40//! let mut hasher = Sha256::default();
41//! let proof = tree.proof(1).unwrap();
42//! assert!(proof.verify_element_inclusion(&mut hasher, &digests[1], 1, &root).is_ok());
43//! ```
44
45use 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
57/// There should never be more than 255 levels in a proof (would mean the Binary Merkle Tree
58/// has more than 2^255 leaves).
59pub const MAX_LEVELS: usize = u8::MAX as usize;
60
61/// Errors that can occur when working with a Binary Merkle Tree (BMT).
62#[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
76/// Constructor for a Binary Merkle Tree (BMT).
77pub struct Builder<H: Hasher> {
78    hasher: H,
79    leaves: Vec<H::Digest>,
80}
81
82impl<H: Hasher> Builder<H> {
83    /// Creates a new Binary Merkle Tree builder.
84    pub fn new(leaves: usize) -> Self {
85        Self {
86            hasher: H::new(),
87            leaves: Vec::with_capacity(leaves),
88        }
89    }
90
91    /// Adds a leaf to the Binary Merkle Tree.
92    ///
93    /// When added, the leaf is hashed with its position.
94    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    /// Builds the Binary Merkle Tree.
103    ///
104    /// It is valid to build a tree with no leaves, in which case
105    /// just an "empty" node is included (no leaves will be provable).
106    pub fn build(self) -> Tree<H::Digest> {
107        Tree::new(self.hasher, self.leaves)
108    }
109}
110
111/// Constructed Binary Merkle Tree (BMT).
112#[derive(Clone, Debug)]
113pub struct Tree<D: Digest> {
114    /// Records whether the tree is empty.
115    empty: bool,
116
117    /// The digests at each level of the tree (from leaves to root).
118    levels: NonEmptyVec<NonEmptyVec<D>>,
119
120    /// The finalized root digest, which incorporates the leaf count.
121    ///
122    /// This is computed as `H(leaf_count || tree_root)` to prevent
123    /// proof malleability where proofs that declare different leaf
124    /// counts could verify against the same root.
125    root: D,
126}
127
128impl<D: Digest> Tree<D> {
129    /// Builds a Merkle Tree from a slice of position-hashed leaf digests.
130    fn new<H: Hasher<Digest = D>>(mut hasher: H, mut leaves: Vec<D>) -> Self {
131        // If no leaves, add an empty node.
132        //
133        // Because this node only includes a position, there is no way a valid proof
134        // can be generated that references it.
135        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        // Create the first level
143        let mut levels = non_empty_vec![non_empty_vec![@leaves]];
144
145        // Construct the tree level-by-level
146        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                // Hash the left child
151                hasher.update(&chunk[0]);
152
153                // Hash the right child
154                if chunk.len() == 2 {
155                    hasher.update(&chunk[1]);
156                } else {
157                    // If no right child exists, duplicate left child.
158                    hasher.update(&chunk[0]);
159                };
160
161                // Compute the parent digest
162                next_level.push(hasher.finalize());
163            }
164
165            // Add the computed level to the tree
166            levels.push(non_empty_vec![@next_level]);
167            current_level = levels.last();
168        }
169
170        // Compute the finalized root: H(leaf_count || tree_root)
171        // This binds the root to the tree size, preventing malleability attacks.
172        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    /// Returns the finalized root of the tree.
185    ///
186    /// The root incorporates the leaf count via `H(leaf_count || tree_root)`,
187    /// which prevents proof malleability attacks where different tree sizes
188    /// could produce valid proofs for the same root.
189    pub const fn root(&self) -> D {
190        self.root
191    }
192
193    /// Generates a Merkle proof for the leaf at `position`.
194    ///
195    /// This is a single-element multi-proof, which includes the minimal siblings
196    /// needed to reconstruct the root.
197    pub fn proof(&self, position: u32) -> Result<Proof<D>, Error> {
198        self.multi_proof(core::iter::once(position))
199    }
200
201    /// Generates a Merkle range proof for a contiguous set of leaves from `start`
202    /// to `end` (inclusive).
203    ///
204    /// The proof contains the minimal set of sibling digests needed to reconstruct
205    /// the root for all elements in the range. This is more efficient than individual
206    /// proofs when proving multiple consecutive elements.
207    pub fn range_proof(&self, start: u32, end: u32) -> Result<Proof<D>, Error> {
208        // For empty trees, return an empty proof
209        if self.empty {
210            if start == 0 && end == 0 {
211                return Ok(Proof::default());
212            }
213            return Err(Error::InvalidPosition(start));
214        }
215
216        // Validate range bounds
217        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        // Compute required siblings without enumerating every leaf in the range.
229        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    /// Generates a Merkle proof for multiple non-contiguous leaves at the given `positions`.
242    ///
243    /// The proof contains the minimal set of sibling digests needed to reconstruct
244    /// the root for all elements at the specified positions. This is more efficient
245    /// than individual proofs when proving multiple elements because shared siblings
246    /// are deduplicated.
247    ///
248    /// Positions are sorted internally; duplicate positions will return an error.
249    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        // Handle empty positions first - can't prove zero elements
257        let first = *positions.peek().ok_or(Error::NoLeaves)?.borrow();
258
259        // Handle empty tree case
260        if self.empty {
261            return Err(Error::InvalidPosition(first));
262        }
263
264        let leaf_count = self.levels.first().len().get() as u32;
265
266        // Get required sibling positions (this validates positions and checks for duplicates)
267        let sibling_positions =
268            siblings_required_for_multi_proof(leaf_count, positions.map(|p| *p.borrow()))?;
269
270        // Collect sibling digests in order
271        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/// A Merkle proof for multiple non-contiguous leaves in a Binary Merkle Tree.
284///
285/// This proof type is more space-efficient than generating individual proofs
286/// for each leaf because sibling nodes that are shared between multiple paths
287/// are deduplicated.
288///
289/// The proof contains the leaf count and sibling digests required for verification.
290/// The leaf count is incorporated into the root hash during finalization, so
291/// modifying it will cause verification to fail (preventing malleability attacks).
292#[derive(Clone, Debug, Eq, PartialEq)]
293pub struct Proof<D: Digest> {
294    /// The number of leaves in the tree.
295    ///
296    /// This value is incorporated into the root hash during finalization,
297    /// so modifying it will cause verification to fail (prevents malleability).
298    pub leaf_count: u32,
299
300    /// The deduplicated sibling digests required to verify all elements,
301    /// ordered by their position in the tree (level-major, then index within level).
302    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    /// The maximum number of items being proven.
323    ///
324    /// The upper bound on sibling hashes is derived as `max_items * MAX_LEVELS`.
325    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
360/// Returns the number of levels in a tree with `leaf_count` leaves.
361/// A tree with 1 leaf has 1 level, a tree with 2 leaves has 2 levels, etc.
362const fn levels_in_tree(leaf_count: u32) -> usize {
363    (u32::BITS - (leaf_count.saturating_sub(1)).leading_zeros() + 1) as usize
364}
365
366/// Returns the sorted, deduplicated positions of siblings required to prove
367/// inclusion of leaves at the given positions.
368///
369/// Each position in the result is encoded as `(level, index)` where level 0 is the leaf level.
370fn siblings_required_for_multi_proof(
371    leaf_count: u32,
372    positions: impl IntoIterator<Item = u32>,
373) -> Result<BTreeSet<(usize, usize)>, Error> {
374    // Validate positions and check for duplicates.
375    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    // Track positions we can compute at each level and record missing siblings.
390    // This keeps the work proportional to the number of positions, not the tree size.
391    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 &current {
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
418/// Returns the sorted, deduplicated positions of siblings required to prove
419/// inclusion of a contiguous range of leaves from `start` to `end` (inclusive).
420fn 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    /// Verifies that a given `leaf` at `position` is included in a Binary Merkle Tree
465    /// with `root` using the provided `hasher`.
466    ///
467    /// The proof consists of sibling hashes stored from the leaf up to the root. At each
468    /// level, if the current node is a left child (even index), the sibling is combined
469    /// to the right; if it is a right child (odd index), the sibling is combined to the
470    /// left.
471    ///
472    /// The `leaf_count` stored in the proof is incorporated into the finalized root
473    /// computation, so any modification to it will cause verification to fail.
474    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        // Validate position
482        if position >= self.leaf_count {
483            return Err(Error::InvalidPosition(position));
484        }
485
486        // Compute the position-hashed leaf
487        hasher.update(&position.to_be_bytes());
488        hasher.update(leaf);
489        let mut computed = hasher.finalize();
490
491        // Track level size to handle odd-sized levels
492        let mut level_size = self.leaf_count as usize;
493        let mut sibling_iter = self.siblings.iter();
494
495        // Traverse from leaf to root
496        while level_size > 1 {
497            // Check if this is the last node at an odd-sized level (no real sibling)
498            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                // Node is duplicated - no sibling consumed from proof
502                (&computed, &computed)
503            } else if position.is_multiple_of(2) {
504                // Even position: sibling is to the right
505                let sibling = sibling_iter.next().ok_or(Error::UnalignedProof)?;
506                (&computed, sibling)
507            } else {
508                // Odd position: sibling is to the left
509                let sibling = sibling_iter.next().ok_or(Error::UnalignedProof)?;
510                (sibling, &computed)
511            };
512
513            // Compute the parent digest
514            hasher.update(left_node);
515            hasher.update(right_node);
516            computed = hasher.finalize();
517
518            // Move up the tree
519            position /= 2;
520            level_size = level_size.div_ceil(2);
521        }
522
523        // Ensure all siblings were consumed
524        if sibling_iter.next().is_some() {
525            return Err(Error::UnalignedProof);
526        }
527
528        // Finalize the root by incorporating the leaf count: H(leaf_count || tree_root)
529        // This binds the proof to the specific tree size, preventing malleability attacks.
530        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    /// Verifies that the given `elements` at their respective positions are included
542    /// in a Binary Merkle Tree with `root`.
543    ///
544    /// Elements can be provided in any order; positions are sorted internally.
545    /// Duplicate positions will cause verification to fail.
546    ///
547    /// The `leaf_count` stored in the proof is incorporated into the finalized root
548    /// computation, so any modification to it will cause verification to fail.
549    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        // Handle empty case
556        if elements.is_empty() {
557            if self.leaf_count == 0 && self.siblings.is_empty() {
558                // Compute finalized empty root: H(0 || empty_tree_root)
559                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        // 1. Sort elements by position and check for duplicates/bounds
573        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        // Check for duplicates (adjacent elements with same position after sorting)
585        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        // 2. Iterate up the tree
592        // Since we process left-to-right and parent_pos = pos/2, next_level stays sorted.
593        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                // Determine if we have the left or right child
606                let (left, right) = if pos.is_multiple_of(2) {
607                    // We are the LEFT child
608                    let left = digest;
609
610                    // Check if we have the right child in our current set
611                    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                        // If no right child exists in tree, duplicate left
616                        left
617                    } else {
618                        // Otherwise, must consume a sibling
619                        *sibling_iter.next().ok_or(Error::UnalignedProof)?
620                    };
621                    (left, right)
622                } else {
623                    // We are the RIGHT child
624                    // This implies the LEFT child was missing from 'current', so it must be a sibling.
625                    let right = digest;
626                    let left = *sibling_iter.next().ok_or(Error::UnalignedProof)?;
627                    (left, right)
628                };
629
630                // Hash parent
631                hasher.update(&left);
632                hasher.update(&right);
633                next_level.push((parent_pos, hasher.finalize()));
634
635                idx += 1;
636            }
637
638            // Prepare for next level
639            core::mem::swap(&mut current, &mut next_level);
640            next_level.clear();
641            level_size = level_size.div_ceil(2);
642        }
643
644        // 3. Verify root
645        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        // Finalize the root by incorporating the leaf count: H(leaf_count || tree_root)
654        // This binds the proof to the specific tree size, preventing malleability attacks.
655        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    /// Verifies that a contiguous range of `leaves` starting at `position` are included
668    /// in a Binary Merkle Tree with `root`.
669    ///
670    /// This is a convenience method for verifying range proofs. The leaves must be
671    /// in order starting from `position`.
672    ///
673    /// The `leaf_count` stored in the proof is incorporated into the finalized root
674    /// computation, so any modification to it will cause verification to fail.
675    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        // For empty trees, only position 0 with empty leaves is valid
683        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        // Convert to format expected by verify_multi_inclusion
698        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    /// Regression test for https://github.com/commonwarexyz/monorepo/issues/2837
715    ///
716    /// Before the fix, two proofs with identical siblings but different leaf_count
717    /// values would both verify successfully against the same root, enabling
718    /// proof malleability attacks.
719    #[test]
720    fn issue_2837_regression() {
721        // Create a tree with 255 leaves (as in the issue report)
722        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        // Get a valid proof for position 0
734        let original_proof = tree.proof(0).unwrap();
735        assert_eq!(original_proof.leaf_count, 255);
736
737        // Original proof should verify
738        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        // Create a malleated proof with leaf_count=254 but same siblings
747        // (This is the exact attack from issue #2837)
748        let malleated_proof = Proof {
749            leaf_count: 254,
750            siblings: original_proof.siblings.clone(),
751        };
752
753        // Malleated proof should NOT verify because the root now incorporates
754        // the leaf_count: root = H(leaf_count || tree_root)
755        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        // Create transactions and digests
765        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        // Build tree
770        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        // Build proof
778        let mut proof = tree.proof(0).unwrap();
779
780        // Tamper with proof
781        proof.siblings = Vec::new();
782
783        // Fail verification with an empty proof.
784        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        // Create transactions and digests
793        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        // Build tree
798        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        // Build proof
806        let mut proof = tree.proof(0).unwrap();
807
808        // Tamper with proof
809        proof.siblings.push(*element);
810
811        // Fail verification with extra sibling
812        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        // Create transactions and digests
821        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        // Build tree
825        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        // Generate a valid proof for leaf at index 2.
833        let proof = tree.proof(2).unwrap();
834
835        // Use a wrong element (e.g. hash of a different transaction).
836        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        // Create transactions and digests
846        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        // Build tree
850        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        // Generate a valid proof for leaf at index 1.
858        let proof = tree.proof(1).unwrap();
859
860        // Use an incorrect index (e.g. 2 instead of 1).
861        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        // Create transactions and digests
870        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        // Build tree
874        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        // Generate a valid proof for leaf at index 0.
881        let proof = tree.proof(0).unwrap();
882
883        // Use a wrong root (hash of a different input).
884        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        // Create transactions and digests
894        let txs = [b"tx1", b"tx2", b"tx3"];
895        let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(*tx)).collect();
896
897        // Build tree
898        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        // Generate a valid proof for leaf at index 1.
905        let proof = tree.proof(1).unwrap();
906        let mut serialized = proof.encode();
907
908        // Truncate one byte.
909        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        // Create transactions and digests
916        let txs = [b"tx1", b"tx2", b"tx3"];
917        let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(*tx)).collect();
918
919        // Build tree
920        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        // Generate a valid proof for leaf at index 1.
927        let proof = tree.proof(1).unwrap();
928        let mut serialized = proof.encode_mut();
929
930        // Append an extra byte.
931        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        // Create transactions and digests
938        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        // Build tree
942        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        // Generate a valid proof for leaf at index 2.
950        let mut proof = tree.proof(2).unwrap();
951
952        // Modify the first hash in the proof.
953        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        // Create transactions and digests
963        let txs = [b"tx1", b"tx2", b"tx3"];
964        let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(*tx)).collect();
965
966        // Build tree
967        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        // The tree was built with 3 leaves; index 2 is the last valid index.
975        let proof = tree.proof(2).unwrap();
976
977        // Verification should succeed for the proper index 2.
978        let mut hasher = Sha256::default();
979        assert!(proof
980            .verify_element_inclusion(&mut hasher, &digests[2], 2, &root)
981            .is_ok());
982
983        // Should not be able to generate a proof for an out-of-range index (e.g. 3).
984        assert!(tree.proof(3).is_err());
985
986        // Attempting to verify using an out-of-range index (e.g. 3, which would correspond
987        // to a duplicate leaf that doesn't actually exist) should fail.
988        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        // Create test data
996        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
997
998        // Build tree
999        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        // Test range proof for elements 2-5
1007        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        // Serialize and deserialize
1016        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        // Create test data
1026        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1027
1028        // Build tree
1029        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        // Test single element range proof
1037        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        // Create test data
1050        let digests: Vec<Digest> = (0..7u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1051
1052        // Build tree
1053        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        // Test full tree range proof
1061        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        // Create test data
1071        let digests: Vec<Digest> = (0..15u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1072
1073        // Build tree
1074        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        // Test first half
1083        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        // Test second half
1089        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        // Test last elements
1095        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        // Create test data
1104        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1105
1106        // Build tree
1107        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        // Test invalid ranges
1114        assert!(tree.range_proof(8, 8).is_err()); // Start out of bounds
1115        assert!(tree.range_proof(0, 8).is_err()); // End out of bounds
1116        assert!(tree.range_proof(5, 8).is_err()); // End out of bounds
1117        assert!(tree.range_proof(2, 1).is_err()); // Start > end
1118    }
1119
1120    #[test]
1121    fn test_range_proof_tampering() {
1122        // Create test data
1123        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1124
1125        // Build tree
1126        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        // Get valid range proof
1134        let range_proof = tree.range_proof(2, 4).unwrap();
1135        let mut hasher = Sha256::default();
1136        let range_leaves = &digests[2..5];
1137
1138        // Test with wrong leaves
1139        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        // Test with wrong number of leaves
1149        assert!(range_proof
1150            .verify_range_inclusion(&mut hasher, 2, &digests[2..4], &root)
1151            .is_err());
1152
1153        // Test with tampered proof
1154        let mut tampered_proof = range_proof.clone();
1155        assert!(!tampered_proof.siblings.is_empty());
1156        // Tamper with the first sibling
1157        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        // Test with wrong root
1163        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        // Test range proofs for trees of various sizes
1172        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            // Build tree
1178            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            // Test various range sizes
1187            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        // Create test data
1212        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1213
1214        // Build tree
1215        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        // Get valid range proof for position 2 to 4
1223        let range_proof = tree.range_proof(2, 4).unwrap();
1224        let mut hasher = Sha256::default();
1225        let range_leaves = &digests[2..5];
1226
1227        // Try to verify with wrong position
1228        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        // Create test data
1239        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1240
1241        // Build tree
1242        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        // Get valid range proof for position 2 to 4
1250        let range_proof = tree.range_proof(2, 4).unwrap();
1251        let mut hasher = Sha256::default();
1252
1253        // Try to verify with reordered leaves
1254        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        // Create test data
1263        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1264
1265        // Build tree
1266        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        // Get valid range proof
1274        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        // Tamper by adding extra siblings
1279        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        // Create test data
1288        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1289
1290        // Build tree
1291        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        // Get valid range proof for a single element (which needs siblings)
1299        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        // The proof should have siblings
1304        assert!(!range_proof.siblings.is_empty());
1305
1306        // Remove a sibling
1307        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        // Create test data
1316        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1317
1318        // Build tree
1319        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        // Test overflow in range_proof generation
1326        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        // Create test data
1334        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1335
1336        // Build tree
1337        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        // Get valid range proof
1345        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        // Add extra sibling (simulating proof from different tree structure)
1350        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        // Remove a sibling
1356        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        // Test various power-of-2 boundary conditions
1368        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            // Build tree
1374            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            // Test edge cases
1383            // First element only
1384            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            // Last element only
1390            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            // Full tree
1402            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        // Build an empty tree
1412        let builder = Builder::<Sha256>::new(0);
1413        let tree = builder.build();
1414
1415        // Empty tree should fail for any position since there are no elements
1416        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        // Build an empty tree
1424        let builder = Builder::<Sha256>::new(0);
1425        let tree = builder.build();
1426        let root = tree.root();
1427
1428        // Empty tree should return default proof only for (0, 0)
1429        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        // All other combinations should fail
1434        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        // Verify empty range proof against empty tree root
1449        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        // Should fail with non-empty leaves
1456        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        // Should fail with wrong root
1462        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        // Should fail with wrong position
1468        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        // Create multiple empty trees and verify they have the same root
1484        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        // All empty trees should have the same root
1492        for i in 1..roots.len() {
1493            assert_eq!(roots[0], roots[i]);
1494        }
1495
1496        // The root should be the hash of empty data
1497        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)] // Range starting at odd index (needs left sibling)
1506    #[case::need_right_sibling(4, 4)] // Range starting at even index
1507    #[case::full_tree(0, 16)] // Full tree (no siblings needed at leaf level)
1508    fn test_range_proof_siblings_usage(#[case] start: u32, #[case] count: u32) {
1509        // This test ensures that all siblings in a range proof are actually used during verification
1510        let digests: Vec<Digest> = (0..16u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1511
1512        // Build tree
1513        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        // Verify the proof works
1525        assert!(range_proof
1526            .verify_range_inclusion(&mut hasher, start, &digests[start as usize..end], &root)
1527            .is_ok());
1528
1529        // For each sibling, try tampering with it and verify the proof fails
1530        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    // Test trees with odd sizes that require duplicate nodes
1540    #[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        // Build tree
1549        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        // Test range including the last element (which may require duplicate handling)
1558        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        // Create test data
1570        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1571
1572        // Build tree
1573        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        // Test multi-proof for non-contiguous positions [0, 3, 5]
1581        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        // Create test data
1597        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1598
1599        // Build tree
1600        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        // Test single element multi-proof for each position
1609        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        // Create test data
1624        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1625
1626        // Build tree
1627        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        // Test multi-proof for all elements
1636        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        // When proving all elements, we shouldn't need any siblings (all can be computed)
1648        assert!(multi_proof.siblings.is_empty());
1649    }
1650
1651    #[test]
1652    fn test_multi_proof_adjacent_elements() {
1653        // Create test data
1654        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1655
1656        // Build tree
1657        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        // Test adjacent positions (should deduplicate shared siblings)
1666        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        // Create test data
1681        let digests: Vec<Digest> = (0..16u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1682
1683        // Build tree
1684        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        // Test widely separated positions
1693        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        // Build empty tree
1708        let builder = Builder::<Sha256>::new(0);
1709        let tree = builder.build();
1710
1711        // Empty tree with empty positions should return NoLeaves error
1712        // (we can't prove zero elements)
1713        assert!(matches!(
1714            tree.multi_proof(std::iter::empty::<u32>()),
1715            Err(Error::NoLeaves)
1716        ));
1717
1718        // Empty tree with any position should fail with InvalidPosition
1719        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        // Create test data
1728        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1729
1730        // Build tree
1731        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        // Empty positions should return error
1738        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        // Create test data
1747        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1748
1749        // Build tree
1750        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        // Duplicate positions should return error
1757        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        // Create test data
1770        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1771
1772        // Build tree
1773        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        // Test with unsorted positions (should work - internal sorting)
1782        let positions = [5, 0, 3];
1783        let multi_proof = tree.multi_proof(positions).unwrap();
1784
1785        // Verify with unsorted elements (should work - internal sorting)
1786        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        // Test multi-proofs for trees of various sizes
1795        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            // Build tree
1801            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            // Test various position combinations
1810            // First and last
1811            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            // Every other element
1828            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        // Create test data
1848        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1849
1850        // Build tree
1851        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        // Generate valid proof
1860        let positions = [0, 3, 5];
1861        let multi_proof = tree.multi_proof(positions).unwrap();
1862
1863        // Verify with wrong elements
1864        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        // Create test data
1877        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1878
1879        // Build tree
1880        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        // Generate valid proof
1889        let positions = [0, 3, 5];
1890        let multi_proof = tree.multi_proof(positions).unwrap();
1891
1892        // Verify with wrong positions (same elements, different positions)
1893        let wrong_positions = [
1894            (digests[0], 1), // wrong position
1895            (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        // Create test data
1906        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1907
1908        // Build tree
1909        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        // Generate valid proof
1917        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        // Verify with wrong root
1926        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        // Create test data
1935        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1936
1937        // Build tree
1938        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        // Generate valid proof
1947        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        // Tamper with sibling
1956        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        // Add extra sibling
1964        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        // Remove a sibling
1971        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        // Create test data
1981        let digests: Vec<Digest> = (0..16u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
1982
1983        // Build tree
1984        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        // Get individual proofs
1991        let individual_siblings: usize = [0u32, 1, 8, 9]
1992            .iter()
1993            .map(|&p| tree.proof(p).unwrap().siblings.len())
1994            .sum();
1995
1996        // Get multi-proof for same positions
1997        let multi_proof = tree.multi_proof([0, 1, 8, 9]).unwrap();
1998
1999        // Multi-proof should have fewer siblings due to deduplication
2000        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        // Create test data
2011        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
2012
2013        // Build tree
2014        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        // Generate proof
2023        let positions = [0, 3, 5];
2024        let multi_proof = tree.multi_proof(positions).unwrap();
2025
2026        // Serialize and deserialize
2027        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        // Verify deserialized proof works
2033        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        // Create test data
2045        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
2046
2047        // Build tree
2048        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        // Generate proof
2055        let positions = [0, 3, 5];
2056        let multi_proof = tree.multi_proof(positions).unwrap();
2057
2058        // Serialize and truncate
2059        let mut serialized = multi_proof.encode();
2060        serialized.truncate(serialized.len() - 1);
2061
2062        // Should fail to deserialize
2063        assert!(Proof::<Digest>::decode_cfg(&mut serialized, &positions.len()).is_err());
2064    }
2065
2066    #[test]
2067    fn test_multi_proof_serialization_extra() {
2068        // Create test data
2069        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
2070
2071        // Build tree
2072        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        // Generate proof
2079        let positions = [0, 3, 5];
2080        let multi_proof = tree.multi_proof(positions).unwrap();
2081
2082        // Serialize and add extra byte
2083        let mut serialized = multi_proof.encode_mut();
2084        serialized.extend_from_slice(&[0u8]);
2085
2086        // Should fail to deserialize
2087        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()); // leaf_count
2094        serialized.extend_from_slice(&1usize.encode()); // claims 1 sibling but no data follows
2095
2096        // Should fail because the buffer claims 1 sibling but doesn't have the data
2097        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        // Create test data
2104        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
2105
2106        // Build tree
2107        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        // Test out of bounds position
2114        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        // Create test data
2127        let digests: Vec<Digest> = (0..8u32).map(|i| Sha256::hash(&i.to_be_bytes())).collect();
2128
2129        // Build tree
2130        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        // Generate valid proof
2139        let positions = [0, 3];
2140        let multi_proof = tree.multi_proof(positions).unwrap();
2141
2142        // Try to verify with out of bounds position
2143        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        // Test odd-sized trees that require node duplication
2152        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            // Build tree
2158            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            // Test with positions including the last element
2167            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        // Create a valid proof and try to verify with empty elements
2186        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        // Generate valid proof
2197        let positions = [0, 3];
2198        let multi_proof = tree.multi_proof(positions).unwrap();
2199
2200        // Try to verify with empty elements
2201        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        // Default (empty) proof should only verify against empty tree
2210        let mut hasher = Sha256::default();
2211        let default_proof = Proof::<Digest>::default();
2212
2213        // Empty elements against default proof
2214        let empty_elements: &[(Digest, u32)] = &[];
2215
2216        // Build empty tree to get the empty root
2217        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        // Should fail with wrong root
2226        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        // Edge case: tree with exactly one leaf
2235        let digest = Sha256::hash(b"only_leaf");
2236
2237        // Build single-leaf tree
2238        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        // Generate multi-proof for the only leaf
2245        let multi_proof = tree.multi_proof([0]).unwrap();
2246
2247        // Single leaf tree: leaf_count should be 1
2248        assert_eq!(multi_proof.leaf_count, 1);
2249
2250        // Single leaf tree: no siblings needed (leaf is the root after position hashing)
2251        assert!(
2252            multi_proof.siblings.is_empty(),
2253            "Single leaf tree should have no siblings"
2254        );
2255
2256        // Verify the proof
2257        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        // Verify with wrong digest fails
2266        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        // Verify with wrong position fails
2276        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        // Attacker sets leaf_count = 0 but provides siblings
2288        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        // Generate valid proof and tamper with leaf_count
2299        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        // Should fail - leaf_count=0 but we have elements
2309        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        // Attacker inflates leaf_count to claim proof is for larger tree
2317        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        // Generate valid proof and inflate leaf_count
2328        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        // Should fail - inflated leaf_count changes required siblings
2339        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        // Attacker deflates leaf_count to claim proof is for smaller tree
2352        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        // Generate valid proof and deflate leaf_count
2363        let positions = [0, 3];
2364        let mut multi_proof = tree.multi_proof(positions).unwrap();
2365        multi_proof.leaf_count = 4; // Smaller than actual tree
2366
2367        let elements: Vec<(Digest, u32)> = positions
2368            .iter()
2369            .map(|&p| (digests[p as usize], p))
2370            .collect();
2371
2372        // Should fail - deflated leaf_count changes tree structure
2373        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        // Provide more or fewer elements than the proof was generated for
2384        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        // Generate proof for 2 positions
2395        let positions = [0, 3];
2396        let multi_proof = tree.multi_proof(positions).unwrap();
2397
2398        // Try to verify with only 1 element (too few)
2399        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        // Try to verify with 3 elements (too many)
2408        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        // Swap the order of siblings in the proof
2420        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        // Generate valid proof with multiple siblings
2431        let positions = [0, 5];
2432        let mut multi_proof = tree.multi_proof(positions).unwrap();
2433
2434        // Ensure we have at least 2 siblings to swap
2435        if multi_proof.siblings.len() >= 2 {
2436            // Swap first two siblings
2437            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        // Attacker sets massive leaf_count trying to cause DoS via memory allocation
2456        // The verify function should NOT allocate proportional to leaf_count
2457        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        // Generate valid proof
2468        let positions = [0, 2];
2469        let mut multi_proof = tree.multi_proof(positions).unwrap();
2470
2471        // Set massive leaf_count (attacker trying to exhaust memory)
2472        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        // This should fail quickly without allocating massive memory
2480        // The function is O(elements * levels), not O(leaf_count)
2481        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            // Build tree
2494            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            // For each leaf, generate and verify its proof
2505            let mut hasher = Sha256::default();
2506            for (i, leaf) in digests.iter().enumerate() {
2507                // Generate proof
2508                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                // Serialize and deserialize the proof
2517                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                // Modify a sibling hash and ensure the proof fails
2527                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                // Add a sibling hash and ensure the proof fails
2539                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                // Remove a sibling hash and ensure the proof fails
2549                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            // Test proof for larger than size
2562            assert!(tree.proof(n as u32).is_err());
2563
2564            // Return the root so we can ensure we don't silently change.
2565            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}