@@ -11,6 +11,7 @@ use core::{
11
11
iter:: { once, repeat} ,
12
12
marker:: PhantomData ,
13
13
ops:: { Add , Mul , Shr } ,
14
+ slice,
14
15
slice:: ChunksExact ,
15
16
} ;
16
17
@@ -751,6 +752,122 @@ where
751
752
Ok ( masks)
752
753
}
753
754
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
+
754
871
// Verify a batch of single and/or aggregated range proofs as a public entity, or recover the masks for single
755
872
// range proofs by a party that can supply the optional seed nonces
756
873
fn verify (
0 commit comments