@@ -219,6 +219,31 @@ impl MerkleTree {
219219
220220 Ok ( current. ct_eq ( auth_root) . into ( ) )
221221 }
222+
223+ /// Generate proofs for multiple leaf indices in one call.
224+ ///
225+ /// Returns proofs in the same order as `indices`.
226+ /// Fails fast if any index is out of bounds.
227+ pub fn proofs ( & self , indices : & [ usize ] ) -> HcaResult < Vec < MerkleProof > > {
228+ indices. iter ( ) . map ( |& i| self . proof ( i) ) . collect ( )
229+ }
230+
231+ /// Verify multiple proofs against the same auth_root.
232+ ///
233+ /// Returns `Ok(true)` only if every proof is valid.
234+ /// Returns `Ok(false)` on the first invalid proof.
235+ /// Returns `Err` if any proof exceeds `MAX_TREE_DEPTH`.
236+ pub fn verify_batch (
237+ items : & [ ( & [ u8 ; 32 ] , & MerkleProof ) ] ,
238+ auth_root : & [ u8 ; 32 ] ,
239+ ) -> HcaResult < bool > {
240+ for ( leaf_hash, proof) in items {
241+ if !Self :: verify ( leaf_hash, proof, auth_root) ? {
242+ return Ok ( false ) ;
243+ }
244+ }
245+ Ok ( true )
246+ }
222247}
223248
224249/// Compute branch node hash
@@ -413,4 +438,85 @@ mod tests {
413438 assert ! ( result. is_err( ) ) ;
414439 assert ! ( matches!( result. unwrap_err( ) , HcaError :: TreeTooDeep { .. } ) ) ;
415440 }
441+
442+ fn make_tree ( n : usize ) -> ( MerkleTree , Vec < Leaf > ) {
443+ let leaves: Vec < Leaf > = ( 0 ..n)
444+ . map ( |i| Leaf :: new ( 0x01 , vec ! [ 0x60 , i as u8 ] , & format ! ( "leaf {}" , i) ) . unwrap ( ) )
445+ . collect ( ) ;
446+ let tree = MerkleTree :: new ( leaves. clone ( ) ) . unwrap ( ) ;
447+ ( tree, leaves)
448+ }
449+
450+ #[ test]
451+ fn test_batch_proofs_all_leaves ( ) {
452+ let ( tree, leaves) = make_tree ( 4 ) ;
453+ let indices: Vec < usize > = ( 0 ..leaves. len ( ) ) . collect ( ) ;
454+ let proofs = tree. proofs ( & indices) . unwrap ( ) ;
455+ assert_eq ! ( proofs. len( ) , 4 ) ;
456+ let root = tree. auth_root ( ) ;
457+ for ( i, proof) in proofs. iter ( ) . enumerate ( ) {
458+ assert ! ( MerkleTree :: verify( & leaves[ i] . hash( ) , proof, & root) . unwrap( ) ) ;
459+ }
460+ }
461+
462+ #[ test]
463+ fn test_batch_proofs_partial ( ) {
464+ let ( tree, leaves) = make_tree ( 4 ) ;
465+ let proofs = tree. proofs ( & [ 0 , 2 ] ) . unwrap ( ) ;
466+ let root = tree. auth_root ( ) ;
467+ assert ! ( MerkleTree :: verify( & leaves[ 0 ] . hash( ) , & proofs[ 0 ] , & root) . unwrap( ) ) ;
468+ assert ! ( MerkleTree :: verify( & leaves[ 2 ] . hash( ) , & proofs[ 1 ] , & root) . unwrap( ) ) ;
469+ }
470+
471+ #[ test]
472+ fn test_batch_proofs_out_of_bounds_fails ( ) {
473+ let ( tree, _) = make_tree ( 4 ) ;
474+ assert ! ( tree. proofs( & [ 0 , 99 ] ) . is_err( ) ) ;
475+ }
476+
477+ #[ test]
478+ fn test_verify_batch_all_valid ( ) {
479+ let ( tree, leaves) = make_tree ( 4 ) ;
480+ let indices: Vec < usize > = ( 0 ..leaves. len ( ) ) . collect ( ) ;
481+ let proofs = tree. proofs ( & indices) . unwrap ( ) ;
482+ let root = tree. auth_root ( ) ;
483+ let items: Vec < ( & [ u8 ; 32 ] , & MerkleProof ) > = leaves
484+ . iter ( )
485+ . map ( |l| l. hash ( ) )
486+ . zip ( proofs. iter ( ) )
487+ . map ( |( h, p) | ( Box :: leak ( Box :: new ( h) ) as & [ u8 ; 32 ] , p) )
488+ . collect ( ) ;
489+ assert ! ( MerkleTree :: verify_batch( & items, & root) . unwrap( ) ) ;
490+ }
491+
492+ #[ test]
493+ fn test_verify_batch_empty ( ) {
494+ let ( tree, _) = make_tree ( 2 ) ;
495+ let root = tree. auth_root ( ) ;
496+ assert ! ( MerkleTree :: verify_batch( & [ ] , & root) . unwrap( ) ) ;
497+ }
498+
499+ #[ test]
500+ fn test_verify_batch_one_invalid_fails ( ) {
501+ let ( tree, leaves) = make_tree ( 4 ) ;
502+ let proofs = tree. proofs ( & [ 0 , 1 ] ) . unwrap ( ) ;
503+ let root = tree. auth_root ( ) ;
504+ // Swap leaf hashes — leaf 1's hash with proof for index 0 should fail
505+ let h0 = leaves[ 0 ] . hash ( ) ;
506+ let h1 = leaves[ 1 ] . hash ( ) ;
507+ let items: Vec < ( & [ u8 ; 32 ] , & MerkleProof ) > =
508+ vec ! [ ( & h0, & proofs[ 0 ] ) , ( & h1, & proofs[ 0 ] ) ] ;
509+ assert ! ( !MerkleTree :: verify_batch( & items, & root) . unwrap( ) ) ;
510+ }
511+
512+ #[ test]
513+ fn test_verify_batch_order_preserved ( ) {
514+ // Verify that proofs() returns proofs in the same order as the requested indices
515+ let ( tree, leaves) = make_tree ( 4 ) ;
516+ let proofs = tree. proofs ( & [ 3 , 1 , 0 ] ) . unwrap ( ) ;
517+ let root = tree. auth_root ( ) ;
518+ assert ! ( MerkleTree :: verify( & leaves[ 3 ] . hash( ) , & proofs[ 0 ] , & root) . unwrap( ) ) ;
519+ assert ! ( MerkleTree :: verify( & leaves[ 1 ] . hash( ) , & proofs[ 1 ] , & root) . unwrap( ) ) ;
520+ assert ! ( MerkleTree :: verify( & leaves[ 0 ] . hash( ) , & proofs[ 2 ] , & root) . unwrap( ) ) ;
521+ }
416522}
0 commit comments