From 883fc2bfb8a8f91e1f5dd8b5afa9c96048095e38 Mon Sep 17 00:00:00 2001 From: Kirill Karbushev Date: Fri, 8 Dec 2023 13:48:51 +0900 Subject: [PATCH 01/17] adjust types to work with new drivers --- bn254/src/driver.rs | 20 +++++++++++++++++ bn254/src/lib.rs | 1 + groth16/src/circuit.rs | 4 ++-- groth16/src/lib.rs | 4 ++-- grumpkin/src/driver.rs | 11 +++++----- nova/src/circuit/nifs.rs | 8 +++---- nova/src/circuit/transcript.rs | 11 +++++----- nova/src/gadget/relaxed_instance.rs | 23 ++++++++++---------- nova/src/hash.rs | 33 +++++++++++++++-------------- nova/src/ivc.rs | 4 ++-- nova/src/prover.rs | 16 +++++++------- nova/src/relaxed_r1cs.rs | 2 +- nova/src/relaxed_r1cs/instance.rs | 8 +++---- nova/src/verifier.rs | 4 ++-- zkstd/src/circuit.rs | 2 +- zkstd/src/circuit/gadget/curve.rs | 25 +++++++++++----------- zkstd/src/lib.rs | 2 +- zkstd/tests/gadget.rs | 29 ++++++++----------------- zkstd/tests/grumpkin.rs | 8 +++---- 19 files changed, 113 insertions(+), 102 deletions(-) create mode 100644 bn254/src/driver.rs diff --git a/bn254/src/driver.rs b/bn254/src/driver.rs new file mode 100644 index 00000000..f5d766d5 --- /dev/null +++ b/bn254/src/driver.rs @@ -0,0 +1,20 @@ +use crate::params::PARAM_B3; +use crate::G1Affine; +use crate::{Fq, Fr}; +use zkstd::circuit::CircuitDriver; + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct Bn254Driver; + +impl CircuitDriver for Bn254Driver { + const NUM_BITS: u16 = 254; + type Affine = G1Affine; + + type Base = Fq; + + type Scalar = Fr; + + fn b3() -> Self::Base { + PARAM_B3 + } +} diff --git a/bn254/src/lib.rs b/bn254/src/lib.rs index 57c65fa0..a4f713c2 100644 --- a/bn254/src/lib.rs +++ b/bn254/src/lib.rs @@ -19,6 +19,7 @@ #![allow(clippy::suspicious_op_assign_impl)] #![allow(clippy::op_ref)] +pub mod driver; mod ff_compat; mod fq; mod fqn; diff --git a/groth16/src/circuit.rs b/groth16/src/circuit.rs index 8ff6fcf3..c3512fb3 100644 --- a/groth16/src/circuit.rs +++ b/groth16/src/circuit.rs @@ -1,10 +1,10 @@ use crate::error::Error; +use bn_254::driver::Bn254Driver; -use grumpkin::driver::GrumpkinDriver; use zkstd::circuit::prelude::R1cs; use zkstd::common::Debug; /// circuit trait pub trait Circuit: Default + Debug { - fn synthesize(&self, constraint_system: &mut R1cs) -> Result<(), Error>; + fn synthesize(&self, constraint_system: &mut R1cs) -> Result<(), Error>; } diff --git a/groth16/src/lib.rs b/groth16/src/lib.rs index 4772891a..df7b9ad7 100644 --- a/groth16/src/lib.rs +++ b/groth16/src/lib.rs @@ -23,9 +23,9 @@ mod tests { use crate::circuit::Circuit; use crate::error::Error; use crate::zksnark::ZkSnark; + use bn_254::driver::Bn254Driver; use bn_254::Fr as BnScalar; - use grumpkin::driver::GrumpkinDriver; use zkstd::circuit::prelude::{FieldAssignment, R1cs}; use zkstd::common::OsRng; @@ -50,7 +50,7 @@ mod tests { } impl Circuit for DummyCircuit { - fn synthesize(&self, composer: &mut R1cs) -> Result<(), Error> { + fn synthesize(&self, composer: &mut R1cs) -> Result<(), Error> { let x = FieldAssignment::instance(composer, self.x); let o = FieldAssignment::instance(composer, self.o); let c = FieldAssignment::constant(&BnScalar::from(5)); diff --git a/grumpkin/src/driver.rs b/grumpkin/src/driver.rs index 5296eb13..67ac87b7 100644 --- a/grumpkin/src/driver.rs +++ b/grumpkin/src/driver.rs @@ -1,5 +1,6 @@ use crate::params::PARAM_B3; -use bn_254::{Fq, Fr, G1Affine}; +use crate::Affine; +use bn_254::{Fq, Fr}; use zkstd::circuit::CircuitDriver; #[derive(Clone, Debug, Default, PartialEq, Eq)] @@ -7,13 +8,13 @@ pub struct GrumpkinDriver; impl CircuitDriver for GrumpkinDriver { const NUM_BITS: u16 = 254; - type Affine = G1Affine; + type Affine = Affine; - type Base = Fq; + type Base = Fr; - type Scalar = Fr; + type Scalar = Fq; - fn b3() -> Self::Scalar { + fn b3() -> Self::Base { PARAM_B3 } } diff --git a/nova/src/circuit/nifs.rs b/nova/src/circuit/nifs.rs index 60b9dc05..77cffd7e 100644 --- a/nova/src/circuit/nifs.rs +++ b/nova/src/circuit/nifs.rs @@ -44,7 +44,7 @@ mod tests { use crate::hash::{MimcRO, MIMC_ROUNDS}; use crate::prover::tests::example_prover; use crate::RelaxedR1cs; - use bn_254::{Fq, Fr}; + use bn_254::Fq; use grumpkin::driver::GrumpkinDriver; use zkstd::r1cs::test::example_r1cs; @@ -57,11 +57,11 @@ mod tests { let r1cs_to_fold = RelaxedR1cs::new(example_r1cs(2)); let (instance, witness, commit_t) = prover.prove(&r1cs_to_fold, &running_r1cs); - let mut transcript = MimcRO::::default(); + let mut transcript = MimcRO::::default(); transcript.append_point(commit_t); running_r1cs.absorb_by_transcript(&mut transcript); let t = prover.compute_cross_term(&r1cs_to_fold, &running_r1cs); - let r = Fr::from(transcript.squeeze()); + let r = transcript.squeeze(); let mut cs = R1cs::::default(); let r = FieldAssignment::witness(&mut cs, r); @@ -73,7 +73,7 @@ mod tests { FieldAssignment::enforce_eq_constant( &mut cs, &FieldAssignment::from(&nifs_check), - &Fr::one(), + &Fq::one(), ); assert!(cs.is_sat()); } diff --git a/nova/src/circuit/transcript.rs b/nova/src/circuit/transcript.rs index b1377556..b4a411d7 100644 --- a/nova/src/circuit/transcript.rs +++ b/nova/src/circuit/transcript.rs @@ -53,22 +53,21 @@ mod tests { use super::MimcROCircuit; use crate::hash::{MimcRO, MIMC_ROUNDS}; - use bn_254::Fr; + use bn_254::Fq; use grumpkin::{driver::GrumpkinDriver, Affine}; use rand_core::OsRng; use zkstd::circuit::prelude::{FieldAssignment, PointAssignment, R1cs}; - use zkstd::common::{CurveGroup, Group}; + use zkstd::common::Group; #[test] fn mimc_circuit() { - let mut mimc = MimcRO::::default(); + let mut mimc = MimcRO::::default(); let mut mimc_circuit = MimcROCircuit::::default(); let mut cs: R1cs = R1cs::default(); let point = Affine::random(OsRng); - let scalar = Fr::random(OsRng); + let scalar = Fq::random(OsRng); - let point_assignment = - PointAssignment::instance(&mut cs, point.get_x(), point.get_y(), point.is_identity()); + let point_assignment = PointAssignment::instance(&mut cs, point); let scalar_assignment = FieldAssignment::instance(&mut cs, scalar); mimc.append(scalar); mimc.append_point(point); diff --git a/nova/src/gadget/relaxed_instance.rs b/nova/src/gadget/relaxed_instance.rs index aa953f7d..dd5ffcb8 100644 --- a/nova/src/gadget/relaxed_instance.rs +++ b/nova/src/gadget/relaxed_instance.rs @@ -94,8 +94,9 @@ impl RelaxedR1csInstanceAssignment { #[cfg(test)] mod tests { use super::*; - use bn_254::{Fr, G1Affine}; + use bn_254::Fq; use grumpkin::driver::GrumpkinDriver; + use grumpkin::Affine; use rand_core::OsRng; use zkstd::common::Group; use zkstd::matrix::DenseVectors; @@ -104,19 +105,19 @@ mod tests { fn instance_assignment_hash() { let mut cs: R1cs = R1cs::default(); let instance = RelaxedR1csInstance { - commit_e: G1Affine::random(OsRng), - u: Fr::random(OsRng), - commit_w: G1Affine::random(OsRng), - x: DenseVectors::new(vec![Fr::random(OsRng); 1]), + commit_e: Affine::random(OsRng), + u: Fq::random(OsRng), + commit_w: Affine::random(OsRng), + x: DenseVectors::new(vec![Fq::random(OsRng); 1]), }; let i = 3; - let z_0 = DenseVectors::new(vec![Fr::from(3)]); + let z_0 = DenseVectors::new(vec![Fq::from(3)]); let z_i = z_0.clone(); let hash = instance.hash(i, &z_0, &z_i); - let i_assignment = FieldAssignment::witness(&mut cs, Fr::from(i as u64)); + let i_assignment = FieldAssignment::witness(&mut cs, Fq::from(i as u64)); let z_0_assignment = z_0 .iter() .map(|x| FieldAssignment::witness(&mut cs, x)) @@ -138,10 +139,10 @@ mod tests { fn relaxed_instance_assignment() { let mut cs: R1cs = R1cs::default(); let instance = RelaxedR1csInstance { - commit_e: G1Affine::random(OsRng), - u: Fr::random(OsRng), - commit_w: G1Affine::random(OsRng), - x: DenseVectors::new(vec![Fr::random(OsRng); 1]), + commit_e: Affine::random(OsRng), + u: Fq::random(OsRng), + commit_w: Affine::random(OsRng), + x: DenseVectors::new(vec![Fq::random(OsRng); 1]), }; let instance_assignment = RelaxedR1csInstanceAssignment::witness(&mut cs, &instance); diff --git a/nova/src/hash.rs b/nova/src/hash.rs index d56d4c89..ab663171 100644 --- a/nova/src/hash.rs +++ b/nova/src/hash.rs @@ -1,7 +1,8 @@ mod helper; use helper::BlakeHelper; -use zkstd::common::{BNAffine, PrimeField}; +use zkstd::circuit::CircuitDriver; +use zkstd::common::{CurveGroup, IntGroup, PrimeField, Ring}; /// Amount of rounds calculated for the 254 bit field. /// Doubled due to the usage of Feistel mode with zero key. @@ -40,45 +41,45 @@ impl Mimc { } } -pub(crate) struct MimcRO { - hasher: Mimc, - state: Vec, - key: F, +pub(crate) struct MimcRO { + hasher: Mimc, + state: Vec, + key: C::Scalar, } -impl Default for MimcRO { +impl Default for MimcRO { fn default() -> Self { Self { hasher: Mimc::default(), state: Vec::default(), - key: F::zero(), + key: C::Scalar::zero(), } } } -impl MimcRO { - pub(crate) fn append(&mut self, absorb: F) { +impl MimcRO { + pub(crate) fn append(&mut self, absorb: C::Scalar) { self.state.push(absorb) } - pub(crate) fn append_point>(&mut self, point: A) { - self.append(point.get_x()); - self.append(point.get_y()); + pub(crate) fn append_point(&mut self, point: C::Affine) { + self.append(point.get_x().into()); + self.append(point.get_y().into()); self.append(if point.is_identity() { - A::Base::zero() + C::Scalar::zero() } else { - A::Base::one() + C::Scalar::one() }); } - pub(crate) fn hash_vec(&mut self, values: Vec) -> F { + pub(crate) fn hash_vec(&mut self, values: Vec) -> C::Scalar { for x in values { self.state.push(x); } self.squeeze() } - pub(crate) fn squeeze(&self) -> F { + pub(crate) fn squeeze(&self) -> C::Scalar { self.state.iter().fold(self.key, |acc, scalar| { let h = self.hasher.hash(*scalar, acc); acc + scalar + h diff --git a/nova/src/ivc.rs b/nova/src/ivc.rs index e2b5a3e7..017edd89 100644 --- a/nova/src/ivc.rs +++ b/nova/src/ivc.rs @@ -128,7 +128,7 @@ mod tests { use crate::test::ExampleFunction; use crate::RecursiveProof; - use bn_254::Fr; + use bn_254::Fq; use grumpkin::driver::GrumpkinDriver; use rand_core::OsRng; use zkstd::circuit::prelude::R1cs; @@ -138,7 +138,7 @@ mod tests { #[test] fn ivc_test() { let r1cs: R1cs = example_r1cs(1); - let z0 = DenseVectors::new(vec![Fr::from(3)]); + let z0 = DenseVectors::new(vec![Fq::from(3)]); let mut ivc = Ivc::>::new(OsRng, z0); let proof_0 = RecursiveProof { i: 0, diff --git a/nova/src/prover.rs b/nova/src/prover.rs index 49f9877d..dd18bda5 100644 --- a/nova/src/prover.rs +++ b/nova/src/prover.rs @@ -32,7 +32,7 @@ impl Prover { r1cs_1: &RelaxedR1cs, r1cs_2: &RelaxedR1cs, ) -> (RelaxedR1csInstance, RelaxedR1csWitness, C::Affine) { - let mut transcript = MimcRO::::default(); + let mut transcript = MimcRO::::default(); // compute cross term t let t = self.compute_cross_term(r1cs_1, r1cs_2); @@ -41,7 +41,7 @@ impl Prover { transcript.append_point(commit_t); r1cs_2.absorb_by_transcript(&mut transcript); - let r = transcript.squeeze().into(); + let r = transcript.squeeze(); // fold instance let instance = r1cs_2.fold_instance(r1cs_1, r, commit_t); @@ -95,7 +95,7 @@ impl Prover { #[cfg(test)] pub(crate) mod tests { use super::{Prover, RelaxedR1cs}; - use bn_254::{Fq, Fr}; + use bn_254::Fq; use crate::hash::{MimcRO, MIMC_ROUNDS}; use crate::Verifier; @@ -112,7 +112,7 @@ pub(crate) mod tests { fn nifs_folding_test() { let prover = example_prover(); - let mut transcript = MimcRO::::default(); + let mut transcript = MimcRO::::default(); let r1cs_1 = example_r1cs(4); let r1cs_2 = example_r1cs(3); @@ -126,10 +126,10 @@ pub(crate) mod tests { transcript.append_point(commit_t); relaxed_r1cs_2.absorb_by_transcript(&mut transcript); let t = prover.compute_cross_term(&relaxed_r1cs_1, &relaxed_r1cs_2); - let r = Fr::from(transcript.squeeze()); + let r = transcript.squeeze(); // naive check that the folded witness satisfies the relaxed r1cs - let z3: Vec = [ + let z3: Vec = [ vec![verified_instance.u], verified_instance.x.get(), witness.w.get(), @@ -137,7 +137,7 @@ pub(crate) mod tests { .concat(); let z1 = [ - vec![Fr::one()], + vec![Fq::one()], relaxed_r1cs_1.x().get(), relaxed_r1cs_1.w().get(), ] @@ -149,7 +149,7 @@ pub(crate) mod tests { ] .concat(); - let z3_aux: Vec = z2 + let z3_aux: Vec = z2 .iter() .map(|x| x * r) .zip(z1) diff --git a/nova/src/relaxed_r1cs.rs b/nova/src/relaxed_r1cs.rs index 8aaf2960..5d521180 100644 --- a/nova/src/relaxed_r1cs.rs +++ b/nova/src/relaxed_r1cs.rs @@ -139,7 +139,7 @@ impl RelaxedR1cs { pub(crate) fn absorb_by_transcript( &self, - transcript: &mut MimcRO, + transcript: &mut MimcRO, ) { self.instance.absorb_by_transcript(transcript); } diff --git a/nova/src/relaxed_r1cs/instance.rs b/nova/src/relaxed_r1cs/instance.rs index 465e64bf..e7536293 100644 --- a/nova/src/relaxed_r1cs/instance.rs +++ b/nova/src/relaxed_r1cs/instance.rs @@ -60,13 +60,13 @@ impl RelaxedR1csInstance { pub(crate) fn absorb_by_transcript( &self, - transcript: &mut MimcRO, + transcript: &mut MimcRO, ) { transcript.append_point(self.commit_w); transcript.append_point(self.commit_e); - transcript.append(self.u.into()); + transcript.append(self.u); for x in &self.x.get() { - transcript.append(C::Base::from(*x)); + transcript.append(*x); } } @@ -78,7 +78,7 @@ impl RelaxedR1csInstance { ) -> C::Scalar { let commit_e = self.commit_e.to_extended(); let commit_w = self.commit_w.to_extended(); - MimcRO::::default().hash_vec( + MimcRO::::default().hash_vec( vec![ vec![C::Scalar::from(i as u64)], z_0.get(), diff --git a/nova/src/verifier.rs b/nova/src/verifier.rs index 6c6aa17d..5f94d0d3 100644 --- a/nova/src/verifier.rs +++ b/nova/src/verifier.rs @@ -14,12 +14,12 @@ impl Verifier { r1cs_1: &RelaxedR1cs, r1cs_2: &RelaxedR1cs, ) -> RelaxedR1csInstance { - let mut transcript = MimcRO::::default(); + let mut transcript = MimcRO::::default(); transcript.append_point(commit_t); r1cs_2.absorb_by_transcript(&mut transcript); - let r = transcript.squeeze().into(); + let r = transcript.squeeze(); r1cs_2.fold_instance(r1cs_1, r, commit_t) } diff --git a/zkstd/src/circuit.rs b/zkstd/src/circuit.rs index 766b9845..9964fe71 100644 --- a/zkstd/src/circuit.rs +++ b/zkstd/src/circuit.rs @@ -15,5 +15,5 @@ pub trait CircuitDriver: Clone + Debug + Default { // curve scalar field type Scalar: PrimeField + From + Serialize + for<'de> Deserialize<'de>; // bn curve 3b param - fn b3() -> Self::Scalar; + fn b3() -> Self::Base; } diff --git a/zkstd/src/circuit/gadget/curve.rs b/zkstd/src/circuit/gadget/curve.rs index cead748e..b2fcd82d 100644 --- a/zkstd/src/circuit/gadget/curve.rs +++ b/zkstd/src/circuit/gadget/curve.rs @@ -2,7 +2,7 @@ use super::binary::BinaryAssignment; use super::field::FieldAssignment; use crate::circuit::CircuitDriver; -use crate::common::{BNProjective, CurveGroup, Group, IntGroup, Ring}; +use crate::common::{BNAffine, BNProjective, CurveGroup, Group, IntGroup, Ring}; use crate::r1cs::R1cs; #[derive(Clone)] @@ -13,12 +13,12 @@ pub struct PointAssignment { } impl PointAssignment { - pub fn instance(cs: &mut R1cs, x: C::Scalar, y: C::Scalar, is_infinity: bool) -> Self { - let x = FieldAssignment::instance(cs, x); - let y = FieldAssignment::instance(cs, y); + pub fn instance(cs: &mut R1cs, point: C::Affine) -> Self { + let x = FieldAssignment::instance(cs, point.get_x().into()); + let y = FieldAssignment::instance(cs, point.get_y().into()); let z = FieldAssignment::instance( cs, - if is_infinity { + if point.is_identity() { C::Scalar::zero() } else { C::Scalar::one() @@ -46,11 +46,11 @@ impl PointAssignment { pub fn assert_equal_public_point( &self, cs: &mut R1cs, - point: impl BNProjective, + point: ::Extended, ) { - let point_x = FieldAssignment::constant(&point.get_x()); - let point_y = FieldAssignment::constant(&point.get_y()); - let point_z = FieldAssignment::constant(&point.get_z()); + let point_x = FieldAssignment::constant(&point.get_x().into()); + let point_y = FieldAssignment::constant(&point.get_y().into()); + let point_z = FieldAssignment::constant(&point.get_z().into()); let xz1 = FieldAssignment::mul(cs, &self.x, &point_z); let xz2 = FieldAssignment::mul(cs, &point_x, &self.z); @@ -64,7 +64,7 @@ impl PointAssignment { } pub fn add(&self, rhs: &Self, cs: &mut R1cs) -> Self { - let b3 = FieldAssignment::::constant(&C::b3()); + let b3 = FieldAssignment::::constant(&C::b3().into()); let t0 = FieldAssignment::mul(cs, &self.x, &rhs.x); let t1 = FieldAssignment::mul(cs, &self.y, &rhs.y); let t2 = FieldAssignment::mul(cs, &self.z, &rhs.z); @@ -107,7 +107,7 @@ impl PointAssignment { } pub fn double(&self, cs: &mut R1cs) -> Self { - let b3 = FieldAssignment::::constant(&C::b3()); + let b3 = FieldAssignment::::constant(&C::b3().into()); let t0 = FieldAssignment::mul(cs, &self.y, &self.y); let z3 = &t0 + &t0; let z3 = &z3 + &z3; @@ -137,8 +137,7 @@ impl PointAssignment { /// coordinate scalar pub fn scalar_point(&self, cs: &mut R1cs, scalar: &FieldAssignment) -> Self { let i = C::Affine::ADDITIVE_IDENTITY; - let mut res = - PointAssignment::instance(cs, i.get_x().into(), i.get_y().into(), i.is_identity()); + let mut res = PointAssignment::instance(cs, i); for bit in FieldAssignment::to_bits(cs, scalar).iter() { res = res.double(cs); let point_to_add = self.select_identity(cs, bit); diff --git a/zkstd/src/lib.rs b/zkstd/src/lib.rs index 44cf5099..de96cfac 100644 --- a/zkstd/src/lib.rs +++ b/zkstd/src/lib.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#![no_std] +// #![no_std] #![doc = include_str!("../README.md")] pub mod arithmetic; diff --git a/zkstd/tests/gadget.rs b/zkstd/tests/gadget.rs index 5f6399e4..8bc7e572 100644 --- a/zkstd/tests/gadget.rs +++ b/zkstd/tests/gadget.rs @@ -2,11 +2,11 @@ mod grumpkin; #[cfg(test)] mod grumpkin_gadget_tests { - use crate::grumpkin::{Affine, Fq as Base, Fr as Scalar, GrumpkinDriver}; + use crate::grumpkin::{Affine, Fq as Scalar, Fr as Base, GrumpkinDriver}; use rand_core::OsRng; use zkstd::circuit::prelude::{FieldAssignment, PointAssignment, R1cs}; - use zkstd::common::{BNAffine, BNProjective, CurveGroup, Group, PrimeField}; + use zkstd::common::{BNAffine, BNProjective, Group, PrimeField}; #[test] fn range_proof_test() { @@ -122,13 +122,7 @@ mod grumpkin_gadget_tests { let mut cs: R1cs = R1cs::default(); let point = Affine::random(OsRng); - let circuit_double = PointAssignment::instance( - &mut cs, - point.get_x(), - point.get_y(), - point.is_identity(), - ) - .double(&mut cs); + let circuit_double = PointAssignment::instance(&mut cs, point).double(&mut cs); let expected = point.to_extended().double(); @@ -146,10 +140,8 @@ mod grumpkin_gadget_tests { let a = Affine::random(OsRng); let b = Affine::ADDITIVE_IDENTITY; - let a_assignment = - PointAssignment::instance(&mut cs, a.get_x(), a.get_y(), a.is_identity()); - let b_assignment = - PointAssignment::instance(&mut cs, b.get_x(), b.get_y(), b.is_identity()); + let a_assignment = PointAssignment::instance(&mut cs, a); + let b_assignment = PointAssignment::instance(&mut cs, b); let expected = a + b; @@ -165,10 +157,8 @@ mod grumpkin_gadget_tests { let a = Affine::random(OsRng); let b = Affine::random(OsRng); - let a_assignment = - PointAssignment::instance(&mut cs, a.get_x(), a.get_y(), a.is_identity()); - let b_assignment = - PointAssignment::instance(&mut cs, b.get_x(), b.get_y(), b.is_identity()); + let a_assignment = PointAssignment::instance(&mut cs, a); + let b_assignment = PointAssignment::instance(&mut cs, b); let expected = a.to_extended() + b.to_extended(); @@ -188,9 +178,8 @@ mod grumpkin_gadget_tests { let p = Affine::random(OsRng); let x_assignment = FieldAssignment::instance(&mut cs, x); // Fr - let p_assignment = - PointAssignment::instance(&mut cs, p.get_x(), p.get_y(), p.is_identity()); - let expected = p * Base::from(x); + let p_assignment = PointAssignment::instance(&mut cs, p); + let expected = p * x; assert_eq!(x.to_bits(), Base::from(x).to_bits()); diff --git a/zkstd/tests/grumpkin.rs b/zkstd/tests/grumpkin.rs index 9ad7c038..3c059049 100644 --- a/zkstd/tests/grumpkin.rs +++ b/zkstd/tests/grumpkin.rs @@ -429,13 +429,13 @@ pub struct GrumpkinDriver; impl CircuitDriver for GrumpkinDriver { const NUM_BITS: u16 = 254; - type Affine = G1Affine; + type Affine = Affine; - type Base = Fq; + type Base = Fr; - type Scalar = Fr; + type Scalar = Fq; - fn b3() -> Self::Scalar { + fn b3() -> Self::Base { FR_PARAM_B3 } } From a9b869f6231af8b05e4ec4ffdbb0ad487f972d95 Mon Sep 17 00:00:00 2001 From: Kirill Karbushev Date: Fri, 8 Dec 2023 17:41:37 +0900 Subject: [PATCH 02/17] bn254 and grumpkin driver refactoring --- bn254/src/driver.rs | 20 --- bn254/src/lib.rs | 1 - groth16/src/circuit.rs | 20 ++- grumpkin/src/driver.rs | 20 --- grumpkin/src/lib.rs | 1 - nova/src/circuit/augmented.rs | 2 +- nova/src/circuit/nifs.rs | 4 +- nova/src/circuit/transcript.rs | 12 +- nova/src/driver.rs | 229 ++++++++++++++++++++++++++++ nova/src/gadget/relaxed_instance.rs | 2 +- nova/src/hash.rs | 24 +-- nova/src/ivc.rs | 2 +- nova/src/lib.rs | 1 + nova/src/prover.rs | 5 +- nova/src/relaxed_r1cs.rs | 2 +- zkstd/src/circuit.rs | 2 +- zkstd/src/circuit/gadget/curve.rs | 27 ++-- zkstd/tests/gadget.rs | 192 ----------------------- 18 files changed, 294 insertions(+), 272 deletions(-) delete mode 100644 bn254/src/driver.rs delete mode 100644 grumpkin/src/driver.rs create mode 100644 nova/src/driver.rs diff --git a/bn254/src/driver.rs b/bn254/src/driver.rs deleted file mode 100644 index f5d766d5..00000000 --- a/bn254/src/driver.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::params::PARAM_B3; -use crate::G1Affine; -use crate::{Fq, Fr}; -use zkstd::circuit::CircuitDriver; - -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct Bn254Driver; - -impl CircuitDriver for Bn254Driver { - const NUM_BITS: u16 = 254; - type Affine = G1Affine; - - type Base = Fq; - - type Scalar = Fr; - - fn b3() -> Self::Base { - PARAM_B3 - } -} diff --git a/bn254/src/lib.rs b/bn254/src/lib.rs index a4f713c2..57c65fa0 100644 --- a/bn254/src/lib.rs +++ b/bn254/src/lib.rs @@ -19,7 +19,6 @@ #![allow(clippy::suspicious_op_assign_impl)] #![allow(clippy::op_ref)] -pub mod driver; mod ff_compat; mod fq; mod fqn; diff --git a/groth16/src/circuit.rs b/groth16/src/circuit.rs index c3512fb3..a6f1e12b 100644 --- a/groth16/src/circuit.rs +++ b/groth16/src/circuit.rs @@ -1,5 +1,7 @@ use crate::error::Error; -use bn_254::driver::Bn254Driver; +use bn_254::{Fq, Fr, G1Affine}; +use grumpkin::params::PARAM_B3 as GRUMPKIN_PARAM_B3; +use zkstd::circuit::CircuitDriver; use zkstd::circuit::prelude::R1cs; use zkstd::common::Debug; @@ -8,3 +10,19 @@ use zkstd::common::Debug; pub trait Circuit: Default + Debug { fn synthesize(&self, constraint_system: &mut R1cs) -> Result<(), Error>; } + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct Bn254Driver; + +impl CircuitDriver for Bn254Driver { + const NUM_BITS: u16 = 254; + type Affine = G1Affine; + + type Base = Fq; + + type Scalar = Fr; + + fn b3() -> Self::Scalar { + GRUMPKIN_PARAM_B3 + } +} diff --git a/grumpkin/src/driver.rs b/grumpkin/src/driver.rs deleted file mode 100644 index 67ac87b7..00000000 --- a/grumpkin/src/driver.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::params::PARAM_B3; -use crate::Affine; -use bn_254::{Fq, Fr}; -use zkstd::circuit::CircuitDriver; - -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct GrumpkinDriver; - -impl CircuitDriver for GrumpkinDriver { - const NUM_BITS: u16 = 254; - type Affine = Affine; - - type Base = Fr; - - type Scalar = Fq; - - fn b3() -> Self::Base { - PARAM_B3 - } -} diff --git a/grumpkin/src/lib.rs b/grumpkin/src/lib.rs index 594fc6db..9a400c33 100644 --- a/grumpkin/src/lib.rs +++ b/grumpkin/src/lib.rs @@ -18,7 +18,6 @@ #![allow(clippy::suspicious_arithmetic_impl)] mod curve; -pub mod driver; pub mod params; pub use curve::{Affine, Projective}; diff --git a/nova/src/circuit/augmented.rs b/nova/src/circuit/augmented.rs index 9b47baf9..ef25e9d0 100644 --- a/nova/src/circuit/augmented.rs +++ b/nova/src/circuit/augmented.rs @@ -151,10 +151,10 @@ impl> AugmentedFCircuit { #[cfg(test)] mod tests { use super::*; + use crate::driver::GrumpkinDriver; use crate::relaxed_r1cs::RelaxedR1csWitness; use crate::test::ExampleFunction; use crate::RelaxedR1cs; - use grumpkin::driver::GrumpkinDriver; #[test] fn augmented_circuit_dummies() { diff --git a/nova/src/circuit/nifs.rs b/nova/src/circuit/nifs.rs index 77cffd7e..d186f0b3 100644 --- a/nova/src/circuit/nifs.rs +++ b/nova/src/circuit/nifs.rs @@ -41,11 +41,11 @@ impl NifsCircuit { #[cfg(test)] mod tests { use super::*; + use crate::driver::{Bn254Driver, GrumpkinDriver}; use crate::hash::{MimcRO, MIMC_ROUNDS}; use crate::prover::tests::example_prover; use crate::RelaxedR1cs; use bn_254::Fq; - use grumpkin::driver::GrumpkinDriver; use zkstd::r1cs::test::example_r1cs; #[test] @@ -64,7 +64,7 @@ mod tests { let r = transcript.squeeze(); let mut cs = R1cs::::default(); - let r = FieldAssignment::witness(&mut cs, r); + let r = FieldAssignment::witness(&mut cs, r.into()); let instance1 = RelaxedR1csInstanceAssignment::witness(&mut cs, &r1cs_to_fold.instance); let instance2 = RelaxedR1csInstanceAssignment::witness(&mut cs, &running_r1cs.instance); let instance3 = RelaxedR1csInstanceAssignment::witness(&mut cs, &instance); diff --git a/nova/src/circuit/transcript.rs b/nova/src/circuit/transcript.rs index b4a411d7..85f341c1 100644 --- a/nova/src/circuit/transcript.rs +++ b/nova/src/circuit/transcript.rs @@ -53,8 +53,8 @@ mod tests { use super::MimcROCircuit; use crate::hash::{MimcRO, MIMC_ROUNDS}; - use bn_254::Fq; - use grumpkin::{driver::GrumpkinDriver, Affine}; + use crate::driver::{Bn254Driver, GrumpkinDriver}; + use bn_254::{Fq, Fr, G1Affine}; use rand_core::OsRng; use zkstd::circuit::prelude::{FieldAssignment, PointAssignment, R1cs}; use zkstd::common::Group; @@ -62,9 +62,9 @@ mod tests { #[test] fn mimc_circuit() { let mut mimc = MimcRO::::default(); - let mut mimc_circuit = MimcROCircuit::::default(); - let mut cs: R1cs = R1cs::default(); - let point = Affine::random(OsRng); + let mut mimc_circuit = MimcROCircuit::::default(); // Base = Fq + let mut cs: R1cs = R1cs::default(); // Base = Fr, Scalar = Fq + let point = G1Affine::random(OsRng); let scalar = Fq::random(OsRng); let point_assignment = PointAssignment::instance(&mut cs, point); @@ -74,7 +74,7 @@ mod tests { mimc_circuit.append(scalar_assignment); mimc_circuit.append_point(point_assignment); - let expected = mimc.squeeze(); + let expected = mimc.squeeze().into(); let circuit_result = mimc_circuit.squeeze(&mut cs); FieldAssignment::enforce_eq_constant(&mut cs, &circuit_result, &expected); assert!(cs.is_sat()); diff --git a/nova/src/driver.rs b/nova/src/driver.rs new file mode 100644 index 00000000..fa5a9701 --- /dev/null +++ b/nova/src/driver.rs @@ -0,0 +1,229 @@ +use bn_254::{params::PARAM_B3 as BN254_PARAM_B3, Fq, Fr, G1Affine}; +use grumpkin::{params::PARAM_B3 as GRUMPKIN_PARAM_B3, Affine}; +use zkstd::circuit::CircuitDriver; + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct GrumpkinDriver; + +impl CircuitDriver for GrumpkinDriver { + const NUM_BITS: u16 = 254; + type Affine = Affine; + + type Base = Fr; + + type Scalar = Fq; + + fn b3() -> Self::Scalar { + BN254_PARAM_B3 + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct Bn254Driver; + +impl CircuitDriver for Bn254Driver { + const NUM_BITS: u16 = 254; + type Affine = G1Affine; + + type Base = Fq; + + type Scalar = Fr; + + fn b3() -> Self::Scalar { + GRUMPKIN_PARAM_B3 + } +} + +#[cfg(test)] +mod grumpkin_gadget_tests { + use super::{Fq as Scalar, Fr as Base, GrumpkinDriver}; + + use bn_254::G1Affine; + use rand_core::OsRng; + use zkstd::circuit::prelude::{FieldAssignment, PointAssignment, R1cs}; + use zkstd::common::{BNAffine, BNProjective, Group, PrimeField}; + + #[test] + fn range_proof_test() { + for _ in 0..100 { + let mut cs: R1cs = R1cs::default(); + let mut ncs = cs.clone(); + let bound = Scalar::from(10); + + let x_ass = FieldAssignment::instance(&mut cs, bound); + let x_bits = FieldAssignment::to_bits(&mut cs, &x_ass); + FieldAssignment::range_check(&mut cs, &x_bits, bound); + assert!(cs.is_sat()); + + let x_ass = FieldAssignment::instance(&mut ncs, bound + Scalar::one()); + let x_bits = FieldAssignment::to_bits(&mut ncs, &x_ass); + FieldAssignment::range_check(&mut ncs, &x_bits, bound); + assert!(!ncs.is_sat()); + } + } + + #[test] + fn field_add_test() { + let mut cs: R1cs = R1cs::default(); + let mut ncs = cs.clone(); + let a = Scalar::random(OsRng); + let b = Scalar::random(OsRng); + let mut c = a + b; + + // a + b == c + let x = FieldAssignment::instance(&mut cs, a); + let y = FieldAssignment::witness(&mut cs, b); + let z = FieldAssignment::instance(&mut cs, c); + let sum = &x + &y; + FieldAssignment::enforce_eq(&mut cs, &z, &sum); + + assert!(cs.is_sat()); + + // a + b != c + c += Scalar::one(); + let x = FieldAssignment::instance(&mut ncs, a); + let y = FieldAssignment::witness(&mut ncs, b); + let z = FieldAssignment::instance(&mut ncs, c); + let sum = &x + &y; + FieldAssignment::enforce_eq(&mut ncs, &z, &sum); + + assert!(!ncs.is_sat()) + } + + #[test] + fn field_mul_test() { + let mut cs: R1cs = R1cs::default(); + let mut ncs = cs.clone(); + let a = Scalar::random(OsRng); + let b = Scalar::random(OsRng); + let mut c = a * b; + + // a * b == c + let x = FieldAssignment::instance(&mut cs, a); + let y = FieldAssignment::witness(&mut cs, b); + let z = FieldAssignment::instance(&mut cs, c); + let product = FieldAssignment::mul(&mut cs, &x, &y); + FieldAssignment::enforce_eq(&mut cs, &z, &product); + + assert!(cs.is_sat()); + + // a * b != c + c += Scalar::one(); + let x = FieldAssignment::instance(&mut ncs, a); + let y = FieldAssignment::witness(&mut ncs, b); + let z = FieldAssignment::instance(&mut ncs, c); + let product = FieldAssignment::mul(&mut ncs, &x, &y); + FieldAssignment::enforce_eq(&mut ncs, &z, &product); + + assert!(!ncs.is_sat()) + } + + #[test] + fn field_ops_test() { + let mut cs: R1cs = R1cs::default(); + let mut ncs = cs.clone(); + let input = Scalar::from(3); + let c = Scalar::from(5); + let out = Scalar::from(35); + + // x^3 + x + 5 == 35 + let x = FieldAssignment::witness(&mut cs, input); + let c = FieldAssignment::constant(&c); + let z = FieldAssignment::instance(&mut cs, out); + let sym_1 = FieldAssignment::mul(&mut cs, &x, &x); + let y = FieldAssignment::mul(&mut cs, &sym_1, &x); + let sym_2 = &y + &x; + FieldAssignment::enforce_eq(&mut cs, &z, &(&sym_2 + &c)); + + assert!(cs.is_sat()); + + // x^3 + x + 5 != 36 + let c = Scalar::from(5); + let out = Scalar::from(36); + let x = FieldAssignment::witness(&mut ncs, input); + let c = FieldAssignment::constant(&c); + let z = FieldAssignment::instance(&mut ncs, out); + let sym_1 = FieldAssignment::mul(&mut ncs, &x, &x); + let y = FieldAssignment::mul(&mut ncs, &sym_1, &x); + let sym_2 = &y + &x; + FieldAssignment::enforce_eq(&mut ncs, &z, &(&sym_2 + &c)); + + assert!(!ncs.is_sat()); + } + + #[test] + fn curve_double_test() { + // Affine + for _ in 0..100 { + let mut cs: R1cs = R1cs::default(); + let point = G1Affine::random(OsRng); + + let circuit_double = PointAssignment::instance(&mut cs, point).double(&mut cs); + + let expected = point.to_extended().double(); + + circuit_double.assert_equal_public_point(&mut cs, expected); + + assert!(cs.is_sat()); + } + } + + #[test] + fn curve_add_test() { + // Identity addition test + { + let mut cs: R1cs = R1cs::default(); + let a = G1Affine::random(OsRng); + let b = G1Affine::ADDITIVE_IDENTITY; + + let a_assignment = PointAssignment::instance(&mut cs, a); + let b_assignment = PointAssignment::instance(&mut cs, b); + + let expected = a + b; + + let sum_circuit = a_assignment.add(&b_assignment, &mut cs); + + sum_circuit.assert_equal_public_point(&mut cs, expected); + + assert!(cs.is_sat()); + } + + for _ in 0..100 { + let mut cs: R1cs = R1cs::default(); + let a = G1Affine::random(OsRng); + let b = G1Affine::random(OsRng); + + let a_assignment = PointAssignment::instance(&mut cs, a); + let b_assignment = PointAssignment::instance(&mut cs, b); + + let expected = a.to_extended() + b.to_extended(); + + let sum_circuit = a_assignment.add(&b_assignment, &mut cs); + + sum_circuit.assert_equal_public_point(&mut cs, expected); + + assert!(cs.is_sat()); + } + } + + #[test] + fn curve_scalar_mul_test() { + for _ in 0..100 { + let mut cs: R1cs = R1cs::default(); + let x = Scalar::random(OsRng); + let p = G1Affine::random(OsRng); + + let x_assignment = FieldAssignment::instance(&mut cs, x); // Fr + let p_assignment = PointAssignment::instance(&mut cs, p); + let expected = p * x; + + assert_eq!(x.to_bits(), Base::from(x).to_bits()); + + let mul_circuit = p_assignment.scalar_point(&mut cs, &x_assignment); + + mul_circuit.assert_equal_public_point(&mut cs, expected); + + assert!(cs.is_sat()); + } + } +} diff --git a/nova/src/gadget/relaxed_instance.rs b/nova/src/gadget/relaxed_instance.rs index dd5ffcb8..18930e6b 100644 --- a/nova/src/gadget/relaxed_instance.rs +++ b/nova/src/gadget/relaxed_instance.rs @@ -94,8 +94,8 @@ impl RelaxedR1csInstanceAssignment { #[cfg(test)] mod tests { use super::*; + use crate::driver::GrumpkinDriver; use bn_254::Fq; - use grumpkin::driver::GrumpkinDriver; use grumpkin::Affine; use rand_core::OsRng; use zkstd::common::Group; diff --git a/nova/src/hash.rs b/nova/src/hash.rs index ab663171..73256304 100644 --- a/nova/src/hash.rs +++ b/nova/src/hash.rs @@ -2,7 +2,7 @@ mod helper; use helper::BlakeHelper; use zkstd::circuit::CircuitDriver; -use zkstd::common::{CurveGroup, IntGroup, PrimeField, Ring}; +use zkstd::common::{BNAffine, CurveGroup, IntGroup, PrimeField, Ring}; /// Amount of rounds calculated for the 254 bit field. /// Doubled due to the usage of Feistel mode with zero key. @@ -42,9 +42,9 @@ impl Mimc { } pub(crate) struct MimcRO { - hasher: Mimc, - state: Vec, - key: C::Scalar, + hasher: Mimc, + state: Vec, + key: C::Base, } impl Default for MimcRO { @@ -52,34 +52,34 @@ impl Default for MimcRO { Self { hasher: Mimc::default(), state: Vec::default(), - key: C::Scalar::zero(), + key: C::Base::zero(), } } } impl MimcRO { - pub(crate) fn append(&mut self, absorb: C::Scalar) { + pub(crate) fn append(&mut self, absorb: C::Base) { self.state.push(absorb) } pub(crate) fn append_point(&mut self, point: C::Affine) { - self.append(point.get_x().into()); - self.append(point.get_y().into()); + self.append(point.get_x()); + self.append(point.get_y()); self.append(if point.is_identity() { - C::Scalar::zero() + C::Base::zero() } else { - C::Scalar::one() + C::Base::one() }); } - pub(crate) fn hash_vec(&mut self, values: Vec) -> C::Scalar { + pub(crate) fn hash_vec(&mut self, values: Vec) -> C::Base { for x in values { self.state.push(x); } self.squeeze() } - pub(crate) fn squeeze(&self) -> C::Scalar { + pub(crate) fn squeeze(&self) -> C::Base { self.state.iter().fold(self.key, |acc, scalar| { let h = self.hasher.hash(*scalar, acc); acc + scalar + h diff --git a/nova/src/ivc.rs b/nova/src/ivc.rs index 017edd89..28e01724 100644 --- a/nova/src/ivc.rs +++ b/nova/src/ivc.rs @@ -127,9 +127,9 @@ mod tests { use super::Ivc; use crate::test::ExampleFunction; + use crate::driver::GrumpkinDriver; use crate::RecursiveProof; use bn_254::Fq; - use grumpkin::driver::GrumpkinDriver; use rand_core::OsRng; use zkstd::circuit::prelude::R1cs; use zkstd::matrix::DenseVectors; diff --git a/nova/src/lib.rs b/nova/src/lib.rs index 8513174f..41f93e78 100644 --- a/nova/src/lib.rs +++ b/nova/src/lib.rs @@ -12,6 +12,7 @@ mod prover; mod relaxed_r1cs; mod verifier; +mod driver; #[cfg(test)] mod test; diff --git a/nova/src/prover.rs b/nova/src/prover.rs index dd18bda5..a53c6c8c 100644 --- a/nova/src/prover.rs +++ b/nova/src/prover.rs @@ -96,14 +96,15 @@ impl Prover { pub(crate) mod tests { use super::{Prover, RelaxedR1cs}; use bn_254::Fq; + use zkstd::circuit::CircuitDriver; + use crate::driver::GrumpkinDriver; use crate::hash::{MimcRO, MIMC_ROUNDS}; use crate::Verifier; - use grumpkin::driver::GrumpkinDriver; use zkstd::common::OsRng; use zkstd::r1cs::test::example_r1cs; - pub(crate) fn example_prover() -> Prover { + pub(crate) fn example_prover() -> Prover { let r1cs = example_r1cs(0); Prover::new(r1cs, OsRng) } diff --git a/nova/src/relaxed_r1cs.rs b/nova/src/relaxed_r1cs.rs index 5d521180..803144de 100644 --- a/nova/src/relaxed_r1cs.rs +++ b/nova/src/relaxed_r1cs.rs @@ -149,7 +149,7 @@ impl RelaxedR1cs { mod tests { use super::RelaxedR1cs; - use grumpkin::driver::GrumpkinDriver; + use crate::driver::GrumpkinDriver; use zkstd::circuit::prelude::R1cs; use zkstd::r1cs::test::example_r1cs; diff --git a/zkstd/src/circuit.rs b/zkstd/src/circuit.rs index 9964fe71..766b9845 100644 --- a/zkstd/src/circuit.rs +++ b/zkstd/src/circuit.rs @@ -15,5 +15,5 @@ pub trait CircuitDriver: Clone + Debug + Default { // curve scalar field type Scalar: PrimeField + From + Serialize + for<'de> Deserialize<'de>; // bn curve 3b param - fn b3() -> Self::Base; + fn b3() -> Self::Scalar; } diff --git a/zkstd/src/circuit/gadget/curve.rs b/zkstd/src/circuit/gadget/curve.rs index b2fcd82d..0e994ffb 100644 --- a/zkstd/src/circuit/gadget/curve.rs +++ b/zkstd/src/circuit/gadget/curve.rs @@ -13,9 +13,9 @@ pub struct PointAssignment { } impl PointAssignment { - pub fn instance(cs: &mut R1cs, point: C::Affine) -> Self { - let x = FieldAssignment::instance(cs, point.get_x().into()); - let y = FieldAssignment::instance(cs, point.get_y().into()); + pub fn instance(cs: &mut R1cs, point: impl BNAffine) -> Self { + let x = FieldAssignment::instance(cs, point.get_x()); + let y = FieldAssignment::instance(cs, point.get_y()); let z = FieldAssignment::instance( cs, if point.is_identity() { @@ -28,6 +28,14 @@ impl PointAssignment { Self { x, y, z } } + pub fn identity() -> Self { + let x = FieldAssignment::constant(&C::Scalar::zero()); + let y = FieldAssignment::constant(&C::Scalar::one()); + let z = FieldAssignment::constant(&C::Scalar::one()); + + Self { x, y, z } + } + pub fn witness(cs: &mut R1cs, x: C::Scalar, y: C::Scalar, is_infinity: bool) -> Self { let x = FieldAssignment::witness(cs, x); let y = FieldAssignment::witness(cs, y); @@ -46,11 +54,11 @@ impl PointAssignment { pub fn assert_equal_public_point( &self, cs: &mut R1cs, - point: ::Extended, + point: impl BNProjective, ) { - let point_x = FieldAssignment::constant(&point.get_x().into()); - let point_y = FieldAssignment::constant(&point.get_y().into()); - let point_z = FieldAssignment::constant(&point.get_z().into()); + let point_x = FieldAssignment::constant(&point.get_x()); + let point_y = FieldAssignment::constant(&point.get_y()); + let point_z = FieldAssignment::constant(&point.get_z()); let xz1 = FieldAssignment::mul(cs, &self.x, &point_z); let xz2 = FieldAssignment::mul(cs, &point_x, &self.z); @@ -64,7 +72,7 @@ impl PointAssignment { } pub fn add(&self, rhs: &Self, cs: &mut R1cs) -> Self { - let b3 = FieldAssignment::::constant(&C::b3().into()); + let b3 = FieldAssignment::::constant(&C::b3()); let t0 = FieldAssignment::mul(cs, &self.x, &rhs.x); let t1 = FieldAssignment::mul(cs, &self.y, &rhs.y); let t2 = FieldAssignment::mul(cs, &self.z, &rhs.z); @@ -136,8 +144,7 @@ impl PointAssignment { /// coordinate scalar pub fn scalar_point(&self, cs: &mut R1cs, scalar: &FieldAssignment) -> Self { - let i = C::Affine::ADDITIVE_IDENTITY; - let mut res = PointAssignment::instance(cs, i); + let mut res = PointAssignment::identity(); for bit in FieldAssignment::to_bits(cs, scalar).iter() { res = res.double(cs); let point_to_add = self.select_identity(cs, bit); diff --git a/zkstd/tests/gadget.rs b/zkstd/tests/gadget.rs index 8bc7e572..18b1875c 100644 --- a/zkstd/tests/gadget.rs +++ b/zkstd/tests/gadget.rs @@ -1,193 +1 @@ mod grumpkin; - -#[cfg(test)] -mod grumpkin_gadget_tests { - use crate::grumpkin::{Affine, Fq as Scalar, Fr as Base, GrumpkinDriver}; - - use rand_core::OsRng; - use zkstd::circuit::prelude::{FieldAssignment, PointAssignment, R1cs}; - use zkstd::common::{BNAffine, BNProjective, Group, PrimeField}; - - #[test] - fn range_proof_test() { - for _ in 0..100 { - let mut cs: R1cs = R1cs::default(); - let mut ncs = cs.clone(); - let bound = Scalar::from(10); - - let x_ass = FieldAssignment::instance(&mut cs, bound); - let x_bits = FieldAssignment::to_bits(&mut cs, &x_ass); - FieldAssignment::range_check(&mut cs, &x_bits, bound); - assert!(cs.is_sat()); - - let x_ass = FieldAssignment::instance(&mut ncs, bound + Scalar::one()); - let x_bits = FieldAssignment::to_bits(&mut ncs, &x_ass); - FieldAssignment::range_check(&mut ncs, &x_bits, bound); - assert!(!ncs.is_sat()); - } - } - - #[test] - fn field_add_test() { - let mut cs: R1cs = R1cs::default(); - let mut ncs = cs.clone(); - let a = Scalar::random(OsRng); - let b = Scalar::random(OsRng); - let mut c = a + b; - - // a + b == c - let x = FieldAssignment::instance(&mut cs, a); - let y = FieldAssignment::witness(&mut cs, b); - let z = FieldAssignment::instance(&mut cs, c); - let sum = &x + &y; - FieldAssignment::enforce_eq(&mut cs, &z, &sum); - - assert!(cs.is_sat()); - - // a + b != c - c += Scalar::one(); - let x = FieldAssignment::instance(&mut ncs, a); - let y = FieldAssignment::witness(&mut ncs, b); - let z = FieldAssignment::instance(&mut ncs, c); - let sum = &x + &y; - FieldAssignment::enforce_eq(&mut ncs, &z, &sum); - - assert!(!ncs.is_sat()) - } - - #[test] - fn field_mul_test() { - let mut cs: R1cs = R1cs::default(); - let mut ncs = cs.clone(); - let a = Scalar::random(OsRng); - let b = Scalar::random(OsRng); - let mut c = a * b; - - // a * b == c - let x = FieldAssignment::instance(&mut cs, a); - let y = FieldAssignment::witness(&mut cs, b); - let z = FieldAssignment::instance(&mut cs, c); - let product = FieldAssignment::mul(&mut cs, &x, &y); - FieldAssignment::enforce_eq(&mut cs, &z, &product); - - assert!(cs.is_sat()); - - // a * b != c - c += Scalar::one(); - let x = FieldAssignment::instance(&mut ncs, a); - let y = FieldAssignment::witness(&mut ncs, b); - let z = FieldAssignment::instance(&mut ncs, c); - let product = FieldAssignment::mul(&mut ncs, &x, &y); - FieldAssignment::enforce_eq(&mut ncs, &z, &product); - - assert!(!ncs.is_sat()) - } - - #[test] - fn field_ops_test() { - let mut cs: R1cs = R1cs::default(); - let mut ncs = cs.clone(); - let input = Scalar::from(3); - let c = Scalar::from(5); - let out = Scalar::from(35); - - // x^3 + x + 5 == 35 - let x = FieldAssignment::witness(&mut cs, input); - let c = FieldAssignment::constant(&c); - let z = FieldAssignment::instance(&mut cs, out); - let sym_1 = FieldAssignment::mul(&mut cs, &x, &x); - let y = FieldAssignment::mul(&mut cs, &sym_1, &x); - let sym_2 = &y + &x; - FieldAssignment::enforce_eq(&mut cs, &z, &(&sym_2 + &c)); - - assert!(cs.is_sat()); - - // x^3 + x + 5 != 36 - let c = Scalar::from(5); - let out = Scalar::from(36); - let x = FieldAssignment::witness(&mut ncs, input); - let c = FieldAssignment::constant(&c); - let z = FieldAssignment::instance(&mut ncs, out); - let sym_1 = FieldAssignment::mul(&mut ncs, &x, &x); - let y = FieldAssignment::mul(&mut ncs, &sym_1, &x); - let sym_2 = &y + &x; - FieldAssignment::enforce_eq(&mut ncs, &z, &(&sym_2 + &c)); - - assert!(!ncs.is_sat()); - } - - #[test] - fn curve_double_test() { - for _ in 0..100 { - let mut cs: R1cs = R1cs::default(); - let point = Affine::random(OsRng); - - let circuit_double = PointAssignment::instance(&mut cs, point).double(&mut cs); - - let expected = point.to_extended().double(); - - circuit_double.assert_equal_public_point(&mut cs, expected); - - assert!(cs.is_sat()); - } - } - - #[test] - fn curve_add_test() { - // Identity addition test - { - let mut cs: R1cs = R1cs::default(); - let a = Affine::random(OsRng); - let b = Affine::ADDITIVE_IDENTITY; - - let a_assignment = PointAssignment::instance(&mut cs, a); - let b_assignment = PointAssignment::instance(&mut cs, b); - - let expected = a + b; - - let sum_circuit = a_assignment.add(&b_assignment, &mut cs); - - sum_circuit.assert_equal_public_point(&mut cs, expected); - - assert!(cs.is_sat()); - } - - for _ in 0..100 { - let mut cs: R1cs = R1cs::default(); - let a = Affine::random(OsRng); - let b = Affine::random(OsRng); - - let a_assignment = PointAssignment::instance(&mut cs, a); - let b_assignment = PointAssignment::instance(&mut cs, b); - - let expected = a.to_extended() + b.to_extended(); - - let sum_circuit = a_assignment.add(&b_assignment, &mut cs); - - sum_circuit.assert_equal_public_point(&mut cs, expected); - - assert!(cs.is_sat()); - } - } - - #[test] - fn curve_scalar_mul_test() { - for _ in 0..100 { - let mut cs: R1cs = R1cs::default(); - let x = Scalar::random(OsRng); - let p = Affine::random(OsRng); - - let x_assignment = FieldAssignment::instance(&mut cs, x); // Fr - let p_assignment = PointAssignment::instance(&mut cs, p); - let expected = p * x; - - assert_eq!(x.to_bits(), Base::from(x).to_bits()); - - let mul_circuit = p_assignment.scalar_point(&mut cs, &x_assignment); - - mul_circuit.assert_equal_public_point(&mut cs, expected); - - assert!(cs.is_sat()); - } - } -} From a8ad969b88212892ccfa2a546a224085cdd14005 Mon Sep 17 00:00:00 2001 From: Kirill Karbushev Date: Sat, 9 Dec 2023 19:01:24 +0900 Subject: [PATCH 03/17] change assignment trait bounds from CircuitDriver to a field element --- groth16/src/lib.rs | 3 +- nova/src/circuit/augmented.rs | 6 +- nova/src/circuit/nifs.rs | 12 +- nova/src/circuit/transcript.rs | 39 +- nova/src/driver.rs | 5 +- nova/src/function.rs | 5 +- nova/src/gadget/mimc.rs | 19 +- nova/src/gadget/relaxed_instance.rs | 18 +- nova/src/hash.rs | 26 +- nova/src/relaxed_r1cs.rs | 2 +- nova/src/relaxed_r1cs/instance.rs | 10 +- nova/src/test.rs | 5 +- nova/src/verifier.rs | 2 +- zkstd/src/circuit/gadget/binary.rs | 28 +- zkstd/src/circuit/gadget/curve.rs | 76 +-- zkstd/src/circuit/gadget/field.rs | 97 +-- zkstd/tests/grumpkin.rs | 882 ++++++++++++++-------------- 17 files changed, 634 insertions(+), 601 deletions(-) diff --git a/groth16/src/lib.rs b/groth16/src/lib.rs index df7b9ad7..b99795d3 100644 --- a/groth16/src/lib.rs +++ b/groth16/src/lib.rs @@ -20,10 +20,9 @@ pub use zksnark::ZkSnark; #[cfg(test)] mod tests { - use crate::circuit::Circuit; + use crate::circuit::{Bn254Driver, Circuit}; use crate::error::Error; use crate::zksnark::ZkSnark; - use bn_254::driver::Bn254Driver; use bn_254::Fr as BnScalar; use zkstd::circuit::prelude::{FieldAssignment, R1cs}; diff --git a/nova/src/circuit/augmented.rs b/nova/src/circuit/augmented.rs index ef25e9d0..aec22d41 100644 --- a/nova/src/circuit/augmented.rs +++ b/nova/src/circuit/augmented.rs @@ -139,9 +139,9 @@ impl> AugmentedFCircuit { pub(crate) fn get_challenge( cs: &mut R1cs, u_range: &RelaxedR1csInstanceAssignment, - commit_t: PointAssignment, - ) -> FieldAssignment { - let mut transcript = MimcROCircuit::::default(); + commit_t: PointAssignment, + ) -> FieldAssignment { + let mut transcript = MimcROCircuit::::default(); transcript.append_point(commit_t); u_range.absorb_by_transcript(&mut transcript); transcript.squeeze(cs) diff --git a/nova/src/circuit/nifs.rs b/nova/src/circuit/nifs.rs index d186f0b3..a42b352b 100644 --- a/nova/src/circuit/nifs.rs +++ b/nova/src/circuit/nifs.rs @@ -10,11 +10,11 @@ pub(crate) struct NifsCircuit { impl NifsCircuit { pub(crate) fn verify( cs: &mut R1cs, - r: FieldAssignment, + r: FieldAssignment, instance1: RelaxedR1csInstanceAssignment, instance2: RelaxedR1csInstanceAssignment, instance3: RelaxedR1csInstanceAssignment, - ) -> BinaryAssignment { + ) -> BinaryAssignment { let r_u = FieldAssignment::mul(cs, &r, &instance2.u); let first_check = FieldAssignment::is_eq(cs, &instance3.u, &(&instance1.u + &r_u)); @@ -26,7 +26,7 @@ impl NifsCircuit { let r_x2 = FieldAssignment::mul(cs, &r, &x2); x1 + &r_x2 }) - .collect::>>(); + .collect::>>(); let second_check = x.iter() .zip(instance3.x) @@ -41,11 +41,11 @@ impl NifsCircuit { #[cfg(test)] mod tests { use super::*; - use crate::driver::{Bn254Driver, GrumpkinDriver}; + use crate::driver::GrumpkinDriver; use crate::hash::{MimcRO, MIMC_ROUNDS}; use crate::prover::tests::example_prover; use crate::RelaxedR1cs; - use bn_254::Fq; + use bn_254::{Fq, Fr}; use zkstd::r1cs::test::example_r1cs; #[test] @@ -57,7 +57,7 @@ mod tests { let r1cs_to_fold = RelaxedR1cs::new(example_r1cs(2)); let (instance, witness, commit_t) = prover.prove(&r1cs_to_fold, &running_r1cs); - let mut transcript = MimcRO::::default(); + let mut transcript = MimcRO::::default(); transcript.append_point(commit_t); running_r1cs.absorb_by_transcript(&mut transcript); let t = prover.compute_cross_term(&r1cs_to_fold, &running_r1cs); diff --git a/nova/src/circuit/transcript.rs b/nova/src/circuit/transcript.rs index 85f341c1..de17b670 100644 --- a/nova/src/circuit/transcript.rs +++ b/nova/src/circuit/transcript.rs @@ -1,46 +1,49 @@ use crate::gadget::MimcAssignment; use zkstd::circuit::prelude::{CircuitDriver, FieldAssignment, PointAssignment, R1cs}; -use zkstd::common::IntGroup; +use zkstd::common::{IntGroup, PrimeField}; -pub(crate) struct MimcROCircuit { - hasher: MimcAssignment, - state: Vec>, - key: FieldAssignment, +pub(crate) struct MimcROCircuit { + hasher: MimcAssignment, + state: Vec>, + key: FieldAssignment, } -impl Default for MimcROCircuit { +impl Default for MimcROCircuit { fn default() -> Self { Self { hasher: MimcAssignment::default(), state: Vec::default(), - key: FieldAssignment::constant(&C::Scalar::zero()), + key: FieldAssignment::constant(&F::zero()), } } } -impl MimcROCircuit { - pub(crate) fn append(&mut self, absorb: FieldAssignment) { +impl MimcROCircuit { + pub(crate) fn append(&mut self, absorb: FieldAssignment) { self.state.push(absorb) } - pub(crate) fn hash_vec( + pub(crate) fn hash_vec>( &mut self, cs: &mut R1cs, - values: Vec>, - ) -> FieldAssignment { + values: Vec>, + ) -> FieldAssignment { for x in values { self.state.push(x); } self.squeeze(cs) } - pub(crate) fn append_point(&mut self, point: PointAssignment) { + pub(crate) fn append_point(&mut self, point: PointAssignment) { self.append(point.get_x()); self.append(point.get_y()); self.append(point.get_z()); } - pub(crate) fn squeeze(&self, cs: &mut R1cs) -> FieldAssignment { + pub(crate) fn squeeze>( + &self, + cs: &mut R1cs, + ) -> FieldAssignment { self.state.iter().fold(self.key.clone(), |acc, scalar| { let h = self.hasher.hash(cs, scalar.clone(), acc.clone()); &(&acc + scalar) + &h @@ -62,10 +65,10 @@ mod tests { #[test] fn mimc_circuit() { let mut mimc = MimcRO::::default(); - let mut mimc_circuit = MimcROCircuit::::default(); // Base = Fq - let mut cs: R1cs = R1cs::default(); // Base = Fr, Scalar = Fq - let point = G1Affine::random(OsRng); - let scalar = Fq::random(OsRng); + let mut mimc_circuit = MimcROCircuit::::default(); // Base = Fr, Scalar = Fq + let mut cs: R1cs = R1cs::default(); // Base = Fq, Scalar = Fr + let point = G1Affine::random(OsRng); // Base = Fq, Scalar = Fr + let scalar = Fr::random(OsRng); let point_assignment = PointAssignment::instance(&mut cs, point); let scalar_assignment = FieldAssignment::instance(&mut cs, scalar); diff --git a/nova/src/driver.rs b/nova/src/driver.rs index fa5a9701..90d3eda6 100644 --- a/nova/src/driver.rs +++ b/nova/src/driver.rs @@ -210,10 +210,11 @@ mod grumpkin_gadget_tests { fn curve_scalar_mul_test() { for _ in 0..100 { let mut cs: R1cs = R1cs::default(); - let x = Scalar::random(OsRng); + // Base == GrumpkingScalar + let x = Base::random(OsRng); let p = G1Affine::random(OsRng); - let x_assignment = FieldAssignment::instance(&mut cs, x); // Fr + let x_assignment = FieldAssignment::instance(&mut cs, x.into()); // Fr let p_assignment = PointAssignment::instance(&mut cs, p); let expected = p * x; diff --git a/nova/src/function.rs b/nova/src/function.rs index c5b0e804..e757e725 100644 --- a/nova/src/function.rs +++ b/nova/src/function.rs @@ -6,5 +6,8 @@ use zkstd::r1cs::R1cs; pub trait FunctionCircuit: Clone + Debug + Default { fn invoke(z_i: &DenseVectors) -> DenseVectors; - fn invoke_cs(cs: &mut R1cs, z_i: Vec>) -> Vec>; + fn invoke_cs( + cs: &mut R1cs, + z_i: Vec>, + ) -> Vec>; } diff --git a/nova/src/gadget/mimc.rs b/nova/src/gadget/mimc.rs index 9f9aef0e..7d76f82e 100644 --- a/nova/src/gadget/mimc.rs +++ b/nova/src/gadget/mimc.rs @@ -1,26 +1,27 @@ use crate::hash::Mimc; use zkstd::circuit::prelude::{CircuitDriver, FieldAssignment, R1cs}; +use zkstd::common::PrimeField; -pub(crate) struct MimcAssignment { - constants: [C::Scalar; ROUND], +pub(crate) struct MimcAssignment { + constants: [F; ROUND], } -impl Default for MimcAssignment { +impl Default for MimcAssignment { fn default() -> Self { Self { - constants: Mimc::::default().constants, + constants: Mimc::::default().constants, } } } -impl MimcAssignment { - pub(crate) fn hash( +impl MimcAssignment { + pub(crate) fn hash>( &self, cs: &mut R1cs, - mut xl: FieldAssignment, - mut xr: FieldAssignment, - ) -> FieldAssignment { + mut xl: FieldAssignment, + mut xr: FieldAssignment, + ) -> FieldAssignment { for c in self.constants.iter().map(|c| FieldAssignment::constant(c)) { let cxl = &xl + &c; let mut ccxl = FieldAssignment::square(cs, &cxl); diff --git a/nova/src/gadget/relaxed_instance.rs b/nova/src/gadget/relaxed_instance.rs index 18930e6b..c9fc7073 100644 --- a/nova/src/gadget/relaxed_instance.rs +++ b/nova/src/gadget/relaxed_instance.rs @@ -7,10 +7,10 @@ use zkstd::common::CurveGroup; #[derive(Clone)] pub(crate) struct RelaxedR1csInstanceAssignment { - pub(crate) commit_w: PointAssignment, - pub(crate) commit_e: PointAssignment, - pub(crate) u: FieldAssignment, - pub(crate) x: Vec>, + pub(crate) commit_w: PointAssignment, + pub(crate) commit_e: PointAssignment, + pub(crate) u: FieldAssignment, + pub(crate) x: Vec>, } impl RelaxedR1csInstanceAssignment { @@ -50,7 +50,7 @@ impl RelaxedR1csInstanceAssignment { pub(crate) fn absorb_by_transcript( &self, - transcript: &mut MimcROCircuit, + transcript: &mut MimcROCircuit, ) { transcript.append_point(self.commit_w.clone()); transcript.append_point(self.commit_e.clone()); @@ -63,10 +63,10 @@ impl RelaxedR1csInstanceAssignment { pub(crate) fn hash( &self, cs: &mut R1cs, - i: FieldAssignment, - z_0: Vec>, - z_i: Vec>, - ) -> FieldAssignment { + i: FieldAssignment, + z_0: Vec>, + z_i: Vec>, + ) -> FieldAssignment { MimcROCircuit::::default().hash_vec( cs, vec![ diff --git a/nova/src/hash.rs b/nova/src/hash.rs index 73256304..ad9fdda8 100644 --- a/nova/src/hash.rs +++ b/nova/src/hash.rs @@ -41,45 +41,45 @@ impl Mimc { } } -pub(crate) struct MimcRO { - hasher: Mimc, - state: Vec, - key: C::Base, +pub(crate) struct MimcRO { + hasher: Mimc, + state: Vec, + key: F, } -impl Default for MimcRO { +impl Default for MimcRO { fn default() -> Self { Self { hasher: Mimc::default(), state: Vec::default(), - key: C::Base::zero(), + key: F::zero(), } } } -impl MimcRO { - pub(crate) fn append(&mut self, absorb: C::Base) { +impl MimcRO { + pub(crate) fn append(&mut self, absorb: F) { self.state.push(absorb) } - pub(crate) fn append_point(&mut self, point: C::Affine) { + pub(crate) fn append_point(&mut self, point: impl BNAffine) { self.append(point.get_x()); self.append(point.get_y()); self.append(if point.is_identity() { - C::Base::zero() + F::zero() } else { - C::Base::one() + F::one() }); } - pub(crate) fn hash_vec(&mut self, values: Vec) -> C::Base { + pub(crate) fn hash_vec(&mut self, values: Vec) -> F { for x in values { self.state.push(x); } self.squeeze() } - pub(crate) fn squeeze(&self) -> C::Base { + pub(crate) fn squeeze(&self) -> F { self.state.iter().fold(self.key, |acc, scalar| { let h = self.hasher.hash(*scalar, acc); acc + scalar + h diff --git a/nova/src/relaxed_r1cs.rs b/nova/src/relaxed_r1cs.rs index 803144de..a7792581 100644 --- a/nova/src/relaxed_r1cs.rs +++ b/nova/src/relaxed_r1cs.rs @@ -139,7 +139,7 @@ impl RelaxedR1cs { pub(crate) fn absorb_by_transcript( &self, - transcript: &mut MimcRO, + transcript: &mut MimcRO, ) { self.instance.absorb_by_transcript(transcript); } diff --git a/nova/src/relaxed_r1cs/instance.rs b/nova/src/relaxed_r1cs/instance.rs index e7536293..9f9f0eaf 100644 --- a/nova/src/relaxed_r1cs/instance.rs +++ b/nova/src/relaxed_r1cs/instance.rs @@ -60,7 +60,7 @@ impl RelaxedR1csInstance { pub(crate) fn absorb_by_transcript( &self, - transcript: &mut MimcRO, + transcript: &mut MimcRO, ) { transcript.append_point(self.commit_w); transcript.append_point(self.commit_e); @@ -73,14 +73,14 @@ impl RelaxedR1csInstance { pub fn hash( &self, i: usize, - z_0: &DenseVectors, - z_i: &DenseVectors, + z_0: &DenseVectors, + z_i: &DenseVectors, ) -> C::Scalar { let commit_e = self.commit_e.to_extended(); let commit_w = self.commit_w.to_extended(); - MimcRO::::default().hash_vec( + MimcRO::::default().hash_vec( vec![ - vec![C::Scalar::from(i as u64)], + vec![C::Base::from(i as u64)], z_0.get(), z_i.get(), vec![self.u], diff --git a/nova/src/test.rs b/nova/src/test.rs index 936913d8..a43c30a9 100644 --- a/nova/src/test.rs +++ b/nova/src/test.rs @@ -16,7 +16,10 @@ impl FunctionCircuit for ExampleFunction { DenseVectors::new(vec![next_z]) } - fn invoke_cs(cs: &mut R1cs, z_i: Vec>) -> Vec> { + fn invoke_cs( + cs: &mut R1cs, + z_i: Vec>, + ) -> Vec> { let five = FieldAssignment::constant(&C::Scalar::from(5)); let z_i_square = FieldAssignment::mul(cs, &z_i[0], &z_i[0]); let z_i_cube = FieldAssignment::mul(cs, &z_i_square, &z_i[0]); diff --git a/nova/src/verifier.rs b/nova/src/verifier.rs index 5f94d0d3..5a04ad87 100644 --- a/nova/src/verifier.rs +++ b/nova/src/verifier.rs @@ -14,7 +14,7 @@ impl Verifier { r1cs_1: &RelaxedR1cs, r1cs_2: &RelaxedR1cs, ) -> RelaxedR1csInstance { - let mut transcript = MimcRO::::default(); + let mut transcript = MimcRO::::default(); transcript.append_point(commit_t); r1cs_2.absorb_by_transcript(&mut transcript); diff --git a/zkstd/src/circuit/gadget/binary.rs b/zkstd/src/circuit/gadget/binary.rs index a7930414..bcb86a87 100644 --- a/zkstd/src/circuit/gadget/binary.rs +++ b/zkstd/src/circuit/gadget/binary.rs @@ -2,20 +2,24 @@ use crate::circuit::prelude::FieldAssignment; use crate::circuit::CircuitDriver; use crate::common::{IntGroup, Ring}; use crate::r1cs::{R1cs, Wire}; -use core::marker::PhantomData; #[derive(Clone, Debug)] -pub struct BinaryAssignment(Wire, PhantomData); +pub struct BinaryAssignment(Wire); -impl BinaryAssignment { - pub fn instance(cs: &mut R1cs, bit: u8) -> Self { +impl BinaryAssignment { + pub fn instance(cs: &mut R1cs, bit: u8) -> Self { let wire = cs.public_wire(); cs.x.push(C::Scalar::from(bit as u64)); - Self(wire, PhantomData::default()) + Self(wire) } - pub fn conditional_enforce_equal(cs: &mut R1cs, x: &Self, y: &Self, should_enforce: &Self) { + pub fn conditional_enforce_equal( + cs: &mut R1cs, + x: &Self, + y: &Self, + should_enforce: &Self, + ) { FieldAssignment::conditional_enforce_equal( cs, &FieldAssignment::from(x), @@ -24,15 +28,15 @@ impl BinaryAssignment { ); } - pub fn witness(cs: &mut R1cs, bit: u8) -> Self { + pub fn witness(cs: &mut R1cs, bit: u8) -> Self { let wire = cs.private_wire(); cs.w.push(C::Scalar::from(bit as u64)); - Self(wire, PhantomData::default()) + Self(wire) } // TODO: Think about the way to do it without new allocation - pub fn not(cs: &mut R1cs, b: &Self) -> Self { + pub fn not(cs: &mut R1cs, b: &Self) -> Self { let wire = cs.private_wire(); let new_val = if cs[b.0] == C::Scalar::one() { C::Scalar::zero() @@ -42,11 +46,11 @@ impl BinaryAssignment { cs.w.push(new_val); - Self(wire, PhantomData::default()) + Self(wire) } // TODO: Do without allocations - pub fn and(cs: &mut R1cs, a: &Self, b: &Self) -> Self { + pub fn and(cs: &mut R1cs, a: &Self, b: &Self) -> Self { let wire = cs.private_wire(); let a_and_b = if cs[a.0] == C::Scalar::one() && cs[b.0] == C::Scalar::one() { @@ -57,7 +61,7 @@ impl BinaryAssignment { cs.w.push(a_and_b); - Self(wire, PhantomData::default()) + Self(wire) } pub fn inner(&self) -> &Wire { diff --git a/zkstd/src/circuit/gadget/curve.rs b/zkstd/src/circuit/gadget/curve.rs index 0e994ffb..f8030830 100644 --- a/zkstd/src/circuit/gadget/curve.rs +++ b/zkstd/src/circuit/gadget/curve.rs @@ -2,26 +2,29 @@ use super::binary::BinaryAssignment; use super::field::FieldAssignment; use crate::circuit::CircuitDriver; -use crate::common::{BNAffine, BNProjective, CurveGroup, Group, IntGroup, Ring}; +use crate::common::{BNAffine, BNProjective, PrimeField}; use crate::r1cs::R1cs; #[derive(Clone)] -pub struct PointAssignment { - x: FieldAssignment, - y: FieldAssignment, - z: FieldAssignment, +pub struct PointAssignment { + x: FieldAssignment, + y: FieldAssignment, + z: FieldAssignment, } -impl PointAssignment { - pub fn instance(cs: &mut R1cs, point: impl BNAffine) -> Self { +impl PointAssignment { + pub fn instance>( + cs: &mut R1cs, + point: impl BNAffine, + ) -> Self { let x = FieldAssignment::instance(cs, point.get_x()); let y = FieldAssignment::instance(cs, point.get_y()); let z = FieldAssignment::instance( cs, if point.is_identity() { - C::Scalar::zero() + F::zero() } else { - C::Scalar::one() + F::one() }, ); @@ -29,32 +32,30 @@ impl PointAssignment { } pub fn identity() -> Self { - let x = FieldAssignment::constant(&C::Scalar::zero()); - let y = FieldAssignment::constant(&C::Scalar::one()); - let z = FieldAssignment::constant(&C::Scalar::one()); + let x = FieldAssignment::constant(&F::zero()); + let y = FieldAssignment::constant(&F::one()); + let z = FieldAssignment::constant(&F::one()); Self { x, y, z } } - pub fn witness(cs: &mut R1cs, x: C::Scalar, y: C::Scalar, is_infinity: bool) -> Self { + pub fn witness>( + cs: &mut R1cs, + x: F, + y: F, + is_infinity: bool, + ) -> Self { let x = FieldAssignment::witness(cs, x); let y = FieldAssignment::witness(cs, y); - let z = FieldAssignment::witness( - cs, - if is_infinity { - C::Scalar::zero() - } else { - C::Scalar::one() - }, - ); + let z = FieldAssignment::witness(cs, if is_infinity { F::zero() } else { F::one() }); Self { x, y, z } } - pub fn assert_equal_public_point( + pub fn assert_equal_public_point>( &self, cs: &mut R1cs, - point: impl BNProjective, + point: impl BNProjective, ) { let point_x = FieldAssignment::constant(&point.get_x()); let point_y = FieldAssignment::constant(&point.get_y()); @@ -71,8 +72,8 @@ impl PointAssignment { FieldAssignment::enforce_eq(cs, &yz1, &yz2); } - pub fn add(&self, rhs: &Self, cs: &mut R1cs) -> Self { - let b3 = FieldAssignment::::constant(&C::b3()); + pub fn add>(&self, rhs: &Self, cs: &mut R1cs) -> Self { + let b3 = FieldAssignment::constant(&C::b3()); let t0 = FieldAssignment::mul(cs, &self.x, &rhs.x); let t1 = FieldAssignment::mul(cs, &self.y, &rhs.y); let t2 = FieldAssignment::mul(cs, &self.z, &rhs.z); @@ -114,8 +115,8 @@ impl PointAssignment { } } - pub fn double(&self, cs: &mut R1cs) -> Self { - let b3 = FieldAssignment::::constant(&C::b3().into()); + pub fn double>(&self, cs: &mut R1cs) -> Self { + let b3 = FieldAssignment::constant(&C::b3().into()); let t0 = FieldAssignment::mul(cs, &self.y, &self.y); let z3 = &t0 + &t0; let z3 = &z3 + &z3; @@ -143,7 +144,11 @@ impl PointAssignment { } /// coordinate scalar - pub fn scalar_point(&self, cs: &mut R1cs, scalar: &FieldAssignment) -> Self { + pub fn scalar_point>( + &self, + cs: &mut R1cs, + scalar: &FieldAssignment, + ) -> Self { let mut res = PointAssignment::identity(); for bit in FieldAssignment::to_bits(cs, scalar).iter() { res = res.double(cs); @@ -154,27 +159,30 @@ impl PointAssignment { res } - pub fn select_identity(&self, cs: &mut R1cs, bit: &BinaryAssignment) -> Self { + pub fn select_identity>( + &self, + cs: &mut R1cs, + bit: &BinaryAssignment, + ) -> Self { let PointAssignment { x, y, z } = self.clone(); let bit = FieldAssignment::from(bit); Self { x: FieldAssignment::mul(cs, &x, &bit), - y: &(&FieldAssignment::mul(cs, &y, &bit) - + &FieldAssignment::constant(&C::Scalar::one())) + y: &(&FieldAssignment::mul(cs, &y, &bit) + &FieldAssignment::constant(&F::one())) - &bit, z: FieldAssignment::mul(cs, &z, &bit), } } - pub fn get_x(&self) -> FieldAssignment { + pub fn get_x(&self) -> FieldAssignment { self.x.clone() } - pub fn get_y(&self) -> FieldAssignment { + pub fn get_y(&self) -> FieldAssignment { self.y.clone() } - pub fn get_z(&self) -> FieldAssignment { + pub fn get_z(&self) -> FieldAssignment { self.z.clone() } } diff --git a/zkstd/src/circuit/gadget/field.rs b/zkstd/src/circuit/gadget/field.rs index 400b6b30..c8f907f9 100644 --- a/zkstd/src/circuit/gadget/field.rs +++ b/zkstd/src/circuit/gadget/field.rs @@ -1,39 +1,39 @@ use super::binary::BinaryAssignment; use crate::circuit::CircuitDriver; -use crate::common::{vec, Add, Group, IntGroup, Neg, PrimeField, Ring, Sub, Vec}; +use crate::common::{vec, Add, Neg, PrimeField, Sub, Vec}; use crate::matrix::SparseRow; use crate::r1cs::{R1cs, Wire}; #[derive(Clone)] -pub struct FieldAssignment(SparseRow); +pub struct FieldAssignment(SparseRow); -impl FieldAssignment { - pub fn inner(&self) -> &SparseRow { +impl FieldAssignment { + pub fn inner(&self) -> &SparseRow { &self.0 } - pub fn instance(cs: &mut R1cs, instance: C::Scalar) -> Self { + pub fn instance>(cs: &mut R1cs, instance: F) -> Self { let wire = cs.public_wire(); cs.x.push(instance); Self(SparseRow::from(wire)) } - pub fn witness(cs: &mut R1cs, witness: C::Scalar) -> Self { + pub fn witness>(cs: &mut R1cs, witness: F) -> Self { let wire = cs.private_wire(); cs.w.push(witness); Self(SparseRow::from(wire)) } - pub fn constant(constant: &C::Scalar) -> Self { + pub fn constant(constant: &F) -> Self { Self(SparseRow(vec![(Wire::ONE, *constant)])) } - pub fn square(cs: &mut R1cs, x: &Self) -> Self { + pub fn square>(cs: &mut R1cs, x: &Self) -> Self { Self::mul(cs, x, x) } - pub fn mul(cs: &mut R1cs, x: &Self, y: &Self) -> Self { + pub fn mul>(cs: &mut R1cs, x: &Self, y: &Self) -> Self { if let Some(c) = x.0.as_constant() { return Self(y.0.clone() * c); } @@ -48,7 +48,7 @@ impl FieldAssignment { z } - pub fn add(cs: &mut R1cs, x: &Self, y: &Self) -> Self { + pub fn add>(cs: &mut R1cs, x: &Self, y: &Self) -> Self { if let Some(c) = x.0.as_constant() { return Self(y.0.clone() + SparseRow::from(c)); } @@ -63,7 +63,11 @@ impl FieldAssignment { z } - pub fn range_check(cs: &mut R1cs, a_bits: &[BinaryAssignment], c: C::Scalar) { + pub fn range_check>( + cs: &mut R1cs, + a_bits: &[BinaryAssignment], + c: F, + ) { let c_bits = c .to_bits() .into_iter() @@ -74,7 +78,7 @@ impl FieldAssignment { assert!(a_bits .iter() .take(a_bits.len() - c_bits.len()) - .all(|b| cs[*b.inner()] == C::Scalar::zero())); + .all(|b| cs[*b.inner()] == F::zero())); let a_bits = a_bits .iter() @@ -104,34 +108,37 @@ impl FieldAssignment { if c == 1 { let bool_constr = FieldAssignment::mul( cs, - &(&bit_field - &FieldAssignment::constant(&C::Scalar::one())), + &(&bit_field - &FieldAssignment::constant(&F::one())), &bit_field, ); FieldAssignment::enforce_eq( cs, &bool_constr, - &FieldAssignment::constant(&C::Scalar::zero()), + &FieldAssignment::constant(&F::zero()), ); } else if c == 0 { let bool_constr = FieldAssignment::mul( cs, - &(&(&FieldAssignment::constant(&C::Scalar::one()) - &bit_field) - &p[i - 1]), + &(&(&FieldAssignment::constant(&F::one()) - &bit_field) - &p[i - 1]), &bit_field, ); FieldAssignment::enforce_eq( cs, &bool_constr, - &FieldAssignment::constant(&C::Scalar::zero()), + &FieldAssignment::constant(&F::zero()), ); } } } /// To bit representation in Big-endian - pub fn to_bits(cs: &mut R1cs, x: &Self) -> Vec> { - let bound = C::Scalar::MODULUS - C::Scalar::one(); + pub fn to_bits>( + cs: &mut R1cs, + x: &Self, + ) -> Vec { + let bound = F::MODULUS - F::one(); - let bit_repr: Vec> = x + let bit_repr: Vec = x .inner() .evaluate(&cs.x, &cs.w) .to_bits() @@ -142,33 +149,41 @@ impl FieldAssignment { bit_repr } - pub fn enforce_eq(cs: &mut R1cs, x: &Self, y: &Self) { + pub fn enforce_eq>(cs: &mut R1cs, x: &Self, y: &Self) { cs.mul_gate(&x.0, &SparseRow::one(), &y.0) } - pub fn conditional_enforce_equal( + pub fn conditional_enforce_equal>( cs: &mut R1cs, x: &Self, y: &Self, - should_enforce: &BinaryAssignment, + should_enforce: &BinaryAssignment, ) { let mul = FieldAssignment::mul(cs, &(x - y), &FieldAssignment::from(should_enforce)); - FieldAssignment::enforce_eq_constant(cs, &mul, &C::Scalar::zero()); + FieldAssignment::enforce_eq_constant(cs, &mul, &F::zero()); } - pub fn is_eq(cs: &mut R1cs, x: &Self, y: &Self) -> BinaryAssignment { + pub fn is_eq>( + cs: &mut R1cs, + x: &Self, + y: &Self, + ) -> BinaryAssignment { let is_neq = Self::is_neq(cs, x, y); BinaryAssignment::not(cs, &is_neq) } - pub fn is_neq(cs: &mut R1cs, x: &Self, y: &Self) -> BinaryAssignment { + pub fn is_neq>( + cs: &mut R1cs, + x: &Self, + y: &Self, + ) -> BinaryAssignment { let x_val = x.inner().evaluate(&cs.x, &cs.w); let y_val = y.inner().evaluate(&cs.x, &cs.w); let is_not_equal = BinaryAssignment::witness(cs, u8::from(x_val != y_val)); let multiplier = if x_val != y_val { FieldAssignment::witness(cs, (x_val - y_val).invert().unwrap()) } else { - FieldAssignment::witness(cs, C::Scalar::one()) + FieldAssignment::witness(cs, F::one()) }; let diff = x - y; @@ -177,44 +192,40 @@ impl FieldAssignment { let not_is_not_equal = BinaryAssignment::not(cs, &is_not_equal); let mul = FieldAssignment::mul(cs, &diff, &FieldAssignment::from(¬_is_not_equal)); - FieldAssignment::enforce_eq(cs, &mul, &FieldAssignment::constant(&C::Scalar::zero())); + FieldAssignment::enforce_eq(cs, &mul, &FieldAssignment::constant(&F::zero())); is_not_equal } - pub fn enforce_eq_constant(cs: &mut R1cs, x: &Self, c: &C::Scalar) { - cs.mul_gate( - &x.0, - &SparseRow::one(), - &FieldAssignment::::constant(c).0, - ) + pub fn enforce_eq_constant>(cs: &mut R1cs, x: &Self, c: &F) { + cs.mul_gate(&x.0, &SparseRow::one(), &FieldAssignment::constant(c).0) } } -impl From<&BinaryAssignment> for FieldAssignment { - fn from(value: &BinaryAssignment) -> Self { +impl From<&BinaryAssignment> for FieldAssignment { + fn from(value: &BinaryAssignment) -> Self { Self(SparseRow::from(value.inner())) } } -impl Add<&FieldAssignment> for &FieldAssignment { - type Output = FieldAssignment; +impl Add<&FieldAssignment> for &FieldAssignment { + type Output = FieldAssignment; - fn add(self, rhs: &FieldAssignment) -> Self::Output { + fn add(self, rhs: &FieldAssignment) -> Self::Output { FieldAssignment(&self.0 + &rhs.0) } } -impl Sub<&FieldAssignment> for &FieldAssignment { - type Output = FieldAssignment; +impl Sub<&FieldAssignment> for &FieldAssignment { + type Output = FieldAssignment; - fn sub(self, rhs: &FieldAssignment) -> Self::Output { + fn sub(self, rhs: &FieldAssignment) -> Self::Output { FieldAssignment(&self.0 - &rhs.0) } } -impl Neg for &FieldAssignment { - type Output = FieldAssignment; +impl Neg for &FieldAssignment { + type Output = FieldAssignment; fn neg(self) -> Self::Output { FieldAssignment(-&self.0) diff --git a/zkstd/tests/grumpkin.rs b/zkstd/tests/grumpkin.rs index 3c059049..575fc432 100644 --- a/zkstd/tests/grumpkin.rs +++ b/zkstd/tests/grumpkin.rs @@ -1,441 +1,441 @@ -#![allow(clippy::suspicious_arithmetic_impl)] -#![allow(clippy::suspicious_op_assign_impl)] - -use zkstd::arithmetic::bits_256::*; -use zkstd::arithmetic::weierstrass::*; -use zkstd::circuit::CircuitDriver; -use zkstd::common::*; -use zkstd::macros::curve::weierstrass::*; -use zkstd::macros::field::*; - -pub(crate) const FR_MODULUS: [u64; 4] = [ - 0x43e1f593f0000001, - 0x2833e84879b97091, - 0xb85045b68181585d, - 0x30644e72e131a029, -]; - -const FR_GENERATOR: [u64; 4] = [7, 0, 0, 0]; - -pub(crate) const FR_R: [u64; 4] = [ - 0xac96341c4ffffffb, - 0x36fc76959f60cd29, - 0x666ea36f7879462e, - 0x0e0a77c19a07df2f, -]; - -pub(crate) const FR_R2: [u64; 4] = [ - 0x1bb8e645ae216da7, - 0x53fe3ab1e35c59e3, - 0x8c49833d53bb8085, - 0x0216d0b17f4e44a5, -]; - -pub(crate) const FR_R3: [u64; 4] = [ - 0x5e94d8e1b4bf0040, - 0x2a489cbe1cfbb6b8, - 0x893cc664a19fcfed, - 0x0cf8594b7fcc657c, -]; - -pub const FR_INV: u64 = 0xc2e1f593efffffff; - -pub(crate) const FQ_MODULUS: [u64; 4] = [ - 0x3c208c16d87cfd47, - 0x97816a916871ca8d, - 0xb85045b68181585d, - 0x30644e72e131a029, -]; - -pub(crate) const FQ_GENERATOR: [u64; 4] = [3, 0, 0, 0]; - -/// R = 2^256 mod q -pub(crate) const FQ_R: [u64; 4] = [ - 0xd35d438dc58f0d9d, - 0x0a78eb28f5c70b3d, - 0x666ea36f7879462c, - 0x0e0a77c19a07df2f, -]; - -/// R^2 = 2^512 mod q -pub(crate) const FQ_R2: [u64; 4] = [ - 0xf32cfc5b538afa89, - 0xb5e71911d44501fb, - 0x47ab1eff0a417ff6, - 0x06d89f71cab8351f, -]; - -/// R^3 = 2^768 mod q -pub(crate) const FQ_R3: [u64; 4] = [ - 0xb1cd6dafda1530df, - 0x62f210e6a7283db6, - 0xef7f0b0c0ada0afb, - 0x20fd6e902d592544, -]; - -/// INV = -(q^{-1} mod 2^64) mod 2^64 -pub(crate) const FQ_INV: u64 = 0x87d20782e4866389; - -#[macro_export] -macro_rules! cycle_pair_field { - ($field:ident, $generator:ident, $modulus:ident, $r:ident, $r2:ident, $r3:ident, $inv:ident) => { - #[derive(Clone, Copy, Decode, Encode, Serialize, Deserialize)] - pub struct $field(pub [u64; 4]); - - impl $field { - pub const fn new_unchecked(val: [u64; 4]) -> Self { - Self(val) - } - pub const fn add_const(self, rhs: Self) -> Self { - Self(add(self.0, rhs.0, $modulus)) - } - - pub const fn to_mont_form(val: [u64; 4]) -> Self { - Self(to_mont_form(val, $r2, $modulus, $inv)) - } - - pub const fn inner(&self) -> &[u64; 4] { - &self.0 - } - - pub(crate) const fn montgomery_reduce(self) -> [u64; 4] { - mont( - [self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0], - $modulus, - $inv, - ) - } - } - - impl SigUtils<32> for $field { - fn to_bytes(self) -> [u8; Self::LENGTH] { - let tmp = self.montgomery_reduce(); - - let mut res = [0; Self::LENGTH]; - res[0..8].copy_from_slice(&tmp[0].to_le_bytes()); - res[8..16].copy_from_slice(&tmp[1].to_le_bytes()); - res[16..24].copy_from_slice(&tmp[2].to_le_bytes()); - res[24..32].copy_from_slice(&tmp[3].to_le_bytes()); - - res - } - - fn from_bytes(bytes: [u8; Self::LENGTH]) -> Option { - // SBP-M1 review: apply proper error handling instead of `unwrap` - let l0 = u64::from_le_bytes(bytes[0..8].try_into().unwrap()); - let l1 = u64::from_le_bytes(bytes[8..16].try_into().unwrap()); - let l2 = u64::from_le_bytes(bytes[16..24].try_into().unwrap()); - let l3 = u64::from_le_bytes(bytes[24..32].try_into().unwrap()); - - let (_, borrow) = sbb(l0, $modulus[0], 0); - let (_, borrow) = sbb(l1, $modulus[1], borrow); - let (_, borrow) = sbb(l2, $modulus[2], borrow); - let (_, borrow) = sbb(l3, $modulus[3], borrow); - - if borrow & 1 == 1 { - Some(Self([l0, l1, l2, l3]) * Self($r2)) - } else { - None - } - } - } - - prime_field_operation!($field, $modulus, $generator, $inv, $r, $r2, $r3); - }; -} - -cycle_pair_field!(Fr, FR_GENERATOR, FR_MODULUS, FR_R, FR_R2, FR_R3, FR_INV); -cycle_pair_field!(Fq, FQ_GENERATOR, FQ_MODULUS, FQ_R, FQ_R2, FQ_R3, FQ_INV); - -pub(crate) const FR_PARAM_B: Fr = Fr::new_unchecked([ - 0xdd7056026000005a, - 0x223fa97acb319311, - 0xcc388229877910c0, - 0x034394632b724eaa, -]); -pub const FR_PARAM_B3: Fr = FR_PARAM_B.add_const(FR_PARAM_B).add_const(FR_PARAM_B); - -pub(crate) const G1_GENERATOR_X: Fq = Fq::one(); -pub(crate) const G1_GENERATOR_Y: Fq = Fq::to_mont_form([2, 0, 0, 0]); -pub(crate) const G1_PARAM_B: Fq = Fq::to_mont_form([3, 0, 0, 0]); -pub const FQ_PARAM_B3: Fq = G1_PARAM_B.add_const(G1_PARAM_B).add_const(G1_PARAM_B); - -impl From for Fr { - fn from(val: Fq) -> Fr { - Self(to_mont_form( - val.montgomery_reduce(), - FR_R2, - FR_MODULUS, - FR_INV, - )) - } -} - -impl From for Fq { - fn from(val: Fr) -> Fq { - Self(to_mont_form( - val.montgomery_reduce(), - FQ_R2, - FQ_MODULUS, - FQ_INV, - )) - } -} - -/// The projective form of coordinate -#[derive(Debug, Clone, Copy, Decode, Encode)] -pub struct G1Affine { - pub(crate) x: Fq, - pub(crate) y: Fq, - is_infinity: bool, -} - -impl Add for G1Affine { - type Output = G1Projective; - - fn add(self, rhs: G1Affine) -> Self::Output { - add_affine_point(self, rhs) - } -} - -impl Neg for G1Affine { - type Output = Self; - - fn neg(self) -> Self { - Self { - x: self.x, - y: -self.y, - is_infinity: self.is_infinity, - } - } -} - -impl Sub for G1Affine { - type Output = G1Projective; - - fn sub(self, rhs: G1Affine) -> Self::Output { - add_affine_point(self, rhs.neg()) - } -} - -impl Mul for G1Affine { - type Output = G1Projective; - - fn mul(self, rhs: Fr) -> Self::Output { - scalar_point(self.to_extended(), &rhs) - } -} - -impl Mul for Fr { - type Output = G1Projective; - - fn mul(self, rhs: G1Affine) -> Self::Output { - scalar_point(rhs.to_extended(), &self) - } -} - -/// The projective form of coordinate -#[derive(Debug, Clone, Copy, Decode, Encode)] -pub struct G1Projective { - pub(crate) x: Fq, - pub(crate) y: Fq, - pub(crate) z: Fq, -} - -impl Add for G1Projective { - type Output = Self; - - fn add(self, rhs: G1Projective) -> Self { - add_projective_point(self, rhs) - } -} - -impl Neg for G1Projective { - type Output = Self; - - fn neg(self) -> Self { - Self { - x: self.x, - y: -self.y, - z: self.z, - } - } -} - -impl Sub for G1Projective { - type Output = Self; - - fn sub(self, rhs: G1Projective) -> Self { - add_projective_point(self, -rhs) - } -} - -impl Mul for G1Projective { - type Output = G1Projective; - - fn mul(self, rhs: Fr) -> Self::Output { - scalar_point(self, &rhs) - } -} - -impl Mul for Fr { - type Output = G1Projective; - - fn mul(self, rhs: G1Projective) -> Self::Output { - scalar_point(rhs, &self) - } -} - -weierstrass_curve_operation!( - Fr, - Fq, - G1_PARAM_B, - FQ_PARAM_B3, - G1Affine, - G1Projective, - G1_GENERATOR_X, - G1_GENERATOR_Y -); - -#[derive(Debug, Clone, Copy, Decode, Encode)] -pub struct Affine { - pub(crate) x: Fr, - pub(crate) y: Fr, - is_infinity: bool, -} -impl Add for Affine { - type Output = Projective; - - fn add(self, rhs: Affine) -> Self::Output { - add_affine_point(self, rhs) - } -} - -impl Neg for Affine { - type Output = Self; - - fn neg(self) -> Self { - Self { - x: self.x, - y: -self.y, - is_infinity: self.is_infinity, - } - } -} - -impl Sub for Affine { - type Output = Projective; - - fn sub(self, rhs: Affine) -> Self::Output { - add_affine_point(self, rhs.neg()) - } -} - -impl Mul for Affine { - type Output = Projective; - - fn mul(self, rhs: Fq) -> Self::Output { - scalar_point(self.to_extended(), &rhs) - } -} - -impl Mul for Fq { - type Output = Projective; - - fn mul(self, rhs: Affine) -> Self::Output { - scalar_point(rhs.to_extended(), &self) - } -} - -#[derive(Debug, Clone, Copy, Decode, Encode)] -pub struct Projective { - pub(crate) x: Fr, - pub(crate) y: Fr, - pub(crate) z: Fr, -} - -impl Add for Projective { - type Output = Self; - - fn add(self, rhs: Projective) -> Self { - add_projective_point(self, rhs) - } -} - -impl Neg for Projective { - type Output = Self; - - fn neg(self) -> Self { - Self { - x: self.x, - y: -self.y, - z: self.z, - } - } -} - -impl Sub for Projective { - type Output = Self; - - fn sub(self, rhs: Projective) -> Self { - add_projective_point(self, -rhs) - } -} - -impl Mul for Projective { - type Output = Projective; - - fn mul(self, rhs: Fq) -> Self::Output { - scalar_point(self, &rhs) - } -} - -impl Mul for Fq { - type Output = Projective; - - fn mul(self, rhs: Projective) -> Self::Output { - scalar_point(rhs, &self) - } -} - -pub const GENERATOR_X: Fr = Fr::one(); -pub const GENERATOR_Y: Fr = Fr::new_unchecked([ - 0x11b2dff1448c41d8, - 0x23d3446f21c77dc3, - 0xaa7b8cf435dfafbb, - 0x14b34cf69dc25d68, -]); -pub(crate) const PARAM_B: Fr = Fr::new_unchecked([ - 0xdd7056026000005a, - 0x223fa97acb319311, - 0xcc388229877910c0, - 0x034394632b724eaa, -]); -pub const PARAM_B3: Fr = PARAM_B.add_const(PARAM_B).add_const(PARAM_B); - -weierstrass_curve_operation!( - Fq, - Fr, - PARAM_B, - PARAM_B3, - Affine, - Projective, - GENERATOR_X, - GENERATOR_Y -); - -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct GrumpkinDriver; - -impl CircuitDriver for GrumpkinDriver { - const NUM_BITS: u16 = 254; - type Affine = Affine; - - type Base = Fr; - - type Scalar = Fq; - - fn b3() -> Self::Base { - FR_PARAM_B3 - } -} +// #![allow(clippy::suspicious_arithmetic_impl)] +// #![allow(clippy::suspicious_op_assign_impl)] +// +// use zkstd::arithmetic::bits_256::*; +// use zkstd::arithmetic::weierstrass::*; +// use zkstd::circuit::CircuitDriver; +// use zkstd::common::*; +// use zkstd::macros::curve::weierstrass::*; +// use zkstd::macros::field::*; +// +// pub(crate) const FR_MODULUS: [u64; 4] = [ +// 0x43e1f593f0000001, +// 0x2833e84879b97091, +// 0xb85045b68181585d, +// 0x30644e72e131a029, +// ]; +// +// const FR_GENERATOR: [u64; 4] = [7, 0, 0, 0]; +// +// pub(crate) const FR_R: [u64; 4] = [ +// 0xac96341c4ffffffb, +// 0x36fc76959f60cd29, +// 0x666ea36f7879462e, +// 0x0e0a77c19a07df2f, +// ]; +// +// pub(crate) const FR_R2: [u64; 4] = [ +// 0x1bb8e645ae216da7, +// 0x53fe3ab1e35c59e3, +// 0x8c49833d53bb8085, +// 0x0216d0b17f4e44a5, +// ]; +// +// pub(crate) const FR_R3: [u64; 4] = [ +// 0x5e94d8e1b4bf0040, +// 0x2a489cbe1cfbb6b8, +// 0x893cc664a19fcfed, +// 0x0cf8594b7fcc657c, +// ]; +// +// pub const FR_INV: u64 = 0xc2e1f593efffffff; +// +// pub(crate) const FQ_MODULUS: [u64; 4] = [ +// 0x3c208c16d87cfd47, +// 0x97816a916871ca8d, +// 0xb85045b68181585d, +// 0x30644e72e131a029, +// ]; +// +// pub(crate) const FQ_GENERATOR: [u64; 4] = [3, 0, 0, 0]; +// +// /// R = 2^256 mod q +// pub(crate) const FQ_R: [u64; 4] = [ +// 0xd35d438dc58f0d9d, +// 0x0a78eb28f5c70b3d, +// 0x666ea36f7879462c, +// 0x0e0a77c19a07df2f, +// ]; +// +// /// R^2 = 2^512 mod q +// pub(crate) const FQ_R2: [u64; 4] = [ +// 0xf32cfc5b538afa89, +// 0xb5e71911d44501fb, +// 0x47ab1eff0a417ff6, +// 0x06d89f71cab8351f, +// ]; +// +// /// R^3 = 2^768 mod q +// pub(crate) const FQ_R3: [u64; 4] = [ +// 0xb1cd6dafda1530df, +// 0x62f210e6a7283db6, +// 0xef7f0b0c0ada0afb, +// 0x20fd6e902d592544, +// ]; +// +// /// INV = -(q^{-1} mod 2^64) mod 2^64 +// pub(crate) const FQ_INV: u64 = 0x87d20782e4866389; +// +// #[macro_export] +// macro_rules! cycle_pair_field { +// ($field:ident, $generator:ident, $modulus:ident, $r:ident, $r2:ident, $r3:ident, $inv:ident) => { +// #[derive(Clone, Copy, Decode, Encode, Serialize, Deserialize)] +// pub struct $field(pub [u64; 4]); +// +// impl $field { +// pub const fn new_unchecked(val: [u64; 4]) -> Self { +// Self(val) +// } +// pub const fn add_const(self, rhs: Self) -> Self { +// Self(add(self.0, rhs.0, $modulus)) +// } +// +// pub const fn to_mont_form(val: [u64; 4]) -> Self { +// Self(to_mont_form(val, $r2, $modulus, $inv)) +// } +// +// pub const fn inner(&self) -> &[u64; 4] { +// &self.0 +// } +// +// pub(crate) const fn montgomery_reduce(self) -> [u64; 4] { +// mont( +// [self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0], +// $modulus, +// $inv, +// ) +// } +// } +// +// impl SigUtils<32> for $field { +// fn to_bytes(self) -> [u8; Self::LENGTH] { +// let tmp = self.montgomery_reduce(); +// +// let mut res = [0; Self::LENGTH]; +// res[0..8].copy_from_slice(&tmp[0].to_le_bytes()); +// res[8..16].copy_from_slice(&tmp[1].to_le_bytes()); +// res[16..24].copy_from_slice(&tmp[2].to_le_bytes()); +// res[24..32].copy_from_slice(&tmp[3].to_le_bytes()); +// +// res +// } +// +// fn from_bytes(bytes: [u8; Self::LENGTH]) -> Option { +// // SBP-M1 review: apply proper error handling instead of `unwrap` +// let l0 = u64::from_le_bytes(bytes[0..8].try_into().unwrap()); +// let l1 = u64::from_le_bytes(bytes[8..16].try_into().unwrap()); +// let l2 = u64::from_le_bytes(bytes[16..24].try_into().unwrap()); +// let l3 = u64::from_le_bytes(bytes[24..32].try_into().unwrap()); +// +// let (_, borrow) = sbb(l0, $modulus[0], 0); +// let (_, borrow) = sbb(l1, $modulus[1], borrow); +// let (_, borrow) = sbb(l2, $modulus[2], borrow); +// let (_, borrow) = sbb(l3, $modulus[3], borrow); +// +// if borrow & 1 == 1 { +// Some(Self([l0, l1, l2, l3]) * Self($r2)) +// } else { +// None +// } +// } +// } +// +// prime_field_operation!($field, $modulus, $generator, $inv, $r, $r2, $r3); +// }; +// } +// +// cycle_pair_field!(Fr, FR_GENERATOR, FR_MODULUS, FR_R, FR_R2, FR_R3, FR_INV); +// cycle_pair_field!(Fq, FQ_GENERATOR, FQ_MODULUS, FQ_R, FQ_R2, FQ_R3, FQ_INV); +// +// pub(crate) const FR_PARAM_B: Fr = Fr::new_unchecked([ +// 0xdd7056026000005a, +// 0x223fa97acb319311, +// 0xcc388229877910c0, +// 0x034394632b724eaa, +// ]); +// pub const FR_PARAM_B3: Fr = FR_PARAM_B.add_const(FR_PARAM_B).add_const(FR_PARAM_B); +// +// pub(crate) const G1_GENERATOR_X: Fq = Fq::one(); +// pub(crate) const G1_GENERATOR_Y: Fq = Fq::to_mont_form([2, 0, 0, 0]); +// pub(crate) const G1_PARAM_B: Fq = Fq::to_mont_form([3, 0, 0, 0]); +// pub const FQ_PARAM_B3: Fq = G1_PARAM_B.add_const(G1_PARAM_B).add_const(G1_PARAM_B); +// +// impl From for Fr { +// fn from(val: Fq) -> Fr { +// Self(to_mont_form( +// val.montgomery_reduce(), +// FR_R2, +// FR_MODULUS, +// FR_INV, +// )) +// } +// } +// +// impl From for Fq { +// fn from(val: Fr) -> Fq { +// Self(to_mont_form( +// val.montgomery_reduce(), +// FQ_R2, +// FQ_MODULUS, +// FQ_INV, +// )) +// } +// } +// +// /// The projective form of coordinate +// #[derive(Debug, Clone, Copy, Decode, Encode)] +// pub struct G1Affine { +// pub(crate) x: Fq, +// pub(crate) y: Fq, +// is_infinity: bool, +// } +// +// impl Add for G1Affine { +// type Output = G1Projective; +// +// fn add(self, rhs: G1Affine) -> Self::Output { +// add_affine_point(self, rhs) +// } +// } +// +// impl Neg for G1Affine { +// type Output = Self; +// +// fn neg(self) -> Self { +// Self { +// x: self.x, +// y: -self.y, +// is_infinity: self.is_infinity, +// } +// } +// } +// +// impl Sub for G1Affine { +// type Output = G1Projective; +// +// fn sub(self, rhs: G1Affine) -> Self::Output { +// add_affine_point(self, rhs.neg()) +// } +// } +// +// impl Mul for G1Affine { +// type Output = G1Projective; +// +// fn mul(self, rhs: Fr) -> Self::Output { +// scalar_point(self.to_extended(), &rhs) +// } +// } +// +// impl Mul for Fr { +// type Output = G1Projective; +// +// fn mul(self, rhs: G1Affine) -> Self::Output { +// scalar_point(rhs.to_extended(), &self) +// } +// } +// +// /// The projective form of coordinate +// #[derive(Debug, Clone, Copy, Decode, Encode)] +// pub struct G1Projective { +// pub(crate) x: Fq, +// pub(crate) y: Fq, +// pub(crate) z: Fq, +// } +// +// impl Add for G1Projective { +// type Output = Self; +// +// fn add(self, rhs: G1Projective) -> Self { +// add_projective_point(self, rhs) +// } +// } +// +// impl Neg for G1Projective { +// type Output = Self; +// +// fn neg(self) -> Self { +// Self { +// x: self.x, +// y: -self.y, +// z: self.z, +// } +// } +// } +// +// impl Sub for G1Projective { +// type Output = Self; +// +// fn sub(self, rhs: G1Projective) -> Self { +// add_projective_point(self, -rhs) +// } +// } +// +// impl Mul for G1Projective { +// type Output = G1Projective; +// +// fn mul(self, rhs: Fr) -> Self::Output { +// scalar_point(self, &rhs) +// } +// } +// +// impl Mul for Fr { +// type Output = G1Projective; +// +// fn mul(self, rhs: G1Projective) -> Self::Output { +// scalar_point(rhs, &self) +// } +// } +// +// weierstrass_curve_operation!( +// Fr, +// Fq, +// G1_PARAM_B, +// FQ_PARAM_B3, +// G1Affine, +// G1Projective, +// G1_GENERATOR_X, +// G1_GENERATOR_Y +// ); +// +// #[derive(Debug, Clone, Copy, Decode, Encode)] +// pub struct Affine { +// pub(crate) x: Fr, +// pub(crate) y: Fr, +// is_infinity: bool, +// } +// impl Add for Affine { +// type Output = Projective; +// +// fn add(self, rhs: Affine) -> Self::Output { +// add_affine_point(self, rhs) +// } +// } +// +// impl Neg for Affine { +// type Output = Self; +// +// fn neg(self) -> Self { +// Self { +// x: self.x, +// y: -self.y, +// is_infinity: self.is_infinity, +// } +// } +// } +// +// impl Sub for Affine { +// type Output = Projective; +// +// fn sub(self, rhs: Affine) -> Self::Output { +// add_affine_point(self, rhs.neg()) +// } +// } +// +// impl Mul for Affine { +// type Output = Projective; +// +// fn mul(self, rhs: Fq) -> Self::Output { +// scalar_point(self.to_extended(), &rhs) +// } +// } +// +// impl Mul for Fq { +// type Output = Projective; +// +// fn mul(self, rhs: Affine) -> Self::Output { +// scalar_point(rhs.to_extended(), &self) +// } +// } +// +// #[derive(Debug, Clone, Copy, Decode, Encode)] +// pub struct Projective { +// pub(crate) x: Fr, +// pub(crate) y: Fr, +// pub(crate) z: Fr, +// } +// +// impl Add for Projective { +// type Output = Self; +// +// fn add(self, rhs: Projective) -> Self { +// add_projective_point(self, rhs) +// } +// } +// +// impl Neg for Projective { +// type Output = Self; +// +// fn neg(self) -> Self { +// Self { +// x: self.x, +// y: -self.y, +// z: self.z, +// } +// } +// } +// +// impl Sub for Projective { +// type Output = Self; +// +// fn sub(self, rhs: Projective) -> Self { +// add_projective_point(self, -rhs) +// } +// } +// +// impl Mul for Projective { +// type Output = Projective; +// +// fn mul(self, rhs: Fq) -> Self::Output { +// scalar_point(self, &rhs) +// } +// } +// +// impl Mul for Fq { +// type Output = Projective; +// +// fn mul(self, rhs: Projective) -> Self::Output { +// scalar_point(rhs, &self) +// } +// } +// +// pub const GENERATOR_X: Fr = Fr::one(); +// pub const GENERATOR_Y: Fr = Fr::new_unchecked([ +// 0x11b2dff1448c41d8, +// 0x23d3446f21c77dc3, +// 0xaa7b8cf435dfafbb, +// 0x14b34cf69dc25d68, +// ]); +// pub(crate) const PARAM_B: Fr = Fr::new_unchecked([ +// 0xdd7056026000005a, +// 0x223fa97acb319311, +// 0xcc388229877910c0, +// 0x034394632b724eaa, +// ]); +// pub const PARAM_B3: Fr = PARAM_B.add_const(PARAM_B).add_const(PARAM_B); +// +// weierstrass_curve_operation!( +// Fq, +// Fr, +// PARAM_B, +// PARAM_B3, +// Affine, +// Projective, +// GENERATOR_X, +// GENERATOR_Y +// ); +// +// #[derive(Clone, Debug, Default, PartialEq, Eq)] +// pub struct GrumpkinDriver; +// +// impl CircuitDriver for GrumpkinDriver { +// const NUM_BITS: u16 = 254; +// type Affine = Affine; +// +// type Base = Fr; +// +// type Scalar = Fq; +// +// fn b3() -> Self::Base { +// FR_PARAM_B3 +// } +// } From 5f2f74377d94c0463de9d56a0f233b7227a73e85 Mon Sep 17 00:00:00 2001 From: Kirill Karbushev Date: Mon, 11 Dec 2023 16:39:57 +0900 Subject: [PATCH 04/17] adjust nova types, make instance allocations on provided CS --- nova/src/circuit/augmented.rs | 44 ++++++++++++++--------------- nova/src/circuit/nifs.rs | 14 ++++----- nova/src/circuit/transcript.rs | 39 ++++++++++++------------- nova/src/driver.rs | 15 ++++++++++ nova/src/function.rs | 13 +++++---- nova/src/gadget/mimc.rs | 2 +- nova/src/gadget/relaxed_instance.rs | 38 ++++++++++++++----------- nova/src/hash.rs | 28 +++++++++--------- nova/src/ivc.rs | 4 +-- nova/src/relaxed_r1cs.rs | 8 +++--- nova/src/relaxed_r1cs/instance.rs | 6 ++-- nova/src/test.rs | 21 +++++++------- 12 files changed, 127 insertions(+), 105 deletions(-) diff --git a/nova/src/circuit/augmented.rs b/nova/src/circuit/augmented.rs index aec22d41..62b4eab1 100644 --- a/nova/src/circuit/augmented.rs +++ b/nova/src/circuit/augmented.rs @@ -12,19 +12,19 @@ use zkstd::matrix::DenseVectors; use zkstd::r1cs::R1cs; #[derive(Debug, Clone)] -pub struct AugmentedFCircuit> { +pub struct AugmentedFCircuit> { pub i: usize, - pub z_0: DenseVectors, - pub z_i: DenseVectors, + pub z_0: DenseVectors, + pub z_i: DenseVectors, pub u_single: RelaxedR1csInstance, pub u_range: RelaxedR1csInstance, pub u_range_next: RelaxedR1csInstance, pub commit_t: C::Affine, pub f: PhantomData, - pub x: C::Scalar, + pub x: C::Base, } -impl> Default for AugmentedFCircuit { +impl> Default for AugmentedFCircuit { fn default() -> Self { Self { i: 0, @@ -44,10 +44,10 @@ impl> Default for AugmentedFCircuit> AugmentedFCircuit { - pub(crate) fn generate(&self, cs: &mut R1cs) { +impl> AugmentedFCircuit { + pub(crate) fn generate>(&self, cs: &mut R1cs) { // allocate inputs - let i = FieldAssignment::witness(cs, C::Scalar::from(self.i as u64)); + let i = FieldAssignment::witness(cs, C::Base::from(self.i as u64)); let z_0 = self .z_0 .iter() @@ -59,7 +59,7 @@ impl> AugmentedFCircuit { .map(|x| FieldAssignment::witness(cs, x)) .collect::>(); - let u_dummy_native = RelaxedR1csInstance::dummy(1); + let u_dummy_native = RelaxedR1csInstance::::dummy(1); let u_dummy = RelaxedR1csInstanceAssignment::witness(cs, &u_dummy_native); let u_i = RelaxedR1csInstanceAssignment::witness(cs, &self.u_single); let u_range = RelaxedR1csInstanceAssignment::witness(cs, &self.u_range); @@ -73,7 +73,7 @@ impl> AugmentedFCircuit { let x = FieldAssignment::instance(cs, self.x); let z_next = FC::invoke_cs(cs, z_i.clone()); - let zero = FieldAssignment::constant(&C::Scalar::zero()); + let zero = FieldAssignment::constant(&C::Base::zero()); let bin_true = BinaryAssignment::witness(cs, 1); let base_case = FieldAssignment::is_eq(cs, &i, &zero); @@ -105,7 +105,7 @@ impl> AugmentedFCircuit { FieldAssignment::conditional_enforce_equal( cs, &u_i.u, - &FieldAssignment::constant(&C::Scalar::one()), + &FieldAssignment::constant(&C::Base::one()), ¬_base_case, ); @@ -117,7 +117,7 @@ impl> AugmentedFCircuit { // 4. (base case) u_{i+1}.X == H(1, z_0, F(z_0)=F(z_i)=z_i1, U_i) (with U_i being dummy) let u_next_x_basecase = u_range.hash( cs, - FieldAssignment::constant(&C::Scalar::one()), + FieldAssignment::constant(&C::Base::one()), z_0.clone(), z_next.clone(), ); @@ -125,7 +125,7 @@ impl> AugmentedFCircuit { // 4. (non-base case). u_{i+1}.x = H(i+1, z_0, z_i+1, U_{i+1}) let u_next_x = u_range_next.hash( cs, - &i + &FieldAssignment::constant(&C::Scalar::one()), + &i + &FieldAssignment::constant(&C::Base::one()), z_0, z_next, ); @@ -136,12 +136,12 @@ impl> AugmentedFCircuit { FieldAssignment::conditional_enforce_equal(cs, &u_next_x, &x, ¬_base_case); } - pub(crate) fn get_challenge( - cs: &mut R1cs, + pub(crate) fn get_challenge>( + cs: &mut R1cs, u_range: &RelaxedR1csInstanceAssignment, - commit_t: PointAssignment, - ) -> FieldAssignment { - let mut transcript = MimcROCircuit::::default(); + commit_t: PointAssignment, + ) -> FieldAssignment { + let mut transcript = MimcROCircuit::::default(); transcript.append_point(commit_t); u_range.absorb_by_transcript(&mut transcript); transcript.squeeze(cs) @@ -151,16 +151,16 @@ impl> AugmentedFCircuit { #[cfg(test)] mod tests { use super::*; - use crate::driver::GrumpkinDriver; + use crate::driver::{Bn254Driver, GrumpkinDriver}; use crate::relaxed_r1cs::RelaxedR1csWitness; use crate::test::ExampleFunction; use crate::RelaxedR1cs; + use bn_254::Fr; #[test] fn augmented_circuit_dummies() { - let mut cs = R1cs::::default(); - let augmented_circuit = - AugmentedFCircuit::>::default(); + let mut cs = R1cs::::default(); + let augmented_circuit = AugmentedFCircuit::>::default(); augmented_circuit.generate(&mut cs); assert!(cs.is_sat()); diff --git a/nova/src/circuit/nifs.rs b/nova/src/circuit/nifs.rs index a42b352b..1b377965 100644 --- a/nova/src/circuit/nifs.rs +++ b/nova/src/circuit/nifs.rs @@ -8,9 +8,9 @@ pub(crate) struct NifsCircuit { } impl NifsCircuit { - pub(crate) fn verify( - cs: &mut R1cs, - r: FieldAssignment, + pub(crate) fn verify>( + cs: &mut R1cs, + r: FieldAssignment, instance1: RelaxedR1csInstanceAssignment, instance2: RelaxedR1csInstanceAssignment, instance3: RelaxedR1csInstanceAssignment, @@ -26,7 +26,7 @@ impl NifsCircuit { let r_x2 = FieldAssignment::mul(cs, &r, &x2); x1 + &r_x2 }) - .collect::>>(); + .collect::>>(); let second_check = x.iter() .zip(instance3.x) @@ -41,7 +41,7 @@ impl NifsCircuit { #[cfg(test)] mod tests { use super::*; - use crate::driver::GrumpkinDriver; + use crate::driver::{Bn254Driver, GrumpkinDriver}; use crate::hash::{MimcRO, MIMC_ROUNDS}; use crate::prover::tests::example_prover; use crate::RelaxedR1cs; @@ -51,13 +51,13 @@ mod tests { #[test] fn nifs_circuit() { let prover = example_prover(); - let r1cs = example_r1cs(1); + let r1cs = example_r1cs::(1); let running_r1cs = RelaxedR1cs::new(r1cs); let r1cs_to_fold = RelaxedR1cs::new(example_r1cs(2)); let (instance, witness, commit_t) = prover.prove(&r1cs_to_fold, &running_r1cs); - let mut transcript = MimcRO::::default(); + let mut transcript = MimcRO::::default(); transcript.append_point(commit_t); running_r1cs.absorb_by_transcript(&mut transcript); let t = prover.compute_cross_term(&r1cs_to_fold, &running_r1cs); diff --git a/nova/src/circuit/transcript.rs b/nova/src/circuit/transcript.rs index de17b670..9f124f01 100644 --- a/nova/src/circuit/transcript.rs +++ b/nova/src/circuit/transcript.rs @@ -1,49 +1,49 @@ use crate::gadget::MimcAssignment; use zkstd::circuit::prelude::{CircuitDriver, FieldAssignment, PointAssignment, R1cs}; -use zkstd::common::{IntGroup, PrimeField}; +use zkstd::common::IntGroup; -pub(crate) struct MimcROCircuit { - hasher: MimcAssignment, - state: Vec>, - key: FieldAssignment, +pub(crate) struct MimcROCircuit { + hasher: MimcAssignment, + state: Vec>, + key: FieldAssignment, } -impl Default for MimcROCircuit { +impl Default for MimcROCircuit { fn default() -> Self { Self { hasher: MimcAssignment::default(), state: Vec::default(), - key: FieldAssignment::constant(&F::zero()), + key: FieldAssignment::constant(&C::Base::zero()), } } } -impl MimcROCircuit { - pub(crate) fn append(&mut self, absorb: FieldAssignment) { +impl MimcROCircuit { + pub(crate) fn append(&mut self, absorb: FieldAssignment) { self.state.push(absorb) } - pub(crate) fn hash_vec>( + pub(crate) fn hash_vec>( &mut self, - cs: &mut R1cs, - values: Vec>, - ) -> FieldAssignment { + cs: &mut R1cs, + values: Vec>, + ) -> FieldAssignment { for x in values { self.state.push(x); } self.squeeze(cs) } - pub(crate) fn append_point(&mut self, point: PointAssignment) { + pub(crate) fn append_point(&mut self, point: PointAssignment) { self.append(point.get_x()); self.append(point.get_y()); self.append(point.get_z()); } - pub(crate) fn squeeze>( + pub(crate) fn squeeze>( &self, - cs: &mut R1cs, - ) -> FieldAssignment { + cs: &mut R1cs, + ) -> FieldAssignment { self.state.iter().fold(self.key.clone(), |acc, scalar| { let h = self.hasher.hash(cs, scalar.clone(), acc.clone()); &(&acc + scalar) + &h @@ -57,7 +57,8 @@ mod tests { use crate::hash::{MimcRO, MIMC_ROUNDS}; use crate::driver::{Bn254Driver, GrumpkinDriver}; - use bn_254::{Fq, Fr, G1Affine}; + use bn_254::Fr; + use grumpkin::Affine; use rand_core::OsRng; use zkstd::circuit::prelude::{FieldAssignment, PointAssignment, R1cs}; use zkstd::common::Group; @@ -67,7 +68,7 @@ mod tests { let mut mimc = MimcRO::::default(); let mut mimc_circuit = MimcROCircuit::::default(); // Base = Fr, Scalar = Fq let mut cs: R1cs = R1cs::default(); // Base = Fq, Scalar = Fr - let point = G1Affine::random(OsRng); // Base = Fq, Scalar = Fr + let point = Affine::random(OsRng); // Base = Fr, Scalar = Fq let scalar = Fr::random(OsRng); let point_assignment = PointAssignment::instance(&mut cs, point); diff --git a/nova/src/driver.rs b/nova/src/driver.rs index 90d3eda6..c5863ff1 100644 --- a/nova/src/driver.rs +++ b/nova/src/driver.rs @@ -1,6 +1,7 @@ use bn_254::{params::PARAM_B3 as BN254_PARAM_B3, Fq, Fr, G1Affine}; use grumpkin::{params::PARAM_B3 as GRUMPKIN_PARAM_B3, Affine}; use zkstd::circuit::CircuitDriver; +use zkstd::common::{IntGroup, PrimeField, Ring}; #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct GrumpkinDriver; @@ -34,6 +35,20 @@ impl CircuitDriver for Bn254Driver { } } +/// interpret scalar as base +pub fn scalar_as_base(input: C::Scalar) -> C::Base { + let input_bits = input.to_bits(); + let mut mult = C::Base::one(); + let mut val = C::Base::zero(); + for bit in input_bits.iter().rev() { + if *bit == 1 { + val += mult; + } + mult = mult + mult; + } + val +} + #[cfg(test)] mod grumpkin_gadget_tests { use super::{Fq as Scalar, Fr as Base, GrumpkinDriver}; diff --git a/nova/src/function.rs b/nova/src/function.rs index e757e725..d0741309 100644 --- a/nova/src/function.rs +++ b/nova/src/function.rs @@ -1,13 +1,14 @@ use std::fmt::Debug; use zkstd::circuit::prelude::{CircuitDriver, FieldAssignment}; +use zkstd::common::PrimeField; use zkstd::matrix::DenseVectors; use zkstd::r1cs::R1cs; -pub trait FunctionCircuit: Clone + Debug + Default { - fn invoke(z_i: &DenseVectors) -> DenseVectors; +pub trait FunctionCircuit: Clone + Debug + Default { + fn invoke(z_i: &DenseVectors) -> DenseVectors; - fn invoke_cs( - cs: &mut R1cs, - z_i: Vec>, - ) -> Vec>; + fn invoke_cs>( + cs: &mut R1cs, + z_i: Vec>, + ) -> Vec>; } diff --git a/nova/src/gadget/mimc.rs b/nova/src/gadget/mimc.rs index 7d76f82e..3846411d 100644 --- a/nova/src/gadget/mimc.rs +++ b/nova/src/gadget/mimc.rs @@ -16,7 +16,7 @@ impl Default for MimcAssignment { } impl MimcAssignment { - pub(crate) fn hash>( + pub(crate) fn hash>( &self, cs: &mut R1cs, mut xl: FieldAssignment, diff --git a/nova/src/gadget/relaxed_instance.rs b/nova/src/gadget/relaxed_instance.rs index c9fc7073..2b8acddb 100644 --- a/nova/src/gadget/relaxed_instance.rs +++ b/nova/src/gadget/relaxed_instance.rs @@ -1,21 +1,22 @@ use crate::relaxed_r1cs::RelaxedR1csInstance; use crate::circuit::MimcROCircuit; +use crate::driver::scalar_as_base; use crate::hash::MIMC_ROUNDS; use zkstd::circuit::prelude::{CircuitDriver, FieldAssignment, PointAssignment, R1cs}; use zkstd::common::CurveGroup; #[derive(Clone)] pub(crate) struct RelaxedR1csInstanceAssignment { - pub(crate) commit_w: PointAssignment, - pub(crate) commit_e: PointAssignment, - pub(crate) u: FieldAssignment, - pub(crate) x: Vec>, + pub(crate) commit_w: PointAssignment, + pub(crate) commit_e: PointAssignment, + pub(crate) u: FieldAssignment, + pub(crate) x: Vec>, } impl RelaxedR1csInstanceAssignment { - pub(crate) fn witness( - cs: &mut R1cs, + pub(crate) fn witness>( + cs: &mut R1cs, relaxed_r1cs_instance: &RelaxedR1csInstance, ) -> Self { let RelaxedR1csInstance { @@ -27,8 +28,8 @@ impl RelaxedR1csInstanceAssignment { let commit_w = PointAssignment::witness( cs, - commit_w.get_x().into(), - commit_w.get_y().into(), + commit_w.get_x(), + commit_w.get_y(), commit_w.is_identity(), ); let commit_e = PointAssignment::witness( @@ -37,8 +38,11 @@ impl RelaxedR1csInstanceAssignment { commit_e.get_y().into(), commit_e.is_identity(), ); - let u = FieldAssignment::witness(cs, *u); - let x = x.iter().map(|x| FieldAssignment::witness(cs, x)).collect(); + let u = FieldAssignment::witness(cs, scalar_as_base::(*u)); + let x = x + .iter() + .map(|x| FieldAssignment::witness(cs, scalar_as_base::(x))) + .collect(); Self { commit_w, @@ -50,7 +54,7 @@ impl RelaxedR1csInstanceAssignment { pub(crate) fn absorb_by_transcript( &self, - transcript: &mut MimcROCircuit, + transcript: &mut MimcROCircuit, ) { transcript.append_point(self.commit_w.clone()); transcript.append_point(self.commit_e.clone()); @@ -60,13 +64,13 @@ impl RelaxedR1csInstanceAssignment { } } - pub(crate) fn hash( + pub(crate) fn hash>( &self, - cs: &mut R1cs, - i: FieldAssignment, - z_0: Vec>, - z_i: Vec>, - ) -> FieldAssignment { + cs: &mut R1cs, + i: FieldAssignment, + z_0: Vec>, + z_i: Vec>, + ) -> FieldAssignment { MimcROCircuit::::default().hash_vec( cs, vec![ diff --git a/nova/src/hash.rs b/nova/src/hash.rs index ad9fdda8..1d8af057 100644 --- a/nova/src/hash.rs +++ b/nova/src/hash.rs @@ -2,7 +2,7 @@ mod helper; use helper::BlakeHelper; use zkstd::circuit::CircuitDriver; -use zkstd::common::{BNAffine, CurveGroup, IntGroup, PrimeField, Ring}; +use zkstd::common::{BNAffine, IntGroup, PrimeField, Ring}; /// Amount of rounds calculated for the 254 bit field. /// Doubled due to the usage of Feistel mode with zero key. @@ -41,45 +41,45 @@ impl Mimc { } } -pub(crate) struct MimcRO { - hasher: Mimc, - state: Vec, - key: F, +pub(crate) struct MimcRO { + hasher: Mimc, + state: Vec, + key: C::Base, } -impl Default for MimcRO { +impl Default for MimcRO { fn default() -> Self { Self { hasher: Mimc::default(), state: Vec::default(), - key: F::zero(), + key: C::Base::zero(), } } } -impl MimcRO { - pub(crate) fn append(&mut self, absorb: F) { +impl MimcRO { + pub(crate) fn append(&mut self, absorb: C::Base) { self.state.push(absorb) } - pub(crate) fn append_point(&mut self, point: impl BNAffine) { + pub(crate) fn append_point(&mut self, point: impl BNAffine) { self.append(point.get_x()); self.append(point.get_y()); self.append(if point.is_identity() { - F::zero() + C::Base::zero() } else { - F::one() + C::Base::one() }); } - pub(crate) fn hash_vec(&mut self, values: Vec) -> F { + pub(crate) fn hash_vec(&mut self, values: Vec) -> C::Base { for x in values { self.state.push(x); } self.squeeze() } - pub(crate) fn squeeze(&self) -> F { + pub(crate) fn squeeze(&self) -> C::Base { self.state.iter().fold(self.key, |acc, scalar| { let h = self.hasher.hash(*scalar, acc); acc + scalar + h diff --git a/nova/src/ivc.rs b/nova/src/ivc.rs index 28e01724..64c81302 100644 --- a/nova/src/ivc.rs +++ b/nova/src/ivc.rs @@ -9,7 +9,7 @@ use zkstd::circuit::prelude::{CircuitDriver, R1cs}; use zkstd::common::{Group, RngCore}; use zkstd::matrix::DenseVectors; -pub struct Ivc> { +pub struct Ivc> { i: usize, z0: DenseVectors, zi: DenseVectors, @@ -26,7 +26,7 @@ pub struct Ivc> { f: PhantomData, } -impl> Ivc { +impl> Ivc { pub fn new(rng: impl RngCore, z0: DenseVectors) -> Self { let mut r1cs = R1cs::default(); diff --git a/nova/src/relaxed_r1cs.rs b/nova/src/relaxed_r1cs.rs index a7792581..89139548 100644 --- a/nova/src/relaxed_r1cs.rs +++ b/nova/src/relaxed_r1cs.rs @@ -12,9 +12,9 @@ pub struct RelaxedR1cs { // 1. Structure S // a, b and c matrices and matrix size m: usize, - a: SparseMatrix, - b: SparseMatrix, - c: SparseMatrix, + a: SparseMatrix, + b: SparseMatrix, + c: SparseMatrix, // 2. Instance // r1cs instance includes public inputs, outputs and scalar @@ -139,7 +139,7 @@ impl RelaxedR1cs { pub(crate) fn absorb_by_transcript( &self, - transcript: &mut MimcRO, + transcript: &mut MimcRO, ) { self.instance.absorb_by_transcript(transcript); } diff --git a/nova/src/relaxed_r1cs/instance.rs b/nova/src/relaxed_r1cs/instance.rs index 9f9f0eaf..cb2e3c78 100644 --- a/nova/src/relaxed_r1cs/instance.rs +++ b/nova/src/relaxed_r1cs/instance.rs @@ -60,7 +60,7 @@ impl RelaxedR1csInstance { pub(crate) fn absorb_by_transcript( &self, - transcript: &mut MimcRO, + transcript: &mut MimcRO, ) { transcript.append_point(self.commit_w); transcript.append_point(self.commit_e); @@ -75,10 +75,10 @@ impl RelaxedR1csInstance { i: usize, z_0: &DenseVectors, z_i: &DenseVectors, - ) -> C::Scalar { + ) -> C::Base { let commit_e = self.commit_e.to_extended(); let commit_w = self.commit_w.to_extended(); - MimcRO::::default().hash_vec( + MimcRO::::default().hash_vec( vec![ vec![C::Base::from(i as u64)], z_0.get(), diff --git a/nova/src/test.rs b/nova/src/test.rs index a43c30a9..1c7dd1af 100644 --- a/nova/src/test.rs +++ b/nova/src/test.rs @@ -2,25 +2,26 @@ use crate::function::FunctionCircuit; use core::marker::PhantomData; use zkstd::circuit::prelude::{CircuitDriver, FieldAssignment}; +use zkstd::common::PrimeField; use zkstd::matrix::DenseVectors; use zkstd::r1cs::R1cs; #[derive(Debug, Clone, Default)] -pub(crate) struct ExampleFunction { - mark: PhantomData, +pub(crate) struct ExampleFunction { + mark: PhantomData, } -impl FunctionCircuit for ExampleFunction { - fn invoke(z: &DenseVectors) -> DenseVectors { - let next_z = z[0] * z[0] * z[0] + z[0] + C::Scalar::from(5); +impl FunctionCircuit for ExampleFunction { + fn invoke(z: &DenseVectors) -> DenseVectors { + let next_z = z[0] * z[0] * z[0] + z[0] + F::from(5); DenseVectors::new(vec![next_z]) } - fn invoke_cs( - cs: &mut R1cs, - z_i: Vec>, - ) -> Vec> { - let five = FieldAssignment::constant(&C::Scalar::from(5)); + fn invoke_cs>( + cs: &mut R1cs, + z_i: Vec>, + ) -> Vec> { + let five = FieldAssignment::constant(&F::from(5)); let z_i_square = FieldAssignment::mul(cs, &z_i[0], &z_i[0]); let z_i_cube = FieldAssignment::mul(cs, &z_i_square, &z_i[0]); From e338bcaeca4143b09f1450af2009aa5773e6d9f9 Mon Sep 17 00:00:00 2001 From: Kirill Karbushev Date: Tue, 12 Dec 2023 16:39:19 +0900 Subject: [PATCH 05/17] separate r1cs shape and relaxed instances/witnesses --- nova/src/circuit/augmented.rs | 25 +++--- nova/src/circuit/nifs.rs | 32 +++++-- nova/src/driver.rs | 14 +++ nova/src/gadget/relaxed_instance.rs | 84 +++++++++--------- nova/src/hash.rs | 9 +- nova/src/ivc.rs | 108 ++++++++++++----------- nova/src/lib.rs | 2 +- nova/src/proof.rs | 46 +++++----- nova/src/prover.rs | 97 ++++++++++----------- nova/src/relaxed_r1cs.rs | 127 +++++++++------------------- nova/src/relaxed_r1cs/instance.rs | 35 +++++--- nova/src/relaxed_r1cs/witness.rs | 11 ++- nova/src/verifier.rs | 42 ++++++--- 13 files changed, 336 insertions(+), 296 deletions(-) diff --git a/nova/src/circuit/augmented.rs b/nova/src/circuit/augmented.rs index 62b4eab1..f654d399 100644 --- a/nova/src/circuit/augmented.rs +++ b/nova/src/circuit/augmented.rs @@ -35,11 +35,18 @@ impl> Default for AugmentedFCircu u_range_next: RelaxedR1csInstance::dummy(1), commit_t: C::Affine::ADDITIVE_IDENTITY, f: Default::default(), - x: RelaxedR1csInstance::::dummy(1).hash( - 1, - &DenseVectors::zero(1), - &FC::invoke(&DenseVectors::zero(1)), - ), + x: C::Base::zero(), // x: RelaxedR1csInstance::::dummy(1) + // .hash( + // 1, + // &DenseVectors::zero(1), + // &DenseVectors::new( + // FC::invoke(&DenseVectors::zero(1)) + // .iter() + // .map(|x| base_as_scalar(x)) + // .collect(), + // ), + // ) + // .into(), } } } @@ -152,9 +159,8 @@ impl> AugmentedFCircuit { mod tests { use super::*; use crate::driver::{Bn254Driver, GrumpkinDriver}; - use crate::relaxed_r1cs::RelaxedR1csWitness; + use crate::relaxed_r1cs::{R1csShape, RelaxedR1csWitness}; use crate::test::ExampleFunction; - use crate::RelaxedR1cs; use bn_254::Fr; #[test] @@ -170,8 +176,7 @@ mod tests { let u_dummy = RelaxedR1csInstance::dummy(cs.l() - 1); let w_dummy = RelaxedR1csWitness::dummy(cs.m_l_1(), cs.m()); - let mut running_r1cs = RelaxedR1cs::new(cs.clone()); - running_r1cs = running_r1cs.update(&u_dummy, &w_dummy); - assert!(running_r1cs.is_sat()); + let mut running_r1cs = R1csShape::from(cs); + assert!(running_r1cs.is_sat(&u_dummy, &w_dummy)); } } diff --git a/nova/src/circuit/nifs.rs b/nova/src/circuit/nifs.rs index 1b377965..b219443a 100644 --- a/nova/src/circuit/nifs.rs +++ b/nova/src/circuit/nifs.rs @@ -44,29 +44,43 @@ mod tests { use crate::driver::{Bn254Driver, GrumpkinDriver}; use crate::hash::{MimcRO, MIMC_ROUNDS}; use crate::prover::tests::example_prover; - use crate::RelaxedR1cs; - use bn_254::{Fq, Fr}; + use crate::relaxed_r1cs::{RelaxedR1csInstance, RelaxedR1csWitness}; + use bn_254::Fq; + use zkstd::matrix::DenseVectors; use zkstd::r1cs::test::example_r1cs; #[test] fn nifs_circuit() { let prover = example_prover(); let r1cs = example_r1cs::(1); - let running_r1cs = RelaxedR1cs::new(r1cs); + let running_instance = RelaxedR1csInstance::new(DenseVectors::new(r1cs.x())); + let running_witness = RelaxedR1csWitness::new(DenseVectors::new(r1cs.w()), r1cs.m()); - let r1cs_to_fold = RelaxedR1cs::new(example_r1cs(2)); - let (instance, witness, commit_t) = prover.prove(&r1cs_to_fold, &running_r1cs); + let r1cs_2 = example_r1cs::(2); + let instance_to_fold = RelaxedR1csInstance::new(DenseVectors::new(r1cs.x())); + let witness_to_fold = RelaxedR1csWitness::new(DenseVectors::new(r1cs.w()), r1cs.m()); + let (instance, witness, commit_t) = prover.prove( + &instance_to_fold, + &witness_to_fold, + &running_instance, + &running_witness, + ); let mut transcript = MimcRO::::default(); transcript.append_point(commit_t); - running_r1cs.absorb_by_transcript(&mut transcript); - let t = prover.compute_cross_term(&r1cs_to_fold, &running_r1cs); + running_instance.absorb_by_transcript(&mut transcript); + let t = prover.compute_cross_term( + &instance_to_fold, + &witness_to_fold, + &running_instance, + &running_witness, + ); let r = transcript.squeeze(); let mut cs = R1cs::::default(); let r = FieldAssignment::witness(&mut cs, r.into()); - let instance1 = RelaxedR1csInstanceAssignment::witness(&mut cs, &r1cs_to_fold.instance); - let instance2 = RelaxedR1csInstanceAssignment::witness(&mut cs, &running_r1cs.instance); + let instance1 = RelaxedR1csInstanceAssignment::witness(&mut cs, &instance_to_fold); + let instance2 = RelaxedR1csInstanceAssignment::witness(&mut cs, &running_instance); let instance3 = RelaxedR1csInstanceAssignment::witness(&mut cs, &instance); let nifs_check = NifsCircuit::verify(&mut cs, r, instance1, instance2, instance3); diff --git a/nova/src/driver.rs b/nova/src/driver.rs index c5863ff1..1d6757a9 100644 --- a/nova/src/driver.rs +++ b/nova/src/driver.rs @@ -49,6 +49,20 @@ pub fn scalar_as_base(input: C::Scalar) -> C::Base { val } +/// interpret base as scalar +pub fn base_as_scalar(input: C::Base) -> C::Scalar { + let input_bits = input.to_bits(); + let mut mult = C::Scalar::one(); + let mut val = C::Scalar::zero(); + for bit in input_bits.iter().rev() { + if *bit == 1 { + val += mult; + } + mult = mult + mult; + } + val +} + #[cfg(test)] mod grumpkin_gadget_tests { use super::{Fq as Scalar, Fr as Base, GrumpkinDriver}; diff --git a/nova/src/gadget/relaxed_instance.rs b/nova/src/gadget/relaxed_instance.rs index 2b8acddb..cddfc8f5 100644 --- a/nova/src/gadget/relaxed_instance.rs +++ b/nova/src/gadget/relaxed_instance.rs @@ -98,51 +98,51 @@ impl RelaxedR1csInstanceAssignment { #[cfg(test)] mod tests { use super::*; - use crate::driver::GrumpkinDriver; + use crate::driver::{Bn254Driver, GrumpkinDriver}; use bn_254::Fq; use grumpkin::Affine; use rand_core::OsRng; use zkstd::common::Group; use zkstd::matrix::DenseVectors; - #[test] - fn instance_assignment_hash() { - let mut cs: R1cs = R1cs::default(); - let instance = RelaxedR1csInstance { - commit_e: Affine::random(OsRng), - u: Fq::random(OsRng), - commit_w: Affine::random(OsRng), - x: DenseVectors::new(vec![Fq::random(OsRng); 1]), - }; - - let i = 3; - let z_0 = DenseVectors::new(vec![Fq::from(3)]); - let z_i = z_0.clone(); - - let hash = instance.hash(i, &z_0, &z_i); - - let i_assignment = FieldAssignment::witness(&mut cs, Fq::from(i as u64)); - let z_0_assignment = z_0 - .iter() - .map(|x| FieldAssignment::witness(&mut cs, x)) - .collect::>(); - let z_i_assignment = z_i - .iter() - .map(|x| FieldAssignment::witness(&mut cs, x)) - .collect::>(); - let instance_assignment = RelaxedR1csInstanceAssignment::witness(&mut cs, &instance); - - let hash_circuit = - instance_assignment.hash(&mut cs, i_assignment, z_0_assignment, z_i_assignment); - - FieldAssignment::enforce_eq_constant(&mut cs, &hash_circuit, &hash); - assert!(cs.is_sat()); - } + // #[test] + // fn instance_assignment_hash() { + // let mut cs: R1cs = R1cs::default(); + // let instance = RelaxedR1csInstance:: { + // commit_e: Affine::random(OsRng), + // u: Fq::random(OsRng), + // commit_w: Affine::random(OsRng), + // x: DenseVectors::new(vec![Fq::random(OsRng); 1]), + // }; + // + // let i = 3; + // let z_0 = DenseVectors::new(vec![Fr::from(3)]); + // let z_i = z_0.clone(); + // + // let hash = instance.hash::(i, &z_0, &z_i); + // + // let i_assignment = FieldAssignment::witness(&mut cs, Fr::from(i as u64)); + // let z_0_assignment = z_0 + // .iter() + // .map(|x| FieldAssignment::witness(&mut cs, x)) + // .collect::>(); + // let z_i_assignment = z_i + // .iter() + // .map(|x| FieldAssignment::witness(&mut cs, x)) + // .collect::>(); + // let instance_assignment = RelaxedR1csInstanceAssignment::witness(&mut cs, &instance); + // + // let hash_circuit = + // instance_assignment.hash(&mut cs, i_assignment, z_0_assignment, z_i_assignment); + // + // FieldAssignment::enforce_eq_constant(&mut cs, &hash_circuit, &hash); + // assert!(cs.is_sat()); + // } #[test] fn relaxed_instance_assignment() { - let mut cs: R1cs = R1cs::default(); - let instance = RelaxedR1csInstance { + let mut cs: R1cs = R1cs::default(); + let instance = RelaxedR1csInstance:: { commit_e: Affine::random(OsRng), u: Fq::random(OsRng), commit_w: Affine::random(OsRng), @@ -150,9 +150,17 @@ mod tests { }; let instance_assignment = RelaxedR1csInstanceAssignment::witness(&mut cs, &instance); - FieldAssignment::enforce_eq_constant(&mut cs, &instance_assignment.u, &instance.u); + FieldAssignment::enforce_eq_constant( + &mut cs, + &instance_assignment.u, + &scalar_as_base::(instance.u), + ); // TODO: Think how to restrict size to 1 - FieldAssignment::enforce_eq_constant(&mut cs, &instance_assignment.x[0], &instance.x[0]); + FieldAssignment::enforce_eq_constant( + &mut cs, + &instance_assignment.x[0], + &scalar_as_base::(instance.x[0]), + ); // Research about curve cycles diff --git a/nova/src/hash.rs b/nova/src/hash.rs index 1d8af057..cbaf98fd 100644 --- a/nova/src/hash.rs +++ b/nova/src/hash.rs @@ -1,5 +1,6 @@ mod helper; +use crate::driver::base_as_scalar; use helper::BlakeHelper; use zkstd::circuit::CircuitDriver; use zkstd::common::{BNAffine, IntGroup, PrimeField, Ring}; @@ -72,17 +73,17 @@ impl MimcRO { }); } - pub(crate) fn hash_vec(&mut self, values: Vec) -> C::Base { + pub(crate) fn hash_vec(&mut self, values: Vec) -> C::Scalar { for x in values { self.state.push(x); } self.squeeze() } - pub(crate) fn squeeze(&self) -> C::Base { - self.state.iter().fold(self.key, |acc, scalar| { + pub(crate) fn squeeze(&self) -> C::Scalar { + base_as_scalar::(self.state.iter().fold(self.key, |acc, scalar| { let h = self.hasher.hash(*scalar, acc); acc + scalar + h - }) + })) } } diff --git a/nova/src/ivc.rs b/nova/src/ivc.rs index 64c81302..a7932f41 100644 --- a/nova/src/ivc.rs +++ b/nova/src/ivc.rs @@ -1,93 +1,104 @@ use crate::function::FunctionCircuit; use crate::proof::RecursiveProof; -use crate::{Prover, RelaxedR1cs}; +use crate::Prover; use std::marker::PhantomData; use crate::circuit::AugmentedFCircuit; -use crate::relaxed_r1cs::{RelaxedR1csInstance, RelaxedR1csWitness}; +use crate::relaxed_r1cs::{R1csShape, RelaxedR1csInstance, RelaxedR1csWitness}; use zkstd::circuit::prelude::{CircuitDriver, R1cs}; use zkstd::common::{Group, RngCore}; use zkstd::matrix::DenseVectors; -pub struct Ivc> { +pub struct Ivc +where + E1: CircuitDriver::Scalar>, + E2: CircuitDriver::Scalar>, + FC: FunctionCircuit, +{ i: usize, - z0: DenseVectors, - zi: DenseVectors, - prover: Prover, - r1cs: R1cs, + z0: DenseVectors, + zi: DenseVectors, + prover: Prover, + r1cs: R1csShape, // r1cs instance to be folded // u_i // represents the correct execution of invocation i of F′ - u_single: RelaxedR1cs, + u_single: RelaxedR1csInstance, + w_single: RelaxedR1csWitness, // running r1cs instance // U_i // represents the correct execution of invocations 1, . . . , i - 1 of F′ - u_range: RelaxedR1cs, - f: PhantomData, + u_range: RelaxedR1csInstance, + w_range: RelaxedR1csWitness, + f: PhantomData<(FC, E2)>, } -impl> Ivc { - pub fn new(rng: impl RngCore, z0: DenseVectors) -> Self { - let mut r1cs = R1cs::default(); +impl Ivc +where + E1: CircuitDriver::Scalar>, + E2: CircuitDriver::Scalar>, + FC: FunctionCircuit, +{ + pub fn new(rng: impl RngCore, z0: DenseVectors) -> Self { + let mut r1cs = R1cs::::default(); - let augmented_circuit = AugmentedFCircuit::::default(); + let augmented_circuit = AugmentedFCircuit::::default(); augmented_circuit.generate(&mut r1cs); - let prover = Prover::new(r1cs.clone(), rng); - let mut relaxed_r1cs = RelaxedR1cs::new(r1cs.clone()); - let u_dummy = RelaxedR1csInstance::::dummy(r1cs.l() - 1); - let w_dummy = RelaxedR1csWitness::::dummy(r1cs.m_l_1(), r1cs.m()); - relaxed_r1cs = relaxed_r1cs.update(&u_dummy, &w_dummy); + let prover = Prover::new(R1csShape::from(r1cs.clone()), rng); + let u_dummy = RelaxedR1csInstance::::dummy(r1cs.l() - 1); + let w_dummy = RelaxedR1csWitness::::dummy(r1cs.m_l_1(), r1cs.m()); Self { i: 0, z0: z0.clone(), zi: z0, prover, - r1cs, - u_single: relaxed_r1cs.clone(), - u_range: relaxed_r1cs, + r1cs: R1csShape::from(r1cs), + u_single: u_dummy.clone(), + w_single: w_dummy.clone(), + u_range: u_dummy, + w_range: w_dummy, f: PhantomData::default(), } } - pub fn prove_step(&mut self) -> RecursiveProof { + pub fn prove_step(&mut self) -> RecursiveProof { let z_next = FC::invoke(&self.zi); let (u_range_next, w_range_next, u_single_next_x, commit_t) = if self.i == 0 { - let u_single_next_x = self.u_range.instance.hash(1, &self.z0, &z_next); + let u_single_next_x = self.u_range.hash(1, &self.z0, &z_next); let (u_range_next, w_range_next, commit_t) = ( - RelaxedR1csInstance::::dummy(1), - RelaxedR1csWitness::::dummy(self.r1cs.w().len(), self.r1cs.m()), - C::Affine::ADDITIVE_IDENTITY, + RelaxedR1csInstance::::dummy(1), + RelaxedR1csWitness::::dummy(self.r1cs.l(), self.r1cs.m()), + E1::Affine::ADDITIVE_IDENTITY, ); (u_range_next, w_range_next, u_single_next_x, commit_t) } else { let (u_range_next, w_range_next, commit_t) = - self.prover.prove(&self.u_single, &self.u_range); + self.prover + .prove(&self.u_single, &self.w_single, &self.u_range, &self.w_range); - let new_instance = RelaxedR1cs::new(self.r1cs.clone()); - new_instance.update(&u_range_next, &w_range_next); - assert!(new_instance.is_sat()); + assert!(self.r1cs.is_sat(&u_range_next, &w_range_next)); let u_single_next_x = u_range_next.hash(self.i + 1, &self.z0, &z_next); (u_range_next, w_range_next, u_single_next_x, commit_t) }; - let augmented_circuit = AugmentedFCircuit { + let augmented_circuit = AugmentedFCircuit:: { i: self.i, z_0: self.z0.clone(), z_i: self.zi.clone(), - u_single: self.u_single.instance.clone(), - u_range: self.u_range.instance.clone(), + u_single: self.u_single.clone(), + u_range: self.u_range.clone(), u_range_next: u_range_next.clone(), commit_t, f: self.f, x: u_single_next_x, }; - let mut cs = R1cs::::default(); + let mut cs = R1cs::::default(); augmented_circuit.generate(&mut cs); let (u_single_next, w_single_next) = ( @@ -98,18 +109,17 @@ impl> Ivc { assert_eq!(u_single_next.x.len(), 1); assert_eq!(u_single_next.x[0], u_single_next_x); - self.u_single = self.u_single.update(&u_single_next, &w_single_next); - self.u_range = self.u_range.update(&u_range_next, &w_range_next); + self.u_single = u_single_next; + self.w_single = w_single_next; + self.u_range = u_range_next; + self.w_range = w_range_next; self.i += 1; self.zi = z_next; // ((Ui+1, Wi+1), (ui+1, wi+1)) let pair = ( - (self.u_range.instance.clone(), self.u_range.witness.clone()), - ( - self.u_single.instance.clone(), - self.u_single.witness.clone(), - ), + (self.u_range.clone(), self.w_range.clone()), + (self.u_single.clone(), self.w_single.clone()), ); RecursiveProof { @@ -118,6 +128,7 @@ impl> Ivc { zi: self.zi.clone(), r1cs: self.r1cs.clone(), pair, + marker: Default::default(), } } } @@ -127,9 +138,9 @@ mod tests { use super::Ivc; use crate::test::ExampleFunction; - use crate::driver::GrumpkinDriver; + use crate::driver::{Bn254Driver, GrumpkinDriver}; use crate::RecursiveProof; - use bn_254::Fq; + use bn_254::Fr; use rand_core::OsRng; use zkstd::circuit::prelude::R1cs; use zkstd::matrix::DenseVectors; @@ -138,17 +149,18 @@ mod tests { #[test] fn ivc_test() { let r1cs: R1cs = example_r1cs(1); - let z0 = DenseVectors::new(vec![Fq::from(3)]); - let mut ivc = Ivc::>::new(OsRng, z0); + let z0 = DenseVectors::new(vec![Fr::from(3)]); + let mut ivc = Ivc::>::new(OsRng, z0); let proof_0 = RecursiveProof { i: 0, z0: ivc.z0.clone(), zi: ivc.zi.clone(), r1cs: ivc.r1cs.clone(), pair: ( - (ivc.u_range.instance.clone(), ivc.u_range.witness.clone()), - (ivc.u_single.instance.clone(), ivc.u_single.witness.clone()), + (ivc.u_range.clone(), ivc.w_range.clone()), + (ivc.u_single.clone(), ivc.w_single.clone()), ), + marker: Default::default(), }; assert!(proof_0.verify()); diff --git a/nova/src/lib.rs b/nova/src/lib.rs index 41f93e78..886c2066 100644 --- a/nova/src/lib.rs +++ b/nova/src/lib.rs @@ -20,5 +20,5 @@ pub use ivc::Ivc; pub use pedersen::PedersenCommitment; pub use proof::RecursiveProof; pub use prover::Prover; -pub use relaxed_r1cs::RelaxedR1cs; +pub use relaxed_r1cs::R1csShape; pub use verifier::Verifier; diff --git a/nova/src/proof.rs b/nova/src/proof.rs index 2766680d..1fd46c95 100644 --- a/nova/src/proof.rs +++ b/nova/src/proof.rs @@ -1,27 +1,35 @@ -use crate::{ - relaxed_r1cs::{RelaxedR1csInstance, RelaxedR1csWitness}, - RelaxedR1cs, -}; +use crate::relaxed_r1cs::{RelaxedR1csInstance, RelaxedR1csWitness}; +use std::marker::PhantomData; -use zkstd::circuit::prelude::{CircuitDriver, R1cs}; +use crate::relaxed_r1cs::R1csShape; +use zkstd::circuit::prelude::CircuitDriver; use zkstd::common::{Group, Ring}; use zkstd::matrix::DenseVectors; #[allow(clippy::type_complexity)] -pub struct RecursiveProof { +pub struct RecursiveProof +where + E1: CircuitDriver::Scalar>, + E2: CircuitDriver::Scalar>, +{ pub(crate) i: usize, - pub(crate) z0: DenseVectors, - pub(crate) zi: DenseVectors, - pub(crate) r1cs: R1cs, + pub(crate) z0: DenseVectors, + pub(crate) zi: DenseVectors, + pub(crate) r1cs: R1csShape, pub(crate) pair: ( // instance-witness pair of instance to be folded - (RelaxedR1csInstance, RelaxedR1csWitness), + (RelaxedR1csInstance, RelaxedR1csWitness), // instance-witness pair of running instance - (RelaxedR1csInstance, RelaxedR1csWitness), + (RelaxedR1csInstance, RelaxedR1csWitness), ), + pub(crate) marker: PhantomData, } -impl RecursiveProof { +impl RecursiveProof +where + E1: CircuitDriver::Scalar>, + E2: CircuitDriver::Scalar>, +{ pub fn verify(&self) -> bool { let Self { i, @@ -29,6 +37,7 @@ impl RecursiveProof { zi, r1cs, pair, + .. } = self; let ((l_ui, l_wi), (s_ui, s_wi)) = pair; @@ -37,18 +46,17 @@ impl RecursiveProof { z0 == zi } else { // check that ui.x = hash(vk, i, z0, zi, Ui) - let expected_x = l_ui.hash(*i, z0, zi); - let check_hash = expected_x == s_ui.x[0]; + let expected_x = l_ui.hash::(*i, z0, zi); + let check_hash = expected_x == s_ui.x[0].into(); // check if folded instance has default error vectors and scalar let check_defaults = - s_ui.commit_e == C::Affine::ADDITIVE_IDENTITY && s_ui.u == C::Scalar::one(); + s_ui.commit_e == E1::Affine::ADDITIVE_IDENTITY && s_ui.u == E1::Scalar::one(); // check if instance-witness pair satisfy - let relaxed_r1cs = RelaxedR1cs::new(r1cs.clone()); - let l_relaxed_r1cs = relaxed_r1cs.update(l_ui, l_wi); - let s_relaxed_r1cs = relaxed_r1cs.update(s_ui, s_wi); - let is_instance_witness_sat = l_relaxed_r1cs.is_sat() && s_relaxed_r1cs.is_sat(); + + let is_instance_witness_sat = + self.r1cs.is_sat(&l_ui, &l_wi) && self.r1cs.is_sat(&s_ui, &s_wi); check_hash && check_defaults && is_instance_witness_sat } diff --git a/nova/src/prover.rs b/nova/src/prover.rs index a53c6c8c..1624cbc5 100644 --- a/nova/src/prover.rs +++ b/nova/src/prover.rs @@ -1,10 +1,11 @@ use crate::{ pedersen::PedersenCommitment, - relaxed_r1cs::{RelaxedR1cs, RelaxedR1csInstance, RelaxedR1csWitness}, + relaxed_r1cs::{RelaxedR1csInstance, RelaxedR1csWitness}, }; use crate::hash::{MimcRO, MIMC_ROUNDS}; -use zkstd::circuit::prelude::{CircuitDriver, R1cs}; +use crate::relaxed_r1cs::R1csShape; +use zkstd::circuit::prelude::CircuitDriver; use zkstd::common::{Ring, RngCore}; use zkstd::matrix::DenseVectors; @@ -13,11 +14,11 @@ pub struct Prover { pub(crate) pp: PedersenCommitment, // r1cs structure - f: R1cs, + f: R1csShape, } impl Prover { - pub fn new(f: R1cs, rng: impl RngCore) -> Self { + pub fn new(f: R1csShape, rng: impl RngCore) -> Self { let m = f.m(); let n = m.next_power_of_two() as u64; let k = n.trailing_zeros(); @@ -29,25 +30,27 @@ impl Prover { pub fn prove( &self, - r1cs_1: &RelaxedR1cs, - r1cs_2: &RelaxedR1cs, + instance1: &RelaxedR1csInstance, + witness1: &RelaxedR1csWitness, + instance2: &RelaxedR1csInstance, + witness2: &RelaxedR1csWitness, ) -> (RelaxedR1csInstance, RelaxedR1csWitness, C::Affine) { let mut transcript = MimcRO::::default(); // compute cross term t - let t = self.compute_cross_term(r1cs_1, r1cs_2); + let t = self.compute_cross_term(instance1, witness1, instance2, witness2); let commit_t = self.pp.commit(&t); transcript.append_point(commit_t); - r1cs_2.absorb_by_transcript(&mut transcript); + instance2.absorb_by_transcript(&mut transcript); let r = transcript.squeeze(); // fold instance - let instance = r1cs_2.fold_instance(r1cs_1, r, commit_t); + let instance = instance2.fold(instance1, r, commit_t); // fold witness - let witness = r1cs_2.fold_witness(r1cs_1, r, t); + let witness = witness2.fold(witness1, r, t); // return folded relaxed r1cs instance, witness and commit T (instance, witness, commit_t) @@ -56,20 +59,22 @@ impl Prover { // T = AZ1 ◦ BZ2 + AZ2 ◦ BZ1 − u1 · CZ2 − u2 · CZ1 pub(crate) fn compute_cross_term( &self, - r1cs_1: &RelaxedR1cs, - r1cs_2: &RelaxedR1cs, + instance1: &RelaxedR1csInstance, + witness1: &RelaxedR1csWitness, + instance2: &RelaxedR1csInstance, + witness2: &RelaxedR1csWitness, ) -> DenseVectors { let u1 = C::Scalar::one(); - let u2 = r1cs_2.u(); + let u2 = instance2.u(); let m = self.f.m(); let (a, b, c) = self.f.matrices(); - let (w0, w1) = (r1cs_1.w(), r1cs_2.w()); - let (x0, x1) = (r1cs_1.x(), r1cs_2.x()); + let (w1, w2) = (witness1.w(), witness2.w()); + let (x1, x2) = (instance1.x(), instance2.x()); - let z1 = DenseVectors::new(vec![vec![u1], x0.get(), w0.get()].concat()); - let l1 = x0.len() + 1; - let z2 = DenseVectors::new(vec![vec![u2], x1.get(), w1.get()].concat()); - let l2 = x1.len() + 1; + let z1 = DenseVectors::new(vec![vec![u1], x1.get(), w1.get()].concat()); + let l1 = x1.len() + 1; + let z2 = DenseVectors::new(vec![vec![u2], x2.get(), w2.get()].concat()); + let l2 = x2.len() + 1; // matrices and z vector matrix multiplication let az2 = a.prod(&m, l2, &z2); @@ -94,19 +99,21 @@ impl Prover { #[cfg(test)] pub(crate) mod tests { - use super::{Prover, RelaxedR1cs}; + use super::Prover; use bn_254::Fq; use zkstd::circuit::CircuitDriver; use crate::driver::GrumpkinDriver; use crate::hash::{MimcRO, MIMC_ROUNDS}; + use crate::relaxed_r1cs::{R1csShape, RelaxedR1csInstance, RelaxedR1csWitness}; use crate::Verifier; use zkstd::common::OsRng; + use zkstd::matrix::DenseVectors; use zkstd::r1cs::test::example_r1cs; pub(crate) fn example_prover() -> Prover { let r1cs = example_r1cs(0); - Prover::new(r1cs, OsRng) + Prover::new(R1csShape::from(r1cs), OsRng) } #[test] @@ -114,41 +121,34 @@ pub(crate) mod tests { let prover = example_prover(); let mut transcript = MimcRO::::default(); - let r1cs_1 = example_r1cs(4); - let r1cs_2 = example_r1cs(3); + let r1cs_1 = example_r1cs::(4); + let r1cs_2 = example_r1cs::(3); - let relaxed_r1cs_1 = RelaxedR1cs::new(r1cs_1); - let mut relaxed_r1cs_2 = RelaxedR1cs::new(r1cs_2); + let instance1 = RelaxedR1csInstance::new(DenseVectors::new(r1cs_1.x())); + let witness1 = RelaxedR1csWitness::new(DenseVectors::new(r1cs_1.w()), r1cs_1.m()); + let instance2 = RelaxedR1csInstance::new(DenseVectors::new(r1cs_2.x())); + let witness2 = RelaxedR1csWitness::new(DenseVectors::new(r1cs_2.w()), r1cs_2.m()); - let (folded_instance, witness, commit_t) = prover.prove(&relaxed_r1cs_1, &relaxed_r1cs_2); - let verified_instance = Verifier::verify(commit_t, &relaxed_r1cs_1, &relaxed_r1cs_2); + let (folded_instance, folded_witness, commit_t) = + prover.prove(&instance1, &witness1, &instance2, &witness2); + let verified_instance = Verifier::verify(commit_t, &instance1, &instance2); assert_eq!(folded_instance, verified_instance); transcript.append_point(commit_t); - relaxed_r1cs_2.absorb_by_transcript(&mut transcript); - let t = prover.compute_cross_term(&relaxed_r1cs_1, &relaxed_r1cs_2); + instance2.absorb_by_transcript(&mut transcript); + let t = prover.compute_cross_term(&instance1, &witness1, &instance2, &witness2); let r = transcript.squeeze(); // naive check that the folded witness satisfies the relaxed r1cs let z3: Vec = [ vec![verified_instance.u], verified_instance.x.get(), - witness.w.get(), + folded_witness.w.get(), ] .concat(); - let z1 = [ - vec![Fq::one()], - relaxed_r1cs_1.x().get(), - relaxed_r1cs_1.w().get(), - ] - .concat(); - let z2 = [ - vec![relaxed_r1cs_2.instance.u], - relaxed_r1cs_2.x().get(), - relaxed_r1cs_2.w().get(), - ] - .concat(); + let z1 = [vec![Fq::one()], instance1.x().get(), witness1.w().get()].concat(); + let z2 = [vec![instance2.u], instance2.x().get(), witness2.w().get()].concat(); let z3_aux: Vec = z2 .iter() @@ -160,17 +160,14 @@ pub(crate) mod tests { assert_eq!(z3, z3_aux); // check that relations hold for the 2 inputted instances and the folded one - let instance1 = relaxed_r1cs_1.instance.clone(); - let instance2 = relaxed_r1cs_2.instance.clone(); - assert!(relaxed_r1cs_2.is_sat()); - relaxed_r1cs_2 = relaxed_r1cs_2.update(&relaxed_r1cs_1.instance, &relaxed_r1cs_1.witness); - assert!(relaxed_r1cs_2.is_sat()); - relaxed_r1cs_2 = relaxed_r1cs_2.update(&folded_instance, &witness); - assert!(relaxed_r1cs_2.is_sat()); + let r1cs = R1csShape::from(r1cs_1); + assert!(r1cs.is_sat(&instance1, &witness1)); + assert!(r1cs.is_sat(&instance2, &witness2)); + assert!(r1cs.is_sat(&folded_instance, &folded_witness)); // next equalities should hold since we started from two cmE of zero-vector E's assert_eq!(verified_instance.commit_e, (commit_t * r).into()); - assert_eq!(witness.e, t * r); + assert_eq!(folded_witness.e, t * r); let r2 = r * r; assert!( diff --git a/nova/src/relaxed_r1cs.rs b/nova/src/relaxed_r1cs.rs index 89139548..67c47522 100644 --- a/nova/src/relaxed_r1cs.rs +++ b/nova/src/relaxed_r1cs.rs @@ -1,113 +1,68 @@ mod instance; mod witness; -use crate::hash::MimcRO; pub(crate) use instance::RelaxedR1csInstance; pub(crate) use witness::RelaxedR1csWitness; use zkstd::circuit::prelude::{CircuitDriver, R1cs}; use zkstd::matrix::{DenseVectors, SparseMatrix}; #[derive(Clone, Debug)] -pub struct RelaxedR1cs { +pub struct R1csShape { // 1. Structure S // a, b and c matrices and matrix size m: usize, - a: SparseMatrix, - b: SparseMatrix, - c: SparseMatrix, - - // 2. Instance - // r1cs instance includes public inputs, outputs and scalar - pub(crate) instance: RelaxedR1csInstance, - - // 3. Witness - // r1cs witness includes private inputs, intermediate value and error vector - pub(crate) witness: RelaxedR1csWitness, + instance_length: usize, + witness_length: usize, + a: SparseMatrix, + b: SparseMatrix, + c: SparseMatrix, } -impl RelaxedR1cs { - pub fn new(r1cs: R1cs) -> Self { - let m = r1cs.m(); - let (a, b, c) = r1cs.matrices(); - let x = DenseVectors::new(r1cs.x()); - let w = DenseVectors::new(r1cs.w()); - - let instance = RelaxedR1csInstance::new(x); - let witness = RelaxedR1csWitness::new(w, m); - +impl From> for R1csShape { + fn from(value: R1cs) -> Self { + let (a, b, c) = value.matrices(); Self { - m, + m: value.m(), + instance_length: value.l(), + witness_length: value.m_l_1(), a, b, c, - instance, - witness, } } +} - pub(crate) fn u(&self) -> C::Scalar { - self.instance.u - } - - pub(crate) fn x(&self) -> DenseVectors { - self.instance.x.clone() +impl R1csShape { + #[allow(clippy::type_complexity)] + pub fn matrices( + &self, + ) -> ( + SparseMatrix, + SparseMatrix, + SparseMatrix, + ) { + (self.a.clone(), self.b.clone(), self.c.clone()) } - pub(crate) fn w(&self) -> DenseVectors { - self.witness.w.clone() + pub fn m(&self) -> usize { + self.m } - pub(crate) fn fold_instance( - &self, - r1cs: &RelaxedR1cs, - r: C::Scalar, - commit_t: C::Affine, - ) -> RelaxedR1csInstance { - self.instance.fold(r1cs, r, commit_t) + pub fn l(&self) -> usize { + self.instance_length } - pub(crate) fn fold_witness( - &self, - r1cs: &RelaxedR1cs, - r: C::Scalar, - t: DenseVectors, - ) -> RelaxedR1csWitness { - self.witness.fold(r1cs, r, t) + pub fn m_l_1(&self) -> usize { + self.witness_length } - pub(crate) fn update( + /// check (A · Z) ◦ (B · Z) = u · (C · Z) + E + pub fn is_sat( &self, instance: &RelaxedR1csInstance, witness: &RelaxedR1csWitness, - ) -> Self { - let RelaxedR1cs { - m, - a, - b, - c, - instance: _, - witness: _, - } = self.clone(); - Self { - m, - a, - b, - c, - instance: instance.clone(), - witness: witness.clone(), - } - } - - /// check (A · Z) ◦ (B · Z) = u · (C · Z) + E - pub fn is_sat(&self) -> bool { - let Self { - m, - a, - b, - c, - instance, - witness, - } = self; + ) -> bool { + let Self { m, a, b, c, .. } = self; let RelaxedR1csInstance { commit_w: _, @@ -136,29 +91,25 @@ impl RelaxedR1cs { .zip(ucze.iter()) .all(|(left, right)| left == right) } - - pub(crate) fn absorb_by_transcript( - &self, - transcript: &mut MimcRO, - ) { - self.instance.absorb_by_transcript(transcript); - } } #[cfg(test)] mod tests { - use super::RelaxedR1cs; + use super::{R1csShape, RelaxedR1csInstance, RelaxedR1csWitness}; use crate::driver::GrumpkinDriver; use zkstd::circuit::prelude::R1cs; + use zkstd::matrix::DenseVectors; use zkstd::r1cs::test::example_r1cs; #[test] fn relaxed_r1cs_test() { for i in 1..10 { let r1cs: R1cs = example_r1cs(i); - let relaxed_r1cs = RelaxedR1cs::new(r1cs); - assert!(relaxed_r1cs.is_sat()) + let instance = RelaxedR1csInstance::new(DenseVectors::new(r1cs.x())); + let witness = RelaxedR1csWitness::new(DenseVectors::new(r1cs.w()), r1cs.m()); + let relaxed_r1cs = R1csShape::from(r1cs); + assert!(relaxed_r1cs.is_sat(&instance, &witness)) } } } diff --git a/nova/src/relaxed_r1cs/instance.rs b/nova/src/relaxed_r1cs/instance.rs index cb2e3c78..489756ef 100644 --- a/nova/src/relaxed_r1cs/instance.rs +++ b/nova/src/relaxed_r1cs/instance.rs @@ -1,5 +1,5 @@ +use crate::driver::scalar_as_base; use crate::hash::{MimcRO, MIMC_ROUNDS}; -use crate::RelaxedR1cs; use zkstd::circuit::prelude::CircuitDriver; use zkstd::common::{BNAffine, BNProjective, CurveGroup, Group, IntGroup, PrimeField, Ring}; use zkstd::matrix::DenseVectors; @@ -35,15 +35,28 @@ impl RelaxedR1csInstance { } } - pub(crate) fn fold(&self, r1cs: &RelaxedR1cs, r: C::Scalar, commit_t: C::Affine) -> Self { + pub(crate) fn u(&self) -> C::Scalar { + self.u + } + + pub(crate) fn x(&self) -> DenseVectors { + self.x.clone() + } + + pub(crate) fn fold( + &self, + instance: &RelaxedR1csInstance, + r: C::Scalar, + commit_t: C::Affine, + ) -> Self { let r2 = r.square(); let (e1, u1, w1, x1) = ( C::Affine::ADDITIVE_IDENTITY, C::Scalar::one(), C::Affine::ADDITIVE_IDENTITY, - r1cs.x(), + instance.x(), ); - let (e2, u2, w2, x2) = (self.commit_e, self.u, self.commit_w, self.x.clone()); + let (e2, u2, w2, x2) = (self.commit_e, self.u, self.commit_w, self.x()); let commit_e = (e1 + commit_t * r + e2 * r2).into(); let u = u1 + r * u2; @@ -64,23 +77,23 @@ impl RelaxedR1csInstance { ) { transcript.append_point(self.commit_w); transcript.append_point(self.commit_e); - transcript.append(self.u); + transcript.append(scalar_as_base::(self.u)); for x in &self.x.get() { - transcript.append(*x); + transcript.append(scalar_as_base::(*x)); } } - pub fn hash( + pub fn hash>( &self, i: usize, - z_0: &DenseVectors, - z_i: &DenseVectors, + z_0: &DenseVectors, + z_i: &DenseVectors, ) -> C::Base { let commit_e = self.commit_e.to_extended(); let commit_w = self.commit_w.to_extended(); - MimcRO::::default().hash_vec( + MimcRO::::default().hash_vec( vec![ - vec![C::Base::from(i as u64)], + vec![C::Scalar::from(i as u64)], z_0.get(), z_i.get(), vec![self.u], diff --git a/nova/src/relaxed_r1cs/witness.rs b/nova/src/relaxed_r1cs/witness.rs index 7df5ce82..bc2ef72c 100644 --- a/nova/src/relaxed_r1cs/witness.rs +++ b/nova/src/relaxed_r1cs/witness.rs @@ -1,4 +1,3 @@ -use crate::RelaxedR1cs; use zkstd::circuit::prelude::CircuitDriver; use zkstd::common::{IntGroup, PrimeField}; use zkstd::matrix::DenseVectors; @@ -19,6 +18,10 @@ impl RelaxedR1csWitness { } } + pub(crate) fn w(&self) -> DenseVectors { + self.w.clone() + } + pub(crate) fn dummy(w_len: usize, m: usize) -> Self { Self { e: DenseVectors::zero(m), @@ -28,14 +31,14 @@ impl RelaxedR1csWitness { pub(crate) fn fold( &self, - r1cs: &RelaxedR1cs, + witness: &RelaxedR1csWitness, r: C::Scalar, t: DenseVectors, ) -> Self { let r2 = r.square(); let e2 = self.e.clone(); - let w1 = r1cs.w(); - let w2 = self.w.clone(); + let w1 = witness.w(); + let w2 = self.w(); let e = t * r + e2 * r2; let w = w1 + w2 * r; diff --git a/nova/src/verifier.rs b/nova/src/verifier.rs index 5a04ad87..f0365f7a 100644 --- a/nova/src/verifier.rs +++ b/nova/src/verifier.rs @@ -1,4 +1,4 @@ -use crate::relaxed_r1cs::{RelaxedR1cs, RelaxedR1csInstance}; +use crate::relaxed_r1cs::RelaxedR1csInstance; use crate::hash::{MimcRO, MIMC_ROUNDS}; use core::marker::PhantomData; @@ -11,39 +11,53 @@ pub struct Verifier { impl Verifier { pub fn verify( commit_t: C::Affine, - r1cs_1: &RelaxedR1cs, - r1cs_2: &RelaxedR1cs, + instance1: &RelaxedR1csInstance, + instance2: &RelaxedR1csInstance, ) -> RelaxedR1csInstance { - let mut transcript = MimcRO::::default(); + let mut transcript = MimcRO::::default(); transcript.append_point(commit_t); - r1cs_2.absorb_by_transcript(&mut transcript); + instance2.absorb_by_transcript(&mut transcript); let r = transcript.squeeze(); - r1cs_2.fold_instance(r1cs_1, r, commit_t) + instance2.fold(instance1, r, commit_t) } } #[cfg(test)] mod tests { - use super::{RelaxedR1cs, Verifier}; + use super::Verifier; use crate::prover::tests::example_prover; + use zkstd::matrix::DenseVectors; + use crate::driver::GrumpkinDriver; + use crate::relaxed_r1cs::{R1csShape, RelaxedR1csInstance, RelaxedR1csWitness}; use zkstd::r1cs::test::example_r1cs; #[test] fn recursive_nifs_test() { let prover = example_prover(); - let r1cs = example_r1cs(1); - let mut running_r1cs = RelaxedR1cs::new(r1cs); + let r1cs = example_r1cs::(1); + let running_instance = RelaxedR1csInstance::new(DenseVectors::new(r1cs.x())); + let running_witness = RelaxedR1csWitness::new(DenseVectors::new(r1cs.w()), r1cs.m()); + for i in 1..10 { - let r1cs_to_fold = RelaxedR1cs::new(example_r1cs(i)); - let (instance, witness, commit_t) = prover.prove(&r1cs_to_fold, &running_r1cs); - let verified_instance = Verifier::verify(commit_t, &r1cs_to_fold, &running_r1cs); + let r1cs_i = example_r1cs::(i); + let instance_to_fold = RelaxedR1csInstance::new(DenseVectors::new(r1cs_i.x())); + let witness_to_fold = + RelaxedR1csWitness::new(DenseVectors::new(r1cs_i.w()), r1cs_i.m()); + + let (instance, witness, commit_t) = prover.prove( + &instance_to_fold, + &witness_to_fold, + &running_instance, + &running_witness, + ); + let verified_instance = + Verifier::verify(commit_t, &instance_to_fold, &running_instance); assert_eq!(instance, verified_instance); - running_r1cs = running_r1cs.update(&instance, &witness); - assert!(running_r1cs.is_sat()) + assert!(R1csShape::from(r1cs_i).is_sat(&instance, &witness)); } } } From 1a0816d2a32c36f1784bb4ebb295cf71e9bf9804 Mon Sep 17 00:00:00 2001 From: Kirill Karbushev Date: Tue, 12 Dec 2023 21:25:33 +0900 Subject: [PATCH 06/17] IVC on cycle curves draft implementation --- nova/Cargo.toml | 2 +- nova/src/circuit/augmented.rs | 68 +++-- nova/src/driver.rs | 10 +- nova/src/gadget/relaxed_instance.rs | 4 +- nova/src/ivc.rs | 295 ++++++++++++------- nova/src/proof.rs | 2 +- zkstd/src/circuit/gadget/curve.rs | 2 +- zkstd/tests/gadget.rs | 1 - zkstd/tests/grumpkin.rs | 441 ---------------------------- 9 files changed, 242 insertions(+), 583 deletions(-) delete mode 100644 zkstd/tests/gadget.rs delete mode 100644 zkstd/tests/grumpkin.rs diff --git a/nova/Cargo.toml b/nova/Cargo.toml index d5d82fee..0098640b 100644 --- a/nova/Cargo.toml +++ b/nova/Cargo.toml @@ -17,9 +17,9 @@ grumpkin = { path = "../grumpkin", default-features = false } zkgroth16 = { path = "../groth16", default-features = false } serde = { version = "1.0.102", default-features = false, features = ["derive"] } blake2b_simd = { version = "1", default-features = false } +rand_core = { version="0.6.4", default-features = false, features = ["getrandom"] } [dev-dependencies] -rand_core = { version="0.6.4", default-features = false, features = ["getrandom"] } grumpkin = { path = "../grumpkin", default-features = false } [features] diff --git a/nova/src/circuit/augmented.rs b/nova/src/circuit/augmented.rs index f654d399..b12f0237 100644 --- a/nova/src/circuit/augmented.rs +++ b/nova/src/circuit/augmented.rs @@ -15,13 +15,13 @@ use zkstd::r1cs::R1cs; pub struct AugmentedFCircuit> { pub i: usize, pub z_0: DenseVectors, - pub z_i: DenseVectors, - pub u_single: RelaxedR1csInstance, - pub u_range: RelaxedR1csInstance, - pub u_range_next: RelaxedR1csInstance, - pub commit_t: C::Affine, + pub z_i: Option>, + pub u_single: Option>, + pub u_range: Option>, + pub u_range_next: Option>, // Remove + pub commit_t: Option, pub f: PhantomData, - pub x: C::Base, + pub x: C::Base, // Remove } impl> Default for AugmentedFCircuit { @@ -29,24 +29,13 @@ impl> Default for AugmentedFCircu Self { i: 0, z_0: DenseVectors::zero(1), - z_i: DenseVectors::zero(1), - u_single: RelaxedR1csInstance::dummy(1), - u_range: RelaxedR1csInstance::dummy(1), - u_range_next: RelaxedR1csInstance::dummy(1), - commit_t: C::Affine::ADDITIVE_IDENTITY, + z_i: Some(DenseVectors::zero(1)), + u_single: Some(RelaxedR1csInstance::dummy(1)), + u_range: Some(RelaxedR1csInstance::dummy(1)), + u_range_next: Some(RelaxedR1csInstance::dummy(1)), + commit_t: Some(C::Affine::ADDITIVE_IDENTITY), f: Default::default(), - x: C::Base::zero(), // x: RelaxedR1csInstance::::dummy(1) - // .hash( - // 1, - // &DenseVectors::zero(1), - // &DenseVectors::new( - // FC::invoke(&DenseVectors::zero(1)) - // .iter() - // .map(|x| base_as_scalar(x)) - // .collect(), - // ), - // ) - // .into(), + x: C::Base::zero(), } } } @@ -62,20 +51,39 @@ impl> AugmentedFCircuit { .collect::>(); let z_i = self .z_i + .clone() + .unwrap_or_else(|| self.z_0.clone()) .iter() .map(|x| FieldAssignment::witness(cs, x)) .collect::>(); let u_dummy_native = RelaxedR1csInstance::::dummy(1); let u_dummy = RelaxedR1csInstanceAssignment::witness(cs, &u_dummy_native); - let u_i = RelaxedR1csInstanceAssignment::witness(cs, &self.u_single); - let u_range = RelaxedR1csInstanceAssignment::witness(cs, &self.u_range); - let u_range_next = RelaxedR1csInstanceAssignment::witness(cs, &self.u_range_next); + let u_i = RelaxedR1csInstanceAssignment::witness( + cs, + &self + .u_single + .clone() + .unwrap_or_else(|| u_dummy_native.clone()), + ); + let u_range = RelaxedR1csInstanceAssignment::witness( + cs, + &self + .u_range + .clone() + .unwrap_or_else(|| u_dummy_native.clone()), + ); + let u_range_next = RelaxedR1csInstanceAssignment::witness( + cs, + &self.u_range_next.clone().unwrap_or(u_dummy_native), + ); + + let commit_t = self.commit_t.unwrap_or(C::Affine::ADDITIVE_IDENTITY); let commit_t = PointAssignment::witness( cs, - self.commit_t.get_x().into(), - self.commit_t.get_y().into(), - self.commit_t.is_identity(), + commit_t.get_x(), + commit_t.get_y(), + commit_t.is_identity(), ); let x = FieldAssignment::instance(cs, self.x); @@ -176,7 +184,7 @@ mod tests { let u_dummy = RelaxedR1csInstance::dummy(cs.l() - 1); let w_dummy = RelaxedR1csWitness::dummy(cs.m_l_1(), cs.m()); - let mut running_r1cs = R1csShape::from(cs); + let running_r1cs = R1csShape::from(cs); assert!(running_r1cs.is_sat(&u_dummy, &w_dummy)); } } diff --git a/nova/src/driver.rs b/nova/src/driver.rs index 1d6757a9..4e2b6be7 100644 --- a/nova/src/driver.rs +++ b/nova/src/driver.rs @@ -70,7 +70,7 @@ mod grumpkin_gadget_tests { use bn_254::G1Affine; use rand_core::OsRng; use zkstd::circuit::prelude::{FieldAssignment, PointAssignment, R1cs}; - use zkstd::common::{BNAffine, BNProjective, Group, PrimeField}; + use zkstd::common::{BNAffine, BNProjective, Group}; #[test] fn range_proof_test() { @@ -240,14 +240,12 @@ mod grumpkin_gadget_tests { for _ in 0..100 { let mut cs: R1cs = R1cs::default(); // Base == GrumpkingScalar - let x = Base::random(OsRng); + let x = Scalar::random(OsRng); let p = G1Affine::random(OsRng); - let x_assignment = FieldAssignment::instance(&mut cs, x.into()); // Fr + let x_assignment = FieldAssignment::instance(&mut cs, x); // Fr let p_assignment = PointAssignment::instance(&mut cs, p); - let expected = p * x; - - assert_eq!(x.to_bits(), Base::from(x).to_bits()); + let expected = p * Base::from(x); let mul_circuit = p_assignment.scalar_point(&mut cs, &x_assignment); diff --git a/nova/src/gadget/relaxed_instance.rs b/nova/src/gadget/relaxed_instance.rs index cddfc8f5..09cf76bd 100644 --- a/nova/src/gadget/relaxed_instance.rs +++ b/nova/src/gadget/relaxed_instance.rs @@ -34,8 +34,8 @@ impl RelaxedR1csInstanceAssignment { ); let commit_e = PointAssignment::witness( cs, - commit_e.get_x().into(), - commit_e.get_y().into(), + commit_e.get_x(), + commit_e.get_y(), commit_e.is_identity(), ); let u = FieldAssignment::witness(cs, scalar_as_base::(*u)); diff --git a/nova/src/ivc.rs b/nova/src/ivc.rs index a7932f41..704e1ed2 100644 --- a/nova/src/ivc.rs +++ b/nova/src/ivc.rs @@ -1,133 +1,213 @@ use crate::function::FunctionCircuit; -use crate::proof::RecursiveProof; use crate::Prover; +use rand_core::OsRng; use std::marker::PhantomData; use crate::circuit::AugmentedFCircuit; use crate::relaxed_r1cs::{R1csShape, RelaxedR1csInstance, RelaxedR1csWitness}; use zkstd::circuit::prelude::{CircuitDriver, R1cs}; -use zkstd::common::{Group, RngCore}; +use zkstd::common::IntGroup; use zkstd::matrix::DenseVectors; -pub struct Ivc +pub struct Ivc where E1: CircuitDriver::Scalar>, E2: CircuitDriver::Scalar>, - FC: FunctionCircuit, + FC1: FunctionCircuit, + FC2: FunctionCircuit, { i: usize, - z0: DenseVectors, - zi: DenseVectors, - prover: Prover, - r1cs: R1csShape, + z0_primary: DenseVectors, + z0_secondary: DenseVectors, + zi_primary: DenseVectors, + zi_secondary: DenseVectors, + prover_primary: Prover, + prover_secondary: Prover, // r1cs instance to be folded // u_i // represents the correct execution of invocation i of F′ - u_single: RelaxedR1csInstance, - w_single: RelaxedR1csWitness, + u_single_secondary: RelaxedR1csInstance, + w_single_secondary: RelaxedR1csWitness, // running r1cs instance // U_i // represents the correct execution of invocations 1, . . . , i - 1 of F′ - u_range: RelaxedR1csInstance, - w_range: RelaxedR1csWitness, - f: PhantomData<(FC, E2)>, + u_range_primary: RelaxedR1csInstance, + w_range_primary: RelaxedR1csWitness, + u_range_secondary: RelaxedR1csInstance, + w_range_secondary: RelaxedR1csWitness, + f: PhantomData<(FC1, FC2)>, } -impl Ivc +impl Ivc where E1: CircuitDriver::Scalar>, E2: CircuitDriver::Scalar>, - FC: FunctionCircuit, + FC1: FunctionCircuit, + FC2: FunctionCircuit, { - pub fn new(rng: impl RngCore, z0: DenseVectors) -> Self { - let mut r1cs = R1cs::::default(); + pub fn new( + rng: OsRng, + pp: &PublicParams, + z0_primary: DenseVectors, + z0_secondary: DenseVectors, + ) -> Self { + let mut cs_primary = R1cs::::default(); + let circuit_primary = AugmentedFCircuit::::default(); + circuit_primary.generate(&mut cs_primary); // get zi_primary - let augmented_circuit = AugmentedFCircuit::::default(); - augmented_circuit.generate(&mut r1cs); + // get u_single_next/w_single_next primary - let prover = Prover::new(R1csShape::from(r1cs.clone()), rng); - let u_dummy = RelaxedR1csInstance::::dummy(r1cs.l() - 1); - let w_dummy = RelaxedR1csWitness::::dummy(r1cs.m_l_1(), r1cs.m()); + let prover_primary = Prover::new(R1csShape::from(cs_primary.clone()), rng); + + let mut cs_secondary = R1cs::::default(); + let circuit_secondary = AugmentedFCircuit::::default(); + circuit_secondary.generate(&mut cs_secondary); // get zi_secondary + + // get u_single_next/w_single_next secondary + + let prover_secondary = Prover::new(R1csShape::from(cs_secondary.clone()), rng); + + let u_dummy = RelaxedR1csInstance::::dummy(cs_primary.l() - 1); + let w_dummy = RelaxedR1csWitness::::dummy(cs_primary.m_l_1(), cs_primary.m()); Self { i: 0, - z0: z0.clone(), - zi: z0, - prover, - r1cs: R1csShape::from(r1cs), - u_single: u_dummy.clone(), - w_single: w_dummy.clone(), - u_range: u_dummy, - w_range: w_dummy, + z0_primary, + z0_secondary, + zi_primary: Default::default(), + zi_secondary: Default::default(), + prover_primary, + prover_secondary, + u_single_secondary: RelaxedR1csInstance::dummy(1), + w_single_secondary: RelaxedR1csWitness::dummy(1, 1), + u_range_primary: RelaxedR1csInstance::dummy(1), + w_range_primary: RelaxedR1csWitness::dummy(1, 1), + u_range_secondary: RelaxedR1csInstance::dummy(1), + w_range_secondary: RelaxedR1csWitness::dummy(1, 1), f: PhantomData::default(), } } - pub fn prove_step(&mut self) -> RecursiveProof { - let z_next = FC::invoke(&self.zi); - let (u_range_next, w_range_next, u_single_next_x, commit_t) = if self.i == 0 { - let u_single_next_x = self.u_range.hash(1, &self.z0, &z_next); - let (u_range_next, w_range_next, commit_t) = ( - RelaxedR1csInstance::::dummy(1), - RelaxedR1csWitness::::dummy(self.r1cs.l(), self.r1cs.m()), - E1::Affine::ADDITIVE_IDENTITY, + pub fn prove_step(&mut self, pp: &PublicParams) { + //-> RecursiveProof { + if self.i == 0 { + self.i = 1; + // return + } + let z_next = FC1::invoke(&self.zi_primary); + let (u_range_next_secondary, w_range_next_secondary, commit_t_secondary) = + self.prover_secondary.prove( + &self.u_range_secondary, + &self.w_range_secondary, + &self.u_single_secondary, + &self.w_single_secondary, ); - (u_range_next, w_range_next, u_single_next_x, commit_t) - } else { - let (u_range_next, w_range_next, commit_t) = - self.prover - .prove(&self.u_single, &self.w_single, &self.u_range, &self.w_range); + let mut cs = R1cs::::default(); + let circuit_primary = AugmentedFCircuit:: { + i: self.i, + z_0: self.z0_primary.clone(), + z_i: Some(self.zi_primary.clone()), + u_single: Some(self.u_single_secondary.clone()), + u_range: Some(self.u_range_secondary.clone()), + u_range_next: None, + commit_t: Some(commit_t_secondary), + f: Default::default(), + x: E2::Base::zero(), + }; - assert!(self.r1cs.is_sat(&u_range_next, &w_range_next)); + circuit_primary.generate(&mut cs); // zi_primary - let u_single_next_x = u_range_next.hash(self.i + 1, &self.z0, &z_next); + // get u_single_next/w_single_next primary - (u_range_next, w_range_next, u_single_next_x, commit_t) - }; + let (u_range_next_primary, w_range_next_primary, commit_t_primary) = + self.prover_primary.prove( + &self.u_range_primary, + &self.w_range_primary, + &self.u_range_primary, // u_single_next_primary + &self.w_range_primary, // w_single_next_primary + ); - let augmented_circuit = AugmentedFCircuit:: { + let mut cs = R1cs::::default(); + let circuit_primary = AugmentedFCircuit:: { i: self.i, - z_0: self.z0.clone(), - z_i: self.zi.clone(), - u_single: self.u_single.clone(), - u_range: self.u_range.clone(), - u_range_next: u_range_next.clone(), - commit_t, - f: self.f, - x: u_single_next_x, + z_0: self.z0_secondary.clone(), + z_i: Some(self.zi_secondary.clone()), + u_single: Some(self.u_range_primary.clone()), // u_single_next_primary + u_range: Some(self.u_range_primary.clone()), + u_range_next: None, + commit_t: Some(commit_t_primary), + f: Default::default(), + x: E1::Base::zero(), }; - let mut cs = R1cs::::default(); - augmented_circuit.generate(&mut cs); - - let (u_single_next, w_single_next) = ( - RelaxedR1csInstance::new(DenseVectors::new(cs.x())), - RelaxedR1csWitness::new(DenseVectors::new(cs.w()), self.r1cs.m()), - ); + circuit_primary.generate(&mut cs); // zi_secondary - assert_eq!(u_single_next.x.len(), 1); - assert_eq!(u_single_next.x[0], u_single_next_x); + // get u_single_next/w_single_next secondary - self.u_single = u_single_next; - self.w_single = w_single_next; - self.u_range = u_range_next; - self.w_range = w_range_next; + // update values + self.u_range_primary = u_range_next_primary; + self.w_range_primary = w_range_next_primary; + self.u_range_secondary = u_range_next_secondary; + self.w_range_secondary = w_range_next_secondary; + // self.u_single_secondary = u_single_next_secondary; + // self.w_single_secondary = w_single_next_secondary; self.i += 1; - self.zi = z_next; + // self.zi_primary = zi_primary; + // self.zi_secondary = zi_secondary; - // ((Ui+1, Wi+1), (ui+1, wi+1)) - let pair = ( - (self.u_range.clone(), self.w_range.clone()), - (self.u_single.clone(), self.w_single.clone()), - ); + // // ((Ui+1, Wi+1), (ui+1, wi+1)) + // let pair = ( + // (self.u_range.clone(), self.w_range.clone()), + // (self.u_single.clone(), self.w_single.clone()), + // ); + // + // RecursiveProof { + // i: self.i, + // z0: self.z0.clone(), + // zi: self.zi.clone(), + // r1cs: self.r1cs.clone(), + // pair, + // marker: Default::default(), + // } + } +} - RecursiveProof { - i: self.i, - z0: self.z0.clone(), - zi: self.zi.clone(), - r1cs: self.r1cs.clone(), - pair, +pub struct PublicParams +where + E1: CircuitDriver::Scalar>, + E2: CircuitDriver::Scalar>, + FC1: FunctionCircuit, + FC2: FunctionCircuit, +{ + r1cs_shape_primary: R1csShape, + r1cs_shape_secondary: R1csShape, + marker: PhantomData<(FC1, FC2)>, +} + +impl PublicParams +where + E1: CircuitDriver::Scalar>, + E2: CircuitDriver::Scalar>, + FC1: FunctionCircuit, + FC2: FunctionCircuit, +{ + pub fn setup() -> Self { + // Initialize shape for the primary + let circuit_primary = AugmentedFCircuit::::default(); + let mut cs = R1cs::::default(); + circuit_primary.generate(&mut cs); + let r1cs_shape_primary = R1csShape::from(cs); + + // Initialize shape for the secondary + let circuit_secondary = AugmentedFCircuit::::default(); + let mut cs = R1cs::::default(); + circuit_secondary.generate(&mut cs); + let r1cs_shape_secondary = R1csShape::from(cs); + + PublicParams { + r1cs_shape_primary, + r1cs_shape_secondary, marker: Default::default(), } } @@ -135,12 +215,11 @@ where #[cfg(test)] mod tests { - use super::Ivc; + use super::{Ivc, PublicParams}; use crate::test::ExampleFunction; use crate::driver::{Bn254Driver, GrumpkinDriver}; - use crate::RecursiveProof; - use bn_254::Fr; + use bn_254::{Fq, Fr}; use rand_core::OsRng; use zkstd::circuit::prelude::R1cs; use zkstd::matrix::DenseVectors; @@ -149,25 +228,41 @@ mod tests { #[test] fn ivc_test() { let r1cs: R1cs = example_r1cs(1); - let z0 = DenseVectors::new(vec![Fr::from(3)]); - let mut ivc = Ivc::>::new(OsRng, z0); - let proof_0 = RecursiveProof { - i: 0, - z0: ivc.z0.clone(), - zi: ivc.zi.clone(), - r1cs: ivc.r1cs.clone(), - pair: ( - (ivc.u_range.clone(), ivc.w_range.clone()), - (ivc.u_single.clone(), ivc.w_single.clone()), - ), - marker: Default::default(), - }; - assert!(proof_0.verify()); + // produce public parameters + let pp = PublicParams::< + Bn254Driver, + GrumpkinDriver, + ExampleFunction, + ExampleFunction, + >::setup(); + + let z0_primary = DenseVectors::new(vec![Fr::from(3)]); + let z0_secondary = DenseVectors::new(vec![Fq::from(3)]); + let mut ivc = + Ivc::, ExampleFunction>::new( + OsRng, + &pp, + z0_primary, + z0_secondary, + ); + // let proof_0 = RecursiveProof { + // i: 0, + // z0: ivc.z0.clone(), + // zi: ivc.zi.clone(), + // r1cs: ivc.r1cs.clone(), + // pair: ( + // (ivc.u_range.clone(), ivc.w_range.clone()), + // (ivc.u_single.clone(), ivc.w_single.clone()), + // ), + // marker: Default::default(), + // }; + // + // assert!(proof_0.verify()); for i in 0..2 { - let proof = ivc.prove_step(); - assert!(proof.verify()); + ivc.prove_step(&pp); + // assert!(proof.verify()); } } } diff --git a/nova/src/proof.rs b/nova/src/proof.rs index 1fd46c95..3efe2a87 100644 --- a/nova/src/proof.rs +++ b/nova/src/proof.rs @@ -56,7 +56,7 @@ where // check if instance-witness pair satisfy let is_instance_witness_sat = - self.r1cs.is_sat(&l_ui, &l_wi) && self.r1cs.is_sat(&s_ui, &s_wi); + self.r1cs.is_sat(l_ui, l_wi) && self.r1cs.is_sat(s_ui, s_wi); check_hash && check_defaults && is_instance_witness_sat } diff --git a/zkstd/src/circuit/gadget/curve.rs b/zkstd/src/circuit/gadget/curve.rs index f8030830..1f59bef5 100644 --- a/zkstd/src/circuit/gadget/curve.rs +++ b/zkstd/src/circuit/gadget/curve.rs @@ -116,7 +116,7 @@ impl PointAssignment { } pub fn double>(&self, cs: &mut R1cs) -> Self { - let b3 = FieldAssignment::constant(&C::b3().into()); + let b3 = FieldAssignment::constant(&C::b3()); let t0 = FieldAssignment::mul(cs, &self.y, &self.y); let z3 = &t0 + &t0; let z3 = &z3 + &z3; diff --git a/zkstd/tests/gadget.rs b/zkstd/tests/gadget.rs deleted file mode 100644 index 18b1875c..00000000 --- a/zkstd/tests/gadget.rs +++ /dev/null @@ -1 +0,0 @@ -mod grumpkin; diff --git a/zkstd/tests/grumpkin.rs b/zkstd/tests/grumpkin.rs deleted file mode 100644 index 575fc432..00000000 --- a/zkstd/tests/grumpkin.rs +++ /dev/null @@ -1,441 +0,0 @@ -// #![allow(clippy::suspicious_arithmetic_impl)] -// #![allow(clippy::suspicious_op_assign_impl)] -// -// use zkstd::arithmetic::bits_256::*; -// use zkstd::arithmetic::weierstrass::*; -// use zkstd::circuit::CircuitDriver; -// use zkstd::common::*; -// use zkstd::macros::curve::weierstrass::*; -// use zkstd::macros::field::*; -// -// pub(crate) const FR_MODULUS: [u64; 4] = [ -// 0x43e1f593f0000001, -// 0x2833e84879b97091, -// 0xb85045b68181585d, -// 0x30644e72e131a029, -// ]; -// -// const FR_GENERATOR: [u64; 4] = [7, 0, 0, 0]; -// -// pub(crate) const FR_R: [u64; 4] = [ -// 0xac96341c4ffffffb, -// 0x36fc76959f60cd29, -// 0x666ea36f7879462e, -// 0x0e0a77c19a07df2f, -// ]; -// -// pub(crate) const FR_R2: [u64; 4] = [ -// 0x1bb8e645ae216da7, -// 0x53fe3ab1e35c59e3, -// 0x8c49833d53bb8085, -// 0x0216d0b17f4e44a5, -// ]; -// -// pub(crate) const FR_R3: [u64; 4] = [ -// 0x5e94d8e1b4bf0040, -// 0x2a489cbe1cfbb6b8, -// 0x893cc664a19fcfed, -// 0x0cf8594b7fcc657c, -// ]; -// -// pub const FR_INV: u64 = 0xc2e1f593efffffff; -// -// pub(crate) const FQ_MODULUS: [u64; 4] = [ -// 0x3c208c16d87cfd47, -// 0x97816a916871ca8d, -// 0xb85045b68181585d, -// 0x30644e72e131a029, -// ]; -// -// pub(crate) const FQ_GENERATOR: [u64; 4] = [3, 0, 0, 0]; -// -// /// R = 2^256 mod q -// pub(crate) const FQ_R: [u64; 4] = [ -// 0xd35d438dc58f0d9d, -// 0x0a78eb28f5c70b3d, -// 0x666ea36f7879462c, -// 0x0e0a77c19a07df2f, -// ]; -// -// /// R^2 = 2^512 mod q -// pub(crate) const FQ_R2: [u64; 4] = [ -// 0xf32cfc5b538afa89, -// 0xb5e71911d44501fb, -// 0x47ab1eff0a417ff6, -// 0x06d89f71cab8351f, -// ]; -// -// /// R^3 = 2^768 mod q -// pub(crate) const FQ_R3: [u64; 4] = [ -// 0xb1cd6dafda1530df, -// 0x62f210e6a7283db6, -// 0xef7f0b0c0ada0afb, -// 0x20fd6e902d592544, -// ]; -// -// /// INV = -(q^{-1} mod 2^64) mod 2^64 -// pub(crate) const FQ_INV: u64 = 0x87d20782e4866389; -// -// #[macro_export] -// macro_rules! cycle_pair_field { -// ($field:ident, $generator:ident, $modulus:ident, $r:ident, $r2:ident, $r3:ident, $inv:ident) => { -// #[derive(Clone, Copy, Decode, Encode, Serialize, Deserialize)] -// pub struct $field(pub [u64; 4]); -// -// impl $field { -// pub const fn new_unchecked(val: [u64; 4]) -> Self { -// Self(val) -// } -// pub const fn add_const(self, rhs: Self) -> Self { -// Self(add(self.0, rhs.0, $modulus)) -// } -// -// pub const fn to_mont_form(val: [u64; 4]) -> Self { -// Self(to_mont_form(val, $r2, $modulus, $inv)) -// } -// -// pub const fn inner(&self) -> &[u64; 4] { -// &self.0 -// } -// -// pub(crate) const fn montgomery_reduce(self) -> [u64; 4] { -// mont( -// [self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0], -// $modulus, -// $inv, -// ) -// } -// } -// -// impl SigUtils<32> for $field { -// fn to_bytes(self) -> [u8; Self::LENGTH] { -// let tmp = self.montgomery_reduce(); -// -// let mut res = [0; Self::LENGTH]; -// res[0..8].copy_from_slice(&tmp[0].to_le_bytes()); -// res[8..16].copy_from_slice(&tmp[1].to_le_bytes()); -// res[16..24].copy_from_slice(&tmp[2].to_le_bytes()); -// res[24..32].copy_from_slice(&tmp[3].to_le_bytes()); -// -// res -// } -// -// fn from_bytes(bytes: [u8; Self::LENGTH]) -> Option { -// // SBP-M1 review: apply proper error handling instead of `unwrap` -// let l0 = u64::from_le_bytes(bytes[0..8].try_into().unwrap()); -// let l1 = u64::from_le_bytes(bytes[8..16].try_into().unwrap()); -// let l2 = u64::from_le_bytes(bytes[16..24].try_into().unwrap()); -// let l3 = u64::from_le_bytes(bytes[24..32].try_into().unwrap()); -// -// let (_, borrow) = sbb(l0, $modulus[0], 0); -// let (_, borrow) = sbb(l1, $modulus[1], borrow); -// let (_, borrow) = sbb(l2, $modulus[2], borrow); -// let (_, borrow) = sbb(l3, $modulus[3], borrow); -// -// if borrow & 1 == 1 { -// Some(Self([l0, l1, l2, l3]) * Self($r2)) -// } else { -// None -// } -// } -// } -// -// prime_field_operation!($field, $modulus, $generator, $inv, $r, $r2, $r3); -// }; -// } -// -// cycle_pair_field!(Fr, FR_GENERATOR, FR_MODULUS, FR_R, FR_R2, FR_R3, FR_INV); -// cycle_pair_field!(Fq, FQ_GENERATOR, FQ_MODULUS, FQ_R, FQ_R2, FQ_R3, FQ_INV); -// -// pub(crate) const FR_PARAM_B: Fr = Fr::new_unchecked([ -// 0xdd7056026000005a, -// 0x223fa97acb319311, -// 0xcc388229877910c0, -// 0x034394632b724eaa, -// ]); -// pub const FR_PARAM_B3: Fr = FR_PARAM_B.add_const(FR_PARAM_B).add_const(FR_PARAM_B); -// -// pub(crate) const G1_GENERATOR_X: Fq = Fq::one(); -// pub(crate) const G1_GENERATOR_Y: Fq = Fq::to_mont_form([2, 0, 0, 0]); -// pub(crate) const G1_PARAM_B: Fq = Fq::to_mont_form([3, 0, 0, 0]); -// pub const FQ_PARAM_B3: Fq = G1_PARAM_B.add_const(G1_PARAM_B).add_const(G1_PARAM_B); -// -// impl From for Fr { -// fn from(val: Fq) -> Fr { -// Self(to_mont_form( -// val.montgomery_reduce(), -// FR_R2, -// FR_MODULUS, -// FR_INV, -// )) -// } -// } -// -// impl From for Fq { -// fn from(val: Fr) -> Fq { -// Self(to_mont_form( -// val.montgomery_reduce(), -// FQ_R2, -// FQ_MODULUS, -// FQ_INV, -// )) -// } -// } -// -// /// The projective form of coordinate -// #[derive(Debug, Clone, Copy, Decode, Encode)] -// pub struct G1Affine { -// pub(crate) x: Fq, -// pub(crate) y: Fq, -// is_infinity: bool, -// } -// -// impl Add for G1Affine { -// type Output = G1Projective; -// -// fn add(self, rhs: G1Affine) -> Self::Output { -// add_affine_point(self, rhs) -// } -// } -// -// impl Neg for G1Affine { -// type Output = Self; -// -// fn neg(self) -> Self { -// Self { -// x: self.x, -// y: -self.y, -// is_infinity: self.is_infinity, -// } -// } -// } -// -// impl Sub for G1Affine { -// type Output = G1Projective; -// -// fn sub(self, rhs: G1Affine) -> Self::Output { -// add_affine_point(self, rhs.neg()) -// } -// } -// -// impl Mul for G1Affine { -// type Output = G1Projective; -// -// fn mul(self, rhs: Fr) -> Self::Output { -// scalar_point(self.to_extended(), &rhs) -// } -// } -// -// impl Mul for Fr { -// type Output = G1Projective; -// -// fn mul(self, rhs: G1Affine) -> Self::Output { -// scalar_point(rhs.to_extended(), &self) -// } -// } -// -// /// The projective form of coordinate -// #[derive(Debug, Clone, Copy, Decode, Encode)] -// pub struct G1Projective { -// pub(crate) x: Fq, -// pub(crate) y: Fq, -// pub(crate) z: Fq, -// } -// -// impl Add for G1Projective { -// type Output = Self; -// -// fn add(self, rhs: G1Projective) -> Self { -// add_projective_point(self, rhs) -// } -// } -// -// impl Neg for G1Projective { -// type Output = Self; -// -// fn neg(self) -> Self { -// Self { -// x: self.x, -// y: -self.y, -// z: self.z, -// } -// } -// } -// -// impl Sub for G1Projective { -// type Output = Self; -// -// fn sub(self, rhs: G1Projective) -> Self { -// add_projective_point(self, -rhs) -// } -// } -// -// impl Mul for G1Projective { -// type Output = G1Projective; -// -// fn mul(self, rhs: Fr) -> Self::Output { -// scalar_point(self, &rhs) -// } -// } -// -// impl Mul for Fr { -// type Output = G1Projective; -// -// fn mul(self, rhs: G1Projective) -> Self::Output { -// scalar_point(rhs, &self) -// } -// } -// -// weierstrass_curve_operation!( -// Fr, -// Fq, -// G1_PARAM_B, -// FQ_PARAM_B3, -// G1Affine, -// G1Projective, -// G1_GENERATOR_X, -// G1_GENERATOR_Y -// ); -// -// #[derive(Debug, Clone, Copy, Decode, Encode)] -// pub struct Affine { -// pub(crate) x: Fr, -// pub(crate) y: Fr, -// is_infinity: bool, -// } -// impl Add for Affine { -// type Output = Projective; -// -// fn add(self, rhs: Affine) -> Self::Output { -// add_affine_point(self, rhs) -// } -// } -// -// impl Neg for Affine { -// type Output = Self; -// -// fn neg(self) -> Self { -// Self { -// x: self.x, -// y: -self.y, -// is_infinity: self.is_infinity, -// } -// } -// } -// -// impl Sub for Affine { -// type Output = Projective; -// -// fn sub(self, rhs: Affine) -> Self::Output { -// add_affine_point(self, rhs.neg()) -// } -// } -// -// impl Mul for Affine { -// type Output = Projective; -// -// fn mul(self, rhs: Fq) -> Self::Output { -// scalar_point(self.to_extended(), &rhs) -// } -// } -// -// impl Mul for Fq { -// type Output = Projective; -// -// fn mul(self, rhs: Affine) -> Self::Output { -// scalar_point(rhs.to_extended(), &self) -// } -// } -// -// #[derive(Debug, Clone, Copy, Decode, Encode)] -// pub struct Projective { -// pub(crate) x: Fr, -// pub(crate) y: Fr, -// pub(crate) z: Fr, -// } -// -// impl Add for Projective { -// type Output = Self; -// -// fn add(self, rhs: Projective) -> Self { -// add_projective_point(self, rhs) -// } -// } -// -// impl Neg for Projective { -// type Output = Self; -// -// fn neg(self) -> Self { -// Self { -// x: self.x, -// y: -self.y, -// z: self.z, -// } -// } -// } -// -// impl Sub for Projective { -// type Output = Self; -// -// fn sub(self, rhs: Projective) -> Self { -// add_projective_point(self, -rhs) -// } -// } -// -// impl Mul for Projective { -// type Output = Projective; -// -// fn mul(self, rhs: Fq) -> Self::Output { -// scalar_point(self, &rhs) -// } -// } -// -// impl Mul for Fq { -// type Output = Projective; -// -// fn mul(self, rhs: Projective) -> Self::Output { -// scalar_point(rhs, &self) -// } -// } -// -// pub const GENERATOR_X: Fr = Fr::one(); -// pub const GENERATOR_Y: Fr = Fr::new_unchecked([ -// 0x11b2dff1448c41d8, -// 0x23d3446f21c77dc3, -// 0xaa7b8cf435dfafbb, -// 0x14b34cf69dc25d68, -// ]); -// pub(crate) const PARAM_B: Fr = Fr::new_unchecked([ -// 0xdd7056026000005a, -// 0x223fa97acb319311, -// 0xcc388229877910c0, -// 0x034394632b724eaa, -// ]); -// pub const PARAM_B3: Fr = PARAM_B.add_const(PARAM_B).add_const(PARAM_B); -// -// weierstrass_curve_operation!( -// Fq, -// Fr, -// PARAM_B, -// PARAM_B3, -// Affine, -// Projective, -// GENERATOR_X, -// GENERATOR_Y -// ); -// -// #[derive(Clone, Debug, Default, PartialEq, Eq)] -// pub struct GrumpkinDriver; -// -// impl CircuitDriver for GrumpkinDriver { -// const NUM_BITS: u16 = 254; -// type Affine = Affine; -// -// type Base = Fr; -// -// type Scalar = Fq; -// -// fn b3() -> Self::Base { -// FR_PARAM_B3 -// } -// } From 40f55fcced8de8197b97c151325e13ff26a1efa7 Mon Sep 17 00:00:00 2001 From: Kirill Karbushev Date: Wed, 13 Dec 2023 18:06:30 +0900 Subject: [PATCH 07/17] circuit, nifs gadget, proof verification logic adjusted --- nova/src/circuit/augmented.rs | 111 ++++++++++++------------ nova/src/circuit/nifs.rs | 77 ++++++++++------- nova/src/gadget/relaxed_instance.rs | 46 +++++++--- nova/src/ivc.rs | 126 +++++++++++++++++----------- nova/src/proof.rs | 87 ++++++++++--------- nova/src/relaxed_r1cs/instance.rs | 14 ++-- zkstd/src/circuit/gadget/curve.rs | 13 +++ zkstd/src/circuit/gadget/field.rs | 30 +++++++ 8 files changed, 310 insertions(+), 194 deletions(-) diff --git a/nova/src/circuit/augmented.rs b/nova/src/circuit/augmented.rs index b12f0237..ded3e6f2 100644 --- a/nova/src/circuit/augmented.rs +++ b/nova/src/circuit/augmented.rs @@ -13,6 +13,7 @@ use zkstd::r1cs::R1cs; #[derive(Debug, Clone)] pub struct AugmentedFCircuit> { + pub is_primary: bool, pub i: usize, pub z_0: DenseVectors, pub z_i: Option>, @@ -27,6 +28,7 @@ pub struct AugmentedFCircuit> { impl> Default for AugmentedFCircuit { fn default() -> Self { Self { + is_primary: false, i: 0, z_0: DenseVectors::zero(1), z_i: Some(DenseVectors::zero(1)), @@ -41,7 +43,10 @@ impl> Default for AugmentedFCircu } impl> AugmentedFCircuit { - pub(crate) fn generate>(&self, cs: &mut R1cs) { + pub(crate) fn generate>( + &self, + cs: &mut R1cs, + ) -> Vec> { // allocate inputs let i = FieldAssignment::witness(cs, C::Base::from(self.i as u64)); let z_0 = self @@ -59,7 +64,7 @@ impl> AugmentedFCircuit { let u_dummy_native = RelaxedR1csInstance::::dummy(1); let u_dummy = RelaxedR1csInstanceAssignment::witness(cs, &u_dummy_native); - let u_i = RelaxedR1csInstanceAssignment::witness( + let u_single = RelaxedR1csInstanceAssignment::witness( cs, &self .u_single @@ -73,10 +78,6 @@ impl> AugmentedFCircuit { .clone() .unwrap_or_else(|| u_dummy_native.clone()), ); - let u_range_next = RelaxedR1csInstanceAssignment::witness( - cs, - &self.u_range_next.clone().unwrap_or(u_dummy_native), - ); let commit_t = self.commit_t.unwrap_or(C::Affine::ADDITIVE_IDENTITY); let commit_t = PointAssignment::witness( @@ -85,70 +86,74 @@ impl> AugmentedFCircuit { commit_t.get_y(), commit_t.is_identity(), ); - let x = FieldAssignment::instance(cs, self.x); - let z_next = FC::invoke_cs(cs, z_i.clone()); let zero = FieldAssignment::constant(&C::Base::zero()); let bin_true = BinaryAssignment::witness(cs, 1); let base_case = FieldAssignment::is_eq(cs, &i, &zero); let not_base_case = FieldAssignment::is_neq(cs, &i, &zero); - // (1) check that ui.x = hash(vk, i, z0, zi, Ui), where ui.x is the public IO of ui - let u_i_x = u_range.hash(cs, i.clone(), z_0.clone(), z_i); - FieldAssignment::conditional_enforce_equal(cs, &u_i.x[0], &u_i_x, ¬_base_case); + // base case + let u_single_next_base = if self.is_primary { + u_dummy + } else { + u_single.clone() + }; - // (2) check that (ui.E, ui.u) = (u⊥.E, 1), - FieldAssignment::conditional_enforce_equal( - cs, - &u_i.commit_e.get_x(), - &u_dummy.commit_e.get_x(), - ¬_base_case, - ); - FieldAssignment::conditional_enforce_equal( - cs, - &u_i.commit_e.get_y(), - &u_dummy.commit_e.get_y(), - ¬_base_case, - ); - FieldAssignment::conditional_enforce_equal( - cs, - &u_i.commit_e.get_z(), - &u_dummy.commit_e.get_z(), - ¬_base_case, - ); - FieldAssignment::conditional_enforce_equal( + // (1) check that ui.x = hash(vk, i, z0, zi, Ui), where ui.x is the public IO of ui + let u_i_x = u_range.hash(cs, i.clone(), z_0.clone(), z_i.clone()); + FieldAssignment::conditional_enforce_equal(cs, &u_single.x0, &u_i_x, ¬_base_case); + + // // (2) check that (ui.E, ui.u) = (u⊥.E, 1), + // FieldAssignment::conditional_enforce_equal( + // cs, + // &u_single.commit_e.get_x(), + // &u_dummy.commit_e.get_x(), + // ¬_base_case, + // ); + // FieldAssignment::conditional_enforce_equal( + // cs, + // &u_single.commit_e.get_y(), + // &u_dummy.commit_e.get_y(), + // ¬_base_case, + // ); + // FieldAssignment::conditional_enforce_equal( + // cs, + // &u_single.commit_e.get_z(), + // &u_dummy.commit_e.get_z(), + // ¬_base_case, + // ); + // FieldAssignment::conditional_enforce_equal( + // cs, + // &u_single.u, + // &FieldAssignment::constant(&C::Base::one()), + // ¬_base_case, + // ); + + // (3) Generate Ui+1 ← NIFS.V(vk, U, u, T) + let r = Self::get_challenge(cs, &u_range, commit_t.clone()); + let u_single_next_non_base = + NifsCircuit::verify(cs, r, u_single.clone(), u_range.clone(), commit_t); + + let u_single_next = RelaxedR1csInstanceAssignment::conditional_select( cs, - &u_i.u, - &FieldAssignment::constant(&C::Base::one()), - ¬_base_case, + &u_single_next_base, + &u_single_next_non_base, + &base_case, ); - // (3) Verify Ui+1 ← NIFS.V(vk, U, u, T ) - let r = Self::get_challenge(cs, &u_range, commit_t); - let nifs_check = NifsCircuit::verify(cs, r, u_i, u_range.clone(), u_range_next.clone()); - BinaryAssignment::conditional_enforce_equal(cs, &nifs_check, &bin_true, ¬_base_case); - - // 4. (base case) u_{i+1}.X == H(1, z_0, F(z_0)=F(z_i)=z_i1, U_i) (with U_i being dummy) - let u_next_x_basecase = u_range.hash( - cs, - FieldAssignment::constant(&C::Base::one()), - z_0.clone(), - z_next.clone(), - ); + let z_next = FC::invoke_cs(cs, z_i); - // 4. (non-base case). u_{i+1}.x = H(i+1, z_0, z_i+1, U_{i+1}) - let u_next_x = u_range_next.hash( + let u_next_x = u_single_next.hash( cs, &i + &FieldAssignment::constant(&C::Base::one()), z_0, - z_next, + z_next.clone(), ); - // constrain u_{i+1}.x for base case - FieldAssignment::conditional_enforce_equal(cs, &u_next_x_basecase, &x, &base_case); - // constrain u_{i+1}.x for non base case - FieldAssignment::conditional_enforce_equal(cs, &u_next_x, &x, ¬_base_case); + let x1 = FieldAssignment::inputize(cs, u_single.x1); + let u_next_x = FieldAssignment::inputize(cs, u_next_x); + z_next } pub(crate) fn get_challenge>( diff --git a/nova/src/circuit/nifs.rs b/nova/src/circuit/nifs.rs index b219443a..e64b8cd5 100644 --- a/nova/src/circuit/nifs.rs +++ b/nova/src/circuit/nifs.rs @@ -1,7 +1,8 @@ use core::marker::PhantomData; use crate::gadget::RelaxedR1csInstanceAssignment; -use zkstd::circuit::prelude::{BinaryAssignment, CircuitDriver, FieldAssignment, R1cs}; +use zkstd::circuit::prelude::{CircuitDriver, FieldAssignment, PointAssignment, R1cs}; +use zkstd::common::IntGroup; pub(crate) struct NifsCircuit { p: PhantomData, @@ -11,30 +12,44 @@ impl NifsCircuit { pub(crate) fn verify>( cs: &mut R1cs, r: FieldAssignment, - instance1: RelaxedR1csInstanceAssignment, - instance2: RelaxedR1csInstanceAssignment, - instance3: RelaxedR1csInstanceAssignment, - ) -> BinaryAssignment { - let r_u = FieldAssignment::mul(cs, &r, &instance2.u); - let first_check = FieldAssignment::is_eq(cs, &instance3.u, &(&instance1.u + &r_u)); + u_single: RelaxedR1csInstanceAssignment, + u_range: RelaxedR1csInstanceAssignment, + commit_t: PointAssignment, + ) -> RelaxedR1csInstanceAssignment { + let r2 = FieldAssignment::mul(cs, &r, &r); + // W_fold = u.W + r * U.W + let r_w = u_range.commit_w.scalar_point(cs, &r); + let w_fold = u_range.commit_w.add(&r_w, cs); - let x = instance1 - .x - .iter() - .zip(instance2.x) - .map(|(x1, x2)| { - let r_x2 = FieldAssignment::mul(cs, &r, &x2); - x1 + &r_x2 - }) - .collect::>>(); - let second_check = - x.iter() - .zip(instance3.x) - .fold(BinaryAssignment::witness(cs, 1), |acc, (a, b)| { - let check = FieldAssignment::is_eq(cs, a, &b); - BinaryAssignment::and(cs, &acc, &check) - }); - BinaryAssignment::and(cs, &first_check, &second_check) + // E_fold = u.E + r * T + U.E * r^2 + let r_t = commit_t.scalar_point(cs, &r); + let r2_e = u_range.commit_e.scalar_point(cs, &r2); + let e_fold = u_range.commit_e.add(&r_t, cs); + let e_fold = e_fold.add(&r2_e, cs); + + // u_fold = u.u + r * U.u + let r_u = FieldAssignment::mul(cs, &r, &u_range.u); + let u_fold = &u_single.u + &r_u; + FieldAssignment::enforce_eq_constant( + cs, + &(&(&u_fold - &u_single.u) - &r), + &C::Base::zero(), + ); + + // Fold x0 + r * U.x0 + let r_x0 = FieldAssignment::mul(cs, &r, &u_range.x0); + let x0_fold = &u_single.x0 + &r_x0; + + // Fold x1 + r * U.x1 + let r_x1 = FieldAssignment::mul(cs, &r, &u_range.x1); + let x1_fold = &u_single.x1 + &r_x1; + RelaxedR1csInstanceAssignment { + commit_w: w_fold, + commit_e: e_fold, + u: u_fold, + x0: x0_fold, + x1: x1_fold, + } } } @@ -45,7 +60,7 @@ mod tests { use crate::hash::{MimcRO, MIMC_ROUNDS}; use crate::prover::tests::example_prover; use crate::relaxed_r1cs::{RelaxedR1csInstance, RelaxedR1csWitness}; - use bn_254::Fq; + use zkstd::common::CurveGroup; use zkstd::matrix::DenseVectors; use zkstd::r1cs::test::example_r1cs; @@ -81,14 +96,14 @@ mod tests { let r = FieldAssignment::witness(&mut cs, r.into()); let instance1 = RelaxedR1csInstanceAssignment::witness(&mut cs, &instance_to_fold); let instance2 = RelaxedR1csInstanceAssignment::witness(&mut cs, &running_instance); - let instance3 = RelaxedR1csInstanceAssignment::witness(&mut cs, &instance); - - let nifs_check = NifsCircuit::verify(&mut cs, r, instance1, instance2, instance3); - FieldAssignment::enforce_eq_constant( + let commit_t = PointAssignment::witness( &mut cs, - &FieldAssignment::from(&nifs_check), - &Fq::one(), + commit_t.get_x(), + commit_t.get_y(), + commit_t.is_identity(), ); + + let instance3 = NifsCircuit::verify(&mut cs, r, instance1, instance2, commit_t); assert!(cs.is_sat()); } } diff --git a/nova/src/gadget/relaxed_instance.rs b/nova/src/gadget/relaxed_instance.rs index 09cf76bd..fdd8e4ff 100644 --- a/nova/src/gadget/relaxed_instance.rs +++ b/nova/src/gadget/relaxed_instance.rs @@ -3,7 +3,9 @@ use crate::relaxed_r1cs::RelaxedR1csInstance; use crate::circuit::MimcROCircuit; use crate::driver::scalar_as_base; use crate::hash::MIMC_ROUNDS; -use zkstd::circuit::prelude::{CircuitDriver, FieldAssignment, PointAssignment, R1cs}; +use zkstd::circuit::prelude::{ + BinaryAssignment, CircuitDriver, FieldAssignment, PointAssignment, R1cs, +}; use zkstd::common::CurveGroup; #[derive(Clone)] @@ -11,7 +13,8 @@ pub(crate) struct RelaxedR1csInstanceAssignment { pub(crate) commit_w: PointAssignment, pub(crate) commit_e: PointAssignment, pub(crate) u: FieldAssignment, - pub(crate) x: Vec>, + pub(crate) x0: FieldAssignment, + pub(crate) x1: FieldAssignment, } impl RelaxedR1csInstanceAssignment { @@ -39,16 +42,35 @@ impl RelaxedR1csInstanceAssignment { commit_e.is_identity(), ); let u = FieldAssignment::witness(cs, scalar_as_base::(*u)); - let x = x - .iter() - .map(|x| FieldAssignment::witness(cs, scalar_as_base::(x))) - .collect(); + let x0 = FieldAssignment::witness(cs, scalar_as_base::(x[0])); + let x1 = FieldAssignment::witness(cs, scalar_as_base::(x[1])); Self { commit_w, commit_e, u, - x, + x0, + x1, + } + } + + pub fn conditional_select>( + cs: &mut R1cs, + a: &Self, + b: &Self, + condition: &BinaryAssignment, + ) -> Self { + let commit_w = PointAssignment::conditional_select(cs, &a.commit_w, &b.commit_w, condition); + let commit_e = PointAssignment::conditional_select(cs, &a.commit_e, &b.commit_e, condition); + let u = FieldAssignment::conditional_select(cs, &a.u, &b.u, condition); + let x0 = FieldAssignment::conditional_select(cs, &a.x0, &b.x0, condition); + let x1 = FieldAssignment::conditional_select(cs, &a.x1, &b.x1, condition); + Self { + commit_w, + commit_e, + u, + x0, + x1, } } @@ -59,9 +81,8 @@ impl RelaxedR1csInstanceAssignment { transcript.append_point(self.commit_w.clone()); transcript.append_point(self.commit_e.clone()); transcript.append(self.u.clone()); - for x in &self.x { - transcript.append(x.clone()); - } + transcript.append(self.x0.clone()); + transcript.append(self.x1.clone()); } pub(crate) fn hash>( @@ -78,7 +99,8 @@ impl RelaxedR1csInstanceAssignment { z_0, z_i, vec![self.u.clone()], - self.x.clone(), + vec![self.x0.clone()], + vec![self.x1.clone()], vec![ self.commit_e.get_x(), self.commit_e.get_y(), @@ -158,7 +180,7 @@ mod tests { // TODO: Think how to restrict size to 1 FieldAssignment::enforce_eq_constant( &mut cs, - &instance_assignment.x[0], + &instance_assignment.x0, &scalar_as_base::(instance.x[0]), ); diff --git a/nova/src/ivc.rs b/nova/src/ivc.rs index 704e1ed2..3b3c162c 100644 --- a/nova/src/ivc.rs +++ b/nova/src/ivc.rs @@ -1,5 +1,5 @@ use crate::function::FunctionCircuit; -use crate::Prover; +use crate::{Prover, RecursiveProof}; use rand_core::OsRng; use std::marker::PhantomData; @@ -45,15 +45,15 @@ where FC1: FunctionCircuit, FC2: FunctionCircuit, { - pub fn new( + pub fn init( rng: OsRng, pp: &PublicParams, z0_primary: DenseVectors, z0_secondary: DenseVectors, - ) -> Self { + ) -> (Self, RecursiveProof) { let mut cs_primary = R1cs::::default(); let circuit_primary = AugmentedFCircuit::::default(); - circuit_primary.generate(&mut cs_primary); // get zi_primary + let zi_primary = circuit_primary.generate(&mut cs_primary); // get zi_primary // get u_single_next/w_single_next primary @@ -61,7 +61,7 @@ where let mut cs_secondary = R1cs::::default(); let circuit_secondary = AugmentedFCircuit::::default(); - circuit_secondary.generate(&mut cs_secondary); // get zi_secondary + let zi_secondary = circuit_secondary.generate(&mut cs_secondary); // get u_single_next/w_single_next secondary @@ -70,12 +70,22 @@ where let u_dummy = RelaxedR1csInstance::::dummy(cs_primary.l() - 1); let w_dummy = RelaxedR1csWitness::::dummy(cs_primary.m_l_1(), cs_primary.m()); - Self { + let ivc = Self { i: 0, z0_primary, z0_secondary, - zi_primary: Default::default(), - zi_secondary: Default::default(), + zi_primary: DenseVectors::new( + zi_primary + .into_iter() + .map(|x| x.value(&mut cs_primary)) + .collect(), + ), + zi_secondary: DenseVectors::new( + zi_secondary + .into_iter() + .map(|x| x.value(&mut cs_secondary)) + .collect(), + ), prover_primary, prover_secondary, u_single_secondary: RelaxedR1csInstance::dummy(1), @@ -85,11 +95,30 @@ where u_range_secondary: RelaxedR1csInstance::dummy(1), w_range_secondary: RelaxedR1csWitness::dummy(1, 1), f: PhantomData::default(), - } + }; + let proof = RecursiveProof { + i: 0, + z0_primary: ivc.z0_primary.clone(), + z0_secondary: ivc.z0_secondary.clone(), + zi_primary: ivc.z0_primary.clone(), + zi_secondary: ivc.z0_secondary.clone(), + instances: ( + ( + ivc.u_single_secondary.clone(), + ivc.w_single_secondary.clone(), + ), + (ivc.u_range_primary.clone(), ivc.w_range_primary.clone()), + (ivc.u_range_secondary.clone(), ivc.w_range_secondary.clone()), + ), + marker: Default::default(), + }; + (ivc, proof) } - pub fn prove_step(&mut self, pp: &PublicParams) { - //-> RecursiveProof { + pub fn prove_step( + &mut self, + pp: &PublicParams, + ) -> RecursiveProof { if self.i == 0 { self.i = 1; // return @@ -105,6 +134,7 @@ where let mut cs = R1cs::::default(); let circuit_primary = AugmentedFCircuit:: { + is_primary: true, i: self.i, z_0: self.z0_primary.clone(), z_i: Some(self.zi_primary.clone()), @@ -129,7 +159,8 @@ where ); let mut cs = R1cs::::default(); - let circuit_primary = AugmentedFCircuit:: { + let circuit_secondary = AugmentedFCircuit:: { + is_primary: false, i: self.i, z_0: self.z0_secondary.clone(), z_i: Some(self.zi_secondary.clone()), @@ -141,7 +172,7 @@ where x: E1::Base::zero(), }; - circuit_primary.generate(&mut cs); // zi_secondary + circuit_secondary.generate(&mut cs); // zi_secondary // get u_single_next/w_single_next secondary @@ -156,20 +187,25 @@ where // self.zi_primary = zi_primary; // self.zi_secondary = zi_secondary; - // // ((Ui+1, Wi+1), (ui+1, wi+1)) - // let pair = ( - // (self.u_range.clone(), self.w_range.clone()), - // (self.u_single.clone(), self.w_single.clone()), - // ); - // - // RecursiveProof { - // i: self.i, - // z0: self.z0.clone(), - // zi: self.zi.clone(), - // r1cs: self.r1cs.clone(), - // pair, - // marker: Default::default(), - // } + RecursiveProof { + i: 0, + z0_primary: self.z0_primary.clone(), + z0_secondary: self.z0_secondary.clone(), + zi_primary: self.z0_primary.clone(), + zi_secondary: self.z0_secondary.clone(), + instances: ( + ( + self.u_single_secondary.clone(), + self.w_single_secondary.clone(), + ), + (self.u_range_primary.clone(), self.w_range_primary.clone()), + ( + self.u_range_secondary.clone(), + self.w_range_secondary.clone(), + ), + ), + marker: Default::default(), + } } } @@ -180,8 +216,8 @@ where FC1: FunctionCircuit, FC2: FunctionCircuit, { - r1cs_shape_primary: R1csShape, - r1cs_shape_secondary: R1csShape, + pub r1cs_shape_primary: R1csShape, + pub r1cs_shape_secondary: R1csShape, marker: PhantomData<(FC1, FC2)>, } @@ -239,30 +275,18 @@ mod tests { let z0_primary = DenseVectors::new(vec![Fr::from(3)]); let z0_secondary = DenseVectors::new(vec![Fq::from(3)]); - let mut ivc = - Ivc::, ExampleFunction>::new( - OsRng, - &pp, - z0_primary, - z0_secondary, - ); - // let proof_0 = RecursiveProof { - // i: 0, - // z0: ivc.z0.clone(), - // zi: ivc.zi.clone(), - // r1cs: ivc.r1cs.clone(), - // pair: ( - // (ivc.u_range.clone(), ivc.w_range.clone()), - // (ivc.u_single.clone(), ivc.w_single.clone()), - // ), - // marker: Default::default(), - // }; - // - // assert!(proof_0.verify()); + let (mut ivc, proof_0) = Ivc::< + Bn254Driver, + GrumpkinDriver, + ExampleFunction, + ExampleFunction, + >::init(OsRng, &pp, z0_primary, z0_secondary); + + assert!(proof_0.verify(&pp)); for i in 0..2 { - ivc.prove_step(&pp); - // assert!(proof.verify()); + let proof = ivc.prove_step(&pp); + assert!(proof.verify(&pp)); } } } diff --git a/nova/src/proof.rs b/nova/src/proof.rs index 3efe2a87..7383a5fd 100644 --- a/nova/src/proof.rs +++ b/nova/src/proof.rs @@ -1,64 +1,71 @@ use crate::relaxed_r1cs::{RelaxedR1csInstance, RelaxedR1csWitness}; use std::marker::PhantomData; -use crate::relaxed_r1cs::R1csShape; +use crate::driver::scalar_as_base; +use crate::function::FunctionCircuit; +use crate::ivc::PublicParams; use zkstd::circuit::prelude::CircuitDriver; -use zkstd::common::{Group, Ring}; use zkstd::matrix::DenseVectors; #[allow(clippy::type_complexity)] -pub struct RecursiveProof +pub struct RecursiveProof where E1: CircuitDriver::Scalar>, E2: CircuitDriver::Scalar>, + FC1: FunctionCircuit, + FC2: FunctionCircuit, { pub(crate) i: usize, - pub(crate) z0: DenseVectors, - pub(crate) zi: DenseVectors, - pub(crate) r1cs: R1csShape, - pub(crate) pair: ( - // instance-witness pair of instance to be folded - (RelaxedR1csInstance, RelaxedR1csWitness), - // instance-witness pair of running instance + pub(crate) z0_primary: DenseVectors, + pub(crate) z0_secondary: DenseVectors, + pub(crate) zi_primary: DenseVectors, + pub(crate) zi_secondary: DenseVectors, + pub(crate) instances: ( + // u_single/w_single secondary + (RelaxedR1csInstance, RelaxedR1csWitness), + // u_range/w_range primary (RelaxedR1csInstance, RelaxedR1csWitness), + // u_range/w_range secondary + (RelaxedR1csInstance, RelaxedR1csWitness), ), - pub(crate) marker: PhantomData, + pub(crate) marker: PhantomData<(FC1, FC2)>, } -impl RecursiveProof +impl RecursiveProof where E1: CircuitDriver::Scalar>, E2: CircuitDriver::Scalar>, + FC1: FunctionCircuit, + FC2: FunctionCircuit, { - pub fn verify(&self) -> bool { - let Self { - i, - z0, - zi, - r1cs, - pair, - .. - } = self; - let ((l_ui, l_wi), (s_ui, s_wi)) = pair; - - if *i == 0 { - // check if z vector is the same - z0 == zi - } else { - // check that ui.x = hash(vk, i, z0, zi, Ui) - let expected_x = l_ui.hash::(*i, z0, zi); - let check_hash = expected_x == s_ui.x[0].into(); - - // check if folded instance has default error vectors and scalar - let check_defaults = - s_ui.commit_e == E1::Affine::ADDITIVE_IDENTITY && s_ui.u == E1::Scalar::one(); - - // check if instance-witness pair satisfy - - let is_instance_witness_sat = - self.r1cs.is_sat(l_ui, l_wi) && self.r1cs.is_sat(s_ui, s_wi); + pub fn verify(&self, pp: &PublicParams) -> bool { + let ( + (l_u_secondary, l_w_secondary), + (r_U_primary, r_W_primary), + (r_U_secondary, r_W_secondary), + ) = self.instances.clone(); + if l_u_secondary.x.len() != 2 || r_U_primary.x.len() != 2 || r_U_secondary.x.len() != 2 { + return false; + } + let (hash_primary, hash_secondary) = { + ( + r_U_secondary.hash::(self.i, &self.z0_primary, &self.zi_primary), + r_U_primary.hash::(self.i, &self.zi_secondary, &self.zi_secondary), + ) + }; - check_hash && check_defaults && is_instance_witness_sat + if hash_primary != l_u_secondary.x[0] + || hash_secondary != scalar_as_base::(l_u_secondary.x[1]) + { + return false; } + + pp.r1cs_shape_primary.is_sat(&r_U_primary, &r_W_primary) + && pp + .r1cs_shape_secondary + .is_sat(&r_U_secondary, &r_W_secondary) + && pp + .r1cs_shape_secondary + .is_sat(&l_u_secondary, &l_w_secondary) } } diff --git a/nova/src/relaxed_r1cs/instance.rs b/nova/src/relaxed_r1cs/instance.rs index 489756ef..7d8c02ff 100644 --- a/nova/src/relaxed_r1cs/instance.rs +++ b/nova/src/relaxed_r1cs/instance.rs @@ -86,18 +86,18 @@ impl RelaxedR1csInstance { pub fn hash>( &self, i: usize, - z_0: &DenseVectors, - z_i: &DenseVectors, - ) -> C::Base { + z_0: &DenseVectors, + z_i: &DenseVectors, + ) -> C::Scalar { let commit_e = self.commit_e.to_extended(); let commit_w = self.commit_w.to_extended(); - MimcRO::::default().hash_vec( + MimcRO::::default().hash_vec( vec![ - vec![C::Scalar::from(i as u64)], + vec![E::Scalar::from(i as u64)], z_0.get(), z_i.get(), - vec![self.u], - self.x.get(), + vec![scalar_as_base::(self.u)], + self.x.iter().map(|x| scalar_as_base::(x)).collect(), vec![ commit_e.get_x().into(), commit_e.get_y().into(), diff --git a/zkstd/src/circuit/gadget/curve.rs b/zkstd/src/circuit/gadget/curve.rs index 1f59bef5..4fc623fd 100644 --- a/zkstd/src/circuit/gadget/curve.rs +++ b/zkstd/src/circuit/gadget/curve.rs @@ -159,6 +159,19 @@ impl PointAssignment { res } + pub fn conditional_select>( + cs: &mut R1cs, + a: &Self, + b: &Self, + condition: &BinaryAssignment, + ) -> PointAssignment { + let x = FieldAssignment::conditional_select(cs, &a.x, &b.x, condition); + let y = FieldAssignment::conditional_select(cs, &a.y, &b.y, condition); + let z = FieldAssignment::conditional_select(cs, &a.z, &b.z, condition); + + Self { x, y, z } + } + pub fn select_identity>( &self, cs: &mut R1cs, diff --git a/zkstd/src/circuit/gadget/field.rs b/zkstd/src/circuit/gadget/field.rs index c8f907f9..3e45d55a 100644 --- a/zkstd/src/circuit/gadget/field.rs +++ b/zkstd/src/circuit/gadget/field.rs @@ -25,6 +25,18 @@ impl FieldAssignment { Self(SparseRow::from(wire)) } + pub fn inputize>(cs: &mut R1cs, witness: Self) -> Self { + let wire = cs.public_wire(); + let value = witness.inner().evaluate(&cs.x, &cs.w); + cs.x.push(value); + + Self(SparseRow::from(wire)) + } + + pub fn value>(&self, cs: &mut R1cs) -> F { + self.inner().evaluate(&cs.x, &cs.w) + } + pub fn constant(constant: &F) -> Self { Self(SparseRow(vec![(Wire::ONE, *constant)])) } @@ -163,6 +175,24 @@ impl FieldAssignment { FieldAssignment::enforce_eq_constant(cs, &mul, &F::zero()); } + pub fn conditional_select>( + cs: &mut R1cs, + x: &Self, + y: &Self, + condition: &BinaryAssignment, + ) -> FieldAssignment { + let c = if cs[*condition.inner()] == F::one() { + x + } else { + y + }; + + let left = FieldAssignment::mul(cs, &(x - y), &FieldAssignment::from(condition)); + + FieldAssignment::enforce_eq(cs, &left, &(c - y)); + c.clone() + } + pub fn is_eq>( cs: &mut R1cs, x: &Self, From d147f8ea6ca41c4b07b1f1f83744aec78e8f3060 Mon Sep 17 00:00:00 2001 From: Kirill Karbushev Date: Thu, 14 Dec 2023 10:05:15 +0900 Subject: [PATCH 08/17] fix scalar multiplication for PointAssignment --- nova/src/driver.rs | 3 +-- nova/src/proof.rs | 26 +++++++++++++++----------- zkstd/src/circuit/gadget/curve.rs | 2 +- zkstd/src/circuit/gadget/field.rs | 2 +- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/nova/src/driver.rs b/nova/src/driver.rs index 4e2b6be7..d202d6bb 100644 --- a/nova/src/driver.rs +++ b/nova/src/driver.rs @@ -239,11 +239,10 @@ mod grumpkin_gadget_tests { fn curve_scalar_mul_test() { for _ in 0..100 { let mut cs: R1cs = R1cs::default(); - // Base == GrumpkingScalar let x = Scalar::random(OsRng); let p = G1Affine::random(OsRng); - let x_assignment = FieldAssignment::instance(&mut cs, x); // Fr + let x_assignment = FieldAssignment::instance(&mut cs, x); let p_assignment = PointAssignment::instance(&mut cs, p); let expected = p * Base::from(x); diff --git a/nova/src/proof.rs b/nova/src/proof.rs index 7383a5fd..eed1938b 100644 --- a/nova/src/proof.rs +++ b/nova/src/proof.rs @@ -40,32 +40,36 @@ where { pub fn verify(&self, pp: &PublicParams) -> bool { let ( - (l_u_secondary, l_w_secondary), - (r_U_primary, r_W_primary), - (r_U_secondary, r_W_secondary), + (u_single_secondary, w_single_secondary), + (u_range_primary, w_range_primary), + (u_range_secondary, w_range_secondary), ) = self.instances.clone(); - if l_u_secondary.x.len() != 2 || r_U_primary.x.len() != 2 || r_U_secondary.x.len() != 2 { + if u_single_secondary.x.len() != 2 + || u_range_primary.x.len() != 2 + || u_range_secondary.x.len() != 2 + { return false; } let (hash_primary, hash_secondary) = { ( - r_U_secondary.hash::(self.i, &self.z0_primary, &self.zi_primary), - r_U_primary.hash::(self.i, &self.zi_secondary, &self.zi_secondary), + u_range_secondary.hash::(self.i, &self.z0_primary, &self.zi_primary), + u_range_primary.hash::(self.i, &self.zi_secondary, &self.zi_secondary), ) }; - if hash_primary != l_u_secondary.x[0] - || hash_secondary != scalar_as_base::(l_u_secondary.x[1]) + if hash_primary != u_single_secondary.x[0] + || hash_secondary != scalar_as_base::(u_single_secondary.x[1]) { return false; } - pp.r1cs_shape_primary.is_sat(&r_U_primary, &r_W_primary) + pp.r1cs_shape_primary + .is_sat(&u_range_primary, &w_range_primary) && pp .r1cs_shape_secondary - .is_sat(&r_U_secondary, &r_W_secondary) + .is_sat(&u_range_secondary, &w_range_secondary) && pp .r1cs_shape_secondary - .is_sat(&l_u_secondary, &l_w_secondary) + .is_sat(&u_single_secondary, &w_single_secondary) } } diff --git a/zkstd/src/circuit/gadget/curve.rs b/zkstd/src/circuit/gadget/curve.rs index 4fc623fd..dca1aa0a 100644 --- a/zkstd/src/circuit/gadget/curve.rs +++ b/zkstd/src/circuit/gadget/curve.rs @@ -34,7 +34,7 @@ impl PointAssignment { pub fn identity() -> Self { let x = FieldAssignment::constant(&F::zero()); let y = FieldAssignment::constant(&F::one()); - let z = FieldAssignment::constant(&F::one()); + let z = FieldAssignment::constant(&F::zero()); Self { x, y, z } } diff --git a/zkstd/src/circuit/gadget/field.rs b/zkstd/src/circuit/gadget/field.rs index 3e45d55a..4fc32ab9 100644 --- a/zkstd/src/circuit/gadget/field.rs +++ b/zkstd/src/circuit/gadget/field.rs @@ -33,7 +33,7 @@ impl FieldAssignment { Self(SparseRow::from(wire)) } - pub fn value>(&self, cs: &mut R1cs) -> F { + pub fn value>(&self, cs: &R1cs) -> F { self.inner().evaluate(&cs.x, &cs.w) } From 23f7edbe2fb07b09664efa5a718df06375a7b7f4 Mon Sep 17 00:00:00 2001 From: Kirill Karbushev Date: Thu, 14 Dec 2023 14:01:18 +0900 Subject: [PATCH 09/17] fix unit tests for nova --- nova/src/circuit/augmented.rs | 23 +++---- nova/src/gadget/relaxed_instance.rs | 96 +++++++++++++++-------------- nova/src/hash.rs | 2 +- nova/src/prover.rs | 22 ++++--- nova/src/relaxed_r1cs.rs | 25 ++++++-- nova/src/relaxed_r1cs/instance.rs | 2 +- nova/src/verifier.rs | 18 +++--- 7 files changed, 105 insertions(+), 83 deletions(-) diff --git a/nova/src/circuit/augmented.rs b/nova/src/circuit/augmented.rs index ded3e6f2..971f364a 100644 --- a/nova/src/circuit/augmented.rs +++ b/nova/src/circuit/augmented.rs @@ -28,13 +28,13 @@ pub struct AugmentedFCircuit> { impl> Default for AugmentedFCircuit { fn default() -> Self { Self { - is_primary: false, + is_primary: true, i: 0, z_0: DenseVectors::zero(1), z_i: Some(DenseVectors::zero(1)), - u_single: Some(RelaxedR1csInstance::dummy(1)), - u_range: Some(RelaxedR1csInstance::dummy(1)), - u_range_next: Some(RelaxedR1csInstance::dummy(1)), + u_single: Some(RelaxedR1csInstance::dummy(2)), + u_range: Some(RelaxedR1csInstance::dummy(2)), + u_range_next: Some(RelaxedR1csInstance::dummy(2)), commit_t: Some(C::Affine::ADDITIVE_IDENTITY), f: Default::default(), x: C::Base::zero(), @@ -62,7 +62,7 @@ impl> AugmentedFCircuit { .map(|x| FieldAssignment::witness(cs, x)) .collect::>(); - let u_dummy_native = RelaxedR1csInstance::::dummy(1); + let u_dummy_native = RelaxedR1csInstance::::dummy(2); let u_dummy = RelaxedR1csInstanceAssignment::witness(cs, &u_dummy_native); let u_single = RelaxedR1csInstanceAssignment::witness( cs, @@ -181,15 +181,10 @@ mod tests { let mut cs = R1cs::::default(); let augmented_circuit = AugmentedFCircuit::>::default(); augmented_circuit.generate(&mut cs); + let shape = R1csShape::from(cs); + let u_dummy = RelaxedR1csInstance::dummy(shape.l()); + let w_dummy = RelaxedR1csWitness::dummy(shape.m_l_1(), shape.m()); - assert!(cs.is_sat()); - - assert_eq!(cs.l(), 2); - - let u_dummy = RelaxedR1csInstance::dummy(cs.l() - 1); - let w_dummy = RelaxedR1csWitness::dummy(cs.m_l_1(), cs.m()); - - let running_r1cs = R1csShape::from(cs); - assert!(running_r1cs.is_sat(&u_dummy, &w_dummy)); + assert!(shape.is_sat(&u_dummy, &w_dummy)); } } diff --git a/nova/src/gadget/relaxed_instance.rs b/nova/src/gadget/relaxed_instance.rs index fdd8e4ff..10a3c836 100644 --- a/nova/src/gadget/relaxed_instance.rs +++ b/nova/src/gadget/relaxed_instance.rs @@ -121,45 +121,49 @@ impl RelaxedR1csInstanceAssignment { mod tests { use super::*; use crate::driver::{Bn254Driver, GrumpkinDriver}; - use bn_254::Fq; + use bn_254::{Fq, Fr}; use grumpkin::Affine; use rand_core::OsRng; - use zkstd::common::Group; + use zkstd::common::{BNAffine, Group}; use zkstd::matrix::DenseVectors; - // #[test] - // fn instance_assignment_hash() { - // let mut cs: R1cs = R1cs::default(); - // let instance = RelaxedR1csInstance:: { - // commit_e: Affine::random(OsRng), - // u: Fq::random(OsRng), - // commit_w: Affine::random(OsRng), - // x: DenseVectors::new(vec![Fq::random(OsRng); 1]), - // }; - // - // let i = 3; - // let z_0 = DenseVectors::new(vec![Fr::from(3)]); - // let z_i = z_0.clone(); - // - // let hash = instance.hash::(i, &z_0, &z_i); - // - // let i_assignment = FieldAssignment::witness(&mut cs, Fr::from(i as u64)); - // let z_0_assignment = z_0 - // .iter() - // .map(|x| FieldAssignment::witness(&mut cs, x)) - // .collect::>(); - // let z_i_assignment = z_i - // .iter() - // .map(|x| FieldAssignment::witness(&mut cs, x)) - // .collect::>(); - // let instance_assignment = RelaxedR1csInstanceAssignment::witness(&mut cs, &instance); - // - // let hash_circuit = - // instance_assignment.hash(&mut cs, i_assignment, z_0_assignment, z_i_assignment); - // - // FieldAssignment::enforce_eq_constant(&mut cs, &hash_circuit, &hash); - // assert!(cs.is_sat()); - // } + #[test] + fn instance_assignment_hash() { + let mut cs: R1cs = R1cs::default(); + let instance = RelaxedR1csInstance:: { + commit_e: Affine::random(OsRng), + u: Fq::random(OsRng), + commit_w: Affine::random(OsRng), + x: DenseVectors::new(vec![Fq::random(OsRng); 2]), + }; + + let i = 3; + let z_0 = DenseVectors::new(vec![Fr::from(3)]); + let z_i = z_0.clone(); + + let hash = instance.hash::(i, &z_0, &z_i); + + let i_assignment = FieldAssignment::witness(&mut cs, Fr::from(i as u64)); + let z_0_assignment = z_0 + .iter() + .map(|x| FieldAssignment::witness(&mut cs, x)) + .collect::>(); + let z_i_assignment = z_i + .iter() + .map(|x| FieldAssignment::witness(&mut cs, x)) + .collect::>(); + let instance_assignment = RelaxedR1csInstanceAssignment::witness(&mut cs, &instance); + + let hash_circuit = + instance_assignment.hash(&mut cs, i_assignment, z_0_assignment, z_i_assignment); // E2::Base + + FieldAssignment::enforce_eq_constant( + &mut cs, + &hash_circuit, + &scalar_as_base::(hash), + ); + assert!(cs.is_sat()); + } #[test] fn relaxed_instance_assignment() { @@ -168,7 +172,7 @@ mod tests { commit_e: Affine::random(OsRng), u: Fq::random(OsRng), commit_w: Affine::random(OsRng), - x: DenseVectors::new(vec![Fq::random(OsRng); 1]), + x: DenseVectors::new(vec![Fq::random(OsRng); 2]), }; let instance_assignment = RelaxedR1csInstanceAssignment::witness(&mut cs, &instance); @@ -177,21 +181,23 @@ mod tests { &instance_assignment.u, &scalar_as_base::(instance.u), ); - // TODO: Think how to restrict size to 1 FieldAssignment::enforce_eq_constant( &mut cs, &instance_assignment.x0, &scalar_as_base::(instance.x[0]), ); + FieldAssignment::enforce_eq_constant( + &mut cs, + &instance_assignment.x1, + &scalar_as_base::(instance.x[1]), + ); - // Research about curve cycles - - // instance_assignment - // .commit_e - // .assert_equal_public_point(&mut cs, &instance.commit_e); - // instance_assignment - // .commit_w - // .assert_equal_public_point(&mut cs, &instance.commit_w); + instance_assignment + .commit_e + .assert_equal_public_point(&mut cs, instance.commit_e.to_extended()); + instance_assignment + .commit_w + .assert_equal_public_point(&mut cs, instance.commit_w.to_extended()); assert!(cs.is_sat()); } diff --git a/nova/src/hash.rs b/nova/src/hash.rs index cbaf98fd..0fd15858 100644 --- a/nova/src/hash.rs +++ b/nova/src/hash.rs @@ -7,7 +7,7 @@ use zkstd::common::{BNAffine, IntGroup, PrimeField, Ring}; /// Amount of rounds calculated for the 254 bit field. /// Doubled due to the usage of Feistel mode with zero key. -pub(crate) const MIMC_ROUNDS: usize = 322; +pub(crate) const MIMC_ROUNDS: usize = 2; pub(crate) struct Mimc { pub(crate) constants: [F; ROUND], diff --git a/nova/src/prover.rs b/nova/src/prover.rs index 1624cbc5..1b6696fe 100644 --- a/nova/src/prover.rs +++ b/nova/src/prover.rs @@ -105,7 +105,9 @@ pub(crate) mod tests { use crate::driver::GrumpkinDriver; use crate::hash::{MimcRO, MIMC_ROUNDS}; - use crate::relaxed_r1cs::{R1csShape, RelaxedR1csInstance, RelaxedR1csWitness}; + use crate::relaxed_r1cs::{ + r1cs_instance_and_witness, R1csShape, RelaxedR1csInstance, RelaxedR1csWitness, + }; use crate::Verifier; use zkstd::common::OsRng; use zkstd::matrix::DenseVectors; @@ -122,12 +124,15 @@ pub(crate) mod tests { let mut transcript = MimcRO::::default(); let r1cs_1 = example_r1cs::(4); + let shape = R1csShape::from(r1cs_1.clone()); let r1cs_2 = example_r1cs::(3); - let instance1 = RelaxedR1csInstance::new(DenseVectors::new(r1cs_1.x())); - let witness1 = RelaxedR1csWitness::new(DenseVectors::new(r1cs_1.w()), r1cs_1.m()); - let instance2 = RelaxedR1csInstance::new(DenseVectors::new(r1cs_2.x())); - let witness2 = RelaxedR1csWitness::new(DenseVectors::new(r1cs_2.w()), r1cs_2.m()); + let (x1, w1) = r1cs_instance_and_witness(&r1cs_1, &shape); + let instance1 = RelaxedR1csInstance::new(DenseVectors::new(x1)); + let witness1 = RelaxedR1csWitness::new(DenseVectors::new(w1), shape.m()); + let (x2, w2) = r1cs_instance_and_witness(&r1cs_2, &shape); + let instance2 = RelaxedR1csInstance::new(DenseVectors::new(x2)); + let witness2 = RelaxedR1csWitness::new(DenseVectors::new(w2), shape.m()); let (folded_instance, folded_witness, commit_t) = prover.prove(&instance1, &witness1, &instance2, &witness2); @@ -160,10 +165,9 @@ pub(crate) mod tests { assert_eq!(z3, z3_aux); // check that relations hold for the 2 inputted instances and the folded one - let r1cs = R1csShape::from(r1cs_1); - assert!(r1cs.is_sat(&instance1, &witness1)); - assert!(r1cs.is_sat(&instance2, &witness2)); - assert!(r1cs.is_sat(&folded_instance, &folded_witness)); + assert!(shape.is_sat(&instance1, &witness1)); + assert!(shape.is_sat(&instance2, &witness2)); + assert!(shape.is_sat(&folded_instance, &folded_witness)); // next equalities should hold since we started from two cmE of zero-vector E's assert_eq!(verified_instance.commit_e, (commit_t * r).into()); diff --git a/nova/src/relaxed_r1cs.rs b/nova/src/relaxed_r1cs.rs index 67c47522..304fa557 100644 --- a/nova/src/relaxed_r1cs.rs +++ b/nova/src/relaxed_r1cs.rs @@ -18,12 +18,24 @@ pub struct R1csShape { c: SparseMatrix, } +pub(crate) fn r1cs_instance_and_witness( + cs: &R1cs, + shape: &R1csShape, +) -> (Vec, Vec) { + assert_eq!(cs.m_l_1(), shape.m_l_1()); + let w = cs.w(); + let x = cs.x()[1..].to_vec(); + assert_eq!(x.len(), shape.l()); + + (x, w) +} + impl From> for R1csShape { fn from(value: R1cs) -> Self { let (a, b, c) = value.matrices(); Self { m: value.m(), - instance_length: value.l(), + instance_length: value.l() - 1, witness_length: value.m_l_1(), a, b, @@ -95,7 +107,7 @@ impl R1csShape { #[cfg(test)] mod tests { - use super::{R1csShape, RelaxedR1csInstance, RelaxedR1csWitness}; + use super::{r1cs_instance_and_witness, R1csShape, RelaxedR1csInstance, RelaxedR1csWitness}; use crate::driver::GrumpkinDriver; use zkstd::circuit::prelude::R1cs; @@ -106,10 +118,11 @@ mod tests { fn relaxed_r1cs_test() { for i in 1..10 { let r1cs: R1cs = example_r1cs(i); - let instance = RelaxedR1csInstance::new(DenseVectors::new(r1cs.x())); - let witness = RelaxedR1csWitness::new(DenseVectors::new(r1cs.w()), r1cs.m()); - let relaxed_r1cs = R1csShape::from(r1cs); - assert!(relaxed_r1cs.is_sat(&instance, &witness)) + let shape = R1csShape::from(r1cs.clone()); + let (x, w) = r1cs_instance_and_witness(&r1cs, &shape); + let instance = RelaxedR1csInstance::new(DenseVectors::new(x)); + let witness = RelaxedR1csWitness::new(DenseVectors::new(w), shape.m()); + assert!(shape.is_sat(&instance, &witness)) } } } diff --git a/nova/src/relaxed_r1cs/instance.rs b/nova/src/relaxed_r1cs/instance.rs index 7d8c02ff..5b076e0a 100644 --- a/nova/src/relaxed_r1cs/instance.rs +++ b/nova/src/relaxed_r1cs/instance.rs @@ -22,7 +22,7 @@ impl RelaxedR1csInstance { commit_w: C::Affine::ADDITIVE_IDENTITY, commit_e: C::Affine::ADDITIVE_IDENTITY, u: C::Scalar::one(), - x: DenseVectors::new(x.get()[1..].to_vec()), + x, } } diff --git a/nova/src/verifier.rs b/nova/src/verifier.rs index f0365f7a..5287b7f4 100644 --- a/nova/src/verifier.rs +++ b/nova/src/verifier.rs @@ -32,21 +32,25 @@ mod tests { use zkstd::matrix::DenseVectors; use crate::driver::GrumpkinDriver; - use crate::relaxed_r1cs::{R1csShape, RelaxedR1csInstance, RelaxedR1csWitness}; + use crate::relaxed_r1cs::{ + r1cs_instance_and_witness, R1csShape, RelaxedR1csInstance, RelaxedR1csWitness, + }; use zkstd::r1cs::test::example_r1cs; #[test] fn recursive_nifs_test() { let prover = example_prover(); let r1cs = example_r1cs::(1); - let running_instance = RelaxedR1csInstance::new(DenseVectors::new(r1cs.x())); - let running_witness = RelaxedR1csWitness::new(DenseVectors::new(r1cs.w()), r1cs.m()); + let shape = R1csShape::from(r1cs.clone()); + let (x, w) = r1cs_instance_and_witness(&r1cs, &shape); + let running_instance = RelaxedR1csInstance::new(DenseVectors::new(x)); + let running_witness = RelaxedR1csWitness::new(DenseVectors::new(w), shape.m()); for i in 1..10 { let r1cs_i = example_r1cs::(i); - let instance_to_fold = RelaxedR1csInstance::new(DenseVectors::new(r1cs_i.x())); - let witness_to_fold = - RelaxedR1csWitness::new(DenseVectors::new(r1cs_i.w()), r1cs_i.m()); + let (x, w) = r1cs_instance_and_witness(&r1cs_i, &shape); + let instance_to_fold = RelaxedR1csInstance::new(DenseVectors::new(x)); + let witness_to_fold = RelaxedR1csWitness::new(DenseVectors::new(w), shape.m()); let (instance, witness, commit_t) = prover.prove( &instance_to_fold, @@ -57,7 +61,7 @@ mod tests { let verified_instance = Verifier::verify(commit_t, &instance_to_fold, &running_instance); assert_eq!(instance, verified_instance); - assert!(R1csShape::from(r1cs_i).is_sat(&instance, &witness)); + assert!(shape.is_sat(&instance, &witness)); } } } From 43fab259b0f8c73d70d53200240c0d0695f82eea Mon Sep 17 00:00:00 2001 From: Kirill Karbushev Date: Thu, 14 Dec 2023 16:03:37 +0900 Subject: [PATCH 10/17] fix proof generation, so the hash check matches --- nova/src/circuit/augmented.rs | 33 ++++++--- nova/src/hash.rs | 2 +- nova/src/ivc.rs | 133 +++++++++++++++++++++------------- nova/src/proof.rs | 15 +++- nova/src/relaxed_r1cs.rs | 1 + nova/src/test.rs | 2 + 6 files changed, 120 insertions(+), 66 deletions(-) diff --git a/nova/src/circuit/augmented.rs b/nova/src/circuit/augmented.rs index 971f364a..7cbd07f8 100644 --- a/nova/src/circuit/augmented.rs +++ b/nova/src/circuit/augmented.rs @@ -19,10 +19,8 @@ pub struct AugmentedFCircuit> { pub z_i: Option>, pub u_single: Option>, pub u_range: Option>, - pub u_range_next: Option>, // Remove pub commit_t: Option, pub f: PhantomData, - pub x: C::Base, // Remove } impl> Default for AugmentedFCircuit { @@ -34,10 +32,8 @@ impl> Default for AugmentedFCircu z_i: Some(DenseVectors::zero(1)), u_single: Some(RelaxedR1csInstance::dummy(2)), u_range: Some(RelaxedR1csInstance::dummy(2)), - u_range_next: Some(RelaxedR1csInstance::dummy(2)), commit_t: Some(C::Affine::ADDITIVE_IDENTITY), f: Default::default(), - x: C::Base::zero(), } } } @@ -94,7 +90,7 @@ impl> AugmentedFCircuit { let not_base_case = FieldAssignment::is_neq(cs, &i, &zero); // base case - let u_single_next_base = if self.is_primary { + let u_range_next_base = if self.is_primary { u_dummy } else { u_single.clone() @@ -132,27 +128,40 @@ impl> AugmentedFCircuit { // (3) Generate Ui+1 ← NIFS.V(vk, U, u, T) let r = Self::get_challenge(cs, &u_range, commit_t.clone()); - let u_single_next_non_base = + let u_range_next_non_base = NifsCircuit::verify(cs, r, u_single.clone(), u_range.clone(), commit_t); - let u_single_next = RelaxedR1csInstanceAssignment::conditional_select( + let u_range_next = RelaxedR1csInstanceAssignment::conditional_select( cs, - &u_single_next_base, - &u_single_next_non_base, + &u_range_next_base, + &u_range_next_non_base, &base_case, ); let z_next = FC::invoke_cs(cs, z_i); - let u_next_x = u_single_next.hash( + // println!( + // "Hash(\n{:?}\n{:?}\n{:?}\n)", + // (&i + &FieldAssignment::constant(&C::Base::one())).value(cs), + // z_0.iter().map(|x| x.value(cs)).collect::>(), + // z_next.iter().map(|x| x.value(cs)).collect::>() + // ); + // println!( + // "U = (\n{:?}\n{:?}\n{:?}\n)", + // u_range_next.u.value(cs), + // u_range_next.x0.value(cs), + // u_range_next.x1.value(cs) + // ); + let u_next_x = u_range_next.hash( cs, &i + &FieldAssignment::constant(&C::Base::one()), z_0, z_next.clone(), ); - let x1 = FieldAssignment::inputize(cs, u_single.x1); - let u_next_x = FieldAssignment::inputize(cs, u_next_x); + let x0 = FieldAssignment::inputize(cs, u_single.x1); + let x1 = FieldAssignment::inputize(cs, u_next_x); + z_next } diff --git a/nova/src/hash.rs b/nova/src/hash.rs index 0fd15858..59e8b91b 100644 --- a/nova/src/hash.rs +++ b/nova/src/hash.rs @@ -7,7 +7,7 @@ use zkstd::common::{BNAffine, IntGroup, PrimeField, Ring}; /// Amount of rounds calculated for the 254 bit field. /// Doubled due to the usage of Feistel mode with zero key. -pub(crate) const MIMC_ROUNDS: usize = 2; +pub(crate) const MIMC_ROUNDS: usize = 0; pub(crate) struct Mimc { pub(crate) constants: [F; ROUND], diff --git a/nova/src/ivc.rs b/nova/src/ivc.rs index 3b3c162c..9963ae3e 100644 --- a/nova/src/ivc.rs +++ b/nova/src/ivc.rs @@ -4,9 +4,10 @@ use rand_core::OsRng; use std::marker::PhantomData; use crate::circuit::AugmentedFCircuit; -use crate::relaxed_r1cs::{R1csShape, RelaxedR1csInstance, RelaxedR1csWitness}; +use crate::relaxed_r1cs::{ + r1cs_instance_and_witness, R1csShape, RelaxedR1csInstance, RelaxedR1csWitness, +}; use zkstd::circuit::prelude::{CircuitDriver, R1cs}; -use zkstd::common::IntGroup; use zkstd::matrix::DenseVectors; pub struct Ivc @@ -50,27 +51,59 @@ where pp: &PublicParams, z0_primary: DenseVectors, z0_secondary: DenseVectors, - ) -> (Self, RecursiveProof) { + ) -> Self { + println!("START"); let mut cs_primary = R1cs::::default(); - let circuit_primary = AugmentedFCircuit::::default(); - let zi_primary = circuit_primary.generate(&mut cs_primary); // get zi_primary + let circuit_primary = AugmentedFCircuit:: { + is_primary: true, + i: 0, + z_0: z0_primary.clone(), + z_i: None, + u_single: None, + u_range: None, + commit_t: None, + f: Default::default(), + }; + let zi_primary = circuit_primary.generate(&mut cs_primary); - // get u_single_next/w_single_next primary + println!("Primary out"); + let (x, w) = r1cs_instance_and_witness(&cs_primary, &pp.r1cs_shape_primary); + let (u_single_next_primary, w_single_next_primary) = ( + RelaxedR1csInstance::new(DenseVectors::new(x)), + RelaxedR1csWitness::new(DenseVectors::new(w), pp.r1cs_shape_primary.m()), + ); let prover_primary = Prover::new(R1csShape::from(cs_primary.clone()), rng); let mut cs_secondary = R1cs::::default(); - let circuit_secondary = AugmentedFCircuit::::default(); + let circuit_secondary = AugmentedFCircuit:: { + is_primary: false, + i: 0, + z_0: z0_secondary.clone(), + z_i: None, + u_single: Some(u_single_next_primary.clone()), + u_range: None, + commit_t: None, + f: Default::default(), + }; let zi_secondary = circuit_secondary.generate(&mut cs_secondary); - // get u_single_next/w_single_next secondary + println!("Secondary out"); + let (x, w) = r1cs_instance_and_witness(&cs_secondary, &pp.r1cs_shape_secondary); + let (u_single_next_secondary, w_single_next_secondary) = ( + RelaxedR1csInstance::new(DenseVectors::new(x)), + RelaxedR1csWitness::new(DenseVectors::new(w), pp.r1cs_shape_secondary.m()), + ); let prover_secondary = Prover::new(R1csShape::from(cs_secondary.clone()), rng); - let u_dummy = RelaxedR1csInstance::::dummy(cs_primary.l() - 1); - let w_dummy = RelaxedR1csWitness::::dummy(cs_primary.m_l_1(), cs_primary.m()); + let u_dummy = RelaxedR1csInstance::::dummy(pp.r1cs_shape_secondary.l()); + let w_dummy = RelaxedR1csWitness::::dummy( + pp.r1cs_shape_secondary.m_l_1(), + pp.r1cs_shape_secondary.m(), + ); - let ivc = Self { + Self { i: 0, z0_primary, z0_secondary, @@ -88,31 +121,14 @@ where ), prover_primary, prover_secondary, - u_single_secondary: RelaxedR1csInstance::dummy(1), - w_single_secondary: RelaxedR1csWitness::dummy(1, 1), - u_range_primary: RelaxedR1csInstance::dummy(1), - w_range_primary: RelaxedR1csWitness::dummy(1, 1), - u_range_secondary: RelaxedR1csInstance::dummy(1), - w_range_secondary: RelaxedR1csWitness::dummy(1, 1), + u_single_secondary: u_single_next_secondary, + w_single_secondary: w_single_next_secondary, + u_range_primary: u_single_next_primary, + w_range_primary: w_single_next_primary, + u_range_secondary: u_dummy, + w_range_secondary: w_dummy, f: PhantomData::default(), - }; - let proof = RecursiveProof { - i: 0, - z0_primary: ivc.z0_primary.clone(), - z0_secondary: ivc.z0_secondary.clone(), - zi_primary: ivc.z0_primary.clone(), - zi_secondary: ivc.z0_secondary.clone(), - instances: ( - ( - ivc.u_single_secondary.clone(), - ivc.w_single_secondary.clone(), - ), - (ivc.u_range_primary.clone(), ivc.w_range_primary.clone()), - (ivc.u_range_secondary.clone(), ivc.w_range_secondary.clone()), - ), - marker: Default::default(), - }; - (ivc, proof) + } } pub fn prove_step( @@ -121,7 +137,25 @@ where ) -> RecursiveProof { if self.i == 0 { self.i = 1; - // return + return RecursiveProof { + i: self.i, + z0_primary: self.z0_primary.clone(), + z0_secondary: self.z0_secondary.clone(), + zi_primary: self.zi_primary.clone(), + zi_secondary: self.zi_secondary.clone(), + instances: ( + ( + self.u_single_secondary.clone(), + self.w_single_secondary.clone(), + ), + (self.u_range_primary.clone(), self.w_range_primary.clone()), + ( + self.u_range_secondary.clone(), + self.w_range_secondary.clone(), + ), + ), + marker: Default::default(), + }; } let z_next = FC1::invoke(&self.zi_primary); let (u_range_next_secondary, w_range_next_secondary, commit_t_secondary) = @@ -140,10 +174,8 @@ where z_i: Some(self.zi_primary.clone()), u_single: Some(self.u_single_secondary.clone()), u_range: Some(self.u_range_secondary.clone()), - u_range_next: None, commit_t: Some(commit_t_secondary), f: Default::default(), - x: E2::Base::zero(), }; circuit_primary.generate(&mut cs); // zi_primary @@ -166,10 +198,8 @@ where z_i: Some(self.zi_secondary.clone()), u_single: Some(self.u_range_primary.clone()), // u_single_next_primary u_range: Some(self.u_range_primary.clone()), - u_range_next: None, commit_t: Some(commit_t_primary), f: Default::default(), - x: E1::Base::zero(), }; circuit_secondary.generate(&mut cs); // zi_secondary @@ -188,7 +218,7 @@ where // self.zi_secondary = zi_secondary; RecursiveProof { - i: 0, + i: self.i, z0_primary: self.z0_primary.clone(), z0_secondary: self.z0_secondary.clone(), zi_primary: self.z0_primary.clone(), @@ -273,18 +303,17 @@ mod tests { ExampleFunction, >::setup(); - let z0_primary = DenseVectors::new(vec![Fr::from(3)]); - let z0_secondary = DenseVectors::new(vec![Fq::from(3)]); - let (mut ivc, proof_0) = Ivc::< - Bn254Driver, - GrumpkinDriver, - ExampleFunction, - ExampleFunction, - >::init(OsRng, &pp, z0_primary, z0_secondary); - - assert!(proof_0.verify(&pp)); + let z0_primary = DenseVectors::new(vec![Fr::from(0)]); + let z0_secondary = DenseVectors::new(vec![Fq::from(0)]); + let mut ivc = + Ivc::, ExampleFunction>::init( + OsRng, + &pp, + z0_primary, + z0_secondary, + ); - for i in 0..2 { + for i in 0..1 { let proof = ivc.prove_step(&pp); assert!(proof.verify(&pp)); } diff --git a/nova/src/proof.rs b/nova/src/proof.rs index eed1938b..842ac3c6 100644 --- a/nova/src/proof.rs +++ b/nova/src/proof.rs @@ -44,25 +44,38 @@ where (u_range_primary, w_range_primary), (u_range_secondary, w_range_secondary), ) = self.instances.clone(); + if u_single_secondary.x.len() != 2 || u_range_primary.x.len() != 2 || u_range_secondary.x.len() != 2 { + println!("Length doesn't match"); return false; } let (hash_primary, hash_secondary) = { ( u_range_secondary.hash::(self.i, &self.z0_primary, &self.zi_primary), - u_range_primary.hash::(self.i, &self.zi_secondary, &self.zi_secondary), + u_range_primary.hash::(self.i, &self.z0_secondary, &self.zi_secondary), ) }; if hash_primary != u_single_secondary.x[0] || hash_secondary != scalar_as_base::(u_single_secondary.x[1]) { + println!("Hash doesn't match"); return false; } + dbg!(pp + .r1cs_shape_primary + .is_sat(&u_range_primary, &w_range_primary)); + dbg!(pp + .r1cs_shape_secondary + .is_sat(&u_range_secondary, &w_range_secondary)); + dbg!(pp + .r1cs_shape_secondary + .is_sat(&u_single_secondary, &w_single_secondary)); + pp.r1cs_shape_primary .is_sat(&u_range_primary, &w_range_primary) && pp diff --git a/nova/src/relaxed_r1cs.rs b/nova/src/relaxed_r1cs.rs index 304fa557..996feca9 100644 --- a/nova/src/relaxed_r1cs.rs +++ b/nova/src/relaxed_r1cs.rs @@ -22,6 +22,7 @@ pub(crate) fn r1cs_instance_and_witness( cs: &R1cs, shape: &R1csShape, ) -> (Vec, Vec) { + println!("Cs.x = {:?}", cs.x()); assert_eq!(cs.m_l_1(), shape.m_l_1()); let w = cs.w(); let x = cs.x()[1..].to_vec(); diff --git a/nova/src/test.rs b/nova/src/test.rs index 1c7dd1af..89e8bd9b 100644 --- a/nova/src/test.rs +++ b/nova/src/test.rs @@ -13,6 +13,7 @@ pub(crate) struct ExampleFunction { impl FunctionCircuit for ExampleFunction { fn invoke(z: &DenseVectors) -> DenseVectors { + // z.clone() let next_z = z[0] * z[0] * z[0] + z[0] + F::from(5); DenseVectors::new(vec![next_z]) } @@ -26,5 +27,6 @@ impl FunctionCircuit for ExampleFunction { let z_i_cube = FieldAssignment::mul(cs, &z_i_square, &z_i[0]); vec![&(&z_i_cube + &z_i[0]) + &five] + // z_i } } From e67935244d4df77411310ca836724e10343161ba Mon Sep 17 00:00:00 2001 From: Kirill Karbushev Date: Thu, 14 Dec 2023 18:01:54 +0900 Subject: [PATCH 11/17] fix ivc base case proof/verification --- nova/src/circuit/augmented.rs | 65 ++++++++++-------------------- nova/src/circuit/nifs.rs | 2 +- nova/src/ivc.rs | 75 ++++++++++++++++++++++++++--------- nova/src/proof.rs | 5 +++ 4 files changed, 82 insertions(+), 65 deletions(-) diff --git a/nova/src/circuit/augmented.rs b/nova/src/circuit/augmented.rs index 7cbd07f8..07ebe991 100644 --- a/nova/src/circuit/augmented.rs +++ b/nova/src/circuit/augmented.rs @@ -5,7 +5,7 @@ use crate::gadget::RelaxedR1csInstanceAssignment; use crate::hash::MIMC_ROUNDS; use crate::relaxed_r1cs::RelaxedR1csInstance; use std::marker::PhantomData; -use zkstd::circuit::prelude::{BinaryAssignment, FieldAssignment, PointAssignment}; +use zkstd::circuit::prelude::{FieldAssignment, PointAssignment}; use zkstd::circuit::CircuitDriver; use zkstd::common::{CurveGroup, Group, IntGroup, Ring}; use zkstd::matrix::DenseVectors; @@ -84,7 +84,6 @@ impl> AugmentedFCircuit { ); let zero = FieldAssignment::constant(&C::Base::zero()); - let bin_true = BinaryAssignment::witness(cs, 1); let base_case = FieldAssignment::is_eq(cs, &i, &zero); let not_base_case = FieldAssignment::is_neq(cs, &i, &zero); @@ -96,37 +95,9 @@ impl> AugmentedFCircuit { u_single.clone() }; - // (1) check that ui.x = hash(vk, i, z0, zi, Ui), where ui.x is the public IO of ui let u_i_x = u_range.hash(cs, i.clone(), z_0.clone(), z_i.clone()); FieldAssignment::conditional_enforce_equal(cs, &u_single.x0, &u_i_x, ¬_base_case); - // // (2) check that (ui.E, ui.u) = (u⊥.E, 1), - // FieldAssignment::conditional_enforce_equal( - // cs, - // &u_single.commit_e.get_x(), - // &u_dummy.commit_e.get_x(), - // ¬_base_case, - // ); - // FieldAssignment::conditional_enforce_equal( - // cs, - // &u_single.commit_e.get_y(), - // &u_dummy.commit_e.get_y(), - // ¬_base_case, - // ); - // FieldAssignment::conditional_enforce_equal( - // cs, - // &u_single.commit_e.get_z(), - // &u_dummy.commit_e.get_z(), - // ¬_base_case, - // ); - // FieldAssignment::conditional_enforce_equal( - // cs, - // &u_single.u, - // &FieldAssignment::constant(&C::Base::one()), - // ¬_base_case, - // ); - - // (3) Generate Ui+1 ← NIFS.V(vk, U, u, T) let r = Self::get_challenge(cs, &u_range, commit_t.clone()); let u_range_next_non_base = NifsCircuit::verify(cs, r, u_single.clone(), u_range.clone(), commit_t); @@ -140,18 +111,6 @@ impl> AugmentedFCircuit { let z_next = FC::invoke_cs(cs, z_i); - // println!( - // "Hash(\n{:?}\n{:?}\n{:?}\n)", - // (&i + &FieldAssignment::constant(&C::Base::one())).value(cs), - // z_0.iter().map(|x| x.value(cs)).collect::>(), - // z_next.iter().map(|x| x.value(cs)).collect::>() - // ); - // println!( - // "U = (\n{:?}\n{:?}\n{:?}\n)", - // u_range_next.u.value(cs), - // u_range_next.x0.value(cs), - // u_range_next.x1.value(cs) - // ); let u_next_x = u_range_next.hash( cs, &i + &FieldAssignment::constant(&C::Base::one()), @@ -181,19 +140,35 @@ impl> AugmentedFCircuit { mod tests { use super::*; use crate::driver::{Bn254Driver, GrumpkinDriver}; - use crate::relaxed_r1cs::{R1csShape, RelaxedR1csWitness}; + use crate::relaxed_r1cs::{r1cs_instance_and_witness, R1csShape, RelaxedR1csWitness}; use crate::test::ExampleFunction; use bn_254::Fr; #[test] fn augmented_circuit_dummies() { let mut cs = R1cs::::default(); - let augmented_circuit = AugmentedFCircuit::>::default(); + let augmented_circuit = AugmentedFCircuit::> { + is_primary: true, + i: 0, + z_0: DenseVectors::new(vec![Fr::zero(); 1]), + z_i: None, + u_single: None, + u_range: None, + commit_t: None, + f: Default::default(), + }; + augmented_circuit.generate(&mut cs); - let shape = R1csShape::from(cs); + let shape = R1csShape::from(cs.clone()); let u_dummy = RelaxedR1csInstance::dummy(shape.l()); let w_dummy = RelaxedR1csWitness::dummy(shape.m_l_1(), shape.m()); + let (x, w) = r1cs_instance_and_witness(&cs, &shape); + let (instance, witness) = ( + RelaxedR1csInstance::::new(DenseVectors::new(x)), + RelaxedR1csWitness::::new(DenseVectors::new(w), shape.m()), + ); assert!(shape.is_sat(&u_dummy, &w_dummy)); + assert!(shape.is_sat(&instance, &witness)); } } diff --git a/nova/src/circuit/nifs.rs b/nova/src/circuit/nifs.rs index e64b8cd5..c519f67c 100644 --- a/nova/src/circuit/nifs.rs +++ b/nova/src/circuit/nifs.rs @@ -32,7 +32,7 @@ impl NifsCircuit { let u_fold = &u_single.u + &r_u; FieldAssignment::enforce_eq_constant( cs, - &(&(&u_fold - &u_single.u) - &r), + &(&(&u_fold - &u_single.u) - &r_u), &C::Base::zero(), ); diff --git a/nova/src/ivc.rs b/nova/src/ivc.rs index 9963ae3e..0156ec09 100644 --- a/nova/src/ivc.rs +++ b/nova/src/ivc.rs @@ -8,6 +8,7 @@ use crate::relaxed_r1cs::{ r1cs_instance_and_witness, R1csShape, RelaxedR1csInstance, RelaxedR1csWitness, }; use zkstd::circuit::prelude::{CircuitDriver, R1cs}; +use zkstd::common::IntGroup; use zkstd::matrix::DenseVectors; pub struct Ivc @@ -73,7 +74,7 @@ where RelaxedR1csWitness::new(DenseVectors::new(w), pp.r1cs_shape_primary.m()), ); - let prover_primary = Prover::new(R1csShape::from(cs_primary.clone()), rng); + let prover_primary = Prover::new(pp.r1cs_shape_primary.clone(), rng); let mut cs_secondary = R1cs::::default(); let circuit_secondary = AugmentedFCircuit:: { @@ -95,7 +96,7 @@ where RelaxedR1csWitness::new(DenseVectors::new(w), pp.r1cs_shape_secondary.m()), ); - let prover_secondary = Prover::new(R1csShape::from(cs_secondary.clone()), rng); + let prover_secondary = Prover::new(pp.r1cs_shape_secondary.clone(), rng); let u_dummy = RelaxedR1csInstance::::dummy(pp.r1cs_shape_secondary.l()); let w_dummy = RelaxedR1csWitness::::dummy( @@ -166,7 +167,7 @@ where &self.w_single_secondary, ); - let mut cs = R1cs::::default(); + let mut cs_primary = R1cs::::default(); let circuit_primary = AugmentedFCircuit:: { is_primary: true, i: self.i, @@ -178,44 +179,62 @@ where f: Default::default(), }; - circuit_primary.generate(&mut cs); // zi_primary + let zi_primary = circuit_primary.generate(&mut cs_primary); - // get u_single_next/w_single_next primary + let (x, w) = r1cs_instance_and_witness(&cs_primary, &pp.r1cs_shape_primary); + let (u_single_next_primary, w_single_next_primary) = ( + RelaxedR1csInstance::new(DenseVectors::new(x)), + RelaxedR1csWitness::new(DenseVectors::new(w), pp.r1cs_shape_primary.m()), + ); let (u_range_next_primary, w_range_next_primary, commit_t_primary) = self.prover_primary.prove( &self.u_range_primary, &self.w_range_primary, - &self.u_range_primary, // u_single_next_primary - &self.w_range_primary, // w_single_next_primary + &u_single_next_primary, // u_single_next_primary + &w_single_next_primary, // w_single_next_primary ); - let mut cs = R1cs::::default(); + let mut cs_secondary = R1cs::::default(); let circuit_secondary = AugmentedFCircuit:: { is_primary: false, i: self.i, z_0: self.z0_secondary.clone(), z_i: Some(self.zi_secondary.clone()), - u_single: Some(self.u_range_primary.clone()), // u_single_next_primary + u_single: Some(u_single_next_primary), u_range: Some(self.u_range_primary.clone()), commit_t: Some(commit_t_primary), f: Default::default(), }; - circuit_secondary.generate(&mut cs); // zi_secondary + let zi_secondary = circuit_secondary.generate(&mut cs_secondary); - // get u_single_next/w_single_next secondary + let (x, w) = r1cs_instance_and_witness(&cs_secondary, &pp.r1cs_shape_secondary); + let (u_single_next_secondary, w_single_next_secondary) = ( + RelaxedR1csInstance::new(DenseVectors::new(x)), + RelaxedR1csWitness::new(DenseVectors::new(w), pp.r1cs_shape_secondary.m()), + ); // update values + self.i += 1; self.u_range_primary = u_range_next_primary; self.w_range_primary = w_range_next_primary; self.u_range_secondary = u_range_next_secondary; self.w_range_secondary = w_range_next_secondary; - // self.u_single_secondary = u_single_next_secondary; - // self.w_single_secondary = w_single_next_secondary; - self.i += 1; - // self.zi_primary = zi_primary; - // self.zi_secondary = zi_secondary; + self.u_single_secondary = u_single_next_secondary; + self.w_single_secondary = w_single_next_secondary; + self.zi_primary = DenseVectors::new( + zi_primary + .into_iter() + .map(|x| x.value(&mut cs_primary)) + .collect(), + ); + self.zi_secondary = DenseVectors::new( + zi_secondary + .into_iter() + .map(|x| x.value(&mut cs_secondary)) + .collect(), + ); RecursiveProof { i: self.i, @@ -260,13 +279,31 @@ where { pub fn setup() -> Self { // Initialize shape for the primary - let circuit_primary = AugmentedFCircuit::::default(); + let circuit_primary = AugmentedFCircuit:: { + is_primary: true, + i: 0, + z_0: DenseVectors::new(vec![E2::Base::zero(); 1]), + z_i: None, + u_single: None, + u_range: None, + commit_t: None, + f: Default::default(), + }; let mut cs = R1cs::::default(); circuit_primary.generate(&mut cs); let r1cs_shape_primary = R1csShape::from(cs); // Initialize shape for the secondary - let circuit_secondary = AugmentedFCircuit::::default(); + let circuit_secondary = AugmentedFCircuit:: { + is_primary: false, + i: 0, + z_0: DenseVectors::new(vec![E1::Base::zero(); 1]), + z_i: None, + u_single: None, + u_range: None, + commit_t: None, + f: Default::default(), + }; let mut cs = R1cs::::default(); circuit_secondary.generate(&mut cs); let r1cs_shape_secondary = R1csShape::from(cs); @@ -313,7 +350,7 @@ mod tests { z0_secondary, ); - for i in 0..1 { + for i in 0..2 { let proof = ivc.prove_step(&pp); assert!(proof.verify(&pp)); } diff --git a/nova/src/proof.rs b/nova/src/proof.rs index 842ac3c6..f82894f8 100644 --- a/nova/src/proof.rs +++ b/nova/src/proof.rs @@ -59,6 +59,9 @@ where ) }; + dbg!(hash_primary); + dbg!(hash_secondary); + if hash_primary != u_single_secondary.x[0] || hash_secondary != scalar_as_base::(u_single_secondary.x[1]) { @@ -66,6 +69,8 @@ where return false; } + println!("r_U_primary = {:#?}", u_range_primary); + dbg!(pp .r1cs_shape_primary .is_sat(&u_range_primary, &w_range_primary)); From f4297c527494b9d7fd3def37604556114e517c15 Mon Sep 17 00:00:00 2001 From: Kirill Karbushev Date: Fri, 15 Dec 2023 11:21:18 +0900 Subject: [PATCH 12/17] move commitment keys to PublicParams, create types for non-relaxed instance/witness --- nova/src/circuit/augmented.rs | 31 ++++++++++++++-- nova/src/circuit/nifs.rs | 5 ++- nova/src/ivc.rs | 61 ++++++++++++++++++------------- nova/src/pedersen.rs | 1 + nova/src/proof.rs | 10 ++++- nova/src/prover.rs | 37 +++++++++---------- nova/src/relaxed_r1cs.rs | 24 +++++++++--- nova/src/relaxed_r1cs/instance.rs | 35 ++++++++++++++++++ nova/src/relaxed_r1cs/witness.rs | 28 ++++++++++++++ nova/src/verifier.rs | 12 +++--- 10 files changed, 181 insertions(+), 63 deletions(-) diff --git a/nova/src/circuit/augmented.rs b/nova/src/circuit/augmented.rs index 07ebe991..39f353fb 100644 --- a/nova/src/circuit/augmented.rs +++ b/nova/src/circuit/augmented.rs @@ -4,6 +4,7 @@ use crate::function::FunctionCircuit; use crate::gadget::RelaxedR1csInstanceAssignment; use crate::hash::MIMC_ROUNDS; use crate::relaxed_r1cs::RelaxedR1csInstance; +use std::any::type_name; use std::marker::PhantomData; use zkstd::circuit::prelude::{FieldAssignment, PointAssignment}; use zkstd::circuit::CircuitDriver; @@ -99,6 +100,8 @@ impl> AugmentedFCircuit { FieldAssignment::conditional_enforce_equal(cs, &u_single.x0, &u_i_x, ¬_base_case); let r = Self::get_challenge(cs, &u_range, commit_t.clone()); + println!("R = {:?}", r.value(cs)); + dbg!(type_name::<::Base>()); let u_range_next_non_base = NifsCircuit::verify(cs, r, u_single.clone(), u_range.clone(), commit_t); @@ -111,6 +114,21 @@ impl> AugmentedFCircuit { let z_next = FC::invoke_cs(cs, z_i); + println!( + "Hash_circuit\ni = {:?}\nz0 = {:?}\nznext = {:?}\nu = {:?}\nx0 = {:?}\nx1 = {:?}\ne =\n({:?}, {:?}, {:?})\nw =\n({:?}, {:?}, {:?})", + (&i + &FieldAssignment::constant(&C::Base::one())).value(cs), + z_0.iter().map(|x| x.value(cs)).collect::>(), + z_next.iter().map(|x| x.value(cs)).collect::>(), + u_range_next.u.value(cs), + u_range_next.x0.value(cs), + u_range_next.x1.value(cs), + u_range_next.commit_e.get_x().value(cs), + u_range_next.commit_e.get_y().value(cs), + u_range_next.commit_e.get_z().value(cs), + u_range_next.commit_w.get_x().value(cs), + u_range_next.commit_w.get_y().value(cs), + u_range_next.commit_w.get_z().value(cs) + ); let u_next_x = u_range_next.hash( cs, &i + &FieldAssignment::constant(&C::Base::one()), @@ -140,9 +158,12 @@ impl> AugmentedFCircuit { mod tests { use super::*; use crate::driver::{Bn254Driver, GrumpkinDriver}; + use crate::ivc::PublicParams; use crate::relaxed_r1cs::{r1cs_instance_and_witness, R1csShape, RelaxedR1csWitness}; use crate::test::ExampleFunction; - use bn_254::Fr; + use crate::PedersenCommitment; + use bn_254::{Fr, G1Affine}; + use rand_core::OsRng; #[test] fn augmented_circuit_dummies() { @@ -160,13 +181,15 @@ mod tests { augmented_circuit.generate(&mut cs); let shape = R1csShape::from(cs.clone()); + let k = (shape.m().next_power_of_two() as u64).trailing_zeros(); + let ck = PedersenCommitment::::new(k.into(), OsRng); let u_dummy = RelaxedR1csInstance::dummy(shape.l()); let w_dummy = RelaxedR1csWitness::dummy(shape.m_l_1(), shape.m()); - let (x, w) = r1cs_instance_and_witness(&cs, &shape); + let (x, w) = r1cs_instance_and_witness(&cs, &shape, &ck); let (instance, witness) = ( - RelaxedR1csInstance::::new(DenseVectors::new(x)), - RelaxedR1csWitness::::new(DenseVectors::new(w), shape.m()), + RelaxedR1csInstance::::new(x.x), + RelaxedR1csWitness::::new(w.w, shape.m()), ); assert!(shape.is_sat(&u_dummy, &w_dummy)); assert!(shape.is_sat(&instance, &witness)); diff --git a/nova/src/circuit/nifs.rs b/nova/src/circuit/nifs.rs index c519f67c..61ea0727 100644 --- a/nova/src/circuit/nifs.rs +++ b/nova/src/circuit/nifs.rs @@ -16,8 +16,9 @@ impl NifsCircuit { u_range: RelaxedR1csInstanceAssignment, commit_t: PointAssignment, ) -> RelaxedR1csInstanceAssignment { - let r2 = FieldAssignment::mul(cs, &r, &r); - // W_fold = u.W + r * U.W + let r2 = FieldAssignment::square(cs, &r); + println!("R2 = {:?}", r2.value(cs)); + // W_fold = U.W + r * u.W let r_w = u_range.commit_w.scalar_point(cs, &r); let w_fold = u_range.commit_w.add(&r_w, cs); diff --git a/nova/src/ivc.rs b/nova/src/ivc.rs index 0156ec09..eed0293a 100644 --- a/nova/src/ivc.rs +++ b/nova/src/ivc.rs @@ -1,5 +1,5 @@ use crate::function::FunctionCircuit; -use crate::{Prover, RecursiveProof}; +use crate::{PedersenCommitment, Prover, RecursiveProof}; use rand_core::OsRng; use std::marker::PhantomData; @@ -48,7 +48,6 @@ where FC2: FunctionCircuit, { pub fn init( - rng: OsRng, pp: &PublicParams, z0_primary: DenseVectors, z0_secondary: DenseVectors, @@ -67,14 +66,13 @@ where }; let zi_primary = circuit_primary.generate(&mut cs_primary); - println!("Primary out"); - let (x, w) = r1cs_instance_and_witness(&cs_primary, &pp.r1cs_shape_primary); + let (x, w) = r1cs_instance_and_witness(&cs_primary, &pp.r1cs_shape_primary, &pp.ck_primary); let (u_single_next_primary, w_single_next_primary) = ( - RelaxedR1csInstance::new(DenseVectors::new(x)), - RelaxedR1csWitness::new(DenseVectors::new(w), pp.r1cs_shape_primary.m()), + RelaxedR1csInstance::new(x.x), + RelaxedR1csWitness::new(w.w, pp.r1cs_shape_primary.m()), ); - let prover_primary = Prover::new(pp.r1cs_shape_primary.clone(), rng); + let prover_primary = Prover::new(pp.r1cs_shape_primary.clone(), pp.ck_primary.clone()); let mut cs_secondary = R1cs::::default(); let circuit_secondary = AugmentedFCircuit:: { @@ -89,14 +87,15 @@ where }; let zi_secondary = circuit_secondary.generate(&mut cs_secondary); - println!("Secondary out"); - let (x, w) = r1cs_instance_and_witness(&cs_secondary, &pp.r1cs_shape_secondary); + let (x, w) = + r1cs_instance_and_witness(&cs_secondary, &pp.r1cs_shape_secondary, &pp.ck_secondary); let (u_single_next_secondary, w_single_next_secondary) = ( - RelaxedR1csInstance::new(DenseVectors::new(x)), - RelaxedR1csWitness::new(DenseVectors::new(w), pp.r1cs_shape_secondary.m()), + RelaxedR1csInstance::new(x.x), + RelaxedR1csWitness::new(w.w, pp.r1cs_shape_secondary.m()), ); - let prover_secondary = Prover::new(pp.r1cs_shape_secondary.clone(), rng); + let prover_secondary = + Prover::new(pp.r1cs_shape_secondary.clone(), pp.ck_secondary.clone()); let u_dummy = RelaxedR1csInstance::::dummy(pp.r1cs_shape_secondary.l()); let w_dummy = RelaxedR1csWitness::::dummy( @@ -181,18 +180,19 @@ where let zi_primary = circuit_primary.generate(&mut cs_primary); - let (x, w) = r1cs_instance_and_witness(&cs_primary, &pp.r1cs_shape_primary); + let (x, w) = r1cs_instance_and_witness(&cs_primary, &pp.r1cs_shape_primary, &pp.ck_primary); + println!("Primary out"); let (u_single_next_primary, w_single_next_primary) = ( - RelaxedR1csInstance::new(DenseVectors::new(x)), - RelaxedR1csWitness::new(DenseVectors::new(w), pp.r1cs_shape_primary.m()), + RelaxedR1csInstance::new(x.x), + RelaxedR1csWitness::new(w.w, pp.r1cs_shape_primary.m()), ); let (u_range_next_primary, w_range_next_primary, commit_t_primary) = self.prover_primary.prove( &self.u_range_primary, &self.w_range_primary, - &u_single_next_primary, // u_single_next_primary - &w_single_next_primary, // w_single_next_primary + &u_single_next_primary, + &w_single_next_primary, ); let mut cs_secondary = R1cs::::default(); @@ -209,10 +209,12 @@ where let zi_secondary = circuit_secondary.generate(&mut cs_secondary); - let (x, w) = r1cs_instance_and_witness(&cs_secondary, &pp.r1cs_shape_secondary); + println!("Secondary out"); + let (x, w) = + r1cs_instance_and_witness(&cs_secondary, &pp.r1cs_shape_secondary, &pp.ck_secondary); let (u_single_next_secondary, w_single_next_secondary) = ( - RelaxedR1csInstance::new(DenseVectors::new(x)), - RelaxedR1csWitness::new(DenseVectors::new(w), pp.r1cs_shape_secondary.m()), + RelaxedR1csInstance::new(x.x), + RelaxedR1csWitness::new(w.w, pp.r1cs_shape_secondary.m()), ); // update values @@ -240,8 +242,8 @@ where i: self.i, z0_primary: self.z0_primary.clone(), z0_secondary: self.z0_secondary.clone(), - zi_primary: self.z0_primary.clone(), - zi_secondary: self.z0_secondary.clone(), + zi_primary: self.zi_primary.clone(), + zi_secondary: self.zi_secondary.clone(), instances: ( ( self.u_single_secondary.clone(), @@ -267,6 +269,8 @@ where { pub r1cs_shape_primary: R1csShape, pub r1cs_shape_secondary: R1csShape, + pub ck_primary: PedersenCommitment, + pub ck_secondary: PedersenCommitment, marker: PhantomData<(FC1, FC2)>, } @@ -277,7 +281,7 @@ where FC1: FunctionCircuit, FC2: FunctionCircuit, { - pub fn setup() -> Self { + pub fn setup(rng: OsRng) -> Self { // Initialize shape for the primary let circuit_primary = AugmentedFCircuit:: { is_primary: true, @@ -308,9 +312,17 @@ where circuit_secondary.generate(&mut cs); let r1cs_shape_secondary = R1csShape::from(cs); + let k = (r1cs_shape_primary.m().next_power_of_two() as u64).trailing_zeros(); + let ck_primary = PedersenCommitment::::new(k.into(), rng); + + let k = (r1cs_shape_secondary.m().next_power_of_two() as u64).trailing_zeros(); + let ck_secondary = PedersenCommitment::::new(k.into(), rng); + PublicParams { r1cs_shape_primary, r1cs_shape_secondary, + ck_primary, + ck_secondary, marker: Default::default(), } } @@ -338,13 +350,12 @@ mod tests { GrumpkinDriver, ExampleFunction, ExampleFunction, - >::setup(); + >::setup(OsRng); let z0_primary = DenseVectors::new(vec![Fr::from(0)]); let z0_secondary = DenseVectors::new(vec![Fq::from(0)]); let mut ivc = Ivc::, ExampleFunction>::init( - OsRng, &pp, z0_primary, z0_secondary, diff --git a/nova/src/pedersen.rs b/nova/src/pedersen.rs index 404b3064..4a3e71ae 100644 --- a/nova/src/pedersen.rs +++ b/nova/src/pedersen.rs @@ -1,6 +1,7 @@ use zkstd::common::{BNAffine, Group, RngCore}; use zkstd::matrix::DenseVectors; +#[derive(Clone)] pub struct PedersenCommitment { g: Vec, } diff --git a/nova/src/proof.rs b/nova/src/proof.rs index f82894f8..c3753988 100644 --- a/nova/src/proof.rs +++ b/nova/src/proof.rs @@ -53,6 +53,14 @@ where return false; } let (hash_primary, hash_secondary) = { + dbg!(self.i); + dbg!(&self.z0_primary); + dbg!(&self.zi_primary); + dbg!(&u_range_secondary); + dbg!(self.i); + dbg!(&self.z0_secondary); + dbg!(&self.zi_secondary); + dbg!(&u_range_primary); ( u_range_secondary.hash::(self.i, &self.z0_primary, &self.zi_primary), u_range_primary.hash::(self.i, &self.z0_secondary, &self.zi_secondary), @@ -69,8 +77,6 @@ where return false; } - println!("r_U_primary = {:#?}", u_range_primary); - dbg!(pp .r1cs_shape_primary .is_sat(&u_range_primary, &w_range_primary)); diff --git a/nova/src/prover.rs b/nova/src/prover.rs index 1b6696fe..c9dca811 100644 --- a/nova/src/prover.rs +++ b/nova/src/prover.rs @@ -11,21 +11,15 @@ use zkstd::matrix::DenseVectors; pub struct Prover { // public parameters - pub(crate) pp: PedersenCommitment, + pub(crate) ck: PedersenCommitment, // r1cs structure f: R1csShape, } impl Prover { - pub fn new(f: R1csShape, rng: impl RngCore) -> Self { - let m = f.m(); - let n = m.next_power_of_two() as u64; - let k = n.trailing_zeros(); - - let pp = PedersenCommitment::new(k.into(), rng); - - Self { pp, f } + pub fn new(shape: R1csShape, ck: PedersenCommitment) -> Self { + Self { ck, f: shape } } pub fn prove( @@ -39,13 +33,15 @@ impl Prover { // compute cross term t let t = self.compute_cross_term(instance1, witness1, instance2, witness2); - let commit_t = self.pp.commit(&t); + let commit_t = self.ck.commit(&t); transcript.append_point(commit_t); instance2.absorb_by_transcript(&mut transcript); let r = transcript.squeeze(); + dbg!(r); + // fold instance let instance = instance2.fold(instance1, r, commit_t); @@ -100,7 +96,7 @@ impl Prover { #[cfg(test)] pub(crate) mod tests { use super::Prover; - use bn_254::Fq; + use bn_254::{Fq, G1Affine}; use zkstd::circuit::CircuitDriver; use crate::driver::GrumpkinDriver; @@ -108,14 +104,17 @@ pub(crate) mod tests { use crate::relaxed_r1cs::{ r1cs_instance_and_witness, R1csShape, RelaxedR1csInstance, RelaxedR1csWitness, }; - use crate::Verifier; + use crate::{PedersenCommitment, Verifier}; use zkstd::common::OsRng; use zkstd::matrix::DenseVectors; use zkstd::r1cs::test::example_r1cs; pub(crate) fn example_prover() -> Prover { let r1cs = example_r1cs(0); - Prover::new(R1csShape::from(r1cs), OsRng) + let shape = R1csShape::from(r1cs); + let k = (shape.m().next_power_of_two() as u64).trailing_zeros(); + let ck = PedersenCommitment::::new(k.into(), OsRng); + Prover::new(shape, ck) } #[test] @@ -127,12 +126,12 @@ pub(crate) mod tests { let shape = R1csShape::from(r1cs_1.clone()); let r1cs_2 = example_r1cs::(3); - let (x1, w1) = r1cs_instance_and_witness(&r1cs_1, &shape); - let instance1 = RelaxedR1csInstance::new(DenseVectors::new(x1)); - let witness1 = RelaxedR1csWitness::new(DenseVectors::new(w1), shape.m()); - let (x2, w2) = r1cs_instance_and_witness(&r1cs_2, &shape); - let instance2 = RelaxedR1csInstance::new(DenseVectors::new(x2)); - let witness2 = RelaxedR1csWitness::new(DenseVectors::new(w2), shape.m()); + let (x1, w1) = r1cs_instance_and_witness(&r1cs_1, &shape, &prover.ck); + let instance1 = RelaxedR1csInstance::new(x1.x); + let witness1 = RelaxedR1csWitness::new(w1.w, shape.m()); + let (x2, w2) = r1cs_instance_and_witness(&r1cs_2, &shape, &prover.ck); + let instance2 = RelaxedR1csInstance::new(x2.x); + let witness2 = RelaxedR1csWitness::new(w2.w, shape.m()); let (folded_instance, folded_witness, commit_t) = prover.prove(&instance1, &witness1, &instance2, &witness2); diff --git a/nova/src/relaxed_r1cs.rs b/nova/src/relaxed_r1cs.rs index 996feca9..4acaa4ee 100644 --- a/nova/src/relaxed_r1cs.rs +++ b/nova/src/relaxed_r1cs.rs @@ -1,6 +1,9 @@ mod instance; mod witness; +use crate::relaxed_r1cs::instance::R1csInstance; +use crate::relaxed_r1cs::witness::R1csWitness; +use crate::PedersenCommitment; pub(crate) use instance::RelaxedR1csInstance; pub(crate) use witness::RelaxedR1csWitness; use zkstd::circuit::prelude::{CircuitDriver, R1cs}; @@ -21,14 +24,19 @@ pub struct R1csShape { pub(crate) fn r1cs_instance_and_witness( cs: &R1cs, shape: &R1csShape, -) -> (Vec, Vec) { + ck: &PedersenCommitment, +) -> (R1csInstance, R1csWitness) { println!("Cs.x = {:?}", cs.x()); assert_eq!(cs.m_l_1(), shape.m_l_1()); let w = cs.w(); let x = cs.x()[1..].to_vec(); assert_eq!(x.len(), shape.l()); - (x, w) + let witness = R1csWitness::new(shape, w); + let commit_w = witness.commit(ck); + let instance = R1csInstance::new(shape, commit_w, x); + + (instance, witness) } impl From> for R1csShape { @@ -110,7 +118,11 @@ impl R1csShape { mod tests { use super::{r1cs_instance_and_witness, R1csShape, RelaxedR1csInstance, RelaxedR1csWitness}; + use grumpkin::Affine; + use rand_core::OsRng; + use crate::driver::GrumpkinDriver; + use crate::PedersenCommitment; use zkstd::circuit::prelude::R1cs; use zkstd::matrix::DenseVectors; use zkstd::r1cs::test::example_r1cs; @@ -120,9 +132,11 @@ mod tests { for i in 1..10 { let r1cs: R1cs = example_r1cs(i); let shape = R1csShape::from(r1cs.clone()); - let (x, w) = r1cs_instance_and_witness(&r1cs, &shape); - let instance = RelaxedR1csInstance::new(DenseVectors::new(x)); - let witness = RelaxedR1csWitness::new(DenseVectors::new(w), shape.m()); + let k = (shape.m().next_power_of_two() as u64).trailing_zeros(); + let ck = PedersenCommitment::::new(k.into(), OsRng); + let (x, w) = r1cs_instance_and_witness(&r1cs, &shape, &ck); + let instance = RelaxedR1csInstance::new(x.x); + let witness = RelaxedR1csWitness::new(w.w, shape.m()); assert!(shape.is_sat(&instance, &witness)) } } diff --git a/nova/src/relaxed_r1cs/instance.rs b/nova/src/relaxed_r1cs/instance.rs index 5b076e0a..40068fbb 100644 --- a/nova/src/relaxed_r1cs/instance.rs +++ b/nova/src/relaxed_r1cs/instance.rs @@ -1,9 +1,29 @@ use crate::driver::scalar_as_base; use crate::hash::{MimcRO, MIMC_ROUNDS}; +use crate::{PedersenCommitment, R1csShape}; +use std::any::type_name; use zkstd::circuit::prelude::CircuitDriver; use zkstd::common::{BNAffine, BNProjective, CurveGroup, Group, IntGroup, PrimeField, Ring}; use zkstd::matrix::DenseVectors; +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct R1csInstance { + /// commitment for witness vectors + pub(crate) commit_w: C::Affine, + /// public inputs and outputs + pub(crate) x: DenseVectors, +} + +impl R1csInstance { + pub fn new(shape: &R1csShape, commit_w: C::Affine, x: Vec) -> Self { + assert_eq!(shape.l(), x.len()); + Self { + commit_w, + x: DenseVectors::new(x), + } + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct RelaxedR1csInstance { /// commitment for witness vectors @@ -26,6 +46,19 @@ impl RelaxedR1csInstance { } } + /// Initializes a new `RelaxedR1CSInstance` from an `R1CSInstance` + pub fn from_r1cs_instance( + ck: &PedersenCommitment, + shape: &R1csShape, + instance: &R1csInstance, + ) -> Self { + let mut r_instance = RelaxedR1csInstance::dummy(shape.l()); + r_instance.commit_w = instance.commit_w; + r_instance.u = C::Scalar::one(); + r_instance.x = instance.x.clone(); + r_instance + } + pub(crate) fn dummy(x_len: usize) -> Self { Self { commit_w: C::Affine::ADDITIVE_IDENTITY, @@ -50,6 +83,8 @@ impl RelaxedR1csInstance { commit_t: C::Affine, ) -> Self { let r2 = r.square(); + dbg!(type_name::<::Scalar>()); + dbg!(r2); let (e1, u1, w1, x1) = ( C::Affine::ADDITIVE_IDENTITY, C::Scalar::one(), diff --git a/nova/src/relaxed_r1cs/witness.rs b/nova/src/relaxed_r1cs/witness.rs index bc2ef72c..3ef1057a 100644 --- a/nova/src/relaxed_r1cs/witness.rs +++ b/nova/src/relaxed_r1cs/witness.rs @@ -1,7 +1,28 @@ +use crate::{PedersenCommitment, R1csShape}; use zkstd::circuit::prelude::CircuitDriver; use zkstd::common::{IntGroup, PrimeField}; use zkstd::matrix::DenseVectors; +/// A type that holds a witness for a given R1CS instance +#[derive(Clone, Debug)] +pub struct R1csWitness { + pub w: DenseVectors, +} + +impl R1csWitness { + pub fn new(shape: &R1csShape, w: Vec) -> Self { + assert_eq!(shape.m_l_1(), w.len()); + Self { + w: DenseVectors::new(w), + } + } + + /// Commits to the witness using the supplied generators + pub fn commit(&self, ck: &PedersenCommitment) -> C::Affine { + ck.commit(&self.w) + } +} + #[derive(Clone, Debug)] pub struct RelaxedR1csWitness { /// witness @@ -18,6 +39,13 @@ impl RelaxedR1csWitness { } } + pub fn from_r1cs_witness(shape: &R1csShape, witness: &R1csWitness) -> Self { + Self { + w: witness.w.clone(), + e: DenseVectors::new(vec![C::Scalar::zero(); shape.m()]), + } + } + pub(crate) fn w(&self) -> DenseVectors { self.w.clone() } diff --git a/nova/src/verifier.rs b/nova/src/verifier.rs index 5287b7f4..ed42f64f 100644 --- a/nova/src/verifier.rs +++ b/nova/src/verifier.rs @@ -42,15 +42,15 @@ mod tests { let prover = example_prover(); let r1cs = example_r1cs::(1); let shape = R1csShape::from(r1cs.clone()); - let (x, w) = r1cs_instance_and_witness(&r1cs, &shape); - let running_instance = RelaxedR1csInstance::new(DenseVectors::new(x)); - let running_witness = RelaxedR1csWitness::new(DenseVectors::new(w), shape.m()); + let (x, w) = r1cs_instance_and_witness(&r1cs, &shape, &prover.ck); + let running_instance = RelaxedR1csInstance::new(x.x); + let running_witness = RelaxedR1csWitness::new(w.w, shape.m()); for i in 1..10 { let r1cs_i = example_r1cs::(i); - let (x, w) = r1cs_instance_and_witness(&r1cs_i, &shape); - let instance_to_fold = RelaxedR1csInstance::new(DenseVectors::new(x)); - let witness_to_fold = RelaxedR1csWitness::new(DenseVectors::new(w), shape.m()); + let (x, w) = r1cs_instance_and_witness(&r1cs_i, &shape, &prover.ck); + let instance_to_fold = RelaxedR1csInstance::new(x.x); + let witness_to_fold = RelaxedR1csWitness::new(w.w, shape.m()); let (instance, witness, commit_t) = prover.prove( &instance_to_fold, From 567d4baf8753a3c5d6ec3d735c519485d3f871b3 Mon Sep 17 00:00:00 2001 From: Kirill Karbushev Date: Fri, 15 Dec 2023 19:40:06 +0900 Subject: [PATCH 13/17] fix the folding logic, make second argument non relaxed --- Cargo.lock | 18 +++- nova/Cargo.toml | 1 + nova/src/circuit/augmented.rs | 61 ++++++------- nova/src/circuit/nifs.rs | 88 +++++++++++-------- nova/src/driver.rs | 46 ++++++++++ nova/src/gadget.rs | 3 + nova/src/gadget/big_nat.rs | 2 + nova/src/gadget/instance.rs | 128 ++++++++++++++++++++++++++++ nova/src/gadget/relaxed_instance.rs | 56 +++++++++--- nova/src/ivc.rs | 70 ++++++++------- nova/src/proof.rs | 36 ++++---- nova/src/prover.rs | 51 ++++++----- nova/src/relaxed_r1cs.rs | 51 +++++++++-- nova/src/relaxed_r1cs/instance.rs | 96 ++++++++++++++------- nova/src/relaxed_r1cs/witness.rs | 24 +++--- nova/src/verifier.rs | 26 +++--- zkstd/src/circuit/gadget/curve.rs | 14 +++ 17 files changed, 547 insertions(+), 224 deletions(-) create mode 100644 nova/src/gadget/big_nat.rs create mode 100644 nova/src/gadget/instance.rs diff --git a/Cargo.lock b/Cargo.lock index bfbe47d4..d69884e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b2e69442aa5628ea6951fa33e24efe8313f4321a91bd729fc2f75bdfc858570" dependencies = [ - "num-bigint", + "num-bigint 0.3.3", "num-integer", "num-traits", ] @@ -322,7 +322,7 @@ checksum = "e9f54704be45ed286151c5e11531316eaef5b8f5af7d597b806fdb8af108d84a" dependencies = [ "addchain", "cfg-if", - "num-bigint", + "num-bigint 0.3.3", "num-integer", "num-traits", "proc-macro2", @@ -504,6 +504,19 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", + "rand", + "serde", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -1165,6 +1178,7 @@ dependencies = [ "blake2b_simd", "bn-254", "grumpkin", + "num-bigint 0.4.4", "rand_core", "serde", "zkgroth16", diff --git a/nova/Cargo.toml b/nova/Cargo.toml index 0098640b..9719de92 100644 --- a/nova/Cargo.toml +++ b/nova/Cargo.toml @@ -16,6 +16,7 @@ bn-254 = { path = "../bn254", default-features = false } grumpkin = { path = "../grumpkin", default-features = false } zkgroth16 = { path = "../groth16", default-features = false } serde = { version = "1.0.102", default-features = false, features = ["derive"] } +num-bigint = { version = "0.4", features = ["serde", "rand"] } blake2b_simd = { version = "1", default-features = false } rand_core = { version="0.6.4", default-features = false, features = ["getrandom"] } diff --git a/nova/src/circuit/augmented.rs b/nova/src/circuit/augmented.rs index 39f353fb..4592af14 100644 --- a/nova/src/circuit/augmented.rs +++ b/nova/src/circuit/augmented.rs @@ -1,10 +1,9 @@ use crate::circuit::nifs::NifsCircuit; use crate::circuit::MimcROCircuit; use crate::function::FunctionCircuit; -use crate::gadget::RelaxedR1csInstanceAssignment; +use crate::gadget::{R1csInstanceAssignment, RelaxedR1csInstanceAssignment}; use crate::hash::MIMC_ROUNDS; -use crate::relaxed_r1cs::RelaxedR1csInstance; -use std::any::type_name; +use crate::relaxed_r1cs::{R1csInstance, RelaxedR1csInstance}; use std::marker::PhantomData; use zkstd::circuit::prelude::{FieldAssignment, PointAssignment}; use zkstd::circuit::CircuitDriver; @@ -18,7 +17,7 @@ pub struct AugmentedFCircuit> { pub i: usize, pub z_0: DenseVectors, pub z_i: Option>, - pub u_single: Option>, + pub u_single: Option>, pub u_range: Option>, pub commit_t: Option, pub f: PhantomData, @@ -31,7 +30,7 @@ impl> Default for AugmentedFCircu i: 0, z_0: DenseVectors::zero(1), z_i: Some(DenseVectors::zero(1)), - u_single: Some(RelaxedR1csInstance::dummy(2)), + u_single: Some(R1csInstance::dummy(2)), u_range: Some(RelaxedR1csInstance::dummy(2)), commit_t: Some(C::Affine::ADDITIVE_IDENTITY), f: Default::default(), @@ -61,12 +60,12 @@ impl> AugmentedFCircuit { let u_dummy_native = RelaxedR1csInstance::::dummy(2); let u_dummy = RelaxedR1csInstanceAssignment::witness(cs, &u_dummy_native); - let u_single = RelaxedR1csInstanceAssignment::witness( + let u_single = R1csInstanceAssignment::witness( cs, &self .u_single .clone() - .unwrap_or_else(|| u_dummy_native.clone()), + .unwrap_or_else(|| R1csInstance::::dummy(2)), ); let u_range = RelaxedR1csInstanceAssignment::witness( cs, @@ -93,17 +92,24 @@ impl> AugmentedFCircuit { let u_range_next_base = if self.is_primary { u_dummy } else { - u_single.clone() + RelaxedR1csInstanceAssignment::from_r1cs_instance(cs, u_single.clone()) }; let u_i_x = u_range.hash(cs, i.clone(), z_0.clone(), z_i.clone()); FieldAssignment::conditional_enforce_equal(cs, &u_single.x0, &u_i_x, ¬_base_case); let r = Self::get_challenge(cs, &u_range, commit_t.clone()); + let r = FieldAssignment::constant(&C::Base::one()); println!("R = {:?}", r.value(cs)); - dbg!(type_name::<::Base>()); + println!( + "R_bits = {:?}", + FieldAssignment::to_bits(cs, &r) + .iter() + .map(|x| FieldAssignment::from(x).value(cs)) + .collect::>() + ); let u_range_next_non_base = - NifsCircuit::verify(cs, r, u_single.clone(), u_range.clone(), commit_t); + NifsCircuit::verify(cs, r, u_range.clone(), u_single.clone(), commit_t); let u_range_next = RelaxedR1csInstanceAssignment::conditional_select( cs, @@ -114,21 +120,16 @@ impl> AugmentedFCircuit { let z_next = FC::invoke_cs(cs, z_i); - println!( - "Hash_circuit\ni = {:?}\nz0 = {:?}\nznext = {:?}\nu = {:?}\nx0 = {:?}\nx1 = {:?}\ne =\n({:?}, {:?}, {:?})\nw =\n({:?}, {:?}, {:?})", - (&i + &FieldAssignment::constant(&C::Base::one())).value(cs), - z_0.iter().map(|x| x.value(cs)).collect::>(), - z_next.iter().map(|x| x.value(cs)).collect::>(), - u_range_next.u.value(cs), - u_range_next.x0.value(cs), - u_range_next.x1.value(cs), - u_range_next.commit_e.get_x().value(cs), - u_range_next.commit_e.get_y().value(cs), - u_range_next.commit_e.get_z().value(cs), - u_range_next.commit_w.get_x().value(cs), - u_range_next.commit_w.get_y().value(cs), - u_range_next.commit_w.get_z().value(cs) - ); + // println!( + // "Hash_circuit\n{:?},\n{:?},\n{:?},\n{:?},\n{:?},\n{:?},", + // (&i + &FieldAssignment::constant(&C::Base::one())).value(cs), + // z_0.iter().map(|x| x.value(cs)).collect::>(), + // z_next.iter().map(|x| x.value(cs)).collect::>(), + // u_range_next.u.value(cs), + // u_range_next.x0.value(cs), + // u_range_next.x1.value(cs), + // ); + println!("HASH"); let u_next_x = u_range_next.hash( cs, &i + &FieldAssignment::constant(&C::Base::one()), @@ -158,7 +159,7 @@ impl> AugmentedFCircuit { mod tests { use super::*; use crate::driver::{Bn254Driver, GrumpkinDriver}; - use crate::ivc::PublicParams; + use crate::relaxed_r1cs::{r1cs_instance_and_witness, R1csShape, RelaxedR1csWitness}; use crate::test::ExampleFunction; use crate::PedersenCommitment; @@ -188,10 +189,10 @@ mod tests { let (x, w) = r1cs_instance_and_witness(&cs, &shape, &ck); let (instance, witness) = ( - RelaxedR1csInstance::::new(x.x), - RelaxedR1csWitness::::new(w.w, shape.m()), + RelaxedR1csInstance::::from_r1cs_instance(&ck, &shape, &x), + RelaxedR1csWitness::::from_r1cs_witness(&shape, &w), ); - assert!(shape.is_sat(&u_dummy, &w_dummy)); - assert!(shape.is_sat(&instance, &witness)); + assert!(shape.is_sat_relaxed(&u_dummy, &w_dummy)); + assert!(shape.is_sat_relaxed(&instance, &witness)); } } diff --git a/nova/src/circuit/nifs.rs b/nova/src/circuit/nifs.rs index 61ea0727..2b649fc9 100644 --- a/nova/src/circuit/nifs.rs +++ b/nova/src/circuit/nifs.rs @@ -1,8 +1,8 @@ use core::marker::PhantomData; -use crate::gadget::RelaxedR1csInstanceAssignment; +use crate::gadget::{R1csInstanceAssignment, RelaxedR1csInstanceAssignment}; use zkstd::circuit::prelude::{CircuitDriver, FieldAssignment, PointAssignment, R1cs}; -use zkstd::common::IntGroup; +use zkstd::common::{Group, IntGroup}; pub(crate) struct NifsCircuit { p: PhantomData, @@ -12,38 +12,50 @@ impl NifsCircuit { pub(crate) fn verify>( cs: &mut R1cs, r: FieldAssignment, - u_single: RelaxedR1csInstanceAssignment, u_range: RelaxedR1csInstanceAssignment, + u_single: R1csInstanceAssignment, commit_t: PointAssignment, ) -> RelaxedR1csInstanceAssignment { - let r2 = FieldAssignment::square(cs, &r); - println!("R2 = {:?}", r2.value(cs)); + println!( + "W1 = {:?}, {:?}, {:?}", + u_range.commit_w.get_x().value(cs), + u_range.commit_w.get_y().value(cs), + u_range.commit_w.get_z().value(cs) + ); + println!( + "W2 = {:?}, {:?}, {:?}", + u_single.commit_w.get_x().value(cs), + u_single.commit_w.get_y().value(cs), + u_single.commit_w.get_z().value(cs) + ); // W_fold = U.W + r * u.W - let r_w = u_range.commit_w.scalar_point(cs, &r); + let r_w = u_single.commit_w.scalar_point(cs, &r); let w_fold = u_range.commit_w.add(&r_w, cs); + let z_inv = w_fold + .get_z() + .value(cs) + .invert() + .unwrap_or_else(C::Base::zero); - // E_fold = u.E + r * T + U.E * r^2 + // E_fold = U.E + r * T let r_t = commit_t.scalar_point(cs, &r); - let r2_e = u_range.commit_e.scalar_point(cs, &r2); let e_fold = u_range.commit_e.add(&r_t, cs); - let e_fold = e_fold.add(&r2_e, cs); - // u_fold = u.u + r * U.u - let r_u = FieldAssignment::mul(cs, &r, &u_range.u); - let u_fold = &u_single.u + &r_u; - FieldAssignment::enforce_eq_constant( - cs, - &(&(&u_fold - &u_single.u) - &r_u), - &C::Base::zero(), - ); + // u_fold = U.u + r + let u_fold = &u_range.u + &r; + FieldAssignment::enforce_eq_constant(cs, &(&(&u_fold - &u_range.u) - &r), &C::Base::zero()); - // Fold x0 + r * U.x0 - let r_x0 = FieldAssignment::mul(cs, &r, &u_range.x0); - let x0_fold = &u_single.x0 + &r_x0; + // Fold U.x0 + r * x0 + let r_x0 = FieldAssignment::mul(cs, &r, &u_single.x0); + let x0_fold = &u_range.x0 + &r_x0; - // Fold x1 + r * U.x1 - let r_x1 = FieldAssignment::mul(cs, &r, &u_range.x1); - let x1_fold = &u_single.x1 + &r_x1; + println!("x1 = {:?}", u_range.x1.value(cs)); + println!("x2 = {:?}", u_single.x1.value(cs)); + // Fold U.x1 + r * x1 + let r_x1 = FieldAssignment::mul(cs, &r, &u_single.x1); + println!("R_x1 = {:?}", r_x1.value(cs)); + let x1_fold = &u_range.x1 + &r_x1; + println!("x1_fold = {:?}", x1_fold.value(cs)); RelaxedR1csInstanceAssignment { commit_w: w_fold, commit_e: e_fold, @@ -58,45 +70,51 @@ impl NifsCircuit { mod tests { use super::*; use crate::driver::{Bn254Driver, GrumpkinDriver}; + use crate::gadget::R1csInstanceAssignment; use crate::hash::{MimcRO, MIMC_ROUNDS}; use crate::prover::tests::example_prover; - use crate::relaxed_r1cs::{RelaxedR1csInstance, RelaxedR1csWitness}; + use crate::relaxed_r1cs::{r1cs_instance_and_witness, RelaxedR1csInstance, RelaxedR1csWitness}; + use crate::R1csShape; use zkstd::common::CurveGroup; - use zkstd::matrix::DenseVectors; + use zkstd::r1cs::test::example_r1cs; #[test] + #[ignore] fn nifs_circuit() { let prover = example_prover(); let r1cs = example_r1cs::(1); - let running_instance = RelaxedR1csInstance::new(DenseVectors::new(r1cs.x())); - let running_witness = RelaxedR1csWitness::new(DenseVectors::new(r1cs.w()), r1cs.m()); + let shape = R1csShape::from(r1cs.clone()); + let (x, w) = r1cs_instance_and_witness(&r1cs, &shape, &prover.ck); + let running_instance = RelaxedR1csInstance::from_r1cs_instance(&prover.ck, &shape, &x); + let running_witness = RelaxedR1csWitness::from_r1cs_witness(&shape, &w); let r1cs_2 = example_r1cs::(2); - let instance_to_fold = RelaxedR1csInstance::new(DenseVectors::new(r1cs.x())); - let witness_to_fold = RelaxedR1csWitness::new(DenseVectors::new(r1cs.w()), r1cs.m()); + let (instance_to_fold, witness_to_fold) = + r1cs_instance_and_witness(&r1cs, &shape, &prover.ck); + let (instance, witness, commit_t) = prover.prove( - &instance_to_fold, - &witness_to_fold, &running_instance, &running_witness, + &instance_to_fold, + &witness_to_fold, ); let mut transcript = MimcRO::::default(); transcript.append_point(commit_t); running_instance.absorb_by_transcript(&mut transcript); let t = prover.compute_cross_term( - &instance_to_fold, - &witness_to_fold, &running_instance, &running_witness, + &instance_to_fold, + &witness_to_fold, ); let r = transcript.squeeze(); let mut cs = R1cs::::default(); let r = FieldAssignment::witness(&mut cs, r.into()); - let instance1 = RelaxedR1csInstanceAssignment::witness(&mut cs, &instance_to_fold); - let instance2 = RelaxedR1csInstanceAssignment::witness(&mut cs, &running_instance); + let instance1 = RelaxedR1csInstanceAssignment::witness(&mut cs, &running_instance); + let instance2 = R1csInstanceAssignment::witness(&mut cs, &instance_to_fold); let commit_t = PointAssignment::witness( &mut cs, commit_t.get_x(), diff --git a/nova/src/driver.rs b/nova/src/driver.rs index d202d6bb..2e5c15e2 100644 --- a/nova/src/driver.rs +++ b/nova/src/driver.rs @@ -1,5 +1,6 @@ use bn_254::{params::PARAM_B3 as BN254_PARAM_B3, Fq, Fr, G1Affine}; use grumpkin::{params::PARAM_B3 as GRUMPKIN_PARAM_B3, Affine}; +use num_bigint::{BigInt, Sign}; use zkstd::circuit::CircuitDriver; use zkstd::common::{IntGroup, PrimeField, Ring}; @@ -35,6 +36,51 @@ impl CircuitDriver for Bn254Driver { } } +/// Convert a field element to a natural number +pub fn f_to_nat(f: &F) -> BigInt { + dbg!(f.to_raw_bytes()); + BigInt::from_bytes_le(Sign::Plus, &f.to_raw_bytes()) +} + +/// Convert a natural number to a field element. +/// Returns `None` if the number is too big for the field. +pub fn nat_to_f(n: &BigInt) -> F { + let bytes = n.to_signed_bytes_le(); + if bytes.len() > 64 { + panic!("Length exceed the field size"); + }; + + let mut res = [0; 64]; + res[0..64].copy_from_slice(&bytes); + + F::from_bytes_wide(&res) +} + +/// Compute the limbs encoding a natural number. +/// The limbs are assumed to be based the `limb_width` power of 2. +pub fn nat_to_limbs(nat: &BigInt, limb_width: usize, n_limbs: usize) -> Vec { + let mask = int_with_n_ones(limb_width); + let mut nat = nat.clone(); + if nat.bits() as usize <= n_limbs * limb_width { + (0..n_limbs) + .map(|_| { + let r = &nat & &mask; + nat >>= limb_width as u32; + nat_to_f(&r) + }) + .collect() + } else { + panic!("Wrong amount of bits"); + } +} + +fn int_with_n_ones(n: usize) -> BigInt { + let mut m = BigInt::from(1); + m <<= n as u32; + m -= 1; + m +} + /// interpret scalar as base pub fn scalar_as_base(input: C::Scalar) -> C::Base { let input_bits = input.to_bits(); diff --git a/nova/src/gadget.rs b/nova/src/gadget.rs index ceed16d4..74f4ca64 100644 --- a/nova/src/gadget.rs +++ b/nova/src/gadget.rs @@ -1,5 +1,8 @@ +mod big_nat; +mod instance; mod mimc; mod relaxed_instance; +pub(crate) use instance::R1csInstanceAssignment; pub(crate) use mimc::MimcAssignment; pub(crate) use relaxed_instance::RelaxedR1csInstanceAssignment; diff --git a/nova/src/gadget/big_nat.rs b/nova/src/gadget/big_nat.rs new file mode 100644 index 00000000..206f527f --- /dev/null +++ b/nova/src/gadget/big_nat.rs @@ -0,0 +1,2 @@ +#[derive(Clone, Debug)] +pub struct BigNatAssignment(); diff --git a/nova/src/gadget/instance.rs b/nova/src/gadget/instance.rs new file mode 100644 index 00000000..83a6b6d1 --- /dev/null +++ b/nova/src/gadget/instance.rs @@ -0,0 +1,128 @@ +use crate::relaxed_r1cs::R1csInstance; + +use crate::circuit::MimcROCircuit; +use crate::driver::scalar_as_base; + +use zkstd::circuit::prelude::{ + BinaryAssignment, CircuitDriver, FieldAssignment, PointAssignment, R1cs, +}; +use zkstd::common::CurveGroup; + +#[derive(Clone)] +pub(crate) struct R1csInstanceAssignment { + pub(crate) commit_w: PointAssignment, + pub(crate) x0: FieldAssignment, + pub(crate) x1: FieldAssignment, +} + +impl R1csInstanceAssignment { + pub(crate) fn witness>( + cs: &mut R1cs, + r1cs_instance: &R1csInstance, + ) -> Self { + let R1csInstance { commit_w, x } = r1cs_instance; + + let commit_w = PointAssignment::witness( + cs, + commit_w.get_x(), + commit_w.get_y(), + commit_w.is_identity(), + ); + + let x0 = FieldAssignment::witness(cs, scalar_as_base::(x[0])); + let x1 = FieldAssignment::witness(cs, scalar_as_base::(x[1])); + + Self { commit_w, x0, x1 } + } + + pub fn conditional_select>( + cs: &mut R1cs, + a: &Self, + b: &Self, + condition: &BinaryAssignment, + ) -> Self { + let commit_w = PointAssignment::conditional_select(cs, &a.commit_w, &b.commit_w, condition); + + let x0 = FieldAssignment::conditional_select(cs, &a.x0, &b.x0, condition); + let x1 = FieldAssignment::conditional_select(cs, &a.x1, &b.x1, condition); + Self { commit_w, x0, x1 } + } + + pub(crate) fn absorb_by_transcript( + &self, + transcript: &mut MimcROCircuit, + ) { + transcript.append_point(self.commit_w.clone()); + transcript.append(self.x0.clone()); + transcript.append(self.x1.clone()); + } + + // pub(crate) fn hash>( + // &self, + // cs: &mut R1cs, + // i: FieldAssignment, + // z_0: Vec>, + // z_i: Vec>, + // ) -> FieldAssignment { + // MimcROCircuit::::default().hash_vec( + // cs, + // vec![ + // vec![i], + // z_0, + // z_i, + // vec![self.u.clone()], + // vec![self.x0.clone()], + // vec![self.x1.clone()], + // vec![ + // self.commit_e.get_x(), + // self.commit_e.get_y(), + // self.commit_e.get_z(), + // ], + // vec![ + // self.commit_w.get_x(), + // self.commit_w.get_y(), + // self.commit_w.get_z(), + // ], + // ] + // .concat(), + // ) + // } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::driver::{Bn254Driver, GrumpkinDriver}; + use bn_254::Fq; + use grumpkin::Affine; + use rand_core::OsRng; + use zkstd::common::{BNAffine, Group}; + use zkstd::matrix::DenseVectors; + + #[test] + fn instance_assignment() { + let mut cs: R1cs = R1cs::default(); + let instance = R1csInstance:: { + commit_w: Affine::random(OsRng), + x: DenseVectors::new(vec![Fq::random(OsRng); 2]), + }; + + let instance_assignment = R1csInstanceAssignment::witness(&mut cs, &instance); + FieldAssignment::enforce_eq_constant( + &mut cs, + &instance_assignment.x0, + &scalar_as_base::(instance.x[0]), + ); + FieldAssignment::enforce_eq_constant( + &mut cs, + &instance_assignment.x1, + &scalar_as_base::(instance.x[1]), + ); + + instance_assignment + .commit_w + .assert_equal_public_point(&mut cs, instance.commit_w.to_extended()); + + assert!(cs.is_sat()); + } +} diff --git a/nova/src/gadget/relaxed_instance.rs b/nova/src/gadget/relaxed_instance.rs index 10a3c836..b092c53f 100644 --- a/nova/src/gadget/relaxed_instance.rs +++ b/nova/src/gadget/relaxed_instance.rs @@ -2,11 +2,12 @@ use crate::relaxed_r1cs::RelaxedR1csInstance; use crate::circuit::MimcROCircuit; use crate::driver::scalar_as_base; +use crate::gadget::R1csInstanceAssignment; use crate::hash::MIMC_ROUNDS; use zkstd::circuit::prelude::{ BinaryAssignment, CircuitDriver, FieldAssignment, PointAssignment, R1cs, }; -use zkstd::common::CurveGroup; +use zkstd::common::{CurveGroup, Ring}; #[derive(Clone)] pub(crate) struct RelaxedR1csInstanceAssignment { @@ -54,6 +55,22 @@ impl RelaxedR1csInstanceAssignment { } } + /// Allocates the R1CS Instance as a `RelaxedR1CSInstance` in the circuit. + /// E = 0, u = 1 + pub fn from_r1cs_instance>( + cs: &mut R1cs, + instance: R1csInstanceAssignment, + ) -> Self { + let commit_e = PointAssignment::identity(); + Self { + commit_w: instance.commit_w, + commit_e, + u: FieldAssignment::constant(&C::Base::one()), + x0: instance.x0, + x1: instance.x1, + } + } + pub fn conditional_select>( cs: &mut R1cs, a: &Self, @@ -92,6 +109,31 @@ impl RelaxedR1csInstanceAssignment { z_0: Vec>, z_i: Vec>, ) -> FieldAssignment { + let commit_e = self.commit_e.to_one_scale(cs); + let commit_w = self.commit_w.to_one_scale(cs); + // println!( + // "{:?},\n{:?},\n{:?},\n{:?},\n{:?},\n{:?},", + // commit_e.get_x().value(cs), + // commit_e.get_y().value(cs), + // commit_e.get_z().value(cs), + // commit_w.get_x().value(cs), + // commit_w.get_y().value(cs), + // commit_w.get_z().value(cs) + // ); + dbg!(vec![ + vec![i.clone()], + z_0.clone(), + z_i.clone(), + vec![self.u.clone()], + vec![self.x0.clone()], + vec![self.x1.clone()], + vec![commit_e.get_x(), commit_e.get_y(), commit_e.get_z()], + vec![commit_w.get_x(), commit_w.get_y(), commit_w.get_z()], + ] + .concat() + .iter() + .map(|x| x.value(cs)) + .collect::>()); MimcROCircuit::::default().hash_vec( cs, vec![ @@ -101,16 +143,8 @@ impl RelaxedR1csInstanceAssignment { vec![self.u.clone()], vec![self.x0.clone()], vec![self.x1.clone()], - vec![ - self.commit_e.get_x(), - self.commit_e.get_y(), - self.commit_e.get_z(), - ], - vec![ - self.commit_w.get_x(), - self.commit_w.get_y(), - self.commit_w.get_z(), - ], + vec![commit_e.get_x(), commit_e.get_y(), commit_e.get_z()], + vec![commit_w.get_x(), commit_w.get_y(), commit_w.get_z()], ] .concat(), ) diff --git a/nova/src/ivc.rs b/nova/src/ivc.rs index eed0293a..0809602c 100644 --- a/nova/src/ivc.rs +++ b/nova/src/ivc.rs @@ -5,7 +5,8 @@ use std::marker::PhantomData; use crate::circuit::AugmentedFCircuit; use crate::relaxed_r1cs::{ - r1cs_instance_and_witness, R1csShape, RelaxedR1csInstance, RelaxedR1csWitness, + r1cs_instance_and_witness, R1csInstance, R1csShape, R1csWitness, RelaxedR1csInstance, + RelaxedR1csWitness, }; use zkstd::circuit::prelude::{CircuitDriver, R1cs}; use zkstd::common::IntGroup; @@ -28,8 +29,8 @@ where // r1cs instance to be folded // u_i // represents the correct execution of invocation i of F′ - u_single_secondary: RelaxedR1csInstance, - w_single_secondary: RelaxedR1csWitness, + u_single_secondary: R1csInstance, + w_single_secondary: R1csWitness, // running r1cs instance // U_i // represents the correct execution of invocations 1, . . . , i - 1 of F′ @@ -66,12 +67,13 @@ where }; let zi_primary = circuit_primary.generate(&mut cs_primary); - let (x, w) = r1cs_instance_and_witness(&cs_primary, &pp.r1cs_shape_primary, &pp.ck_primary); - let (u_single_next_primary, w_single_next_primary) = ( - RelaxedR1csInstance::new(x.x), - RelaxedR1csWitness::new(w.w, pp.r1cs_shape_primary.m()), - ); - + let (u_single_next_primary, w_single_next_primary) = + r1cs_instance_and_witness(&cs_primary, &pp.r1cs_shape_primary, &pp.ck_primary); + assert!(pp.r1cs_shape_primary.is_sat( + &pp.ck_primary, + &u_single_next_primary, + &w_single_next_primary, + )); let prover_primary = Prover::new(pp.r1cs_shape_primary.clone(), pp.ck_primary.clone()); let mut cs_secondary = R1cs::::default(); @@ -87,12 +89,13 @@ where }; let zi_secondary = circuit_secondary.generate(&mut cs_secondary); - let (x, w) = + let (u_single_next_secondary, w_single_next_secondary) = r1cs_instance_and_witness(&cs_secondary, &pp.r1cs_shape_secondary, &pp.ck_secondary); - let (u_single_next_secondary, w_single_next_secondary) = ( - RelaxedR1csInstance::new(x.x), - RelaxedR1csWitness::new(w.w, pp.r1cs_shape_secondary.m()), - ); + assert!(pp.r1cs_shape_secondary.is_sat( + &pp.ck_secondary, + &u_single_next_secondary, + &w_single_next_secondary, + )); let prover_secondary = Prover::new(pp.r1cs_shape_secondary.clone(), pp.ck_secondary.clone()); @@ -110,21 +113,28 @@ where zi_primary: DenseVectors::new( zi_primary .into_iter() - .map(|x| x.value(&mut cs_primary)) + .map(|x| x.value(&cs_primary)) .collect(), ), zi_secondary: DenseVectors::new( zi_secondary .into_iter() - .map(|x| x.value(&mut cs_secondary)) + .map(|x| x.value(&cs_secondary)) .collect(), ), prover_primary, prover_secondary, u_single_secondary: u_single_next_secondary, w_single_secondary: w_single_next_secondary, - u_range_primary: u_single_next_primary, - w_range_primary: w_single_next_primary, + u_range_primary: RelaxedR1csInstance::from_r1cs_instance( + &pp.ck_primary, + &pp.r1cs_shape_primary, + &u_single_next_primary, + ), + w_range_primary: RelaxedR1csWitness::from_r1cs_witness( + &pp.r1cs_shape_primary, + &w_single_next_primary, + ), u_range_secondary: u_dummy, w_range_secondary: w_dummy, f: PhantomData::default(), @@ -135,6 +145,7 @@ where &mut self, pp: &PublicParams, ) -> RecursiveProof { + println!("STEP"); if self.i == 0 { self.i = 1; return RecursiveProof { @@ -180,12 +191,9 @@ where let zi_primary = circuit_primary.generate(&mut cs_primary); - let (x, w) = r1cs_instance_and_witness(&cs_primary, &pp.r1cs_shape_primary, &pp.ck_primary); println!("Primary out"); - let (u_single_next_primary, w_single_next_primary) = ( - RelaxedR1csInstance::new(x.x), - RelaxedR1csWitness::new(w.w, pp.r1cs_shape_primary.m()), - ); + let (u_single_next_primary, w_single_next_primary) = + r1cs_instance_and_witness(&cs_primary, &pp.r1cs_shape_primary, &pp.ck_primary); let (u_range_next_primary, w_range_next_primary, commit_t_primary) = self.prover_primary.prove( @@ -210,12 +218,14 @@ where let zi_secondary = circuit_secondary.generate(&mut cs_secondary); println!("Secondary out"); - let (x, w) = + let (u_single_next_secondary, w_single_next_secondary) = r1cs_instance_and_witness(&cs_secondary, &pp.r1cs_shape_secondary, &pp.ck_secondary); - let (u_single_next_secondary, w_single_next_secondary) = ( - RelaxedR1csInstance::new(x.x), - RelaxedR1csWitness::new(w.w, pp.r1cs_shape_secondary.m()), - ); + + // assert!(pp.r1cs_shape_secondary.is_sat( + // &pp.ck_secondary, + // &u_single_next_secondary, + // &w_single_next_secondary, + // )); // update values self.i += 1; @@ -228,13 +238,13 @@ where self.zi_primary = DenseVectors::new( zi_primary .into_iter() - .map(|x| x.value(&mut cs_primary)) + .map(|x| x.value(&cs_primary)) .collect(), ); self.zi_secondary = DenseVectors::new( zi_secondary .into_iter() - .map(|x| x.value(&mut cs_secondary)) + .map(|x| x.value(&cs_secondary)) .collect(), ); diff --git a/nova/src/proof.rs b/nova/src/proof.rs index c3753988..c82d798a 100644 --- a/nova/src/proof.rs +++ b/nova/src/proof.rs @@ -1,4 +1,4 @@ -use crate::relaxed_r1cs::{RelaxedR1csInstance, RelaxedR1csWitness}; +use crate::relaxed_r1cs::{R1csInstance, R1csWitness, RelaxedR1csInstance, RelaxedR1csWitness}; use std::marker::PhantomData; use crate::driver::scalar_as_base; @@ -22,7 +22,7 @@ where pub(crate) zi_secondary: DenseVectors, pub(crate) instances: ( // u_single/w_single secondary - (RelaxedR1csInstance, RelaxedR1csWitness), + (R1csInstance, R1csWitness), // u_range/w_range primary (RelaxedR1csInstance, RelaxedR1csWitness), // u_range/w_range secondary @@ -53,14 +53,6 @@ where return false; } let (hash_primary, hash_secondary) = { - dbg!(self.i); - dbg!(&self.z0_primary); - dbg!(&self.zi_primary); - dbg!(&u_range_secondary); - dbg!(self.i); - dbg!(&self.z0_secondary); - dbg!(&self.zi_secondary); - dbg!(&u_range_primary); ( u_range_secondary.hash::(self.i, &self.z0_primary, &self.zi_primary), u_range_primary.hash::(self.i, &self.z0_secondary, &self.zi_secondary), @@ -79,21 +71,25 @@ where dbg!(pp .r1cs_shape_primary - .is_sat(&u_range_primary, &w_range_primary)); - dbg!(pp - .r1cs_shape_secondary - .is_sat(&u_range_secondary, &w_range_secondary)); + .is_sat_relaxed(&u_range_primary, &w_range_primary)); dbg!(pp .r1cs_shape_secondary - .is_sat(&u_single_secondary, &w_single_secondary)); + .is_sat_relaxed(&u_range_secondary, &w_range_secondary)); + dbg!(pp.r1cs_shape_secondary.is_sat( + &pp.ck_secondary, + &u_single_secondary, + &w_single_secondary + )); pp.r1cs_shape_primary - .is_sat(&u_range_primary, &w_range_primary) + .is_sat_relaxed(&u_range_primary, &w_range_primary) && pp .r1cs_shape_secondary - .is_sat(&u_range_secondary, &w_range_secondary) - && pp - .r1cs_shape_secondary - .is_sat(&u_single_secondary, &w_single_secondary) + .is_sat_relaxed(&u_range_secondary, &w_range_secondary) + && pp.r1cs_shape_secondary.is_sat( + &pp.ck_secondary, + &u_single_secondary, + &w_single_secondary, + ) } } diff --git a/nova/src/prover.rs b/nova/src/prover.rs index c9dca811..cef32cc6 100644 --- a/nova/src/prover.rs +++ b/nova/src/prover.rs @@ -4,9 +4,9 @@ use crate::{ }; use crate::hash::{MimcRO, MIMC_ROUNDS}; -use crate::relaxed_r1cs::R1csShape; +use crate::relaxed_r1cs::{R1csInstance, R1csShape, R1csWitness}; use zkstd::circuit::prelude::CircuitDriver; -use zkstd::common::{Ring, RngCore}; +use zkstd::common::Ring; use zkstd::matrix::DenseVectors; pub struct Prover { @@ -26,8 +26,8 @@ impl Prover { &self, instance1: &RelaxedR1csInstance, witness1: &RelaxedR1csWitness, - instance2: &RelaxedR1csInstance, - witness2: &RelaxedR1csWitness, + instance2: &R1csInstance, + witness2: &R1csWitness, ) -> (RelaxedR1csInstance, RelaxedR1csWitness, C::Affine) { let mut transcript = MimcRO::::default(); // compute cross term t @@ -36,17 +36,17 @@ impl Prover { let commit_t = self.ck.commit(&t); transcript.append_point(commit_t); - instance2.absorb_by_transcript(&mut transcript); + instance1.absorb_by_transcript(&mut transcript); let r = transcript.squeeze(); dbg!(r); // fold instance - let instance = instance2.fold(instance1, r, commit_t); + let instance = instance1.fold(instance2, r, commit_t); // fold witness - let witness = witness2.fold(witness1, r, t); + let witness = witness1.fold(witness2, r, t); // return folded relaxed r1cs instance, witness and commit T (instance, witness, commit_t) @@ -57,11 +57,11 @@ impl Prover { &self, instance1: &RelaxedR1csInstance, witness1: &RelaxedR1csWitness, - instance2: &RelaxedR1csInstance, - witness2: &RelaxedR1csWitness, + instance2: &R1csInstance, + witness2: &R1csWitness, ) -> DenseVectors { - let u1 = C::Scalar::one(); - let u2 = instance2.u(); + let u1 = instance1.u; + let u2 = C::Scalar::one(); let m = self.f.m(); let (a, b, c) = self.f.matrices(); let (w1, w2) = (witness1.w(), witness2.w()); @@ -96,7 +96,7 @@ impl Prover { #[cfg(test)] pub(crate) mod tests { use super::Prover; - use bn_254::{Fq, G1Affine}; + use bn_254::Fq; use zkstd::circuit::CircuitDriver; use crate::driver::GrumpkinDriver; @@ -106,7 +106,6 @@ pub(crate) mod tests { }; use crate::{PedersenCommitment, Verifier}; use zkstd::common::OsRng; - use zkstd::matrix::DenseVectors; use zkstd::r1cs::test::example_r1cs; pub(crate) fn example_prover() -> Prover { @@ -127,11 +126,10 @@ pub(crate) mod tests { let r1cs_2 = example_r1cs::(3); let (x1, w1) = r1cs_instance_and_witness(&r1cs_1, &shape, &prover.ck); - let instance1 = RelaxedR1csInstance::new(x1.x); - let witness1 = RelaxedR1csWitness::new(w1.w, shape.m()); - let (x2, w2) = r1cs_instance_and_witness(&r1cs_2, &shape, &prover.ck); - let instance2 = RelaxedR1csInstance::new(x2.x); - let witness2 = RelaxedR1csWitness::new(w2.w, shape.m()); + let instance1 = RelaxedR1csInstance::from_r1cs_instance(&prover.ck, &shape, &x1); + let witness1 = RelaxedR1csWitness::from_r1cs_witness(&shape, &w1); + + let (instance2, witness2) = r1cs_instance_and_witness(&r1cs_2, &shape, &prover.ck); let (folded_instance, folded_witness, commit_t) = prover.prove(&instance1, &witness1, &instance2, &witness2); @@ -139,7 +137,7 @@ pub(crate) mod tests { assert_eq!(folded_instance, verified_instance); transcript.append_point(commit_t); - instance2.absorb_by_transcript(&mut transcript); + instance1.absorb_by_transcript(&mut transcript); let t = prover.compute_cross_term(&instance1, &witness1, &instance2, &witness2); let r = transcript.squeeze(); @@ -151,8 +149,8 @@ pub(crate) mod tests { ] .concat(); - let z1 = [vec![Fq::one()], instance1.x().get(), witness1.w().get()].concat(); - let z2 = [vec![instance2.u], instance2.x().get(), witness2.w().get()].concat(); + let z1 = [vec![instance1.u()], instance1.x().get(), witness1.w().get()].concat(); + let z2 = [vec![Fq::one()], instance2.x().get(), witness2.w().get()].concat(); let z3_aux: Vec = z2 .iter() @@ -164,9 +162,9 @@ pub(crate) mod tests { assert_eq!(z3, z3_aux); // check that relations hold for the 2 inputted instances and the folded one - assert!(shape.is_sat(&instance1, &witness1)); - assert!(shape.is_sat(&instance2, &witness2)); - assert!(shape.is_sat(&folded_instance, &folded_witness)); + assert!(shape.is_sat_relaxed(&instance1, &witness1)); + assert!(shape.is_sat(&prover.ck, &instance2, &witness2)); + assert!(shape.is_sat_relaxed(&folded_instance, &folded_witness)); // next equalities should hold since we started from two cmE of zero-vector E's assert_eq!(verified_instance.commit_e, (commit_t * r).into()); @@ -174,9 +172,8 @@ pub(crate) mod tests { let r2 = r * r; assert!( - folded_instance.commit_e - == (instance1.commit_e + commit_t * r + instance2.commit_e * r2).into() - && folded_instance.u == instance1.u + r * instance2.u + folded_instance.commit_e == (instance1.commit_e + commit_t * r).into() + && folded_instance.u == instance1.u + r && folded_instance.commit_w == (instance1.commit_w + instance2.commit_w * r).into() && folded_instance.x == &instance1.x + &(&instance2.x * r) ); diff --git a/nova/src/relaxed_r1cs.rs b/nova/src/relaxed_r1cs.rs index 4acaa4ee..430928b4 100644 --- a/nova/src/relaxed_r1cs.rs +++ b/nova/src/relaxed_r1cs.rs @@ -1,12 +1,11 @@ mod instance; mod witness; -use crate::relaxed_r1cs::instance::R1csInstance; -use crate::relaxed_r1cs::witness::R1csWitness; use crate::PedersenCommitment; -pub(crate) use instance::RelaxedR1csInstance; -pub(crate) use witness::RelaxedR1csWitness; +pub(crate) use instance::{R1csInstance, RelaxedR1csInstance}; +pub(crate) use witness::{R1csWitness, RelaxedR1csWitness}; use zkstd::circuit::prelude::{CircuitDriver, R1cs}; +use zkstd::common::Ring; use zkstd::matrix::{DenseVectors, SparseMatrix}; #[derive(Clone, Debug)] @@ -78,7 +77,7 @@ impl R1csShape { } /// check (A · Z) ◦ (B · Z) = u · (C · Z) + E - pub fn is_sat( + pub fn is_sat_relaxed( &self, instance: &RelaxedR1csInstance, witness: &RelaxedR1csWitness, @@ -112,6 +111,41 @@ impl R1csShape { .zip(ucze.iter()) .all(|(left, right)| left == right) } + + /// check (A · Z) ◦ (B · Z) = (C · Z) + pub fn is_sat( + &self, + ck: &PedersenCommitment, + instance: &R1csInstance, + witness: &R1csWitness, + ) -> bool { + let Self { m, a, b, c, .. } = self; + + let R1csInstance { commit_w, x } = instance; + dbg!(x); + let R1csWitness { w } = witness; + + let l = x.len() + 1; + let z = DenseVectors::new(vec![vec![C::Scalar::one()], x.get(), w.get()].concat()); + // A · Z + let az = a.prod(m, l, &z); + // B · Z + let bz = b.prod(m, l, &z); + // C · Z + let cz = c.prod(m, l, &z); + // (A · Z) ◦ (B · Z) + let azbz = az * bz; + + let constraints_check = azbz + .iter() + .zip(cz.iter()) + .all(|(left, right)| left == right); + dbg!(constraints_check); + let commit_check = *commit_w == witness.commit(ck); + dbg!(commit_check); + + constraints_check && commit_check + } } #[cfg(test)] @@ -124,7 +158,6 @@ mod tests { use crate::driver::GrumpkinDriver; use crate::PedersenCommitment; use zkstd::circuit::prelude::R1cs; - use zkstd::matrix::DenseVectors; use zkstd::r1cs::test::example_r1cs; #[test] @@ -135,9 +168,9 @@ mod tests { let k = (shape.m().next_power_of_two() as u64).trailing_zeros(); let ck = PedersenCommitment::::new(k.into(), OsRng); let (x, w) = r1cs_instance_and_witness(&r1cs, &shape, &ck); - let instance = RelaxedR1csInstance::new(x.x); - let witness = RelaxedR1csWitness::new(w.w, shape.m()); - assert!(shape.is_sat(&instance, &witness)) + let instance = RelaxedR1csInstance::from_r1cs_instance(&ck, &shape, &x); + let witness = RelaxedR1csWitness::from_r1cs_witness(&shape, &w); + assert!(shape.is_sat_relaxed(&instance, &witness)) } } } diff --git a/nova/src/relaxed_r1cs/instance.rs b/nova/src/relaxed_r1cs/instance.rs index 40068fbb..34a3c0d6 100644 --- a/nova/src/relaxed_r1cs/instance.rs +++ b/nova/src/relaxed_r1cs/instance.rs @@ -1,9 +1,8 @@ use crate::driver::scalar_as_base; use crate::hash::{MimcRO, MIMC_ROUNDS}; use crate::{PedersenCommitment, R1csShape}; -use std::any::type_name; use zkstd::circuit::prelude::CircuitDriver; -use zkstd::common::{BNAffine, BNProjective, CurveGroup, Group, IntGroup, PrimeField, Ring}; +use zkstd::common::{CurveGroup, Group, IntGroup, Ring}; use zkstd::matrix::DenseVectors; #[derive(Clone, Debug, PartialEq, Eq)] @@ -22,6 +21,17 @@ impl R1csInstance { x: DenseVectors::new(x), } } + + pub(crate) fn dummy(x_len: usize) -> Self { + Self { + commit_w: C::Affine::ADDITIVE_IDENTITY, + x: DenseVectors::zero(x_len), + } + } + + pub(crate) fn x(&self) -> DenseVectors { + self.x.clone() + } } #[derive(Clone, Debug, PartialEq, Eq)] @@ -37,15 +47,6 @@ pub struct RelaxedR1csInstance { } impl RelaxedR1csInstance { - pub(crate) fn new(x: DenseVectors) -> Self { - Self { - commit_w: C::Affine::ADDITIVE_IDENTITY, - commit_e: C::Affine::ADDITIVE_IDENTITY, - u: C::Scalar::one(), - x, - } - } - /// Initializes a new `RelaxedR1CSInstance` from an `R1CSInstance` pub fn from_r1cs_instance( ck: &PedersenCommitment, @@ -78,25 +79,22 @@ impl RelaxedR1csInstance { pub(crate) fn fold( &self, - instance: &RelaxedR1csInstance, + instance: &R1csInstance, r: C::Scalar, commit_t: C::Affine, ) -> Self { - let r2 = r.square(); - dbg!(type_name::<::Scalar>()); - dbg!(r2); - let (e1, u1, w1, x1) = ( - C::Affine::ADDITIVE_IDENTITY, - C::Scalar::one(), - C::Affine::ADDITIVE_IDENTITY, - instance.x(), - ); - let (e2, u2, w2, x2) = (self.commit_e, self.u, self.commit_w, self.x()); + let (e1, u1, w1, x1) = (self.commit_e, self.u, self.commit_w, self.x()); + let (w2, x2) = (instance.commit_w, instance.x()); - let commit_e = (e1 + commit_t * r + e2 * r2).into(); - let u = u1 + r * u2; + let commit_e = (e1 + commit_t * r).into(); + let u = u1 + r; let commit_w = (w1 + w2 * r).into(); + // dbg!(commit_w); + dbg!(&x1); + dbg!(&x2); + dbg!(x2.clone() * r); let x = x1 + x2 * r; + dbg!(&x); Self { commit_w, @@ -124,8 +122,34 @@ impl RelaxedR1csInstance { z_0: &DenseVectors, z_i: &DenseVectors, ) -> C::Scalar { - let commit_e = self.commit_e.to_extended(); - let commit_w = self.commit_w.to_extended(); + // let commit_e = self.commit_e.to_extended(); + // let commit_w = self.commit_w.to_extended(); + dbg!(vec![ + vec![E::Scalar::from(i as u64)], + z_0.get(), + z_i.get(), + vec![scalar_as_base::(self.u)], + self.x.iter().map(|x| scalar_as_base::(x)).collect(), + vec![ + self.commit_e.get_x(), + self.commit_e.get_y(), + if self.commit_e.is_identity() { + C::Base::zero() + } else { + C::Base::one() + }, + ], + vec![ + self.commit_w.get_x(), + self.commit_w.get_y(), + if self.commit_w.is_identity() { + C::Base::zero() + } else { + C::Base::one() + }, + ], + ] + .concat()); MimcRO::::default().hash_vec( vec![ vec![E::Scalar::from(i as u64)], @@ -134,14 +158,22 @@ impl RelaxedR1csInstance { vec![scalar_as_base::(self.u)], self.x.iter().map(|x| scalar_as_base::(x)).collect(), vec![ - commit_e.get_x().into(), - commit_e.get_y().into(), - commit_e.get_z().into(), + self.commit_e.get_x(), + self.commit_e.get_y(), + if self.commit_e.is_identity() { + C::Base::zero() + } else { + C::Base::one() + }, ], vec![ - commit_w.get_x().into(), - commit_w.get_y().into(), - commit_w.get_z().into(), + self.commit_w.get_x(), + self.commit_w.get_y(), + if self.commit_w.is_identity() { + C::Base::zero() + } else { + C::Base::one() + }, ], ] .concat(), diff --git a/nova/src/relaxed_r1cs/witness.rs b/nova/src/relaxed_r1cs/witness.rs index 3ef1057a..e8682505 100644 --- a/nova/src/relaxed_r1cs/witness.rs +++ b/nova/src/relaxed_r1cs/witness.rs @@ -1,6 +1,6 @@ use crate::{PedersenCommitment, R1csShape}; use zkstd::circuit::prelude::CircuitDriver; -use zkstd::common::{IntGroup, PrimeField}; +use zkstd::common::IntGroup; use zkstd::matrix::DenseVectors; /// A type that holds a witness for a given R1CS instance @@ -21,6 +21,10 @@ impl R1csWitness { pub fn commit(&self, ck: &PedersenCommitment) -> C::Affine { ck.commit(&self.w) } + + pub(crate) fn w(&self) -> DenseVectors { + self.w.clone() + } } #[derive(Clone, Debug)] @@ -32,13 +36,6 @@ pub struct RelaxedR1csWitness { } impl RelaxedR1csWitness { - pub(crate) fn new(w: DenseVectors, m: usize) -> Self { - Self { - e: DenseVectors::new(vec![C::Scalar::zero(); m]), - w, - } - } - pub fn from_r1cs_witness(shape: &R1csShape, witness: &R1csWitness) -> Self { Self { w: witness.w.clone(), @@ -59,16 +56,15 @@ impl RelaxedR1csWitness { pub(crate) fn fold( &self, - witness: &RelaxedR1csWitness, + witness: &R1csWitness, r: C::Scalar, t: DenseVectors, ) -> Self { - let r2 = r.square(); - let e2 = self.e.clone(); - let w1 = witness.w(); - let w2 = self.w(); + let w1 = self.w(); + let w2 = witness.w(); + let e1 = self.e.clone(); - let e = t * r + e2 * r2; + let e = e1 + t * r; let w = w1 + w2 * r; Self { e, w } diff --git a/nova/src/verifier.rs b/nova/src/verifier.rs index ed42f64f..e32dd809 100644 --- a/nova/src/verifier.rs +++ b/nova/src/verifier.rs @@ -1,4 +1,4 @@ -use crate::relaxed_r1cs::RelaxedR1csInstance; +use crate::relaxed_r1cs::{R1csInstance, RelaxedR1csInstance}; use crate::hash::{MimcRO, MIMC_ROUNDS}; use core::marker::PhantomData; @@ -12,16 +12,16 @@ impl Verifier { pub fn verify( commit_t: C::Affine, instance1: &RelaxedR1csInstance, - instance2: &RelaxedR1csInstance, + instance2: &R1csInstance, ) -> RelaxedR1csInstance { let mut transcript = MimcRO::::default(); transcript.append_point(commit_t); - instance2.absorb_by_transcript(&mut transcript); + instance1.absorb_by_transcript(&mut transcript); let r = transcript.squeeze(); - instance2.fold(instance1, r, commit_t) + instance1.fold(instance2, r, commit_t) } } @@ -29,7 +29,6 @@ impl Verifier { mod tests { use super::Verifier; use crate::prover::tests::example_prover; - use zkstd::matrix::DenseVectors; use crate::driver::GrumpkinDriver; use crate::relaxed_r1cs::{ @@ -43,25 +42,24 @@ mod tests { let r1cs = example_r1cs::(1); let shape = R1csShape::from(r1cs.clone()); let (x, w) = r1cs_instance_and_witness(&r1cs, &shape, &prover.ck); - let running_instance = RelaxedR1csInstance::new(x.x); - let running_witness = RelaxedR1csWitness::new(w.w, shape.m()); + let running_instance = RelaxedR1csInstance::from_r1cs_instance(&prover.ck, &shape, &x); + let running_witness = RelaxedR1csWitness::from_r1cs_witness(&shape, &w); for i in 1..10 { let r1cs_i = example_r1cs::(i); - let (x, w) = r1cs_instance_and_witness(&r1cs_i, &shape, &prover.ck); - let instance_to_fold = RelaxedR1csInstance::new(x.x); - let witness_to_fold = RelaxedR1csWitness::new(w.w, shape.m()); + let (instance_to_fold, witness_to_fold) = + r1cs_instance_and_witness(&r1cs_i, &shape, &prover.ck); let (instance, witness, commit_t) = prover.prove( - &instance_to_fold, - &witness_to_fold, &running_instance, &running_witness, + &instance_to_fold, + &witness_to_fold, ); let verified_instance = - Verifier::verify(commit_t, &instance_to_fold, &running_instance); + Verifier::verify(commit_t, &running_instance, &instance_to_fold); assert_eq!(instance, verified_instance); - assert!(shape.is_sat(&instance, &witness)); + assert!(shape.is_sat_relaxed(&instance, &witness)); } } } diff --git a/zkstd/src/circuit/gadget/curve.rs b/zkstd/src/circuit/gadget/curve.rs index dca1aa0a..e2a90b37 100644 --- a/zkstd/src/circuit/gadget/curve.rs +++ b/zkstd/src/circuit/gadget/curve.rs @@ -31,6 +31,20 @@ impl PointAssignment { Self { x, y, z } } + pub fn to_one_scale>(&self, cs: &mut R1cs) -> Self { + let z = self.z.value(cs).invert(); + if let Some(inv) = z { + let inv = FieldAssignment::constant(&inv); + Self { + x: FieldAssignment::mul(cs, &self.x, &inv), + y: FieldAssignment::mul(cs, &self.y, &inv), + z: FieldAssignment::constant(&F::one()), + } + } else { + self.clone() + } + } + pub fn identity() -> Self { let x = FieldAssignment::constant(&F::zero()); let y = FieldAssignment::constant(&F::one()); From d4b46992a9790567e80f1f2316a11a089a67deef Mon Sep 17 00:00:00 2001 From: Kirill Karbushev Date: Sat, 16 Dec 2023 14:37:13 +0900 Subject: [PATCH 14/17] do IO folding arithmetics with BigNat, hashes fixed --- Cargo.lock | 1 + groth16/src/circuit.rs | 4 ++ nova/Cargo.toml | 1 + nova/src/circuit/augmented.rs | 17 ++++--- nova/src/circuit/nifs.rs | 69 +++++++++++++++++++---------- nova/src/driver.rs | 14 +++++- nova/src/gadget.rs | 1 + nova/src/gadget/big_nat.rs | 57 +++++++++++++++++++++++- nova/src/gadget/relaxed_instance.rs | 38 +++++++++------- nova/src/ivc.rs | 5 +++ nova/src/relaxed_r1cs/instance.rs | 52 +++++++++++----------- zkstd/src/circuit.rs | 2 + 12 files changed, 183 insertions(+), 78 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d69884e0..43f0be78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1179,6 +1179,7 @@ dependencies = [ "bn-254", "grumpkin", "num-bigint 0.4.4", + "num-traits", "rand_core", "serde", "zkgroth16", diff --git a/groth16/src/circuit.rs b/groth16/src/circuit.rs index a6f1e12b..f4259e91 100644 --- a/groth16/src/circuit.rs +++ b/groth16/src/circuit.rs @@ -15,6 +15,10 @@ pub trait Circuit: Default + Debug { pub struct Bn254Driver; impl CircuitDriver for Bn254Driver { + const ORDER_STR: &'static str = + "30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"; + const BASE_STR: &'static str = + "30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"; const NUM_BITS: u16 = 254; type Affine = G1Affine; diff --git a/nova/Cargo.toml b/nova/Cargo.toml index 9719de92..aa6e4156 100644 --- a/nova/Cargo.toml +++ b/nova/Cargo.toml @@ -17,6 +17,7 @@ grumpkin = { path = "../grumpkin", default-features = false } zkgroth16 = { path = "../groth16", default-features = false } serde = { version = "1.0.102", default-features = false, features = ["derive"] } num-bigint = { version = "0.4", features = ["serde", "rand"] } +num-traits = "0.2" blake2b_simd = { version = "1", default-features = false } rand_core = { version="0.6.4", default-features = false, features = ["getrandom"] } diff --git a/nova/src/circuit/augmented.rs b/nova/src/circuit/augmented.rs index 4592af14..4a4b9094 100644 --- a/nova/src/circuit/augmented.rs +++ b/nova/src/circuit/augmented.rs @@ -99,15 +99,14 @@ impl> AugmentedFCircuit { FieldAssignment::conditional_enforce_equal(cs, &u_single.x0, &u_i_x, ¬_base_case); let r = Self::get_challenge(cs, &u_range, commit_t.clone()); - let r = FieldAssignment::constant(&C::Base::one()); - println!("R = {:?}", r.value(cs)); - println!( - "R_bits = {:?}", - FieldAssignment::to_bits(cs, &r) - .iter() - .map(|x| FieldAssignment::from(x).value(cs)) - .collect::>() - ); + // println!("R = {:?}", r.value(cs)); + // println!( + // "R_bits = {:?}", + // FieldAssignment::to_bits(cs, &r) + // .iter() + // .map(|x| FieldAssignment::from(x).value(cs)) + // .collect::>() + // ); let u_range_next_non_base = NifsCircuit::verify(cs, r, u_range.clone(), u_single.clone(), commit_t); diff --git a/nova/src/circuit/nifs.rs b/nova/src/circuit/nifs.rs index 2b649fc9..00b3efb8 100644 --- a/nova/src/circuit/nifs.rs +++ b/nova/src/circuit/nifs.rs @@ -1,6 +1,10 @@ use core::marker::PhantomData; +use num_bigint::BigInt; +use num_traits::Num; +use std::ops::Mul; -use crate::gadget::{R1csInstanceAssignment, RelaxedR1csInstanceAssignment}; +use crate::driver::{f_to_nat, nat_to_f}; +use crate::gadget::{BigNatAssignment, R1csInstanceAssignment, RelaxedR1csInstanceAssignment}; use zkstd::circuit::prelude::{CircuitDriver, FieldAssignment, PointAssignment, R1cs}; use zkstd::common::{Group, IntGroup}; @@ -16,18 +20,18 @@ impl NifsCircuit { u_single: R1csInstanceAssignment, commit_t: PointAssignment, ) -> RelaxedR1csInstanceAssignment { - println!( - "W1 = {:?}, {:?}, {:?}", - u_range.commit_w.get_x().value(cs), - u_range.commit_w.get_y().value(cs), - u_range.commit_w.get_z().value(cs) - ); - println!( - "W2 = {:?}, {:?}, {:?}", - u_single.commit_w.get_x().value(cs), - u_single.commit_w.get_y().value(cs), - u_single.commit_w.get_z().value(cs) - ); + // println!( + // "W1 = {:?}, {:?}, {:?}", + // u_range.commit_w.get_x().value(cs), + // u_range.commit_w.get_y().value(cs), + // u_range.commit_w.get_z().value(cs) + // ); + // println!( + // "W2 = {:?}, {:?}, {:?}", + // u_single.commit_w.get_x().value(cs), + // u_single.commit_w.get_y().value(cs), + // u_single.commit_w.get_z().value(cs) + // ); // W_fold = U.W + r * u.W let r_w = u_single.commit_w.scalar_point(cs, &r); let w_fold = u_range.commit_w.add(&r_w, cs); @@ -45,23 +49,42 @@ impl NifsCircuit { let u_fold = &u_range.u + &r; FieldAssignment::enforce_eq_constant(cs, &(&(&u_fold - &u_range.u) - &r), &C::Base::zero()); + let r_bn = f_to_nat(&r.value(cs)); + let m_bn = BigInt::from_str_radix(C::ORDER_STR, 16).unwrap(); + let x0_range_bn = f_to_nat(&u_range.x0.value(cs)); + let x1_range_bn = f_to_nat(&u_range.x1.value(cs)); + let x0_single_bn = f_to_nat(&u_single.x0.value(cs)); + let x1_single_bn = f_to_nat(&u_single.x1.value(cs)); + + // println!("x1 = {:?}", u_range.x1.value(cs)); + // println!("x2 = {:?}", u_single.x1.value(cs)); + + let r_x0 = x0_single_bn.mul(r_bn.clone()) % m_bn.clone(); + + // println!("R_x0 = {:?}", nat_to_f::(&r_x0)); + + // Fold U.x0 + r * x0 + // let r_x0 = FieldAssignment::mul(cs, &r, &u_single.x0); + let x0_fold = (x0_range_bn + r_x0) % m_bn.clone(); + // println!("x0_fold = {:?}", nat_to_f::(&x0_fold)); + + let r_x1 = x1_single_bn.mul(r_bn) % m_bn.clone(); + // Fold U.x0 + r * x0 - let r_x0 = FieldAssignment::mul(cs, &r, &u_single.x0); - let x0_fold = &u_range.x0 + &r_x0; + // let r_x0 = FieldAssignment::mul(cs, &r, &u_single.x0); + let x1_fold = (x1_range_bn + r_x1.clone()) % m_bn; - println!("x1 = {:?}", u_range.x1.value(cs)); - println!("x2 = {:?}", u_single.x1.value(cs)); // Fold U.x1 + r * x1 - let r_x1 = FieldAssignment::mul(cs, &r, &u_single.x1); - println!("R_x1 = {:?}", r_x1.value(cs)); - let x1_fold = &u_range.x1 + &r_x1; - println!("x1_fold = {:?}", x1_fold.value(cs)); + // let r_x1 = FieldAssignment::mul(cs, &r, &u_single.x1); + // println!("R_x1 = {:?}", nat_to_f::(&r_x1)); + // let x1_fold = &u_range.x1 + &r_x1; + // println!("x1_fold = {:?}", nat_to_f::(&x1_fold)); RelaxedR1csInstanceAssignment { commit_w: w_fold, commit_e: e_fold, u: u_fold, - x0: x0_fold, - x1: x1_fold, + x0: FieldAssignment::witness(cs, nat_to_f(&x0_fold)), + x1: FieldAssignment::witness(cs, nat_to_f(&x1_fold)), } } } diff --git a/nova/src/driver.rs b/nova/src/driver.rs index 2e5c15e2..c13bf00d 100644 --- a/nova/src/driver.rs +++ b/nova/src/driver.rs @@ -8,6 +8,10 @@ use zkstd::common::{IntGroup, PrimeField, Ring}; pub struct GrumpkinDriver; impl CircuitDriver for GrumpkinDriver { + const ORDER_STR: &'static str = + "30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"; + const BASE_STR: &'static str = + "30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"; const NUM_BITS: u16 = 254; type Affine = Affine; @@ -24,6 +28,11 @@ impl CircuitDriver for GrumpkinDriver { pub struct Bn254Driver; impl CircuitDriver for Bn254Driver { + const ORDER_STR: &'static str = + "30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"; + const BASE_STR: &'static str = + "30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"; + const NUM_BITS: u16 = 254; type Affine = G1Affine; @@ -38,17 +47,18 @@ impl CircuitDriver for Bn254Driver { /// Convert a field element to a natural number pub fn f_to_nat(f: &F) -> BigInt { - dbg!(f.to_raw_bytes()); + // dbg!(f); BigInt::from_bytes_le(Sign::Plus, &f.to_raw_bytes()) } /// Convert a natural number to a field element. /// Returns `None` if the number is too big for the field. pub fn nat_to_f(n: &BigInt) -> F { - let bytes = n.to_signed_bytes_le(); + let mut bytes = n.to_signed_bytes_le(); if bytes.len() > 64 { panic!("Length exceed the field size"); }; + bytes.resize(64, 0); let mut res = [0; 64]; res[0..64].copy_from_slice(&bytes); diff --git a/nova/src/gadget.rs b/nova/src/gadget.rs index 74f4ca64..a25f8db3 100644 --- a/nova/src/gadget.rs +++ b/nova/src/gadget.rs @@ -3,6 +3,7 @@ mod instance; mod mimc; mod relaxed_instance; +pub(crate) use big_nat::BigNatAssignment; pub(crate) use instance::R1csInstanceAssignment; pub(crate) use mimc::MimcAssignment; pub(crate) use relaxed_instance::RelaxedR1csInstanceAssignment; diff --git a/nova/src/gadget/big_nat.rs b/nova/src/gadget/big_nat.rs index 206f527f..ab5d266e 100644 --- a/nova/src/gadget/big_nat.rs +++ b/nova/src/gadget/big_nat.rs @@ -1,2 +1,55 @@ -#[derive(Clone, Debug)] -pub struct BigNatAssignment(); +use crate::driver::nat_to_limbs; +use num_bigint::BigInt; +use zkstd::circuit::prelude::{BinaryAssignment, FieldAssignment}; +use zkstd::circuit::CircuitDriver; +use zkstd::common::PrimeField; +use zkstd::r1cs::R1cs; + +pub(crate) const BN_LIMB_WIDTH: usize = 64; +pub(crate) const BN_N_LIMBS: usize = 4; + +#[derive(Clone)] +pub struct BigNatAssignment { + limbs: Vec>, + max_word: BigInt, +} + +impl BigNatAssignment { + pub fn witness>(cs: &mut R1cs, num: BigInt) -> Self { + let limb_values = nat_to_limbs::(&num, BN_LIMB_WIDTH, BN_N_LIMBS); + let mut limbs = vec![FieldAssignment::constant(&F::zero()); BN_N_LIMBS]; + for (i, limb) in limb_values.iter().enumerate() { + limbs[i] = FieldAssignment::witness(cs, *limb); + } + dbg!(limb_values); + + Self { + limbs, + max_word: num, + } + } + + pub fn conditional_select>( + cs: &mut R1cs, + a: &Self, + b: &Self, + condition: &BinaryAssignment, + ) -> BigNatAssignment { + let mut limbs = vec![FieldAssignment::constant(&F::zero()); BN_N_LIMBS]; + for i in 0..BN_N_LIMBS { + limbs[i] = FieldAssignment::conditional_select(cs, &a.limbs[i], &b.limbs[i], condition); + } + + let max_word = if cs[*condition.inner()] == F::one() { + a.max_word.clone() + } else { + b.max_word.clone() + }; + + Self { limbs, max_word } + } + + pub fn n_bits(&self) -> usize { + BN_LIMB_WIDTH * (BN_N_LIMBS - 1) + self.max_word.bits() as usize + } +} diff --git a/nova/src/gadget/relaxed_instance.rs b/nova/src/gadget/relaxed_instance.rs index b092c53f..d70943a9 100644 --- a/nova/src/gadget/relaxed_instance.rs +++ b/nova/src/gadget/relaxed_instance.rs @@ -1,13 +1,15 @@ use crate::relaxed_r1cs::RelaxedR1csInstance; +use rand_core::OsRng; use crate::circuit::MimcROCircuit; -use crate::driver::scalar_as_base; +use crate::driver::{f_to_nat, scalar_as_base}; +use crate::gadget::big_nat::BigNatAssignment; use crate::gadget::R1csInstanceAssignment; use crate::hash::MIMC_ROUNDS; use zkstd::circuit::prelude::{ BinaryAssignment, CircuitDriver, FieldAssignment, PointAssignment, R1cs, }; -use zkstd::common::{CurveGroup, Ring}; +use zkstd::common::{CurveGroup, Group, Ring}; #[derive(Clone)] pub(crate) struct RelaxedR1csInstanceAssignment { @@ -16,6 +18,8 @@ pub(crate) struct RelaxedR1csInstanceAssignment { pub(crate) u: FieldAssignment, pub(crate) x0: FieldAssignment, pub(crate) x1: FieldAssignment, + // pub(crate) x0: BigInt, + // pub(crate) x1: BigInt, } impl RelaxedR1csInstanceAssignment { @@ -44,6 +48,8 @@ impl RelaxedR1csInstanceAssignment { ); let u = FieldAssignment::witness(cs, scalar_as_base::(*u)); let x0 = FieldAssignment::witness(cs, scalar_as_base::(x[0])); + // let x0 = BigNatAssignment::witness(cs, f_to_nat(&x[0])); + // let x1 = BigNatAssignment::witness(cs, f_to_nat(&x[1])); let x1 = FieldAssignment::witness(cs, scalar_as_base::(x[1])); Self { @@ -120,20 +126,20 @@ impl RelaxedR1csInstanceAssignment { // commit_w.get_y().value(cs), // commit_w.get_z().value(cs) // ); - dbg!(vec![ - vec![i.clone()], - z_0.clone(), - z_i.clone(), - vec![self.u.clone()], - vec![self.x0.clone()], - vec![self.x1.clone()], - vec![commit_e.get_x(), commit_e.get_y(), commit_e.get_z()], - vec![commit_w.get_x(), commit_w.get_y(), commit_w.get_z()], - ] - .concat() - .iter() - .map(|x| x.value(cs)) - .collect::>()); + // dbg!(vec![ + // vec![i.clone()], + // z_0.clone(), + // z_i.clone(), + // vec![self.u.clone()], + // vec![self.x0.clone()], + // vec![self.x1.clone()], + // vec![commit_e.get_x(), commit_e.get_y(), commit_e.get_z()], + // vec![commit_w.get_x(), commit_w.get_y(), commit_w.get_z()], + // ] + // .concat() + // .iter() + // .map(|x| x.value(cs)) + // .collect::>()); MimcROCircuit::::default().hash_vec( cs, vec![ diff --git a/nova/src/ivc.rs b/nova/src/ivc.rs index 0809602c..efc76824 100644 --- a/nova/src/ivc.rs +++ b/nova/src/ivc.rs @@ -194,6 +194,11 @@ where println!("Primary out"); let (u_single_next_primary, w_single_next_primary) = r1cs_instance_and_witness(&cs_primary, &pp.r1cs_shape_primary, &pp.ck_primary); + assert!(pp.r1cs_shape_primary.is_sat( + &pp.ck_primary, + &u_single_next_primary, + &w_single_next_primary + )); let (u_range_next_primary, w_range_next_primary, commit_t_primary) = self.prover_primary.prove( diff --git a/nova/src/relaxed_r1cs/instance.rs b/nova/src/relaxed_r1cs/instance.rs index 34a3c0d6..97457d7d 100644 --- a/nova/src/relaxed_r1cs/instance.rs +++ b/nova/src/relaxed_r1cs/instance.rs @@ -124,32 +124,32 @@ impl RelaxedR1csInstance { ) -> C::Scalar { // let commit_e = self.commit_e.to_extended(); // let commit_w = self.commit_w.to_extended(); - dbg!(vec![ - vec![E::Scalar::from(i as u64)], - z_0.get(), - z_i.get(), - vec![scalar_as_base::(self.u)], - self.x.iter().map(|x| scalar_as_base::(x)).collect(), - vec![ - self.commit_e.get_x(), - self.commit_e.get_y(), - if self.commit_e.is_identity() { - C::Base::zero() - } else { - C::Base::one() - }, - ], - vec![ - self.commit_w.get_x(), - self.commit_w.get_y(), - if self.commit_w.is_identity() { - C::Base::zero() - } else { - C::Base::one() - }, - ], - ] - .concat()); + // dbg!(vec![ + // vec![E::Scalar::from(i as u64)], + // z_0.get(), + // z_i.get(), + // vec![scalar_as_base::(self.u)], + // self.x.iter().map(|x| scalar_as_base::(x)).collect(), + // vec![ + // self.commit_e.get_x(), + // self.commit_e.get_y(), + // if self.commit_e.is_identity() { + // C::Base::zero() + // } else { + // C::Base::one() + // }, + // ], + // vec![ + // self.commit_w.get_x(), + // self.commit_w.get_y(), + // if self.commit_w.is_identity() { + // C::Base::zero() + // } else { + // C::Base::one() + // }, + // ], + // ] + // .concat()); MimcRO::::default().hash_vec( vec![ vec![E::Scalar::from(i as u64)], diff --git a/zkstd/src/circuit.rs b/zkstd/src/circuit.rs index 766b9845..00bffc1d 100644 --- a/zkstd/src/circuit.rs +++ b/zkstd/src/circuit.rs @@ -5,6 +5,8 @@ use crate::common::{BNAffine, Deserialize, PrimeField, Serialize}; use core::fmt::Debug; pub trait CircuitDriver: Clone + Debug + Default { + const ORDER_STR: &'static str; + const BASE_STR: &'static str; const NUM_BITS: u16; // curve affine type Affine: BNAffine; From 59f8c20bc32029a33d44a3b87741fe17f3bbb64c Mon Sep 17 00:00:00 2001 From: Kirill Karbushev Date: Mon, 18 Dec 2023 12:02:55 +0900 Subject: [PATCH 15/17] fix IVC for MIMC_ROUNDS=0 --- nova/src/ivc.rs | 4 ++++ nova/src/relaxed_r1cs.rs | 1 + zkstd/src/circuit/gadget/curve.rs | 15 ++++++++++++++ zkstd/src/circuit/gadget/field.rs | 33 ++++++++++++++++++++----------- 4 files changed, 41 insertions(+), 12 deletions(-) diff --git a/nova/src/ivc.rs b/nova/src/ivc.rs index efc76824..e9357bc9 100644 --- a/nova/src/ivc.rs +++ b/nova/src/ivc.rs @@ -177,6 +177,10 @@ where &self.w_single_secondary, ); + assert!(pp + .r1cs_shape_secondary + .is_sat_relaxed(&u_range_next_secondary, &w_range_next_secondary)); + let mut cs_primary = R1cs::::default(); let circuit_primary = AugmentedFCircuit:: { is_primary: true, diff --git a/nova/src/relaxed_r1cs.rs b/nova/src/relaxed_r1cs.rs index 430928b4..bee9e949 100644 --- a/nova/src/relaxed_r1cs.rs +++ b/nova/src/relaxed_r1cs.rs @@ -27,6 +27,7 @@ pub(crate) fn r1cs_instance_and_witness( ) -> (R1csInstance, R1csWitness) { println!("Cs.x = {:?}", cs.x()); assert_eq!(cs.m_l_1(), shape.m_l_1()); + assert_eq!(cs.m(), shape.m()); let w = cs.w(); let x = cs.x()[1..].to_vec(); assert_eq!(x.len(), shape.l()); diff --git a/zkstd/src/circuit/gadget/curve.rs b/zkstd/src/circuit/gadget/curve.rs index e2a90b37..34a49876 100644 --- a/zkstd/src/circuit/gadget/curve.rs +++ b/zkstd/src/circuit/gadget/curve.rs @@ -43,6 +43,21 @@ impl PointAssignment { } else { self.clone() } + + // let take_value = + // FieldAssignment::is_neq(cs, &self.z, &FieldAssignment::constant(&F::zero())); + // let z = FieldAssignment::constant(&self.z.value(cs).invert().unwrap_or(F::zero())); + // + // // let inv = FieldAssignment::constant(&z); + // let p = Self { + // x: FieldAssignment::mul(cs, &self.x, &z), + // y: FieldAssignment::mul(cs, &self.y, &z), + // z: FieldAssignment::constant(&F::one()), + // }; + // + // PointAssignment::conditional_select(cs, &p, &PointAssignment::identity(), &take_value) + + // p.select_identity(cs, &take_value) } pub fn identity() -> Self { diff --git a/zkstd/src/circuit/gadget/field.rs b/zkstd/src/circuit/gadget/field.rs index 4fc32ab9..64977dc8 100644 --- a/zkstd/src/circuit/gadget/field.rs +++ b/zkstd/src/circuit/gadget/field.rs @@ -177,20 +177,29 @@ impl FieldAssignment { pub fn conditional_select>( cs: &mut R1cs, - x: &Self, - y: &Self, + a: &Self, + b: &Self, condition: &BinaryAssignment, ) -> FieldAssignment { - let c = if cs[*condition.inner()] == F::one() { - x - } else { - y - }; - - let left = FieldAssignment::mul(cs, &(x - y), &FieldAssignment::from(condition)); - - FieldAssignment::enforce_eq(cs, &left, &(c - y)); - c.clone() + // let c = if cs[*condition.inner()] == F::one() { + // a + // } else { + // b + // }; + + let select_a = FieldAssignment::mul(cs, a, &FieldAssignment::from(condition)); + let select_b = FieldAssignment::mul( + cs, + b, + &(&FieldAssignment::constant(&F::one()) - &FieldAssignment::from(condition)), + ); + + // let left = FieldAssignment::mul(cs, &(a - b), &FieldAssignment::from(condition)); + // + // FieldAssignment::enforce_eq(cs, &left, &(c - b)); + // c.clone() + + &select_a + &select_b } pub fn is_eq>( From f39f623fa72a3a0099b4d6e748d0fe1dc021ebcc Mon Sep 17 00:00:00 2001 From: Kirill Karbushev Date: Mon, 18 Dec 2023 15:02:10 +0900 Subject: [PATCH 16/17] fix IVC test for larger amount of steps --- nova/src/circuit/augmented.rs | 18 ----- nova/src/circuit/nifs.rs | 112 +++------------------------- nova/src/circuit/transcript.rs | 6 +- nova/src/driver.rs | 2 - nova/src/gadget.rs | 1 - nova/src/gadget/big_nat.rs | 1 - nova/src/gadget/instance.rs | 40 ---------- nova/src/gadget/relaxed_instance.rs | 39 ++-------- nova/src/hash.rs | 2 +- nova/src/ivc.rs | 42 ++--------- nova/src/proof.rs | 17 ----- nova/src/prover.rs | 11 +-- nova/src/relaxed_r1cs.rs | 6 -- nova/src/relaxed_r1cs/instance.rs | 35 +-------- nova/src/relaxed_r1cs/witness.rs | 1 - nova/src/test.rs | 2 - zkstd/src/circuit/gadget/curve.rs | 39 +++------- zkstd/src/lib.rs | 2 +- 18 files changed, 44 insertions(+), 332 deletions(-) diff --git a/nova/src/circuit/augmented.rs b/nova/src/circuit/augmented.rs index 4a4b9094..3e1ff5be 100644 --- a/nova/src/circuit/augmented.rs +++ b/nova/src/circuit/augmented.rs @@ -99,14 +99,6 @@ impl> AugmentedFCircuit { FieldAssignment::conditional_enforce_equal(cs, &u_single.x0, &u_i_x, ¬_base_case); let r = Self::get_challenge(cs, &u_range, commit_t.clone()); - // println!("R = {:?}", r.value(cs)); - // println!( - // "R_bits = {:?}", - // FieldAssignment::to_bits(cs, &r) - // .iter() - // .map(|x| FieldAssignment::from(x).value(cs)) - // .collect::>() - // ); let u_range_next_non_base = NifsCircuit::verify(cs, r, u_range.clone(), u_single.clone(), commit_t); @@ -119,16 +111,6 @@ impl> AugmentedFCircuit { let z_next = FC::invoke_cs(cs, z_i); - // println!( - // "Hash_circuit\n{:?},\n{:?},\n{:?},\n{:?},\n{:?},\n{:?},", - // (&i + &FieldAssignment::constant(&C::Base::one())).value(cs), - // z_0.iter().map(|x| x.value(cs)).collect::>(), - // z_next.iter().map(|x| x.value(cs)).collect::>(), - // u_range_next.u.value(cs), - // u_range_next.x0.value(cs), - // u_range_next.x1.value(cs), - // ); - println!("HASH"); let u_next_x = u_range_next.hash( cs, &i + &FieldAssignment::constant(&C::Base::one()), diff --git a/nova/src/circuit/nifs.rs b/nova/src/circuit/nifs.rs index 00b3efb8..8c0e24a9 100644 --- a/nova/src/circuit/nifs.rs +++ b/nova/src/circuit/nifs.rs @@ -1,10 +1,10 @@ use core::marker::PhantomData; use num_bigint::BigInt; use num_traits::Num; -use std::ops::Mul; +use std::ops::{Add, Mul}; use crate::driver::{f_to_nat, nat_to_f}; -use crate::gadget::{BigNatAssignment, R1csInstanceAssignment, RelaxedR1csInstanceAssignment}; +use crate::gadget::{R1csInstanceAssignment, RelaxedR1csInstanceAssignment}; use zkstd::circuit::prelude::{CircuitDriver, FieldAssignment, PointAssignment, R1cs}; use zkstd::common::{Group, IntGroup}; @@ -20,18 +20,6 @@ impl NifsCircuit { u_single: R1csInstanceAssignment, commit_t: PointAssignment, ) -> RelaxedR1csInstanceAssignment { - // println!( - // "W1 = {:?}, {:?}, {:?}", - // u_range.commit_w.get_x().value(cs), - // u_range.commit_w.get_y().value(cs), - // u_range.commit_w.get_z().value(cs) - // ); - // println!( - // "W2 = {:?}, {:?}, {:?}", - // u_single.commit_w.get_x().value(cs), - // u_single.commit_w.get_y().value(cs), - // u_single.commit_w.get_z().value(cs) - // ); // W_fold = U.W + r * u.W let r_w = u_single.commit_w.scalar_point(cs, &r); let w_fold = u_range.commit_w.add(&r_w, cs); @@ -45,40 +33,25 @@ impl NifsCircuit { let r_t = commit_t.scalar_point(cs, &r); let e_fold = u_range.commit_e.add(&r_t, cs); - // u_fold = U.u + r - let u_fold = &u_range.u + &r; - FieldAssignment::enforce_eq_constant(cs, &(&(&u_fold - &u_range.u) - &r), &C::Base::zero()); - let r_bn = f_to_nat(&r.value(cs)); let m_bn = BigInt::from_str_radix(C::ORDER_STR, 16).unwrap(); - let x0_range_bn = f_to_nat(&u_range.x0.value(cs)); - let x1_range_bn = f_to_nat(&u_range.x1.value(cs)); - let x0_single_bn = f_to_nat(&u_single.x0.value(cs)); - let x1_single_bn = f_to_nat(&u_single.x1.value(cs)); - // println!("x1 = {:?}", u_range.x1.value(cs)); - // println!("x2 = {:?}", u_single.x1.value(cs)); - - let r_x0 = x0_single_bn.mul(r_bn.clone()) % m_bn.clone(); - - // println!("R_x0 = {:?}", nat_to_f::(&r_x0)); + // u_fold = U.u + r + let u = f_to_nat(&u_range.u.value(cs)); + let u_fold = FieldAssignment::witness(cs, nat_to_f(&(u.add(r_bn.clone()) % m_bn.clone()))); + // FieldAssignment::enforce_eq_constant(cs, &(&(&u_fold - &u_range.u) - &r), &C::Base::zero()); // Fold U.x0 + r * x0 - // let r_x0 = FieldAssignment::mul(cs, &r, &u_single.x0); + let x0_range_bn = f_to_nat(&u_range.x0.value(cs)); + let x0_single_bn = f_to_nat(&u_single.x0.value(cs)); + let r_x0 = x0_single_bn.mul(r_bn.clone()) % m_bn.clone(); let x0_fold = (x0_range_bn + r_x0) % m_bn.clone(); - // println!("x0_fold = {:?}", nat_to_f::(&x0_fold)); - + // Fold U.x1 + r * x1 + let x1_range_bn = f_to_nat(&u_range.x1.value(cs)); + let x1_single_bn = f_to_nat(&u_single.x1.value(cs)); let r_x1 = x1_single_bn.mul(r_bn) % m_bn.clone(); - - // Fold U.x0 + r * x0 - // let r_x0 = FieldAssignment::mul(cs, &r, &u_single.x0); let x1_fold = (x1_range_bn + r_x1.clone()) % m_bn; - // Fold U.x1 + r * x1 - // let r_x1 = FieldAssignment::mul(cs, &r, &u_single.x1); - // println!("R_x1 = {:?}", nat_to_f::(&r_x1)); - // let x1_fold = &u_range.x1 + &r_x1; - // println!("x1_fold = {:?}", nat_to_f::(&x1_fold)); RelaxedR1csInstanceAssignment { commit_w: w_fold, commit_e: e_fold, @@ -88,64 +61,3 @@ impl NifsCircuit { } } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::driver::{Bn254Driver, GrumpkinDriver}; - use crate::gadget::R1csInstanceAssignment; - use crate::hash::{MimcRO, MIMC_ROUNDS}; - use crate::prover::tests::example_prover; - use crate::relaxed_r1cs::{r1cs_instance_and_witness, RelaxedR1csInstance, RelaxedR1csWitness}; - use crate::R1csShape; - use zkstd::common::CurveGroup; - - use zkstd::r1cs::test::example_r1cs; - - #[test] - #[ignore] - fn nifs_circuit() { - let prover = example_prover(); - let r1cs = example_r1cs::(1); - let shape = R1csShape::from(r1cs.clone()); - let (x, w) = r1cs_instance_and_witness(&r1cs, &shape, &prover.ck); - let running_instance = RelaxedR1csInstance::from_r1cs_instance(&prover.ck, &shape, &x); - let running_witness = RelaxedR1csWitness::from_r1cs_witness(&shape, &w); - - let r1cs_2 = example_r1cs::(2); - let (instance_to_fold, witness_to_fold) = - r1cs_instance_and_witness(&r1cs, &shape, &prover.ck); - - let (instance, witness, commit_t) = prover.prove( - &running_instance, - &running_witness, - &instance_to_fold, - &witness_to_fold, - ); - - let mut transcript = MimcRO::::default(); - transcript.append_point(commit_t); - running_instance.absorb_by_transcript(&mut transcript); - let t = prover.compute_cross_term( - &running_instance, - &running_witness, - &instance_to_fold, - &witness_to_fold, - ); - let r = transcript.squeeze(); - - let mut cs = R1cs::::default(); - let r = FieldAssignment::witness(&mut cs, r.into()); - let instance1 = RelaxedR1csInstanceAssignment::witness(&mut cs, &running_instance); - let instance2 = R1csInstanceAssignment::witness(&mut cs, &instance_to_fold); - let commit_t = PointAssignment::witness( - &mut cs, - commit_t.get_x(), - commit_t.get_y(), - commit_t.is_identity(), - ); - - let instance3 = NifsCircuit::verify(&mut cs, r, instance1, instance2, commit_t); - assert!(cs.is_sat()); - } -} diff --git a/nova/src/circuit/transcript.rs b/nova/src/circuit/transcript.rs index 9f124f01..cf725641 100644 --- a/nova/src/circuit/transcript.rs +++ b/nova/src/circuit/transcript.rs @@ -66,9 +66,9 @@ mod tests { #[test] fn mimc_circuit() { let mut mimc = MimcRO::::default(); - let mut mimc_circuit = MimcROCircuit::::default(); // Base = Fr, Scalar = Fq - let mut cs: R1cs = R1cs::default(); // Base = Fq, Scalar = Fr - let point = Affine::random(OsRng); // Base = Fr, Scalar = Fq + let mut mimc_circuit = MimcROCircuit::::default(); + let mut cs: R1cs = R1cs::default(); + let point = Affine::random(OsRng); let scalar = Fr::random(OsRng); let point_assignment = PointAssignment::instance(&mut cs, point); diff --git a/nova/src/driver.rs b/nova/src/driver.rs index c13bf00d..d543ba28 100644 --- a/nova/src/driver.rs +++ b/nova/src/driver.rs @@ -47,12 +47,10 @@ impl CircuitDriver for Bn254Driver { /// Convert a field element to a natural number pub fn f_to_nat(f: &F) -> BigInt { - // dbg!(f); BigInt::from_bytes_le(Sign::Plus, &f.to_raw_bytes()) } /// Convert a natural number to a field element. -/// Returns `None` if the number is too big for the field. pub fn nat_to_f(n: &BigInt) -> F { let mut bytes = n.to_signed_bytes_le(); if bytes.len() > 64 { diff --git a/nova/src/gadget.rs b/nova/src/gadget.rs index a25f8db3..74f4ca64 100644 --- a/nova/src/gadget.rs +++ b/nova/src/gadget.rs @@ -3,7 +3,6 @@ mod instance; mod mimc; mod relaxed_instance; -pub(crate) use big_nat::BigNatAssignment; pub(crate) use instance::R1csInstanceAssignment; pub(crate) use mimc::MimcAssignment; pub(crate) use relaxed_instance::RelaxedR1csInstanceAssignment; diff --git a/nova/src/gadget/big_nat.rs b/nova/src/gadget/big_nat.rs index ab5d266e..aa79689a 100644 --- a/nova/src/gadget/big_nat.rs +++ b/nova/src/gadget/big_nat.rs @@ -21,7 +21,6 @@ impl BigNatAssignment { for (i, limb) in limb_values.iter().enumerate() { limbs[i] = FieldAssignment::witness(cs, *limb); } - dbg!(limb_values); Self { limbs, diff --git a/nova/src/gadget/instance.rs b/nova/src/gadget/instance.rs index 83a6b6d1..5c1d5aed 100644 --- a/nova/src/gadget/instance.rs +++ b/nova/src/gadget/instance.rs @@ -47,46 +47,6 @@ impl R1csInstanceAssignment { let x1 = FieldAssignment::conditional_select(cs, &a.x1, &b.x1, condition); Self { commit_w, x0, x1 } } - - pub(crate) fn absorb_by_transcript( - &self, - transcript: &mut MimcROCircuit, - ) { - transcript.append_point(self.commit_w.clone()); - transcript.append(self.x0.clone()); - transcript.append(self.x1.clone()); - } - - // pub(crate) fn hash>( - // &self, - // cs: &mut R1cs, - // i: FieldAssignment, - // z_0: Vec>, - // z_i: Vec>, - // ) -> FieldAssignment { - // MimcROCircuit::::default().hash_vec( - // cs, - // vec![ - // vec![i], - // z_0, - // z_i, - // vec![self.u.clone()], - // vec![self.x0.clone()], - // vec![self.x1.clone()], - // vec![ - // self.commit_e.get_x(), - // self.commit_e.get_y(), - // self.commit_e.get_z(), - // ], - // vec![ - // self.commit_w.get_x(), - // self.commit_w.get_y(), - // self.commit_w.get_z(), - // ], - // ] - // .concat(), - // ) - // } } #[cfg(test)] diff --git a/nova/src/gadget/relaxed_instance.rs b/nova/src/gadget/relaxed_instance.rs index d70943a9..8c75f59e 100644 --- a/nova/src/gadget/relaxed_instance.rs +++ b/nova/src/gadget/relaxed_instance.rs @@ -1,25 +1,22 @@ use crate::relaxed_r1cs::RelaxedR1csInstance; -use rand_core::OsRng; use crate::circuit::MimcROCircuit; -use crate::driver::{f_to_nat, scalar_as_base}; -use crate::gadget::big_nat::BigNatAssignment; +use crate::driver::scalar_as_base; use crate::gadget::R1csInstanceAssignment; use crate::hash::MIMC_ROUNDS; use zkstd::circuit::prelude::{ BinaryAssignment, CircuitDriver, FieldAssignment, PointAssignment, R1cs, }; -use zkstd::common::{CurveGroup, Group, Ring}; +use zkstd::common::{CurveGroup, Ring}; #[derive(Clone)] pub(crate) struct RelaxedR1csInstanceAssignment { pub(crate) commit_w: PointAssignment, pub(crate) commit_e: PointAssignment, pub(crate) u: FieldAssignment, + // TODO: change BigNatAssignment pub(crate) x0: FieldAssignment, pub(crate) x1: FieldAssignment, - // pub(crate) x0: BigInt, - // pub(crate) x1: BigInt, } impl RelaxedR1csInstanceAssignment { @@ -48,8 +45,6 @@ impl RelaxedR1csInstanceAssignment { ); let u = FieldAssignment::witness(cs, scalar_as_base::(*u)); let x0 = FieldAssignment::witness(cs, scalar_as_base::(x[0])); - // let x0 = BigNatAssignment::witness(cs, f_to_nat(&x[0])); - // let x1 = BigNatAssignment::witness(cs, f_to_nat(&x[1])); let x1 = FieldAssignment::witness(cs, scalar_as_base::(x[1])); Self { @@ -61,7 +56,6 @@ impl RelaxedR1csInstanceAssignment { } } - /// Allocates the R1CS Instance as a `RelaxedR1CSInstance` in the circuit. /// E = 0, u = 1 pub fn from_r1cs_instance>( cs: &mut R1cs, @@ -115,31 +109,8 @@ impl RelaxedR1csInstanceAssignment { z_0: Vec>, z_i: Vec>, ) -> FieldAssignment { - let commit_e = self.commit_e.to_one_scale(cs); - let commit_w = self.commit_w.to_one_scale(cs); - // println!( - // "{:?},\n{:?},\n{:?},\n{:?},\n{:?},\n{:?},", - // commit_e.get_x().value(cs), - // commit_e.get_y().value(cs), - // commit_e.get_z().value(cs), - // commit_w.get_x().value(cs), - // commit_w.get_y().value(cs), - // commit_w.get_z().value(cs) - // ); - // dbg!(vec![ - // vec![i.clone()], - // z_0.clone(), - // z_i.clone(), - // vec![self.u.clone()], - // vec![self.x0.clone()], - // vec![self.x1.clone()], - // vec![commit_e.get_x(), commit_e.get_y(), commit_e.get_z()], - // vec![commit_w.get_x(), commit_w.get_y(), commit_w.get_z()], - // ] - // .concat() - // .iter() - // .map(|x| x.value(cs)) - // .collect::>()); + let commit_e = self.commit_e.descale(cs); + let commit_w = self.commit_w.descale(cs); MimcROCircuit::::default().hash_vec( cs, vec![ diff --git a/nova/src/hash.rs b/nova/src/hash.rs index 59e8b91b..cbaf98fd 100644 --- a/nova/src/hash.rs +++ b/nova/src/hash.rs @@ -7,7 +7,7 @@ use zkstd::common::{BNAffine, IntGroup, PrimeField, Ring}; /// Amount of rounds calculated for the 254 bit field. /// Doubled due to the usage of Feistel mode with zero key. -pub(crate) const MIMC_ROUNDS: usize = 0; +pub(crate) const MIMC_ROUNDS: usize = 322; pub(crate) struct Mimc { pub(crate) constants: [F; ROUND], diff --git a/nova/src/ivc.rs b/nova/src/ivc.rs index e9357bc9..fb2abbd7 100644 --- a/nova/src/ivc.rs +++ b/nova/src/ivc.rs @@ -26,16 +26,16 @@ where zi_secondary: DenseVectors, prover_primary: Prover, prover_secondary: Prover, - // r1cs instance to be folded - // u_i + // u_i for primary circuit // represents the correct execution of invocation i of F′ u_single_secondary: R1csInstance, w_single_secondary: R1csWitness, - // running r1cs instance - // U_i + // U_i for primary circuit // represents the correct execution of invocations 1, . . . , i - 1 of F′ u_range_primary: RelaxedR1csInstance, w_range_primary: RelaxedR1csWitness, + // U_i for secondary circuit + // represents the correct execution of invocations 1, . . . , i - 1 of F′ u_range_secondary: RelaxedR1csInstance, w_range_secondary: RelaxedR1csWitness, f: PhantomData<(FC1, FC2)>, @@ -53,7 +53,6 @@ where z0_primary: DenseVectors, z0_secondary: DenseVectors, ) -> Self { - println!("START"); let mut cs_primary = R1cs::::default(); let circuit_primary = AugmentedFCircuit:: { is_primary: true, @@ -69,11 +68,6 @@ where let (u_single_next_primary, w_single_next_primary) = r1cs_instance_and_witness(&cs_primary, &pp.r1cs_shape_primary, &pp.ck_primary); - assert!(pp.r1cs_shape_primary.is_sat( - &pp.ck_primary, - &u_single_next_primary, - &w_single_next_primary, - )); let prover_primary = Prover::new(pp.r1cs_shape_primary.clone(), pp.ck_primary.clone()); let mut cs_secondary = R1cs::::default(); @@ -91,12 +85,6 @@ where let (u_single_next_secondary, w_single_next_secondary) = r1cs_instance_and_witness(&cs_secondary, &pp.r1cs_shape_secondary, &pp.ck_secondary); - assert!(pp.r1cs_shape_secondary.is_sat( - &pp.ck_secondary, - &u_single_next_secondary, - &w_single_next_secondary, - )); - let prover_secondary = Prover::new(pp.r1cs_shape_secondary.clone(), pp.ck_secondary.clone()); @@ -145,7 +133,6 @@ where &mut self, pp: &PublicParams, ) -> RecursiveProof { - println!("STEP"); if self.i == 0 { self.i = 1; return RecursiveProof { @@ -177,10 +164,6 @@ where &self.w_single_secondary, ); - assert!(pp - .r1cs_shape_secondary - .is_sat_relaxed(&u_range_next_secondary, &w_range_next_secondary)); - let mut cs_primary = R1cs::::default(); let circuit_primary = AugmentedFCircuit:: { is_primary: true, @@ -194,15 +177,8 @@ where }; let zi_primary = circuit_primary.generate(&mut cs_primary); - - println!("Primary out"); let (u_single_next_primary, w_single_next_primary) = r1cs_instance_and_witness(&cs_primary, &pp.r1cs_shape_primary, &pp.ck_primary); - assert!(pp.r1cs_shape_primary.is_sat( - &pp.ck_primary, - &u_single_next_primary, - &w_single_next_primary - )); let (u_range_next_primary, w_range_next_primary, commit_t_primary) = self.prover_primary.prove( @@ -225,17 +201,9 @@ where }; let zi_secondary = circuit_secondary.generate(&mut cs_secondary); - - println!("Secondary out"); let (u_single_next_secondary, w_single_next_secondary) = r1cs_instance_and_witness(&cs_secondary, &pp.r1cs_shape_secondary, &pp.ck_secondary); - // assert!(pp.r1cs_shape_secondary.is_sat( - // &pp.ck_secondary, - // &u_single_next_secondary, - // &w_single_next_secondary, - // )); - // update values self.i += 1; self.u_range_primary = u_range_next_primary; @@ -380,7 +348,7 @@ mod tests { z0_secondary, ); - for i in 0..2 { + for i in 0..4 { let proof = ivc.prove_step(&pp); assert!(proof.verify(&pp)); } diff --git a/nova/src/proof.rs b/nova/src/proof.rs index c82d798a..ea937f46 100644 --- a/nova/src/proof.rs +++ b/nova/src/proof.rs @@ -49,7 +49,6 @@ where || u_range_primary.x.len() != 2 || u_range_secondary.x.len() != 2 { - println!("Length doesn't match"); return false; } let (hash_primary, hash_secondary) = { @@ -59,28 +58,12 @@ where ) }; - dbg!(hash_primary); - dbg!(hash_secondary); - if hash_primary != u_single_secondary.x[0] || hash_secondary != scalar_as_base::(u_single_secondary.x[1]) { - println!("Hash doesn't match"); return false; } - dbg!(pp - .r1cs_shape_primary - .is_sat_relaxed(&u_range_primary, &w_range_primary)); - dbg!(pp - .r1cs_shape_secondary - .is_sat_relaxed(&u_range_secondary, &w_range_secondary)); - dbg!(pp.r1cs_shape_secondary.is_sat( - &pp.ck_secondary, - &u_single_secondary, - &w_single_secondary - )); - pp.r1cs_shape_primary .is_sat_relaxed(&u_range_primary, &w_range_primary) && pp diff --git a/nova/src/prover.rs b/nova/src/prover.rs index cef32cc6..0a29476f 100644 --- a/nova/src/prover.rs +++ b/nova/src/prover.rs @@ -12,14 +12,13 @@ use zkstd::matrix::DenseVectors; pub struct Prover { // public parameters pub(crate) ck: PedersenCommitment, - // r1cs structure - f: R1csShape, + shape: R1csShape, } impl Prover { pub fn new(shape: R1csShape, ck: PedersenCommitment) -> Self { - Self { ck, f: shape } + Self { ck, shape } } pub fn prove( @@ -40,8 +39,6 @@ impl Prover { let r = transcript.squeeze(); - dbg!(r); - // fold instance let instance = instance1.fold(instance2, r, commit_t); @@ -62,8 +59,8 @@ impl Prover { ) -> DenseVectors { let u1 = instance1.u; let u2 = C::Scalar::one(); - let m = self.f.m(); - let (a, b, c) = self.f.matrices(); + let m = self.shape.m(); + let (a, b, c) = self.shape.matrices(); let (w1, w2) = (witness1.w(), witness2.w()); let (x1, x2) = (instance1.x(), instance2.x()); diff --git a/nova/src/relaxed_r1cs.rs b/nova/src/relaxed_r1cs.rs index bee9e949..2950a04b 100644 --- a/nova/src/relaxed_r1cs.rs +++ b/nova/src/relaxed_r1cs.rs @@ -10,8 +10,6 @@ use zkstd::matrix::{DenseVectors, SparseMatrix}; #[derive(Clone, Debug)] pub struct R1csShape { - // 1. Structure S - // a, b and c matrices and matrix size m: usize, instance_length: usize, witness_length: usize, @@ -25,7 +23,6 @@ pub(crate) fn r1cs_instance_and_witness( shape: &R1csShape, ck: &PedersenCommitment, ) -> (R1csInstance, R1csWitness) { - println!("Cs.x = {:?}", cs.x()); assert_eq!(cs.m_l_1(), shape.m_l_1()); assert_eq!(cs.m(), shape.m()); let w = cs.w(); @@ -123,7 +120,6 @@ impl R1csShape { let Self { m, a, b, c, .. } = self; let R1csInstance { commit_w, x } = instance; - dbg!(x); let R1csWitness { w } = witness; let l = x.len() + 1; @@ -141,9 +137,7 @@ impl R1csShape { .iter() .zip(cz.iter()) .all(|(left, right)| left == right); - dbg!(constraints_check); let commit_check = *commit_w == witness.commit(ck); - dbg!(commit_check); constraints_check && commit_check } diff --git a/nova/src/relaxed_r1cs/instance.rs b/nova/src/relaxed_r1cs/instance.rs index 97457d7d..491e3aba 100644 --- a/nova/src/relaxed_r1cs/instance.rs +++ b/nova/src/relaxed_r1cs/instance.rs @@ -47,7 +47,7 @@ pub struct RelaxedR1csInstance { } impl RelaxedR1csInstance { - /// Initializes a new `RelaxedR1CSInstance` from an `R1CSInstance` + /// Initializes a new `RelaxedR1csInstance` from an `R1csInstance` pub fn from_r1cs_instance( ck: &PedersenCommitment, shape: &R1csShape, @@ -89,12 +89,7 @@ impl RelaxedR1csInstance { let commit_e = (e1 + commit_t * r).into(); let u = u1 + r; let commit_w = (w1 + w2 * r).into(); - // dbg!(commit_w); - dbg!(&x1); - dbg!(&x2); - dbg!(x2.clone() * r); let x = x1 + x2 * r; - dbg!(&x); Self { commit_w, @@ -122,34 +117,6 @@ impl RelaxedR1csInstance { z_0: &DenseVectors, z_i: &DenseVectors, ) -> C::Scalar { - // let commit_e = self.commit_e.to_extended(); - // let commit_w = self.commit_w.to_extended(); - // dbg!(vec![ - // vec![E::Scalar::from(i as u64)], - // z_0.get(), - // z_i.get(), - // vec![scalar_as_base::(self.u)], - // self.x.iter().map(|x| scalar_as_base::(x)).collect(), - // vec![ - // self.commit_e.get_x(), - // self.commit_e.get_y(), - // if self.commit_e.is_identity() { - // C::Base::zero() - // } else { - // C::Base::one() - // }, - // ], - // vec![ - // self.commit_w.get_x(), - // self.commit_w.get_y(), - // if self.commit_w.is_identity() { - // C::Base::zero() - // } else { - // C::Base::one() - // }, - // ], - // ] - // .concat()); MimcRO::::default().hash_vec( vec![ vec![E::Scalar::from(i as u64)], diff --git a/nova/src/relaxed_r1cs/witness.rs b/nova/src/relaxed_r1cs/witness.rs index e8682505..c680d0f4 100644 --- a/nova/src/relaxed_r1cs/witness.rs +++ b/nova/src/relaxed_r1cs/witness.rs @@ -17,7 +17,6 @@ impl R1csWitness { } } - /// Commits to the witness using the supplied generators pub fn commit(&self, ck: &PedersenCommitment) -> C::Affine { ck.commit(&self.w) } diff --git a/nova/src/test.rs b/nova/src/test.rs index 89e8bd9b..1c7dd1af 100644 --- a/nova/src/test.rs +++ b/nova/src/test.rs @@ -13,7 +13,6 @@ pub(crate) struct ExampleFunction { impl FunctionCircuit for ExampleFunction { fn invoke(z: &DenseVectors) -> DenseVectors { - // z.clone() let next_z = z[0] * z[0] * z[0] + z[0] + F::from(5); DenseVectors::new(vec![next_z]) } @@ -27,6 +26,5 @@ impl FunctionCircuit for ExampleFunction { let z_i_cube = FieldAssignment::mul(cs, &z_i_square, &z_i[0]); vec![&(&z_i_cube + &z_i[0]) + &five] - // z_i } } diff --git a/zkstd/src/circuit/gadget/curve.rs b/zkstd/src/circuit/gadget/curve.rs index 34a49876..d92f89e8 100644 --- a/zkstd/src/circuit/gadget/curve.rs +++ b/zkstd/src/circuit/gadget/curve.rs @@ -31,33 +31,18 @@ impl PointAssignment { Self { x, y, z } } - pub fn to_one_scale>(&self, cs: &mut R1cs) -> Self { - let z = self.z.value(cs).invert(); - if let Some(inv) = z { - let inv = FieldAssignment::constant(&inv); - Self { - x: FieldAssignment::mul(cs, &self.x, &inv), - y: FieldAssignment::mul(cs, &self.y, &inv), - z: FieldAssignment::constant(&F::one()), - } - } else { - self.clone() - } - - // let take_value = - // FieldAssignment::is_neq(cs, &self.z, &FieldAssignment::constant(&F::zero())); - // let z = FieldAssignment::constant(&self.z.value(cs).invert().unwrap_or(F::zero())); - // - // // let inv = FieldAssignment::constant(&z); - // let p = Self { - // x: FieldAssignment::mul(cs, &self.x, &z), - // y: FieldAssignment::mul(cs, &self.y, &z), - // z: FieldAssignment::constant(&F::one()), - // }; - // - // PointAssignment::conditional_select(cs, &p, &PointAssignment::identity(), &take_value) - - // p.select_identity(cs, &take_value) + pub fn descale>(&self, cs: &mut R1cs) -> Self { + let take_value = + FieldAssignment::is_neq(cs, &self.z, &FieldAssignment::constant(&F::zero())); + let inv = FieldAssignment::witness(cs, self.z.value(cs).invert().unwrap_or(F::zero())); + + let p = Self { + x: FieldAssignment::mul(cs, &self.x, &inv), + y: FieldAssignment::mul(cs, &self.y, &inv), + z: FieldAssignment::constant(&F::one()), + }; + + p.select_identity(cs, &take_value) } pub fn identity() -> Self { diff --git a/zkstd/src/lib.rs b/zkstd/src/lib.rs index de96cfac..44cf5099 100644 --- a/zkstd/src/lib.rs +++ b/zkstd/src/lib.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// #![no_std] +#![no_std] #![doc = include_str!("../README.md")] pub mod arithmetic; From ff1e4285a4542de57accc51384eb99205d703551 Mon Sep 17 00:00:00 2001 From: Kirill Karbushev Date: Mon, 18 Dec 2023 15:41:03 +0900 Subject: [PATCH 17/17] remove unnecessary comments and imports --- groth16/src/circuit.rs | 3 +-- nova/src/circuit/nifs.rs | 4 +++- nova/src/driver.rs | 5 +---- nova/src/gadget/big_nat.rs | 4 ++-- nova/src/gadget/instance.rs | 1 - zkstd/src/circuit.rs | 1 - zkstd/src/circuit/gadget/curve.rs | 2 +- zkstd/src/circuit/gadget/field.rs | 11 ----------- 8 files changed, 8 insertions(+), 23 deletions(-) diff --git a/groth16/src/circuit.rs b/groth16/src/circuit.rs index f4259e91..ed3b0556 100644 --- a/groth16/src/circuit.rs +++ b/groth16/src/circuit.rs @@ -17,8 +17,7 @@ pub struct Bn254Driver; impl CircuitDriver for Bn254Driver { const ORDER_STR: &'static str = "30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"; - const BASE_STR: &'static str = - "30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"; + const NUM_BITS: u16 = 254; type Affine = G1Affine; diff --git a/nova/src/circuit/nifs.rs b/nova/src/circuit/nifs.rs index 8c0e24a9..36140910 100644 --- a/nova/src/circuit/nifs.rs +++ b/nova/src/circuit/nifs.rs @@ -36,11 +36,13 @@ impl NifsCircuit { let r_bn = f_to_nat(&r.value(cs)); let m_bn = BigInt::from_str_radix(C::ORDER_STR, 16).unwrap(); + // TODO: Should be done without using BigInt // u_fold = U.u + r let u = f_to_nat(&u_range.u.value(cs)); let u_fold = FieldAssignment::witness(cs, nat_to_f(&(u.add(r_bn.clone()) % m_bn.clone()))); // FieldAssignment::enforce_eq_constant(cs, &(&(&u_fold - &u_range.u) - &r), &C::Base::zero()); + // TODO: BigNatAssignment should be use for module arithmetics // Fold U.x0 + r * x0 let x0_range_bn = f_to_nat(&u_range.x0.value(cs)); let x0_single_bn = f_to_nat(&u_single.x0.value(cs)); @@ -50,7 +52,7 @@ impl NifsCircuit { let x1_range_bn = f_to_nat(&u_range.x1.value(cs)); let x1_single_bn = f_to_nat(&u_single.x1.value(cs)); let r_x1 = x1_single_bn.mul(r_bn) % m_bn.clone(); - let x1_fold = (x1_range_bn + r_x1.clone()) % m_bn; + let x1_fold = (x1_range_bn + r_x1) % m_bn; RelaxedR1csInstanceAssignment { commit_w: w_fold, diff --git a/nova/src/driver.rs b/nova/src/driver.rs index d543ba28..4f220a4b 100644 --- a/nova/src/driver.rs +++ b/nova/src/driver.rs @@ -10,8 +10,7 @@ pub struct GrumpkinDriver; impl CircuitDriver for GrumpkinDriver { const ORDER_STR: &'static str = "30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"; - const BASE_STR: &'static str = - "30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"; + const NUM_BITS: u16 = 254; type Affine = Affine; @@ -30,8 +29,6 @@ pub struct Bn254Driver; impl CircuitDriver for Bn254Driver { const ORDER_STR: &'static str = "30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"; - const BASE_STR: &'static str = - "30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"; const NUM_BITS: u16 = 254; type Affine = G1Affine; diff --git a/nova/src/gadget/big_nat.rs b/nova/src/gadget/big_nat.rs index aa79689a..2bfc3641 100644 --- a/nova/src/gadget/big_nat.rs +++ b/nova/src/gadget/big_nat.rs @@ -35,8 +35,8 @@ impl BigNatAssignment { condition: &BinaryAssignment, ) -> BigNatAssignment { let mut limbs = vec![FieldAssignment::constant(&F::zero()); BN_N_LIMBS]; - for i in 0..BN_N_LIMBS { - limbs[i] = FieldAssignment::conditional_select(cs, &a.limbs[i], &b.limbs[i], condition); + for (i, limb) in limbs.iter_mut().enumerate().take(BN_N_LIMBS) { + *limb = FieldAssignment::conditional_select(cs, &a.limbs[i], &b.limbs[i], condition); } let max_word = if cs[*condition.inner()] == F::one() { diff --git a/nova/src/gadget/instance.rs b/nova/src/gadget/instance.rs index 5c1d5aed..a6272e45 100644 --- a/nova/src/gadget/instance.rs +++ b/nova/src/gadget/instance.rs @@ -1,6 +1,5 @@ use crate::relaxed_r1cs::R1csInstance; -use crate::circuit::MimcROCircuit; use crate::driver::scalar_as_base; use zkstd::circuit::prelude::{ diff --git a/zkstd/src/circuit.rs b/zkstd/src/circuit.rs index 00bffc1d..c6bbc794 100644 --- a/zkstd/src/circuit.rs +++ b/zkstd/src/circuit.rs @@ -6,7 +6,6 @@ use core::fmt::Debug; pub trait CircuitDriver: Clone + Debug + Default { const ORDER_STR: &'static str; - const BASE_STR: &'static str; const NUM_BITS: u16; // curve affine type Affine: BNAffine; diff --git a/zkstd/src/circuit/gadget/curve.rs b/zkstd/src/circuit/gadget/curve.rs index d92f89e8..708b56fa 100644 --- a/zkstd/src/circuit/gadget/curve.rs +++ b/zkstd/src/circuit/gadget/curve.rs @@ -34,7 +34,7 @@ impl PointAssignment { pub fn descale>(&self, cs: &mut R1cs) -> Self { let take_value = FieldAssignment::is_neq(cs, &self.z, &FieldAssignment::constant(&F::zero())); - let inv = FieldAssignment::witness(cs, self.z.value(cs).invert().unwrap_or(F::zero())); + let inv = FieldAssignment::witness(cs, self.z.value(cs).invert().unwrap_or_else(F::zero)); let p = Self { x: FieldAssignment::mul(cs, &self.x, &inv), diff --git a/zkstd/src/circuit/gadget/field.rs b/zkstd/src/circuit/gadget/field.rs index 64977dc8..033fd30c 100644 --- a/zkstd/src/circuit/gadget/field.rs +++ b/zkstd/src/circuit/gadget/field.rs @@ -181,12 +181,6 @@ impl FieldAssignment { b: &Self, condition: &BinaryAssignment, ) -> FieldAssignment { - // let c = if cs[*condition.inner()] == F::one() { - // a - // } else { - // b - // }; - let select_a = FieldAssignment::mul(cs, a, &FieldAssignment::from(condition)); let select_b = FieldAssignment::mul( cs, @@ -194,11 +188,6 @@ impl FieldAssignment { &(&FieldAssignment::constant(&F::one()) - &FieldAssignment::from(condition)), ); - // let left = FieldAssignment::mul(cs, &(a - b), &FieldAssignment::from(condition)); - // - // FieldAssignment::enforce_eq(cs, &left, &(c - b)); - // c.clone() - &select_a + &select_b }