Skip to content

Commit 4e1e8d0

Browse files
alex-ozdemirmmagicianPratyush
authored
Publicize non-zero weierstrass affine variables (#84)
Co-authored-by: Marcin <[email protected]> Co-authored-by: Pratyush Mishra <[email protected]>
1 parent 51133ad commit 4e1e8d0

File tree

3 files changed

+114
-7
lines changed

3 files changed

+114
-7
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
### Features
1010

11+
- [\#84](https://github.com/arkworks-rs/r1cs-std/pull/84) Expose `short_weierstrass::non_zero_affine` module
12+
and implement `EqGadget` for `NonZeroAffineVar`.
1113
- [\#79](https://github.com/arkworks-rs/r1cs-std/pull/79) Move `NonNativeFieldVar` from `ark-nonnative` to `ark-r1cs-std`.
1214
- [\#76](https://github.com/arkworks-rs/r1cs-std/pull/76) Implement `ToBytesGadget` for `Vec<UInt8>`.
1315
- [nonnative/\#45](https://github.com/arkworks-rs/nonnative/pull/45) Add `new_witness_with_le_bits` which returns the bits used during variable allocation.

src/groups/curves/short_weierstrass/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@ pub mod mnt4;
2222
/// family of bilinear groups.
2323
pub mod mnt6;
2424

25-
mod non_zero_affine;
25+
/// This module provides a generic implementation of elliptic curve operations for points on
26+
/// short-weierstrass curves in affine coordinates that **are not** equal to zero.
27+
///
28+
/// Note: this module is **unsafe** in general: it can synthesize unsatisfiable or
29+
/// underconstrained constraint systems when a represented point _is_ equal to zero.
30+
/// The [ProjectiveVar] gadget is the recommended way of working with elliptic curve points.
31+
pub mod non_zero_affine;
2632
/// An implementation of arithmetic for Short Weierstrass curves that relies on
2733
/// the complete formulae derived in the paper of
2834
/// [[Renes, Costello, Batina 2015]](<https://eprint.iacr.org/2015/1060>).

src/groups/curves/short_weierstrass/non_zero_affine.rs

Lines changed: 105 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ where
2525
F: FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>,
2626
for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
2727
{
28-
pub(crate) fn new(x: F, y: F) -> Self {
28+
pub fn new(x: F, y: F) -> Self {
2929
Self {
3030
x,
3131
y,
@@ -35,13 +35,13 @@ where
3535

3636
/// Converts self into a non-zero projective point.
3737
#[tracing::instrument(target = "r1cs", skip(self))]
38-
pub(crate) fn into_projective(&self) -> ProjectiveVar<P, F> {
38+
pub fn into_projective(&self) -> ProjectiveVar<P, F> {
3939
ProjectiveVar::new(self.x.clone(), self.y.clone(), F::one())
4040
}
4141

4242
/// Performs an addition without checking that other != ±self.
4343
#[tracing::instrument(target = "r1cs", skip(self, other))]
44-
pub(crate) fn add_unchecked(&self, other: &Self) -> Result<Self, SynthesisError> {
44+
pub fn add_unchecked(&self, other: &Self) -> Result<Self, SynthesisError> {
4545
if [self, other].is_constant() {
4646
let result =
4747
(self.value()?.into_projective() + other.value()?.into_projective()).into_affine();
@@ -67,7 +67,7 @@ where
6767
/// Doubles `self`. As this is a prime order curve point,
6868
/// the output is guaranteed to not be the point at infinity.
6969
#[tracing::instrument(target = "r1cs", skip(self))]
70-
pub(crate) fn double(&self) -> Result<Self, SynthesisError> {
70+
pub fn double(&self) -> Result<Self, SynthesisError> {
7171
if [self].is_constant() {
7272
let result = self.value()?.into_projective().double().into_affine();
7373
// Panic if the result is zero.
@@ -96,7 +96,7 @@ where
9696
///
9797
/// This follows the formulae from [\[ELM03\]](https://arxiv.org/abs/math/0208038).
9898
#[tracing::instrument(target = "r1cs", skip(self))]
99-
pub(crate) fn double_and_add_unchecked(&self, other: &Self) -> Result<Self, SynthesisError> {
99+
pub fn double_and_add_unchecked(&self, other: &Self) -> Result<Self, SynthesisError> {
100100
if [self].is_constant() || other.is_constant() {
101101
self.double()?.add_unchecked(other)
102102
} else {
@@ -126,7 +126,7 @@ where
126126

127127
/// Doubles `self` in place.
128128
#[tracing::instrument(target = "r1cs", skip(self))]
129-
pub(crate) fn double_in_place(&mut self) -> Result<(), SynthesisError> {
129+
pub fn double_in_place(&mut self) -> Result<(), SynthesisError> {
130130
*self = self.double()?;
131131
Ok(())
132132
}
@@ -169,9 +169,62 @@ where
169169
}
170170
}
171171

172+
impl<P, F> EqGadget<<P::BaseField as Field>::BasePrimeField> for NonZeroAffineVar<P, F>
173+
where
174+
P: SWModelParameters,
175+
F: FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>,
176+
for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
177+
{
178+
#[tracing::instrument(target = "r1cs")]
179+
fn is_eq(
180+
&self,
181+
other: &Self,
182+
) -> Result<Boolean<<P::BaseField as Field>::BasePrimeField>, SynthesisError> {
183+
let x_equal = self.x.is_eq(&other.x)?;
184+
let y_equal = self.y.is_eq(&other.y)?;
185+
x_equal.and(&y_equal)
186+
}
187+
188+
#[inline]
189+
#[tracing::instrument(target = "r1cs")]
190+
fn conditional_enforce_equal(
191+
&self,
192+
other: &Self,
193+
condition: &Boolean<<P::BaseField as Field>::BasePrimeField>,
194+
) -> Result<(), SynthesisError> {
195+
let x_equal = self.x.is_eq(&other.x)?;
196+
let y_equal = self.y.is_eq(&other.y)?;
197+
let coordinates_equal = x_equal.and(&y_equal)?;
198+
coordinates_equal.conditional_enforce_equal(&Boolean::Constant(true), condition)?;
199+
Ok(())
200+
}
201+
202+
#[inline]
203+
#[tracing::instrument(target = "r1cs")]
204+
fn enforce_equal(&self, other: &Self) -> Result<(), SynthesisError> {
205+
self.x.enforce_equal(&other.x)?;
206+
self.y.enforce_equal(&other.y)?;
207+
Ok(())
208+
}
209+
210+
#[inline]
211+
#[tracing::instrument(target = "r1cs")]
212+
fn conditional_enforce_not_equal(
213+
&self,
214+
other: &Self,
215+
condition: &Boolean<<P::BaseField as Field>::BasePrimeField>,
216+
) -> Result<(), SynthesisError> {
217+
let is_equal = self.is_eq(other)?;
218+
is_equal
219+
.and(condition)?
220+
.enforce_equal(&Boolean::Constant(false))
221+
}
222+
}
223+
172224
#[cfg(test)]
173225
mod test_non_zero_affine {
174226
use crate::alloc::AllocVar;
227+
use crate::eq::EqGadget;
175228
use crate::fields::fp::{AllocatedFp, FpVar};
176229
use crate::groups::curves::short_weierstrass::non_zero_affine::NonZeroAffineVar;
177230
use crate::groups::curves::short_weierstrass::ProjectiveVar;
@@ -300,4 +353,50 @@ mod test_non_zero_affine {
300353
assert_eq!(sum_a.0, sum_b.0);
301354
assert_eq!(sum_a.1, sum_b.1);
302355
}
356+
357+
#[test]
358+
fn correctness_test_eq() {
359+
let cs = ConstraintSystem::<Fq>::new_ref();
360+
361+
let x = FpVar::Var(
362+
AllocatedFp::<Fq>::new_witness(cs.clone(), || {
363+
Ok(G1Parameters::AFFINE_GENERATOR_COEFFS.0)
364+
})
365+
.unwrap(),
366+
);
367+
let y = FpVar::Var(
368+
AllocatedFp::<Fq>::new_witness(cs.clone(), || {
369+
Ok(G1Parameters::AFFINE_GENERATOR_COEFFS.1)
370+
})
371+
.unwrap(),
372+
);
373+
374+
let a = NonZeroAffineVar::<G1Parameters, FpVar<Fq>>::new(x, y);
375+
376+
let n = 10;
377+
378+
let a_multiples: Vec<NonZeroAffineVar<G1Parameters, FpVar<Fq>>> =
379+
std::iter::successors(Some(a.clone()), |acc| Some(acc.add_unchecked(&a).unwrap()))
380+
.take(n)
381+
.collect();
382+
383+
let all_equal: Vec<NonZeroAffineVar<G1Parameters, FpVar<Fq>>> = (0..n / 2)
384+
.map(|i| {
385+
a_multiples[i]
386+
.add_unchecked(&a_multiples[n - i - 1])
387+
.unwrap()
388+
})
389+
.collect();
390+
391+
for i in 0..n - 1 {
392+
a_multiples[i]
393+
.enforce_not_equal(&a_multiples[i + 1])
394+
.unwrap();
395+
}
396+
for i in 0..all_equal.len() - 1 {
397+
all_equal[i].enforce_equal(&all_equal[i + 1]).unwrap();
398+
}
399+
400+
assert!(cs.is_satisfied().unwrap());
401+
}
303402
}

0 commit comments

Comments
 (0)