From b07d06613bbd300a1a9b340de21f199ab91bfec1 Mon Sep 17 00:00:00 2001 From: Violet Vienhage <> Date: Tue, 28 Mar 2023 15:56:14 +0700 Subject: [PATCH 1/4] prover merge nodes --- src/parallel_prover.rs | 202 ++++++++++++++++++++++++++++++++++------- 1 file changed, 168 insertions(+), 34 deletions(-) diff --git a/src/parallel_prover.rs b/src/parallel_prover.rs index 10862593..789bd731 100644 --- a/src/parallel_prover.rs +++ b/src/parallel_prover.rs @@ -178,17 +178,17 @@ where C2: StepCircuit, { // The running instance of the primary - r_W_primary: RelaxedR1CSWitness, - r_U_primary: RelaxedR1CSInstance, + W_primary: RelaxedR1CSWitness, + U_primary: RelaxedR1CSInstance, // The new instance of the primary - l_w_primary: RelaxedR1CSWitness, - l_u_primary: RelaxedR1CSInstance, + w_primary: RelaxedR1CSWitness, + u_primary: RelaxedR1CSInstance, // The running instance of the secondary - r_W_secondary: RelaxedR1CSWitness, - r_U_secondary: RelaxedR1CSInstance, + W_secondary: RelaxedR1CSWitness, + U_secondary: RelaxedR1CSInstance, // The running instance of the secondary - l_w_secondary: RelaxedR1CSWitness, - l_u_secondary: RelaxedR1CSInstance, + w_secondary: RelaxedR1CSWitness, + u_secondary: RelaxedR1CSInstance, i_start: usize, i_end: usize, z_start_primary: Vec, @@ -283,26 +283,26 @@ where .map_err(|_e| NovaError::UnSat)?; // IVC proof for the primary circuit - let l_w_primary = + let w_primary = RelaxedR1CSWitness::from_r1cs_witness(&pp.r1cs_shape_primary, &w_primary); - let l_u_primary = RelaxedR1CSInstance::from_r1cs_instance( + let u_primary = RelaxedR1CSInstance::from_r1cs_instance( &pp.ck_primary, &pp.r1cs_shape_primary, &u_primary, ); - let r_W_primary = l_w_primary.clone(); - let r_U_primary = l_u_primary.clone(); + let W_primary = w_primary.clone(); + let U_primary = u_primary.clone(); // IVC proof of the secondary circuit - let l_w_secondary = + let w_secondary = RelaxedR1CSWitness::::from_r1cs_witness(&pp.r1cs_shape_secondary, &w_secondary); - let l_u_secondary = RelaxedR1CSInstance::::from_r1cs_instance( + let u_secondary = RelaxedR1CSInstance::::from_r1cs_instance( &pp.ck_secondary, &pp.r1cs_shape_secondary, &u_secondary, ); - let r_W_secondary = l_w_secondary.clone(); - let r_U_secondary = l_u_secondary.clone(); + let W_secondary = w_secondary.clone(); + let U_secondary = u_secondary.clone(); if z_start_primary.len() != pp.F_arity_primary || z_start_secondary.len() != pp.F_arity_secondary { @@ -313,14 +313,14 @@ where let i_end = i+1; Ok(Self { - r_W_primary, - r_U_primary, - l_w_primary, - l_u_primary, - r_W_secondary, - r_U_secondary, - l_w_secondary, - l_u_secondary, + W_primary, + U_primary, + w_primary, + u_primary, + W_secondary, + U_secondary, + w_secondary, + u_secondary, i_start, i_end, z_start_primary, @@ -348,24 +348,24 @@ where return Err(NovaError::InvalidNodeMerge); } - // First we fold the secondary instances of both the left and right children + // First we fold the secondary instances of both the left and right children in the secondary curve let (nifs_left_secondary, (left_U_secondary, left_W_secondary)) = NIFS::prove( &pp.ck_secondary, &pp.ro_consts_secondary, &pp.r1cs_shape_secondary, - &self.r_U_secondary, - &self.r_W_secondary, - &self.l_u_secondary, - &self.l_w_secondary, + &self.U_secondary, + &self.W_secondary, + &self.u_secondary, + &self.w_secondary, )?; let (nifs_right_secondary, (right_U_secondary, right_W_secondary)) = NIFS::prove( &pp.ck_secondary, &pp.ro_consts_secondary, &pp.r1cs_shape_secondary, - &right.r_U_secondary, - &right.r_W_secondary, - &right.l_u_secondary, - &right.l_w_secondary, + &right.U_secondary, + &right.W_secondary, + &right.u_secondary, + &right.w_secondary, )?; let (nifs_secondary, (U_secondary, W_secondary)) = NIFS::prove( &pp.ck_secondary, @@ -377,7 +377,141 @@ where &right_W_secondary, )?; + // Next we construct a proof of this folding and of the invocation of F - return Err(NovaError::InvalidStepOutputLength); + let mut cs_primary: SatisfyingAssignment = SatisfyingAssignment::new(); + + let inputs_primary: NovaAugmentedParallelCircuitInputs = NovaAugmentedParallelCircuitInputs::new( + pp.r1cs_shape_secondary.get_digest(), + G1::Scalar::from(self.i_start as u64), + G1::Scalar::from(self.i_end as u64), + G1::Scalar::from(right.i_start as u64), + G1::Scalar::from(right.i_end as u64), + self.z_start_primary.clone(), + self.z_end_primary, + right.z_start_primary, + right.z_end_primary.clone(), + Some(self.U_secondary), + Some(self.u_secondary), + Some(right.U_secondary), + Some(right.u_secondary), + Some(Commitment::::decompress(&nifs_left_secondary.comm_T)?), + Some(Commitment::::decompress(&nifs_right_secondary.comm_T)?), + Some(Commitment::::decompress(&nifs_secondary.comm_T)?) + ); + + let circuit_primary: NovaAugmentedParallelCircuit = NovaAugmentedParallelCircuit::new( + pp.augmented_circuit_params_primary.clone(), + Some(inputs_primary), + c_primary.clone(), + pp.ro_consts_circuit_primary.clone(), + ); + let _ = circuit_primary.synthesize(&mut cs_primary); + + let (u_primary, w_primary) = cs_primary + .r1cs_instance_and_witness(&pp.r1cs_shape_primary, &pp.ck_primary) + .map_err(|_e| NovaError::UnSat)?; + + let u_primary = RelaxedR1CSInstance::from_r1cs_instance(&pp.ck_primary, &pp.r1cs_shape_primary, &u_primary); + let w_primary = RelaxedR1CSWitness::from_r1cs_witness(&pp.r1cs_shape_primary, &w_primary); + + // Now we fold the instances of the primary proof + let (nifs_left_primary, (left_U_primary, left_W_primary)) = NIFS::prove( + &pp.ck_primary, + &pp.ro_consts_primary, + &pp.r1cs_shape_primary, + &self.U_primary, + &self.W_primary, + &self.u_primary, + &self.w_primary, + )?; + let (nifs_right_primary, (right_U_primary, right_W_primary)) = NIFS::prove( + &pp.ck_primary, + &pp.ro_consts_primary, + &pp.r1cs_shape_primary, + &right.U_primary, + &right.W_primary, + &right.u_primary, + &right.w_primary, + )?; + let (nifs_primary, (U_primary, W_primary)) = NIFS::prove( + &pp.ck_primary, + &pp.ro_consts_primary, + &pp.r1cs_shape_primary, + &left_U_primary, + &left_W_primary, + &right_U_primary, + &right_W_primary, + )?; + + // Next we construct a proof of this folding in the secondary curve + let mut cs_secondary: SatisfyingAssignment = SatisfyingAssignment::new(); + + let inputs_secondary: NovaAugmentedParallelCircuitInputs = NovaAugmentedParallelCircuitInputs::::new( + pp.r1cs_shape_primary.get_digest(), + G2::Scalar::from(self.i_start as u64), + G2::Scalar::from(self.i_end as u64), + G2::Scalar::from(right.i_start as u64), + G2::Scalar::from(right.i_end as u64), + self.z_start_secondary.clone(), + self.z_end_secondary, + right.z_start_secondary, + right.z_end_secondary.clone(), + Some(self.U_primary), + Some(self.u_primary), + Some(right.U_primary), + Some(right.u_primary), + Some(Commitment::::decompress(&nifs_left_primary.comm_T)?), + Some(Commitment::::decompress(&nifs_right_primary.comm_T)?), + Some(Commitment::::decompress(&nifs_primary.comm_T)?) + ); + + let circuit_secondary: NovaAugmentedParallelCircuit = NovaAugmentedParallelCircuit::new( + pp.augmented_circuit_params_primary.clone(), + Some(inputs_secondary), + c_secondary.clone(), + pp.ro_consts_circuit_secondary.clone(), + ); + let _ = circuit_secondary.synthesize(&mut cs_secondary); + + let (u_secondary, w_secondary) = cs_secondary + .r1cs_instance_and_witness(&pp.r1cs_shape_secondary, &pp.ck_secondary) + .map_err(|_e| NovaError::UnSat)?; + + // Give these a trivial error vector + let u_secondary = RelaxedR1CSInstance::from_r1cs_instance(&pp.ck_secondary, &pp.r1cs_shape_secondary, &u_secondary); + let w_secondary = RelaxedR1CSWitness::from_r1cs_witness(&pp.r1cs_shape_secondary, &w_secondary); + + // Name each of these to match struct fields + let i_start = self.i_start.clone(); + let i_end = right.i_end.clone(); + let z_start_primary = self.z_start_primary; + let z_end_primary = right.z_end_primary; + let z_start_secondary = self.z_start_secondary; + let z_end_secondary = right.z_end_secondary; + + Ok(Self { + // Primary running instance + W_primary, + U_primary, + // Primary new instance + w_primary, + u_primary, + // The running instance of the secondary + W_secondary, + U_secondary, + // The running instance of the secondary + w_secondary, + u_secondary, + // The range data + i_start, + i_end, + z_start_primary, + z_end_primary, + z_start_secondary, + z_end_secondary, + _p_c1: Default::default(), + _p_c2: Default::default() + }) } } \ No newline at end of file From 3da930e0d9a0ffab3353e17b96f3d19192bc0798 Mon Sep 17 00:00:00 2001 From: Violet Vienhage <> Date: Wed, 29 Mar 2023 10:45:20 +0700 Subject: [PATCH 2/4] usize -> u64 --- src/parallel_prover.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/parallel_prover.rs b/src/parallel_prover.rs index 789bd731..07a7de03 100644 --- a/src/parallel_prover.rs +++ b/src/parallel_prover.rs @@ -189,8 +189,8 @@ where // The running instance of the secondary w_secondary: RelaxedR1CSWitness, u_secondary: RelaxedR1CSInstance, - i_start: usize, - i_end: usize, + i_start: u64, + i_end: u64, z_start_primary: Vec, z_end_primary: Vec, z_start_secondary: Vec, @@ -212,7 +212,7 @@ where pp: &PublicParams, c_primary: C1, c_secondary: C2, - i: usize, + i: u64, z_start_primary: Vec, z_end_primary: Vec, z_start_secondary: Vec, @@ -255,10 +255,10 @@ where let inputs_secondary: NovaAugmentedParallelCircuitInputs = NovaAugmentedParallelCircuitInputs::new( pp.r1cs_shape_primary.get_digest(), - G2::Scalar::from(i.try_into().unwrap()), - G2::Scalar::from((i+1).try_into().unwrap()), - G2::Scalar::from((i).try_into().unwrap()), - G2::Scalar::from((i+1).try_into().unwrap()), + G2::Scalar::from(i), + G2::Scalar::from(i+1), + G2::Scalar::from(i), + G2::Scalar::from(i+1), z_start_secondary.clone(), z_start_secondary.clone(), z_end_secondary.clone(), From 84073e03fc8b71343ec1df8e23f6bfe21a25405f Mon Sep 17 00:00:00 2001 From: Violet Vienhage <> Date: Wed, 29 Mar 2023 10:52:42 +0700 Subject: [PATCH 3/4] fmt --- src/circuit.rs | 4 +- src/errors.rs | 6 +- src/gadgets/r1cs.rs | 192 +++---- src/lib.rs | 46 +- src/nifs.rs | 2 +- src/parallel_circuit.rs | 1123 ++++++++++++++++++++------------------- src/parallel_prover.rs | 433 +++++++-------- src/r1cs.rs | 5 +- 8 files changed, 942 insertions(+), 869 deletions(-) diff --git a/src/circuit.rs b/src/circuit.rs index e9cee5df..18c2f179 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -15,7 +15,7 @@ use crate::{ alloc_num_equals, alloc_scalar_as_base, alloc_zero, conditionally_select_vec, le_bits_to_num, }, }, - r1cs::{RelaxedR1CSInstance}, + r1cs::RelaxedR1CSInstance, traits::{ circuit::StepCircuit, commitment::CommitmentTrait, Group, ROCircuitTrait, ROConstantsCircuit, }, @@ -377,7 +377,7 @@ mod tests { use crate::constants::{BN_LIMB_WIDTH, BN_N_LIMBS}; use crate::r1cs::RelaxedR1CSWitness; -use crate::{ + use crate::{ bellperson::r1cs::{NovaShape, NovaWitness}, provider::poseidon::PoseidonConstantsCircuit, traits::{circuit::TrivialTestCircuit, ROConstantsTrait}, diff --git a/src/errors.rs b/src/errors.rs index 4b18f1c6..a446e8a8 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -50,7 +50,7 @@ pub enum NovaError { /// returned when the product proof check fails #[error("InvalidProductProof")] InvalidProductProof, - /// returned when the tree node is attempting to merge with a node which has a greater than 1 gap in steps - #[error("InvalidNodeMerge")] - InvalidNodeMerge, + /// returned when the tree node is attempting to merge with a node which has a greater than 1 gap in steps + #[error("InvalidNodeMerge")] + InvalidNodeMerge, } diff --git a/src/gadgets/r1cs.rs b/src/gadgets/r1cs.rs index b8d0e60c..fc2df928 100644 --- a/src/gadgets/r1cs.rs +++ b/src/gadgets/r1cs.rs @@ -12,7 +12,7 @@ use crate::{ conditionally_select_bignat, le_bits_to_num, }, }, - r1cs::{RelaxedR1CSInstance}, + r1cs::RelaxedR1CSInstance, traits::{commitment::CommitmentTrait, Group, ROCircuitTrait, ROConstantsCircuit}, }; use bellperson::{ @@ -338,100 +338,102 @@ impl AllocatedRelaxedR1CSInstance { }) } - /// Folds self with a relaxed r1cs instance and returns the result - #[allow(clippy::too_many_arguments)] - #[allow(unused)] - pub fn fold_with_relaxed_r1cs::Base>>( - &self, - mut cs: CS, - params: AllocatedNum, // hash of R1CSShape of F' - u: AllocatedRelaxedR1CSInstance, - T: AllocatedPoint, - ro_consts: ROConstantsCircuit, - limb_width: usize, - n_limbs: usize, - ) -> Result, SynthesisError> { - // Compute r: - let mut ro = G::ROCircuit::new(ro_consts, NUM_FE_FOR_RO); - ro.absorb(params); - self.absorb_in_ro(cs.namespace(|| "absorb running instance"), &mut ro)?; - u.absorb_in_ro(cs.namespace(|| "absorb running instance u"), &mut ro)?; - ro.absorb(T.x.clone()); - ro.absorb(T.y.clone()); - ro.absorb(T.is_infinity.clone()); - let r_bits = ro.squeeze(cs.namespace(|| "r bits"), NUM_CHALLENGE_BITS)?; - let r = le_bits_to_num(cs.namespace(|| "r"), r_bits.clone())?; - - // W_fold = self.W + r * u.W - let rW = u.W.scalar_mul(cs.namespace(|| "r * u.W"), r_bits.clone())?; - let W_fold = self.W.add(cs.namespace(|| "self.W + r * u.W"), &rW)?; - - // E_fold = self.E + r * T + r * r * U.E - let rT = T.scalar_mul(cs.namespace(|| "r * T"), r_bits.clone())?; - let r_e_2 = u.E.scalar_mul(cs.namespace(|| "r * E_2"), r_bits.clone())?; - // Todo - there has to be a better way than 2 scalar mul - let r_squared_e_2 = r_e_2.scalar_mul(cs.namespace(|| "r * r * E_2"), r_bits)?; - let rT_plus_r_squared_E_2 = rT.add(cs.namespace(|| "r * r * E_2"), &r_squared_e_2)?; - let E_fold = self.E.add(cs.namespace(|| "self.E + r * T"), &rT_plus_r_squared_E_2)?; - - // u_fold = u_r + r - let u_fold = AllocatedNum::alloc(cs.namespace(|| "u_fold"), || { - Ok(*self.u.get_value().get()? + r.get_value().get()?) - })?; - cs.enforce( - || "Check u_fold", - |lc| lc, - |lc| lc, - |lc| lc + u_fold.get_variable() - self.u.get_variable() - r.get_variable(), - ); - - // Fold the IO: - // Analyze r into limbs - let r_bn = BigNat::from_num( - cs.namespace(|| "allocate r_bn"), - Num::from(r.clone()), - limb_width, - n_limbs, - )?; - - // Allocate the order of the non-native field as a constant - let m_bn = alloc_bignat_constant( - cs.namespace(|| "alloc m"), - &G::get_curve_params().2, - limb_width, - n_limbs, - )?; - - // Analyze X0 to bignat, NOTE - we copied this code from above but here changed it because the u.X0 is already BigNat - // for u of the type relaxed R1CS - let X0_bn = u.X0.clone(); - - // Fold self.X[0] + r * X[0] - let (_, r_0) = X0_bn.mult_mod(cs.namespace(|| "r*X[0]"), &r_bn, &m_bn)?; - // add X_r[0] - let r_new_0 = self.X0.add::(&r_0)?; - // Now reduce - let X0_fold = r_new_0.red_mod(cs.namespace(|| "reduce folded X[0]"), &m_bn)?; - - // Analyze X1 to bignat, NOTE - we copied this code from above but here changed it because the u.X0 is already BigNat - // for u of the type relaxed R1CS - let X1_bn = u.X1.clone(); - - // Fold self.X[1] + r * X[1] - let (_, r_1) = X1_bn.mult_mod(cs.namespace(|| "r*X[1]"), &r_bn, &m_bn)?; - // add X_r[1] - let r_new_1 = self.X1.add::(&r_1)?; - // Now reduce - let X1_fold = r_new_1.red_mod(cs.namespace(|| "reduce folded X[1]"), &m_bn)?; - - Ok(Self { - W: W_fold, - E: E_fold, - u: u_fold, - X0: X0_fold, - X1: X1_fold, - }) - } + /// Folds self with a relaxed r1cs instance and returns the result + #[allow(clippy::too_many_arguments)] + #[allow(unused)] + pub fn fold_with_relaxed_r1cs::Base>>( + &self, + mut cs: CS, + params: AllocatedNum, // hash of R1CSShape of F' + u: AllocatedRelaxedR1CSInstance, + T: AllocatedPoint, + ro_consts: ROConstantsCircuit, + limb_width: usize, + n_limbs: usize, + ) -> Result, SynthesisError> { + // Compute r: + let mut ro = G::ROCircuit::new(ro_consts, NUM_FE_FOR_RO); + ro.absorb(params); + self.absorb_in_ro(cs.namespace(|| "absorb running instance"), &mut ro)?; + u.absorb_in_ro(cs.namespace(|| "absorb running instance u"), &mut ro)?; + ro.absorb(T.x.clone()); + ro.absorb(T.y.clone()); + ro.absorb(T.is_infinity.clone()); + let r_bits = ro.squeeze(cs.namespace(|| "r bits"), NUM_CHALLENGE_BITS)?; + let r = le_bits_to_num(cs.namespace(|| "r"), r_bits.clone())?; + + // W_fold = self.W + r * u.W + let rW = u.W.scalar_mul(cs.namespace(|| "r * u.W"), r_bits.clone())?; + let W_fold = self.W.add(cs.namespace(|| "self.W + r * u.W"), &rW)?; + + // E_fold = self.E + r * T + r * r * U.E + let rT = T.scalar_mul(cs.namespace(|| "r * T"), r_bits.clone())?; + let r_e_2 = u.E.scalar_mul(cs.namespace(|| "r * E_2"), r_bits.clone())?; + // Todo - there has to be a better way than 2 scalar mul + let r_squared_e_2 = r_e_2.scalar_mul(cs.namespace(|| "r * r * E_2"), r_bits)?; + let rT_plus_r_squared_E_2 = rT.add(cs.namespace(|| "r * r * E_2"), &r_squared_e_2)?; + let E_fold = self + .E + .add(cs.namespace(|| "self.E + r * T"), &rT_plus_r_squared_E_2)?; + + // u_fold = u_r + r + let u_fold = AllocatedNum::alloc(cs.namespace(|| "u_fold"), || { + Ok(*self.u.get_value().get()? + r.get_value().get()?) + })?; + cs.enforce( + || "Check u_fold", + |lc| lc, + |lc| lc, + |lc| lc + u_fold.get_variable() - self.u.get_variable() - r.get_variable(), + ); + + // Fold the IO: + // Analyze r into limbs + let r_bn = BigNat::from_num( + cs.namespace(|| "allocate r_bn"), + Num::from(r.clone()), + limb_width, + n_limbs, + )?; + + // Allocate the order of the non-native field as a constant + let m_bn = alloc_bignat_constant( + cs.namespace(|| "alloc m"), + &G::get_curve_params().2, + limb_width, + n_limbs, + )?; + + // Analyze X0 to bignat, NOTE - we copied this code from above but here changed it because the u.X0 is already BigNat + // for u of the type relaxed R1CS + let X0_bn = u.X0.clone(); + + // Fold self.X[0] + r * X[0] + let (_, r_0) = X0_bn.mult_mod(cs.namespace(|| "r*X[0]"), &r_bn, &m_bn)?; + // add X_r[0] + let r_new_0 = self.X0.add::(&r_0)?; + // Now reduce + let X0_fold = r_new_0.red_mod(cs.namespace(|| "reduce folded X[0]"), &m_bn)?; + + // Analyze X1 to bignat, NOTE - we copied this code from above but here changed it because the u.X0 is already BigNat + // for u of the type relaxed R1CS + let X1_bn = u.X1.clone(); + + // Fold self.X[1] + r * X[1] + let (_, r_1) = X1_bn.mult_mod(cs.namespace(|| "r*X[1]"), &r_bn, &m_bn)?; + // add X_r[1] + let r_new_1 = self.X1.add::(&r_1)?; + // Now reduce + let X1_fold = r_new_1.red_mod(cs.namespace(|| "reduce folded X[1]"), &m_bn)?; + + Ok(Self { + W: W_fold, + E: E_fold, + u: u_fold, + X0: X0_fold, + X1: X1_fold, + }) + } /// If the condition is true then returns this otherwise it returns the other pub fn conditionally_select::Base>>( diff --git a/src/lib.rs b/src/lib.rs index 930b973e..0e6df593 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,16 +16,16 @@ mod bellperson; mod circuit; mod constants; mod nifs; -mod r1cs; mod parallel_circuit; +mod r1cs; // public modules pub mod errors; pub mod gadgets; +pub mod parallel_prover; pub mod provider; pub mod spartan; pub mod traits; -pub mod parallel_prover; use crate::bellperson::{ r1cs::{NovaShape, NovaWitness}, @@ -227,9 +227,13 @@ where .r1cs_instance_and_witness(&pp.r1cs_shape_primary, &pp.ck_primary) .map_err(|_e| NovaError::UnSat)?; - let u_primary = RelaxedR1CSInstance::from_r1cs_instance(&pp.ck_primary, &pp.r1cs_shape_primary, &u_primary); + let u_primary = RelaxedR1CSInstance::from_r1cs_instance( + &pp.ck_primary, + &pp.r1cs_shape_primary, + &u_primary, + ); let w_primary = RelaxedR1CSWitness::from_r1cs_witness(&pp.r1cs_shape_primary, &w_primary); - + // base case for the secondary let mut cs_secondary: SatisfyingAssignment = SatisfyingAssignment::new(); let inputs_secondary: NovaAugmentedCircuitInputs = NovaAugmentedCircuitInputs::new( @@ -259,8 +263,13 @@ where let r_U_primary = u_primary; // IVC proof of the secondary circuit - let l_w_secondary = RelaxedR1CSWitness::::from_r1cs_witness(&pp.r1cs_shape_secondary, &w_secondary); - let l_u_secondary = RelaxedR1CSInstance::::from_r1cs_instance( &pp.ck_secondary,&pp.r1cs_shape_secondary,&u_secondary); + let l_w_secondary = + RelaxedR1CSWitness::::from_r1cs_witness(&pp.r1cs_shape_secondary, &w_secondary); + let l_u_secondary = RelaxedR1CSInstance::::from_r1cs_instance( + &pp.ck_secondary, + &pp.r1cs_shape_secondary, + &u_secondary, + ); let r_W_secondary = RelaxedR1CSWitness::::default(&pp.r1cs_shape_secondary); let r_U_secondary = RelaxedR1CSInstance::::default(&pp.ck_secondary, &pp.r1cs_shape_secondary); @@ -324,8 +333,13 @@ where .r1cs_instance_and_witness(&pp.r1cs_shape_primary, &pp.ck_primary) .map_err(|_e| NovaError::UnSat)?; - let l_u_primary = RelaxedR1CSInstance::from_r1cs_instance(&pp.ck_primary, &pp.r1cs_shape_primary, &l_u_primary); - let l_w_primary = RelaxedR1CSWitness::from_r1cs_witness(&pp.r1cs_shape_primary, &l_w_primary); + let l_u_primary = RelaxedR1CSInstance::from_r1cs_instance( + &pp.ck_primary, + &pp.r1cs_shape_primary, + &l_u_primary, + ); + let l_w_primary = + RelaxedR1CSWitness::from_r1cs_witness(&pp.r1cs_shape_primary, &l_w_primary); // fold the primary circuit's instance let (nifs_primary, (r_U_primary, r_W_primary)) = NIFS::prove( @@ -361,8 +375,13 @@ where .r1cs_instance_and_witness(&pp.r1cs_shape_secondary, &pp.ck_secondary) .map_err(|_e| NovaError::UnSat)?; - let l_w_secondary = RelaxedR1CSWitness::::from_r1cs_witness(&pp.r1cs_shape_secondary, &l_w_secondary); - let l_u_secondary = RelaxedR1CSInstance::::from_r1cs_instance( &pp.ck_secondary,&pp.r1cs_shape_secondary,&l_u_secondary); + let l_w_secondary = + RelaxedR1CSWitness::::from_r1cs_witness(&pp.r1cs_shape_secondary, &l_w_secondary); + let l_u_secondary = RelaxedR1CSInstance::::from_r1cs_instance( + &pp.ck_secondary, + &pp.r1cs_shape_secondary, + &l_u_secondary, + ); // update the running instances and witnesses let zi_primary = c_primary.output(&r_snark.zi_primary); @@ -468,8 +487,11 @@ where ) }, || { - pp.r1cs_shape_primary - .is_sat_relaxed(&pp.ck_primary, &self.l_u_primary, &self.l_w_primary) + pp.r1cs_shape_primary.is_sat_relaxed( + &pp.ck_primary, + &self.l_u_primary, + &self.l_w_primary, + ) }, ) }, diff --git a/src/nifs.rs b/src/nifs.rs index bb914fb0..908e36b1 100644 --- a/src/nifs.rs +++ b/src/nifs.rs @@ -117,7 +117,7 @@ impl NIFS { mod tests { use super::*; use crate::{ - r1cs::{R1CS, R1CSInstance, R1CSWitness}, + r1cs::{R1CSInstance, R1CSWitness, R1CS}, traits::{Group, ROConstantsTrait}, }; use ::bellperson::{gadgets::num::AllocatedNum, ConstraintSystem, SynthesisError}; diff --git a/src/parallel_circuit.rs b/src/parallel_circuit.rs index c6fe8b98..52f08236 100644 --- a/src/parallel_circuit.rs +++ b/src/parallel_circuit.rs @@ -7,35 +7,57 @@ //! the other into the running instance use crate::{ - constants::{NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_BITS}, - gadgets::{ - ecc::AllocatedPoint, - r1cs::{AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance}, - utils::{ - alloc_num_equals, alloc_scalar_as_base, conditionally_select_vec, le_bits_to_num, - }, - }, - r1cs::{RelaxedR1CSInstance}, - traits::{ - circuit::StepCircuit, commitment::CommitmentTrait, Group, ROCircuitTrait, ROConstantsCircuit, - }, - Commitment, - circuit::{NovaAugmentedCircuitParams} - }; - use bellperson::{ - gadgets::{ - boolean::{AllocatedBit, Boolean}, - num::AllocatedNum, - Assignment, - }, - Circuit, ConstraintSystem, SynthesisError, - }; - use ff::Field; - use serde::{Deserialize, Serialize}; - - #[derive(Debug, Serialize, Deserialize)] - #[serde(bound = "")] - pub struct NovaAugmentedParallelCircuitInputs { + circuit::NovaAugmentedCircuitParams, + constants::{NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_BITS}, + gadgets::{ + ecc::AllocatedPoint, + r1cs::{AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance}, + utils::{alloc_num_equals, alloc_scalar_as_base, conditionally_select_vec, le_bits_to_num}, + }, + r1cs::RelaxedR1CSInstance, + traits::{ + circuit::StepCircuit, commitment::CommitmentTrait, Group, ROCircuitTrait, ROConstantsCircuit, + }, + Commitment, +}; +use bellperson::{ + gadgets::{ + boolean::{AllocatedBit, Boolean}, + num::AllocatedNum, + Assignment, + }, + Circuit, ConstraintSystem, SynthesisError, +}; +use ff::Field; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +#[serde(bound = "")] +pub struct NovaAugmentedParallelCircuitInputs { + params: G::Scalar, // Hash(Shape of u2, Gens for u2). Needed for computing the challenge. + i_start_U: G::Base, + i_end_U: G::Base, + i_start_R: G::Base, + i_end_R: G::Base, + z_U_start: Vec, + z_U_end: Vec, + z_R_start: Vec, + z_R_end: Vec, + U: Option>, + u: Option>, + R: Option>, + r: Option>, + T_u: Option>, + T_r: Option>, + T_R_U: Option>, +} + +impl NovaAugmentedParallelCircuitInputs { + /// Create new inputs/witness for the verification circuit + #[allow(clippy::too_many_arguments)] + // Remove when we write the struct implementing the parallel nova instance + #[allow(unused)] + pub fn new( params: G::Scalar, // Hash(Shape of u2, Gens for u2). Needed for computing the challenge. i_start_U: G::Base, i_end_U: G::Base, @@ -51,538 +73,563 @@ use crate::{ r: Option>, T_u: Option>, T_r: Option>, - T_R_U: Option> - } - - impl NovaAugmentedParallelCircuitInputs { - /// Create new inputs/witness for the verification circuit - #[allow(clippy::too_many_arguments)] - // Remove when we write the struct implementing the parallel nova instance - #[allow(unused)] - pub fn new( - params: G::Scalar, // Hash(Shape of u2, Gens for u2). Needed for computing the challenge. - i_start_U: G::Base, - i_end_U: G::Base, - i_start_R: G::Base, - i_end_R: G::Base, - z_U_start: Vec, - z_U_end: Vec, - z_R_start: Vec, - z_R_end: Vec, - U: Option>, - u: Option>, - R: Option>, - r: Option>, - T_u: Option>, - T_r: Option>, - T_R_U: Option> - ) -> Self { - Self { - params, - i_start_U, - i_end_U, - i_start_R, - i_end_R, - z_U_start, - z_U_end, - z_R_start, - z_R_end, - U, - u, - R, - r, - T_u, - T_r, - T_R_U - } + T_R_U: Option>, + ) -> Self { + Self { + params, + i_start_U, + i_end_U, + i_start_R, + i_end_R, + z_U_start, + z_U_end, + z_R_start, + z_R_end, + U, + u, + R, + r, + T_u, + T_r, + T_R_U, } } - - /// The augmented circuit F' in Nova that includes a step circuit F - /// and the circuit for the verifier in Nova's non-interactive folding scheme - pub struct NovaAugmentedParallelCircuit> { +} + +/// The augmented circuit F' in Nova that includes a step circuit F +/// and the circuit for the verifier in Nova's non-interactive folding scheme +pub struct NovaAugmentedParallelCircuit> { + params: NovaAugmentedCircuitParams, + ro_consts: ROConstantsCircuit, + inputs: Option>, + step_circuit: SC, // The function that is applied for each step +} + +impl> NovaAugmentedParallelCircuit { + /// Create a new verification circuit for the input relaxed r1cs instances + // Remove when we write the struct implementing the parallel nova instance + #[allow(unused)] + pub fn new( params: NovaAugmentedCircuitParams, - ro_consts: ROConstantsCircuit, inputs: Option>, - step_circuit: SC, // The function that is applied for each step - } - - impl> NovaAugmentedParallelCircuit { - /// Create a new verification circuit for the input relaxed r1cs instances - // Remove when we write the struct implementing the parallel nova instance - #[allow(unused)] - pub fn new( - params: NovaAugmentedCircuitParams, - inputs: Option>, - step_circuit: SC, - ro_consts: ROConstantsCircuit, - ) -> Self { - Self { - params, - inputs, - step_circuit, - ro_consts, - } + step_circuit: SC, + ro_consts: ROConstantsCircuit, + ) -> Self { + Self { + params, + inputs, + step_circuit, + ro_consts, } - - /// Allocate all witnesses and return - fn alloc_witness::Base>>( - &self, - mut cs: CS, - arity: usize, - ) -> Result< - ( - AllocatedNum, - AllocatedNum, - AllocatedNum, - AllocatedNum, - AllocatedNum, - Vec>, - Vec>, - Vec>, - Vec>, - AllocatedRelaxedR1CSInstance, - AllocatedR1CSInstance, - AllocatedRelaxedR1CSInstance, - AllocatedR1CSInstance, - AllocatedPoint, - AllocatedPoint, - AllocatedPoint, - usize - ), - SynthesisError, - > { - // Allocate the params - let params = alloc_scalar_as_base::( - cs.namespace(|| "params"), - self.inputs.get().map_or(None, |inputs| Some(inputs.params)), - )?; - - // Allocate idexes - let i_start_U = AllocatedNum::alloc(cs.namespace(|| "i_start_U"), || Ok(self.inputs.get()?.i_start_U))?; - let i_end_U = AllocatedNum::alloc(cs.namespace(|| "i_end_U"), || Ok(self.inputs.get()?.i_end_U))?; - let i_start_R = AllocatedNum::alloc(cs.namespace(|| "i_start_R"), || Ok(self.inputs.get()?.i_start_R))?; - let i_end_R = AllocatedNum::alloc(cs.namespace(|| "i_end_R"), || Ok(self.inputs.get()?.i_end_R))?; - - // Allocate input and output vectors - let z_U_start = (0..arity) - .map(|i| { - AllocatedNum::alloc(cs.namespace(|| format!(" - {i}")), || { - Ok(self.inputs.get()?.z_U_start[i]) - }) - }) - .collect::>, _>>()?; - let z_U_end = (0..arity) - .map(|i| { - AllocatedNum::alloc(cs.namespace(|| format!(" - {i}")), || { - Ok(self.inputs.get()?.z_U_end[i]) - }) - }) - .collect::>, _>>()?; - // Allocate z_R_start - let z_R_start = (0..arity) - .map(|i| { - AllocatedNum::alloc(cs.namespace(|| format!(" - {i}")), || { - Ok(self.inputs.get()?.z_R_start[i]) - }) - }) - .collect::>, _>>()?; - // Allocate z_R_end - let z_R_end = (0..arity) - .map(|i| { - AllocatedNum::alloc(cs.namespace(|| format!(" - {i}")), || { - Ok(self.inputs.get()?.z_R_end[i]) - }) - }) - .collect::>, _>>()?; - - - // Allocate the running instance U - let U: AllocatedRelaxedR1CSInstance = AllocatedRelaxedR1CSInstance::alloc( - cs.namespace(|| "Allocate U"), - self.inputs.get().map_or(None, |inputs| { - inputs.U.get().map_or(None, |U| Some(U.clone())) - }), - self.params.limb_width, - self.params.n_limbs, - )?; - - // Allocate the instance u to be folded in - let u = AllocatedR1CSInstance::alloc( - cs.namespace(|| "allocate instance u to fold"), - self.inputs.get().map_or(None, |inputs| { - inputs.u.get().map_or(None, |u| Some(u.clone())) - }), - )?; + } - // Allocate the running instance U - let R: AllocatedRelaxedR1CSInstance = AllocatedRelaxedR1CSInstance::alloc( - cs.namespace(|| "Allocate R"), - self.inputs.get().map_or(None, |inputs| { - inputs.R.get().map_or(None, |U| Some(U.clone())) - }), - self.params.limb_width, - self.params.n_limbs, - )?; - - // Allocate the instance r to be folded in - let r = AllocatedR1CSInstance::alloc( - cs.namespace(|| "allocate instance r to fold"), - self.inputs.get().map_or(None, |inputs| { - inputs.u.get().map_or(None, |u| Some(u.clone())) - }), - )?; - - // Allocate T - let T_u = AllocatedPoint::alloc( - cs.namespace(|| "allocate T_u"), - self.inputs.get().map_or(None, |inputs| { - inputs.T_r.get().map_or(None, |T_r| Some(T_r.to_coordinates())) - }), - )?; + /// Allocate all witnesses and return + fn alloc_witness::Base>>( + &self, + mut cs: CS, + arity: usize, + ) -> Result< + ( + AllocatedNum, + AllocatedNum, + AllocatedNum, + AllocatedNum, + AllocatedNum, + Vec>, + Vec>, + Vec>, + Vec>, + AllocatedRelaxedR1CSInstance, + AllocatedR1CSInstance, + AllocatedRelaxedR1CSInstance, + AllocatedR1CSInstance, + AllocatedPoint, + AllocatedPoint, + AllocatedPoint, + usize, + ), + SynthesisError, + > { + // Allocate the params + let params = alloc_scalar_as_base::( + cs.namespace(|| "params"), + self.inputs.get().map_or(None, |inputs| Some(inputs.params)), + )?; + + // Allocate idexes + let i_start_U = AllocatedNum::alloc(cs.namespace(|| "i_start_U"), || { + Ok(self.inputs.get()?.i_start_U) + })?; + let i_end_U = AllocatedNum::alloc(cs.namespace(|| "i_end_U"), || { + Ok(self.inputs.get()?.i_end_U) + })?; + let i_start_R = AllocatedNum::alloc(cs.namespace(|| "i_start_R"), || { + Ok(self.inputs.get()?.i_start_R) + })?; + let i_end_R = AllocatedNum::alloc(cs.namespace(|| "i_end_R"), || { + Ok(self.inputs.get()?.i_end_R) + })?; + + // Allocate input and output vectors + let z_U_start = (0..arity) + .map(|i| { + AllocatedNum::alloc( + cs.namespace(|| { + format!( + " + {i}" + ) + }), + || Ok(self.inputs.get()?.z_U_start[i]), + ) + }) + .collect::>, _>>()?; + let z_U_end = (0..arity) + .map(|i| { + AllocatedNum::alloc( + cs.namespace(|| { + format!( + " + {i}" + ) + }), + || Ok(self.inputs.get()?.z_U_end[i]), + ) + }) + .collect::>, _>>()?; + // Allocate z_R_start + let z_R_start = (0..arity) + .map(|i| { + AllocatedNum::alloc( + cs.namespace(|| { + format!( + " + {i}" + ) + }), + || Ok(self.inputs.get()?.z_R_start[i]), + ) + }) + .collect::>, _>>()?; + // Allocate z_R_end + let z_R_end = (0..arity) + .map(|i| { + AllocatedNum::alloc( + cs.namespace(|| { + format!( + " + {i}" + ) + }), + || Ok(self.inputs.get()?.z_R_end[i]), + ) + }) + .collect::>, _>>()?; + // Allocate the running instance U + let U: AllocatedRelaxedR1CSInstance = AllocatedRelaxedR1CSInstance::alloc( + cs.namespace(|| "Allocate U"), + self.inputs.get().map_or(None, |inputs| { + inputs.U.get().map_or(None, |U| Some(U.clone())) + }), + self.params.limb_width, + self.params.n_limbs, + )?; + + // Allocate the instance u to be folded in + let u = AllocatedR1CSInstance::alloc( + cs.namespace(|| "allocate instance u to fold"), + self.inputs.get().map_or(None, |inputs| { + inputs.u.get().map_or(None, |u| Some(u.clone())) + }), + )?; + + // Allocate the running instance U + let R: AllocatedRelaxedR1CSInstance = AllocatedRelaxedR1CSInstance::alloc( + cs.namespace(|| "Allocate R"), + self.inputs.get().map_or(None, |inputs| { + inputs.R.get().map_or(None, |U| Some(U.clone())) + }), + self.params.limb_width, + self.params.n_limbs, + )?; + + // Allocate the instance r to be folded in + let r = AllocatedR1CSInstance::alloc( + cs.namespace(|| "allocate instance r to fold"), + self.inputs.get().map_or(None, |inputs| { + inputs.u.get().map_or(None, |u| Some(u.clone())) + }), + )?; + + // Allocate T + let T_u = AllocatedPoint::alloc( + cs.namespace(|| "allocate T_u"), + self.inputs.get().map_or(None, |inputs| { + inputs + .T_r + .get() + .map_or(None, |T_r| Some(T_r.to_coordinates())) + }), + )?; let T_r = AllocatedPoint::alloc( - cs.namespace(|| "allocate T_r"), - self.inputs.get().map_or(None, |inputs| { - inputs.T_u.get().map_or(None, |T_u| Some(T_u.to_coordinates())) - }), - )?; - + cs.namespace(|| "allocate T_r"), + self.inputs.get().map_or(None, |inputs| { + inputs + .T_u + .get() + .map_or(None, |T_u| Some(T_u.to_coordinates())) + }), + )?; let T_R_U = AllocatedPoint::alloc( - cs.namespace(|| "allocate T_R_U"), - self.inputs.get().map_or(None, |inputs| { - inputs.T_R_U.get().map_or(None, |T_R_U| Some(T_R_U.to_coordinates())) - }), - )?; - - Ok((params, i_start_U, i_end_U, i_start_R, i_end_R, z_U_start, z_U_end, z_R_start, z_R_end, U, u, R, r, T_u, T_r, T_R_U, arity)) - } - - /// Synthesizes base case and returns the new relaxed R1CSInstance - fn synthesize_base_case::Base>>( - &self, - mut cs: CS, - u: AllocatedR1CSInstance, - ) -> Result, SynthesisError> { - let U_default: AllocatedRelaxedR1CSInstance = if self.params.is_primary_circuit { - // The primary circuit just returns the default R1CS instance - AllocatedRelaxedR1CSInstance::default( - cs.namespace(|| "Allocate U_default"), - self.params.limb_width, - self.params.n_limbs, - )? - } else { - // The secondary circuit returns the incoming R1CS instance - AllocatedRelaxedR1CSInstance::from_r1cs_instance( - cs.namespace(|| "Allocate U_default"), - u, - self.params.limb_width, - self.params.n_limbs, - )? - }; - Ok(U_default) - } - - /// Synthesizes non base case and returns the new relaxed R1CSInstance - /// And a boolean indicating if all checks pass - #[allow(clippy::too_many_arguments)] - fn synthesize_non_base_case::Base>>( - &self, - mut cs: CS, - params: AllocatedNum, - i_start_U: AllocatedNum, - i_end_U: AllocatedNum, - i_start_R: AllocatedNum, - i_end_R: AllocatedNum, - z_U_start: Vec>, - z_U_end: Vec>, - z_R_start: Vec>, - z_R_end: Vec>, - U: AllocatedRelaxedR1CSInstance, - u: AllocatedR1CSInstance, - R: AllocatedRelaxedR1CSInstance, - r: AllocatedR1CSInstance, - T_u: AllocatedPoint, - T_r: AllocatedPoint, - T_R_U: AllocatedPoint, - arity: usize, - ) -> Result<(AllocatedRelaxedR1CSInstance, AllocatedBit), SynthesisError> { - // Check that u.x[0] = Hash(params, U, i, z_U_start, z_U_end) - let mut ro = G::ROCircuit::new( - self.ro_consts.clone(), - NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * arity, - ); - ro.absorb(params.clone()); - ro.absorb(i_start_U.clone()); - ro.absorb(i_end_U.clone()); - for e in z_U_start.clone() { - ro.absorb(e); - } - for e in z_U_end { - ro.absorb(e); - } - U.absorb_in_ro(cs.namespace(|| "absorb U"), &mut ro)?; - - let hash_bits = ro.squeeze(cs.namespace(|| "Input hash"), NUM_HASH_BITS)?; - let hash_u = le_bits_to_num(cs.namespace(|| "bits to hash"), hash_bits)?; - let check_pass_u = alloc_num_equals( - cs.namespace(|| "check consistency of u.X[0] with H(params, U, i, z_u_start, z_u_end)"), - &u.X0, - &hash_u, - )?; - - // Run NIFS Verifier - let U_fold = U.fold_with_r1cs( - cs.namespace(|| "compute fold of U and u"), - params.clone(), - u, - T_u, - self.ro_consts.clone(), - self.params.limb_width, - self.params.n_limbs, - )?; + cs.namespace(|| "allocate T_R_U"), + self.inputs.get().map_or(None, |inputs| { + inputs + .T_R_U + .get() + .map_or(None, |T_R_U| Some(T_R_U.to_coordinates())) + }), + )?; - // Check that r.x[0] = Hash(params, R, i, z_R_start, z_R_end) - ro = G::ROCircuit::new( - self.ro_consts.clone(), - NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * arity, - ); - ro.absorb(params.clone()); - ro.absorb(i_start_R); - ro.absorb(i_end_R); - for e in z_R_start.clone() { - ro.absorb(e); - } - for e in z_R_end.clone() { - ro.absorb(e); - } - R.absorb_in_ro(cs.namespace(|| "absorb R"), &mut ro)?; - - let hash_bits = ro.squeeze(cs.namespace(|| "Input hash"), NUM_HASH_BITS)?; - let hash_r = le_bits_to_num(cs.namespace(|| "bits to hash"), hash_bits)?; - let check_pass_r = alloc_num_equals( - cs.namespace(|| "check consistency of r.X[0] with H(params, R, i, z_r_start, z_r_end)"), - &r.X0, - &hash_r, - )?; + Ok(( + params, i_start_U, i_end_U, i_start_R, i_end_R, z_U_start, z_U_end, z_R_start, z_R_end, U, u, + R, r, T_u, T_r, T_R_U, arity, + )) + } - // Run NIFS Verifier - let R_fold = R.fold_with_r1cs( - cs.namespace(|| "compute fold of U and u"), - params.clone(), - r, - T_r, - self.ro_consts.clone(), + /// Synthesizes base case and returns the new relaxed R1CSInstance + fn synthesize_base_case::Base>>( + &self, + mut cs: CS, + u: AllocatedR1CSInstance, + ) -> Result, SynthesisError> { + let U_default: AllocatedRelaxedR1CSInstance = if self.params.is_primary_circuit { + // The primary circuit just returns the default R1CS instance + AllocatedRelaxedR1CSInstance::default( + cs.namespace(|| "Allocate U_default"), self.params.limb_width, self.params.n_limbs, - )?; - - // Finally we hash z_U_start with z_R_end and use that to fold the relaxed instances resulting from the fold - // of the two running instances - ro = G::ROCircuit::new( - self.ro_consts.clone(), - NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * arity, - ); - ro.absorb(params.clone()); - ro.absorb(i_start_U); - for e in z_U_start { - ro.absorb(e); - } - for e in z_R_end { - ro.absorb(e); - } - ro.absorb(hash_r); - ro.absorb(hash_u); - - // Run NIFS Verifier - let U_R_fold = U_fold.fold_with_relaxed_r1cs( - cs.namespace(|| "compute fold of U and u"), - params, - R_fold, - T_R_U, - self.ro_consts.clone(), + )? + } else { + // The secondary circuit returns the incoming R1CS instance + AllocatedRelaxedR1CSInstance::from_r1cs_instance( + cs.namespace(|| "Allocate U_default"), + u, self.params.limb_width, self.params.n_limbs, - )?; + )? + }; + Ok(U_default) + } - let hashChecks = AllocatedBit::and( - cs.namespace(|| "check both hashes are correct"), - &check_pass_u, - &check_pass_r, - )?; - - Ok((U_R_fold, hashChecks)) + /// Synthesizes non base case and returns the new relaxed R1CSInstance + /// And a boolean indicating if all checks pass + #[allow(clippy::too_many_arguments)] + fn synthesize_non_base_case::Base>>( + &self, + mut cs: CS, + params: AllocatedNum, + i_start_U: AllocatedNum, + i_end_U: AllocatedNum, + i_start_R: AllocatedNum, + i_end_R: AllocatedNum, + z_U_start: Vec>, + z_U_end: Vec>, + z_R_start: Vec>, + z_R_end: Vec>, + U: AllocatedRelaxedR1CSInstance, + u: AllocatedR1CSInstance, + R: AllocatedRelaxedR1CSInstance, + r: AllocatedR1CSInstance, + T_u: AllocatedPoint, + T_r: AllocatedPoint, + T_R_U: AllocatedPoint, + arity: usize, + ) -> Result<(AllocatedRelaxedR1CSInstance, AllocatedBit), SynthesisError> { + // Check that u.x[0] = Hash(params, U, i, z_U_start, z_U_end) + let mut ro = G::ROCircuit::new( + self.ro_consts.clone(), + NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * arity, + ); + ro.absorb(params.clone()); + ro.absorb(i_start_U.clone()); + ro.absorb(i_end_U.clone()); + for e in z_U_start.clone() { + ro.absorb(e); } + for e in z_U_end { + ro.absorb(e); + } + U.absorb_in_ro(cs.namespace(|| "absorb U"), &mut ro)?; + + let hash_bits = ro.squeeze(cs.namespace(|| "Input hash"), NUM_HASH_BITS)?; + let hash_u = le_bits_to_num(cs.namespace(|| "bits to hash"), hash_bits)?; + let check_pass_u = alloc_num_equals( + cs.namespace(|| "check consistency of u.X[0] with H(params, U, i, z_u_start, z_u_end)"), + &u.X0, + &hash_u, + )?; + + // Run NIFS Verifier + let U_fold = U.fold_with_r1cs( + cs.namespace(|| "compute fold of U and u"), + params.clone(), + u, + T_u, + self.ro_consts.clone(), + self.params.limb_width, + self.params.n_limbs, + )?; + + // Check that r.x[0] = Hash(params, R, i, z_R_start, z_R_end) + ro = G::ROCircuit::new( + self.ro_consts.clone(), + NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * arity, + ); + ro.absorb(params.clone()); + ro.absorb(i_start_R); + ro.absorb(i_end_R); + for e in z_R_start.clone() { + ro.absorb(e); + } + for e in z_R_end.clone() { + ro.absorb(e); + } + R.absorb_in_ro(cs.namespace(|| "absorb R"), &mut ro)?; + + let hash_bits = ro.squeeze(cs.namespace(|| "Input hash"), NUM_HASH_BITS)?; + let hash_r = le_bits_to_num(cs.namespace(|| "bits to hash"), hash_bits)?; + let check_pass_r = alloc_num_equals( + cs.namespace(|| "check consistency of r.X[0] with H(params, R, i, z_r_start, z_r_end)"), + &r.X0, + &hash_r, + )?; + + // Run NIFS Verifier + let R_fold = R.fold_with_r1cs( + cs.namespace(|| "compute fold of U and u"), + params.clone(), + r, + T_r, + self.ro_consts.clone(), + self.params.limb_width, + self.params.n_limbs, + )?; + + // Finally we hash z_U_start with z_R_end and use that to fold the relaxed instances resulting from the fold + // of the two running instances + ro = G::ROCircuit::new( + self.ro_consts.clone(), + NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * arity, + ); + ro.absorb(params.clone()); + ro.absorb(i_start_U); + for e in z_U_start { + ro.absorb(e); + } + for e in z_R_end { + ro.absorb(e); + } + ro.absorb(hash_r); + ro.absorb(hash_u); + + // Run NIFS Verifier + let U_R_fold = U_fold.fold_with_relaxed_r1cs( + cs.namespace(|| "compute fold of U and u"), + params, + R_fold, + T_R_U, + self.ro_consts.clone(), + self.params.limb_width, + self.params.n_limbs, + )?; + + let hashChecks = AllocatedBit::and( + cs.namespace(|| "check both hashes are correct"), + &check_pass_u, + &check_pass_r, + )?; + + Ok((U_R_fold, hashChecks)) } - - impl> Circuit<::Base> - for NovaAugmentedParallelCircuit - { - fn synthesize::Base>>( - self, - cs: &mut CS, - ) -> Result<(), SynthesisError> { - let arity = self.step_circuit.arity(); - - // Allocate all witnesses - let ( - params, - i_start_U, - i_end_U, - i_start_R, - i_end_R, - z_U_start, - z_U_end, - z_R_start, - z_R_end, - U, - u, - R, - r, - T_u, - T_r, - T_R_U, - arity) = - self.alloc_witness(cs.namespace(|| "allocate the circuit witness"), arity)?; - - // Compute variable indicating if this is the base case - let mut is_base_case = alloc_num_equals(cs.namespace(|| "Check if base case as i_start_U == i_end_U"), &i_start_U.clone(), &i_end_U)?; - let r_index_equal = alloc_num_equals(cs.namespace(|| "In base case i_start_R == i_end_R"), &i_start_R, &i_end_R)?; - is_base_case = AllocatedBit::and( - cs.namespace(|| "i_start_U == i_end_U and i_start_U + 1 == i_end_R"), - &is_base_case, - &r_index_equal - )?; - - // Synthesize the circuit for the base case and get the new running instance - let Unew_base = self.synthesize_base_case(cs.namespace(|| "base case"), u.clone())?; - - // Synthesize the circuit for the non-base case and get the new running - // instance along with a boolean indicating if all checks have passed - let (Unew_non_base, check_non_base_pass) = self.synthesize_non_base_case( - cs.namespace(|| "synthesize non base case"), - params.clone(), - i_start_U.clone(), - i_end_U.clone(), - i_start_R.clone(), - i_end_R.clone(), - z_U_start.clone(), - z_U_end.clone(), - z_R_start.clone(), - z_R_end.clone(), - U, - u.clone(), - R, - r, - T_u, - T_r, - T_R_U, - arity - )?; - - // Either check_non_base_pass=true or we are in the base case - let should_be_false = AllocatedBit::nor( - cs.namespace(|| "check_non_base_pass nor base_case"), - &check_non_base_pass, - &is_base_case, - )?; - cs.enforce( - || "check_non_base_pass nor base_case = false", - |lc| lc + should_be_false.get_variable(), - |lc| lc + CS::one(), - |lc| lc, - ); - - // Compute the U_new - let Unew = Unew_base.conditionally_select( - cs.namespace(|| "compute U_new"), - Unew_non_base, - &Boolean::from(is_base_case.clone()), - )?; - - // Compute i_u_end + 1 == i_r_start, this enforces only one invocation of F between ranges - let i_new = AllocatedNum::alloc(cs.namespace(|| "i + 1"), || { - Ok(*i_end_U.get_value().get()? + G::Base::one()) - })?; - cs.enforce( - || "check i + 1", - |lc| lc, - |lc| lc, - |lc| lc + i_new.get_variable() - i_start_R.get_variable(), - ); - - // The input to the F function is either z_U_end in default case or z_U_start in the base case - let z_input = conditionally_select_vec( - cs.namespace(|| "select input to F"), - &z_U_start, - &z_U_end, - &Boolean::from(is_base_case.clone()), - )?; - - let z_next = self - .step_circuit - .synthesize(&mut cs.namespace(|| "F"), &z_input)?; +} + +impl> Circuit<::Base> + for NovaAugmentedParallelCircuit +{ + fn synthesize::Base>>( + self, + cs: &mut CS, + ) -> Result<(), SynthesisError> { + let arity = self.step_circuit.arity(); + + // Allocate all witnesses + let ( + params, + i_start_U, + i_end_U, + i_start_R, + i_end_R, + z_U_start, + z_U_end, + z_R_start, + z_R_end, + U, + u, + R, + r, + T_u, + T_r, + T_R_U, + arity, + ) = self.alloc_witness(cs.namespace(|| "allocate the circuit witness"), arity)?; + + // Compute variable indicating if this is the base case + let mut is_base_case = alloc_num_equals( + cs.namespace(|| "Check if base case as i_start_U == i_end_U"), + &i_start_U.clone(), + &i_end_U, + )?; + let r_index_equal = alloc_num_equals( + cs.namespace(|| "In base case i_start_R == i_end_R"), + &i_start_R, + &i_end_R, + )?; + is_base_case = AllocatedBit::and( + cs.namespace(|| "i_start_U == i_end_U and i_start_U + 1 == i_end_R"), + &is_base_case, + &r_index_equal, + )?; + + // Synthesize the circuit for the base case and get the new running instance + let Unew_base = self.synthesize_base_case(cs.namespace(|| "base case"), u.clone())?; + + // Synthesize the circuit for the non-base case and get the new running + // instance along with a boolean indicating if all checks have passed + let (Unew_non_base, check_non_base_pass) = self.synthesize_non_base_case( + cs.namespace(|| "synthesize non base case"), + params.clone(), + i_start_U.clone(), + i_end_U.clone(), + i_start_R.clone(), + i_end_R.clone(), + z_U_start.clone(), + z_U_end.clone(), + z_R_start.clone(), + z_R_end.clone(), + U, + u.clone(), + R, + r, + T_u, + T_r, + T_R_U, + arity, + )?; + + // Either check_non_base_pass=true or we are in the base case + let should_be_false = AllocatedBit::nor( + cs.namespace(|| "check_non_base_pass nor base_case"), + &check_non_base_pass, + &is_base_case, + )?; + cs.enforce( + || "check_non_base_pass nor base_case = false", + |lc| lc + should_be_false.get_variable(), + |lc| lc + CS::one(), + |lc| lc, + ); + + // Compute the U_new + let Unew = Unew_base.conditionally_select( + cs.namespace(|| "compute U_new"), + Unew_non_base, + &Boolean::from(is_base_case.clone()), + )?; + + // Compute i_u_end + 1 == i_r_start, this enforces only one invocation of F between ranges + let i_new = AllocatedNum::alloc(cs.namespace(|| "i + 1"), || { + Ok(*i_end_U.get_value().get()? + G::Base::one()) + })?; + cs.enforce( + || "check i + 1", + |lc| lc, + |lc| lc, + |lc| lc + i_new.get_variable() - i_start_R.get_variable(), + ); + + // The input to the F function is either z_U_end in default case or z_U_start in the base case + let z_input = conditionally_select_vec( + cs.namespace(|| "select input to F"), + &z_U_start, + &z_U_end, + &Boolean::from(is_base_case.clone()), + )?; + + let z_next = self + .step_circuit + .synthesize(&mut cs.namespace(|| "F"), &z_input)?; // In the base case our output is in the z_R_end field and in our normal case it's in z_R_start let z_output = conditionally_select_vec( - cs.namespace(|| "select input to F"), - &z_R_start, - &z_R_end, - &Boolean::from(is_base_case), + cs.namespace(|| "select input to F"), + &z_R_start, + &z_R_end, + &Boolean::from(is_base_case), )?; - - if z_next.len() != arity { - return Err(SynthesisError::IncompatibleLengthVector( - "z_next".to_string(), - )); - } - - // Check vector equality of the step output and of the start of the R block - let mut outputEqual = AllocatedBit::alloc(cs.namespace(|| "allocate bit equal"), Some(true))?; - for (next, input) in z_next.clone().iter().zip(z_output.clone().iter()) { - // We check that each index is equal then and it with the global check - let entryEqual = alloc_num_equals(cs.namespace(|| "equality check of z_next and z_output"), &next, &input)?; - outputEqual = AllocatedBit::and( - cs.namespace(|| "accumulate equality checks"), - &outputEqual, - &entryEqual - )?; - } - - // TODO - I really don't know how this R1CS lib works and this is a guess - cs.enforce( - || "check outputEqual = true", - |lc| lc, - |lc| lc, - |lc| lc + outputEqual.get_variable() - CS::one(), - ); - - // Compute the new hash H(params, Unew, i_u_start, z_U_start, z_R_end) - let mut ro = G::ROCircuit::new(self.ro_consts, NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * arity); - ro.absorb(params); - ro.absorb(i_start_U.clone()); - ro.absorb(i_end_R.clone()); - for e in z_input { - ro.absorb(e); - } - for e in z_output { - ro.absorb(e); - } - Unew.absorb_in_ro(cs.namespace(|| "absorb U_new"), &mut ro)?; - let hash_bits = ro.squeeze(cs.namespace(|| "output hash bits"), NUM_HASH_BITS)?; - let hash = le_bits_to_num(cs.namespace(|| "convert hash to num"), hash_bits)?; - - // Outputs the computed hash and u.X[1] that corresponds to the hash of the other circuit - u.X1 - .inputize(cs.namespace(|| "Output unmodified hash of the other circuit"))?; - hash.inputize(cs.namespace(|| "output new hash of this circuit"))?; - - Ok(()) + + if z_next.len() != arity { + return Err(SynthesisError::IncompatibleLengthVector( + "z_next".to_string(), + )); + } + + // Check vector equality of the step output and of the start of the R block + let mut outputEqual = AllocatedBit::alloc(cs.namespace(|| "allocate bit equal"), Some(true))?; + for (next, input) in z_next.clone().iter().zip(z_output.clone().iter()) { + // We check that each index is equal then and it with the global check + let entryEqual = alloc_num_equals( + cs.namespace(|| "equality check of z_next and z_output"), + &next, + &input, + )?; + outputEqual = AllocatedBit::and( + cs.namespace(|| "accumulate equality checks"), + &outputEqual, + &entryEqual, + )?; } + + // TODO - I really don't know how this R1CS lib works and this is a guess + cs.enforce( + || "check outputEqual = true", + |lc| lc, + |lc| lc, + |lc| lc + outputEqual.get_variable() - CS::one(), + ); + + // Compute the new hash H(params, Unew, i_u_start, z_U_start, z_R_end) + let mut ro = G::ROCircuit::new(self.ro_consts, NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * arity); + ro.absorb(params); + ro.absorb(i_start_U.clone()); + ro.absorb(i_end_R.clone()); + for e in z_input { + ro.absorb(e); + } + for e in z_output { + ro.absorb(e); + } + Unew.absorb_in_ro(cs.namespace(|| "absorb U_new"), &mut ro)?; + let hash_bits = ro.squeeze(cs.namespace(|| "output hash bits"), NUM_HASH_BITS)?; + let hash = le_bits_to_num(cs.namespace(|| "convert hash to num"), hash_bits)?; + + // Outputs the computed hash and u.X[1] that corresponds to the hash of the other circuit + u.X1 + .inputize(cs.namespace(|| "Output unmodified hash of the other circuit"))?; + hash.inputize(cs.namespace(|| "output new hash of this circuit"))?; + + Ok(()) } +} diff --git a/src/parallel_prover.rs b/src/parallel_prover.rs index 07a7de03..46f3fc63 100644 --- a/src/parallel_prover.rs +++ b/src/parallel_prover.rs @@ -14,43 +14,42 @@ //! z_end that the output is z_start of the right node use crate::{ - constants::{NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_BITS}, - gadgets::{ - ecc::AllocatedPoint, - r1cs::{AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance}, - utils::{ - alloc_num_equals, alloc_scalar_as_base, conditionally_select_vec, le_bits_to_num, - }, - }, - r1cs::{RelaxedR1CSInstance, R1CSShape, RelaxedR1CSWitness}, - traits::{ - circuit::StepCircuit, - commitment::{CommitmentEngineTrait, CommitmentTrait}, - snark::RelaxedR1CSSNARKTrait, - AbsorbInROTrait, Group, ROConstants, ROConstantsCircuit, ROConstantsTrait, ROTrait, - }, - Commitment, - parallel_circuit::{NovaAugmentedParallelCircuit, NovaAugmentedParallelCircuitInputs}, - constants::{BN_LIMB_WIDTH, BN_N_LIMBS}, - circuit::NovaAugmentedCircuitParams, - bellperson::{ - r1cs::{NovaShape, NovaWitness}, - shape_cs::ShapeCS, - solver::SatisfyingAssignment}, - errors::NovaError, - nifs::NIFS - }; - use bellperson::{ - gadgets::{ - boolean::{AllocatedBit, Boolean}, - num::AllocatedNum, - Assignment, - }, - Circuit, ConstraintSystem, SynthesisError, - }; - use ff::Field; - use serde::{Deserialize, Serialize}; - use core::marker::PhantomData; + bellperson::{ + r1cs::{NovaShape, NovaWitness}, + shape_cs::ShapeCS, + solver::SatisfyingAssignment, + }, + circuit::NovaAugmentedCircuitParams, + constants::{BN_LIMB_WIDTH, BN_N_LIMBS}, + constants::{NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_BITS}, + errors::NovaError, + gadgets::{ + ecc::AllocatedPoint, + r1cs::{AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance}, + utils::{alloc_num_equals, alloc_scalar_as_base, conditionally_select_vec, le_bits_to_num}, + }, + nifs::NIFS, + parallel_circuit::{NovaAugmentedParallelCircuit, NovaAugmentedParallelCircuitInputs}, + r1cs::{R1CSShape, RelaxedR1CSInstance, RelaxedR1CSWitness}, + traits::{ + circuit::StepCircuit, + commitment::{CommitmentEngineTrait, CommitmentTrait}, + snark::RelaxedR1CSSNARKTrait, + AbsorbInROTrait, Group, ROConstants, ROConstantsCircuit, ROConstantsTrait, ROTrait, + }, + Commitment, +}; +use bellperson::{ + gadgets::{ + boolean::{AllocatedBit, Boolean}, + num::AllocatedNum, + Assignment, + }, + Circuit, ConstraintSystem, SynthesisError, +}; +use core::marker::PhantomData; +use ff::Field; +use serde::{Deserialize, Serialize}; // TODO - This is replicated from lib but we should actually instead have another file for it and use both here and there @@ -92,9 +91,9 @@ where /// Create a new `PublicParams` pub fn setup(c_primary: C1, c_secondary: C2) -> Self { let augmented_circuit_params_primary = - NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, true); + NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, true); let augmented_circuit_params_secondary = - NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, false); + NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, false); let ro_consts_primary: ROConstants = ROConstants::::new(); let ro_consts_secondary: ROConstants = ROConstants::::new(); @@ -165,7 +164,6 @@ where // This ends the 1 to 1 copied code - /// A type that holds one node the tree based nova proof. This will have both running instances and fresh instances /// of the primary and secondary circuit. #[derive(Serialize, Deserialize)] @@ -177,26 +175,26 @@ where C1: StepCircuit, C2: StepCircuit, { - // The running instance of the primary - W_primary: RelaxedR1CSWitness, - U_primary: RelaxedR1CSInstance, - // The new instance of the primary - w_primary: RelaxedR1CSWitness, - u_primary: RelaxedR1CSInstance, - // The running instance of the secondary - W_secondary: RelaxedR1CSWitness, - U_secondary: RelaxedR1CSInstance, - // The running instance of the secondary - w_secondary: RelaxedR1CSWitness, - u_secondary: RelaxedR1CSInstance, - i_start: u64, - i_end: u64, - z_start_primary: Vec, - z_end_primary: Vec, - z_start_secondary: Vec, - z_end_secondary: Vec, - _p_c1: PhantomData, - _p_c2: PhantomData, + // The running instance of the primary + W_primary: RelaxedR1CSWitness, + U_primary: RelaxedR1CSInstance, + // The new instance of the primary + w_primary: RelaxedR1CSWitness, + u_primary: RelaxedR1CSInstance, + // The running instance of the secondary + W_secondary: RelaxedR1CSWitness, + U_secondary: RelaxedR1CSInstance, + // The running instance of the secondary + w_secondary: RelaxedR1CSWitness, + u_secondary: RelaxedR1CSInstance, + i_start: u64, + i_end: u64, + z_start_primary: Vec, + z_end_primary: Vec, + z_start_secondary: Vec, + z_end_secondary: Vec, + _p_c1: PhantomData, + _p_c2: PhantomData, } impl NovaTreeNode @@ -216,28 +214,29 @@ where z_start_primary: Vec, z_end_primary: Vec, z_start_secondary: Vec, - z_end_secondary: Vec - ) -> Result { + z_end_secondary: Vec, + ) -> Result { // base case for the primary let mut cs_primary: SatisfyingAssignment = SatisfyingAssignment::new(); - let inputs_primary: NovaAugmentedParallelCircuitInputs = NovaAugmentedParallelCircuitInputs::new( - pp.r1cs_shape_secondary.get_digest(), - G1::Scalar::from(i.try_into().unwrap()), - G1::Scalar::from((i+1).try_into().unwrap()), - G1::Scalar::from((i).try_into().unwrap()), - G1::Scalar::from((i+1).try_into().unwrap()), - z_start_primary.clone(), - z_start_primary.clone(), - z_end_primary.clone(), - z_end_primary.clone(), - None, - None, - None, - None, - None, - None, - None - ); + let inputs_primary: NovaAugmentedParallelCircuitInputs = + NovaAugmentedParallelCircuitInputs::new( + pp.r1cs_shape_secondary.get_digest(), + G1::Scalar::from(i.try_into().unwrap()), + G1::Scalar::from((i + 1).try_into().unwrap()), + G1::Scalar::from((i).try_into().unwrap()), + G1::Scalar::from((i + 1).try_into().unwrap()), + z_start_primary.clone(), + z_start_primary.clone(), + z_end_primary.clone(), + z_end_primary.clone(), + None, + None, + None, + None, + None, + None, + None, + ); let circuit_primary: NovaAugmentedParallelCircuit = NovaAugmentedParallelCircuit::new( pp.augmented_circuit_params_primary.clone(), @@ -253,24 +252,25 @@ where // base case for the secondary let mut cs_secondary: SatisfyingAssignment = SatisfyingAssignment::new(); - let inputs_secondary: NovaAugmentedParallelCircuitInputs = NovaAugmentedParallelCircuitInputs::new( - pp.r1cs_shape_primary.get_digest(), - G2::Scalar::from(i), - G2::Scalar::from(i+1), - G2::Scalar::from(i), - G2::Scalar::from(i+1), - z_start_secondary.clone(), - z_start_secondary.clone(), - z_end_secondary.clone(), - z_end_secondary.clone(), + let inputs_secondary: NovaAugmentedParallelCircuitInputs = + NovaAugmentedParallelCircuitInputs::new( + pp.r1cs_shape_primary.get_digest(), + G2::Scalar::from(i), + G2::Scalar::from(i + 1), + G2::Scalar::from(i), + G2::Scalar::from(i + 1), + z_start_secondary.clone(), + z_start_secondary.clone(), + z_end_secondary.clone(), + z_end_secondary.clone(), None, None, None, None, None, None, - None - ); + None, + ); let circuit_secondary: NovaAugmentedParallelCircuit = NovaAugmentedParallelCircuit::new( pp.augmented_circuit_params_secondary.clone(), Some(inputs_secondary), @@ -283,13 +283,9 @@ where .map_err(|_e| NovaError::UnSat)?; // IVC proof for the primary circuit - let w_primary = - RelaxedR1CSWitness::from_r1cs_witness(&pp.r1cs_shape_primary, &w_primary); - let u_primary = RelaxedR1CSInstance::from_r1cs_instance( - &pp.ck_primary, - &pp.r1cs_shape_primary, - &u_primary, - ); + let w_primary = RelaxedR1CSWitness::from_r1cs_witness(&pp.r1cs_shape_primary, &w_primary); + let u_primary = + RelaxedR1CSInstance::from_r1cs_instance(&pp.ck_primary, &pp.r1cs_shape_primary, &u_primary); let W_primary = w_primary.clone(); let U_primary = u_primary.clone(); @@ -304,13 +300,14 @@ where let W_secondary = w_secondary.clone(); let U_secondary = u_secondary.clone(); - - if z_start_primary.len() != pp.F_arity_primary || z_start_secondary.len() != pp.F_arity_secondary { + if z_start_primary.len() != pp.F_arity_primary + || z_start_secondary.len() != pp.F_arity_secondary + { return Err(NovaError::InvalidStepOutputLength); } let i_start = i; - let i_end = i+1; + let i_end = i + 1; Ok(Self { W_primary, @@ -339,66 +336,66 @@ where right: NovaTreeNode, pp: &PublicParams, c_primary: C1, - c_secondary: C2 - ) -> Result { - + c_secondary: C2, + ) -> Result { // We have to merge two proofs where the right starts one index after the left ends // note that this would fail in the proof step but we error earlier here for debugging clarity. if self.i_end + 1 != right.i_start { - return Err(NovaError::InvalidNodeMerge); + return Err(NovaError::InvalidNodeMerge); } // First we fold the secondary instances of both the left and right children in the secondary curve let (nifs_left_secondary, (left_U_secondary, left_W_secondary)) = NIFS::prove( - &pp.ck_secondary, - &pp.ro_consts_secondary, - &pp.r1cs_shape_secondary, - &self.U_secondary, - &self.W_secondary, - &self.u_secondary, - &self.w_secondary, + &pp.ck_secondary, + &pp.ro_consts_secondary, + &pp.r1cs_shape_secondary, + &self.U_secondary, + &self.W_secondary, + &self.u_secondary, + &self.w_secondary, )?; let (nifs_right_secondary, (right_U_secondary, right_W_secondary)) = NIFS::prove( - &pp.ck_secondary, - &pp.ro_consts_secondary, - &pp.r1cs_shape_secondary, - &right.U_secondary, - &right.W_secondary, - &right.u_secondary, - &right.w_secondary, + &pp.ck_secondary, + &pp.ro_consts_secondary, + &pp.r1cs_shape_secondary, + &right.U_secondary, + &right.W_secondary, + &right.u_secondary, + &right.w_secondary, )?; let (nifs_secondary, (U_secondary, W_secondary)) = NIFS::prove( - &pp.ck_secondary, - &pp.ro_consts_secondary, - &pp.r1cs_shape_secondary, - &left_U_secondary, - &left_W_secondary, - &right_U_secondary, - &right_W_secondary, + &pp.ck_secondary, + &pp.ro_consts_secondary, + &pp.r1cs_shape_secondary, + &left_U_secondary, + &left_W_secondary, + &right_U_secondary, + &right_W_secondary, )?; // Next we construct a proof of this folding and of the invocation of F let mut cs_primary: SatisfyingAssignment = SatisfyingAssignment::new(); - let inputs_primary: NovaAugmentedParallelCircuitInputs = NovaAugmentedParallelCircuitInputs::new( - pp.r1cs_shape_secondary.get_digest(), - G1::Scalar::from(self.i_start as u64), - G1::Scalar::from(self.i_end as u64), - G1::Scalar::from(right.i_start as u64), - G1::Scalar::from(right.i_end as u64), - self.z_start_primary.clone(), - self.z_end_primary, - right.z_start_primary, - right.z_end_primary.clone(), - Some(self.U_secondary), - Some(self.u_secondary), - Some(right.U_secondary), - Some(right.u_secondary), - Some(Commitment::::decompress(&nifs_left_secondary.comm_T)?), - Some(Commitment::::decompress(&nifs_right_secondary.comm_T)?), - Some(Commitment::::decompress(&nifs_secondary.comm_T)?) - ); + let inputs_primary: NovaAugmentedParallelCircuitInputs = + NovaAugmentedParallelCircuitInputs::new( + pp.r1cs_shape_secondary.get_digest(), + G1::Scalar::from(self.i_start as u64), + G1::Scalar::from(self.i_end as u64), + G1::Scalar::from(right.i_start as u64), + G1::Scalar::from(right.i_end as u64), + self.z_start_primary.clone(), + self.z_end_primary, + right.z_start_primary, + right.z_end_primary.clone(), + Some(self.U_secondary), + Some(self.u_secondary), + Some(right.U_secondary), + Some(right.u_secondary), + Some(Commitment::::decompress(&nifs_left_secondary.comm_T)?), + Some(Commitment::::decompress(&nifs_right_secondary.comm_T)?), + Some(Commitment::::decompress(&nifs_secondary.comm_T)?), + ); let circuit_primary: NovaAugmentedParallelCircuit = NovaAugmentedParallelCircuit::new( pp.augmented_circuit_params_primary.clone(), @@ -409,62 +406,64 @@ where let _ = circuit_primary.synthesize(&mut cs_primary); let (u_primary, w_primary) = cs_primary - .r1cs_instance_and_witness(&pp.r1cs_shape_primary, &pp.ck_primary) - .map_err(|_e| NovaError::UnSat)?; + .r1cs_instance_and_witness(&pp.r1cs_shape_primary, &pp.ck_primary) + .map_err(|_e| NovaError::UnSat)?; - let u_primary = RelaxedR1CSInstance::from_r1cs_instance(&pp.ck_primary, &pp.r1cs_shape_primary, &u_primary); - let w_primary = RelaxedR1CSWitness::from_r1cs_witness(&pp.r1cs_shape_primary, &w_primary); + let u_primary = + RelaxedR1CSInstance::from_r1cs_instance(&pp.ck_primary, &pp.r1cs_shape_primary, &u_primary); + let w_primary = RelaxedR1CSWitness::from_r1cs_witness(&pp.r1cs_shape_primary, &w_primary); // Now we fold the instances of the primary proof let (nifs_left_primary, (left_U_primary, left_W_primary)) = NIFS::prove( - &pp.ck_primary, - &pp.ro_consts_primary, - &pp.r1cs_shape_primary, - &self.U_primary, - &self.W_primary, - &self.u_primary, - &self.w_primary, + &pp.ck_primary, + &pp.ro_consts_primary, + &pp.r1cs_shape_primary, + &self.U_primary, + &self.W_primary, + &self.u_primary, + &self.w_primary, )?; let (nifs_right_primary, (right_U_primary, right_W_primary)) = NIFS::prove( - &pp.ck_primary, - &pp.ro_consts_primary, - &pp.r1cs_shape_primary, - &right.U_primary, - &right.W_primary, - &right.u_primary, - &right.w_primary, + &pp.ck_primary, + &pp.ro_consts_primary, + &pp.r1cs_shape_primary, + &right.U_primary, + &right.W_primary, + &right.u_primary, + &right.w_primary, )?; let (nifs_primary, (U_primary, W_primary)) = NIFS::prove( - &pp.ck_primary, - &pp.ro_consts_primary, - &pp.r1cs_shape_primary, - &left_U_primary, - &left_W_primary, - &right_U_primary, - &right_W_primary, + &pp.ck_primary, + &pp.ro_consts_primary, + &pp.r1cs_shape_primary, + &left_U_primary, + &left_W_primary, + &right_U_primary, + &right_W_primary, )?; // Next we construct a proof of this folding in the secondary curve let mut cs_secondary: SatisfyingAssignment = SatisfyingAssignment::new(); - let inputs_secondary: NovaAugmentedParallelCircuitInputs = NovaAugmentedParallelCircuitInputs::::new( - pp.r1cs_shape_primary.get_digest(), - G2::Scalar::from(self.i_start as u64), - G2::Scalar::from(self.i_end as u64), - G2::Scalar::from(right.i_start as u64), - G2::Scalar::from(right.i_end as u64), - self.z_start_secondary.clone(), - self.z_end_secondary, - right.z_start_secondary, - right.z_end_secondary.clone(), - Some(self.U_primary), - Some(self.u_primary), - Some(right.U_primary), - Some(right.u_primary), - Some(Commitment::::decompress(&nifs_left_primary.comm_T)?), - Some(Commitment::::decompress(&nifs_right_primary.comm_T)?), - Some(Commitment::::decompress(&nifs_primary.comm_T)?) - ); + let inputs_secondary: NovaAugmentedParallelCircuitInputs = + NovaAugmentedParallelCircuitInputs::::new( + pp.r1cs_shape_primary.get_digest(), + G2::Scalar::from(self.i_start as u64), + G2::Scalar::from(self.i_end as u64), + G2::Scalar::from(right.i_start as u64), + G2::Scalar::from(right.i_end as u64), + self.z_start_secondary.clone(), + self.z_end_secondary, + right.z_start_secondary, + right.z_end_secondary.clone(), + Some(self.U_primary), + Some(self.u_primary), + Some(right.U_primary), + Some(right.u_primary), + Some(Commitment::::decompress(&nifs_left_primary.comm_T)?), + Some(Commitment::::decompress(&nifs_right_primary.comm_T)?), + Some(Commitment::::decompress(&nifs_primary.comm_T)?), + ); let circuit_secondary: NovaAugmentedParallelCircuit = NovaAugmentedParallelCircuit::new( pp.augmented_circuit_params_primary.clone(), @@ -475,12 +474,16 @@ where let _ = circuit_secondary.synthesize(&mut cs_secondary); let (u_secondary, w_secondary) = cs_secondary - .r1cs_instance_and_witness(&pp.r1cs_shape_secondary, &pp.ck_secondary) - .map_err(|_e| NovaError::UnSat)?; + .r1cs_instance_and_witness(&pp.r1cs_shape_secondary, &pp.ck_secondary) + .map_err(|_e| NovaError::UnSat)?; // Give these a trivial error vector - let u_secondary = RelaxedR1CSInstance::from_r1cs_instance(&pp.ck_secondary, &pp.r1cs_shape_secondary, &u_secondary); - let w_secondary = RelaxedR1CSWitness::from_r1cs_witness(&pp.r1cs_shape_secondary, &w_secondary); + let u_secondary = RelaxedR1CSInstance::from_r1cs_instance( + &pp.ck_secondary, + &pp.r1cs_shape_secondary, + &u_secondary, + ); + let w_secondary = RelaxedR1CSWitness::from_r1cs_witness(&pp.r1cs_shape_secondary, &w_secondary); // Name each of these to match struct fields let i_start = self.i_start.clone(); @@ -491,27 +494,27 @@ where let z_end_secondary = right.z_end_secondary; Ok(Self { - // Primary running instance - W_primary, - U_primary, - // Primary new instance - w_primary, - u_primary, - // The running instance of the secondary - W_secondary, - U_secondary, - // The running instance of the secondary - w_secondary, - u_secondary, - // The range data - i_start, - i_end, - z_start_primary, - z_end_primary, - z_start_secondary, - z_end_secondary, - _p_c1: Default::default(), - _p_c2: Default::default() + // Primary running instance + W_primary, + U_primary, + // Primary new instance + w_primary, + u_primary, + // The running instance of the secondary + W_secondary, + U_secondary, + // The running instance of the secondary + w_secondary, + u_secondary, + // The range data + i_start, + i_end, + z_start_primary, + z_end_primary, + z_start_secondary, + z_end_secondary, + _p_c1: Default::default(), + _p_c2: Default::default(), }) - } -} \ No newline at end of file + } +} diff --git a/src/r1cs.rs b/src/r1cs.rs index f4bf995b..cc59bc1a 100644 --- a/src/r1cs.rs +++ b/src/r1cs.rs @@ -531,7 +531,7 @@ impl RelaxedR1CSWitness { .par_iter() .zip(T) .zip(E2) - .map(|((a, b), c)| *a + *r * * b + *r * *r * *c) + .map(|((a, b), c)| *a + *r * *b + *r * *r * *c) .collect::>(); Ok(RelaxedR1CSWitness { W, E }) } @@ -588,8 +588,7 @@ impl RelaxedR1CSInstance { ) -> Result, NovaError> { let (X1, u1, comm_W_1, comm_E_1) = (&self.X, &self.u, &self.comm_W.clone(), &self.comm_E.clone()); - let (X2, u2, comm_W_2, comm_E_2) = - (&U2.X, &U2.u, &U2.comm_W.clone(), &U2.comm_E.clone()); + let (X2, u2, comm_W_2, comm_E_2) = (&U2.X, &U2.u, &U2.comm_W.clone(), &U2.comm_E.clone()); // weighted sum of X, comm_W, comm_E, and u let X = X1 From 398caa96449bdc244b3598dc2f8bfe591d324c9b Mon Sep 17 00:00:00 2001 From: Violet Vienhage <> Date: Wed, 29 Mar 2023 11:02:08 +0700 Subject: [PATCH 4/4] merge bug fix --- src/parallel_prover.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parallel_prover.rs b/src/parallel_prover.rs index ea32b51d..488f468b 100644 --- a/src/parallel_prover.rs +++ b/src/parallel_prover.rs @@ -333,7 +333,7 @@ where /// consumed is treated as the right node. pub fn merge( self, - right: &NovaTreeNode, + right: NovaTreeNode, pp: &PublicParams, c_primary: &C1, c_secondary: &C2,