diff --git a/r1cs-std/src/groups/curves/short_weierstrass/bls12/mod.rs b/r1cs-std/src/groups/curves/short_weierstrass/bls12/mod.rs index 7494b3b..d189d1e 100644 --- a/r1cs-std/src/groups/curves/short_weierstrass/bls12/mod.rs +++ b/r1cs-std/src/groups/curves/short_weierstrass/bls12/mod.rs @@ -1,234 +1,195 @@ use algebra::{ - curves::bls12::{Bls12Parameters, G1Prepared, G2Prepared, TwistType}, + curves::{ + bls12::{Bls12Parameters, G1Prepared, G2Prepared, TwistType}, + short_weierstrass_jacobian::GroupAffine, + }, fields::Field, - BitIterator, One, ProjectiveCurve, + BitIterator, One, }; -use r1cs_core::{ConstraintSystem, SynthesisError}; +use r1cs_core::SynthesisError; use crate::{ - fields::{fp::FpGadget, fp2::Fp2Gadget, FieldGadget}, - groups::curves::short_weierstrass::AffineGadget, - prelude::*, + fields::{fp::FpVar, fp2::Fp2Var, FieldVar}, + groups::curves::short_weierstrass::*, Vec, }; -use core::{borrow::Borrow, fmt::Debug, ops::Mul}; +use core::fmt::Debug; -pub type G1Gadget

= AffineGadget< -

::G1Parameters, -

::Fp, - FpGadget<

::Fp>, ->; +pub type G1Var

= + ProjectiveVar<

::G1Parameters, FpVar<

::Fp>>; +pub type G1AffineVar

= + AffineVar<

::G1Parameters, FpVar<

::Fp>>; -pub type G2Gadget

= - AffineGadget<

::G2Parameters,

::Fp, Fp2G

>; +pub type G2Var

= ProjectiveVar<

::G2Parameters, Fp2G

>; +pub type G2AffineVar

= AffineVar<

::G2Parameters, Fp2G

>; #[derive(Derivative)] -#[derivative( - Clone(bound = "G1Gadget

: Clone"), - Debug(bound = "G1Gadget

: Debug") -)] -pub struct G1PreparedGadget(pub G1Gadget

); - -impl AllocGadget, P::Fp> for G1PreparedGadget

{ - fn alloc_constant>( - mut cs: CS, - t: T, - ) -> Result - where - T: Borrow>, - { - let obj = t.borrow(); - - Ok(Self(G1Gadget::

::alloc_constant( - &mut cs.ns(|| "g1"), - &obj.0.into(), - )?)) - } - - fn alloc>(_cs: CS, _f: F) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - todo!() +#[derivative(Clone(bound = "G1Var

: Clone"), Debug(bound = "G1Var

: Debug"))] +pub struct G1PreparedVar(pub AffineVar>); + +impl G1PreparedVar

{ + pub fn value(&self) -> Result, SynthesisError> { + let x = self.0.x.value()?; + let y = self.0.y.value()?; + let infinity = self.0.infinity.value()?; + let g = GroupAffine::new(x, y, infinity); + Ok(g.into()) } - fn alloc_input>( - _cs: CS, - _f: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - todo!() + pub fn from_group_var(q: &G1Var

) -> Result { + let g = q.to_affine()?; + Ok(Self(g)) } } -impl G1PreparedGadget

{ - pub fn get_value(&self) -> Option> { - Some(G1Prepared::from(self.0.get_value().unwrap().into_affine())) - } - - pub fn from_affine>( - _cs: CS, - q: &G1Gadget

, +impl AllocVar, P::Fp> for G1PreparedVar

{ + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, ) -> Result { - Ok(G1PreparedGadget(q.clone())) + let ns = cs.into(); + let cs = ns.cs(); + let g1_prep = f().map(|b| b.borrow().0); + + let x = FpVar::new_variable(cs.ns("x"), || g1_prep.map(|g| g.x), mode)?; + let y = FpVar::new_variable(cs.ns("y"), || g1_prep.map(|g| g.y), mode)?; + let infinity = Boolean::new_variable(cs.ns("inf"), || g1_prep.map(|g| g.infinity), mode)?; + let g = AffineVar::new(x, y, infinity); + Ok(Self(g)) } } -impl ToBytesGadget for G1PreparedGadget

{ +impl ToBytesGadget for G1PreparedVar

{ #[inline] - fn to_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - self.0.to_bytes(&mut cs.ns(|| "g_alpha to bytes")) + fn to_bytes(&self) -> Result>, SynthesisError> { + let mut bytes = self.0.x.to_bytes()?; + let y_bytes = self.0.y.to_bytes()?; + let inf_bytes = self.0.infinity.to_bytes()?; + bytes.extend_from_slice(&y_bytes); + bytes.extend_from_slice(&inf_bytes); + Ok(bytes) } - fn to_non_unique_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - self.0 - .to_non_unique_bytes(&mut cs.ns(|| "g_alpha to bytes")) + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + let mut bytes = self.0.x.to_bytes()?; + let y_bytes = self.0.y.to_bytes()?; + let inf_bytes = self.0.infinity.to_bytes()?; + bytes.extend_from_slice(&y_bytes); + bytes.extend_from_slice(&inf_bytes); + Ok(bytes) } } -type Fp2G

= Fp2Gadget<

::Fp2Params,

::Fp>; +type Fp2G

= Fp2Var<

::Fp2Params>; type LCoeff

= (Fp2G

, Fp2G

); #[derive(Derivative)] #[derivative( - Clone(bound = "Fp2Gadget: Clone"), - Debug(bound = "Fp2Gadget: Debug") + Clone(bound = "Fp2Var: Clone"), + Debug(bound = "Fp2Var: Debug") )] -pub struct G2PreparedGadget { +pub struct G2PreparedVar { pub ell_coeffs: Vec>, } -impl AllocGadget, P::Fp> for G2PreparedGadget

{ - fn alloc_constant>( - mut cs: CS, - t: T, - ) -> Result - where - T: Borrow>, - { - let obj = t.borrow(); - let mut res = Vec::>::new(); - - for (i, (x, y, z)) in obj.ell_coeffs.iter().enumerate() { - let z_inverse = z.inverse().unwrap(); - - let x_normalized = x.mul(&z_inverse); - let y_normalized = y.mul(&z_inverse); - - let x_gadget = - Fp2Gadget::alloc_constant(&mut cs.ns(|| format!("alloc_x#{}", i)), x_normalized)?; - let y_gadget = - Fp2Gadget::alloc_constant(&mut cs.ns(|| format!("alloc_y#{}", i)), y_normalized)?; - - res.push((x_gadget, y_gadget)); - } - - Ok(Self { ell_coeffs: res }) - } - - fn alloc>(_cs: CS, _f: F) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - todo!() - } - - fn alloc_input>( - _cs: CS, - _f: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - todo!() +impl AllocVar, P::Fp> for G2PreparedVar

{ + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + let g2_prep = f().map(|b| { + let projective_coeffs = &b.borrow().ell_coeffs; + let mut z_s = projective_coeffs + .iter() + .map(|(_, _, z)| *z) + .collect::>(); + algebra::fields::batch_inversion(&mut z_s); + projective_coeffs + .iter() + .zip(z_s) + .map(|((x, y, _), z_inv)| (*x * &z_inv, *y * &z_inv)) + .collect::>() + }); + + let l = Vec::new_variable( + cs.ns("l"), + || { + g2_prep + .clone() + .map(|c| c.iter().map(|(l, _)| *l).collect::>()) + }, + mode, + )?; + let r = Vec::new_variable( + cs.ns("r"), + || g2_prep.map(|c| c.iter().map(|(_, r)| *r).collect::>()), + mode, + )?; + let ell_coeffs = l.into_iter().zip(r).collect(); + Ok(Self { ell_coeffs }) } } -impl ToBytesGadget for G2PreparedGadget

{ +impl ToBytesGadget for G2PreparedVar

{ #[inline] - fn to_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { + fn to_bytes(&self) -> Result>, SynthesisError> { let mut bytes = Vec::new(); - for (i, coeffs) in self.ell_coeffs.iter().enumerate() { - let mut cs = cs.ns(|| format!("Iteration {}", i)); - bytes.extend_from_slice(&coeffs.0.to_bytes(&mut cs.ns(|| "c0"))?); - bytes.extend_from_slice(&coeffs.1.to_bytes(&mut cs.ns(|| "c1"))?); + for coeffs in &self.ell_coeffs { + bytes.extend_from_slice(&coeffs.0.to_bytes()?); + bytes.extend_from_slice(&coeffs.1.to_bytes()?); } Ok(bytes) } - fn to_non_unique_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { let mut bytes = Vec::new(); - for (i, coeffs) in self.ell_coeffs.iter().enumerate() { - let mut cs = cs.ns(|| format!("Iteration {}", i)); - bytes.extend_from_slice(&coeffs.0.to_non_unique_bytes(&mut cs.ns(|| "c0"))?); - bytes.extend_from_slice(&coeffs.1.to_non_unique_bytes(&mut cs.ns(|| "c1"))?); + for coeffs in &self.ell_coeffs { + bytes.extend_from_slice(&coeffs.0.to_non_unique_bytes()?); + bytes.extend_from_slice(&coeffs.1.to_non_unique_bytes()?); } Ok(bytes) } } -impl G2PreparedGadget

{ - pub fn from_affine>( - mut cs: CS, - q: &G2Gadget

, - ) -> Result { +impl G2PreparedVar

{ + pub fn from_group_var(q: &G2Var

) -> Result { + let q = q.to_affine()?; let two_inv = P::Fp::one().double().inverse().unwrap(); - let zero = G2Gadget::

::zero(cs.ns(|| "zero"))?; - q.enforce_not_equal(cs.ns(|| "enforce not zero"), &zero)?; + // Enforce that `q` is not the point at infinity. + q.infinity.enforce_not_equal(&Boolean::Constant(true))?; let mut ell_coeffs = vec![]; let mut r = q.clone(); - for (j, i) in BitIterator::new(P::X).skip(1).enumerate() { - let mut cs = cs.ns(|| format!("Iteration {}", j)); - ell_coeffs.push(Self::double(cs.ns(|| "double"), &mut r, &two_inv)?); + for i in BitIterator::new(P::X).skip(1) { + ell_coeffs.push(Self::double(&mut r, &two_inv)?); if i { - ell_coeffs.push(Self::add(cs.ns(|| "add"), &mut r, &q)?); + ell_coeffs.push(Self::add(&mut r, &q)?); } } Ok(Self { ell_coeffs }) } - fn double>( - mut cs: CS, - r: &mut G2Gadget

, - two_inv: &P::Fp, - ) -> Result, SynthesisError> { - let a = r.y.inverse(cs.ns(|| "Inverse"))?; - let mut b = r.x.square(cs.ns(|| "square x"))?; + fn double(r: &mut G2AffineVar

, two_inv: &P::Fp) -> Result, SynthesisError> { + let a = r.y.inverse()?; + let mut b = r.x.square()?; let b_tmp = b.clone(); - b.mul_by_fp_constant_in_place(cs.ns(|| "mul by two_inv"), two_inv)?; - b.add_in_place(cs.ns(|| "compute b"), &b_tmp)?; - - let c = a.mul(cs.ns(|| "compute c"), &b)?; - let d = r.x.double(cs.ns(|| "compute d"))?; - let x3 = c.square(cs.ns(|| "c^2"))?.sub(cs.ns(|| "sub d"), &d)?; - let e = c - .mul(cs.ns(|| "c*r.x"), &r.x)? - .sub(cs.ns(|| "sub r.y"), &r.y)?; - let c_x3 = c.mul(cs.ns(|| "c*x_3"), &x3)?; - let y3 = e.sub(cs.ns(|| "e = c * x3"), &c_x3)?; + b.mul_assign_by_base_field_constant(*two_inv); + b += &b_tmp; + + let c = &a * &b; + let d = r.x.double()?; + let x3 = c.square()? - &d; + let e = &c * &r.x - &r.y; + let c_x3 = &c * &x3; + let y3 = &e - &c_x3; let mut f = c; - f.negate_in_place(cs.ns(|| "c = -c"))?; + f.negate_in_place()?; r.x = x3; r.y = y3; match P::TWIST_TYPE { @@ -237,28 +198,18 @@ impl G2PreparedGadget

{ } } - fn add>( - mut cs: CS, - r: &mut G2Gadget

, - q: &G2Gadget

, - ) -> Result, SynthesisError> { - let a = - q.x.sub(cs.ns(|| "q.x - r.x"), &r.x)? - .inverse(cs.ns(|| "calc a"))?; - let b = q.y.sub(cs.ns(|| "q.y - r.y"), &r.y)?; - let c = a.mul(cs.ns(|| "compute c"), &b)?; - let d = r.x.add(cs.ns(|| "r.x + q.x"), &q.x)?; - let x3 = c.square(cs.ns(|| "c^2"))?.sub(cs.ns(|| "sub d"), &d)?; + fn add(r: &mut G2AffineVar

, q: &G2AffineVar

) -> Result, SynthesisError> { + let a = (&q.x - &r.x).inverse()?; + let b = &q.y - &r.y; + let c = &a * &b; + let d = &r.x + &q.x; + let x3 = c.square()? - &d; - let e = - r.x.sub(cs.ns(|| "r.x - x3"), &x3)? - .mul(cs.ns(|| "c * (r.x - x3)"), &c)?; - let y3 = e.sub(cs.ns(|| "calc y3"), &r.y)?; - let g = c - .mul(cs.ns(|| "c*r.x"), &r.x)? - .sub(cs.ns(|| "calc g"), &r.y)?; + let e = (&r.x - &x3) * &c; + let y3 = e - &r.y; + let g = &c * &r.x - &r.y; let mut f = c; - f.negate_in_place(cs.ns(|| "c = -c"))?; + f.negate_in_place()?; r.x = x3; r.y = y3; match P::TWIST_TYPE { diff --git a/r1cs-std/src/groups/curves/short_weierstrass/mnt4/mod.rs b/r1cs-std/src/groups/curves/short_weierstrass/mnt4/mod.rs index def96b4..6f515af 100644 --- a/r1cs-std/src/groups/curves/short_weierstrass/mnt4/mod.rs +++ b/r1cs-std/src/groups/curves/short_weierstrass/mnt4/mod.rs @@ -5,94 +5,64 @@ use algebra::{ }, Field, }; -use r1cs_core::{ConstraintSystem, SynthesisError}; +use r1cs_core::{Namespace, SynthesisError}; use crate::{ - fields::{fp::FpGadget, fp2::Fp2Gadget, FieldGadget}, - groups::curves::short_weierstrass::AffineGadget, - pairing::mnt4::PairingGadget, + fields::{fp::FpVar, fp2::Fp2Var, FieldVar}, + groups::curves::short_weierstrass::ProjectiveVar, + pairing::mnt4::PairingVar, prelude::*, Vec, }; use core::borrow::Borrow; -pub type G1Gadget

= AffineGadget< -

::G1Parameters, -

::Fp, - FpGadget<

::Fp>, ->; +pub type G1Var

= + ProjectiveVar<

::G1Parameters, FpVar<

::Fp>>; -pub type G2Gadget

= - AffineGadget<

::G2Parameters,

::Fp, Fp2G

>; +pub type G2Var

= ProjectiveVar<

::G2Parameters, Fp2G

>; #[derive(Derivative)] #[derivative(Clone(bound = "P: MNT4Parameters"), Debug(bound = "P: MNT4Parameters"))] -pub struct G1PreparedGadget { - pub x: FpGadget, - pub y: FpGadget, - pub x_twist: Fp2Gadget, - pub y_twist: Fp2Gadget, +pub struct G1PreparedVar { + pub x: FpVar, + pub y: FpVar, + pub x_twist: Fp2Var, + pub y_twist: Fp2Var, } -impl G1PreparedGadget

{ - pub fn get_value(&self) -> Option> { - match ( - self.x.get_value(), - self.y.get_value(), - self.x_twist.get_value(), - self.y_twist.get_value(), - ) { - (Some(x), Some(y), Some(x_twist), Some(y_twist)) => Some(G1Prepared { - x, - y, - x_twist, - y_twist, - }), - _ => None, - } - } - - pub fn from_affine>( - mut cs: CS, - q: &G1Gadget

, +impl AllocVar, P::Fp> for G1PreparedVar

{ + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, ) -> Result { - let x_twist = Fp2Gadget::new( - q.x.mul_by_constant(cs.ns(|| "g1.x * twist.c0"), &P::TWIST.c0)?, - q.x.mul_by_constant(cs.ns(|| "g1.x * twist.c1"), &P::TWIST.c1)?, - ); - let y_twist = Fp2Gadget::new( - q.y.mul_by_constant(cs.ns(|| "g1.y * twist.c0"), &P::TWIST.c0)?, - q.y.mul_by_constant(cs.ns(|| "g1.y * twist.c1"), &P::TWIST.c1)?, - ); - Ok(G1PreparedGadget { - x: q.x.clone(), - y: q.y.clone(), + let ns = cs.into(); + let cs = ns.cs(); + + let g1_prep = f().map(|b| *b.borrow()); + + let x = FpVar::new_variable(cs.ns("x"), || g1_prep.map(|g| g.x), mode)?; + let y = FpVar::new_variable(cs.ns("y"), || g1_prep.map(|g| g.y), mode)?; + let x_twist = Fp2Var::new_variable(cs.ns("x_twist"), || g1_prep.map(|g| g.x_twist), mode)?; + let y_twist = Fp2Var::new_variable(cs.ns("y_twist"), || g1_prep.map(|g| g.y_twist), mode)?; + Ok(Self { + x, + y, x_twist, y_twist, }) } } -impl AllocGadget, P::Fp> for G1PreparedGadget

{ - fn alloc_constant>( - mut cs: CS, - t: T, - ) -> Result - where - T: Borrow>, - { - let obj = t.borrow(); - let x = FpGadget::::alloc_constant(&mut cs.ns(|| "alloc_x"), &obj.x)?; - let y = FpGadget::::alloc_constant(&mut cs.ns(|| "alloc_y"), &obj.y)?; - let x_twist = Fp2Gadget::::alloc_constant( - &mut cs.ns(|| "alloc_x_twist"), - &obj.x_twist, - )?; - let y_twist = Fp2Gadget::::alloc_constant( - &mut cs.ns(|| "alloc_y_twist"), - &obj.y_twist, - )?; - Ok(G1PreparedGadget { +impl G1PreparedVar

{ + pub fn value(&self) -> Result, SynthesisError> { + let (x, y, x_twist, y_twist) = ( + self.x.value()?, + self.y.value()?, + self.x_twist.value()?, + self.y_twist.value()?, + ); + Ok(G1Prepared { x, y, x_twist, @@ -100,36 +70,26 @@ impl AllocGadget, P::Fp> for G1PreparedGadget

>(_cs: CS, _f: F) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - todo!() - } - - fn alloc_input>( - _cs: CS, - _f: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - todo!() + pub fn from_group_var(q: &G1Var

) -> Result { + let q = q.to_affine()?; + let x_twist = Fp2Var::new(&q.x * P::TWIST.c0, &q.x * P::TWIST.c1); + let y_twist = Fp2Var::new(&q.y * P::TWIST.c0, &q.y * P::TWIST.c1); + Ok(G1PreparedVar { + x: q.x.clone(), + y: q.y.clone(), + x_twist, + y_twist, + }) } } -impl ToBytesGadget for G1PreparedGadget

{ +impl ToBytesGadget for G1PreparedVar

{ #[inline] - fn to_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut x = self.x.to_bytes(&mut cs.ns(|| "x to bytes"))?; - let mut y = self.y.to_bytes(&mut cs.ns(|| "y to bytes"))?; - let mut x_twist = self.x_twist.to_bytes(&mut cs.ns(|| "x_twist to bytes"))?; - let mut y_twist = self.y_twist.to_bytes(&mut cs.ns(|| "y_twist to bytes"))?; + fn to_bytes(&self) -> Result>, SynthesisError> { + let mut x = self.x.to_bytes()?; + let mut y = self.y.to_bytes()?; + let mut x_twist = self.x_twist.to_bytes()?; + let mut y_twist = self.y_twist.to_bytes()?; x.append(&mut y); x.append(&mut x_twist); @@ -137,18 +97,11 @@ impl ToBytesGadget for G1PreparedGadget

{ Ok(x) } - fn to_non_unique_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut x = self.x.to_non_unique_bytes(&mut cs.ns(|| "x to bytes"))?; - let mut y = self.y.to_non_unique_bytes(&mut cs.ns(|| "y to bytes"))?; - let mut x_twist = self - .x_twist - .to_non_unique_bytes(&mut cs.ns(|| "x_twist to bytes"))?; - let mut y_twist = self - .y_twist - .to_non_unique_bytes(&mut cs.ns(|| "y_twist to bytes"))?; + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + let mut x = self.x.to_non_unique_bytes()?; + let mut y = self.y.to_non_unique_bytes()?; + let mut x_twist = self.x_twist.to_non_unique_bytes()?; + let mut y_twist = self.y_twist.to_non_unique_bytes()?; x.append(&mut y); x.append(&mut x_twist); @@ -157,53 +110,48 @@ impl ToBytesGadget for G1PreparedGadget

{ } } -type Fp2G

= Fp2Gadget<

::Fp2Params,

::Fp>; +type Fp2G

= Fp2Var<

::Fp2Params>; + #[derive(Derivative)] #[derivative(Clone(bound = "P: MNT4Parameters"), Debug(bound = "P: MNT4Parameters"))] -pub struct G2PreparedGadget { - pub x: Fp2Gadget, - pub y: Fp2Gadget, - pub x_over_twist: Fp2Gadget, - pub y_over_twist: Fp2Gadget, - pub double_coefficients: Vec>, - pub addition_coefficients: Vec>, +pub struct G2PreparedVar { + pub x: Fp2Var, + pub y: Fp2Var, + pub x_over_twist: Fp2Var, + pub y_over_twist: Fp2Var, + pub double_coefficients: Vec>, + pub addition_coefficients: Vec>, } -impl AllocGadget, P::Fp> for G2PreparedGadget

{ - fn alloc_constant>( - mut cs: CS, - t: T, - ) -> Result - where - T: Borrow>, - { - let obj = t.borrow(); - let x = Fp2Gadget::::alloc_constant(&mut cs.ns(|| "alloc_x"), &obj.x)?; - let y = Fp2Gadget::::alloc_constant(&mut cs.ns(|| "alloc_y"), &obj.y)?; - let x_over_twist = Fp2Gadget::::alloc_constant( - &mut cs.ns(|| "alloc_x_over_twist"), - &obj.x_over_twist, +impl AllocVar, P::Fp> for G2PreparedVar

{ + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + + let g2_prep = f().map(|b| b.borrow().clone()); + let g2 = g2_prep.as_ref().map_err(|e| *e); + + let x = Fp2Var::new_variable(cs.ns("x"), || g2.map(|g| g.x), mode)?; + let y = Fp2Var::new_variable(cs.ns("y"), || g2.map(|g| g.y), mode)?; + let x_over_twist = + Fp2Var::new_variable(cs.ns("x_over_twist"), || g2.map(|g| g.x_over_twist), mode)?; + let y_over_twist = + Fp2Var::new_variable(cs.ns("y_over_twist"), || g2.map(|g| g.y_over_twist), mode)?; + let double_coefficients = Vec::new_variable( + cs.ns("double coeffs"), + || g2.map(|g| g.double_coefficients.clone()), + mode, )?; - let y_over_twist = Fp2Gadget::::alloc_constant( - &mut cs.ns(|| "alloc_y_over_twist"), - &obj.y_over_twist, + let addition_coefficients = Vec::new_variable( + cs.ns("add coeffs"), + || g2.map(|g| g.addition_coefficients.clone()), + mode, )?; - let mut double_coefficients = Vec::>::new(); - for (i, item) in obj.double_coefficients.iter().enumerate() { - double_coefficients.push(AteDoubleCoefficientsGadget::

::alloc_constant( - &mut cs.ns(|| format!("alloc_double_coefficients_{}", i)), - item, - )?); - } - let mut addition_coefficients = Vec::>::new(); - for (i, item) in obj.addition_coefficients.iter().enumerate() { - addition_coefficients.push(AteAdditionCoefficientsGadget::

::alloc_constant( - &mut cs.ns(|| format!("alloc_addition_coefficients_{}", i)), - item, - )?); - } - - Ok(G2PreparedGadget { + Ok(Self { x, y, x_over_twist, @@ -212,142 +160,93 @@ impl AllocGadget, P::Fp> for G2PreparedGadget

>(_cs: CS, _f: F) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - todo!() - } - - fn alloc_input>( - _cs: CS, - _f: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - todo!() - } } -impl ToBytesGadget for G2PreparedGadget

{ +impl ToBytesGadget for G2PreparedVar

{ #[inline] - fn to_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut x = self.x.to_bytes(&mut cs.ns(|| "x to bytes"))?; - let mut y = self.y.to_bytes(&mut cs.ns(|| "y to bytes"))?; - let mut x_over_twist = self - .x_over_twist - .to_bytes(&mut cs.ns(|| "x_over_twist to bytes"))?; - let mut y_over_twist = self - .y_over_twist - .to_bytes(&mut cs.ns(|| "y_over_twist to bytes"))?; + fn to_bytes(&self) -> Result>, SynthesisError> { + let mut x = self.x.to_bytes()?; + let mut y = self.y.to_bytes()?; + let mut x_over_twist = self.x_over_twist.to_bytes()?; + let mut y_over_twist = self.y_over_twist.to_bytes()?; x.append(&mut y); x.append(&mut x_over_twist); x.append(&mut y_over_twist); - for (i, coeff) in self.double_coefficients.iter().enumerate() { - x.extend_from_slice(&coeff.to_bytes(cs.ns(|| format!("double_coefficients {}", i)))?); + for coeff in &self.double_coefficients { + x.extend_from_slice(&coeff.to_bytes()?); } - for (i, coeff) in self.addition_coefficients.iter().enumerate() { - x.extend_from_slice(&coeff.to_bytes(cs.ns(|| format!("addition_coefficients {}", i)))?); + for coeff in &self.addition_coefficients { + x.extend_from_slice(&coeff.to_bytes()?); } Ok(x) } - fn to_non_unique_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut x = self.x.to_non_unique_bytes(&mut cs.ns(|| "x to bytes"))?; - let mut y = self.y.to_non_unique_bytes(&mut cs.ns(|| "y to bytes"))?; - let mut x_over_twist = self - .x_over_twist - .to_non_unique_bytes(&mut cs.ns(|| "x_over_twist to bytes"))?; - let mut y_over_twist = self - .y_over_twist - .to_non_unique_bytes(&mut cs.ns(|| "y_over_twist to bytes"))?; + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + let mut x = self.x.to_non_unique_bytes()?; + let mut y = self.y.to_non_unique_bytes()?; + let mut x_over_twist = self.x_over_twist.to_non_unique_bytes()?; + let mut y_over_twist = self.y_over_twist.to_non_unique_bytes()?; x.append(&mut y); x.append(&mut x_over_twist); x.append(&mut y_over_twist); - for (i, coeff) in self.double_coefficients.iter().enumerate() { - x.extend_from_slice( - &coeff.to_non_unique_bytes(cs.ns(|| format!("double_coefficients {}", i)))?, - ); + for coeff in &self.double_coefficients { + x.extend_from_slice(&coeff.to_non_unique_bytes()?); } - for (i, coeff) in self.addition_coefficients.iter().enumerate() { - x.extend_from_slice( - &coeff.to_non_unique_bytes(cs.ns(|| format!("addition_coefficients {}", i)))?, - ); + for coeff in &self.addition_coefficients { + x.extend_from_slice(&coeff.to_non_unique_bytes()?); } Ok(x) } } -impl G2PreparedGadget

{ - pub fn get_value(&self) -> Option> { - match ( - self.x.get_value(), - self.y.get_value(), - self.x_over_twist.get_value(), - self.y_over_twist.get_value(), - self.double_coefficients - .iter() - .map(|coeff| coeff.get_value()) - .collect::>>>(), - self.addition_coefficients - .iter() - .map(|coeff| coeff.get_value()) - .collect::>>>(), - ) { - ( - Some(x), - Some(y), - Some(x_over_twist), - Some(y_over_twist), - Some(double_coefficients), - Some(addition_coefficients), - ) => Some(G2Prepared { - x, - y, - x_over_twist, - y_over_twist, - double_coefficients, - addition_coefficients, - }), - _ => None, - } +impl G2PreparedVar

{ + pub fn value(&self) -> Result, SynthesisError> { + let x = self.x.value()?; + let y = self.y.value()?; + let x_over_twist = self.x_over_twist.value()?; + let y_over_twist = self.y_over_twist.value()?; + let double_coefficients = self + .double_coefficients + .iter() + .map(|coeff| coeff.value()) + .collect::>, _>>()?; + let addition_coefficients = self + .addition_coefficients + .iter() + .map(|coeff| coeff.value()) + .collect::>, _>>()?; + Ok(G2Prepared { + x, + y, + x_over_twist, + y_over_twist, + double_coefficients, + addition_coefficients, + }) } - pub fn from_affine>( - mut cs: CS, - q: &G2Gadget

, - ) -> Result { + pub fn from_group_var(q: &G2Var

) -> Result { let twist_inv = P::TWIST.inverse().unwrap(); + let q = q.to_affine()?; - let mut g2p = G2PreparedGadget { + let mut g2p = G2PreparedVar { x: q.x.clone(), y: q.y.clone(), - x_over_twist: q.x.mul_by_constant(cs.ns(|| "x over twist"), &twist_inv)?, - y_over_twist: q.y.mul_by_constant(cs.ns(|| "y over twist"), &twist_inv)?, + x_over_twist: &q.x * twist_inv, + y_over_twist: &q.y * twist_inv, double_coefficients: vec![], addition_coefficients: vec![], }; - let fp2_one = Fp2G::

::one(cs.ns(|| "one"))?; - let mut r = G2ProjectiveExtendedGadget { + let mut r = G2ProjectiveExtendedVar { x: q.x.clone(), y: q.y.clone(), - z: fp2_one.clone(), - t: fp2_one, + z: Fp2G::

::one(), + t: Fp2G::

::one(), }; for (idx, value) in P::ATE_LOOP_COUNT.iter().rev().enumerate() { @@ -362,24 +261,15 @@ impl G2PreparedGadget

{ tmp >>= 1; } - let mut cs = cs.ns(|| format!("ate loop iteration {}", idx)); - - for (j, bit) in v.iter().rev().enumerate() { - let (r2, coeff) = PairingGadget::

::doubling_step_for_flipped_miller_loop( - cs.ns(|| format!("doubling step {}", j)), - &r, - )?; + for bit in v.iter().rev() { + let (r2, coeff) = PairingVar::

::doubling_step_for_flipped_miller_loop(&r)?; g2p.double_coefficients.push(coeff); r = r2; if *bit { - let (r2, coeff) = - PairingGadget::

::mixed_addition_step_for_flipped_miller_loop( - cs.ns(|| format!("mixed addition step {}", j)), - &q.x, - &q.y, - &r, - )?; + let (r2, coeff) = PairingVar::

::mixed_addition_step_for_flipped_miller_loop( + &q.x, &q.y, &r, + )?; g2p.addition_coefficients.push(coeff); r = r2; } @@ -389,17 +279,14 @@ impl G2PreparedGadget

{ } if P::ATE_IS_LOOP_COUNT_NEG { - let rz_inv = r.z.inverse(cs.ns(|| "inverse r.z"))?; - let rz2_inv = rz_inv.square(cs.ns(|| "rz_inv^2"))?; - let rz3_inv = rz_inv.mul(cs.ns(|| "rz_inv * rz_inv^2"), &rz2_inv)?; + let rz_inv = r.z.inverse()?; + let rz2_inv = rz_inv.square()?; + let rz3_inv = &rz_inv * &rz2_inv; - let minus_r_affine_x = r.x.mul(cs.ns(|| "r.x * rz2_inv"), &rz2_inv)?; - let minus_r_affine_y = - r.y.negate(cs.ns(|| "-r.y"))? - .mul(cs.ns(|| "-r.y * rz3_inv"), &rz3_inv)?; + let minus_r_affine_x = &r.x * &rz2_inv; + let minus_r_affine_y = r.y.negate()? * &rz3_inv; - let add_result = PairingGadget::

::mixed_addition_step_for_flipped_miller_loop( - cs.ns(|| "mixed_addition step"), + let add_result = PairingVar::

::mixed_addition_step_for_flipped_miller_loop( &minus_r_affine_x, &minus_r_affine_y, &r, @@ -413,73 +300,45 @@ impl G2PreparedGadget

{ #[derive(Derivative)] #[derivative(Clone(bound = "P: MNT4Parameters"), Debug(bound = "P: MNT4Parameters"))] -pub struct AteDoubleCoefficientsGadget { - pub c_h: Fp2Gadget, - pub c_4c: Fp2Gadget, - pub c_j: Fp2Gadget, - pub c_l: Fp2Gadget, +pub struct AteDoubleCoefficientsVar { + pub c_h: Fp2Var, + pub c_4c: Fp2Var, + pub c_j: Fp2Var, + pub c_l: Fp2Var, } -impl AllocGadget, P::Fp> - for AteDoubleCoefficientsGadget

-{ - fn alloc_constant>( - mut cs: CS, - t: T, - ) -> Result - where - T: Borrow>, - { - let obj = t.borrow(); - let c_h = - Fp2Gadget::::alloc_constant(&mut cs.ns(|| "alloc_c_h"), &obj.c_h)?; - let c_4c = Fp2Gadget::::alloc_constant( - &mut cs.ns(|| "alloc_c_4c"), - &obj.c_4c, - )?; - let c_j = - Fp2Gadget::::alloc_constant(&mut cs.ns(|| "alloc_c_j"), &obj.c_j)?; - let c_l = - Fp2Gadget::::alloc_constant(&mut cs.ns(|| "alloc_c_l"), &obj.c_l)?; +impl AllocVar, P::Fp> for AteDoubleCoefficientsVar

{ + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); - Ok(AteDoubleCoefficientsGadget { + let c_prep = f().map(|c| c.borrow().clone()); + let c = c_prep.as_ref().map_err(|e| *e); + + let c_h = Fp2Var::new_variable(cs.ns("c_h"), || c.map(|c| c.c_h), mode)?; + let c_4c = Fp2Var::new_variable(cs.ns("c_4c"), || c.map(|c| c.c_4c), mode)?; + let c_j = Fp2Var::new_variable(cs.ns("c_j"), || c.map(|c| c.c_j), mode)?; + let c_l = Fp2Var::new_variable(cs.ns("c_l"), || c.map(|c| c.c_l), mode)?; + Ok(Self { c_h, c_4c, c_j, c_l, }) } - - fn alloc>(_cs: CS, _f: F) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - todo!() - } - - fn alloc_input>( - _cs: CS, - _f: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - todo!() - } } -impl ToBytesGadget for AteDoubleCoefficientsGadget

{ +impl ToBytesGadget for AteDoubleCoefficientsVar

{ #[inline] - fn to_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c_h = self.c_h.to_bytes(&mut cs.ns(|| "c_h to bytes"))?; - let mut c_4c = self.c_4c.to_bytes(&mut cs.ns(|| "c_4c to bytes"))?; - let mut c_j = self.c_j.to_bytes(&mut cs.ns(|| "c_j to bytes"))?; - let mut c_l = self.c_l.to_bytes(&mut cs.ns(|| "c_l to bytes"))?; + fn to_bytes(&self) -> Result>, SynthesisError> { + let mut c_h = self.c_h.to_bytes()?; + let mut c_4c = self.c_4c.to_bytes()?; + let mut c_j = self.c_j.to_bytes()?; + let mut c_l = self.c_l.to_bytes()?; c_h.append(&mut c_4c); c_h.append(&mut c_j); @@ -487,22 +346,11 @@ impl ToBytesGadget for AteDoubleCoefficientsGadget

Ok(c_h) } - fn to_non_unique_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c_h = self - .c_h - .to_non_unique_bytes(&mut cs.ns(|| "c_h to bytes"))?; - let mut c_4c = self - .c_4c - .to_non_unique_bytes(&mut cs.ns(|| "c_4c to bytes"))?; - let mut c_j = self - .c_j - .to_non_unique_bytes(&mut cs.ns(|| "c_j to bytes"))?; - let mut c_l = self - .c_l - .to_non_unique_bytes(&mut cs.ns(|| "c_l to bytes"))?; + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + let mut c_h = self.c_h.to_non_unique_bytes()?; + let mut c_4c = self.c_4c.to_non_unique_bytes()?; + let mut c_j = self.c_j.to_non_unique_bytes()?; + let mut c_l = self.c_l.to_non_unique_bytes()?; c_h.append(&mut c_4c); c_h.append(&mut c_j); @@ -511,115 +359,79 @@ impl ToBytesGadget for AteDoubleCoefficientsGadget

} } -impl AteDoubleCoefficientsGadget

{ - pub fn get_value(&self) -> Option> { - match ( - self.c_h.get_value(), - self.c_4c.get_value(), - self.c_j.get_value(), - self.c_l.get_value(), - ) { - (Some(c_h), Some(c_4c), Some(c_j), Some(c_l)) => Some(AteDoubleCoefficients { - c_h, - c_4c, - c_j, - c_l, - }), - _ => None, - } +impl AteDoubleCoefficientsVar

{ + pub fn value(&self) -> Result, SynthesisError> { + let (c_h, c_4c, c_j, c_l) = ( + self.c_l.value()?, + self.c_4c.value()?, + self.c_j.value()?, + self.c_l.value()?, + ); + Ok(AteDoubleCoefficients { + c_h, + c_4c, + c_j, + c_l, + }) } } #[derive(Derivative)] #[derivative(Clone(bound = "P: MNT4Parameters"), Debug(bound = "P: MNT4Parameters"))] -pub struct AteAdditionCoefficientsGadget { - pub c_l1: Fp2Gadget, - pub c_rz: Fp2Gadget, +pub struct AteAdditionCoefficientsVar { + pub c_l1: Fp2Var, + pub c_rz: Fp2Var, } -impl AllocGadget, P::Fp> - for AteAdditionCoefficientsGadget

+impl AllocVar, P::Fp> + for AteAdditionCoefficientsVar

{ - fn alloc_constant>( - mut cs: CS, - t: T, - ) -> Result - where - T: Borrow>, - { - let obj = t.borrow(); - let c_l1 = Fp2Gadget::::alloc_constant( - &mut cs.ns(|| "alloc_c_l1"), - &obj.c_l1, - )?; - let c_rz = Fp2Gadget::::alloc_constant( - &mut cs.ns(|| "alloc_c_rz"), - &obj.c_rz, - )?; - Ok(AteAdditionCoefficientsGadget { c_l1, c_rz }) - } + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); - fn alloc>(_cs: CS, _f: F) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - todo!() - } + let c_prep = f().map(|c| c.borrow().clone()); + let c = c_prep.as_ref().map_err(|e| *e); - fn alloc_input>( - _cs: CS, - _f: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - todo!() + let c_l1 = Fp2Var::new_variable(cs.ns("c_l1"), || c.map(|c| c.c_l1), mode)?; + let c_rz = Fp2Var::new_variable(cs.ns("c_rz"), || c.map(|c| c.c_rz), mode)?; + Ok(Self { c_l1, c_rz }) } } -impl ToBytesGadget for AteAdditionCoefficientsGadget

{ +impl ToBytesGadget for AteAdditionCoefficientsVar

{ #[inline] - fn to_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c_l1 = self.c_l1.to_bytes(&mut cs.ns(|| "c_l1 to bytes"))?; - let mut c_rz = self.c_rz.to_bytes(&mut cs.ns(|| "c_rz to bytes"))?; + fn to_bytes(&self) -> Result>, SynthesisError> { + let mut c_l1 = self.c_l1.to_bytes()?; + let mut c_rz = self.c_rz.to_bytes()?; c_l1.append(&mut c_rz); Ok(c_l1) } - fn to_non_unique_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c_l1 = self - .c_l1 - .to_non_unique_bytes(&mut cs.ns(|| "c_l1 to bytes"))?; - let mut c_rz = self - .c_rz - .to_non_unique_bytes(&mut cs.ns(|| "c_rz to bytes"))?; + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + let mut c_l1 = self.c_l1.to_non_unique_bytes()?; + let mut c_rz = self.c_rz.to_non_unique_bytes()?; c_l1.append(&mut c_rz); Ok(c_l1) } } -impl AteAdditionCoefficientsGadget

{ - pub fn get_value(&self) -> Option> { - match (self.c_l1.get_value(), self.c_rz.get_value()) { - (Some(c_l1), Some(c_rz)) => Some(AteAdditionCoefficients { c_l1, c_rz }), - _ => None, - } +impl AteAdditionCoefficientsVar

{ + pub fn value(&self) -> Result, SynthesisError> { + let (c_l1, c_rz) = (self.c_l1.value()?, self.c_rz.value()?); + Ok(AteAdditionCoefficients { c_l1, c_rz }) } } -pub struct G2ProjectiveExtendedGadget { - pub x: Fp2Gadget, - pub y: Fp2Gadget, - pub z: Fp2Gadget, - pub t: Fp2Gadget, +pub struct G2ProjectiveExtendedVar { + pub x: Fp2Var, + pub y: Fp2Var, + pub z: Fp2Var, + pub t: Fp2Var, } diff --git a/r1cs-std/src/groups/curves/short_weierstrass/mnt6/mod.rs b/r1cs-std/src/groups/curves/short_weierstrass/mnt6/mod.rs index 7318c13..348bb7b 100644 --- a/r1cs-std/src/groups/curves/short_weierstrass/mnt6/mod.rs +++ b/r1cs-std/src/groups/curves/short_weierstrass/mnt6/mod.rs @@ -5,135 +5,91 @@ use algebra::{ }, Field, }; -use core::borrow::Borrow; -use r1cs_core::{ConstraintSystem, SynthesisError}; +use r1cs_core::{Namespace, SynthesisError}; use crate::{ - fields::{fp::FpGadget, fp3::Fp3Gadget, FieldGadget}, - groups::curves::short_weierstrass::AffineGadget, - pairing::mnt6::PairingGadget, + fields::{fp::FpVar, fp3::Fp3Var, FieldVar}, + groups::curves::short_weierstrass::ProjectiveVar, + pairing::mnt6::PairingVar, prelude::*, Vec, }; +use core::borrow::Borrow; -pub type G1Gadget

= AffineGadget< -

::G1Parameters, -

::Fp, - FpGadget<

::Fp>, ->; +pub type G1Var

= + ProjectiveVar<

::G1Parameters, FpVar<

::Fp>>; -pub type G2Gadget

= - AffineGadget<

::G2Parameters,

::Fp, Fp3G

>; +pub type G2Var

= ProjectiveVar<

::G2Parameters, Fp3G

>; #[derive(Derivative)] #[derivative(Clone(bound = "P: MNT6Parameters"), Debug(bound = "P: MNT6Parameters"))] -pub struct G1PreparedGadget { - pub x: FpGadget, - pub y: FpGadget, - pub x_twist: Fp3Gadget, - pub y_twist: Fp3Gadget, +pub struct G1PreparedVar { + pub x: FpVar, + pub y: FpVar, + pub x_twist: Fp3Var, + pub y_twist: Fp3Var, } -impl AllocGadget, P::Fp> for G1PreparedGadget

{ - fn alloc_constant>( - mut cs: CS, - t: T, - ) -> Result - where - T: Borrow>, - { - let obj = t.borrow(); - - let x_gadget = FpGadget::::alloc_constant(&mut cs.ns(|| "x"), &obj.x)?; - let y_gadget = FpGadget::::alloc_constant(&mut cs.ns(|| "y"), &obj.y)?; - let x_twist_gadget = Fp3Gadget::::alloc_constant( - &mut cs.ns(|| "x_twist"), - &obj.x_twist, - )?; - let y_twist_gadget = Fp3Gadget::::alloc_constant( - &mut cs.ns(|| "y_twist"), - &obj.y_twist, - )?; - - Ok(Self { - x: x_gadget, - y: y_gadget, - x_twist: x_twist_gadget, - y_twist: y_twist_gadget, +impl G1PreparedVar

{ + pub fn value(&self) -> Result, SynthesisError> { + let x = self.x.value()?; + let y = self.y.value()?; + let x_twist = self.x_twist.value()?; + let y_twist = self.y_twist.value()?; + Ok(G1Prepared { + x, + y, + x_twist, + y_twist, }) } - fn alloc>(_cs: CS, _f: F) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - todo!() - } - - fn alloc_input>( - _cs: CS, - _f: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - todo!() + pub fn from_group_var(q: &G1Var

) -> Result { + let q = q.to_affine()?; + let zero = FpVar::::zero(); + let x_twist = Fp3Var::new(q.x.clone(), zero.clone(), zero.clone()) * P::TWIST; + let y_twist = Fp3Var::new(q.y.clone(), zero.clone(), zero.clone()) * P::TWIST; + let result = G1PreparedVar { + x: q.x.clone(), + y: q.y.clone(), + x_twist, + y_twist, + }; + Ok(result) } } -impl G1PreparedGadget

{ - pub fn get_value(&self) -> Option> { - match ( - self.x.get_value(), - self.y.get_value(), - self.x_twist.get_value(), - self.y_twist.get_value(), - ) { - (Some(x), Some(y), Some(x_twist), Some(y_twist)) => Some(G1Prepared { - x, - y, - x_twist, - y_twist, - }), - _ => None, - } - } - - pub fn from_affine>( - mut cs: CS, - q: &G1Gadget

, +impl AllocVar, P::Fp> for G1PreparedVar

{ + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, ) -> Result { - let x_twist = Fp3Gadget::new( - q.x.mul_by_constant(cs.ns(|| "g1.x * twist.c0"), &P::TWIST.c0)?, - q.x.mul_by_constant(cs.ns(|| "g1.x * twist.c1"), &P::TWIST.c1)?, - q.x.mul_by_constant(cs.ns(|| "g1.x * twist.c2"), &P::TWIST.c2)?, - ); - let y_twist = Fp3Gadget::new( - q.y.mul_by_constant(cs.ns(|| "g1.y * twist.c0"), &P::TWIST.c0)?, - q.y.mul_by_constant(cs.ns(|| "g1.y * twist.c1"), &P::TWIST.c1)?, - q.y.mul_by_constant(cs.ns(|| "g1.y * twist.c2"), &P::TWIST.c2)?, - ); - Ok(G1PreparedGadget { - x: q.x.clone(), - y: q.y.clone(), + let ns = cs.into(); + let cs = ns.cs(); + + let g1_prep = f().map(|b| *b.borrow()); + + let x = FpVar::new_variable(cs.ns("x"), || g1_prep.map(|g| g.x), mode)?; + let y = FpVar::new_variable(cs.ns("y"), || g1_prep.map(|g| g.y), mode)?; + let x_twist = Fp3Var::new_variable(cs.ns("x_twist"), || g1_prep.map(|g| g.x_twist), mode)?; + let y_twist = Fp3Var::new_variable(cs.ns("y_twist"), || g1_prep.map(|g| g.y_twist), mode)?; + Ok(Self { + x, + y, x_twist, y_twist, }) } } -impl ToBytesGadget for G1PreparedGadget

{ +impl ToBytesGadget for G1PreparedVar

{ #[inline] - fn to_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut x = self.x.to_bytes(&mut cs.ns(|| "x to bytes"))?; - let mut y = self.y.to_bytes(&mut cs.ns(|| "y to bytes"))?; - let mut x_twist = self.x_twist.to_bytes(&mut cs.ns(|| "x_twist to bytes"))?; - let mut y_twist = self.y_twist.to_bytes(&mut cs.ns(|| "y_twist to bytes"))?; + fn to_bytes(&self) -> Result>, SynthesisError> { + let mut x = self.x.to_bytes()?; + let mut y = self.y.to_bytes()?; + let mut x_twist = self.x_twist.to_bytes()?; + let mut y_twist = self.y_twist.to_bytes()?; x.append(&mut y); x.append(&mut x_twist); @@ -141,18 +97,11 @@ impl ToBytesGadget for G1PreparedGadget

{ Ok(x) } - fn to_non_unique_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut x = self.x.to_non_unique_bytes(&mut cs.ns(|| "x to bytes"))?; - let mut y = self.y.to_non_unique_bytes(&mut cs.ns(|| "y to bytes"))?; - let mut x_twist = self - .x_twist - .to_non_unique_bytes(&mut cs.ns(|| "x_twist to bytes"))?; - let mut y_twist = self - .y_twist - .to_non_unique_bytes(&mut cs.ns(|| "y_twist to bytes"))?; + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + let mut x = self.x.to_non_unique_bytes()?; + let mut y = self.y.to_non_unique_bytes()?; + let mut x_twist = self.x_twist.to_non_unique_bytes()?; + let mut y_twist = self.y_twist.to_non_unique_bytes()?; x.append(&mut y); x.append(&mut x_twist); @@ -161,203 +110,142 @@ impl ToBytesGadget for G1PreparedGadget

{ } } -type Fp3G

= Fp3Gadget<

::Fp3Params,

::Fp>; +type Fp3G

= Fp3Var<

::Fp3Params>; #[derive(Derivative)] #[derivative(Clone(bound = "P: MNT6Parameters"), Debug(bound = "P: MNT6Parameters"))] -pub struct G2PreparedGadget { - pub x: Fp3Gadget, - pub y: Fp3Gadget, - pub x_over_twist: Fp3Gadget, - pub y_over_twist: Fp3Gadget, - pub double_coefficients: Vec>, - pub addition_coefficients: Vec>, +pub struct G2PreparedVar { + pub x: Fp3Var, + pub y: Fp3Var, + pub x_over_twist: Fp3Var, + pub y_over_twist: Fp3Var, + pub double_coefficients: Vec>, + pub addition_coefficients: Vec>, } -impl AllocGadget, P::Fp> for G2PreparedGadget

{ - fn alloc_constant>( - mut cs: CS, - t: T, - ) -> Result - where - T: Borrow>, - { - let obj = t.borrow(); - - let x_gadget = - Fp3Gadget::::alloc_constant(&mut cs.ns(|| "x"), &obj.x)?; - let y_gadget = - Fp3Gadget::::alloc_constant(&mut cs.ns(|| "y"), &obj.y)?; - - let x_over_twist_gadget = Fp3Gadget::::alloc_constant( - &mut cs.ns(|| "x_over_twist"), - &obj.x_over_twist, +impl AllocVar, P::Fp> for G2PreparedVar

{ + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + + let g2_prep = f().map(|b| b.borrow().clone()); + let g2 = g2_prep.as_ref().map_err(|e| *e); + + let x = Fp3Var::new_variable(cs.ns("x"), || g2.map(|g| g.x), mode)?; + let y = Fp3Var::new_variable(cs.ns("y"), || g2.map(|g| g.y), mode)?; + let x_over_twist = + Fp3Var::new_variable(cs.ns("x_over_twist"), || g2.map(|g| g.x_over_twist), mode)?; + let y_over_twist = + Fp3Var::new_variable(cs.ns("y_over_twist"), || g2.map(|g| g.y_over_twist), mode)?; + let double_coefficients = Vec::new_variable( + cs.ns("double coeffs"), + || g2.map(|g| g.double_coefficients.clone()), + mode, )?; - let y_over_twist_gadget = Fp3Gadget::::alloc_constant( - &mut cs.ns(|| "y_over_twist"), - &obj.y_over_twist, + let addition_coefficients = Vec::new_variable( + cs.ns("add coeffs"), + || g2.map(|g| g.addition_coefficients.clone()), + mode, )?; - - let mut double_coefficients_gadget = Vec::>::new(); - for (i, double_coefficient) in obj.double_coefficients.iter().enumerate() { - double_coefficients_gadget.push(AteDoubleCoefficientsGadget::

::alloc_constant( - &mut cs.ns(|| format!("double_coefficient#{}", i)), - double_coefficient, - )?); - } - - let mut addition_coefficients_gadget = Vec::>::new(); - for (i, addition_coefficient) in obj.addition_coefficients.iter().enumerate() { - addition_coefficients_gadget.push(AteAdditionCoefficientsGadget::

::alloc_constant( - &mut cs.ns(|| format!("addition_coefficient#{}", i)), - addition_coefficient, - )?); - } - Ok(Self { - x: x_gadget, - y: y_gadget, - x_over_twist: x_over_twist_gadget, - y_over_twist: y_over_twist_gadget, - double_coefficients: double_coefficients_gadget, - addition_coefficients: addition_coefficients_gadget, + x, + y, + x_over_twist, + y_over_twist, + double_coefficients, + addition_coefficients, }) } - - fn alloc>(_cs: CS, _f: F) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - todo!() - } - - fn alloc_input>( - _cs: CS, - _f: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - todo!() - } } -impl ToBytesGadget for G2PreparedGadget

{ +impl ToBytesGadget for G2PreparedVar

{ #[inline] - fn to_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut x = self.x.to_bytes(&mut cs.ns(|| "x to bytes"))?; - let mut y = self.y.to_bytes(&mut cs.ns(|| "y to bytes"))?; - let mut x_over_twist = self - .x_over_twist - .to_bytes(&mut cs.ns(|| "x_over_twist to bytes"))?; - let mut y_over_twist = self - .y_over_twist - .to_bytes(&mut cs.ns(|| "y_over_twist to bytes"))?; + fn to_bytes(&self) -> Result>, SynthesisError> { + let mut x = self.x.to_bytes()?; + let mut y = self.y.to_bytes()?; + let mut x_over_twist = self.x_over_twist.to_bytes()?; + let mut y_over_twist = self.y_over_twist.to_bytes()?; x.append(&mut y); x.append(&mut x_over_twist); x.append(&mut y_over_twist); - for (i, coeff) in self.double_coefficients.iter().enumerate() { - x.extend_from_slice(&coeff.to_bytes(cs.ns(|| format!("double_coefficients {}", i)))?); + for coeff in self.double_coefficients.iter() { + x.extend_from_slice(&coeff.to_bytes()?); } - for (i, coeff) in self.addition_coefficients.iter().enumerate() { - x.extend_from_slice(&coeff.to_bytes(cs.ns(|| format!("addition_coefficients {}", i)))?); + for coeff in self.addition_coefficients.iter() { + x.extend_from_slice(&coeff.to_bytes()?); } Ok(x) } - fn to_non_unique_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut x = self.x.to_non_unique_bytes(&mut cs.ns(|| "x to bytes"))?; - let mut y = self.y.to_non_unique_bytes(&mut cs.ns(|| "y to bytes"))?; - let mut x_over_twist = self - .x_over_twist - .to_non_unique_bytes(&mut cs.ns(|| "x_over_twist to bytes"))?; - let mut y_over_twist = self - .y_over_twist - .to_non_unique_bytes(&mut cs.ns(|| "y_over_twist to bytes"))?; + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + let mut x = self.x.to_non_unique_bytes()?; + let mut y = self.y.to_non_unique_bytes()?; + let mut x_over_twist = self.x_over_twist.to_non_unique_bytes()?; + let mut y_over_twist = self.y_over_twist.to_non_unique_bytes()?; x.append(&mut y); x.append(&mut x_over_twist); x.append(&mut y_over_twist); - for (i, coeff) in self.double_coefficients.iter().enumerate() { - x.extend_from_slice( - &coeff.to_non_unique_bytes(cs.ns(|| format!("double_coefficients {}", i)))?, - ); + for coeff in self.double_coefficients.iter() { + x.extend_from_slice(&coeff.to_non_unique_bytes()?); } - for (i, coeff) in self.addition_coefficients.iter().enumerate() { - x.extend_from_slice( - &coeff.to_non_unique_bytes(cs.ns(|| format!("addition_coefficients {}", i)))?, - ); + for coeff in self.addition_coefficients.iter() { + x.extend_from_slice(&coeff.to_non_unique_bytes()?); } Ok(x) } } -impl G2PreparedGadget

{ - pub fn get_value(&self) -> Option> { - match ( - self.x.get_value(), - self.y.get_value(), - self.x_over_twist.get_value(), - self.y_over_twist.get_value(), - self.double_coefficients - .iter() - .map(|coeff| coeff.get_value()) - .collect::>>>(), - self.addition_coefficients - .iter() - .map(|coeff| coeff.get_value()) - .collect::>>>(), - ) { - ( - Some(x), - Some(y), - Some(x_over_twist), - Some(y_over_twist), - Some(double_coefficients), - Some(addition_coefficients), - ) => Some(G2Prepared { - x, - y, - x_over_twist, - y_over_twist, - double_coefficients, - addition_coefficients, - }), - _ => None, - } +impl G2PreparedVar

{ + pub fn value(&self) -> Result, SynthesisError> { + let x = self.x.value()?; + let y = self.y.value()?; + let x_over_twist = self.x_over_twist.value()?; + let y_over_twist = self.y_over_twist.value()?; + let double_coefficients = self + .double_coefficients + .iter() + .map(|coeff| coeff.value()) + .collect::, SynthesisError>>()?; + let addition_coefficients = self + .addition_coefficients + .iter() + .map(|coeff| coeff.value()) + .collect::, SynthesisError>>()?; + Ok(G2Prepared { + x, + y, + x_over_twist, + y_over_twist, + double_coefficients, + addition_coefficients, + }) } - pub fn from_affine>( - mut cs: CS, - q: &G2Gadget

, - ) -> Result { + pub fn from_group_var(q: &G2Var

) -> Result { + let q = q.to_affine()?; let twist_inv = P::TWIST.inverse().unwrap(); - let mut g2p = G2PreparedGadget { + let mut g2p = G2PreparedVar { x: q.x.clone(), y: q.y.clone(), - x_over_twist: q.x.mul_by_constant(cs.ns(|| "x over twist"), &twist_inv)?, - y_over_twist: q.y.mul_by_constant(cs.ns(|| "y over twist"), &twist_inv)?, + x_over_twist: &q.x * twist_inv, + y_over_twist: &q.y * twist_inv, double_coefficients: vec![], addition_coefficients: vec![], }; - let fp2_one = Fp3G::

::one(cs.ns(|| "one"))?; - let mut r = G2ProjectiveExtendedGadget { + let mut r = G2ProjectiveExtendedVar { x: q.x.clone(), y: q.y.clone(), - z: fp2_one.clone(), - t: fp2_one, + z: Fp3G::

::one(), + t: Fp3G::

::one(), }; for (idx, value) in P::ATE_LOOP_COUNT.iter().rev().enumerate() { @@ -372,24 +260,15 @@ impl G2PreparedGadget

{ tmp >>= 1; } - let mut cs = cs.ns(|| format!("ate loop iteration {}", idx)); - - for (j, bit) in v.iter().rev().enumerate() { - let (r2, coeff) = PairingGadget::

::doubling_step_for_flipped_miller_loop( - cs.ns(|| format!("doubling step {}", j)), - &r, - )?; + for bit in v.iter().rev() { + let (r2, coeff) = PairingVar::

::doubling_step_for_flipped_miller_loop(&r)?; g2p.double_coefficients.push(coeff); r = r2; if *bit { - let (r2, coeff) = - PairingGadget::

::mixed_addition_step_for_flipped_miller_loop( - cs.ns(|| format!("mixed addition step {}", j)), - &q.x, - &q.y, - &r, - )?; + let (r2, coeff) = PairingVar::

::mixed_addition_step_for_flipped_miller_loop( + &q.x, &q.y, &r, + )?; g2p.addition_coefficients.push(coeff); r = r2; } @@ -399,17 +278,14 @@ impl G2PreparedGadget

{ } if P::ATE_IS_LOOP_COUNT_NEG { - let rz_inv = r.z.inverse(cs.ns(|| "inverse r.z"))?; - let rz2_inv = rz_inv.square(cs.ns(|| "rz_inv^2"))?; - let rz3_inv = rz_inv.mul(cs.ns(|| "rz_inv * rz_inv^2"), &rz2_inv)?; + let rz_inv = r.z.inverse()?; + let rz2_inv = rz_inv.square()?; + let rz3_inv = &rz_inv * &rz2_inv; - let minus_r_affine_x = r.x.mul(cs.ns(|| "r.x * rz2_inv"), &rz2_inv)?; - let minus_r_affine_y = - r.y.negate(cs.ns(|| "-r.y"))? - .mul(cs.ns(|| "-r.y * rz3_inv"), &rz3_inv)?; + let minus_r_affine_x = &r.x * &rz2_inv; + let minus_r_affine_y = r.y.negate()? * &rz3_inv; - let add_result = PairingGadget::

::mixed_addition_step_for_flipped_miller_loop( - cs.ns(|| "mixed_addition step"), + let add_result = PairingVar::

::mixed_addition_step_for_flipped_miller_loop( &minus_r_affine_x, &minus_r_affine_y, &r, @@ -423,72 +299,45 @@ impl G2PreparedGadget

{ #[derive(Derivative)] #[derivative(Clone(bound = "P: MNT6Parameters"), Debug(bound = "P: MNT6Parameters"))] -pub struct AteDoubleCoefficientsGadget { - pub c_h: Fp3Gadget, - pub c_4c: Fp3Gadget, - pub c_j: Fp3Gadget, - pub c_l: Fp3Gadget, +pub struct AteDoubleCoefficientsVar { + pub c_h: Fp3Var, + pub c_4c: Fp3Var, + pub c_j: Fp3Var, + pub c_l: Fp3Var, } -impl AllocGadget, P::Fp> - for AteDoubleCoefficientsGadget

-{ - fn alloc_constant>( - mut cs: CS, - t: T, - ) -> Result - where - T: Borrow>, - { - let obj = t.borrow(); - - let c_h_gadget = - Fp3Gadget::::alloc_constant(&mut cs.ns(|| "c_h"), &obj.c_h)?; - let c_4c_gadget = - Fp3Gadget::::alloc_constant(&mut cs.ns(|| "c_4c"), &obj.c_4c)?; - let c_j_gadget = - Fp3Gadget::::alloc_constant(&mut cs.ns(|| "c_j"), &obj.c_j)?; - let c_l_gadget = - Fp3Gadget::::alloc_constant(&mut cs.ns(|| "c_l"), &obj.c_l)?; +impl AllocVar, P::Fp> for AteDoubleCoefficientsVar

{ + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + + let c_prep = f().map(|c| c.borrow().clone()); + let c = c_prep.as_ref().map_err(|e| *e); + let c_h = Fp3Var::new_variable(cs.ns("c_h"), || c.map(|c| c.c_h), mode)?; + let c_4c = Fp3Var::new_variable(cs.ns("c_4c"), || c.map(|c| c.c_4c), mode)?; + let c_j = Fp3Var::new_variable(cs.ns("c_j"), || c.map(|c| c.c_j), mode)?; + let c_l = Fp3Var::new_variable(cs.ns("c_l"), || c.map(|c| c.c_l), mode)?; Ok(Self { - c_h: c_h_gadget, - c_4c: c_4c_gadget, - c_j: c_j_gadget, - c_l: c_l_gadget, + c_h, + c_4c, + c_j, + c_l, }) } - - fn alloc>(_cs: CS, _f: F) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - todo!() - } - - fn alloc_input>( - _cs: CS, - _f: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - todo!() - } } -impl ToBytesGadget for AteDoubleCoefficientsGadget

{ +impl ToBytesGadget for AteDoubleCoefficientsVar

{ #[inline] - fn to_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c_h = self.c_h.to_bytes(&mut cs.ns(|| "c_h to bytes"))?; - let mut c_4c = self.c_4c.to_bytes(&mut cs.ns(|| "c_4c to bytes"))?; - let mut c_j = self.c_j.to_bytes(&mut cs.ns(|| "c_j to bytes"))?; - let mut c_l = self.c_l.to_bytes(&mut cs.ns(|| "c_l to bytes"))?; + fn to_bytes(&self) -> Result>, SynthesisError> { + let mut c_h = self.c_h.to_bytes()?; + let mut c_4c = self.c_4c.to_bytes()?; + let mut c_j = self.c_j.to_bytes()?; + let mut c_l = self.c_l.to_bytes()?; c_h.append(&mut c_4c); c_h.append(&mut c_j); @@ -496,22 +345,11 @@ impl ToBytesGadget for AteDoubleCoefficientsGadget

Ok(c_h) } - fn to_non_unique_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c_h = self - .c_h - .to_non_unique_bytes(&mut cs.ns(|| "c_h to bytes"))?; - let mut c_4c = self - .c_4c - .to_non_unique_bytes(&mut cs.ns(|| "c_4c to bytes"))?; - let mut c_j = self - .c_j - .to_non_unique_bytes(&mut cs.ns(|| "c_j to bytes"))?; - let mut c_l = self - .c_l - .to_non_unique_bytes(&mut cs.ns(|| "c_l to bytes"))?; + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + let mut c_h = self.c_h.to_non_unique_bytes()?; + let mut c_4c = self.c_4c.to_non_unique_bytes()?; + let mut c_j = self.c_j.to_non_unique_bytes()?; + let mut c_l = self.c_l.to_non_unique_bytes()?; c_h.append(&mut c_4c); c_h.append(&mut c_j); @@ -520,111 +358,78 @@ impl ToBytesGadget for AteDoubleCoefficientsGadget

} } -impl AteDoubleCoefficientsGadget

{ - pub fn get_value(&self) -> Option> { - match ( - self.c_h.get_value(), - self.c_4c.get_value(), - self.c_j.get_value(), - self.c_l.get_value(), - ) { - (Some(c_h), Some(c_4c), Some(c_j), Some(c_l)) => Some(AteDoubleCoefficients { - c_h, - c_4c, - c_j, - c_l, - }), - _ => None, - } +impl AteDoubleCoefficientsVar

{ + pub fn value(&self) -> Result, SynthesisError> { + let c_h = self.c_h.value()?; + let c_4c = self.c_4c.value()?; + let c_j = self.c_j.value()?; + let c_l = self.c_l.value()?; + Ok(AteDoubleCoefficients { + c_h, + c_4c, + c_j, + c_l, + }) } } #[derive(Derivative)] #[derivative(Clone(bound = "P: MNT6Parameters"), Debug(bound = "P: MNT6Parameters"))] -pub struct AteAdditionCoefficientsGadget { - pub c_l1: Fp3Gadget, - pub c_rz: Fp3Gadget, +pub struct AteAdditionCoefficientsVar { + pub c_l1: Fp3Var, + pub c_rz: Fp3Var, } -impl AllocGadget, P::Fp> - for AteAdditionCoefficientsGadget

+impl AllocVar, P::Fp> + for AteAdditionCoefficientsVar

{ - fn alloc_constant>( - mut cs: CS, - t: T, - ) -> Result - where - T: Borrow>, - { - let t = t.borrow(); - - let c_l1 = Fp3Gadget::alloc_constant(&mut cs.ns(|| "c_l1"), &t.c_l1)?; - let c_rz = Fp3Gadget::alloc_constant(&mut cs.ns(|| "c_rz"), &t.c_rz)?; - - Ok(Self { c_l1, c_rz }) - } + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); - fn alloc>(_cs: CS, _f: F) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - todo!() - } + let c_prep = f().map(|c| c.borrow().clone()); + let c = c_prep.as_ref().map_err(|e| *e); - fn alloc_input>( - _cs: CS, - _f: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - todo!() + let c_l1 = Fp3Var::new_variable(cs.ns("c_l1"), || c.map(|c| c.c_l1), mode)?; + let c_rz = Fp3Var::new_variable(cs.ns("c_rz"), || c.map(|c| c.c_rz), mode)?; + Ok(Self { c_l1, c_rz }) } } -impl ToBytesGadget for AteAdditionCoefficientsGadget

{ +impl ToBytesGadget for AteAdditionCoefficientsVar

{ #[inline] - fn to_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c_l1 = self.c_l1.to_bytes(&mut cs.ns(|| "c_l1 to bytes"))?; - let mut c_rz = self.c_rz.to_bytes(&mut cs.ns(|| "c_rz to bytes"))?; + fn to_bytes(&self) -> Result>, SynthesisError> { + let mut c_l1 = self.c_l1.to_bytes()?; + let mut c_rz = self.c_rz.to_bytes()?; c_l1.append(&mut c_rz); Ok(c_l1) } - fn to_non_unique_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c_l1 = self - .c_l1 - .to_non_unique_bytes(&mut cs.ns(|| "c_l1 to bytes"))?; - let mut c_rz = self - .c_rz - .to_non_unique_bytes(&mut cs.ns(|| "c_rz to bytes"))?; + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + let mut c_l1 = self.c_l1.to_non_unique_bytes()?; + let mut c_rz = self.c_rz.to_non_unique_bytes()?; c_l1.append(&mut c_rz); Ok(c_l1) } } -impl AteAdditionCoefficientsGadget

{ - pub fn get_value(&self) -> Option> { - match (self.c_l1.get_value(), self.c_rz.get_value()) { - (Some(c_l1), Some(c_rz)) => Some(AteAdditionCoefficients { c_l1, c_rz }), - _ => None, - } +impl AteAdditionCoefficientsVar

{ + pub fn value(&self) -> Result, SynthesisError> { + let c_l1 = self.c_l1.value()?; + let c_rz = self.c_rz.value()?; + Ok(AteAdditionCoefficients { c_l1, c_rz }) } } -pub struct G2ProjectiveExtendedGadget { - pub x: Fp3Gadget, - pub y: Fp3Gadget, - pub z: Fp3Gadget, - pub t: Fp3Gadget, +pub struct G2ProjectiveExtendedVar { + pub x: Fp3Var, + pub y: Fp3Var, + pub z: Fp3Var, + pub t: Fp3Var, } diff --git a/r1cs-std/src/groups/curves/short_weierstrass/mod.rs b/r1cs-std/src/groups/curves/short_weierstrass/mod.rs index 5ab7d35..c7bf7a7 100644 --- a/r1cs-std/src/groups/curves/short_weierstrass/mod.rs +++ b/r1cs-std/src/groups/curves/short_weierstrass/mod.rs @@ -3,83 +3,177 @@ use algebra::{ short_weierstrass_jacobian::{GroupAffine as SWAffine, GroupProjective as SWProjective}, SWModelParameters, }, - AffineCurve, BitIterator, Field, One, PrimeField, ProjectiveCurve, Zero, + AffineCurve, BigInteger, BitIterator, Field, One, PrimeField, ProjectiveCurve, Zero, }; -use core::{borrow::Borrow, marker::PhantomData, ops::Neg}; -use r1cs_core::{ConstraintSystem, SynthesisError}; +use core::{borrow::Borrow, marker::PhantomData}; +use r1cs_core::{ConstraintSystemRef, Namespace, SynthesisError}; -use crate::{prelude::*, Assignment, Vec}; +use crate::{prelude::*, Vec}; pub mod bls12; pub mod mnt4; pub mod mnt6; +/// An implementation of arithmetic for Short Weierstrass curves that relies on +/// the complete formulae derived in the paper of +/// [[Renes, Costello, Batina 2015]](https://eprint.iacr.org/2015/1060). #[derive(Derivative)] #[derivative(Debug, Clone)] #[must_use] -pub struct AffineGadget< +pub struct ProjectiveVar< P: SWModelParameters, - ConstraintF: Field, - F: FieldGadget, -> { + F: FieldVar::BasePrimeField>, +> where + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + /// The x-coordinate. pub x: F, + /// The y-coordinate. pub y: F, - pub infinity: Boolean, + /// The z-coordinate. + pub z: F, + #[derivative(Debug = "ignore")] _params: PhantomData

, - _engine: PhantomData, } -impl< - P: SWModelParameters, - ConstraintF: PrimeField, - F: FieldGadget + ToConstraintFieldGadget, - > ToConstraintFieldGadget for AffineGadget +/// An affine representation of a curve point. +#[derive(Derivative)] +#[derivative(Debug, Clone)] +#[must_use] +pub struct AffineVar< + P: SWModelParameters, + F: FieldVar::BasePrimeField>, +> where + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, { - fn to_constraint_field>( - &self, - mut cs: CS, - ) -> Result>, SynthesisError> { - let mut res = Vec::new(); + /// The x-coordinate. + pub x: F, + /// The y-coordinate. + pub y: F, + /// Is `self` the point at infinity. + pub infinity: Boolean<::BasePrimeField>, + #[derivative(Debug = "ignore")] + _params: PhantomData

, +} + +impl AffineVar +where + P: SWModelParameters, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + fn new(x: F, y: F, infinity: Boolean<::BasePrimeField>) -> Self { + Self { + x, + y, + infinity, + _params: PhantomData, + } + } - let mut x_gadget = self.x.to_constraint_field(&mut cs.ns(|| "x"))?; - let mut y_gadget = self.y.to_constraint_field(&mut cs.ns(|| "y"))?; + pub fn value(&self) -> Result, SynthesisError> { + Ok(SWAffine::new( + self.x.value()?, + self.y.value()?, + self.infinity.value()?, + )) + } +} - let mut infinity_gadget = self - .infinity - .to_constraint_field(&mut cs.ns(|| "infinity"))?; +impl R1CSVar<::BasePrimeField> for ProjectiveVar +where + P: SWModelParameters, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + type Value = SWProjective

; - res.append(&mut x_gadget); - res.append(&mut y_gadget); - res.append(&mut infinity_gadget); + fn cs(&self) -> Option::BasePrimeField>> { + self.x.cs().or(self.y.cs()).or(self.z.cs()) + } - Ok(res) + fn value(&self) -> Result { + let (x, y, z) = (self.x.value()?, self.y.value()?, self.z.value()?); + let result = if let Some(z_inv) = z.inverse() { + SWAffine::new(x * &z_inv, y * &z_inv, false) + } else { + SWAffine::zero() + }; + Ok(result.into()) } } -impl> - AffineGadget +impl::BasePrimeField>> + ProjectiveVar +where + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, { - pub fn new(x: F, y: F, infinity: Boolean) -> Self { + pub fn new(x: F, y: F, z: F) -> Self { Self { x, y, - infinity, + z, _params: PhantomData, - _engine: PhantomData, } } - pub fn alloc_without_check>( - mut cs: CS, - value_gen: F, - ) -> Result - where - F: FnOnce() -> Result, SynthesisError>, - { - let (x, y, infinity) = match value_gen() { + /// Convert this point into affine form. + pub fn to_affine(&self) -> Result, SynthesisError> { + let cs = self.cs().unwrap_or(ConstraintSystemRef::None); + let mode = if self.is_constant() { + AllocationMode::Constant + } else { + AllocationMode::Witness + }; + + let infinity = self.is_zero()?; + let zero_x = F::zero(); + let zero_y = F::one(); + + let non_zero_x = F::new_variable( + cs.ns("non-zero x"), + || { + let z_inv = self.z.value()?.inverse().unwrap_or(P::BaseField::zero()); + Ok(self.x.value()? * &z_inv) + }, + mode, + )?; + let non_zero_y = F::new_variable( + cs.ns("non-zero y"), + || { + let z_inv = self.z.value()?.inverse().unwrap_or(P::BaseField::zero()); + Ok(self.y.value()? * &z_inv) + }, + mode, + )?; + let x = infinity.select(&zero_x, &non_zero_x)?; + let y = infinity.select(&zero_y, &non_zero_y)?; + Ok(AffineVar::new(x, y, infinity)) + } + + /// Allocates a new variable without performing an on-curve check, which is + /// useful if the variable is known to be on the curve (eg., if the point + /// is a constant or is a public input). + pub fn new_variable_omit_on_curve_check( + cs: impl Into::BasePrimeField>>, + f: impl FnOnce() -> Result, SynthesisError>, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + + let (x, y, z) = match f() { Ok(ge) => { let ge = ge.into_affine(); - (Ok(ge.x), Ok(ge.y), Ok(ge.infinity)) + if ge.is_zero() { + ( + Ok(P::BaseField::zero()), + Ok(P::BaseField::one()), + Ok(P::BaseField::zero()), + ) + } else { + (Ok(ge.x), Ok(ge.y), Ok(P::BaseField::one())) + } } _ => ( Err(SynthesisError::AssignmentMissing), @@ -88,731 +182,581 @@ impl PartialEq for AffineGadget +impl CurveVar, ::BasePrimeField> + for ProjectiveVar where P: SWModelParameters, - ConstraintF: Field, - F: FieldGadget, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, { - fn eq(&self, other: &Self) -> bool { - self.x == other.x && self.y == other.y + fn constant(g: SWProjective

) -> Self { + let cs = ConstraintSystemRef::None; + Self::new_variable_omit_on_curve_check(cs, || Ok(g), AllocationMode::Constant).unwrap() } -} -impl Eq for AffineGadget -where - P: SWModelParameters, - ConstraintF: Field, - F: FieldGadget, -{ -} - -impl GroupGadget, ConstraintF> - for AffineGadget -where - P: SWModelParameters, - ConstraintF: PrimeField, - F: FieldGadget, -{ - type Value = SWProjective

; - type Variable = (F::Variable, F::Variable); - - #[inline] - fn get_value(&self) -> Option { - match ( - self.x.get_value(), - self.y.get_value(), - self.infinity.get_value(), - ) { - (Some(x), Some(y), Some(infinity)) => { - Some(SWAffine::new(x, y, infinity).into_projective()) - } - (None, None, None) => None, - _ => unreachable!(), - } - } - - #[inline] - fn get_variable(&self) -> Self::Variable { - (self.x.get_variable(), self.y.get_variable()) + fn zero() -> Self { + Self::new(F::zero(), F::one(), F::zero()) } - #[inline] - fn zero>(mut cs: CS) -> Result { - Ok(Self::new( - F::zero(cs.ns(|| "zero"))?, - F::one(cs.ns(|| "one"))?, - Boolean::Constant(true), - )) + fn is_zero(&self) -> Result::BasePrimeField>, SynthesisError> { + self.z.is_zero() } - #[inline] - /// Incomplete addition: neither `self` nor `other` can be the neutral - /// element. - fn add>( - &self, - mut cs: CS, - other: &Self, + fn new_variable_omit_prime_order_check( + cs: impl Into::BasePrimeField>>, + f: impl FnOnce() -> Result, SynthesisError>, + mode: AllocationMode, ) -> Result { - // lambda = (B.y - A.y)/(B.x - A.x) - // C.x = lambda^2 - A.x - B.x - // C.y = lambda(A.x - C.x) - A.y + let ns = cs.into(); + let cs = ns.cs(); + // Curve equation in projective form: + // E: Y² * Z = X³ + aX * Z² + bZ³ // - // Special cases: + // This can be re-written as + // E: Y² * Z - bZ³ = X³ + aX * Z² + // E: Z * (Y² - bZ²) = X * (X² + aZ²) + // so, compute X², Y², Z², + // compute temp = X * (X² + aZ²) + // check Z.mul_equals((Y² - bZ²), temp) // - // doubling: if B.y = A.y and B.x = A.x then lambda is unbound and - // C = (lambda^2, lambda^3) - // - // addition of negative point: if B.y = -A.y and B.x = A.x then no - // lambda can satisfy the first equation unless B.y - A.y = 0. But - // then this reduces to doubling. - // - // So we need to check that A.x - B.x != 0, which can be done by - // enforcing I * (B.x - A.x) = 1 - // This is done below when we calculate inv (by F::inverse) - - let x2_minus_x1 = other.x.sub(cs.ns(|| "x2 - x1"), &self.x)?; - let y2_minus_y1 = other.y.sub(cs.ns(|| "y2 - y1"), &self.y)?; - - let inv = x2_minus_x1.inverse(cs.ns(|| "compute inv"))?; - - let lambda = F::alloc(cs.ns(|| "lambda"), || { - Ok(y2_minus_y1.get_value().get()? * &inv.get_value().get()?) - })?; - - let x_3 = F::alloc(&mut cs.ns(|| "x_3"), || { - let lambda_val = lambda.get_value().get()?; - let x1 = self.x.get_value().get()?; - let x2 = other.x.get_value().get()?; - Ok((lambda_val.square() - &x1) - &x2) - })?; - - let y_3 = F::alloc(&mut cs.ns(|| "y_3"), || { - let lambda_val = lambda.get_value().get()?; - let x_1 = self.x.get_value().get()?; - let y_1 = self.y.get_value().get()?; - let x_3 = x_3.get_value().get()?; - Ok(lambda_val * &(x_1 - &x_3) - &y_1) - })?; - - // Check lambda - lambda.mul_equals(cs.ns(|| "check lambda"), &x2_minus_x1, &y2_minus_y1)?; - - // Check x3 - let x3_plus_x1_plus_x2 = x_3 - .add(cs.ns(|| "x3 + x1"), &self.x)? - .add(cs.ns(|| "x3 + x1 + x2"), &other.x)?; - lambda.mul_equals(cs.ns(|| "check x3"), &lambda, &x3_plus_x1_plus_x2)?; - - // Check y3 - let y3_plus_y1 = y_3.add(cs.ns(|| "y3 + y1"), &self.y)?; - let x1_minus_x3 = self.x.sub(cs.ns(|| "x1 - x3"), &x_3)?; - - lambda.mul_equals(cs.ns(|| ""), &x1_minus_x3, &y3_plus_y1)?; - - Ok(Self::new(x_3, y_3, Boolean::Constant(false))) + // A total of 5 multiplications + + let g = Self::new_variable_omit_on_curve_check(cs, f, mode)?; + + if mode != AllocationMode::Constant { + // Perform on-curve check. + let b = P::COEFF_B; + let a = P::COEFF_A; + + let x2 = g.x.square()?; + let y2 = g.y.square()?; + let z2 = g.z.square()?; + let t = &g.x * (x2 + &z2 * a); + + g.z.mul_equals(&(y2 - z2 * b), &t)?; + } + Ok(g) } - /// Incomplete addition: neither `self` nor `other` can be the neutral - /// element. - fn add_constant>( - &self, - mut cs: CS, - other: &SWProjective

, - ) -> Result { - // lambda = (B.y - A.y)/(B.x - A.x) - // C.x = lambda^2 - A.x - B.x - // C.y = lambda(A.x - C.x) - A.y - // - // Special cases: - // - // doubling: if B.y = A.y and B.x = A.x then lambda is unbound and - // C = (lambda^2, lambda^3) - // - // addition of negative point: if B.y = -A.y and B.x = A.x then no - // lambda can satisfy the first equation unless B.y - A.y = 0. But - // then this reduces to doubling. - // - // So we need to check that A.x - B.x != 0, which can be done by - // enforcing I * (B.x - A.x) = 1 - if other.is_zero() { - return Err(SynthesisError::AssignmentMissing); + /// Enforce that `self` is in the prime-order subgroup. + /// + /// Does so by multiplying by the prime order, and checking that the result + /// is unchanged. + // TODO: at the moment this doesn't work, because the addition and doubling + // formulae are incomplete for even-order points. + fn enforce_prime_order(&self) -> Result<(), SynthesisError> { + let r_minus_1 = (-P::ScalarField::one()).into_repr(); + + let mut seen_one = false; + let mut result = Self::zero(); + for b in BitIterator::new(r_minus_1) { + let old_seen_one = seen_one; + if seen_one { + result.double_in_place()?; + } else { + seen_one = b; + } + + if b { + result = if old_seen_one { + result + self + } else { + self.clone() + }; + } } - let other = other.into_affine(); - let other_x = other.x; - let other_y = other.y; - - let x2_minus_x1 = self - .x - .sub_constant(cs.ns(|| "x2 - x1"), &other_x)? - .negate(cs.ns(|| "neg1"))?; - let y2_minus_y1 = self - .y - .sub_constant(cs.ns(|| "y2 - y1"), &other_y)? - .negate(cs.ns(|| "neg2"))?; - - let inv = x2_minus_x1.inverse(cs.ns(|| "compute inv"))?; - - let lambda = F::alloc(cs.ns(|| "lambda"), || { - Ok(y2_minus_y1.get_value().get()? * &inv.get_value().get()?) - })?; - - let x_3 = F::alloc(&mut cs.ns(|| "x_3"), || { - let lambda_val = lambda.get_value().get()?; - let x1 = self.x.get_value().get()?; - let x2 = other_x; - Ok((lambda_val.square() - &x1) - &x2) - })?; - - let y_3 = F::alloc(&mut cs.ns(|| "y_3"), || { - let lambda_val = lambda.get_value().get()?; - let x_1 = self.x.get_value().get()?; - let y_1 = self.y.get_value().get()?; - let x_3 = x_3.get_value().get()?; - Ok(lambda_val * &(x_1 - &x_3) - &y_1) - })?; - - // Check lambda - lambda.mul_equals(cs.ns(|| "check lambda"), &x2_minus_x1, &y2_minus_y1)?; - - // Check x3 - let x3_plus_x1_plus_x2 = x_3 - .add(cs.ns(|| "x3 + x1"), &self.x)? - .add_constant(cs.ns(|| "x3 + x1 + x2"), &other_x)?; - lambda.mul_equals(cs.ns(|| "check x3"), &lambda, &x3_plus_x1_plus_x2)?; - - // Check y3 - let y3_plus_y1 = y_3.add(cs.ns(|| "y3 + y1"), &self.y)?; - let x1_minus_x3 = self.x.sub(cs.ns(|| "x1 - x3"), &x_3)?; - - lambda.mul_equals(cs.ns(|| ""), &x1_minus_x3, &y3_plus_y1)?; - - Ok(Self::new(x_3, y_3, Boolean::Constant(false))) + self.negate()?.enforce_equal(&result)?; + Ok(()) } #[inline] - fn double_in_place>( - &mut self, - mut cs: CS, - ) -> Result<(), SynthesisError> { - let a = P::COEFF_A; - let x_squared = self.x.square(cs.ns(|| "x^2"))?; + fn double_in_place(&mut self) -> Result<(), SynthesisError> { + // Complete doubling formula from Renes-Costello-Batina 2015 + // Algorithm 3 + // (https://eprint.iacr.org/2015/1060). + // + // Adapted from code in + // https://github.com/RustCrypto/elliptic-curves/blob/master/p256/src/arithmetic.rs + let three_b = P::COEFF_B.double() + &P::COEFF_B; + + let xx = self.x.square()?; // 1 + let yy = self.y.square()?; // 2 + let zz = self.z.square()?; // 3 + let xy2 = (&self.x * &self.y).double()?; // 4, 5 + let xz2 = (&self.x * &self.z).double()?; // 6, 7 + + let axz2 = mul_by_coeff_a::(&xz2); // 8 + + let bzz3_part = &axz2 + &zz * three_b; // 9, 10 + let yy_m_bzz3 = &yy - &bzz3_part; // 11 + let yy_p_bzz3 = &yy + &bzz3_part; // 12 + let y_frag = yy_p_bzz3 * &yy_m_bzz3; // 13 + let x_frag = yy_m_bzz3 * &xy2; // 14 + + let bxz3 = xz2 * three_b; // 15 + let azz = mul_by_coeff_a::(&zz); // 16 + let b3_xz_pairs = mul_by_coeff_a::(&(&xx - &azz)) + &bxz3; // 15, 16, 17, 18, 19 + let xx3_p_azz = (xx.double()? + &xx + &azz) * &b3_xz_pairs; // 23, 24, 25 + + let y = y_frag + &xx3_p_azz; // 26, 27 + let yz2 = (&self.y * &self.z).double()?; // 28, 29 + let x = x_frag - &(b3_xz_pairs * &yz2); // 30, 31 + let z = (yz2 * &yy).double()?.double()?; // 32, 33, 34 + self.x = x; + self.y = y; + self.z = z; + Ok(()) + } - let one = P::BaseField::one(); - let two = one.double(); - let three = two + &one; + fn negate(&self) -> Result { + Ok(Self::new(self.x.clone(), self.y.negate()?, self.z.clone())) + } +} - let three_x_squared = x_squared.mul_by_constant(cs.ns(|| "3 * x^2"), &three)?; - let three_x_squared_plus_a = three_x_squared.add_constant(cs.ns(|| "3 * x^2 + a"), &a)?; +fn mul_by_coeff_a< + P: SWModelParameters, + F: FieldVar::BasePrimeField>, +>( + f: &F, +) -> F +where + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + if !P::COEFF_A.is_zero() { + f * P::COEFF_A + } else { + F::zero() + } +} - let two_y = self.y.double(cs.ns(|| "2y"))?; +impl_bounded_ops!( + ProjectiveVar, + SWProjective

, + Add, + add, + AddAssign, + add_assign, + |this: &'a ProjectiveVar, other: &'a ProjectiveVar| { + // Complete addition formula from Renes-Costello-Batina 2015 + // Algorithm 1 + // (https://eprint.iacr.org/2015/1060). + // + // Adapted from code in + // https://github.com/RustCrypto/elliptic-curves/blob/master/p256/src/arithmetic.rs - let lambda = F::alloc(cs.ns(|| "lambda"), || { - let y_doubled_inv = two_y.get_value().get()?.inverse().get()?; - Ok(three_x_squared_plus_a.get_value().get()? * &y_doubled_inv) - })?; + let three_b = P::COEFF_B.double() + &P::COEFF_B; - // Check lambda - lambda.mul_equals(cs.ns(|| "check lambda"), &two_y, &three_x_squared_plus_a)?; + let xx = &this.x * &other.x; // 1 + let yy = &this.y * &other.y; // 2 + let zz = &this.z * &other.z; // 3 + let xy_pairs = ((&this.x + &this.y) * &(&other.x + &other.y)) - (&xx + &yy); // 4, 5, 6, 7, 8 + let xz_pairs = ((&this.x + &this.z) * &(&other.x + &other.z)) - (&xx + &zz); // 9, 10, 11, 12, 13 + let yz_pairs = ((&this.y + &this.z) * &(&other.y + &other.z)) - (&yy + &zz); // 14, 15, 16, 17, 18 - let x = lambda - .square(cs.ns(|| "lambda^2"))? - .sub(cs.ns(|| "lambda^2 - x"), &self.x)? - .sub(cs.ns(|| "lambda^2 - 2x"), &self.x)?; + let axz = mul_by_coeff_a::(&xz_pairs); // 19 - let y = self - .x - .sub(cs.ns(|| "x - self.x"), &x)? - .mul(cs.ns(|| "times lambda"), &lambda)? - .sub(cs.ns(|| "plus self.y"), &self.y)?; + let bzz3_part = &axz + &zz * three_b; // 20, 21 - *self = Self::new(x, y, Boolean::Constant(false)); - Ok(()) - } + let yy_m_bzz3 = &yy - &bzz3_part; // 22 + let yy_p_bzz3 = &yy + &bzz3_part; // 23 - fn negate>( - &self, - mut cs: CS, - ) -> Result { - Ok(Self::new( - self.x.clone(), - self.y.negate(cs.ns(|| "negate y"))?, - self.infinity, - )) - } + let azz = mul_by_coeff_a::(&zz); + let xx3_p_azz = xx.double().unwrap() + &xx + &azz; // 25, 26, 27, 29 - fn cost_of_add() -> usize { - 3 * F::cost_of_mul_equals() + F::cost_of_inv() - } + let bxz3 = &xz_pairs * three_b; // 28 + let b3_xz_pairs = mul_by_coeff_a::(&(&xx - &azz)) + &bxz3; // 30, 31, 32 - fn cost_of_double() -> usize { - 4 * F::cost_of_mul() - } -} + let x = (&yy_m_bzz3 * &xy_pairs) - &yz_pairs * &b3_xz_pairs; // 35, 39, 40 + let y = (&yy_p_bzz3 * &yy_m_bzz3) + &xx3_p_azz * b3_xz_pairs; // 24, 36, 37, 38 + let z = (&yy_p_bzz3 * &yz_pairs) + xy_pairs * xx3_p_azz; // 41, 42, 43 -impl CondSelectGadget for AffineGadget + ProjectiveVar::new(x, y, z) + }, + |this: &'a ProjectiveVar, other: SWProjective

| { + this + ProjectiveVar::constant(other) + }, + (F: FieldVar::BasePrimeField>, P: SWModelParameters), + for <'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>, +); + +impl_bounded_ops!( + ProjectiveVar, + SWProjective

, + Sub, + sub, + SubAssign, + sub_assign, + |this: &'a ProjectiveVar, other: &'a ProjectiveVar| this + other.negate().unwrap(), + |this: &'a ProjectiveVar, other: SWProjective

| this - ProjectiveVar::constant(other), + (F: FieldVar::BasePrimeField>, P: SWModelParameters), + for <'b> &'b F: FieldOpsBounds<'b, P::BaseField, F> +); + +impl<'a, P, F> GroupOpsBounds<'a, SWProjective

, ProjectiveVar> for ProjectiveVar where P: SWModelParameters, - ConstraintF: PrimeField, - F: FieldGadget, + F: FieldVar::BasePrimeField>, + for<'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>, { - #[inline] - fn conditionally_select>( - mut cs: CS, - cond: &Boolean, - true_value: &Self, - false_value: &Self, - ) -> Result { - let x = F::conditionally_select(&mut cs.ns(|| "x"), cond, &true_value.x, &false_value.x)?; - let y = F::conditionally_select(&mut cs.ns(|| "y"), cond, &true_value.y, &false_value.y)?; - let infinity = Boolean::conditionally_select( - &mut cs.ns(|| "infinity"), - cond, - &true_value.infinity, - &false_value.infinity, - )?; - - Ok(Self::new(x, y, infinity)) - } - - fn cost() -> usize { - 2 * >::cost() - + >::cost() - } } -impl EqGadget for AffineGadget +impl<'a, P, F> GroupOpsBounds<'a, SWProjective

, ProjectiveVar> for &'a ProjectiveVar where P: SWModelParameters, - ConstraintF: Field, - F: FieldGadget, + F: FieldVar::BasePrimeField>, + for<'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>, { } -impl ConditionalEqGadget for AffineGadget +impl CondSelectGadget<::BasePrimeField> for ProjectiveVar where P: SWModelParameters, - ConstraintF: Field, - F: FieldGadget, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, { #[inline] - fn conditional_enforce_equal>( - &self, - mut cs: CS, - other: &Self, - condition: &Boolean, - ) -> Result<(), SynthesisError> { - self.x.conditional_enforce_equal( - &mut cs.ns(|| "X Coordinate Conditional Equality"), - &other.x, - condition, - )?; - self.y.conditional_enforce_equal( - &mut cs.ns(|| "Y Coordinate Conditional Equality"), - &other.y, - condition, - )?; - self.infinity.conditional_enforce_equal( - &mut cs.ns(|| "Infinity Conditional Equality"), - &other.infinity, - condition, - )?; - Ok(()) - } + fn conditionally_select( + cond: &Boolean<::BasePrimeField>, + true_value: &Self, + false_value: &Self, + ) -> Result { + let x = cond.select(&true_value.x, &false_value.x)?; + let y = cond.select(&true_value.y, &false_value.y)?; + let z = cond.select(&true_value.z, &false_value.z)?; - fn cost() -> usize { - 2 * >::cost() + Ok(Self::new(x, y, z)) } } -impl NEqGadget for AffineGadget +impl EqGadget<::BasePrimeField> for ProjectiveVar where P: SWModelParameters, - ConstraintF: Field, - F: FieldGadget, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, { + fn is_eq( + &self, + other: &Self, + ) -> Result::BasePrimeField>, SynthesisError> { + let x_equal = (&self.x * &other.z).is_eq(&(&other.x * &self.z))?; + let y_equal = (&self.y * &other.z).is_eq(&(&other.y * &self.z))?; + let coordinates_equal = x_equal.and(&y_equal)?; + let both_are_zero = self.is_zero()?.and(&other.is_zero()?)?; + both_are_zero.or(&coordinates_equal) + } + #[inline] - fn enforce_not_equal>( + fn conditional_enforce_equal( &self, - mut cs: CS, other: &Self, + condition: &Boolean<::BasePrimeField>, ) -> Result<(), SynthesisError> { - self.x - .enforce_not_equal(&mut cs.ns(|| "X Coordinate Inequality"), &other.x)?; - self.y - .enforce_not_equal(&mut cs.ns(|| "Y Coordinate Inequality"), &other.y)?; + let x_equal = (&self.x * &other.z).is_eq(&(&other.x * &self.z))?; + let y_equal = (&self.y * &other.z).is_eq(&(&other.y * &self.z))?; + let coordinates_equal = x_equal.and(&y_equal)?; + let both_are_zero = self.is_zero()?.and(&other.is_zero()?)?; + both_are_zero + .or(&coordinates_equal)? + .conditional_enforce_equal(&Boolean::Constant(true), condition)?; Ok(()) } - fn cost() -> usize { - 2 * >::cost() + #[inline] + fn conditional_enforce_not_equal( + &self, + other: &Self, + condition: &Boolean<::BasePrimeField>, + ) -> Result<(), SynthesisError> { + let is_equal = self.is_eq(other)?; + is_equal + .and(condition)? + .enforce_equal(&Boolean::Constant(false)) } } -impl AllocGadget, ConstraintF> - for AffineGadget +impl AllocVar, ::BasePrimeField> for ProjectiveVar where P: SWModelParameters, - ConstraintF: PrimeField, - F: FieldGadget, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, { - #[inline] - fn alloc_constant>( - mut cs: CS, - t: T, - ) -> Result - where - T: Borrow>, - { - let p = t.borrow().into_affine(); - Ok(Self { - x: F::alloc_constant(cs.ns(|| "x"), &p.x)?, - y: F::alloc_constant(cs.ns(|| "y"), &p.y)?, - infinity: Boolean::constant(p.infinity), - _params: PhantomData, - _engine: PhantomData, - }) - } - - #[inline] - fn alloc>( - mut cs: CS, - value_gen: FN, - ) -> Result - where - FN: FnOnce() -> Result, - T: Borrow>, - { - let (x, y, infinity) = match value_gen() { - Ok(ge) => { - let ge = ge.borrow().into_affine(); - (Ok(ge.x), Ok(ge.y), Ok(ge.infinity)) - } - _ => ( - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - ), - }; - - // Perform on-curve check. - let b = P::COEFF_B; - let a = P::COEFF_A; - - let x = F::alloc(&mut cs.ns(|| "x"), || x)?; - let y = F::alloc(&mut cs.ns(|| "y"), || y)?; - let infinity = Boolean::alloc(&mut cs.ns(|| "infinity"), || infinity)?; - - // Check that y^2 = x^3 + ax +b - // We do this by checking that y^2 - b = x * (x^2 +a) - let x2 = x.square(&mut cs.ns(|| "x^2"))?; - let y2 = y.square(&mut cs.ns(|| "y^2"))?; - - let x2_plus_a = x2.add_constant(cs.ns(|| "x^2 + a"), &a)?; - let y2_minus_b = y2.add_constant(cs.ns(|| "y^2 - b"), &b.neg())?; - - x2_plus_a.mul_equals(cs.ns(|| "on curve check"), &x, &y2_minus_b)?; - - Ok(Self::new(x, y, infinity)) + fn new_variable>>( + cs: impl Into::BasePrimeField>>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + Self::new_variable(cs, || f().map(|b| b.borrow().into_projective()), mode) } +} - #[inline] - fn alloc_checked>( - mut cs: CS, - value_gen: FN, - ) -> Result - where - FN: FnOnce() -> Result, - T: Borrow>, - { - let cofactor_weight = BitIterator::new(P::COFACTOR).filter(|b| *b).count(); - // If we multiply by r, we actually multiply by r - 2. - let r_minus_1 = (-P::ScalarField::one()).into_repr(); - let r_weight = BitIterator::new(&r_minus_1).filter(|b| *b).count(); +impl AllocVar, ::BasePrimeField> + for ProjectiveVar +where + P: SWModelParameters, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + fn new_variable>>( + cs: impl Into::BasePrimeField>>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + let f = || Ok(*f()?.borrow()); + match mode { + AllocationMode::Constant => Self::new_variable_omit_prime_order_check(cs, f, mode), + AllocationMode::Input => Self::new_variable_omit_prime_order_check(cs, f, mode), + AllocationMode::Witness => { + // if cofactor.is_even(): + // divide until you've removed all even factors + // else: + // just directly use double and add. + let mut power_of_2: u32 = 0; + let mut cofactor = P::COFACTOR.to_vec(); + while cofactor[0] % 2 == 0 { + div2(&mut cofactor); + power_of_2 += 1; + } - // We pick the most efficient method of performing the prime order check: - // If the cofactor has lower hamming weight than the scalar field's modulus, - // we first multiply by the inverse of the cofactor, and then, after allocating, - // multiply by the cofactor. This ensures the resulting point has no cofactors - // - // Else, we multiply by the scalar field's modulus and ensure that the result - // is zero. - if cofactor_weight < r_weight { - let ge = Self::alloc(cs.ns(|| "Alloc checked"), || { - value_gen().map(|ge| { - ge.borrow() - .into_affine() - .mul_by_cofactor_inv() - .into_projective() - }) - })?; - let mut seen_one = false; - let mut result = Self::zero(cs.ns(|| "result"))?; - for (i, b) in BitIterator::new(P::COFACTOR).enumerate() { - let mut cs = cs.ns(|| format!("Iteration {}", i)); - - let old_seen_one = seen_one; - if seen_one { - result.double_in_place(cs.ns(|| "Double"))?; + let cofactor_weight = BitIterator::new(cofactor.as_slice()).filter(|b| *b).count(); + let modulus_minus_1 = (-P::ScalarField::one()).into_repr(); // r - 1 + let modulus_minus_1_weight = + BitIterator::new(modulus_minus_1).filter(|b| *b).count(); + + // We pick the most efficient method of performing the prime order check: + // If the cofactor has lower hamming weight than the scalar field's modulus, + // we first multiply by the inverse of the cofactor, and then, after allocating, + // multiply by the cofactor. This ensures the resulting point has no cofactors + // + // Else, we multiply by the scalar field's modulus and ensure that the result + // equals the identity. + + let (mut ge, iter) = if cofactor_weight < modulus_minus_1_weight { + let ge = Self::new_variable_omit_prime_order_check( + cs.ns("Witness without subgroup check with cofactor mul"), + || f().map(|g| g.borrow().into_affine().mul_by_cofactor_inv().into()), + mode, + )?; + (ge, BitIterator::new(cofactor.as_slice())) } else { - seen_one = b; + let ge = Self::new_variable_omit_prime_order_check( + cs.ns("Witness without subgroup check with `r` check"), + || { + f().map(|g| { + let g = g.into_affine(); + let mut power_of_two = P::ScalarField::one().into_repr(); + power_of_two.muln(power_of_2); + let power_of_two_inv = + P::ScalarField::from(power_of_two).inverse().unwrap(); + g.mul(power_of_two_inv) + }) + }, + mode, + )?; + + (ge, BitIterator::new(modulus_minus_1.as_ref())) + }; + // Remove the even part of the cofactor + for _ in 0..power_of_2 { + ge.double_in_place()?; } - if b { - result = if old_seen_one { - result.add(cs.ns(|| "Add"), &ge)? + let mut seen_one = false; + let mut result = Self::zero(); + for b in iter { + let old_seen_one = seen_one; + if seen_one { + result.double_in_place()?; } else { - ge.clone() - }; + seen_one = b; + } + + if b { + result = if old_seen_one { + result + &ge + } else { + ge.clone() + }; + } } - } - Ok(result) - } else { - let ge = Self::alloc(cs.ns(|| "Alloc checked"), value_gen)?; - let mut seen_one = false; - let mut result = Self::zero(cs.ns(|| "result"))?; - // Returns bits in big-endian order - for (i, b) in BitIterator::new(r_minus_1).enumerate() { - let mut cs = cs.ns(|| format!("Iteration {}", i)); - - let old_seen_one = seen_one; - if seen_one { - result.double_in_place(cs.ns(|| "Double"))?; + if cofactor_weight < modulus_minus_1_weight { + Ok(result) } else { - seen_one = b; - } - - if b { - result = if old_seen_one { - result.add(cs.ns(|| "Add"), &ge)? - } else { - ge.clone() - }; + ge.enforce_equal(&ge)?; + Ok(ge) } } - let neg_ge = ge.negate(cs.ns(|| "Negate ge"))?; - neg_ge.enforce_equal(cs.ns(|| "Check equals"), &result)?; - Ok(ge) } } +} - #[inline] - fn alloc_input>( - mut cs: CS, - value_gen: FN, - ) -> Result - where - FN: FnOnce() -> Result, - T: Borrow>, - { - // When allocating the input we assume that the verifier has performed - // any on curve checks already. - let (x, y, infinity) = match value_gen() { - Ok(ge) => { - let ge = ge.borrow().into_affine(); - (Ok(ge.x), Ok(ge.y), Ok(ge.infinity)) - } - _ => ( - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - ), - }; - - let x = F::alloc_input(&mut cs.ns(|| "x"), || x)?; - let y = F::alloc_input(&mut cs.ns(|| "y"), || y)?; - let infinity = Boolean::alloc_input(&mut cs.ns(|| "infinity"), || infinity)?; - - Ok(Self::new(x, y, infinity)) +#[inline] +fn div2(limbs: &mut [u64]) { + let mut t = 0; + for i in limbs.iter_mut().rev() { + let t2 = *i << 63; + *i >>= 1; + *i |= t; + t = t2; } } -impl ToBitsGadget for AffineGadget +impl ToBitsGadget<::BasePrimeField> for ProjectiveVar where P: SWModelParameters, - ConstraintF: Field, - F: FieldGadget, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, { - fn to_bits>( + fn to_bits( &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut x_bits = self.x.to_bits(&mut cs.ns(|| "X Coordinate To Bits"))?; - let y_bits = self.y.to_bits(&mut cs.ns(|| "Y Coordinate To Bits"))?; - x_bits.extend_from_slice(&y_bits); - x_bits.push(self.infinity); - Ok(x_bits) + ) -> Result::BasePrimeField>>, SynthesisError> { + let g = self.to_affine()?; + let mut bits = g.x.to_bits()?; + let y_bits = g.y.to_bits()?; + bits.extend_from_slice(&y_bits); + bits.push(g.infinity); + Ok(bits) } - fn to_non_unique_bits>( + fn to_non_unique_bits( &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut x_bits = self - .x - .to_non_unique_bits(&mut cs.ns(|| "X Coordinate To Bits"))?; - let y_bits = self - .y - .to_non_unique_bits(&mut cs.ns(|| "Y Coordinate To Bits"))?; - x_bits.extend_from_slice(&y_bits); - x_bits.push(self.infinity); - - Ok(x_bits) + ) -> Result::BasePrimeField>>, SynthesisError> { + let g = self.to_affine()?; + let mut bits = g.x.to_non_unique_bits()?; + let y_bits = g.y.to_non_unique_bits()?; + bits.extend_from_slice(&y_bits); + bits.push(g.infinity); + Ok(bits) } } -impl ToBytesGadget for AffineGadget +impl ToBytesGadget<::BasePrimeField> for ProjectiveVar where P: SWModelParameters, - ConstraintF: Field, - F: FieldGadget, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, { - fn to_bytes>( + fn to_bytes( &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut x_bytes = self.x.to_bytes(&mut cs.ns(|| "X Coordinate To Bytes"))?; - let y_bytes = self.y.to_bytes(&mut cs.ns(|| "Y Coordinate To Bytes"))?; - let inf_bytes = self.infinity.to_bytes(&mut cs.ns(|| "Infinity to Bytes"))?; - x_bytes.extend_from_slice(&y_bytes); - x_bytes.extend_from_slice(&inf_bytes); - Ok(x_bytes) + ) -> Result::BasePrimeField>>, SynthesisError> { + let g = self.to_affine()?; + let mut bytes = g.x.to_bytes()?; + let y_bytes = g.y.to_bytes()?; + let inf_bytes = g.infinity.to_bytes()?; + bytes.extend_from_slice(&y_bytes); + bytes.extend_from_slice(&inf_bytes); + Ok(bytes) } - fn to_non_unique_bytes>( + fn to_non_unique_bytes( &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut x_bytes = self - .x - .to_non_unique_bytes(&mut cs.ns(|| "X Coordinate To Bytes"))?; - let y_bytes = self - .y - .to_non_unique_bytes(&mut cs.ns(|| "Y Coordinate To Bytes"))?; - let inf_bytes = self - .infinity - .to_non_unique_bytes(&mut cs.ns(|| "Infinity to Bytes"))?; - x_bytes.extend_from_slice(&y_bytes); - x_bytes.extend_from_slice(&inf_bytes); - - Ok(x_bytes) + ) -> Result::BasePrimeField>>, SynthesisError> { + let g = self.to_affine()?; + let mut bytes = g.x.to_non_unique_bytes()?; + let y_bytes = g.y.to_non_unique_bytes()?; + let inf_bytes = g.infinity.to_non_unique_bytes()?; + bytes.extend_from_slice(&y_bytes); + bytes.extend_from_slice(&inf_bytes); + Ok(bytes) } } #[cfg(test)] #[allow(dead_code)] -pub(crate) fn test() +pub(crate) fn test() -> Result<(), SynthesisError> where - ConstraintF: Field, P: SWModelParameters, - GG: GroupGadget, ConstraintF, Value = SWProjective

>, + GG: CurveVar, ::BasePrimeField>, + for<'a> &'a GG: GroupOpsBounds<'a, SWProjective

, GG>, { - use crate::{boolean::AllocatedBit, prelude::*, test_constraint_system::TestConstraintSystem}; + use crate::prelude::*; use algebra::{test_rng, Group, UniformRand}; - use rand::Rng; + use r1cs_core::ConstraintSystem; - // Incomplete addition doesn't allow us to call the group_test. - // group_test::, GG>(); + crate::groups::test::group_test::, _, GG>()?; let mut rng = test_rng(); - let mut cs = TestConstraintSystem::::new(); + let cs = ConstraintSystem::<::BasePrimeField>::new_ref(); let a = SWProjective::

::rand(&mut rng); let b = SWProjective::

::rand(&mut rng); let a_affine = a.into_affine(); let b_affine = b.into_affine(); - let mut gadget_a = GG::alloc(&mut cs.ns(|| "a"), || Ok(a)).unwrap(); - let gadget_b = GG::alloc_checked(&mut cs.ns(|| "b"), || Ok(b)).unwrap(); - assert_eq!(gadget_a.get_value().unwrap().x, a_affine.x); - assert_eq!(gadget_a.get_value().unwrap().y, a_affine.y); - assert_eq!(gadget_b.get_value().unwrap().x, b_affine.x); - assert_eq!(gadget_b.get_value().unwrap().y, b_affine.y); + println!("Allocating things"); + let ns = cs.ns("allocating variables"); + println!("{:?}", cs.current_namespace()); + let mut gadget_a = GG::new_witness(cs.ns("a"), || Ok(a))?; + let gadget_b = GG::new_witness(cs.ns("b"), || Ok(b))?; + println!("{:?}", cs.current_namespace()); + ns.leave_namespace(); + println!("Done Allocating things"); + assert_eq!(gadget_a.value()?.into_affine().x, a_affine.x); + assert_eq!(gadget_a.value()?.into_affine().y, a_affine.y); + assert_eq!(gadget_b.value()?.into_affine().x, b_affine.x); + assert_eq!(gadget_b.value()?.into_affine().y, b_affine.y); + assert_eq!(cs.which_is_unsatisfied(), None); + + println!("Checking addition"); // Check addition let ab = a + &b; let ab_affine = ab.into_affine(); - let gadget_ab = gadget_a.add(&mut cs.ns(|| "ab"), &gadget_b).unwrap(); - let gadget_ba = gadget_b.add(&mut cs.ns(|| "ba"), &gadget_a).unwrap(); - gadget_ba - .enforce_equal(&mut cs.ns(|| "b + a == a + b?"), &gadget_ab) - .unwrap(); - - let ab_val = gadget_ab - .get_value() - .expect("Doubling should be successful") - .into_affine(); + let gadget_ab = &gadget_a + &gadget_b; + let gadget_ba = &gadget_b + &gadget_a; + gadget_ba.enforce_equal(&gadget_ab)?; + + let ab_val = gadget_ab.value()?.into_affine(); assert_eq!(ab_val, ab_affine, "Result of addition is unequal"); + assert!(cs.is_satisfied().unwrap()); + println!("Done checking addition"); + println!("Checking doubling"); // Check doubling let aa = Group::double(&a); let aa_affine = aa.into_affine(); - gadget_a.double_in_place(&mut cs.ns(|| "2a")).unwrap(); - let aa_val = gadget_a - .get_value() - .expect("Doubling should be successful") - .into_affine(); + gadget_a.double_in_place()?; + let aa_val = gadget_a.value()?.into_affine(); assert_eq!( aa_val, aa_affine, "Gadget and native values are unequal after double." ); + assert!(cs.is_satisfied().unwrap()); + println!("Done checking doubling"); + println!("Checking mul_bits"); // Check mul_bits let scalar = P::ScalarField::rand(&mut rng); - let native_result = aa.into_affine().mul(scalar) + &b; + let native_result = aa.into_affine().mul(scalar); let native_result = native_result.into_affine(); let mut scalar: Vec = BitIterator::new(scalar.into_repr()).collect(); // Get the scalar bits into little-endian form. scalar.reverse(); - let input = Vec::::alloc(cs.ns(|| "Input"), || Ok(scalar)).unwrap(); - let result = gadget_a - .mul_bits(cs.ns(|| "mul_bits"), &gadget_b, input.iter()) - .unwrap(); - let result_val = result.get_value().unwrap().into_affine(); + let input: Vec> = Vec::new_witness(cs.ns("bits"), || Ok(scalar)).unwrap(); + let result = gadget_a.mul_bits(input.iter())?; + let result_val = result.value()?.into_affine(); assert_eq!( result_val, native_result, "gadget & native values are diff. after scalar mul" ); + assert!(cs.is_satisfied().unwrap()); + println!("Done checking mul_bits"); - if !cs.is_satisfied() { + if !cs.is_satisfied().unwrap() { + println!("Not satisfied"); println!("{:?}", cs.which_is_unsatisfied().unwrap()); } - assert!(cs.is_satisfied()); - - // Constraint cost etc. - let mut cs = TestConstraintSystem::::new(); - - let bit = AllocatedBit::alloc(&mut cs.ns(|| "bool"), || Ok(true)) - .unwrap() - .into(); - - let mut rng = test_rng(); - let a: SWProjective

= rng.gen(); - let b: SWProjective

= rng.gen(); - let gadget_a = GG::alloc(&mut cs.ns(|| "a"), || Ok(a)).unwrap(); - let gadget_b = GG::alloc(&mut cs.ns(|| "b"), || Ok(b)).unwrap(); - let alloc_cost = cs.num_constraints(); - let _ = - GG::conditionally_select(&mut cs.ns(|| "cond_select"), &bit, &gadget_a, &gadget_b).unwrap(); - let cond_select_cost = cs.num_constraints() - alloc_cost; - - let _ = gadget_a.add(&mut cs.ns(|| "ab"), &gadget_b).unwrap(); - let add_cost = cs.num_constraints() - cond_select_cost - alloc_cost; - - assert!(cs.is_satisfied()); - assert_eq!( - cond_select_cost, - >::cost() - ); - assert_eq!(add_cost, GG::cost_of_add()); + assert!(cs.is_satisfied().unwrap()); + Ok(()) } diff --git a/r1cs-std/src/groups/curves/twisted_edwards/mod.rs b/r1cs-std/src/groups/curves/twisted_edwards/mod.rs index 2def1c7..ab13a45 100644 --- a/r1cs-std/src/groups/curves/twisted_edwards/mod.rs +++ b/r1cs-std/src/groups/curves/twisted_edwards/mod.rs @@ -1,50 +1,68 @@ use algebra::{ curves::{ - twisted_edwards_extended::GroupAffine as TEAffine, MontgomeryModelParameters, - TEModelParameters, + twisted_edwards_extended::{GroupAffine as TEAffine, GroupProjective as TEProjective}, + AffineCurve, MontgomeryModelParameters, ProjectiveCurve, TEModelParameters, }, - BitIterator, Field, One, PrimeField, Zero, + BigInteger, BitIterator, Field, One, PrimeField, Zero, }; -use r1cs_core::{ConstraintSystem, SynthesisError}; +use r1cs_core::{ConstraintSystemRef, Namespace, SynthesisError}; use crate::{prelude::*, Vec}; -use crate::fields::fp::FpGadget; use core::{borrow::Borrow, marker::PhantomData}; #[derive(Derivative)] #[derivative(Debug, Clone)] -#[derivative(Debug(bound = "P: TEModelParameters, ConstraintF: Field"))] #[must_use] -pub struct MontgomeryAffineGadget< +pub struct MontgomeryAffineVar< P: TEModelParameters, - ConstraintF: Field, - F: FieldGadget, -> { + F: FieldVar::BasePrimeField>, +> where + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ pub x: F, pub y: F, #[derivative(Debug = "ignore")] _params: PhantomData

, - #[derivative(Debug = "ignore")] - _engine: PhantomData, } mod montgomery_affine_impl { use super::*; - use crate::Assignment; use algebra::{twisted_edwards_extended::GroupAffine, Field}; - use core::ops::{AddAssign, MulAssign, SubAssign}; + use core::ops::Add; - impl> - MontgomeryAffineGadget + impl R1CSVar<::BasePrimeField> for MontgomeryAffineVar + where + P: TEModelParameters, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, + { + type Value = (P::BaseField, P::BaseField); + + fn cs(&self) -> Option::BasePrimeField>> { + self.x.cs().or(self.y.cs()) + } + + fn value(&self) -> Result { + let x = self.x.value()?; + let y = self.y.value()?; + Ok((x, y)) + } + } + + impl< + P: TEModelParameters, + F: FieldVar::BasePrimeField>, + > MontgomeryAffineVar + where + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, { pub fn new(x: F, y: F) -> Self { Self { x, y, _params: PhantomData, - _engine: PhantomData, } } @@ -65,1395 +83,792 @@ mod montgomery_affine_impl { Ok((montgomery_point.x, montgomery_point.y)) } - pub fn from_edwards>( - mut cs: CS, + pub fn new_witness_from_edwards( + cs: ConstraintSystemRef<::BasePrimeField>, p: &TEAffine

, ) -> Result { let montgomery_coords = Self::from_edwards_to_coords(p)?; - - let u = F::alloc(cs.ns(|| "u"), || Ok(montgomery_coords.0))?; - - let v = F::alloc(cs.ns(|| "v"), || Ok(montgomery_coords.1))?; - + let u = F::new_witness(cs.ns("u"), || Ok(montgomery_coords.0))?; + let v = F::new_witness(cs.ns("v"), || Ok(montgomery_coords.1))?; Ok(Self::new(u, v)) } - pub fn into_edwards>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { + pub fn into_edwards(&self) -> Result, SynthesisError> { + let cs = self.cs().unwrap_or(ConstraintSystemRef::None); // Compute u = x / y - let u = F::alloc(cs.ns(|| "u"), || { - let mut t0 = self.x.get_value().get()?; - - match self.y.get_value().get()?.inverse() { - Some(invy) => { - t0.mul_assign(&invy); - - Ok(t0) - } - None => Err(SynthesisError::DivisionByZero), - } + let u = F::new_witness(cs.ns("u"), || { + let y_inv = self + .y + .value()? + .inverse() + .ok_or(SynthesisError::DivisionByZero)?; + Ok(self.x.value()? * &y_inv) })?; - u.mul_equals(cs.ns(|| "u equals"), &self.y, &self.x)?; + u.mul_equals(&self.y, &self.x)?; - let v = F::alloc(cs.ns(|| "v"), || { - let mut t0 = self.x.get_value().get()?; - let mut t1 = t0.clone(); - t0.sub_assign(&P::BaseField::one()); - t1.add_assign(&P::BaseField::one()); + let v = F::new_witness(cs.ns("v"), || { + let mut t0 = self.x.value()?; + let mut t1 = t0; + t0 -= &P::BaseField::one(); + t1 += &P::BaseField::one(); - match t1.inverse() { - Some(t1) => { - t0.mul_assign(&t1); - - Ok(t0) - } - None => Err(SynthesisError::DivisionByZero), - } + Ok(t0 * &t1.inverse().ok_or(SynthesisError::DivisionByZero)?) })?; - let xplusone = self - .x - .add_constant(cs.ns(|| "x plus one"), &P::BaseField::one())?; - let xminusone = self - .x - .sub_constant(cs.ns(|| "x minus one"), &P::BaseField::one())?; - v.mul_equals(cs.ns(|| "v equals"), &xplusone, &xminusone)?; + let xplusone = &self.x + P::BaseField::one(); + let xminusone = &self.x - P::BaseField::one(); + v.mul_equals(&xplusone, &xminusone)?; - Ok(AffineGadget::new(u, v)) + Ok(AffineVar::new(u, v)) } + } - pub fn add>( - &self, - mut cs: CS, - other: &Self, - ) -> Result { - let lambda = F::alloc(cs.ns(|| "lambda"), || { - let mut n = other.y.get_value().get()?; - n.sub_assign(&self.y.get_value().get()?); - - let mut d = other.x.get_value().get()?; - d.sub_assign(&self.x.get_value().get()?); - - match d.inverse() { - Some(d) => { - n.mul_assign(&d); - Ok(n) - } - None => Err(SynthesisError::DivisionByZero), - } - })?; - let lambda_n = other.y.sub(cs.ns(|| "other.y - self.y"), &self.y)?; - let lambda_d = other.x.sub(cs.ns(|| "other.x - self.x"), &self.x)?; - lambda_d.mul_equals(cs.ns(|| "lambda equals"), &lambda, &lambda_n)?; + impl<'a, P, F> Add<&'a MontgomeryAffineVar> for MontgomeryAffineVar + where + P: TEModelParameters, + F: FieldVar::BasePrimeField>, + for<'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>, + { + type Output = MontgomeryAffineVar; + fn add(self, other: &'a Self) -> Self::Output { + let cs = [&self, other].cs(); + let mode = if cs.is_none() || matches!(cs, Some(ConstraintSystemRef::None)) { + AllocationMode::Constant + } else { + AllocationMode::Witness + }; + let cs = cs.unwrap_or(ConstraintSystemRef::None); + + let coeff_b = P::MontgomeryModelParameters::COEFF_B; + let coeff_a = P::MontgomeryModelParameters::COEFF_A; + + let lambda = F::new_variable( + cs.ns("lambda"), + || { + let n = other.y.value()? - &self.y.value()?; + let d = other.x.value()? - &self.x.value()?; + Ok(n * &d.inverse().ok_or(SynthesisError::DivisionByZero)?) + }, + mode, + ) + .unwrap(); + let lambda_n = &other.y - &self.y; + let lambda_d = &other.x - &self.x; + lambda_d.mul_equals(&lambda, &lambda_n).unwrap(); // Compute x'' = B*lambda^2 - A - x - x' - let xprime = F::alloc(cs.ns(|| "xprime"), || { - Ok( - lambda.get_value().get()?.square() * &P::MontgomeryModelParameters::COEFF_B - - &P::MontgomeryModelParameters::COEFF_A - - &self.x.get_value().get()? - - &other.x.get_value().get()?, - ) - })?; - - let xprime_lc = self - .x - .add(cs.ns(|| "self.x + other.x"), &other.x)? - .add(cs.ns(|| "+ xprime"), &xprime)? - .add_constant(cs.ns(|| "+ A"), &P::MontgomeryModelParameters::COEFF_A)?; + let xprime = F::new_variable( + cs.ns("xprime"), + || { + Ok(lambda.value()?.square() * &coeff_b + - &coeff_a + - &self.x.value()? + - &other.x.value()?) + }, + mode, + ) + .unwrap(); + + let xprime_lc = &self.x + &other.x + &xprime + coeff_a; // (lambda) * (lambda) = (A + x + x' + x'') - let lambda_b = lambda.mul_by_constant( - cs.ns(|| "lambda * b"), - &P::MontgomeryModelParameters::COEFF_B, - )?; - lambda_b.mul_equals(cs.ns(|| "xprime equals"), &lambda, &xprime_lc)?; - - let yprime = F::alloc(cs.ns(|| "yprime"), || { - Ok(-(self.y.get_value().get()? - + &(lambda.get_value().get()? - * &(xprime.get_value().get()? - &self.x.get_value().get()?)))) - })?; - - let xres = self.x.sub(cs.ns(|| "xres"), &xprime)?; - let yres = self.y.add(cs.ns(|| "yres"), &yprime)?; - lambda.mul_equals(cs.ns(|| "yprime equals"), &xres, &yres)?; - Ok(MontgomeryAffineGadget::new(xprime, yprime)) + let lambda_b = &lambda * coeff_b; + lambda_b.mul_equals(&lambda, &xprime_lc).unwrap(); + + let yprime = F::new_variable( + cs.ns("yprime"), + || { + Ok(-(self.y.value()? + + &(lambda.value()? * &(xprime.value()? - &self.x.value()?)))) + }, + mode, + ) + .unwrap(); + + let xres = &self.x - &xprime; + let yres = &self.y + &yprime; + lambda.mul_equals(&xres, &yres).unwrap(); + MontgomeryAffineVar::new(xprime, yprime) } } } #[derive(Derivative)] #[derivative(Debug, Clone)] -#[derivative(Debug(bound = "P: TEModelParameters, ConstraintF: Field"))] #[must_use] -pub struct AffineGadget< +pub struct AffineVar< P: TEModelParameters, - ConstraintF: Field, - F: FieldGadget, -> { + F: FieldVar::BasePrimeField>, +> where + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ pub x: F, pub y: F, #[derivative(Debug = "ignore")] _params: PhantomData

, - #[derivative(Debug = "ignore")] - _engine: PhantomData, } -impl> - AffineGadget +impl::BasePrimeField>> + AffineVar +where + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, { pub fn new(x: F, y: F) -> Self { Self { x, y, _params: PhantomData, - _engine: PhantomData, } } - pub fn alloc_without_check>( - mut cs: CS, - value_gen: F, - ) -> Result - where - F: FnOnce() -> Result, SynthesisError>, - { - let (x, y) = match value_gen() { - Ok(fe) => (Ok(fe.x), Ok(fe.y)), + /// Allocates a new variable without performing an on-curve check, which is + /// useful if the variable is known to be on the curve (eg., if the point + /// is a constant or is a public input). + pub fn new_variable_omit_on_curve_check>>( + cs: impl Into::BasePrimeField>>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + + let (x, y) = match f() { + Ok(ge) => { + let ge: TEAffine

= ge.into(); + (Ok(ge.x), Ok(ge.y)) + } _ => ( Err(SynthesisError::AssignmentMissing), Err(SynthesisError::AssignmentMissing), ), }; - let x = F::alloc(&mut cs.ns(|| "x"), || x)?; - let y = F::alloc(&mut cs.ns(|| "y"), || y)?; + let x = F::new_variable(cs.ns("x"), || x, mode)?; + let y = F::new_variable(cs.ns("y"), || y, mode)?; Ok(Self::new(x, y)) } } -impl ToConstraintFieldGadget for AffineGadget +impl R1CSVar<::BasePrimeField> for AffineVar where P: TEModelParameters, - ConstraintF: PrimeField, - F: FieldGadget + ToConstraintFieldGadget, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, { - fn to_constraint_field>( - &self, - mut cs: CS, - ) -> Result>, SynthesisError> { - let mut res = Vec::new(); - - let mut x_gadget = self.x.to_constraint_field(&mut cs.ns(|| "x"))?; - let mut y_gadget = self.y.to_constraint_field(&mut cs.ns(|| "y"))?; - - res.append(&mut x_gadget); - res.append(&mut y_gadget); + type Value = TEProjective

; - Ok(res) + fn cs(&self) -> Option::BasePrimeField>> { + self.x.cs().or(self.y.cs()) } -} -impl PartialEq for AffineGadget -where - P: TEModelParameters, - ConstraintF: Field, - F: FieldGadget, -{ - fn eq(&self, other: &Self) -> bool { - self.x == other.x && self.y == other.y + #[inline] + fn value(&self) -> Result, SynthesisError> { + let (x, y) = (self.x.value()?, self.y.value()?); + let result = TEAffine::new(x, y); + Ok(result.into()) } } -impl Eq for AffineGadget +impl CurveVar, ::BasePrimeField> for AffineVar where P: TEModelParameters, - ConstraintF: Field, - F: FieldGadget, + F: FieldVar::BasePrimeField> + + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField> + + ThreeBitCondNegLookupGadget< + ::BasePrimeField, + TableConstant = P::BaseField, + >, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, { -} + fn constant(g: TEProjective

) -> Self { + let cs = ConstraintSystemRef::None; + Self::new_variable_omit_on_curve_check(cs, || Ok(g), AllocationMode::Constant).unwrap() + } -mod affine_impl { - use super::*; - use crate::Assignment; - use algebra::{curves::AffineCurve, Field, PrimeField}; - use core::ops::Neg; + fn zero() -> Self { + Self::new(F::zero(), F::one()) + } - impl GroupGadget, ConstraintF> for AffineGadget - where - P: TEModelParameters, - ConstraintF: Field, - F: FieldGadget, - { - type Value = TEAffine

; - type Variable = (F::Variable, F::Variable); - - #[inline] - fn get_value(&self) -> Option { - match (self.x.get_value(), self.y.get_value()) { - (Some(x), Some(y)) => Some(TEAffine::new(x, y)), - (..) => None, - } - } + fn is_zero(&self) -> Result::BasePrimeField>, SynthesisError> { + self.x.is_zero()?.and(&self.x.is_one()?) + } - #[inline] - fn get_variable(&self) -> Self::Variable { - (self.x.get_variable(), self.y.get_variable()) - } + fn new_variable_omit_prime_order_check( + cs: impl Into::BasePrimeField>>, + f: impl FnOnce() -> Result, SynthesisError>, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); - #[inline] - fn zero>(mut cs: CS) -> Result { - Ok(Self::new( - F::zero(cs.ns(|| "zero"))?, - F::one(cs.ns(|| "one"))?, - )) - } + let g = Self::new_variable_omit_on_curve_check(cs, f, mode)?; - /// Optimized constraints for checking Edwards point addition from ZCash - /// developers Daira Hopwood and Sean Bowe. Requires only 6 constraints - /// compared to 7 for the straightforward version we had earlier. - fn add>( - &self, - mut cs: CS, - other: &Self, - ) -> Result { - let a = P::COEFF_A; + if mode != AllocationMode::Constant { let d = P::COEFF_D; - - // Compute U = (x1 + y1) * (x2 + y2) - let u1 = self - .x - .mul_by_constant(cs.ns(|| "-A * x1"), &a.neg())? - .add(cs.ns(|| "-A * x1 + y1"), &self.y)?; - let u2 = other.x.add(cs.ns(|| "x2 + y2"), &other.y)?; - - let u = u1.mul(cs.ns(|| "(-A * x1 + y1) * (x2 + y2)"), &u2)?; - - // Compute v0 = x1 * y2 - let v0 = other.y.mul(&mut cs.ns(|| "v0"), &self.x)?; - - // Compute v1 = x2 * y1 - let v1 = other.x.mul(&mut cs.ns(|| "v1"), &self.y)?; - - // Compute C = d*v0*v1 - let v2 = v0 - .mul(cs.ns(|| "v0 * v1"), &v1)? - .mul_by_constant(cs.ns(|| "D * v0 * v1"), &d)?; - - // Compute x3 = (v0 + v1) / (1 + v2) - let x3 = F::alloc(&mut cs.ns(|| "x3"), || { - let t0 = v0.get_value().get()? + &v1.get_value().get()?; - let t1 = P::BaseField::one() + &v2.get_value().get()?; - Ok(t0 * &t1.inverse().get()?) - })?; - - let one = P::BaseField::one(); - let v2_plus_one = v2.add_constant(cs.ns(|| "v2 + 1"), &one)?; - let v0_plus_v1 = v0.add(cs.ns(|| "v0 + v1"), &v1)?; - x3.mul_equals(cs.ns(|| "check x3"), &v2_plus_one, &v0_plus_v1)?; - - // Compute y3 = (U + a * v0 - v1) / (1 - v2) - let y3 = F::alloc(&mut cs.ns(|| "y3"), || { - let t0 = - u.get_value().get()? + &(a * &v0.get_value().get()?) - &v1.get_value().get()?; - let t1 = P::BaseField::one() - &v2.get_value().get()?; - Ok(t0 * &t1.inverse().get()?) - })?; - - let one_minus_v2 = v2 - .add_constant(cs.ns(|| "v2 - 1"), &(-one))? - .negate(cs.ns(|| "1 - v2"))?; - let a_v0 = v0.mul_by_constant(cs.ns(|| "a * v0"), &a)?; - let u_plus_a_v0_minus_v1 = u - .add(cs.ns(|| "u + a * v0"), &a_v0)? - .sub(cs.ns(|| "u + a * v0 - v1"), &v1)?; - - y3.mul_equals(cs.ns(|| "check y3"), &one_minus_v2, &u_plus_a_v0_minus_v1)?; - - Ok(Self::new(x3, y3)) - } - - fn add_constant>( - &self, - mut cs: CS, - other: &TEAffine

, - ) -> Result { let a = P::COEFF_A; - let d = P::COEFF_D; - let other_x = other.x; - let other_y = other.y; - - // Compute U = (x1 + y1) * (x2 + y2) - let u1 = self - .x - .mul_by_constant(cs.ns(|| "-A * x1"), &a.neg())? - .add(cs.ns(|| "-A * x1 + y1"), &self.y)?; - let u2 = other_x + &other_y; - - let u = u1.mul_by_constant(cs.ns(|| "(-A * x1 + y1) * (x2 + y2)"), &u2)?; - - // Compute v0 = x1 * y2 - let v0 = self.x.mul_by_constant(&mut cs.ns(|| "v0"), &other_y)?; - - // Compute v1 = x2 * y1 - let v1 = self.y.mul_by_constant(&mut cs.ns(|| "v1"), &other.x)?; - - // Compute C = d*v0*v1 - let v2 = v0 - .mul(cs.ns(|| "v0 * v1"), &v1)? - .mul_by_constant(cs.ns(|| "D * v0 * v1"), &d)?; - - // Compute x3 = (v0 + v1) / (1 + v2) - let x3 = F::alloc(&mut cs.ns(|| "x3"), || { - let t0 = v0.get_value().get()? + &v1.get_value().get()?; - let t1 = P::BaseField::one() + &v2.get_value().get()?; - Ok(t0 * &t1.inverse().get()?) - })?; + // Check that ax^2 + y^2 = 1 + dx^2y^2 + // We do this by checking that ax^2 - 1 = y^2 * (dx^2 - 1) + let x2 = g.x.square()?; + let y2 = g.y.square()?; let one = P::BaseField::one(); - let v2_plus_one = v2.add_constant(cs.ns(|| "v2 + 1"), &one)?; - let v0_plus_v1 = v0.add(cs.ns(|| "v0 + v1"), &v1)?; - x3.mul_equals(cs.ns(|| "check x3"), &v2_plus_one, &v0_plus_v1)?; - - // Compute y3 = (U + a * v0 - v1) / (1 - v2) - let y3 = F::alloc(&mut cs.ns(|| "y3"), || { - let t0 = - u.get_value().get()? + &(a * &v0.get_value().get()?) - &v1.get_value().get()?; - let t1 = P::BaseField::one() - &v2.get_value().get()?; - Ok(t0 * &t1.inverse().get()?) - })?; + let d_x2_minus_one = &x2 * d - one; + let a_x2_minus_one = &x2 * a - one; - let one_minus_v2 = v2 - .add_constant(cs.ns(|| "v2 - 1"), &(-one))? - .negate(cs.ns(|| "1 - v2"))?; - let a_v0 = v0.mul_by_constant(cs.ns(|| "a * v0"), &a)?; - let u_plus_a_v0_minus_v1 = u - .add(cs.ns(|| "u + a * v0"), &a_v0)? - .sub(cs.ns(|| "u + a * v0 - v1"), &v1)?; + d_x2_minus_one.mul_equals(&y2, &a_x2_minus_one)?; + } + Ok(g) + } - y3.mul_equals(cs.ns(|| "check y3"), &one_minus_v2, &u_plus_a_v0_minus_v1)?; + /// Enforce that `self` is in the prime-order subgroup. + /// + /// Does so by multiplying by the prime order, and checking that the result + /// is unchanged. + fn enforce_prime_order(&self) -> Result<(), SynthesisError> { + let r_minus_1 = (-P::ScalarField::one()).into_repr(); + + let mut seen_one = false; + let mut result = Self::zero(); + for b in BitIterator::new(r_minus_1) { + let old_seen_one = seen_one; + if seen_one { + result.double_in_place()?; + } else { + seen_one = b; + } - Ok(Self::new(x3, y3)) + if b { + result = if old_seen_one { + result + self + } else { + self.clone() + }; + } } + self.negate()?.enforce_equal(&result)?; + Ok(()) + } - fn double_in_place>( - &mut self, - mut cs: CS, - ) -> Result<(), SynthesisError> { + #[inline] + fn double_in_place(&mut self) -> Result<(), SynthesisError> { + if let Some(cs) = self.cs() { let a = P::COEFF_A; // xy - let xy = self.x.mul(cs.ns(|| "x * y"), &self.y)?; - let x2 = self.x.square(cs.ns(|| "x * x"))?; - let y2 = self.y.square(cs.ns(|| "y * y"))?; + let xy = &self.x * &self.y; + let x2 = self.x.square()?; + let y2 = self.y.square()?; - let a_x2 = x2.mul_by_constant(cs.ns(|| "a * x^2"), &a)?; + let a_x2 = &x2 * a; // Compute x3 = (2xy) / (ax^2 + y^2) - let x3 = F::alloc(&mut cs.ns(|| "x3"), || { - let t0 = xy.get_value().get()?.double(); - let t1 = a * &x2.get_value().get()? + &y2.get_value().get()?; - Ok(t0 * &t1.inverse().get()?) + let x3 = F::new_witness(cs.ns("x3"), || { + let t0 = xy.value()?.double(); + let t1 = a * &x2.value()? + &y2.value()?; + Ok(t0 * &t1.inverse().ok_or(SynthesisError::DivisionByZero)?) })?; - let a_x2_plus_y2 = a_x2.add(cs.ns(|| "v2 + 1"), &y2)?; - let two_xy = xy.double(cs.ns(|| "2xy"))?; - x3.mul_equals(cs.ns(|| "check x3"), &a_x2_plus_y2, &two_xy)?; + let a_x2_plus_y2 = &a_x2 + &y2; + let two_xy = xy.double()?; + x3.mul_equals(&a_x2_plus_y2, &two_xy)?; // Compute y3 = (y^2 - ax^2) / (2 - ax^2 - y^2) let two = P::BaseField::one().double(); - let y3 = F::alloc(&mut cs.ns(|| "y3"), || { - let a_x2 = a * &x2.get_value().get()?; - let t0 = y2.get_value().get()? - &a_x2; - let t1 = two - &a_x2 - &y2.get_value().get()?; - Ok(t0 * &t1.inverse().get()?) + let y3 = F::new_witness(cs.ns("y3"), || { + let a_x2 = a * &x2.value()?; + let t0 = y2.value()? - &a_x2; + let t1 = two - &a_x2 - &y2.value()?; + Ok(t0 * &t1.inverse().ok_or(SynthesisError::DivisionByZero)?) })?; - let y2_minus_a_x2 = y2.sub(cs.ns(|| "y^2 - ax^2"), &a_x2)?; - let two_minus_ax2_minus_y2 = a_x2 - .add(cs.ns(|| "ax2 + y2"), &y2)? - .negate(cs.ns(|| "-ax2 - y2"))? - .add_constant(cs.ns(|| "2 -ax2 - y2"), &two)?; - - y3.mul_equals( - cs.ns(|| "check y3"), - &two_minus_ax2_minus_y2, - &y2_minus_a_x2, - )?; + let y2_minus_a_x2 = &y2 - &a_x2; + let two_minus_ax2_minus_y2 = (&a_x2 + &y2).negate()? + two; + + y3.mul_equals(&two_minus_ax2_minus_y2, &y2_minus_a_x2)?; self.x = x3; self.y = y3; - - Ok(()) + } else { + let value = self.value()?; + *self = Self::constant(value.double()); } + Ok(()) + } - fn negate>( - &self, - mut cs: CS, - ) -> Result { - Ok(Self::new( - self.x.negate(cs.ns(|| "negate x"))?, - self.y.clone(), - )) - } + fn negate(&self) -> Result { + Ok(Self::new(self.x.negate()?, self.y.clone())) + } - fn cost_of_add() -> usize { - 4 + 2 * F::cost_of_mul() + fn precomputed_base_scalar_mul<'a, I, B>( + &mut self, + scalar_bits_with_base_powers: I, + ) -> Result<(), SynthesisError> + where + I: Iterator)>, + B: Borrow::BasePrimeField>>, + { + let scalar_bits_with_base_powers = scalar_bits_with_base_powers + .map(|(bit, base)| (bit.borrow().clone(), (*base).into())) + .collect::)>>(); + let zero = TEProjective::zero(); + for bits_base_powers in scalar_bits_with_base_powers.chunks(2) { + if bits_base_powers.len() == 2 { + let bits = [bits_base_powers[0].0.clone(), bits_base_powers[1].0.clone()]; + let base_powers = [&bits_base_powers[0].1, &bits_base_powers[1].1]; + + let mut table = [ + zero, + *base_powers[0], + *base_powers[1], + *base_powers[0] + base_powers[1], + ]; + + TEProjective::batch_normalization(&mut table); + let x_s = [table[0].x, table[1].x, table[2].x, table[3].x]; + let y_s = [table[0].y, table[1].y, table[2].y, table[3].y]; + + let x = F::two_bit_lookup(&bits, &x_s)?; + let y = F::two_bit_lookup(&bits, &y_s)?; + *self += Self::new(x, y); + } else if bits_base_powers.len() == 1 { + let bit = bits_base_powers[0].0.clone(); + let base_power = bits_base_powers[0].1; + let new_encoded = &*self + base_power; + *self = bit.select(&new_encoded, &self)?; + } } - fn cost_of_double() -> usize { - 4 + F::cost_of_mul() - } + Ok(()) } - impl AllocGadget, ConstraintF> for AffineGadget + fn precomputed_base_3_bit_signed_digit_scalar_mul<'a, I, J, B>( + bases: &[B], + scalars: &[J], + ) -> Result where - P: TEModelParameters, - ConstraintF: Field, - F: FieldGadget, - Self: GroupGadget, ConstraintF>, + I: Borrow<[Boolean<::BasePrimeField>]>, + J: Borrow<[I]>, + B: Borrow<[TEProjective

]>, { - #[inline] - fn alloc_constant>( - mut cs: CS, - t: T, - ) -> Result - where - T: Borrow>, - { - let p = t.borrow(); - Ok(Self { - x: F::alloc_constant(cs.ns(|| "x"), &p.x)?, - y: F::alloc_constant(cs.ns(|| "y"), &p.y)?, - _params: PhantomData, - _engine: PhantomData, - }) - } + const CHUNK_SIZE: usize = 3; + let mut ed_result: Option> = None; + let mut result: Option> = None; + + let mut process_segment_result = |result: &MontgomeryAffineVar| { + let sgmt_result = result.into_edwards()?; + ed_result = match ed_result.as_ref() { + None => Some(sgmt_result), + Some(r) => Some(sgmt_result + r), + }; + Ok::<(), SynthesisError>(()) + }; - fn alloc>( - mut cs: CS, - value_gen: FN, - ) -> Result - where - FN: FnOnce() -> Result, - T: Borrow>, - { - let (x, y) = match value_gen() { - Ok(ge) => { - let ge = *ge.borrow(); - (Ok(ge.x), Ok(ge.y)) + // Compute ∏(h_i^{m_i}) for all i. + for (segment_bits_chunks, segment_powers) in scalars.iter().zip(bases) { + for (bits, base_power) in segment_bits_chunks + .borrow() + .iter() + .zip(segment_powers.borrow()) + { + let base_power = base_power.borrow(); + let mut acc_power = *base_power; + let mut coords = vec![]; + for _ in 0..4 { + coords.push(acc_power); + acc_power += base_power; } - _ => ( - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - ), - }; - let d = P::COEFF_D; - let a = P::COEFF_A; + let bits = bits.borrow().to_bits()?; + if bits.len() != CHUNK_SIZE { + return Err(SynthesisError::Unsatisfiable); + } - let x = F::alloc(&mut cs.ns(|| "x"), || x)?; - let y = F::alloc(&mut cs.ns(|| "y"), || y)?; + let coords = coords + .iter() + .map(|p| MontgomeryAffineVar::from_edwards_to_coords(&p.into_affine())) + .collect::, _>>()?; - // Check that ax^2 + y^2 = 1 + dx^2y^2 - // We do this by checking that ax^2 - 1 = y^2 * (dx^2 - 1) - let x2 = x.square(&mut cs.ns(|| "x^2"))?; - let y2 = y.square(&mut cs.ns(|| "y^2"))?; + let x_coeffs = coords.iter().map(|p| p.0).collect::>(); + let y_coeffs = coords.iter().map(|p| p.1).collect::>(); - let one = P::BaseField::one(); - let d_x2_minus_one = x2 - .mul_by_constant(cs.ns(|| "d * x^2"), &d)? - .add_constant(cs.ns(|| "d * x^2 - 1"), &one.neg())?; + let precomp = bits[0].and(&bits[1])?; + + let x = F::zero() + + x_coeffs[0] + + F::from(bits[0].clone()) * (x_coeffs[1] - &x_coeffs[0]) + + F::from(bits[1].clone()) * (x_coeffs[2] - &x_coeffs[0]) + + F::from(precomp.clone()) + * (x_coeffs[3] - &x_coeffs[2] - &x_coeffs[1] + &x_coeffs[0]); - let a_x2_minus_one = x2 - .mul_by_constant(cs.ns(|| "a * x^2"), &a)? - .add_constant(cs.ns(|| "a * x^2 - 1"), &one.neg())?; + let y = F::three_bit_cond_neg_lookup(&bits, &precomp, &y_coeffs)?; - d_x2_minus_one.mul_equals(cs.ns(|| "on curve check"), &y2, &a_x2_minus_one)?; - Ok(Self::new(x, y)) + let tmp = MontgomeryAffineVar::new(x, y); + result = match result.as_ref() { + None => Some(tmp), + Some(r) => Some(tmp + r), + }; + } + + process_segment_result(&result.unwrap())?; + result = None; + } + if result.is_some() { + process_segment_result(&result.unwrap())?; } + Ok(ed_result.unwrap()) + } +} - fn alloc_checked>( - mut cs: CS, - value_gen: FN, - ) -> Result - where - FN: FnOnce() -> Result, - T: Borrow>, - { - let cofactor_weight = BitIterator::new(P::COFACTOR).filter(|b| *b).count(); - // If we multiply by r, we actually multiply by r - 2. - let r_minus_1 = (-P::ScalarField::one()).into_repr(); - let r_weight = BitIterator::new(&r_minus_1).filter(|b| *b).count(); - - // We pick the most efficient method of performing the prime order check: - // If the cofactor has lower hamming weight than the scalar field's modulus, - // we first multiply by the inverse of the cofactor, and then, after allocating, - // multiply by the cofactor. This ensures the resulting point has no cofactors - // - // Else, we multiply by the scalar field's modulus and ensure that the result - // is zero. - if cofactor_weight < r_weight { - let ge = Self::alloc(cs.ns(|| "Alloc checked"), || { - value_gen().map(|ge| ge.borrow().mul_by_cofactor_inv()) - })?; - let mut seen_one = false; - let mut result = Self::zero(cs.ns(|| "result"))?; - for (i, b) in BitIterator::new(P::COFACTOR).enumerate() { - let mut cs = cs.ns(|| format!("Iteration {}", i)); +impl AllocVar, ::BasePrimeField> for AffineVar +where + P: TEModelParameters, + F: FieldVar::BasePrimeField> + + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField> + + ThreeBitCondNegLookupGadget< + ::BasePrimeField, + TableConstant = P::BaseField, + >, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + fn new_variable>>( + cs: impl Into::BasePrimeField>>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + let f = || Ok(*f()?.borrow()); + match mode { + AllocationMode::Constant => Self::new_variable_omit_prime_order_check(cs, f, mode), + AllocationMode::Input => Self::new_variable_omit_prime_order_check(cs, f, mode), + AllocationMode::Witness => { + // if cofactor.is_even(): + // divide until you've removed all even factors + // else: + // just directly use double and add. + let mut power_of_2: u32 = 0; + let mut cofactor = P::COFACTOR.to_vec(); + while cofactor[0] % 2 == 0 { + div2(&mut cofactor); + power_of_2 += 1; + } - let old_seen_one = seen_one; - if seen_one { - result.double_in_place(cs.ns(|| "Double"))?; - } else { - seen_one = b; - } + let cofactor_weight = BitIterator::new(cofactor.as_slice()).filter(|b| *b).count(); + let modulus_minus_1 = (-P::ScalarField::one()).into_repr(); // r - 1 + let modulus_minus_1_weight = + BitIterator::new(modulus_minus_1).filter(|b| *b).count(); + + // We pick the most efficient method of performing the prime order check: + // If the cofactor has lower hamming weight than the scalar field's modulus, + // we first multiply by the inverse of the cofactor, and then, after allocating, + // multiply by the cofactor. This ensures the resulting point has no cofactors + // + // Else, we multiply by the scalar field's modulus and ensure that the result + // equals the identity. + + let (mut ge, iter) = if cofactor_weight < modulus_minus_1_weight { + let ge = Self::new_variable_omit_prime_order_check( + cs.ns("Witness without subgroup check with cofactor mul"), + || f().map(|g| g.borrow().into_affine().mul_by_cofactor_inv().into()), + mode, + )?; + (ge, BitIterator::new(cofactor.as_slice())) + } else { + let ge = Self::new_variable_omit_prime_order_check( + cs.ns("Witness without subgroup check with `r` check"), + || { + f().map(|g| { + let g = g.into_affine(); + let mut power_of_two = P::ScalarField::one().into_repr(); + power_of_two.muln(power_of_2); + let power_of_two_inv = + P::ScalarField::from(power_of_two).inverse().unwrap(); + g.mul(power_of_two_inv) + }) + }, + mode, + )?; - if b { - result = if old_seen_one { - result.add(cs.ns(|| "Add"), &ge)? - } else { - ge.clone() - }; - } + (ge, BitIterator::new(modulus_minus_1.as_ref())) + }; + // Remove the even part of the cofactor + for _ in 0..power_of_2 { + ge.double_in_place()?; } - Ok(result) - } else { - let ge = Self::alloc(cs.ns(|| "Alloc checked"), value_gen)?; - let mut seen_one = false; - let mut result = Self::zero(cs.ns(|| "result"))?; - // Returns bits in big-endian order - for (i, b) in BitIterator::new(r_minus_1).enumerate() { - let mut cs = cs.ns(|| format!("Iteration {}", i)); + let mut seen_one = false; + let mut result = Self::zero(); + for b in iter { let old_seen_one = seen_one; if seen_one { - result.double_in_place(cs.ns(|| "Double"))?; + result.double_in_place()?; } else { seen_one = b; } if b { result = if old_seen_one { - result.add(cs.ns(|| "Add"), &ge)? + result + &ge } else { ge.clone() }; } } - let neg_ge = ge.negate(cs.ns(|| "Negate ge"))?; - neg_ge.enforce_equal(cs.ns(|| "Check equals"), &result)?; - Ok(ge) - } - } - - fn alloc_input>( - mut cs: CS, - value_gen: FN, - ) -> Result - where - FN: FnOnce() -> Result, - T: Borrow>, - { - let (x, y) = match value_gen() { - Ok(ge) => { - let ge = *ge.borrow(); - (Ok(ge.x), Ok(ge.y)) + if cofactor_weight < modulus_minus_1_weight { + Ok(result) + } else { + ge.enforce_equal(&ge)?; + Ok(ge) } - _ => ( - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - ), - }; - - let d = P::COEFF_D; - let a = P::COEFF_A; - - let x = F::alloc_input(&mut cs.ns(|| "x"), || x)?; - let y = F::alloc_input(&mut cs.ns(|| "y"), || y)?; - - // Check that ax^2 + y^2 = 1 + dx^2y^2 - // We do this by checking that ax^2 - 1 = y^2 * (dx^2 - 1) - let x2 = x.square(&mut cs.ns(|| "x^2"))?; - let y2 = y.square(&mut cs.ns(|| "y^2"))?; - - let one = P::BaseField::one(); - let d_x2_minus_one = x2 - .mul_by_constant(cs.ns(|| "d * x^2"), &d)? - .add_constant(cs.ns(|| "d * x^2 - 1"), &one.neg())?; - - let a_x2_minus_one = x2 - .mul_by_constant(cs.ns(|| "a * x^2"), &a)? - .add_constant(cs.ns(|| "a * x^2 - 1"), &one.neg())?; - - d_x2_minus_one.mul_equals(cs.ns(|| "on curve check"), &y2, &a_x2_minus_one)?; - Ok(Self::new(x, y)) + } } } } -mod projective_impl { - use super::*; - use crate::{Assignment, Vec}; - use algebra::{ - curves::twisted_edwards_extended::GroupProjective as TEProjective, AffineCurve, Field, - PrimeField, ProjectiveCurve, - }; - use core::ops::Neg; - - impl GroupGadget, ConstraintF> - for AffineGadget - where - P: TEModelParameters, - ConstraintF: Field, - F: FieldGadget, - { - type Value = TEProjective

; - type Variable = (F::Variable, F::Variable); - - #[inline] - fn get_value(&self) -> Option { - match (self.x.get_value(), self.y.get_value()) { - (Some(x), Some(y)) => Some(TEAffine::new(x, y).into()), - (..) => None, - } - } - - #[inline] - fn get_variable(&self) -> Self::Variable { - (self.x.get_variable(), self.y.get_variable()) - } - - #[inline] - fn zero>(mut cs: CS) -> Result { - Ok(Self::new( - F::zero(cs.ns(|| "zero"))?, - F::one(cs.ns(|| "one"))?, - )) - } - - /// Optimized constraints for checking Edwards point addition from ZCash - /// developers Daira Hopwood and Sean Bowe. Requires only 6 constraints - /// compared to 7 for the straightforward version we had earlier. - fn add>( - &self, - mut cs: CS, - other: &Self, - ) -> Result { - let a = P::COEFF_A; - let d = P::COEFF_D; - - // Compute U = (x1 + y1) * (x2 + y2) - let u1 = self - .x - .mul_by_constant(cs.ns(|| "-A * x1"), &a.neg())? - .add(cs.ns(|| "-A * x1 + y1"), &self.y)?; - let u2 = other.x.add(cs.ns(|| "x2 + y2"), &other.y)?; - - let u = u1.mul(cs.ns(|| "(-A * x1 + y1) * (x2 + y2)"), &u2)?; - - // Compute v0 = x1 * y2 - let v0 = other.y.mul(&mut cs.ns(|| "v0"), &self.x)?; - - // Compute v1 = x2 * y1 - let v1 = other.x.mul(&mut cs.ns(|| "v1"), &self.y)?; - - // Compute C = d*v0*v1 - let v2 = v0 - .mul(cs.ns(|| "v0 * v1"), &v1)? - .mul_by_constant(cs.ns(|| "D * v0 * v1"), &d)?; - - // Compute x3 = (v0 + v1) / (1 + v2) - let x3 = F::alloc(&mut cs.ns(|| "x3"), || { - let t0 = v0.get_value().get()? + &v1.get_value().get()?; - let t1 = P::BaseField::one() + &v2.get_value().get()?; - Ok(t0 * &t1.inverse().get()?) - })?; - - let one = P::BaseField::one(); - let v2_plus_one = v2.add_constant(cs.ns(|| "v2 + 1"), &one)?; - let v0_plus_v1 = v0.add(cs.ns(|| "v0 + v1"), &v1)?; - x3.mul_equals(cs.ns(|| "check x3"), &v2_plus_one, &v0_plus_v1)?; - - // Compute y3 = (U + a * v0 - v1) / (1 - v2) - let y3 = F::alloc(&mut cs.ns(|| "y3"), || { - let t0 = - u.get_value().get()? + &(a * &v0.get_value().get()?) - &v1.get_value().get()?; - let t1 = P::BaseField::one() - &v2.get_value().get()?; - Ok(t0 * &t1.inverse().get()?) - })?; - - let one_minus_v2 = v2 - .add_constant(cs.ns(|| "v2 - 1"), &(-one))? - .negate(cs.ns(|| "1 - v2"))?; - let a_v0 = v0.mul_by_constant(cs.ns(|| "a * v0"), &a)?; - let u_plus_a_v0_minus_v1 = u - .add(cs.ns(|| "u + a * v0"), &a_v0)? - .sub(cs.ns(|| "u + a * v0 - v1"), &v1)?; - - y3.mul_equals(cs.ns(|| "check y3"), &one_minus_v2, &u_plus_a_v0_minus_v1)?; +impl AllocVar, ::BasePrimeField> for AffineVar +where + P: TEModelParameters, + F: FieldVar::BasePrimeField> + + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField> + + ThreeBitCondNegLookupGadget< + ::BasePrimeField, + TableConstant = P::BaseField, + >, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + fn new_variable>>( + cs: impl Into::BasePrimeField>>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + Self::new_variable(cs, || f().map(|b| b.borrow().into_projective()), mode) + } +} - Ok(Self::new(x3, y3)) - } +#[inline] +fn div2(limbs: &mut [u64]) { + let mut t = 0; + for i in limbs.iter_mut().rev() { + let t2 = *i << 63; + *i >>= 1; + *i |= t; + t = t2; + } +} - fn add_constant>( - &self, - mut cs: CS, - other: &TEProjective

, - ) -> Result { +impl_bounded_ops!( + AffineVar, + TEProjective

, + Add, + add, + AddAssign, + add_assign, + |this: &'a AffineVar, other: &'a AffineVar| { + if let Some(cs) = [this, other].cs() { let a = P::COEFF_A; let d = P::COEFF_D; - let other = other.into_affine(); - let other_x = other.x; - let other_y = other.y; // Compute U = (x1 + y1) * (x2 + y2) - let u1 = self - .x - .mul_by_constant(cs.ns(|| "-A * x1"), &a.neg())? - .add(cs.ns(|| "-A * x1 + y1"), &self.y)?; - let u2 = other_x + &other_y; + let u1 = (&this.x * -a) + &this.y; + let u2 = &other.x + &other.y; - let u = u1.mul_by_constant(cs.ns(|| "(-A * x1 + y1) * (x2 + y2)"), &u2)?; + let u = u1 * &u2; // Compute v0 = x1 * y2 - let v0 = self.x.mul_by_constant(&mut cs.ns(|| "v0"), &other_y)?; + let v0 = &other.y * &this.x; // Compute v1 = x2 * y1 - let v1 = self.y.mul_by_constant(&mut cs.ns(|| "v1"), &other.x)?; + let v1 = &other.x * &this.y; // Compute C = d*v0*v1 - let v2 = v0 - .mul(cs.ns(|| "v0 * v1"), &v1)? - .mul_by_constant(cs.ns(|| "D * v0 * v1"), &d)?; + let v2 = &v0 * &v1 * d; // Compute x3 = (v0 + v1) / (1 + v2) - let x3 = F::alloc(&mut cs.ns(|| "x3"), || { - let t0 = v0.get_value().get()? + &v1.get_value().get()?; - let t1 = P::BaseField::one() + &v2.get_value().get()?; - Ok(t0 * &t1.inverse().get()?) - })?; + let x3 = F::new_witness(cs.ns("x3"), || { + let t0 = v0.value()? + &v1.value()?; + let t1 = P::BaseField::one() + &v2.value()?; + Ok(t0 * &t1.inverse().ok_or(SynthesisError::DivisionByZero)?) + }).unwrap(); - let one = P::BaseField::one(); - let v2_plus_one = v2.add_constant(cs.ns(|| "v2 + 1"), &one)?; - let v0_plus_v1 = v0.add(cs.ns(|| "v0 + v1"), &v1)?; - x3.mul_equals(cs.ns(|| "check x3"), &v2_plus_one, &v0_plus_v1)?; + let v2_plus_one = &v2 + P::BaseField::one(); + let v0_plus_v1 = &v0 + &v1; + x3.mul_equals(&v2_plus_one, &v0_plus_v1).unwrap(); // Compute y3 = (U + a * v0 - v1) / (1 - v2) - let y3 = F::alloc(&mut cs.ns(|| "y3"), || { - let t0 = - u.get_value().get()? + &(a * &v0.get_value().get()?) - &v1.get_value().get()?; - let t1 = P::BaseField::one() - &v2.get_value().get()?; - Ok(t0 * &t1.inverse().get()?) - })?; + let y3 = F::new_witness(cs.ns("y3"), || { + let t0 = u.value()? + &(a * &v0.value()?) - &v1.value()?; + let t1 = P::BaseField::one() - &v2.value()?; + Ok(t0 * &t1.inverse().ok_or(SynthesisError::DivisionByZero)?) + }).unwrap(); - let one_minus_v2 = v2 - .add_constant(cs.ns(|| "v2 - 1"), &(-one))? - .negate(cs.ns(|| "1 - v2"))?; - let a_v0 = v0.mul_by_constant(cs.ns(|| "a * v0"), &a)?; - let u_plus_a_v0_minus_v1 = u - .add(cs.ns(|| "u + a * v0"), &a_v0)? - .sub(cs.ns(|| "u + a * v0 - v1"), &v1)?; + let one_minus_v2 = (&v2 - P::BaseField::one()).negate().unwrap(); + let a_v0 = &v0 * a; + let u_plus_a_v0_minus_v1 = &u + &a_v0 - &v1; - y3.mul_equals(cs.ns(|| "check y3"), &one_minus_v2, &u_plus_a_v0_minus_v1)?; + y3.mul_equals(&one_minus_v2, &u_plus_a_v0_minus_v1).unwrap(); - Ok(Self::new(x3, y3)) + AffineVar::new(x3, y3) + } else { + assert!(this.is_constant() && other.is_constant()); + AffineVar::constant(this.value().unwrap() + &other.value().unwrap()) } - - fn double_in_place>( - &mut self, - mut cs: CS, - ) -> Result<(), SynthesisError> { - let a = P::COEFF_A; - - // xy - let xy = self.x.mul(cs.ns(|| "x * y"), &self.y)?; - let x2 = self.x.square(cs.ns(|| "x * x"))?; - let y2 = self.y.square(cs.ns(|| "y * y"))?; - - let a_x2 = x2.mul_by_constant(cs.ns(|| "a * x^2"), &a)?; - - // Compute x3 = (2xy) / (ax^2 + y^2) - let x3 = F::alloc(&mut cs.ns(|| "x3"), || { - let t0 = xy.get_value().get()?.double(); - let t1 = a * &x2.get_value().get()? + &y2.get_value().get()?; - Ok(t0 * &t1.inverse().get()?) - })?; - - let a_x2_plus_y2 = a_x2.add(cs.ns(|| "v2 + 1"), &y2)?; - let two_xy = xy.double(cs.ns(|| "2xy"))?; - x3.mul_equals(cs.ns(|| "check x3"), &a_x2_plus_y2, &two_xy)?; - - // Compute y3 = (y^2 - ax^2) / (2 - ax^2 - y^2) - let two = P::BaseField::one().double(); - let y3 = F::alloc(&mut cs.ns(|| "y3"), || { - let a_x2 = a * &x2.get_value().get()?; - let t0 = y2.get_value().get()? - &a_x2; - let t1 = two - &a_x2 - &y2.get_value().get()?; - Ok(t0 * &t1.inverse().get()?) - })?; - let y2_minus_a_x2 = y2.sub(cs.ns(|| "y^2 - ax^2"), &a_x2)?; - let two_minus_ax2_minus_y2 = a_x2 - .add(cs.ns(|| "ax2 + y2"), &y2)? - .negate(cs.ns(|| "-ax2 - y2"))? - .add_constant(cs.ns(|| "2 -ax2 - y2"), &two)?; - - y3.mul_equals( - cs.ns(|| "check y3"), - &two_minus_ax2_minus_y2, - &y2_minus_a_x2, - )?; - self.x = x3; - self.y = y3; - - Ok(()) - } - - fn negate>( - &self, - mut cs: CS, - ) -> Result { - Ok(Self::new( - self.x.negate(cs.ns(|| "negate x"))?, - self.y.clone(), - )) - } - - fn precomputed_base_scalar_mul<'a, CS, I, B>( - &mut self, - mut cs: CS, - scalar_bits_with_base_powers: I, - ) -> Result<(), SynthesisError> - where - CS: ConstraintSystem, - I: Iterator)>, - B: Borrow, - { - let scalar_bits_with_base_powers: Vec<_> = scalar_bits_with_base_powers - .map(|(bit, base)| (bit.borrow().clone(), base.clone())) - .collect(); - let zero = TEProjective::zero(); - for (i, bits_base_powers) in scalar_bits_with_base_powers.chunks(2).enumerate() { - let mut cs = cs.ns(|| format!("Chunk {}", i)); - if bits_base_powers.len() == 2 { - let bits = [bits_base_powers[0].0, bits_base_powers[1].0]; - let base_powers = [bits_base_powers[0].1, bits_base_powers[1].1]; - - let mut table = [ - zero, - base_powers[0], - base_powers[1], - base_powers[0] + &base_powers[1], - ]; - - TEProjective::batch_normalization(&mut table); - let x_s = [table[0].x, table[1].x, table[2].x, table[3].x]; - let y_s = [table[0].y, table[1].y, table[2].y, table[3].y]; - - let x: F = F::two_bit_lookup(cs.ns(|| "Lookup x"), &bits, &x_s)?; - let y: F = F::two_bit_lookup(cs.ns(|| "Lookup y"), &bits, &y_s)?; - let adder: Self = Self::new(x, y); - *self = , ConstraintF>>::add( - self, - &mut cs.ns(|| "Add"), - &adder, - )?; - } else if bits_base_powers.len() == 1 { - let bit = bits_base_powers[0].0; - let base_power = bits_base_powers[0].1; - let new_encoded = - self.add_constant(&mut cs.ns(|| "Add base power"), &base_power)?; - *self = Self::conditionally_select( - &mut cs.ns(|| "Conditional Select"), - &bit, - &new_encoded, - &self, - )?; - } - } - - Ok(()) - } - - fn precomputed_base_3_bit_signed_digit_scalar_mul<'a, CS, I, J, B>( - mut cs: CS, - bases: &[B], - scalars: &[J], - ) -> Result - where - CS: ConstraintSystem, - I: Borrow<[Boolean]>, - J: Borrow<[I]>, - B: Borrow<[TEProjective

]>, - { - const CHUNK_SIZE: usize = 3; - let mut edwards_result: Option> = None; - let mut result: Option> = None; - - let mut process_segment_result = - |mut cs: r1cs_core::Namespace<_, _>, - result: &MontgomeryAffineGadget| - -> Result<(), SynthesisError> { - let segment_result = result.into_edwards(cs.ns(|| "segment result"))?; - match edwards_result { - None => { - edwards_result = Some(segment_result); - } - Some(ref mut edwards_result) => { - *edwards_result = GroupGadget::, ConstraintF>::add( - &segment_result, - cs.ns(|| "edwards addition"), - edwards_result, - )?; - } - } - - Ok(()) - }; - - // Compute ∏(h_i^{m_i}) for all i. - for (segment_i, (segment_bits_chunks, segment_powers)) in - scalars.iter().zip(bases.iter()).enumerate() - { - for (i, (bits, base_power)) in segment_bits_chunks - .borrow() - .iter() - .zip(segment_powers.borrow().iter()) - .enumerate() - { - let base_power = base_power.borrow(); - let mut acc_power = *base_power; - let mut coords = vec![]; - for _ in 0..4 { - coords.push(acc_power); - acc_power += base_power; - } - - let bits = bits.borrow().to_bits( - &mut cs.ns(|| format!("Convert Scalar {}, {} to bits", segment_i, i)), - )?; - if bits.len() != CHUNK_SIZE { - return Err(SynthesisError::Unsatisfiable); - } - - let coords = coords - .iter() - .map(|p| { - let p = p.into_affine(); - MontgomeryAffineGadget::::from_edwards_to_coords(&p) - .unwrap() - }) - .collect::>(); - - let x_coeffs = coords.iter().map(|p| p.0).collect::>(); - let y_coeffs = coords.iter().map(|p| p.1).collect::>(); - - let precomp = Boolean::and( - cs.ns(|| format!("precomp in window {}, {}", segment_i, i)), - &bits[0], - &bits[1], - )?; - - let x = F::zero(cs.ns(|| format!("x in window {}, {}", segment_i, i)))? - .conditionally_add_constant( - cs.ns(|| format!("add bool 00 in window {}, {}", segment_i, i)), - &Boolean::constant(true), - x_coeffs[0], - )? - .conditionally_add_constant( - cs.ns(|| format!("add bool 01 in window {}, {}", segment_i, i)), - &bits[0], - x_coeffs[1] - &x_coeffs[0], - )? - .conditionally_add_constant( - cs.ns(|| format!("add bool 10 in window {}, {}", segment_i, i)), - &bits[1], - x_coeffs[2] - &x_coeffs[0], - )? - .conditionally_add_constant( - cs.ns(|| format!("add bool 11 in window {}, {}", segment_i, i)), - &precomp, - x_coeffs[3] - &x_coeffs[2] - &x_coeffs[1] + &x_coeffs[0], - )?; - - let y = F::three_bit_cond_neg_lookup( - cs.ns(|| format!("y lookup in window {}, {}", segment_i, i)), - &bits, - &precomp, - &y_coeffs, - )?; - - let tmp = MontgomeryAffineGadget::new(x, y); - - match result { - None => { - result = Some(tmp); - } - Some(ref mut result) => { - *result = tmp.add( - cs.ns(|| format!("addition of window {}, {}", segment_i, i)), - result, - )?; - } - } - } - - process_segment_result( - cs.ns(|| format!("window {}", segment_i)), - &result.unwrap(), - )?; - result = None; - } - if result.is_some() { - process_segment_result(cs.ns(|| "leftover"), &result.unwrap())?; - } - Ok(edwards_result.unwrap()) - } - - fn cost_of_add() -> usize { - 4 + 2 * F::cost_of_mul() - } - - fn cost_of_double() -> usize { - 4 + F::cost_of_mul() - } - } - - impl AllocGadget, ConstraintF> - for AffineGadget - where + }, + |this: &'a AffineVar, other: TEProjective

| this + AffineVar::constant(other), + ( + F :FieldVar::BasePrimeField> + + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField> + + ThreeBitCondNegLookupGadget<::BasePrimeField, TableConstant = P::BaseField>, P: TEModelParameters, - ConstraintF: Field, - F: FieldGadget, - Self: GroupGadget, ConstraintF>, - { - #[inline] - fn alloc_constant>( - mut cs: CS, - t: T, - ) -> Result - where - T: Borrow>, - { - let p = t.borrow().into_affine(); - Ok(Self { - x: F::alloc_constant(cs.ns(|| "x"), &p.x)?, - y: F::alloc_constant(cs.ns(|| "y"), &p.y)?, - _params: PhantomData, - _engine: PhantomData, - }) - } - - fn alloc>( - mut cs: CS, - value_gen: FN, - ) -> Result - where - FN: FnOnce() -> Result, - T: Borrow>, - { - let (x, y) = match value_gen() { - Ok(ge) => { - let ge = ge.borrow().into_affine(); - (Ok(ge.x), Ok(ge.y)) - } - _ => ( - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - ), - }; - - let d = P::COEFF_D; - let a = P::COEFF_A; - - let x = F::alloc(&mut cs.ns(|| "x"), || x)?; - let y = F::alloc(&mut cs.ns(|| "y"), || y)?; - - // Check that ax^2 + y^2 = 1 + dx^2y^2 - // We do this by checking that ax^2 - 1 = y^2 * (dx^2 - 1) - let x2 = x.square(&mut cs.ns(|| "x^2"))?; - let y2 = y.square(&mut cs.ns(|| "y^2"))?; - - let one = P::BaseField::one(); - let d_x2_minus_one = x2 - .mul_by_constant(cs.ns(|| "d * x^2"), &d)? - .add_constant(cs.ns(|| "d * x^2 - 1"), &one.neg())?; - - let a_x2_minus_one = x2 - .mul_by_constant(cs.ns(|| "a * x^2"), &a)? - .add_constant(cs.ns(|| "a * x^2 - 1"), &one.neg())?; - - d_x2_minus_one.mul_equals(cs.ns(|| "on curve check"), &y2, &a_x2_minus_one)?; - Ok(Self::new(x, y)) - } - - fn alloc_checked>( - mut cs: CS, - value_gen: FN, - ) -> Result - where - FN: FnOnce() -> Result, - T: Borrow>, - { - let cofactor_weight = BitIterator::new(P::COFACTOR).filter(|b| *b).count(); - // If we multiply by r, we actually multiply by r - 2. - let r_minus_1 = (-P::ScalarField::one()).into_repr(); - let r_weight = BitIterator::new(&r_minus_1).filter(|b| *b).count(); - - // We pick the most efficient method of performing the prime order check: - // If the cofactor has lower hamming weight than the scalar field's modulus, - // we first multiply by the inverse of the cofactor, and then, after allocating, - // multiply by the cofactor. This ensures the resulting point has no cofactors - // - // Else, we multiply by the scalar field's modulus and ensure that the result - // is zero. - if cofactor_weight < r_weight { - let ge = Self::alloc(cs.ns(|| "Alloc checked"), || { - value_gen().map(|ge| { - ge.borrow() - .into_affine() - .mul_by_cofactor_inv() - .into_projective() - }) - })?; - let mut seen_one = false; - let mut result = Self::zero(cs.ns(|| "result"))?; - for (i, b) in BitIterator::new(P::COFACTOR).enumerate() { - let mut cs = cs.ns(|| format!("Iteration {}", i)); - - let old_seen_one = seen_one; - if seen_one { - result.double_in_place(cs.ns(|| "Double"))?; - } else { - seen_one = b; - } - - if b { - result = if old_seen_one { - result.add(cs.ns(|| "Add"), &ge)? - } else { - ge.clone() - }; - } - } - Ok(result) - } else { - let ge = Self::alloc(cs.ns(|| "Alloc checked"), value_gen)?; - let mut seen_one = false; - let mut result = Self::zero(cs.ns(|| "result"))?; - // Returns bits in big-endian order - for (i, b) in BitIterator::new(r_minus_1).enumerate() { - let mut cs = cs.ns(|| format!("Iteration {}", i)); - - let old_seen_one = seen_one; - if seen_one { - result.double_in_place(cs.ns(|| "Double"))?; - } else { - seen_one = b; - } - - if b { - result = if old_seen_one { - result.add(cs.ns(|| "Add"), &ge)? - } else { - ge.clone() - }; - } - } - let neg_ge = ge.negate(cs.ns(|| "Negate ge"))?; - neg_ge.enforce_equal(cs.ns(|| "Check equals"), &result)?; - Ok(ge) - } - } - - fn alloc_input>( - mut cs: CS, - value_gen: FN, - ) -> Result - where - FN: FnOnce() -> Result, - T: Borrow>, - { - let (x, y) = match value_gen() { - Ok(ge) => { - let ge = ge.borrow().into_affine(); - (Ok(ge.x), Ok(ge.y)) - } - _ => ( - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - ), - }; - - let d = P::COEFF_D; - let a = P::COEFF_A; - - let x = F::alloc_input(&mut cs.ns(|| "x"), || x)?; - let y = F::alloc_input(&mut cs.ns(|| "y"), || y)?; - - // Check that ax^2 + y^2 = 1 + dx^2y^2 - // We do this by checking that ax^2 - 1 = y^2 * (dx^2 - 1) - let x2 = x.square(&mut cs.ns(|| "x^2"))?; - let y2 = y.square(&mut cs.ns(|| "y^2"))?; - - let one = P::BaseField::one(); - let d_x2_minus_one = x2 - .mul_by_constant(cs.ns(|| "d * x^2"), &d)? - .add_constant(cs.ns(|| "d * x^2 - 1"), &one.neg())?; + ), + for <'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>, +); + +impl_bounded_ops!( + AffineVar, + TEProjective

, + Sub, + sub, + SubAssign, + sub_assign, + |this: &'a AffineVar, other: &'a AffineVar| this + other.negate().unwrap(), + |this: &'a AffineVar, other: TEProjective

| this - AffineVar::constant(other), + ( + F :FieldVar::BasePrimeField> + + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField> + + ThreeBitCondNegLookupGadget<::BasePrimeField, TableConstant = P::BaseField>, + P: TEModelParameters, + ), + for <'b> &'b F: FieldOpsBounds<'b, P::BaseField, F> +); - let a_x2_minus_one = x2 - .mul_by_constant(cs.ns(|| "a * x^2"), &a)? - .add_constant(cs.ns(|| "a * x^2 - 1"), &one.neg())?; +impl<'a, P, F> GroupOpsBounds<'a, TEProjective

, AffineVar> for AffineVar +where + P: TEModelParameters, + F: FieldVar::BasePrimeField> + + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField> + + ThreeBitCondNegLookupGadget< + ::BasePrimeField, + TableConstant = P::BaseField, + >, + for<'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>, +{ +} - d_x2_minus_one.mul_equals(cs.ns(|| "on curve check"), &y2, &a_x2_minus_one)?; - Ok(Self::new(x, y)) - } - } +impl<'a, P, F> GroupOpsBounds<'a, TEProjective

, AffineVar> for &'a AffineVar +where + P: TEModelParameters, + F: FieldVar::BasePrimeField> + + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField> + + ThreeBitCondNegLookupGadget< + ::BasePrimeField, + TableConstant = P::BaseField, + >, + for<'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>, +{ } -impl CondSelectGadget for AffineGadget +impl CondSelectGadget<::BasePrimeField> for AffineVar where P: TEModelParameters, - ConstraintF: Field, - F: FieldGadget, + F: FieldVar::BasePrimeField>, + for<'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>, { #[inline] - fn conditionally_select>( - mut cs: CS, - cond: &Boolean, + fn conditionally_select( + cond: &Boolean<::BasePrimeField>, true_value: &Self, false_value: &Self, ) -> Result { - let x = F::conditionally_select(&mut cs.ns(|| "x"), cond, &true_value.x, &false_value.x)?; - let y = F::conditionally_select(&mut cs.ns(|| "y"), cond, &true_value.y, &false_value.y)?; + let x = cond.select(&true_value.x, &false_value.x)?; + let y = cond.select(&true_value.y, &false_value.y)?; Ok(Self::new(x, y)) } - - fn cost() -> usize { - 2 * >::cost() - } } -impl EqGadget for AffineGadget +impl EqGadget<::BasePrimeField> for AffineVar where P: TEModelParameters, - ConstraintF: Field, - F: FieldGadget, + F: FieldVar::BasePrimeField>, + for<'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>, { -} + fn is_eq( + &self, + other: &Self, + ) -> Result::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) + } -impl ConditionalEqGadget for AffineGadget -where - P: TEModelParameters, - ConstraintF: Field, - F: FieldGadget, -{ #[inline] - fn conditional_enforce_equal>( + fn conditional_enforce_equal( &self, - mut cs: CS, other: &Self, - condition: &Boolean, + condition: &Boolean<::BasePrimeField>, ) -> Result<(), SynthesisError> { - self.x.conditional_enforce_equal( - &mut cs.ns(|| "X Coordinate Conditional Equality"), - &other.x, - condition, - )?; - self.y.conditional_enforce_equal( - &mut cs.ns(|| "Y Coordinate Conditional Equality"), - &other.y, - condition, - )?; + self.x.conditional_enforce_equal(&other.x, condition)?; + self.y.conditional_enforce_equal(&other.y, condition)?; Ok(()) } - fn cost() -> usize { - 2 * >::cost() - } -} - -impl NEqGadget for AffineGadget -where - P: TEModelParameters, - ConstraintF: Field, - F: FieldGadget, -{ #[inline] - fn enforce_not_equal>( + fn conditional_enforce_not_equal( &self, - mut cs: CS, other: &Self, + condition: &Boolean<::BasePrimeField>, ) -> Result<(), SynthesisError> { - self.x - .enforce_not_equal(&mut cs.ns(|| "X Coordinate Inequality"), &other.x)?; - self.y - .enforce_not_equal(&mut cs.ns(|| "Y Coordinate Inequality"), &other.y)?; - Ok(()) - } - - fn cost() -> usize { - 2 * >::cost() + self.is_eq(other)? + .and(condition)? + .enforce_equal(&Boolean::Constant(false)) } } -impl ToBitsGadget for AffineGadget +impl ToBitsGadget<::BasePrimeField> for AffineVar where P: TEModelParameters, - ConstraintF: Field, - F: FieldGadget, + F: FieldVar::BasePrimeField>, + for<'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>, { - fn to_bits>( + fn to_bits( &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut x_bits = self.x.to_bits(cs.ns(|| "X Coordinate To Bits"))?; - let y_bits = self.y.to_bits(cs.ns(|| "Y Coordinate To Bits"))?; + ) -> Result::BasePrimeField>>, SynthesisError> { + let mut x_bits = self.x.to_bits()?; + let y_bits = self.y.to_bits()?; x_bits.extend_from_slice(&y_bits); Ok(x_bits) } - fn to_non_unique_bits>( + fn to_non_unique_bits( &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut x_bits = self - .x - .to_non_unique_bits(cs.ns(|| "X Coordinate To Bits"))?; - let y_bits = self - .y - .to_non_unique_bits(cs.ns(|| "Y Coordinate To Bits"))?; + ) -> Result::BasePrimeField>>, SynthesisError> { + let mut x_bits = self.x.to_non_unique_bits()?; + let y_bits = self.y.to_non_unique_bits()?; x_bits.extend_from_slice(&y_bits); Ok(x_bits) } } -impl ToBytesGadget for AffineGadget +impl ToBytesGadget<::BasePrimeField> for AffineVar where P: TEModelParameters, - ConstraintF: Field, - F: FieldGadget, + F: FieldVar::BasePrimeField>, + for<'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>, { - fn to_bytes>( + fn to_bytes( &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut x_bytes = self.x.to_bytes(cs.ns(|| "x"))?; - let y_bytes = self.y.to_bytes(cs.ns(|| "y"))?; + ) -> Result::BasePrimeField>>, SynthesisError> { + let mut x_bytes = self.x.to_bytes()?; + let y_bytes = self.y.to_bytes()?; x_bytes.extend_from_slice(&y_bytes); Ok(x_bytes) } - fn to_non_unique_bytes>( + fn to_non_unique_bytes( &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut x_bytes = self.x.to_non_unique_bytes(cs.ns(|| "x"))?; - let y_bytes = self.y.to_non_unique_bytes(cs.ns(|| "y"))?; + ) -> Result::BasePrimeField>>, SynthesisError> { + let mut x_bytes = self.x.to_non_unique_bytes()?; + let y_bytes = self.y.to_non_unique_bytes()?; x_bytes.extend_from_slice(&y_bytes); Ok(x_bytes) @@ -1462,62 +877,91 @@ where #[cfg(test)] #[allow(dead_code)] -pub(crate) fn test() +pub(crate) fn test() -> Result<(), SynthesisError> where - ConstraintF: Field, P: TEModelParameters, - GG: GroupGadget, ConstraintF, Value = TEAffine

>, + GG: CurveVar, ::BasePrimeField>, + for<'a> &'a GG: GroupOpsBounds<'a, TEProjective

, GG>, { - use crate::{ - boolean::AllocatedBit, groups::test::group_test, prelude::*, - test_constraint_system::TestConstraintSystem, - }; + use crate::prelude::*; use algebra::{test_rng, Group, UniformRand}; - use rand::Rng; + use r1cs_core::ConstraintSystem; - group_test::, GG>(); + crate::groups::test::group_test::, _, GG>()?; - let mut cs = TestConstraintSystem::new(); + let mut rng = test_rng(); - let a: TEAffine

= UniformRand::rand(&mut test_rng()); - let gadget_a = GG::alloc(&mut cs.ns(|| "a"), || Ok(a)).unwrap(); + let cs = ConstraintSystem::<::BasePrimeField>::new_ref(); + + let a = TEProjective::

::rand(&mut rng); + let b = TEProjective::

::rand(&mut rng); + let a_affine = a.into_affine(); + let b_affine = b.into_affine(); + + println!("Allocating things"); + let ns = cs.ns("allocating variables"); + println!("{:?}", cs.current_namespace()); + let mut gadget_a = GG::new_witness(cs.ns("a"), || Ok(a))?; + let gadget_b = GG::new_witness(cs.ns("b"), || Ok(b))?; + println!("{:?}", cs.current_namespace()); + ns.leave_namespace(); + println!("Done Allocating things"); + assert_eq!(gadget_a.value()?.into_affine().x, a_affine.x); + assert_eq!(gadget_a.value()?.into_affine().y, a_affine.y); + assert_eq!(gadget_b.value()?.into_affine().x, b_affine.x); + assert_eq!(gadget_b.value()?.into_affine().y, b_affine.y); + assert_eq!(cs.which_is_unsatisfied(), None); + + println!("Checking addition"); + // Check addition + let ab = a + &b; + let ab_affine = ab.into_affine(); + let gadget_ab = &gadget_a + &gadget_b; + let gadget_ba = &gadget_b + &gadget_a; + gadget_ba.enforce_equal(&gadget_ab)?; + + let ab_val = gadget_ab.value()?.into_affine(); + assert_eq!(ab_val, ab_affine, "Result of addition is unequal"); + assert!(cs.is_satisfied().unwrap()); + println!("Done checking addition"); + + println!("Checking doubling"); + // Check doubling + let aa = Group::double(&a); + let aa_affine = aa.into_affine(); + gadget_a.double_in_place()?; + let aa_val = gadget_a.value()?.into_affine(); + assert_eq!( + aa_val, aa_affine, + "Gadget and native values are unequal after double." + ); + assert!(cs.is_satisfied().unwrap()); + println!("Done checking doubling"); + + println!("Checking mul_bits"); // Check mul_bits - let scalar: as Group>::ScalarField = UniformRand::rand(&mut test_rng()); - let native_result = a.mul(&scalar); + let scalar = P::ScalarField::rand(&mut rng); + let native_result = AffineCurve::mul(&aa.into_affine(), scalar); + let native_result = native_result.into_affine(); let mut scalar: Vec = BitIterator::new(scalar.into_repr()).collect(); // Get the scalar bits into little-endian form. scalar.reverse(); - let input = Vec::::alloc(cs.ns(|| "Input"), || Ok(scalar)).unwrap(); - let zero = GG::zero(cs.ns(|| "zero")).unwrap(); - let result = gadget_a - .mul_bits(cs.ns(|| "mul_bits"), &zero, input.iter()) - .unwrap(); - let gadget_value = result.get_value().expect("Gadget_result failed"); - assert_eq!(native_result, gadget_value); - - assert!(cs.is_satisfied()); - - // Test the cost of allocation, conditional selection, and point addition. - let mut cs = TestConstraintSystem::new(); - - let bit = AllocatedBit::alloc(&mut cs.ns(|| "bool"), || Ok(true)) - .unwrap() - .into(); + let input: Vec> = Vec::new_witness(cs.ns("bits"), || Ok(scalar)).unwrap(); + let result = gadget_a.mul_bits(input.iter())?; + let result_val = result.value()?.into_affine(); + assert_eq!( + result_val, native_result, + "gadget & native values are diff. after scalar mul" + ); + assert!(cs.is_satisfied().unwrap()); + println!("Done checking mul_bits"); + + if !cs.is_satisfied().unwrap() { + println!("Not satisfied"); + println!("{:?}", cs.which_is_unsatisfied().unwrap()); + } - let mut rng = test_rng(); - let a: TEAffine

= rng.gen(); - let b: TEAffine

= rng.gen(); - let gadget_a = GG::alloc(&mut cs.ns(|| "a"), || Ok(a)).unwrap(); - let gadget_b = GG::alloc(&mut cs.ns(|| "b"), || Ok(b)).unwrap(); - let alloc_cost = cs.num_constraints(); - let _ = - GG::conditionally_select(&mut cs.ns(|| "cond_select"), &bit, &gadget_a, &gadget_b).unwrap(); - let cond_select_cost = cs.num_constraints() - alloc_cost; - - let _ = gadget_a.add(&mut cs.ns(|| "ab"), &gadget_b).unwrap(); - let add_cost = cs.num_constraints() - cond_select_cost - alloc_cost; - assert_eq!(cond_select_cost, >::cost()); - assert_eq!(add_cost, GG::cost_of_add()); - assert!(cs.is_satisfied()); + assert!(cs.is_satisfied().unwrap()); + Ok(()) } diff --git a/r1cs-std/src/groups/mod.rs b/r1cs-std/src/groups/mod.rs index 0aecf72..b3b3290 100644 --- a/r1cs-std/src/groups/mod.rs +++ b/r1cs-std/src/groups/mod.rs @@ -1,222 +1,214 @@ use crate::prelude::*; -use algebra::{Field, Group}; -use r1cs_core::{ConstraintSystem, SynthesisError}; +use algebra::{Field, ProjectiveCurve}; +use core::ops::{Add, AddAssign, Sub, SubAssign}; +use r1cs_core::{Namespace, SynthesisError}; use core::{borrow::Borrow, fmt::Debug}; pub mod curves; -pub use self::curves::short_weierstrass::{bls12, mnt4, mnt6}; +pub use self::curves::short_weierstrass::bls12; +pub use self::curves::short_weierstrass::mnt4; +pub use self::curves::short_weierstrass::mnt6; -pub trait GroupGadget: +/// A hack used to work around the lack of implied bounds. +pub trait GroupOpsBounds<'a, F, T: 'a>: Sized + + Add<&'a T, Output = T> + + Sub<&'a T, Output = T> + + Add + + Sub + + Add + + Sub +{ +} + +pub trait CurveVar: + 'static + + Sized + + Clone + + Debug + + R1CSVar + + ToBitsGadget + ToBytesGadget - + NEqGadget + EqGadget - + ToBitsGadget + CondSelectGadget - + AllocGadget - + Clone - + Debug + + AllocVar + + AllocVar + + for<'a> GroupOpsBounds<'a, C, Self> + + for<'a> AddAssign<&'a Self> + + for<'a> SubAssign<&'a Self> + + AddAssign + + SubAssign + + AddAssign + + SubAssign { - type Value: Debug; - type Variable; - - fn get_value(&self) -> Option; - - fn get_variable(&self) -> Self::Variable; - - fn zero>(cs: CS) -> Result; + fn constant(other: C) -> Self; - fn add>( - &self, - cs: CS, - other: &Self, - ) -> Result; + fn zero() -> Self; - fn sub>( - &self, - mut cs: CS, - other: &Self, - ) -> Result { - let neg_other = other.negate(cs.ns(|| "Negate other"))?; - self.add(cs.ns(|| "Self - other"), &neg_other) + fn is_zero(&self) -> Result, SynthesisError> { + self.is_eq(&Self::zero()) } - fn add_constant>( - &self, - cs: CS, - other: &G, + /// Allocate a variable in the subgroup without checking if it's in the + /// prime-order subgroup + fn new_variable_omit_prime_order_check( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, ) -> Result; - fn sub_constant>( - &self, - mut cs: CS, - other: &G, - ) -> Result { - let neg_other = -(*other); - self.add_constant(cs.ns(|| "Self - other"), &neg_other) + /// Enforce that `self` is in the prime-order subgroup. + fn enforce_prime_order(&self) -> Result<(), SynthesisError>; + + fn double(&self) -> Result { + let mut result = self.clone(); + result.double_in_place()?; + Ok(result) } - fn double_in_place>( - &mut self, - cs: CS, - ) -> Result<(), SynthesisError>; + fn double_in_place(&mut self) -> Result<(), SynthesisError>; - fn negate>(&self, cs: CS) -> Result; + fn negate(&self) -> Result; /// Inputs must be specified in *little-endian* form. /// If the addition law is incomplete for the identity element, /// `result` must not be the identity element. - fn mul_bits<'a, CS: ConstraintSystem>( + fn mul_bits<'a>( &self, - mut cs: CS, - result: &Self, - bits: impl Iterator, + bits: impl Iterator>, ) -> Result { let mut power = self.clone(); - let mut result = result.clone(); - for (i, bit) in bits.enumerate() { - let new_encoded = result.add(&mut cs.ns(|| format!("Add {}-th power", i)), &power)?; - result = Self::conditionally_select( - &mut cs.ns(|| format!("Select {}", i)), - bit.borrow(), - &new_encoded, - &result, - )?; - power.double_in_place(&mut cs.ns(|| format!("{}-th Doubling", i)))?; + let mut result = Self::zero(); + for bit in bits { + let new_encoded = result.clone() + &power; + result = bit.borrow().select(&new_encoded, &result)?; + power.double_in_place()?; } Ok(result) } - fn precomputed_base_scalar_mul<'a, CS, I, B>( + fn precomputed_base_scalar_mul<'a, I, B>( &mut self, - mut cs: CS, scalar_bits_with_base_powers: I, ) -> Result<(), SynthesisError> where - CS: ConstraintSystem, - I: Iterator, - B: Borrow, - G: 'a, + I: Iterator, + B: Borrow>, + C: 'a, { - for (i, (bit, base_power)) in scalar_bits_with_base_powers.enumerate() { - let new_encoded = self.add_constant( - &mut cs.ns(|| format!("Add {}-th base power", i)), - &base_power, - )?; - *self = Self::conditionally_select( - &mut cs.ns(|| format!("Conditional Select {}", i)), - bit.borrow(), - &new_encoded, - &self, - )?; + for (bit, base_power) in scalar_bits_with_base_powers { + let new_encoded = self.clone() + *base_power; + *self = bit.borrow().select(&new_encoded, self)?; } Ok(()) } - fn precomputed_base_3_bit_signed_digit_scalar_mul<'a, CS, I, J, B>( - _: CS, + fn precomputed_base_3_bit_signed_digit_scalar_mul<'a, I, J, B>( _: &[B], _: &[J], ) -> Result where - CS: ConstraintSystem, - I: Borrow<[Boolean]>, + I: Borrow<[Boolean]>, J: Borrow<[I]>, - B: Borrow<[G]>, + B: Borrow<[C]>, { Err(SynthesisError::AssignmentMissing) } - fn precomputed_base_multiscalar_mul<'a, CS, T, I, B>( - mut cs: CS, + fn precomputed_base_multiscalar_mul<'a, T, I, B>( bases: &[B], scalars: I, ) -> Result where - CS: ConstraintSystem, T: 'a + ToBitsGadget + ?Sized, I: Iterator, - B: Borrow<[G]>, + B: Borrow<[C]>, { - let mut result = Self::zero(&mut cs.ns(|| "Declare Result"))?; + let mut result = Self::zero(); // Compute ∏(h_i^{m_i}) for all i. - for (i, (bits, base_powers)) in scalars.zip(bases).enumerate() { + for (bits, base_powers) in scalars.zip(bases) { let base_powers = base_powers.borrow(); - let bits = bits.to_bits(&mut cs.ns(|| format!("Convert Scalar {} to bits", i)))?; - result.precomputed_base_scalar_mul( - cs.ns(|| format!("Chunk {}", i)), - bits.iter().zip(base_powers), - )?; + let bits = bits.to_bits()?; + result.precomputed_base_scalar_mul(bits.iter().zip(base_powers))?; } Ok(result) } - - fn cost_of_add() -> usize; - - fn cost_of_double() -> usize; } #[cfg(test)] mod test { - use algebra::{test_rng, Field}; - use r1cs_core::ConstraintSystem; + use algebra::{test_rng, Field, ProjectiveCurve}; + use r1cs_core::{ConstraintSystem, SynthesisError}; - use crate::{prelude::*, test_constraint_system::TestConstraintSystem}; - use algebra::groups::Group; + use crate::prelude::*; - pub(crate) fn group_test>() { - let mut cs = TestConstraintSystem::::new(); + pub(crate) fn group_test>( + ) -> Result<(), SynthesisError> + where + for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + { + let cs = ConstraintSystem::::new_ref(); let mut rng = test_rng(); - let a_native = G::rand(&mut rng); - let b_native = G::rand(&mut rng); - let a = GG::alloc(&mut cs.ns(|| "generate_a"), || Ok(a_native)).unwrap(); - let b = GG::alloc(&mut cs.ns(|| "generate_b"), || Ok(b_native)).unwrap(); + let a_native = C::rand(&mut rng); + let b_native = C::rand(&mut rng); + let a = GG::new_witness(cs.ns("generate_a"), || Ok(a_native)).unwrap(); + let b = GG::new_witness(cs.ns("generate_b"), || Ok(b_native)).unwrap(); - let zero = GG::zero(cs.ns(|| "Zero")).unwrap(); - assert_eq!(zero, zero); + let zero = GG::zero(); + assert_eq!(zero.value()?, zero.value()?); // a == a - assert_eq!(a, a); + assert_eq!(a.value()?, a.value()?); // a + 0 = a - assert_eq!(a.add(cs.ns(|| "a_plus_zero"), &zero).unwrap(), a); + assert_eq!((&a + &zero).value()?, a.value()?); // a - 0 = a - assert_eq!(a.sub(cs.ns(|| "a_minus_zero"), &zero).unwrap(), a); + assert_eq!((&a - &zero).value()?, a.value()?); // a - a = 0 - assert_eq!(a.sub(cs.ns(|| "a_minus_a"), &a).unwrap(), zero); + assert_eq!((&a - &a).value()?, zero.value()?); // a + b = b + a - let a_b = a.add(cs.ns(|| "a_plus_b"), &b).unwrap(); - let b_a = b.add(cs.ns(|| "b_plus_a"), &a).unwrap(); - assert_eq!(a_b, b_a); + let a_b = &a + &b; + let b_a = &b + &a; + assert_eq!(a_b.value()?, b_a.value()?); + a_b.enforce_equal(&b_a)?; + assert!(cs.is_satisfied().unwrap()); + // (a + b) + a = a + (b + a) - let ab_a = a_b.add(&mut cs.ns(|| "a_b_plus_a"), &a).unwrap(); - let a_ba = a.add(&mut cs.ns(|| "a_plus_b_a"), &b_a).unwrap(); - assert_eq!(ab_a, a_ba); + let ab_a = &a_b + &a; + let a_ba = &a + &b_a; + assert_eq!(ab_a.value()?, a_ba.value()?); + ab_a.enforce_equal(&a_ba)?; + assert!(cs.is_satisfied().unwrap()); + // a.double() = a + a - let a_a = a.add(cs.ns(|| "a + a"), &a).unwrap(); + let a_a = &a + &a; let mut a2 = a.clone(); - a2.double_in_place(cs.ns(|| "2a")).unwrap(); - assert_eq!(a2, a_a); + a2.double_in_place()?; + a2.enforce_equal(&a_a)?; + assert_eq!(a2.value()?, a_native.double()); + assert_eq!(a_a.value()?, a_native.double()); + assert_eq!(a2.value()?, a_a.value()?); + assert!(cs.is_satisfied().unwrap()); + // b.double() = b + b let mut b2 = b.clone(); - b2.double_in_place(cs.ns(|| "2b")).unwrap(); - let b_b = b.add(cs.ns(|| "b + b"), &b).unwrap(); - assert_eq!(b2, b_b); - - let _ = a.to_bytes(&mut cs.ns(|| "ToBytes")).unwrap(); - let _ = a - .to_non_unique_bytes(&mut cs.ns(|| "ToBytes Strict")) - .unwrap(); - - let _ = b.to_bytes(&mut cs.ns(|| "b ToBytes")).unwrap(); - let _ = b - .to_non_unique_bytes(&mut cs.ns(|| "b ToBytes Strict")) - .unwrap(); - if !cs.is_satisfied() { + b2.double_in_place()?; + let b_b = &b + &b; + b2.enforce_equal(&b_b)?; + assert!(cs.is_satisfied().unwrap()); + assert_eq!(b2.value()?, b_b.value()?); + + let _ = a.to_bytes()?; + let _ = a.to_non_unique_bytes()?; + + let _ = b.to_bytes()?; + let _ = b.to_non_unique_bytes()?; + if !cs.is_satisfied().unwrap() { println!("{:?}", cs.which_is_unsatisfied().unwrap()); } - assert!(cs.is_satisfied()); + assert!(cs.is_satisfied().unwrap()); + Ok(()) } }