From cbc3fe81dc409c5b879a5f734efbd1a6a8ae04ac Mon Sep 17 00:00:00 2001 From: Srinath Setty Date: Fri, 8 Apr 2022 14:19:32 -0700 Subject: [PATCH] cleanup: merge ecc and ecc_circuit (#25) --- rustfmt.toml | 1 - src/circuit.rs | 2 +- src/gadgets/ecc.rs | 536 ++++++++++++++++++++++++++++++++--- src/gadgets/ecc_circuit.rs | 552 ------------------------------------- src/gadgets/mod.rs | 3 +- 5 files changed, 503 insertions(+), 591 deletions(-) delete mode 100644 src/gadgets/ecc_circuit.rs diff --git a/rustfmt.toml b/rustfmt.toml index d80cfda..7b20d96 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,5 +1,4 @@ edition = "2018" tab_spaces = 2 newline_style = "Unix" -report_fixme = "Always" use_try_shorthand = true diff --git a/src/circuit.rs b/src/circuit.rs index 24e7125..e6aa13f 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -12,7 +12,7 @@ use super::commitments::Commitment; use super::gadgets::{ - ecc_circuit::AllocatedPoint, + ecc::AllocatedPoint, utils::{ alloc_bignat_constant, alloc_num_equals, alloc_one, alloc_zero, conditionally_select, conditionally_select_bignat, le_bits_to_num, diff --git a/src/gadgets/ecc.rs b/src/gadgets/ecc.rs index 613442b..e101e5e 100644 --- a/src/gadgets/ecc.rs +++ b/src/gadgets/ecc.rs @@ -1,26 +1,457 @@ #![allow(non_snake_case)] -use ff::{PrimeField, PrimeFieldBits}; +use crate::gadgets::utils::{ + alloc_one, alloc_zero, conditionally_select, conditionally_select2, select_one_or, select_zero_or, +}; +use bellperson::{ + gadgets::{ + boolean::{AllocatedBit, Boolean}, + num::AllocatedNum, + Assignment, + }, + ConstraintSystem, SynthesisError, +}; +use ff::PrimeField; + +#[derive(Clone)] +pub struct AllocatedPoint +where + Fp: PrimeField, +{ + pub(crate) x: AllocatedNum, + pub(crate) y: AllocatedNum, + pub(crate) is_infinity: AllocatedNum, +} + +impl AllocatedPoint +where + Fp: PrimeField, +{ + // Creates a new allocated point from allocated nums. + pub fn new(x: AllocatedNum, y: AllocatedNum, is_infinity: AllocatedNum) -> Self { + Self { x, y, is_infinity } + } + + // Check that is infinity is 0/1 + pub fn check_is_infinity>( + &self, + mut cs: CS, + ) -> Result<(), SynthesisError> { + // Check that is_infinity * ( 1 - is_infinity ) = 0 + cs.enforce( + || "is_infinity is bit", + |lc| lc + self.is_infinity.get_variable(), + |lc| lc + CS::one() - self.is_infinity.get_variable(), + |lc| lc, + ); + Ok(()) + } + + // Allocate a random point. Only used for testing + #[cfg(test)] + pub fn random_vartime>(mut cs: CS) -> Result { + loop { + let x = Fp::random(&mut OsRng); + let y = (x * x * x + Fp::one() + Fp::one() + Fp::one() + Fp::one() + Fp::one()).sqrt(); + if y.is_some().unwrap_u8() == 1 { + let x_alloc = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(x))?; + let y_alloc = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(y.unwrap()))?; + let is_infinity = alloc_zero(cs.namespace(|| "Is Infinity"))?; + return Ok(Self::new(x_alloc, y_alloc, is_infinity)); + } + } + } + + // Make the point io + #[cfg(test)] + pub fn inputize>(&self, mut cs: CS) -> Result<(), SynthesisError> { + let _ = self.x.inputize(cs.namespace(|| "Input point.x")); + let _ = self.y.inputize(cs.namespace(|| "Input point.y")); + let _ = self + .is_infinity + .inputize(cs.namespace(|| "Input point.is_infinity")); + Ok(()) + } + + // Adds other point to this point and returns the result + // Assumes that both other.is_infinity and this.is_infinty are bits + pub fn add>( + &self, + mut cs: CS, + other: &AllocatedPoint, + ) -> Result { + // Allocate the boolean variables that check if either of the points is infinity + + //************************************************************************/ + // lambda = (other.y - self.y) * (other.x - self.x).invert().unwrap(); + //************************************************************************/ + // First compute (other.x - self.x).inverse() + // If either self or other are 1 then compute bogus values + + // x_diff = other != inf && self != inf ? (other.x - self.x) : 1 + let x_diff_actual = AllocatedNum::alloc(cs.namespace(|| "actual x diff"), || { + Ok(*other.x.get_value().get()? - *self.x.get_value().get()?) + })?; + cs.enforce( + || "actual x_diff is correct", + |lc| lc + other.x.get_variable() - self.x.get_variable(), + |lc| lc + CS::one(), + |lc| lc + x_diff_actual.get_variable(), + ); + + // Compute self.is_infinity OR other.is_infinity + let at_least_one_inf = AllocatedNum::alloc(cs.namespace(|| "at least one inf"), || { + Ok(*self.is_infinity.get_value().get()? * *other.is_infinity.get_value().get()?) + })?; + cs.enforce( + || "at least one inf = self.is_infinity * other.is_infinity", + |lc| lc + self.is_infinity.get_variable(), + |lc| lc + other.is_infinity.get_variable(), + |lc| lc + at_least_one_inf.get_variable(), + ); + + // x_diff = 1 if either self.is_infinity or other.is_infinity else x_diff_actual + let x_diff = select_one_or( + cs.namespace(|| "Compute x_diff"), + &x_diff_actual, + &at_least_one_inf, + )?; + + let x_diff_inv = AllocatedNum::alloc(cs.namespace(|| "x diff inverse"), || { + if *at_least_one_inf.get_value().get()? == Fp::one() { + // Set to default + Ok(Fp::one()) + } else { + // Set to the actual inverse + let inv = (*other.x.get_value().get()? - *self.x.get_value().get()?).invert(); + if inv.is_some().unwrap_u8() == 1 { + Ok(inv.unwrap()) + } else { + Err(SynthesisError::DivisionByZero) + } + } + })?; + + cs.enforce( + || "Check inverse", + |lc| lc + x_diff.get_variable(), + |lc| lc + x_diff_inv.get_variable(), + |lc| lc + CS::one(), + ); + + let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || { + Ok( + (*other.y.get_value().get()? - *self.y.get_value().get()?) + * x_diff_inv.get_value().get()?, + ) + })?; + cs.enforce( + || "Check that lambda is correct", + |lc| lc + other.y.get_variable() - self.y.get_variable(), + |lc| lc + x_diff_inv.get_variable(), + |lc| lc + lambda.get_variable(), + ); + + //************************************************************************/ + // x = lambda * lambda - self.x - other.x; + //************************************************************************/ + let x = AllocatedNum::alloc(cs.namespace(|| "x"), || { + Ok( + *lambda.get_value().get()? * lambda.get_value().get()? + - *self.x.get_value().get()? + - *other.x.get_value().get()?, + ) + })?; + cs.enforce( + || "check that x is correct", + |lc| lc + lambda.get_variable(), + |lc| lc + lambda.get_variable(), + |lc| lc + x.get_variable() + self.x.get_variable() + other.x.get_variable(), + ); + + //************************************************************************/ + // y = lambda * (self.x - x) - self.y; + //************************************************************************/ + let y = AllocatedNum::alloc(cs.namespace(|| "y"), || { + Ok( + *lambda.get_value().get()? * (*self.x.get_value().get()? - *x.get_value().get()?) + - *self.y.get_value().get()?, + ) + })?; + + cs.enforce( + || "Check that y is correct", + |lc| lc + lambda.get_variable(), + |lc| lc + self.x.get_variable() - x.get_variable(), + |lc| lc + y.get_variable() + self.y.get_variable(), + ); + + let is_infinity = AllocatedNum::alloc(cs.namespace(|| "is infinity"), || Ok(Fp::zero()))?; + + //************************************************************************/ + // We only return the computed x, y if neither of the points is infinity. + // if self.is_infinity return other.clone() + // elif other.is_infinity return self.clone() + // Otherwise return the computed points. + //************************************************************************/ + // Now compute the output x + let inner_x = conditionally_select2( + cs.namespace(|| "final x: inner if"), + &self.x, + &x, + &other.is_infinity, + )?; + let final_x = conditionally_select2( + cs.namespace(|| "final x: outer if"), + &other.x, + &inner_x, + &self.is_infinity, + )?; + + // The output y + let inner_y = conditionally_select2( + cs.namespace(|| "final y: inner if"), + &self.y, + &y, + &other.is_infinity, + )?; + let final_y = conditionally_select2( + cs.namespace(|| "final y: outer if"), + &other.y, + &inner_y, + &self.is_infinity, + )?; + + // The output is_infinity + let inner_is_infinity = conditionally_select2( + cs.namespace(|| "final is infinity: inner if"), + &self.is_infinity, + &is_infinity, + &other.is_infinity, + )?; + let final_is_infinity = conditionally_select2( + cs.namespace(|| "final is infinity: outer if"), + &other.is_infinity, + &inner_is_infinity, + &self.is_infinity, + )?; + Ok(Self::new(final_x, final_y, final_is_infinity)) + } + + pub fn double>(&self, mut cs: CS) -> Result { + //*************************************************************/ + // lambda = (Fp::one() + Fp::one() + Fp::one()) + // * self.x + // * self.x + // * ((Fp::one() + Fp::one()) * self.y).invert().unwrap(); + /*************************************************************/ + + // Compute tmp = (Fp::one() + Fp::one())* self.y ? self != inf : 1 + let tmp_actual = AllocatedNum::alloc(cs.namespace(|| "tmp_actual"), || { + Ok(*self.y.get_value().get()? + *self.y.get_value().get()?) + })?; + cs.enforce( + || "check tmp_actual", + |lc| lc + CS::one() + CS::one(), + |lc| lc + self.y.get_variable(), + |lc| lc + tmp_actual.get_variable(), + ); + + let tmp = select_one_or(cs.namespace(|| "tmp"), &tmp_actual, &self.is_infinity)?; + + // Compute inv = tmp.invert + let tmp_inv = AllocatedNum::alloc(cs.namespace(|| "tmp inverse"), || { + if *self.is_infinity.get_value().get()? == Fp::one() { + // Return default value 1 + Ok(Fp::one()) + } else { + // Return the actual inverse + let inv = (*tmp.get_value().get()?).invert(); + if inv.is_some().unwrap_u8() == 1 { + Ok(inv.unwrap()) + } else { + Err(SynthesisError::DivisionByZero) + } + } + })?; + cs.enforce( + || "Check inverse", + |lc| lc + tmp.get_variable(), + |lc| lc + tmp_inv.get_variable(), + |lc| lc + CS::one(), + ); + + // Now compute lambda as (Fp::one() + Fp::one + Fp::one()) * self.x * self.x * tmp_inv + let prod_1 = AllocatedNum::alloc(cs.namespace(|| "alloc prod 1"), || { + Ok(*tmp_inv.get_value().get()? * self.x.get_value().get()?) + })?; + cs.enforce( + || "Check prod 1", + |lc| lc + self.x.get_variable(), + |lc| lc + tmp_inv.get_variable(), + |lc| lc + prod_1.get_variable(), + ); + + let prod_2 = AllocatedNum::alloc(cs.namespace(|| "alloc prod 2"), || { + Ok(*prod_1.get_value().get()? * self.x.get_value().get()?) + })?; + cs.enforce( + || "Check prod 2", + |lc| lc + self.x.get_variable(), + |lc| lc + prod_1.get_variable(), + |lc| lc + prod_2.get_variable(), + ); + + let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || { + Ok(*prod_2.get_value().get()? * (Fp::one() + Fp::one() + Fp::one())) + })?; + cs.enforce( + || "Check lambda", + |lc| lc + CS::one() + CS::one() + CS::one(), + |lc| lc + prod_2.get_variable(), + |lc| lc + lambda.get_variable(), + ); + + /*************************************************************/ + // x = lambda * lambda - self.x - self.x; + /*************************************************************/ + + let x = AllocatedNum::alloc(cs.namespace(|| "x"), || { + Ok( + ((*lambda.get_value().get()?) * (*lambda.get_value().get()?)) + - *self.x.get_value().get()? + - self.x.get_value().get()?, + ) + })?; + cs.enforce( + || "Check x", + |lc| lc + lambda.get_variable(), + |lc| lc + lambda.get_variable(), + |lc| lc + x.get_variable() + self.x.get_variable() + self.x.get_variable(), + ); + + /*************************************************************/ + // y = lambda * (self.x - x) - self.y; + /*************************************************************/ + + let y = AllocatedNum::alloc(cs.namespace(|| "y"), || { + Ok( + (*lambda.get_value().get()?) * (*self.x.get_value().get()? - x.get_value().get()?) + - self.y.get_value().get()?, + ) + })?; + cs.enforce( + || "Check y", + |lc| lc + lambda.get_variable(), + |lc| lc + self.x.get_variable() - x.get_variable(), + |lc| lc + y.get_variable() + self.y.get_variable(), + ); + + /*************************************************************/ + // Only return the computed x and y if the point is not infinity + /*************************************************************/ + + // x + let final_x = select_zero_or(cs.namespace(|| "final x"), &x, &self.is_infinity)?; + + // y + let final_y = select_zero_or(cs.namespace(|| "final y"), &y, &self.is_infinity)?; + + // is_infinity + let final_is_infinity = self.is_infinity.clone(); + + Ok(Self::new(final_x, final_y, final_is_infinity)) + } + + pub fn scalar_mul>( + &self, + mut cs: CS, + scalar: Vec, + ) -> Result { + /*************************************************************/ + // Initialize res = Self { + // x: Fp::zero(), + // y: Fp::zero(), + // is_infinity: true, + // _p: Default::default(), + //}; + /*************************************************************/ + + let zero = alloc_zero(cs.namespace(|| "Allocate zero"))?; + let one = alloc_one(cs.namespace(|| "Allocate one"))?; + let mut res = Self::new(zero.clone(), zero, one); + + for i in (0..scalar.len()).rev() { + /*************************************************************/ + // res = res.double(); + /*************************************************************/ + + res = res.double(cs.namespace(|| format!("{}: double", i)))?; + + /*************************************************************/ + // if scalar[i] { + // res = self.add(&res); + // } + /*************************************************************/ + + let self_and_res = self.add(cs.namespace(|| format!("{}: add", i)), &res)?; + res = Self::conditionally_select( + cs.namespace(|| format!("{}: Update res", i)), + &self_and_res, + &res, + &Boolean::from(scalar[i].clone()), + )?; + } + Ok(res) + } + + /// If condition outputs a otherwise outputs b + pub fn conditionally_select>( + mut cs: CS, + a: &Self, + b: &Self, + condition: &Boolean, + ) -> Result { + let x = conditionally_select(cs.namespace(|| "select x"), &a.x, &b.x, condition)?; + + let y = conditionally_select(cs.namespace(|| "select y"), &a.y, &b.y, condition)?; + + let is_infinity = conditionally_select( + cs.namespace(|| "select is_infinity"), + &a.is_infinity, + &b.is_infinity, + condition, + )?; + + Ok(Self::new(x, y, is_infinity)) + } +} + +#[cfg(test)] +use ff::PrimeFieldBits; +#[cfg(test)] use rand::rngs::OsRng; +#[cfg(test)] use std::marker::PhantomData; +#[cfg(test)] #[derive(Debug, Clone)] pub struct Point where Fp: PrimeField, Fq: PrimeField + PrimeFieldBits, { - pub(crate) x: Fp, //TODO: Make this not public - pub(crate) y: Fp, + x: Fp, + y: Fp, is_infinity: bool, _p: PhantomData, } +#[cfg(test)] impl Point where Fp: PrimeField, Fq: PrimeField + PrimeFieldBits, { - #[allow(dead_code)] pub fn new(x: Fp, y: Fp, is_infinity: bool) -> Self { Self { x, @@ -30,7 +461,6 @@ where } } - #[allow(dead_code)] pub fn random_vartime() -> Self { loop { let x = Fp::random(&mut OsRng); @@ -90,30 +520,6 @@ where } } - #[allow(dead_code)] - pub fn scalar_mul_mont(&self, scalar: &Fq) -> Self { - let mut R0 = Self { - x: Fp::zero(), - y: Fp::zero(), - is_infinity: true, - _p: Default::default(), - }; - - let mut R1 = self.clone(); - let bits = scalar.to_le_bits(); - for i in (0..bits.len()).rev() { - if bits[i] { - R0 = R0.add(&R1); - R1 = R1.double(); - } else { - R1 = R0.add(&R1); - R0 = R0.double(); - } - } - R0 - } - - #[allow(dead_code)] pub fn scalar_mul(&self, scalar: &Fq) -> Self { let mut res = Self { x: Fp::zero(), @@ -160,15 +566,11 @@ mod fq { #[cfg(test)] mod tests { use super::*; - use super::{fp::Fp, fq::Fq}; - use ff::Field; - use pasta_curves::arithmetic::CurveAffine; - use pasta_curves::group::Curve; - use pasta_curves::EpAffine; - use std::ops::Mul; #[test] fn test_ecc_ops() { + use super::{fp::Fp, fq::Fq}; + // perform some curve arithmetic let a = Point::::random_vartime(); let b = Point::::random_vartime(); @@ -216,4 +618,68 @@ mod tests { assert_eq!(d_pasta, d_pasta_2); assert_eq!(e_pasta, e_pasta_2); } + + use crate::bellperson::shape_cs::ShapeCS; + use crate::bellperson::solver::SatisfyingAssignment; + use ff::{Field, PrimeFieldBits}; + use pasta_curves::arithmetic::CurveAffine; + use pasta_curves::group::Curve; + use pasta_curves::EpAffine; + use std::ops::Mul; + type G = pasta_curves::pallas::Point; + type Fp = pasta_curves::pallas::Scalar; + type Fq = pasta_curves::vesta::Scalar; + use crate::bellperson::r1cs::{NovaShape, NovaWitness}; + + fn synthesize_smul(mut cs: CS) -> (AllocatedPoint, AllocatedPoint, Fq) + where + Fp: PrimeField, + Fq: PrimeField + PrimeFieldBits, + CS: ConstraintSystem, + { + let a = AllocatedPoint::::random_vartime(cs.namespace(|| "a")).unwrap(); + let _ = a.inputize(cs.namespace(|| "inputize a")).unwrap(); + let s = Fq::random(&mut OsRng); + // Allocate random bits and only keep 128 bits + let bits: Vec = s + .to_le_bits() + .into_iter() + .enumerate() + .map(|(i, bit)| AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), Some(bit))) + .collect::, SynthesisError>>() + .unwrap(); + let e = a.scalar_mul(cs.namespace(|| "Scalar Mul"), bits).unwrap(); + let _ = e.inputize(cs.namespace(|| "inputize e")).unwrap(); + (a, e, s) + } + + #[test] + fn test_ecc_circuit_ops() { + // First create the shape + let mut cs: ShapeCS = ShapeCS::new(); + let _ = synthesize_smul::(cs.namespace(|| "synthesize")); + println!("Number of constraints: {}", cs.num_constraints()); + let shape = cs.r1cs_shape(); + let gens = cs.r1cs_gens(); + + // Then the satisfying assignment + let mut cs: SatisfyingAssignment = SatisfyingAssignment::new(); + let (a, e, s) = synthesize_smul::(cs.namespace(|| "synthesize")); + let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap(); + + let a_p: Point = Point::new( + a.x.get_value().unwrap(), + a.y.get_value().unwrap(), + a.is_infinity.get_value().unwrap() == Fp::one(), + ); + let e_p: Point = Point::new( + e.x.get_value().unwrap(), + e.y.get_value().unwrap(), + e.is_infinity.get_value().unwrap() == Fp::one(), + ); + let e_new = a_p.scalar_mul(&s); + assert!(e_p.x == e_new.x && e_p.y == e_new.y); + // Make sure that this is satisfiable + assert!(shape.is_sat(&gens, &inst, &witness).is_ok()); + } } diff --git a/src/gadgets/ecc_circuit.rs b/src/gadgets/ecc_circuit.rs deleted file mode 100644 index 3d13417..0000000 --- a/src/gadgets/ecc_circuit.rs +++ /dev/null @@ -1,552 +0,0 @@ -#![allow(non_snake_case)] -use crate::gadgets::utils::{ - alloc_one, alloc_zero, conditionally_select, conditionally_select2, select_one_or, select_zero_or, -}; -use bellperson::{ - gadgets::{ - boolean::{AllocatedBit, Boolean}, - num::AllocatedNum, - Assignment, - }, - ConstraintSystem, SynthesisError, -}; -use ff::PrimeField; -use rand::rngs::OsRng; - -#[derive(Clone)] -pub struct AllocatedPoint -where - Fp: PrimeField, -{ - pub(crate) x: AllocatedNum, - pub(crate) y: AllocatedNum, - pub(crate) is_infinity: AllocatedNum, -} - -impl AllocatedPoint -where - Fp: PrimeField, -{ - // Creates a new allocated point from allocated nums. - pub fn new(x: AllocatedNum, y: AllocatedNum, is_infinity: AllocatedNum) -> Self { - Self { x, y, is_infinity } - } - - // Check that is infinity is 0/1 - #[allow(dead_code)] - pub fn check_is_infinity>( - &self, - mut cs: CS, - ) -> Result<(), SynthesisError> { - // Check that is_infinity * ( 1 - is_infinity ) = 0 - cs.enforce( - || "is_infinity is bit", - |lc| lc + self.is_infinity.get_variable(), - |lc| lc + CS::one() - self.is_infinity.get_variable(), - |lc| lc, - ); - Ok(()) - } - - #[allow(dead_code)] - // Allocate a random point. Only used for testing - pub fn random_vartime>(mut cs: CS) -> Result { - loop { - let x = Fp::random(&mut OsRng); - let y = (x * x * x + Fp::one() + Fp::one() + Fp::one() + Fp::one() + Fp::one()).sqrt(); - if y.is_some().unwrap_u8() == 1 { - let x_alloc = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(x))?; - let y_alloc = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(y.unwrap()))?; - let is_infinity = alloc_zero(cs.namespace(|| "Is Infinity"))?; - return Ok(Self::new(x_alloc, y_alloc, is_infinity)); - } - } - } - - // Make the point io - #[allow(dead_code)] - pub fn inputize>(&self, mut cs: CS) -> Result<(), SynthesisError> { - let _ = self.x.inputize(cs.namespace(|| "Input point.x")); - let _ = self.y.inputize(cs.namespace(|| "Input point.y")); - let _ = self - .is_infinity - .inputize(cs.namespace(|| "Input point.is_infinity")); - Ok(()) - } - - // Adds other point to this point and returns the result - // Assumes that both other.is_infinity and this.is_infinty are bits - pub fn add>( - &self, - mut cs: CS, - other: &AllocatedPoint, - ) -> Result { - // Allocate the boolean variables that check if either of the points is infinity - - //************************************************************************/ - // lambda = (other.y - self.y) * (other.x - self.x).invert().unwrap(); - //************************************************************************/ - // First compute (other.x - self.x).inverse() - // If either self or other are 1 then compute bogus values - - // x_diff = other != inf && self != inf ? (other.x - self.x) : 1 - let x_diff_actual = AllocatedNum::alloc(cs.namespace(|| "actual x diff"), || { - Ok(*other.x.get_value().get()? - *self.x.get_value().get()?) - })?; - cs.enforce( - || "actual x_diff is correct", - |lc| lc + other.x.get_variable() - self.x.get_variable(), - |lc| lc + CS::one(), - |lc| lc + x_diff_actual.get_variable(), - ); - - // Compute self.is_infinity OR other.is_infinity - let at_least_one_inf = AllocatedNum::alloc(cs.namespace(|| "at least one inf"), || { - Ok(*self.is_infinity.get_value().get()? * *other.is_infinity.get_value().get()?) - })?; - cs.enforce( - || "at least one inf = self.is_infinity * other.is_infinity", - |lc| lc + self.is_infinity.get_variable(), - |lc| lc + other.is_infinity.get_variable(), - |lc| lc + at_least_one_inf.get_variable(), - ); - - // x_diff = 1 if either self.is_infinity or other.is_infinity else x_diff_actual - let x_diff = select_one_or( - cs.namespace(|| "Compute x_diff"), - &x_diff_actual, - &at_least_one_inf, - )?; - - let x_diff_inv = AllocatedNum::alloc(cs.namespace(|| "x diff inverse"), || { - if *at_least_one_inf.get_value().get()? == Fp::one() { - // Set to default - Ok(Fp::one()) - } else { - // Set to the actual inverse - let inv = (*other.x.get_value().get()? - *self.x.get_value().get()?).invert(); - if inv.is_some().unwrap_u8() == 1 { - Ok(inv.unwrap()) - } else { - Err(SynthesisError::DivisionByZero) - } - } - })?; - - cs.enforce( - || "Check inverse", - |lc| lc + x_diff.get_variable(), - |lc| lc + x_diff_inv.get_variable(), - |lc| lc + CS::one(), - ); - - let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || { - Ok( - (*other.y.get_value().get()? - *self.y.get_value().get()?) - * x_diff_inv.get_value().get()?, - ) - })?; - cs.enforce( - || "Check that lambda is correct", - |lc| lc + other.y.get_variable() - self.y.get_variable(), - |lc| lc + x_diff_inv.get_variable(), - |lc| lc + lambda.get_variable(), - ); - - //************************************************************************/ - // x = lambda * lambda - self.x - other.x; - //************************************************************************/ - let x = AllocatedNum::alloc(cs.namespace(|| "x"), || { - Ok( - *lambda.get_value().get()? * lambda.get_value().get()? - - *self.x.get_value().get()? - - *other.x.get_value().get()?, - ) - })?; - cs.enforce( - || "check that x is correct", - |lc| lc + lambda.get_variable(), - |lc| lc + lambda.get_variable(), - |lc| lc + x.get_variable() + self.x.get_variable() + other.x.get_variable(), - ); - - //************************************************************************/ - // y = lambda * (self.x - x) - self.y; - //************************************************************************/ - let y = AllocatedNum::alloc(cs.namespace(|| "y"), || { - Ok( - *lambda.get_value().get()? * (*self.x.get_value().get()? - *x.get_value().get()?) - - *self.y.get_value().get()?, - ) - })?; - - cs.enforce( - || "Check that y is correct", - |lc| lc + lambda.get_variable(), - |lc| lc + self.x.get_variable() - x.get_variable(), - |lc| lc + y.get_variable() + self.y.get_variable(), - ); - - let is_infinity = AllocatedNum::alloc(cs.namespace(|| "is infinity"), || Ok(Fp::zero()))?; - - //************************************************************************/ - // We only return the computed x, y if neither of the points is infinity. - // if self.is_infinity return other.clone() - // elif other.is_infinity return self.clone() - // Otherwise return the computed points. - //************************************************************************/ - // Now compute the output x - let inner_x = conditionally_select2( - cs.namespace(|| "final x: inner if"), - &self.x, - &x, - &other.is_infinity, - )?; - let final_x = conditionally_select2( - cs.namespace(|| "final x: outer if"), - &other.x, - &inner_x, - &self.is_infinity, - )?; - - // The output y - let inner_y = conditionally_select2( - cs.namespace(|| "final y: inner if"), - &self.y, - &y, - &other.is_infinity, - )?; - let final_y = conditionally_select2( - cs.namespace(|| "final y: outer if"), - &other.y, - &inner_y, - &self.is_infinity, - )?; - - // The output is_infinity - let inner_is_infinity = conditionally_select2( - cs.namespace(|| "final is infinity: inner if"), - &self.is_infinity, - &is_infinity, - &other.is_infinity, - )?; - let final_is_infinity = conditionally_select2( - cs.namespace(|| "final is infinity: outer if"), - &other.is_infinity, - &inner_is_infinity, - &self.is_infinity, - )?; - Ok(Self::new(final_x, final_y, final_is_infinity)) - } - - pub fn double>(&self, mut cs: CS) -> Result { - //*************************************************************/ - // lambda = (Fp::one() + Fp::one() + Fp::one()) - // * self.x - // * self.x - // * ((Fp::one() + Fp::one()) * self.y).invert().unwrap(); - /*************************************************************/ - - // Compute tmp = (Fp::one() + Fp::one())* self.y ? self != inf : 1 - let tmp_actual = AllocatedNum::alloc(cs.namespace(|| "tmp_actual"), || { - Ok(*self.y.get_value().get()? + *self.y.get_value().get()?) - })?; - cs.enforce( - || "check tmp_actual", - |lc| lc + CS::one() + CS::one(), - |lc| lc + self.y.get_variable(), - |lc| lc + tmp_actual.get_variable(), - ); - - let tmp = select_one_or(cs.namespace(|| "tmp"), &tmp_actual, &self.is_infinity)?; - - // Compute inv = tmp.invert - let tmp_inv = AllocatedNum::alloc(cs.namespace(|| "tmp inverse"), || { - if *self.is_infinity.get_value().get()? == Fp::one() { - // Return default value 1 - Ok(Fp::one()) - } else { - // Return the actual inverse - let inv = (*tmp.get_value().get()?).invert(); - if inv.is_some().unwrap_u8() == 1 { - Ok(inv.unwrap()) - } else { - Err(SynthesisError::DivisionByZero) - } - } - })?; - cs.enforce( - || "Check inverse", - |lc| lc + tmp.get_variable(), - |lc| lc + tmp_inv.get_variable(), - |lc| lc + CS::one(), - ); - - // Now compute lambda as (Fp::one() + Fp::one + Fp::one()) * self.x * self.x * tmp_inv - let prod_1 = AllocatedNum::alloc(cs.namespace(|| "alloc prod 1"), || { - Ok(*tmp_inv.get_value().get()? * self.x.get_value().get()?) - })?; - cs.enforce( - || "Check prod 1", - |lc| lc + self.x.get_variable(), - |lc| lc + tmp_inv.get_variable(), - |lc| lc + prod_1.get_variable(), - ); - - let prod_2 = AllocatedNum::alloc(cs.namespace(|| "alloc prod 2"), || { - Ok(*prod_1.get_value().get()? * self.x.get_value().get()?) - })?; - cs.enforce( - || "Check prod 2", - |lc| lc + self.x.get_variable(), - |lc| lc + prod_1.get_variable(), - |lc| lc + prod_2.get_variable(), - ); - - let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || { - Ok(*prod_2.get_value().get()? * (Fp::one() + Fp::one() + Fp::one())) - })?; - cs.enforce( - || "Check lambda", - |lc| lc + CS::one() + CS::one() + CS::one(), - |lc| lc + prod_2.get_variable(), - |lc| lc + lambda.get_variable(), - ); - - /*************************************************************/ - // x = lambda * lambda - self.x - self.x; - /*************************************************************/ - - let x = AllocatedNum::alloc(cs.namespace(|| "x"), || { - Ok( - ((*lambda.get_value().get()?) * (*lambda.get_value().get()?)) - - *self.x.get_value().get()? - - self.x.get_value().get()?, - ) - })?; - cs.enforce( - || "Check x", - |lc| lc + lambda.get_variable(), - |lc| lc + lambda.get_variable(), - |lc| lc + x.get_variable() + self.x.get_variable() + self.x.get_variable(), - ); - - /*************************************************************/ - // y = lambda * (self.x - x) - self.y; - /*************************************************************/ - - let y = AllocatedNum::alloc(cs.namespace(|| "y"), || { - Ok( - (*lambda.get_value().get()?) * (*self.x.get_value().get()? - x.get_value().get()?) - - self.y.get_value().get()?, - ) - })?; - cs.enforce( - || "Check y", - |lc| lc + lambda.get_variable(), - |lc| lc + self.x.get_variable() - x.get_variable(), - |lc| lc + y.get_variable() + self.y.get_variable(), - ); - - /*************************************************************/ - // Only return the computed x and y if the point is not infinity - /*************************************************************/ - - // x - let final_x = select_zero_or(cs.namespace(|| "final x"), &x, &self.is_infinity)?; - - // y - let final_y = select_zero_or(cs.namespace(|| "final y"), &y, &self.is_infinity)?; - - // is_infinity - let final_is_infinity = self.is_infinity.clone(); - - Ok(Self::new(final_x, final_y, final_is_infinity)) - } - - #[allow(dead_code)] - pub fn scalar_mul_mont>( - &self, - mut cs: CS, - scalar: Vec, - ) -> Result { - /*************************************************************/ - // Initialize RO = Self { - // x: Fp::zero(), - // y: Fp::zero(), - // is_infinity: true, - // _p: Default::default(), - //}; - /*************************************************************/ - - let zero = alloc_zero(cs.namespace(|| "Allocate zero"))?; - let one = alloc_one(cs.namespace(|| "Allocate one"))?; - let mut R0 = Self::new(zero.clone(), zero, one); - - /*************************************************************/ - // Initialize R1 and the bits of the scalar - /*************************************************************/ - - let mut R1 = self.clone(); - - for i in (0..scalar.len()).rev() { - /*************************************************************/ - //if bits[i] { - // R0 = R0.add(&R1); - // R1 = R1.double(); - //} else { - // R0 = R0.double(); - // R1 = R0.add(&R1); - //} - /*************************************************************/ - - let R0_and_R1 = R0.add(cs.namespace(|| format!("{}: R0 + R1", i)), &R1)?; - let R0_double = R0.double(cs.namespace(|| format!("{}: 2 * R0", i)))?; - let R1_double = R1.double(cs.namespace(|| format!("{}: 2 * R1", i)))?; - - R0 = Self::conditionally_select( - cs.namespace(|| format!("{}: Update R0", i)), - &R0_and_R1, - &R0_double, - &Boolean::from(scalar[i].clone()), - )?; - - R1 = Self::conditionally_select( - cs.namespace(|| format!("{}: Update R1", i)), - &R1_double, - &R0_and_R1, - &Boolean::from(scalar[i].clone()), - )?; - } - Ok(R0) - } - - #[allow(dead_code)] - pub fn scalar_mul>( - &self, - mut cs: CS, - scalar: Vec, - ) -> Result { - /*************************************************************/ - // Initialize res = Self { - // x: Fp::zero(), - // y: Fp::zero(), - // is_infinity: true, - // _p: Default::default(), - //}; - /*************************************************************/ - - let zero = alloc_zero(cs.namespace(|| "Allocate zero"))?; - let one = alloc_one(cs.namespace(|| "Allocate one"))?; - let mut res = Self::new(zero.clone(), zero, one); - - for i in (0..scalar.len()).rev() { - /*************************************************************/ - // res = res.double(); - /*************************************************************/ - - res = res.double(cs.namespace(|| format!("{}: double", i)))?; - - /*************************************************************/ - // if scalar[i] { - // res = self.add(&res); - // } - /*************************************************************/ - - let self_and_res = self.add(cs.namespace(|| format!("{}: add", i)), &res)?; - res = Self::conditionally_select( - cs.namespace(|| format!("{}: Update res", i)), - &self_and_res, - &res, - &Boolean::from(scalar[i].clone()), - )?; - } - Ok(res) - } - - /// If condition outputs a otherwise outputs b - pub fn conditionally_select>( - mut cs: CS, - a: &Self, - b: &Self, - condition: &Boolean, - ) -> Result { - let x = conditionally_select(cs.namespace(|| "select x"), &a.x, &b.x, condition)?; - - let y = conditionally_select(cs.namespace(|| "select y"), &a.y, &b.y, condition)?; - - let is_infinity = conditionally_select( - cs.namespace(|| "select is_infinity"), - &a.is_infinity, - &b.is_infinity, - condition, - )?; - - Ok(Self::new(x, y, is_infinity)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::bellperson::shape_cs::ShapeCS; - use crate::bellperson::solver::SatisfyingAssignment; - type G = pasta_curves::pallas::Point; - type Fp = pasta_curves::pallas::Scalar; - type Fq = pasta_curves::vesta::Scalar; - use crate::bellperson::r1cs::{NovaShape, NovaWitness}; - use crate::gadgets::ecc::Point; - use ff::PrimeFieldBits; - - fn synthesize_smul(mut cs: CS) -> (AllocatedPoint, AllocatedPoint, Fq) - where - Fp: PrimeField, - Fq: PrimeField + PrimeFieldBits, - CS: ConstraintSystem, - { - let a = AllocatedPoint::::random_vartime(cs.namespace(|| "a")).unwrap(); - let _ = a.inputize(cs.namespace(|| "inputize a")).unwrap(); - let s = Fq::random(&mut OsRng); - // Allocate random bits and only keep 128 bits - let bits: Vec = s - .to_le_bits() - .into_iter() - .enumerate() - .map(|(i, bit)| AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), Some(bit))) - .collect::, SynthesisError>>() - .unwrap(); - let e = a.scalar_mul(cs.namespace(|| "Scalar Mul"), bits).unwrap(); - let _ = e.inputize(cs.namespace(|| "inputize e")).unwrap(); - (a, e, s) - } - - #[test] - fn test_ecc_circuit_ops() { - // First create the shape - let mut cs: ShapeCS = ShapeCS::new(); - let _ = synthesize_smul::(cs.namespace(|| "synthesize")); - println!("Number of constraints: {}", cs.num_constraints()); - let shape = cs.r1cs_shape(); - let gens = cs.r1cs_gens(); - - // Then the satisfying assignment - let mut cs: SatisfyingAssignment = SatisfyingAssignment::new(); - let (a, e, s) = synthesize_smul::(cs.namespace(|| "synthesize")); - let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap(); - - let a_p: Point = Point::new( - a.x.get_value().unwrap(), - a.y.get_value().unwrap(), - a.is_infinity.get_value().unwrap() == Fp::one(), - ); - let e_p: Point = Point::new( - e.x.get_value().unwrap(), - e.y.get_value().unwrap(), - e.is_infinity.get_value().unwrap() == Fp::one(), - ); - let e_new = a_p.scalar_mul(&s); - assert!(e_p.x == e_new.x && e_p.y == e_new.y); - // Make sure that this is satisfiable - assert!(shape.is_sat(&gens, &inst, &witness).is_ok()); - } -} diff --git a/src/gadgets/mod.rs b/src/gadgets/mod.rs index 18bad97..c845d83 100644 --- a/src/gadgets/mod.rs +++ b/src/gadgets/mod.rs @@ -1,3 +1,2 @@ -mod ecc; -pub mod ecc_circuit; +pub mod ecc; pub mod utils;