Browse Source

Publicize non-zero weierstrass affine variables (#84)

Co-authored-by: Marcin <marcin.gorny.94@protonmail.com>
Co-authored-by: Pratyush Mishra <pratyushmishra@berkeley.edu>
master
Alex Ozdemir 2 years ago
committed by GitHub
parent
commit
4e1e8d048d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 114 additions and 7 deletions
  1. +2
    -0
      CHANGELOG.md
  2. +7
    -1
      src/groups/curves/short_weierstrass/mod.rs
  3. +105
    -6
      src/groups/curves/short_weierstrass/non_zero_affine.rs

+ 2
- 0
CHANGELOG.md

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

+ 7
- 1
src/groups/curves/short_weierstrass/mod.rs

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

+ 105
- 6
src/groups/curves/short_weierstrass/non_zero_affine.rs

@ -25,7 +25,7 @@ where
F: FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>, F: FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>,
for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
{ {
pub(crate) fn new(x: F, y: F) -> Self {
pub fn new(x: F, y: F) -> Self {
Self { Self {
x, x,
y, y,
@ -35,13 +35,13 @@ where
/// Converts self into a non-zero projective point. /// Converts self into a non-zero projective point.
#[tracing::instrument(target = "r1cs", skip(self))] #[tracing::instrument(target = "r1cs", skip(self))]
pub(crate) fn into_projective(&self) -> ProjectiveVar<P, F> {
pub fn into_projective(&self) -> ProjectiveVar<P, F> {
ProjectiveVar::new(self.x.clone(), self.y.clone(), F::one()) ProjectiveVar::new(self.x.clone(), self.y.clone(), F::one())
} }
/// Performs an addition without checking that other != ±self. /// Performs an addition without checking that other != ±self.
#[tracing::instrument(target = "r1cs", skip(self, other))] #[tracing::instrument(target = "r1cs", skip(self, other))]
pub(crate) fn add_unchecked(&self, other: &Self) -> Result<Self, SynthesisError> {
pub fn add_unchecked(&self, other: &Self) -> Result<Self, SynthesisError> {
if [self, other].is_constant() { if [self, other].is_constant() {
let result = let result =
(self.value()?.into_projective() + other.value()?.into_projective()).into_affine(); (self.value()?.into_projective() + other.value()?.into_projective()).into_affine();
@ -67,7 +67,7 @@ where
/// Doubles `self`. As this is a prime order curve point, /// Doubles `self`. As this is a prime order curve point,
/// the output is guaranteed to not be the point at infinity. /// the output is guaranteed to not be the point at infinity.
#[tracing::instrument(target = "r1cs", skip(self))] #[tracing::instrument(target = "r1cs", skip(self))]
pub(crate) fn double(&self) -> Result<Self, SynthesisError> {
pub fn double(&self) -> Result<Self, SynthesisError> {
if [self].is_constant() { if [self].is_constant() {
let result = self.value()?.into_projective().double().into_affine(); let result = self.value()?.into_projective().double().into_affine();
// Panic if the result is zero. // Panic if the result is zero.
@ -96,7 +96,7 @@ where
/// ///
/// This follows the formulae from [\[ELM03\]](https://arxiv.org/abs/math/0208038). /// This follows the formulae from [\[ELM03\]](https://arxiv.org/abs/math/0208038).
#[tracing::instrument(target = "r1cs", skip(self))] #[tracing::instrument(target = "r1cs", skip(self))]
pub(crate) fn double_and_add_unchecked(&self, other: &Self) -> Result<Self, SynthesisError> {
pub fn double_and_add_unchecked(&self, other: &Self) -> Result<Self, SynthesisError> {
if [self].is_constant() || other.is_constant() { if [self].is_constant() || other.is_constant() {
self.double()?.add_unchecked(other) self.double()?.add_unchecked(other)
} else { } else {
@ -126,7 +126,7 @@ where
/// Doubles `self` in place. /// Doubles `self` in place.
#[tracing::instrument(target = "r1cs", skip(self))] #[tracing::instrument(target = "r1cs", skip(self))]
pub(crate) fn double_in_place(&mut self) -> Result<(), SynthesisError> {
pub fn double_in_place(&mut self) -> Result<(), SynthesisError> {
*self = self.double()?; *self = self.double()?;
Ok(()) Ok(())
} }
@ -169,9 +169,62 @@ where
} }
} }
impl<P, F> EqGadget<<P::BaseField as Field>::BasePrimeField> for NonZeroAffineVar<P, F>
where
P: SWModelParameters,
F: FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>,
for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
{
#[tracing::instrument(target = "r1cs")]
fn is_eq(
&self,
other: &Self,
) -> Result<Boolean<<P::BaseField as Field>::BasePrimeField>, SynthesisError> {
let x_equal = self.x.is_eq(&other.x)?;
let y_equal = self.y.is_eq(&other.y)?;
x_equal.and(&y_equal)
}
#[inline]
#[tracing::instrument(target = "r1cs")]
fn conditional_enforce_equal(
&self,
other: &Self,
condition: &Boolean<<P::BaseField as Field>::BasePrimeField>,
) -> Result<(), SynthesisError> {
let x_equal = self.x.is_eq(&other.x)?;
let y_equal = self.y.is_eq(&other.y)?;
let coordinates_equal = x_equal.and(&y_equal)?;
coordinates_equal.conditional_enforce_equal(&Boolean::Constant(true), condition)?;
Ok(())
}
#[inline]
#[tracing::instrument(target = "r1cs")]
fn enforce_equal(&self, other: &Self) -> Result<(), SynthesisError> {
self.x.enforce_equal(&other.x)?;
self.y.enforce_equal(&other.y)?;
Ok(())
}
#[inline]
#[tracing::instrument(target = "r1cs")]
fn conditional_enforce_not_equal(
&self,
other: &Self,
condition: &Boolean<<P::BaseField as Field>::BasePrimeField>,
) -> Result<(), SynthesisError> {
let is_equal = self.is_eq(other)?;
is_equal
.and(condition)?
.enforce_equal(&Boolean::Constant(false))
}
}
#[cfg(test)] #[cfg(test)]
mod test_non_zero_affine { mod test_non_zero_affine {
use crate::alloc::AllocVar; use crate::alloc::AllocVar;
use crate::eq::EqGadget;
use crate::fields::fp::{AllocatedFp, FpVar}; use crate::fields::fp::{AllocatedFp, FpVar};
use crate::groups::curves::short_weierstrass::non_zero_affine::NonZeroAffineVar; use crate::groups::curves::short_weierstrass::non_zero_affine::NonZeroAffineVar;
use crate::groups::curves::short_weierstrass::ProjectiveVar; use crate::groups::curves::short_weierstrass::ProjectiveVar;
@ -300,4 +353,50 @@ mod test_non_zero_affine {
assert_eq!(sum_a.0, sum_b.0); assert_eq!(sum_a.0, sum_b.0);
assert_eq!(sum_a.1, sum_b.1); assert_eq!(sum_a.1, sum_b.1);
} }
#[test]
fn correctness_test_eq() {
let cs = ConstraintSystem::<Fq>::new_ref();
let x = FpVar::Var(
AllocatedFp::<Fq>::new_witness(cs.clone(), || {
Ok(G1Parameters::AFFINE_GENERATOR_COEFFS.0)
})
.unwrap(),
);
let y = FpVar::Var(
AllocatedFp::<Fq>::new_witness(cs.clone(), || {
Ok(G1Parameters::AFFINE_GENERATOR_COEFFS.1)
})
.unwrap(),
);
let a = NonZeroAffineVar::<G1Parameters, FpVar<Fq>>::new(x, y);
let n = 10;
let a_multiples: Vec<NonZeroAffineVar<G1Parameters, FpVar<Fq>>> =
std::iter::successors(Some(a.clone()), |acc| Some(acc.add_unchecked(&a).unwrap()))
.take(n)
.collect();
let all_equal: Vec<NonZeroAffineVar<G1Parameters, FpVar<Fq>>> = (0..n / 2)
.map(|i| {
a_multiples[i]
.add_unchecked(&a_multiples[n - i - 1])
.unwrap()
})
.collect();
for i in 0..n - 1 {
a_multiples[i]
.enforce_not_equal(&a_multiples[i + 1])
.unwrap();
}
for i in 0..all_equal.len() - 1 {
all_equal[i].enforce_equal(&all_equal[i + 1]).unwrap();
}
assert!(cs.is_satisfied().unwrap());
}
} }

Loading…
Cancel
Save