@@ -6,6 +6,7 @@ use ark_relations::r1cs::{
6
6
use core:: borrow:: Borrow ;
7
7
8
8
use crate :: {
9
+ boolean:: AllocatedBool ,
9
10
fields:: { FieldOpsBounds , FieldVar } ,
10
11
prelude:: * ,
11
12
Assignment , ToConstraintFieldGadget , Vec ,
@@ -320,20 +321,23 @@ impl<F: PrimeField> AllocatedFp<F> {
320
321
321
322
/// Outputs the bit `self == other`.
322
323
///
323
- /// This requires three constraints.
324
+ /// This requires two constraints.
324
325
#[ tracing:: instrument( target = "r1cs" ) ]
325
326
pub fn is_eq ( & self , other : & Self ) -> Result < Boolean < F > , SynthesisError > {
326
327
Ok ( self . is_neq ( other) ?. not ( ) )
327
328
}
328
329
329
330
/// Outputs the bit `self != other`.
330
331
///
331
- /// This requires three constraints.
332
+ /// This requires two constraints.
332
333
#[ tracing:: instrument( target = "r1cs" ) ]
333
334
pub fn is_neq ( & self , other : & Self ) -> Result < Boolean < F > , SynthesisError > {
334
- let is_not_equal = Boolean :: new_witness ( self . cs . clone ( ) , || {
335
- Ok ( self . value . get ( ) ? != other. value . get ( ) ?)
336
- } ) ?;
335
+ // We don't need to enforce `is_not_equal` to be boolean here;
336
+ // see the comments above the constraints below for why.
337
+ let is_not_equal = Boolean :: from ( AllocatedBool :: new_witness_without_booleanity_check (
338
+ self . cs . clone ( ) ,
339
+ || Ok ( self . value . get ( ) ? != other. value . get ( ) ?) ,
340
+ ) ?) ;
337
341
let multiplier = self . cs . new_witness_variable ( || {
338
342
if is_not_equal. value ( ) ? {
339
343
( self . value . get ( ) ? - other. value . get ( ) ?) . inverse ( ) . get ( )
@@ -369,21 +373,23 @@ impl<F: PrimeField> AllocatedFp<F> {
369
373
// --------------------------------------------------------------------
370
374
//
371
375
// Soundness:
372
- // Case 1: self != other, but is_not_equal = 0 .
376
+ // Case 1: self != other, but is_not_equal != 1 .
373
377
// --------------------------------------------
374
- // constraint 1:
375
- // (self - other) * multiplier = is_not_equal
376
- // => non_zero * multiplier = 0 (only satisfiable if multiplier == 0)
377
- //
378
378
// constraint 2:
379
379
// (self - other) * not(is_not_equal) = 0
380
- // => (non_zero) * 1 = 0 (impossible)
380
+ // => (non_zero) * (1 - is_not_equal) = 0
381
+ // => non_zero = 0 (contradiction) || 1 - is_not_equal = 0 (contradiction)
381
382
//
382
- // Case 2: self == other, but is_not_equal = 1 .
383
+ // Case 2: self == other, but is_not_equal != 0 .
383
384
// --------------------------------------------
384
385
// constraint 1:
385
386
// (self - other) * multiplier = is_not_equal
386
- // 0 * multiplier = 1 (unsatisfiable)
387
+ // 0 * multiplier = is_not_equal != 0 (unsatisfiable)
388
+ //
389
+ // That is, constraint 1 enforces that if self == other, then `is_not_equal = 0`
390
+ // and constraint 2 enforces that if self != other, then `is_not_equal = 1`.
391
+ // Since these are the only possible two cases, `is_not_equal` is always
392
+ // constrained to 0 or 1.
387
393
self . cs . enforce_constraint (
388
394
lc ! ( ) + self . variable - other. variable ,
389
395
lc ! ( ) + multiplier,
0 commit comments