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 proof = tree.proof(1).unwrap();
41//! assert!(proof.verify_element_inclusion::<Sha256>(&digests[1], 1, &root).is_ok());
42//! ```
43
44use alloc::{
45    string::{String, ToString},
46    vec,
47    vec::Vec,
48};
49use bytes::BufMut;
50use commonware_codec::{
51    Buf, EncodeSize, FixedSize, Read, ReadExt, ReadRangeExt, Write, util::max_items_size,
52};
53use commonware_cryptography::{Digest, Hasher};
54use commonware_parallel::Sequential;
55use commonware_utils::{non_empty_vec, vec::NonEmptyVec};
56use thiserror::Error;
57
58/// There should never be more than 32 sibling levels in a proof. Because
59/// [Proof::leaf_count] is a `u32`, a tree can have at most `u32::MAX` leaves,
60/// which requires at most `u32::BITS` sibling hashes per proven item.
61pub const MAX_LEVELS: usize = u32::BITS as usize;
62
63/// Errors that can occur when working with a Binary Merkle Tree (BMT).
64#[derive(Error, Debug)]
65pub enum Error {
66    #[error("invalid position: {0}")]
67    InvalidPosition(u32),
68    #[error("invalid proof: {0} != {1}")]
69    InvalidProof(String, String),
70    #[error("no leaves")]
71    NoLeaves,
72    #[error("unaligned proof")]
73    UnalignedProof,
74    #[error("duplicate position: {0}")]
75    DuplicatePosition(u32),
76    #[error("unsorted position: {0}")]
77    UnsortedPosition(u32),
78}
79
80/// Constructor for a Binary Merkle Tree (BMT).
81pub struct Builder<H: Hasher> {
82    leaves: Vec<H::Digest>,
83}
84
85impl<H: Hasher> Builder<H> {
86    /// Creates a new Binary Merkle Tree builder.
87    pub fn new(leaves: usize) -> Self {
88        Self {
89            leaves: Vec::with_capacity(leaves),
90        }
91    }
92
93    /// Adds a leaf to the Binary Merkle Tree.
94    ///
95    /// When added, the leaf is hashed with its position.
96    ///
97    /// # Panics
98    ///
99    /// Panics if the tree already has `u32::MAX` leaves.
100    pub fn add(&mut self, leaf: &H::Digest) -> u32 {
101        // The count after this add must fit in Proof::leaf_count, a u32, so the
102        // maximum position is u32::MAX - 1.
103        let position: u32 = self.leaves.len().try_into().expect("too many leaves");
104        assert!(position < u32::MAX, "too many leaves");
105
106        let digest = H::hash(&[&position.to_be_bytes(), leaf.as_ref()]);
107        self.leaves.push(digest);
108        position
109    }
110
111    /// Builds the Binary Merkle Tree.
112    ///
113    /// It is valid to build a tree with no leaves, in which case
114    /// just an "empty" node is included (no leaves will be provable).
115    pub fn build(self) -> Tree<H::Digest> {
116        Tree::new::<H>(self.leaves)
117    }
118}
119
120/// Constructed Binary Merkle Tree (BMT).
121#[derive(Clone, Debug)]
122pub struct Tree<D: Digest> {
123    /// Records whether the tree is empty.
124    empty: bool,
125
126    /// The digests at each level of the tree (from leaves to root).
127    levels: NonEmptyVec<NonEmptyVec<D>>,
128
129    /// The finalized root digest, which incorporates the leaf count.
130    ///
131    /// This is computed as `H(leaf_count || tree_root)` to prevent
132    /// proof malleability where proofs that declare different leaf
133    /// counts could verify against the same root.
134    root: D,
135}
136
137impl<D: Digest> Tree<D> {
138    /// Builds a Merkle Tree from a slice of position-hashed leaf digests.
139    fn new<H: Hasher<Digest = D>>(mut leaves: Vec<D>) -> Self {
140        // If no leaves, add an empty node.
141        //
142        // Because this node only includes a position, there is no way a valid proof
143        // can be generated that references it.
144        let mut empty = false;
145        let leaf_count = leaves.len() as u32;
146        if leaves.is_empty() {
147            leaves.push(H::hash(&[]));
148            empty = true;
149        }
150
151        // Create the first level
152        let mut levels = non_empty_vec![non_empty_vec![@leaves]];
153
154        // Construct the tree level-by-level
155        let mut current_level = levels.last();
156        while !current_level.is_singleton() {
157            let mut next_level = Vec::with_capacity(current_level.len().get().div_ceil(2));
158
159            // Process four nodes (two sibling pairs) at a time, duplicating an unpaired
160            // trailing node. Hashing both pairs together lets the underlying hasher
161            // interleave independent messages (see `Hasher::hash_pair`). A trailing
162            // group with a single pair falls back to a single hash.
163            for group in current_level.chunks(4) {
164                match group {
165                    [a, b, c, d] => {
166                        let (left, right) = H::hash_pair(
167                            &[a.as_ref(), b.as_ref()],
168                            &[c.as_ref(), d.as_ref()],
169                            &Sequential,
170                        );
171                        next_level.push(left);
172                        next_level.push(right);
173                    }
174                    [a, b, c] => {
175                        let (left, right) = H::hash_pair(
176                            &[a.as_ref(), b.as_ref()],
177                            &[c.as_ref(), c.as_ref()],
178                            &Sequential,
179                        );
180                        next_level.push(left);
181                        next_level.push(right);
182                    }
183                    [a, b] => next_level.push(H::hash(&[a.as_ref(), b.as_ref()])),
184                    [a] => next_level.push(H::hash(&[a.as_ref(), a.as_ref()])),
185                    _ => unreachable!("chunks(4) yields at most 4 elements"),
186                }
187            }
188
189            // Add the computed level to the tree
190            levels.push(non_empty_vec![@next_level]);
191            current_level = levels.last();
192        }
193
194        // Compute the finalized root: H(leaf_count || tree_root)
195        // This binds the root to the tree size, preventing malleability attacks.
196        let tree_root = levels.last().first();
197        let root = H::hash(&[&leaf_count.to_be_bytes(), tree_root.as_ref()]);
198
199        Self {
200            empty,
201            levels,
202            root,
203        }
204    }
205
206    /// Returns the finalized root of the tree.
207    ///
208    /// The root incorporates the leaf count via `H(leaf_count || tree_root)`,
209    /// which prevents proof malleability attacks where different tree sizes
210    /// could produce valid proofs for the same root.
211    pub const fn root(&self) -> D {
212        self.root
213    }
214
215    /// Generates a Merkle proof for the leaf at `position`.
216    ///
217    /// The proof holds the minimal siblings needed to reconstruct the root and
218    /// equals the single-element multi-proof for `position`.
219    pub fn proof(&self, position: u32) -> Result<Proof<D>, Error> {
220        // The empty tree's placeholder node passes the bound check, so it is
221        // rejected separately.
222        let leaf_count = self.levels.first().len().get() as u32;
223        if self.empty || position >= leaf_count {
224            return Err(Error::InvalidPosition(position));
225        }
226
227        // Walk up from the leaf and record the other child of each parent on the
228        // path. `index ^ 1` is the left neighbor of an odd index or the right
229        // neighbor of an even index. The right neighbor is absent only for the last
230        // node of an odd-sized level, and that node is paired with itself. The root
231        // level has no parent and is skipped.
232        let levels = self.levels.len().get();
233        let mut siblings = Vec::with_capacity(levels - 1);
234        let mut index = position as usize;
235        for level in &self.levels[..levels - 1] {
236            if let Some(sibling) = level.get(index ^ 1) {
237                siblings.push(*sibling);
238            }
239            index /= 2;
240        }
241
242        Ok(Proof {
243            leaf_count,
244            siblings,
245        })
246    }
247
248    /// Generates a Merkle range proof for a contiguous set of leaves from `start`
249    /// to `end` (inclusive).
250    ///
251    /// The proof contains the minimal set of sibling digests needed to reconstruct
252    /// the root for all elements in the range. This is more efficient than individual
253    /// proofs when proving multiple consecutive elements.
254    pub fn range_proof(&self, start: u32, end: u32) -> Result<Proof<D>, Error> {
255        // For empty trees, return an empty proof
256        if self.empty {
257            if start == 0 && end == 0 {
258                return Ok(Proof::default());
259            }
260            return Err(Error::InvalidPosition(start));
261        }
262
263        // Validate range bounds
264        if start > end {
265            return Err(Error::InvalidPosition(start));
266        }
267        let leaf_count = self.levels.first().len().get() as u32;
268        if start >= leaf_count {
269            return Err(Error::InvalidPosition(start));
270        }
271        if end >= leaf_count {
272            return Err(Error::InvalidPosition(end));
273        }
274
275        // Compute required siblings without enumerating every leaf in the range.
276        // There are at most two per level and they arrive sorted by level and
277        // index, so their digests are pushed straight into the proof.
278        let mut siblings = Vec::with_capacity(2 * (levels_in_tree(leaf_count) - 1));
279        siblings_required_for_range_proof(leaf_count, start, end, |level, index| {
280            siblings.push(self.levels[level][index])
281        })?;
282
283        Ok(Proof {
284            leaf_count,
285            siblings,
286        })
287    }
288
289    /// Generates a Merkle proof for multiple non-contiguous leaves at the given `positions`.
290    ///
291    /// The proof contains the minimal set of sibling digests needed to reconstruct
292    /// the root for all elements at the specified positions. This is more efficient
293    /// than individual proofs when proving multiple elements because shared siblings
294    /// are deduplicated.
295    ///
296    /// Positions must be strictly ascending and in range. The error names the
297    /// first position in input order that is out of range, repeats the previous
298    /// one, or precedes it.
299    pub fn multi_proof<I, P>(&self, positions: I) -> Result<Proof<D>, Error>
300    where
301        I: IntoIterator<Item = P>,
302        P: core::borrow::Borrow<u32>,
303    {
304        let mut positions = positions.into_iter().peekable();
305
306        // Handle empty positions first, since a proof cannot cover zero elements
307        let first = *positions.peek().ok_or(Error::NoLeaves)?.borrow();
308
309        // Handle empty tree case
310        if self.empty {
311            return Err(Error::InvalidPosition(first));
312        }
313
314        let leaf_count = self.levels.first().len().get() as u32;
315
316        // Get required sibling positions (this validates positions and their order)
317        let sibling_positions =
318            siblings_required_for_multi_proof(leaf_count, positions.map(|p| *p.borrow()))?;
319
320        // Collect sibling digests in order. Their count is known from the helper's
321        // result, so the digests need at most one allocation.
322        let siblings: Vec<D> = sibling_positions
323            .iter()
324            .map(|&(level, index)| self.levels[level][index])
325            .collect();
326
327        Ok(Proof {
328            leaf_count,
329            siblings,
330        })
331    }
332}
333
334/// A Merkle proof for multiple non-contiguous leaves in a Binary Merkle Tree.
335///
336/// This proof type is more space-efficient than generating individual proofs
337/// for each leaf because sibling nodes that are shared between multiple paths
338/// are deduplicated.
339///
340/// The proof contains the leaf count and sibling digests required for verification.
341/// The leaf count is incorporated into the root hash during finalization, so
342/// modifying it will cause verification to fail (preventing malleability attacks).
343#[derive(Clone, Debug, Eq, PartialEq)]
344pub struct Proof<D: Digest> {
345    /// The number of leaves in the tree.
346    ///
347    /// This value is incorporated into the root hash during finalization,
348    /// so modifying it will cause verification to fail (prevents malleability).
349    pub leaf_count: u32,
350
351    /// The deduplicated sibling digests required to verify all elements,
352    /// ordered by their position in the tree (level-major, then index within level).
353    pub siblings: Vec<D>,
354}
355
356impl<D: Digest> Default for Proof<D> {
357    fn default() -> Self {
358        Self {
359            leaf_count: 0,
360            siblings: Vec::new(),
361        }
362    }
363}
364
365impl<D: Digest> Write for Proof<D> {
366    fn write(&self, writer: &mut impl BufMut) {
367        self.leaf_count.write(writer);
368        self.siblings.write(writer);
369    }
370}
371
372impl<D: Digest> Read for Proof<D> {
373    /// The maximum number of items being proven.
374    ///
375    /// The upper bound on sibling hashes is derived as `max_items * MAX_LEVELS`.
376    type Cfg = usize;
377
378    fn read_cfg(
379        reader: &mut impl Buf,
380        max_items: &Self::Cfg,
381    ) -> Result<Self, commonware_codec::Error> {
382        let leaf_count = u32::read(reader)?;
383        let max_siblings = max_items.saturating_mul(MAX_LEVELS);
384        let siblings = Vec::<D>::read_range(reader, ..=max_siblings)?;
385        Ok(Self {
386            leaf_count,
387            siblings,
388        })
389    }
390}
391
392impl<D: Digest> EncodeSize for Proof<D> {
393    fn encode_size(&self) -> usize {
394        self.leaf_count.encode_size() + self.siblings.encode_size()
395    }
396}
397
398#[cfg(feature = "arbitrary")]
399impl<D: Digest> arbitrary::Arbitrary<'_> for Proof<D>
400where
401    D: for<'a> arbitrary::Arbitrary<'a>,
402{
403    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
404        Ok(Self {
405            leaf_count: u.arbitrary()?,
406            siblings: u.arbitrary()?,
407        })
408    }
409}
410
411/// Returns the number of levels in a tree with `leaf_count` leaves.
412/// A tree with 1 leaf has 1 level, a tree with 2 leaves has 2 levels, etc.
413const fn levels_in_tree(leaf_count: u32) -> usize {
414    (u32::BITS - (leaf_count.saturating_sub(1)).leading_zeros() + 1) as usize
415}
416
417/// Returns the positions of siblings required to prove inclusion of leaves at the
418/// given positions. The result is ordered by level and then by index within the
419/// level.
420///
421/// Each position in the result is encoded as `(level, index)` where level 0 is the leaf level.
422///
423/// Positions must be strictly ascending and below `leaf_count`. The error names
424/// the first position in input order that is not.
425fn siblings_required_for_multi_proof(
426    leaf_count: u32,
427    positions: impl IntoIterator<Item = u32>,
428) -> Result<Vec<(usize, usize)>, Error> {
429    // Strictly ascending input is sorted and unique by construction, so each
430    // position only needs comparing with the previous one, and reading stops at
431    // the first invalid position.
432    //
433    // An exact length (as from a slice) sizes the buffer upfront. Any other
434    // iterator grows it as positions are read, so one that never ends cannot
435    // reserve memory for positions it has not produced.
436    let positions = positions.into_iter();
437    let capacity = match positions.size_hint() {
438        (lower, Some(upper)) if lower == upper => lower.min(leaf_count as usize),
439        _ => 0,
440    };
441    let mut current: Vec<usize> = Vec::with_capacity(capacity);
442    for pos in positions {
443        if pos >= leaf_count {
444            return Err(Error::InvalidPosition(pos));
445        }
446        match current.last() {
447            Some(&last) if last == pos as usize => return Err(Error::DuplicatePosition(pos)),
448            Some(&last) if last > pos as usize => return Err(Error::UnsortedPosition(pos)),
449            _ => current.push(pos as usize),
450        }
451    }
452    if current.is_empty() {
453        return Err(Error::NoLeaves);
454    }
455
456    // Track the sorted, unique indices that can be computed at each level and
457    // record the missing siblings. Each level costs work proportional to the
458    // number of positions, not to the level's size.
459    let mut next = Vec::with_capacity(current.len());
460    let mut sibling_positions = Vec::with_capacity(current.len());
461    let levels_count = levels_in_tree(leaf_count);
462    let mut level_size = leaf_count as usize;
463    for level in 0..levels_count - 1 {
464        for (offset, &index) in current.iter().enumerate() {
465            // The sibling is the other child of the same parent. Because
466            // `current` is sorted and unique, the sibling is known exactly when
467            // it is the adjacent entry.
468            let sibling = index ^ 1;
469            let known = if index.is_multiple_of(2) {
470                current.get(offset + 1) == Some(&sibling)
471            } else {
472                offset > 0 && current[offset - 1] == sibling
473            };
474
475            // The last node of an odd-sized level is paired with itself and needs
476            // no sibling. Sibling pairs are disjoint, so each level's pushes are
477            // ascending and unique.
478            if sibling < level_size && !known {
479                sibling_positions.push((level, sibling));
480            }
481
482            // Parents of sorted indices are sorted, and children of one parent
483            // are adjacent, so skipping a repeat of the last parent keeps
484            // `next` unique.
485            let parent = index / 2;
486            if next.last() != Some(&parent) {
487                next.push(parent);
488            }
489        }
490
491        // Move up a level, keeping the old buffer for the next level's parents.
492        core::mem::swap(&mut current, &mut next);
493        next.clear();
494        level_size = level_size.div_ceil(2);
495    }
496
497    Ok(sibling_positions)
498}
499
500/// Calls `sibling` with the position of each sibling required to prove inclusion
501/// of a contiguous range of leaves from `start` to `end` (inclusive). Calls are
502/// ordered by level and then by index within the level.
503///
504/// Each position is passed as `(level, index)` where level 0 is the leaf level.
505/// Bounds are validated before any position is passed.
506fn siblings_required_for_range_proof(
507    leaf_count: u32,
508    start: u32,
509    end: u32,
510    mut sibling: impl FnMut(usize, usize),
511) -> Result<(), Error> {
512    if leaf_count == 0 {
513        return Err(Error::NoLeaves);
514    }
515    if start > end {
516        return Err(Error::InvalidPosition(start));
517    }
518    if start >= leaf_count {
519        return Err(Error::InvalidPosition(start));
520    }
521    if end >= leaf_count {
522        return Err(Error::InvalidPosition(end));
523    }
524
525    let levels_count = levels_in_tree(leaf_count);
526    let mut level_start = start as usize;
527    let mut level_end = end as usize;
528    let mut level_size = leaf_count as usize;
529
530    // Each level needs at most two siblings: the left neighbor of the range
531    // start, then the right neighbor of the range end. They lie on opposite
532    // sides of the range, so they are distinct and ascending.
533    for level in 0..levels_count - 1 {
534        if !level_start.is_multiple_of(2) {
535            sibling(level, level_start - 1);
536        }
537        if level_end.is_multiple_of(2) {
538            let right = level_end + 1;
539            if right < level_size {
540                sibling(level, right);
541            }
542        }
543
544        level_start /= 2;
545        level_end /= 2;
546        level_size = level_size.div_ceil(2);
547    }
548
549    Ok(())
550}
551
552impl<D: Digest> Proof<D> {
553    /// Returns an upper bound on the encoded size of a proof with at most `siblings` sibling
554    /// digests.
555    pub fn max_encode_size(siblings: usize) -> usize {
556        max_items_size(siblings, D::SIZE) + u32::SIZE
557    }
558
559    /// Verifies that a given `leaf` at `position` is included in a Binary Merkle Tree
560    /// with `root`.
561    ///
562    /// The proof consists of sibling hashes stored from the leaf up to the root. At each
563    /// level, if the current node is a left child (even index), the sibling is combined
564    /// to the right; if it is a right child (odd index), the sibling is combined to the
565    /// left.
566    ///
567    /// The `leaf_count` stored in the proof is incorporated into the finalized root
568    /// computation, so any modification to it will cause verification to fail.
569    pub fn verify_element_inclusion<H: Hasher<Digest = D>>(
570        &self,
571        leaf: &D,
572        mut position: u32,
573        root: &D,
574    ) -> Result<(), Error> {
575        // Validate position
576        if position >= self.leaf_count {
577            return Err(Error::InvalidPosition(position));
578        }
579
580        // Compute the position-hashed leaf
581        let mut computed = H::hash(&[&position.to_be_bytes(), leaf.as_ref()]);
582
583        // Track level size to handle odd-sized levels
584        let mut level_size = self.leaf_count as usize;
585        let mut sibling_iter = self.siblings.iter();
586
587        // Traverse from leaf to root
588        while level_size > 1 {
589            // Check if this is the last node at an odd-sized level (no real sibling)
590            let is_last_odd = position.is_multiple_of(2) && position as usize + 1 >= level_size;
591
592            let (left_node, right_node) = if is_last_odd {
593                // Node is duplicated, so no sibling is consumed from the proof
594                (computed, computed)
595            } else if position.is_multiple_of(2) {
596                // Even position: sibling is to the right
597                let sibling = *sibling_iter.next().ok_or(Error::UnalignedProof)?;
598                (computed, sibling)
599            } else {
600                // Odd position: sibling is to the left
601                let sibling = *sibling_iter.next().ok_or(Error::UnalignedProof)?;
602                (sibling, computed)
603            };
604
605            // Compute the parent digest
606            computed = H::hash(&[left_node.as_ref(), right_node.as_ref()]);
607
608            // Move up the tree
609            position /= 2;
610            level_size = level_size.div_ceil(2);
611        }
612
613        // Ensure all siblings were consumed
614        if sibling_iter.next().is_some() {
615            return Err(Error::UnalignedProof);
616        }
617
618        // Finalize the root by incorporating the leaf count: H(leaf_count || tree_root)
619        // This binds the proof to the specific tree size, preventing malleability attacks.
620        let finalized = H::hash(&[&self.leaf_count.to_be_bytes(), computed.as_ref()]);
621
622        if finalized == *root {
623            Ok(())
624        } else {
625            Err(Error::InvalidProof(finalized.to_string(), root.to_string()))
626        }
627    }
628
629    /// Verifies that the given `elements` at their respective positions are included
630    /// in a Binary Merkle Tree with `root`.
631    ///
632    /// Positions must be strictly ascending and in range. The error names the
633    /// first position in input order that is out of range, repeats the previous
634    /// one, or precedes it.
635    ///
636    /// The `leaf_count` stored in the proof is incorporated into the finalized root
637    /// computation, so any modification to it will cause verification to fail.
638    pub fn verify_multi_inclusion<H: Hasher<Digest = D>>(
639        &self,
640        elements: &[(D, u32)],
641        root: &D,
642    ) -> Result<(), Error> {
643        // Handle empty case
644        if elements.is_empty() {
645            if self.leaf_count == 0 && self.siblings.is_empty() {
646                // Compute finalized empty root: H(0 || empty_tree_root)
647                let empty_tree_root = H::hash(&[]);
648                let finalized = H::hash(&[&0u32.to_be_bytes(), empty_tree_root.as_ref()]);
649                if finalized == *root {
650                    return Ok(());
651                } else {
652                    return Err(Error::InvalidProof(finalized.to_string(), root.to_string()));
653                }
654            }
655            return Err(Error::NoLeaves);
656        }
657
658        // 1. Check that positions are strictly ascending and in range
659        let mut previous = None;
660        for &(_, position) in elements {
661            if position >= self.leaf_count {
662                return Err(Error::InvalidPosition(position));
663            }
664            match previous {
665                Some(previous) if position == previous => {
666                    return Err(Error::DuplicatePosition(position));
667                }
668                Some(previous) if position < previous => {
669                    return Err(Error::UnsortedPosition(position));
670                }
671                _ => previous = Some(position),
672            }
673        }
674
675        // 2. Hash the position-prefixed leaves, two at a time via `hash_pair`
676        let mut current: Vec<(u32, D)> = Vec::with_capacity(elements.len());
677        let (leaf_chunks, leaf_remainder) = elements.as_chunks::<2>();
678        for chunk in leaf_chunks {
679            let (leaf_a, pos_a) = &chunk[0];
680            let (leaf_b, pos_b) = &chunk[1];
681            let (digest_a, digest_b) = H::hash_pair(
682                &[&pos_a.to_be_bytes(), leaf_a.as_ref()],
683                &[&pos_b.to_be_bytes(), leaf_b.as_ref()],
684                &Sequential,
685            );
686            current.push((*pos_a, digest_a));
687            current.push((*pos_b, digest_b));
688        }
689        for (leaf, position) in leaf_remainder {
690            let digest = H::hash(&[&position.to_be_bytes(), leaf.as_ref()]);
691            current.push((*position, digest));
692        }
693
694        // 3. Iterate up the tree
695        // Since we process left-to-right and parent_pos = pos/2, next_level stays sorted.
696        let levels = levels_in_tree(self.leaf_count);
697        let mut level_size = self.leaf_count;
698        let mut sibling_iter = self.siblings.iter();
699        let mut next_level: Vec<(u32, D)> = Vec::with_capacity(current.len());
700        let mut parents: Vec<(u32, D, D)> = Vec::with_capacity(current.len());
701
702        for _ in 0..levels - 1 {
703            // First pass: determine each parent's (left, right) children without hashing, so
704            // independent parent digests can be batched together in the second pass.
705            let mut idx = 0;
706            while idx < current.len() {
707                let (pos, digest) = current[idx];
708                let parent_pos = pos / 2;
709
710                // Determine if we have the left or right child
711                let (left, right) = if pos.is_multiple_of(2) {
712                    // We are the LEFT child
713                    let left = digest;
714
715                    // Check if we have the right child in our current set
716                    let right = if idx + 1 < current.len() && current[idx + 1].0 == pos + 1 {
717                        idx += 1;
718                        current[idx].1
719                    } else if pos + 1 >= level_size {
720                        // If no right child exists in tree, duplicate left
721                        left
722                    } else {
723                        // Otherwise, must consume a sibling
724                        *sibling_iter.next().ok_or(Error::UnalignedProof)?
725                    };
726                    (left, right)
727                } else {
728                    // We are the RIGHT child
729                    // This implies the LEFT child was missing from 'current', so it must be a sibling.
730                    let right = digest;
731                    let left = *sibling_iter.next().ok_or(Error::UnalignedProof)?;
732                    (left, right)
733                };
734
735                parents.push((parent_pos, left, right));
736                idx += 1;
737            }
738
739            // Second pass: hash independent parent digests two at a time via `hash_pair`.
740            let (parent_chunks, parent_remainder) = parents.as_chunks::<2>();
741            for chunk in parent_chunks {
742                let (pos_a, left_a, right_a) = chunk[0];
743                let (pos_b, left_b, right_b) = chunk[1];
744                let (digest_a, digest_b) = H::hash_pair(
745                    &[left_a.as_ref(), right_a.as_ref()],
746                    &[left_b.as_ref(), right_b.as_ref()],
747                    &Sequential,
748                );
749                next_level.push((pos_a, digest_a));
750                next_level.push((pos_b, digest_b));
751            }
752            for &(pos, left, right) in parent_remainder {
753                next_level.push((pos, H::hash(&[left.as_ref(), right.as_ref()])));
754            }
755            parents.clear();
756
757            // Prepare for next level
758            core::mem::swap(&mut current, &mut next_level);
759            next_level.clear();
760            level_size = level_size.div_ceil(2);
761        }
762
763        // 4. Verify root
764        if sibling_iter.next().is_some() {
765            return Err(Error::UnalignedProof);
766        }
767
768        if current.len() != 1 {
769            return Err(Error::UnalignedProof);
770        }
771
772        // Finalize the root by incorporating the leaf count: H(leaf_count || tree_root)
773        // This binds the proof to the specific tree size, preventing malleability attacks.
774        let tree_root = current[0].1;
775        let finalized = H::hash(&[&self.leaf_count.to_be_bytes(), tree_root.as_ref()]);
776
777        if finalized == *root {
778            Ok(())
779        } else {
780            Err(Error::InvalidProof(finalized.to_string(), root.to_string()))
781        }
782    }
783
784    /// Verifies that a contiguous range of `leaves` starting at `position` are included
785    /// in a Binary Merkle Tree with `root`.
786    ///
787    /// This is a convenience method for verifying range proofs. The leaves must be
788    /// in order starting from `position`.
789    ///
790    /// The `leaf_count` stored in the proof is incorporated into the finalized root
791    /// computation, so any modification to it will cause verification to fail.
792    pub fn verify_range_inclusion<H: Hasher<Digest = D>>(
793        &self,
794        position: u32,
795        leaves: &[D],
796        root: &D,
797    ) -> Result<(), Error> {
798        // For empty trees, only position 0 with empty leaves is valid
799        if leaves.is_empty() && position != 0 {
800            return Err(Error::InvalidPosition(position));
801        }
802        if !leaves.is_empty() {
803            let leaves_len =
804                u32::try_from(leaves.len()).map_err(|_| Error::InvalidPosition(position))?;
805            let end = position
806                .checked_add(leaves_len - 1)
807                .ok_or(Error::InvalidPosition(position))?;
808            if end >= self.leaf_count {
809                return Err(Error::InvalidPosition(end));
810            }
811        }
812
813        // Convert to format expected by verify_multi_inclusion
814        let elements: Vec<(D, u32)> = leaves
815            .iter()
816            .enumerate()
817            .map(|(i, leaf)| (*leaf, position + i as u32))
818            .collect();
819        self.verify_multi_inclusion::<H>(&elements, root)
820    }
821}
822
823#[cfg(test)]
824mod tests {
825    use super::*;
826    use commonware_codec::{Decode, Encode};
827    use commonware_cryptography::sha256::{Digest, Sha256};
828    use rstest::rstest;
829
830    /// Regression test for https://github.com/commonwarexyz/monorepo/issues/2837
831    ///
832    /// Before the fix, two proofs with identical siblings but different leaf_count
833    /// values would both verify successfully against the same root, enabling
834    /// proof malleability attacks.
835    #[test]
836    fn issue_2837_regression() {
837        // Create a tree with 255 leaves (as in the issue report)
838        let digests: Vec<Digest> = (0..255u32)
839            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
840            .collect();
841
842        let mut builder = Builder::<Sha256>::new(255);
843        for digest in &digests {
844            builder.add(digest);
845        }
846        let tree = builder.build();
847        let root = tree.root();
848
849        // Get a valid proof for position 0
850        let original_proof = tree.proof(0).unwrap();
851        assert_eq!(original_proof.leaf_count, 255);
852
853        // Original proof should verify
854        assert!(
855            original_proof
856                .verify_element_inclusion::<Sha256>(&digests[0], 0, &root)
857                .is_ok(),
858            "Original proof should verify"
859        );
860
861        // Create a malleated proof with leaf_count=254 but same siblings
862        // (This is the exact attack from issue #2837)
863        let malleated_proof = Proof {
864            leaf_count: 254,
865            siblings: original_proof.siblings,
866        };
867
868        // Malleated proof should NOT verify because the root now incorporates
869        // the leaf_count: root = H(leaf_count || tree_root)
870        let result = malleated_proof.verify_element_inclusion::<Sha256>(&digests[0], 0, &root);
871        assert!(
872            result.is_err(),
873            "Malleated proof with wrong leaf_count must fail verification"
874        );
875    }
876
877    #[test]
878    fn test_tampered_proof_no_siblings() {
879        // Create transactions and digests
880        let txs = [b"tx1", b"tx2", b"tx3", b"tx4"];
881        let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(&[*tx])).collect();
882        let element = &digests[0];
883
884        // Build tree
885        let mut builder = Builder::<Sha256>::new(txs.len());
886        for digest in &digests {
887            builder.add(digest);
888        }
889        let tree = builder.build();
890        let root = tree.root();
891
892        // Build proof
893        let mut proof = tree.proof(0).unwrap();
894
895        // Tamper with proof
896        proof.siblings = Vec::new();
897
898        // Fail verification with an empty proof.
899        assert!(
900            proof
901                .verify_element_inclusion::<Sha256>(element, 0, &root)
902                .is_err()
903        );
904    }
905
906    #[test]
907    fn test_tampered_proof_extra_sibling() {
908        // Create transactions and digests
909        let txs = [b"tx1", b"tx2", b"tx3", b"tx4"];
910        let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(&[*tx])).collect();
911        let element = &digests[0];
912
913        // Build tree
914        let mut builder = Builder::<Sha256>::new(txs.len());
915        for digest in &digests {
916            builder.add(digest);
917        }
918        let tree = builder.build();
919        let root = tree.root();
920
921        // Build proof
922        let mut proof = tree.proof(0).unwrap();
923
924        // Tamper with proof
925        proof.siblings.push(*element);
926
927        // Fail verification with extra sibling
928        assert!(
929            proof
930                .verify_element_inclusion::<Sha256>(element, 0, &root)
931                .is_err()
932        );
933    }
934
935    #[test]
936    fn test_invalid_proof_wrong_element() {
937        // 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 proof = tree.proof(2).unwrap();
951
952        // Use a wrong element (e.g. hash of a different transaction).
953        let wrong_leaf = Sha256::hash(&[b"wrong_tx"]);
954        assert!(
955            proof
956                .verify_element_inclusion::<Sha256>(&wrong_leaf, 2, &root)
957                .is_err()
958        );
959    }
960
961    #[test]
962    fn test_invalid_proof_wrong_index() {
963        // Create transactions and digests
964        let txs = [b"tx1", b"tx2", b"tx3", b"tx4"];
965        let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(&[*tx])).collect();
966
967        // Build tree
968        let mut builder = Builder::<Sha256>::new(txs.len());
969        for digest in &digests {
970            builder.add(digest);
971        }
972        let tree = builder.build();
973        let root = tree.root();
974
975        // Generate a valid proof for leaf at index 1.
976        let proof = tree.proof(1).unwrap();
977
978        // Use an incorrect index (e.g. 2 instead of 1).
979        assert!(
980            proof
981                .verify_element_inclusion::<Sha256>(&digests[1], 2, &root)
982                .is_err()
983        );
984    }
985
986    #[test]
987    fn test_invalid_proof_wrong_root() {
988        // Create transactions and digests
989        let txs = [b"tx1", b"tx2", b"tx3", b"tx4"];
990        let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(&[*tx])).collect();
991
992        // Build tree
993        let mut builder = Builder::<Sha256>::new(txs.len());
994        for digest in &digests {
995            builder.add(digest);
996        }
997        let tree = builder.build();
998
999        // Generate a valid proof for leaf at index 0.
1000        let proof = tree.proof(0).unwrap();
1001
1002        // Use a wrong root (hash of a different input).
1003        let wrong_root = Sha256::hash(&[b"wrong_root"]);
1004        assert!(
1005            proof
1006                .verify_element_inclusion::<Sha256>(&digests[0], 0, &wrong_root)
1007                .is_err()
1008        );
1009    }
1010
1011    #[test]
1012    fn test_invalid_proof_serialization_truncated() {
1013        // Create transactions and digests
1014        let txs = [b"tx1", b"tx2", b"tx3"];
1015        let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(&[*tx])).collect();
1016
1017        // Build tree
1018        let mut builder = Builder::<Sha256>::new(txs.len());
1019        for digest in &digests {
1020            builder.add(digest);
1021        }
1022        let tree = builder.build();
1023
1024        // Generate a valid proof for leaf at index 1.
1025        let proof = tree.proof(1).unwrap();
1026        let mut serialized = proof.encode();
1027
1028        // Truncate one byte.
1029        serialized.truncate(serialized.len() - 1);
1030        assert!(Proof::<Digest>::decode_cfg(&mut serialized, &1).is_err());
1031    }
1032
1033    #[test]
1034    fn test_invalid_proof_serialization_extra() {
1035        // Create transactions and digests
1036        let txs = [b"tx1", b"tx2", b"tx3"];
1037        let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(&[*tx])).collect();
1038
1039        // Build tree
1040        let mut builder = Builder::<Sha256>::new(txs.len());
1041        for digest in &digests {
1042            builder.add(digest);
1043        }
1044        let tree = builder.build();
1045
1046        // Generate a valid proof for leaf at index 1.
1047        let proof = tree.proof(1).unwrap();
1048        let mut serialized = proof.encode_mut();
1049
1050        // Append an extra byte.
1051        serialized.extend_from_slice(&[0u8]);
1052        assert!(Proof::<Digest>::decode_cfg(&mut serialized, &1).is_err());
1053    }
1054
1055    #[test]
1056    fn test_invalid_proof_modified_hash() {
1057        // Create transactions and digests
1058        let txs = [b"tx1", b"tx2", b"tx3", b"tx4"];
1059        let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(&[*tx])).collect();
1060
1061        // Build tree
1062        let mut builder = Builder::<Sha256>::new(txs.len());
1063        for digest in &digests {
1064            builder.add(digest);
1065        }
1066        let tree = builder.build();
1067        let root = tree.root();
1068
1069        // Generate a valid proof for leaf at index 2.
1070        let mut proof = tree.proof(2).unwrap();
1071
1072        // Modify the first hash in the proof.
1073        proof.siblings[0] = Sha256::hash(&[b"modified"]);
1074        assert!(
1075            proof
1076                .verify_element_inclusion::<Sha256>(&digests[2], 2, &root)
1077                .is_err()
1078        );
1079    }
1080
1081    #[test]
1082    fn test_odd_tree_duplicate_index_proof() {
1083        // Create transactions and digests
1084        let txs = [b"tx1", b"tx2", b"tx3"];
1085        let digests: Vec<Digest> = txs.iter().map(|tx| Sha256::hash(&[*tx])).collect();
1086
1087        // Build tree
1088        let mut builder = Builder::<Sha256>::new(txs.len());
1089        for digest in &digests {
1090            builder.add(digest);
1091        }
1092        let tree = builder.build();
1093        let root = tree.root();
1094
1095        // The tree was built with 3 leaves; index 2 is the last valid index.
1096        let proof = tree.proof(2).unwrap();
1097
1098        // Verification should succeed for the proper index 2.
1099        assert!(
1100            proof
1101                .verify_element_inclusion::<Sha256>(&digests[2], 2, &root)
1102                .is_ok()
1103        );
1104
1105        // Should not be able to generate a proof for an out-of-range index (e.g. 3).
1106        assert!(tree.proof(3).is_err());
1107
1108        // Attempting to verify using an out-of-range index (e.g. 3, which would correspond
1109        // to a duplicate leaf that doesn't actually exist) should fail.
1110        assert!(
1111            proof
1112                .verify_element_inclusion::<Sha256>(&digests[2], 3, &root)
1113                .is_err()
1114        );
1115    }
1116
1117    #[test]
1118    fn test_range_proof_basic() {
1119        // Create test data
1120        let digests: Vec<Digest> = (0..8u32)
1121            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1122            .collect();
1123
1124        // Build tree
1125        let mut builder = Builder::<Sha256>::new(digests.len());
1126        for digest in &digests {
1127            builder.add(digest);
1128        }
1129        let tree = builder.build();
1130        let root = tree.root();
1131
1132        // Test range proof for elements 2-5
1133        let range_proof = tree.range_proof(2, 5).unwrap();
1134        let range_leaves = &digests[2..6];
1135
1136        assert!(
1137            range_proof
1138                .verify_range_inclusion::<Sha256>(2, range_leaves, &root)
1139                .is_ok()
1140        );
1141
1142        // Serialize and deserialize
1143        let mut serialized = range_proof.encode();
1144        let deserialized = Proof::<Digest>::decode_cfg(&mut serialized, &4).unwrap();
1145        assert!(
1146            deserialized
1147                .verify_range_inclusion::<Sha256>(2, range_leaves, &root)
1148                .is_ok()
1149        );
1150    }
1151
1152    #[test]
1153    fn test_range_proof_single_element() {
1154        // Create test data
1155        let digests: Vec<Digest> = (0..8u32)
1156            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1157            .collect();
1158
1159        // Build tree
1160        let mut builder = Builder::<Sha256>::new(digests.len());
1161        for digest in &digests {
1162            builder.add(digest);
1163        }
1164        let tree = builder.build();
1165        let root = tree.root();
1166
1167        // Test single element range proof
1168        for (i, digest) in digests.iter().enumerate() {
1169            let range_proof = tree.range_proof(i as u32, i as u32).unwrap();
1170
1171            let result = range_proof.verify_range_inclusion::<Sha256>(i as u32, &[*digest], &root);
1172            assert!(result.is_ok());
1173        }
1174    }
1175
1176    #[test]
1177    fn test_range_proof_full_tree() {
1178        // Create test data
1179        let digests: Vec<Digest> = (0..7u32)
1180            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1181            .collect();
1182
1183        // Build tree
1184        let mut builder = Builder::<Sha256>::new(digests.len());
1185        for digest in &digests {
1186            builder.add(digest);
1187        }
1188        let tree = builder.build();
1189        let root = tree.root();
1190
1191        // Test full tree range proof
1192        let range_proof = tree.range_proof(0, (digests.len() - 1) as u32).unwrap();
1193        assert!(
1194            range_proof
1195                .verify_range_inclusion::<Sha256>(0, &digests, &root)
1196                .is_ok()
1197        );
1198    }
1199
1200    #[test]
1201    fn test_range_proof_edge_cases() {
1202        // Create test data
1203        let digests: Vec<Digest> = (0..15u32)
1204            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1205            .collect();
1206
1207        // Build tree
1208        let mut builder = Builder::<Sha256>::new(digests.len());
1209        for digest in &digests {
1210            builder.add(digest);
1211        }
1212        let tree = builder.build();
1213        let root = tree.root();
1214
1215        // Test first half
1216        let range_proof = tree.range_proof(0, 7).unwrap();
1217        assert!(
1218            range_proof
1219                .verify_range_inclusion::<Sha256>(0, &digests[0..8], &root)
1220                .is_ok()
1221        );
1222
1223        // Test second half
1224        let range_proof = tree.range_proof(8, 14).unwrap();
1225        assert!(
1226            range_proof
1227                .verify_range_inclusion::<Sha256>(8, &digests[8..15], &root)
1228                .is_ok()
1229        );
1230
1231        // Test last elements
1232        let range_proof = tree.range_proof(13, 14).unwrap();
1233        assert!(
1234            range_proof
1235                .verify_range_inclusion::<Sha256>(13, &digests[13..15], &root)
1236                .is_ok()
1237        );
1238    }
1239
1240    #[test]
1241    fn test_range_proof_invalid_range() {
1242        // Create test data
1243        let digests: Vec<Digest> = (0..8u32)
1244            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1245            .collect();
1246
1247        // Build tree
1248        let mut builder = Builder::<Sha256>::new(digests.len());
1249        for digest in &digests {
1250            builder.add(digest);
1251        }
1252        let tree = builder.build();
1253
1254        // Test invalid ranges
1255        assert!(tree.range_proof(8, 8).is_err()); // Start out of bounds
1256        assert!(tree.range_proof(0, 8).is_err()); // End out of bounds
1257        assert!(tree.range_proof(5, 8).is_err()); // End out of bounds
1258        assert!(tree.range_proof(2, 1).is_err()); // Start > end
1259    }
1260
1261    #[test]
1262    fn test_range_proof_tampering() {
1263        // Create test data
1264        let digests: Vec<Digest> = (0..8u32)
1265            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1266            .collect();
1267
1268        // Build tree
1269        let mut builder = Builder::<Sha256>::new(digests.len());
1270        for digest in &digests {
1271            builder.add(digest);
1272        }
1273        let tree = builder.build();
1274        let root = tree.root();
1275
1276        // Get valid range proof
1277        let range_proof = tree.range_proof(2, 4).unwrap();
1278        let range_leaves = &digests[2..5];
1279
1280        // Test with wrong leaves
1281        let wrong_leaves = vec![
1282            Sha256::hash(&[b"wrong1"]),
1283            Sha256::hash(&[b"wrong2"]),
1284            Sha256::hash(&[b"wrong3"]),
1285        ];
1286        assert!(
1287            range_proof
1288                .verify_range_inclusion::<Sha256>(2, &wrong_leaves, &root)
1289                .is_err()
1290        );
1291
1292        // Test with wrong number of leaves
1293        assert!(
1294            range_proof
1295                .verify_range_inclusion::<Sha256>(2, &digests[2..4], &root)
1296                .is_err()
1297        );
1298
1299        // Test with tampered proof
1300        let mut tampered_proof = range_proof.clone();
1301        assert!(!tampered_proof.siblings.is_empty());
1302        // Tamper with the first sibling
1303        tampered_proof.siblings[0] = Sha256::hash(&[b"tampered"]);
1304        assert!(
1305            tampered_proof
1306                .verify_range_inclusion::<Sha256>(2, range_leaves, &root)
1307                .is_err()
1308        );
1309
1310        // Test with wrong root
1311        let wrong_root = Sha256::hash(&[b"wrong_root"]);
1312        assert!(
1313            range_proof
1314                .verify_range_inclusion::<Sha256>(2, range_leaves, &wrong_root)
1315                .is_err()
1316        );
1317    }
1318
1319    #[test]
1320    fn test_range_proof_various_sizes() {
1321        // Test range proofs for trees of various sizes
1322        for tree_size in [1, 2, 3, 4, 5, 7, 8, 15, 16, 31, 32, 63, 64] {
1323            let digests: Vec<Digest> = (0..tree_size as u32)
1324                .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1325                .collect();
1326
1327            // Build tree
1328            let mut builder = Builder::<Sha256>::new(digests.len());
1329            for digest in &digests {
1330                builder.add(digest);
1331            }
1332            let tree = builder.build();
1333            let root = tree.root();
1334
1335            // Test various range sizes
1336            for range_size in 1..=tree_size.min(8) {
1337                for start in 0..=(tree_size - range_size) {
1338                    let range_proof = tree
1339                        .range_proof(start as u32, (start + range_size - 1) as u32)
1340                        .unwrap();
1341                    let end = start + range_size;
1342                    assert!(
1343                        range_proof
1344                            .verify_range_inclusion::<Sha256>(
1345                                start as u32,
1346                                &digests[start..end],
1347                                &root
1348                            )
1349                            .is_ok(),
1350                        "Failed for tree_size={tree_size}, start={start}, range_size={range_size}"
1351                    );
1352                }
1353            }
1354        }
1355    }
1356
1357    #[test]
1358    fn test_range_proof_malicious_wrong_position() {
1359        // Create test data
1360        let digests: Vec<Digest> = (0..8u32)
1361            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1362            .collect();
1363
1364        // Build tree
1365        let mut builder = Builder::<Sha256>::new(digests.len());
1366        for digest in &digests {
1367            builder.add(digest);
1368        }
1369        let tree = builder.build();
1370        let root = tree.root();
1371
1372        // Get valid range proof for position 2 to 4
1373        let range_proof = tree.range_proof(2, 4).unwrap();
1374        let range_leaves = &digests[2..5];
1375
1376        // Try to verify with wrong position
1377        assert!(
1378            range_proof
1379                .verify_range_inclusion::<Sha256>(3, range_leaves, &root)
1380                .is_err()
1381        );
1382        assert!(
1383            range_proof
1384                .verify_range_inclusion::<Sha256>(1, range_leaves, &root)
1385                .is_err()
1386        );
1387    }
1388
1389    #[test]
1390    fn test_range_proof_malicious_reordered_leaves() {
1391        // Create test data
1392        let digests: Vec<Digest> = (0..8u32)
1393            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1394            .collect();
1395
1396        // Build tree
1397        let mut builder = Builder::<Sha256>::new(digests.len());
1398        for digest in &digests {
1399            builder.add(digest);
1400        }
1401        let tree = builder.build();
1402        let root = tree.root();
1403
1404        // Get valid range proof for position 2 to 4
1405        let range_proof = tree.range_proof(2, 4).unwrap();
1406
1407        // Try to verify with reordered leaves
1408        let reordered_leaves = vec![digests[3], digests[2], digests[4]];
1409        assert!(
1410            range_proof
1411                .verify_range_inclusion::<Sha256>(2, &reordered_leaves, &root)
1412                .is_err()
1413        );
1414    }
1415
1416    #[test]
1417    fn test_range_proof_malicious_extra_siblings() {
1418        // Create test data
1419        let digests: Vec<Digest> = (0..8u32)
1420            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1421            .collect();
1422
1423        // Build tree
1424        let mut builder = Builder::<Sha256>::new(digests.len());
1425        for digest in &digests {
1426            builder.add(digest);
1427        }
1428        let tree = builder.build();
1429        let root = tree.root();
1430
1431        // Get valid range proof
1432        let mut range_proof = tree.range_proof(2, 3).unwrap();
1433        let range_leaves = &digests[2..4];
1434
1435        // Tamper by adding extra siblings
1436        range_proof.siblings.push(Sha256::hash(&[b"extra"]));
1437        assert!(
1438            range_proof
1439                .verify_range_inclusion::<Sha256>(2, range_leaves, &root)
1440                .is_err()
1441        );
1442    }
1443
1444    #[test]
1445    fn test_range_proof_malicious_missing_siblings() {
1446        // Create test data
1447        let digests: Vec<Digest> = (0..8u32)
1448            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1449            .collect();
1450
1451        // Build tree
1452        let mut builder = Builder::<Sha256>::new(digests.len());
1453        for digest in &digests {
1454            builder.add(digest);
1455        }
1456        let tree = builder.build();
1457        let root = tree.root();
1458
1459        // Get valid range proof for a single element (which needs siblings)
1460        let mut range_proof = tree.range_proof(2, 2).unwrap();
1461        let range_leaves = &digests[2..3];
1462
1463        // The proof should have siblings
1464        assert!(!range_proof.siblings.is_empty());
1465
1466        // Remove a sibling
1467        range_proof.siblings.pop();
1468        assert!(
1469            range_proof
1470                .verify_range_inclusion::<Sha256>(2, range_leaves, &root)
1471                .is_err()
1472        );
1473    }
1474
1475    #[test]
1476    fn test_range_proof_integer_overflow_protection() {
1477        // Create test data
1478        let digests: Vec<Digest> = (0..8u32)
1479            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1480            .collect();
1481
1482        // Build tree
1483        let mut builder = Builder::<Sha256>::new(digests.len());
1484        for digest in &digests {
1485            builder.add(digest);
1486        }
1487        let tree = builder.build();
1488
1489        // Test overflow in range_proof generation
1490        assert!(tree.range_proof(u32::MAX, u32::MAX).is_err());
1491        assert!(tree.range_proof(u32::MAX - 1, u32::MAX).is_err());
1492        assert!(tree.range_proof(7, u32::MAX).is_err());
1493    }
1494
1495    #[test]
1496    fn test_range_proof_malicious_wrong_tree_structure() {
1497        // Create test data
1498        let digests: Vec<Digest> = (0..8u32)
1499            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1500            .collect();
1501
1502        // Build tree
1503        let mut builder = Builder::<Sha256>::new(digests.len());
1504        for digest in &digests {
1505            builder.add(digest);
1506        }
1507        let tree = builder.build();
1508        let root = tree.root();
1509
1510        // Get valid range proof
1511        let mut range_proof = tree.range_proof(2, 3).unwrap();
1512        let range_leaves = &digests[2..4];
1513
1514        // Add extra sibling (simulating proof from different tree structure)
1515        range_proof.siblings.push(Sha256::hash(&[b"fake_level"]));
1516        assert!(
1517            range_proof
1518                .verify_range_inclusion::<Sha256>(2, range_leaves, &root)
1519                .is_err()
1520        );
1521
1522        // Remove a sibling
1523        let mut range_proof = tree.range_proof(2, 2).unwrap();
1524        let range_leaves = &digests[2..3];
1525        assert!(!range_proof.siblings.is_empty());
1526        range_proof.siblings.pop();
1527        assert!(
1528            range_proof
1529                .verify_range_inclusion::<Sha256>(2, range_leaves, &root)
1530                .is_err()
1531        );
1532    }
1533
1534    #[test]
1535    fn test_range_proof_boundary_conditions() {
1536        // Test various power-of-2 boundary conditions
1537        for tree_size in [1, 2, 4, 8, 16, 32] {
1538            let digests: Vec<Digest> = (0..tree_size as u32)
1539                .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1540                .collect();
1541
1542            // Build tree
1543            let mut builder = Builder::<Sha256>::new(digests.len());
1544            for digest in &digests {
1545                builder.add(digest);
1546            }
1547            let tree = builder.build();
1548            let root = tree.root();
1549
1550            // Test edge cases
1551            // First element only
1552            let proof = tree.range_proof(0, 0).unwrap();
1553            assert!(
1554                proof
1555                    .verify_range_inclusion::<Sha256>(0, &digests[0..1], &root)
1556                    .is_ok()
1557            );
1558
1559            // Last element only
1560            let last_idx = tree_size - 1;
1561            let proof = tree.range_proof(last_idx as u32, last_idx as u32).unwrap();
1562            assert!(
1563                proof
1564                    .verify_range_inclusion::<Sha256>(
1565                        last_idx as u32,
1566                        &digests[last_idx..tree_size],
1567                        &root
1568                    )
1569                    .is_ok()
1570            );
1571
1572            // Full tree
1573            let proof = tree.range_proof(0, (tree_size - 1) as u32).unwrap();
1574            assert!(
1575                proof
1576                    .verify_range_inclusion::<Sha256>(0, &digests, &root)
1577                    .is_ok()
1578            );
1579        }
1580    }
1581
1582    #[test]
1583    fn test_empty_tree_proof() {
1584        // Build an empty tree
1585        let builder = Builder::<Sha256>::new(0);
1586        let tree = builder.build();
1587
1588        // Empty tree should fail for any position since there are no elements
1589        assert!(tree.proof(0).is_err());
1590        assert!(tree.proof(1).is_err());
1591        assert!(tree.proof(100).is_err());
1592    }
1593
1594    #[test]
1595    fn test_empty_tree_range_proof() {
1596        // Build an empty tree
1597        let builder = Builder::<Sha256>::new(0);
1598        let tree = builder.build();
1599        let root = tree.root();
1600
1601        // Empty tree should return default proof only for (0, 0)
1602        let range_proof = tree.range_proof(0, 0).unwrap();
1603        assert!(range_proof.siblings.is_empty());
1604        assert_eq!(range_proof, Proof::default());
1605
1606        // All other combinations should fail
1607        let invalid_ranges = vec![
1608            (0, 1),
1609            (0, 10),
1610            (1, 1),
1611            (1, 2),
1612            (5, 5),
1613            (10, 10),
1614            (0, u32::MAX),
1615            (u32::MAX, u32::MAX),
1616        ];
1617        for (start, end) in invalid_ranges {
1618            assert!(tree.range_proof(start, end).is_err());
1619        }
1620
1621        // Verify empty range proof against empty tree root
1622        let empty_leaves: &[Digest] = &[];
1623        assert!(
1624            range_proof
1625                .verify_range_inclusion::<Sha256>(0, empty_leaves, &root)
1626                .is_ok()
1627        );
1628
1629        // Should fail with non-empty leaves
1630        let non_empty_leaves = vec![Sha256::hash(&[b"leaf"])];
1631        assert!(
1632            range_proof
1633                .verify_range_inclusion::<Sha256>(0, &non_empty_leaves, &root)
1634                .is_err()
1635        );
1636
1637        // Should fail with wrong root
1638        let wrong_root = Sha256::hash(&[b"wrong"]);
1639        assert!(
1640            range_proof
1641                .verify_range_inclusion::<Sha256>(0, empty_leaves, &wrong_root)
1642                .is_err()
1643        );
1644
1645        // Should fail with wrong position
1646        assert!(
1647            range_proof
1648                .verify_range_inclusion::<Sha256>(1, empty_leaves, &root)
1649                .is_err()
1650        );
1651    }
1652
1653    #[test]
1654    fn test_empty_range_proof_serialization() {
1655        let proof = Proof::<Digest>::default();
1656        let mut serialized = proof.encode();
1657        let deserialized = Proof::<Digest>::decode_cfg(&mut serialized, &0).unwrap();
1658        assert_eq!(proof, deserialized);
1659    }
1660
1661    #[test]
1662    fn test_empty_tree_root_consistency() {
1663        // Create multiple empty trees and verify they have the same root
1664        let mut roots = Vec::new();
1665        for _ in 0..5 {
1666            let builder = Builder::<Sha256>::new(0);
1667            let tree = builder.build();
1668            roots.push(tree.root());
1669        }
1670
1671        // All empty trees should have the same root
1672        for i in 1..roots.len() {
1673            assert_eq!(roots[0], roots[i]);
1674        }
1675
1676        // The root should be the hash of empty data
1677        let empty = Sha256::hash(&[b""]);
1678        let expected_root = Sha256::hash(&[&0u32.to_be_bytes(), &empty]);
1679        assert_eq!(roots[0], expected_root);
1680    }
1681
1682    #[rstest]
1683    #[case::need_left_sibling(1, 2)] // Range starting at odd index (needs left sibling)
1684    #[case::need_right_sibling(4, 4)] // Range starting at even index
1685    #[case::full_tree(0, 16)] // Full tree (no siblings needed at leaf level)
1686    fn test_range_proof_siblings_usage(#[case] start: u32, #[case] count: u32) {
1687        // This test ensures that all siblings in a range proof are actually used during verification
1688        let digests: Vec<Digest> = (0..16u32)
1689            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1690            .collect();
1691
1692        // Build tree
1693        let mut builder = Builder::<Sha256>::new(digests.len());
1694        for digest in &digests {
1695            builder.add(digest);
1696        }
1697        let tree = builder.build();
1698        let root = tree.root();
1699
1700        let range_proof = tree.range_proof(start, start + count - 1).unwrap();
1701        let end = start as usize + count as usize;
1702
1703        // Verify the proof works
1704        assert!(
1705            range_proof
1706                .verify_range_inclusion::<Sha256>(start, &digests[start as usize..end], &root)
1707                .is_ok()
1708        );
1709
1710        // For each sibling, try tampering with it and verify the proof fails
1711        for sibling_idx in 0..range_proof.siblings.len() {
1712            let mut tampered_proof = range_proof.clone();
1713            tampered_proof.siblings[sibling_idx] = Sha256::hash(&[b"tampered"]);
1714            assert!(
1715                tampered_proof
1716                    .verify_range_inclusion::<Sha256>(start, &digests[start as usize..end], &root)
1717                    .is_err()
1718            );
1719        }
1720    }
1721
1722    // Test trees with odd sizes that require duplicate nodes
1723    #[rstest]
1724    fn test_range_proof_duplicate_node_edge_cases(
1725        #[values(3, 5, 7, 9, 11, 13, 15)] tree_size: usize,
1726    ) {
1727        let digests: Vec<Digest> = (0..tree_size as u32)
1728            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1729            .collect();
1730
1731        // Build tree
1732        let mut builder = Builder::<Sha256>::new(digests.len());
1733        for digest in &digests {
1734            builder.add(digest);
1735        }
1736        let tree = builder.build();
1737        let root = tree.root();
1738
1739        // Test range including the last element (which may require duplicate handling)
1740        let start = tree_size - 2;
1741        let proof = tree
1742            .range_proof(start as u32, (tree_size - 1) as u32)
1743            .unwrap();
1744        assert!(
1745            proof
1746                .verify_range_inclusion::<Sha256>(start as u32, &digests[start..tree_size], &root)
1747                .is_ok()
1748        );
1749    }
1750
1751    #[test]
1752    fn test_multi_proof_basic() {
1753        // Create test data
1754        let digests: Vec<Digest> = (0..8u32)
1755            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1756            .collect();
1757
1758        // Build tree
1759        let mut builder = Builder::<Sha256>::new(digests.len());
1760        for digest in &digests {
1761            builder.add(digest);
1762        }
1763        let tree = builder.build();
1764        let root = tree.root();
1765
1766        // Test multi-proof for non-contiguous positions [0, 3, 5]
1767        let positions = [0, 3, 5];
1768        let multi_proof = tree.multi_proof(positions).unwrap();
1769
1770        let elements: Vec<(Digest, u32)> = positions
1771            .iter()
1772            .map(|&p| (digests[p as usize], p))
1773            .collect();
1774        assert!(
1775            multi_proof
1776                .verify_multi_inclusion::<Sha256>(&elements, &root)
1777                .is_ok()
1778        );
1779    }
1780
1781    #[test]
1782    fn test_multi_proof_single_element() {
1783        // Create test data
1784        let digests: Vec<Digest> = (0..8u32)
1785            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1786            .collect();
1787
1788        // Build tree
1789        let mut builder = Builder::<Sha256>::new(digests.len());
1790        for digest in &digests {
1791            builder.add(digest);
1792        }
1793        let tree = builder.build();
1794        let root = tree.root();
1795
1796        // Test single element multi-proof for each position
1797        for (i, digest) in digests.iter().enumerate() {
1798            let multi_proof = tree.multi_proof([i as u32]).unwrap();
1799            let elements = [(*digest, i as u32)];
1800            assert!(
1801                multi_proof
1802                    .verify_multi_inclusion::<Sha256>(&elements, &root)
1803                    .is_ok(),
1804                "Failed for position {i}"
1805            );
1806        }
1807    }
1808
1809    #[test]
1810    fn test_multi_proof_all_elements() {
1811        // Create test data
1812        let digests: Vec<Digest> = (0..8u32)
1813            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1814            .collect();
1815
1816        // Build tree
1817        let mut builder = Builder::<Sha256>::new(digests.len());
1818        for digest in &digests {
1819            builder.add(digest);
1820        }
1821        let tree = builder.build();
1822        let root = tree.root();
1823
1824        // Test multi-proof for all elements
1825        let positions: Vec<u32> = (0..digests.len() as u32).collect();
1826        let multi_proof = tree.multi_proof(&positions).unwrap();
1827
1828        let elements: Vec<(Digest, u32)> = positions
1829            .iter()
1830            .map(|&p| (digests[p as usize], p))
1831            .collect();
1832        assert!(
1833            multi_proof
1834                .verify_multi_inclusion::<Sha256>(&elements, &root)
1835                .is_ok()
1836        );
1837
1838        // When proving all elements, we shouldn't need any siblings (all can be computed)
1839        assert!(multi_proof.siblings.is_empty());
1840    }
1841
1842    #[test]
1843    fn test_multi_proof_adjacent_elements() {
1844        // Create test data
1845        let digests: Vec<Digest> = (0..8u32)
1846            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1847            .collect();
1848
1849        // Build tree
1850        let mut builder = Builder::<Sha256>::new(digests.len());
1851        for digest in &digests {
1852            builder.add(digest);
1853        }
1854        let tree = builder.build();
1855        let root = tree.root();
1856
1857        // Test adjacent positions (should deduplicate shared siblings)
1858        let positions = [2, 3];
1859        let multi_proof = tree.multi_proof(positions).unwrap();
1860
1861        let elements: Vec<(Digest, u32)> = positions
1862            .iter()
1863            .map(|&p| (digests[p as usize], p))
1864            .collect();
1865        assert!(
1866            multi_proof
1867                .verify_multi_inclusion::<Sha256>(&elements, &root)
1868                .is_ok()
1869        );
1870    }
1871
1872    #[test]
1873    fn test_multi_proof_sparse_positions() {
1874        // Create test data
1875        let digests: Vec<Digest> = (0..16u32)
1876            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1877            .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
1887        // Test widely separated positions
1888        let positions = [0, 7, 8, 15];
1889        let multi_proof = tree.multi_proof(positions).unwrap();
1890
1891        let elements: Vec<(Digest, u32)> = positions
1892            .iter()
1893            .map(|&p| (digests[p as usize], p))
1894            .collect();
1895        assert!(
1896            multi_proof
1897                .verify_multi_inclusion::<Sha256>(&elements, &root)
1898                .is_ok()
1899        );
1900    }
1901
1902    #[test]
1903    fn test_multi_proof_empty_tree() {
1904        // Build empty tree
1905        let builder = Builder::<Sha256>::new(0);
1906        let tree = builder.build();
1907
1908        // Empty tree with empty positions should return NoLeaves error
1909        // (we can't prove zero elements)
1910        assert!(matches!(
1911            tree.multi_proof(std::iter::empty::<u32>()),
1912            Err(Error::NoLeaves)
1913        ));
1914
1915        // Empty tree with any position should fail with InvalidPosition
1916        assert!(matches!(
1917            tree.multi_proof([0]),
1918            Err(Error::InvalidPosition(0))
1919        ));
1920    }
1921
1922    #[test]
1923    fn test_multi_proof_empty_positions() {
1924        // Create test data
1925        let digests: Vec<Digest> = (0..8u32)
1926            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1927            .collect();
1928
1929        // Build tree
1930        let mut builder = Builder::<Sha256>::new(digests.len());
1931        for digest in &digests {
1932            builder.add(digest);
1933        }
1934        let tree = builder.build();
1935
1936        // Empty positions should return error
1937        assert!(matches!(
1938            tree.multi_proof(std::iter::empty::<u32>()),
1939            Err(Error::NoLeaves)
1940        ));
1941    }
1942
1943    #[test]
1944    fn test_multi_proof_duplicate_positions_error() {
1945        // Create test data
1946        let digests: Vec<Digest> = (0..8u32)
1947            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1948            .collect();
1949
1950        // Build tree
1951        let mut builder = Builder::<Sha256>::new(digests.len());
1952        for digest in &digests {
1953            builder.add(digest);
1954        }
1955        let tree = builder.build();
1956
1957        // Duplicate positions should return error
1958        assert!(matches!(
1959            tree.multi_proof([1, 1]),
1960            Err(Error::DuplicatePosition(1))
1961        ));
1962        assert!(matches!(
1963            tree.multi_proof([0, 2, 2, 5]),
1964            Err(Error::DuplicatePosition(2))
1965        ));
1966    }
1967
1968    #[test]
1969    fn test_multi_proof_reports_first_invalid_position() {
1970        // Build a tree with four leaves
1971        let digests: Vec<Digest> = (0..4u32)
1972            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
1973            .collect();
1974        let mut builder = Builder::<Sha256>::new(digests.len());
1975        for digest in &digests {
1976            builder.add(digest);
1977        }
1978        let tree = builder.build();
1979
1980        // Each position is checked against the previous one in input order, so the
1981        // first out-of-range, repeated, or descending position is reported even
1982        // when a later one would sort first.
1983        let cases = [
1984            (vec![], Error::NoLeaves),
1985            (vec![0, 0, 4], Error::DuplicatePosition(0)),
1986            (vec![4, 0, 0], Error::InvalidPosition(4)),
1987            (vec![3, 4], Error::InvalidPosition(4)),
1988            (vec![3, 1, 1, 3], Error::UnsortedPosition(1)),
1989            (vec![1, 3, 3, 1], Error::DuplicatePosition(3)),
1990            (vec![0, 1, 2, 3, 0, 4], Error::UnsortedPosition(0)),
1991            (vec![0, 1, 2, 3, 4, 0], Error::InvalidPosition(4)),
1992        ];
1993
1994        // Verification checks positions the same way before hashing anything, so
1995        // any proof from this tree must report the same errors.
1996        let proof = tree.multi_proof([0, 3]).unwrap();
1997        let root = tree.root();
1998        for (positions, expected) in cases {
1999            let result = tree.multi_proof(&positions);
2000            assert_eq!(
2001                format!("{:?}", result.err()),
2002                format!("{:?}", Some(&expected)),
2003                "multi_proof({positions:?}) must report the first invalid position"
2004            );
2005
2006            let elements: Vec<_> = positions.iter().map(|&p| (digests[0], p)).collect();
2007            let result = proof.verify_multi_inclusion::<Sha256>(&elements, &root);
2008            assert_eq!(
2009                format!("{:?}", result.err()),
2010                format!("{:?}", Some(&expected)),
2011                "verify_multi_inclusion({positions:?}) must report the first invalid position"
2012            );
2013        }
2014    }
2015
2016    #[test]
2017    fn test_multi_proof_stops_reading_at_first_invalid_position() {
2018        // Reading stops at the first invalid position, so an endless iterator is
2019        // rejected after its first repeat, descent, or out-of-range position.
2020        let mut read = 0;
2021        let result =
2022            siblings_required_for_multi_proof(3, core::iter::repeat(0).inspect(|_| read += 1));
2023        assert!(matches!(result, Err(Error::DuplicatePosition(0))));
2024        assert_eq!(read, 2);
2025
2026        let mut read = 0;
2027        let result = siblings_required_for_multi_proof(3, (0..3).cycle().inspect(|_| read += 1));
2028        assert!(matches!(result, Err(Error::UnsortedPosition(0))));
2029        assert_eq!(read, 4);
2030
2031        let mut read = 0;
2032        let result = siblings_required_for_multi_proof(
2033            u32::MAX,
2034            core::iter::repeat(u32::MAX).inspect(|_| read += 1),
2035        );
2036        assert!(matches!(result, Err(Error::InvalidPosition(u32::MAX))));
2037        assert_eq!(read, 1);
2038
2039        // The public API also stops at the first repeat.
2040        let mut builder = Builder::<Sha256>::new(3);
2041        for i in 0..3u32 {
2042            builder.add(&Sha256::hash(&[&i.to_be_bytes()]));
2043        }
2044        let tree = builder.build();
2045        assert!(matches!(
2046            tree.multi_proof(core::iter::repeat(2u32)),
2047            Err(Error::DuplicatePosition(2))
2048        ));
2049    }
2050
2051    #[test]
2052    fn test_multi_proof_unsorted_input() {
2053        // Create test data
2054        let digests: Vec<Digest> = (0..8u32)
2055            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2056            .collect();
2057
2058        // Build tree
2059        let mut builder = Builder::<Sha256>::new(digests.len());
2060        for digest in &digests {
2061            builder.add(digest);
2062        }
2063        let tree = builder.build();
2064        let root = tree.root();
2065
2066        // Positions must be ascending for generation, so the first descent is
2067        // rejected
2068        assert!(matches!(
2069            tree.multi_proof([5, 0, 3]),
2070            Err(Error::UnsortedPosition(0))
2071        ));
2072        let multi_proof = tree.multi_proof([0, 3, 5]).unwrap();
2073
2074        // Verification requires the same order
2075        let unsorted_elements = [(digests[5], 5), (digests[0], 0), (digests[3], 3)];
2076        assert!(matches!(
2077            multi_proof.verify_multi_inclusion::<Sha256>(&unsorted_elements, &root),
2078            Err(Error::UnsortedPosition(0))
2079        ));
2080        let sorted_elements = [(digests[0], 0), (digests[3], 3), (digests[5], 5)];
2081        assert!(
2082            multi_proof
2083                .verify_multi_inclusion::<Sha256>(&sorted_elements, &root)
2084                .is_ok()
2085        );
2086    }
2087
2088    #[test]
2089    fn test_multi_proof_various_sizes() {
2090        // Test multi-proofs for trees of various sizes
2091        for tree_size in [1, 2, 3, 4, 5, 7, 8, 15, 16, 31, 32] {
2092            let digests: Vec<Digest> = (0..tree_size as u32)
2093                .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2094                .collect();
2095
2096            // Build tree
2097            let mut builder = Builder::<Sha256>::new(digests.len());
2098            for digest in &digests {
2099                builder.add(digest);
2100            }
2101            let tree = builder.build();
2102            let root = tree.root();
2103
2104            // Test various position combinations
2105            // First and last
2106            if tree_size >= 2 {
2107                let positions = [0, (tree_size - 1) as u32];
2108                let multi_proof = tree.multi_proof(positions).unwrap();
2109                let elements: Vec<(Digest, u32)> = positions
2110                    .iter()
2111                    .map(|&p| (digests[p as usize], p))
2112                    .collect();
2113                assert!(
2114                    multi_proof
2115                        .verify_multi_inclusion::<Sha256>(&elements, &root)
2116                        .is_ok(),
2117                    "Failed for tree_size={tree_size}, positions=[0, {}]",
2118                    tree_size - 1
2119                );
2120            }
2121
2122            // Every other element
2123            if tree_size >= 4 {
2124                let positions: Vec<u32> = (0..tree_size as u32).step_by(2).collect();
2125                let multi_proof = tree.multi_proof(&positions).unwrap();
2126                let elements: Vec<(Digest, u32)> = positions
2127                    .iter()
2128                    .map(|&p| (digests[p as usize], p))
2129                    .collect();
2130                assert!(
2131                    multi_proof
2132                        .verify_multi_inclusion::<Sha256>(&elements, &root)
2133                        .is_ok(),
2134                    "Failed for tree_size={tree_size}, every other element"
2135                );
2136            }
2137        }
2138    }
2139
2140    #[test]
2141    fn test_multi_proof_wrong_elements() {
2142        // Create test data
2143        let digests: Vec<Digest> = (0..8u32)
2144            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2145            .collect();
2146
2147        // Build tree
2148        let mut builder = Builder::<Sha256>::new(digests.len());
2149        for digest in &digests {
2150            builder.add(digest);
2151        }
2152        let tree = builder.build();
2153        let root = tree.root();
2154
2155        // Generate valid proof
2156        let positions = [0, 3, 5];
2157        let multi_proof = tree.multi_proof(positions).unwrap();
2158
2159        // Verify with wrong elements
2160        let wrong_elements = [
2161            (Sha256::hash(&[b"wrong1"]), 0),
2162            (digests[3], 3),
2163            (digests[5], 5),
2164        ];
2165        assert!(
2166            multi_proof
2167                .verify_multi_inclusion::<Sha256>(&wrong_elements, &root)
2168                .is_err()
2169        );
2170    }
2171
2172    #[test]
2173    fn test_multi_proof_wrong_positions() {
2174        // Create test data
2175        let digests: Vec<Digest> = (0..8u32)
2176            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2177            .collect();
2178
2179        // Build tree
2180        let mut builder = Builder::<Sha256>::new(digests.len());
2181        for digest in &digests {
2182            builder.add(digest);
2183        }
2184        let tree = builder.build();
2185        let root = tree.root();
2186
2187        // Generate valid proof
2188        let positions = [0, 3, 5];
2189        let multi_proof = tree.multi_proof(positions).unwrap();
2190
2191        // Verify with wrong positions (same elements, different positions)
2192        let wrong_positions = [
2193            (digests[0], 1), // wrong position
2194            (digests[3], 3),
2195            (digests[5], 5),
2196        ];
2197        assert!(
2198            multi_proof
2199                .verify_multi_inclusion::<Sha256>(&wrong_positions, &root)
2200                .is_err()
2201        );
2202    }
2203
2204    #[test]
2205    fn test_multi_proof_wrong_root() {
2206        // Create test data
2207        let digests: Vec<Digest> = (0..8u32)
2208            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2209            .collect();
2210
2211        // Build tree
2212        let mut builder = Builder::<Sha256>::new(digests.len());
2213        for digest in &digests {
2214            builder.add(digest);
2215        }
2216        let tree = builder.build();
2217
2218        // Generate valid proof
2219        let positions = [0, 3, 5];
2220        let multi_proof = tree.multi_proof(positions).unwrap();
2221
2222        let elements: Vec<(Digest, u32)> = positions
2223            .iter()
2224            .map(|&p| (digests[p as usize], p))
2225            .collect();
2226
2227        // Verify with wrong root
2228        let wrong_root = Sha256::hash(&[b"wrong_root"]);
2229        assert!(
2230            multi_proof
2231                .verify_multi_inclusion::<Sha256>(&elements, &wrong_root)
2232                .is_err()
2233        );
2234    }
2235
2236    #[test]
2237    fn test_multi_proof_tampering() {
2238        // Create test data
2239        let digests: Vec<Digest> = (0..8u32)
2240            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2241            .collect();
2242
2243        // Build tree
2244        let mut builder = Builder::<Sha256>::new(digests.len());
2245        for digest in &digests {
2246            builder.add(digest);
2247        }
2248        let tree = builder.build();
2249        let root = tree.root();
2250
2251        // Generate valid proof
2252        let positions = [0, 5];
2253        let multi_proof = tree.multi_proof(positions).unwrap();
2254
2255        let elements: Vec<(Digest, u32)> = positions
2256            .iter()
2257            .map(|&p| (digests[p as usize], p))
2258            .collect();
2259
2260        // Tamper with sibling
2261        assert!(!multi_proof.siblings.is_empty());
2262        let mut modified = multi_proof.clone();
2263        modified.siblings[0] = Sha256::hash(&[b"tampered"]);
2264        assert!(
2265            modified
2266                .verify_multi_inclusion::<Sha256>(&elements, &root)
2267                .is_err()
2268        );
2269
2270        // Add extra sibling
2271        let mut extra = multi_proof.clone();
2272        extra.siblings.push(Sha256::hash(&[b"extra"]));
2273        assert!(
2274            extra
2275                .verify_multi_inclusion::<Sha256>(&elements, &root)
2276                .is_err()
2277        );
2278
2279        // Remove a sibling
2280        let mut missing = multi_proof;
2281        missing.siblings.pop();
2282        assert!(
2283            missing
2284                .verify_multi_inclusion::<Sha256>(&elements, &root)
2285                .is_err()
2286        );
2287    }
2288
2289    #[test]
2290    fn test_multi_proof_deduplication() {
2291        // Create test data
2292        let digests: Vec<Digest> = (0..16u32)
2293            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2294            .collect();
2295
2296        // Build tree
2297        let mut builder = Builder::<Sha256>::new(digests.len());
2298        for digest in &digests {
2299            builder.add(digest);
2300        }
2301        let tree = builder.build();
2302
2303        // Get individual proofs
2304        let individual_siblings: usize = [0u32, 1, 8, 9]
2305            .iter()
2306            .map(|&p| tree.proof(p).unwrap().siblings.len())
2307            .sum();
2308
2309        // Get multi-proof for same positions
2310        let multi_proof = tree.multi_proof([0, 1, 8, 9]).unwrap();
2311
2312        // Multi-proof should have fewer siblings due to deduplication
2313        assert!(
2314            multi_proof.siblings.len() < individual_siblings,
2315            "Multi-proof ({}) should have fewer siblings than sum of individual proofs ({})",
2316            multi_proof.siblings.len(),
2317            individual_siblings
2318        );
2319    }
2320
2321    #[test]
2322    fn test_multi_proof_serialization() {
2323        // Create test data
2324        let digests: Vec<Digest> = (0..8u32)
2325            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2326            .collect();
2327
2328        // Build tree
2329        let mut builder = Builder::<Sha256>::new(digests.len());
2330        for digest in &digests {
2331            builder.add(digest);
2332        }
2333        let tree = builder.build();
2334        let root = tree.root();
2335
2336        // Generate proof
2337        let positions = [0, 3, 5];
2338        let multi_proof = tree.multi_proof(positions).unwrap();
2339
2340        // Serialize and deserialize
2341        let serialized = multi_proof.encode();
2342        let deserialized = Proof::<Digest>::decode_cfg(serialized, &positions.len()).unwrap();
2343
2344        assert_eq!(multi_proof, deserialized);
2345
2346        // Verify deserialized proof works
2347        let elements: Vec<(Digest, u32)> = positions
2348            .iter()
2349            .map(|&p| (digests[p as usize], p))
2350            .collect();
2351        assert!(
2352            deserialized
2353                .verify_multi_inclusion::<Sha256>(&elements, &root)
2354                .is_ok()
2355        );
2356    }
2357
2358    #[test]
2359    fn test_multi_proof_serialization_truncated() {
2360        // Create test data
2361        let digests: Vec<Digest> = (0..8u32)
2362            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2363            .collect();
2364
2365        // Build tree
2366        let mut builder = Builder::<Sha256>::new(digests.len());
2367        for digest in &digests {
2368            builder.add(digest);
2369        }
2370        let tree = builder.build();
2371
2372        // Generate proof
2373        let positions = [0, 3, 5];
2374        let multi_proof = tree.multi_proof(positions).unwrap();
2375
2376        // Serialize and truncate
2377        let mut serialized = multi_proof.encode();
2378        serialized.truncate(serialized.len() - 1);
2379
2380        // Should fail to deserialize
2381        assert!(Proof::<Digest>::decode_cfg(&mut serialized, &positions.len()).is_err());
2382    }
2383
2384    #[test]
2385    fn test_multi_proof_serialization_extra() {
2386        // Create test data
2387        let digests: Vec<Digest> = (0..8u32)
2388            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2389            .collect();
2390
2391        // Build tree
2392        let mut builder = Builder::<Sha256>::new(digests.len());
2393        for digest in &digests {
2394            builder.add(digest);
2395        }
2396        let tree = builder.build();
2397
2398        // Generate proof
2399        let positions = [0, 3, 5];
2400        let multi_proof = tree.multi_proof(positions).unwrap();
2401
2402        // Serialize and add extra byte
2403        let mut serialized = multi_proof.encode_mut();
2404        serialized.extend_from_slice(&[0u8]);
2405
2406        // Should fail to deserialize
2407        assert!(Proof::<Digest>::decode_cfg(&mut serialized, &positions.len()).is_err());
2408    }
2409
2410    #[test]
2411    fn test_multi_proof_decode_insufficient_data() {
2412        let mut serialized = Vec::new();
2413        serialized.extend_from_slice(&8u32.encode()); // leaf_count
2414        serialized.extend_from_slice(&1usize.encode()); // claims 1 sibling but no data follows
2415
2416        // Should fail because the buffer claims 1 sibling but doesn't have the data
2417        let err = Proof::<Digest>::decode_cfg(serialized, &1).unwrap_err();
2418        assert!(matches!(err, commonware_codec::Error::EndOfBuffer));
2419    }
2420
2421    #[test]
2422    fn test_multi_proof_invalid_position() {
2423        // Create test data
2424        let digests: Vec<Digest> = (0..8u32)
2425            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2426            .collect();
2427
2428        // Build tree
2429        let mut builder = Builder::<Sha256>::new(digests.len());
2430        for digest in &digests {
2431            builder.add(digest);
2432        }
2433        let tree = builder.build();
2434
2435        // Test out of bounds position
2436        assert!(matches!(
2437            tree.multi_proof([0, 8]),
2438            Err(Error::InvalidPosition(8))
2439        ));
2440        assert!(matches!(
2441            tree.multi_proof([100]),
2442            Err(Error::InvalidPosition(100))
2443        ));
2444    }
2445
2446    #[test]
2447    fn test_multi_proof_verify_invalid_position() {
2448        // Create test data
2449        let digests: Vec<Digest> = (0..8u32)
2450            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2451            .collect();
2452
2453        // Build tree
2454        let mut builder = Builder::<Sha256>::new(digests.len());
2455        for digest in &digests {
2456            builder.add(digest);
2457        }
2458        let tree = builder.build();
2459        let root = tree.root();
2460
2461        // Generate valid proof
2462        let positions = [0, 3];
2463        let multi_proof = tree.multi_proof(positions).unwrap();
2464
2465        // Try to verify with out of bounds position
2466        let invalid_elements = [(digests[0], 0), (digests[3], 100)];
2467        assert!(
2468            multi_proof
2469                .verify_multi_inclusion::<Sha256>(&invalid_elements, &root)
2470                .is_err()
2471        );
2472    }
2473
2474    #[test]
2475    fn test_multi_proof_odd_tree_sizes() {
2476        // Test odd-sized trees that require node duplication
2477        for tree_size in [3, 5, 7, 9, 11, 13, 15] {
2478            let digests: Vec<Digest> = (0..tree_size as u32)
2479                .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2480                .collect();
2481
2482            // Build tree
2483            let mut builder = Builder::<Sha256>::new(digests.len());
2484            for digest in &digests {
2485                builder.add(digest);
2486            }
2487            let tree = builder.build();
2488            let root = tree.root();
2489
2490            // Test with positions including the last element
2491            let positions = [0, (tree_size - 1) as u32];
2492            let multi_proof = tree.multi_proof(positions).unwrap();
2493
2494            let elements: Vec<(Digest, u32)> = positions
2495                .iter()
2496                .map(|&p| (digests[p as usize], p))
2497                .collect();
2498            assert!(
2499                multi_proof
2500                    .verify_multi_inclusion::<Sha256>(&elements, &root)
2501                    .is_ok(),
2502                "Failed for tree_size={tree_size}"
2503            );
2504        }
2505    }
2506
2507    #[test]
2508    fn test_multi_proof_verify_empty_elements() {
2509        // Create a valid proof and try to verify with empty elements
2510        let digests: Vec<Digest> = (0..8u32)
2511            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2512            .collect();
2513
2514        let mut builder = Builder::<Sha256>::new(digests.len());
2515        for digest in &digests {
2516            builder.add(digest);
2517        }
2518        let tree = builder.build();
2519        let root = tree.root();
2520
2521        // Generate valid proof
2522        let positions = [0, 3];
2523        let multi_proof = tree.multi_proof(positions).unwrap();
2524
2525        // Try to verify with empty elements
2526        let empty_elements: &[(Digest, u32)] = &[];
2527        assert!(
2528            multi_proof
2529                .verify_multi_inclusion::<Sha256>(empty_elements, &root)
2530                .is_err()
2531        );
2532    }
2533
2534    #[test]
2535    fn test_multi_proof_default_verify() {
2536        // Default (empty) proof should only verify against empty tree
2537        let default_proof = Proof::<Digest>::default();
2538
2539        // Empty elements against default proof
2540        let empty_elements: &[(Digest, u32)] = &[];
2541
2542        // Build empty tree to get the empty root
2543        let builder = Builder::<Sha256>::new(0);
2544        let empty_tree = builder.build();
2545        let empty_root = empty_tree.root();
2546
2547        assert!(
2548            default_proof
2549                .verify_multi_inclusion::<Sha256>(empty_elements, &empty_root)
2550                .is_ok()
2551        );
2552
2553        // Should fail with wrong root
2554        let wrong_root = Sha256::hash(&[b"not_empty"]);
2555        assert!(
2556            default_proof
2557                .verify_multi_inclusion::<Sha256>(empty_elements, &wrong_root)
2558                .is_err()
2559        );
2560    }
2561
2562    #[test]
2563    fn test_multi_proof_single_leaf_tree() {
2564        // Edge case: tree with exactly one leaf
2565        let digest = Sha256::hash(&[b"only_leaf"]);
2566
2567        // Build single-leaf tree
2568        let mut builder = Builder::<Sha256>::new(1);
2569        builder.add(&digest);
2570        let tree = builder.build();
2571        let root = tree.root();
2572
2573        // Generate multi-proof for the only leaf
2574        let multi_proof = tree.multi_proof([0]).unwrap();
2575
2576        // Single leaf tree: leaf_count should be 1
2577        assert_eq!(multi_proof.leaf_count, 1);
2578
2579        // Single leaf tree: no siblings needed (leaf is the root after position hashing)
2580        assert!(
2581            multi_proof.siblings.is_empty(),
2582            "Single leaf tree should have no siblings"
2583        );
2584
2585        // Verify the proof
2586        let elements = [(digest, 0u32)];
2587        assert!(
2588            multi_proof
2589                .verify_multi_inclusion::<Sha256>(&elements, &root)
2590                .is_ok(),
2591            "Single leaf multi-proof verification failed"
2592        );
2593
2594        // Verify with wrong digest fails
2595        let wrong_digest = Sha256::hash(&[b"wrong"]);
2596        let wrong_elements = [(wrong_digest, 0u32)];
2597        assert!(
2598            multi_proof
2599                .verify_multi_inclusion::<Sha256>(&wrong_elements, &root)
2600                .is_err(),
2601            "Should fail with wrong digest"
2602        );
2603
2604        // Verify with wrong position fails
2605        let wrong_position_elements = [(digest, 1u32)];
2606        assert!(
2607            multi_proof
2608                .verify_multi_inclusion::<Sha256>(&wrong_position_elements, &root)
2609                .is_err(),
2610            "Should fail with invalid position"
2611        );
2612    }
2613
2614    #[test]
2615    fn test_multi_proof_malicious_leaf_count_zero() {
2616        // Attacker sets leaf_count = 0 but provides siblings
2617        let digests: Vec<Digest> = (0..8u32)
2618            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2619            .collect();
2620
2621        let mut builder = Builder::<Sha256>::new(digests.len());
2622        for digest in &digests {
2623            builder.add(digest);
2624        }
2625        let tree = builder.build();
2626        let root = tree.root();
2627
2628        // Generate valid proof and tamper with leaf_count
2629        let positions = [0, 3];
2630        let mut multi_proof = tree.multi_proof(positions).unwrap();
2631        multi_proof.leaf_count = 0;
2632
2633        let elements: Vec<(Digest, u32)> = positions
2634            .iter()
2635            .map(|&p| (digests[p as usize], p))
2636            .collect();
2637
2638        // Should fail because leaf_count=0 but there are elements
2639        assert!(
2640            multi_proof
2641                .verify_multi_inclusion::<Sha256>(&elements, &root)
2642                .is_err()
2643        );
2644    }
2645
2646    #[test]
2647    fn test_multi_proof_malicious_leaf_count_larger() {
2648        // Attacker inflates leaf_count to claim proof is for larger tree
2649        let digests: Vec<Digest> = (0..8u32)
2650            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2651            .collect();
2652
2653        let mut builder = Builder::<Sha256>::new(digests.len());
2654        for digest in &digests {
2655            builder.add(digest);
2656        }
2657        let tree = builder.build();
2658        let root = tree.root();
2659
2660        // Generate valid proof and inflate leaf_count
2661        let positions = [0, 3];
2662        let mut multi_proof = tree.multi_proof(positions).unwrap();
2663        let original_leaf_count = multi_proof.leaf_count;
2664        multi_proof.leaf_count = 1000;
2665
2666        let elements: Vec<(Digest, u32)> = positions
2667            .iter()
2668            .map(|&p| (digests[p as usize], p))
2669            .collect();
2670
2671        // Should fail because an inflated leaf_count changes the required siblings
2672        assert!(
2673            multi_proof
2674                .verify_multi_inclusion::<Sha256>(&elements, &root)
2675                .is_err(),
2676            "Should reject proof with inflated leaf_count ({} -> {})",
2677            original_leaf_count,
2678            multi_proof.leaf_count
2679        );
2680    }
2681
2682    #[test]
2683    fn test_multi_proof_malicious_leaf_count_smaller() {
2684        // Attacker deflates leaf_count to claim proof is for smaller tree
2685        let digests: Vec<Digest> = (0..8u32)
2686            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2687            .collect();
2688
2689        let mut builder = Builder::<Sha256>::new(digests.len());
2690        for digest in &digests {
2691            builder.add(digest);
2692        }
2693        let tree = builder.build();
2694        let root = tree.root();
2695
2696        // Generate valid proof and deflate leaf_count
2697        let positions = [0, 3];
2698        let mut multi_proof = tree.multi_proof(positions).unwrap();
2699        multi_proof.leaf_count = 4; // Smaller than actual tree
2700
2701        let elements: Vec<(Digest, u32)> = positions
2702            .iter()
2703            .map(|&p| (digests[p as usize], p))
2704            .collect();
2705
2706        // Should fail because a deflated leaf_count changes the tree structure
2707        assert!(
2708            multi_proof
2709                .verify_multi_inclusion::<Sha256>(&elements, &root)
2710                .is_err(),
2711            "Should reject proof with deflated leaf_count"
2712        );
2713    }
2714
2715    #[test]
2716    fn test_multi_proof_mismatched_element_count() {
2717        // Provide more or fewer elements than the proof was generated for
2718        let digests: Vec<Digest> = (0..8u32)
2719            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2720            .collect();
2721
2722        let mut builder = Builder::<Sha256>::new(digests.len());
2723        for digest in &digests {
2724            builder.add(digest);
2725        }
2726        let tree = builder.build();
2727        let root = tree.root();
2728
2729        // Generate proof for 2 positions
2730        let positions = [0, 3];
2731        let multi_proof = tree.multi_proof(positions).unwrap();
2732
2733        // Try to verify with only 1 element (too few)
2734        let too_few = [(digests[0], 0u32)];
2735        assert!(
2736            multi_proof
2737                .verify_multi_inclusion::<Sha256>(&too_few, &root)
2738                .is_err(),
2739            "Should reject when fewer elements provided than proof was generated for"
2740        );
2741
2742        // Try to verify with 3 elements (too many)
2743        let too_many = [(digests[0], 0u32), (digests[3], 3), (digests[5], 5)];
2744        assert!(
2745            multi_proof
2746                .verify_multi_inclusion::<Sha256>(&too_many, &root)
2747                .is_err(),
2748            "Should reject when more elements provided than proof was generated for"
2749        );
2750    }
2751
2752    #[test]
2753    fn test_multi_proof_swapped_siblings() {
2754        // Swap the order of siblings in the proof
2755        let digests: Vec<Digest> = (0..8u32)
2756            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2757            .collect();
2758
2759        let mut builder = Builder::<Sha256>::new(digests.len());
2760        for digest in &digests {
2761            builder.add(digest);
2762        }
2763        let tree = builder.build();
2764        let root = tree.root();
2765
2766        // Generate valid proof with multiple siblings
2767        let positions = [0, 5];
2768        let mut multi_proof = tree.multi_proof(positions).unwrap();
2769
2770        // Ensure we have at least 2 siblings to swap
2771        if multi_proof.siblings.len() >= 2 {
2772            // Swap first two siblings
2773            multi_proof.siblings.swap(0, 1);
2774
2775            let elements: Vec<(Digest, u32)> = positions
2776                .iter()
2777                .map(|&p| (digests[p as usize], p))
2778                .collect();
2779
2780            assert!(
2781                multi_proof
2782                    .verify_multi_inclusion::<Sha256>(&elements, &root)
2783                    .is_err(),
2784                "Should reject proof with swapped siblings"
2785            );
2786        }
2787    }
2788
2789    #[test]
2790    fn test_multi_proof_dos_large_leaf_count() {
2791        // Attacker sets massive leaf_count trying to cause DoS via memory allocation
2792        // The verify function should NOT allocate proportional to leaf_count
2793        let digests: Vec<Digest> = (0..4u32)
2794            .map(|i| Sha256::hash(&[&i.to_be_bytes()]))
2795            .collect();
2796
2797        let mut builder = Builder::<Sha256>::new(digests.len());
2798        for digest in &digests {
2799            builder.add(digest);
2800        }
2801        let tree = builder.build();
2802        let root = tree.root();
2803
2804        // Generate valid proof
2805        let positions = [0, 2];
2806        let mut multi_proof = tree.multi_proof(positions).unwrap();
2807
2808        // Set massive leaf_count (attacker trying to exhaust memory)
2809        multi_proof.leaf_count = u32::MAX;
2810
2811        let elements: Vec<(Digest, u32)> = positions
2812            .iter()
2813            .map(|&p| (digests[p as usize], p))
2814            .collect();
2815
2816        // This should fail quickly without allocating massive memory
2817        // The function is O(elements * levels), not O(leaf_count)
2818        let result = multi_proof.verify_multi_inclusion::<Sha256>(&elements, &root);
2819        assert!(result.is_err(), "Should reject malicious large leaf_count");
2820    }
2821
2822    #[cfg(feature = "arbitrary")]
2823    mod conformance {
2824        use super::*;
2825        use commonware_codec::conformance::CodecConformance;
2826        use commonware_conformance::Conformance;
2827        use commonware_cryptography::sha256::Digest as Sha256Digest;
2828
2829        fn test_merkle_tree(n: usize) -> Digest {
2830            // Build tree
2831            let mut digests = Vec::with_capacity(n);
2832            let mut builder = Builder::<Sha256>::new(n);
2833            for i in 0..n {
2834                let digest = Sha256::hash(&[&i.to_be_bytes()]);
2835                builder.add(&digest);
2836                digests.push(digest);
2837            }
2838            let tree = builder.build();
2839            let root = tree.root();
2840
2841            // For each leaf, generate and verify its proof
2842            for (i, leaf) in digests.iter().enumerate() {
2843                // Generate proof
2844                let proof = tree.proof(i as u32).unwrap();
2845                assert!(
2846                    proof
2847                        .verify_element_inclusion::<Sha256>(leaf, i as u32, &root)
2848                        .is_ok(),
2849                    "correct fail for size={n} leaf={i}"
2850                );
2851
2852                // Serialize and deserialize the proof
2853                let serialized = proof.encode();
2854                let deserialized = Proof::<Digest>::decode_cfg(serialized, &1).unwrap();
2855                assert!(
2856                    deserialized
2857                        .verify_element_inclusion::<Sha256>(leaf, i as u32, &root)
2858                        .is_ok(),
2859                    "deserialize fail for size={n} leaf={i}"
2860                );
2861
2862                // Modify a sibling hash and ensure the proof fails
2863                if !proof.siblings.is_empty() {
2864                    let mut update_tamper = proof.clone();
2865                    update_tamper.siblings[0] = Sha256::hash(&[b"tampered"]);
2866                    assert!(
2867                        update_tamper
2868                            .verify_element_inclusion::<Sha256>(leaf, i as u32, &root)
2869                            .is_err(),
2870                        "modify fail for size={n} leaf={i}"
2871                    );
2872                }
2873
2874                // Add a sibling hash and ensure the proof fails
2875                let mut add_tamper = proof.clone();
2876                add_tamper.siblings.push(Sha256::hash(&[b"tampered"]));
2877                assert!(
2878                    add_tamper
2879                        .verify_element_inclusion::<Sha256>(leaf, i as u32, &root)
2880                        .is_err(),
2881                    "add fail for size={n} leaf={i}"
2882                );
2883
2884                // Remove a sibling hash and ensure the proof fails
2885                if !proof.siblings.is_empty() {
2886                    let mut remove_tamper = proof.clone();
2887                    remove_tamper.siblings.pop();
2888                    assert!(
2889                        remove_tamper
2890                            .verify_element_inclusion::<Sha256>(leaf, i as u32, &root)
2891                            .is_err(),
2892                        "remove fail for size={n} leaf={i}"
2893                    );
2894                }
2895            }
2896
2897            // Test proof for larger than size
2898            assert!(tree.proof(n as u32).is_err());
2899
2900            // Return the root so we can ensure we don't silently change.
2901            root
2902        }
2903
2904        struct RootConformance;
2905
2906        impl Conformance for RootConformance {
2907            async fn commit(seed: u64) -> Vec<u8> {
2908                let root = test_merkle_tree(seed as usize);
2909                root.to_vec()
2910            }
2911        }
2912
2913        commonware_conformance::conformance_tests! {
2914            CodecConformance<Proof<Sha256Digest>>,
2915            RootConformance => 200
2916        }
2917    }
2918}