Skip to content

Commit 3cb9fde

Browse files
windericaPratyush
andauthored
FpVar::{is_eq, is_neq} only need two constraints (#133)
Co-authored-by: Pratyush Mishra <[email protected]>
1 parent ed2d55e commit 3cb9fde

File tree

3 files changed

+21
-14
lines changed

3 files changed

+21
-14
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
- [\#117](https://github.com/arkworks-rs/r1cs-std/pull/117) Fix result of `precomputed_base_scalar_mul_le` to not discard previous value.
2222
- [\#124](https://github.com/arkworks-rs/r1cs-std/pull/124) Fix `scalar_mul_le` constraints unsatisfiability when short Weierstrass point is zero.
2323
- [\#127](https://github.com/arkworks-rs/r1cs-std/pull/127) Convert `NonNativeFieldVar` constants to little-endian bytes instead of big-endian (`ToBytesGadget`).
24+
- [\#133](https://github.com/arkworks-rs/r1cs-std/pull/133) Save 1 constraint in `FpVar::{is_eq, is_neq}` by removing the unnecessary booleanity check.
2425

2526
### Breaking changes
2627

src/bits/boolean.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ impl<F: Field> AllocatedBool<F> {
4646
}
4747

4848
/// Allocate a witness variable without a booleanity check.
49-
fn new_witness_without_booleanity_check<T: Borrow<bool>>(
49+
pub(crate) fn new_witness_without_booleanity_check<T: Borrow<bool>>(
5050
cs: ConstraintSystemRef<F>,
5151
f: impl FnOnce() -> Result<T, SynthesisError>,
5252
) -> Result<Self, SynthesisError> {

src/fields/fp/mod.rs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use ark_relations::r1cs::{
66
use core::borrow::Borrow;
77

88
use crate::{
9+
boolean::AllocatedBool,
910
fields::{FieldOpsBounds, FieldVar},
1011
prelude::*,
1112
Assignment, ToConstraintFieldGadget, Vec,
@@ -320,20 +321,23 @@ impl<F: PrimeField> AllocatedFp<F> {
320321

321322
/// Outputs the bit `self == other`.
322323
///
323-
/// This requires three constraints.
324+
/// This requires two constraints.
324325
#[tracing::instrument(target = "r1cs")]
325326
pub fn is_eq(&self, other: &Self) -> Result<Boolean<F>, SynthesisError> {
326327
Ok(self.is_neq(other)?.not())
327328
}
328329

329330
/// Outputs the bit `self != other`.
330331
///
331-
/// This requires three constraints.
332+
/// This requires two constraints.
332333
#[tracing::instrument(target = "r1cs")]
333334
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+
)?);
337341
let multiplier = self.cs.new_witness_variable(|| {
338342
if is_not_equal.value()? {
339343
(self.value.get()? - other.value.get()?).inverse().get()
@@ -369,21 +373,23 @@ impl<F: PrimeField> AllocatedFp<F> {
369373
// --------------------------------------------------------------------
370374
//
371375
// Soundness:
372-
// Case 1: self != other, but is_not_equal = 0.
376+
// Case 1: self != other, but is_not_equal != 1.
373377
// --------------------------------------------
374-
// constraint 1:
375-
// (self - other) * multiplier = is_not_equal
376-
// => non_zero * multiplier = 0 (only satisfiable if multiplier == 0)
377-
//
378378
// constraint 2:
379379
// (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)
381382
//
382-
// Case 2: self == other, but is_not_equal = 1.
383+
// Case 2: self == other, but is_not_equal != 0.
383384
// --------------------------------------------
384385
// constraint 1:
385386
// (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.
387393
self.cs.enforce_constraint(
388394
lc!() + self.variable - other.variable,
389395
lc!() + multiplier,

0 commit comments

Comments
 (0)