From f29b591d49ba693078016392572cbf1e1808d88a Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 10 Nov 2021 11:42:06 -0800 Subject: [PATCH 01/39] merge --- src/gadgets/nonnative.rs | 52 ++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/gadgets/nonnative.rs b/src/gadgets/nonnative.rs index 84691421..bc090cd5 100644 --- a/src/gadgets/nonnative.rs +++ b/src/gadgets/nonnative.rs @@ -7,54 +7,54 @@ use crate::field::{extension_field::Extendable, field_types::Field}; use crate::gadgets::biguint::BigUintTarget; use crate::plonk::circuit_builder::CircuitBuilder; -pub struct ForeignFieldTarget { +pub struct NonNativeTarget { value: BigUintTarget, _phantom: PhantomData, } impl, const D: usize> CircuitBuilder { - pub fn biguint_to_nonnative(&mut self, x: &BigUintTarget) -> ForeignFieldTarget { - ForeignFieldTarget { + pub fn biguint_to_nonnative(&mut self, x: &BigUintTarget) -> NonNativeTarget { + NonNativeTarget { value: x.clone(), _phantom: PhantomData, } } - pub fn nonnative_to_biguint(&mut self, x: &ForeignFieldTarget) -> BigUintTarget { + pub fn nonnative_to_biguint(&mut self, x: &NonNativeTarget) -> BigUintTarget { x.value.clone() } - pub fn constant_nonnative(&mut self, x: FF) -> ForeignFieldTarget { + pub fn constant_nonnative(&mut self, x: FF) -> NonNativeTarget { let x_biguint = self.constant_biguint(&x.to_biguint()); self.biguint_to_nonnative(&x_biguint) } - // Assert that two ForeignFieldTarget's, both assumed to be in reduced form, are equal. + // Assert that two NonNativeTarget's, both assumed to be in reduced form, are equal. pub fn connect_nonnative( &mut self, - lhs: &ForeignFieldTarget, - rhs: &ForeignFieldTarget, + lhs: &NonNativeTarget, + rhs: &NonNativeTarget, ) { self.connect_biguint(&lhs.value, &rhs.value); } - // Add two `ForeignFieldTarget`s. + // Add two `NonNativeTarget`s. pub fn add_nonnative( &mut self, - a: &ForeignFieldTarget, - b: &ForeignFieldTarget, - ) -> ForeignFieldTarget { + a: &NonNativeTarget, + b: &NonNativeTarget, + ) -> NonNativeTarget { let result = self.add_biguint(&a.value, &b.value); self.reduce(&result) } - // Subtract two `ForeignFieldTarget`s. + // Subtract two `NonNativeTarget`s. pub fn sub_nonnative( &mut self, - a: &ForeignFieldTarget, - b: &ForeignFieldTarget, - ) -> ForeignFieldTarget { + a: &NonNativeTarget, + b: &NonNativeTarget, + ) -> NonNativeTarget { let order = self.constant_biguint(&FF::order()); let a_plus_order = self.add_biguint(&order, &a.value); let result = self.sub_biguint(&a_plus_order, &b.value); @@ -65,9 +65,9 @@ impl, const D: usize> CircuitBuilder { pub fn mul_nonnative( &mut self, - a: &ForeignFieldTarget, - b: &ForeignFieldTarget, - ) -> ForeignFieldTarget { + a: &NonNativeTarget, + b: &NonNativeTarget, + ) -> NonNativeTarget { let result = self.mul_biguint(&a.value, &b.value); self.reduce(&result) @@ -75,8 +75,8 @@ impl, const D: usize> CircuitBuilder { pub fn neg_nonnative( &mut self, - x: &ForeignFieldTarget, - ) -> ForeignFieldTarget { + x: &NonNativeTarget, + ) -> NonNativeTarget { let neg_one = FF::order() - BigUint::one(); let neg_one_target = self.constant_biguint(&neg_one); let neg_one_ff = self.biguint_to_nonnative(&neg_one_target); @@ -84,13 +84,13 @@ impl, const D: usize> CircuitBuilder { self.mul_nonnative(&neg_one_ff, x) } - /// Returns `x % |FF|` as a `ForeignFieldTarget`. - fn reduce(&mut self, x: &BigUintTarget) -> ForeignFieldTarget { + /// Returns `x % |FF|` as a `NonNativeTarget`. + fn reduce(&mut self, x: &BigUintTarget) -> NonNativeTarget { let modulus = FF::order(); let order_target = self.constant_biguint(&modulus); let value = self.rem_biguint(x, &order_target); - ForeignFieldTarget { + NonNativeTarget { value, _phantom: PhantomData, } @@ -99,8 +99,8 @@ impl, const D: usize> CircuitBuilder { #[allow(dead_code)] fn reduce_nonnative( &mut self, - x: &ForeignFieldTarget, - ) -> ForeignFieldTarget { + x: &NonNativeTarget, + ) -> NonNativeTarget { let x_biguint = self.nonnative_to_biguint(x); self.reduce(&x_biguint) } From d9868de6932478a987889c521121566908f565d9 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 19 Nov 2021 15:29:22 -0800 Subject: [PATCH 02/39] merge --- src/plonk/circuit_builder.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plonk/circuit_builder.rs b/src/plonk/circuit_builder.rs index 3cb62cf3..6126ab90 100644 --- a/src/plonk/circuit_builder.rs +++ b/src/plonk/circuit_builder.rs @@ -15,8 +15,9 @@ use crate::gadgets::arithmetic::BaseArithmeticOperation; use crate::gadgets::arithmetic_extension::ExtensionArithmeticOperation; use crate::gadgets::arithmetic_u32::U32Target; use crate::gates::arithmetic_base::ArithmeticGate; -use crate::gates::arithmetic_extension::ArithmeticExtensionGate; -use crate::gates::arithmetic_u32::{U32ArithmeticGate, NUM_U32_ARITHMETIC_OPS}; +use crate::gates::arithmetic::ArithmeticExtensionGate; +use crate::gates::arithmetic_u32::{NUM_U32_ARITHMETIC_OPS, U32ArithmeticGate}; +use crate::gates::subtraction_u32::{NUM_U32_SUBTRACTION_OPS, U32SubtractionGate}; use crate::gates::constant::ConstantGate; use crate::gates::gate::{Gate, GateInstance, GateRef, PrefixedGate}; use crate::gates::gate_tree::Tree; From fd2e276405fe3457bb8e027e77375687ef48d025 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 10 Nov 2021 11:47:20 -0800 Subject: [PATCH 03/39] merge --- src/plonk/circuit_builder.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plonk/circuit_builder.rs b/src/plonk/circuit_builder.rs index 6126ab90..693abe0a 100644 --- a/src/plonk/circuit_builder.rs +++ b/src/plonk/circuit_builder.rs @@ -16,8 +16,7 @@ use crate::gadgets::arithmetic_extension::ExtensionArithmeticOperation; use crate::gadgets::arithmetic_u32::U32Target; use crate::gates::arithmetic_base::ArithmeticGate; use crate::gates::arithmetic::ArithmeticExtensionGate; -use crate::gates::arithmetic_u32::{NUM_U32_ARITHMETIC_OPS, U32ArithmeticGate}; -use crate::gates::subtraction_u32::{NUM_U32_SUBTRACTION_OPS, U32SubtractionGate}; +use crate::gates::arithmetic_u32::{U32ArithmeticGate, NUM_U32_ARITHMETIC_OPS}; use crate::gates::constant::ConstantGate; use crate::gates::gate::{Gate, GateInstance, GateRef, PrefixedGate}; use crate::gates::gate_tree::Tree; From f9c9cc83f4df212d78e933e3c3f8a90b7d04d87b Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 19 Oct 2021 15:39:39 -0700 Subject: [PATCH 04/39] fix: run all U32SubtractionGate generators --- src/gadgets/biguint.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/gadgets/biguint.rs b/src/gadgets/biguint.rs index fa5aa0b6..05a5406e 100644 --- a/src/gadgets/biguint.rs +++ b/src/gadgets/biguint.rs @@ -368,4 +368,28 @@ mod tests { let proof = data.prove(pw).unwrap(); verify(proof, &data.verifier_only, &data.common) } + + #[test] + fn test_biguint_sub() -> Result<()> { + let x_value = BigUint::from_u128(33333333333333333333333333333333333333).unwrap(); + let y_value = BigUint::from_u128(22222222222222222222222222222222222222).unwrap(); + let expected_z_value = &x_value - &y_value; + + type F = CrandallField; + let config = CircuitConfig::large_config(); + let pw = PartialWitness::new(); + let mut builder = CircuitBuilder::::new(config); + + let x = builder.constant_biguint(x_value); + let y = builder.constant_biguint(y_value); + let z = builder.sub_biguint(x, y); + let expected_z = builder.constant_biguint(expected_z_value); + + builder.connect_biguint(z, expected_z); + + let data = builder.build(); + let proof = data.prove(pw).unwrap(); + + verify(proof, &data.verifier_only, &data.common) + } } From ebce0799a2abca0ef0e58d062edc285c527afe18 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 28 Oct 2021 11:48:14 -0700 Subject: [PATCH 05/39] initial curve_types and curve_adds --- src/curve/curve_adds.rs | 129 +++++++++++++++++ src/curve/curve_types.rs | 299 +++++++++++++++++++++++++++++++++++++++ src/curve/mod.rs | 2 + src/field/field_types.rs | 8 ++ src/lib.rs | 1 + 5 files changed, 439 insertions(+) create mode 100644 src/curve/curve_adds.rs create mode 100644 src/curve/curve_types.rs create mode 100644 src/curve/mod.rs diff --git a/src/curve/curve_adds.rs b/src/curve/curve_adds.rs new file mode 100644 index 00000000..c5fc9ba4 --- /dev/null +++ b/src/curve/curve_adds.rs @@ -0,0 +1,129 @@ +use std::ops::Add; + +use crate::field::field_types::Field; +use crate::curve::curve_types::{AffinePoint, Curve, ProjectivePoint}; + +impl Add> for ProjectivePoint { + type Output = ProjectivePoint; + + fn add(self, rhs: ProjectivePoint) -> Self::Output { + let ProjectivePoint { x: x1, y: y1, z: z1, zero: zero1 } = self; + let ProjectivePoint { x: x2, y: y2, z: z2, zero: zero2 } = rhs; + + if zero1 { + return rhs; + } + if zero2 { + return self; + } + + let x1z2 = x1 * z2; + let y1z2 = y1 * z2; + let x2z1 = x2 * z1; + let y2z1 = y2 * z1; + + // Check if we're doubling or adding inverses. + if x1z2 == x2z1 { + if y1z2 == y2z1 { + // TODO: inline to avoid redundant muls. + return self.double(); + } + if y1z2 == -y2z1 { + return ProjectivePoint::ZERO; + } + } + + let z1z2 = z1 * z2; + let u = y2z1 - y1z2; + let uu = u.square(); + let v = x2z1 - x1z2; + let vv = v.square(); + let vvv = v * vv; + let r = vv * x1z2; + let a = uu * z1z2 - vvv - r.double(); + let x3 = v * a; + let y3 = u * (r - a) - vvv * y1z2; + let z3 = vvv * z1z2; + ProjectivePoint::nonzero(x3, y3, z3) + } +} + +impl Add> for ProjectivePoint { + type Output = ProjectivePoint; + + fn add(self, rhs: AffinePoint) -> Self::Output { + let ProjectivePoint { x: x1, y: y1, z: z1, zero: zero1 } = self; + let AffinePoint { x: x2, y: y2, zero: zero2 } = rhs; + + if zero1 { + return rhs.to_projective(); + } + if zero2 { + return self; + } + + let x2z1 = x2 * z1; + let y2z1 = y2 * z1; + + // Check if we're doubling or adding inverses. + if x1 == x2z1 { + if y1 == y2z1 { + // TODO: inline to avoid redundant muls. + return self.double(); + } + if y1 == -y2z1 { + return ProjectivePoint::ZERO; + } + } + + let u = y2z1 - y1; + let uu = u.square(); + let v = x2z1 - x1; + let vv = v.square(); + let vvv = v * vv; + let r = vv * x1; + let a = uu * z1 - vvv - r.double(); + let x3 = v * a; + let y3 = u * (r - a) - vvv * y1; + let z3 = vvv * z1; + ProjectivePoint::nonzero(x3, y3, z3) + } +} + +impl Add> for AffinePoint { + type Output = ProjectivePoint; + + fn add(self, rhs: AffinePoint) -> Self::Output { + let AffinePoint { x: x1, y: y1, zero: zero1 } = self; + let AffinePoint { x: x2, y: y2, zero: zero2 } = rhs; + + if zero1 { + return rhs.to_projective(); + } + if zero2 { + return self.to_projective(); + } + + // Check if we're doubling or adding inverses. + if x1 == x2 { + if y1 == y2 { + return self.to_projective().double(); + } + if y1 == -y2 { + return ProjectivePoint::ZERO; + } + } + + let u = y2 - y1; + let uu = u.square(); + let v = x2 - x1; + let vv = v.square(); + let vvv = v * vv; + let r = vv * x1; + let a = uu - vvv - r.double(); + let x3 = v * a; + let y3 = u * (r - a) - vvv * y1; + let z3 = vvv; + ProjectivePoint::nonzero(x3, y3, z3) + } +} diff --git a/src/curve/curve_types.rs b/src/curve/curve_types.rs new file mode 100644 index 00000000..f7d55c0c --- /dev/null +++ b/src/curve/curve_types.rs @@ -0,0 +1,299 @@ +use std::ops::Neg; + +use anyhow::Result; + +use crate::field::field_types::Field; +use std::fmt::Debug; + +// To avoid implementation conflicts from associated types, +// see https://github.com/rust-lang/rust/issues/20400 +pub struct CurveScalar(pub ::ScalarField); + +/// A short Weierstrass curve. +pub trait Curve: 'static + Sync + Sized + Copy + Debug { + type BaseField: Field; + type ScalarField: Field; + + const A: Self::BaseField; + const B: Self::BaseField; + + const GENERATOR_AFFINE: AffinePoint; + + const GENERATOR_PROJECTIVE: ProjectivePoint = ProjectivePoint { + x: Self::GENERATOR_AFFINE.x, + y: Self::GENERATOR_AFFINE.y, + z: Self::BaseField::ONE, + zero: false, + }; + + fn convert(x: Self::ScalarField) -> CurveScalar { + CurveScalar(x) + } + + /*fn try_convert_b2s(x: Self::BaseField) -> Result { + x.try_convert::() + } + + fn try_convert_s2b(x: Self::ScalarField) -> Result { + x.try_convert::() + } + + fn try_convert_s2b_slice(s: &[Self::ScalarField]) -> Result> { + let mut res = Vec::with_capacity(s.len()); + for &x in s { + res.push(Self::try_convert_s2b(x)?); + } + Ok(res) + } + + fn try_convert_b2s_slice(s: &[Self::BaseField]) -> Result> { + let mut res = Vec::with_capacity(s.len()); + for &x in s { + res.push(Self::try_convert_b2s(x)?); + } + Ok(res) + }*/ + + fn is_safe_curve() -> bool{ + // Added additional check to prevent using vulnerabilties in case a discriminant is equal to 0. + (Self::A.cube().double().double() + Self::B.square().triple().triple().triple()).is_nonzero() + } +} + +/// A point on a short Weierstrass curve, represented in affine coordinates. +#[derive(Copy, Clone, Debug)] +pub struct AffinePoint { + pub x: C::BaseField, + pub y: C::BaseField, + pub zero: bool, +} + +impl AffinePoint { + pub const ZERO: Self = Self { + x: C::BaseField::ZERO, + y: C::BaseField::ZERO, + zero: true, + }; + + pub fn nonzero(x: C::BaseField, y: C::BaseField) -> Self { + let point = Self { x, y, zero: false }; + debug_assert!(point.is_valid()); + point + } + + pub fn is_valid(&self) -> bool { + let Self { x, y, zero } = *self; + zero || y.square() == x.cube() + C::A * x + C::B + } + + pub fn to_projective(&self) -> ProjectivePoint { + let Self { x, y, zero } = *self; + ProjectivePoint { + x, + y, + z: C::BaseField::ONE, + zero, + } + } + + pub fn batch_to_projective(affine_points: &[Self]) -> Vec> { + affine_points.iter().map(Self::to_projective).collect() + } + + pub fn double(&self) -> Self { + let AffinePoint { + x: x1, + y: y1, + zero, + } = *self; + + if zero { + return AffinePoint::ZERO; + } + + let double_y = y1.double(); + let inv_double_y = double_y.inverse(); // (2y)^(-1) + let triple_xx = x1.square().triple(); // 3x^2 + let lambda = (triple_xx + C::A) * inv_double_y; + let x3 = lambda.square() - self.x.double(); + let y3 = lambda * (x1 - x3) - y1; + + Self { + x: x3, + y: y3, + zero: false, + } + } + +} + +impl PartialEq for AffinePoint { + fn eq(&self, other: &Self) -> bool { + let AffinePoint { + x: x1, + y: y1, + zero: zero1, + } = *self; + let AffinePoint { + x: x2, + y: y2, + zero: zero2, + } = *other; + if zero1 || zero2 { + return zero1 == zero2; + } + x1 == x2 && y1 == y2 + } +} + +impl Eq for AffinePoint {} + +/// A point on a short Weierstrass curve, represented in projective coordinates. +#[derive(Copy, Clone, Debug)] +pub struct ProjectivePoint { + pub x: C::BaseField, + pub y: C::BaseField, + pub z: C::BaseField, + pub zero: bool, +} + +impl ProjectivePoint { + pub const ZERO: Self = Self { + x: C::BaseField::ZERO, + y: C::BaseField::ZERO, + z: C::BaseField::ZERO, + zero: true, + }; + + pub fn nonzero(x: C::BaseField, y: C::BaseField, z: C::BaseField) -> Self { + let point = Self { + x, + y, + z, + zero: false, + }; + debug_assert!(point.is_valid()); + point + } + + pub fn is_valid(&self) -> bool { + self.to_affine().is_valid() + } + + pub fn to_affine(&self) -> AffinePoint { + let Self { x, y, z, zero } = *self; + if zero { + AffinePoint::ZERO + } else { + let z_inv = z.inverse(); + AffinePoint::nonzero(x * z_inv, y * z_inv) + } + } + + pub fn batch_to_affine(proj_points: &[Self]) -> Vec> { + let n = proj_points.len(); + let zs: Vec = proj_points.iter().map(|pp| pp.z).collect(); + let z_invs = C::BaseField::batch_multiplicative_inverse(&zs); + + let mut result = Vec::with_capacity(n); + for i in 0..n { + let Self { x, y, z: _, zero } = proj_points[i]; + result.push(if zero { + AffinePoint::ZERO + } else { + let z_inv = z_invs[i]; + AffinePoint::nonzero(x * z_inv, y * z_inv) + }); + } + result + } + + pub fn double(&self) -> Self { + let Self { x, y, z, zero } = *self; + if zero { + return ProjectivePoint::ZERO; + } + + let xx = x.square(); + let zz = z.square(); + let mut w = xx.triple(); + if C::A.is_nonzero() { + w = w + C::A * zz; + } + let s = y.double() * z; + let r = y * s; + let rr = r.square(); + let b = (x + r).square() - (xx + rr); + let h = w.square() - b.double(); + let x3 = h * s; + let y3 = w * (b - h) - rr.double(); + let z3 = s.cube(); + Self { + x: x3, + y: y3, + z: z3, + zero: false, + } + } + + pub fn add_slices(a: &[Self], b: &[Self]) -> Vec { + assert_eq!(a.len(), b.len()); + a.iter() + .zip(b.iter()) + .map(|(&a_i, &b_i)| a_i + b_i) + .collect() + } + + pub fn neg(&self) -> Self { + Self { + x: self.x, + y: -self.y, + z: self.z, + zero: self.zero, + } + } +} + +impl PartialEq for ProjectivePoint { + fn eq(&self, other: &Self) -> bool { + let ProjectivePoint { + x: x1, + y: y1, + z: z1, + zero: zero1, + } = *self; + let ProjectivePoint { + x: x2, + y: y2, + z: z2, + zero: zero2, + } = *other; + if zero1 || zero2 { + return zero1 == zero2; + } + + // We want to compare (x1/z1, y1/z1) == (x2/z2, y2/z2). + // But to avoid field division, it is better to compare (x1*z2, y1*z2) == (x2*z1, y2*z1). + x1 * z2 == x2 * z1 && y1 * z2 == y2 * z1 + } +} + +impl Eq for ProjectivePoint {} + +impl Neg for AffinePoint { + type Output = AffinePoint; + + fn neg(self) -> Self::Output { + let AffinePoint { x, y, zero } = self; + AffinePoint { x, y: -y, zero } + } +} + +impl Neg for ProjectivePoint { + type Output = ProjectivePoint; + + fn neg(self) -> Self::Output { + let ProjectivePoint { x, y, z, zero } = self; + ProjectivePoint { x, y: -y, z, zero } + } +} diff --git a/src/curve/mod.rs b/src/curve/mod.rs new file mode 100644 index 00000000..1e536564 --- /dev/null +++ b/src/curve/mod.rs @@ -0,0 +1,2 @@ +pub mod curve_adds; +pub mod curve_types; diff --git a/src/field/field_types.rs b/src/field/field_types.rs index 036793cc..250338fb 100644 --- a/src/field/field_types.rs +++ b/src/field/field_types.rs @@ -91,6 +91,14 @@ pub trait Field: self.square() * *self } + fn double(&self) -> Self { + *self * Self::TWO + } + + fn triple(&self) -> Self { + *self * (Self::ONE + Self::TWO) + } + /// Compute the multiplicative inverse of this field element. fn try_inverse(&self) -> Option; diff --git a/src/lib.rs b/src/lib.rs index e76e312c..b0158d7a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ #![feature(specialization)] #![feature(stdsimd)] +pub mod curve; pub mod field; pub mod fri; pub mod gadgets; From db464f739e3cac1ff3eac054dd90dbfe9d43b3fc Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 10 Nov 2021 11:49:30 -0800 Subject: [PATCH 06/39] merge --- src/curve/curve_adds.rs | 41 ++++- src/curve/curve_multiplication.rs | 89 ++++++++++ src/curve/curve_summation.rs | 236 +++++++++++++++++++++++++ src/curve/curve_types.rs | 14 +- src/curve/mod.rs | 2 + src/field/extension_field/quadratic.rs | 2 + src/field/extension_field/quartic.rs | 2 + src/field/field_types.rs | 2 + src/field/goldilocks_field.rs | 2 + src/field/secp256k1.rs | 2 + 10 files changed, 376 insertions(+), 16 deletions(-) create mode 100644 src/curve/curve_multiplication.rs create mode 100644 src/curve/curve_summation.rs diff --git a/src/curve/curve_adds.rs b/src/curve/curve_adds.rs index c5fc9ba4..32a5adcc 100644 --- a/src/curve/curve_adds.rs +++ b/src/curve/curve_adds.rs @@ -1,14 +1,24 @@ use std::ops::Add; -use crate::field::field_types::Field; use crate::curve::curve_types::{AffinePoint, Curve, ProjectivePoint}; +use crate::field::field_types::Field; impl Add> for ProjectivePoint { type Output = ProjectivePoint; fn add(self, rhs: ProjectivePoint) -> Self::Output { - let ProjectivePoint { x: x1, y: y1, z: z1, zero: zero1 } = self; - let ProjectivePoint { x: x2, y: y2, z: z2, zero: zero2 } = rhs; + let ProjectivePoint { + x: x1, + y: y1, + z: z1, + zero: zero1, + } = self; + let ProjectivePoint { + x: x2, + y: y2, + z: z2, + zero: zero2, + } = rhs; if zero1 { return rhs; @@ -52,8 +62,17 @@ impl Add> for ProjectivePoint { type Output = ProjectivePoint; fn add(self, rhs: AffinePoint) -> Self::Output { - let ProjectivePoint { x: x1, y: y1, z: z1, zero: zero1 } = self; - let AffinePoint { x: x2, y: y2, zero: zero2 } = rhs; + let ProjectivePoint { + x: x1, + y: y1, + z: z1, + zero: zero1, + } = self; + let AffinePoint { + x: x2, + y: y2, + zero: zero2, + } = rhs; if zero1 { return rhs.to_projective(); @@ -94,8 +113,16 @@ impl Add> for AffinePoint { type Output = ProjectivePoint; fn add(self, rhs: AffinePoint) -> Self::Output { - let AffinePoint { x: x1, y: y1, zero: zero1 } = self; - let AffinePoint { x: x2, y: y2, zero: zero2 } = rhs; + let AffinePoint { + x: x1, + y: y1, + zero: zero1, + } = self; + let AffinePoint { + x: x2, + y: y2, + zero: zero2, + } = rhs; if zero1 { return rhs.to_projective(); diff --git a/src/curve/curve_multiplication.rs b/src/curve/curve_multiplication.rs new file mode 100644 index 00000000..e5ac0eb3 --- /dev/null +++ b/src/curve/curve_multiplication.rs @@ -0,0 +1,89 @@ +use std::ops::Mul; + +use crate::curve::curve_summation::affine_summation_batch_inversion; +use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar, ProjectivePoint}; +use crate::field::field_types::Field; + +const WINDOW_BITS: usize = 4; +const BASE: usize = 1 << WINDOW_BITS; + +fn digits_per_scalar() -> usize { + (C::ScalarField::BITS + WINDOW_BITS - 1) / WINDOW_BITS +} + +/// Precomputed state used for scalar x ProjectivePoint multiplications, +/// specific to a particular generator. +#[derive(Clone)] +pub struct MultiplicationPrecomputation { + /// [(2^w)^i] g for each i < digits_per_scalar. + powers: Vec>, +} + +impl ProjectivePoint { + pub fn mul_precompute(&self) -> MultiplicationPrecomputation { + let num_digits = digits_per_scalar::(); + let mut powers_proj = Vec::with_capacity(num_digits); + powers_proj.push(*self); + for i in 1..num_digits { + let mut power_i_proj = powers_proj[i - 1]; + for _j in 0..WINDOW_BITS { + power_i_proj = power_i_proj.double(); + } + powers_proj.push(power_i_proj); + } + + let powers = ProjectivePoint::batch_to_affine(&powers_proj); + MultiplicationPrecomputation { powers } + } + + pub fn mul_with_precomputation( + &self, + scalar: C::ScalarField, + precomputation: MultiplicationPrecomputation, + ) -> Self { + // Yao's method; see https://koclab.cs.ucsb.edu/teaching/ecc/eccPapers/Doche-ch09.pdf + let precomputed_powers = precomputation.powers; + + let digits = to_digits::(&scalar); + + let mut y = ProjectivePoint::ZERO; + let mut u = ProjectivePoint::ZERO; + for j in (1..BASE).rev() { + let mut u_summands = Vec::new(); + for (i, &digit) in digits.iter().enumerate() { + if digit == j as u64 { + u_summands.push(precomputed_powers[i]); + } + } + u = u + affine_summation_batch_inversion(u_summands); + y = y + u; + } + y + } +} + +impl Mul> for CurveScalar { + type Output = ProjectivePoint; + + fn mul(self, rhs: ProjectivePoint) -> Self::Output { + let precomputation = rhs.mul_precompute(); + rhs.mul_with_precomputation(self.0, precomputation) + } +} + +#[allow(clippy::assertions_on_constants)] +fn to_digits(x: &C::ScalarField) -> Vec { + debug_assert!( + 64 % WINDOW_BITS == 0, + "For simplicity, only power-of-two window sizes are handled for now" + ); + let digits_per_u64 = 64 / WINDOW_BITS; + let mut digits = Vec::with_capacity(digits_per_scalar::()); + for limb in x.to_biguint().to_u64_digits() { + for j in 0..digits_per_u64 { + digits.push((limb >> (j * WINDOW_BITS) as u64) % BASE as u64); + } + } + + digits +} diff --git a/src/curve/curve_summation.rs b/src/curve/curve_summation.rs new file mode 100644 index 00000000..501a4977 --- /dev/null +++ b/src/curve/curve_summation.rs @@ -0,0 +1,236 @@ +use std::iter::Sum; + +use crate::curve::curve_types::{AffinePoint, Curve, ProjectivePoint}; +use crate::field::field_types::Field; + +impl Sum> for ProjectivePoint { + fn sum>>(iter: I) -> ProjectivePoint { + let points: Vec<_> = iter.collect(); + affine_summation_best(points) + } +} + +impl Sum for ProjectivePoint { + fn sum>>(iter: I) -> ProjectivePoint { + iter.fold(ProjectivePoint::ZERO, |acc, x| acc + x) + } +} + +pub fn affine_summation_best(summation: Vec>) -> ProjectivePoint { + let result = affine_multisummation_best(vec![summation]); + debug_assert_eq!(result.len(), 1); + result[0] +} + +pub fn affine_multisummation_best( + summations: Vec>>, +) -> Vec> { + let pairwise_sums: usize = summations.iter().map(|summation| summation.len() / 2).sum(); + + // This threshold is chosen based on data from the summation benchmarks. + if pairwise_sums < 70 { + affine_multisummation_pairwise(summations) + } else { + affine_multisummation_batch_inversion(summations) + } +} + +/// Adds each pair of points using an affine + affine = projective formula, then adds up the +/// intermediate sums using a projective formula. +pub fn affine_multisummation_pairwise( + summations: Vec>>, +) -> Vec> { + summations + .into_iter() + .map(affine_summation_pairwise) + .collect() +} + +/// Adds each pair of points using an affine + affine = projective formula, then adds up the +/// intermediate sums using a projective formula. +pub fn affine_summation_pairwise(points: Vec>) -> ProjectivePoint { + let mut reduced_points: Vec> = Vec::new(); + for chunk in points.chunks(2) { + match chunk.len() { + 1 => reduced_points.push(chunk[0].to_projective()), + 2 => reduced_points.push(chunk[0] + chunk[1]), + _ => panic!(), + } + } + // TODO: Avoid copying (deref) + reduced_points + .iter() + .fold(ProjectivePoint::ZERO, |sum, x| sum + *x) +} + +/// Computes several summations of affine points by applying an affine group law, except that the +/// divisions are batched via Montgomery's trick. +pub fn affine_summation_batch_inversion( + summation: Vec>, +) -> ProjectivePoint { + let result = affine_multisummation_batch_inversion(vec![summation]); + debug_assert_eq!(result.len(), 1); + result[0] +} + +/// Computes several summations of affine points by applying an affine group law, except that the +/// divisions are batched via Montgomery's trick. +pub fn affine_multisummation_batch_inversion( + summations: Vec>>, +) -> Vec> { + let mut elements_to_invert = Vec::new(); + + // For each pair of points, (x1, y1) and (x2, y2), that we're going to add later, we want to + // invert either y (if the points are equal) or x1 - x2 (otherwise). We will use these later. + for summation in &summations { + let n = summation.len(); + // The special case for n=0 is to avoid underflow. + let range_end = if n == 0 { 0 } else { n - 1 }; + + for i in (0..range_end).step_by(2) { + let p1 = summation[i]; + let p2 = summation[i + 1]; + let AffinePoint { + x: x1, + y: y1, + zero: zero1, + } = p1; + let AffinePoint { + x: x2, + y: _y2, + zero: zero2, + } = p2; + + if zero1 || zero2 || p1 == -p2 { + // These are trivial cases where we won't need any inverse. + } else if p1 == p2 { + elements_to_invert.push(y1.double()); + } else { + elements_to_invert.push(x1 - x2); + } + } + } + + let inverses: Vec = + C::BaseField::batch_multiplicative_inverse(&elements_to_invert); + + let mut all_reduced_points = Vec::with_capacity(summations.len()); + let mut inverse_index = 0; + for summation in summations { + let n = summation.len(); + let mut reduced_points = Vec::with_capacity((n + 1) / 2); + + // The special case for n=0 is to avoid underflow. + let range_end = if n == 0 { 0 } else { n - 1 }; + + for i in (0..range_end).step_by(2) { + let p1 = summation[i]; + let p2 = summation[i + 1]; + let AffinePoint { + x: x1, + y: y1, + zero: zero1, + } = p1; + let AffinePoint { + x: x2, + y: y2, + zero: zero2, + } = p2; + + let sum = if zero1 { + p2 + } else if zero2 { + p1 + } else if p1 == -p2 { + AffinePoint::ZERO + } else { + // It's a non-trivial case where we need one of the inverses we computed earlier. + let inverse = inverses[inverse_index]; + inverse_index += 1; + + if p1 == p2 { + // This is the doubling case. + let mut numerator = x1.square().triple(); + if C::A.is_nonzero() { + numerator = numerator + C::A; + } + let quotient = numerator * inverse; + let x3 = quotient.square() - x1.double(); + let y3 = quotient * (x1 - x3) - y1; + AffinePoint::nonzero(x3, y3) + } else { + // This is the general case. We use the incomplete addition formulas 4.3 and 4.4. + let quotient = (y1 - y2) * inverse; + let x3 = quotient.square() - x1 - x2; + let y3 = quotient * (x1 - x3) - y1; + AffinePoint::nonzero(x3, y3) + } + }; + reduced_points.push(sum); + } + + // If n is odd, the last point was not part of a pair. + if n % 2 == 1 { + reduced_points.push(summation[n - 1]); + } + + all_reduced_points.push(reduced_points); + } + + // We should have consumed all of the inverses from the batch computation. + debug_assert_eq!(inverse_index, inverses.len()); + + // Recurse with our smaller set of points. + affine_multisummation_best(all_reduced_points) +} + +#[cfg(test)] +mod tests { + use crate::{ + affine_summation_batch_inversion, affine_summation_pairwise, Bls12377, Curve, + ProjectivePoint, + }; + + #[test] + fn test_pairwise_affine_summation() { + let g_affine = Bls12377::GENERATOR_AFFINE; + let g2_affine = (g_affine + g_affine).to_affine(); + let g3_affine = (g_affine + g_affine + g_affine).to_affine(); + let g2_proj = g2_affine.to_projective(); + let g3_proj = g3_affine.to_projective(); + assert_eq!( + affine_summation_pairwise::(vec![g_affine, g_affine]), + g2_proj + ); + assert_eq!( + affine_summation_pairwise::(vec![g_affine, g2_affine]), + g3_proj + ); + assert_eq!( + affine_summation_pairwise::(vec![g_affine, g_affine, g_affine]), + g3_proj + ); + assert_eq!( + affine_summation_pairwise::(vec![]), + ProjectivePoint::ZERO + ); + } + + #[test] + fn test_pairwise_affine_summation_batch_inversion() { + let g = Bls12377::GENERATOR_AFFINE; + let g_proj = g.to_projective(); + assert_eq!( + affine_summation_batch_inversion::(vec![g, g]), + g_proj + g_proj + ); + assert_eq!( + affine_summation_batch_inversion::(vec![g, g, g]), + g_proj + g_proj + g_proj + ); + assert_eq!( + affine_summation_batch_inversion::(vec![]), + ProjectivePoint::ZERO + ); + } +} diff --git a/src/curve/curve_types.rs b/src/curve/curve_types.rs index f7d55c0c..830dc7c1 100644 --- a/src/curve/curve_types.rs +++ b/src/curve/curve_types.rs @@ -1,9 +1,9 @@ +use std::fmt::Debug; use std::ops::Neg; use anyhow::Result; use crate::field::field_types::Field; -use std::fmt::Debug; // To avoid implementation conflicts from associated types, // see https://github.com/rust-lang/rust/issues/20400 @@ -54,9 +54,10 @@ pub trait Curve: 'static + Sync + Sized + Copy + Debug { Ok(res) }*/ - fn is_safe_curve() -> bool{ + fn is_safe_curve() -> bool { // Added additional check to prevent using vulnerabilties in case a discriminant is equal to 0. - (Self::A.cube().double().double() + Self::B.square().triple().triple().triple()).is_nonzero() + (Self::A.cube().double().double() + Self::B.square().triple().triple().triple()) + .is_nonzero() } } @@ -101,11 +102,7 @@ impl AffinePoint { } pub fn double(&self) -> Self { - let AffinePoint { - x: x1, - y: y1, - zero, - } = *self; + let AffinePoint { x: x1, y: y1, zero } = *self; if zero { return AffinePoint::ZERO; @@ -124,7 +121,6 @@ impl AffinePoint { zero: false, } } - } impl PartialEq for AffinePoint { diff --git a/src/curve/mod.rs b/src/curve/mod.rs index 1e536564..8b9df88e 100644 --- a/src/curve/mod.rs +++ b/src/curve/mod.rs @@ -1,2 +1,4 @@ pub mod curve_adds; +pub mod curve_multiplication; +pub mod curve_summation; pub mod curve_types; diff --git a/src/field/extension_field/quadratic.rs b/src/field/extension_field/quadratic.rs index e2794330..b724095a 100644 --- a/src/field/extension_field/quadratic.rs +++ b/src/field/extension_field/quadratic.rs @@ -67,6 +67,8 @@ impl> Field for QuadraticExtension { const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self(F::EXT_MULTIPLICATIVE_GROUP_GENERATOR); const POWER_OF_TWO_GENERATOR: Self = Self(F::EXT_POWER_OF_TWO_GENERATOR); + const BITS: usize = F::BITS * 2; + fn order() -> BigUint { F::order() * F::order() } diff --git a/src/field/extension_field/quartic.rs b/src/field/extension_field/quartic.rs index 01918ff3..0d221401 100644 --- a/src/field/extension_field/quartic.rs +++ b/src/field/extension_field/quartic.rs @@ -69,6 +69,8 @@ impl> Field for QuarticExtension { const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self(F::EXT_MULTIPLICATIVE_GROUP_GENERATOR); const POWER_OF_TWO_GENERATOR: Self = Self(F::EXT_POWER_OF_TWO_GENERATOR); + const BITS: usize = F::BITS * 4; + fn order() -> BigUint { F::order().pow(4u32) } diff --git a/src/field/field_types.rs b/src/field/field_types.rs index 250338fb..80eeecff 100644 --- a/src/field/field_types.rs +++ b/src/field/field_types.rs @@ -59,6 +59,8 @@ pub trait Field: /// Generator of a multiplicative subgroup of order `2^TWO_ADICITY`. const POWER_OF_TWO_GENERATOR: Self; + const BITS: usize; + fn order() -> BigUint; #[inline] diff --git a/src/field/goldilocks_field.rs b/src/field/goldilocks_field.rs index cb85d56d..058b6db8 100644 --- a/src/field/goldilocks_field.rs +++ b/src/field/goldilocks_field.rs @@ -82,6 +82,8 @@ impl Field for GoldilocksField { // ``` const POWER_OF_TWO_GENERATOR: Self = Self(1753635133440165772); + const BITS: usize = 64; + fn order() -> BigUint { Self::ORDER.into() } diff --git a/src/field/secp256k1.rs b/src/field/secp256k1.rs index 75221a1f..acb1df4e 100644 --- a/src/field/secp256k1.rs +++ b/src/field/secp256k1.rs @@ -91,6 +91,8 @@ impl Field for Secp256K1Base { // Sage: `g_2 = g^((p - 1) / 2)` const POWER_OF_TWO_GENERATOR: Self = Self::NEG_ONE; + const BITS: usize = 256; + fn order() -> BigUint { BigUint::from_slice(&[ 0xFFFFFC2F, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, From 869a5860f424268b3b7368d98944acb1b638d003 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 28 Oct 2021 19:54:39 -0700 Subject: [PATCH 07/39] Secp256K1 scalar field --- src/curve/mod.rs | 1 + src/curve/secp256k1_curve.rs | 81 ++++++ src/field/mod.rs | 3 +- src/field/{secp256k1.rs => secp256k1_base.rs} | 2 +- src/field/secp256k1_scalar.rs | 253 ++++++++++++++++++ 5 files changed, 338 insertions(+), 2 deletions(-) create mode 100644 src/curve/secp256k1_curve.rs rename src/field/{secp256k1.rs => secp256k1_base.rs} (99%) create mode 100644 src/field/secp256k1_scalar.rs diff --git a/src/curve/mod.rs b/src/curve/mod.rs index 8b9df88e..01841018 100644 --- a/src/curve/mod.rs +++ b/src/curve/mod.rs @@ -2,3 +2,4 @@ pub mod curve_adds; pub mod curve_multiplication; pub mod curve_summation; pub mod curve_types; +//pub mod secp256k1_curve; \ No newline at end of file diff --git a/src/curve/secp256k1_curve.rs b/src/curve/secp256k1_curve.rs new file mode 100644 index 00000000..78ce993e --- /dev/null +++ b/src/curve/secp256k1_curve.rs @@ -0,0 +1,81 @@ +use crate::curve::curve_types::{AffinePoint, Curve}; +use crate::field::field_types::Field; +use crate::field::secp256k1_base::Secp256K1Base; +use crate::field::secp256k1_scalar::Secp256K1Scalar; + +// Parameters taken from the implementation of Bls12-377 in Zexe found here: +// https://github.com/scipr-lab/zexe/blob/master/algebra/src/curves/bls12_377/g1.rs + +#[derive(Debug, Copy, Clone)] +pub struct Secp256K1; + +impl Curve for Bls12377 { + type BaseField = Bls12377Base; + type ScalarField = Bls12377Scalar; + + const A: Bls12377Base = Bls12377Base::ZERO; + const B: Bls12377Base = Bls12377Base::ONE; + const GENERATOR_AFFINE: AffinePoint = AffinePoint { + x: BLS12_377_GENERATOR_X, + y: BLS12_377_GENERATOR_Y, + zero: false, + }; +} + +/// 81937999373150964239938255573465948239988671502647976594219695644855304257327692006745978603320413799295628339695 +const BLS12_377_GENERATOR_X: Bls12377Base = Bls12377Base { + limbs: [2742467569752756724, 14217256487979144792, 6635299530028159197, 8509097278468658840, + 14518893593143693938, 46181716169194829] +}; + +/// 241266749859715473739788878240585681733927191168601896383759122102112907357779751001206799952863815012735208165030 +const BLS12_377_GENERATOR_Y: Bls12377Base = Bls12377Base { + limbs: [9336971515457667571, 28021381849722296, 18085035374859187530, 14013031479170682136, + 3369780711397861396, 35370409237953649] +}; + +#[cfg(test)] +mod tests { + use crate::{blake_hash_usize_to_curve, Bls12377, Bls12377Scalar, Curve, Field, ProjectivePoint}; + + #[test] + fn test_double_affine() { + for i in 0..100 { + let p = blake_hash_usize_to_curve::(i); + assert_eq!( + p.double(), + p.to_projective().double().to_affine()); + } + } + + #[test] + fn test_naive_multiplication() { + let g = Bls12377::GENERATOR_PROJECTIVE; + let ten = Bls12377Scalar::from_canonical_u64(10); + let product = mul_naive(ten, g); + let sum = g + g + g + g + g + g + g + g + g + g; + assert_eq!(product, sum); + } + + #[test] + fn test_g1_multiplication() { + let lhs = Bls12377Scalar::from_canonical([11111111, 22222222, 33333333, 44444444]); + assert_eq!(Bls12377::convert(lhs) * Bls12377::GENERATOR_PROJECTIVE, mul_naive(lhs, Bls12377::GENERATOR_PROJECTIVE)); + } + + /// A simple, somewhat inefficient implementation of multiplication which is used as a reference + /// for correctness. + fn mul_naive(lhs: Bls12377Scalar, rhs: ProjectivePoint) -> ProjectivePoint { + let mut g = rhs; + let mut sum = ProjectivePoint::ZERO; + for limb in lhs.to_canonical().iter() { + for j in 0..64 { + if (limb >> j & 1u64) != 0u64 { + sum = sum + g; + } + g = g.double(); + } + } + sum + } +} diff --git a/src/field/mod.rs b/src/field/mod.rs index 5ed64a54..74e0fbf4 100644 --- a/src/field/mod.rs +++ b/src/field/mod.rs @@ -7,7 +7,8 @@ pub(crate) mod interpolation; mod inversion; pub(crate) mod packable; pub(crate) mod packed_field; -pub mod secp256k1; +pub mod secp256k1_base; +pub mod secp256k1_scalar; #[cfg(target_feature = "avx2")] pub(crate) mod packed_avx2; diff --git a/src/field/secp256k1.rs b/src/field/secp256k1_base.rs similarity index 99% rename from src/field/secp256k1.rs rename to src/field/secp256k1_base.rs index acb1df4e..a09edc30 100644 --- a/src/field/secp256k1.rs +++ b/src/field/secp256k1_base.rs @@ -88,7 +88,7 @@ impl Field for Secp256K1Base { // Sage: `g = GF(p).multiplicative_generator()` const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([5, 0, 0, 0]); - // Sage: `g_2 = g^((p - 1) / 2)` + // Sage: `g_2 = power_mod(g, (p - 1) // 2), p)` const POWER_OF_TWO_GENERATOR: Self = Self::NEG_ONE; const BITS: usize = 256; diff --git a/src/field/secp256k1_scalar.rs b/src/field/secp256k1_scalar.rs new file mode 100644 index 00000000..4423f726 --- /dev/null +++ b/src/field/secp256k1_scalar.rs @@ -0,0 +1,253 @@ +use std::convert::TryInto; +use std::fmt; +use std::fmt::{Debug, Display, Formatter}; +use std::hash::{Hash, Hasher}; +use std::iter::{Product, Sum}; +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +use itertools::Itertools; +use num::bigint::{BigUint, RandBigInt}; +use num::{Integer, One}; +use rand::Rng; +use serde::{Deserialize, Serialize}; + +use crate::field::field_types::Field; +use crate::field::goldilocks_field::GoldilocksField; + +/// The base field of the secp256k1 elliptic curve. +/// +/// Its order is +/// ```ignore +/// P = 0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141 +/// = 115792089237316195423570985008687907852837564279074904382605163141518161494337 +/// = 2**256 - 432420386565659656852420866394968145599 +/// ``` +#[derive(Copy, Clone, Serialize, Deserialize)] +pub struct Secp256K1Scalar(pub [u64; 4]); + +fn biguint_from_array(arr: [u64; 4]) -> BigUint { + BigUint::from_slice(&[ + arr[0] as u32, + (arr[0] >> 32) as u32, + arr[1] as u32, + (arr[1] >> 32) as u32, + arr[2] as u32, + (arr[2] >> 32) as u32, + arr[3] as u32, + (arr[3] >> 32) as u32, + ]) +} + +impl Default for Secp256K1Scalar { + fn default() -> Self { + Self::ZERO + } +} + +impl PartialEq for Secp256K1Scalar { + fn eq(&self, other: &Self) -> bool { + self.to_biguint() == other.to_biguint() + } +} + +impl Eq for Secp256K1Scalar {} + +impl Hash for Secp256K1Scalar { + fn hash(&self, state: &mut H) { + self.to_biguint().hash(state) + } +} + +impl Display for Secp256K1Scalar { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + Display::fmt(&self.to_biguint(), f) + } +} + +impl Debug for Secp256K1Scalar { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + Debug::fmt(&self.to_biguint(), f) + } +} + +impl Field for Secp256K1Scalar { + // TODO: fix + type PrimeField = GoldilocksField; + + const ZERO: Self = Self([0; 4]); + const ONE: Self = Self([1, 0, 0, 0]); + const TWO: Self = Self([2, 0, 0, 0]); + const NEG_ONE: Self = Self([ + 0xBFD25E8CD0364140, + 0xBAAEDCE6AF48A03B, + 0xFFFFFFFFFFFFFC2F, + 0xFFFFFFFFFFFFFFFF + ]); + + // TODO: fix + const CHARACTERISTIC: u64 = 0; + + const TWO_ADICITY: usize = 6; + + // Sage: `g = GF(p).multiplicative_generator()` + const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([7, 0, 0, 0]); + + // Sage: `g_2 = power_mod(g, (p - 1) // 2^6), p)` + // 5480320495727936603795231718619559942670027629901634955707709633242980176626 + const POWER_OF_TWO_GENERATOR: Self = Self([ + 0x992f4b5402b052f2, + 0x98BDEAB680756045, + 0xDF9879A3FBC483A8, + 0xC1DC060E7A91986, + ]); + + const BITS: usize = 256; + + fn order() -> BigUint { + BigUint::from_slice(&[ + 0xD0364141, 0xBFD25E8C, 0xAF48A03B, 0xBAAEDCE6, 0xFFFFFC2F, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF + ]) + } + + fn try_inverse(&self) -> Option { + if self.is_zero() { + return None; + } + + // Fermat's Little Theorem + Some(self.exp_biguint(&(Self::order() - BigUint::one() - BigUint::one()))) + } + + fn to_biguint(&self) -> BigUint { + let mut result = biguint_from_array(self.0); + if result >= Self::order() { + result -= Self::order(); + } + result + } + + fn from_biguint(val: BigUint) -> Self { + Self( + val.to_u64_digits() + .into_iter() + .pad_using(4, |_| 0) + .collect::>()[..] + .try_into() + .expect("error converting to u64 array"), + ) + } + + #[inline] + fn from_canonical_u64(n: u64) -> Self { + Self([n, 0, 0, 0]) + } + + #[inline] + fn from_noncanonical_u128(n: u128) -> Self { + Self([n as u64, (n >> 64) as u64, 0, 0]) + } + + #[inline] + fn from_noncanonical_u96(n: (u64, u32)) -> Self { + Self([n.0, n.1 as u64, 0, 0]) + } + + fn rand_from_rng(rng: &mut R) -> Self { + Self::from_biguint(rng.gen_biguint_below(&Self::order())) + } +} + +impl Neg for Secp256K1Scalar { + type Output = Self; + + #[inline] + fn neg(self) -> Self { + if self.is_zero() { + Self::ZERO + } else { + Self::from_biguint(Self::order() - self.to_biguint()) + } + } +} + +impl Add for Secp256K1Scalar { + type Output = Self; + + #[inline] + fn add(self, rhs: Self) -> Self { + let mut result = self.to_biguint() + rhs.to_biguint(); + if result >= Self::order() { + result -= Self::order(); + } + Self::from_biguint(result) + } +} + +impl AddAssign for Secp256K1Scalar { + #[inline] + fn add_assign(&mut self, rhs: Self) { + *self = *self + rhs; + } +} + +impl Sum for Secp256K1Scalar { + fn sum>(iter: I) -> Self { + iter.fold(Self::ZERO, |acc, x| acc + x) + } +} + +impl Sub for Secp256K1Scalar { + type Output = Self; + + #[inline] + #[allow(clippy::suspicious_arithmetic_impl)] + fn sub(self, rhs: Self) -> Self { + self + -rhs + } +} + +impl SubAssign for Secp256K1Scalar { + #[inline] + fn sub_assign(&mut self, rhs: Self) { + *self = *self - rhs; + } +} + +impl Mul for Secp256K1Scalar { + type Output = Self; + + #[inline] + fn mul(self, rhs: Self) -> Self { + Self::from_biguint((self.to_biguint() * rhs.to_biguint()).mod_floor(&Self::order())) + } +} + +impl MulAssign for Secp256K1Scalar { + #[inline] + fn mul_assign(&mut self, rhs: Self) { + *self = *self * rhs; + } +} + +impl Product for Secp256K1Scalar { + #[inline] + fn product>(iter: I) -> Self { + iter.reduce(|acc, x| acc * x).unwrap_or(Self::ONE) + } +} + +impl Div for Secp256K1Scalar { + type Output = Self; + + #[allow(clippy::suspicious_arithmetic_impl)] + fn div(self, rhs: Self) -> Self::Output { + self * rhs.inverse() + } +} + +impl DivAssign for Secp256K1Scalar { + fn div_assign(&mut self, rhs: Self) { + *self = *self / rhs; + } +} From 50db118718ac8bd7fa2f05e417cdb1786593444a Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 28 Oct 2021 20:10:19 -0700 Subject: [PATCH 08/39] Secp256K1 curve (in progress) --- src/curve/mod.rs | 2 +- src/curve/secp256k1_curve.rs | 56 ++++++++++++++++++++---------------- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/curve/mod.rs b/src/curve/mod.rs index 01841018..e1bcb291 100644 --- a/src/curve/mod.rs +++ b/src/curve/mod.rs @@ -2,4 +2,4 @@ pub mod curve_adds; pub mod curve_multiplication; pub mod curve_summation; pub mod curve_types; -//pub mod secp256k1_curve; \ No newline at end of file +pub mod secp256k1_curve; \ No newline at end of file diff --git a/src/curve/secp256k1_curve.rs b/src/curve/secp256k1_curve.rs index 78ce993e..4e74a5f7 100644 --- a/src/curve/secp256k1_curve.rs +++ b/src/curve/secp256k1_curve.rs @@ -9,39 +9,45 @@ use crate::field::secp256k1_scalar::Secp256K1Scalar; #[derive(Debug, Copy, Clone)] pub struct Secp256K1; -impl Curve for Bls12377 { - type BaseField = Bls12377Base; - type ScalarField = Bls12377Scalar; +impl Curve for Secp256K1 { + type BaseField = Secp256K1Base; + type ScalarField = Secp256K1Scalar; - const A: Bls12377Base = Bls12377Base::ZERO; - const B: Bls12377Base = Bls12377Base::ONE; + const A: Secp256K1Base = Secp256K1Base::ZERO; + const B: Secp256K1Base = Secp256K1Base::ONE; const GENERATOR_AFFINE: AffinePoint = AffinePoint { - x: BLS12_377_GENERATOR_X, - y: BLS12_377_GENERATOR_Y, + x: SECP256K1_GENERATOR_X, + y: SECP256K1_GENERATOR_Y, zero: false, }; } -/// 81937999373150964239938255573465948239988671502647976594219695644855304257327692006745978603320413799295628339695 -const BLS12_377_GENERATOR_X: Bls12377Base = Bls12377Base { - limbs: [2742467569752756724, 14217256487979144792, 6635299530028159197, 8509097278468658840, - 14518893593143693938, 46181716169194829] -}; +const SECP256K1_GENERATOR_X: Secp256K1Base = Secp256K1Base([ + 0x59F2815B16F81798, + 0x029BFCDB2DCE28D9, + 0x55A06295CE870B07, + 0x79BE667EF9DCBBAC, +]); /// 241266749859715473739788878240585681733927191168601896383759122102112907357779751001206799952863815012735208165030 -const BLS12_377_GENERATOR_Y: Bls12377Base = Bls12377Base { - limbs: [9336971515457667571, 28021381849722296, 18085035374859187530, 14013031479170682136, - 3369780711397861396, 35370409237953649] -}; +const SECP256K1_GENERATOR_Y: Secp256K1Base = Secp256K1Base([ + 0x9C47D08FFB10D4B8, + 0xFD17B448A6855419, + 0x5DA4FBFC0E1108A8, + 0x483ADA7726A3C465, +]); #[cfg(test)] mod tests { - use crate::{blake_hash_usize_to_curve, Bls12377, Bls12377Scalar, Curve, Field, ProjectivePoint}; + use crate::field::field_types::Field; + use crate::field::secp256k1_scalar::Secp256K1Scalar; + use crate::curve::curve_types::{Curve, ProjectivePoint}; + use crate::curve::secp256k1_curve::Secp256K1; - #[test] + /*#[test] fn test_double_affine() { for i in 0..100 { - let p = blake_hash_usize_to_curve::(i); + //let p = blake_hash_usize_to_curve::(i); assert_eq!( p.double(), p.to_projective().double().to_affine()); @@ -50,8 +56,8 @@ mod tests { #[test] fn test_naive_multiplication() { - let g = Bls12377::GENERATOR_PROJECTIVE; - let ten = Bls12377Scalar::from_canonical_u64(10); + let g = Secp256K1::GENERATOR_PROJECTIVE; + let ten = Secp256K1Scalar::from_canonical_u64(10); let product = mul_naive(ten, g); let sum = g + g + g + g + g + g + g + g + g + g; assert_eq!(product, sum); @@ -59,13 +65,13 @@ mod tests { #[test] fn test_g1_multiplication() { - let lhs = Bls12377Scalar::from_canonical([11111111, 22222222, 33333333, 44444444]); - assert_eq!(Bls12377::convert(lhs) * Bls12377::GENERATOR_PROJECTIVE, mul_naive(lhs, Bls12377::GENERATOR_PROJECTIVE)); + let lhs = Secp256K1Scalar::from_canonical([11111111, 22222222, 33333333, 44444444]); + assert_eq!(Secp256K1::convert(lhs) * Secp256K1::GENERATOR_PROJECTIVE, mul_naive(lhs, Secp256K1::GENERATOR_PROJECTIVE)); } /// A simple, somewhat inefficient implementation of multiplication which is used as a reference /// for correctness. - fn mul_naive(lhs: Bls12377Scalar, rhs: ProjectivePoint) -> ProjectivePoint { + fn mul_naive(lhs: Secp256K1Scalar, rhs: ProjectivePoint) -> ProjectivePoint { let mut g = rhs; let mut sum = ProjectivePoint::ZERO; for limb in lhs.to_canonical().iter() { @@ -77,5 +83,5 @@ mod tests { } } sum - } + }*/ } From 2c2d36a6be23fed25af854259c78e2c1b11f039f Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 10 Nov 2021 11:49:50 -0800 Subject: [PATCH 09/39] merge --- src/curve/mod.rs | 2 +- src/curve/secp256k1_curve.rs | 6 +++--- src/field/secp256k1_scalar.rs | 4 ++-- src/gadgets/mod.rs | 1 + src/gadgets/secp256k1.rs | 32 ++++++++++++++++++++++++++++++++ 5 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 src/gadgets/secp256k1.rs diff --git a/src/curve/mod.rs b/src/curve/mod.rs index e1bcb291..c65f2acd 100644 --- a/src/curve/mod.rs +++ b/src/curve/mod.rs @@ -2,4 +2,4 @@ pub mod curve_adds; pub mod curve_multiplication; pub mod curve_summation; pub mod curve_types; -pub mod secp256k1_curve; \ No newline at end of file +pub mod secp256k1_curve; diff --git a/src/curve/secp256k1_curve.rs b/src/curve/secp256k1_curve.rs index 4e74a5f7..21340c64 100644 --- a/src/curve/secp256k1_curve.rs +++ b/src/curve/secp256k1_curve.rs @@ -22,7 +22,7 @@ impl Curve for Secp256K1 { }; } -const SECP256K1_GENERATOR_X: Secp256K1Base = Secp256K1Base([ +const SECP256K1_GENERATOR_X: Secp256K1Base = Secp256K1Base([ 0x59F2815B16F81798, 0x029BFCDB2DCE28D9, 0x55A06295CE870B07, @@ -39,10 +39,10 @@ const SECP256K1_GENERATOR_Y: Secp256K1Base = Secp256K1Base([ #[cfg(test)] mod tests { - use crate::field::field_types::Field; - use crate::field::secp256k1_scalar::Secp256K1Scalar; use crate::curve::curve_types::{Curve, ProjectivePoint}; use crate::curve::secp256k1_curve::Secp256K1; + use crate::field::field_types::Field; + use crate::field::secp256k1_scalar::Secp256K1Scalar; /*#[test] fn test_double_affine() { diff --git a/src/field/secp256k1_scalar.rs b/src/field/secp256k1_scalar.rs index 4423f726..0c406b86 100644 --- a/src/field/secp256k1_scalar.rs +++ b/src/field/secp256k1_scalar.rs @@ -81,7 +81,7 @@ impl Field for Secp256K1Scalar { 0xBFD25E8CD0364140, 0xBAAEDCE6AF48A03B, 0xFFFFFFFFFFFFFC2F, - 0xFFFFFFFFFFFFFFFF + 0xFFFFFFFFFFFFFFFF, ]); // TODO: fix @@ -106,7 +106,7 @@ impl Field for Secp256K1Scalar { fn order() -> BigUint { BigUint::from_slice(&[ 0xD0364141, 0xBFD25E8C, 0xAF48A03B, 0xBAAEDCE6, 0xFFFFFC2F, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF + 0xFFFFFFFF, ]) } diff --git a/src/gadgets/mod.rs b/src/gadgets/mod.rs index 8b6e60f6..42b3044c 100644 --- a/src/gadgets/mod.rs +++ b/src/gadgets/mod.rs @@ -11,6 +11,7 @@ pub mod permutation; pub mod polynomial; pub mod random_access; pub mod range_check; +pub mod secp256k1; pub mod select; pub mod sorting; pub mod split_base; diff --git a/src/gadgets/secp256k1.rs b/src/gadgets/secp256k1.rs new file mode 100644 index 00000000..36d8d145 --- /dev/null +++ b/src/gadgets/secp256k1.rs @@ -0,0 +1,32 @@ +use crate::curve::curve_types::{AffinePoint, Curve}; +use crate::field::extension_field::Extendable; +use crate::field::field_types::RichField; +use crate::gadgets::nonnative::ForeignFieldTarget; +use crate::plonk::circuit_builder::CircuitBuilder; + +#[derive(Clone, Debug)] +pub struct AffinePointTarget { + pub x: ForeignFieldTarget, + pub y: ForeignFieldTarget, +} + +impl AffinePointTarget { + pub fn to_vec(&self) -> Vec> { + vec![self.x.clone(), self.y.clone()] + } +} + +impl, const D: usize> CircuitBuilder { + pub fn constant_affine_point>( + &mut self, + point: AffinePoint, + ) -> AffinePointTarget { + debug_assert!(!point.zero); + AffinePointTarget { + x: self.constant_ff(point.x), + y: self.constant_ff(point.y), + } + } +} + +mod tests {} From 0e1f0c556293b66fd647290e59dc154f5ca5aaa7 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 10 Nov 2021 11:50:04 -0800 Subject: [PATCH 10/39] merge --- src/curve/curve_summation.rs | 25 ++++++++++++------------ src/gadgets/secp256k1.rs | 38 +++++++++++++++++++++++++++++------- 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/curve/curve_summation.rs b/src/curve/curve_summation.rs index 501a4977..ad4232ce 100644 --- a/src/curve/curve_summation.rs +++ b/src/curve/curve_summation.rs @@ -186,50 +186,51 @@ pub fn affine_multisummation_batch_inversion( #[cfg(test)] mod tests { - use crate::{ - affine_summation_batch_inversion, affine_summation_pairwise, Bls12377, Curve, - ProjectivePoint, + use crate::curve::curve_summation::{ + affine_summation_batch_inversion, affine_summation_pairwise, }; + use crate::curve::curve_types::{Curve, ProjectivePoint}; + use crate::curve::secp256k1_curve::Secp256K1; #[test] fn test_pairwise_affine_summation() { - let g_affine = Bls12377::GENERATOR_AFFINE; + let g_affine = Secp256K1::GENERATOR_AFFINE; let g2_affine = (g_affine + g_affine).to_affine(); let g3_affine = (g_affine + g_affine + g_affine).to_affine(); let g2_proj = g2_affine.to_projective(); let g3_proj = g3_affine.to_projective(); assert_eq!( - affine_summation_pairwise::(vec![g_affine, g_affine]), + affine_summation_pairwise::(vec![g_affine, g_affine]), g2_proj ); assert_eq!( - affine_summation_pairwise::(vec![g_affine, g2_affine]), + affine_summation_pairwise::(vec![g_affine, g2_affine]), g3_proj ); assert_eq!( - affine_summation_pairwise::(vec![g_affine, g_affine, g_affine]), + affine_summation_pairwise::(vec![g_affine, g_affine, g_affine]), g3_proj ); assert_eq!( - affine_summation_pairwise::(vec![]), + affine_summation_pairwise::(vec![]), ProjectivePoint::ZERO ); } #[test] fn test_pairwise_affine_summation_batch_inversion() { - let g = Bls12377::GENERATOR_AFFINE; + let g = Secp256K1::GENERATOR_AFFINE; let g_proj = g.to_projective(); assert_eq!( - affine_summation_batch_inversion::(vec![g, g]), + affine_summation_batch_inversion::(vec![g, g]), g_proj + g_proj ); assert_eq!( - affine_summation_batch_inversion::(vec![g, g, g]), + affine_summation_batch_inversion::(vec![g, g, g]), g_proj + g_proj + g_proj ); assert_eq!( - affine_summation_batch_inversion::(vec![]), + affine_summation_batch_inversion::(vec![]), ProjectivePoint::ZERO ); } diff --git a/src/gadgets/secp256k1.rs b/src/gadgets/secp256k1.rs index 36d8d145..3294a954 100644 --- a/src/gadgets/secp256k1.rs +++ b/src/gadgets/secp256k1.rs @@ -4,29 +4,53 @@ use crate::field::field_types::RichField; use crate::gadgets::nonnative::ForeignFieldTarget; use crate::plonk::circuit_builder::CircuitBuilder; +/// A Target representing an affine point on the curve `C`. #[derive(Clone, Debug)] pub struct AffinePointTarget { - pub x: ForeignFieldTarget, - pub y: ForeignFieldTarget, + pub x: ForeignFieldTarget, + pub y: ForeignFieldTarget, } impl AffinePointTarget { - pub fn to_vec(&self) -> Vec> { + pub fn to_vec(&self) -> Vec> { vec![self.x.clone(), self.y.clone()] } } impl, const D: usize> CircuitBuilder { - pub fn constant_affine_point>( + pub fn constant_affine_point( &mut self, - point: AffinePoint, + point: AffinePoint, ) -> AffinePointTarget { debug_assert!(!point.zero); AffinePointTarget { - x: self.constant_ff(point.x), - y: self.constant_ff(point.y), + x: self.constant_nonnative(point.x), + y: self.constant_nonnative(point.y), } } + + pub fn connect_affine_point( + &mut self, + lhs: AffinePointTarget, + rhs: AffinePointTarget, + ) { + self.connect_nonnative(&lhs.x, &rhs.x); + self.connect_nonnative(&lhs.y, &rhs.y); + } + + pub fn curve_assert_valid(&mut self, p: AffinePointTarget) { + let a = self.constant_nonnative(C::A); + let b = self.constant_nonnative(C::B); + + let y_squared = self.mul_nonnative(&p.y, &p.y); + let x_squared = self.mul_nonnative(&p.x, &p.x); + let x_cubed = self.mul_nonnative(&x_squared, &p.x); + let a_x = self.mul_nonnative(&a, &p.x); + let a_x_plus_b = self.add_nonnative(&a_x, &b); + let rhs = self.add_nonnative(&x_cubed, &a_x_plus_b); + + self.connect_nonnative(&y_squared, &rhs); + } } mod tests {} From d1ad3fdbad00aae07379b91e5a52ea2890c46c65 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 1 Nov 2021 16:53:48 -0700 Subject: [PATCH 11/39] fix: generator value --- src/curve/secp256k1_curve.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/curve/secp256k1_curve.rs b/src/curve/secp256k1_curve.rs index 21340c64..89497a7d 100644 --- a/src/curve/secp256k1_curve.rs +++ b/src/curve/secp256k1_curve.rs @@ -14,7 +14,7 @@ impl Curve for Secp256K1 { type ScalarField = Secp256K1Scalar; const A: Secp256K1Base = Secp256K1Base::ZERO; - const B: Secp256K1Base = Secp256K1Base::ONE; + const B: Secp256K1Base = Secp256K1Base([7, 0, 0, 0]); const GENERATOR_AFFINE: AffinePoint = AffinePoint { x: SECP256K1_GENERATOR_X, y: SECP256K1_GENERATOR_Y, From a5f21de0beade0f5b966b33151e7f3616e78b4d9 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 1 Nov 2021 17:52:52 -0700 Subject: [PATCH 12/39] fixed curve_summation tests --- src/curve/secp256k1_curve.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/curve/secp256k1_curve.rs b/src/curve/secp256k1_curve.rs index 89497a7d..1e721640 100644 --- a/src/curve/secp256k1_curve.rs +++ b/src/curve/secp256k1_curve.rs @@ -39,6 +39,8 @@ const SECP256K1_GENERATOR_Y: Secp256K1Base = Secp256K1Base([ #[cfg(test)] mod tests { + use num::BigUint; + use crate::curve::curve_types::{Curve, ProjectivePoint}; use crate::curve::secp256k1_curve::Secp256K1; use crate::field::field_types::Field; @@ -52,7 +54,7 @@ mod tests { p.double(), p.to_projective().double().to_affine()); } - } + }*/ #[test] fn test_naive_multiplication() { @@ -65,7 +67,9 @@ mod tests { #[test] fn test_g1_multiplication() { - let lhs = Secp256K1Scalar::from_canonical([11111111, 22222222, 33333333, 44444444]); + let lhs = Secp256K1Scalar::from_biguint( + BigUint::from_slice(&[1111, 2222, 3333, 4444, 5555, 6666, 7777, 8888]) + ); assert_eq!(Secp256K1::convert(lhs) * Secp256K1::GENERATOR_PROJECTIVE, mul_naive(lhs, Secp256K1::GENERATOR_PROJECTIVE)); } @@ -74,7 +78,7 @@ mod tests { fn mul_naive(lhs: Secp256K1Scalar, rhs: ProjectivePoint) -> ProjectivePoint { let mut g = rhs; let mut sum = ProjectivePoint::ZERO; - for limb in lhs.to_canonical().iter() { + for limb in lhs.to_biguint().to_u64_digits().iter() { for j in 0..64 { if (limb >> j & 1u64) != 0u64 { sum = sum + g; @@ -83,5 +87,5 @@ mod tests { } } sum - }*/ + } } From f11fe2a92889f0760903c4182a3755dbc6f3b776 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 1 Nov 2021 17:53:01 -0700 Subject: [PATCH 13/39] fmt --- src/curve/secp256k1_curve.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/curve/secp256k1_curve.rs b/src/curve/secp256k1_curve.rs index 1e721640..7b84855b 100644 --- a/src/curve/secp256k1_curve.rs +++ b/src/curve/secp256k1_curve.rs @@ -67,15 +67,21 @@ mod tests { #[test] fn test_g1_multiplication() { - let lhs = Secp256K1Scalar::from_biguint( - BigUint::from_slice(&[1111, 2222, 3333, 4444, 5555, 6666, 7777, 8888]) + let lhs = Secp256K1Scalar::from_biguint(BigUint::from_slice(&[ + 1111, 2222, 3333, 4444, 5555, 6666, 7777, 8888, + ])); + assert_eq!( + Secp256K1::convert(lhs) * Secp256K1::GENERATOR_PROJECTIVE, + mul_naive(lhs, Secp256K1::GENERATOR_PROJECTIVE) ); - assert_eq!(Secp256K1::convert(lhs) * Secp256K1::GENERATOR_PROJECTIVE, mul_naive(lhs, Secp256K1::GENERATOR_PROJECTIVE)); } /// A simple, somewhat inefficient implementation of multiplication which is used as a reference /// for correctness. - fn mul_naive(lhs: Secp256K1Scalar, rhs: ProjectivePoint) -> ProjectivePoint { + fn mul_naive( + lhs: Secp256K1Scalar, + rhs: ProjectivePoint, + ) -> ProjectivePoint { let mut g = rhs; let mut sum = ProjectivePoint::ZERO; for limb in lhs.to_biguint().to_u64_digits().iter() { From 0e6c5bb80c98ed601b86cbbe8d7b38c39227dd9d Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 2 Nov 2021 12:03:24 -0700 Subject: [PATCH 14/39] curve gadget changes --- src/curve/curve_summation.rs | 2 +- src/curve/mod.rs | 2 +- .../{secp256k1_curve.rs => secp256k1.rs} | 2 +- src/gadgets/{secp256k1.rs => curve.rs} | 42 ++++++++++++++++++- src/gadgets/mod.rs | 2 +- 5 files changed, 45 insertions(+), 5 deletions(-) rename src/curve/{secp256k1_curve.rs => secp256k1.rs} (98%) rename src/gadgets/{secp256k1.rs => curve.rs} (63%) diff --git a/src/curve/curve_summation.rs b/src/curve/curve_summation.rs index ad4232ce..8f347eda 100644 --- a/src/curve/curve_summation.rs +++ b/src/curve/curve_summation.rs @@ -190,7 +190,7 @@ mod tests { affine_summation_batch_inversion, affine_summation_pairwise, }; use crate::curve::curve_types::{Curve, ProjectivePoint}; - use crate::curve::secp256k1_curve::Secp256K1; + use crate::curve::secp256k1::Secp256K1; #[test] fn test_pairwise_affine_summation() { diff --git a/src/curve/mod.rs b/src/curve/mod.rs index c65f2acd..6555404e 100644 --- a/src/curve/mod.rs +++ b/src/curve/mod.rs @@ -2,4 +2,4 @@ pub mod curve_adds; pub mod curve_multiplication; pub mod curve_summation; pub mod curve_types; -pub mod secp256k1_curve; +pub mod secp256k1; diff --git a/src/curve/secp256k1_curve.rs b/src/curve/secp256k1.rs similarity index 98% rename from src/curve/secp256k1_curve.rs rename to src/curve/secp256k1.rs index 7b84855b..2fa476e1 100644 --- a/src/curve/secp256k1_curve.rs +++ b/src/curve/secp256k1.rs @@ -42,7 +42,7 @@ mod tests { use num::BigUint; use crate::curve::curve_types::{Curve, ProjectivePoint}; - use crate::curve::secp256k1_curve::Secp256K1; + use crate::curve::secp256k1::Secp256K1; use crate::field::field_types::Field; use crate::field::secp256k1_scalar::Secp256K1Scalar; diff --git a/src/gadgets/secp256k1.rs b/src/gadgets/curve.rs similarity index 63% rename from src/gadgets/secp256k1.rs rename to src/gadgets/curve.rs index 3294a954..83f73a3f 100644 --- a/src/gadgets/secp256k1.rs +++ b/src/gadgets/curve.rs @@ -51,6 +51,46 @@ impl, const D: usize> CircuitBuilder { self.connect_nonnative(&y_squared, &rhs); } + + pub fn curve_neg(&mut self, p: AffinePointTarget) { + let neg_y = self.neg_nonnative(p.y); + AffinePointTarget { + x: p.x, + y: neg_y, + } + } } -mod tests {} +mod tests { + use anyhow::Result; + + + + #[test] + fn test_curve_gadget_is_valid() -> Result<()> { + type F = CrandallField; + const D: usize = 4; + + let config = CircuitConfig::large_config(); + + let pw = PartialWitness::new(); + let mut builder = CircuitBuilder::::new(config); + + let + + let lst: Vec = (0..size * 2).map(|n| F::from_canonical_usize(n)).collect(); + let a: Vec> = lst[..] + .chunks(2) + .map(|pair| vec![builder.constant(pair[0]), builder.constant(pair[1])]) + .collect(); + let mut b = a.clone(); + b.shuffle(&mut thread_rng()); + + builder.assert_permutation(a, b); + + let data = builder.build(); + let proof = data.prove(pw).unwrap(); + + verify(proof, &data.verifier_only, &data.common) + } +} diff --git a/src/gadgets/mod.rs b/src/gadgets/mod.rs index 42b3044c..2518e1ab 100644 --- a/src/gadgets/mod.rs +++ b/src/gadgets/mod.rs @@ -11,7 +11,7 @@ pub mod permutation; pub mod polynomial; pub mod random_access; pub mod range_check; -pub mod secp256k1; +pub mod curve; pub mod select; pub mod sorting; pub mod split_base; From 86573fc65c83f905a817a90e43399d84f5aff7ac Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 9 Nov 2021 17:51:04 -0800 Subject: [PATCH 15/39] resolve --- src/curve/secp256k1.rs | 15 ++++++++++- src/gadgets/curve.rs | 61 +++++++++++++++++++++++++++++------------- src/gadgets/mod.rs | 2 +- 3 files changed, 58 insertions(+), 20 deletions(-) diff --git a/src/curve/secp256k1.rs b/src/curve/secp256k1.rs index 2fa476e1..7102b5c9 100644 --- a/src/curve/secp256k1.rs +++ b/src/curve/secp256k1.rs @@ -41,11 +41,24 @@ const SECP256K1_GENERATOR_Y: Secp256K1Base = Secp256K1Base([ mod tests { use num::BigUint; - use crate::curve::curve_types::{Curve, ProjectivePoint}; + use crate::curve::curve_types::{AffinePoint, Curve, ProjectivePoint}; use crate::curve::secp256k1::Secp256K1; use crate::field::field_types::Field; use crate::field::secp256k1_scalar::Secp256K1Scalar; + #[test] + fn test_generator() { + let g = Secp256K1::GENERATOR_AFFINE; + assert!(g.is_valid()); + + let neg_g = AffinePoint:: { + x: g.x, + y: -g.y, + zero: g.zero, + }; + assert!(neg_g.is_valid()); + } + /*#[test] fn test_double_affine() { for i in 0..100 { diff --git a/src/gadgets/curve.rs b/src/gadgets/curve.rs index 83f73a3f..2c617b20 100644 --- a/src/gadgets/curve.rs +++ b/src/gadgets/curve.rs @@ -52,22 +52,27 @@ impl, const D: usize> CircuitBuilder { self.connect_nonnative(&y_squared, &rhs); } - pub fn curve_neg(&mut self, p: AffinePointTarget) { - let neg_y = self.neg_nonnative(p.y); - AffinePointTarget { - x: p.x, - y: neg_y, - } + pub fn curve_neg(&mut self, p: AffinePointTarget) -> AffinePointTarget { + let neg_y = self.neg_nonnative(&p.y); + AffinePointTarget { x: p.x, y: neg_y } } } mod tests { use anyhow::Result; - + use crate::curve::curve_types::{AffinePoint, Curve}; + use crate::curve::secp256k1::Secp256K1; + use crate::field::crandall_field::CrandallField; + use crate::field::field_types::Field; + use crate::field::secp256k1_base::Secp256K1Base; + use crate::iop::witness::PartialWitness; + use crate::plonk::circuit_builder::CircuitBuilder; + use crate::plonk::circuit_data::CircuitConfig; + use crate::plonk::verifier::verify; #[test] - fn test_curve_gadget_is_valid() -> Result<()> { + fn test_curve_point_is_valid() -> Result<()> { type F = CrandallField; const D: usize = 4; @@ -76,21 +81,41 @@ mod tests { let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); - let + let g = Secp256K1::GENERATOR_AFFINE; + let g_target = builder.constant_affine_point(g); - let lst: Vec = (0..size * 2).map(|n| F::from_canonical_usize(n)).collect(); - let a: Vec> = lst[..] - .chunks(2) - .map(|pair| vec![builder.constant(pair[0]), builder.constant(pair[1])]) - .collect(); - let mut b = a.clone(); - b.shuffle(&mut thread_rng()); - - builder.assert_permutation(a, b); + builder.curve_assert_valid(g_target); let data = builder.build(); let proof = data.prove(pw).unwrap(); verify(proof, &data.verifier_only, &data.common) } + + #[test] + #[should_panic] + fn test_curve_point_is_not_valid() { + type F = CrandallField; + const D: usize = 4; + + let config = CircuitConfig::large_config(); + + let pw = PartialWitness::new(); + let mut builder = CircuitBuilder::::new(config); + + let g = Secp256K1::GENERATOR_AFFINE; + let not_g = AffinePoint:: { + x: g.x, + y: g.y + Secp256K1Base::ONE, + zero: g.zero, + }; + let g_target = builder.constant_affine_point(not_g); + + builder.curve_assert_valid(g_target); + + let data = builder.build(); + let proof = data.prove(pw).unwrap(); + + verify(proof, &data.verifier_only, &data.common).unwrap(); + } } diff --git a/src/gadgets/mod.rs b/src/gadgets/mod.rs index 2518e1ab..09acb9de 100644 --- a/src/gadgets/mod.rs +++ b/src/gadgets/mod.rs @@ -2,6 +2,7 @@ pub mod arithmetic; pub mod arithmetic_extension; pub mod arithmetic_u32; pub mod biguint; +pub mod curve; pub mod hash; pub mod insert; pub mod interpolation; @@ -11,7 +12,6 @@ pub mod permutation; pub mod polynomial; pub mod random_access; pub mod range_check; -pub mod curve; pub mod select; pub mod sorting; pub mod split_base; From fa480854fec755afc61ccc12130fe375cb13dc8a Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 2 Nov 2021 15:04:53 -0700 Subject: [PATCH 16/39] updates --- src/gadgets/curve.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/gadgets/curve.rs b/src/gadgets/curve.rs index 2c617b20..42bfd8a9 100644 --- a/src/gadgets/curve.rs +++ b/src/gadgets/curve.rs @@ -31,14 +31,14 @@ impl, const D: usize> CircuitBuilder { pub fn connect_affine_point( &mut self, - lhs: AffinePointTarget, - rhs: AffinePointTarget, + lhs: &AffinePointTarget, + rhs: &AffinePointTarget, ) { self.connect_nonnative(&lhs.x, &rhs.x); self.connect_nonnative(&lhs.y, &rhs.y); } - pub fn curve_assert_valid(&mut self, p: AffinePointTarget) { + pub fn curve_assert_valid(&mut self, p: &AffinePointTarget) { let a = self.constant_nonnative(C::A); let b = self.constant_nonnative(C::B); @@ -52,9 +52,12 @@ impl, const D: usize> CircuitBuilder { self.connect_nonnative(&y_squared, &rhs); } - pub fn curve_neg(&mut self, p: AffinePointTarget) -> AffinePointTarget { + pub fn curve_neg(&mut self, p: &AffinePointTarget) -> AffinePointTarget { let neg_y = self.neg_nonnative(&p.y); - AffinePointTarget { x: p.x, y: neg_y } + AffinePointTarget { + x: p.x.clone(), + y: neg_y, + } } } @@ -83,8 +86,10 @@ mod tests { let g = Secp256K1::GENERATOR_AFFINE; let g_target = builder.constant_affine_point(g); + let neg_g_target = builder.curve_neg(&g_target); - builder.curve_assert_valid(g_target); + builder.curve_assert_valid(&g_target); + builder.curve_assert_valid(&neg_g_target); let data = builder.build(); let proof = data.prove(pw).unwrap(); @@ -109,9 +114,9 @@ mod tests { y: g.y + Secp256K1Base::ONE, zero: g.zero, }; - let g_target = builder.constant_affine_point(not_g); + let not_g_target = builder.constant_affine_point(not_g); - builder.curve_assert_valid(g_target); + builder.curve_assert_valid(¬_g_target); let data = builder.build(); let proof = data.prove(pw).unwrap(); From 4d4605af1f31e80c9b933a7f75d09a5e11d84526 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 10 Nov 2021 11:50:25 -0800 Subject: [PATCH 17/39] merge --- src/gadgets/curve.rs | 32 ++++++++++++++++++++++++ src/gadgets/nonnative.rs | 54 ++++++++++++++++++++++++++++++++++++++-- src/iop/generator.rs | 5 ++++ src/iop/witness.rs | 6 +++++ 4 files changed, 95 insertions(+), 2 deletions(-) diff --git a/src/gadgets/curve.rs b/src/gadgets/curve.rs index 42bfd8a9..eeb966a5 100644 --- a/src/gadgets/curve.rs +++ b/src/gadgets/curve.rs @@ -59,6 +59,38 @@ impl, const D: usize> CircuitBuilder { y: neg_y, } } + + pub fn curve_double(&mut self, p: &AffinePointTarget) -> AffinePointTarget { + let AffinePointTarget { x, y } = p; + let double_y = self.add_nonnative(y, y); + let inv_double_y = self.inv_nonnative(&double_y); + let x_squared = self.mul_nonnative(x, x); + let double_x_squared = self.add_nonnative(&x_squared, &x_squared); + let triple_x_squared = self.add_nonnative(&double_x_squared, &x_squared); + + let a = self.constant_nonnative(C::A); + let triple_xx_a = self.add_nonnative(&triple_x_squared, &a); + let lambda = self.mul_nonnative(&triple_xx_a, &inv_double_y); + let lambda_squared = self.mul_nonnative(&lambda, &lambda); + let x_double = self.add_nonnative(x, x); + + let x3 = self.sub_nonnative(&lambda_squared, &x_double); + + let x_diff = self.sub_nonnative(x, &x3); + let lambda_x_diff = self.mul_nonnative(&lambda, &x_diff); + + let y3 = self.sub_nonnative(&lambda_x_diff, y); + + AffinePointTarget { x: x3, y: y3 } + } + + pub fn curve_add( + &mut self, + a: &AffinePointTarget, + b: &AffinePointTarget, + ) -> AffinePointTarget { + todo!() + } } mod tests { diff --git a/src/gadgets/nonnative.rs b/src/gadgets/nonnative.rs index bc090cd5..d6b20f14 100644 --- a/src/gadgets/nonnative.rs +++ b/src/gadgets/nonnative.rs @@ -5,6 +5,9 @@ use num::{BigUint, One}; use crate::field::field_types::RichField; use crate::field::{extension_field::Extendable, field_types::Field}; use crate::gadgets::biguint::BigUintTarget; +use crate::iop::generator::{GeneratedValues, SimpleGenerator}; +use crate::iop::target::Target; +use crate::iop::witness::{PartitionWitness, Witness}; use crate::plonk::circuit_builder::CircuitBuilder; pub struct NonNativeTarget { @@ -46,6 +49,7 @@ impl, const D: usize> CircuitBuilder { ) -> NonNativeTarget { let result = self.add_biguint(&a.value, &b.value); + // TODO: reduce add result with only one conditional subtraction self.reduce(&result) } @@ -84,8 +88,32 @@ impl, const D: usize> CircuitBuilder { self.mul_nonnative(&neg_one_ff, x) } - /// Returns `x % |FF|` as a `NonNativeTarget`. - fn reduce(&mut self, x: &BigUintTarget) -> NonNativeTarget { + pub fn inv_nonnative( + &mut self, + x: &ForeignFieldTarget, + ) -> ForeignFieldTarget { + let num_limbs = x.value.num_limbs(); + let inv_biguint = self.add_virtual_biguint_target(num_limbs); + let inv = ForeignFieldTarget:: { + value: inv_biguint, + _phantom: PhantomData, + }; + + self.add_simple_generator(NonNativeInverseGenerator:: { + x: x.clone(), + inv: inv.clone(), + _phantom: PhantomData, + }); + + let product = self.mul_nonnative(&x, &inv); + let one = self.constant_nonnative(FF::ONE); + self.connect_nonnative_reduced(&product, &one); + + inv + } + + /// Returns `x % |FF|` as a `ForeignFieldTarget`. + fn reduce(&mut self, x: &BigUintTarget) -> ForeignFieldTarget { let modulus = FF::order(); let order_target = self.constant_biguint(&modulus); let value = self.rem_biguint(x, &order_target); @@ -106,6 +134,28 @@ impl, const D: usize> CircuitBuilder { } } +#[derive(Debug)] +struct NonNativeInverseGenerator, const D: usize, FF: Field> { + x: ForeignFieldTarget, + inv: ForeignFieldTarget, + _phantom: PhantomData, +} + +impl, const D: usize, FF: Field> SimpleGenerator + for NonNativeInverseGenerator +{ + fn dependencies(&self) -> Vec { + self.x.value.limbs.iter().map(|&l| l.0).collect() + } + + fn run_once(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues) { + let x = witness.get_nonnative_target(self.x.clone()); + let inv = x.inverse(); + + out_buffer.set_nonnative_target(self.inv.clone(), inv); + } +} + #[cfg(test)] mod tests { use anyhow::Result; diff --git a/src/iop/generator.rs b/src/iop/generator.rs index ae973d7c..ff7f66e0 100644 --- a/src/iop/generator.rs +++ b/src/iop/generator.rs @@ -8,6 +8,7 @@ use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field_types::{Field, RichField}; use crate::gadgets::arithmetic_u32::U32Target; use crate::gadgets::biguint::BigUintTarget; +use crate::gadgets::nonnative::ForeignFieldTarget; use crate::hash::hash_types::{HashOut, HashOutTarget}; use crate::iop::target::Target; use crate::iop::wire::Wire; @@ -168,6 +169,10 @@ impl GeneratedValues { } } + pub fn set_nonnative_target(&mut self, target: ForeignFieldTarget, value: FF) { + self.set_biguint_target(target.value, value.to_biguint()) + } + pub fn set_hash_target(&mut self, ht: HashOutTarget, value: HashOut) { ht.elements .iter() diff --git a/src/iop/witness.rs b/src/iop/witness.rs index a773c1a9..d6f4fb59 100644 --- a/src/iop/witness.rs +++ b/src/iop/witness.rs @@ -6,6 +6,7 @@ use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field_types::Field; use crate::gadgets::biguint::BigUintTarget; +use crate::gadgets::nonnative::ForeignFieldTarget; use crate::hash::hash_types::HashOutTarget; use crate::hash::hash_types::{HashOut, MerkleCapTarget}; use crate::hash::merkle_tree::MerkleCap; @@ -68,6 +69,11 @@ pub trait Witness { result } + fn get_nonnative_target(&self, target: ForeignFieldTarget) -> FF { + let val = self.get_biguint_target(target.value); + FF::from_biguint(val) + } + fn get_hash_target(&self, ht: HashOutTarget) -> HashOut { HashOut { elements: self.get_targets(&ht.elements).try_into().unwrap(), From a4b7772c3456e38322e84ffd10621f3ba6a6bf01 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 9 Nov 2021 17:51:38 -0800 Subject: [PATCH 18/39] resolve --- src/gadgets/biguint.rs | 2 +- src/gadgets/curve.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/gadgets/biguint.rs b/src/gadgets/biguint.rs index 05a5406e..fb7eb4e0 100644 --- a/src/gadgets/biguint.rs +++ b/src/gadgets/biguint.rs @@ -110,8 +110,8 @@ impl, const D: usize> CircuitBuilder { // Subtract two `BigUintTarget`s. We assume that the first is larger than the second. pub fn sub_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BigUintTarget { - let num_limbs = a.limbs.len(); let (a, b) = self.pad_biguints(a, b); + let num_limbs = a.limbs.len(); let mut result_limbs = vec![]; diff --git a/src/gadgets/curve.rs b/src/gadgets/curve.rs index eeb966a5..abb1b39a 100644 --- a/src/gadgets/curve.rs +++ b/src/gadgets/curve.rs @@ -155,4 +155,30 @@ mod tests { verify(proof, &data.verifier_only, &data.common).unwrap(); } + + #[test] + fn test_curve_double() -> Result<()> { + type F = CrandallField; + const D: usize = 4; + + let config = CircuitConfig::large_config(); + + let pw = PartialWitness::new(); + let mut builder = CircuitBuilder::::new(config); + + let g = Secp256K1::GENERATOR_AFFINE; + let g_target = builder.constant_affine_point(g); + let neg_g_target = builder.curve_neg(&g_target); + + let double_g = builder.curve_double(&g_target); + let double_neg_g = builder.curve_double(&neg_g_target); + + builder.curve_assert_valid(&double_g); + builder.curve_assert_valid(&double_neg_g); + + let data = builder.build(); + let proof = data.prove(pw).unwrap(); + + verify(proof, &data.verifier_only, &data.common) + } } From dfad7708af81187c9636fce11685e34de9ee6f6d Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 10 Nov 2021 11:50:58 -0800 Subject: [PATCH 19/39] merge --- src/gadgets/biguint.rs | 7 ++++--- src/gadgets/curve.rs | 23 +++++++++++++++++------ src/gadgets/nonnative.rs | 31 +++++++++++++++++++++++++++---- 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/src/gadgets/biguint.rs b/src/gadgets/biguint.rs index fb7eb4e0..2d7ed693 100644 --- a/src/gadgets/biguint.rs +++ b/src/gadgets/biguint.rs @@ -160,9 +160,10 @@ impl, const D: usize> CircuitBuilder { a: &BigUintTarget, b: &BigUintTarget, ) -> (BigUintTarget, BigUintTarget) { - let num_limbs = a.limbs.len(); - let div = self.add_virtual_biguint_target(num_limbs); - let rem = self.add_virtual_biguint_target(num_limbs); + let a_len = a.limbs.len(); + let b_len = b.limbs.len(); + let div = self.add_virtual_biguint_target(a_len - b_len + 1); + let rem = self.add_virtual_biguint_target(b_len); self.add_simple_generator(BigUintDivRemGenerator:: { a: a.clone(), diff --git a/src/gadgets/curve.rs b/src/gadgets/curve.rs index abb1b39a..bf9a1ac0 100644 --- a/src/gadgets/curve.rs +++ b/src/gadgets/curve.rs @@ -1,6 +1,6 @@ use crate::curve::curve_types::{AffinePoint, Curve}; use crate::field::extension_field::Extendable; -use crate::field::field_types::RichField; +use crate::field::field_types::{Field, RichField}; use crate::gadgets::nonnative::ForeignFieldTarget; use crate::plonk::circuit_builder::CircuitBuilder; @@ -60,7 +60,11 @@ impl, const D: usize> CircuitBuilder { } } - pub fn curve_double(&mut self, p: &AffinePointTarget) -> AffinePointTarget { + pub fn curve_double( + &mut self, + p: &AffinePointTarget, + p_orig: AffinePoint, + ) -> AffinePointTarget { let AffinePointTarget { x, y } = p; let double_y = self.add_nonnative(y, y); let inv_double_y = self.inv_nonnative(&double_y); @@ -94,6 +98,8 @@ impl, const D: usize> CircuitBuilder { } mod tests { + use std::ops::Neg; + use anyhow::Result; use crate::curve::curve_types::{AffinePoint, Curve}; @@ -167,14 +173,19 @@ mod tests { let mut builder = CircuitBuilder::::new(config); let g = Secp256K1::GENERATOR_AFFINE; + let neg_g = g.neg(); let g_target = builder.constant_affine_point(g); let neg_g_target = builder.curve_neg(&g_target); - let double_g = builder.curve_double(&g_target); - let double_neg_g = builder.curve_double(&neg_g_target); + let double_g = g.double(); + let double_g_other_target = builder.constant_affine_point(double_g); + builder.curve_assert_valid(&double_g_other_target); - builder.curve_assert_valid(&double_g); - builder.curve_assert_valid(&double_neg_g); + let double_g_target = builder.curve_double(&g_target, g); + let double_neg_g_target = builder.curve_double(&neg_g_target, neg_g); + + builder.curve_assert_valid(&double_g_target); + builder.curve_assert_valid(&double_neg_g_target); let data = builder.build(); let proof = data.prove(pw).unwrap(); diff --git a/src/gadgets/nonnative.rs b/src/gadgets/nonnative.rs index d6b20f14..a3fa7fcd 100644 --- a/src/gadgets/nonnative.rs +++ b/src/gadgets/nonnative.rs @@ -81,6 +81,7 @@ impl, const D: usize> CircuitBuilder { &mut self, x: &NonNativeTarget, ) -> NonNativeTarget { + // TODO: zero - x would be more efficient but doesn't seem to work? let neg_one = FF::order() - BigUint::one(); let neg_one_target = self.constant_biguint(&neg_one); let neg_one_ff = self.biguint_to_nonnative(&neg_one_target); @@ -90,11 +91,11 @@ impl, const D: usize> CircuitBuilder { pub fn inv_nonnative( &mut self, - x: &ForeignFieldTarget, - ) -> ForeignFieldTarget { + x: &NonNativeTarget, + ) -> NonNativeTarget { let num_limbs = x.value.num_limbs(); let inv_biguint = self.add_virtual_biguint_target(num_limbs); - let inv = ForeignFieldTarget:: { + let inv = NonNativeTarget:: { value: inv_biguint, _phantom: PhantomData, }; @@ -107,7 +108,7 @@ impl, const D: usize> CircuitBuilder { let product = self.mul_nonnative(&x, &inv); let one = self.constant_nonnative(FF::ONE); - self.connect_nonnative_reduced(&product, &one); + self.connect_nonnative(&product, &one); inv } @@ -264,4 +265,26 @@ mod tests { let proof = data.prove(pw).unwrap(); verify(proof, &data.verifier_only, &data.common) } + + #[test] + fn test_nonnative_inv() -> Result<()> { + type FF = Secp256K1Base; + let x_ff = FF::rand(); + let inv_x_ff = x_ff.inverse(); + + type F = CrandallField; + let config = CircuitConfig::large_config(); + let pw = PartialWitness::new(); + let mut builder = CircuitBuilder::::new(config); + + let x = builder.constant_nonnative(x_ff); + let inv_x = builder.inv_nonnative(&x); + + let inv_x_expected = builder.constant_nonnative(inv_x_ff); + builder.connect_nonnative(&inv_x, &inv_x_expected); + + let data = builder.build(); + let proof = data.prove(pw).unwrap(); + verify(proof, &data.verifier_only, &data.common) + } } From 051b79db2c383336825ea1041d07cb36a4e47238 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 8 Nov 2021 15:17:12 -0800 Subject: [PATCH 20/39] curve_add_two_affine --- src/gadgets/curve.rs | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/gadgets/curve.rs b/src/gadgets/curve.rs index bf9a1ac0..a3fd5b90 100644 --- a/src/gadgets/curve.rs +++ b/src/gadgets/curve.rs @@ -88,12 +88,38 @@ impl, const D: usize> CircuitBuilder { AffinePointTarget { x: x3, y: y3 } } - pub fn curve_add( + pub fn curve_add_two_affine( &mut self, - a: &AffinePointTarget, - b: &AffinePointTarget, + p1: &AffinePointTarget, + p2: &AffinePointTarget, ) -> AffinePointTarget { - todo!() + let AffinePointTarget { x: x1, y: y1 } = p1; + let AffinePointTarget { x: x2, y: y2 } = p2; + + let u = self.sub_nonnative(y2, y1); + let uu = self.mul_nonnative(&u, &u); + let v = self.sub_nonnative(x2, x1); + let vv = self.mul_nonnative(&v, &v); + let vvv = self.mul_nonnative(&v, &vv); + let r = self.mul_nonnative(&vv, x1); + let diff = self.sub_nonnative(&uu, &vvv); + let r2 = self.add_nonnative(&r, &r); + let a = self.sub_nonnative(&diff, &r2); + let x3 = self.mul_nonnative(&v, &a); + + let r_a = self.sub_nonnative(&r, &a); + let y3_first = self.mul_nonnative(&u, &r_a); + let y3_second = self.mul_nonnative(&vvv, y1); + let y3 = self.sub_nonnative(&y3_first, &y3_second); + + let z3_inv = self.inv_nonnative(&vvv); + let x3_norm = self.mul_nonnative(&x3, &z3_inv); + let y3_norm = self.mul_nonnative(&y3, &z3_inv); + + AffinePointTarget { + x: x3_norm, + y: y3_norm, + } } } From d6630869e124bf929a0d523952cb0460ea5ae8ab Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 9 Nov 2021 16:23:22 -0800 Subject: [PATCH 21/39] msm (outside circuit) --- src/curve/curve_msm.rs | 263 +++++++++++++++++++++++++++++++++++++++++ src/curve/mod.rs | 1 + src/gadgets/curve.rs | 2 +- 3 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 src/curve/curve_msm.rs diff --git a/src/curve/curve_msm.rs b/src/curve/curve_msm.rs new file mode 100644 index 00000000..d2cb8049 --- /dev/null +++ b/src/curve/curve_msm.rs @@ -0,0 +1,263 @@ +use itertools::Itertools; +use rayon::prelude::*; + +use crate::curve::curve_summation::affine_multisummation_best; +use crate::curve::curve_types::{AffinePoint, Curve, ProjectivePoint}; +use crate::field::field_types::Field; + +/// In Yao's method, we compute an affine summation for each digit. In a parallel setting, it would +/// be easiest to assign individual summations to threads, but this would be sub-optimal because +/// multi-summations can be more efficient than repeating individual summations (see +/// `affine_multisummation_best`). Thus we divide digits into large chunks, and assign chunks of +/// digits to threads. Note that there is a delicate balance here, as large chunks can result in +/// uneven distributions of work among threads. +const DIGITS_PER_CHUNK: usize = 80; + +#[derive(Clone, Debug)] +pub struct MsmPrecomputation { + /// For each generator (in the order they were passed to `msm_precompute`), contains a vector + /// of powers, i.e. [(2^w)^i] for i < DIGITS. + // TODO: Use compressed coordinates here. + powers_per_generator: Vec>>, + + /// The window size. + w: usize, +} + +pub fn msm_precompute( + generators: &[ProjectivePoint], + w: usize, +) -> MsmPrecomputation { + MsmPrecomputation { + powers_per_generator: generators + .into_par_iter() + .map(|&g| precompute_single_generator(g, w)) + .collect(), + w, + } +} + +fn precompute_single_generator(g: ProjectivePoint, w: usize) -> Vec> { + let digits = (C::ScalarField::BITS + w - 1) / w; + let mut powers: Vec> = Vec::with_capacity(digits); + powers.push(g); + for i in 1..digits { + let mut power_i_proj = powers[i - 1]; + for _j in 0..w { + power_i_proj = power_i_proj.double(); + } + powers.push(power_i_proj); + } + ProjectivePoint::batch_to_affine(&powers) +} + +pub fn msm_parallel( + scalars: &[C::ScalarField], + generators: &[ProjectivePoint], + w: usize, +) -> ProjectivePoint { + let precomputation = msm_precompute(generators, w); + msm_execute_parallel(&precomputation, scalars) +} + +pub fn msm_execute( + precomputation: &MsmPrecomputation, + scalars: &[C::ScalarField], +) -> ProjectivePoint { + assert_eq!(precomputation.powers_per_generator.len(), scalars.len()); + let w = precomputation.w; + let digits = (C::ScalarField::BITS + w - 1) / w; + let base = 1 << w; + + // This is a variant of Yao's method, adapted to the multi-scalar setting. Because we use + // extremely large windows, the repeated scans in Yao's method could be more expensive than the + // actual group operations. To avoid this, we store a multimap from each possible digit to the + // positions in which that digit occurs in the scalars. These positions have the form (i, j), + // where i is the index of the generator and j is an index into the digits of the scalar + // associated with that generator. + let mut digit_occurrences: Vec> = Vec::with_capacity(digits); + for _i in 0..base { + digit_occurrences.push(Vec::new()); + } + for (i, scalar) in scalars.iter().enumerate() { + let digits = to_digits::(scalar, w); + for (j, &digit) in digits.iter().enumerate() { + digit_occurrences[digit].push((i, j)); + } + } + + let mut y = ProjectivePoint::ZERO; + let mut u = ProjectivePoint::ZERO; + + for digit in (1..base).rev() { + for &(i, j) in &digit_occurrences[digit] { + u = u + precomputation.powers_per_generator[i][j]; + } + y = y + u; + } + + y +} + +pub fn msm_execute_parallel( + precomputation: &MsmPrecomputation, + scalars: &[C::ScalarField], +) -> ProjectivePoint { + assert_eq!(precomputation.powers_per_generator.len(), scalars.len()); + let w = precomputation.w; + let digits = (C::ScalarField::BITS + w - 1) / w; + let base = 1 << w; + + // This is a variant of Yao's method, adapted to the multi-scalar setting. Because we use + // extremely large windows, the repeated scans in Yao's method could be more expensive than the + // actual group operations. To avoid this, we store a multimap from each possible digit to the + // positions in which that digit occurs in the scalars. These positions have the form (i, j), + // where i is the index of the generator and j is an index into the digits of the scalar + // associated with that generator. + let mut digit_occurrences: Vec> = Vec::with_capacity(digits); + for _i in 0..base { + digit_occurrences.push(Vec::new()); + } + for (i, scalar) in scalars.iter().enumerate() { + let digits = to_digits::(scalar, w); + for (j, &digit) in digits.iter().enumerate() { + digit_occurrences[digit].push((i, j)); + } + } + + // For each digit, we add up the powers associated with all occurrences that digit. + let digits: Vec = (0..base).collect(); + let digit_acc: Vec> = digits + .par_chunks(DIGITS_PER_CHUNK) + .flat_map(|chunk| { + let summations: Vec>> = chunk + .iter() + .map(|&digit| { + digit_occurrences[digit] + .iter() + .map(|&(i, j)| precomputation.powers_per_generator[i][j]) + .collect() + }) + .collect(); + affine_multisummation_best(summations) + }) + .collect(); + // println!("Computing the per-digit summations (in parallel) took {}s", start.elapsed().as_secs_f64()); + + let mut y = ProjectivePoint::ZERO; + let mut u = ProjectivePoint::ZERO; + for digit in (1..base).rev() { + u = u + digit_acc[digit]; + y = y + u; + } + // println!("Final summation (sequential) {}s", start.elapsed().as_secs_f64()); + y +} + +pub(crate) fn to_digits(x: &C::ScalarField, w: usize) -> Vec { + let scalar_bits = C::ScalarField::BITS; + let num_digits = (scalar_bits + w - 1) / w; + + // Convert x to a bool array. + let x_canonical: Vec<_> = x + .to_biguint() + .to_u64_digits() + .iter() + .cloned() + .pad_using(scalar_bits / 64, |_| 0) + .collect(); + let mut x_bits = Vec::with_capacity(scalar_bits); + for i in 0..scalar_bits { + x_bits.push((x_canonical[i / 64] >> (i as u64 % 64) & 1) != 0); + } + + let mut digits = Vec::with_capacity(num_digits); + for i in 0..num_digits { + let mut digit = 0; + for j in ((i * w)..((i + 1) * w).min(scalar_bits)).rev() { + digit <<= 1; + digit |= x_bits[j] as usize; + } + digits.push(digit); + } + digits +} + +#[cfg(test)] +mod tests { + use num::BigUint; + + use crate::curve::curve_msm::{msm_execute, msm_precompute, to_digits}; + use crate::curve::curve_types::Curve; + use crate::curve::secp256k1::Secp256K1; + use crate::field::field_types::Field; + use crate::field::secp256k1_scalar::Secp256K1Scalar; + + #[test] + fn test_to_digits() { + let x_canonical = [ + 0b10101010101010101010101010101010, + 0b10101010101010101010101010101010, + 0b11001100110011001100110011001100, + 0b11001100110011001100110011001100, + 0b11110000111100001111000011110000, + 0b11110000111100001111000011110000, + 0b00001111111111111111111111111111, + 0b11111111111111111111111111111111, + ]; + let x = Secp256K1Scalar::from_biguint(BigUint::from_slice(&x_canonical)); + assert_eq!(x.to_biguint().to_u32_digits(), x_canonical); + assert_eq!( + to_digits::(&x, 17), + vec![ + 0b01010101010101010, + 0b10101010101010101, + 0b01010101010101010, + 0b11001010101010101, + 0b01100110011001100, + 0b00110011001100110, + 0b10011001100110011, + 0b11110000110011001, + 0b01111000011110000, + 0b00111100001111000, + 0b00011110000111100, + 0b11111111111111110, + 0b01111111111111111, + 0b11111111111111000, + 0b11111111111111111, + 0b1, + ] + ); + } + + #[test] + fn test_msm() { + let w = 5; + + let generator_1 = Secp256K1::GENERATOR_PROJECTIVE; + let generator_2 = generator_1 + generator_1; + let generator_3 = generator_1 + generator_2; + + let scalar_1 = Secp256K1Scalar::from_biguint(BigUint::from_slice(&[ + 11111111, 22222222, 33333333, 44444444, + ])); + let scalar_2 = Secp256K1Scalar::from_biguint(BigUint::from_slice(&[ + 22222222, 22222222, 33333333, 44444444, + ])); + let scalar_3 = Secp256K1Scalar::from_biguint(BigUint::from_slice(&[ + 33333333, 22222222, 33333333, 44444444, + ])); + + let generators = vec![generator_1, generator_2, generator_3]; + let scalars = vec![scalar_1, scalar_2, scalar_3]; + + let precomputation = msm_precompute(&generators, w); + let result_msm = msm_execute(&precomputation, &scalars); + + let result_naive = Secp256K1::convert(scalar_1) * generator_1 + + Secp256K1::convert(scalar_2) * generator_2 + + Secp256K1::convert(scalar_3) * generator_3; + + assert_eq!(result_msm, result_naive); + } +} diff --git a/src/curve/mod.rs b/src/curve/mod.rs index 6555404e..d31e373e 100644 --- a/src/curve/mod.rs +++ b/src/curve/mod.rs @@ -1,4 +1,5 @@ pub mod curve_adds; +pub mod curve_msm; pub mod curve_multiplication; pub mod curve_summation; pub mod curve_types; diff --git a/src/gadgets/curve.rs b/src/gadgets/curve.rs index a3fd5b90..fe6ae306 100644 --- a/src/gadgets/curve.rs +++ b/src/gadgets/curve.rs @@ -88,7 +88,7 @@ impl, const D: usize> CircuitBuilder { AffinePointTarget { x: x3, y: y3 } } - pub fn curve_add_two_affine( + pub fn curve_add( &mut self, p1: &AffinePointTarget, p2: &AffinePointTarget, From e4b894cb12867c5e9b09c9212e284040d7184ef2 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 19 Nov 2021 15:29:48 -0800 Subject: [PATCH 22/39] merge --- src/gadgets/nonnative.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/gadgets/nonnative.rs b/src/gadgets/nonnative.rs index a3fa7fcd..0f16ddc3 100644 --- a/src/gadgets/nonnative.rs +++ b/src/gadgets/nonnative.rs @@ -77,10 +77,7 @@ impl, const D: usize> CircuitBuilder { self.reduce(&result) } - pub fn neg_nonnative( - &mut self, - x: &NonNativeTarget, - ) -> NonNativeTarget { + pub fn neg_nonnative(&mut self, x: &NonNativeTarget) -> NonNativeTarget { // TODO: zero - x would be more efficient but doesn't seem to work? let neg_one = FF::order() - BigUint::one(); let neg_one_target = self.constant_biguint(&neg_one); @@ -89,10 +86,7 @@ impl, const D: usize> CircuitBuilder { self.mul_nonnative(&neg_one_ff, x) } - pub fn inv_nonnative( - &mut self, - x: &NonNativeTarget, - ) -> NonNativeTarget { + pub fn inv_nonnative(&mut self, x: &NonNativeTarget) -> NonNativeTarget { let num_limbs = x.value.num_limbs(); let inv_biguint = self.add_virtual_biguint_target(num_limbs); let inv = NonNativeTarget:: { From c7fda246ca4c3f009d419ffce5103736be247b2f Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 10 Nov 2021 11:53:45 -0800 Subject: [PATCH 23/39] fixes --- src/field/field_types.rs | 4 ---- src/gadgets/curve.rs | 16 ++++++++-------- src/gadgets/nonnative.rs | 11 ++++++----- src/iop/generator.rs | 4 ++-- src/iop/witness.rs | 4 ++-- 5 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/field/field_types.rs b/src/field/field_types.rs index 80eeecff..b7b9ddf4 100644 --- a/src/field/field_types.rs +++ b/src/field/field_types.rs @@ -93,10 +93,6 @@ pub trait Field: self.square() * *self } - fn double(&self) -> Self { - *self * Self::TWO - } - fn triple(&self) -> Self { *self * (Self::ONE + Self::TWO) } diff --git a/src/gadgets/curve.rs b/src/gadgets/curve.rs index fe6ae306..5acd38d2 100644 --- a/src/gadgets/curve.rs +++ b/src/gadgets/curve.rs @@ -1,18 +1,18 @@ use crate::curve::curve_types::{AffinePoint, Curve}; use crate::field::extension_field::Extendable; use crate::field::field_types::{Field, RichField}; -use crate::gadgets::nonnative::ForeignFieldTarget; +use crate::gadgets::nonnative::NonNativeTarget; use crate::plonk::circuit_builder::CircuitBuilder; /// A Target representing an affine point on the curve `C`. #[derive(Clone, Debug)] pub struct AffinePointTarget { - pub x: ForeignFieldTarget, - pub y: ForeignFieldTarget, + pub x: NonNativeTarget, + pub y: NonNativeTarget, } impl AffinePointTarget { - pub fn to_vec(&self) -> Vec> { + pub fn to_vec(&self) -> Vec> { vec![self.x.clone(), self.y.clone()] } } @@ -130,8 +130,8 @@ mod tests { use crate::curve::curve_types::{AffinePoint, Curve}; use crate::curve::secp256k1::Secp256K1; - use crate::field::crandall_field::CrandallField; use crate::field::field_types::Field; + use crate::field::goldilocks_field::GoldilocksField; use crate::field::secp256k1_base::Secp256K1Base; use crate::iop::witness::PartialWitness; use crate::plonk::circuit_builder::CircuitBuilder; @@ -140,7 +140,7 @@ mod tests { #[test] fn test_curve_point_is_valid() -> Result<()> { - type F = CrandallField; + type F = GoldilocksField; const D: usize = 4; let config = CircuitConfig::large_config(); @@ -164,7 +164,7 @@ mod tests { #[test] #[should_panic] fn test_curve_point_is_not_valid() { - type F = CrandallField; + type F = GoldilocksField; const D: usize = 4; let config = CircuitConfig::large_config(); @@ -190,7 +190,7 @@ mod tests { #[test] fn test_curve_double() -> Result<()> { - type F = CrandallField; + type F = GoldilocksField; const D: usize = 4; let config = CircuitConfig::large_config(); diff --git a/src/gadgets/nonnative.rs b/src/gadgets/nonnative.rs index 0f16ddc3..10629ad9 100644 --- a/src/gadgets/nonnative.rs +++ b/src/gadgets/nonnative.rs @@ -10,8 +10,9 @@ use crate::iop::target::Target; use crate::iop::witness::{PartitionWitness, Witness}; use crate::plonk::circuit_builder::CircuitBuilder; +#[derive(Clone, Debug)] pub struct NonNativeTarget { - value: BigUintTarget, + pub(crate) value: BigUintTarget, _phantom: PhantomData, } @@ -107,8 +108,8 @@ impl, const D: usize> CircuitBuilder { inv } - /// Returns `x % |FF|` as a `ForeignFieldTarget`. - fn reduce(&mut self, x: &BigUintTarget) -> ForeignFieldTarget { + /// Returns `x % |FF|` as a `NonNativeTarget`. + fn reduce(&mut self, x: &BigUintTarget) -> NonNativeTarget { let modulus = FF::order(); let order_target = self.constant_biguint(&modulus); let value = self.rem_biguint(x, &order_target); @@ -131,8 +132,8 @@ impl, const D: usize> CircuitBuilder { #[derive(Debug)] struct NonNativeInverseGenerator, const D: usize, FF: Field> { - x: ForeignFieldTarget, - inv: ForeignFieldTarget, + x: NonNativeTarget, + inv: NonNativeTarget, _phantom: PhantomData, } diff --git a/src/iop/generator.rs b/src/iop/generator.rs index ff7f66e0..ea4ac1f6 100644 --- a/src/iop/generator.rs +++ b/src/iop/generator.rs @@ -8,7 +8,7 @@ use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field_types::{Field, RichField}; use crate::gadgets::arithmetic_u32::U32Target; use crate::gadgets::biguint::BigUintTarget; -use crate::gadgets::nonnative::ForeignFieldTarget; +use crate::gadgets::nonnative::NonNativeTarget; use crate::hash::hash_types::{HashOut, HashOutTarget}; use crate::iop::target::Target; use crate::iop::wire::Wire; @@ -169,7 +169,7 @@ impl GeneratedValues { } } - pub fn set_nonnative_target(&mut self, target: ForeignFieldTarget, value: FF) { + pub fn set_nonnative_target(&mut self, target: NonNativeTarget, value: FF) { self.set_biguint_target(target.value, value.to_biguint()) } diff --git a/src/iop/witness.rs b/src/iop/witness.rs index d6f4fb59..8b6df90a 100644 --- a/src/iop/witness.rs +++ b/src/iop/witness.rs @@ -6,7 +6,7 @@ use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field_types::Field; use crate::gadgets::biguint::BigUintTarget; -use crate::gadgets::nonnative::ForeignFieldTarget; +use crate::gadgets::nonnative::NonNativeTarget; use crate::hash::hash_types::HashOutTarget; use crate::hash::hash_types::{HashOut, MerkleCapTarget}; use crate::hash::merkle_tree::MerkleCap; @@ -69,7 +69,7 @@ pub trait Witness { result } - fn get_nonnative_target(&self, target: ForeignFieldTarget) -> FF { + fn get_nonnative_target(&self, target: NonNativeTarget) -> FF { let val = self.get_biguint_target(target.value); FF::from_biguint(val) } From f6954704d97d9922dd9f9c2e0c5957c85b00e3f3 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 10 Nov 2021 11:54:25 -0800 Subject: [PATCH 24/39] fix --- src/gadgets/curve.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gadgets/curve.rs b/src/gadgets/curve.rs index 5acd38d2..3c205e2f 100644 --- a/src/gadgets/curve.rs +++ b/src/gadgets/curve.rs @@ -143,7 +143,7 @@ mod tests { type F = GoldilocksField; const D: usize = 4; - let config = CircuitConfig::large_config(); + let config = CircuitConfig::standard_recursion_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); @@ -167,7 +167,7 @@ mod tests { type F = GoldilocksField; const D: usize = 4; - let config = CircuitConfig::large_config(); + let config = CircuitConfig::standard_recursion_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); @@ -193,7 +193,7 @@ mod tests { type F = GoldilocksField; const D: usize = 4; - let config = CircuitConfig::large_config(); + let config = CircuitConfig::standard_recursion_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); From 7da99ad4d431faf39a7c0945fec4aa63c51fdaa6 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 10 Nov 2021 12:59:04 -0800 Subject: [PATCH 25/39] test fixes --- src/gadgets/biguint.rs | 24 ------------------------ src/gadgets/nonnative.rs | 6 +++--- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/src/gadgets/biguint.rs b/src/gadgets/biguint.rs index 2d7ed693..3aa96235 100644 --- a/src/gadgets/biguint.rs +++ b/src/gadgets/biguint.rs @@ -369,28 +369,4 @@ mod tests { let proof = data.prove(pw).unwrap(); verify(proof, &data.verifier_only, &data.common) } - - #[test] - fn test_biguint_sub() -> Result<()> { - let x_value = BigUint::from_u128(33333333333333333333333333333333333333).unwrap(); - let y_value = BigUint::from_u128(22222222222222222222222222222222222222).unwrap(); - let expected_z_value = &x_value - &y_value; - - type F = CrandallField; - let config = CircuitConfig::large_config(); - let pw = PartialWitness::new(); - let mut builder = CircuitBuilder::::new(config); - - let x = builder.constant_biguint(x_value); - let y = builder.constant_biguint(y_value); - let z = builder.sub_biguint(x, y); - let expected_z = builder.constant_biguint(expected_z_value); - - builder.connect_biguint(z, expected_z); - - let data = builder.build(); - let proof = data.prove(pw).unwrap(); - - verify(proof, &data.verifier_only, &data.common) - } } diff --git a/src/gadgets/nonnative.rs b/src/gadgets/nonnative.rs index 10629ad9..7cae727d 100644 --- a/src/gadgets/nonnative.rs +++ b/src/gadgets/nonnative.rs @@ -158,7 +158,7 @@ mod tests { use crate::field::field_types::Field; use crate::field::goldilocks_field::GoldilocksField; - use crate::field::secp256k1::Secp256K1Base; + use crate::field::secp256k1_base::Secp256K1Base; use crate::iop::witness::PartialWitness; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_data::CircuitConfig; @@ -267,8 +267,8 @@ mod tests { let x_ff = FF::rand(); let inv_x_ff = x_ff.inverse(); - type F = CrandallField; - let config = CircuitConfig::large_config(); + type F = GoldilocksField; + let config = CircuitConfig::standard_recursion_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); From 0f49f6461e528c6dfd855cb55d806ad8286cd0e4 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 15 Nov 2021 11:29:06 -0800 Subject: [PATCH 26/39] removed from ProjectivePoint --- src/curve/curve_adds.rs | 9 +++----- src/curve/curve_types.rs | 47 +++++++++++++++------------------------- 2 files changed, 21 insertions(+), 35 deletions(-) diff --git a/src/curve/curve_adds.rs b/src/curve/curve_adds.rs index 32a5adcc..66e66bd0 100644 --- a/src/curve/curve_adds.rs +++ b/src/curve/curve_adds.rs @@ -11,19 +11,17 @@ impl Add> for ProjectivePoint { x: x1, y: y1, z: z1, - zero: zero1, } = self; let ProjectivePoint { x: x2, y: y2, z: z2, - zero: zero2, } = rhs; - if zero1 { + if z1 == C::BaseField::ZERO { return rhs; } - if zero2 { + if z2 == C::BaseField::ZERO { return self; } @@ -66,7 +64,6 @@ impl Add> for ProjectivePoint { x: x1, y: y1, z: z1, - zero: zero1, } = self; let AffinePoint { x: x2, @@ -74,7 +71,7 @@ impl Add> for ProjectivePoint { zero: zero2, } = rhs; - if zero1 { + if z1 == C::BaseField::ZERO { return rhs.to_projective(); } if zero2 { diff --git a/src/curve/curve_types.rs b/src/curve/curve_types.rs index 830dc7c1..3c16651e 100644 --- a/src/curve/curve_types.rs +++ b/src/curve/curve_types.rs @@ -23,7 +23,6 @@ pub trait Curve: 'static + Sync + Sized + Copy + Debug { x: Self::GENERATOR_AFFINE.x, y: Self::GENERATOR_AFFINE.y, z: Self::BaseField::ONE, - zero: false, }; fn convert(x: Self::ScalarField) -> CurveScalar { @@ -89,12 +88,13 @@ impl AffinePoint { pub fn to_projective(&self) -> ProjectivePoint { let Self { x, y, zero } = *self; - ProjectivePoint { - x, - y, - z: C::BaseField::ONE, - zero, - } + let z = if zero { + C::BaseField::ZERO + } else { + C::BaseField::ONE + }; + + ProjectivePoint { x, y, z } } pub fn batch_to_projective(affine_points: &[Self]) -> Vec> { @@ -150,7 +150,6 @@ pub struct ProjectivePoint { pub x: C::BaseField, pub y: C::BaseField, pub z: C::BaseField, - pub zero: bool, } impl ProjectivePoint { @@ -158,16 +157,10 @@ impl ProjectivePoint { x: C::BaseField::ZERO, y: C::BaseField::ZERO, z: C::BaseField::ZERO, - zero: true, }; pub fn nonzero(x: C::BaseField, y: C::BaseField, z: C::BaseField) -> Self { - let point = Self { - x, - y, - z, - zero: false, - }; + let point = Self { x, y, z }; debug_assert!(point.is_valid()); point } @@ -177,8 +170,8 @@ impl ProjectivePoint { } pub fn to_affine(&self) -> AffinePoint { - let Self { x, y, z, zero } = *self; - if zero { + let Self { x, y, z } = *self; + if z == C::BaseField::ZERO { AffinePoint::ZERO } else { let z_inv = z.inverse(); @@ -193,8 +186,8 @@ impl ProjectivePoint { let mut result = Vec::with_capacity(n); for i in 0..n { - let Self { x, y, z: _, zero } = proj_points[i]; - result.push(if zero { + let Self { x, y, z } = proj_points[i]; + result.push(if z == C::BaseField::ZERO { AffinePoint::ZERO } else { let z_inv = z_invs[i]; @@ -205,8 +198,8 @@ impl ProjectivePoint { } pub fn double(&self) -> Self { - let Self { x, y, z, zero } = *self; - if zero { + let Self { x, y, z } = *self; + if z == C::BaseField::ZERO { return ProjectivePoint::ZERO; } @@ -228,7 +221,6 @@ impl ProjectivePoint { x: x3, y: y3, z: z3, - zero: false, } } @@ -245,7 +237,6 @@ impl ProjectivePoint { x: self.x, y: -self.y, z: self.z, - zero: self.zero, } } } @@ -256,16 +247,14 @@ impl PartialEq for ProjectivePoint { x: x1, y: y1, z: z1, - zero: zero1, } = *self; let ProjectivePoint { x: x2, y: y2, z: z2, - zero: zero2, } = *other; - if zero1 || zero2 { - return zero1 == zero2; + if z1 == C::BaseField::ZERO || z2 == C::BaseField::ZERO { + return z1 == z2; } // We want to compare (x1/z1, y1/z1) == (x2/z2, y2/z2). @@ -289,7 +278,7 @@ impl Neg for ProjectivePoint { type Output = ProjectivePoint; fn neg(self) -> Self::Output { - let ProjectivePoint { x, y, z, zero } = self; - ProjectivePoint { x, y: -y, z, zero } + let ProjectivePoint { x, y, z } = self; + ProjectivePoint { x, y: -y, z } } } From 70abf3e9cbcd921d92ed0ed06c69f22111252cbe Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 16 Nov 2021 14:26:50 -0800 Subject: [PATCH 27/39] addressed comments --- src/curve/curve_adds.rs | 3 +++ src/curve/curve_types.rs | 1 + src/curve/secp256k1.rs | 3 ++- src/field/secp256k1_base.rs | 2 +- src/gadgets/curve.rs | 5 +++-- src/gadgets/nonnative.rs | 10 ++++------ 6 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/curve/curve_adds.rs b/src/curve/curve_adds.rs index 66e66bd0..f25d3847 100644 --- a/src/curve/curve_adds.rs +++ b/src/curve/curve_adds.rs @@ -41,6 +41,7 @@ impl Add> for ProjectivePoint { } } + // From https://www.hyperelliptic.org/EFD/g1p/data/shortw/projective/addition/add-1998-cmo-2 let z1z2 = z1 * z2; let u = y2z1 - y1z2; let uu = u.square(); @@ -92,6 +93,7 @@ impl Add> for ProjectivePoint { } } + // From https://www.hyperelliptic.org/EFD/g1p/data/shortw/projective/addition/madd-1998-cmo let u = y2z1 - y1; let uu = u.square(); let v = x2z1 - x1; @@ -138,6 +140,7 @@ impl Add> for AffinePoint { } } + // From https://www.hyperelliptic.org/EFD/g1p/data/shortw/projective/addition/mmadd-1998-cmo let u = y2 - y1; let uu = u.square(); let v = x2 - x1; diff --git a/src/curve/curve_types.rs b/src/curve/curve_types.rs index 3c16651e..f2bb24b5 100644 --- a/src/curve/curve_types.rs +++ b/src/curve/curve_types.rs @@ -197,6 +197,7 @@ impl ProjectivePoint { result } + // From https://www.hyperelliptic.org/EFD/g1p/data/shortw/projective/doubling/dbl-2007-bl pub fn double(&self) -> Self { let Self { x, y, z } = *self; if z == C::BaseField::ZERO { diff --git a/src/curve/secp256k1.rs b/src/curve/secp256k1.rs index 7102b5c9..47c6ebb2 100644 --- a/src/curve/secp256k1.rs +++ b/src/curve/secp256k1.rs @@ -22,6 +22,7 @@ impl Curve for Secp256K1 { }; } +// 55066263022277343669578718895168534326250603453777594175500187360389116729240 const SECP256K1_GENERATOR_X: Secp256K1Base = Secp256K1Base([ 0x59F2815B16F81798, 0x029BFCDB2DCE28D9, @@ -29,7 +30,7 @@ const SECP256K1_GENERATOR_X: Secp256K1Base = Secp256K1Base([ 0x79BE667EF9DCBBAC, ]); -/// 241266749859715473739788878240585681733927191168601896383759122102112907357779751001206799952863815012735208165030 +/// 32670510020758816978083085130507043184471273380659243275938904335757337482424 const SECP256K1_GENERATOR_Y: Secp256K1Base = Secp256K1Base([ 0x9C47D08FFB10D4B8, 0xFD17B448A6855419, diff --git a/src/field/secp256k1_base.rs b/src/field/secp256k1_base.rs index a09edc30..acb1df4e 100644 --- a/src/field/secp256k1_base.rs +++ b/src/field/secp256k1_base.rs @@ -88,7 +88,7 @@ impl Field for Secp256K1Base { // Sage: `g = GF(p).multiplicative_generator()` const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([5, 0, 0, 0]); - // Sage: `g_2 = power_mod(g, (p - 1) // 2), p)` + // Sage: `g_2 = g^((p - 1) / 2)` const POWER_OF_TWO_GENERATOR: Self = Self::NEG_ONE; const BITS: usize = 256; diff --git a/src/gadgets/curve.rs b/src/gadgets/curve.rs index 3c205e2f..5a458a56 100644 --- a/src/gadgets/curve.rs +++ b/src/gadgets/curve.rs @@ -1,10 +1,11 @@ use crate::curve::curve_types::{AffinePoint, Curve}; use crate::field::extension_field::Extendable; -use crate::field::field_types::{Field, RichField}; +use crate::field::field_types::RichField; use crate::gadgets::nonnative::NonNativeTarget; use crate::plonk::circuit_builder::CircuitBuilder; -/// A Target representing an affine point on the curve `C`. +/// A Target representing an affine point on the curve `C`. We use incomplete arithmetic for efficiency, +/// so we assume these points are not zero. #[derive(Clone, Debug)] pub struct AffinePointTarget { pub x: NonNativeTarget, diff --git a/src/gadgets/nonnative.rs b/src/gadgets/nonnative.rs index 7cae727d..90735a61 100644 --- a/src/gadgets/nonnative.rs +++ b/src/gadgets/nonnative.rs @@ -1,6 +1,6 @@ use std::marker::PhantomData; -use num::{BigUint, One}; +use num::{BigUint, One, Zero}; use crate::field::field_types::RichField; use crate::field::{extension_field::Extendable, field_types::Field}; @@ -79,12 +79,10 @@ impl, const D: usize> CircuitBuilder { } pub fn neg_nonnative(&mut self, x: &NonNativeTarget) -> NonNativeTarget { - // TODO: zero - x would be more efficient but doesn't seem to work? - let neg_one = FF::order() - BigUint::one(); - let neg_one_target = self.constant_biguint(&neg_one); - let neg_one_ff = self.biguint_to_nonnative(&neg_one_target); + let zero_target = self.constant_biguint(&BigUint::zero()); + let zero_ff = self.biguint_to_nonnative(&zero_target); - self.mul_nonnative(&neg_one_ff, x) + self.sub_nonnative(&zero_ff, x) } pub fn inv_nonnative(&mut self, x: &NonNativeTarget) -> NonNativeTarget { From 284f9a412ca385959a8e156541276fced5e80e1c Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 18 Nov 2021 10:30:57 -0800 Subject: [PATCH 28/39] curve multiply; test for curve add; addressed comments --- src/curve/curve_multiplication.rs | 10 +- src/curve/curve_types.rs | 31 +----- src/gadgets/biguint.rs | 11 ++ src/gadgets/curve.rs | 179 +++++++++++++++++++++++++++--- src/gadgets/nonnative.rs | 32 ++++++ 5 files changed, 220 insertions(+), 43 deletions(-) diff --git a/src/curve/curve_multiplication.rs b/src/curve/curve_multiplication.rs index e5ac0eb3..b09b8a0f 100644 --- a/src/curve/curve_multiplication.rs +++ b/src/curve/curve_multiplication.rs @@ -1,6 +1,6 @@ use std::ops::Mul; -use crate::curve::curve_summation::affine_summation_batch_inversion; +use crate::curve::curve_summation::affine_multisummation_batch_inversion; use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar, ProjectivePoint}; use crate::field::field_types::Field; @@ -48,6 +48,7 @@ impl ProjectivePoint { let mut y = ProjectivePoint::ZERO; let mut u = ProjectivePoint::ZERO; + let mut all_summands = Vec::new(); for j in (1..BASE).rev() { let mut u_summands = Vec::new(); for (i, &digit) in digits.iter().enumerate() { @@ -55,7 +56,12 @@ impl ProjectivePoint { u_summands.push(precomputed_powers[i]); } } - u = u + affine_summation_batch_inversion(u_summands); + all_summands.push(u_summands); + } + + let all_sums = affine_multisummation_batch_inversion(all_summands); + for i in 0..all_sums.len() { + u = u + all_sums[i]; y = y + u; } y diff --git a/src/curve/curve_types.rs b/src/curve/curve_types.rs index f2bb24b5..c9a04ab2 100644 --- a/src/curve/curve_types.rs +++ b/src/curve/curve_types.rs @@ -1,8 +1,6 @@ use std::fmt::Debug; use std::ops::Neg; -use anyhow::Result; - use crate::field::field_types::Field; // To avoid implementation conflicts from associated types, @@ -29,30 +27,6 @@ pub trait Curve: 'static + Sync + Sized + Copy + Debug { CurveScalar(x) } - /*fn try_convert_b2s(x: Self::BaseField) -> Result { - x.try_convert::() - } - - fn try_convert_s2b(x: Self::ScalarField) -> Result { - x.try_convert::() - } - - fn try_convert_s2b_slice(s: &[Self::ScalarField]) -> Result> { - let mut res = Vec::with_capacity(s.len()); - for &x in s { - res.push(Self::try_convert_s2b(x)?); - } - Ok(res) - } - - fn try_convert_b2s_slice(s: &[Self::BaseField]) -> Result> { - let mut res = Vec::with_capacity(s.len()); - for &x in s { - res.push(Self::try_convert_b2s(x)?); - } - Ok(res) - }*/ - fn is_safe_curve() -> bool { // Added additional check to prevent using vulnerabilties in case a discriminant is equal to 0. (Self::A.cube().double().double() + Self::B.square().triple().triple().triple()) @@ -155,7 +129,7 @@ pub struct ProjectivePoint { impl ProjectivePoint { pub const ZERO: Self = Self { x: C::BaseField::ZERO, - y: C::BaseField::ZERO, + y: C::BaseField::ONE, z: C::BaseField::ZERO, }; @@ -166,7 +140,8 @@ impl ProjectivePoint { } pub fn is_valid(&self) -> bool { - self.to_affine().is_valid() + let Self { x, y, z } = *self; + z.is_zero() || y.square() * z == x.cube() + C::A * x * z.square() + C::B * z.cube() } pub fn to_affine(&self) -> AffinePoint { diff --git a/src/gadgets/biguint.rs b/src/gadgets/biguint.rs index 3aa96235..b67c85a5 100644 --- a/src/gadgets/biguint.rs +++ b/src/gadgets/biguint.rs @@ -155,6 +155,17 @@ impl, const D: usize> CircuitBuilder { } } + // Returns x * y + z. This is no more efficient than mul-then-add; it's purely for convenience (only need to call one CircuitBuilder function). + pub fn mul_add_biguint( + &mut self, + x: &BigUintTarget, + y: &BigUintTarget, + z: &BigUintTarget, + ) -> BigUintTarget { + let prod = self.mul_biguint(x, y); + self.add_biguint(&prod, z) + } + pub fn div_rem_biguint( &mut self, a: &BigUintTarget, diff --git a/src/gadgets/curve.rs b/src/gadgets/curve.rs index 5a458a56..eda0e5e0 100644 --- a/src/gadgets/curve.rs +++ b/src/gadgets/curve.rs @@ -1,6 +1,6 @@ use crate::curve::curve_types::{AffinePoint, Curve}; use crate::field::extension_field::Extendable; -use crate::field::field_types::RichField; +use crate::field::field_types::{Field, RichField}; use crate::gadgets::nonnative::NonNativeTarget; use crate::plonk::circuit_builder::CircuitBuilder; @@ -18,6 +18,17 @@ impl AffinePointTarget { } } +const WINDOW_BITS: usize = 4; +const BASE: usize = 1 << WINDOW_BITS; + +fn digits_per_scalar() -> usize { + (C::ScalarField::BITS + WINDOW_BITS - 1) / WINDOW_BITS +} + +pub struct MulPrecomputationTarget { + powers: Vec>, +} + impl, const D: usize> CircuitBuilder { pub fn constant_affine_point( &mut self, @@ -39,6 +50,13 @@ impl, const D: usize> CircuitBuilder { self.connect_nonnative(&lhs.y, &rhs.y); } + pub fn add_virtual_affine_point_target(&mut self) -> AffinePointTarget { + let x = self.add_virtual_nonnative_target(); + let y = self.add_virtual_nonnative_target(); + + AffinePointTarget { x, y } + } + pub fn curve_assert_valid(&mut self, p: &AffinePointTarget) { let a = self.constant_nonnative(C::A); let b = self.constant_nonnative(C::B); @@ -61,11 +79,7 @@ impl, const D: usize> CircuitBuilder { } } - pub fn curve_double( - &mut self, - p: &AffinePointTarget, - p_orig: AffinePoint, - ) -> AffinePointTarget { + pub fn curve_double(&mut self, p: &AffinePointTarget) -> AffinePointTarget { let AffinePointTarget { x, y } = p; let double_y = self.add_nonnative(y, y); let inv_double_y = self.inv_nonnative(&double_y); @@ -89,6 +103,7 @@ impl, const D: usize> CircuitBuilder { AffinePointTarget { x: x3, y: y3 } } + // Add two points, which are assumed to be non-equal. pub fn curve_add( &mut self, p1: &AffinePointTarget, @@ -122,6 +137,110 @@ impl, const D: usize> CircuitBuilder { y: y3_norm, } } + + pub fn mul_precompute( + &mut self, + p: &AffinePointTarget, + ) -> MulPrecomputationTarget { + let num_digits = digits_per_scalar::(); + + let mut powers = Vec::with_capacity(num_digits); + powers.push(p.clone()); + for i in 1..num_digits { + let mut power_i = powers[i - 1].clone(); + for _j in 0..WINDOW_BITS { + power_i = self.curve_double(&power_i); + } + powers.push(power_i); + } + + MulPrecomputationTarget { powers } + } + + /*fn to_digits(&mut self, x: &NonNativeTarget) -> Vec> { + debug_assert!( + 64 % WINDOW_BITS == 0, + "For simplicity, only power-of-two window sizes are handled for now" + ); + + let base = self.constant_nonnative(C::ScalarField::from_canonical_u64(BASE as u64)); + + let num_digits = digits_per_scalar::(); + let mut digits = Vec::with_capacity(num_digits); + + let (rest, limb) = self.div_rem_nonnative(&x, &base); + for _ in 0..num_digits { + digits.push(limb); + + let (rest, limb) = self.div_rem_nonnative(&rest, &base); + } + + digits + } + + pub fn mul_with_precomputation( + &mut self, + p: &AffinePointTarget, + n: &NonNativeTarget, + precomputation: MulPrecomputationTarget, + ) -> AffinePointTarget { + // Yao's method; see https://koclab.cs.ucsb.edu/teaching/ecc/eccPapers/Doche-ch09.pdf + let precomputed_powers = precomputation.powers; + + let digits = self.to_digits(n); + + + }*/ + + pub fn curve_scalar_mul( + &mut self, + p: &AffinePointTarget, + n: &NonNativeTarget, + ) -> AffinePointTarget { + let one = self.constant_nonnative(C::BaseField::ONE); + let two = self.constant_nonnative(C::ScalarField::TWO); + let num_bits = C::ScalarField::BITS; + + // Result starts at p, which is later subtracted, because we don't support arithmetic with the zero point. + let mut result = self.add_virtual_affine_point_target(); + self.connect_affine_point(p, &result); + let mut two_i_times_p = self.add_virtual_affine_point_target(); + self.connect_affine_point(p, &two_i_times_p); + + let mut cur_n = self.add_virtual_nonnative_target::(); + for _i in 0..num_bits { + let (bit_scalar, new_n) = self.div_rem_nonnative(&cur_n, &two); + let bit_biguint = self.nonnative_to_biguint(&bit_scalar); + let bit = self.biguint_to_nonnative::(&bit_biguint); + let not_bit = self.sub_nonnative(&one, &bit); + + let result_plus_2_i_p = self.curve_add(&result, &two_i_times_p); + + let result_x = result.x; + let result_y = result.y; + let result_plus_2_i_p_x = result_plus_2_i_p.x; + let result_plus_2_i_p_y = result_plus_2_i_p.y; + + let new_x_if_bit = self.mul_nonnative(&bit, &result_plus_2_i_p_x); + let new_x_if_not_bit = self.mul_nonnative(¬_bit, &result_x); + let new_y_if_bit = self.mul_nonnative(&bit, &result_plus_2_i_p_y); + let new_y_if_not_bit = self.mul_nonnative(¬_bit, &result_y); + + let new_x = self.add_nonnative(&new_x_if_bit, &new_x_if_not_bit); + let new_y = self.add_nonnative(&new_y_if_bit, &new_y_if_not_bit); + + result = AffinePointTarget { x: new_x, y: new_y }; + + two_i_times_p = self.curve_double(&two_i_times_p); + cur_n = new_n; + } + + // Subtract off result's intial value of p. + let neg_p = self.curve_neg(&p); + result = self.curve_add(&result, &neg_p); + + result + } } mod tests { @@ -200,19 +319,53 @@ mod tests { let mut builder = CircuitBuilder::::new(config); let g = Secp256K1::GENERATOR_AFFINE; - let neg_g = g.neg(); let g_target = builder.constant_affine_point(g); let neg_g_target = builder.curve_neg(&g_target); let double_g = g.double(); - let double_g_other_target = builder.constant_affine_point(double_g); - builder.curve_assert_valid(&double_g_other_target); + let double_g_expected = builder.constant_affine_point(double_g); + builder.curve_assert_valid(&double_g_expected); - let double_g_target = builder.curve_double(&g_target, g); - let double_neg_g_target = builder.curve_double(&neg_g_target, neg_g); + let double_neg_g = (-g).double(); + let double_neg_g_expected = builder.constant_affine_point(double_neg_g); + builder.curve_assert_valid(&double_neg_g_expected); - builder.curve_assert_valid(&double_g_target); - builder.curve_assert_valid(&double_neg_g_target); + let double_g_actual = builder.curve_double(&g_target); + let double_neg_g_actual = builder.curve_double(&neg_g_target); + builder.curve_assert_valid(&double_g_actual); + builder.curve_assert_valid(&double_neg_g_actual); + + builder.connect_affine_point(&double_g_expected, &double_g_actual); + builder.connect_affine_point(&double_neg_g_expected, &double_neg_g_actual); + + let data = builder.build(); + let proof = data.prove(pw).unwrap(); + + verify(proof, &data.verifier_only, &data.common) + } + + #[test] + fn test_curve_add() -> Result<()> { + type F = GoldilocksField; + const D: usize = 4; + + let config = CircuitConfig::standard_recursion_config(); + + let pw = PartialWitness::new(); + let mut builder = CircuitBuilder::::new(config); + + let g = Secp256K1::GENERATOR_AFFINE; + let double_g = g.double(); + let g_plus_2g = (g + double_g).to_affine(); + let g_plus_2g_expected = builder.constant_affine_point(g_plus_2g); + builder.curve_assert_valid(&g_plus_2g_expected); + + let g_target = builder.constant_affine_point(g); + let double_g_target = builder.curve_double(&g_target); + let g_plus_2g_actual = builder.curve_add(&g_target, &double_g_target); + builder.curve_assert_valid(&g_plus_2g_actual); + + builder.connect_affine_point(&g_plus_2g_expected, &g_plus_2g_actual); let data = builder.build(); let proof = data.prove(pw).unwrap(); diff --git a/src/gadgets/nonnative.rs b/src/gadgets/nonnative.rs index 90735a61..19d86658 100644 --- a/src/gadgets/nonnative.rs +++ b/src/gadgets/nonnative.rs @@ -17,6 +17,14 @@ pub struct NonNativeTarget { } impl, const D: usize> CircuitBuilder { + fn num_nonnative_limbs() -> usize { + let ff_size = FF::order(); + let f_size = F::order(); + let num_limbs = ((ff_size + f_size.clone() - BigUint::one()) / f_size).to_u32_digits()[0]; + + num_limbs as usize + } + pub fn biguint_to_nonnative(&mut self, x: &BigUintTarget) -> NonNativeTarget { NonNativeTarget { value: x.clone(), @@ -42,6 +50,16 @@ impl, const D: usize> CircuitBuilder { self.connect_biguint(&lhs.value, &rhs.value); } + pub fn add_virtual_nonnative_target(&mut self) -> NonNativeTarget { + let num_limbs = Self::num_nonnative_limbs::(); + let value = self.add_virtual_biguint_target(num_limbs); + + NonNativeTarget { + value, + _phantom: PhantomData, + } + } + // Add two `NonNativeTarget`s. pub fn add_nonnative( &mut self, @@ -106,6 +124,20 @@ impl, const D: usize> CircuitBuilder { inv } + pub fn div_rem_nonnative( + &mut self, + x: &NonNativeTarget, + y: &NonNativeTarget, + ) -> (NonNativeTarget, NonNativeTarget) { + let x_biguint = self.nonnative_to_biguint(x); + let y_biguint = self.nonnative_to_biguint(y); + + let (div_biguint, rem_biguint) = self.div_rem_biguint(&x_biguint, &y_biguint); + let div = self.biguint_to_nonnative(&div_biguint); + let rem = self.biguint_to_nonnative(&rem_biguint); + (div, rem) + } + /// Returns `x % |FF|` as a `NonNativeTarget`. fn reduce(&mut self, x: &BigUintTarget) -> NonNativeTarget { let modulus = FF::order(); From 2ec3ea8634e7f25d9c5d3f70f5511c36048d663c Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 18 Nov 2021 15:48:28 -0800 Subject: [PATCH 29/39] new curve_mul --- src/gadgets/curve.rs | 52 +++++++++++++++++++++++++++++++--------- src/gadgets/nonnative.rs | 35 ++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 12 deletions(-) diff --git a/src/gadgets/curve.rs b/src/gadgets/curve.rs index eda0e5e0..0982d5f9 100644 --- a/src/gadgets/curve.rs +++ b/src/gadgets/curve.rs @@ -201,18 +201,18 @@ impl, const D: usize> CircuitBuilder { let two = self.constant_nonnative(C::ScalarField::TWO); let num_bits = C::ScalarField::BITS; + let bits = self.split_nonnative_to_bits(&n); + let bits_as_base: Vec> = + bits.iter().map(|b| self.bool_to_nonnative(b)).collect(); + // Result starts at p, which is later subtracted, because we don't support arithmetic with the zero point. let mut result = self.add_virtual_affine_point_target(); self.connect_affine_point(p, &result); let mut two_i_times_p = self.add_virtual_affine_point_target(); self.connect_affine_point(p, &two_i_times_p); - let mut cur_n = self.add_virtual_nonnative_target::(); - for _i in 0..num_bits { - let (bit_scalar, new_n) = self.div_rem_nonnative(&cur_n, &two); - let bit_biguint = self.nonnative_to_biguint(&bit_scalar); - let bit = self.biguint_to_nonnative::(&bit_biguint); - let not_bit = self.sub_nonnative(&one, &bit); + for bit in bits_as_base.iter() { + let not_bit = self.sub_nonnative(&one, bit); let result_plus_2_i_p = self.curve_add(&result, &two_i_times_p); @@ -221,9 +221,9 @@ impl, const D: usize> CircuitBuilder { let result_plus_2_i_p_x = result_plus_2_i_p.x; let result_plus_2_i_p_y = result_plus_2_i_p.y; - let new_x_if_bit = self.mul_nonnative(&bit, &result_plus_2_i_p_x); + let new_x_if_bit = self.mul_nonnative(bit, &result_plus_2_i_p_x); let new_x_if_not_bit = self.mul_nonnative(¬_bit, &result_x); - let new_y_if_bit = self.mul_nonnative(&bit, &result_plus_2_i_p_y); + let new_y_if_bit = self.mul_nonnative(bit, &result_plus_2_i_p_y); let new_y_if_not_bit = self.mul_nonnative(¬_bit, &result_y); let new_x = self.add_nonnative(&new_x_if_bit, &new_x_if_not_bit); @@ -232,7 +232,6 @@ impl, const D: usize> CircuitBuilder { result = AffinePointTarget { x: new_x, y: new_y }; two_i_times_p = self.curve_double(&two_i_times_p); - cur_n = new_n; } // Subtract off result's intial value of p. @@ -244,15 +243,16 @@ impl, const D: usize> CircuitBuilder { } mod tests { - use std::ops::Neg; + use std::ops::{Mul, Neg}; use anyhow::Result; - use crate::curve::curve_types::{AffinePoint, Curve}; + use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar}; use crate::curve::secp256k1::Secp256K1; use crate::field::field_types::Field; use crate::field::goldilocks_field::GoldilocksField; use crate::field::secp256k1_base::Secp256K1Base; + use crate::field::secp256k1_scalar::Secp256K1Scalar; use crate::iop::witness::PartialWitness; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_data::CircuitConfig; @@ -372,4 +372,34 @@ mod tests { verify(proof, &data.verifier_only, &data.common) } + + #[test] + fn test_curve_mul() -> Result<()> { + type F = GoldilocksField; + const D: usize = 4; + + let config = CircuitConfig::standard_recursion_config(); + + let pw = PartialWitness::new(); + let mut builder = CircuitBuilder::::new(config); + + let g = Secp256K1::GENERATOR_AFFINE; + let five = Secp256K1Scalar::from_canonical_usize(5); + let five_scalar = CurveScalar::(five); + let five_g = (five_scalar * g.to_projective()).to_affine(); + let five_g_expected = builder.constant_affine_point(five_g); + builder.curve_assert_valid(&five_g_expected); + + let g_target = builder.constant_affine_point(g); + let five_target = builder.constant_nonnative(five); + let five_g_actual = builder.curve_scalar_mul(&g_target, &five_target); + builder.curve_assert_valid(&five_g_actual); + + builder.connect_affine_point(&five_g_expected, &five_g_actual); + + let data = builder.build(); + let proof = data.prove(pw).unwrap(); + + verify(proof, &data.verifier_only, &data.common) + } } diff --git a/src/gadgets/nonnative.rs b/src/gadgets/nonnative.rs index 19d86658..88250093 100644 --- a/src/gadgets/nonnative.rs +++ b/src/gadgets/nonnative.rs @@ -4,9 +4,10 @@ use num::{BigUint, One, Zero}; use crate::field::field_types::RichField; use crate::field::{extension_field::Extendable, field_types::Field}; +use crate::gadgets::arithmetic_u32::U32Target; use crate::gadgets::biguint::BigUintTarget; use crate::iop::generator::{GeneratedValues, SimpleGenerator}; -use crate::iop::target::Target; +use crate::iop::target::{BoolTarget, Target}; use crate::iop::witness::{PartitionWitness, Witness}; use crate::plonk::circuit_builder::CircuitBuilder; @@ -158,6 +159,38 @@ impl, const D: usize> CircuitBuilder { let x_biguint = self.nonnative_to_biguint(x); self.reduce(&x_biguint) } + + pub fn bool_to_nonnative(&mut self, b: &BoolTarget) -> NonNativeTarget { + let limbs = vec![U32Target(b.target)]; + let value = BigUintTarget { limbs }; + + NonNativeTarget { + value, + _phantom: PhantomData, + } + } + + // Split a nonnative field element to bits. + pub fn split_nonnative_to_bits( + &mut self, + x: &NonNativeTarget, + ) -> Vec { + let num_limbs = x.value.num_limbs(); + let mut result = Vec::with_capacity(num_limbs * 32); + + for i in 0..num_limbs { + let limb = x.value.get_limb(i); + let bit_targets = self.split_le_base::<2>(limb.0, 32); + let mut bits: Vec<_> = bit_targets + .iter() + .map(|&t| BoolTarget::new_unsafe(t)) + .collect(); + + result.append(&mut bits); + } + + result + } } #[derive(Debug)] From a6ddc2ed5dc8275758cbdd18764e8e4b41204a7c Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 19 Nov 2021 15:27:02 -0800 Subject: [PATCH 30/39] curve_mul testing --- src/gadgets/biguint.rs | 7 ++++++- src/gadgets/curve.rs | 11 ++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/gadgets/biguint.rs b/src/gadgets/biguint.rs index b67c85a5..9e14cdb7 100644 --- a/src/gadgets/biguint.rs +++ b/src/gadgets/biguint.rs @@ -173,7 +173,12 @@ impl, const D: usize> CircuitBuilder { ) -> (BigUintTarget, BigUintTarget) { let a_len = a.limbs.len(); let b_len = b.limbs.len(); - let div = self.add_virtual_biguint_target(a_len - b_len + 1); + let div_num_limbs = if b_len > a_len + 1 { + 0 + } else { + a_len - b_len + 1 + }; + let div = self.add_virtual_biguint_target(div_num_limbs); let rem = self.add_virtual_biguint_target(b_len); self.add_simple_generator(BigUintDivRemGenerator:: { diff --git a/src/gadgets/curve.rs b/src/gadgets/curve.rs index 0982d5f9..37d99997 100644 --- a/src/gadgets/curve.rs +++ b/src/gadgets/curve.rs @@ -198,8 +198,6 @@ impl, const D: usize> CircuitBuilder { n: &NonNativeTarget, ) -> AffinePointTarget { let one = self.constant_nonnative(C::BaseField::ONE); - let two = self.constant_nonnative(C::ScalarField::TWO); - let num_bits = C::ScalarField::BITS; let bits = self.split_nonnative_to_bits(&n); let bits_as_base: Vec> = @@ -378,7 +376,10 @@ mod tests { type F = GoldilocksField; const D: usize = 4; - let config = CircuitConfig::standard_recursion_config(); + let config = CircuitConfig { + num_routed_wires: 33, + ..CircuitConfig::standard_recursion_config() + }; let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); @@ -393,9 +394,9 @@ mod tests { let g_target = builder.constant_affine_point(g); let five_target = builder.constant_nonnative(five); let five_g_actual = builder.curve_scalar_mul(&g_target, &five_target); - builder.curve_assert_valid(&five_g_actual); + /*builder.curve_assert_valid(&five_g_actual); - builder.connect_affine_point(&five_g_expected, &five_g_actual); + builder.connect_affine_point(&five_g_expected, &five_g_actual);*/ let data = builder.build(); let proof = data.prove(pw).unwrap(); From 5029f87b80ee5ff769f4e84a6fa42592f06dec58 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 19 Nov 2021 15:45:42 -0800 Subject: [PATCH 31/39] fixes --- src/gadgets/nonnative.rs | 5 +---- src/plonk/circuit_builder.rs | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/gadgets/nonnative.rs b/src/gadgets/nonnative.rs index 88250093..b04b5c1f 100644 --- a/src/gadgets/nonnative.rs +++ b/src/gadgets/nonnative.rs @@ -152,10 +152,7 @@ impl, const D: usize> CircuitBuilder { } #[allow(dead_code)] - fn reduce_nonnative( - &mut self, - x: &NonNativeTarget, - ) -> NonNativeTarget { + fn reduce_nonnative(&mut self, x: &NonNativeTarget) -> NonNativeTarget { let x_biguint = self.nonnative_to_biguint(x); self.reduce(&x_biguint) } diff --git a/src/plonk/circuit_builder.rs b/src/plonk/circuit_builder.rs index 693abe0a..3cb62cf3 100644 --- a/src/plonk/circuit_builder.rs +++ b/src/plonk/circuit_builder.rs @@ -15,7 +15,7 @@ use crate::gadgets::arithmetic::BaseArithmeticOperation; use crate::gadgets::arithmetic_extension::ExtensionArithmeticOperation; use crate::gadgets::arithmetic_u32::U32Target; use crate::gates::arithmetic_base::ArithmeticGate; -use crate::gates::arithmetic::ArithmeticExtensionGate; +use crate::gates::arithmetic_extension::ArithmeticExtensionGate; use crate::gates::arithmetic_u32::{U32ArithmeticGate, NUM_U32_ARITHMETIC_OPS}; use crate::gates::constant::ConstantGate; use crate::gates::gate::{Gate, GateInstance, GateRef, PrefixedGate}; From b1bbe30dac23187434e1ea6b0bc456c77b015ab4 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 23 Nov 2021 18:16:38 -0800 Subject: [PATCH 32/39] Fixed tests -- thanks William! --- src/gadgets/curve.rs | 54 ++++++++-------------------------------- src/gadgets/nonnative.rs | 7 ++---- 2 files changed, 13 insertions(+), 48 deletions(-) diff --git a/src/gadgets/curve.rs b/src/gadgets/curve.rs index 37d99997..fe2e186a 100644 --- a/src/gadgets/curve.rs +++ b/src/gadgets/curve.rs @@ -1,4 +1,4 @@ -use crate::curve::curve_types::{AffinePoint, Curve}; +use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar}; use crate::field::extension_field::Extendable; use crate::field::field_types::{Field, RichField}; use crate::gadgets::nonnative::NonNativeTarget; @@ -157,41 +157,6 @@ impl, const D: usize> CircuitBuilder { MulPrecomputationTarget { powers } } - /*fn to_digits(&mut self, x: &NonNativeTarget) -> Vec> { - debug_assert!( - 64 % WINDOW_BITS == 0, - "For simplicity, only power-of-two window sizes are handled for now" - ); - - let base = self.constant_nonnative(C::ScalarField::from_canonical_u64(BASE as u64)); - - let num_digits = digits_per_scalar::(); - let mut digits = Vec::with_capacity(num_digits); - - let (rest, limb) = self.div_rem_nonnative(&x, &base); - for _ in 0..num_digits { - digits.push(limb); - - let (rest, limb) = self.div_rem_nonnative(&rest, &base); - } - - digits - } - - pub fn mul_with_precomputation( - &mut self, - p: &AffinePointTarget, - n: &NonNativeTarget, - precomputation: MulPrecomputationTarget, - ) -> AffinePointTarget { - // Yao's method; see https://koclab.cs.ucsb.edu/teaching/ecc/eccPapers/Doche-ch09.pdf - let precomputed_powers = precomputation.powers; - - let digits = self.to_digits(n); - - - }*/ - pub fn curve_scalar_mul( &mut self, p: &AffinePointTarget, @@ -203,9 +168,12 @@ impl, const D: usize> CircuitBuilder { let bits_as_base: Vec> = bits.iter().map(|b| self.bool_to_nonnative(b)).collect(); - // Result starts at p, which is later subtracted, because we don't support arithmetic with the zero point. + let rando = (CurveScalar(C::ScalarField::rand()) * C::GENERATOR_PROJECTIVE).to_affine(); + let randot = self.constant_affine_point(rando); + // Result starts at `rando`, which is later subtracted, because we don't support arithmetic with the zero point. let mut result = self.add_virtual_affine_point_target(); - self.connect_affine_point(p, &result); + self.connect_affine_point(&randot, &result); + let mut two_i_times_p = self.add_virtual_affine_point_target(); self.connect_affine_point(p, &two_i_times_p); @@ -232,9 +200,9 @@ impl, const D: usize> CircuitBuilder { two_i_times_p = self.curve_double(&two_i_times_p); } - // Subtract off result's intial value of p. - let neg_p = self.curve_neg(&p); - result = self.curve_add(&result, &neg_p); + // Subtract off result's intial value of `rando`. + let neg_r = self.curve_neg(&randot); + result = self.curve_add(&result, &neg_r); result } @@ -394,9 +362,9 @@ mod tests { let g_target = builder.constant_affine_point(g); let five_target = builder.constant_nonnative(five); let five_g_actual = builder.curve_scalar_mul(&g_target, &five_target); - /*builder.curve_assert_valid(&five_g_actual); + builder.curve_assert_valid(&five_g_actual); - builder.connect_affine_point(&five_g_expected, &five_g_actual);*/ + builder.connect_affine_point(&five_g_expected, &five_g_actual); let data = builder.build(); let proof = data.prove(pw).unwrap(); diff --git a/src/gadgets/nonnative.rs b/src/gadgets/nonnative.rs index b04b5c1f..9ee50f3a 100644 --- a/src/gadgets/nonnative.rs +++ b/src/gadgets/nonnative.rs @@ -10,6 +10,7 @@ use crate::iop::generator::{GeneratedValues, SimpleGenerator}; use crate::iop::target::{BoolTarget, Target}; use crate::iop::witness::{PartitionWitness, Witness}; use crate::plonk::circuit_builder::CircuitBuilder; +use crate::util::ceil_div_usize; #[derive(Clone, Debug)] pub struct NonNativeTarget { @@ -19,11 +20,7 @@ pub struct NonNativeTarget { impl, const D: usize> CircuitBuilder { fn num_nonnative_limbs() -> usize { - let ff_size = FF::order(); - let f_size = F::order(); - let num_limbs = ((ff_size + f_size.clone() - BigUint::one()) / f_size).to_u32_digits()[0]; - - num_limbs as usize + ceil_div_usize(FF::BITS, 32) } pub fn biguint_to_nonnative(&mut self, x: &BigUintTarget) -> NonNativeTarget { From 39300bcf0142d9e45721c5ef9a27ffb68b68b2af Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 30 Nov 2021 15:00:12 -0800 Subject: [PATCH 33/39] fixed Secp256K1Scalar --- src/field/field_testing.rs | 13 +++++++ src/field/secp256k1_base.rs | 7 ++++ src/field/secp256k1_scalar.rs | 11 +++++- src/gadgets/curve.rs | 71 ++++++++++++++++------------------- 4 files changed, 61 insertions(+), 41 deletions(-) diff --git a/src/field/field_testing.rs b/src/field/field_testing.rs index 767a3cf2..b4ee0595 100644 --- a/src/field/field_testing.rs +++ b/src/field/field_testing.rs @@ -84,6 +84,19 @@ macro_rules! test_field_arithmetic { assert_eq!(base.exp_biguint(&pow), base.exp_biguint(&big_pow)); assert_ne!(base.exp_biguint(&pow), base.exp_biguint(&big_pow_wrong)); } + + #[test] + fn inverses() { + type F = $field; + + let x = F::rand(); + let x1 = x.inverse(); + let x2 = x1.inverse(); + let x3 = x2.inverse(); + + assert_eq!(x, x2); + assert_eq!(x1, x3); + } } }; } diff --git a/src/field/secp256k1_base.rs b/src/field/secp256k1_base.rs index acb1df4e..b3fb0148 100644 --- a/src/field/secp256k1_base.rs +++ b/src/field/secp256k1_base.rs @@ -241,3 +241,10 @@ impl DivAssign for Secp256K1Base { *self = *self / rhs; } } + +#[cfg(test)] +mod tests { + use crate::test_field_arithmetic; + + test_field_arithmetic!(crate::field::secp256k1_base::Secp256K1Base); +} diff --git a/src/field/secp256k1_scalar.rs b/src/field/secp256k1_scalar.rs index 0c406b86..f4f2e6ab 100644 --- a/src/field/secp256k1_scalar.rs +++ b/src/field/secp256k1_scalar.rs @@ -80,7 +80,7 @@ impl Field for Secp256K1Scalar { const NEG_ONE: Self = Self([ 0xBFD25E8CD0364140, 0xBAAEDCE6AF48A03B, - 0xFFFFFFFFFFFFFC2F, + 0xFFFFFFFFFFFFFFFE, 0xFFFFFFFFFFFFFFFF, ]); @@ -105,7 +105,7 @@ impl Field for Secp256K1Scalar { fn order() -> BigUint { BigUint::from_slice(&[ - 0xD0364141, 0xBFD25E8C, 0xAF48A03B, 0xBAAEDCE6, 0xFFFFFC2F, 0xFFFFFFFF, 0xFFFFFFFF, + 0xD0364141, 0xBFD25E8C, 0xAF48A03B, 0xBAAEDCE6, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, ]) } @@ -251,3 +251,10 @@ impl DivAssign for Secp256K1Scalar { *self = *self / rhs; } } + +#[cfg(test)] +mod tests { + use crate::test_field_arithmetic; + + test_field_arithmetic!(crate::field::secp256k1_scalar::Secp256K1Scalar); +} diff --git a/src/gadgets/curve.rs b/src/gadgets/curve.rs index fe2e186a..e5602c5f 100644 --- a/src/gadgets/curve.rs +++ b/src/gadgets/curve.rs @@ -18,17 +18,6 @@ impl AffinePointTarget { } } -const WINDOW_BITS: usize = 4; -const BASE: usize = 1 << WINDOW_BITS; - -fn digits_per_scalar() -> usize { - (C::ScalarField::BITS + WINDOW_BITS - 1) / WINDOW_BITS -} - -pub struct MulPrecomputationTarget { - powers: Vec>, -} - impl, const D: usize> CircuitBuilder { pub fn constant_affine_point( &mut self, @@ -138,25 +127,6 @@ impl, const D: usize> CircuitBuilder { } } - pub fn mul_precompute( - &mut self, - p: &AffinePointTarget, - ) -> MulPrecomputationTarget { - let num_digits = digits_per_scalar::(); - - let mut powers = Vec::with_capacity(num_digits); - powers.push(p.clone()); - for i in 1..num_digits { - let mut power_i = powers[i - 1].clone(); - for _j in 0..WINDOW_BITS { - power_i = self.curve_double(&power_i); - } - powers.push(power_i); - } - - MulPrecomputationTarget { powers } - } - pub fn curve_scalar_mul( &mut self, p: &AffinePointTarget, @@ -182,15 +152,10 @@ impl, const D: usize> CircuitBuilder { let result_plus_2_i_p = self.curve_add(&result, &two_i_times_p); - let result_x = result.x; - let result_y = result.y; - let result_plus_2_i_p_x = result_plus_2_i_p.x; - let result_plus_2_i_p_y = result_plus_2_i_p.y; - - let new_x_if_bit = self.mul_nonnative(bit, &result_plus_2_i_p_x); - let new_x_if_not_bit = self.mul_nonnative(¬_bit, &result_x); - let new_y_if_bit = self.mul_nonnative(bit, &result_plus_2_i_p_y); - let new_y_if_not_bit = self.mul_nonnative(¬_bit, &result_y); + let new_x_if_bit = self.mul_nonnative(bit, &result_plus_2_i_p.x); + let new_x_if_not_bit = self.mul_nonnative(¬_bit, &result.x); + let new_y_if_bit = self.mul_nonnative(bit, &result_plus_2_i_p.y); + let new_y_if_not_bit = self.mul_nonnative(¬_bit, &result.y); let new_x = self.add_nonnative(&new_x_if_bit, &new_x_if_not_bit); let new_y = self.add_nonnative(&new_y_if_bit, &new_y_if_not_bit); @@ -371,4 +336,32 @@ mod tests { verify(proof, &data.verifier_only, &data.common) } + + #[test] + fn test_curve_random() -> Result<()> { + type F = GoldilocksField; + const D: usize = 4; + + let config = CircuitConfig { + num_routed_wires: 33, + ..CircuitConfig::standard_recursion_config() + }; + + let pw = PartialWitness::new(); + let mut builder = CircuitBuilder::::new(config); + + let rando = + (CurveScalar(Secp256K1Scalar::rand()) * Secp256K1::GENERATOR_PROJECTIVE).to_affine(); + let randot = builder.constant_affine_point(rando); + + let two_target = builder.constant_nonnative(Secp256K1Scalar::TWO); + let randot_doubled = builder.curve_double(&randot); + let randot_times_two = builder.curve_scalar_mul(&randot, &two_target); + builder.connect_affine_point(&randot_doubled, &randot_times_two); + + let data = builder.build(); + let proof = data.prove(pw).unwrap(); + + verify(proof, &data.verifier_only, &data.common) + } } From b9868ec782d08770eabba60b1619c9a02773f4d3 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 30 Nov 2021 15:22:06 -0800 Subject: [PATCH 34/39] multiplication using projective --- src/curve/curve_multiplication.rs | 19 +++++++++++-------- src/curve/mod.rs | 1 + src/gadgets/mod.rs | 1 + 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/curve/curve_multiplication.rs b/src/curve/curve_multiplication.rs index b09b8a0f..83c444d8 100644 --- a/src/curve/curve_multiplication.rs +++ b/src/curve/curve_multiplication.rs @@ -16,23 +16,22 @@ fn digits_per_scalar() -> usize { #[derive(Clone)] pub struct MultiplicationPrecomputation { /// [(2^w)^i] g for each i < digits_per_scalar. - powers: Vec>, + powers: Vec>, } impl ProjectivePoint { pub fn mul_precompute(&self) -> MultiplicationPrecomputation { let num_digits = digits_per_scalar::(); - let mut powers_proj = Vec::with_capacity(num_digits); - powers_proj.push(*self); + let mut powers = Vec::with_capacity(num_digits); + powers.push(*self); for i in 1..num_digits { - let mut power_i_proj = powers_proj[i - 1]; + let mut power_i = powers[i - 1]; for _j in 0..WINDOW_BITS { - power_i_proj = power_i_proj.double(); + power_i = power_i.double(); } - powers_proj.push(power_i_proj); + powers.push(power_i); } - let powers = ProjectivePoint::batch_to_affine(&powers_proj); MultiplicationPrecomputation { powers } } @@ -59,7 +58,11 @@ impl ProjectivePoint { all_summands.push(u_summands); } - let all_sums = affine_multisummation_batch_inversion(all_summands); + let all_sums: Vec> = all_summands + .iter() + .cloned() + .map(|vec| vec.iter().fold(ProjectivePoint::ZERO, |a, &b| a + b)) + .collect(); for i in 0..all_sums.len() { u = u + all_sums[i]; y = y + u; diff --git a/src/curve/mod.rs b/src/curve/mod.rs index d31e373e..8dd6f0d6 100644 --- a/src/curve/mod.rs +++ b/src/curve/mod.rs @@ -3,4 +3,5 @@ pub mod curve_msm; pub mod curve_multiplication; pub mod curve_summation; pub mod curve_types; +pub mod ecdsa; pub mod secp256k1; diff --git a/src/gadgets/mod.rs b/src/gadgets/mod.rs index 09acb9de..6bb372a3 100644 --- a/src/gadgets/mod.rs +++ b/src/gadgets/mod.rs @@ -3,6 +3,7 @@ pub mod arithmetic_extension; pub mod arithmetic_u32; pub mod biguint; pub mod curve; +//pub mod ecdsa; pub mod hash; pub mod insert; pub mod interpolation; From f1dc1d4446c964c8221112143bed3321021f667d Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 30 Nov 2021 15:23:57 -0800 Subject: [PATCH 35/39] fix --- src/curve/mod.rs | 1 - src/gadgets/mod.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/curve/mod.rs b/src/curve/mod.rs index 8dd6f0d6..d31e373e 100644 --- a/src/curve/mod.rs +++ b/src/curve/mod.rs @@ -3,5 +3,4 @@ pub mod curve_msm; pub mod curve_multiplication; pub mod curve_summation; pub mod curve_types; -pub mod ecdsa; pub mod secp256k1; diff --git a/src/gadgets/mod.rs b/src/gadgets/mod.rs index 6bb372a3..09acb9de 100644 --- a/src/gadgets/mod.rs +++ b/src/gadgets/mod.rs @@ -3,7 +3,6 @@ pub mod arithmetic_extension; pub mod arithmetic_u32; pub mod biguint; pub mod curve; -//pub mod ecdsa; pub mod hash; pub mod insert; pub mod interpolation; From 406092f3585967a2bde0bbbb7d64851e6c0c158f Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 30 Nov 2021 15:56:12 -0800 Subject: [PATCH 36/39] clippy fixes --- src/curve/curve_multiplication.rs | 3 +-- src/curve/curve_summation.rs | 2 +- src/curve/curve_types.rs | 2 +- src/gadgets/curve.rs | 5 ++--- src/gadgets/nonnative.rs | 4 ++-- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/curve/curve_multiplication.rs b/src/curve/curve_multiplication.rs index 83c444d8..eb5bade1 100644 --- a/src/curve/curve_multiplication.rs +++ b/src/curve/curve_multiplication.rs @@ -1,7 +1,6 @@ use std::ops::Mul; -use crate::curve::curve_summation::affine_multisummation_batch_inversion; -use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar, ProjectivePoint}; +use crate::curve::curve_types::{Curve, CurveScalar, ProjectivePoint}; use crate::field::field_types::Field; const WINDOW_BITS: usize = 4; diff --git a/src/curve/curve_summation.rs b/src/curve/curve_summation.rs index 8f347eda..c67bc026 100644 --- a/src/curve/curve_summation.rs +++ b/src/curve/curve_summation.rs @@ -152,7 +152,7 @@ pub fn affine_multisummation_batch_inversion( // This is the doubling case. let mut numerator = x1.square().triple(); if C::A.is_nonzero() { - numerator = numerator + C::A; + numerator += C::A; } let quotient = numerator * inverse; let x3 = quotient.square() - x1.double(); diff --git a/src/curve/curve_types.rs b/src/curve/curve_types.rs index c9a04ab2..ef1f6186 100644 --- a/src/curve/curve_types.rs +++ b/src/curve/curve_types.rs @@ -183,7 +183,7 @@ impl ProjectivePoint { let zz = z.square(); let mut w = xx.triple(); if C::A.is_nonzero() { - w = w + C::A * zz; + w += C::A * zz; } let s = y.double() * z; let r = y * s; diff --git a/src/gadgets/curve.rs b/src/gadgets/curve.rs index e5602c5f..f7c5eaaf 100644 --- a/src/gadgets/curve.rs +++ b/src/gadgets/curve.rs @@ -134,7 +134,7 @@ impl, const D: usize> CircuitBuilder { ) -> AffinePointTarget { let one = self.constant_nonnative(C::BaseField::ONE); - let bits = self.split_nonnative_to_bits(&n); + let bits = self.split_nonnative_to_bits(n); let bits_as_base: Vec> = bits.iter().map(|b| self.bool_to_nonnative(b)).collect(); @@ -173,9 +173,8 @@ impl, const D: usize> CircuitBuilder { } } +#[cfg(test)] mod tests { - use std::ops::{Mul, Neg}; - use anyhow::Result; use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar}; diff --git a/src/gadgets/nonnative.rs b/src/gadgets/nonnative.rs index 9ee50f3a..56d717e3 100644 --- a/src/gadgets/nonnative.rs +++ b/src/gadgets/nonnative.rs @@ -1,6 +1,6 @@ use std::marker::PhantomData; -use num::{BigUint, One, Zero}; +use num::{BigUint, Zero}; use crate::field::field_types::RichField; use crate::field::{extension_field::Extendable, field_types::Field}; @@ -115,7 +115,7 @@ impl, const D: usize> CircuitBuilder { _phantom: PhantomData, }); - let product = self.mul_nonnative(&x, &inv); + let product = self.mul_nonnative(x, &inv); let one = self.constant_nonnative(FF::ONE); self.connect_nonnative(&product, &one); From 5aa5cc9c6559ce16f553c2886999e02f89feac93 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 1 Dec 2021 09:28:00 -0800 Subject: [PATCH 37/39] ignore huge tests --- src/gadgets/curve.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gadgets/curve.rs b/src/gadgets/curve.rs index f7c5eaaf..c86c3c0d 100644 --- a/src/gadgets/curve.rs +++ b/src/gadgets/curve.rs @@ -304,6 +304,7 @@ mod tests { } #[test] + #[ignore] fn test_curve_mul() -> Result<()> { type F = GoldilocksField; const D: usize = 4; @@ -337,6 +338,7 @@ mod tests { } #[test] + #[ignore] fn test_curve_random() -> Result<()> { type F = GoldilocksField; const D: usize = 4; From 9d8a5fc01e0f4e6bf1573bd5c40146ff89cd6dd1 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 1 Dec 2021 09:28:31 -0800 Subject: [PATCH 38/39] removed outdated comment --- src/curve/secp256k1.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/curve/secp256k1.rs b/src/curve/secp256k1.rs index 47c6ebb2..191343a9 100644 --- a/src/curve/secp256k1.rs +++ b/src/curve/secp256k1.rs @@ -3,9 +3,6 @@ use crate::field::field_types::Field; use crate::field::secp256k1_base::Secp256K1Base; use crate::field::secp256k1_scalar::Secp256K1Scalar; -// Parameters taken from the implementation of Bls12-377 in Zexe found here: -// https://github.com/scipr-lab/zexe/blob/master/algebra/src/curves/bls12_377/g1.rs - #[derive(Debug, Copy, Clone)] pub struct Secp256K1; From 12defa80f4ce62b4f05be905b8d6922621a499b1 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 1 Dec 2021 09:28:47 -0800 Subject: [PATCH 39/39] remove unused test --- src/curve/secp256k1.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/curve/secp256k1.rs b/src/curve/secp256k1.rs index 191343a9..58472eb4 100644 --- a/src/curve/secp256k1.rs +++ b/src/curve/secp256k1.rs @@ -57,16 +57,6 @@ mod tests { assert!(neg_g.is_valid()); } - /*#[test] - fn test_double_affine() { - for i in 0..100 { - //let p = blake_hash_usize_to_curve::(i); - assert_eq!( - p.double(), - p.to_projective().double().to_affine()); - } - }*/ - #[test] fn test_naive_multiplication() { let g = Secp256K1::GENERATOR_PROJECTIVE;