@@ -11,6 +11,7 @@ use core::{
1111 iter:: { once, repeat} ,
1212 marker:: PhantomData ,
1313 ops:: { Add , Mul , Shr } ,
14+ slice,
1415 slice:: ChunksExact ,
1516} ;
1617
@@ -751,6 +752,122 @@ where
751752 Ok ( masks)
752753 }
753754
755+ /// Verify a batch of proofs.
756+ /// If verification fails, this performs a binary search to identify the first failing proof in the batch and
757+ /// returns an error containing its index. If something goes wrong internally, returns `Err(None)`.
758+ ///
759+ /// If initial verification of the entire batch fails, this performs a number of subsequent batch verifications that
760+ /// is logarithmic in the number of proofs. Note that this cannot tell if a batch contains multiple failing
761+ /// proofs, or just one!
762+ pub fn verify_batch_with_first_blame (
763+ transcripts : & mut [ Transcript ] ,
764+ statements : & [ RangeStatement < P > ] ,
765+ proofs : & [ RangeProof < P > ] ,
766+ action : VerifyAction ,
767+ ) -> Result < Vec < Option < ExtendedMask > > , Option < usize > > {
768+ // Try to verify the entire batch
769+ if let Ok ( masks) = Self :: verify_batch ( & mut transcripts. to_vec ( ) , statements, proofs, action) {
770+ return Ok ( masks) ;
771+ }
772+
773+ // The batch failed, so perform a binary search to identify the first failing proof
774+ let mut left = 0 ;
775+ let mut right = proofs. len ( ) ;
776+
777+ while left < right {
778+ #[ allow( clippy:: arithmetic_side_effects) ]
779+ let average = left
780+ . checked_add (
781+ // This cannot underflow since `left < right`
782+ ( right - left) / 2 ,
783+ )
784+ . ok_or ( None ) ?;
785+
786+ #[ allow( clippy:: arithmetic_side_effects) ]
787+ // This cannot underflow since `left < right`
788+ let mid = if ( right - left) % 2 == 0 {
789+ average
790+ } else {
791+ average. checked_add ( 1 ) . ok_or ( None ) ?
792+ } ;
793+
794+ // Which side is the failure on?
795+ let failure_on_left = Self :: verify_batch (
796+ & mut transcripts. to_vec ( ) [ left..mid] ,
797+ & statements[ left..mid] ,
798+ & proofs[ left..mid] ,
799+ action,
800+ )
801+ . is_err ( ) ;
802+
803+ if failure_on_left {
804+ let left_check = mid. checked_sub ( 1 ) . ok_or ( None ) ?;
805+
806+ // Are we done?
807+ if left == left_check {
808+ return Err ( Some ( left) ) ;
809+ }
810+
811+ // Discard the right side and continue
812+ right = mid;
813+ } else {
814+ let right_check = mid. checked_add ( 1 ) . ok_or ( None ) ?;
815+
816+ // Are we done?
817+ if right == right_check {
818+ return Err ( Some ( right) ) ;
819+ }
820+
821+ // Discard the left side and continue
822+ left = mid;
823+ }
824+ }
825+
826+ // We should never get here! If we do, something has gone wrong unexpectedly
827+ Err ( None )
828+ }
829+
830+ /// Verify a batch of proofs.
831+ /// If verification fails, this verifies each proof in the batch separately and returns an error containing the
832+ /// indexes of all failing proofs. If something goes wrong internally, returns `Err(None)`.
833+ ///
834+ /// If initial verification of the entire batch fails, this performs a subsequent number of verifications that is
835+ /// linear in the number of proofs.
836+ pub fn verify_batch_with_full_blame (
837+ transcripts : & mut [ Transcript ] ,
838+ statements : & [ RangeStatement < P > ] ,
839+ proofs : & [ RangeProof < P > ] ,
840+ action : VerifyAction ,
841+ ) -> Result < Vec < Option < ExtendedMask > > , Option < Vec < usize > > > {
842+ // Try to verify the entire batch
843+ if let Ok ( masks) = Self :: verify_batch ( & mut transcripts. to_vec ( ) , statements, proofs, action) {
844+ return Ok ( masks) ;
845+ }
846+
847+ let mut failures = Vec :: with_capacity ( proofs. len ( ) ) ;
848+
849+ // If the batch fails, verify all proofs and identify failures
850+ for ( index, ( transcript, proof, statement) ) in izip ! ( transcripts. iter_mut( ) , proofs, statements) . enumerate ( ) {
851+ if Self :: verify_batch (
852+ slice:: from_mut ( transcript) ,
853+ slice:: from_ref ( statement) ,
854+ slice:: from_ref ( proof) ,
855+ action,
856+ )
857+ . is_err ( )
858+ {
859+ failures. push ( index) ;
860+ }
861+ }
862+
863+ // Ensure that we have found at least one failed proof; otherwise, something has gone wrong unexpectedly
864+ if failures. is_empty ( ) {
865+ return Err ( None ) ;
866+ }
867+
868+ Err ( Some ( failures) )
869+ }
870+
754871 // Verify a batch of single and/or aggregated range proofs as a public entity, or recover the masks for single
755872 // range proofs by a party that can supply the optional seed nonces
756873 fn verify (
0 commit comments