Browse Source

Make Nova's ecc gadgets read curve parameters from the group trait (#115)

* make ecc gadgets defined over Group rather than PrimeField

* use curve parameters from Group trait
main
Srinath Setty 2 years ago
committed by GitHub
parent
commit
f9672faf23
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 324 additions and 348 deletions
  1. +10
    -10
      examples/signature.rs
  2. +4
    -4
      src/circuit.rs
  3. +287
    -320
      src/gadgets/ecc.rs
  4. +5
    -5
      src/gadgets/r1cs.rs
  5. +14
    -6
      src/pasta.rs
  6. +4
    -3
      src/traits/mod.rs

+ 10
- 10
examples/signature.rs

@ -7,7 +7,7 @@ use ff::{
derive::byteorder::{ByteOrder, LittleEndian}, derive::byteorder::{ByteOrder, LittleEndian},
Field, PrimeField, PrimeFieldBits, Field, PrimeField, PrimeFieldBits,
}; };
use nova_snark::gadgets::ecc::AllocatedPoint;
use nova_snark::{gadgets::ecc::AllocatedPoint, traits::Group as NovaGroup};
use num_bigint::BigUint; use num_bigint::BigUint;
use pasta_curves::{ use pasta_curves::{
arithmetic::CurveAffine, arithmetic::CurveAffine,
@ -192,21 +192,21 @@ pub fn synthesize_bits>(
.collect::<Result<Vec<AllocatedBit>, SynthesisError>>() .collect::<Result<Vec<AllocatedBit>, SynthesisError>>()
} }
pub fn verify_signature<F: PrimeField + PrimeFieldBits, CS: ConstraintSystem<F>>(
pub fn verify_signature<G: NovaGroup, CS: ConstraintSystem<G::Base>>(
cs: &mut CS, cs: &mut CS,
pk: AllocatedPoint<F>,
r: AllocatedPoint<F>,
pk: AllocatedPoint<G>,
r: AllocatedPoint<G>,
s_bits: Vec<AllocatedBit>, s_bits: Vec<AllocatedBit>,
c_bits: Vec<AllocatedBit>, c_bits: Vec<AllocatedBit>,
) -> Result<(), SynthesisError> { ) -> Result<(), SynthesisError> {
let g = AllocatedPoint::alloc(
let g = AllocatedPoint::<G>::alloc(
cs.namespace(|| "g"), cs.namespace(|| "g"),
Some(( Some((
F::from_str_vartime(
G::Base::from_str_vartime(
"28948022309329048855892746252171976963363056481941647379679742748393362948096", "28948022309329048855892746252171976963363056481941647379679742748393362948096",
) )
.unwrap(), .unwrap(),
F::from_str_vartime("2").unwrap(),
G::Base::from_str_vartime("2").unwrap(),
false, false,
)), )),
) )
@ -218,7 +218,7 @@ pub fn verify_signature>
|lc| lc + CS::one(), |lc| lc + CS::one(),
|lc| { |lc| {
lc + ( lc + (
F::from_str_vartime(
G::Base::from_str_vartime(
"28948022309329048855892746252171976963363056481941647379679742748393362948096", "28948022309329048855892746252171976963363056481941647379679742748393362948096",
) )
.unwrap(), .unwrap(),
@ -231,7 +231,7 @@ pub fn verify_signature>
|| "gy is vesta curve", || "gy is vesta curve",
|lc| lc + g.get_coordinates().1.get_variable(), |lc| lc + g.get_coordinates().1.get_variable(),
|lc| lc + CS::one(), |lc| lc + CS::one(),
|lc| lc + (F::from_str_vartime("2").unwrap(), CS::one()),
|lc| lc + (G::Base::from_str_vartime("2").unwrap(), CS::one()),
); );
let sg = g.scalar_mul(cs.namespace(|| "[s]G"), s_bits)?; let sg = g.scalar_mul(cs.namespace(|| "[s]G"), s_bits)?;
@ -281,7 +281,7 @@ fn main() {
let pk = { let pk = {
let pkxy = pk.0.to_affine().coordinates().unwrap(); let pkxy = pk.0.to_affine().coordinates().unwrap();
AllocatedPoint::alloc(
AllocatedPoint::<G2>::alloc(
cs.namespace(|| "pub key"), cs.namespace(|| "pub key"),
Some((*pkxy.x(), *pkxy.y(), false)), Some((*pkxy.x(), *pkxy.y(), false)),
) )

+ 4
- 4
src/circuit.rs

@ -130,7 +130,7 @@ where
Vec<AllocatedNum<G::Base>>, Vec<AllocatedNum<G::Base>>,
AllocatedRelaxedR1CSInstance<G>, AllocatedRelaxedR1CSInstance<G>,
AllocatedR1CSInstance<G>, AllocatedR1CSInstance<G>,
AllocatedPoint<G::Base>,
AllocatedPoint<G>,
), ),
SynthesisError, SynthesisError,
> { > {
@ -231,7 +231,7 @@ where
z_i: Vec<AllocatedNum<G::Base>>, z_i: Vec<AllocatedNum<G::Base>>,
U: AllocatedRelaxedR1CSInstance<G>, U: AllocatedRelaxedR1CSInstance<G>,
u: AllocatedR1CSInstance<G>, u: AllocatedR1CSInstance<G>,
T: AllocatedPoint<G::Base>,
T: AllocatedPoint<G>,
arity: usize, arity: usize,
) -> Result<(AllocatedRelaxedR1CSInstance<G>, AllocatedBit), SynthesisError> { ) -> Result<(AllocatedRelaxedR1CSInstance<G>, AllocatedBit), SynthesisError> {
// Check that u.x[0] = Hash(params, U, i, z0, zi) // Check that u.x[0] = Hash(params, U, i, z0, zi)
@ -412,7 +412,7 @@ mod tests {
let mut cs: ShapeCS<G1> = ShapeCS::new(); let mut cs: ShapeCS<G1> = ShapeCS::new();
let _ = circuit1.synthesize(&mut cs); let _ = circuit1.synthesize(&mut cs);
let (shape1, gens1) = (cs.r1cs_shape(), cs.r1cs_gens()); let (shape1, gens1) = (cs.r1cs_shape(), cs.r1cs_gens());
assert_eq!(cs.num_constraints(), 9819);
assert_eq!(cs.num_constraints(), 9815);
// Initialize the shape and gens for the secondary // Initialize the shape and gens for the secondary
let circuit2: NovaAugmentedCircuit<G1, TrivialTestCircuit<<G1 as Group>::Base>> = let circuit2: NovaAugmentedCircuit<G1, TrivialTestCircuit<<G1 as Group>::Base>> =
@ -425,7 +425,7 @@ mod tests {
let mut cs: ShapeCS<G2> = ShapeCS::new(); let mut cs: ShapeCS<G2> = ShapeCS::new();
let _ = circuit2.synthesize(&mut cs); let _ = circuit2.synthesize(&mut cs);
let (shape2, gens2) = (cs.r1cs_shape(), cs.r1cs_gens()); let (shape2, gens2) = (cs.r1cs_shape(), cs.r1cs_gens());
assert_eq!(cs.num_constraints(), 10351);
assert_eq!(cs.num_constraints(), 10347);
// Execute the base case for the primary // Execute the base case for the primary
let zero1 = <<G2 as Group>::Base as Field>::zero(); let zero1 = <<G2 as Group>::Base as Field>::zero();

+ 287
- 320
src/gadgets/ecc.rs

@ -1,9 +1,12 @@
//! This module implements various elliptic curve gadgets //! This module implements various elliptic curve gadgets
#![allow(non_snake_case)] #![allow(non_snake_case)]
use crate::gadgets::utils::{
alloc_num_equals, alloc_one, alloc_zero, conditionally_select, conditionally_select2,
select_num_or_one, select_num_or_zero, select_num_or_zero2, select_one_or_diff2,
select_one_or_num2, select_zero_or_num2,
use crate::{
gadgets::utils::{
alloc_num_equals, alloc_one, alloc_zero, conditionally_select, conditionally_select2,
select_num_or_one, select_num_or_zero, select_num_or_zero2, select_one_or_diff2,
select_one_or_num2, select_zero_or_num2,
},
traits::Group,
}; };
use bellperson::{ use bellperson::{
gadgets::{ gadgets::{
@ -13,40 +16,43 @@ use bellperson::{
}, },
ConstraintSystem, SynthesisError, ConstraintSystem, SynthesisError,
}; };
use ff::PrimeField;
use ff::{Field, PrimeField};
/// AllocatedPoint provides an elliptic curve abstraction inside a circuit. /// AllocatedPoint provides an elliptic curve abstraction inside a circuit.
#[derive(Clone)] #[derive(Clone)]
pub struct AllocatedPoint<Fp>
pub struct AllocatedPoint<G>
where where
Fp: PrimeField,
G: Group,
{ {
pub(crate) x: AllocatedNum<Fp>,
pub(crate) y: AllocatedNum<Fp>,
pub(crate) is_infinity: AllocatedNum<Fp>,
pub(crate) x: AllocatedNum<G::Base>,
pub(crate) y: AllocatedNum<G::Base>,
pub(crate) is_infinity: AllocatedNum<G::Base>,
} }
impl<Fp> AllocatedPoint<Fp>
impl<G> AllocatedPoint<G>
where where
Fp: PrimeField,
G: Group,
{ {
/// Allocates a new point on the curve using coordinates provided by `coords`. /// Allocates a new point on the curve using coordinates provided by `coords`.
/// If coords = None, it allocates the default infinity point /// If coords = None, it allocates the default infinity point
pub fn alloc<CS>(mut cs: CS, coords: Option<(Fp, Fp, bool)>) -> Result<Self, SynthesisError>
pub fn alloc<CS>(
mut cs: CS,
coords: Option<(G::Base, G::Base, bool)>,
) -> Result<Self, SynthesisError>
where where
CS: ConstraintSystem<Fp>,
CS: ConstraintSystem<G::Base>,
{ {
let x = AllocatedNum::alloc(cs.namespace(|| "x"), || { let x = AllocatedNum::alloc(cs.namespace(|| "x"), || {
Ok(coords.map_or(Fp::zero(), |c| c.0))
Ok(coords.map_or(G::Base::zero(), |c| c.0))
})?; })?;
let y = AllocatedNum::alloc(cs.namespace(|| "y"), || { let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
Ok(coords.map_or(Fp::zero(), |c| c.1))
Ok(coords.map_or(G::Base::zero(), |c| c.1))
})?; })?;
let is_infinity = AllocatedNum::alloc(cs.namespace(|| "is_infinity"), || { let is_infinity = AllocatedNum::alloc(cs.namespace(|| "is_infinity"), || {
Ok(if coords.map_or(true, |c| c.2) { Ok(if coords.map_or(true, |c| c.2) {
Fp::one()
G::Base::one()
} else { } else {
Fp::zero()
G::Base::zero()
}) })
})?; })?;
cs.enforce( cs.enforce(
@ -62,7 +68,7 @@ where
/// Allocates a default point on the curve. /// Allocates a default point on the curve.
pub fn default<CS>(mut cs: CS) -> Result<Self, SynthesisError> pub fn default<CS>(mut cs: CS) -> Result<Self, SynthesisError>
where where
CS: ConstraintSystem<Fp>,
CS: ConstraintSystem<G::Base>,
{ {
let zero = alloc_zero(cs.namespace(|| "zero"))?; let zero = alloc_zero(cs.namespace(|| "zero"))?;
let one = alloc_one(cs.namespace(|| "one"))?; let one = alloc_one(cs.namespace(|| "one"))?;
@ -75,42 +81,18 @@ where
} }
/// Returns coordinates associated with the point. /// Returns coordinates associated with the point.
pub fn get_coordinates(&self) -> (&AllocatedNum<Fp>, &AllocatedNum<Fp>, &AllocatedNum<Fp>) {
pub fn get_coordinates(
&self,
) -> (
&AllocatedNum<G::Base>,
&AllocatedNum<G::Base>,
&AllocatedNum<G::Base>,
) {
(&self.x, &self.y, &self.is_infinity) (&self.x, &self.y, &self.is_infinity)
} }
// Allocate a random point. Only used for testing
#[cfg(test)]
pub fn random_vartime<CS: ConstraintSystem<Fp>>(mut cs: CS) -> Result<Self, SynthesisError> {
loop {
let x = Fp::random(&mut OsRng);
let y = (x * x * x + Fp::one() + Fp::one() + Fp::one() + Fp::one() + Fp::one()).sqrt();
if y.is_some().unwrap_u8() == 1 {
let x_alloc = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(x))?;
let y_alloc = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(y.unwrap()))?;
let is_infinity = alloc_zero(cs.namespace(|| "Is Infinity"))?;
return Ok(Self {
x: x_alloc,
y: y_alloc,
is_infinity,
});
}
}
}
/// Make the point io
#[cfg(test)]
pub fn inputize<CS: ConstraintSystem<Fp>>(&self, mut cs: CS) -> Result<(), SynthesisError> {
let _ = self.x.inputize(cs.namespace(|| "Input point.x"));
let _ = self.y.inputize(cs.namespace(|| "Input point.y"));
let _ = self
.is_infinity
.inputize(cs.namespace(|| "Input point.is_infinity"));
Ok(())
}
/// Negates the provided point /// Negates the provided point
pub fn negate<CS: ConstraintSystem<Fp>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
pub fn negate<CS: ConstraintSystem<G::Base>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
let y = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(-*self.y.get_value().get()?))?; let y = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(-*self.y.get_value().get()?))?;
cs.enforce( cs.enforce(
@ -128,10 +110,10 @@ where
} }
/// Add two points (may be equal) /// Add two points (may be equal)
pub fn add<CS: ConstraintSystem<Fp>>(
pub fn add<CS: ConstraintSystem<G::Base>>(
&self, &self,
mut cs: CS, mut cs: CS,
other: &AllocatedPoint<Fp>,
other: &AllocatedPoint<G>,
) -> Result<Self, SynthesisError> { ) -> Result<Self, SynthesisError> {
// Compute boolean equal indicating if self = other // Compute boolean equal indicating if self = other
@ -177,10 +159,10 @@ where
/// Adds other point to this point and returns the result. Assumes that the two points are /// Adds other point to this point and returns the result. Assumes that the two points are
/// different and that both other.is_infinity and this.is_infinty are bits /// different and that both other.is_infinity and this.is_infinty are bits
pub fn add_internal<CS: ConstraintSystem<Fp>>(
pub fn add_internal<CS: ConstraintSystem<G::Base>>(
&self, &self,
mut cs: CS, mut cs: CS,
other: &AllocatedPoint<Fp>,
other: &AllocatedPoint<G>,
equal_x: &AllocatedBit, equal_x: &AllocatedBit,
) -> Result<Self, SynthesisError> { ) -> Result<Self, SynthesisError> {
//************************************************************************/ //************************************************************************/
@ -195,9 +177,9 @@ where
// NOT(NOT(self.is_ifninity) AND NOT(other.is_infinity)) // NOT(NOT(self.is_ifninity) AND NOT(other.is_infinity))
let at_least_one_inf = AllocatedNum::alloc(cs.namespace(|| "at least one inf"), || { let at_least_one_inf = AllocatedNum::alloc(cs.namespace(|| "at least one inf"), || {
Ok( Ok(
Fp::one()
- (Fp::one() - *self.is_infinity.get_value().get()?)
* (Fp::one() - *other.is_infinity.get_value().get()?),
G::Base::one()
- (G::Base::one() - *self.is_infinity.get_value().get()?)
* (G::Base::one() - *other.is_infinity.get_value().get()?),
) )
})?; })?;
cs.enforce( cs.enforce(
@ -211,7 +193,7 @@ where
let x_diff_is_actual = let x_diff_is_actual =
AllocatedNum::alloc(cs.namespace(|| "allocate x_diff_is_actual"), || { AllocatedNum::alloc(cs.namespace(|| "allocate x_diff_is_actual"), || {
Ok(if *equal_x.get_value().get()? { Ok(if *equal_x.get_value().get()? {
Fp::one()
G::Base::one()
} else { } else {
*at_least_one_inf.get_value().get()? *at_least_one_inf.get_value().get()?
}) })
@ -233,9 +215,9 @@ where
)?; )?;
let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || { let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || {
let x_diff_inv = if *x_diff_is_actual.get_value().get()? == Fp::one() {
let x_diff_inv = if *x_diff_is_actual.get_value().get()? == G::Base::one() {
// Set to default // Set to default
Fp::one()
G::Base::one()
} else { } else {
// Set to the actual inverse // Set to the actual inverse
(*other.x.get_value().get()? - *self.x.get_value().get()?) (*other.x.get_value().get()? - *self.x.get_value().get()?)
@ -340,15 +322,13 @@ where
} }
/// Doubles the supplied point. /// Doubles the supplied point.
pub fn double<CS: ConstraintSystem<Fp>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
pub fn double<CS: ConstraintSystem<G::Base>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
//*************************************************************/ //*************************************************************/
// lambda = (Fp::one() + Fp::one() + Fp::one())
// * self.x
// * self.x
// * ((Fp::one() + Fp::one()) * self.y).invert().unwrap();
// lambda = (G::Base::from(3) * self.x * self.x + G::A())
// * (G::Base::from(2)) * self.y).invert().unwrap();
/*************************************************************/ /*************************************************************/
// Compute tmp = (Fp::one() + Fp::one())* self.y ? self != inf : 1
// Compute tmp = (G::Base::one() + G::Base::one())* self.y ? self != inf : 1
let tmp_actual = AllocatedNum::alloc(cs.namespace(|| "tmp_actual"), || { let tmp_actual = AllocatedNum::alloc(cs.namespace(|| "tmp_actual"), || {
Ok(*self.y.get_value().get()? + *self.y.get_value().get()?) Ok(*self.y.get_value().get()? + *self.y.get_value().get()?)
})?; })?;
@ -361,44 +341,35 @@ where
let tmp = select_one_or_num2(cs.namespace(|| "tmp"), &tmp_actual, &self.is_infinity)?; let tmp = select_one_or_num2(cs.namespace(|| "tmp"), &tmp_actual, &self.is_infinity)?;
// Now compute lambda as (Fp::one() + Fp::one + Fp::one()) * self.x * self.x * tmp_inv
let prod_1 = AllocatedNum::alloc(cs.namespace(|| "alloc prod 1"), || {
let tmp_inv = if *self.is_infinity.get_value().get()? == Fp::one() {
// Return default value 1
Fp::one()
} else {
// Return the actual inverse
(*tmp.get_value().get()?).invert().unwrap()
};
// Now compute lambda as (G::Base::from(3) * self.x * self.x + G::A()) * tmp_inv
Ok(tmp_inv * self.x.get_value().get()?)
let prod_1 = AllocatedNum::alloc(cs.namespace(|| "alloc prod 1"), || {
Ok(G::Base::from(3) * self.x.get_value().get()? * self.x.get_value().get()?)
})?; })?;
cs.enforce( cs.enforce(
|| "Check prod 1", || "Check prod 1",
|lc| lc + tmp.get_variable(),
|lc| lc + prod_1.get_variable(),
|lc| lc + self.x.get_variable(),
);
let prod_2 = AllocatedNum::alloc(cs.namespace(|| "alloc prod 2"), || {
Ok(*prod_1.get_value().get()? * self.x.get_value().get()?)
})?;
cs.enforce(
|| "Check prod 2",
|lc| lc + (G::Base::from(3), self.x.get_variable()),
|lc| lc + self.x.get_variable(), |lc| lc + self.x.get_variable(),
|lc| lc + prod_1.get_variable(), |lc| lc + prod_1.get_variable(),
|lc| lc + prod_2.get_variable(),
); );
let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || {
Ok(*prod_2.get_value().get()? * (Fp::one() + Fp::one() + Fp::one()))
let lambda = AllocatedNum::alloc(cs.namespace(|| "alloc lambda"), || {
let tmp_inv = if *self.is_infinity.get_value().get()? == G::Base::one() {
// Return default value 1
G::Base::one()
} else {
// Return the actual inverse
(*tmp.get_value().get()?).invert().unwrap()
};
Ok(tmp_inv * (*prod_1.get_value().get()? + G::get_curve_params().0))
})?; })?;
cs.enforce( cs.enforce(
|| "Check lambda", || "Check lambda",
|lc| lc + CS::one() + CS::one() + CS::one(),
|lc| lc + prod_2.get_variable(),
|lc| lc + tmp.get_variable(),
|lc| lc + lambda.get_variable(), |lc| lc + lambda.get_variable(),
|lc| lc + prod_1.get_variable() + (G::get_curve_params().0, CS::one()),
); );
/*************************************************************/ /*************************************************************/
@ -455,12 +426,12 @@ where
/// A gadget for scalar multiplication, optimized to use incomplete addition law. /// A gadget for scalar multiplication, optimized to use incomplete addition law.
/// The optimization here is analogous to https://github.com/arkworks-rs/r1cs-std/blob/6d64f379a27011b3629cf4c9cb38b7b7b695d5a0/src/groups/curves/short_weierstrass/mod.rs#L295, /// The optimization here is analogous to https://github.com/arkworks-rs/r1cs-std/blob/6d64f379a27011b3629cf4c9cb38b7b7b695d5a0/src/groups/curves/short_weierstrass/mod.rs#L295,
/// except we use complete addition law over affine coordinates instead of projective coordinates for the tail bits /// except we use complete addition law over affine coordinates instead of projective coordinates for the tail bits
pub fn scalar_mul<CS: ConstraintSystem<Fp>>(
pub fn scalar_mul<CS: ConstraintSystem<G::Base>>(
&self, &self,
mut cs: CS, mut cs: CS,
scalar_bits: Vec<AllocatedBit>, scalar_bits: Vec<AllocatedBit>,
) -> Result<Self, SynthesisError> { ) -> Result<Self, SynthesisError> {
let split_len = core::cmp::min(scalar_bits.len(), (Fp::NUM_BITS - 2) as usize);
let split_len = core::cmp::min(scalar_bits.len(), (G::Base::NUM_BITS - 2) as usize);
let (incomplete_bits, complete_bits) = scalar_bits.split_at(split_len); let (incomplete_bits, complete_bits) = scalar_bits.split_at(split_len);
// we convert AllocatedPoint into AllocatedPointNonInfinity; we deal with the case where self.is_infinity = 1 below // we convert AllocatedPoint into AllocatedPointNonInfinity; we deal with the case where self.is_infinity = 1 below
@ -544,7 +515,7 @@ where
} }
/// If condition outputs a otherwise outputs b /// If condition outputs a otherwise outputs b
pub fn conditionally_select<CS: ConstraintSystem<Fp>>(
pub fn conditionally_select<CS: ConstraintSystem<G::Base>>(
mut cs: CS, mut cs: CS,
a: &Self, a: &Self,
b: &Self, b: &Self,
@ -565,7 +536,7 @@ where
} }
/// If condition outputs a otherwise infinity /// If condition outputs a otherwise infinity
pub fn select_point_or_infinity<CS: ConstraintSystem<Fp>>(
pub fn select_point_or_infinity<CS: ConstraintSystem<G::Base>>(
mut cs: CS, mut cs: CS,
a: &Self, a: &Self,
condition: &Boolean, condition: &Boolean,
@ -584,163 +555,29 @@ where
} }
} }
#[cfg(test)]
use ff::PrimeFieldBits;
#[cfg(test)]
use rand::rngs::OsRng;
#[cfg(test)]
use std::marker::PhantomData;
#[cfg(test)]
#[derive(Debug, Clone)]
pub struct Point<Fp, Fq>
where
Fp: PrimeField,
Fq: PrimeField + PrimeFieldBits,
{
x: Fp,
y: Fp,
is_infinity: bool,
_p: PhantomData<Fq>,
}
#[cfg(test)]
impl<Fp, Fq> Point<Fp, Fq>
where
Fp: PrimeField,
Fq: PrimeField + PrimeFieldBits,
{
pub fn new(x: Fp, y: Fp, is_infinity: bool) -> Self {
Self {
x,
y,
is_infinity,
_p: Default::default(),
}
}
pub fn random_vartime() -> Self {
loop {
let x = Fp::random(&mut OsRng);
let y = (x * x * x + Fp::one() + Fp::one() + Fp::one() + Fp::one() + Fp::one()).sqrt();
if y.is_some().unwrap_u8() == 1 {
return Self {
x,
y: y.unwrap(),
is_infinity: false,
_p: Default::default(),
};
}
}
}
/// Add any two points
pub fn add(&self, other: &Point<Fp, Fq>) -> Self {
if self.x == other.x {
// If self == other then call double
if self.y == other.y {
self.double()
} else {
// if self.x == other.x and self.y != other.y then return infinity
Self {
x: Fp::zero(),
y: Fp::zero(),
is_infinity: true,
_p: Default::default(),
}
}
} else {
self.add_internal(other)
}
}
/// Add two different points
pub fn add_internal(&self, other: &Point<Fp, Fq>) -> Self {
if self.is_infinity {
return other.clone();
}
if other.is_infinity {
return self.clone();
}
let lambda = (other.y - self.y) * (other.x - self.x).invert().unwrap();
let x = lambda * lambda - self.x - other.x;
let y = lambda * (self.x - x) - self.y;
Self {
x,
y,
is_infinity: false,
_p: Default::default(),
}
}
pub fn double(&self) -> Self {
if self.is_infinity {
return Self {
x: Fp::zero(),
y: Fp::zero(),
is_infinity: true,
_p: Default::default(),
};
}
let lambda = (Fp::one() + Fp::one() + Fp::one())
* self.x
* self.x
* ((Fp::one() + Fp::one()) * self.y).invert().unwrap();
let x = lambda * lambda - self.x - self.x;
let y = lambda * (self.x - x) - self.y;
Self {
x,
y,
is_infinity: false,
_p: Default::default(),
}
}
pub fn scalar_mul(&self, scalar: &Fq) -> Self {
let mut res = Self {
x: Fp::zero(),
y: Fp::zero(),
is_infinity: true,
_p: Default::default(),
};
let bits = scalar.to_le_bits();
for i in (0..bits.len()).rev() {
res = res.double();
if bits[i] {
res = self.add(&res);
}
}
res
}
}
#[derive(Clone)] #[derive(Clone)]
/// AllocatedPoint but one that is guaranteed to be not infinity /// AllocatedPoint but one that is guaranteed to be not infinity
pub struct AllocatedPointNonInfinity<Fp>
pub struct AllocatedPointNonInfinity<G>
where where
Fp: PrimeField,
G: Group,
{ {
x: AllocatedNum<Fp>,
y: AllocatedNum<Fp>,
x: AllocatedNum<G::Base>,
y: AllocatedNum<G::Base>,
} }
impl<Fp> AllocatedPointNonInfinity<Fp>
impl<G> AllocatedPointNonInfinity<G>
where where
Fp: PrimeField,
G: Group,
{ {
/// Creates a new AllocatedPointNonInfinity from the specified coordinates /// Creates a new AllocatedPointNonInfinity from the specified coordinates
pub fn new(x: AllocatedNum<Fp>, y: AllocatedNum<Fp>) -> Self {
pub fn new(x: AllocatedNum<G::Base>, y: AllocatedNum<G::Base>) -> Self {
Self { x, y } Self { x, y }
} }
/// Allocates a new point on the curve using coordinates provided by `coords`. /// Allocates a new point on the curve using coordinates provided by `coords`.
pub fn alloc<CS>(mut cs: CS, coords: Option<(Fp, Fp)>) -> Result<Self, SynthesisError>
pub fn alloc<CS>(mut cs: CS, coords: Option<(G::Base, G::Base)>) -> Result<Self, SynthesisError>
where where
CS: ConstraintSystem<Fp>,
CS: ConstraintSystem<G::Base>,
{ {
let x = AllocatedNum::alloc(cs.namespace(|| "x"), || { let x = AllocatedNum::alloc(cs.namespace(|| "x"), || {
coords.map_or(Err(SynthesisError::AssignmentMissing), |c| Ok(c.0)) coords.map_or(Err(SynthesisError::AssignmentMissing), |c| Ok(c.0))
@ -753,7 +590,7 @@ where
} }
/// Turns an AllocatedPoint into an AllocatedPointNonInfinity (assumes it is not infinity) /// Turns an AllocatedPoint into an AllocatedPointNonInfinity (assumes it is not infinity)
pub fn from_allocated_point(p: &AllocatedPoint<Fp>) -> Self {
pub fn from_allocated_point(p: &AllocatedPoint<G>) -> Self {
Self { Self {
x: p.x.clone(), x: p.x.clone(),
y: p.y.clone(), y: p.y.clone(),
@ -763,8 +600,8 @@ where
/// Returns an AllocatedPoint from an AllocatedPointNonInfinity /// Returns an AllocatedPoint from an AllocatedPointNonInfinity
pub fn to_allocated_point( pub fn to_allocated_point(
&self, &self,
is_infinity: &AllocatedNum<Fp>,
) -> Result<AllocatedPoint<Fp>, SynthesisError> {
is_infinity: &AllocatedNum<G::Base>,
) -> Result<AllocatedPoint<G>, SynthesisError> {
Ok(AllocatedPoint { Ok(AllocatedPoint {
x: self.x.clone(), x: self.x.clone(),
y: self.y.clone(), y: self.y.clone(),
@ -773,19 +610,19 @@ where
} }
/// Returns coordinates associated with the point. /// Returns coordinates associated with the point.
pub fn get_coordinates(&self) -> (&AllocatedNum<Fp>, &AllocatedNum<Fp>) {
pub fn get_coordinates(&self) -> (&AllocatedNum<G::Base>, &AllocatedNum<G::Base>) {
(&self.x, &self.y) (&self.x, &self.y)
} }
/// Add two points assuming self != +/- other /// Add two points assuming self != +/- other
pub fn add_incomplete<CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError> pub fn add_incomplete<CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
where where
CS: ConstraintSystem<Fp>,
CS: ConstraintSystem<G::Base>,
{ {
// allocate a free variable that an honest prover sets to lambda = (y2-y1)/(x2-x1) // allocate a free variable that an honest prover sets to lambda = (y2-y1)/(x2-x1)
let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || { let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || {
if *other.x.get_value().get()? == *self.x.get_value().get()? { if *other.x.get_value().get()? == *self.x.get_value().get()? {
Ok(Fp::one())
Ok(G::Base::one())
} else { } else {
Ok( Ok(
(*other.y.get_value().get()? - *self.y.get_value().get()?) (*other.y.get_value().get()? - *self.y.get_value().get()?)
@ -842,17 +679,17 @@ where
/// doubles the point; since this is called with a point not at infinity, it is guaranteed to be not infinity /// doubles the point; since this is called with a point not at infinity, it is guaranteed to be not infinity
pub fn double_incomplete<CS>(&self, mut cs: CS) -> Result<Self, SynthesisError> pub fn double_incomplete<CS>(&self, mut cs: CS) -> Result<Self, SynthesisError>
where where
CS: ConstraintSystem<Fp>,
CS: ConstraintSystem<G::Base>,
{ {
// lambda = (3 x^2 + a) / 2 * y. For pasta curves, a = 0
// lambda = (3 x^2 + a) / 2 * y
let x_sq = self.x.square(cs.namespace(|| "x_sq"))?; let x_sq = self.x.square(cs.namespace(|| "x_sq"))?;
let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || { let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || {
let n = Fp::from(3) * x_sq.get_value().get()?;
let d = Fp::from(2) * *self.y.get_value().get()?;
if d == Fp::zero() {
Ok(Fp::one())
let n = G::Base::from(3) * x_sq.get_value().get()? + G::get_curve_params().0;
let d = G::Base::from(2) * *self.y.get_value().get()?;
if d == G::Base::zero() {
Ok(G::Base::one())
} else { } else {
Ok(n * d.invert().unwrap()) Ok(n * d.invert().unwrap())
} }
@ -860,8 +697,8 @@ where
cs.enforce( cs.enforce(
|| "Check that lambda is computed correctly", || "Check that lambda is computed correctly",
|lc| lc + lambda.get_variable(), |lc| lc + lambda.get_variable(),
|lc| lc + (Fp::from(2), self.y.get_variable()),
|lc| lc + (Fp::from(3), x_sq.get_variable()),
|lc| lc + (G::Base::from(2), self.y.get_variable()),
|lc| lc + (G::Base::from(3), x_sq.get_variable()) + (G::get_curve_params().0, CS::one()),
); );
let x = AllocatedNum::alloc(cs.namespace(|| "x"), || { let x = AllocatedNum::alloc(cs.namespace(|| "x"), || {
@ -876,7 +713,7 @@ where
|| "check that x is correct", || "check that x is correct",
|lc| lc + lambda.get_variable(), |lc| lc + lambda.get_variable(),
|lc| lc + lambda.get_variable(), |lc| lc + lambda.get_variable(),
|lc| lc + x.get_variable() + (Fp::from(2), self.x.get_variable()),
|lc| lc + x.get_variable() + (G::Base::from(2), self.x.get_variable()),
); );
let y = AllocatedNum::alloc(cs.namespace(|| "y"), || { let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
@ -897,14 +734,13 @@ where
} }
/// If condition outputs a otherwise outputs b /// If condition outputs a otherwise outputs b
pub fn conditionally_select<CS: ConstraintSystem<Fp>>(
pub fn conditionally_select<CS: ConstraintSystem<G::Base>>(
mut cs: CS, mut cs: CS,
a: &Self, a: &Self,
b: &Self, b: &Self,
condition: &Boolean, condition: &Boolean,
) -> Result<Self, SynthesisError> { ) -> Result<Self, SynthesisError> {
let x = conditionally_select(cs.namespace(|| "select x"), &a.x, &b.x, condition)?; let x = conditionally_select(cs.namespace(|| "select x"), &a.x, &b.x, condition)?;
let y = conditionally_select(cs.namespace(|| "select y"), &a.y, &b.y, condition)?; let y = conditionally_select(cs.namespace(|| "select y"), &a.y, &b.y, condition)?;
Ok(Self { x, y }) Ok(Self { x, y })
@ -914,18 +750,161 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::bellperson::{
r1cs::{NovaShape, NovaWitness},
{shape_cs::ShapeCS, solver::SatisfyingAssignment},
};
use ff::{Field, PrimeFieldBits};
use pasta_curves::{arithmetic::CurveAffine, group::Curve, EpAffine};
use rand::rngs::OsRng;
use std::ops::Mul;
type G1 = pasta_curves::pallas::Point;
type G2 = pasta_curves::vesta::Point;
#[derive(Debug, Clone)]
pub struct Point<G>
where
G: Group,
{
x: G::Base,
y: G::Base,
is_infinity: bool,
}
#[cfg(test)]
impl<G> Point<G>
where
G: Group,
{
pub fn new(x: G::Base, y: G::Base, is_infinity: bool) -> Self {
Self { x, y, is_infinity }
}
pub fn random_vartime() -> Self {
loop {
let x = G::Base::random(&mut OsRng);
let y = (x * x * x + G::Base::from(5)).sqrt();
if y.is_some().unwrap_u8() == 1 {
return Self {
x,
y: y.unwrap(),
is_infinity: false,
};
}
}
}
/// Add any two points
pub fn add(&self, other: &Point<G>) -> Self {
if self.x == other.x {
// If self == other then call double
if self.y == other.y {
self.double()
} else {
// if self.x == other.x and self.y != other.y then return infinity
Self {
x: G::Base::zero(),
y: G::Base::zero(),
is_infinity: true,
}
}
} else {
self.add_internal(other)
}
}
/// Add two different points
pub fn add_internal(&self, other: &Point<G>) -> Self {
if self.is_infinity {
return other.clone();
}
if other.is_infinity {
return self.clone();
}
let lambda = (other.y - self.y) * (other.x - self.x).invert().unwrap();
let x = lambda * lambda - self.x - other.x;
let y = lambda * (self.x - x) - self.y;
Self {
x,
y,
is_infinity: false,
}
}
pub fn double(&self) -> Self {
if self.is_infinity {
return Self {
x: G::Base::zero(),
y: G::Base::zero(),
is_infinity: true,
};
}
let lambda = G::Base::from(3)
* self.x
* self.x
* ((G::Base::one() + G::Base::one()) * self.y)
.invert()
.unwrap();
let x = lambda * lambda - self.x - self.x;
let y = lambda * (self.x - x) - self.y;
Self {
x,
y,
is_infinity: false,
}
}
pub fn scalar_mul(&self, scalar: &G::Scalar) -> Self {
let mut res = Self {
x: G::Base::zero(),
y: G::Base::zero(),
is_infinity: true,
};
let bits = scalar.to_le_bits();
for i in (0..bits.len()).rev() {
res = res.double();
if bits[i] {
res = self.add(&res);
}
}
res
}
}
// Allocate a random point. Only used for testing
pub fn alloc_random_point<G: Group, CS: ConstraintSystem<G::Base>>(
mut cs: CS,
) -> Result<AllocatedPoint<G>, SynthesisError> {
// get a random point
let p = Point::<G>::random_vartime();
AllocatedPoint::alloc(cs.namespace(|| "alloc p"), Some((p.x, p.y, p.is_infinity)))
}
/// Make the point io
pub fn inputize_allocted_point<G: Group, CS: ConstraintSystem<G::Base>>(
p: &AllocatedPoint<G>,
mut cs: CS,
) -> Result<(), SynthesisError> {
let _ = p.x.inputize(cs.namespace(|| "Input point.x"));
let _ = p.y.inputize(cs.namespace(|| "Input point.y"));
let _ = p
.is_infinity
.inputize(cs.namespace(|| "Input point.is_infinity"));
Ok(())
}
#[test] #[test]
fn test_ecc_ops() { fn test_ecc_ops() {
type Fp = pasta_curves::pallas::Base;
type Fq = pasta_curves::pallas::Scalar;
// perform some curve arithmetic // perform some curve arithmetic
let a = Point::<Fp, Fq>::random_vartime();
let b = Point::<Fp, Fq>::random_vartime();
let a = Point::<G1>::random_vartime();
let b = Point::<G1>::random_vartime();
let c = a.add(&b); let c = a.add(&b);
let d = a.double(); let d = a.double();
let s = Fq::random(&mut OsRng);
let s = <G1 as Group>::Scalar::random(&mut OsRng);
let e = a.scalar_mul(&s); let e = a.scalar_mul(&s);
// perform the same computation by translating to pasta_curve types // perform the same computation by translating to pasta_curve types
@ -968,27 +947,15 @@ mod tests {
assert_eq!(e_pasta, e_pasta_2); assert_eq!(e_pasta, e_pasta_2);
} }
use crate::bellperson::{
r1cs::{NovaShape, NovaWitness},
{shape_cs::ShapeCS, solver::SatisfyingAssignment},
};
use ff::{Field, PrimeFieldBits};
use pasta_curves::{arithmetic::CurveAffine, group::Curve, EpAffine};
use std::ops::Mul;
type G = pasta_curves::pallas::Point;
type Fp = pasta_curves::pallas::Scalar;
type Fq = pasta_curves::vesta::Scalar;
fn synthesize_smul<Fp, Fq, CS>(mut cs: CS) -> (AllocatedPoint<Fp>, AllocatedPoint<Fp>, Fq)
fn synthesize_smul<G, CS>(mut cs: CS) -> (AllocatedPoint<G>, AllocatedPoint<G>, G::Scalar)
where where
Fp: PrimeField,
Fq: PrimeField + PrimeFieldBits,
CS: ConstraintSystem<Fp>,
G: Group,
CS: ConstraintSystem<G::Base>,
{ {
let a = AllocatedPoint::<Fp>::random_vartime(cs.namespace(|| "a")).unwrap();
a.inputize(cs.namespace(|| "inputize a")).unwrap();
let a = alloc_random_point(cs.namespace(|| "a")).unwrap();
inputize_allocted_point(&a, cs.namespace(|| "inputize a")).unwrap();
let s = Fq::random(&mut OsRng);
let s = G::Scalar::random(&mut OsRng);
// Allocate bits for s // Allocate bits for s
let bits: Vec<AllocatedBit> = s let bits: Vec<AllocatedBit> = s
.to_le_bits() .to_le_bits()
@ -998,33 +965,33 @@ mod tests {
.collect::<Result<Vec<AllocatedBit>, SynthesisError>>() .collect::<Result<Vec<AllocatedBit>, SynthesisError>>()
.unwrap(); .unwrap();
let e = a.scalar_mul(cs.namespace(|| "Scalar Mul"), bits).unwrap(); let e = a.scalar_mul(cs.namespace(|| "Scalar Mul"), bits).unwrap();
e.inputize(cs.namespace(|| "inputize e")).unwrap();
inputize_allocted_point(&e, cs.namespace(|| "inputize e")).unwrap();
(a, e, s) (a, e, s)
} }
#[test] #[test]
fn test_ecc_circuit_ops() { fn test_ecc_circuit_ops() {
// First create the shape // First create the shape
let mut cs: ShapeCS<G> = ShapeCS::new();
let _ = synthesize_smul::<Fp, Fq, _>(cs.namespace(|| "synthesize"));
let mut cs: ShapeCS<G2> = ShapeCS::new();
let _ = synthesize_smul::<G1, _>(cs.namespace(|| "synthesize"));
println!("Number of constraints: {}", cs.num_constraints()); println!("Number of constraints: {}", cs.num_constraints());
let shape = cs.r1cs_shape(); let shape = cs.r1cs_shape();
let gens = cs.r1cs_gens(); let gens = cs.r1cs_gens();
// Then the satisfying assignment // Then the satisfying assignment
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
let (a, e, s) = synthesize_smul::<Fp, Fq, _>(cs.namespace(|| "synthesize"));
let mut cs: SatisfyingAssignment<G2> = SatisfyingAssignment::new();
let (a, e, s) = synthesize_smul::<G1, _>(cs.namespace(|| "synthesize"));
let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap(); let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap();
let a_p: Point<Fp, Fq> = Point::new(
let a_p: Point<G1> = Point::new(
a.x.get_value().unwrap(), a.x.get_value().unwrap(),
a.y.get_value().unwrap(), a.y.get_value().unwrap(),
a.is_infinity.get_value().unwrap() == Fp::one(),
a.is_infinity.get_value().unwrap() == <G1 as Group>::Base::one(),
); );
let e_p: Point<Fp, Fq> = Point::new(
let e_p: Point<G1> = Point::new(
e.x.get_value().unwrap(), e.x.get_value().unwrap(),
e.y.get_value().unwrap(), e.y.get_value().unwrap(),
e.is_infinity.get_value().unwrap() == Fp::one(),
e.is_infinity.get_value().unwrap() == <G1 as Group>::Base::one(),
); );
let e_new = a_p.scalar_mul(&s); let e_new = a_p.scalar_mul(&s);
assert!(e_p.x == e_new.x && e_p.y == e_new.y); assert!(e_p.x == e_new.x && e_p.y == e_new.y);
@ -1032,41 +999,40 @@ mod tests {
assert!(shape.is_sat(&gens, &inst, &witness).is_ok()); assert!(shape.is_sat(&gens, &inst, &witness).is_ok());
} }
fn synthesize_add_equal<Fp, Fq, CS>(mut cs: CS) -> (AllocatedPoint<Fp>, AllocatedPoint<Fp>)
fn synthesize_add_equal<G, CS>(mut cs: CS) -> (AllocatedPoint<G>, AllocatedPoint<G>)
where where
Fp: PrimeField,
Fq: PrimeField + PrimeFieldBits,
CS: ConstraintSystem<Fp>,
G: Group,
CS: ConstraintSystem<G::Base>,
{ {
let a = AllocatedPoint::<Fp>::random_vartime(cs.namespace(|| "a")).unwrap();
a.inputize(cs.namespace(|| "inputize a")).unwrap();
let a = alloc_random_point(cs.namespace(|| "a")).unwrap();
inputize_allocted_point(&a, cs.namespace(|| "inputize a")).unwrap();
let e = a.add(cs.namespace(|| "add a to a"), &a).unwrap(); let e = a.add(cs.namespace(|| "add a to a"), &a).unwrap();
e.inputize(cs.namespace(|| "inputize e")).unwrap();
inputize_allocted_point(&e, cs.namespace(|| "inputize e")).unwrap();
(a, e) (a, e)
} }
#[test] #[test]
fn test_ecc_circuit_add_equal() { fn test_ecc_circuit_add_equal() {
// First create the shape // First create the shape
let mut cs: ShapeCS<G> = ShapeCS::new();
let _ = synthesize_add_equal::<Fp, Fq, _>(cs.namespace(|| "synthesize add equal"));
let mut cs: ShapeCS<G2> = ShapeCS::new();
let _ = synthesize_add_equal::<G1, _>(cs.namespace(|| "synthesize add equal"));
println!("Number of constraints: {}", cs.num_constraints()); println!("Number of constraints: {}", cs.num_constraints());
let shape = cs.r1cs_shape(); let shape = cs.r1cs_shape();
let gens = cs.r1cs_gens(); let gens = cs.r1cs_gens();
// Then the satisfying assignment // Then the satisfying assignment
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
let (a, e) = synthesize_add_equal::<Fp, Fq, _>(cs.namespace(|| "synthesize add equal"));
let mut cs: SatisfyingAssignment<G2> = SatisfyingAssignment::new();
let (a, e) = synthesize_add_equal::<G1, _>(cs.namespace(|| "synthesize add equal"));
let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap(); let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap();
let a_p: Point<Fp, Fq> = Point::new(
let a_p: Point<G1> = Point::new(
a.x.get_value().unwrap(), a.x.get_value().unwrap(),
a.y.get_value().unwrap(), a.y.get_value().unwrap(),
a.is_infinity.get_value().unwrap() == Fp::one(),
a.is_infinity.get_value().unwrap() == <G1 as Group>::Base::one(),
); );
let e_p: Point<Fp, Fq> = Point::new(
let e_p: Point<G1> = Point::new(
e.x.get_value().unwrap(), e.x.get_value().unwrap(),
e.y.get_value().unwrap(), e.y.get_value().unwrap(),
e.is_infinity.get_value().unwrap() == Fp::one(),
e.is_infinity.get_value().unwrap() == <G1 as Group>::Base::one(),
); );
let e_new = a_p.add(&a_p); let e_new = a_p.add(&a_p);
assert!(e_p.x == e_new.x && e_p.y == e_new.y); assert!(e_p.x == e_new.x && e_p.y == e_new.y);
@ -1074,18 +1040,19 @@ mod tests {
assert!(shape.is_sat(&gens, &inst, &witness).is_ok()); assert!(shape.is_sat(&gens, &inst, &witness).is_ok());
} }
fn synthesize_add_negation<Fp, Fq, CS>(mut cs: CS) -> AllocatedPoint<Fp>
fn synthesize_add_negation<G, CS>(mut cs: CS) -> AllocatedPoint<G>
where where
Fp: PrimeField,
Fq: PrimeField + PrimeFieldBits,
CS: ConstraintSystem<Fp>,
G: Group,
CS: ConstraintSystem<G::Base>,
{ {
let a = AllocatedPoint::<Fp>::random_vartime(cs.namespace(|| "a")).unwrap();
a.inputize(cs.namespace(|| "inputize a")).unwrap();
let a = alloc_random_point(cs.namespace(|| "a")).unwrap();
inputize_allocted_point(&a, cs.namespace(|| "inputize a")).unwrap();
let mut b = a.clone(); let mut b = a.clone();
b.y =
AllocatedNum::alloc(cs.namespace(|| "allocate negation of a"), || Ok(Fp::zero())).unwrap();
b.inputize(cs.namespace(|| "inputize b")).unwrap();
b.y = AllocatedNum::alloc(cs.namespace(|| "allocate negation of a"), || {
Ok(G::Base::zero())
})
.unwrap();
inputize_allocted_point(&b, cs.namespace(|| "inputize b")).unwrap();
let e = a.add(cs.namespace(|| "add a to b"), &b).unwrap(); let e = a.add(cs.namespace(|| "add a to b"), &b).unwrap();
e e
} }
@ -1093,20 +1060,20 @@ mod tests {
#[test] #[test]
fn test_ecc_circuit_add_negation() { fn test_ecc_circuit_add_negation() {
// First create the shape // First create the shape
let mut cs: ShapeCS<G> = ShapeCS::new();
let _ = synthesize_add_negation::<Fp, Fq, _>(cs.namespace(|| "synthesize add equal"));
let mut cs: ShapeCS<G2> = ShapeCS::new();
let _ = synthesize_add_negation::<G1, _>(cs.namespace(|| "synthesize add equal"));
println!("Number of constraints: {}", cs.num_constraints()); println!("Number of constraints: {}", cs.num_constraints());
let shape = cs.r1cs_shape(); let shape = cs.r1cs_shape();
let gens = cs.r1cs_gens(); let gens = cs.r1cs_gens();
// Then the satisfying assignment // Then the satisfying assignment
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
let e = synthesize_add_negation::<Fp, Fq, _>(cs.namespace(|| "synthesize add negation"));
let mut cs: SatisfyingAssignment<G2> = SatisfyingAssignment::new();
let e = synthesize_add_negation::<G1, _>(cs.namespace(|| "synthesize add negation"));
let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap(); let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap();
let e_p: Point<Fp, Fq> = Point::new(
let e_p: Point<G1> = Point::new(
e.x.get_value().unwrap(), e.x.get_value().unwrap(),
e.y.get_value().unwrap(), e.y.get_value().unwrap(),
e.is_infinity.get_value().unwrap() == Fp::one(),
e.is_infinity.get_value().unwrap() == <G1 as Group>::Base::one(),
); );
assert!(e_p.is_infinity); assert!(e_p.is_infinity);
// Make sure that it is satisfiable // Make sure that it is satisfiable

+ 5
- 5
src/gadgets/r1cs.rs

@ -27,7 +27,7 @@ pub struct AllocatedR1CSInstance
where where
G: Group, G: Group,
{ {
pub(crate) W: AllocatedPoint<G::Base>,
pub(crate) W: AllocatedPoint<G>,
pub(crate) X0: AllocatedNum<G::Base>, pub(crate) X0: AllocatedNum<G::Base>,
pub(crate) X1: AllocatedNum<G::Base>, pub(crate) X1: AllocatedNum<G::Base>,
} }
@ -75,8 +75,8 @@ pub struct AllocatedRelaxedR1CSInstance
where where
G: Group, G: Group,
{ {
pub(crate) W: AllocatedPoint<G::Base>,
pub(crate) E: AllocatedPoint<G::Base>,
pub(crate) W: AllocatedPoint<G>,
pub(crate) E: AllocatedPoint<G>,
pub(crate) u: AllocatedNum<G::Base>, pub(crate) u: AllocatedNum<G::Base>,
pub(crate) X0: BigNat<G::Base>, pub(crate) X0: BigNat<G::Base>,
pub(crate) X1: BigNat<G::Base>, pub(crate) X1: BigNat<G::Base>,
@ -262,7 +262,7 @@ where
mut cs: CS, mut cs: CS,
params: AllocatedNum<G::Base>, // hash of R1CSShape of F' params: AllocatedNum<G::Base>, // hash of R1CSShape of F'
u: AllocatedR1CSInstance<G>, u: AllocatedR1CSInstance<G>,
T: AllocatedPoint<G::Base>,
T: AllocatedPoint<G>,
ro_consts: ROConstantsCircuit<G>, ro_consts: ROConstantsCircuit<G>,
limb_width: usize, limb_width: usize,
n_limbs: usize, n_limbs: usize,
@ -309,7 +309,7 @@ where
// Allocate the order of the non-native field as a constant // Allocate the order of the non-native field as a constant
let m_bn = alloc_bignat_constant( let m_bn = alloc_bignat_constant(
cs.namespace(|| "alloc m"), cs.namespace(|| "alloc m"),
&G::get_order(),
&G::get_curve_params().2,
limb_width, limb_width,
n_limbs, n_limbs,
)?; )?;

+ 14
- 6
src/pasta.rs

@ -89,12 +89,16 @@ impl Group for pallas::Point {
} }
} }
fn get_order() -> BigInt {
BigInt::from_str_radix(
fn get_curve_params() -> (Self::Base, Self::Base, BigInt) {
let A = Self::Base::zero();
let B = Self::Base::from(5);
let order = BigInt::from_str_radix(
"40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001", "40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001",
16, 16,
) )
.unwrap()
.unwrap();
(A, B, order)
} }
fn zero() -> Self { fn zero() -> Self {
@ -195,12 +199,16 @@ impl Group for vesta::Point {
} }
} }
fn get_order() -> BigInt {
BigInt::from_str_radix(
fn get_curve_params() -> (Self::Base, Self::Base, BigInt) {
let A = Self::Base::zero();
let B = Self::Base::from(5);
let order = BigInt::from_str_radix(
"40000000000000000000000000000000224698fc094cf91b992d30ed00000001", "40000000000000000000000000000000224698fc094cf91b992d30ed00000001",
16, 16,
) )
.unwrap()
.unwrap();
(A, B, order)
} }
fn zero() -> Self { fn zero() -> Self {

+ 4
- 3
src/traits/mod.rs

@ -12,6 +12,7 @@ use merlin::Transcript;
use num_bigint::BigInt; use num_bigint::BigInt;
/// Represents an element of a group /// Represents an element of a group
/// This is currently tailored for an elliptic curve group
pub trait Group: pub trait Group:
Clone Clone
+ Copy + Copy
@ -62,14 +63,14 @@ pub trait Group:
/// Returns the affine coordinates (x, y, infinty) for the point /// Returns the affine coordinates (x, y, infinty) for the point
fn to_coordinates(&self) -> (Self::Base, Self::Base, bool); fn to_coordinates(&self) -> (Self::Base, Self::Base, bool);
/// Returns the order of the group as a big integer
fn get_order() -> BigInt;
/// Returns an element that is the additive identity of the group /// Returns an element that is the additive identity of the group
fn zero() -> Self; fn zero() -> Self;
/// Returns the generator of the group /// Returns the generator of the group
fn get_generator() -> Self; fn get_generator() -> Self;
/// Returns A, B, and the order of the group as a big integer
fn get_curve_params() -> (Self::Base, Self::Base, BigInt);
} }
/// Represents a compressed version of a group element /// Represents a compressed version of a group element

Loading…
Cancel
Save