From 772ff8d69ab7fb4e1d64dca10cec1a6739f0694e Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 25 Feb 2022 16:30:01 +0100 Subject: [PATCH 01/17] Works --- plonky2/src/gadgets/curve_msm.rs | 155 +++++++++++++++++++++++++ plonky2/src/gadgets/ecdsa.rs | 5 +- plonky2/src/gadgets/mod.rs | 1 + plonky2/src/gadgets/split_nonnative.rs | 11 ++ 4 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 plonky2/src/gadgets/curve_msm.rs diff --git a/plonky2/src/gadgets/curve_msm.rs b/plonky2/src/gadgets/curve_msm.rs new file mode 100644 index 00000000..d0add327 --- /dev/null +++ b/plonky2/src/gadgets/curve_msm.rs @@ -0,0 +1,155 @@ +use num::BigUint; +use plonky2_field::extension_field::Extendable; + +use crate::curve::curve_types::{Curve, CurveScalar}; +use crate::field::field_types::Field; +use crate::gadgets::curve::AffinePointTarget; +use crate::gadgets::nonnative::NonNativeTarget; +use crate::hash::hash_types::RichField; +use crate::hash::keccak::KeccakHash; +use crate::plonk::circuit_builder::CircuitBuilder; +use crate::plonk::config::{GenericHashOut, Hasher}; + +impl, const D: usize> CircuitBuilder { + /// Computes `n*p + m*q`. + pub fn curve_msm( + &mut self, + p: &AffinePointTarget, + q: &AffinePointTarget, + n: &NonNativeTarget, + m: &NonNativeTarget, + ) -> AffinePointTarget { + let bits_n = self.split_nonnative_to_bits(n); + let bits_m = self.split_nonnative_to_bits(m); + assert_eq!(bits_n.len(), bits_m.len()); + + let sum = self.curve_add(p, q); + let precomputation = vec![p.clone(), p.clone(), q.clone(), sum]; + + let two = self.two(); + let hash_0 = KeccakHash::<32>::hash_no_pad(&[F::ZERO]); + let hash_0_scalar = C::ScalarField::from_biguint(BigUint::from_bytes_le( + &GenericHashOut::::to_bytes(&hash_0), + )); + let starting_point = CurveScalar(hash_0_scalar) * C::GENERATOR_PROJECTIVE; + let starting_point_multiplied = + (0..C::ScalarField::BITS).fold(starting_point, |acc, _| acc.double()); + + let zero = self.zero(); + let mut result = self.constant_affine_point(starting_point.to_affine()); + for (b_n, b_m) in bits_n.into_iter().zip(bits_m).rev() { + result = self.curve_double(&result); + let index = self.mul_add(two, b_m.target, b_n.target); + let r = self.random_access_curve_points(index, precomputation.clone()); + let is_zero = self.is_equal(index, zero); + let should_add = self.not(is_zero); + result = self.curve_conditional_add(&result, &r, should_add); + } + let to_subtract = self.constant_affine_point(starting_point_multiplied.to_affine()); + let to_add = self.curve_neg(&to_subtract); + result = self.curve_add(&result, &to_add); + + result + } +} + +#[cfg(test)] +mod tests { + use std::ops::Neg; + + use anyhow::Result; + use plonky2_field::secp256k1_scalar::Secp256K1Scalar; + + use crate::curve::curve_types::{Curve, CurveScalar}; + use crate::curve::secp256k1::Secp256K1; + use crate::field::field_types::Field; + use crate::iop::witness::PartialWitness; + use crate::plonk::circuit_builder::CircuitBuilder; + use crate::plonk::circuit_data::CircuitConfig; + use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + use crate::plonk::verifier::verify; + + #[test] + fn test_yo() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + + let config = CircuitConfig::standard_ecc_config(); + + let pw = PartialWitness::new(); + let mut builder = CircuitBuilder::::new(config); + + let p = + (CurveScalar(Secp256K1Scalar::rand()) * Secp256K1::GENERATOR_PROJECTIVE).to_affine(); + let q = + (CurveScalar(Secp256K1Scalar::rand()) * Secp256K1::GENERATOR_PROJECTIVE).to_affine(); + let n = Secp256K1Scalar::rand(); + let m = Secp256K1Scalar::rand(); + + let res = + (CurveScalar(n) * p.to_projective() + CurveScalar(m) * q.to_projective()).to_affine(); + let res_expected = builder.constant_affine_point(res); + builder.curve_assert_valid(&res_expected); + + let p_target = builder.constant_affine_point(p); + let q_target = builder.constant_affine_point(q); + let n_target = builder.constant_nonnative(n); + let m_target = builder.constant_nonnative(m); + + let res_target = builder.curve_msm(&p_target, &q_target, &n_target, &m_target); + builder.curve_assert_valid(&res_target); + + builder.connect_affine_point(&res_target, &res_expected); + + dbg!(builder.num_gates()); + let data = builder.build::(); + let proof = data.prove(pw).unwrap(); + + verify(proof, &data.verifier_only, &data.common) + } + + #[test] + fn test_ya() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + + let config = CircuitConfig::standard_ecc_config(); + + let pw = PartialWitness::new(); + let mut builder = CircuitBuilder::::new(config); + + let p = + (CurveScalar(Secp256K1Scalar::rand()) * Secp256K1::GENERATOR_PROJECTIVE).to_affine(); + let q = + (CurveScalar(Secp256K1Scalar::rand()) * Secp256K1::GENERATOR_PROJECTIVE).to_affine(); + let n = Secp256K1Scalar::rand(); + let m = Secp256K1Scalar::rand(); + + let res = + (CurveScalar(n) * p.to_projective() + CurveScalar(m) * q.to_projective()).to_affine(); + let res_expected = builder.constant_affine_point(res); + builder.curve_assert_valid(&res_expected); + + let p_target = builder.constant_affine_point(p); + let q_target = builder.constant_affine_point(q); + let n_target = builder.constant_nonnative(n); + let m_target = builder.constant_nonnative(m); + + // let res0_target = builder.curve_scalar_mul_windowed(&p_target, &n_target); + // let res1_target = builder.curve_scalar_mul_windowed(&q_target, &m_target); + let res0_target = builder.curve_scalar_mul(&p_target, &n_target); + let res1_target = builder.curve_scalar_mul(&q_target, &m_target); + let res_target = builder.curve_add(&res0_target, &res1_target); + builder.curve_assert_valid(&res_target); + + builder.connect_affine_point(&res_target, &res_expected); + + dbg!(builder.num_gates()); + let data = builder.build::(); + let proof = data.prove(pw).unwrap(); + + verify(proof, &data.verifier_only, &data.common) + } +} diff --git a/plonky2/src/gadgets/ecdsa.rs b/plonky2/src/gadgets/ecdsa.rs index 0a95e189..5f4c4ff1 100644 --- a/plonky2/src/gadgets/ecdsa.rs +++ b/plonky2/src/gadgets/ecdsa.rs @@ -35,8 +35,8 @@ impl, const D: usize> CircuitBuilder { let u2 = self.mul_nonnative(&r, &c); let g = self.constant_affine_point(C::GENERATOR_AFFINE); - let point1 = self.curve_scalar_mul(&g, &u1); - let point2 = self.curve_scalar_mul(&pk.0, &u2); + let point1 = self.curve_scalar_mul_windowed(&g, &u1); + let point2 = self.curve_scalar_mul_windowed(&pk.0, &u2); let point = self.curve_add(&point1, &point2); let x = NonNativeTarget:: { @@ -97,6 +97,7 @@ mod tests { builder.verify_message(msg_target, sig_target, pk_target); + dbg!(builder.num_gates()); let data = builder.build::(); let proof = data.prove(pw).unwrap(); verify(proof, &data.verifier_only, &data.common) diff --git a/plonky2/src/gadgets/mod.rs b/plonky2/src/gadgets/mod.rs index d9c93db3..e35afeed 100644 --- a/plonky2/src/gadgets/mod.rs +++ b/plonky2/src/gadgets/mod.rs @@ -5,6 +5,7 @@ pub mod biguint; pub mod curve; pub mod curve_windowed_mul; // pub mod curve_msm; +pub mod curve_msm; pub mod ecdsa; pub mod glv; pub mod hash; diff --git a/plonky2/src/gadgets/split_nonnative.rs b/plonky2/src/gadgets/split_nonnative.rs index 70661506..18fc0264 100644 --- a/plonky2/src/gadgets/split_nonnative.rs +++ b/plonky2/src/gadgets/split_nonnative.rs @@ -35,6 +35,17 @@ impl, const D: usize> CircuitBuilder { .collect() } + pub fn split_nonnative_to_2_bit_limbs( + &mut self, + val: &NonNativeTarget, + ) -> Vec { + val.value + .limbs + .iter() + .flat_map(|&l| self.split_le_base::<4>(l.0, 16)) + .collect() + } + // Note: assumes its inputs are 4-bit limbs, and does not range-check. pub fn recombine_nonnative_4_bit_limbs( &mut self, From efb074b247bf6b7f86986cbe50fdb729f79f201d Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 25 Feb 2022 17:21:35 +0100 Subject: [PATCH 02/17] Works with 2 --- plonky2/src/gadgets/curve_msm.rs | 52 +++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/plonky2/src/gadgets/curve_msm.rs b/plonky2/src/gadgets/curve_msm.rs index d0add327..e8db6fdd 100644 --- a/plonky2/src/gadgets/curve_msm.rs +++ b/plonky2/src/gadgets/curve_msm.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use num::BigUint; use plonky2_field::extension_field::Extendable; @@ -19,27 +21,51 @@ impl, const D: usize> CircuitBuilder { n: &NonNativeTarget, m: &NonNativeTarget, ) -> AffinePointTarget { - let bits_n = self.split_nonnative_to_bits(n); - let bits_m = self.split_nonnative_to_bits(m); - assert_eq!(bits_n.len(), bits_m.len()); + let limbs_n = self.split_nonnative_to_2_bit_limbs(n); + let limbs_m = self.split_nonnative_to_2_bit_limbs(m); + assert_eq!(limbs_n.len(), limbs_m.len()); - let sum = self.curve_add(p, q); - let precomputation = vec![p.clone(), p.clone(), q.clone(), sum]; - - let two = self.two(); let hash_0 = KeccakHash::<32>::hash_no_pad(&[F::ZERO]); let hash_0_scalar = C::ScalarField::from_biguint(BigUint::from_bytes_le( &GenericHashOut::::to_bytes(&hash_0), )); let starting_point = CurveScalar(hash_0_scalar) * C::GENERATOR_PROJECTIVE; + let starting_point_t = self.constant_affine_point(starting_point.to_affine()); + let neg = { + let mut neg = starting_point.to_affine(); + neg.y = -neg.y; + self.constant_affine_point(neg) + }; + + let mut precomputation = vec![p.clone(); 16]; + let mut cur_p = starting_point_t.clone(); + let mut cur_q = starting_point_t.clone(); + for i in 0..4 { + precomputation[i] = cur_p.clone(); + precomputation[4 * i] = cur_q.clone(); + cur_p = self.curve_add(&cur_p, p); + cur_q = self.curve_add(&cur_q, q); + } + for i in 1..4 { + precomputation[i] = self.curve_add(&precomputation[i], &neg); + precomputation[4 * i] = self.curve_add(&precomputation[4 * i], &neg); + } + for i in 1..4 { + for j in 1..4 { + precomputation[i + 4 * j] = + self.curve_add(&precomputation[i], &precomputation[4 * j]); + } + } + + let four = self.constant(F::from_canonical_usize(4)); let starting_point_multiplied = (0..C::ScalarField::BITS).fold(starting_point, |acc, _| acc.double()); let zero = self.zero(); let mut result = self.constant_affine_point(starting_point.to_affine()); - for (b_n, b_m) in bits_n.into_iter().zip(bits_m).rev() { - result = self.curve_double(&result); - let index = self.mul_add(two, b_m.target, b_n.target); + for (limb_n, limb_m) in limbs_n.into_iter().zip(limbs_m).rev() { + result = self.curve_repeated_double(&result, 2); + let index = self.mul_add(four, limb_m, limb_n); let r = self.random_access_curve_points(index, precomputation.clone()); let is_zero = self.is_equal(index, zero); let should_add = self.not(is_zero); @@ -137,10 +163,8 @@ mod tests { let n_target = builder.constant_nonnative(n); let m_target = builder.constant_nonnative(m); - // let res0_target = builder.curve_scalar_mul_windowed(&p_target, &n_target); - // let res1_target = builder.curve_scalar_mul_windowed(&q_target, &m_target); - let res0_target = builder.curve_scalar_mul(&p_target, &n_target); - let res1_target = builder.curve_scalar_mul(&q_target, &m_target); + let res0_target = builder.curve_scalar_mul_windowed(&p_target, &n_target); + let res1_target = builder.curve_scalar_mul_windowed(&q_target, &m_target); let res_target = builder.curve_add(&res0_target, &res1_target); builder.curve_assert_valid(&res_target); From 61af3a0de2dc85d336526b3014fa06d0d5765405 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 25 Feb 2022 19:39:30 +0100 Subject: [PATCH 03/17] Cleaning --- plonky2/src/gadgets/curve_msm.rs | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/plonky2/src/gadgets/curve_msm.rs b/plonky2/src/gadgets/curve_msm.rs index e8db6fdd..12f15306 100644 --- a/plonky2/src/gadgets/curve_msm.rs +++ b/plonky2/src/gadgets/curve_msm.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use num::BigUint; use plonky2_field::extension_field::Extendable; @@ -29,17 +27,17 @@ impl, const D: usize> CircuitBuilder { let hash_0_scalar = C::ScalarField::from_biguint(BigUint::from_bytes_le( &GenericHashOut::::to_bytes(&hash_0), )); - let starting_point = CurveScalar(hash_0_scalar) * C::GENERATOR_PROJECTIVE; - let starting_point_t = self.constant_affine_point(starting_point.to_affine()); - let neg = { - let mut neg = starting_point.to_affine(); + let rando = (CurveScalar(hash_0_scalar) * C::GENERATOR_PROJECTIVE).to_affine(); + let rando_t = self.constant_affine_point(rando); + let neg_rando = { + let mut neg = rando; neg.y = -neg.y; self.constant_affine_point(neg) }; let mut precomputation = vec![p.clone(); 16]; - let mut cur_p = starting_point_t.clone(); - let mut cur_q = starting_point_t.clone(); + let mut cur_p = rando_t.clone(); + let mut cur_q = rando_t.clone(); for i in 0..4 { precomputation[i] = cur_p.clone(); precomputation[4 * i] = cur_q.clone(); @@ -47,8 +45,8 @@ impl, const D: usize> CircuitBuilder { cur_q = self.curve_add(&cur_q, q); } for i in 1..4 { - precomputation[i] = self.curve_add(&precomputation[i], &neg); - precomputation[4 * i] = self.curve_add(&precomputation[4 * i], &neg); + precomputation[i] = self.curve_add(&precomputation[i], &neg_rando); + precomputation[4 * i] = self.curve_add(&precomputation[4 * i], &neg_rando); } for i in 1..4 { for j in 1..4 { @@ -59,10 +57,10 @@ impl, const D: usize> CircuitBuilder { let four = self.constant(F::from_canonical_usize(4)); let starting_point_multiplied = - (0..C::ScalarField::BITS).fold(starting_point, |acc, _| acc.double()); + (0..C::ScalarField::BITS).fold(rando, |acc, _| acc.double()); let zero = self.zero(); - let mut result = self.constant_affine_point(starting_point.to_affine()); + let mut result = rando_t; for (limb_n, limb_m) in limbs_n.into_iter().zip(limbs_m).rev() { result = self.curve_repeated_double(&result, 2); let index = self.mul_add(four, limb_m, limb_n); @@ -71,7 +69,7 @@ impl, const D: usize> CircuitBuilder { let should_add = self.not(is_zero); result = self.curve_conditional_add(&result, &r, should_add); } - let to_subtract = self.constant_affine_point(starting_point_multiplied.to_affine()); + let to_subtract = self.constant_affine_point(starting_point_multiplied); let to_add = self.curve_neg(&to_subtract); result = self.curve_add(&result, &to_add); @@ -81,7 +79,6 @@ impl, const D: usize> CircuitBuilder { #[cfg(test)] mod tests { - use std::ops::Neg; use anyhow::Result; use plonky2_field::secp256k1_scalar::Secp256K1Scalar; @@ -96,7 +93,7 @@ mod tests { use crate::plonk::verifier::verify; #[test] - fn test_yo() -> Result<()> { + fn test_curve_msm() -> Result<()> { const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; @@ -136,7 +133,7 @@ mod tests { } #[test] - fn test_ya() -> Result<()> { + fn test_naive_msm() -> Result<()> { const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; From 74cf1d38870aa4b8343320f4872a74bc2e00acbd Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 1 Mar 2022 07:59:35 +0100 Subject: [PATCH 04/17] Minor improvement --- plonky2/src/gadgets/curve_msm.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/plonky2/src/gadgets/curve_msm.rs b/plonky2/src/gadgets/curve_msm.rs index 12f15306..43a22da2 100644 --- a/plonky2/src/gadgets/curve_msm.rs +++ b/plonky2/src/gadgets/curve_msm.rs @@ -29,11 +29,7 @@ impl, const D: usize> CircuitBuilder { )); let rando = (CurveScalar(hash_0_scalar) * C::GENERATOR_PROJECTIVE).to_affine(); let rando_t = self.constant_affine_point(rando); - let neg_rando = { - let mut neg = rando; - neg.y = -neg.y; - self.constant_affine_point(neg) - }; + let neg_rando = self.constant_affine_point(-rando); let mut precomputation = vec![p.clone(); 16]; let mut cur_p = rando_t.clone(); @@ -56,8 +52,6 @@ impl, const D: usize> CircuitBuilder { } let four = self.constant(F::from_canonical_usize(4)); - let starting_point_multiplied = - (0..C::ScalarField::BITS).fold(rando, |acc, _| acc.double()); let zero = self.zero(); let mut result = rando_t; @@ -69,8 +63,9 @@ impl, const D: usize> CircuitBuilder { let should_add = self.not(is_zero); result = self.curve_conditional_add(&result, &r, should_add); } - let to_subtract = self.constant_affine_point(starting_point_multiplied); - let to_add = self.curve_neg(&to_subtract); + let starting_point_multiplied = + (0..C::ScalarField::BITS).fold(rando, |acc, _| acc.double()); + let to_add = self.constant_affine_point(-starting_point_multiplied); result = self.curve_add(&result, &to_add); result From ba5b1f7278e27b54a361bd4f150b185eaf63930f Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 2 Mar 2022 10:27:20 +0100 Subject: [PATCH 05/17] Fix `set_biguint_target` --- plonky2/src/iop/witness.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plonky2/src/iop/witness.rs b/plonky2/src/iop/witness.rs index de6d4a05..a013f811 100644 --- a/plonky2/src/iop/witness.rs +++ b/plonky2/src/iop/witness.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::iter::repeat; use itertools::Itertools; use num::{BigUint, FromPrimitive, Zero}; @@ -160,7 +161,11 @@ pub trait Witness { } fn set_biguint_target(&mut self, target: &BigUintTarget, value: &BigUint) { - for (<, &l) in target.limbs.iter().zip(&value.to_u32_digits()) { + for (<, l) in target + .limbs + .iter() + .zip(value.to_u32_digits().into_iter().chain(repeat(0))) + { self.set_u32_target(lt, l); } } From 6f3ca6a0bc32d5aa9e68e32ccc89dbc7e2a0055c Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 2 Mar 2022 11:04:05 +0100 Subject: [PATCH 06/17] Fixed base works --- plonky2/src/gadgets/curve_msm.rs | 149 +++++++++++++++++++++- plonky2/src/gadgets/curve_windowed_mul.rs | 15 ++- plonky2/src/gadgets/glv.rs | 64 +++++++++- plonky2/src/plonk/circuit_builder.rs | 2 +- 4 files changed, 216 insertions(+), 14 deletions(-) diff --git a/plonky2/src/gadgets/curve_msm.rs b/plonky2/src/gadgets/curve_msm.rs index 43a22da2..aa5a8d34 100644 --- a/plonky2/src/gadgets/curve_msm.rs +++ b/plonky2/src/gadgets/curve_msm.rs @@ -1,7 +1,8 @@ +use itertools::Itertools; use num::BigUint; use plonky2_field::extension_field::Extendable; -use crate::curve::curve_types::{Curve, CurveScalar}; +use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar}; use crate::field::field_types::Field; use crate::gadgets::curve::AffinePointTarget; use crate::gadgets::nonnative::NonNativeTarget; @@ -70,18 +71,126 @@ impl, const D: usize> CircuitBuilder { result } + + // pub fn quad_curve_msm( + // &mut self, + // points: [AffinePointTarget; 4], + // scalars: [NonNativeTarget; 4], + // ) -> AffinePointTarget { + // let limbs = scalars + // .iter() + // .map(|s| self.split_nonnative_to_bits(n)) + // .collect_vec(); + // + // let hash_0 = KeccakHash::<32>::hash_no_pad(&[F::ZERO]); + // let hash_0_scalar = C::ScalarField::from_biguint(BigUint::from_bytes_le( + // &GenericHashOut::::to_bytes(&hash_0), + // )); + // let rando = (CurveScalar(hash_0_scalar) * C::GENERATOR_PROJECTIVE).to_affine(); + // let rando_t = self.constant_affine_point(rando); + // let neg_rando = self.constant_affine_point(-rando); + // + // let mut precomputation = vec![points[0].clone(); 16]; + // for i in 0..4 { + // precomputation[1 << i] = points[i].clone(); + // for j in 1..1 << (i - 1) {} + // } + // let mut cur_p = rando_t.clone(); + // let mut cur_q = rando_t.clone(); + // for i in 0..4 { + // precomputation[i] = cur_p.clone(); + // precomputation[4 * i] = cur_q.clone(); + // cur_p = self.curve_add(&cur_p, p); + // cur_q = self.curve_add(&cur_q, q); + // } + // for i in 1..4 { + // precomputation[i] = self.curve_add(&precomputation[i], &neg_rando); + // precomputation[4 * i] = self.curve_add(&precomputation[4 * i], &neg_rando); + // } + // for i in 1..4 { + // for j in 1..4 { + // precomputation[i + 4 * j] = + // self.curve_add(&precomputation[i], &precomputation[4 * j]); + // } + // } + // + // let four = self.constant(F::from_canonical_usize(4)); + // + // let zero = self.zero(); + // let mut result = rando_t; + // for (limb_n, limb_m) in limbs_n.into_iter().zip(limbs_m).rev() { + // result = self.curve_repeated_double(&result, 2); + // let index = self.mul_add(four, limb_m, limb_n); + // let r = self.random_access_curve_points(index, precomputation.clone()); + // let is_zero = self.is_equal(index, zero); + // let should_add = self.not(is_zero); + // result = self.curve_conditional_add(&result, &r, should_add); + // } + // let starting_point_multiplied = + // (0..C::ScalarField::BITS).fold(rando, |acc, _| acc.double()); + // let to_add = self.constant_affine_point(-starting_point_multiplied); + // result = self.curve_add(&result, &to_add); + // + // result + // } + + pub fn fixed_base_curve_mul( + &mut self, + base: &AffinePoint, + scalar: &NonNativeTarget, + ) -> AffinePointTarget { + let doubled_base = (0..scalar.value.limbs.len() * 8).scan(base.clone(), |acc, _| { + let tmp = acc.clone(); + for _ in 0..4 { + *acc = acc.double(); + } + Some(tmp) + }); + + let bits = self.split_nonnative_to_4_bit_limbs(scalar); + + // let rando = (CurveScalar(C::ScalarField::rand()) * C::GENERATOR_PROJECTIVE).to_affine(); + let hash_0 = KeccakHash::<32>::hash_no_pad(&[F::ZERO]); + let hash_0_scalar = C::ScalarField::from_biguint(BigUint::from_bytes_le( + &GenericHashOut::::to_bytes(&hash_0), + )); + let rando = (CurveScalar(hash_0_scalar) * C::GENERATOR_PROJECTIVE).to_affine(); + let zero = self.zero(); + let mut result = self.constant_affine_point(rando.clone()); + for (limb, point) in bits.into_iter().zip(doubled_base) { + let mul_point = (0..16) + .scan(AffinePoint::ZERO, |acc, _| { + let tmp = acc.clone(); + *acc = (point + *acc).to_affine(); + Some(tmp) + }) + .map(|p| self.constant_affine_point(p)) + .collect::>(); + let is_zero = self.is_equal(limb, zero); + let should_add = self.not(is_zero); + let r = self.random_access_curve_points(limb, mul_point); + result = self.curve_conditional_add(&result, &r, should_add); + } + + let to_add = self.constant_affine_point(-rando); + self.curve_add(&result, &to_add) + } } #[cfg(test)] mod tests { + use std::str::FromStr; use anyhow::Result; + use num::BigUint; + use plonky2_field::field_types::PrimeField; + use plonky2_field::secp256k1_base::Secp256K1Base; use plonky2_field::secp256k1_scalar::Secp256K1Scalar; - use crate::curve::curve_types::{Curve, CurveScalar}; + use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar}; use crate::curve::secp256k1::Secp256K1; use crate::field::field_types::Field; - use crate::iop::witness::PartialWitness; + use crate::iop::witness::{PartialWitness, Witness}; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_data::CircuitConfig; use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; @@ -168,4 +277,38 @@ mod tests { verify(proof, &data.verifier_only, &data.common) } + + #[test] + fn test_fixed_base() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + + let config = CircuitConfig::standard_ecc_config(); + + let mut pw = PartialWitness::new(); + let mut builder = CircuitBuilder::::new(config); + + let g = Secp256K1::GENERATOR_AFFINE; + let n = Secp256K1Scalar::from_canonical_usize(10); + let n = Secp256K1Scalar::rand(); + + let res = (CurveScalar(n) * g.to_projective()).to_affine(); + let res_expected = builder.constant_affine_point(res); + builder.curve_assert_valid(&res_expected); + + let n_target = builder.add_virtual_nonnative_target::(); + pw.set_biguint_target(&n_target.value, &n.to_canonical_biguint()); + + let res_target = builder.fixed_base_curve_mul(&g, &n_target); + builder.curve_assert_valid(&res_target); + + builder.connect_affine_point(&res_target, &res_expected); + + dbg!(builder.num_gates()); + let data = builder.build::(); + let proof = data.prove(pw).unwrap(); + + verify(proof, &data.verifier_only, &data.common) + } } diff --git a/plonky2/src/gadgets/curve_windowed_mul.rs b/plonky2/src/gadgets/curve_windowed_mul.rs index 879e1ade..46c663a0 100644 --- a/plonky2/src/gadgets/curve_windowed_mul.rs +++ b/plonky2/src/gadgets/curve_windowed_mul.rs @@ -44,12 +44,21 @@ impl, const D: usize> CircuitBuilder { access_index: Target, v: Vec>, ) -> AffinePointTarget { - let num_limbs = v[0].x.value.num_limbs(); + let num_limbs = C::BaseField::BITS / 32; + let zero = self.zero_u32(); let x_limbs: Vec> = (0..num_limbs) - .map(|i| v.iter().map(|p| p.x.value.limbs[i].0).collect()) + .map(|i| { + v.iter() + .map(|p| p.x.value.limbs.get(i).unwrap_or(&zero).0) + .collect() + }) .collect(); let y_limbs: Vec> = (0..num_limbs) - .map(|i| v.iter().map(|p| p.y.value.limbs[i].0).collect()) + .map(|i| { + v.iter() + .map(|p| p.y.value.limbs.get(i).unwrap_or(&zero).0) + .collect() + }) .collect(); let selected_x_limbs: Vec<_> = x_limbs diff --git a/plonky2/src/gadgets/glv.rs b/plonky2/src/gadgets/glv.rs index 89013dd1..b9a3a380 100644 --- a/plonky2/src/gadgets/glv.rs +++ b/plonky2/src/gadgets/glv.rs @@ -19,6 +19,7 @@ impl, const D: usize> CircuitBuilder { self.constant_nonnative(BETA) } + // TODO: Add decomposition check. pub fn decompose_secp256k1_scalar( &mut self, k: &NonNativeTarget, @@ -59,12 +60,24 @@ impl, const D: usize> CircuitBuilder { y: p.y.clone(), }; - let part1 = self.curve_scalar_mul_windowed(p, &k1); - let part1_neg = self.curve_conditional_neg(&part1, k1_neg); - let part2 = self.curve_scalar_mul_windowed(&sp, &k2); - let part2_neg = self.curve_conditional_neg(&part2, k2_neg); - - self.curve_add(&part1_neg, &part2_neg) + // let part1 = self.curve_scalar_mul_windowed(p, &k1); + // let part1_neg = self.curve_conditional_neg(&part1, k1_neg); + // let part2 = self.curve_scalar_mul_windowed(&sp, &k2); + // let part2_neg = self.curve_conditional_neg(&part2, k2_neg); + // + // self.curve_add(&part1_neg, &part2_neg) + // dbg!(k1.value.limbs.len()); + // dbg!(k2.value.limbs.len()); + let p_neg = self.curve_conditional_neg(&p, k1_neg); + let sp_neg = self.curve_conditional_neg(&sp, k2_neg); + // let yo = self.curve_scalar_mul_windowed(&p_neg, &k1); + // let ya = self.curve_scalar_mul_windowed(&sp_neg, &k2); + // dbg!(&yo); + // dbg!(&ya); + // self.connect_affine_point(&part1_neg, &yo); + // self.connect_affine_point(&part2_neg, &ya); + self.curve_msm(&p_neg, &sp_neg, &k1, &k2) + // self.curve_add(&yo, &ya) } } @@ -105,7 +118,7 @@ mod tests { use crate::curve::curve_types::{Curve, CurveScalar}; use crate::curve::glv::glv_mul; use crate::curve::secp256k1::Secp256K1; - use crate::iop::witness::PartialWitness; + use crate::iop::witness::{PartialWitness, Witness}; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_data::CircuitConfig; use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; @@ -134,6 +147,43 @@ mod tests { let actual = builder.glv_mul(&randot, &scalar_target); builder.connect_affine_point(&expected, &actual); + dbg!(builder.num_gates()); + let data = builder.build::(); + let proof = data.prove(pw).unwrap(); + + verify(proof, &data.verifier_only, &data.common) + } + + #[test] + fn test_wtf() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + + let config = CircuitConfig::standard_ecc_config(); + + let mut 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 scalar = Secp256K1Scalar::rand(); + let scalar_target = builder.constant_nonnative(scalar); + + let tr = builder.add_virtual_bool_target(); + pw.set_bool_target(tr, false); + + let randotneg = builder.curve_conditional_neg(&randot, tr); + let y = builder.curve_scalar_mul_windowed(&randotneg, &scalar_target); + + let yy = builder.curve_scalar_mul_windowed(&randot, &scalar_target); + let yy = builder.curve_conditional_neg(&yy, tr); + + builder.connect_affine_point(&y, &yy); + + dbg!(builder.num_gates()); let data = builder.build::(); let proof = data.prove(pw).unwrap(); diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index 63f45fec..e0d05c24 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -70,7 +70,7 @@ pub struct CircuitBuilder, const D: usize> { marked_targets: Vec>, /// Generators used to generate the witness. - generators: Vec>>, + pub generators: Vec>>, constants_to_targets: HashMap, targets_to_constants: HashMap, From 850df4dfb1413b9a00555789caf70fb3dd0ac461 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 2 Mar 2022 11:16:32 +0100 Subject: [PATCH 07/17] Add fixed base file --- plonky2/src/gadgets/curve_fixed_base.rs | 103 ++++++++++++++++ plonky2/src/gadgets/curve_msm.rs | 149 +----------------------- plonky2/src/gadgets/mod.rs | 4 +- 3 files changed, 108 insertions(+), 148 deletions(-) create mode 100644 plonky2/src/gadgets/curve_fixed_base.rs diff --git a/plonky2/src/gadgets/curve_fixed_base.rs b/plonky2/src/gadgets/curve_fixed_base.rs new file mode 100644 index 00000000..3b826f78 --- /dev/null +++ b/plonky2/src/gadgets/curve_fixed_base.rs @@ -0,0 +1,103 @@ +use num::BigUint; +use plonky2_field::extension_field::Extendable; + +use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar}; +use crate::field::field_types::Field; +use crate::gadgets::curve::AffinePointTarget; +use crate::gadgets::nonnative::NonNativeTarget; +use crate::hash::hash_types::RichField; +use crate::hash::keccak::KeccakHash; +use crate::plonk::circuit_builder::CircuitBuilder; +use crate::plonk::config::{GenericHashOut, Hasher}; + +impl, const D: usize> CircuitBuilder { + pub fn fixed_base_curve_mul( + &mut self, + base: &AffinePoint, + scalar: &NonNativeTarget, + ) -> AffinePointTarget { + let doubled_base = (0..scalar.value.limbs.len() * 8).scan(base.clone(), |acc, _| { + let tmp = acc.clone(); + for _ in 0..4 { + *acc = acc.double(); + } + Some(tmp) + }); + + let bits = self.split_nonnative_to_4_bit_limbs(scalar); + + let hash_0 = KeccakHash::<32>::hash_no_pad(&[F::ZERO]); + let hash_0_scalar = C::ScalarField::from_biguint(BigUint::from_bytes_le( + &GenericHashOut::::to_bytes(&hash_0), + )); + let rando = (CurveScalar(hash_0_scalar) * C::GENERATOR_PROJECTIVE).to_affine(); + let zero = self.zero(); + let mut result = self.constant_affine_point(rando.clone()); + for (limb, point) in bits.into_iter().zip(doubled_base) { + let mul_point = (0..16) + .scan(AffinePoint::ZERO, |acc, _| { + let tmp = acc.clone(); + *acc = (point + *acc).to_affine(); + Some(tmp) + }) + .map(|p| self.constant_affine_point(p)) + .collect::>(); + let is_zero = self.is_equal(limb, zero); + let should_add = self.not(is_zero); + let r = self.random_access_curve_points(limb, mul_point); + result = self.curve_conditional_add(&result, &r, should_add); + } + + let to_add = self.constant_affine_point(-rando); + self.curve_add(&result, &to_add) + } +} + +#[cfg(test)] +mod tests { + use anyhow::Result; + use plonky2_field::field_types::PrimeField; + use plonky2_field::secp256k1_scalar::Secp256K1Scalar; + + use crate::curve::curve_types::{Curve, CurveScalar}; + use crate::curve::secp256k1::Secp256K1; + use crate::field::field_types::Field; + use crate::iop::witness::{PartialWitness, Witness}; + use crate::plonk::circuit_builder::CircuitBuilder; + use crate::plonk::circuit_data::CircuitConfig; + use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + use crate::plonk::verifier::verify; + + #[test] + fn test_fixed_base() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + + let config = CircuitConfig::standard_ecc_config(); + + let mut pw = PartialWitness::new(); + let mut builder = CircuitBuilder::::new(config); + + let g = Secp256K1::GENERATOR_AFFINE; + let n = Secp256K1Scalar::rand(); + + let res = (CurveScalar(n) * g.to_projective()).to_affine(); + let res_expected = builder.constant_affine_point(res); + builder.curve_assert_valid(&res_expected); + + let n_target = builder.add_virtual_nonnative_target::(); + pw.set_biguint_target(&n_target.value, &n.to_canonical_biguint()); + + let res_target = builder.fixed_base_curve_mul(&g, &n_target); + builder.curve_assert_valid(&res_target); + + builder.connect_affine_point(&res_target, &res_expected); + + dbg!(builder.num_gates()); + let data = builder.build::(); + let proof = data.prove(pw).unwrap(); + + verify(proof, &data.verifier_only, &data.common) + } +} diff --git a/plonky2/src/gadgets/curve_msm.rs b/plonky2/src/gadgets/curve_msm.rs index aa5a8d34..43a22da2 100644 --- a/plonky2/src/gadgets/curve_msm.rs +++ b/plonky2/src/gadgets/curve_msm.rs @@ -1,8 +1,7 @@ -use itertools::Itertools; use num::BigUint; use plonky2_field::extension_field::Extendable; -use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar}; +use crate::curve::curve_types::{Curve, CurveScalar}; use crate::field::field_types::Field; use crate::gadgets::curve::AffinePointTarget; use crate::gadgets::nonnative::NonNativeTarget; @@ -71,126 +70,18 @@ impl, const D: usize> CircuitBuilder { result } - - // pub fn quad_curve_msm( - // &mut self, - // points: [AffinePointTarget; 4], - // scalars: [NonNativeTarget; 4], - // ) -> AffinePointTarget { - // let limbs = scalars - // .iter() - // .map(|s| self.split_nonnative_to_bits(n)) - // .collect_vec(); - // - // let hash_0 = KeccakHash::<32>::hash_no_pad(&[F::ZERO]); - // let hash_0_scalar = C::ScalarField::from_biguint(BigUint::from_bytes_le( - // &GenericHashOut::::to_bytes(&hash_0), - // )); - // let rando = (CurveScalar(hash_0_scalar) * C::GENERATOR_PROJECTIVE).to_affine(); - // let rando_t = self.constant_affine_point(rando); - // let neg_rando = self.constant_affine_point(-rando); - // - // let mut precomputation = vec![points[0].clone(); 16]; - // for i in 0..4 { - // precomputation[1 << i] = points[i].clone(); - // for j in 1..1 << (i - 1) {} - // } - // let mut cur_p = rando_t.clone(); - // let mut cur_q = rando_t.clone(); - // for i in 0..4 { - // precomputation[i] = cur_p.clone(); - // precomputation[4 * i] = cur_q.clone(); - // cur_p = self.curve_add(&cur_p, p); - // cur_q = self.curve_add(&cur_q, q); - // } - // for i in 1..4 { - // precomputation[i] = self.curve_add(&precomputation[i], &neg_rando); - // precomputation[4 * i] = self.curve_add(&precomputation[4 * i], &neg_rando); - // } - // for i in 1..4 { - // for j in 1..4 { - // precomputation[i + 4 * j] = - // self.curve_add(&precomputation[i], &precomputation[4 * j]); - // } - // } - // - // let four = self.constant(F::from_canonical_usize(4)); - // - // let zero = self.zero(); - // let mut result = rando_t; - // for (limb_n, limb_m) in limbs_n.into_iter().zip(limbs_m).rev() { - // result = self.curve_repeated_double(&result, 2); - // let index = self.mul_add(four, limb_m, limb_n); - // let r = self.random_access_curve_points(index, precomputation.clone()); - // let is_zero = self.is_equal(index, zero); - // let should_add = self.not(is_zero); - // result = self.curve_conditional_add(&result, &r, should_add); - // } - // let starting_point_multiplied = - // (0..C::ScalarField::BITS).fold(rando, |acc, _| acc.double()); - // let to_add = self.constant_affine_point(-starting_point_multiplied); - // result = self.curve_add(&result, &to_add); - // - // result - // } - - pub fn fixed_base_curve_mul( - &mut self, - base: &AffinePoint, - scalar: &NonNativeTarget, - ) -> AffinePointTarget { - let doubled_base = (0..scalar.value.limbs.len() * 8).scan(base.clone(), |acc, _| { - let tmp = acc.clone(); - for _ in 0..4 { - *acc = acc.double(); - } - Some(tmp) - }); - - let bits = self.split_nonnative_to_4_bit_limbs(scalar); - - // let rando = (CurveScalar(C::ScalarField::rand()) * C::GENERATOR_PROJECTIVE).to_affine(); - let hash_0 = KeccakHash::<32>::hash_no_pad(&[F::ZERO]); - let hash_0_scalar = C::ScalarField::from_biguint(BigUint::from_bytes_le( - &GenericHashOut::::to_bytes(&hash_0), - )); - let rando = (CurveScalar(hash_0_scalar) * C::GENERATOR_PROJECTIVE).to_affine(); - let zero = self.zero(); - let mut result = self.constant_affine_point(rando.clone()); - for (limb, point) in bits.into_iter().zip(doubled_base) { - let mul_point = (0..16) - .scan(AffinePoint::ZERO, |acc, _| { - let tmp = acc.clone(); - *acc = (point + *acc).to_affine(); - Some(tmp) - }) - .map(|p| self.constant_affine_point(p)) - .collect::>(); - let is_zero = self.is_equal(limb, zero); - let should_add = self.not(is_zero); - let r = self.random_access_curve_points(limb, mul_point); - result = self.curve_conditional_add(&result, &r, should_add); - } - - let to_add = self.constant_affine_point(-rando); - self.curve_add(&result, &to_add) - } } #[cfg(test)] mod tests { - use std::str::FromStr; use anyhow::Result; - use num::BigUint; - use plonky2_field::field_types::PrimeField; - use plonky2_field::secp256k1_base::Secp256K1Base; use plonky2_field::secp256k1_scalar::Secp256K1Scalar; - use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar}; + use crate::curve::curve_types::{Curve, CurveScalar}; use crate::curve::secp256k1::Secp256K1; use crate::field::field_types::Field; - use crate::iop::witness::{PartialWitness, Witness}; + use crate::iop::witness::PartialWitness; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_data::CircuitConfig; use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; @@ -277,38 +168,4 @@ mod tests { verify(proof, &data.verifier_only, &data.common) } - - #[test] - fn test_fixed_base() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - - let config = CircuitConfig::standard_ecc_config(); - - let mut pw = PartialWitness::new(); - let mut builder = CircuitBuilder::::new(config); - - let g = Secp256K1::GENERATOR_AFFINE; - let n = Secp256K1Scalar::from_canonical_usize(10); - let n = Secp256K1Scalar::rand(); - - let res = (CurveScalar(n) * g.to_projective()).to_affine(); - let res_expected = builder.constant_affine_point(res); - builder.curve_assert_valid(&res_expected); - - let n_target = builder.add_virtual_nonnative_target::(); - pw.set_biguint_target(&n_target.value, &n.to_canonical_biguint()); - - let res_target = builder.fixed_base_curve_mul(&g, &n_target); - builder.curve_assert_valid(&res_target); - - builder.connect_affine_point(&res_target, &res_expected); - - dbg!(builder.num_gates()); - let data = builder.build::(); - let proof = data.prove(pw).unwrap(); - - verify(proof, &data.verifier_only, &data.common) - } } diff --git a/plonky2/src/gadgets/mod.rs b/plonky2/src/gadgets/mod.rs index e35afeed..50fc0437 100644 --- a/plonky2/src/gadgets/mod.rs +++ b/plonky2/src/gadgets/mod.rs @@ -3,9 +3,9 @@ pub mod arithmetic_extension; pub mod arithmetic_u32; pub mod biguint; pub mod curve; -pub mod curve_windowed_mul; -// pub mod curve_msm; +pub mod curve_fixed_base; pub mod curve_msm; +pub mod curve_windowed_mul; pub mod ecdsa; pub mod glv; pub mod hash; From 7c70c46ca7c787c287ed20e021c63a52f42f8b7b Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 2 Mar 2022 13:19:31 +0100 Subject: [PATCH 08/17] Working GLV with MSM --- plonky2/src/gadgets/curve.rs | 5 +- plonky2/src/gadgets/curve_fixed_base.rs | 2 + plonky2/src/gadgets/curve_msm.rs | 89 ++++++++++++++++++++++--- plonky2/src/gadgets/glv.rs | 57 ++-------------- plonky2/src/gadgets/nonnative.rs | 2 +- plonky2/src/gadgets/split_nonnative.rs | 8 +-- plonky2/src/iop/generator.rs | 62 +++++++++++++++++ 7 files changed, 152 insertions(+), 73 deletions(-) diff --git a/plonky2/src/gadgets/curve.rs b/plonky2/src/gadgets/curve.rs index aa8c4112..a1fc3a8b 100644 --- a/plonky2/src/gadgets/curve.rs +++ b/plonky2/src/gadgets/curve.rs @@ -78,15 +78,12 @@ impl, const D: usize> CircuitBuilder { ) -> AffinePointTarget { let not_b = self.not(b); let neg = self.curve_neg(p); - let x_if_true = self.mul_nonnative_by_bool(&neg.x, b); let y_if_true = self.mul_nonnative_by_bool(&neg.y, b); - let x_if_false = self.mul_nonnative_by_bool(&p.x, not_b); let y_if_false = self.mul_nonnative_by_bool(&p.y, not_b); - let x = self.add_nonnative(&x_if_true, &x_if_false); let y = self.add_nonnative(&y_if_true, &y_if_false); - AffinePointTarget { x, y } + AffinePointTarget { x: p.x.clone(), y } } pub fn curve_double(&mut self, p: &AffinePointTarget) -> AffinePointTarget { diff --git a/plonky2/src/gadgets/curve_fixed_base.rs b/plonky2/src/gadgets/curve_fixed_base.rs index 3b826f78..70def6bc 100644 --- a/plonky2/src/gadgets/curve_fixed_base.rs +++ b/plonky2/src/gadgets/curve_fixed_base.rs @@ -11,6 +11,8 @@ use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::config::{GenericHashOut, Hasher}; impl, const D: usize> CircuitBuilder { + /// Do windowed fixed-base scalar multiplication, using a 4-bit window. + // TODO: Benchmark other window sizes. pub fn fixed_base_curve_mul( &mut self, base: &AffinePoint, diff --git a/plonky2/src/gadgets/curve_msm.rs b/plonky2/src/gadgets/curve_msm.rs index 43a22da2..df13e8f3 100644 --- a/plonky2/src/gadgets/curve_msm.rs +++ b/plonky2/src/gadgets/curve_msm.rs @@ -3,8 +3,8 @@ use plonky2_field::extension_field::Extendable; use crate::curve::curve_types::{Curve, CurveScalar}; use crate::field::field_types::Field; +use crate::gadgets::biguint::BigUintTarget; use crate::gadgets::curve::AffinePointTarget; -use crate::gadgets::nonnative::NonNativeTarget; use crate::hash::hash_types::RichField; use crate::hash::keccak::KeccakHash; use crate::plonk::circuit_builder::CircuitBuilder; @@ -16,12 +16,13 @@ impl, const D: usize> CircuitBuilder { &mut self, p: &AffinePointTarget, q: &AffinePointTarget, - n: &NonNativeTarget, - m: &NonNativeTarget, + n: &BigUintTarget, + m: &BigUintTarget, ) -> AffinePointTarget { - let limbs_n = self.split_nonnative_to_2_bit_limbs(n); - let limbs_m = self.split_nonnative_to_2_bit_limbs(m); + let limbs_n = self.split_biguint_to_2_bit_limbs(n); + let limbs_m = self.split_biguint_to_2_bit_limbs(m); assert_eq!(limbs_n.len(), limbs_m.len()); + let num_limbs = limbs_n.len(); let hash_0 = KeccakHash::<32>::hash_no_pad(&[F::ZERO]); let hash_0_scalar = C::ScalarField::from_biguint(BigUint::from_bytes_le( @@ -63,8 +64,7 @@ impl, const D: usize> CircuitBuilder { let should_add = self.not(is_zero); result = self.curve_conditional_add(&result, &r, should_add); } - let starting_point_multiplied = - (0..C::ScalarField::BITS).fold(rando, |acc, _| acc.double()); + let starting_point_multiplied = (0..2 * num_limbs).fold(rando, |acc, _| acc.double()); let to_add = self.constant_affine_point(-starting_point_multiplied); result = self.curve_add(&result, &to_add); @@ -74,11 +74,14 @@ impl, const D: usize> CircuitBuilder { #[cfg(test)] mod tests { + use std::str::FromStr; use anyhow::Result; + use num::BigUint; + use plonky2_field::secp256k1_base::Secp256K1Base; use plonky2_field::secp256k1_scalar::Secp256K1Scalar; - use crate::curve::curve_types::{Curve, CurveScalar}; + use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar}; use crate::curve::secp256k1::Secp256K1; use crate::field::field_types::Field; use crate::iop::witness::PartialWitness; @@ -115,7 +118,7 @@ mod tests { let n_target = builder.constant_nonnative(n); let m_target = builder.constant_nonnative(m); - let res_target = builder.curve_msm(&p_target, &q_target, &n_target, &m_target); + let res_target = builder.curve_msm(&p_target, &q_target, &n_target.value, &m_target.value); builder.curve_assert_valid(&res_target); builder.connect_affine_point(&res_target, &res_expected); @@ -168,4 +171,72 @@ mod tests { verify(proof, &data.verifier_only, &data.common) } + + #[test] + fn test_curve_lul() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + + let config = CircuitConfig::standard_ecc_config(); + + let pw = PartialWitness::new(); + let mut builder = CircuitBuilder::::new(config); + + let p = AffinePoint:: { + x: Secp256K1Base::from_biguint( + BigUint::from_str( + "95702873347299649035220040874584348285675823985309557645567012532974768144045", + ) + .unwrap(), + ), + y: Secp256K1Base::from_biguint( + BigUint::from_str( + "34849299245821426255020320369755722155634282348110887335812955146294938249053", + ) + .unwrap(), + ), + zero: false, + }; + let q = AffinePoint:: { + x: Secp256K1Base::from_biguint( + BigUint::from_str( + "66037057977021147605301350925941983227524093291368248236634649161657340356645", + ) + .unwrap(), + ), + y: Secp256K1Base::from_biguint( + BigUint::from_str( + "80942789991494769168550664638932185697635702317529676703644628861613896422610", + ) + .unwrap(), + ), + zero: false, + }; + + let n = BigUint::from_str("89874493710619023150462632713212469930").unwrap(); + let m = BigUint::from_str("76073901947022186525975758425319149118").unwrap(); + + let res = (CurveScalar(Secp256K1Scalar::from_biguint(n.clone())) * p.to_projective() + + CurveScalar(Secp256K1Scalar::from_biguint(m.clone())) * q.to_projective()) + .to_affine(); + let res_expected = builder.constant_affine_point(res); + builder.curve_assert_valid(&res_expected); + + let p_target = builder.constant_affine_point(p); + let q_target = builder.constant_affine_point(q); + let n_target = builder.constant_biguint(&n); + let m_target = builder.constant_biguint(&m); + + let res_target = builder.curve_msm(&p_target, &q_target, &n_target, &m_target); + builder.curve_assert_valid(&res_target); + + builder.connect_affine_point(&res_target, &res_expected); + + dbg!(builder.num_gates()); + let data = builder.build::(); + let proof = data.prove(pw).unwrap(); + + verify(proof, &data.verifier_only, &data.common) + } } diff --git a/plonky2/src/gadgets/glv.rs b/plonky2/src/gadgets/glv.rs index b9a3a380..e0a4cfaa 100644 --- a/plonky2/src/gadgets/glv.rs +++ b/plonky2/src/gadgets/glv.rs @@ -43,6 +43,8 @@ impl, const D: usize> CircuitBuilder { _phantom: PhantomData, }); + // debug_assert!(k1_raw + S * k2_raw == k); + (k1, k2, k1_neg, k2_neg) } @@ -60,24 +62,9 @@ impl, const D: usize> CircuitBuilder { y: p.y.clone(), }; - // let part1 = self.curve_scalar_mul_windowed(p, &k1); - // let part1_neg = self.curve_conditional_neg(&part1, k1_neg); - // let part2 = self.curve_scalar_mul_windowed(&sp, &k2); - // let part2_neg = self.curve_conditional_neg(&part2, k2_neg); - // - // self.curve_add(&part1_neg, &part2_neg) - // dbg!(k1.value.limbs.len()); - // dbg!(k2.value.limbs.len()); let p_neg = self.curve_conditional_neg(&p, k1_neg); let sp_neg = self.curve_conditional_neg(&sp, k2_neg); - // let yo = self.curve_scalar_mul_windowed(&p_neg, &k1); - // let ya = self.curve_scalar_mul_windowed(&sp_neg, &k2); - // dbg!(&yo); - // dbg!(&ya); - // self.connect_affine_point(&part1_neg, &yo); - // self.connect_affine_point(&part2_neg, &ya); - self.curve_msm(&p_neg, &sp_neg, &k1, &k2) - // self.curve_add(&yo, &ya) + self.curve_msm(&p_neg, &sp_neg, &k1.value, &k2.value) } } @@ -118,7 +105,7 @@ mod tests { use crate::curve::curve_types::{Curve, CurveScalar}; use crate::curve::glv::glv_mul; use crate::curve::secp256k1::Secp256K1; - use crate::iop::witness::{PartialWitness, Witness}; + use crate::iop::witness::PartialWitness; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_data::CircuitConfig; use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; @@ -153,40 +140,4 @@ mod tests { verify(proof, &data.verifier_only, &data.common) } - - #[test] - fn test_wtf() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - - let config = CircuitConfig::standard_ecc_config(); - - let mut 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 scalar = Secp256K1Scalar::rand(); - let scalar_target = builder.constant_nonnative(scalar); - - let tr = builder.add_virtual_bool_target(); - pw.set_bool_target(tr, false); - - let randotneg = builder.curve_conditional_neg(&randot, tr); - let y = builder.curve_scalar_mul_windowed(&randotneg, &scalar_target); - - let yy = builder.curve_scalar_mul_windowed(&randot, &scalar_target); - let yy = builder.curve_conditional_neg(&yy, tr); - - builder.connect_affine_point(&y, &yy); - - dbg!(builder.num_gates()); - let data = builder.build::(); - let proof = data.prove(pw).unwrap(); - - verify(proof, &data.verifier_only, &data.common) - } } diff --git a/plonky2/src/gadgets/nonnative.rs b/plonky2/src/gadgets/nonnative.rs index 910915d0..73bc0ad3 100644 --- a/plonky2/src/gadgets/nonnative.rs +++ b/plonky2/src/gadgets/nonnative.rs @@ -454,7 +454,7 @@ impl, const D: usize, FF: PrimeField> SimpleGenerat let b_biguint = b.to_canonical_biguint(); let modulus = FF::order(); - let (diff_biguint, overflow) = if a_biguint > b_biguint { + let (diff_biguint, overflow) = if a_biguint >= b_biguint { (a_biguint - b_biguint, false) } else { (modulus + a_biguint - b_biguint, true) diff --git a/plonky2/src/gadgets/split_nonnative.rs b/plonky2/src/gadgets/split_nonnative.rs index 18fc0264..becf1177 100644 --- a/plonky2/src/gadgets/split_nonnative.rs +++ b/plonky2/src/gadgets/split_nonnative.rs @@ -35,12 +35,8 @@ impl, const D: usize> CircuitBuilder { .collect() } - pub fn split_nonnative_to_2_bit_limbs( - &mut self, - val: &NonNativeTarget, - ) -> Vec { - val.value - .limbs + pub fn split_biguint_to_2_bit_limbs(&mut self, val: &BigUintTarget) -> Vec { + val.limbs .iter() .flat_map(|&l| self.split_le_base::<4>(l.0, 16)) .collect() diff --git a/plonky2/src/iop/generator.rs b/plonky2/src/iop/generator.rs index 1569e889..4dcd11da 100644 --- a/plonky2/src/iop/generator.rs +++ b/plonky2/src/iop/generator.rs @@ -89,6 +89,68 @@ pub(crate) fn generate_partial_witness< } pending_generator_indices = next_pending_generator_indices; + // for t in [ + // Target::VirtualTarget { index: 57934 }, + // Target::VirtualTarget { index: 57935 }, + // Target::VirtualTarget { index: 57936 }, + // Target::VirtualTarget { index: 57937 }, + // Target::VirtualTarget { index: 57938 }, + // Target::VirtualTarget { index: 57939 }, + // Target::VirtualTarget { index: 57940 }, + // Target::VirtualTarget { index: 57941 }, + // ] { + // if let Some(v) = witness.try_get_target(t) { + // println!("a {}", v); + // } + // } + // for t in [ + // Target::VirtualTarget { index: 57952 }, + // Target::VirtualTarget { index: 57953 }, + // Target::VirtualTarget { index: 57954 }, + // Target::VirtualTarget { index: 57955 }, + // Target::VirtualTarget { index: 57956 }, + // Target::VirtualTarget { index: 57957 }, + // Target::VirtualTarget { index: 57958 }, + // Target::VirtualTarget { index: 57959 }, + // ] { + // if let Some(v) = witness.try_get_target(t) { + // println!("b {}", v); + // } + // } + // + // let t = Target::Wire(Wire { + // gate: 141_857, + // input: 8, + // }); + // if let Some(v) = witness.try_get_target(t) { + // println!("prod_exp {}", v); + // } + // let t = Target::Wire(Wire { + // gate: 141_863, + // input: 22, + // }); + // if let Some(v) = witness.try_get_target(t) { + // println!("prod act {}", v); + // } + // let t = Target::Wire(Wire { gate: 9, input: 3 }); + // if let Some(v) = witness.try_get_target(t) { + // println!("modulus {}", v); + // } + // let t = Target::VirtualTarget { index: 57_976 }; + // if let Some(v) = witness.try_get_target(t) { + // println!("overflow {}", v); + // } + // let t = Target::Wire(Wire { + // gate: 141_885, + // input: 8, + // }); + // if let Some(v) = witness.try_get_target(t) { + // println!("mod time ov {}", v); + // } + // let t = Target::VirtualTarget { index: 57_968 }; + // if let Some(v) = witness.try_get_target(t) { + // println!("prod {}", v); + // } } assert_eq!( From 2571862f00eb997c4bacc2118b731ce18a05e996 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 2 Mar 2022 13:31:16 +0100 Subject: [PATCH 09/17] Working GLV decomposition check --- plonky2/src/curve/glv.rs | 12 ++++++------ plonky2/src/gadgets/curve.rs | 12 ++++-------- plonky2/src/gadgets/glv.rs | 12 +++++++++--- plonky2/src/gadgets/nonnative.rs | 13 +++++++++++++ 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/plonky2/src/curve/glv.rs b/plonky2/src/curve/glv.rs index 11172be0..aeeb463e 100644 --- a/plonky2/src/curve/glv.rs +++ b/plonky2/src/curve/glv.rs @@ -8,14 +8,14 @@ use crate::curve::curve_msm::msm_parallel; use crate::curve::curve_types::{AffinePoint, ProjectivePoint}; use crate::curve::secp256k1::Secp256K1; -pub const BETA: Secp256K1Base = Secp256K1Base([ +pub const GLV_BETA: Secp256K1Base = Secp256K1Base([ 13923278643952681454, 11308619431505398165, 7954561588662645993, 8856726876819556112, ]); -const S: Secp256K1Scalar = Secp256K1Scalar([ +pub const GLV_S: Secp256K1Scalar = Secp256K1Scalar([ 16069571880186789234, 1310022930574435960, 11900229862571533402, @@ -52,7 +52,7 @@ pub fn decompose_secp256k1_scalar( let k1_raw = k - c1 * A1 - c2 * A2; let k2_raw = c1 * MINUS_B1 - c2 * B2; - debug_assert!(k1_raw + S * k2_raw == k); + debug_assert!(k1_raw + GLV_S * k2_raw == k); let two = BigUint::from_slice(&[2]); let k1_neg = k1_raw.to_canonical_biguint() > p.clone() / two.clone(); @@ -80,7 +80,7 @@ pub fn glv_mul(p: ProjectivePoint, k: Secp256K1Scalar) -> ProjectiveP let p_affine = p.to_affine(); let sp = AffinePoint:: { - x: p_affine.x * BETA, + x: p_affine.x * GLV_BETA, y: p_affine.y, zero: p_affine.zero, }; @@ -102,7 +102,7 @@ mod tests { use plonky2_field::secp256k1_scalar::Secp256K1Scalar; use crate::curve::curve_types::{Curve, CurveScalar}; - use crate::curve::glv::{decompose_secp256k1_scalar, glv_mul, S}; + use crate::curve::glv::{decompose_secp256k1_scalar, glv_mul, GLV_S}; use crate::curve::secp256k1::Secp256K1; #[test] @@ -113,7 +113,7 @@ mod tests { let m1 = if k1_neg { -one } else { one }; let m2 = if k2_neg { -one } else { one }; - assert!(k1 * m1 + S * k2 * m2 == k); + assert!(k1 * m1 + GLV_S * k2 * m2 == k); Ok(()) } diff --git a/plonky2/src/gadgets/curve.rs b/plonky2/src/gadgets/curve.rs index a1fc3a8b..e4e66a4e 100644 --- a/plonky2/src/gadgets/curve.rs +++ b/plonky2/src/gadgets/curve.rs @@ -76,14 +76,10 @@ impl, const D: usize> CircuitBuilder { p: &AffinePointTarget, b: BoolTarget, ) -> AffinePointTarget { - let not_b = self.not(b); - let neg = self.curve_neg(p); - let y_if_true = self.mul_nonnative_by_bool(&neg.y, b); - let y_if_false = self.mul_nonnative_by_bool(&p.y, not_b); - - let y = self.add_nonnative(&y_if_true, &y_if_false); - - AffinePointTarget { x: p.x.clone(), y } + AffinePointTarget { + x: p.x.clone(), + y: self.nonnative_conditional_neg(&p.y, b), + } } pub fn curve_double(&mut self, p: &AffinePointTarget) -> AffinePointTarget { diff --git a/plonky2/src/gadgets/glv.rs b/plonky2/src/gadgets/glv.rs index e0a4cfaa..8447137d 100644 --- a/plonky2/src/gadgets/glv.rs +++ b/plonky2/src/gadgets/glv.rs @@ -4,7 +4,7 @@ use plonky2_field::extension_field::Extendable; use plonky2_field::secp256k1_base::Secp256K1Base; use plonky2_field::secp256k1_scalar::Secp256K1Scalar; -use crate::curve::glv::{decompose_secp256k1_scalar, BETA}; +use crate::curve::glv::{decompose_secp256k1_scalar, GLV_BETA, GLV_S}; use crate::curve::secp256k1::Secp256K1; use crate::gadgets::curve::AffinePointTarget; use crate::gadgets::nonnative::NonNativeTarget; @@ -16,7 +16,7 @@ use crate::plonk::circuit_builder::CircuitBuilder; impl, const D: usize> CircuitBuilder { pub fn secp256k1_glv_beta(&mut self) -> NonNativeTarget { - self.constant_nonnative(BETA) + self.constant_nonnative(GLV_BETA) } // TODO: Add decomposition check. @@ -43,7 +43,13 @@ impl, const D: usize> CircuitBuilder { _phantom: PhantomData, }); - // debug_assert!(k1_raw + S * k2_raw == k); + // Check that `k1_raw + GLV_S * k2_raw == k`. + let k1_raw = self.nonnative_conditional_neg(&k1, k1_neg); + let k2_raw = self.nonnative_conditional_neg(&k2, k2_neg); + let s = self.constant_nonnative(GLV_S); + let mut should_be_k = self.mul_nonnative(&s, &k2_raw); + should_be_k = self.add_nonnative(&should_be_k, &k1_raw); + self.connect_nonnative(&should_be_k, &k); (k1, k2, k1_neg, k2_neg) } diff --git a/plonky2/src/gadgets/nonnative.rs b/plonky2/src/gadgets/nonnative.rs index 73bc0ad3..6c483a86 100644 --- a/plonky2/src/gadgets/nonnative.rs +++ b/plonky2/src/gadgets/nonnative.rs @@ -338,6 +338,19 @@ impl, const D: usize> CircuitBuilder { result } + + pub fn nonnative_conditional_neg( + &mut self, + x: &NonNativeTarget, + b: BoolTarget, + ) -> NonNativeTarget { + let not_b = self.not(b); + let neg = self.neg_nonnative(x); + let x_if_true = self.mul_nonnative_by_bool(&neg, b); + let x_if_false = self.mul_nonnative_by_bool(x, not_b); + + self.add_nonnative(&x_if_true, &x_if_false) + } } #[derive(Debug)] From c8d3335bce88da27995045644ab520d4ddcf372f Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 2 Mar 2022 13:37:01 +0100 Subject: [PATCH 10/17] ECDSA verification in 101k gates --- plonky2/src/gadgets/ecdsa.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/plonky2/src/gadgets/ecdsa.rs b/plonky2/src/gadgets/ecdsa.rs index 5f4c4ff1..5bf43b04 100644 --- a/plonky2/src/gadgets/ecdsa.rs +++ b/plonky2/src/gadgets/ecdsa.rs @@ -1,6 +1,9 @@ use std::marker::PhantomData; +use plonky2_field::secp256k1_scalar::Secp256K1Scalar; + use crate::curve::curve_types::Curve; +use crate::curve::secp256k1::Secp256K1; use crate::field::extension_field::Extendable; use crate::gadgets::curve::AffinePointTarget; use crate::gadgets::nonnative::NonNativeTarget; @@ -20,11 +23,11 @@ pub struct ECDSASignatureTarget { } impl, const D: usize> CircuitBuilder { - pub fn verify_message( + pub fn verify_message( &mut self, - msg: NonNativeTarget, - sig: ECDSASignatureTarget, - pk: ECDSAPublicKeyTarget, + msg: NonNativeTarget, + sig: ECDSASignatureTarget, + pk: ECDSAPublicKeyTarget, ) { let ECDSASignatureTarget { r, s } = sig; @@ -34,12 +37,11 @@ impl, const D: usize> CircuitBuilder { let u1 = self.mul_nonnative(&msg, &c); let u2 = self.mul_nonnative(&r, &c); - let g = self.constant_affine_point(C::GENERATOR_AFFINE); - let point1 = self.curve_scalar_mul_windowed(&g, &u1); - let point2 = self.curve_scalar_mul_windowed(&pk.0, &u2); + let point1 = self.fixed_base_curve_mul(&Secp256K1::GENERATOR_AFFINE, &u1); + let point2 = self.glv_mul(&pk.0, &u2); let point = self.curve_add(&point1, &point2); - let x = NonNativeTarget:: { + let x = NonNativeTarget:: { value: point.x.value, _phantom: PhantomData, }; From f6525ed11afd252c0d4faec9346323db2d519a2a Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 3 Mar 2022 04:15:15 +0100 Subject: [PATCH 11/17] Add wide config for ECDSA in < 2^16 gates --- plonky2/src/gadgets/ecdsa.rs | 10 +++++++++- plonky2/src/plonk/circuit_data.rs | 7 +++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/plonky2/src/gadgets/ecdsa.rs b/plonky2/src/gadgets/ecdsa.rs index 5bf43b04..1f4012a6 100644 --- a/plonky2/src/gadgets/ecdsa.rs +++ b/plonky2/src/gadgets/ecdsa.rs @@ -74,7 +74,15 @@ mod tests { type Curve = Secp256K1; - let config = CircuitConfig::standard_ecc_config(); + const WIDE: bool = true; + + let config = if WIDE { + // < 2^16 gates. + CircuitConfig::wide_ecc_config() + } else { + // < 2^17 gates. + CircuitConfig::standard_ecc_config() + }; let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); diff --git a/plonky2/src/plonk/circuit_data.rs b/plonky2/src/plonk/circuit_data.rs index 3d4ee2df..34b38fcf 100644 --- a/plonky2/src/plonk/circuit_data.rs +++ b/plonky2/src/plonk/circuit_data.rs @@ -86,6 +86,13 @@ impl CircuitConfig { } } + pub fn wide_ecc_config() -> Self { + Self { + num_wires: 234, + ..Self::standard_recursion_config() + } + } + pub fn standard_recursion_zk_config() -> Self { CircuitConfig { zero_knowledge: true, From 90df0d9d3accce4e063affe1fb21336cca2fb33b Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 3 Mar 2022 04:19:17 +0100 Subject: [PATCH 12/17] Clippy --- plonky2/src/gadgets/curve_fixed_base.rs | 8 ++++---- plonky2/src/gadgets/glv.rs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/plonky2/src/gadgets/curve_fixed_base.rs b/plonky2/src/gadgets/curve_fixed_base.rs index 70def6bc..4ba8d11e 100644 --- a/plonky2/src/gadgets/curve_fixed_base.rs +++ b/plonky2/src/gadgets/curve_fixed_base.rs @@ -18,8 +18,8 @@ impl, const D: usize> CircuitBuilder { base: &AffinePoint, scalar: &NonNativeTarget, ) -> AffinePointTarget { - let doubled_base = (0..scalar.value.limbs.len() * 8).scan(base.clone(), |acc, _| { - let tmp = acc.clone(); + let doubled_base = (0..scalar.value.limbs.len() * 8).scan(*base, |acc, _| { + let tmp = *acc; for _ in 0..4 { *acc = acc.double(); } @@ -34,11 +34,11 @@ impl, const D: usize> CircuitBuilder { )); let rando = (CurveScalar(hash_0_scalar) * C::GENERATOR_PROJECTIVE).to_affine(); let zero = self.zero(); - let mut result = self.constant_affine_point(rando.clone()); + let mut result = self.constant_affine_point(rando); for (limb, point) in bits.into_iter().zip(doubled_base) { let mul_point = (0..16) .scan(AffinePoint::ZERO, |acc, _| { - let tmp = acc.clone(); + let tmp = *acc; *acc = (point + *acc).to_affine(); Some(tmp) }) diff --git a/plonky2/src/gadgets/glv.rs b/plonky2/src/gadgets/glv.rs index 8447137d..f0c4704b 100644 --- a/plonky2/src/gadgets/glv.rs +++ b/plonky2/src/gadgets/glv.rs @@ -49,7 +49,7 @@ impl, const D: usize> CircuitBuilder { let s = self.constant_nonnative(GLV_S); let mut should_be_k = self.mul_nonnative(&s, &k2_raw); should_be_k = self.add_nonnative(&should_be_k, &k1_raw); - self.connect_nonnative(&should_be_k, &k); + self.connect_nonnative(&should_be_k, k); (k1, k2, k1_neg, k2_neg) } @@ -68,7 +68,7 @@ impl, const D: usize> CircuitBuilder { y: p.y.clone(), }; - let p_neg = self.curve_conditional_neg(&p, k1_neg); + let p_neg = self.curve_conditional_neg(p, k1_neg); let sp_neg = self.curve_conditional_neg(&sp, k2_neg); self.curve_msm(&p_neg, &sp_neg, &k1.value, &k2.value) } From 47523c086a783c202d9e2da6b5ada7a8b853dbbd Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 3 Mar 2022 04:43:04 +0100 Subject: [PATCH 13/17] Minor --- plonky2/src/gadgets/curve_fixed_base.rs | 6 +++--- plonky2/src/gadgets/ecdsa.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plonky2/src/gadgets/curve_fixed_base.rs b/plonky2/src/gadgets/curve_fixed_base.rs index 4ba8d11e..0cbd7a9e 100644 --- a/plonky2/src/gadgets/curve_fixed_base.rs +++ b/plonky2/src/gadgets/curve_fixed_base.rs @@ -15,10 +15,10 @@ impl, const D: usize> CircuitBuilder { // TODO: Benchmark other window sizes. pub fn fixed_base_curve_mul( &mut self, - base: &AffinePoint, + base: AffinePoint, scalar: &NonNativeTarget, ) -> AffinePointTarget { - let doubled_base = (0..scalar.value.limbs.len() * 8).scan(*base, |acc, _| { + let doubled_base = (0..scalar.value.limbs.len() * 8).scan(base, |acc, _| { let tmp = *acc; for _ in 0..4 { *acc = acc.double(); @@ -91,7 +91,7 @@ mod tests { let n_target = builder.add_virtual_nonnative_target::(); pw.set_biguint_target(&n_target.value, &n.to_canonical_biguint()); - let res_target = builder.fixed_base_curve_mul(&g, &n_target); + let res_target = builder.fixed_base_curve_mul(g, &n_target); builder.curve_assert_valid(&res_target); builder.connect_affine_point(&res_target, &res_expected); diff --git a/plonky2/src/gadgets/ecdsa.rs b/plonky2/src/gadgets/ecdsa.rs index 1f4012a6..672700c8 100644 --- a/plonky2/src/gadgets/ecdsa.rs +++ b/plonky2/src/gadgets/ecdsa.rs @@ -37,7 +37,7 @@ impl, const D: usize> CircuitBuilder { let u1 = self.mul_nonnative(&msg, &c); let u2 = self.mul_nonnative(&r, &c); - let point1 = self.fixed_base_curve_mul(&Secp256K1::GENERATOR_AFFINE, &u1); + let point1 = self.fixed_base_curve_mul(Secp256K1::GENERATOR_AFFINE, &u1); let point2 = self.glv_mul(&pk.0, &u2); let point = self.curve_add(&point1, &point2); From 18e341ff18becda2f6fe942bce664ccf1264d37b Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 3 Mar 2022 08:06:21 +0100 Subject: [PATCH 14/17] Comments --- plonky2/src/gadgets/curve_fixed_base.rs | 16 ++++++++++------ plonky2/src/gadgets/curve_msm.rs | 6 +++++- plonky2/src/gadgets/glv.rs | 1 - 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/plonky2/src/gadgets/curve_fixed_base.rs b/plonky2/src/gadgets/curve_fixed_base.rs index 0cbd7a9e..3c470044 100644 --- a/plonky2/src/gadgets/curve_fixed_base.rs +++ b/plonky2/src/gadgets/curve_fixed_base.rs @@ -12,13 +12,13 @@ use crate::plonk::config::{GenericHashOut, Hasher}; impl, const D: usize> CircuitBuilder { /// Do windowed fixed-base scalar multiplication, using a 4-bit window. - // TODO: Benchmark other window sizes. pub fn fixed_base_curve_mul( &mut self, base: AffinePoint, scalar: &NonNativeTarget, ) -> AffinePointTarget { - let doubled_base = (0..scalar.value.limbs.len() * 8).scan(base, |acc, _| { + // Holds `(16^i) * base` for `i=0..scalar.value.limbs.len() * 8`. + let scaled_base = (0..scalar.value.limbs.len() * 8).scan(base, |acc, _| { let tmp = *acc; for _ in 0..4 { *acc = acc.double(); @@ -26,17 +26,20 @@ impl, const D: usize> CircuitBuilder { Some(tmp) }); - let bits = self.split_nonnative_to_4_bit_limbs(scalar); + let limbs = self.split_nonnative_to_4_bit_limbs(scalar); let hash_0 = KeccakHash::<32>::hash_no_pad(&[F::ZERO]); let hash_0_scalar = C::ScalarField::from_biguint(BigUint::from_bytes_le( &GenericHashOut::::to_bytes(&hash_0), )); let rando = (CurveScalar(hash_0_scalar) * C::GENERATOR_PROJECTIVE).to_affine(); + let zero = self.zero(); let mut result = self.constant_affine_point(rando); - for (limb, point) in bits.into_iter().zip(doubled_base) { - let mul_point = (0..16) + // `s * P = sum s_i * P_i` with `P_i = (16^i) * P` and `s = sum s_i * (16^i)`. + for (limb, point) in limbs.into_iter().zip(scaled_base) { + // Holds `t * P_i` for `p=0..16`. + let muls_point = (0..16) .scan(AffinePoint::ZERO, |acc, _| { let tmp = *acc; *acc = (point + *acc).to_affine(); @@ -46,7 +49,8 @@ impl, const D: usize> CircuitBuilder { .collect::>(); let is_zero = self.is_equal(limb, zero); let should_add = self.not(is_zero); - let r = self.random_access_curve_points(limb, mul_point); + // `r = s_i * P_i` + let r = self.random_access_curve_points(limb, muls_point); result = self.curve_conditional_add(&result, &r, should_add); } diff --git a/plonky2/src/gadgets/curve_msm.rs b/plonky2/src/gadgets/curve_msm.rs index df13e8f3..5d505c4d 100644 --- a/plonky2/src/gadgets/curve_msm.rs +++ b/plonky2/src/gadgets/curve_msm.rs @@ -11,7 +11,10 @@ use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::config::{GenericHashOut, Hasher}; impl, const D: usize> CircuitBuilder { - /// Computes `n*p + m*q`. + /// Computes `n*p + m*q` using windowed MSM, with a 2-bit window. + /// See Algorithm 9.23 in Handbook of Elliptic and Hyperelliptic Curve Cryptography for a + /// description. + /// Note: Doesn't work if `p == q`. pub fn curve_msm( &mut self, p: &AffinePointTarget, @@ -32,6 +35,7 @@ impl, const D: usize> CircuitBuilder { let rando_t = self.constant_affine_point(rando); let neg_rando = self.constant_affine_point(-rando); + // Precomputes `precomputation[i + 4*j] = i*p + j*q` for `i,j=0..4`. let mut precomputation = vec![p.clone(); 16]; let mut cur_p = rando_t.clone(); let mut cur_q = rando_t.clone(); diff --git a/plonky2/src/gadgets/glv.rs b/plonky2/src/gadgets/glv.rs index f0c4704b..4bc3efd6 100644 --- a/plonky2/src/gadgets/glv.rs +++ b/plonky2/src/gadgets/glv.rs @@ -19,7 +19,6 @@ impl, const D: usize> CircuitBuilder { self.constant_nonnative(GLV_BETA) } - // TODO: Add decomposition check. pub fn decompose_secp256k1_scalar( &mut self, k: &NonNativeTarget, From 5febea778be1efb0f53225dff0bca327a561e777 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 3 Mar 2022 08:14:56 +0100 Subject: [PATCH 15/17] Fixes --- plonky2/src/gadgets/curve_fixed_base.rs | 4 +- plonky2/src/gadgets/curve_msm.rs | 116 +----------------------- plonky2/src/iop/generator.rs | 62 ------------- 3 files changed, 3 insertions(+), 179 deletions(-) diff --git a/plonky2/src/gadgets/curve_fixed_base.rs b/plonky2/src/gadgets/curve_fixed_base.rs index 3c470044..e248d951 100644 --- a/plonky2/src/gadgets/curve_fixed_base.rs +++ b/plonky2/src/gadgets/curve_fixed_base.rs @@ -11,7 +11,7 @@ use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::config::{GenericHashOut, Hasher}; impl, const D: usize> CircuitBuilder { - /// Do windowed fixed-base scalar multiplication, using a 4-bit window. + /// Compute windowed fixed-base scalar multiplication, using a 4-bit window. pub fn fixed_base_curve_mul( &mut self, base: AffinePoint, @@ -38,7 +38,7 @@ impl, const D: usize> CircuitBuilder { let mut result = self.constant_affine_point(rando); // `s * P = sum s_i * P_i` with `P_i = (16^i) * P` and `s = sum s_i * (16^i)`. for (limb, point) in limbs.into_iter().zip(scaled_base) { - // Holds `t * P_i` for `p=0..16`. + // `muls_point[t] = t * P_i` for `t=0..16`. let muls_point = (0..16) .scan(AffinePoint::ZERO, |acc, _| { let tmp = *acc; diff --git a/plonky2/src/gadgets/curve_msm.rs b/plonky2/src/gadgets/curve_msm.rs index 5d505c4d..99aa0f36 100644 --- a/plonky2/src/gadgets/curve_msm.rs +++ b/plonky2/src/gadgets/curve_msm.rs @@ -78,14 +78,10 @@ impl, const D: usize> CircuitBuilder { #[cfg(test)] mod tests { - use std::str::FromStr; - use anyhow::Result; - use num::BigUint; - use plonky2_field::secp256k1_base::Secp256K1Base; use plonky2_field::secp256k1_scalar::Secp256K1Scalar; - use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar}; + use crate::curve::curve_types::{Curve, CurveScalar}; use crate::curve::secp256k1::Secp256K1; use crate::field::field_types::Field; use crate::iop::witness::PartialWitness; @@ -133,114 +129,4 @@ mod tests { verify(proof, &data.verifier_only, &data.common) } - - #[test] - fn test_naive_msm() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - - let config = CircuitConfig::standard_ecc_config(); - - let pw = PartialWitness::new(); - let mut builder = CircuitBuilder::::new(config); - - let p = - (CurveScalar(Secp256K1Scalar::rand()) * Secp256K1::GENERATOR_PROJECTIVE).to_affine(); - let q = - (CurveScalar(Secp256K1Scalar::rand()) * Secp256K1::GENERATOR_PROJECTIVE).to_affine(); - let n = Secp256K1Scalar::rand(); - let m = Secp256K1Scalar::rand(); - - let res = - (CurveScalar(n) * p.to_projective() + CurveScalar(m) * q.to_projective()).to_affine(); - let res_expected = builder.constant_affine_point(res); - builder.curve_assert_valid(&res_expected); - - let p_target = builder.constant_affine_point(p); - let q_target = builder.constant_affine_point(q); - let n_target = builder.constant_nonnative(n); - let m_target = builder.constant_nonnative(m); - - let res0_target = builder.curve_scalar_mul_windowed(&p_target, &n_target); - let res1_target = builder.curve_scalar_mul_windowed(&q_target, &m_target); - let res_target = builder.curve_add(&res0_target, &res1_target); - builder.curve_assert_valid(&res_target); - - builder.connect_affine_point(&res_target, &res_expected); - - dbg!(builder.num_gates()); - let data = builder.build::(); - let proof = data.prove(pw).unwrap(); - - verify(proof, &data.verifier_only, &data.common) - } - - #[test] - fn test_curve_lul() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - - let config = CircuitConfig::standard_ecc_config(); - - let pw = PartialWitness::new(); - let mut builder = CircuitBuilder::::new(config); - - let p = AffinePoint:: { - x: Secp256K1Base::from_biguint( - BigUint::from_str( - "95702873347299649035220040874584348285675823985309557645567012532974768144045", - ) - .unwrap(), - ), - y: Secp256K1Base::from_biguint( - BigUint::from_str( - "34849299245821426255020320369755722155634282348110887335812955146294938249053", - ) - .unwrap(), - ), - zero: false, - }; - let q = AffinePoint:: { - x: Secp256K1Base::from_biguint( - BigUint::from_str( - "66037057977021147605301350925941983227524093291368248236634649161657340356645", - ) - .unwrap(), - ), - y: Secp256K1Base::from_biguint( - BigUint::from_str( - "80942789991494769168550664638932185697635702317529676703644628861613896422610", - ) - .unwrap(), - ), - zero: false, - }; - - let n = BigUint::from_str("89874493710619023150462632713212469930").unwrap(); - let m = BigUint::from_str("76073901947022186525975758425319149118").unwrap(); - - let res = (CurveScalar(Secp256K1Scalar::from_biguint(n.clone())) * p.to_projective() - + CurveScalar(Secp256K1Scalar::from_biguint(m.clone())) * q.to_projective()) - .to_affine(); - let res_expected = builder.constant_affine_point(res); - builder.curve_assert_valid(&res_expected); - - let p_target = builder.constant_affine_point(p); - let q_target = builder.constant_affine_point(q); - let n_target = builder.constant_biguint(&n); - let m_target = builder.constant_biguint(&m); - - let res_target = builder.curve_msm(&p_target, &q_target, &n_target, &m_target); - builder.curve_assert_valid(&res_target); - - builder.connect_affine_point(&res_target, &res_expected); - - dbg!(builder.num_gates()); - let data = builder.build::(); - let proof = data.prove(pw).unwrap(); - - verify(proof, &data.verifier_only, &data.common) - } } diff --git a/plonky2/src/iop/generator.rs b/plonky2/src/iop/generator.rs index 4dcd11da..1569e889 100644 --- a/plonky2/src/iop/generator.rs +++ b/plonky2/src/iop/generator.rs @@ -89,68 +89,6 @@ pub(crate) fn generate_partial_witness< } pending_generator_indices = next_pending_generator_indices; - // for t in [ - // Target::VirtualTarget { index: 57934 }, - // Target::VirtualTarget { index: 57935 }, - // Target::VirtualTarget { index: 57936 }, - // Target::VirtualTarget { index: 57937 }, - // Target::VirtualTarget { index: 57938 }, - // Target::VirtualTarget { index: 57939 }, - // Target::VirtualTarget { index: 57940 }, - // Target::VirtualTarget { index: 57941 }, - // ] { - // if let Some(v) = witness.try_get_target(t) { - // println!("a {}", v); - // } - // } - // for t in [ - // Target::VirtualTarget { index: 57952 }, - // Target::VirtualTarget { index: 57953 }, - // Target::VirtualTarget { index: 57954 }, - // Target::VirtualTarget { index: 57955 }, - // Target::VirtualTarget { index: 57956 }, - // Target::VirtualTarget { index: 57957 }, - // Target::VirtualTarget { index: 57958 }, - // Target::VirtualTarget { index: 57959 }, - // ] { - // if let Some(v) = witness.try_get_target(t) { - // println!("b {}", v); - // } - // } - // - // let t = Target::Wire(Wire { - // gate: 141_857, - // input: 8, - // }); - // if let Some(v) = witness.try_get_target(t) { - // println!("prod_exp {}", v); - // } - // let t = Target::Wire(Wire { - // gate: 141_863, - // input: 22, - // }); - // if let Some(v) = witness.try_get_target(t) { - // println!("prod act {}", v); - // } - // let t = Target::Wire(Wire { gate: 9, input: 3 }); - // if let Some(v) = witness.try_get_target(t) { - // println!("modulus {}", v); - // } - // let t = Target::VirtualTarget { index: 57_976 }; - // if let Some(v) = witness.try_get_target(t) { - // println!("overflow {}", v); - // } - // let t = Target::Wire(Wire { - // gate: 141_885, - // input: 8, - // }); - // if let Some(v) = witness.try_get_target(t) { - // println!("mod time ov {}", v); - // } - // let t = Target::VirtualTarget { index: 57_968 }; - // if let Some(v) = witness.try_get_target(t) { - // println!("prod {}", v); - // } } assert_eq!( From 3a68a458c4638f575625b9db55377902981b8bb0 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 3 Mar 2022 08:44:27 +0100 Subject: [PATCH 16/17] Ignore large tests --- plonky2/src/gadgets/curve_fixed_base.rs | 1 + plonky2/src/gadgets/curve_msm.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/plonky2/src/gadgets/curve_fixed_base.rs b/plonky2/src/gadgets/curve_fixed_base.rs index e248d951..f28e45d1 100644 --- a/plonky2/src/gadgets/curve_fixed_base.rs +++ b/plonky2/src/gadgets/curve_fixed_base.rs @@ -75,6 +75,7 @@ mod tests { use crate::plonk::verifier::verify; #[test] + #[ignore] fn test_fixed_base() -> Result<()> { const D: usize = 2; type C = PoseidonGoldilocksConfig; diff --git a/plonky2/src/gadgets/curve_msm.rs b/plonky2/src/gadgets/curve_msm.rs index 99aa0f36..8d019b3a 100644 --- a/plonky2/src/gadgets/curve_msm.rs +++ b/plonky2/src/gadgets/curve_msm.rs @@ -91,6 +91,7 @@ mod tests { use crate::plonk::verifier::verify; #[test] + #[ignore] fn test_curve_msm() -> Result<()> { const D: usize = 2; type C = PoseidonGoldilocksConfig; From 954eaf16f26dd229f558a438c746ffe949a51147 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Sat, 5 Mar 2022 02:36:08 +0100 Subject: [PATCH 17/17] PR feedback --- plonky2/src/gadgets/curve_msm.rs | 12 ++++++------ plonky2/src/gadgets/ecdsa.rs | 26 +++++++++++++------------- plonky2/src/gadgets/glv.rs | 2 +- plonky2/src/gadgets/split_nonnative.rs | 8 ++++++-- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/plonky2/src/gadgets/curve_msm.rs b/plonky2/src/gadgets/curve_msm.rs index 8d019b3a..fba7c229 100644 --- a/plonky2/src/gadgets/curve_msm.rs +++ b/plonky2/src/gadgets/curve_msm.rs @@ -3,8 +3,8 @@ use plonky2_field::extension_field::Extendable; use crate::curve::curve_types::{Curve, CurveScalar}; use crate::field::field_types::Field; -use crate::gadgets::biguint::BigUintTarget; use crate::gadgets::curve::AffinePointTarget; +use crate::gadgets::nonnative::NonNativeTarget; use crate::hash::hash_types::RichField; use crate::hash::keccak::KeccakHash; use crate::plonk::circuit_builder::CircuitBuilder; @@ -19,11 +19,11 @@ impl, const D: usize> CircuitBuilder { &mut self, p: &AffinePointTarget, q: &AffinePointTarget, - n: &BigUintTarget, - m: &BigUintTarget, + n: &NonNativeTarget, + m: &NonNativeTarget, ) -> AffinePointTarget { - let limbs_n = self.split_biguint_to_2_bit_limbs(n); - let limbs_m = self.split_biguint_to_2_bit_limbs(m); + let limbs_n = self.split_nonnative_to_2_bit_limbs(n); + let limbs_m = self.split_nonnative_to_2_bit_limbs(m); assert_eq!(limbs_n.len(), limbs_m.len()); let num_limbs = limbs_n.len(); @@ -119,7 +119,7 @@ mod tests { let n_target = builder.constant_nonnative(n); let m_target = builder.constant_nonnative(m); - let res_target = builder.curve_msm(&p_target, &q_target, &n_target.value, &m_target.value); + let res_target = builder.curve_msm(&p_target, &q_target, &n_target, &m_target); builder.curve_assert_valid(&res_target); builder.connect_affine_point(&res_target, &res_expected); diff --git a/plonky2/src/gadgets/ecdsa.rs b/plonky2/src/gadgets/ecdsa.rs index 672700c8..a376e56a 100644 --- a/plonky2/src/gadgets/ecdsa.rs +++ b/plonky2/src/gadgets/ecdsa.rs @@ -65,25 +65,13 @@ mod tests { use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; use crate::plonk::verifier::verify; - #[test] - #[ignore] - fn test_ecdsa_circuit() -> Result<()> { + fn test_ecdsa_circuit_with_config(config: CircuitConfig) -> Result<()> { const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; type Curve = Secp256K1; - const WIDE: bool = true; - - let config = if WIDE { - // < 2^16 gates. - CircuitConfig::wide_ecc_config() - } else { - // < 2^17 gates. - CircuitConfig::standard_ecc_config() - }; - let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); @@ -112,4 +100,16 @@ mod tests { let proof = data.prove(pw).unwrap(); verify(proof, &data.verifier_only, &data.common) } + + #[test] + #[ignore] + fn test_ecdsa_circuit_narrow() -> Result<()> { + test_ecdsa_circuit_with_config(CircuitConfig::standard_ecc_config()) + } + + #[test] + #[ignore] + fn test_ecdsa_circuit_wide() -> Result<()> { + test_ecdsa_circuit_with_config(CircuitConfig::wide_ecc_config()) + } } diff --git a/plonky2/src/gadgets/glv.rs b/plonky2/src/gadgets/glv.rs index 4bc3efd6..8a0179ec 100644 --- a/plonky2/src/gadgets/glv.rs +++ b/plonky2/src/gadgets/glv.rs @@ -69,7 +69,7 @@ impl, const D: usize> CircuitBuilder { let p_neg = self.curve_conditional_neg(p, k1_neg); let sp_neg = self.curve_conditional_neg(&sp, k2_neg); - self.curve_msm(&p_neg, &sp_neg, &k1.value, &k2.value) + self.curve_msm(&p_neg, &sp_neg, &k1, &k2) } } diff --git a/plonky2/src/gadgets/split_nonnative.rs b/plonky2/src/gadgets/split_nonnative.rs index becf1177..18fc0264 100644 --- a/plonky2/src/gadgets/split_nonnative.rs +++ b/plonky2/src/gadgets/split_nonnative.rs @@ -35,8 +35,12 @@ impl, const D: usize> CircuitBuilder { .collect() } - pub fn split_biguint_to_2_bit_limbs(&mut self, val: &BigUintTarget) -> Vec { - val.limbs + pub fn split_nonnative_to_2_bit_limbs( + &mut self, + val: &NonNativeTarget, + ) -> Vec { + val.value + .limbs .iter() .flat_map(|&l| self.split_le_base::<4>(l.0, 16)) .collect()