From 37562943220bbad0d3cadf089e287724d39566df Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 30 Nov 2021 15:32:27 -0800 Subject: [PATCH 01/36] ECDSA rebase --- plonky2/src/curve/ecdsa.rs | 111 +++++++++++++++++++++++++++++++++++++ plonky2/src/curve/mod.rs | 1 + plonky2/src/gadgets/mod.rs | 1 + waksman/src/ecdsa.rs | 53 ++++++++++++++++++ 4 files changed, 166 insertions(+) create mode 100644 plonky2/src/curve/ecdsa.rs create mode 100644 waksman/src/ecdsa.rs diff --git a/plonky2/src/curve/ecdsa.rs b/plonky2/src/curve/ecdsa.rs new file mode 100644 index 00000000..ca5b27ad --- /dev/null +++ b/plonky2/src/curve/ecdsa.rs @@ -0,0 +1,111 @@ +use std::ops::Mul; + +use itertools::unfold; + +use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar}; +use crate::field::field_types::{Field, RichField}; +use crate::hash::hashing::hash_n_to_1; + +pub struct ECDSASignature { + pub r: C::ScalarField, + pub s: C::ScalarField, +} + +pub struct ECDSASecretKey(C::ScalarField); +pub struct ECDSAPublicKey(AffinePoint); + +pub fn base_to_scalar(x: C::BaseField) -> C::ScalarField { + C::ScalarField::from_biguint(x.to_biguint()) +} + +pub fn scalar_to_base(x: C::ScalarField) -> C::BaseField { + C::BaseField::from_biguint(x.to_biguint()) +} + +pub fn hash_to_scalar(msg: F, num_bits: usize) -> C::ScalarField { + let h_bits = hash_to_bits(msg, num_bits); + let h_u32 = h_bits + .iter() + .zip(0..32) + .fold(0u32, |acc, (&bit, pow)| acc + (bit as u32) * (2 << pow)); + C::ScalarField::from_canonical_u32(h_u32) +} + +pub fn hash_to_bits(x: F, num_bits: usize) -> Vec { + let hashed = hash_n_to_1(vec![x], true); + + let mut val = hashed.to_canonical_u64(); + unfold((), move |_| { + let ret = val % 2 != 0; + val /= 2; + Some(ret) + }) + .take(num_bits) + .collect() +} + +pub fn sign_message(msg: F, sk: ECDSASecretKey) -> ECDSASignature { + let h = hash_to_scalar::(msg, 32); + println!("SIGNING h: {:?}", h); + + let k = C::ScalarField::rand(); + let rr = (CurveScalar(k) * C::GENERATOR_PROJECTIVE).to_affine(); + let r = base_to_scalar::(rr.x); + let s = k.inverse() * (h + r * sk.0); + + println!("SIGNING s: {:?}", s); + println!("SIGNING s^-1: {:?}", s.inverse()); + println!("SIGNING s^-1^-1: {:?}", s.inverse().inverse()); + + ECDSASignature { r, s } +} + +pub fn verify_message( + msg: F, + sig: ECDSASignature, + pk: ECDSAPublicKey, +) -> bool { + let ECDSASignature { r, s } = sig; + + let h = hash_to_scalar::(msg, 32); + println!("VERIFYING h: {:?}", h); + + let c = s.inverse(); + + println!("VERIFYING c^-1: {:?}", c.inverse()); + let u1 = h * c; + let u2 = r * c; + + let g = C::GENERATOR_PROJECTIVE; + let point_proj = CurveScalar(u1) * g + CurveScalar(u2) * pk.0.to_projective(); + let point = point_proj.to_affine(); + + let x = base_to_scalar::(point.x); + r == x +} + +mod tests { + use anyhow::Result; + + use crate::curve::curve_types::{Curve, CurveScalar}; + use crate::curve::ecdsa::{sign_message, verify_message, ECDSAPublicKey, ECDSASecretKey}; + use crate::curve::secp256k1::Secp256K1; + use crate::field::field_types::Field; + use crate::field::goldilocks_field::GoldilocksField; + use crate::field::secp256k1_scalar::Secp256K1Scalar; + use crate::plonk::circuit_data::CircuitConfig; + + #[test] + fn test_ecdsa_native() { + type F = GoldilocksField; + type C = Secp256K1; + + let msg = F::rand(); + let sk = ECDSASecretKey(Secp256K1Scalar::rand()); + let pk = ECDSAPublicKey((CurveScalar(sk.0) * C::GENERATOR_PROJECTIVE).to_affine()); + + let sig = sign_message(msg, sk); + let result = verify_message(msg, sig, pk); + assert!(result); + } +} diff --git a/plonky2/src/curve/mod.rs b/plonky2/src/curve/mod.rs index d31e373e..8dd6f0d6 100644 --- a/plonky2/src/curve/mod.rs +++ b/plonky2/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/plonky2/src/gadgets/mod.rs b/plonky2/src/gadgets/mod.rs index b73e2a7f..ec4d1263 100644 --- a/plonky2/src/gadgets/mod.rs +++ b/plonky2/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 interpolation; pub mod multiple_comparison; diff --git a/waksman/src/ecdsa.rs b/waksman/src/ecdsa.rs new file mode 100644 index 00000000..47068ee8 --- /dev/null +++ b/waksman/src/ecdsa.rs @@ -0,0 +1,53 @@ +pub struct ECDSASecretKeyTarget(NonNativeTarget); +pub struct ECDSAPublicKeyTarget(AffinePointTarget); + +pub struct ECDSASignatureTarget { + pub r: NonNativeTarget, + pub s: NonNativeTarget, +} + + + +impl, const D: usize> CircuitBuilder { + +} + +mod tests { + use std::ops::{Mul, Neg}; + + use anyhow::Result; + + 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; + use crate::plonk::verifier::verify; + + /*#[test] + fn test_curve_point_is_valid() -> 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 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(&neg_g_target); + + let data = builder.build(); + let proof = data.prove(pw).unwrap(); + + verify(proof, &data.verifier_only, &data.common) + }*/ +} From f55948a6212129860450460bb27efff491128778 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 1 Dec 2021 09:31:10 -0800 Subject: [PATCH 02/36] removed debugging prints --- plonky2/src/curve/ecdsa.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/plonky2/src/curve/ecdsa.rs b/plonky2/src/curve/ecdsa.rs index ca5b27ad..177f08ed 100644 --- a/plonky2/src/curve/ecdsa.rs +++ b/plonky2/src/curve/ecdsa.rs @@ -46,17 +46,12 @@ pub fn hash_to_bits(x: F, num_bits: usize) -> Vec { pub fn sign_message(msg: F, sk: ECDSASecretKey) -> ECDSASignature { let h = hash_to_scalar::(msg, 32); - println!("SIGNING h: {:?}", h); let k = C::ScalarField::rand(); let rr = (CurveScalar(k) * C::GENERATOR_PROJECTIVE).to_affine(); let r = base_to_scalar::(rr.x); let s = k.inverse() * (h + r * sk.0); - println!("SIGNING s: {:?}", s); - println!("SIGNING s^-1: {:?}", s.inverse()); - println!("SIGNING s^-1^-1: {:?}", s.inverse().inverse()); - ECDSASignature { r, s } } @@ -68,11 +63,8 @@ pub fn verify_message( let ECDSASignature { r, s } = sig; let h = hash_to_scalar::(msg, 32); - println!("VERIFYING h: {:?}", h); let c = s.inverse(); - - println!("VERIFYING c^-1: {:?}", c.inverse()); let u1 = h * c; let u2 = r * c; From b796c73e49ba01e577aa0ac9b68b351a23b85a7e Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 2 Dec 2021 10:27:47 -0800 Subject: [PATCH 03/36] ECDSA gadget and test --- plonky2/src/curve/ecdsa.rs | 28 ++++----- plonky2/src/gadgets/nonnative.rs | 2 +- waksman/src/ecdsa.rs | 99 +++++++++++++++++++++++++++----- 3 files changed, 97 insertions(+), 32 deletions(-) diff --git a/plonky2/src/curve/ecdsa.rs b/plonky2/src/curve/ecdsa.rs index 177f08ed..0ed777d9 100644 --- a/plonky2/src/curve/ecdsa.rs +++ b/plonky2/src/curve/ecdsa.rs @@ -1,5 +1,3 @@ -use std::ops::Mul; - use itertools::unfold; use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar}; @@ -11,8 +9,8 @@ pub struct ECDSASignature { pub s: C::ScalarField, } -pub struct ECDSASecretKey(C::ScalarField); -pub struct ECDSAPublicKey(AffinePoint); +pub struct ECDSASecretKey(pub C::ScalarField); +pub struct ECDSAPublicKey(pub AffinePoint); pub fn base_to_scalar(x: C::BaseField) -> C::ScalarField { C::ScalarField::from_biguint(x.to_biguint()) @@ -22,15 +20,6 @@ pub fn scalar_to_base(x: C::ScalarField) -> C::BaseField { C::BaseField::from_biguint(x.to_biguint()) } -pub fn hash_to_scalar(msg: F, num_bits: usize) -> C::ScalarField { - let h_bits = hash_to_bits(msg, num_bits); - let h_u32 = h_bits - .iter() - .zip(0..32) - .fold(0u32, |acc, (&bit, pow)| acc + (bit as u32) * (2 << pow)); - C::ScalarField::from_canonical_u32(h_u32) -} - pub fn hash_to_bits(x: F, num_bits: usize) -> Vec { let hashed = hash_n_to_1(vec![x], true); @@ -44,6 +33,15 @@ pub fn hash_to_bits(x: F, num_bits: usize) -> Vec { .collect() } +pub fn hash_to_scalar(x: F, num_bits: usize) -> C::ScalarField { + let h_bits = hash_to_bits(x, num_bits); + let h_u32 = h_bits + .iter() + .zip(0..32) + .fold(0u32, |acc, (&bit, pow)| acc + (bit as u32) * (2 << pow)); + C::ScalarField::from_canonical_u32(h_u32) +} + pub fn sign_message(msg: F, sk: ECDSASecretKey) -> ECDSASignature { let h = hash_to_scalar::(msg, 32); @@ -76,16 +74,14 @@ pub fn verify_message( r == x } +#[cfg(test)] mod tests { - use anyhow::Result; - use crate::curve::curve_types::{Curve, CurveScalar}; use crate::curve::ecdsa::{sign_message, verify_message, ECDSAPublicKey, ECDSASecretKey}; use crate::curve::secp256k1::Secp256K1; use crate::field::field_types::Field; use crate::field::goldilocks_field::GoldilocksField; use crate::field::secp256k1_scalar::Secp256K1Scalar; - use crate::plonk::circuit_data::CircuitConfig; #[test] fn test_ecdsa_native() { diff --git a/plonky2/src/gadgets/nonnative.rs b/plonky2/src/gadgets/nonnative.rs index 16fd022e..824d851c 100644 --- a/plonky2/src/gadgets/nonnative.rs +++ b/plonky2/src/gadgets/nonnative.rs @@ -15,7 +15,7 @@ use crate::plonk::circuit_builder::CircuitBuilder; #[derive(Clone, Debug)] pub struct NonNativeTarget { pub(crate) value: BigUintTarget, - _phantom: PhantomData, + pub(crate) _phantom: PhantomData, } impl, const D: usize> CircuitBuilder { diff --git a/waksman/src/ecdsa.rs b/waksman/src/ecdsa.rs index 47068ee8..199a8c56 100644 --- a/waksman/src/ecdsa.rs +++ b/waksman/src/ecdsa.rs @@ -1,3 +1,15 @@ +use std::marker::PhantomData; + +use crate::curve::curve_types::Curve; +use crate::field::extension_field::Extendable; +use crate::field::field_types::RichField; +use crate::gadgets::arithmetic_u32::U32Target; +use crate::gadgets::biguint::BigUintTarget; +use crate::gadgets::curve::AffinePointTarget; +use crate::gadgets::nonnative::NonNativeTarget; +use crate::iop::target::{BoolTarget, Target}; +use crate::plonk::circuit_builder::CircuitBuilder; + pub struct ECDSASecretKeyTarget(NonNativeTarget); pub struct ECDSAPublicKeyTarget(AffinePointTarget); @@ -6,48 +18,105 @@ pub struct ECDSASignatureTarget { pub s: NonNativeTarget, } - - impl, const D: usize> CircuitBuilder { - + pub fn hash_to_bits(&mut self, x: Target, num_bits: usize) -> Vec { + let inputs = vec![x]; + let hashed = self.hash_n_to_m(inputs, 1, true)[0]; + self.split_le(hashed, num_bits) + } + + pub fn hash_to_scalar(&mut self, x: Target, num_bits: usize) -> NonNativeTarget { + let h_bits = self.hash_to_bits(x, num_bits); + + let two = self.two(); + let mut rev_bits = h_bits.iter().rev(); + let mut sum = rev_bits.next().unwrap().target; + for &bit in rev_bits { + sum = self.mul_add(two, sum, bit.target); + } + let limbs = vec![U32Target(sum)]; + let value = BigUintTarget { + limbs, + }; + + NonNativeTarget { + value, + _phantom: PhantomData, + } + } + + pub fn verify_message(&mut self, msg: Target, sig: ECDSASignatureTarget, pk: ECDSAPublicKeyTarget) { + let ECDSASignatureTarget { r, s } = sig; + + let h = self.hash_to_scalar::(msg, 32); + + let c = self.inv_nonnative(&s); + let u1 = self.mul_nonnative(&h, &c); + 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 point = self.curve_add(&point1, &point2); + + let x = NonNativeTarget:: { + value: point.x.value, + _phantom: PhantomData, + }; + self.connect_nonnative(&r, &x); + } } +#[cfg(test)] mod tests { - use std::ops::{Mul, Neg}; - use anyhow::Result; - use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar}; + use crate::curve::curve_types::{Curve, CurveScalar}; + use crate::curve::ecdsa::{ECDSAPublicKey, ECDSASecretKey, ECDSASignature, sign_message}; 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::gadgets::ecdsa::{ECDSAPublicKeyTarget, ECDSASignatureTarget}; 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_point_is_valid() -> Result<()> { + #[test] + fn test_ecdsa_circuit() -> Result<()> { type F = GoldilocksField; const D: usize = 4; + type C = Secp256K1; let config = CircuitConfig::standard_recursion_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 msg = F::rand(); + let msg_target = builder.constant(msg); + + let sk = ECDSASecretKey::(Secp256K1Scalar::rand()); + let pk = ECDSAPublicKey((CurveScalar(sk.0) * C::GENERATOR_PROJECTIVE).to_affine()); - builder.curve_assert_valid(&g_target); - builder.curve_assert_valid(&neg_g_target); + let pk_target = ECDSAPublicKeyTarget(builder.constant_affine_point(pk.0)); + + let sig = sign_message(msg, sk); + + let ECDSASignature { r, s } = sig; + let r_target = builder.constant_nonnative(r); + let s_target = builder.constant_nonnative(s); + let sig_target = ECDSASignatureTarget { + r: r_target, + s: s_target, + }; + + builder.verify_message(msg_target, sig_target, pk_target); let data = builder.build(); let proof = data.prove(pw).unwrap(); verify(proof, &data.verifier_only, &data.common) - }*/ + } } From 9cac6d3a4d6943a4333d2ea10b256d2a9b407e06 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 2 Dec 2021 10:28:00 -0800 Subject: [PATCH 04/36] fmt --- waksman/src/ecdsa.rs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/waksman/src/ecdsa.rs b/waksman/src/ecdsa.rs index 199a8c56..362fbc6d 100644 --- a/waksman/src/ecdsa.rs +++ b/waksman/src/ecdsa.rs @@ -25,9 +25,13 @@ impl, const D: usize> CircuitBuilder { self.split_le(hashed, num_bits) } - pub fn hash_to_scalar(&mut self, x: Target, num_bits: usize) -> NonNativeTarget { + pub fn hash_to_scalar( + &mut self, + x: Target, + num_bits: usize, + ) -> NonNativeTarget { let h_bits = self.hash_to_bits(x, num_bits); - + let two = self.two(); let mut rev_bits = h_bits.iter().rev(); let mut sum = rev_bits.next().unwrap().target; @@ -35,9 +39,7 @@ impl, const D: usize> CircuitBuilder { sum = self.mul_add(two, sum, bit.target); } let limbs = vec![U32Target(sum)]; - let value = BigUintTarget { - limbs, - }; + let value = BigUintTarget { limbs }; NonNativeTarget { value, @@ -45,7 +47,12 @@ impl, const D: usize> CircuitBuilder { } } - pub fn verify_message(&mut self, msg: Target, sig: ECDSASignatureTarget, pk: ECDSAPublicKeyTarget) { + pub fn verify_message( + &mut self, + msg: Target, + sig: ECDSASignatureTarget, + pk: ECDSAPublicKeyTarget, + ) { let ECDSASignatureTarget { r, s } = sig; let h = self.hash_to_scalar::(msg, 32); @@ -72,7 +79,7 @@ mod tests { use anyhow::Result; use crate::curve::curve_types::{Curve, CurveScalar}; - use crate::curve::ecdsa::{ECDSAPublicKey, ECDSASecretKey, ECDSASignature, sign_message}; + use crate::curve::ecdsa::{sign_message, ECDSAPublicKey, ECDSASecretKey, ECDSASignature}; use crate::curve::secp256k1::Secp256K1; use crate::field::field_types::Field; use crate::field::goldilocks_field::GoldilocksField; @@ -96,7 +103,7 @@ mod tests { let msg = F::rand(); let msg_target = builder.constant(msg); - + let sk = ECDSASecretKey::(Secp256K1Scalar::rand()); let pk = ECDSAPublicKey((CurveScalar(sk.0) * C::GENERATOR_PROJECTIVE).to_affine()); From 08fa4031badd0ec42355404ed70ee6b4886c8f5f Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 20 Jan 2022 16:06:00 -0800 Subject: [PATCH 05/36] ECDSA merge --- plonky2/src/gadgets/arithmetic_u32.rs | 82 +++++++ {waksman/src => plonky2/src/gadgets}/ecdsa.rs | 0 plonky2/src/gadgets/mod.rs | 1 + plonky2/src/gadgets/multiple_comparison.rs | 9 +- plonky2/src/gadgets/nonnative.rs | 206 ++++++++++++++++-- plonky2/src/gates/mod.rs | 2 + plonky2/src/iop/generator.rs | 7 +- plonky2/src/plonk/circuit_builder.rs | 82 ++++++- 8 files changed, 370 insertions(+), 19 deletions(-) rename {waksman/src => plonky2/src/gadgets}/ecdsa.rs (100%) diff --git a/plonky2/src/gadgets/arithmetic_u32.rs b/plonky2/src/gadgets/arithmetic_u32.rs index 6116f61b..0fbd076f 100644 --- a/plonky2/src/gadgets/arithmetic_u32.rs +++ b/plonky2/src/gadgets/arithmetic_u32.rs @@ -4,6 +4,7 @@ use crate::gates::arithmetic_u32::U32ArithmeticGate; use crate::gates::subtraction_u32::U32SubtractionGate; use crate::hash::hash_types::RichField; use crate::iop::target::Target; +use crate::iop::witness::{PartitionWitness, Witness}; use crate::plonk::circuit_builder::CircuitBuilder; #[derive(Clone, Copy, Debug)] @@ -152,4 +153,85 @@ impl, const D: usize> CircuitBuilder { (output_result, output_borrow) } + + pub fn split_to_u32(&mut self, x: Target) -> (U32Target, U32Target) { + let low = self.add_virtual_u32_target(); + let high = self.add_virtual_u32_target(); + + let base = self.constant(F::from_canonical_u64(1u64 << 32)); + let combined = self.mul_add(high.0, base, low.0); + self.connect(x, combined); + + self.add_simple_generator(SplitToU32Generator:: { + x: x.clone(), + low: low.clone(), + high: high.clone(), + _phantom: PhantomData, + }); + + (low, high) + } } + +#[derive(Debug)] +struct SplitToU32Generator, const D: usize> { + x: Target, + low: U32Target, + high: U32Target, + _phantom: PhantomData, +} + +impl, const D: usize> SimpleGenerator + for SplitToU32Generator +{ + fn dependencies(&self) -> Vec { + vec![self.x] + } + + fn run_once(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues) { + let x = witness.get_target(self.x.clone()); + let x_u64 = x.to_canonical_u64(); + let low = x_u64 as u32; + let high: u32 = (x_u64 >> 32).try_into().unwrap(); + println!("LOW: {}", low); + println!("HIGH: {}", high); + + out_buffer.set_u32_target(self.low.clone(), low); + out_buffer.set_u32_target(self.high.clone(), high); + } +} + +#[cfg(test)] +mod tests { + use anyhow::Result; + + use rand::{thread_rng, Rng}; + + use crate::field::goldilocks_field::GoldilocksField; + use crate::iop::witness::PartialWitness; + use crate::plonk::circuit_builder::CircuitBuilder; + use crate::plonk::circuit_data::CircuitConfig; + use crate::plonk::verifier::verify; + + #[test] + pub fn test_add_many_u32s() -> 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 mut rng = thread_rng(); + let mut to_add = Vec::new(); + for _ in 0..10 { + to_add.push(builder.constant_u32(rng.gen())); + } + let _ = builder.add_many_u32(&to_add); + + let data = builder.build(); + let proof = data.prove(pw).unwrap(); + verify(proof, &data.verifier_only, &data.common) + } +} \ No newline at end of file diff --git a/waksman/src/ecdsa.rs b/plonky2/src/gadgets/ecdsa.rs similarity index 100% rename from waksman/src/ecdsa.rs rename to plonky2/src/gadgets/ecdsa.rs diff --git a/plonky2/src/gadgets/mod.rs b/plonky2/src/gadgets/mod.rs index ec4d1263..5dacdb51 100644 --- a/plonky2/src/gadgets/mod.rs +++ b/plonky2/src/gadgets/mod.rs @@ -2,6 +2,7 @@ pub mod arithmetic; pub mod arithmetic_extension; pub mod arithmetic_u32; pub mod biguint; +pub mod binary_arithmetic; pub mod curve; pub mod ecdsa; pub mod hash; diff --git a/plonky2/src/gadgets/multiple_comparison.rs b/plonky2/src/gadgets/multiple_comparison.rs index 88b94f3f..70afcab5 100644 --- a/plonky2/src/gadgets/multiple_comparison.rs +++ b/plonky2/src/gadgets/multiple_comparison.rs @@ -60,8 +60,13 @@ impl, const D: usize> CircuitBuilder { /// Helper function for comparing, specifically, lists of `U32Target`s. pub fn list_le_u32(&mut self, a: Vec, b: Vec) -> BoolTarget { - let a_targets = a.iter().map(|&t| t.0).collect(); - let b_targets = b.iter().map(|&t| t.0).collect(); + // let a_targets = a.iter().map(|&t| t.0).collect(); + // let b_targets = b.iter().map(|&t| t.0).collect(); + // self.list_le(a_targets, b_targets, 32) + + let num = a.len() / 2; + let a_targets = self.add_virtual_targets(num); + let b_targets = self.add_virtual_targets(num); self.list_le(a_targets, b_targets, 32) } } diff --git a/plonky2/src/gadgets/nonnative.rs b/plonky2/src/gadgets/nonnative.rs index 824d851c..cca22fd1 100644 --- a/plonky2/src/gadgets/nonnative.rs +++ b/plonky2/src/gadgets/nonnative.rs @@ -39,6 +39,10 @@ impl, const D: usize> CircuitBuilder { self.biguint_to_nonnative(&x_biguint) } + pub fn zero_nonnative(&mut self) -> NonNativeTarget { + self.constant_nonnative(FF::ZERO) + } + // Assert that two NonNativeTarget's, both assumed to be in reduced form, are equal. pub fn connect_nonnative( &mut self, @@ -70,6 +74,22 @@ impl, const D: usize> CircuitBuilder { self.reduce(&result) } + pub fn add_many_nonnative( + &mut self, + to_add: &[NonNativeTarget], + ) -> NonNativeTarget { + if to_add.len() == 1 { + return to_add[0].clone(); + } + + let mut result = self.add_biguint(&to_add[0].value, &to_add[1].value); + for i in 2..to_add.len() { + result = self.add_biguint(&result, &to_add[i].value); + } + + self.reduce(&result) + } + // Subtract two `NonNativeTarget`s. pub fn sub_nonnative( &mut self, @@ -94,6 +114,22 @@ impl, const D: usize> CircuitBuilder { self.reduce(&result) } + pub fn mul_many_nonnative( + &mut self, + to_mul: &[NonNativeTarget], + ) -> NonNativeTarget { + if to_mul.len() == 1 { + return to_mul[0].clone(); + } + + let mut result = self.mul_biguint(&to_mul[0].value, &to_mul[1].value); + for i in 2..to_mul.len() { + result = self.mul_biguint(&result, &to_mul[i].value); + } + + self.reduce(&result) + } + pub fn neg_nonnative(&mut self, x: &NonNativeTarget) -> NonNativeTarget { let zero_target = self.constant_biguint(&BigUint::zero()); let zero_ff = self.biguint_to_nonnative(&zero_target); @@ -104,21 +140,27 @@ impl, const D: usize> CircuitBuilder { 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 div = self.add_virtual_biguint_target(num_limbs); + + self.add_simple_generator(NonNativeInverseGenerator:: { + x: x.clone(), + inv: inv_biguint.clone(), + div: div.clone(), + _phantom: PhantomData, + }); + + let product = self.mul_biguint(&x.value, &inv_biguint); + + let modulus = self.constant_biguint(&FF::order()); + let mod_times_div = self.mul_biguint(&modulus, &div); + let one = self.constant_biguint(&BigUint::one()); + let expected_product = self.add_biguint(&mod_times_div, &one); + self.connect_biguint(&product, &expected_product); + let inv = NonNativeTarget:: { 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(&product, &one); - inv } @@ -138,10 +180,70 @@ impl, const D: usize> CircuitBuilder { /// Returns `x % |FF|` as a `NonNativeTarget`. fn reduce(&mut self, x: &BigUintTarget) -> NonNativeTarget { + println!("NUM LIMBS: {}", x.limbs.len()); + let before = self.num_gates(); + let modulus = FF::order(); let order_target = self.constant_biguint(&modulus); let value = self.rem_biguint(x, &order_target); + println!("NUMBER OF GATES: {}", self.num_gates() - before); + println!("OUTPUT LIMBS: {}", value.limbs.len()); + + NonNativeTarget { + value, + _phantom: PhantomData, + } + } + + /// Returns `x % |FF|` as a `NonNativeTarget`. + fn reduce_by_bits(&mut self, x: &BigUintTarget) -> NonNativeTarget { + println!("NUM LIMBS: {}", x.limbs.len()); + let before = self.num_gates(); + + let mut powers_of_two = Vec::new(); + let mut cur_power_of_two = FF::ONE; + let two = FF::TWO; + let mut max_num_limbs = 0; + for _ in 0..(x.limbs.len() * 32) { + let cur_power = self.constant_biguint(&cur_power_of_two.to_biguint()); + max_num_limbs = max_num_limbs.max(cur_power.limbs.len()); + powers_of_two.push(cur_power.limbs); + + cur_power_of_two *= two; + } + + let mut result_limbs_unreduced = vec![self.zero(); max_num_limbs]; + for i in 0..x.limbs.len() { + let this_limb = x.limbs[i]; + let bits = self.split_le(this_limb.0, 32); + for b in 0..bits.len() { + let this_power = powers_of_two[32 * i + b].clone(); + for x in 0..this_power.len() { + result_limbs_unreduced[x] = self.mul_add(bits[b].target, this_power[x].0, result_limbs_unreduced[x]); + } + } + } + + let mut result_limbs_reduced = Vec::new(); + let mut carry = self.zero_u32(); + for i in 0..result_limbs_unreduced.len() { + println!("{}", i); + let (low, high) = self.split_to_u32(result_limbs_unreduced[i]); + let (cur, overflow) = self.add_u32(carry, low); + let (new_carry, _) = self.add_many_u32(&[overflow, high, carry]); + result_limbs_reduced.push(cur); + carry = new_carry; + } + result_limbs_reduced.push(carry); + + let value = BigUintTarget { + limbs: result_limbs_reduced, + }; + + println!("NUMBER OF GATES: {}", self.num_gates() - before); + println!("OUTPUT LIMBS: {}", value.limbs.len()); + NonNativeTarget { value, _phantom: PhantomData, @@ -190,7 +292,8 @@ impl, const D: usize> CircuitBuilder { #[derive(Debug)] struct NonNativeInverseGenerator, const D: usize, FF: Field> { x: NonNativeTarget, - inv: NonNativeTarget, + inv: BigUintTarget, + div: BigUintTarget, _phantom: PhantomData, } @@ -205,7 +308,14 @@ impl, const D: usize, FF: Field> SimpleGenerator let x = witness.get_nonnative_target(self.x.clone()); let inv = x.inverse(); - out_buffer.set_nonnative_target(self.inv.clone(), inv); + let x_biguint = x.to_biguint(); + let inv_biguint = inv.to_biguint(); + let prod = x_biguint * &inv_biguint; + let modulus = FF::order(); + let (div, _rem) = prod.div_rem(&modulus); + + out_buffer.set_biguint_target(self.div.clone(), div); + out_buffer.set_biguint_target(self.inv.clone(), inv_biguint); } } @@ -247,6 +357,43 @@ mod tests { verify(proof, &data.verifier_only, &data.common) } + #[test] + fn test_nonnative_many_adds() -> Result<()> { + type FF = Secp256K1Base; + let a_ff = FF::rand(); + let b_ff = FF::rand(); + let c_ff = FF::rand(); + let d_ff = FF::rand(); + let e_ff = FF::rand(); + let f_ff = FF::rand(); + let g_ff = FF::rand(); + let h_ff = FF::rand(); + let sum_ff = a_ff + b_ff + c_ff + d_ff + e_ff + f_ff + g_ff + h_ff; + + type F = GoldilocksField; + let config = CircuitConfig::standard_recursion_config(); + let pw = PartialWitness::new(); + let mut builder = CircuitBuilder::::new(config); + + let a = builder.constant_nonnative(a_ff); + let b = builder.constant_nonnative(b_ff); + let c = builder.constant_nonnative(c_ff); + let d = builder.constant_nonnative(d_ff); + let e = builder.constant_nonnative(e_ff); + let f = builder.constant_nonnative(f_ff); + let g = builder.constant_nonnative(g_ff); + let h = builder.constant_nonnative(h_ff); + let all = [a, b, c, d, e, f, g, h]; + let sum = builder.add_many_nonnative(&all); + + let sum_expected = builder.constant_nonnative(sum_ff); + builder.connect_nonnative(&sum, &sum_expected); + + let data = builder.build(); + let proof = data.prove(pw).unwrap(); + verify(proof, &data.verifier_only, &data.common) + } + #[test] fn test_nonnative_sub() -> Result<()> { type FF = Secp256K1Base; @@ -285,6 +432,7 @@ mod tests { let x_ff = FF::rand(); let y_ff = FF::rand(); let product_ff = x_ff * y_ff; + println!("PRODUCT FF: {:?}", product_ff); let config = CircuitConfig::standard_recursion_config(); let pw = PartialWitness::new(); @@ -302,6 +450,38 @@ mod tests { verify(proof, &data.verifier_only, &data.common) } + fn test_nonnative_many_muls_helper(num: usize) { + type FF = Secp256K1Base; + + type F = GoldilocksField; + let config = CircuitConfig::standard_recursion_config(); + let mut unop_builder = CircuitBuilder::::new(config.clone()); + let mut op_builder = CircuitBuilder::::new(config); + + println!("NUM: {}", num); + + let ffs: Vec<_> = (0..num).map(|_| FF::rand()).collect(); + + let op_targets: Vec<_> = ffs.iter().map(|&x| op_builder.constant_nonnative(x)).collect(); + op_builder.mul_many_nonnative(&op_targets); + println!("OPTIMIZED GATE COUNT: {}", op_builder.num_gates()); + + let unop_targets: Vec<_> = ffs.iter().map(|&x| unop_builder.constant_nonnative(x)).collect(); + let mut result = unop_targets[0].clone(); + for i in 1..unop_targets.len() { + result = unop_builder.mul_nonnative(&result, &unop_targets[i]); + } + + println!("UNOPTIMIZED GATE COUNT: {}", unop_builder.num_gates()); + } + + #[test] + fn test_nonnative_many_muls() { + for num in 2..10 { + test_nonnative_many_muls_helper(num); + } + } + #[test] fn test_nonnative_neg() -> Result<()> { type FF = Secp256K1Base; diff --git a/plonky2/src/gates/mod.rs b/plonky2/src/gates/mod.rs index a3f92615..ac4600fa 100644 --- a/plonky2/src/gates/mod.rs +++ b/plonky2/src/gates/mod.rs @@ -3,6 +3,8 @@ pub mod arithmetic_base; pub mod arithmetic_extension; +pub mod binary_arithmetic; +pub mod binary_subtraction; pub mod arithmetic_u32; pub mod assert_le; pub mod base_sum; diff --git a/plonky2/src/iop/generator.rs b/plonky2/src/iop/generator.rs index 368232fd..994ba62b 100644 --- a/plonky2/src/iop/generator.rs +++ b/plonky2/src/iop/generator.rs @@ -7,6 +7,7 @@ use plonky2_field::field_types::Field; use crate::gadgets::arithmetic_u32::U32Target; use crate::gadgets::biguint::BigUintTarget; +use crate::gadgets::binary_arithmetic::BinaryTarget; use crate::gadgets::nonnative::NonNativeTarget; use crate::hash::hash_types::{HashOut, HashOutTarget, RichField}; use crate::iop::ext_target::ExtensionTarget; @@ -161,10 +162,14 @@ impl GeneratedValues { self.target_values.push((target, value)) } - fn set_u32_target(&mut self, target: U32Target, value: u32) { + pub fn set_u32_target(&mut self, target: U32Target, value: u32) { self.set_target(target.0, F::from_canonical_u32(value)) } + pub fn set_binary_target(&mut self, target: BinaryTarget, value: F) { + self.set_target(target.0, value) + } + pub fn set_biguint_target(&mut self, target: BigUintTarget, value: BigUint) { let mut limbs = value.to_u32_digits(); assert!(target.num_limbs() >= limbs.len()); diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index d9bcc1cf..c9d01abe 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -19,6 +19,8 @@ use crate::gadgets::polynomial::PolynomialCoeffsExtTarget; use crate::gates::arithmetic_base::ArithmeticGate; use crate::gates::arithmetic_extension::ArithmeticExtensionGate; use crate::gates::arithmetic_u32::U32ArithmeticGate; +use crate::gates::binary_arithmetic::BinaryArithmeticGate; +use crate::gates::binary_subtraction::BinarySubtractionGate; use crate::gates::constant::ConstantGate; use crate::gates::gate::{Gate, GateInstance, GateRef, PrefixedGate}; use crate::gates::gate_tree::Tree; @@ -222,6 +224,11 @@ impl, const D: usize> CircuitBuilder { let gate_ref = GateRef::new(gate_type); self.gates.insert(gate_ref.clone()); + /*println!("ADDING GATE {}: {:?}", index, gate_ref); + if index == 145 { + panic!(); + }*/ + self.gate_instances.push(GateInstance { gate_ref, constants, @@ -346,6 +353,11 @@ impl, const D: usize> CircuitBuilder { U32Target(self.constant(F::from_canonical_u32(c))) } + /// Returns a BinaryTarget for the value `c`, which is assumed to be at most BITS bits. + pub fn constant_binary(&mut self, c: F) -> BinaryTarget { + BinaryTarget(self.constant(c)) + } + /// If the given target is a constant (i.e. it was created by the `constant(F)` method), returns /// its constant value. Otherwise, returns `None`. pub fn target_as_constant(&self, target: Target) -> Option { @@ -818,10 +830,14 @@ pub struct BatchedGates, const D: usize> { /// The `U32ArithmeticGate` currently being filled (so new u32 arithmetic operations will be added to this gate before creating a new one) pub(crate) current_u32_arithmetic_gate: Option<(usize, usize)>, - /// The `U32SubtractionGate` currently being filled (so new u32 subtraction operations will be added to this gate before creating a new one) pub(crate) current_u32_subtraction_gate: Option<(usize, usize)>, + /// A map `b -> (g, i)` from `b` bits to an available `BinaryArithmeticGate` for number of bits `b`. + pub(crate) free_binary_arithmetic_gate: HashMap, + /// A map `b -> (g, i)` from `b` bits to an available `BinarySubtractionGate` for number of bits `b`. + pub(crate) free_binary_subtraction_gate: HashMap, + /// An available `ConstantGate` instance, if any. pub(crate) free_constant: Option<(usize, usize)>, } @@ -836,6 +852,8 @@ impl, const D: usize> BatchedGates { current_switch_gates: Vec::new(), current_u32_arithmetic_gate: None, current_u32_subtraction_gate: None, + free_binary_arithmetic_gate: HashMap::new(), + free_binary_subtraction_gate: HashMap::new(), free_constant: None, } } @@ -931,8 +949,8 @@ impl, const D: usize> CircuitBuilder { (gate, i) } - /// Finds the last available random access gate with the given `vec_size` or add one if there aren't any. - /// Returns `(g,i)` such that there is a random access gate with the given `vec_size` at index + /// Finds the last available random access gate with the given `bits` or add one if there aren't any. + /// Returns `(g,i)` such that there is a random access gate for the given `bits` at index /// `g` and the gate's `i`-th random access is available. pub(crate) fn find_random_access_gate(&mut self, bits: usize) -> (usize, usize) { let (gate, i) = self @@ -1031,6 +1049,64 @@ impl, const D: usize> CircuitBuilder { (gate_index, copy) } + + /// Finds the last available binary arithmetic with the given `bits` or add one if there aren't any. + /// Returns `(g,i)` such that there is a binary arithmetic for the given `bits` at index + /// `g` and the gate's `i`-th copy is available. + pub(crate) fn find_binary_arithmetic_gate(&mut self) -> (usize, usize) { + let (gate, i) = self + .batched_gates + .free_binary_arithmetic_gate + .get(&BITS) + .copied() + .unwrap_or_else(|| { + let gate = self.add_gate( + BinaryArithmeticGate::::new_from_config(&self.config), + vec![], + ); + (gate, 0) + }); + + // Update `free_binary_arithmetic` with new values. + if i + 1 < BinaryArithmeticGate::::new_from_config(&self.config).num_ops { + self.batched_gates + .free_random_access + .insert(BITS, (gate, i + 1)); + } else { + self.batched_gates.free_random_access.remove(&BITS); + } + + (gate, i) + } + + /// Finds the last available binary subtraction with the given `bits` or add one if there aren't any. + /// Returns `(g,i)` such that there is a binary subtraction for the given `bits` at index + /// `g` and the gate's `i`-th copy is available. + pub(crate) fn find_binary_subtraction_gate(&mut self) -> (usize, usize) { + let (gate, i) = self + .batched_gates + .free_binary_subtraction_gate + .get(&BITS) + .copied() + .unwrap_or_else(|| { + let gate = self.add_gate( + BinarySubtractionGate::::new_from_config(&self.config), + vec![], + ); + (gate, 0) + }); + + // Update `free_binary_subtraction` with new values. + if i + 1 < BinarySubtractionGate::::new_from_config(&self.config).num_ops { + self.batched_gates + .free_random_access + .insert(BITS, (gate, i + 1)); + } else { + self.batched_gates.free_random_access.remove(&BITS); + } + + (gate, i) + } /// Returns the gate index and copy index of a free `ConstantGate` slot, potentially adding a /// new `ConstantGate` if needed. From 82ce3ea8b2c1202a97ef2f20fa543bf7e8660f52 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 20 Jan 2022 16:07:54 -0800 Subject: [PATCH 06/36] ECDSA merge --- plonky2/src/gadgets/biguint.rs | 50 ++++++++++++---------- plonky2/src/gadgets/ecdsa.rs | 4 +- plonky2/src/gadgets/multiple_comparison.rs | 13 ++---- plonky2/src/gadgets/nonnative.rs | 12 +++--- plonky2/src/iop/generator.rs | 15 +++++-- 5 files changed, 51 insertions(+), 43 deletions(-) diff --git a/plonky2/src/gadgets/biguint.rs b/plonky2/src/gadgets/biguint.rs index 77013d27..289a9589 100644 --- a/plonky2/src/gadgets/biguint.rs +++ b/plonky2/src/gadgets/biguint.rs @@ -12,7 +12,7 @@ use crate::plonk::circuit_builder::CircuitBuilder; #[derive(Clone, Debug)] pub struct BigUintTarget { - pub limbs: Vec, + pub limbs: Vec>, } impl BigUintTarget { @@ -20,15 +20,23 @@ impl BigUintTarget { self.limbs.len() } - pub fn get_limb(&self, i: usize) -> U32Target { + pub fn get_limb(&self, i: usize) -> BinaryTarget<30> { self.limbs[i] } } impl, const D: usize> CircuitBuilder { pub fn constant_biguint(&mut self, value: &BigUint) -> BigUintTarget { - let limb_values = value.to_u32_digits(); - let limbs = limb_values.iter().map(|&l| self.constant_u32(l)).collect(); + let base = BigUint::from_u64(1 << 30).unwrap(); + let mut limb_values = Vec::new(); + let mut current = value.clone(); + while current > BigUint::zero() { + let (div, rem) = current.div_rem(&base); + current = div; + let rem_u64 = rem.to_u64_digits()[0]; + limb_values.push(F::from_canonical_u64(rem_u64)); + } + let limbs = limb_values.iter().map(|&l| self.constant_binary(l)).collect(); BigUintTarget { limbs } } @@ -36,14 +44,14 @@ impl, const D: usize> CircuitBuilder { pub fn connect_biguint(&mut self, lhs: &BigUintTarget, rhs: &BigUintTarget) { let min_limbs = lhs.num_limbs().min(rhs.num_limbs()); for i in 0..min_limbs { - self.connect_u32(lhs.get_limb(i), rhs.get_limb(i)); + self.connect_binary(lhs.get_limb(i), rhs.get_limb(i)); } for i in min_limbs..lhs.num_limbs() { - self.assert_zero_u32(lhs.get_limb(i)); + self.assert_zero_binary(lhs.get_limb(i)); } for i in min_limbs..rhs.num_limbs() { - self.assert_zero_u32(rhs.get_limb(i)); + self.assert_zero_binary(rhs.get_limb(i)); } } @@ -55,14 +63,14 @@ impl, const D: usize> CircuitBuilder { if a.num_limbs() > b.num_limbs() { let mut padded_b = b.clone(); for _ in b.num_limbs()..a.num_limbs() { - padded_b.limbs.push(self.zero_u32()); + padded_b.limbs.push(self.zero_binary()); } (a.clone(), padded_b) } else { let mut padded_a = a.clone(); for _ in a.num_limbs()..b.num_limbs() { - padded_a.limbs.push(self.zero_u32()); + padded_a.limbs.push(self.zero_binary()); } (padded_a, b.clone()) @@ -72,13 +80,11 @@ impl, const D: usize> CircuitBuilder { pub fn cmp_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BoolTarget { let (a, b) = self.pad_biguints(a, b); - self.list_le_u32(a.limbs, b.limbs) + self.list_le_binary::<30>(a.limbs, b.limbs) } pub fn add_virtual_biguint_target(&mut self, num_limbs: usize) -> BigUintTarget { - let limbs = (0..num_limbs) - .map(|_| self.add_virtual_u32_target()) - .collect(); + let limbs = self.add_virtual_binary_targets(num_limbs); BigUintTarget { limbs } } @@ -88,16 +94,16 @@ impl, const D: usize> CircuitBuilder { let num_limbs = a.num_limbs().max(b.num_limbs()); let mut combined_limbs = vec![]; - let mut carry = self.zero_u32(); + let mut carry = self.zero_binary(); for i in 0..num_limbs { let a_limb = (i < a.num_limbs()) .then(|| a.limbs[i]) - .unwrap_or_else(|| self.zero_u32()); + .unwrap_or_else(|| self.zero_binary()); let b_limb = (i < b.num_limbs()) .then(|| b.limbs[i]) - .unwrap_or_else(|| self.zero_u32()); + .unwrap_or_else(|| self.zero_binary()); - let (new_limb, new_carry) = self.add_many_u32(&[carry, a_limb, b_limb]); + let (new_limb, new_carry) = self.add_many_binary(&[carry, a_limb, b_limb]); carry = new_carry; combined_limbs.push(new_limb); } @@ -115,9 +121,9 @@ impl, const D: usize> CircuitBuilder { let mut result_limbs = vec![]; - let mut borrow = self.zero_u32(); + let mut borrow = self.zero_binary(); for i in 0..num_limbs { - let (result, new_borrow) = self.sub_u32(a.limbs[i], b.limbs[i], borrow); + let (result, new_borrow) = self.sub_binary(a.limbs[i], b.limbs[i], borrow); result_limbs.push(result); borrow = new_borrow; } @@ -134,17 +140,17 @@ impl, const D: usize> CircuitBuilder { let mut to_add = vec![vec![]; total_limbs]; for i in 0..a.limbs.len() { for j in 0..b.limbs.len() { - let (product, carry) = self.mul_u32(a.limbs[i], b.limbs[j]); + let (product, carry) = self.mul_binary(a.limbs[i], b.limbs[j]); to_add[i + j].push(product); to_add[i + j + 1].push(carry); } } let mut combined_limbs = vec![]; - let mut carry = self.zero_u32(); + let mut carry = self.zero_binary(); for summands in &mut to_add { summands.push(carry); - let (new_result, new_carry) = self.add_many_u32(summands); + let (new_result, new_carry) = self.add_many_binary(summands); combined_limbs.push(new_result); carry = new_carry; } diff --git a/plonky2/src/gadgets/ecdsa.rs b/plonky2/src/gadgets/ecdsa.rs index 362fbc6d..20fca13d 100644 --- a/plonky2/src/gadgets/ecdsa.rs +++ b/plonky2/src/gadgets/ecdsa.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use crate::curve::curve_types::Curve; use crate::field::extension_field::Extendable; use crate::field::field_types::RichField; -use crate::gadgets::arithmetic_u32::U32Target; +use crate::gadgets::binary_arithmetic::BinaryTarget; use crate::gadgets::biguint::BigUintTarget; use crate::gadgets::curve::AffinePointTarget; use crate::gadgets::nonnative::NonNativeTarget; @@ -38,7 +38,7 @@ impl, const D: usize> CircuitBuilder { for &bit in rev_bits { sum = self.mul_add(two, sum, bit.target); } - let limbs = vec![U32Target(sum)]; + let limbs = vec![BinaryTarget::<30>(sum)]; let value = BigUintTarget { limbs }; NonNativeTarget { diff --git a/plonky2/src/gadgets/multiple_comparison.rs b/plonky2/src/gadgets/multiple_comparison.rs index 70afcab5..acb157e5 100644 --- a/plonky2/src/gadgets/multiple_comparison.rs +++ b/plonky2/src/gadgets/multiple_comparison.rs @@ -59,15 +59,10 @@ impl, const D: usize> CircuitBuilder { } /// Helper function for comparing, specifically, lists of `U32Target`s. - pub fn list_le_u32(&mut self, a: Vec, b: Vec) -> BoolTarget { - // let a_targets = a.iter().map(|&t| t.0).collect(); - // let b_targets = b.iter().map(|&t| t.0).collect(); - // self.list_le(a_targets, b_targets, 32) - - let num = a.len() / 2; - let a_targets = self.add_virtual_targets(num); - let b_targets = self.add_virtual_targets(num); - self.list_le(a_targets, b_targets, 32) + pub fn list_le_binary(&mut self, a: Vec>, b: Vec>) -> BoolTarget { + let a_targets = a.iter().map(|&t| t.0).collect(); + let b_targets = b.iter().map(|&t| t.0).collect(); + self.list_le(a_targets, b_targets, BITS) } } diff --git a/plonky2/src/gadgets/nonnative.rs b/plonky2/src/gadgets/nonnative.rs index cca22fd1..946bf35a 100644 --- a/plonky2/src/gadgets/nonnative.rs +++ b/plonky2/src/gadgets/nonnative.rs @@ -5,10 +5,8 @@ use plonky2_field::{extension_field::Extendable, field_types::Field}; use plonky2_util::ceil_div_usize; use crate::gadgets::arithmetic_u32::U32Target; -use crate::gadgets::biguint::BigUintTarget; -use crate::hash::hash_types::RichField; -use crate::iop::generator::{GeneratedValues, SimpleGenerator}; -use crate::iop::target::{BoolTarget, Target}; +use crate::field::field_types::RichField; +use crate::gadgets::binary_arithmetic::BinaryTarget; use crate::iop::witness::{PartitionWitness, Witness}; use crate::plonk::circuit_builder::CircuitBuilder; @@ -197,7 +195,7 @@ impl, const D: usize> CircuitBuilder { } /// Returns `x % |FF|` as a `NonNativeTarget`. - fn reduce_by_bits(&mut self, x: &BigUintTarget) -> NonNativeTarget { + /*fn reduce_by_bits(&mut self, x: &BigUintTarget) -> NonNativeTarget { println!("NUM LIMBS: {}", x.limbs.len()); let before = self.num_gates(); @@ -248,7 +246,7 @@ impl, const D: usize> CircuitBuilder { value, _phantom: PhantomData, } - } + }*/ #[allow(dead_code)] fn reduce_nonnative(&mut self, x: &NonNativeTarget) -> NonNativeTarget { @@ -257,7 +255,7 @@ impl, const D: usize> CircuitBuilder { } pub fn bool_to_nonnative(&mut self, b: &BoolTarget) -> NonNativeTarget { - let limbs = vec![U32Target(b.target)]; + let limbs = vec![BinaryTarget::<30>(b.target)]; let value = BigUintTarget { limbs }; NonNativeTarget { diff --git a/plonky2/src/iop/generator.rs b/plonky2/src/iop/generator.rs index 994ba62b..70f99d54 100644 --- a/plonky2/src/iop/generator.rs +++ b/plonky2/src/iop/generator.rs @@ -171,12 +171,21 @@ impl GeneratedValues { } pub fn set_biguint_target(&mut self, target: BigUintTarget, value: BigUint) { - let mut limbs = value.to_u32_digits(); + let base = BigUint::from_u64(1 << 30).unwrap(); + let mut limbs = Vec::new(); + let mut current = value.clone(); + while current > BigUint::zero() { + let (div, rem) = current.div_rem(&base); + current = div; + let rem_u64 = rem.to_u64_digits()[0]; + limbs.push(F::from_canonical_u64(rem_u64)); + } + assert!(target.num_limbs() >= limbs.len()); - limbs.resize(target.num_limbs(), 0); + limbs.resize(target.num_limbs(), F::ZERO); for i in 0..target.num_limbs() { - self.set_u32_target(target.get_limb(i), limbs[i]); + self.set_binary_target(target.get_limb(i), limbs[i]); } } From c561333c220df6a5f2095ead952bb59c2b982d05 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 20 Jan 2022 16:09:11 -0800 Subject: [PATCH 07/36] ECDSA merge --- plonky2/src/gadgets/arithmetic_u32.rs | 3 +-- plonky2/src/gadgets/biguint.rs | 5 ++++- plonky2/src/gadgets/ecdsa.rs | 2 +- plonky2/src/gadgets/multiple_comparison.rs | 6 +++++- plonky2/src/gadgets/nonnative.rs | 14 ++++++++++---- plonky2/src/gates/mod.rs | 4 ++-- plonky2/src/plonk/circuit_builder.rs | 2 +- 7 files changed, 24 insertions(+), 12 deletions(-) diff --git a/plonky2/src/gadgets/arithmetic_u32.rs b/plonky2/src/gadgets/arithmetic_u32.rs index 0fbd076f..0ba50c85 100644 --- a/plonky2/src/gadgets/arithmetic_u32.rs +++ b/plonky2/src/gadgets/arithmetic_u32.rs @@ -204,7 +204,6 @@ impl, const D: usize> SimpleGenerator #[cfg(test)] mod tests { use anyhow::Result; - use rand::{thread_rng, Rng}; use crate::field::goldilocks_field::GoldilocksField; @@ -234,4 +233,4 @@ mod tests { let proof = data.prove(pw).unwrap(); verify(proof, &data.verifier_only, &data.common) } -} \ No newline at end of file +} diff --git a/plonky2/src/gadgets/biguint.rs b/plonky2/src/gadgets/biguint.rs index 289a9589..6f9081b7 100644 --- a/plonky2/src/gadgets/biguint.rs +++ b/plonky2/src/gadgets/biguint.rs @@ -36,7 +36,10 @@ impl, const D: usize> CircuitBuilder { let rem_u64 = rem.to_u64_digits()[0]; limb_values.push(F::from_canonical_u64(rem_u64)); } - let limbs = limb_values.iter().map(|&l| self.constant_binary(l)).collect(); + let limbs = limb_values + .iter() + .map(|&l| self.constant_binary(l)) + .collect(); BigUintTarget { limbs } } diff --git a/plonky2/src/gadgets/ecdsa.rs b/plonky2/src/gadgets/ecdsa.rs index 20fca13d..6466cea7 100644 --- a/plonky2/src/gadgets/ecdsa.rs +++ b/plonky2/src/gadgets/ecdsa.rs @@ -3,8 +3,8 @@ use std::marker::PhantomData; use crate::curve::curve_types::Curve; use crate::field::extension_field::Extendable; use crate::field::field_types::RichField; -use crate::gadgets::binary_arithmetic::BinaryTarget; use crate::gadgets::biguint::BigUintTarget; +use crate::gadgets::binary_arithmetic::BinaryTarget; use crate::gadgets::curve::AffinePointTarget; use crate::gadgets::nonnative::NonNativeTarget; use crate::iop::target::{BoolTarget, Target}; diff --git a/plonky2/src/gadgets/multiple_comparison.rs b/plonky2/src/gadgets/multiple_comparison.rs index acb157e5..222a6858 100644 --- a/plonky2/src/gadgets/multiple_comparison.rs +++ b/plonky2/src/gadgets/multiple_comparison.rs @@ -59,7 +59,11 @@ impl, const D: usize> CircuitBuilder { } /// Helper function for comparing, specifically, lists of `U32Target`s. - pub fn list_le_binary(&mut self, a: Vec>, b: Vec>) -> BoolTarget { + pub fn list_le_binary( + &mut self, + a: Vec>, + b: Vec>, + ) -> BoolTarget { let a_targets = a.iter().map(|&t| t.0).collect(); let b_targets = b.iter().map(|&t| t.0).collect(); self.list_le(a_targets, b_targets, BITS) diff --git a/plonky2/src/gadgets/nonnative.rs b/plonky2/src/gadgets/nonnative.rs index 946bf35a..12709300 100644 --- a/plonky2/src/gadgets/nonnative.rs +++ b/plonky2/src/gadgets/nonnative.rs @@ -139,7 +139,7 @@ impl, const D: usize> CircuitBuilder { let num_limbs = x.value.num_limbs(); let inv_biguint = self.add_virtual_biguint_target(num_limbs); let div = self.add_virtual_biguint_target(num_limbs); - + self.add_simple_generator(NonNativeInverseGenerator:: { x: x.clone(), inv: inv_biguint.clone(), @@ -148,7 +148,7 @@ impl, const D: usize> CircuitBuilder { }); let product = self.mul_biguint(&x.value, &inv_biguint); - + let modulus = self.constant_biguint(&FF::order()); let mod_times_div = self.mul_biguint(&modulus, &div); let one = self.constant_biguint(&BigUint::one()); @@ -460,11 +460,17 @@ mod tests { let ffs: Vec<_> = (0..num).map(|_| FF::rand()).collect(); - let op_targets: Vec<_> = ffs.iter().map(|&x| op_builder.constant_nonnative(x)).collect(); + let op_targets: Vec<_> = ffs + .iter() + .map(|&x| op_builder.constant_nonnative(x)) + .collect(); op_builder.mul_many_nonnative(&op_targets); println!("OPTIMIZED GATE COUNT: {}", op_builder.num_gates()); - let unop_targets: Vec<_> = ffs.iter().map(|&x| unop_builder.constant_nonnative(x)).collect(); + let unop_targets: Vec<_> = ffs + .iter() + .map(|&x| unop_builder.constant_nonnative(x)) + .collect(); let mut result = unop_targets[0].clone(); for i in 1..unop_targets.len() { result = unop_builder.mul_nonnative(&result, &unop_targets[i]); diff --git a/plonky2/src/gates/mod.rs b/plonky2/src/gates/mod.rs index ac4600fa..f63080e5 100644 --- a/plonky2/src/gates/mod.rs +++ b/plonky2/src/gates/mod.rs @@ -3,11 +3,11 @@ pub mod arithmetic_base; pub mod arithmetic_extension; -pub mod binary_arithmetic; -pub mod binary_subtraction; pub mod arithmetic_u32; pub mod assert_le; pub mod base_sum; +pub mod binary_arithmetic; +pub mod binary_subtraction; pub mod comparison; pub mod constant; pub mod exponentiation; diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index c9d01abe..f9704e0e 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -1049,7 +1049,7 @@ impl, const D: usize> CircuitBuilder { (gate_index, copy) } - + /// Finds the last available binary arithmetic with the given `bits` or add one if there aren't any. /// Returns `(g,i)` such that there is a binary arithmetic for the given `bits` at index /// `g` and the gate's `i`-th copy is available. From f436c142421498b0c7c8224031df27394ac056f5 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 20 Jan 2022 16:09:31 -0800 Subject: [PATCH 08/36] ECDSA merge --- plonky2/src/gadgets/arithmetic_u32.rs | 2 -- plonky2/src/gadgets/nonnative.rs | 13 -------- plonky2/src/iop/witness.rs | 2 +- plonky2/src/plonk/circuit_builder.rs | 45 +++++++++++++++++++++------ 4 files changed, 37 insertions(+), 25 deletions(-) diff --git a/plonky2/src/gadgets/arithmetic_u32.rs b/plonky2/src/gadgets/arithmetic_u32.rs index 0ba50c85..af1682f6 100644 --- a/plonky2/src/gadgets/arithmetic_u32.rs +++ b/plonky2/src/gadgets/arithmetic_u32.rs @@ -193,8 +193,6 @@ impl, const D: usize> SimpleGenerator let x_u64 = x.to_canonical_u64(); let low = x_u64 as u32; let high: u32 = (x_u64 >> 32).try_into().unwrap(); - println!("LOW: {}", low); - println!("HIGH: {}", high); out_buffer.set_u32_target(self.low.clone(), low); out_buffer.set_u32_target(self.high.clone(), high); diff --git a/plonky2/src/gadgets/nonnative.rs b/plonky2/src/gadgets/nonnative.rs index 12709300..20ecd382 100644 --- a/plonky2/src/gadgets/nonnative.rs +++ b/plonky2/src/gadgets/nonnative.rs @@ -178,16 +178,10 @@ impl, const D: usize> CircuitBuilder { /// Returns `x % |FF|` as a `NonNativeTarget`. fn reduce(&mut self, x: &BigUintTarget) -> NonNativeTarget { - println!("NUM LIMBS: {}", x.limbs.len()); - let before = self.num_gates(); - let modulus = FF::order(); let order_target = self.constant_biguint(&modulus); let value = self.rem_biguint(x, &order_target); - println!("NUMBER OF GATES: {}", self.num_gates() - before); - println!("OUTPUT LIMBS: {}", value.limbs.len()); - NonNativeTarget { value, _phantom: PhantomData, @@ -196,7 +190,6 @@ impl, const D: usize> CircuitBuilder { /// Returns `x % |FF|` as a `NonNativeTarget`. /*fn reduce_by_bits(&mut self, x: &BigUintTarget) -> NonNativeTarget { - println!("NUM LIMBS: {}", x.limbs.len()); let before = self.num_gates(); let mut powers_of_two = Vec::new(); @@ -430,7 +423,6 @@ mod tests { let x_ff = FF::rand(); let y_ff = FF::rand(); let product_ff = x_ff * y_ff; - println!("PRODUCT FF: {:?}", product_ff); let config = CircuitConfig::standard_recursion_config(); let pw = PartialWitness::new(); @@ -456,8 +448,6 @@ mod tests { let mut unop_builder = CircuitBuilder::::new(config.clone()); let mut op_builder = CircuitBuilder::::new(config); - println!("NUM: {}", num); - let ffs: Vec<_> = (0..num).map(|_| FF::rand()).collect(); let op_targets: Vec<_> = ffs @@ -465,7 +455,6 @@ mod tests { .map(|&x| op_builder.constant_nonnative(x)) .collect(); op_builder.mul_many_nonnative(&op_targets); - println!("OPTIMIZED GATE COUNT: {}", op_builder.num_gates()); let unop_targets: Vec<_> = ffs .iter() @@ -475,8 +464,6 @@ mod tests { for i in 1..unop_targets.len() { result = unop_builder.mul_nonnative(&result, &unop_targets[i]); } - - println!("UNOPTIMIZED GATE COUNT: {}", unop_builder.num_gates()); } #[test] diff --git a/plonky2/src/iop/witness.rs b/plonky2/src/iop/witness.rs index efe4d911..558832d6 100644 --- a/plonky2/src/iop/witness.rs +++ b/plonky2/src/iop/witness.rs @@ -65,7 +65,7 @@ pub trait Witness { fn get_biguint_target(&self, target: BigUintTarget) -> BigUint { let mut result = BigUint::zero(); - let limb_base = BigUint::from_u64(1 << 32u64).unwrap(); + let limb_base = BigUint::from_u64(1 << 30u64).unwrap(); for i in (0..target.num_limbs()).rev() { let limb = target.get_limb(i); result *= &limb_base; diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index f9704e0e..ae65a53b 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -224,11 +224,6 @@ impl, const D: usize> CircuitBuilder { let gate_ref = GateRef::new(gate_type); self.gates.insert(gate_ref.clone()); - /*println!("ADDING GATE {}: {:?}", index, gate_ref); - if index == 145 { - panic!(); - }*/ - self.gate_instances.push(GateInstance { gate_ref, constants, @@ -1070,10 +1065,10 @@ impl, const D: usize> CircuitBuilder { // Update `free_binary_arithmetic` with new values. if i + 1 < BinaryArithmeticGate::::new_from_config(&self.config).num_ops { self.batched_gates - .free_random_access + .free_binary_arithmetic_gate .insert(BITS, (gate, i + 1)); } else { - self.batched_gates.free_random_access.remove(&BITS); + self.batched_gates.free_binary_arithmetic_gate.remove(&BITS); } (gate, i) @@ -1099,10 +1094,10 @@ impl, const D: usize> CircuitBuilder { // Update `free_binary_subtraction` with new values. if i + 1 < BinarySubtractionGate::::new_from_config(&self.config).num_ops { self.batched_gates - .free_random_access + .free_binary_subtraction_gate .insert(BITS, (gate, i + 1)); } else { - self.batched_gates.free_random_access.remove(&BITS); + self.batched_gates.free_binary_subtraction_gate.remove(&BITS); } (gate, i) @@ -1242,6 +1237,36 @@ impl, const D: usize> CircuitBuilder { } } + /// Fill the remaining unused binary arithmetic operations with zeros, so that all + /// `BinaryArithmeticGenerator`s are run. + fn fill_binary_arithmetic_gates(&mut self) { + let zero = self.zero_binary::<30>(); + if let Some(&(_, i)) = self.batched_gates.free_binary_arithmetic_gate.get(&30) { + let max_copies = + BinaryArithmeticGate::::new_from_config(&self.config).num_ops; + for _ in i..max_copies { + let dummy = self.add_virtual_binary_target(); + self.mul_add_binary(dummy, dummy, dummy); + self.connect_binary(dummy, zero); + } + } + } + + /// Fill the remaining unused binary subtraction operations with zeros, so that all + /// `BinarySubtractionGenerator`s are run. + fn fill_binary_subtraction_gates(&mut self) { + let zero = self.zero_binary::<30>(); + if let Some(&(_, i)) = self.batched_gates.free_binary_subtraction_gate.get(&30) { + let max_copies = + BinarySubtractionGate::::new_from_config(&self.config).num_ops; + for _ in i..max_copies { + let dummy = self.add_virtual_binary_target(); + self.sub_binary(dummy, dummy, dummy); + self.connect_binary(dummy, zero); + } + } + } + fn fill_batched_gates(&mut self) { self.fill_arithmetic_gates(); self.fill_base_arithmetic_gates(); @@ -1250,5 +1275,7 @@ impl, const D: usize> CircuitBuilder { self.fill_switch_gates(); self.fill_u32_arithmetic_gates(); self.fill_u32_subtraction_gates(); + self.fill_binary_arithmetic_gates(); + self.fill_binary_subtraction_gates(); } } From 440a5bd5d97ff20972e68b7bf353918b51ce90b8 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 4 Jan 2022 15:27:59 -0800 Subject: [PATCH 09/36] fmt --- plonky2/src/plonk/circuit_builder.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index ae65a53b..058c5d0d 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -1097,7 +1097,9 @@ impl, const D: usize> CircuitBuilder { .free_binary_subtraction_gate .insert(BITS, (gate, i + 1)); } else { - self.batched_gates.free_binary_subtraction_gate.remove(&BITS); + self.batched_gates + .free_binary_subtraction_gate + .remove(&BITS); } (gate, i) From 07b71a961398ec12876b7d8be084dc7ccae8b84f Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 20 Jan 2022 16:10:21 -0800 Subject: [PATCH 10/36] ECDSA merge --- plonky2/src/gadgets/biguint.rs | 6 +- plonky2/src/gadgets/multiple_comparison.rs | 13 +- plonky2/src/gates/add_many_u32.rs | 437 +++++++++++++++++++++ plonky2/src/gates/mod.rs | 1 + 4 files changed, 448 insertions(+), 9 deletions(-) create mode 100644 plonky2/src/gates/add_many_u32.rs diff --git a/plonky2/src/gadgets/biguint.rs b/plonky2/src/gadgets/biguint.rs index 6f9081b7..f647a30f 100644 --- a/plonky2/src/gadgets/biguint.rs +++ b/plonky2/src/gadgets/biguint.rs @@ -83,7 +83,7 @@ impl, const D: usize> CircuitBuilder { pub fn cmp_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BoolTarget { let (a, b) = self.pad_biguints(a, b); - self.list_le_binary::<30>(a.limbs, b.limbs) + self.list_le_30(a.limbs, b.limbs) } pub fn add_virtual_biguint_target(&mut self, num_limbs: usize) -> BigUintTarget { @@ -138,6 +138,8 @@ impl, const D: usize> CircuitBuilder { } pub fn mul_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BigUintTarget { + let before = self.num_gates(); + let total_limbs = a.limbs.len() + b.limbs.len(); let mut to_add = vec![vec![]; total_limbs]; @@ -159,6 +161,8 @@ impl, const D: usize> CircuitBuilder { } combined_limbs.push(carry); + println!("NUMBER OF GATES: {}", self.num_gates() - before); + BigUintTarget { limbs: combined_limbs, } diff --git a/plonky2/src/gadgets/multiple_comparison.rs b/plonky2/src/gadgets/multiple_comparison.rs index 222a6858..bca337fe 100644 --- a/plonky2/src/gadgets/multiple_comparison.rs +++ b/plonky2/src/gadgets/multiple_comparison.rs @@ -59,14 +59,11 @@ impl, const D: usize> CircuitBuilder { } /// Helper function for comparing, specifically, lists of `U32Target`s. - pub fn list_le_binary( - &mut self, - a: Vec>, - b: Vec>, - ) -> BoolTarget { - let a_targets = a.iter().map(|&t| t.0).collect(); - let b_targets = b.iter().map(|&t| t.0).collect(); - self.list_le(a_targets, b_targets, BITS) + pub fn list_le_30(&mut self, a: Vec>, b: Vec>) -> BoolTarget { + let a_targets: Vec = a.iter().map(|&t| t.0).collect(); + let b_targets: Vec = b.iter().map(|&t| t.0).collect(); + + self.list_le(a_targets, b_targets, 30) } } diff --git a/plonky2/src/gates/add_many_u32.rs b/plonky2/src/gates/add_many_u32.rs new file mode 100644 index 00000000..7ad8523d --- /dev/null +++ b/plonky2/src/gates/add_many_u32.rs @@ -0,0 +1,437 @@ +use std::marker::PhantomData; + +use itertools::unfold; + +use crate::field::extension_field::target::ExtensionTarget; +use crate::field::extension_field::Extendable; +use crate::field::field_types::{Field, RichField}; +use crate::gates::gate::Gate; +use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; +use crate::iop::target::Target; +use crate::iop::wire::Wire; +use crate::iop::witness::{PartitionWitness, Witness}; +use crate::plonk::circuit_builder::CircuitBuilder; +use crate::plonk::circuit_data::CircuitConfig; +use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; + +const LOG2_MAX_NUM_ADDENDS: usize = 6; +const MAX_NUM_ADDENDS: usize = 1 << LOG2_MAX_NUM_ADDENDS; + +/// A gate to perform addition on `num_addends` different 32-bit values, plus a small carry +#[derive(Copy, Clone, Debug)] +pub struct U32AddManyGate, const D: usize> { + pub num_addends: usize, + pub num_ops: usize, + _phantom: PhantomData, +} + +impl, const D: usize> U32AddManyGate { + pub fn new_from_config(num_addends: usize, config: &CircuitConfig) -> Self { + Self { + num_addends, + num_ops: Self::num_ops(num_addends, config), + _phantom: PhantomData, + } + } + + pub(crate) fn num_ops(num_addends: usize, config: &CircuitConfig) -> usize { + debug_assert!(num_addends < MAX_NUM_ADDENDS); + let wires_per_op = (num_addends + 3) + Self::num_limbs(); + let routed_wires_per_op = 5; + (config.num_wires / wires_per_op).min(config.num_routed_wires / routed_wires_per_op) + } + + pub fn wire_ith_op_jth_addend(&self, i: usize, j: usize) -> usize { + debug_assert!(i < self.num_ops); + debug_assert!(i < self.num_addends); + (self.num_addends + 3) * i + j + } + pub fn wire_ith_carry(&self, i: usize) -> usize { + debug_assert!(i < self.num_ops); + (self.num_addends + 3) * i + self.num_addends + } + + pub fn wire_ith_output_low_half(&self, i: usize) -> usize { + debug_assert!(i < self.num_ops); + (self.num_addends + 3) * i + self.num_addends + 1 + } + pub fn wire_ith_output_high_half(&self, i: usize) -> usize { + debug_assert!(i < self.num_ops); + (self.num_addends + 3) * i + self.num_addends + 2 + } + + pub fn limb_bits() -> usize { + 2 + } + pub fn num_limbs() -> usize { + 32 / Self::limb_bits() + } + + pub fn wire_ith_output_jth_limb(&self, i: usize, j: usize) -> usize { + debug_assert!(i < self.num_ops); + debug_assert!(j < Self::num_limbs()); + (self.num_addends + 3) * self.num_ops + Self::num_limbs() * i + j + } +} + +impl, const D: usize> Gate for U32AddManyGate { + fn id(&self) -> String { + format!("{:?}", self) + } + + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { + let mut constraints = Vec::with_capacity(self.num_constraints()); + for i in 0..self.num_ops { + let addends: Vec = (0..self.num_addends) + .map(|j| vars.local_wires[self.wire_ith_op_jth_addend(i, j)]) + .collect(); + let borrow = vars.local_wires[self.wire_ith_carry(i)]; + + let computed_output = addends.iter().fold(F::Extension::ZERO, |x, &y| x + y) + borrow; + + let output_low = vars.local_wires[self.wire_ith_output_low_half(i)]; + let output_high = vars.local_wires[self.wire_ith_output_high_half(i)]; + + let base = F::Extension::from_canonical_u64(1 << 32u64); + let combined_output = output_high * base + output_low; + + constraints.push(combined_output - computed_output); + + let mut combined_low_limbs = F::Extension::ZERO; + let base = F::Extension::from_canonical_u64(1u64 << Self::limb_bits()); + for j in (0..Self::num_limbs()).rev() { + let this_limb = vars.local_wires[self.wire_ith_output_jth_limb(i, j)]; + let max_limb = 1 << Self::limb_bits(); + let product = (0..max_limb) + .map(|x| this_limb - F::Extension::from_canonical_usize(x)) + .product(); + constraints.push(product); + + combined_low_limbs = base * combined_low_limbs + this_limb; + } + constraints.push(combined_low_limbs - output_low); + + let max_overflow = self.num_addends; + let product = (0..max_overflow) + .map(|x| output_high - F::Extension::from_canonical_usize(x)) + .product(); + constraints.push(product); + } + + constraints + } + + fn eval_unfiltered_base(&self, vars: EvaluationVarsBase) -> Vec { + let mut constraints = Vec::with_capacity(self.num_constraints()); + for i in 0..self.num_ops { + let addends: Vec = (0..self.num_addends) + .map(|j| vars.local_wires[self.wire_ith_op_jth_addend(i, j)]) + .collect(); + let borrow = vars.local_wires[self.wire_ith_carry(i)]; + + let computed_output = addends.iter().fold(F::ZERO, |x, &y| x + y) + borrow; + + let output_low = vars.local_wires[self.wire_ith_output_low_half(i)]; + let output_high = vars.local_wires[self.wire_ith_output_high_half(i)]; + + let base = F::from_canonical_u64(1 << 32u64); + let combined_output = output_high * base + output_low; + + constraints.push(combined_output - computed_output); + + let mut combined_low_limbs = F::ZERO; + let base = F::from_canonical_u64(1u64 << Self::limb_bits()); + for j in (0..Self::num_limbs()).rev() { + let this_limb = vars.local_wires[self.wire_ith_output_jth_limb(i, j)]; + let max_limb = 1 << Self::limb_bits(); + let product = (0..max_limb) + .map(|x| this_limb - F::from_canonical_usize(x)) + .product(); + constraints.push(product); + + combined_low_limbs = base * combined_low_limbs + this_limb; + } + constraints.push(combined_low_limbs - output_low); + + let max_overflow = self.num_addends; + let product = (0..max_overflow) + .map(|x| output_high - F::from_canonical_usize(x)) + .product(); + constraints.push(product); + } + + constraints + } + + fn eval_unfiltered_recursively( + &self, + builder: &mut CircuitBuilder, + vars: EvaluationTargets, + ) -> Vec> { + let mut constraints = Vec::with_capacity(self.num_constraints()); + + for i in 0..self.num_ops { + let addends: Vec> = (0..self.num_addends) + .map(|j| vars.local_wires[self.wire_ith_op_jth_addend(i, j)]) + .collect(); + let borrow = vars.local_wires[self.wire_ith_carry(i)]; + + let mut computed_output = borrow; + for addend in addends { + computed_output = builder.add_extension(computed_output, addend); + } + + let output_low = vars.local_wires[self.wire_ith_output_low_half(i)]; + let output_high = vars.local_wires[self.wire_ith_output_high_half(i)]; + + let base: F::Extension = F::from_canonical_u64(1 << 32u64).into(); + let base_target = builder.constant_extension(base); + let combined_output = builder.mul_add_extension(output_high, base_target, output_low); + + constraints.push(builder.sub_extension(combined_output, computed_output)); + + let mut combined_low_limbs = builder.zero_extension(); + let base = builder + .constant_extension(F::Extension::from_canonical_u64(1u64 << Self::limb_bits())); + for j in (0..Self::num_limbs()).rev() { + let this_limb = vars.local_wires[self.wire_ith_output_jth_limb(i, j)]; + let max_limb = 1 << Self::limb_bits(); + + let mut product = builder.one_extension(); + for x in 0..max_limb { + let x_target = + builder.constant_extension(F::Extension::from_canonical_usize(x)); + let diff = builder.sub_extension(this_limb, x_target); + product = builder.mul_extension(product, diff); + } + constraints.push(product); + + combined_low_limbs = builder.mul_add_extension(base, combined_low_limbs, this_limb); + } + constraints.push(builder.sub_extension(combined_low_limbs, output_low)); + + let max_overflow = self.num_addends; + let mut product = builder.one_extension(); + for x in 0..max_overflow { + let x_target = builder.constant_extension(F::Extension::from_canonical_usize(x)); + let diff = builder.sub_extension(output_high, x_target); + product = builder.mul_extension(product, diff); + } + constraints.push(product); + } + + constraints + } + + fn generators( + &self, + gate_index: usize, + _local_constants: &[F], + ) -> Vec>> { + (0..self.num_ops) + .map(|i| { + let g: Box> = Box::new( + U32AddManyGenerator { + gate: *self, + gate_index, + i, + _phantom: PhantomData, + } + .adapter(), + ); + g + }) + .collect::>() + } + + fn num_wires(&self) -> usize { + (self.num_addends + 3) * self.num_ops + Self::num_limbs() * self.num_ops + } + + fn num_constants(&self) -> usize { + 0 + } + + fn degree(&self) -> usize { + 1 << Self::limb_bits() + } + + fn num_constraints(&self) -> usize { + self.num_ops * (3 + Self::num_limbs()) + } +} + +#[derive(Clone, Debug)] +struct U32AddManyGenerator, const D: usize> { + gate: U32AddManyGate, + gate_index: usize, + i: usize, + _phantom: PhantomData, +} + +impl, const D: usize> SimpleGenerator + for U32AddManyGenerator +{ + fn dependencies(&self) -> Vec { + let local_target = |input| Target::wire(self.gate_index, input); + + (0..self.gate.num_addends) + .map(|j| local_target(self.gate.wire_ith_op_jth_addend(self.i, j))) + .chain([local_target(self.gate.wire_ith_carry(self.i))]) + .collect() + } + + fn run_once(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues) { + let local_wire = |input| Wire { + gate: self.gate_index, + input, + }; + + let get_local_wire = |input| witness.get_wire(local_wire(input)); + + let addends: Vec<_> = (0..self.gate.num_addends).map(|j| get_local_wire(self.gate.wire_ith_output_jth_limb(self.i, j))).collect(); + let carry = get_local_wire(self.gate.wire_ith_carry(self.i)); + + let output = addends.iter().fold(F::ZERO, |x, &y| x + y) + carry; + let mut output_u64 = output.to_canonical_u64(); + + let output_high_u64 = output_u64 >> 32; + let output_low_u64 = output_u64 & ((1 << 32) - 1); + + let output_high = F::from_canonical_u64(output_high_u64); + let output_low = F::from_canonical_u64(output_low_u64); + + let output_high_wire = local_wire(self.gate.wire_ith_output_high_half(self.i)); + let output_low_wire = local_wire(self.gate.wire_ith_output_low_half(self.i)); + + out_buffer.set_wire(output_high_wire, output_high); + out_buffer.set_wire(output_low_wire, output_low); + + let num_limbs = U32AddManyGate::::num_limbs(); + let limb_base = 1 << U32AddManyGate::::limb_bits(); + let output_limbs_u64 = unfold((), move |_| { + let ret = output_u64 % limb_base; + output_u64 /= limb_base; + Some(ret) + }) + .take(num_limbs); + let output_limbs_f = output_limbs_u64.map(F::from_canonical_u64); + + for (j, output_limb) in output_limbs_f.enumerate() { + let wire = local_wire(self.gate.wire_ith_output_jth_limb(self.i, j)); + out_buffer.set_wire(wire, output_limb); + } + } +} + +#[cfg(test)] +mod tests { + use std::marker::PhantomData; + + use anyhow::Result; + use rand::Rng; + + use crate::field::extension_field::quartic::QuarticExtension; + use crate::field::field_types::Field; + use crate::field::goldilocks_field::GoldilocksField; + use crate::gates::add_many_u32::U32AddManyGate; + use crate::gates::gate::Gate; + use crate::gates::gate_testing::{test_eval_fns, test_low_degree}; + use crate::hash::hash_types::HashOut; + use crate::plonk::vars::EvaluationVars; + + #[test] + fn low_degree() { + test_low_degree::(U32AddManyGate:: { + num_addends: 4, + num_ops: 3, + _phantom: PhantomData, + }) + } + + #[test] + fn eval_fns() -> Result<()> { + test_eval_fns::(U32AddManyGate:: { + num_addends: 4, + num_ops: 3, + _phantom: PhantomData, + }) + } + + #[test] + fn test_gate_constraint() { + type F = GoldilocksField; + type FF = QuarticExtension; + const D: usize = 4; + const NUM_ADDENDS: usize = 4; + const NUM_U32_ADD_MANY_OPS: usize = 3; + + fn get_wires( + addends: Vec>, + carries: Vec, + ) -> Vec { + let mut v0 = Vec::new(); + let mut v1 = Vec::new(); + + let limb_bits = U32AddManyGate::::limb_bits(); + let num_limbs = U32AddManyGate::::num_limbs(); + let limb_base = 1 << limb_bits; + for op in 0..NUM_U32_ADD_MANY_OPS { + let adds = &addends[op]; + let ca = carries[op]; + + let mut output = adds.iter().sum::() + ca; + let output_low = output & ((1 << 32) - 1); + let output_high = output >> 32; + + let mut output_limbs = Vec::with_capacity(num_limbs); + for _i in 0..num_limbs { + output_limbs.push(output % limb_base); + output /= limb_base; + } + let mut output_limbs_f: Vec<_> = output_limbs + .into_iter() + .map(F::from_canonical_u64) + .collect(); + + for a in adds { + v0.push(F::from_canonical_u64(*a)); + } + v0.push(F::from_canonical_u64(ca)); + v0.push(F::from_canonical_u64(output_low)); + v0.push(F::from_canonical_u64(output_high)); + v1.append(&mut output_limbs_f); + } + + v0.iter() + .chain(v1.iter()) + .map(|&x| x.into()) + .collect::>() + } + + let mut rng = rand::thread_rng(); + let addends: Vec> = (0..NUM_U32_ADD_MANY_OPS) + .map(|_| (0..NUM_ADDENDS).map(|_| rng.gen::() as u64).collect()) + .collect(); + let carries: Vec<_> = (0..NUM_U32_ADD_MANY_OPS) + .map(|_| rng.gen::() as u64) + .collect(); + + let gate = U32AddManyGate:: { + num_addends: NUM_ADDENDS, + num_ops: NUM_U32_ADD_MANY_OPS, + _phantom: PhantomData, + }; + + let vars = EvaluationVars { + local_constants: &[], + local_wires: &get_wires(addends, carries), + public_inputs_hash: &HashOut::rand(), + }; + + assert!( + gate.eval_unfiltered(vars).iter().all(|x| x.is_zero()), + "Gate constraints are not satisfied." + ); + } +} diff --git a/plonky2/src/gates/mod.rs b/plonky2/src/gates/mod.rs index f63080e5..a7591648 100644 --- a/plonky2/src/gates/mod.rs +++ b/plonky2/src/gates/mod.rs @@ -1,6 +1,7 @@ // Gates have `new` methods that return `GateRef`s. #![allow(clippy::new_ret_no_self)] +pub mod add_many_u32; pub mod arithmetic_base; pub mod arithmetic_extension; pub mod arithmetic_u32; From 3ba61a4e9ce8111704b4e18bfb8e08e7b3c67147 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 20 Jan 2022 16:11:04 -0800 Subject: [PATCH 11/36] ECDSA merge --- plonky2/src/gadgets/biguint.rs | 47 ++++++++++------------ plonky2/src/gadgets/ecdsa.rs | 4 +- plonky2/src/gadgets/multiple_comparison.rs | 4 +- plonky2/src/gadgets/nonnative.rs | 2 +- plonky2/src/iop/generator.rs | 9 ++--- plonky2/src/iop/witness.rs | 2 +- 6 files changed, 31 insertions(+), 37 deletions(-) diff --git a/plonky2/src/gadgets/biguint.rs b/plonky2/src/gadgets/biguint.rs index f647a30f..559d5b92 100644 --- a/plonky2/src/gadgets/biguint.rs +++ b/plonky2/src/gadgets/biguint.rs @@ -12,7 +12,7 @@ use crate::plonk::circuit_builder::CircuitBuilder; #[derive(Clone, Debug)] pub struct BigUintTarget { - pub limbs: Vec>, + pub limbs: Vec, } impl BigUintTarget { @@ -20,25 +20,24 @@ impl BigUintTarget { self.limbs.len() } - pub fn get_limb(&self, i: usize) -> BinaryTarget<30> { + pub fn get_limb(&self, i: usize) -> U32Target { self.limbs[i] } } impl, const D: usize> CircuitBuilder { pub fn constant_biguint(&mut self, value: &BigUint) -> BigUintTarget { - let base = BigUint::from_u64(1 << 30).unwrap(); + let base = BigUint::from_u64(1 << 32).unwrap(); let mut limb_values = Vec::new(); let mut current = value.clone(); while current > BigUint::zero() { let (div, rem) = current.div_rem(&base); current = div; - let rem_u64 = rem.to_u64_digits()[0]; - limb_values.push(F::from_canonical_u64(rem_u64)); + limb_values.push(rem.to_u64_digits()[0] as u32); } let limbs = limb_values .iter() - .map(|&l| self.constant_binary(l)) + .map(|&l| self.constant_u32(l)) .collect(); BigUintTarget { limbs } @@ -47,14 +46,14 @@ impl, const D: usize> CircuitBuilder { pub fn connect_biguint(&mut self, lhs: &BigUintTarget, rhs: &BigUintTarget) { let min_limbs = lhs.num_limbs().min(rhs.num_limbs()); for i in 0..min_limbs { - self.connect_binary(lhs.get_limb(i), rhs.get_limb(i)); + self.connect_u32(lhs.get_limb(i), rhs.get_limb(i)); } for i in min_limbs..lhs.num_limbs() { - self.assert_zero_binary(lhs.get_limb(i)); + self.assert_zero_u32(lhs.get_limb(i)); } for i in min_limbs..rhs.num_limbs() { - self.assert_zero_binary(rhs.get_limb(i)); + self.assert_zero_u32(rhs.get_limb(i)); } } @@ -66,14 +65,14 @@ impl, const D: usize> CircuitBuilder { if a.num_limbs() > b.num_limbs() { let mut padded_b = b.clone(); for _ in b.num_limbs()..a.num_limbs() { - padded_b.limbs.push(self.zero_binary()); + padded_b.limbs.push(self.zero_u32()); } (a.clone(), padded_b) } else { let mut padded_a = a.clone(); for _ in a.num_limbs()..b.num_limbs() { - padded_a.limbs.push(self.zero_binary()); + padded_a.limbs.push(self.zero_u32()); } (padded_a, b.clone()) @@ -83,11 +82,11 @@ impl, const D: usize> CircuitBuilder { pub fn cmp_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BoolTarget { let (a, b) = self.pad_biguints(a, b); - self.list_le_30(a.limbs, b.limbs) + self.list_le_u32(a.limbs, b.limbs) } pub fn add_virtual_biguint_target(&mut self, num_limbs: usize) -> BigUintTarget { - let limbs = self.add_virtual_binary_targets(num_limbs); + let limbs = self.add_virtual_u32_targets(num_limbs); BigUintTarget { limbs } } @@ -97,16 +96,16 @@ impl, const D: usize> CircuitBuilder { let num_limbs = a.num_limbs().max(b.num_limbs()); let mut combined_limbs = vec![]; - let mut carry = self.zero_binary(); + let mut carry = self.zero_u32(); for i in 0..num_limbs { let a_limb = (i < a.num_limbs()) .then(|| a.limbs[i]) - .unwrap_or_else(|| self.zero_binary()); + .unwrap_or_else(|| self.zero_u32()); let b_limb = (i < b.num_limbs()) .then(|| b.limbs[i]) - .unwrap_or_else(|| self.zero_binary()); + .unwrap_or_else(|| self.zero_u32()); - let (new_limb, new_carry) = self.add_many_binary(&[carry, a_limb, b_limb]); + let (new_limb, new_carry) = self.add_many_u32(&[carry, a_limb, b_limb]); carry = new_carry; combined_limbs.push(new_limb); } @@ -124,9 +123,9 @@ impl, const D: usize> CircuitBuilder { let mut result_limbs = vec![]; - let mut borrow = self.zero_binary(); + let mut borrow = self.zero_u32(); for i in 0..num_limbs { - let (result, new_borrow) = self.sub_binary(a.limbs[i], b.limbs[i], borrow); + let (result, new_borrow) = self.sub_u32(a.limbs[i], b.limbs[i], borrow); result_limbs.push(result); borrow = new_borrow; } @@ -138,31 +137,27 @@ impl, const D: usize> CircuitBuilder { } pub fn mul_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BigUintTarget { - let before = self.num_gates(); - let total_limbs = a.limbs.len() + b.limbs.len(); let mut to_add = vec![vec![]; total_limbs]; for i in 0..a.limbs.len() { for j in 0..b.limbs.len() { - let (product, carry) = self.mul_binary(a.limbs[i], b.limbs[j]); + let (product, carry) = self.mul_u32(a.limbs[i], b.limbs[j]); to_add[i + j].push(product); to_add[i + j + 1].push(carry); } } let mut combined_limbs = vec![]; - let mut carry = self.zero_binary(); + let mut carry = self.zero_u32(); for summands in &mut to_add { summands.push(carry); - let (new_result, new_carry) = self.add_many_binary(summands); + let (new_result, new_carry) = self.add_many_u32(summands); combined_limbs.push(new_result); carry = new_carry; } combined_limbs.push(carry); - println!("NUMBER OF GATES: {}", self.num_gates() - before); - BigUintTarget { limbs: combined_limbs, } diff --git a/plonky2/src/gadgets/ecdsa.rs b/plonky2/src/gadgets/ecdsa.rs index 6466cea7..8238cbad 100644 --- a/plonky2/src/gadgets/ecdsa.rs +++ b/plonky2/src/gadgets/ecdsa.rs @@ -4,7 +4,7 @@ use crate::curve::curve_types::Curve; use crate::field::extension_field::Extendable; use crate::field::field_types::RichField; use crate::gadgets::biguint::BigUintTarget; -use crate::gadgets::binary_arithmetic::BinaryTarget; +use crate::gadgets::arithmetic_u32::U32Target; use crate::gadgets::curve::AffinePointTarget; use crate::gadgets::nonnative::NonNativeTarget; use crate::iop::target::{BoolTarget, Target}; @@ -38,7 +38,7 @@ impl, const D: usize> CircuitBuilder { for &bit in rev_bits { sum = self.mul_add(two, sum, bit.target); } - let limbs = vec![BinaryTarget::<30>(sum)]; + let limbs = vec![U32Target(sum)]; let value = BigUintTarget { limbs }; NonNativeTarget { diff --git a/plonky2/src/gadgets/multiple_comparison.rs b/plonky2/src/gadgets/multiple_comparison.rs index bca337fe..434fb6c1 100644 --- a/plonky2/src/gadgets/multiple_comparison.rs +++ b/plonky2/src/gadgets/multiple_comparison.rs @@ -59,11 +59,11 @@ impl, const D: usize> CircuitBuilder { } /// Helper function for comparing, specifically, lists of `U32Target`s. - pub fn list_le_30(&mut self, a: Vec>, b: Vec>) -> BoolTarget { + pub fn list_le_u32(&mut self, a: Vec, b: Vec) -> BoolTarget { let a_targets: Vec = a.iter().map(|&t| t.0).collect(); let b_targets: Vec = b.iter().map(|&t| t.0).collect(); - self.list_le(a_targets, b_targets, 30) + self.list_le(a_targets, b_targets, 32) } } diff --git a/plonky2/src/gadgets/nonnative.rs b/plonky2/src/gadgets/nonnative.rs index 20ecd382..7a62b04f 100644 --- a/plonky2/src/gadgets/nonnative.rs +++ b/plonky2/src/gadgets/nonnative.rs @@ -248,7 +248,7 @@ impl, const D: usize> CircuitBuilder { } pub fn bool_to_nonnative(&mut self, b: &BoolTarget) -> NonNativeTarget { - let limbs = vec![BinaryTarget::<30>(b.target)]; + let limbs = vec![U32Target(b.target)]; let value = BigUintTarget { limbs }; NonNativeTarget { diff --git a/plonky2/src/iop/generator.rs b/plonky2/src/iop/generator.rs index 70f99d54..d4e37dcb 100644 --- a/plonky2/src/iop/generator.rs +++ b/plonky2/src/iop/generator.rs @@ -171,21 +171,20 @@ impl GeneratedValues { } pub fn set_biguint_target(&mut self, target: BigUintTarget, value: BigUint) { - let base = BigUint::from_u64(1 << 30).unwrap(); + let base = BigUint::from_u64(1 << 32).unwrap(); let mut limbs = Vec::new(); let mut current = value.clone(); while current > BigUint::zero() { let (div, rem) = current.div_rem(&base); current = div; - let rem_u64 = rem.to_u64_digits()[0]; - limbs.push(F::from_canonical_u64(rem_u64)); + limbs.push(rem.to_u64_digits()[0] as u32); } assert!(target.num_limbs() >= limbs.len()); - limbs.resize(target.num_limbs(), F::ZERO); + limbs.resize(target.num_limbs(), 0); for i in 0..target.num_limbs() { - self.set_binary_target(target.get_limb(i), limbs[i]); + self.set_u32_target(target.get_limb(i), limbs[i]); } } diff --git a/plonky2/src/iop/witness.rs b/plonky2/src/iop/witness.rs index 558832d6..efe4d911 100644 --- a/plonky2/src/iop/witness.rs +++ b/plonky2/src/iop/witness.rs @@ -65,7 +65,7 @@ pub trait Witness { fn get_biguint_target(&self, target: BigUintTarget) -> BigUint { let mut result = BigUint::zero(); - let limb_base = BigUint::from_u64(1 << 30u64).unwrap(); + let limb_base = BigUint::from_u64(1 << 32u64).unwrap(); for i in (0..target.num_limbs()).rev() { let limb = target.get_limb(i); result *= &limb_base; From 3bbedecddb0114c3c66efc7610425f26a1c1bfbb Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 20 Jan 2022 16:11:30 -0800 Subject: [PATCH 12/36] ECDSA merge --- plonky2/src/gadgets/arithmetic_u32.rs | 69 +++++++-- plonky2/src/gadgets/biguint.rs | 16 +- plonky2/src/gadgets/ecdsa.rs | 2 +- plonky2/src/gadgets/nonnative.rs | 1 + plonky2/src/gates/add_many_u32.rs | 210 ++++++++++++++------------ plonky2/src/plonk/circuit_builder.rs | 59 +++++++- 6 files changed, 240 insertions(+), 117 deletions(-) diff --git a/plonky2/src/gadgets/arithmetic_u32.rs b/plonky2/src/gadgets/arithmetic_u32.rs index af1682f6..9bf78d44 100644 --- a/plonky2/src/gadgets/arithmetic_u32.rs +++ b/plonky2/src/gadgets/arithmetic_u32.rs @@ -114,18 +114,57 @@ impl, const D: usize> CircuitBuilder { 1 => (to_add[0], self.zero_u32()), 2 => self.add_u32(to_add[0], to_add[1]), _ => { - let (mut low, mut carry) = self.add_u32(to_add[0], to_add[1]); - for i in 2..to_add.len() { - let (new_low, new_carry) = self.add_u32(to_add[i], low); - let (combined_carry, _zero) = self.add_u32(carry, new_carry); - low = new_low; - carry = combined_carry; + let num_addends = to_add.len(); + let gate = U32AddManyGate::::new_from_config(&self.config, num_addends); + let (gate_index, copy) = self.find_u32_add_many_gate(num_addends); + + for j in 0..num_addends { + self.connect( + Target::wire(gate_index, gate.wire_ith_op_jth_addend(copy, j)), + to_add[j].0, + ); } - (low, carry) + let zero = self.zero(); + self.connect(Target::wire(gate_index, gate.wire_ith_carry(copy)), zero); + + let output_low = + U32Target(Target::wire(gate_index, gate.wire_ith_output_result(copy))); + let output_high = + U32Target(Target::wire(gate_index, gate.wire_ith_output_carry(copy))); + + (output_low, output_high) } } } + pub fn add_u32s_with_carry( + &mut self, + to_add: &[U32Target], + carry: U32Target, + ) -> (U32Target, U32Target) { + if to_add.len() == 1 { + return self.add_u32(to_add[0], carry); + } + + let num_addends = to_add.len(); + + let gate = U32AddManyGate::::new_from_config(&self.config, num_addends); + let (gate_index, copy) = self.find_u32_add_many_gate(num_addends); + + for j in 0..num_addends { + self.connect( + Target::wire(gate_index, gate.wire_ith_op_jth_addend(copy, j)), + to_add[j].0, + ); + } + self.connect(Target::wire(gate_index, gate.wire_ith_carry(copy)), carry.0); + + let output = U32Target(Target::wire(gate_index, gate.wire_ith_output_result(copy))); + let output_carry = U32Target(Target::wire(gate_index, gate.wire_ith_output_carry(copy))); + + (output, output_carry) + } + pub fn mul_u32(&mut self, a: U32Target, b: U32Target) -> (U32Target, U32Target) { let zero = self.zero_u32(); self.mul_add_u32(a, b, zero) @@ -214,6 +253,7 @@ mod tests { pub fn test_add_many_u32s() -> Result<()> { type F = GoldilocksField; const D: usize = 4; + const NUM_ADDENDS: usize = 15; let config = CircuitConfig::standard_recursion_config(); @@ -222,10 +262,19 @@ mod tests { let mut rng = thread_rng(); let mut to_add = Vec::new(); - for _ in 0..10 { - to_add.push(builder.constant_u32(rng.gen())); + let mut sum = 0u64; + for _ in 0..NUM_ADDENDS { + let x: u32 = rng.gen(); + sum += x as u64; + to_add.push(builder.constant_u32(x)); } - let _ = builder.add_many_u32(&to_add); + let carry = builder.zero_u32(); + let (result_low, result_high) = builder.add_u32s_with_carry(&to_add, carry); + let expected_low = builder.constant_u32((sum % (1 << 32)) as u32); + let expected_high = builder.constant_u32((sum >> 32) as u32); + + builder.connect_u32(result_low, expected_low); + builder.connect_u32(result_high, expected_high); let data = builder.build(); let proof = data.prove(pw).unwrap(); diff --git a/plonky2/src/gadgets/biguint.rs b/plonky2/src/gadgets/biguint.rs index 559d5b92..24d3da7d 100644 --- a/plonky2/src/gadgets/biguint.rs +++ b/plonky2/src/gadgets/biguint.rs @@ -35,10 +35,7 @@ impl, const D: usize> CircuitBuilder { current = div; limb_values.push(rem.to_u64_digits()[0] as u32); } - let limbs = limb_values - .iter() - .map(|&l| self.constant_u32(l)) - .collect(); + let limbs = limb_values.iter().map(|&l| self.constant_u32(l)).collect(); BigUintTarget { limbs } } @@ -151,8 +148,7 @@ impl, const D: usize> CircuitBuilder { let mut combined_limbs = vec![]; let mut carry = self.zero_u32(); for summands in &mut to_add { - summands.push(carry); - let (new_result, new_carry) = self.add_many_u32(summands); + let (new_result, new_carry) = self.add_u32s_with_carry(summands, carry); combined_limbs.push(new_result); carry = new_carry; } @@ -400,11 +396,11 @@ mod tests { let y = builder.constant_biguint(&y_value); let (div, rem) = builder.div_rem_biguint(&x, &y); - let expected_div = builder.constant_biguint(&expected_div_value); - let expected_rem = builder.constant_biguint(&expected_rem_value); + // let expected_div = builder.constant_biguint(&expected_div_value); + // let expected_rem = builder.constant_biguint(&expected_rem_value); - builder.connect_biguint(&div, &expected_div); - builder.connect_biguint(&rem, &expected_rem); + // builder.connect_biguint(&div, &expected_div); + // builder.connect_biguint(&rem, &expected_rem); let data = builder.build::(); let proof = data.prove(pw).unwrap(); diff --git a/plonky2/src/gadgets/ecdsa.rs b/plonky2/src/gadgets/ecdsa.rs index 8238cbad..362fbc6d 100644 --- a/plonky2/src/gadgets/ecdsa.rs +++ b/plonky2/src/gadgets/ecdsa.rs @@ -3,8 +3,8 @@ use std::marker::PhantomData; use crate::curve::curve_types::Curve; use crate::field::extension_field::Extendable; use crate::field::field_types::RichField; -use crate::gadgets::biguint::BigUintTarget; use crate::gadgets::arithmetic_u32::U32Target; +use crate::gadgets::biguint::BigUintTarget; use crate::gadgets::curve::AffinePointTarget; use crate::gadgets::nonnative::NonNativeTarget; use crate::iop::target::{BoolTarget, Target}; diff --git a/plonky2/src/gadgets/nonnative.rs b/plonky2/src/gadgets/nonnative.rs index 7a62b04f..fd3dab87 100644 --- a/plonky2/src/gadgets/nonnative.rs +++ b/plonky2/src/gadgets/nonnative.rs @@ -430,6 +430,7 @@ mod tests { let x = builder.constant_nonnative(x_ff); let y = builder.constant_nonnative(y_ff); + println!("LIMBS LIMBS LIMBS {}", y.value.limbs.len()); let product = builder.mul_nonnative(&x, &y); let product_expected = builder.constant_nonnative(product_ff); diff --git a/plonky2/src/gates/add_many_u32.rs b/plonky2/src/gates/add_many_u32.rs index 7ad8523d..571106e2 100644 --- a/plonky2/src/gates/add_many_u32.rs +++ b/plonky2/src/gates/add_many_u32.rs @@ -13,9 +13,10 @@ use crate::iop::witness::{PartitionWitness, Witness}; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_data::CircuitConfig; use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; +use crate::util::ceil_div_usize; -const LOG2_MAX_NUM_ADDENDS: usize = 6; -const MAX_NUM_ADDENDS: usize = 1 << LOG2_MAX_NUM_ADDENDS; +const LOG2_MAX_NUM_ADDENDS: usize = 4; +const MAX_NUM_ADDENDS: usize = 16; /// A gate to perform addition on `num_addends` different 32-bit values, plus a small carry #[derive(Copy, Clone, Debug)] @@ -26,7 +27,7 @@ pub struct U32AddManyGate, const D: usize> { } impl, const D: usize> U32AddManyGate { - pub fn new_from_config(num_addends: usize, config: &CircuitConfig) -> Self { + pub fn new_from_config(config: &CircuitConfig, num_addends: usize) -> Self { Self { num_addends, num_ops: Self::num_ops(num_addends, config), @@ -35,7 +36,7 @@ impl, const D: usize> U32AddManyGate { } pub(crate) fn num_ops(num_addends: usize, config: &CircuitConfig) -> usize { - debug_assert!(num_addends < MAX_NUM_ADDENDS); + debug_assert!(num_addends <= MAX_NUM_ADDENDS); let wires_per_op = (num_addends + 3) + Self::num_limbs(); let routed_wires_per_op = 5; (config.num_wires / wires_per_op).min(config.num_routed_wires / routed_wires_per_op) @@ -43,7 +44,7 @@ impl, const D: usize> U32AddManyGate { pub fn wire_ith_op_jth_addend(&self, i: usize, j: usize) -> usize { debug_assert!(i < self.num_ops); - debug_assert!(i < self.num_addends); + debug_assert!(j < self.num_addends); (self.num_addends + 3) * i + j } pub fn wire_ith_carry(&self, i: usize) -> usize { @@ -51,11 +52,11 @@ impl, const D: usize> U32AddManyGate { (self.num_addends + 3) * i + self.num_addends } - pub fn wire_ith_output_low_half(&self, i: usize) -> usize { + pub fn wire_ith_output_result(&self, i: usize) -> usize { debug_assert!(i < self.num_ops); (self.num_addends + 3) * i + self.num_addends + 1 } - pub fn wire_ith_output_high_half(&self, i: usize) -> usize { + pub fn wire_ith_output_carry(&self, i: usize) -> usize { debug_assert!(i < self.num_ops); (self.num_addends + 3) * i + self.num_addends + 2 } @@ -63,8 +64,14 @@ impl, const D: usize> U32AddManyGate { pub fn limb_bits() -> usize { 2 } + pub fn num_result_limbs() -> usize { + ceil_div_usize(32, Self::limb_bits()) + } + pub fn num_carry_limbs() -> usize { + ceil_div_usize(LOG2_MAX_NUM_ADDENDS, Self::limb_bits()) + } pub fn num_limbs() -> usize { - 32 / Self::limb_bits() + Self::num_result_limbs() + Self::num_carry_limbs() } pub fn wire_ith_output_jth_limb(&self, i: usize, j: usize) -> usize { @@ -85,19 +92,20 @@ impl, const D: usize> Gate for U32AddManyGate let addends: Vec = (0..self.num_addends) .map(|j| vars.local_wires[self.wire_ith_op_jth_addend(i, j)]) .collect(); - let borrow = vars.local_wires[self.wire_ith_carry(i)]; + let carry = vars.local_wires[self.wire_ith_carry(i)]; - let computed_output = addends.iter().fold(F::Extension::ZERO, |x, &y| x + y) + borrow; + let computed_output = addends.iter().fold(F::Extension::ZERO, |x, &y| x + y) + carry; - let output_low = vars.local_wires[self.wire_ith_output_low_half(i)]; - let output_high = vars.local_wires[self.wire_ith_output_high_half(i)]; + let output_result = vars.local_wires[self.wire_ith_output_result(i)]; + let output_carry = vars.local_wires[self.wire_ith_output_carry(i)]; let base = F::Extension::from_canonical_u64(1 << 32u64); - let combined_output = output_high * base + output_low; + let combined_output = output_carry * base + output_result; constraints.push(combined_output - computed_output); - let mut combined_low_limbs = F::Extension::ZERO; + let mut combined_result_limbs = F::Extension::ZERO; + let mut combined_carry_limbs = F::Extension::ZERO; let base = F::Extension::from_canonical_u64(1u64 << Self::limb_bits()); for j in (0..Self::num_limbs()).rev() { let this_limb = vars.local_wires[self.wire_ith_output_jth_limb(i, j)]; @@ -107,15 +115,14 @@ impl, const D: usize> Gate for U32AddManyGate .product(); constraints.push(product); - combined_low_limbs = base * combined_low_limbs + this_limb; + if j < Self::num_result_limbs() { + combined_result_limbs = base * combined_result_limbs + this_limb; + } else { + combined_carry_limbs = base * combined_carry_limbs + this_limb; + } } - constraints.push(combined_low_limbs - output_low); - - let max_overflow = self.num_addends; - let product = (0..max_overflow) - .map(|x| output_high - F::Extension::from_canonical_usize(x)) - .product(); - constraints.push(product); + constraints.push(combined_result_limbs - output_result); + constraints.push(combined_carry_limbs - output_carry); } constraints @@ -127,19 +134,20 @@ impl, const D: usize> Gate for U32AddManyGate let addends: Vec = (0..self.num_addends) .map(|j| vars.local_wires[self.wire_ith_op_jth_addend(i, j)]) .collect(); - let borrow = vars.local_wires[self.wire_ith_carry(i)]; + let carry = vars.local_wires[self.wire_ith_carry(i)]; - let computed_output = addends.iter().fold(F::ZERO, |x, &y| x + y) + borrow; + let computed_output = addends.iter().fold(F::ZERO, |x, &y| x + y) + carry; - let output_low = vars.local_wires[self.wire_ith_output_low_half(i)]; - let output_high = vars.local_wires[self.wire_ith_output_high_half(i)]; + let output_result = vars.local_wires[self.wire_ith_output_result(i)]; + let output_carry = vars.local_wires[self.wire_ith_output_carry(i)]; let base = F::from_canonical_u64(1 << 32u64); - let combined_output = output_high * base + output_low; + let combined_output = output_carry * base + output_result; constraints.push(combined_output - computed_output); - let mut combined_low_limbs = F::ZERO; + let mut combined_result_limbs = F::ZERO; + let mut combined_carry_limbs = F::ZERO; let base = F::from_canonical_u64(1u64 << Self::limb_bits()); for j in (0..Self::num_limbs()).rev() { let this_limb = vars.local_wires[self.wire_ith_output_jth_limb(i, j)]; @@ -149,15 +157,14 @@ impl, const D: usize> Gate for U32AddManyGate .product(); constraints.push(product); - combined_low_limbs = base * combined_low_limbs + this_limb; + if j < Self::num_result_limbs() { + combined_result_limbs = base * combined_result_limbs + this_limb; + } else { + combined_carry_limbs = base * combined_carry_limbs + this_limb; + } } - constraints.push(combined_low_limbs - output_low); - - let max_overflow = self.num_addends; - let product = (0..max_overflow) - .map(|x| output_high - F::from_canonical_usize(x)) - .product(); - constraints.push(product); + constraints.push(combined_result_limbs - output_result); + constraints.push(combined_carry_limbs - output_carry); } constraints @@ -174,23 +181,25 @@ impl, const D: usize> Gate for U32AddManyGate let addends: Vec> = (0..self.num_addends) .map(|j| vars.local_wires[self.wire_ith_op_jth_addend(i, j)]) .collect(); - let borrow = vars.local_wires[self.wire_ith_carry(i)]; + let carry = vars.local_wires[self.wire_ith_carry(i)]; - let mut computed_output = borrow; + let mut computed_output = carry; for addend in addends { computed_output = builder.add_extension(computed_output, addend); } - let output_low = vars.local_wires[self.wire_ith_output_low_half(i)]; - let output_high = vars.local_wires[self.wire_ith_output_high_half(i)]; + let output_result = vars.local_wires[self.wire_ith_output_result(i)]; + let output_carry = vars.local_wires[self.wire_ith_output_carry(i)]; let base: F::Extension = F::from_canonical_u64(1 << 32u64).into(); let base_target = builder.constant_extension(base); - let combined_output = builder.mul_add_extension(output_high, base_target, output_low); + let combined_output = + builder.mul_add_extension(output_carry, base_target, output_result); constraints.push(builder.sub_extension(combined_output, computed_output)); - let mut combined_low_limbs = builder.zero_extension(); + let mut combined_result_limbs = builder.zero_extension(); + let mut combined_carry_limbs = builder.zero_extension(); let base = builder .constant_extension(F::Extension::from_canonical_u64(1u64 << Self::limb_bits())); for j in (0..Self::num_limbs()).rev() { @@ -206,18 +215,16 @@ impl, const D: usize> Gate for U32AddManyGate } constraints.push(product); - combined_low_limbs = builder.mul_add_extension(base, combined_low_limbs, this_limb); + if j < Self::num_result_limbs() { + combined_result_limbs = + builder.mul_add_extension(base, combined_result_limbs, this_limb); + } else { + combined_carry_limbs = + builder.mul_add_extension(base, combined_carry_limbs, this_limb); + } } - constraints.push(builder.sub_extension(combined_low_limbs, output_low)); - - let max_overflow = self.num_addends; - let mut product = builder.one_extension(); - for x in 0..max_overflow { - let x_target = builder.constant_extension(F::Extension::from_canonical_usize(x)); - let diff = builder.sub_extension(output_high, x_target); - product = builder.mul_extension(product, diff); - } - constraints.push(product); + constraints.push(builder.sub_extension(combined_result_limbs, output_result)); + constraints.push(builder.sub_extension(combined_carry_limbs, output_carry)); } constraints @@ -289,37 +296,46 @@ impl, const D: usize> SimpleGenerator let get_local_wire = |input| witness.get_wire(local_wire(input)); - let addends: Vec<_> = (0..self.gate.num_addends).map(|j| get_local_wire(self.gate.wire_ith_output_jth_limb(self.i, j))).collect(); + let addends: Vec<_> = (0..self.gate.num_addends) + .map(|j| get_local_wire(self.gate.wire_ith_op_jth_addend(self.i, j))) + .collect(); let carry = get_local_wire(self.gate.wire_ith_carry(self.i)); let output = addends.iter().fold(F::ZERO, |x, &y| x + y) + carry; - let mut output_u64 = output.to_canonical_u64(); + let output_u64 = output.to_canonical_u64(); - let output_high_u64 = output_u64 >> 32; - let output_low_u64 = output_u64 & ((1 << 32) - 1); + let output_carry_u64 = output_u64 >> 32; + let output_result_u64 = output_u64 & ((1 << 32) - 1); - let output_high = F::from_canonical_u64(output_high_u64); - let output_low = F::from_canonical_u64(output_low_u64); + let output_carry = F::from_canonical_u64(output_carry_u64); + let output_result = F::from_canonical_u64(output_result_u64); - let output_high_wire = local_wire(self.gate.wire_ith_output_high_half(self.i)); - let output_low_wire = local_wire(self.gate.wire_ith_output_low_half(self.i)); + let output_carry_wire = local_wire(self.gate.wire_ith_output_carry(self.i)); + let output_result_wire = local_wire(self.gate.wire_ith_output_result(self.i)); - out_buffer.set_wire(output_high_wire, output_high); - out_buffer.set_wire(output_low_wire, output_low); + out_buffer.set_wire(output_carry_wire, output_carry); + out_buffer.set_wire(output_result_wire, output_result); - let num_limbs = U32AddManyGate::::num_limbs(); + let num_result_limbs = U32AddManyGate::::num_result_limbs(); + let num_carry_limbs = U32AddManyGate::::num_carry_limbs(); let limb_base = 1 << U32AddManyGate::::limb_bits(); - let output_limbs_u64 = unfold((), move |_| { - let ret = output_u64 % limb_base; - output_u64 /= limb_base; - Some(ret) - }) - .take(num_limbs); - let output_limbs_f = output_limbs_u64.map(F::from_canonical_u64); - for (j, output_limb) in output_limbs_f.enumerate() { + let split_to_limbs = |mut val, num| { + unfold((), move |_| { + let ret = val % limb_base; + val /= limb_base; + Some(ret) + }) + .take(num) + .map(F::from_canonical_u64) + }; + + let result_limbs = split_to_limbs(output_result_u64, num_result_limbs); + let carry_limbs = split_to_limbs(output_carry_u64, num_carry_limbs); + + for (j, limb) in result_limbs.chain(carry_limbs).enumerate() { let wire = local_wire(self.gate.wire_ith_output_jth_limb(self.i, j)); - out_buffer.set_wire(wire, output_limb); + out_buffer.set_wire(wire, limb); } } } @@ -329,6 +345,7 @@ mod tests { use std::marker::PhantomData; use anyhow::Result; + use itertools::unfold; use rand::Rng; use crate::field::extension_field::quartic::QuarticExtension; @@ -363,44 +380,47 @@ mod tests { type F = GoldilocksField; type FF = QuarticExtension; const D: usize = 4; - const NUM_ADDENDS: usize = 4; + const NUM_ADDENDS: usize = 10; const NUM_U32_ADD_MANY_OPS: usize = 3; - fn get_wires( - addends: Vec>, - carries: Vec, - ) -> Vec { + fn get_wires(addends: Vec>, carries: Vec) -> Vec { let mut v0 = Vec::new(); let mut v1 = Vec::new(); - let limb_bits = U32AddManyGate::::limb_bits(); - let num_limbs = U32AddManyGate::::num_limbs(); - let limb_base = 1 << limb_bits; + let num_result_limbs = U32AddManyGate::::num_result_limbs(); + let num_carry_limbs = U32AddManyGate::::num_carry_limbs(); + let limb_base = 1 << U32AddManyGate::::limb_bits(); for op in 0..NUM_U32_ADD_MANY_OPS { let adds = &addends[op]; let ca = carries[op]; - let mut output = adds.iter().sum::() + ca; - let output_low = output & ((1 << 32) - 1); - let output_high = output >> 32; + let output = adds.iter().sum::() + ca; + let output_result = output & ((1 << 32) - 1); + let output_carry = output >> 32; - let mut output_limbs = Vec::with_capacity(num_limbs); - for _i in 0..num_limbs { - output_limbs.push(output % limb_base); - output /= limb_base; - } - let mut output_limbs_f: Vec<_> = output_limbs - .into_iter() + let split_to_limbs = |mut val, num| { + unfold((), move |_| { + let ret = val % limb_base; + val /= limb_base; + Some(ret) + }) + .take(num) .map(F::from_canonical_u64) - .collect(); + }; + + let mut result_limbs: Vec<_> = + split_to_limbs(output_result, num_result_limbs).collect(); + let mut carry_limbs: Vec<_> = + split_to_limbs(output_carry, num_carry_limbs).collect(); for a in adds { v0.push(F::from_canonical_u64(*a)); } v0.push(F::from_canonical_u64(ca)); - v0.push(F::from_canonical_u64(output_low)); - v0.push(F::from_canonical_u64(output_high)); - v1.append(&mut output_limbs_f); + v0.push(F::from_canonical_u64(output_result)); + v0.push(F::from_canonical_u64(output_carry)); + v1.append(&mut result_limbs); + v1.append(&mut carry_limbs); } v0.iter() diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index 058c5d0d..d9f79f59 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -823,6 +823,10 @@ pub struct BatchedGates, const D: usize> { /// of switches pub(crate) current_switch_gates: Vec, usize, usize)>>, + /// A map `n -> (g, i)` from `n` number of addends to an available `U32AddManyGate` of that size with gate + /// index `g` and already using `i` random accesses. + pub(crate) free_u32_add_many: HashMap, + /// The `U32ArithmeticGate` currently being filled (so new u32 arithmetic operations will be added to this gate before creating a new one) pub(crate) current_u32_arithmetic_gate: Option<(usize, usize)>, /// The `U32SubtractionGate` currently being filled (so new u32 subtraction operations will be added to this gate before creating a new one) @@ -845,6 +849,7 @@ impl, const D: usize> BatchedGates { free_mul: HashMap::new(), free_random_access: HashMap::new(), current_switch_gates: Vec::new(), + free_u32_add_many: HashMap::new(), current_u32_arithmetic_gate: None, current_u32_subtraction_gate: None, free_binary_arithmetic_gate: HashMap::new(), @@ -944,7 +949,7 @@ impl, const D: usize> CircuitBuilder { (gate, i) } - /// Finds the last available random access gate with the given `bits` or add one if there aren't any. + /// Finds the last available random access gate with the given `bits` or adds one if there aren't any. /// Returns `(g,i)` such that there is a random access gate for the given `bits` at index /// `g` and the gate's `i`-th random access is available. pub(crate) fn find_random_access_gate(&mut self, bits: usize) -> (usize, usize) { @@ -1007,6 +1012,35 @@ impl, const D: usize> CircuitBuilder { (gate, gate_index, next_copy) } + /// Finds the last available U32 add-many gate with the given `num_addends` or adds one if there aren't any. + /// Returns `(g,i)` such that there is a `U32AddManyGate` for the given `num_addends` at index + /// `g` and the gate's `i`-th copy is available. + pub(crate) fn find_u32_add_many_gate(&mut self, num_addends: usize) -> (usize, usize) { + let (gate, i) = self + .batched_gates + .free_u32_add_many + .get(&num_addends) + .copied() + .unwrap_or_else(|| { + let gate = self.add_gate( + U32AddManyGate::new_from_config(&self.config, num_addends), + vec![], + ); + (gate, 0) + }); + + // Update `free_u32_add_many` with new values. + if i + 1 < U32AddManyGate::::new_from_config(&self.config, num_addends).num_ops { + self.batched_gates + .free_u32_add_many + .insert(num_addends, (gate, i + 1)); + } else { + self.batched_gates.free_u32_add_many.remove(&num_addends); + } + + (gate, i) + } + pub(crate) fn find_u32_arithmetic_gate(&mut self) -> (usize, usize) { let (gate_index, copy) = match self.batched_gates.current_u32_arithmetic_gate { None => { @@ -1213,6 +1247,28 @@ impl, const D: usize> CircuitBuilder { } } + /// Fill the remaining unused u32 add-many operations with zeros, so that all + /// `U32AddManyGenerator`s are run. + fn fill_u32_add_many_gates(&mut self) { + let zero = self.zero_u32(); + for (num_addends, (_, i)) in self.batched_gates.free_u32_add_many.clone() { + let max_copies = + U32AddManyGate::::new_from_config(&self.config, num_addends).num_ops; + for _ in i..max_copies { + let gate = U32AddManyGate::::new_from_config(&self.config, num_addends); + let (gate_index, copy) = self.find_u32_add_many_gate(num_addends); + + for j in 0..num_addends { + self.connect( + Target::wire(gate_index, gate.wire_ith_op_jth_addend(copy, j)), + zero.0, + ); + } + self.connect(Target::wire(gate_index, gate.wire_ith_carry(copy)), zero.0); + } + } + } + /// Fill the remaining unused U32 arithmetic operations with zeros, so that all /// `U32ArithmeticGenerator`s are run. fn fill_u32_arithmetic_gates(&mut self) { @@ -1275,6 +1331,7 @@ impl, const D: usize> CircuitBuilder { self.fill_mul_gates(); self.fill_random_access_gates(); self.fill_switch_gates(); + self.fill_u32_add_many_gates(); self.fill_u32_arithmetic_gates(); self.fill_u32_subtraction_gates(); self.fill_binary_arithmetic_gates(); From facb5661f3fbcd4d77ebfe2c0d88c2d56bc67167 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 12 Jan 2022 15:49:27 -0800 Subject: [PATCH 13/36] fix --- plonky2/src/gates/add_many_u32.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plonky2/src/gates/add_many_u32.rs b/plonky2/src/gates/add_many_u32.rs index 571106e2..c8b5f8af 100644 --- a/plonky2/src/gates/add_many_u32.rs +++ b/plonky2/src/gates/add_many_u32.rs @@ -38,7 +38,7 @@ impl, const D: usize> U32AddManyGate { pub(crate) fn num_ops(num_addends: usize, config: &CircuitConfig) -> usize { debug_assert!(num_addends <= MAX_NUM_ADDENDS); let wires_per_op = (num_addends + 3) + Self::num_limbs(); - let routed_wires_per_op = 5; + let routed_wires_per_op = num_addends + 3; (config.num_wires / wires_per_op).min(config.num_routed_wires / routed_wires_per_op) } From 50c24dfe8af84275900d32eb28d1735d4f4fce24 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 18 Jan 2022 14:07:47 -0800 Subject: [PATCH 14/36] more efficient nonnative add and multi-add --- plonky2/src/gadgets/biguint.rs | 24 +++- plonky2/src/gadgets/curve.rs | 4 +- plonky2/src/gadgets/nonnative.rs | 186 ++++++++++++++++++++----------- plonky2/src/gates/mod.rs | 1 + plonky2/src/iop/generator.rs | 4 + 5 files changed, 151 insertions(+), 68 deletions(-) diff --git a/plonky2/src/gadgets/biguint.rs b/plonky2/src/gadgets/biguint.rs index 24d3da7d..1a829f97 100644 --- a/plonky2/src/gadgets/biguint.rs +++ b/plonky2/src/gadgets/biguint.rs @@ -40,6 +40,10 @@ impl, const D: usize> CircuitBuilder { BigUintTarget { limbs } } + pub fn zero_biguint(&mut self) -> BigUintTarget { + self.constant_biguint(&BigUint::zero()) + } + pub fn connect_biguint(&mut self, lhs: &BigUintTarget, rhs: &BigUintTarget) { let min_limbs = lhs.num_limbs().min(rhs.num_limbs()); for i in 0..min_limbs { @@ -159,6 +163,18 @@ impl, const D: usize> CircuitBuilder { } } + pub fn mul_biguint_by_bool( + &mut self, + a: &BigUintTarget, + b: BoolTarget, + ) -> BigUintTarget { + let t = b.target; + + BigUintTarget { + limbs: a.limbs.iter().map(|l| U32Target(self.mul(l.0, t))).collect() + } + } + // 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, @@ -396,11 +412,11 @@ mod tests { let y = builder.constant_biguint(&y_value); let (div, rem) = builder.div_rem_biguint(&x, &y); - // let expected_div = builder.constant_biguint(&expected_div_value); - // let expected_rem = builder.constant_biguint(&expected_rem_value); + let expected_div = builder.constant_biguint(&expected_div_value); + let expected_rem = builder.constant_biguint(&expected_rem_value); - // builder.connect_biguint(&div, &expected_div); - // builder.connect_biguint(&rem, &expected_rem); + builder.connect_biguint(&div, &expected_div); + builder.connect_biguint(&rem, &expected_rem); let data = builder.build::(); let proof = data.prove(pw).unwrap(); diff --git a/plonky2/src/gadgets/curve.rs b/plonky2/src/gadgets/curve.rs index 63e96721..907aa5e3 100644 --- a/plonky2/src/gadgets/curve.rs +++ b/plonky2/src/gadgets/curve.rs @@ -100,6 +100,7 @@ impl, const D: usize> CircuitBuilder { p1: &AffinePointTarget, p2: &AffinePointTarget, ) -> AffinePointTarget { + let before = self.num_gates(); let AffinePointTarget { x: x1, y: y1 } = p1; let AffinePointTarget { x: x2, y: y2 } = p2; @@ -123,6 +124,7 @@ impl, const D: usize> CircuitBuilder { let x3_norm = self.mul_nonnative(&x3, &z3_inv); let y3_norm = self.mul_nonnative(&y3, &z3_inv); + println!("NUM GATES: {}", self.num_gates() - before); AffinePointTarget { x: x3_norm, y: y3_norm, @@ -310,7 +312,6 @@ mod tests { } #[test] - #[ignore] fn test_curve_mul() -> Result<()> { const D: usize = 2; type C = PoseidonGoldilocksConfig; @@ -345,7 +346,6 @@ mod tests { } #[test] - #[ignore] fn test_curve_random() -> Result<()> { const D: usize = 2; type C = PoseidonGoldilocksConfig; diff --git a/plonky2/src/gadgets/nonnative.rs b/plonky2/src/gadgets/nonnative.rs index fd3dab87..1006b513 100644 --- a/plonky2/src/gadgets/nonnative.rs +++ b/plonky2/src/gadgets/nonnative.rs @@ -60,16 +60,45 @@ impl, const D: usize> CircuitBuilder { } } - // Add two `NonNativeTarget`s. pub fn add_nonnative( &mut self, a: &NonNativeTarget, b: &NonNativeTarget, ) -> NonNativeTarget { - let result = self.add_biguint(&a.value, &b.value); + let sum = self.add_virtual_nonnative_target::(); + let overflow = self.add_virtual_bool_target(); - // TODO: reduce add result with only one conditional subtraction - self.reduce(&result) + self.add_simple_generator(NonNativeAdditionGenerator:: { + a: a.clone(), + b: b.clone(), + sum: sum.clone(), + overflow: overflow.clone(), + _phantom: PhantomData, + }); + + let sum_expected = self.add_biguint(&a.value, &b.value); + + let modulus = self.constant_biguint(&FF::order()); + let mod_times_overflow = self.mul_biguint_by_bool(&modulus, overflow); + let sum_actual = self.add_biguint(&sum.value, &mod_times_overflow); + self.connect_biguint(&sum_expected, &sum_actual); + + sum + } + + pub fn mul_nonnative_by_bool( + &mut self, + a: &NonNativeTarget, + b: BoolTarget, + ) -> NonNativeTarget { + let t = b.target; + + NonNativeTarget { + value: BigUintTarget { + limbs: a.value.limbs.iter().map(|l| U32Target(self.mul(l.0, t))).collect() + }, + _phantom: PhantomData, + } } pub fn add_many_nonnative( @@ -80,12 +109,28 @@ impl, const D: usize> CircuitBuilder { return to_add[0].clone(); } - let mut result = self.add_biguint(&to_add[0].value, &to_add[1].value); - for i in 2..to_add.len() { - result = self.add_biguint(&result, &to_add[i].value); - } + let sum = self.add_virtual_nonnative_target::(); + let overflow = self.add_virtual_u32_target(); + let summands = to_add.to_vec(); - self.reduce(&result) + self.add_simple_generator(NonNativeMultipleAddsGenerator:: { + summands: summands.clone(), + sum: sum.clone(), + overflow: overflow.clone(), + _phantom: PhantomData, + }); + + let sum_expected = summands.iter().fold(self.zero_biguint(), |a, b| self.add_biguint(&a, &b.value)); + + let modulus = self.constant_biguint(&FF::order()); + let overflow_biguint = BigUintTarget { + limbs: vec![overflow], + }; + let mod_times_overflow = self.mul_biguint(&modulus, &overflow_biguint); + let sum_actual = self.add_biguint(&sum.value, &mod_times_overflow); + self.connect_biguint(&sum_expected, &sum_actual); + + sum } // Subtract two `NonNativeTarget`s. @@ -188,59 +233,6 @@ impl, const D: usize> CircuitBuilder { } } - /// Returns `x % |FF|` as a `NonNativeTarget`. - /*fn reduce_by_bits(&mut self, x: &BigUintTarget) -> NonNativeTarget { - let before = self.num_gates(); - - let mut powers_of_two = Vec::new(); - let mut cur_power_of_two = FF::ONE; - let two = FF::TWO; - let mut max_num_limbs = 0; - for _ in 0..(x.limbs.len() * 32) { - let cur_power = self.constant_biguint(&cur_power_of_two.to_biguint()); - max_num_limbs = max_num_limbs.max(cur_power.limbs.len()); - powers_of_two.push(cur_power.limbs); - - cur_power_of_two *= two; - } - - let mut result_limbs_unreduced = vec![self.zero(); max_num_limbs]; - for i in 0..x.limbs.len() { - let this_limb = x.limbs[i]; - let bits = self.split_le(this_limb.0, 32); - for b in 0..bits.len() { - let this_power = powers_of_two[32 * i + b].clone(); - for x in 0..this_power.len() { - result_limbs_unreduced[x] = self.mul_add(bits[b].target, this_power[x].0, result_limbs_unreduced[x]); - } - } - } - - let mut result_limbs_reduced = Vec::new(); - let mut carry = self.zero_u32(); - for i in 0..result_limbs_unreduced.len() { - println!("{}", i); - let (low, high) = self.split_to_u32(result_limbs_unreduced[i]); - let (cur, overflow) = self.add_u32(carry, low); - let (new_carry, _) = self.add_many_u32(&[overflow, high, carry]); - result_limbs_reduced.push(cur); - carry = new_carry; - } - result_limbs_reduced.push(carry); - - let value = BigUintTarget { - limbs: result_limbs_reduced, - }; - - println!("NUMBER OF GATES: {}", self.num_gates() - before); - println!("OUTPUT LIMBS: {}", value.limbs.len()); - - NonNativeTarget { - value, - _phantom: PhantomData, - } - }*/ - #[allow(dead_code)] fn reduce_nonnative(&mut self, x: &NonNativeTarget) -> NonNativeTarget { let x_biguint = self.nonnative_to_biguint(x); @@ -280,6 +272,74 @@ impl, const D: usize> CircuitBuilder { } } +#[derive(Debug)] +struct NonNativeAdditionGenerator, const D: usize, FF: Field> { + a: NonNativeTarget, + b: NonNativeTarget, + sum: NonNativeTarget, + overflow: BoolTarget, + _phantom: PhantomData, +} + +impl, const D: usize, FF: Field> SimpleGenerator + for NonNativeAdditionGenerator +{ + fn dependencies(&self) -> Vec { + self.a.value.limbs.iter().cloned().chain(self.b.value.limbs.clone()) + .map(|l| l.0) + .collect() + } + + fn run_once(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues) { + let a = witness.get_nonnative_target(self.a.clone()); + let b = witness.get_nonnative_target(self.b.clone()); + let a_biguint = a.to_biguint(); + let b_biguint = b.to_biguint(); + let sum_biguint = a_biguint + b_biguint; + let modulus = FF::order(); + let (overflow, sum_reduced) = if sum_biguint > modulus { + (true, sum_biguint - modulus) + } else { + (false, sum_biguint) + }; + + out_buffer.set_biguint_target(self.sum.value.clone(), sum_reduced); + out_buffer.set_bool_target(self.overflow, overflow); + } +} + +#[derive(Debug)] +struct NonNativeMultipleAddsGenerator, const D: usize, FF: Field> { + summands: Vec>, + sum: NonNativeTarget, + overflow: U32Target, + _phantom: PhantomData, +} + +impl, const D: usize, FF: Field> SimpleGenerator + for NonNativeMultipleAddsGenerator +{ + fn dependencies(&self) -> Vec { + self.summands.iter().map(|summand| summand.value.limbs.iter().map(|limb| limb.0)) + .flatten() + .collect() + } + + fn run_once(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues) { + let summands: Vec<_> = self.summands.iter().map(|summand| witness.get_nonnative_target(summand.clone())).collect(); + let summand_biguints: Vec<_> = summands.iter().map(|summand| summand.to_biguint()).collect(); + + let sum_biguint = summand_biguints.iter().fold(BigUint::zero(), |a, b| a + b.clone()); + + let modulus = FF::order(); + let (overflow_biguint, sum_reduced) = sum_biguint.div_rem(&modulus); + let overflow = overflow_biguint.to_u64_digits()[0] as u32; + + out_buffer.set_biguint_target(self.sum.value.clone(), sum_reduced); + out_buffer.set_u32_target(self.overflow, overflow); + } +} + #[derive(Debug)] struct NonNativeInverseGenerator, const D: usize, FF: Field> { x: NonNativeTarget, @@ -310,6 +370,8 @@ impl, const D: usize, FF: Field> SimpleGenerator } } + + #[cfg(test)] mod tests { use anyhow::Result; diff --git a/plonky2/src/gates/mod.rs b/plonky2/src/gates/mod.rs index a7591648..177db7cf 100644 --- a/plonky2/src/gates/mod.rs +++ b/plonky2/src/gates/mod.rs @@ -11,6 +11,7 @@ pub mod binary_arithmetic; pub mod binary_subtraction; pub mod comparison; pub mod constant; +// pub mod curve_double; pub mod exponentiation; pub mod gate; pub mod gate_tree; diff --git a/plonky2/src/iop/generator.rs b/plonky2/src/iop/generator.rs index d4e37dcb..5f8b8a5f 100644 --- a/plonky2/src/iop/generator.rs +++ b/plonky2/src/iop/generator.rs @@ -162,6 +162,10 @@ impl GeneratedValues { self.target_values.push((target, value)) } + pub fn set_bool_target(&mut self, target: BoolTarget, value: bool) { + self.set_target(target.target, F::from_bool(value)) + } + pub fn set_u32_target(&mut self, target: U32Target, value: u32) { self.set_target(target.0, F::from_canonical_u32(value)) } From 8d3662692e6cbcec73ae35d6066fbca21202a29c Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 18 Jan 2022 14:11:21 -0800 Subject: [PATCH 15/36] fmt --- plonky2/src/gadgets/biguint.rs | 12 ++++---- plonky2/src/gadgets/nonnative.rs | 51 ++++++++++++++++++++++---------- 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/plonky2/src/gadgets/biguint.rs b/plonky2/src/gadgets/biguint.rs index 1a829f97..4f9e1574 100644 --- a/plonky2/src/gadgets/biguint.rs +++ b/plonky2/src/gadgets/biguint.rs @@ -163,15 +163,15 @@ impl, const D: usize> CircuitBuilder { } } - pub fn mul_biguint_by_bool( - &mut self, - a: &BigUintTarget, - b: BoolTarget, - ) -> BigUintTarget { + pub fn mul_biguint_by_bool(&mut self, a: &BigUintTarget, b: BoolTarget) -> BigUintTarget { let t = b.target; BigUintTarget { - limbs: a.limbs.iter().map(|l| U32Target(self.mul(l.0, t))).collect() + limbs: a + .limbs + .iter() + .map(|l| U32Target(self.mul(l.0, t))) + .collect(), } } diff --git a/plonky2/src/gadgets/nonnative.rs b/plonky2/src/gadgets/nonnative.rs index 1006b513..83333491 100644 --- a/plonky2/src/gadgets/nonnative.rs +++ b/plonky2/src/gadgets/nonnative.rs @@ -77,7 +77,7 @@ impl, const D: usize> CircuitBuilder { }); let sum_expected = self.add_biguint(&a.value, &b.value); - + let modulus = self.constant_biguint(&FF::order()); let mod_times_overflow = self.mul_biguint_by_bool(&modulus, overflow); let sum_actual = self.add_biguint(&sum.value, &mod_times_overflow); @@ -95,7 +95,12 @@ impl, const D: usize> CircuitBuilder { NonNativeTarget { value: BigUintTarget { - limbs: a.value.limbs.iter().map(|l| U32Target(self.mul(l.0, t))).collect() + limbs: a + .value + .limbs + .iter() + .map(|l| U32Target(self.mul(l.0, t))) + .collect(), }, _phantom: PhantomData, } @@ -120,8 +125,10 @@ impl, const D: usize> CircuitBuilder { _phantom: PhantomData, }); - let sum_expected = summands.iter().fold(self.zero_biguint(), |a, b| self.add_biguint(&a, &b.value)); - + let sum_expected = summands + .iter() + .fold(self.zero_biguint(), |a, b| self.add_biguint(&a, &b.value)); + let modulus = self.constant_biguint(&FF::order()); let overflow_biguint = BigUintTarget { limbs: vec![overflow], @@ -285,9 +292,14 @@ impl, const D: usize, FF: Field> SimpleGenerator for NonNativeAdditionGenerator { fn dependencies(&self) -> Vec { - self.a.value.limbs.iter().cloned().chain(self.b.value.limbs.clone()) - .map(|l| l.0) - .collect() + self.a + .value + .limbs + .iter() + .cloned() + .chain(self.b.value.limbs.clone()) + .map(|l| l.0) + .collect() } fn run_once(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues) { @@ -320,16 +332,27 @@ impl, const D: usize, FF: Field> SimpleGenerator for NonNativeMultipleAddsGenerator { fn dependencies(&self) -> Vec { - self.summands.iter().map(|summand| summand.value.limbs.iter().map(|limb| limb.0)) - .flatten() - .collect() + self.summands + .iter() + .map(|summand| summand.value.limbs.iter().map(|limb| limb.0)) + .flatten() + .collect() } fn run_once(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues) { - let summands: Vec<_> = self.summands.iter().map(|summand| witness.get_nonnative_target(summand.clone())).collect(); - let summand_biguints: Vec<_> = summands.iter().map(|summand| summand.to_biguint()).collect(); + let summands: Vec<_> = self + .summands + .iter() + .map(|summand| witness.get_nonnative_target(summand.clone())) + .collect(); + let summand_biguints: Vec<_> = summands + .iter() + .map(|summand| summand.to_biguint()) + .collect(); - let sum_biguint = summand_biguints.iter().fold(BigUint::zero(), |a, b| a + b.clone()); + let sum_biguint = summand_biguints + .iter() + .fold(BigUint::zero(), |a, b| a + b.clone()); let modulus = FF::order(); let (overflow_biguint, sum_reduced) = sum_biguint.div_rem(&modulus); @@ -370,8 +393,6 @@ impl, const D: usize, FF: Field> SimpleGenerator } } - - #[cfg(test)] mod tests { use anyhow::Result; From ddf5ee5d1f587879c7d254dc1c6c76df27a9feaa Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 18 Jan 2022 14:59:39 -0800 Subject: [PATCH 16/36] more efficient nonnative subtraction --- plonky2/src/gadgets/nonnative.rs | 63 +++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 5 deletions(-) diff --git a/plonky2/src/gadgets/nonnative.rs b/plonky2/src/gadgets/nonnative.rs index 83333491..8726fde7 100644 --- a/plonky2/src/gadgets/nonnative.rs +++ b/plonky2/src/gadgets/nonnative.rs @@ -146,12 +146,24 @@ impl, const D: usize> CircuitBuilder { 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); + let diff = self.add_virtual_nonnative_target::(); + let overflow = self.add_virtual_bool_target(); - // TODO: reduce sub result with only one conditional addition? - self.reduce(&result) + self.add_simple_generator(NonNativeSubtractionGenerator:: { + a: a.clone(), + b: b.clone(), + diff: diff.clone(), + overflow: overflow.clone(), + _phantom: PhantomData, + }); + + let diff_plus_b = self.add_biguint(&diff.value, &b.value); + let modulus = self.constant_biguint(&FF::order()); + let mod_times_overflow = self.mul_biguint_by_bool(&modulus, overflow); + let diff_plus_b_reduced = self.sub_biguint(&diff_plus_b, &mod_times_overflow); + self.connect_biguint(&a.value, &diff_plus_b_reduced); + + diff } pub fn mul_nonnative( @@ -363,6 +375,47 @@ impl, const D: usize, FF: Field> SimpleGenerator } } +#[derive(Debug)] +struct NonNativeSubtractionGenerator, const D: usize, FF: Field> { + a: NonNativeTarget, + b: NonNativeTarget, + diff: NonNativeTarget, + overflow: BoolTarget, + _phantom: PhantomData, +} + +impl, const D: usize, FF: Field> SimpleGenerator + for NonNativeSubtractionGenerator +{ + fn dependencies(&self) -> Vec { + self.a + .value + .limbs + .iter() + .cloned() + .chain(self.b.value.limbs.clone()) + .map(|l| l.0) + .collect() + } + + fn run_once(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues) { + let a = witness.get_nonnative_target(self.a.clone()); + let b = witness.get_nonnative_target(self.b.clone()); + let a_biguint = a.to_biguint(); + let b_biguint = b.to_biguint(); + + let modulus = FF::order(); + let (diff_biguint, overflow) = if a_biguint > b_biguint { + (a_biguint - b_biguint, false) + } else { + (modulus + a_biguint - b_biguint, true) + }; + + out_buffer.set_biguint_target(self.diff.value.clone(), diff_biguint); + out_buffer.set_bool_target(self.overflow, overflow); + } +} + #[derive(Debug)] struct NonNativeInverseGenerator, const D: usize, FF: Field> { x: NonNativeTarget, From 6e9318c068914a11e6fd9573b34d7b4a98efa11d Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 19 Jan 2022 13:24:48 -0800 Subject: [PATCH 17/36] u32 range check gate --- insertion/src/insertion_gate.rs | 2 +- plonky2/src/gadgets/nonnative.rs | 77 +++-- plonky2/src/gates/add_many_u32.rs | 4 +- plonky2/src/gates/arithmetic_base.rs | 2 +- plonky2/src/gates/arithmetic_u32.rs | 2 +- plonky2/src/gates/assert_le.rs | 2 +- plonky2/src/gates/low_degree_interpolation.rs | 2 +- plonky2/src/gates/mod.rs | 2 +- plonky2/src/gates/multiplication_extension.rs | 2 +- plonky2/src/gates/random_access.rs | 2 +- plonky2/src/gates/range_check_u32.rs | 305 ++++++++++++++++++ plonky2/src/gates/subtraction_u32.rs | 2 +- 12 files changed, 374 insertions(+), 30 deletions(-) create mode 100644 plonky2/src/gates/range_check_u32.rs diff --git a/insertion/src/insertion_gate.rs b/insertion/src/insertion_gate.rs index 8ee60483..442416d3 100644 --- a/insertion/src/insertion_gate.rs +++ b/insertion/src/insertion_gate.rs @@ -404,7 +404,7 @@ mod tests { v.extend(equality_dummy_vals); v.extend(insert_here_vals); - v.iter().map(|&x| x.into()).collect::>() + v.iter().map(|&x| x.into()).collect() } let orig_vec = vec![FF::rand(); 3]; diff --git a/plonky2/src/gadgets/nonnative.rs b/plonky2/src/gadgets/nonnative.rs index 8726fde7..31b3cb1b 100644 --- a/plonky2/src/gadgets/nonnative.rs +++ b/plonky2/src/gadgets/nonnative.rs @@ -171,9 +171,25 @@ impl, const D: usize> CircuitBuilder { a: &NonNativeTarget, b: &NonNativeTarget, ) -> NonNativeTarget { - let result = self.mul_biguint(&a.value, &b.value); + let prod = self.add_virtual_nonnative_target::(); + let modulus = self.constant_biguint(&FF::order()); + let overflow = self.add_virtual_biguint_target(a.value.num_limbs() + b.value.num_limbs() - modulus.num_limbs()); - self.reduce(&result) + self.add_simple_generator(NonNativeMultiplicationGenerator:: { + a: a.clone(), + b: b.clone(), + prod: prod.clone(), + overflow: overflow.clone(), + _phantom: PhantomData, + }); + + let prod_expected = self.mul_biguint(&a.value, &b.value); + + let mod_times_overflow = self.mul_biguint(&modulus, &overflow); + let prod_actual = self.add_biguint(&prod.value, &mod_times_overflow); + self.connect_biguint(&prod_expected, &prod_actual); + + prod } pub fn mul_many_nonnative( @@ -226,20 +242,6 @@ 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(); @@ -252,8 +254,7 @@ impl, const D: usize> CircuitBuilder { } } - #[allow(dead_code)] - fn reduce_nonnative(&mut self, x: &NonNativeTarget) -> NonNativeTarget { + pub fn reduce_nonnative(&mut self, x: &NonNativeTarget) -> NonNativeTarget { let x_biguint = self.nonnative_to_biguint(x); self.reduce(&x_biguint) } @@ -416,6 +417,45 @@ impl, const D: usize, FF: Field> SimpleGenerator } } +#[derive(Debug)] +struct NonNativeMultiplicationGenerator, const D: usize, FF: Field> { + a: NonNativeTarget, + b: NonNativeTarget, + prod: NonNativeTarget, + overflow: BigUintTarget, + _phantom: PhantomData, +} + +impl, const D: usize, FF: Field> SimpleGenerator + for NonNativeMultiplicationGenerator +{ + fn dependencies(&self) -> Vec { + self.a + .value + .limbs + .iter() + .cloned() + .chain(self.b.value.limbs.clone()) + .map(|l| l.0) + .collect() + } + + fn run_once(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues) { + let a = witness.get_nonnative_target(self.a.clone()); + let b = witness.get_nonnative_target(self.b.clone()); + let a_biguint = a.to_biguint(); + let b_biguint = b.to_biguint(); + + let prod_biguint = a_biguint * b_biguint; + + let modulus = FF::order(); + let (overflow_biguint, prod_reduced) = prod_biguint.div_rem(&modulus); + + out_buffer.set_biguint_target(self.prod.value.clone(), prod_reduced); + out_buffer.set_biguint_target(self.overflow.clone(), overflow_biguint); + } +} + #[derive(Debug)] struct NonNativeInverseGenerator, const D: usize, FF: Field> { x: NonNativeTarget, @@ -566,7 +606,6 @@ mod tests { let x = builder.constant_nonnative(x_ff); let y = builder.constant_nonnative(y_ff); - println!("LIMBS LIMBS LIMBS {}", y.value.limbs.len()); let product = builder.mul_nonnative(&x, &y); let product_expected = builder.constant_nonnative(product_ff); diff --git a/plonky2/src/gates/add_many_u32.rs b/plonky2/src/gates/add_many_u32.rs index c8b5f8af..01c7ed30 100644 --- a/plonky2/src/gates/add_many_u32.rs +++ b/plonky2/src/gates/add_many_u32.rs @@ -248,7 +248,7 @@ impl, const D: usize> Gate for U32AddManyGate ); g }) - .collect::>() + .collect() } fn num_wires(&self) -> usize { @@ -426,7 +426,7 @@ mod tests { v0.iter() .chain(v1.iter()) .map(|&x| x.into()) - .collect::>() + .collect() } let mut rng = rand::thread_rng(); diff --git a/plonky2/src/gates/arithmetic_base.rs b/plonky2/src/gates/arithmetic_base.rs index 8f67dab2..738b8ad4 100644 --- a/plonky2/src/gates/arithmetic_base.rs +++ b/plonky2/src/gates/arithmetic_base.rs @@ -131,7 +131,7 @@ impl, const D: usize> Gate for ArithmeticGate ); g }) - .collect::>() + .collect() } fn num_wires(&self) -> usize { diff --git a/plonky2/src/gates/arithmetic_u32.rs b/plonky2/src/gates/arithmetic_u32.rs index 1d4a834c..bef21a97 100644 --- a/plonky2/src/gates/arithmetic_u32.rs +++ b/plonky2/src/gates/arithmetic_u32.rs @@ -212,7 +212,7 @@ impl, const D: usize> Gate for U32ArithmeticG ); g }) - .collect::>() + .collect() } fn num_wires(&self) -> usize { diff --git a/plonky2/src/gates/assert_le.rs b/plonky2/src/gates/assert_le.rs index 6a99acd9..c385bb31 100644 --- a/plonky2/src/gates/assert_le.rs +++ b/plonky2/src/gates/assert_le.rs @@ -578,7 +578,7 @@ mod tests { v.append(&mut chunks_equal); v.append(&mut intermediate_values); - v.iter().map(|&x| x.into()).collect::>() + v.iter().map(|&x| x.into()).collect() }; let mut rng = rand::thread_rng(); diff --git a/plonky2/src/gates/low_degree_interpolation.rs b/plonky2/src/gates/low_degree_interpolation.rs index b7307470..845da5ab 100644 --- a/plonky2/src/gates/low_degree_interpolation.rs +++ b/plonky2/src/gates/low_degree_interpolation.rs @@ -443,7 +443,7 @@ mod tests { .take(gate.num_points() - 2) .flat_map(|ff| ff.0), ); - v.iter().map(|&x| x.into()).collect::>() + v.iter().map(|&x| x.into()).collect() } // Get a working row for LowDegreeInterpolationGate. diff --git a/plonky2/src/gates/mod.rs b/plonky2/src/gates/mod.rs index 177db7cf..163a7dac 100644 --- a/plonky2/src/gates/mod.rs +++ b/plonky2/src/gates/mod.rs @@ -11,7 +11,6 @@ pub mod binary_arithmetic; pub mod binary_subtraction; pub mod comparison; pub mod constant; -// pub mod curve_double; pub mod exponentiation; pub mod gate; pub mod gate_tree; @@ -24,6 +23,7 @@ pub mod poseidon; pub(crate) mod poseidon_mds; pub(crate) mod public_input; pub mod random_access; +pub mod range_check_u32; pub mod reducing; pub mod reducing_extension; pub mod subtraction_u32; diff --git a/plonky2/src/gates/multiplication_extension.rs b/plonky2/src/gates/multiplication_extension.rs index 9ccfe637..54629a47 100644 --- a/plonky2/src/gates/multiplication_extension.rs +++ b/plonky2/src/gates/multiplication_extension.rs @@ -125,7 +125,7 @@ impl, const D: usize> Gate for MulExtensionGa ); g }) - .collect::>() + .collect() } fn num_wires(&self) -> usize { diff --git a/plonky2/src/gates/random_access.rs b/plonky2/src/gates/random_access.rs index 77359a19..6379f99f 100644 --- a/plonky2/src/gates/random_access.rs +++ b/plonky2/src/gates/random_access.rs @@ -209,7 +209,7 @@ impl, const D: usize> Gate for RandomAccessGa ); g }) - .collect::>() + .collect() } fn num_wires(&self) -> usize { diff --git a/plonky2/src/gates/range_check_u32.rs b/plonky2/src/gates/range_check_u32.rs new file mode 100644 index 00000000..2533b51f --- /dev/null +++ b/plonky2/src/gates/range_check_u32.rs @@ -0,0 +1,305 @@ +use std::marker::PhantomData; + +use crate::field::extension_field::target::ExtensionTarget; +use crate::field::extension_field::Extendable; +use crate::field::field_types::{Field, RichField}; +use crate::gates::gate::Gate; +use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; +use crate::iop::target::Target; +use crate::iop::witness::{PartitionWitness, Witness}; +use crate::plonk::circuit_builder::CircuitBuilder; +use crate::plonk::plonk_common::{reduce_with_powers, reduce_with_powers_ext_recursive}; +use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; +use crate::util::ceil_div_usize; + +/// A gate which can decompose a number into base B little-endian limbs. +#[derive(Copy, Clone, Debug)] +pub struct U32RangeCheckGate, const D: usize> { + pub num_input_limbs: usize, + _phantom: PhantomData, +} + +impl, const D: usize> U32RangeCheckGate { + pub fn new(num_input_limbs: usize) -> Self { + Self { + num_input_limbs, + _phantom: PhantomData, + } + } + + pub const AUX_LIMB_BITS: usize = 3; + pub const BASE: usize = 1 << Self::AUX_LIMB_BITS; + + fn aux_limbs_per_input_limb(&self) -> usize { + ceil_div_usize(32, Self::AUX_LIMB_BITS) + } + pub fn wire_ith_input_limb(&self, i: usize) -> usize{ + debug_assert!(i < self.num_input_limbs); + i + } + pub fn wire_ith_input_limb_jth_aux_limb(&self, i: usize, j: usize) -> usize { + debug_assert!(i < self.num_input_limbs); + debug_assert!(j < self.aux_limbs_per_input_limb()); + self.num_input_limbs + self.aux_limbs_per_input_limb() * i + j + } +} + +impl, const D: usize> Gate for U32RangeCheckGate{ + fn id(&self) -> String { + format!("{:?}", self) + } + + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { + let mut constraints = Vec::with_capacity(self.num_constraints()); + + let base = F::Extension::from_canonical_usize(Self::BASE); + for i in 0..self.num_input_limbs { + let input_limb = vars.local_wires[self.wire_ith_input_limb(i)]; + let aux_limbs: Vec<_> = (0..self.aux_limbs_per_input_limb()).map(|j| vars.local_wires[self.wire_ith_input_limb_jth_aux_limb(i, j)]).collect(); + let computed_sum = reduce_with_powers(&aux_limbs, base); + + constraints.push(computed_sum - input_limb); + for aux_limb in aux_limbs { + constraints.push( + (0..Self::BASE) + .map(|i| aux_limb - F::Extension::from_canonical_usize(i)) + .product(), + ); + } + + } + + constraints + } + + fn eval_unfiltered_base(&self, vars: EvaluationVarsBase) -> Vec { + let mut constraints = Vec::with_capacity(self.num_constraints()); + + let base = F::from_canonical_usize(Self::BASE); + for i in 0..self.num_input_limbs { + let input_limb = vars.local_wires[self.wire_ith_input_limb(i)]; + let aux_limbs: Vec<_> = (0..self.aux_limbs_per_input_limb()).map(|j| vars.local_wires[self.wire_ith_input_limb_jth_aux_limb(i, j)]).collect(); + let computed_sum = reduce_with_powers(&aux_limbs, base); + + constraints.push(computed_sum - input_limb); + for aux_limb in aux_limbs { + constraints.push( + (0..Self::BASE) + .map(|i| aux_limb - F::from_canonical_usize(i)) + .product(), + ); + } + + } + + constraints + } + + fn eval_unfiltered_recursively( + &self, + builder: &mut CircuitBuilder, + vars: EvaluationTargets, + ) -> Vec> { + let mut constraints = Vec::with_capacity(self.num_constraints()); + + let base = builder.constant(F::from_canonical_usize(Self::BASE)); + for i in 0..self.num_input_limbs { + let input_limb = vars.local_wires[self.wire_ith_input_limb(i)]; + let aux_limbs: Vec<_> = (0..self.aux_limbs_per_input_limb()).map(|j| vars.local_wires[self.wire_ith_input_limb_jth_aux_limb(i, j)]).collect(); + let computed_sum = reduce_with_powers_ext_recursive(builder, &aux_limbs, base); + + constraints.push(builder.sub_extension(computed_sum, input_limb)); + for aux_limb in aux_limbs { + constraints.push({ + let mut acc = builder.one_extension(); + (0..Self::BASE).for_each(|i| { + // We update our accumulator as: + // acc' = acc (x - i) + // = acc x + (-i) acc + // Since -i is constant, we can do this in one arithmetic_extension call. + let neg_i = -F::from_canonical_usize(i); + acc = builder.arithmetic_extension(F::ONE, neg_i, acc, aux_limb, acc) + }); + acc + }); + } + + } + + constraints + } + + fn generators( + &self, + gate_index: usize, + _local_constants: &[F], + ) -> Vec>> { + let gen = U32RangeCheckGenerator { + gate: self.clone(), + gate_index, + }; + vec![Box::new(gen.adapter())] + } + + fn num_wires(&self) -> usize { + self.num_input_limbs * (1 + self.aux_limbs_per_input_limb()) + } + + fn num_constants(&self) -> usize { + 0 + } + + // Bounded by the range-check (x-0)*(x-1)*...*(x-BASE+1). + fn degree(&self) -> usize { + Self::BASE + } + + // 1 for checking the each sum of aux limbs, plus a range check for each aux limb. + fn num_constraints(&self) -> usize { + self.num_input_limbs * (1 + self.aux_limbs_per_input_limb()) + } +} + +#[derive(Debug)] +pub struct U32RangeCheckGenerator, const D: usize> { + gate: U32RangeCheckGate, + gate_index: usize, +} + +impl, const D: usize> SimpleGenerator for U32RangeCheckGenerator { + fn dependencies(&self) -> Vec { + let num_input_limbs = self.gate.num_input_limbs; + (0..num_input_limbs).map(|i| Target::wire(self.gate_index, self.gate.wire_ith_input_limb(i))).collect() + } + + fn run_once(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues) { + let num_input_limbs = self.gate.num_input_limbs; + for i in 0..num_input_limbs { + let sum_value = witness + .get_target(Target::wire(self.gate_index, self.gate.wire_ith_input_limb(i))) + .to_canonical_u64() as u32; + + let base = U32RangeCheckGate::::BASE as u32; + let limbs = (0..self.gate.aux_limbs_per_input_limb()) + .map(|j| Target::wire(self.gate_index, self.gate.wire_ith_input_limb_jth_aux_limb(i, j))); + let limbs_value = (0..self.gate.aux_limbs_per_input_limb()) + .scan(sum_value, |acc, _| { + let tmp = *acc % base; + *acc /= base; + Some(F::from_canonical_u32(tmp)) + }) + .collect::>(); + + for (b, b_value) in limbs.zip(limbs_value) { + out_buffer.set_target(b, b_value); + } + } + + } +} + +#[cfg(test)] +mod tests { + use std::marker::PhantomData; + + use anyhow::Result; + use itertools::unfold; + use rand::Rng; + + use crate::field::extension_field::quartic::QuarticExtension; + use crate::field::field_types::Field; + use crate::field::goldilocks_field::GoldilocksField; + use crate::gates::gate::Gate; + use crate::gates::gate_testing::{test_eval_fns, test_low_degree}; + use crate::gates::range_check_u32::U32RangeCheckGate; + use crate::hash::hash_types::HashOut; + use crate::plonk::vars::EvaluationVars; + use crate::util::ceil_div_usize; + + #[test] + fn low_degree() { + test_low_degree::(U32RangeCheckGate::new(8)) + } + + #[test] + fn eval_fns() -> Result<()> { + test_eval_fns::(U32RangeCheckGate::new(8)) + } + + fn test_gate_constraint(input_limbs: Vec) { + type F = GoldilocksField; + type FF = QuarticExtension; + const D: usize = 4; + const AUX_LIMB_BITS: usize = 3; + const BASE: usize = 1 << AUX_LIMB_BITS; + const AUX_LIMBS_PER_INPUT_LIMB: usize = ceil_div_usize(32, AUX_LIMB_BITS); + + fn get_wires(input_limbs: Vec) -> Vec { + let num_input_limbs = input_limbs.len(); + let mut v = Vec::new(); + + for i in 0..num_input_limbs { + let input_limb = input_limbs[i]; + + let split_to_limbs = |mut val, num| { + unfold((), move |_| { + let ret = val % (BASE as u64); + val /= BASE as u64; + Some(ret) + }) + .take(num) + .map(F::from_canonical_u64) + }; + + let mut aux_limbs: Vec<_> = + split_to_limbs(input_limb, AUX_LIMBS_PER_INPUT_LIMB).collect(); + + v.append(&mut aux_limbs); + } + + input_limbs.iter() + .cloned() + .map(F::from_canonical_u64) + .chain(v.iter().cloned()) + .map(|x| x.into()) + .collect() + } + + let gate = U32RangeCheckGate:: { + num_input_limbs: 8, + _phantom: PhantomData, + }; + + let vars = EvaluationVars { + local_constants: &[], + local_wires: &get_wires(input_limbs), + public_inputs_hash: &HashOut::rand(), + }; + + assert!( + gate.eval_unfiltered(vars).iter().all(|x| x.is_zero()), + "Gate constraints are not satisfied." + ); + } + + #[test] + fn test_gate_constraint_good() { + let mut rng = rand::thread_rng(); + let input_limbs: Vec<_> = (0..8) + .map(|_| rng.gen::() as u64) + .collect(); + + test_gate_constraint(input_limbs); + } + + #[test] + #[should_panic] + fn test_gate_constraint_bad() { + let mut rng = rand::thread_rng(); + let input_limbs: Vec<_> = (0..8) + .map(|_| rng.gen()) + .collect(); + + test_gate_constraint(input_limbs); + } +} diff --git a/plonky2/src/gates/subtraction_u32.rs b/plonky2/src/gates/subtraction_u32.rs index fa817ce4..ffb2e2cb 100644 --- a/plonky2/src/gates/subtraction_u32.rs +++ b/plonky2/src/gates/subtraction_u32.rs @@ -419,7 +419,7 @@ mod tests { v0.iter() .chain(v1.iter()) .map(|&x| x.into()) - .collect::>() + .collect() } let mut rng = rand::thread_rng(); From 2ddfb03aeaaab817bb41c74b764072559d85816c Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 19 Jan 2022 13:25:00 -0800 Subject: [PATCH 18/36] various cleanup --- plonky2/src/gadgets/nonnative.rs | 4 +- plonky2/src/gates/add_many_u32.rs | 5 +- plonky2/src/gates/range_check_u32.rs | 70 ++++++++++++++++------------ plonky2/src/gates/subtraction_u32.rs | 5 +- 4 files changed, 45 insertions(+), 39 deletions(-) diff --git a/plonky2/src/gadgets/nonnative.rs b/plonky2/src/gadgets/nonnative.rs index 31b3cb1b..5107be29 100644 --- a/plonky2/src/gadgets/nonnative.rs +++ b/plonky2/src/gadgets/nonnative.rs @@ -173,7 +173,9 @@ impl, const D: usize> CircuitBuilder { ) -> NonNativeTarget { let prod = self.add_virtual_nonnative_target::(); let modulus = self.constant_biguint(&FF::order()); - let overflow = self.add_virtual_biguint_target(a.value.num_limbs() + b.value.num_limbs() - modulus.num_limbs()); + let overflow = self.add_virtual_biguint_target( + a.value.num_limbs() + b.value.num_limbs() - modulus.num_limbs(), + ); self.add_simple_generator(NonNativeMultiplicationGenerator:: { a: a.clone(), diff --git a/plonky2/src/gates/add_many_u32.rs b/plonky2/src/gates/add_many_u32.rs index 01c7ed30..9f67c827 100644 --- a/plonky2/src/gates/add_many_u32.rs +++ b/plonky2/src/gates/add_many_u32.rs @@ -423,10 +423,7 @@ mod tests { v1.append(&mut carry_limbs); } - v0.iter() - .chain(v1.iter()) - .map(|&x| x.into()) - .collect() + v0.iter().chain(v1.iter()).map(|&x| x.into()).collect() } let mut rng = rand::thread_rng(); diff --git a/plonky2/src/gates/range_check_u32.rs b/plonky2/src/gates/range_check_u32.rs index 2533b51f..72e5ad4b 100644 --- a/plonky2/src/gates/range_check_u32.rs +++ b/plonky2/src/gates/range_check_u32.rs @@ -29,11 +29,11 @@ impl, const D: usize> U32RangeCheckGate { pub const AUX_LIMB_BITS: usize = 3; pub const BASE: usize = 1 << Self::AUX_LIMB_BITS; - + fn aux_limbs_per_input_limb(&self) -> usize { ceil_div_usize(32, Self::AUX_LIMB_BITS) } - pub fn wire_ith_input_limb(&self, i: usize) -> usize{ + pub fn wire_ith_input_limb(&self, i: usize) -> usize { debug_assert!(i < self.num_input_limbs); i } @@ -44,7 +44,7 @@ impl, const D: usize> U32RangeCheckGate { } } -impl, const D: usize> Gate for U32RangeCheckGate{ +impl, const D: usize> Gate for U32RangeCheckGate { fn id(&self) -> String { format!("{:?}", self) } @@ -55,7 +55,9 @@ impl, const D: usize> Gate for U32RangeCheckG let base = F::Extension::from_canonical_usize(Self::BASE); for i in 0..self.num_input_limbs { let input_limb = vars.local_wires[self.wire_ith_input_limb(i)]; - let aux_limbs: Vec<_> = (0..self.aux_limbs_per_input_limb()).map(|j| vars.local_wires[self.wire_ith_input_limb_jth_aux_limb(i, j)]).collect(); + let aux_limbs: Vec<_> = (0..self.aux_limbs_per_input_limb()) + .map(|j| vars.local_wires[self.wire_ith_input_limb_jth_aux_limb(i, j)]) + .collect(); let computed_sum = reduce_with_powers(&aux_limbs, base); constraints.push(computed_sum - input_limb); @@ -66,7 +68,6 @@ impl, const D: usize> Gate for U32RangeCheckG .product(), ); } - } constraints @@ -78,7 +79,9 @@ impl, const D: usize> Gate for U32RangeCheckG let base = F::from_canonical_usize(Self::BASE); for i in 0..self.num_input_limbs { let input_limb = vars.local_wires[self.wire_ith_input_limb(i)]; - let aux_limbs: Vec<_> = (0..self.aux_limbs_per_input_limb()).map(|j| vars.local_wires[self.wire_ith_input_limb_jth_aux_limb(i, j)]).collect(); + let aux_limbs: Vec<_> = (0..self.aux_limbs_per_input_limb()) + .map(|j| vars.local_wires[self.wire_ith_input_limb_jth_aux_limb(i, j)]) + .collect(); let computed_sum = reduce_with_powers(&aux_limbs, base); constraints.push(computed_sum - input_limb); @@ -89,7 +92,6 @@ impl, const D: usize> Gate for U32RangeCheckG .product(), ); } - } constraints @@ -105,7 +107,9 @@ impl, const D: usize> Gate for U32RangeCheckG let base = builder.constant(F::from_canonical_usize(Self::BASE)); for i in 0..self.num_input_limbs { let input_limb = vars.local_wires[self.wire_ith_input_limb(i)]; - let aux_limbs: Vec<_> = (0..self.aux_limbs_per_input_limb()).map(|j| vars.local_wires[self.wire_ith_input_limb_jth_aux_limb(i, j)]).collect(); + let aux_limbs: Vec<_> = (0..self.aux_limbs_per_input_limb()) + .map(|j| vars.local_wires[self.wire_ith_input_limb_jth_aux_limb(i, j)]) + .collect(); let computed_sum = reduce_with_powers_ext_recursive(builder, &aux_limbs, base); constraints.push(builder.sub_extension(computed_sum, input_limb)); @@ -123,7 +127,6 @@ impl, const D: usize> Gate for U32RangeCheckG acc }); } - } constraints @@ -166,22 +169,33 @@ pub struct U32RangeCheckGenerator, const D: usize> gate_index: usize, } -impl, const D: usize> SimpleGenerator for U32RangeCheckGenerator { +impl, const D: usize> SimpleGenerator + for U32RangeCheckGenerator +{ fn dependencies(&self) -> Vec { let num_input_limbs = self.gate.num_input_limbs; - (0..num_input_limbs).map(|i| Target::wire(self.gate_index, self.gate.wire_ith_input_limb(i))).collect() + (0..num_input_limbs) + .map(|i| Target::wire(self.gate_index, self.gate.wire_ith_input_limb(i))) + .collect() } fn run_once(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues) { let num_input_limbs = self.gate.num_input_limbs; for i in 0..num_input_limbs { let sum_value = witness - .get_target(Target::wire(self.gate_index, self.gate.wire_ith_input_limb(i))) + .get_target(Target::wire( + self.gate_index, + self.gate.wire_ith_input_limb(i), + )) .to_canonical_u64() as u32; let base = U32RangeCheckGate::::BASE as u32; - let limbs = (0..self.gate.aux_limbs_per_input_limb()) - .map(|j| Target::wire(self.gate_index, self.gate.wire_ith_input_limb_jth_aux_limb(i, j))); + let limbs = (0..self.gate.aux_limbs_per_input_limb()).map(|j| { + Target::wire( + self.gate_index, + self.gate.wire_ith_input_limb_jth_aux_limb(i, j), + ) + }); let limbs_value = (0..self.gate.aux_limbs_per_input_limb()) .scan(sum_value, |acc, _| { let tmp = *acc % base; @@ -194,7 +208,6 @@ impl, const D: usize> SimpleGenerator for U32Ran out_buffer.set_target(b, b_value); } } - } } @@ -233,14 +246,14 @@ mod tests { const AUX_LIMB_BITS: usize = 3; const BASE: usize = 1 << AUX_LIMB_BITS; const AUX_LIMBS_PER_INPUT_LIMB: usize = ceil_div_usize(32, AUX_LIMB_BITS); - + fn get_wires(input_limbs: Vec) -> Vec { let num_input_limbs = input_limbs.len(); let mut v = Vec::new(); for i in 0..num_input_limbs { let input_limb = input_limbs[i]; - + let split_to_limbs = |mut val, num| { unfold((), move |_| { let ret = val % (BASE as u64); @@ -253,16 +266,17 @@ mod tests { let mut aux_limbs: Vec<_> = split_to_limbs(input_limb, AUX_LIMBS_PER_INPUT_LIMB).collect(); - + v.append(&mut aux_limbs); } - input_limbs.iter() - .cloned() - .map(F::from_canonical_u64) - .chain(v.iter().cloned()) - .map(|x| x.into()) - .collect() + input_limbs + .iter() + .cloned() + .map(F::from_canonical_u64) + .chain(v.iter().cloned()) + .map(|x| x.into()) + .collect() } let gate = U32RangeCheckGate:: { @@ -285,9 +299,7 @@ mod tests { #[test] fn test_gate_constraint_good() { let mut rng = rand::thread_rng(); - let input_limbs: Vec<_> = (0..8) - .map(|_| rng.gen::() as u64) - .collect(); + let input_limbs: Vec<_> = (0..8).map(|_| rng.gen::() as u64).collect(); test_gate_constraint(input_limbs); } @@ -296,9 +308,7 @@ mod tests { #[should_panic] fn test_gate_constraint_bad() { let mut rng = rand::thread_rng(); - let input_limbs: Vec<_> = (0..8) - .map(|_| rng.gen()) - .collect(); + let input_limbs: Vec<_> = (0..8).map(|_| rng.gen()).collect(); test_gate_constraint(input_limbs); } diff --git a/plonky2/src/gates/subtraction_u32.rs b/plonky2/src/gates/subtraction_u32.rs index ffb2e2cb..f083db5a 100644 --- a/plonky2/src/gates/subtraction_u32.rs +++ b/plonky2/src/gates/subtraction_u32.rs @@ -416,10 +416,7 @@ mod tests { v1.append(&mut output_limbs); } - v0.iter() - .chain(v1.iter()) - .map(|&x| x.into()) - .collect() + v0.iter().chain(v1.iter()).map(|&x| x.into()).collect() } let mut rng = rand::thread_rng(); From c392606a9a285b89773248a52118972b221f80bd Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 20 Jan 2022 15:44:03 -0800 Subject: [PATCH 19/36] optimizations and cleanup --- plonky2/src/gadgets/arithmetic.rs | 6 ++ plonky2/src/gadgets/curve.rs | 85 +++++++++++----------------- plonky2/src/gadgets/ecdsa.rs | 3 +- plonky2/src/gadgets/nonnative.rs | 16 ++++-- plonky2/src/gadgets/range_check.rs | 19 +++++++ plonky2/src/gates/range_check_u32.rs | 4 +- plonky2/src/plonk/circuit_builder.rs | 8 ++- plonky2/src/plonk/circuit_data.rs | 9 ++- 8 files changed, 87 insertions(+), 63 deletions(-) diff --git a/plonky2/src/gadgets/arithmetic.rs b/plonky2/src/gadgets/arithmetic.rs index e280fa5e..90f3b090 100644 --- a/plonky2/src/gadgets/arithmetic.rs +++ b/plonky2/src/gadgets/arithmetic.rs @@ -315,6 +315,12 @@ impl, const D: usize> CircuitBuilder { let x_ext = self.convert_to_ext(x); self.inverse_extension(x_ext).0[0] } + + pub fn not(&mut self, b: BoolTarget) -> BoolTarget { + let one = self.one(); + let res = self.sub(one, b.target); + BoolTarget::new_unsafe(res) + } } /// Represents a base arithmetic operation in the circuit. Used to memoize results. diff --git a/plonky2/src/gadgets/curve.rs b/plonky2/src/gadgets/curve.rs index 907aa5e3..92f45242 100644 --- a/plonky2/src/gadgets/curve.rs +++ b/plonky2/src/gadgets/curve.rs @@ -100,35 +100,21 @@ impl, const D: usize> CircuitBuilder { p1: &AffinePointTarget, p2: &AffinePointTarget, ) -> AffinePointTarget { - let before = self.num_gates(); 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 v_inv = self.inv_nonnative(&v); + let s = self.mul_nonnative(&u, &v_inv); + let s_squared = self.mul_nonnative(&s, &s); + let x_sum = self.add_nonnative(x2, x1); + let x3 = self.sub_nonnative(&s_squared, &x_sum); + let x_diff = self.sub_nonnative(&x1, &x3); + let prod = self.mul_nonnative(&s, &x_diff); + let y3 = self.sub_nonnative(&prod, &y1); - 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); - - println!("NUM GATES: {}", self.num_gates() - before); - AffinePointTarget { - x: x3_norm, - y: y3_norm, - } + AffinePointTarget { x: x3, y: y3 } } pub fn curve_scalar_mul( @@ -136,11 +122,7 @@ impl, const D: usize> CircuitBuilder { p: &AffinePointTarget, n: &NonNativeTarget, ) -> AffinePointTarget { - let one = self.constant_nonnative(C::BaseField::ONE); - let bits = self.split_nonnative_to_bits(n); - let bits_as_base: Vec> = - bits.iter().map(|b| self.bool_to_nonnative(b)).collect(); let rando = (CurveScalar(C::ScalarField::rand()) * C::GENERATOR_PROJECTIVE).to_affine(); let randot = self.constant_affine_point(rando); @@ -151,15 +133,15 @@ impl, const D: usize> CircuitBuilder { let mut two_i_times_p = self.add_virtual_affine_point_target(); self.connect_affine_point(p, &two_i_times_p); - for bit in bits_as_base.iter() { - let not_bit = self.sub_nonnative(&one, bit); + for &bit in bits.iter() { + let not_bit = self.not(bit); let result_plus_2_i_p = self.curve_add(&result, &two_i_times_p); - 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_by_bool(&result_plus_2_i_p.x, bit); + let new_x_if_not_bit = self.mul_nonnative_by_bool(&result.x, not_bit); + let new_y_if_bit = self.mul_nonnative_by_bool(&result_plus_2_i_p.y, bit); + let new_y_if_not_bit = self.mul_nonnative_by_bool(&result.y, not_bit); 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); @@ -179,6 +161,8 @@ impl, const D: usize> CircuitBuilder { #[cfg(test)] mod tests { + use std::ops::Neg; + use anyhow::Result; use plonky2_field::field_types::Field; use plonky2_field::secp256k1_base::Secp256K1Base; @@ -198,7 +182,7 @@ mod tests { type C = PoseidonGoldilocksConfig; type F = >::F; - let config = CircuitConfig::standard_recursion_config(); + let config = CircuitConfig::standard_ecc_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); @@ -223,7 +207,7 @@ mod tests { type C = PoseidonGoldilocksConfig; type F = >::F; - let config = CircuitConfig::standard_recursion_config(); + let config = CircuitConfig::standard_ecc_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); @@ -250,7 +234,7 @@ mod tests { type C = PoseidonGoldilocksConfig; type F = >::F; - let config = CircuitConfig::standard_recursion_config(); + let config = CircuitConfig::standard_ecc_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); @@ -287,7 +271,7 @@ mod tests { type C = PoseidonGoldilocksConfig; type F = >::F; - let config = CircuitConfig::standard_recursion_config(); + let config = CircuitConfig::standard_ecc_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); @@ -317,27 +301,25 @@ mod tests { type C = PoseidonGoldilocksConfig; type F = >::F; - let config = CircuitConfig { - num_routed_wires: 33, - ..CircuitConfig::standard_recursion_config() - }; + let config = CircuitConfig::standard_ecc_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 neg_five = five.neg(); + let neg_five_scalar = CurveScalar::(neg_five); + let neg_five_g = (neg_five_scalar * g.to_projective()).to_affine(); + let neg_five_g_expected = builder.constant_affine_point(neg_five_g); + builder.curve_assert_valid(&neg_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); + let neg_five_target = builder.constant_nonnative(neg_five); + let neg_five_g_actual = builder.curve_scalar_mul(&g_target, &neg_five_target); + builder.curve_assert_valid(&neg_five_g_actual); - builder.connect_affine_point(&five_g_expected, &five_g_actual); + builder.connect_affine_point(&neg_five_g_expected, &neg_five_g_actual); let data = builder.build::(); let proof = data.prove(pw).unwrap(); @@ -351,10 +333,7 @@ mod tests { type C = PoseidonGoldilocksConfig; type F = >::F; - let config = CircuitConfig { - num_routed_wires: 33, - ..CircuitConfig::standard_recursion_config() - }; + let config = CircuitConfig::standard_ecc_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); diff --git a/plonky2/src/gadgets/ecdsa.rs b/plonky2/src/gadgets/ecdsa.rs index 362fbc6d..71214ae7 100644 --- a/plonky2/src/gadgets/ecdsa.rs +++ b/plonky2/src/gadgets/ecdsa.rs @@ -96,7 +96,7 @@ mod tests { const D: usize = 4; type C = Secp256K1; - let config = CircuitConfig::standard_recursion_config(); + let config = CircuitConfig::standard_ecc_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); @@ -123,7 +123,6 @@ mod tests { 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 5107be29..a46ee376 100644 --- a/plonky2/src/gadgets/nonnative.rs +++ b/plonky2/src/gadgets/nonnative.rs @@ -125,6 +125,9 @@ impl, const D: usize> CircuitBuilder { _phantom: PhantomData, }); + self.range_check_u32(sum.value.limbs.clone()); + self.range_check_u32(vec![overflow]); + let sum_expected = summands .iter() .fold(self.zero_biguint(), |a, b| self.add_biguint(&a, &b.value)); @@ -157,6 +160,9 @@ impl, const D: usize> CircuitBuilder { _phantom: PhantomData, }); + self.range_check_u32(diff.value.limbs.clone()); + self.assert_bool(overflow); + let diff_plus_b = self.add_biguint(&diff.value, &b.value); let modulus = self.constant_biguint(&FF::order()); let mod_times_overflow = self.mul_biguint_by_bool(&modulus, overflow); @@ -185,6 +191,9 @@ impl, const D: usize> CircuitBuilder { _phantom: PhantomData, }); + self.range_check_u32(prod.value.limbs.clone()); + self.range_check_u32(overflow.limbs.clone()); + let prod_expected = self.mul_biguint(&a.value, &b.value); let mod_times_overflow = self.mul_biguint(&modulus, &overflow); @@ -202,12 +211,11 @@ impl, const D: usize> CircuitBuilder { return to_mul[0].clone(); } - let mut result = self.mul_biguint(&to_mul[0].value, &to_mul[1].value); + let mut accumulator = self.mul_nonnative(&to_mul[0], &to_mul[1]); for i in 2..to_mul.len() { - result = self.mul_biguint(&result, &to_mul[i].value); + accumulator = self.mul_nonnative(&accumulator, &to_mul[i]); } - - self.reduce(&result) + accumulator } pub fn neg_nonnative(&mut self, x: &NonNativeTarget) -> NonNativeTarget { diff --git a/plonky2/src/gadgets/range_check.rs b/plonky2/src/gadgets/range_check.rs index f8ada106..5a90afd0 100644 --- a/plonky2/src/gadgets/range_check.rs +++ b/plonky2/src/gadgets/range_check.rs @@ -41,6 +41,25 @@ impl, const D: usize> CircuitBuilder { (low, high) } + + pub fn range_check_u32(&mut self, vals: Vec) { + let num_input_limbs = vals.len(); + let gate = U32RangeCheckGate::::new(num_input_limbs); + let gate_index = self.add_gate(gate, vec![]); + + for i in 0..num_input_limbs { + self.connect( + Target::wire(gate_index, gate.wire_ith_input_limb(i)), + vals[i].0, + ); + } + } + + pub fn assert_bool(&mut self, b: BoolTarget) { + let z = self.mul_sub(b.target, b.target, b.target); + let zero = self.zero(); + self.connect(z, zero); + } } #[derive(Debug)] diff --git a/plonky2/src/gates/range_check_u32.rs b/plonky2/src/gates/range_check_u32.rs index 72e5ad4b..83cdb223 100644 --- a/plonky2/src/gates/range_check_u32.rs +++ b/plonky2/src/gates/range_check_u32.rs @@ -27,7 +27,7 @@ impl, const D: usize> U32RangeCheckGate { } } - pub const AUX_LIMB_BITS: usize = 3; + pub const AUX_LIMB_BITS: usize = 2; pub const BASE: usize = 1 << Self::AUX_LIMB_BITS; fn aux_limbs_per_input_limb(&self) -> usize { @@ -243,7 +243,7 @@ mod tests { type F = GoldilocksField; type FF = QuarticExtension; const D: usize = 4; - const AUX_LIMB_BITS: usize = 3; + const AUX_LIMB_BITS: usize = 2; const BASE: usize = 1 << AUX_LIMB_BITS; const AUX_LIMBS_PER_INPUT_LIMB: usize = ceil_div_usize(32, AUX_LIMB_BITS); diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index d9f79f59..38091bc9 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -205,6 +205,12 @@ impl, const D: usize> CircuitBuilder { BoolTarget::new_unsafe(self.add_virtual_target()) } + pub fn add_virtual_bool_target_safe(&mut self) -> BoolTarget { + let b = BoolTarget::new_unsafe(self.add_virtual_target()); + self.assert_bool(b); + b + } + /// Adds a gate to the circuit, and returns its index. pub fn add_gate>(&mut self, gate_type: G, constants: Vec) -> usize { self.check_gate_compatibility(&gate_type); @@ -235,7 +241,7 @@ impl, const D: usize> CircuitBuilder { fn check_gate_compatibility>(&self, gate: &G) { assert!( gate.num_wires() <= self.config.num_wires, - "{:?} requires {} wires, but our GateConfig has only {}", + "{:?} requires {} wires, but our CircuitConfig has only {}", gate.id(), gate.num_wires(), self.config.num_wires diff --git a/plonky2/src/plonk/circuit_data.rs b/plonky2/src/plonk/circuit_data.rs index dd7ebc25..7e667b8d 100644 --- a/plonky2/src/plonk/circuit_data.rs +++ b/plonky2/src/plonk/circuit_data.rs @@ -49,7 +49,7 @@ pub struct CircuitConfig { impl Default for CircuitConfig { fn default() -> Self { - CircuitConfig::standard_recursion_config() + Self::standard_recursion_config() } } @@ -79,6 +79,13 @@ impl CircuitConfig { } } + pub fn standard_ecc_config() -> Self { + Self { + num_wires: 136, + ..Self::standard_recursion_config() + } + } + pub fn standard_recursion_zk_config() -> Self { CircuitConfig { zero_knowledge: true, From e116ab7809a9add16ec4b1e6432263b76f9d05c9 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 20 Jan 2022 15:49:29 -0800 Subject: [PATCH 20/36] fix --- plonky2/src/gadgets/ecdsa.rs | 1 + plonky2/src/gadgets/nonnative.rs | 15 +++++++-------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/plonky2/src/gadgets/ecdsa.rs b/plonky2/src/gadgets/ecdsa.rs index 71214ae7..def3b5b4 100644 --- a/plonky2/src/gadgets/ecdsa.rs +++ b/plonky2/src/gadgets/ecdsa.rs @@ -91,6 +91,7 @@ mod tests { use crate::plonk::verifier::verify; #[test] + #[ignore] fn test_ecdsa_circuit() -> Result<()> { type F = GoldilocksField; const D: usize = 4; diff --git a/plonky2/src/gadgets/nonnative.rs b/plonky2/src/gadgets/nonnative.rs index a46ee376..2c556616 100644 --- a/plonky2/src/gadgets/nonnative.rs +++ b/plonky2/src/gadgets/nonnative.rs @@ -518,7 +518,7 @@ mod tests { let y_ff = FF::rand(); let sum_ff = x_ff + y_ff; - let config = CircuitConfig::standard_recursion_config(); + let config = CircuitConfig::standard_ecc_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); @@ -547,8 +547,7 @@ mod tests { let h_ff = FF::rand(); let sum_ff = a_ff + b_ff + c_ff + d_ff + e_ff + f_ff + g_ff + h_ff; - type F = GoldilocksField; - let config = CircuitConfig::standard_recursion_config(); + let config = CircuitConfig::standard_ecc_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); @@ -584,7 +583,7 @@ mod tests { } let diff_ff = x_ff - y_ff; - let config = CircuitConfig::standard_recursion_config(); + let config = CircuitConfig::standard_ecc_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); @@ -610,7 +609,7 @@ mod tests { let y_ff = FF::rand(); let product_ff = x_ff * y_ff; - let config = CircuitConfig::standard_recursion_config(); + let config = CircuitConfig::standard_ecc_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); @@ -630,7 +629,7 @@ mod tests { type FF = Secp256K1Base; type F = GoldilocksField; - let config = CircuitConfig::standard_recursion_config(); + let config = CircuitConfig::standard_ecc_config(); let mut unop_builder = CircuitBuilder::::new(config.clone()); let mut op_builder = CircuitBuilder::::new(config); @@ -668,7 +667,7 @@ mod tests { let x_ff = FF::rand(); let neg_x_ff = -x_ff; - let config = CircuitConfig::standard_recursion_config(); + let config = CircuitConfig::standard_ecc_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); @@ -692,7 +691,7 @@ mod tests { let x_ff = FF::rand(); let inv_x_ff = x_ff.inverse(); - let config = CircuitConfig::standard_recursion_config(); + let config = CircuitConfig::standard_ecc_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); From 5de2b69558462516659ac39230f3b8c54ec8a6d1 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 20 Jan 2022 15:59:43 -0800 Subject: [PATCH 21/36] 256-bit hashing --- plonky2/src/curve/ecdsa.rs | 19 ++++++++++++------- plonky2/src/gadgets/ecdsa.rs | 2 +- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/plonky2/src/curve/ecdsa.rs b/plonky2/src/curve/ecdsa.rs index 0ed777d9..82eeba08 100644 --- a/plonky2/src/curve/ecdsa.rs +++ b/plonky2/src/curve/ecdsa.rs @@ -1,4 +1,5 @@ -use itertools::unfold; +use itertools::{unfold, Itertools}; +use num::BigUint; use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar}; use crate::field::field_types::{Field, RichField}; @@ -35,15 +36,19 @@ pub fn hash_to_bits(x: F, num_bits: usize) -> Vec { pub fn hash_to_scalar(x: F, num_bits: usize) -> C::ScalarField { let h_bits = hash_to_bits(x, num_bits); - let h_u32 = h_bits + let h_vals: Vec<_> = h_bits .iter() - .zip(0..32) - .fold(0u32, |acc, (&bit, pow)| acc + (bit as u32) * (2 << pow)); - C::ScalarField::from_canonical_u32(h_u32) + .chunks(32) + .into_iter() + .map(|chunk| { + chunk.enumerate() + .fold(0u32, |acc, (pow, &bit)| acc + (bit as u32) * (2 << pow)) + }).collect(); + C::ScalarField::from_biguint(BigUint::new(h_vals)) } pub fn sign_message(msg: F, sk: ECDSASecretKey) -> ECDSASignature { - let h = hash_to_scalar::(msg, 32); + let h = hash_to_scalar::(msg, 256); let k = C::ScalarField::rand(); let rr = (CurveScalar(k) * C::GENERATOR_PROJECTIVE).to_affine(); @@ -60,7 +65,7 @@ pub fn verify_message( ) -> bool { let ECDSASignature { r, s } = sig; - let h = hash_to_scalar::(msg, 32); + let h = hash_to_scalar::(msg, 256); let c = s.inverse(); let u1 = h * c; diff --git a/plonky2/src/gadgets/ecdsa.rs b/plonky2/src/gadgets/ecdsa.rs index def3b5b4..03a3807d 100644 --- a/plonky2/src/gadgets/ecdsa.rs +++ b/plonky2/src/gadgets/ecdsa.rs @@ -55,7 +55,7 @@ impl, const D: usize> CircuitBuilder { ) { let ECDSASignatureTarget { r, s } = sig; - let h = self.hash_to_scalar::(msg, 32); + let h = self.hash_to_scalar::(msg, 256); let c = self.inv_nonnative(&s); let u1 = self.mul_nonnative(&h, &c); From 1035438df46df7dcb3d855a907763016a81fd1ba Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 21 Jan 2022 16:10:25 -0800 Subject: [PATCH 22/36] updated for changes in main --- plonky2/src/curve/ecdsa.rs | 29 ++++--- plonky2/src/gadgets/arithmetic_u32.rs | 14 +++- plonky2/src/gadgets/biguint.rs | 2 +- plonky2/src/gadgets/ecdsa.rs | 21 ++--- plonky2/src/gadgets/mod.rs | 1 - plonky2/src/gadgets/nonnative.rs | 26 +++++-- plonky2/src/gadgets/range_check.rs | 2 + plonky2/src/gates/add_many_u32.rs | 31 +++++--- plonky2/src/gates/mod.rs | 2 - plonky2/src/gates/range_check_u32.rs | 31 +++++--- plonky2/src/iop/generator.rs | 9 +-- plonky2/src/plonk/circuit_builder.rs | 106 -------------------------- 12 files changed, 101 insertions(+), 173 deletions(-) diff --git a/plonky2/src/curve/ecdsa.rs b/plonky2/src/curve/ecdsa.rs index 82eeba08..cce83d4c 100644 --- a/plonky2/src/curve/ecdsa.rs +++ b/plonky2/src/curve/ecdsa.rs @@ -2,8 +2,10 @@ use itertools::{unfold, Itertools}; use num::BigUint; use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar}; -use crate::field::field_types::{Field, RichField}; -use crate::hash::hashing::hash_n_to_1; +use crate::field::field_types::Field; +use crate::hash::hash_types::RichField; +use crate::hash::hashing::{hash_n_to_m, PlonkyPermutation}; +use crate::hash::poseidon::PoseidonPermutation; pub struct ECDSASignature { pub r: C::ScalarField, @@ -21,8 +23,8 @@ pub fn scalar_to_base(x: C::ScalarField) -> C::BaseField { C::BaseField::from_biguint(x.to_biguint()) } -pub fn hash_to_bits(x: F, num_bits: usize) -> Vec { - let hashed = hash_n_to_1(vec![x], true); +pub fn hash_to_bits>(x: F, num_bits: usize) -> Vec { + let hashed = hash_n_to_m::(&vec![x], 1, true)[0]; let mut val = hashed.to_canonical_u64(); unfold((), move |_| { @@ -34,21 +36,26 @@ pub fn hash_to_bits(x: F, num_bits: usize) -> Vec { .collect() } -pub fn hash_to_scalar(x: F, num_bits: usize) -> C::ScalarField { - let h_bits = hash_to_bits(x, num_bits); +pub fn hash_to_scalar>( + x: F, + num_bits: usize, +) -> C::ScalarField { + let h_bits = hash_to_bits::(x, num_bits); let h_vals: Vec<_> = h_bits .iter() .chunks(32) .into_iter() .map(|chunk| { - chunk.enumerate() - .fold(0u32, |acc, (pow, &bit)| acc + (bit as u32) * (2 << pow)) - }).collect(); + chunk + .enumerate() + .fold(0u32, |acc, (pow, &bit)| acc + (bit as u32) * (2 << pow)) + }) + .collect(); C::ScalarField::from_biguint(BigUint::new(h_vals)) } pub fn sign_message(msg: F, sk: ECDSASecretKey) -> ECDSASignature { - let h = hash_to_scalar::(msg, 256); + let h = hash_to_scalar::(msg, 256); let k = C::ScalarField::rand(); let rr = (CurveScalar(k) * C::GENERATOR_PROJECTIVE).to_affine(); @@ -65,7 +72,7 @@ pub fn verify_message( ) -> bool { let ECDSASignature { r, s } = sig; - let h = hash_to_scalar::(msg, 256); + let h = hash_to_scalar::(msg, 256); let c = s.inverse(); let u1 = h * c; diff --git a/plonky2/src/gadgets/arithmetic_u32.rs b/plonky2/src/gadgets/arithmetic_u32.rs index 9bf78d44..8da01dc1 100644 --- a/plonky2/src/gadgets/arithmetic_u32.rs +++ b/plonky2/src/gadgets/arithmetic_u32.rs @@ -1,8 +1,12 @@ +use std::marker::PhantomData; + use plonky2_field::extension_field::Extendable; +use crate::gates::add_many_u32::U32AddManyGate; use crate::gates::arithmetic_u32::U32ArithmeticGate; use crate::gates::subtraction_u32::U32SubtractionGate; use crate::hash::hash_types::RichField; +use crate::iop::generator::{GeneratedValues, SimpleGenerator}; use crate::iop::target::Target; use crate::iop::witness::{PartitionWitness, Witness}; use crate::plonk::circuit_builder::CircuitBuilder; @@ -243,16 +247,18 @@ mod tests { use anyhow::Result; use rand::{thread_rng, Rng}; - use crate::field::goldilocks_field::GoldilocksField; 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] pub fn test_add_many_u32s() -> Result<()> { - type F = GoldilocksField; - const D: usize = 4; + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + const NUM_ADDENDS: usize = 15; let config = CircuitConfig::standard_recursion_config(); @@ -276,7 +282,7 @@ mod tests { builder.connect_u32(result_low, expected_low); builder.connect_u32(result_high, expected_high); - let data = builder.build(); + let data = builder.build::(); let proof = data.prove(pw).unwrap(); verify(proof, &data.verifier_only, &data.common) } diff --git a/plonky2/src/gadgets/biguint.rs b/plonky2/src/gadgets/biguint.rs index 4f9e1574..51cfcc06 100644 --- a/plonky2/src/gadgets/biguint.rs +++ b/plonky2/src/gadgets/biguint.rs @@ -1,6 +1,6 @@ use std::marker::PhantomData; -use num::{BigUint, Integer}; +use num::{BigUint, FromPrimitive, Integer, Zero}; use plonky2_field::extension_field::Extendable; use crate::gadgets::arithmetic_u32::U32Target; diff --git a/plonky2/src/gadgets/ecdsa.rs b/plonky2/src/gadgets/ecdsa.rs index 03a3807d..cc787eb1 100644 --- a/plonky2/src/gadgets/ecdsa.rs +++ b/plonky2/src/gadgets/ecdsa.rs @@ -2,11 +2,12 @@ use std::marker::PhantomData; use crate::curve::curve_types::Curve; use crate::field::extension_field::Extendable; -use crate::field::field_types::RichField; use crate::gadgets::arithmetic_u32::U32Target; use crate::gadgets::biguint::BigUintTarget; use crate::gadgets::curve::AffinePointTarget; use crate::gadgets::nonnative::NonNativeTarget; +use crate::hash::hash_types::RichField; +use crate::hash::poseidon::PoseidonHash; use crate::iop::target::{BoolTarget, Target}; use crate::plonk::circuit_builder::CircuitBuilder; @@ -21,7 +22,7 @@ pub struct ECDSASignatureTarget { impl, const D: usize> CircuitBuilder { pub fn hash_to_bits(&mut self, x: Target, num_bits: usize) -> Vec { let inputs = vec![x]; - let hashed = self.hash_n_to_m(inputs, 1, true)[0]; + let hashed = self.hash_n_to_m::(inputs, 1, true)[0]; self.split_le(hashed, num_bits) } @@ -82,20 +83,22 @@ mod tests { use crate::curve::ecdsa::{sign_message, ECDSAPublicKey, ECDSASecretKey, ECDSASignature}; use crate::curve::secp256k1::Secp256K1; use crate::field::field_types::Field; - use crate::field::goldilocks_field::GoldilocksField; use crate::field::secp256k1_scalar::Secp256K1Scalar; use crate::gadgets::ecdsa::{ECDSAPublicKeyTarget, ECDSASignatureTarget}; 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] #[ignore] fn test_ecdsa_circuit() -> Result<()> { - type F = GoldilocksField; - const D: usize = 4; - type C = Secp256K1; + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + + type Curve = Secp256K1; let config = CircuitConfig::standard_ecc_config(); @@ -105,8 +108,8 @@ mod tests { let msg = F::rand(); let msg_target = builder.constant(msg); - let sk = ECDSASecretKey::(Secp256K1Scalar::rand()); - let pk = ECDSAPublicKey((CurveScalar(sk.0) * C::GENERATOR_PROJECTIVE).to_affine()); + let sk = ECDSASecretKey::(Secp256K1Scalar::rand()); + let pk = ECDSAPublicKey((CurveScalar(sk.0) * Curve::GENERATOR_PROJECTIVE).to_affine()); let pk_target = ECDSAPublicKeyTarget(builder.constant_affine_point(pk.0)); @@ -122,7 +125,7 @@ mod tests { builder.verify_message(msg_target, sig_target, pk_target); - let data = builder.build(); + 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 5dacdb51..ec4d1263 100644 --- a/plonky2/src/gadgets/mod.rs +++ b/plonky2/src/gadgets/mod.rs @@ -2,7 +2,6 @@ pub mod arithmetic; pub mod arithmetic_extension; pub mod arithmetic_u32; pub mod biguint; -pub mod binary_arithmetic; pub mod curve; pub mod ecdsa; pub mod hash; diff --git a/plonky2/src/gadgets/nonnative.rs b/plonky2/src/gadgets/nonnative.rs index 2c556616..998ce628 100644 --- a/plonky2/src/gadgets/nonnative.rs +++ b/plonky2/src/gadgets/nonnative.rs @@ -1,12 +1,14 @@ use std::marker::PhantomData; -use num::{BigUint, Zero}; +use num::{BigUint, Integer, One, Zero}; use plonky2_field::{extension_field::Extendable, field_types::Field}; use plonky2_util::ceil_div_usize; use crate::gadgets::arithmetic_u32::U32Target; -use crate::field::field_types::RichField; -use crate::gadgets::binary_arithmetic::BinaryTarget; +use crate::gadgets::biguint::BigUintTarget; +use crate::hash::hash_types::RichField; +use crate::iop::generator::{GeneratedValues, SimpleGenerator}; +use crate::iop::target::{BoolTarget, Target}; use crate::iop::witness::{PartitionWitness, Witness}; use crate::plonk::circuit_builder::CircuitBuilder; @@ -514,6 +516,7 @@ mod tests { const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; + let x_ff = FF::rand(); let y_ff = FF::rand(); let sum_ff = x_ff + y_ff; @@ -537,6 +540,10 @@ mod tests { #[test] fn test_nonnative_many_adds() -> Result<()> { type FF = Secp256K1Base; + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + let a_ff = FF::rand(); let b_ff = FF::rand(); let c_ff = FF::rand(); @@ -549,7 +556,7 @@ mod tests { let config = CircuitConfig::standard_ecc_config(); let pw = PartialWitness::new(); - let mut builder = CircuitBuilder::::new(config); + let mut builder = CircuitBuilder::::new(config); let a = builder.constant_nonnative(a_ff); let b = builder.constant_nonnative(b_ff); @@ -565,7 +572,7 @@ mod tests { let sum_expected = builder.constant_nonnative(sum_ff); builder.connect_nonnative(&sum, &sum_expected); - let data = builder.build(); + let data = builder.build::(); let proof = data.prove(pw).unwrap(); verify(proof, &data.verifier_only, &data.common) } @@ -576,6 +583,7 @@ mod tests { const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; + let x_ff = FF::rand(); let mut y_ff = FF::rand(); while y_ff.to_biguint() > x_ff.to_biguint() { @@ -627,11 +635,13 @@ mod tests { fn test_nonnative_many_muls_helper(num: usize) { type FF = Secp256K1Base; + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; - type F = GoldilocksField; let config = CircuitConfig::standard_ecc_config(); - let mut unop_builder = CircuitBuilder::::new(config.clone()); - let mut op_builder = CircuitBuilder::::new(config); + let mut unop_builder = CircuitBuilder::::new(config.clone()); + let mut op_builder = CircuitBuilder::::new(config); let ffs: Vec<_> = (0..num).map(|_| FF::rand()).collect(); diff --git a/plonky2/src/gadgets/range_check.rs b/plonky2/src/gadgets/range_check.rs index 5a90afd0..0776fc68 100644 --- a/plonky2/src/gadgets/range_check.rs +++ b/plonky2/src/gadgets/range_check.rs @@ -1,5 +1,7 @@ use plonky2_field::extension_field::Extendable; +use crate::gadgets::arithmetic_u32::U32Target; +use crate::gates::range_check_u32::U32RangeCheckGate; use crate::hash::hash_types::RichField; use crate::iop::generator::{GeneratedValues, SimpleGenerator}; use crate::iop::target::{BoolTarget, Target}; diff --git a/plonky2/src/gates/add_many_u32.rs b/plonky2/src/gates/add_many_u32.rs index 9f67c827..4f9c4293 100644 --- a/plonky2/src/gates/add_many_u32.rs +++ b/plonky2/src/gates/add_many_u32.rs @@ -1,11 +1,14 @@ use std::marker::PhantomData; use itertools::unfold; +use plonky2_util::ceil_div_usize; -use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; -use crate::field::field_types::{Field, RichField}; +use crate::field::field_types::Field; use crate::gates::gate::Gate; +use crate::gates::util::StridedConstraintConsumer; +use crate::hash::hash_types::RichField; +use crate::iop::ext_target::ExtensionTarget; use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; use crate::iop::target::Target; use crate::iop::wire::Wire; @@ -13,7 +16,6 @@ use crate::iop::witness::{PartitionWitness, Witness}; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_data::CircuitConfig; use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; -use crate::util::ceil_div_usize; const LOG2_MAX_NUM_ADDENDS: usize = 4; const MAX_NUM_ADDENDS: usize = 16; @@ -128,8 +130,11 @@ impl, const D: usize> Gate for U32AddManyGate constraints } - fn eval_unfiltered_base(&self, vars: EvaluationVarsBase) -> Vec { - let mut constraints = Vec::with_capacity(self.num_constraints()); + fn eval_unfiltered_base_one( + &self, + vars: EvaluationVarsBase, + mut yield_constr: StridedConstraintConsumer, + ) { for i in 0..self.num_ops { let addends: Vec = (0..self.num_addends) .map(|j| vars.local_wires[self.wire_ith_op_jth_addend(i, j)]) @@ -144,7 +149,7 @@ impl, const D: usize> Gate for U32AddManyGate let base = F::from_canonical_u64(1 << 32u64); let combined_output = output_carry * base + output_result; - constraints.push(combined_output - computed_output); + yield_constr.one(combined_output - computed_output); let mut combined_result_limbs = F::ZERO; let mut combined_carry_limbs = F::ZERO; @@ -155,7 +160,7 @@ impl, const D: usize> Gate for U32AddManyGate let product = (0..max_limb) .map(|x| this_limb - F::from_canonical_usize(x)) .product(); - constraints.push(product); + yield_constr.one(product); if j < Self::num_result_limbs() { combined_result_limbs = base * combined_result_limbs + this_limb; @@ -163,11 +168,9 @@ impl, const D: usize> Gate for U32AddManyGate combined_carry_limbs = base * combined_carry_limbs + this_limb; } } - constraints.push(combined_result_limbs - output_result); - constraints.push(combined_carry_limbs - output_carry); + yield_constr.one(combined_result_limbs - output_result); + yield_constr.one(combined_carry_limbs - output_carry); } - - constraints } fn eval_unfiltered_recursively( @@ -355,6 +358,7 @@ mod tests { use crate::gates::gate::Gate; use crate::gates::gate_testing::{test_eval_fns, test_low_degree}; use crate::hash::hash_types::HashOut; + use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; use crate::plonk::vars::EvaluationVars; #[test] @@ -368,7 +372,10 @@ mod tests { #[test] fn eval_fns() -> Result<()> { - test_eval_fns::(U32AddManyGate:: { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + test_eval_fns::(U32AddManyGate:: { num_addends: 4, num_ops: 3, _phantom: PhantomData, diff --git a/plonky2/src/gates/mod.rs b/plonky2/src/gates/mod.rs index 163a7dac..18e3e99b 100644 --- a/plonky2/src/gates/mod.rs +++ b/plonky2/src/gates/mod.rs @@ -7,8 +7,6 @@ pub mod arithmetic_extension; pub mod arithmetic_u32; pub mod assert_le; pub mod base_sum; -pub mod binary_arithmetic; -pub mod binary_subtraction; pub mod comparison; pub mod constant; pub mod exponentiation; diff --git a/plonky2/src/gates/range_check_u32.rs b/plonky2/src/gates/range_check_u32.rs index 83cdb223..0e73990d 100644 --- a/plonky2/src/gates/range_check_u32.rs +++ b/plonky2/src/gates/range_check_u32.rs @@ -1,16 +1,19 @@ use std::marker::PhantomData; -use crate::field::extension_field::target::ExtensionTarget; +use plonky2_util::ceil_div_usize; + use crate::field::extension_field::Extendable; -use crate::field::field_types::{Field, RichField}; +use crate::field::field_types::Field; use crate::gates::gate::Gate; +use crate::gates::util::StridedConstraintConsumer; +use crate::hash::hash_types::RichField; +use crate::iop::ext_target::ExtensionTarget; use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; use crate::iop::target::Target; use crate::iop::witness::{PartitionWitness, Witness}; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::plonk_common::{reduce_with_powers, reduce_with_powers_ext_recursive}; use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; -use crate::util::ceil_div_usize; /// A gate which can decompose a number into base B little-endian limbs. #[derive(Copy, Clone, Debug)] @@ -73,9 +76,11 @@ impl, const D: usize> Gate for U32RangeCheckG constraints } - fn eval_unfiltered_base(&self, vars: EvaluationVarsBase) -> Vec { - let mut constraints = Vec::with_capacity(self.num_constraints()); - + fn eval_unfiltered_base_one( + &self, + vars: EvaluationVarsBase, + mut yield_constr: StridedConstraintConsumer, + ) { let base = F::from_canonical_usize(Self::BASE); for i in 0..self.num_input_limbs { let input_limb = vars.local_wires[self.wire_ith_input_limb(i)]; @@ -84,17 +89,15 @@ impl, const D: usize> Gate for U32RangeCheckG .collect(); let computed_sum = reduce_with_powers(&aux_limbs, base); - constraints.push(computed_sum - input_limb); + yield_constr.one(computed_sum - input_limb); for aux_limb in aux_limbs { - constraints.push( + yield_constr.one( (0..Self::BASE) .map(|i| aux_limb - F::from_canonical_usize(i)) .product(), ); } } - - constraints } fn eval_unfiltered_recursively( @@ -217,6 +220,7 @@ mod tests { use anyhow::Result; use itertools::unfold; + use plonky2_util::ceil_div_usize; use rand::Rng; use crate::field::extension_field::quartic::QuarticExtension; @@ -226,8 +230,8 @@ mod tests { use crate::gates::gate_testing::{test_eval_fns, test_low_degree}; use crate::gates::range_check_u32::U32RangeCheckGate; use crate::hash::hash_types::HashOut; + use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; use crate::plonk::vars::EvaluationVars; - use crate::util::ceil_div_usize; #[test] fn low_degree() { @@ -236,7 +240,10 @@ mod tests { #[test] fn eval_fns() -> Result<()> { - test_eval_fns::(U32RangeCheckGate::new(8)) + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + test_eval_fns::(U32RangeCheckGate::new(8)) } fn test_gate_constraint(input_limbs: Vec) { diff --git a/plonky2/src/iop/generator.rs b/plonky2/src/iop/generator.rs index 5f8b8a5f..5d36ed1d 100644 --- a/plonky2/src/iop/generator.rs +++ b/plonky2/src/iop/generator.rs @@ -1,17 +1,16 @@ use std::fmt::Debug; use std::marker::PhantomData; -use num::BigUint; +use num::{BigUint, FromPrimitive, Integer, Zero}; use plonky2_field::extension_field::{Extendable, FieldExtension}; use plonky2_field::field_types::Field; use crate::gadgets::arithmetic_u32::U32Target; use crate::gadgets::biguint::BigUintTarget; -use crate::gadgets::binary_arithmetic::BinaryTarget; use crate::gadgets::nonnative::NonNativeTarget; use crate::hash::hash_types::{HashOut, HashOutTarget, RichField}; use crate::iop::ext_target::ExtensionTarget; -use crate::iop::target::Target; +use crate::iop::target::{BoolTarget, Target}; use crate::iop::wire::Wire; use crate::iop::witness::{PartialWitness, PartitionWitness, Witness}; use crate::plonk::circuit_data::{CommonCircuitData, ProverOnlyCircuitData}; @@ -170,10 +169,6 @@ impl GeneratedValues { self.set_target(target.0, F::from_canonical_u32(value)) } - pub fn set_binary_target(&mut self, target: BinaryTarget, value: F) { - self.set_target(target.0, value) - } - pub fn set_biguint_target(&mut self, target: BigUintTarget, value: BigUint) { let base = BigUint::from_u64(1 << 32).unwrap(); let mut limbs = Vec::new(); diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index 38091bc9..33c88950 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -19,8 +19,6 @@ use crate::gadgets::polynomial::PolynomialCoeffsExtTarget; use crate::gates::arithmetic_base::ArithmeticGate; use crate::gates::arithmetic_extension::ArithmeticExtensionGate; use crate::gates::arithmetic_u32::U32ArithmeticGate; -use crate::gates::binary_arithmetic::BinaryArithmeticGate; -use crate::gates::binary_subtraction::BinarySubtractionGate; use crate::gates::constant::ConstantGate; use crate::gates::gate::{Gate, GateInstance, GateRef, PrefixedGate}; use crate::gates::gate_tree::Tree; @@ -354,11 +352,6 @@ impl, const D: usize> CircuitBuilder { U32Target(self.constant(F::from_canonical_u32(c))) } - /// Returns a BinaryTarget for the value `c`, which is assumed to be at most BITS bits. - pub fn constant_binary(&mut self, c: F) -> BinaryTarget { - BinaryTarget(self.constant(c)) - } - /// If the given target is a constant (i.e. it was created by the `constant(F)` method), returns /// its constant value. Otherwise, returns `None`. pub fn target_as_constant(&self, target: Target) -> Option { @@ -838,11 +831,6 @@ pub struct BatchedGates, const D: usize> { /// The `U32SubtractionGate` currently being filled (so new u32 subtraction operations will be added to this gate before creating a new one) pub(crate) current_u32_subtraction_gate: Option<(usize, usize)>, - /// A map `b -> (g, i)` from `b` bits to an available `BinaryArithmeticGate` for number of bits `b`. - pub(crate) free_binary_arithmetic_gate: HashMap, - /// A map `b -> (g, i)` from `b` bits to an available `BinarySubtractionGate` for number of bits `b`. - pub(crate) free_binary_subtraction_gate: HashMap, - /// An available `ConstantGate` instance, if any. pub(crate) free_constant: Option<(usize, usize)>, } @@ -858,8 +846,6 @@ impl, const D: usize> BatchedGates { free_u32_add_many: HashMap::new(), current_u32_arithmetic_gate: None, current_u32_subtraction_gate: None, - free_binary_arithmetic_gate: HashMap::new(), - free_binary_subtraction_gate: HashMap::new(), free_constant: None, } } @@ -1085,66 +1071,6 @@ impl, const D: usize> CircuitBuilder { (gate_index, copy) } - /// Finds the last available binary arithmetic with the given `bits` or add one if there aren't any. - /// Returns `(g,i)` such that there is a binary arithmetic for the given `bits` at index - /// `g` and the gate's `i`-th copy is available. - pub(crate) fn find_binary_arithmetic_gate(&mut self) -> (usize, usize) { - let (gate, i) = self - .batched_gates - .free_binary_arithmetic_gate - .get(&BITS) - .copied() - .unwrap_or_else(|| { - let gate = self.add_gate( - BinaryArithmeticGate::::new_from_config(&self.config), - vec![], - ); - (gate, 0) - }); - - // Update `free_binary_arithmetic` with new values. - if i + 1 < BinaryArithmeticGate::::new_from_config(&self.config).num_ops { - self.batched_gates - .free_binary_arithmetic_gate - .insert(BITS, (gate, i + 1)); - } else { - self.batched_gates.free_binary_arithmetic_gate.remove(&BITS); - } - - (gate, i) - } - - /// Finds the last available binary subtraction with the given `bits` or add one if there aren't any. - /// Returns `(g,i)` such that there is a binary subtraction for the given `bits` at index - /// `g` and the gate's `i`-th copy is available. - pub(crate) fn find_binary_subtraction_gate(&mut self) -> (usize, usize) { - let (gate, i) = self - .batched_gates - .free_binary_subtraction_gate - .get(&BITS) - .copied() - .unwrap_or_else(|| { - let gate = self.add_gate( - BinarySubtractionGate::::new_from_config(&self.config), - vec![], - ); - (gate, 0) - }); - - // Update `free_binary_subtraction` with new values. - if i + 1 < BinarySubtractionGate::::new_from_config(&self.config).num_ops { - self.batched_gates - .free_binary_subtraction_gate - .insert(BITS, (gate, i + 1)); - } else { - self.batched_gates - .free_binary_subtraction_gate - .remove(&BITS); - } - - (gate, i) - } - /// Returns the gate index and copy index of a free `ConstantGate` slot, potentially adding a /// new `ConstantGate` if needed. fn constant_gate_instance(&mut self) -> (usize, usize) { @@ -1301,36 +1227,6 @@ impl, const D: usize> CircuitBuilder { } } - /// Fill the remaining unused binary arithmetic operations with zeros, so that all - /// `BinaryArithmeticGenerator`s are run. - fn fill_binary_arithmetic_gates(&mut self) { - let zero = self.zero_binary::<30>(); - if let Some(&(_, i)) = self.batched_gates.free_binary_arithmetic_gate.get(&30) { - let max_copies = - BinaryArithmeticGate::::new_from_config(&self.config).num_ops; - for _ in i..max_copies { - let dummy = self.add_virtual_binary_target(); - self.mul_add_binary(dummy, dummy, dummy); - self.connect_binary(dummy, zero); - } - } - } - - /// Fill the remaining unused binary subtraction operations with zeros, so that all - /// `BinarySubtractionGenerator`s are run. - fn fill_binary_subtraction_gates(&mut self) { - let zero = self.zero_binary::<30>(); - if let Some(&(_, i)) = self.batched_gates.free_binary_subtraction_gate.get(&30) { - let max_copies = - BinarySubtractionGate::::new_from_config(&self.config).num_ops; - for _ in i..max_copies { - let dummy = self.add_virtual_binary_target(); - self.sub_binary(dummy, dummy, dummy); - self.connect_binary(dummy, zero); - } - } - } - fn fill_batched_gates(&mut self) { self.fill_arithmetic_gates(); self.fill_base_arithmetic_gates(); @@ -1340,7 +1236,5 @@ impl, const D: usize> CircuitBuilder { self.fill_u32_add_many_gates(); self.fill_u32_arithmetic_gates(); self.fill_u32_subtraction_gates(); - self.fill_binary_arithmetic_gates(); - self.fill_binary_subtraction_gates(); } } From edf75632086abaf9b709d2d5d5e549c40d5482ed Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 26 Jan 2022 11:30:13 -0800 Subject: [PATCH 23/36] MulBigUintByBool gate --- plonky2/src/gates/mul_biguint_bool.rs | 241 ++++++++++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 plonky2/src/gates/mul_biguint_bool.rs diff --git a/plonky2/src/gates/mul_biguint_bool.rs b/plonky2/src/gates/mul_biguint_bool.rs new file mode 100644 index 00000000..2fca84e1 --- /dev/null +++ b/plonky2/src/gates/mul_biguint_bool.rs @@ -0,0 +1,241 @@ +use std::marker::PhantomData; + +use plonky2_field::extension_field::Extendable; + +use crate::gates::gate::Gate; +use crate::gates::util::StridedConstraintConsumer; +use crate::hash::hash_types::RichField; +use crate::iop::ext_target::ExtensionTarget; +use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; +use crate::iop::target::Target; +use crate::iop::wire::Wire; +use crate::iop::witness::{PartitionWitness, Witness}; +use crate::plonk::circuit_builder::CircuitBuilder; +use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; + +/// A gate to perform a basic mul-add on 32-bit values (we assume they are range-checked beforehand). +#[derive(Copy, Clone, Debug)] +pub struct MulBiguintBoolGate, const D: usize> { + pub num_limbs: usize, + _phantom: PhantomData, +} + +impl, const D: usize> MulBiguintBoolGate { + pub fn new(num_limbs: usize) -> Self { + Self { + num_limbs, + _phantom: PhantomData, + } + } + + pub fn wire_ith_input_limb(&self, i: usize) -> usize { + debug_assert!(i < self.num_limbs); + i + } + pub fn wire_input_bool(&self) -> usize { + self.num_limbs + } + pub fn wire_ith_output_limb(&self, i: usize) -> usize { + debug_assert!(i < self.num_limbs); + self.num_limbs + 1 + i + } +} + +impl, const D: usize> Gate for MulBiguintBoolGate { + fn id(&self) -> String { + format!("{:?}", self) + } + + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { + let mut constraints = Vec::with_capacity(self.num_constraints()); + + let input_bool = vars.local_wires[self.wire_input_bool()]; + for i in 0..self.num_limbs { + let input_i = vars.local_wires[self.wire_ith_input_limb(i)]; + let output_i = vars.local_wires[self.wire_ith_output_limb(i)]; + + constraints.push(input_i * input_bool - output_i); + } + + constraints + } + + fn eval_unfiltered_base_one( + &self, + vars: EvaluationVarsBase, + mut yield_constr: StridedConstraintConsumer, + ) { + let input_bool = vars.local_wires[self.wire_input_bool()]; + for i in 0..self.num_limbs { + let input_i = vars.local_wires[self.wire_ith_input_limb(i)]; + let output_i = vars.local_wires[self.wire_ith_output_limb(i)]; + + yield_constr.one(input_i * input_bool - output_i); + } + } + + fn eval_unfiltered_recursively( + &self, + builder: &mut CircuitBuilder, + vars: EvaluationTargets, + ) -> Vec> { + let mut constraints = Vec::with_capacity(self.num_constraints()); + + let input_bool = vars.local_wires[self.wire_input_bool()]; + for i in 0..self.num_limbs { + let input_i = vars.local_wires[self.wire_ith_input_limb(i)]; + let output_i = vars.local_wires[self.wire_ith_output_limb(i)]; + + constraints.push(builder.mul_sub_extension(input_i, input_bool, output_i)); + } + + constraints + } + + fn generators( + &self, + gate_index: usize, + _local_constants: &[F], + ) -> Vec>> { + let gen = MulBiguintBoolGenerator { + gate: *self, + gate_index, + _phantom: PhantomData, + }; + vec![Box::new(gen.adapter())] + } + + fn num_wires(&self) -> usize { + self.num_limbs * 2 + 1 + } + + fn num_constants(&self) -> usize { + 0 + } + + fn degree(&self) -> usize { + 2 + } + + fn num_constraints(&self) -> usize { + self.num_limbs + } +} + +#[derive(Clone, Debug)] +struct MulBiguintBoolGenerator, const D: usize> { + gate: MulBiguintBoolGate, + gate_index: usize, + _phantom: PhantomData, +} + +impl, const D: usize> SimpleGenerator + for MulBiguintBoolGenerator +{ + fn dependencies(&self) -> Vec { + let local_target = |input| Target::wire(self.gate_index, input); + + (0..self.gate.num_limbs) + .map(|i| local_target(self.gate.wire_ith_input_limb(i))) + .chain([local_target(self.gate.wire_input_bool())]) + .collect() + } + + fn run_once(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues) { + let local_wire = |input| Wire { + gate: self.gate_index, + input, + }; + + let get_local_wire = |input| witness.get_wire(local_wire(input)); + + let input_bool = get_local_wire(self.gate.wire_input_bool()); + for i in 0..self.gate.num_limbs { + let input_limb = get_local_wire(self.gate.wire_ith_input_limb(i)); + let output_wire = local_wire(self.gate.wire_ith_output_limb(i)); + let output_limb = input_limb * input_bool; + out_buffer.set_wire(output_wire, output_limb); + } + } +} + +#[cfg(test)] +mod tests { + use std::marker::PhantomData; + + use anyhow::Result; + use plonky2_field::field_types::Field; + use plonky2_field::goldilocks_field::GoldilocksField; + use rand::Rng; + + use crate::gates::gate::Gate; + use crate::gates::gate_testing::{test_eval_fns, test_low_degree}; + use crate::gates::mul_biguint_bool::MulBiguintBoolGate; + use crate::hash::hash_types::HashOut; + use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + use crate::plonk::vars::EvaluationVars; + + #[test] + fn low_degree() { + test_low_degree::(MulBiguintBoolGate:: { + num_limbs: 8, + _phantom: PhantomData, + }) + } + + #[test] + fn eval_fns() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + test_eval_fns::(MulBiguintBoolGate:: { + num_limbs: 8, + _phantom: PhantomData, + }) + } + + #[test] + fn test_gate_constraint() { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type FF = >::FE; + const NUM_LIMBS: usize = 8; + + fn get_wires(input_limbs: Vec, input_bool: bool) -> Vec { + let output_limbs = input_limbs + .iter() + .map(|&l| if input_bool { l } else { F::ZERO }); + + input_limbs + .iter() + .cloned() + .chain([F::from_bool(input_bool)]) + .chain(output_limbs) + .map(|x| x.into()) + .collect() + } + + let mut rng = rand::thread_rng(); + let input_limbs: Vec<_> = (0..NUM_LIMBS) + .map(|_| F::from_canonical_u64(rng.gen())) + .collect(); + let input_bool: bool = rng.gen(); + + let gate = MulBiguintBoolGate:: { + num_limbs: NUM_LIMBS, + _phantom: PhantomData, + }; + + let vars = EvaluationVars { + local_constants: &[], + local_wires: &get_wires(input_limbs, input_bool), + public_inputs_hash: &HashOut::rand(), + }; + + assert!( + gate.eval_unfiltered(vars).iter().all(|x| x.is_zero()), + "Gate constraints are not satisfied." + ); + } +} From 82e2872f5e6de13376b56ead7be1b5d1d33988b9 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 27 Jan 2022 13:34:27 -0800 Subject: [PATCH 24/36] updates and addressed comments --- plonky2/src/curve/curve_types.rs | 8 + plonky2/src/curve/ecdsa.rs | 10 +- plonky2/src/gadgets/arithmetic_u32.rs | 20 +- plonky2/src/gadgets/biguint.rs | 20 +- plonky2/src/gadgets/curve.rs | 3 +- plonky2/src/gadgets/nonnative.rs | 11 +- plonky2/src/gates/arithmetic_extension.rs | 2 +- plonky2/src/gates/arithmetic_u32.rs | 5 +- plonky2/src/gates/comparison.rs | 2 +- plonky2/src/gates/interpolation.rs | 2 +- plonky2/src/gates/mul_biguint_bool.rs | 241 ---------------------- plonky2/src/gates/switch.rs | 2 +- 12 files changed, 24 insertions(+), 302 deletions(-) delete mode 100644 plonky2/src/gates/mul_biguint_bool.rs diff --git a/plonky2/src/curve/curve_types.rs b/plonky2/src/curve/curve_types.rs index b7ee34e6..9599f6fe 100644 --- a/plonky2/src/curve/curve_types.rs +++ b/plonky2/src/curve/curve_types.rs @@ -259,3 +259,11 @@ impl Neg for ProjectivePoint { ProjectivePoint { x, y: -y, z } } } + +pub fn base_to_scalar(x: C::BaseField) -> C::ScalarField { + C::ScalarField::from_biguint(x.to_biguint()) +} + +pub fn scalar_to_base(x: C::ScalarField) -> C::BaseField { + C::BaseField::from_biguint(x.to_biguint()) +} diff --git a/plonky2/src/curve/ecdsa.rs b/plonky2/src/curve/ecdsa.rs index cce83d4c..f708a827 100644 --- a/plonky2/src/curve/ecdsa.rs +++ b/plonky2/src/curve/ecdsa.rs @@ -1,7 +1,7 @@ use itertools::{unfold, Itertools}; use num::BigUint; -use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar}; +use crate::curve::curve_types::{base_to_scalar, AffinePoint, Curve, CurveScalar}; use crate::field::field_types::Field; use crate::hash::hash_types::RichField; use crate::hash::hashing::{hash_n_to_m, PlonkyPermutation}; @@ -15,14 +15,6 @@ pub struct ECDSASignature { pub struct ECDSASecretKey(pub C::ScalarField); pub struct ECDSAPublicKey(pub AffinePoint); -pub fn base_to_scalar(x: C::BaseField) -> C::ScalarField { - C::ScalarField::from_biguint(x.to_biguint()) -} - -pub fn scalar_to_base(x: C::ScalarField) -> C::BaseField { - C::BaseField::from_biguint(x.to_biguint()) -} - pub fn hash_to_bits>(x: F, num_bits: usize) -> Vec { let hashed = hash_n_to_m::(&vec![x], 1, true)[0]; diff --git a/plonky2/src/gadgets/arithmetic_u32.rs b/plonky2/src/gadgets/arithmetic_u32.rs index 8da01dc1..f57c1db5 100644 --- a/plonky2/src/gadgets/arithmetic_u32.rs +++ b/plonky2/src/gadgets/arithmetic_u32.rs @@ -196,24 +196,6 @@ impl, const D: usize> CircuitBuilder { (output_result, output_borrow) } - - pub fn split_to_u32(&mut self, x: Target) -> (U32Target, U32Target) { - let low = self.add_virtual_u32_target(); - let high = self.add_virtual_u32_target(); - - let base = self.constant(F::from_canonical_u64(1u64 << 32)); - let combined = self.mul_add(high.0, base, low.0); - self.connect(x, combined); - - self.add_simple_generator(SplitToU32Generator:: { - x: x.clone(), - low: low.clone(), - high: high.clone(), - _phantom: PhantomData, - }); - - (low, high) - } } #[derive(Debug)] @@ -235,7 +217,7 @@ impl, const D: usize> SimpleGenerator let x = witness.get_target(self.x.clone()); let x_u64 = x.to_canonical_u64(); let low = x_u64 as u32; - let high: u32 = (x_u64 >> 32).try_into().unwrap(); + let high = (x_u64 >> 32) as u32; out_buffer.set_u32_target(self.low.clone(), low); out_buffer.set_u32_target(self.high.clone(), high); diff --git a/plonky2/src/gadgets/biguint.rs b/plonky2/src/gadgets/biguint.rs index 51cfcc06..15c2a630 100644 --- a/plonky2/src/gadgets/biguint.rs +++ b/plonky2/src/gadgets/biguint.rs @@ -1,6 +1,6 @@ use std::marker::PhantomData; -use num::{BigUint, FromPrimitive, Integer, Zero}; +use num::{BigUint, Integer, Zero}; use plonky2_field::extension_field::Extendable; use crate::gadgets::arithmetic_u32::U32Target; @@ -27,14 +27,7 @@ impl BigUintTarget { impl, const D: usize> CircuitBuilder { pub fn constant_biguint(&mut self, value: &BigUint) -> BigUintTarget { - let base = BigUint::from_u64(1 << 32).unwrap(); - let mut limb_values = Vec::new(); - let mut current = value.clone(); - while current > BigUint::zero() { - let (div, rem) = current.div_rem(&base); - current = div; - limb_values.push(rem.to_u64_digits()[0] as u32); - } + let limb_values = value.to_u32_digits(); let limbs = limb_values.iter().map(|&l| self.constant_u32(l)).collect(); BigUintTarget { limbs } @@ -167,11 +160,10 @@ impl, const D: usize> CircuitBuilder { let t = b.target; BigUintTarget { - limbs: a - .limbs - .iter() - .map(|l| U32Target(self.mul(l.0, t))) - .collect(), + limbs: a.limbs + .iter() + .map(|&l| U32Target(self.mul(l.0, t))) + .collect(), } } diff --git a/plonky2/src/gadgets/curve.rs b/plonky2/src/gadgets/curve.rs index 92f45242..d16aaefa 100644 --- a/plonky2/src/gadgets/curve.rs +++ b/plonky2/src/gadgets/curve.rs @@ -135,7 +135,7 @@ impl, const D: usize> CircuitBuilder { for &bit in bits.iter() { let not_bit = self.not(bit); - + let result_plus_2_i_p = self.curve_add(&result, &two_i_times_p); let new_x_if_bit = self.mul_nonnative_by_bool(&result_plus_2_i_p.x, bit); @@ -321,6 +321,7 @@ mod tests { builder.connect_affine_point(&neg_five_g_expected, &neg_five_g_actual); + println!("NUM GATES: {}", builder.num_gates()); let data = builder.build::(); let proof = data.prove(pw).unwrap(); diff --git a/plonky2/src/gadgets/nonnative.rs b/plonky2/src/gadgets/nonnative.rs index 998ce628..b513abbb 100644 --- a/plonky2/src/gadgets/nonnative.rs +++ b/plonky2/src/gadgets/nonnative.rs @@ -93,17 +93,8 @@ impl, const D: usize> CircuitBuilder { a: &NonNativeTarget, b: BoolTarget, ) -> NonNativeTarget { - let t = b.target; - NonNativeTarget { - value: BigUintTarget { - limbs: a - .value - .limbs - .iter() - .map(|l| U32Target(self.mul(l.0, t))) - .collect(), - }, + value: self.mul_biguint_by_bool(&a.value, b), _phantom: PhantomData, } } diff --git a/plonky2/src/gates/arithmetic_extension.rs b/plonky2/src/gates/arithmetic_extension.rs index 93f7277c..def09473 100644 --- a/plonky2/src/gates/arithmetic_extension.rs +++ b/plonky2/src/gates/arithmetic_extension.rs @@ -138,7 +138,7 @@ impl, const D: usize> Gate for ArithmeticExte ); g }) - .collect::>() + .collect() } fn num_wires(&self) -> usize { diff --git a/plonky2/src/gates/arithmetic_u32.rs b/plonky2/src/gates/arithmetic_u32.rs index bef21a97..dc03e296 100644 --- a/plonky2/src/gates/arithmetic_u32.rs +++ b/plonky2/src/gates/arithmetic_u32.rs @@ -425,10 +425,7 @@ mod tests { v1.append(&mut output_limbs_f); } - v0.iter() - .chain(v1.iter()) - .map(|&x| x.into()) - .collect::>() + v0.iter().chain(v1.iter()).map(|&x| x.into()).collect() } let mut rng = rand::thread_rng(); diff --git a/plonky2/src/gates/comparison.rs b/plonky2/src/gates/comparison.rs index 9a119b1d..424ecb5b 100644 --- a/plonky2/src/gates/comparison.rs +++ b/plonky2/src/gates/comparison.rs @@ -658,7 +658,7 @@ mod tests { v.append(&mut intermediate_values); v.append(&mut msd_bits); - v.iter().map(|&x| x.into()).collect::>() + v.iter().map(|&x| x.into()).collect() }; let mut rng = rand::thread_rng(); diff --git a/plonky2/src/gates/interpolation.rs b/plonky2/src/gates/interpolation.rs index 9d3d6e3f..46c42113 100644 --- a/plonky2/src/gates/interpolation.rs +++ b/plonky2/src/gates/interpolation.rs @@ -343,7 +343,7 @@ mod tests { for i in 0..coeffs.len() { v.extend(coeffs.coeffs[i].0); } - v.iter().map(|&x| x.into()).collect::>() + v.iter().map(|&x| x.into()).collect() } // Get a working row for InterpolationGate. diff --git a/plonky2/src/gates/mul_biguint_bool.rs b/plonky2/src/gates/mul_biguint_bool.rs deleted file mode 100644 index 2fca84e1..00000000 --- a/plonky2/src/gates/mul_biguint_bool.rs +++ /dev/null @@ -1,241 +0,0 @@ -use std::marker::PhantomData; - -use plonky2_field::extension_field::Extendable; - -use crate::gates::gate::Gate; -use crate::gates::util::StridedConstraintConsumer; -use crate::hash::hash_types::RichField; -use crate::iop::ext_target::ExtensionTarget; -use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; -use crate::iop::target::Target; -use crate::iop::wire::Wire; -use crate::iop::witness::{PartitionWitness, Witness}; -use crate::plonk::circuit_builder::CircuitBuilder; -use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; - -/// A gate to perform a basic mul-add on 32-bit values (we assume they are range-checked beforehand). -#[derive(Copy, Clone, Debug)] -pub struct MulBiguintBoolGate, const D: usize> { - pub num_limbs: usize, - _phantom: PhantomData, -} - -impl, const D: usize> MulBiguintBoolGate { - pub fn new(num_limbs: usize) -> Self { - Self { - num_limbs, - _phantom: PhantomData, - } - } - - pub fn wire_ith_input_limb(&self, i: usize) -> usize { - debug_assert!(i < self.num_limbs); - i - } - pub fn wire_input_bool(&self) -> usize { - self.num_limbs - } - pub fn wire_ith_output_limb(&self, i: usize) -> usize { - debug_assert!(i < self.num_limbs); - self.num_limbs + 1 + i - } -} - -impl, const D: usize> Gate for MulBiguintBoolGate { - fn id(&self) -> String { - format!("{:?}", self) - } - - fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { - let mut constraints = Vec::with_capacity(self.num_constraints()); - - let input_bool = vars.local_wires[self.wire_input_bool()]; - for i in 0..self.num_limbs { - let input_i = vars.local_wires[self.wire_ith_input_limb(i)]; - let output_i = vars.local_wires[self.wire_ith_output_limb(i)]; - - constraints.push(input_i * input_bool - output_i); - } - - constraints - } - - fn eval_unfiltered_base_one( - &self, - vars: EvaluationVarsBase, - mut yield_constr: StridedConstraintConsumer, - ) { - let input_bool = vars.local_wires[self.wire_input_bool()]; - for i in 0..self.num_limbs { - let input_i = vars.local_wires[self.wire_ith_input_limb(i)]; - let output_i = vars.local_wires[self.wire_ith_output_limb(i)]; - - yield_constr.one(input_i * input_bool - output_i); - } - } - - fn eval_unfiltered_recursively( - &self, - builder: &mut CircuitBuilder, - vars: EvaluationTargets, - ) -> Vec> { - let mut constraints = Vec::with_capacity(self.num_constraints()); - - let input_bool = vars.local_wires[self.wire_input_bool()]; - for i in 0..self.num_limbs { - let input_i = vars.local_wires[self.wire_ith_input_limb(i)]; - let output_i = vars.local_wires[self.wire_ith_output_limb(i)]; - - constraints.push(builder.mul_sub_extension(input_i, input_bool, output_i)); - } - - constraints - } - - fn generators( - &self, - gate_index: usize, - _local_constants: &[F], - ) -> Vec>> { - let gen = MulBiguintBoolGenerator { - gate: *self, - gate_index, - _phantom: PhantomData, - }; - vec![Box::new(gen.adapter())] - } - - fn num_wires(&self) -> usize { - self.num_limbs * 2 + 1 - } - - fn num_constants(&self) -> usize { - 0 - } - - fn degree(&self) -> usize { - 2 - } - - fn num_constraints(&self) -> usize { - self.num_limbs - } -} - -#[derive(Clone, Debug)] -struct MulBiguintBoolGenerator, const D: usize> { - gate: MulBiguintBoolGate, - gate_index: usize, - _phantom: PhantomData, -} - -impl, const D: usize> SimpleGenerator - for MulBiguintBoolGenerator -{ - fn dependencies(&self) -> Vec { - let local_target = |input| Target::wire(self.gate_index, input); - - (0..self.gate.num_limbs) - .map(|i| local_target(self.gate.wire_ith_input_limb(i))) - .chain([local_target(self.gate.wire_input_bool())]) - .collect() - } - - fn run_once(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues) { - let local_wire = |input| Wire { - gate: self.gate_index, - input, - }; - - let get_local_wire = |input| witness.get_wire(local_wire(input)); - - let input_bool = get_local_wire(self.gate.wire_input_bool()); - for i in 0..self.gate.num_limbs { - let input_limb = get_local_wire(self.gate.wire_ith_input_limb(i)); - let output_wire = local_wire(self.gate.wire_ith_output_limb(i)); - let output_limb = input_limb * input_bool; - out_buffer.set_wire(output_wire, output_limb); - } - } -} - -#[cfg(test)] -mod tests { - use std::marker::PhantomData; - - use anyhow::Result; - use plonky2_field::field_types::Field; - use plonky2_field::goldilocks_field::GoldilocksField; - use rand::Rng; - - use crate::gates::gate::Gate; - use crate::gates::gate_testing::{test_eval_fns, test_low_degree}; - use crate::gates::mul_biguint_bool::MulBiguintBoolGate; - use crate::hash::hash_types::HashOut; - use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - use crate::plonk::vars::EvaluationVars; - - #[test] - fn low_degree() { - test_low_degree::(MulBiguintBoolGate:: { - num_limbs: 8, - _phantom: PhantomData, - }) - } - - #[test] - fn eval_fns() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - test_eval_fns::(MulBiguintBoolGate:: { - num_limbs: 8, - _phantom: PhantomData, - }) - } - - #[test] - fn test_gate_constraint() { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type FF = >::FE; - const NUM_LIMBS: usize = 8; - - fn get_wires(input_limbs: Vec, input_bool: bool) -> Vec { - let output_limbs = input_limbs - .iter() - .map(|&l| if input_bool { l } else { F::ZERO }); - - input_limbs - .iter() - .cloned() - .chain([F::from_bool(input_bool)]) - .chain(output_limbs) - .map(|x| x.into()) - .collect() - } - - let mut rng = rand::thread_rng(); - let input_limbs: Vec<_> = (0..NUM_LIMBS) - .map(|_| F::from_canonical_u64(rng.gen())) - .collect(); - let input_bool: bool = rng.gen(); - - let gate = MulBiguintBoolGate:: { - num_limbs: NUM_LIMBS, - _phantom: PhantomData, - }; - - let vars = EvaluationVars { - local_constants: &[], - local_wires: &get_wires(input_limbs, input_bool), - public_inputs_hash: &HashOut::rand(), - }; - - assert!( - gate.eval_unfiltered(vars).iter().all(|x| x.is_zero()), - "Gate constraints are not satisfied." - ); - } -} diff --git a/plonky2/src/gates/switch.rs b/plonky2/src/gates/switch.rs index c5271a60..583f9a6e 100644 --- a/plonky2/src/gates/switch.rs +++ b/plonky2/src/gates/switch.rs @@ -432,7 +432,7 @@ mod tests { v.push(F::from_bool(switch)); } - v.iter().map(|&x| x.into()).collect::>() + v.iter().map(|&x| x.into()).collect() } let first_inputs: Vec> = (0..num_copies).map(|_| F::rand_vec(CHUNK_SIZE)).collect(); From 493f516fac517f068b0fff0b22d1677063d12609 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 27 Jan 2022 14:54:10 -0800 Subject: [PATCH 25/36] removed hashing --- plonky2/src/curve/ecdsa.rs | 58 +++++------------------------------- plonky2/src/gadgets/ecdsa.rs | 38 +++-------------------- 2 files changed, 12 insertions(+), 84 deletions(-) diff --git a/plonky2/src/curve/ecdsa.rs b/plonky2/src/curve/ecdsa.rs index f708a827..3276e9cb 100644 --- a/plonky2/src/curve/ecdsa.rs +++ b/plonky2/src/curve/ecdsa.rs @@ -1,11 +1,5 @@ -use itertools::{unfold, Itertools}; -use num::BigUint; - -use crate::curve::curve_types::{base_to_scalar, AffinePoint, Curve, CurveScalar}; +use crate::curve::curve_types::{AffinePoint, base_to_scalar, Curve, CurveScalar}; use crate::field::field_types::Field; -use crate::hash::hash_types::RichField; -use crate::hash::hashing::{hash_n_to_m, PlonkyPermutation}; -use crate::hash::poseidon::PoseidonPermutation; pub struct ECDSASignature { pub r: C::ScalarField, @@ -15,59 +9,25 @@ pub struct ECDSASignature { pub struct ECDSASecretKey(pub C::ScalarField); pub struct ECDSAPublicKey(pub AffinePoint); -pub fn hash_to_bits>(x: F, num_bits: usize) -> Vec { - let hashed = hash_n_to_m::(&vec![x], 1, true)[0]; - - let mut val = hashed.to_canonical_u64(); - unfold((), move |_| { - let ret = val % 2 != 0; - val /= 2; - Some(ret) - }) - .take(num_bits) - .collect() -} - -pub fn hash_to_scalar>( - x: F, - num_bits: usize, -) -> C::ScalarField { - let h_bits = hash_to_bits::(x, num_bits); - let h_vals: Vec<_> = h_bits - .iter() - .chunks(32) - .into_iter() - .map(|chunk| { - chunk - .enumerate() - .fold(0u32, |acc, (pow, &bit)| acc + (bit as u32) * (2 << pow)) - }) - .collect(); - C::ScalarField::from_biguint(BigUint::new(h_vals)) -} - -pub fn sign_message(msg: F, sk: ECDSASecretKey) -> ECDSASignature { - let h = hash_to_scalar::(msg, 256); - +pub fn sign_message(msg: C::ScalarField, sk: ECDSASecretKey) -> ECDSASignature { let k = C::ScalarField::rand(); let rr = (CurveScalar(k) * C::GENERATOR_PROJECTIVE).to_affine(); let r = base_to_scalar::(rr.x); - let s = k.inverse() * (h + r * sk.0); + + let s = k.inverse() * (msg + r * sk.0); ECDSASignature { r, s } } -pub fn verify_message( - msg: F, +pub fn verify_message( + msg: C::ScalarField, sig: ECDSASignature, pk: ECDSAPublicKey, ) -> bool { let ECDSASignature { r, s } = sig; - let h = hash_to_scalar::(msg, 256); - let c = s.inverse(); - let u1 = h * c; + let u1 = msg * c; let u2 = r * c; let g = C::GENERATOR_PROJECTIVE; @@ -84,15 +44,13 @@ mod tests { use crate::curve::ecdsa::{sign_message, verify_message, ECDSAPublicKey, ECDSASecretKey}; use crate::curve::secp256k1::Secp256K1; use crate::field::field_types::Field; - use crate::field::goldilocks_field::GoldilocksField; use crate::field::secp256k1_scalar::Secp256K1Scalar; #[test] fn test_ecdsa_native() { - type F = GoldilocksField; type C = Secp256K1; - let msg = F::rand(); + let msg = Secp256K1Scalar::rand(); let sk = ECDSASecretKey(Secp256K1Scalar::rand()); let pk = ECDSAPublicKey((CurveScalar(sk.0) * C::GENERATOR_PROJECTIVE).to_affine()); diff --git a/plonky2/src/gadgets/ecdsa.rs b/plonky2/src/gadgets/ecdsa.rs index cc787eb1..9a7a8257 100644 --- a/plonky2/src/gadgets/ecdsa.rs +++ b/plonky2/src/gadgets/ecdsa.rs @@ -20,46 +20,16 @@ pub struct ECDSASignatureTarget { } impl, const D: usize> CircuitBuilder { - pub fn hash_to_bits(&mut self, x: Target, num_bits: usize) -> Vec { - let inputs = vec![x]; - let hashed = self.hash_n_to_m::(inputs, 1, true)[0]; - self.split_le(hashed, num_bits) - } - - pub fn hash_to_scalar( - &mut self, - x: Target, - num_bits: usize, - ) -> NonNativeTarget { - let h_bits = self.hash_to_bits(x, num_bits); - - let two = self.two(); - let mut rev_bits = h_bits.iter().rev(); - let mut sum = rev_bits.next().unwrap().target; - for &bit in rev_bits { - sum = self.mul_add(two, sum, bit.target); - } - let limbs = vec![U32Target(sum)]; - let value = BigUintTarget { limbs }; - - NonNativeTarget { - value, - _phantom: PhantomData, - } - } - pub fn verify_message( &mut self, - msg: Target, + msg: NonNativeTarget, sig: ECDSASignatureTarget, pk: ECDSAPublicKeyTarget, ) { let ECDSASignatureTarget { r, s } = sig; - let h = self.hash_to_scalar::(msg, 256); - let c = self.inv_nonnative(&s); - let u1 = self.mul_nonnative(&h, &c); + let u1 = self.mul_nonnative(&msg, &c); let u2 = self.mul_nonnative(&r, &c); let g = self.constant_affine_point(C::GENERATOR_AFFINE); @@ -105,8 +75,8 @@ mod tests { let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); - let msg = F::rand(); - let msg_target = builder.constant(msg); + let msg = Secp256K1Scalar::rand(); + let msg_target = builder.constant_nonnative(msg); let sk = ECDSASecretKey::(Secp256K1Scalar::rand()); let pk = ECDSAPublicKey((CurveScalar(sk.0) * Curve::GENERATOR_PROJECTIVE).to_affine()); From b62fa3f60880689d0ee62a8d2d39674c4871eb13 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 27 Jan 2022 14:54:16 -0800 Subject: [PATCH 26/36] fmt --- plonky2/src/curve/ecdsa.rs | 2 +- plonky2/src/gadgets/biguint.rs | 9 +++++---- plonky2/src/gadgets/curve.rs | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/plonky2/src/curve/ecdsa.rs b/plonky2/src/curve/ecdsa.rs index 3276e9cb..787567aa 100644 --- a/plonky2/src/curve/ecdsa.rs +++ b/plonky2/src/curve/ecdsa.rs @@ -1,4 +1,4 @@ -use crate::curve::curve_types::{AffinePoint, base_to_scalar, Curve, CurveScalar}; +use crate::curve::curve_types::{base_to_scalar, AffinePoint, Curve, CurveScalar}; use crate::field::field_types::Field; pub struct ECDSASignature { diff --git a/plonky2/src/gadgets/biguint.rs b/plonky2/src/gadgets/biguint.rs index 15c2a630..c9ad7280 100644 --- a/plonky2/src/gadgets/biguint.rs +++ b/plonky2/src/gadgets/biguint.rs @@ -160,10 +160,11 @@ impl, const D: usize> CircuitBuilder { let t = b.target; BigUintTarget { - limbs: a.limbs - .iter() - .map(|&l| U32Target(self.mul(l.0, t))) - .collect(), + limbs: a + .limbs + .iter() + .map(|&l| U32Target(self.mul(l.0, t))) + .collect(), } } diff --git a/plonky2/src/gadgets/curve.rs b/plonky2/src/gadgets/curve.rs index d16aaefa..59b13840 100644 --- a/plonky2/src/gadgets/curve.rs +++ b/plonky2/src/gadgets/curve.rs @@ -135,7 +135,7 @@ impl, const D: usize> CircuitBuilder { for &bit in bits.iter() { let not_bit = self.not(bit); - + let result_plus_2_i_p = self.curve_add(&result, &two_i_times_p); let new_x_if_bit = self.mul_nonnative_by_bool(&result_plus_2_i_p.x, bit); From b1c8709f7e670f6767ea8fdd02c7b92ae6a07684 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 28 Jan 2022 10:55:17 -0800 Subject: [PATCH 27/36] addressed more comments --- plonky2/src/curve/ecdsa.rs | 2 ++ plonky2/src/gadgets/ecdsa.rs | 7 ++----- plonky2/src/gadgets/nonnative.rs | 35 -------------------------------- plonky2/src/iop/generator.rs | 9 +------- 4 files changed, 5 insertions(+), 48 deletions(-) diff --git a/plonky2/src/curve/ecdsa.rs b/plonky2/src/curve/ecdsa.rs index 787567aa..e86b40d8 100644 --- a/plonky2/src/curve/ecdsa.rs +++ b/plonky2/src/curve/ecdsa.rs @@ -26,6 +26,8 @@ pub fn verify_message( ) -> bool { let ECDSASignature { r, s } = sig; + assert!(pk.0.is_valid()); + let c = s.inverse(); let u1 = msg * c; let u2 = r * c; diff --git a/plonky2/src/gadgets/ecdsa.rs b/plonky2/src/gadgets/ecdsa.rs index 9a7a8257..2acd1c80 100644 --- a/plonky2/src/gadgets/ecdsa.rs +++ b/plonky2/src/gadgets/ecdsa.rs @@ -2,13 +2,9 @@ use std::marker::PhantomData; use crate::curve::curve_types::Curve; use crate::field::extension_field::Extendable; -use crate::gadgets::arithmetic_u32::U32Target; -use crate::gadgets::biguint::BigUintTarget; use crate::gadgets::curve::AffinePointTarget; use crate::gadgets::nonnative::NonNativeTarget; use crate::hash::hash_types::RichField; -use crate::hash::poseidon::PoseidonHash; -use crate::iop::target::{BoolTarget, Target}; use crate::plonk::circuit_builder::CircuitBuilder; pub struct ECDSASecretKeyTarget(NonNativeTarget); @@ -28,6 +24,8 @@ impl, const D: usize> CircuitBuilder { ) { let ECDSASignatureTarget { r, s } = sig; + self.curve_assert_valid(&pk.0); + let c = self.inv_nonnative(&s); let u1 = self.mul_nonnative(&msg, &c); let u2 = self.mul_nonnative(&r, &c); @@ -62,7 +60,6 @@ mod tests { use crate::plonk::verifier::verify; #[test] - #[ignore] fn test_ecdsa_circuit() -> Result<()> { const D: usize = 2; type C = PoseidonGoldilocksConfig; diff --git a/plonky2/src/gadgets/nonnative.rs b/plonky2/src/gadgets/nonnative.rs index b513abbb..293a7183 100644 --- a/plonky2/src/gadgets/nonnative.rs +++ b/plonky2/src/gadgets/nonnative.rs @@ -624,41 +624,6 @@ mod tests { verify(proof, &data.verifier_only, &data.common) } - fn test_nonnative_many_muls_helper(num: usize) { - type FF = Secp256K1Base; - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - - let config = CircuitConfig::standard_ecc_config(); - let mut unop_builder = CircuitBuilder::::new(config.clone()); - let mut op_builder = CircuitBuilder::::new(config); - - let ffs: Vec<_> = (0..num).map(|_| FF::rand()).collect(); - - let op_targets: Vec<_> = ffs - .iter() - .map(|&x| op_builder.constant_nonnative(x)) - .collect(); - op_builder.mul_many_nonnative(&op_targets); - - let unop_targets: Vec<_> = ffs - .iter() - .map(|&x| unop_builder.constant_nonnative(x)) - .collect(); - let mut result = unop_targets[0].clone(); - for i in 1..unop_targets.len() { - result = unop_builder.mul_nonnative(&result, &unop_targets[i]); - } - } - - #[test] - fn test_nonnative_many_muls() { - for num in 2..10 { - test_nonnative_many_muls_helper(num); - } - } - #[test] fn test_nonnative_neg() -> Result<()> { type FF = Secp256K1Base; diff --git a/plonky2/src/iop/generator.rs b/plonky2/src/iop/generator.rs index 5d36ed1d..fb089d29 100644 --- a/plonky2/src/iop/generator.rs +++ b/plonky2/src/iop/generator.rs @@ -170,14 +170,7 @@ impl GeneratedValues { } pub fn set_biguint_target(&mut self, target: BigUintTarget, value: BigUint) { - let base = BigUint::from_u64(1 << 32).unwrap(); - let mut limbs = Vec::new(); - let mut current = value.clone(); - while current > BigUint::zero() { - let (div, rem) = current.div_rem(&base); - current = div; - limbs.push(rem.to_u64_digits()[0] as u32); - } + let mut limbs = value.to_u32_digits(); assert!(target.num_limbs() >= limbs.len()); From c1b8515e48a69f87b71516a389eb8cf1e493a8b2 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 28 Jan 2022 10:55:58 -0800 Subject: [PATCH 28/36] warning --- plonky2/src/iop/generator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plonky2/src/iop/generator.rs b/plonky2/src/iop/generator.rs index fb089d29..73978f5c 100644 --- a/plonky2/src/iop/generator.rs +++ b/plonky2/src/iop/generator.rs @@ -1,7 +1,7 @@ use std::fmt::Debug; use std::marker::PhantomData; -use num::{BigUint, FromPrimitive, Integer, Zero}; +use num::BigUint; use plonky2_field::extension_field::{Extendable, FieldExtension}; use plonky2_field::field_types::Field; From 30f936c43436a0e1f93f58008aba03041aeb4161 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Sat, 29 Jan 2022 07:54:55 -0800 Subject: [PATCH 29/36] ecdsa changes --- plonky2/src/curve/ecdsa.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/plonky2/src/curve/ecdsa.rs b/plonky2/src/curve/ecdsa.rs index e86b40d8..68660f0d 100644 --- a/plonky2/src/curve/ecdsa.rs +++ b/plonky2/src/curve/ecdsa.rs @@ -1,3 +1,4 @@ +use crate::curve::curve_msm::msm_parallel; use crate::curve::curve_types::{base_to_scalar, AffinePoint, Curve, CurveScalar}; use crate::field::field_types::Field; @@ -10,8 +11,15 @@ pub struct ECDSASecretKey(pub C::ScalarField); pub struct ECDSAPublicKey(pub AffinePoint); pub fn sign_message(msg: C::ScalarField, sk: ECDSASecretKey) -> ECDSASignature { - let k = C::ScalarField::rand(); - let rr = (CurveScalar(k) * C::GENERATOR_PROJECTIVE).to_affine(); + let (k, rr) = { + let mut k = C::ScalarField::rand(); + let mut rr = (CurveScalar(k) * C::GENERATOR_PROJECTIVE).to_affine(); + while rr.x == C::BaseField::ZERO { + k = C::ScalarField::rand(); + rr = (CurveScalar(k) * C::GENERATOR_PROJECTIVE).to_affine(); + } + (k, rr) + }; let r = base_to_scalar::(rr.x); let s = k.inverse() * (msg + r * sk.0); From d68ab119130c8e3af1f2b5177aa4492fa041480e Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Sun, 30 Jan 2022 08:30:54 -0800 Subject: [PATCH 30/36] msm --- plonky2/src/curve/ecdsa.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plonky2/src/curve/ecdsa.rs b/plonky2/src/curve/ecdsa.rs index 68660f0d..c84c4c10 100644 --- a/plonky2/src/curve/ecdsa.rs +++ b/plonky2/src/curve/ecdsa.rs @@ -41,7 +41,8 @@ pub fn verify_message( let u2 = r * c; let g = C::GENERATOR_PROJECTIVE; - let point_proj = CurveScalar(u1) * g + CurveScalar(u2) * pk.0.to_projective(); + let w = 5; // Experimentally fastest + let point_proj = msm_parallel(&[u1, u2], &[g, pk.0.to_projective()], w); let point = point_proj.to_affine(); let x = base_to_scalar::(point.x); From 20930e008697301b70750c80e41bc273bba1e7a8 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Sun, 30 Jan 2022 08:52:26 -0800 Subject: [PATCH 31/36] range-check add results --- plonky2/src/gadgets/curve.rs | 1 - plonky2/src/gadgets/nonnative.rs | 8 ++++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/plonky2/src/gadgets/curve.rs b/plonky2/src/gadgets/curve.rs index 59b13840..92f45242 100644 --- a/plonky2/src/gadgets/curve.rs +++ b/plonky2/src/gadgets/curve.rs @@ -321,7 +321,6 @@ mod tests { builder.connect_affine_point(&neg_five_g_expected, &neg_five_g_actual); - println!("NUM GATES: {}", builder.num_gates()); let data = builder.build::(); let proof = data.prove(pw).unwrap(); diff --git a/plonky2/src/gadgets/nonnative.rs b/plonky2/src/gadgets/nonnative.rs index 293a7183..8ce5933c 100644 --- a/plonky2/src/gadgets/nonnative.rs +++ b/plonky2/src/gadgets/nonnative.rs @@ -85,6 +85,10 @@ impl, const D: usize> CircuitBuilder { let sum_actual = self.add_biguint(&sum.value, &mod_times_overflow); self.connect_biguint(&sum_expected, &sum_actual); + let cmp = self.cmp_biguint(&sum.value, &modulus); + let one = self.one(); + self.connect(cmp.target, one); + sum } @@ -133,6 +137,10 @@ impl, const D: usize> CircuitBuilder { let sum_actual = self.add_biguint(&sum.value, &mod_times_overflow); self.connect_biguint(&sum_expected, &sum_actual); + let cmp = self.cmp_biguint(&sum.value, &modulus); + let one = self.one(); + self.connect(cmp.target, one); + sum } From 8a56af93c2e921f3fad3028230cc38dc48801d54 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 31 Jan 2022 10:20:05 -0800 Subject: [PATCH 32/36] TODOs --- plonky2/src/gadgets/nonnative.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plonky2/src/gadgets/nonnative.rs b/plonky2/src/gadgets/nonnative.rs index 8ce5933c..637eaccc 100644 --- a/plonky2/src/gadgets/nonnative.rs +++ b/plonky2/src/gadgets/nonnative.rs @@ -85,6 +85,8 @@ impl, const D: usize> CircuitBuilder { let sum_actual = self.add_biguint(&sum.value, &mod_times_overflow); self.connect_biguint(&sum_expected, &sum_actual); + // Range-check result. + // TODO: can potentially leave unreduced until necessary (e.g. when connecting values). let cmp = self.cmp_biguint(&sum.value, &modulus); let one = self.one(); self.connect(cmp.target, one); @@ -137,6 +139,8 @@ impl, const D: usize> CircuitBuilder { let sum_actual = self.add_biguint(&sum.value, &mod_times_overflow); self.connect_biguint(&sum_expected, &sum_actual); + // Range-check result. + // TODO: can potentially leave unreduced until necessary (e.g. when connecting values). let cmp = self.cmp_biguint(&sum.value, &modulus); let one = self.one(); self.connect(cmp.target, one); From a471574f785ef386f81e90a39f590a73f858eec7 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 31 Jan 2022 10:38:02 -0800 Subject: [PATCH 33/36] fix --- plonky2/src/plonk/circuit_builder.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index 33c88950..58fff814 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -16,6 +16,7 @@ use crate::gadgets::arithmetic::BaseArithmeticOperation; use crate::gadgets::arithmetic_extension::ExtensionArithmeticOperation; use crate::gadgets::arithmetic_u32::U32Target; use crate::gadgets::polynomial::PolynomialCoeffsExtTarget; +use crate::gates::add_many_u32::U32AddManyGate; use crate::gates::arithmetic_base::ArithmeticGate; use crate::gates::arithmetic_extension::ArithmeticExtensionGate; use crate::gates::arithmetic_u32::U32ArithmeticGate; From 5b5084b180bbf1991c80d239f77393574181c411 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 31 Jan 2022 10:49:06 -0800 Subject: [PATCH 34/36] clippy --- plonky2/src/gadgets/arithmetic_u32.rs | 6 +++--- plonky2/src/gadgets/curve.rs | 4 ++-- plonky2/src/gadgets/nonnative.rs | 14 ++++++-------- plonky2/src/gates/range_check_u32.rs | 2 +- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/plonky2/src/gadgets/arithmetic_u32.rs b/plonky2/src/gadgets/arithmetic_u32.rs index f57c1db5..8a0d5784 100644 --- a/plonky2/src/gadgets/arithmetic_u32.rs +++ b/plonky2/src/gadgets/arithmetic_u32.rs @@ -214,13 +214,13 @@ impl, const D: usize> SimpleGenerator } fn run_once(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues) { - let x = witness.get_target(self.x.clone()); + let x = witness.get_target(self.x); let x_u64 = x.to_canonical_u64(); let low = x_u64 as u32; let high = (x_u64 >> 32) as u32; - out_buffer.set_u32_target(self.low.clone(), low); - out_buffer.set_u32_target(self.high.clone(), high); + out_buffer.set_u32_target(self.low, low); + out_buffer.set_u32_target(self.high, high); } } diff --git a/plonky2/src/gadgets/curve.rs b/plonky2/src/gadgets/curve.rs index 92f45242..2ff22319 100644 --- a/plonky2/src/gadgets/curve.rs +++ b/plonky2/src/gadgets/curve.rs @@ -110,9 +110,9 @@ impl, const D: usize> CircuitBuilder { let s_squared = self.mul_nonnative(&s, &s); let x_sum = self.add_nonnative(x2, x1); let x3 = self.sub_nonnative(&s_squared, &x_sum); - let x_diff = self.sub_nonnative(&x1, &x3); + let x_diff = self.sub_nonnative(x1, &x3); let prod = self.mul_nonnative(&s, &x_diff); - let y3 = self.sub_nonnative(&prod, &y1); + let y3 = self.sub_nonnative(&prod, y1); AffinePointTarget { x: x3, y: y3 } } diff --git a/plonky2/src/gadgets/nonnative.rs b/plonky2/src/gadgets/nonnative.rs index 637eaccc..245b0403 100644 --- a/plonky2/src/gadgets/nonnative.rs +++ b/plonky2/src/gadgets/nonnative.rs @@ -74,7 +74,7 @@ impl, const D: usize> CircuitBuilder { a: a.clone(), b: b.clone(), sum: sum.clone(), - overflow: overflow.clone(), + overflow, _phantom: PhantomData, }); @@ -120,7 +120,7 @@ impl, const D: usize> CircuitBuilder { self.add_simple_generator(NonNativeMultipleAddsGenerator:: { summands: summands.clone(), sum: sum.clone(), - overflow: overflow.clone(), + overflow, _phantom: PhantomData, }); @@ -161,7 +161,7 @@ impl, const D: usize> CircuitBuilder { a: a.clone(), b: b.clone(), diff: diff.clone(), - overflow: overflow.clone(), + overflow, _phantom: PhantomData, }); @@ -250,11 +250,10 @@ impl, const D: usize> CircuitBuilder { let expected_product = self.add_biguint(&mod_times_div, &one); self.connect_biguint(&product, &expected_product); - let inv = NonNativeTarget:: { + NonNativeTarget:: { value: inv_biguint, _phantom: PhantomData, - }; - inv + } } /// Returns `x % |FF|` as a `NonNativeTarget`. @@ -362,8 +361,7 @@ impl, const D: usize, FF: Field> SimpleGenerator fn dependencies(&self) -> Vec { self.summands .iter() - .map(|summand| summand.value.limbs.iter().map(|limb| limb.0)) - .flatten() + .flat_map(|summand| summand.value.limbs.iter().map(|limb| limb.0)) .collect() } diff --git a/plonky2/src/gates/range_check_u32.rs b/plonky2/src/gates/range_check_u32.rs index 0e73990d..79e91de8 100644 --- a/plonky2/src/gates/range_check_u32.rs +++ b/plonky2/src/gates/range_check_u32.rs @@ -141,7 +141,7 @@ impl, const D: usize> Gate for U32RangeCheckG _local_constants: &[F], ) -> Vec>> { let gen = U32RangeCheckGenerator { - gate: self.clone(), + gate: *self, gate_index, }; vec![Box::new(gen.adapter())] From 11a1e52c38a140a1aed9c6a9f4684a056d97be60 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 31 Jan 2022 11:17:00 -0800 Subject: [PATCH 35/36] ignore ecdsa circuit test --- plonky2/src/gadgets/ecdsa.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/plonky2/src/gadgets/ecdsa.rs b/plonky2/src/gadgets/ecdsa.rs index 2acd1c80..eba04d85 100644 --- a/plonky2/src/gadgets/ecdsa.rs +++ b/plonky2/src/gadgets/ecdsa.rs @@ -60,6 +60,7 @@ mod tests { use crate::plonk::verifier::verify; #[test] + #[ignore] fn test_ecdsa_circuit() -> Result<()> { const D: usize = 2; type C = PoseidonGoldilocksConfig; From b0afb581f1d7f631f0c7469e8f7fefff855daba0 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 31 Jan 2022 11:23:40 -0800 Subject: [PATCH 36/36] fixed clippy warnings from CI --- plonky2/src/gates/gate.rs | 2 +- plonky2/src/plonk/circuit_builder.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plonky2/src/gates/gate.rs b/plonky2/src/gates/gate.rs index 9ae2a271..9d21c273 100644 --- a/plonky2/src/gates/gate.rs +++ b/plonky2/src/gates/gate.rs @@ -113,7 +113,7 @@ pub trait Gate, const D: usize>: 'static + Send + S builder: &mut CircuitBuilder, mut vars: EvaluationTargets, prefix: &[bool], - combined_gate_constraints: &mut Vec>, + combined_gate_constraints: &mut [ExtensionTarget], ) { let filter = compute_filter_recursively(builder, prefix, vars.local_constants); vars.remove_prefix(prefix); diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index 58fff814..ad216d69 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -661,14 +661,14 @@ impl, const D: usize> CircuitBuilder { let subgroup = F::two_adic_subgroup(degree_bits); let constant_vecs = timed!( - &mut timing, + timing, "generate constant polynomials", self.constant_polys(&prefixed_gates, num_constants) ); let k_is = get_unique_coset_shifts(degree, self.config.num_routed_wires); let (sigma_vecs, forest) = timed!( - &mut timing, + timing, "generate sigma polynomials", self.sigma_vecs(&k_is, &subgroup) );