From e63ea229751b76d3edfb48d8f12add88a712ad73 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Thu, 18 Jan 2024 23:33:40 -0500 Subject: [PATCH 01/77] skeleton --- src/cyclefold/circuit.rs | 222 ++++++++++++++++++++++++++++ src/cyclefold/compressed.rs | 2 + src/cyclefold/mod.rs | 7 + src/cyclefold/nova_circuit.rs | 174 ++++++++++++++++++++++ src/cyclefold/snark.rs | 271 ++++++++++++++++++++++++++++++++++ src/lib.rs | 95 +++++++++++- src/nifs.rs | 25 +++- src/supernova/mod.rs | 135 ++++++++++++++++- src/supernova/snark.rs | 2 +- src/traits/mod.rs | 1 + src/traits/recursive.rs | 42 ++++++ 11 files changed, 963 insertions(+), 13 deletions(-) create mode 100644 src/cyclefold/circuit.rs create mode 100644 src/cyclefold/compressed.rs create mode 100644 src/cyclefold/mod.rs create mode 100644 src/cyclefold/nova_circuit.rs create mode 100644 src/cyclefold/snark.rs create mode 100644 src/traits/recursive.rs diff --git a/src/cyclefold/circuit.rs b/src/cyclefold/circuit.rs new file mode 100644 index 000000000..6f9216d2e --- /dev/null +++ b/src/cyclefold/circuit.rs @@ -0,0 +1,222 @@ +//! This module defines Cyclefold stuff + +use bellpepper_core::{boolean::AllocatedBit, ConstraintSystem, SynthesisError}; + +use crate::{ + gadgets::ecc::AllocatedPoint, + traits::{commitment::CommitmentTrait, Engine}, + Commitment, +}; + +/// TODO: docs +pub struct CyclefoldCircuitInputs { + commit_1: Option>, + commit_2: Option>, + result: Option>, + scalar: Vec>, +} + +impl CyclefoldCircuitInputs { + /// TODO + pub fn new( + commit_1: Option>, + commit_2: Option>, + result: Option>, + scalar: Vec>, + ) -> Self { + Self { + commit_1, + commit_2, + result, + scalar, + } + } +} + +/// TODO: docs +pub struct CyclefoldCircuit { + inputs: Option>, +} + +impl CyclefoldCircuit { + /// TODO: docs + pub fn new(inputs: Option>) -> Self { + Self { inputs } + } + + fn alloc_witness::Base>>( + &self, + mut cs: CS, + ) -> Result< + ( + AllocatedPoint, + AllocatedPoint, + AllocatedPoint, + Vec, + ), + SynthesisError, + > { + let commit_1 = AllocatedPoint::alloc( + cs.namespace(|| "allocate C_1"), + self + .inputs + .as_ref() + .and_then(|inputs| inputs.commit_1.map(|C_1| C_1.to_coordinates())), + )?; + + let commit_2 = AllocatedPoint::alloc( + cs.namespace(|| "allocate C_2"), + self + .inputs + .as_ref() + .and_then(|inputs| inputs.commit_2.map(|C_2| C_2.to_coordinates())), + )?; + + let result = AllocatedPoint::alloc( + cs.namespace(|| "allocate C_1 + r * C_2"), + self + .inputs + .as_ref() + .and_then(|inputs| inputs.result.map(|result| result.to_coordinates())), + )?; + + let scalar = self + .inputs + .as_ref() + .ok_or(SynthesisError::AssignmentMissing)? + .scalar + .iter() + .enumerate() + .map(|(idx, b)| AllocatedBit::alloc(cs.namespace(|| format!("scalar bit {}", idx)), *b)) + .collect::>()?; + + Ok((commit_1, commit_2, result, scalar)) + } + + /// TODO: docs + pub fn synthesize::Base>>( + &self, + mut cs: CS, + ) -> Result, SynthesisError> { + let (C_1, C_2, result, r) = self.alloc_witness(cs.namespace(|| "allocate circuit witness"))?; + + // Calculate C_final + let r_C_2 = C_2.scalar_mul(cs.namespace(|| "r * C_2"), &r)?; + + let C_final = C_1.add(cs.namespace(|| "C_1 + r * C_2"), &r_C_2)?; + + let (res_x, res_y, res_inf) = result.get_coordinates(); + + // enforce C_final = result + cs.enforce( + || "C_final_x = res_x", + |lc| lc + res_x.get_variable(), + |lc| lc + CS::one(), + |lc| lc + C_final.x.get_variable(), + ); + + cs.enforce( + || "C_final_y = res_y", + |lc| lc + res_y.get_variable(), + |lc| lc + CS::one(), + |lc| lc + C_final.y.get_variable(), + ); + + cs.enforce( + || "C_final_inf = res_inf", + |lc| lc + res_inf.get_variable(), + |lc| lc + CS::one(), + |lc| lc + C_final.is_infinity.get_variable(), + ); + + Ok(C_final) + } +} + +#[cfg(test)] +mod tests { + use ff::{Field, PrimeFieldBits}; + use rand_core::OsRng; + + use crate::{ + bellpepper::{solver::SatisfyingAssignment, test_shape_cs::TestShapeCS}, + provider::{ + Bn256Engine, GrumpkinEngine, PallasEngine, Secp256k1Engine, Secq256k1Engine, VestaEngine, + }, + traits::commitment::CommitmentEngineTrait, + }; + + use super::*; + + fn test_cyclefold_circuit_size_with(expected_constraints: usize, expected_vars: usize) + where + E1: Engine::Scalar>, + E2: Engine::Scalar>, + { + let rng = OsRng; + + let ck = <::CE as CommitmentEngineTrait>::setup(b"hi", 5); + + let v1 = (0..5) + .map(|_| ::random(rng)) + .collect::>(); + + let v2 = (0..5) + .map(|_| ::random(rng)) + .collect::>(); + + let C_1 = <::CE as CommitmentEngineTrait>::commit(&ck, &v1); + + let C_2 = <::CE as CommitmentEngineTrait>::commit(&ck, &v2); + + let r = ::random(rng); + + let result = C_1 + C_2 * r; + + let r_bits = r.to_le_bits().iter().map(|b| Some(*b)).collect::>(); + + let inputs: CyclefoldCircuitInputs = + CyclefoldCircuitInputs::new(Some(C_1), Some(C_2), Some(result), r_bits); + + let circuit: CyclefoldCircuit<_> = CyclefoldCircuit::new(Some(inputs)); + + // Test the circuit size does not change + let mut cs: TestShapeCS = TestShapeCS::default(); + let _ = circuit.synthesize(cs.namespace(|| "synthesizing shape")); + + let num_constraints = cs.num_constraints(); + + let num_variables = cs.num_aux(); + + assert_eq!(expected_constraints, num_constraints); + assert_eq!(expected_vars, num_variables); + + // Test the circuit calculation matches weighted sum of commitments + let mut cs = SatisfyingAssignment::::new(); + + let P = circuit + .synthesize(cs.namespace(|| "synthesizing witness")) + .unwrap(); + + let r_C_2 = C_2 * r; + + let C_final = C_1 + r_C_2; + + let P_x = P.x.get_value().unwrap(); + let P_y = P.y.get_value().unwrap(); + let P_infty = P.is_infinity.get_value().unwrap(); + + let (x, y, infty) = C_final.to_coordinates(); + + assert_eq!(x, P_x); + assert_eq!(y, P_y); + assert_eq!(infty, P_infty == ::ONE); + } + + #[test] + fn test_cyclefold_circuit_size() { + test_cyclefold_circuit_size_with::(2735, 2728); + test_cyclefold_circuit_size_with::(2769, 2760); + test_cyclefold_circuit_size_with::(2701, 2696); + } +} diff --git a/src/cyclefold/compressed.rs b/src/cyclefold/compressed.rs new file mode 100644 index 000000000..4e19fd2c9 --- /dev/null +++ b/src/cyclefold/compressed.rs @@ -0,0 +1,2 @@ +//! This module defines the cyclefold `CompressedSNARK` type +//! diff --git a/src/cyclefold/mod.rs b/src/cyclefold/mod.rs new file mode 100644 index 000000000..b21169935 --- /dev/null +++ b/src/cyclefold/mod.rs @@ -0,0 +1,7 @@ +//! This module defines Cyclefold stuff + +mod circuit; +mod nova_circuit; + +pub mod compressed; +pub mod snark; diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs new file mode 100644 index 000000000..de3b01fb2 --- /dev/null +++ b/src/cyclefold/nova_circuit.rs @@ -0,0 +1,174 @@ +//! This module defines the Nova augmented circuit used for Cyclefold + +// FIXME: Make an NIFS instance/proof struct to reduce input and output sizes + +use crate::{ + gadgets::{ + ecc::AllocatedPoint, + r1cs::{AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance}, + }, + r1cs::{R1CSInstance, RelaxedR1CSInstance}, + traits::{circuit::StepCircuit, Engine, ROConstantsCircuit}, + Commitment, +}; + +use abomonation_derive::Abomonation; +use bellpepper::gadgets::num::AllocatedNum; +use bellpepper_core::{ConstraintSystem, SynthesisError}; +use serde::{Deserialize, Serialize}; + +/// TODO: docs +#[derive(Clone, PartialEq, Serialize, Deserialize, Abomonation)] +pub struct AugmentedCircuitParams { + limb_width: usize, + n_limbs: usize, +} + +impl AugmentedCircuitParams { + pub const fn new(limb_width: usize, n_limbs: usize) -> Self { + Self { + limb_width, + n_limbs, + } + } +} + +/// TODO: Docs +#[derive(Debug, Serialize, Deserialize)] +#[serde(bound = "")] +pub struct AugmentedCircuitInputs +where + E1: Engine::Scalar>, + E2: Engine::Scalar>, +{ + pp_digest: E1::Scalar, + i: E1::Base, + z0: Vec, + + zi: Option>, + U_p: Option>, + u_p: Option>, + T_p: Option>, + + U_c: Option>, + u_c_1: Option>, + T_c_1: Option>, + + U_c_1: Option>, + u_c_2: Option>, + T_c_2: Option>, + + E_new: Option>, + W_new: Option>, +} + +impl AugmentedCircuitInputs +where + E1: Engine::Scalar>, + E2: Engine::Scalar>, +{ + pub fn new( + pp_digest: E1::Scalar, + i: E1::Base, + z0: Vec, + zi: Option>, + U_p: Option>, + u_p: Option>, + T_p: Option>, + U_c: Option>, + u_c_1: Option>, + T_c_1: Option>, + U_c_1: Option>, + u_c_2: Option>, + T_c_2: Option>, + E_new: Option>, + W_new: Option>, + ) -> Self { + Self { + pp_digest, + i, + z0, + zi, + U_p, + u_p, + T_p, + U_c, + u_c_1, + T_c_1, + U_c_1, + u_c_2, + T_c_2, + E_new, + W_new, + } + } +} + +pub struct AugmentedCircuit<'a, E1, E2, SC> +where + E1: Engine::Scalar>, + E2: Engine::Scalar>, + SC: StepCircuit, +{ + params: &'a AugmentedCircuitParams, + ro_consts: ROConstantsCircuit, + inputs: Option>, + step_circuit: &'a SC, +} + +impl<'a, E1, E2, SC> AugmentedCircuit<'a, E1, E2, SC> +where + E1: Engine::Scalar>, + E2: Engine::Scalar>, + SC: StepCircuit, +{ + pub const fn new( + params: &'a AugmentedCircuitParams, + ro_consts: ROConstantsCircuit, + inputs: Option>, + step_circuit: &'a SC, + ) -> Self { + Self { + params, + ro_consts, + inputs, + step_circuit, + } + } + + // FIXME: Need to actually figure out how to encode wrong-field points and instances + fn alloc_witness::Base>>( + &self, + mut cs: CS, + arity: usize, + ) -> Result< + ( + AllocatedNum, // pp_digest + AllocatedNum, // i + Vec>, // z0 + Vec>, // zi + AllocatedRelaxedR1CSInstance, // U_p + AllocatedR1CSInstance, // u_p + AllocatedPoint, // T_p + AllocatedRelaxedR1CSInstance, // U_c + AllocatedR1CSInstance, // u_c_1 + AllocatedPoint, // T_c_1 + AllocatedRelaxedR1CSInstance, // U_c_1 + AllocatedR1CSInstance, // u_c_2 + AllocatedPoint, // T_c_2 + AllocatedPoint, // E_new + AllocatedPoint, // W_new + ), + SynthesisError, + > { + todo!() + } + + fn synthesize::Base>>( + self, + cs: &mut CS, + ) -> Result>, SynthesisError> { + // TODO: It's written down here https://hackmd.io/@mpenciak/HybHrnNFT + todo!() + } +} diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs new file mode 100644 index 000000000..db0366874 --- /dev/null +++ b/src/cyclefold/snark.rs @@ -0,0 +1,271 @@ +//! This module defines the Cyclefold `RecursiveSNARK` type +//! + +use std::marker::PhantomData; + +use crate::{ + bellpepper::{r1cs::NovaWitness, solver::SatisfyingAssignment}, + cyclefold::circuit::{CyclefoldCircuit, CyclefoldCircuitInputs}, + errors::NovaError, + gadgets::utils::scalar_as_base, + nifs::NIFS, + r1cs::{CommitmentKeyHint, R1CSInstance, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness}, + traits::{circuit::StepCircuit, commitment::CommitmentTrait, Engine, ROConstantsCircuit}, + Commitment, CommitmentKey, DigestComputer, R1CSWithArity, ROConstants, ResourceBuffer, + SimpleDigestible, +}; + +use super::nova_circuit::{AugmentedCircuit, AugmentedCircuitInputs, AugmentedCircuitParams}; + +use abomonation::Abomonation; +use abomonation_derive::Abomonation; +use ff::{PrimeField, PrimeFieldBits}; +use once_cell::sync::OnceCell; +use serde::{Deserialize, Serialize}; + +/// TODO: docs +#[derive(Clone, PartialEq, Serialize, Deserialize, Abomonation)] +#[serde(bound = "")] +#[abomonation_bounds( +where + E1: Engine::Scalar>, + E2: Engine::Scalar>, + C1: StepCircuit, + ::Repr: Abomonation, + ::Repr: Abomonation, +)] +pub struct PublicParams +where + E1: Engine::Scalar>, + E2: Engine::Scalar>, + C1: StepCircuit, +{ + F_arity_primary: usize, + ro_consts_primary: ROConstants, + ro_consts_circuit_primary: ROConstantsCircuit, + ck_primary: CommitmentKey, + circuit_shape_primary: R1CSWithArity, + augmented_circuit_params_primary: AugmentedCircuitParams, + + ro_consts_cyclefold: ROConstants, + ck_cyclefold: CommitmentKey, + circuit_shape_cyclefold: R1CSWithArity, + #[abomonation_skip] + #[serde(skip, default = "OnceCell::new")] + digest: OnceCell, + _p: PhantomData<(E1, E2, C1)>, +} + +impl PublicParams +where + E1: Engine::Scalar>, + E2: Engine::Scalar>, + C1: StepCircuit, +{ + /// TODO: docs + pub fn setup( + _c_primary: &C1, + _ck_hint1: &CommitmentKeyHint, + _ck_hint_cyclefold: &CommitmentKeyHint, + ) -> Self { + todo!() + } + + /// TODO: docs + pub fn digest(&self) -> E1::Scalar { + self + .digest + .get_or_try_init(|| DigestComputer::new(self).digest()) + .cloned() + .expect("Failure in retrieving digest") + } + + /// TODO: docs + pub const fn num_constraints(&self) -> usize { + todo!() + } + + /// TODO: docs + pub const fn num_variables(&self) -> usize { + todo!() + } +} + +impl SimpleDigestible for PublicParams +where + E1: Engine::Scalar>, + E2: Engine::Scalar>, + C1: StepCircuit, +{ +} + +/// TODO: docs +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(bound = "")] +pub struct RecursiveSNARK +where + E1: Engine::Scalar>, + E2: Engine::Scalar>, + C1: StepCircuit, +{ + // Input + z0_primary: Vec, + + // primary circuit data + r_W_primary: RelaxedR1CSWitness, + r_U_primary: RelaxedR1CSInstance, + l_w_primary: R1CSWitness, + l_u_primary: R1CSInstance, + + // cyclefold circuit data + r_W_cyclefold: RelaxedR1CSWitness, + r_U_cyclefold: RelaxedR1CSInstance, + + // memory buffers for folding steps + buffer_primary: ResourceBuffer, + buffer_cyclefold: ResourceBuffer, + + i: usize, + zi_primary: Vec, + + _p: PhantomData, +} + +impl RecursiveSNARK +where + E1: Engine::Scalar>, + E2: Engine::Scalar>, + C1: StepCircuit, +{ + /// TODO: docs + pub fn new( + _pp: &PublicParams, + _c_primary: &C1, + _z0_primary: &[E1::Scalar], + ) -> Result { + todo!() + } + + /// TODO: docs + pub fn prove_step( + &mut self, + pp: &PublicParams, + c_primary: &C1, + ) -> Result<(), NovaError> { + if self.i == 0 { + self.i = 1; + return Ok(()); + } + + let (nifs_primary, r) = NIFS::prove_mut( + &pp.ck_primary, + &pp.ro_consts_primary, + &pp.digest(), + &pp.circuit_shape_primary.r1cs_shape, + &mut self.r_U_primary, + &mut self.r_W_primary, + &self.l_u_primary, + &self.l_w_primary, + &mut self.buffer_primary.T, + &mut self.buffer_primary.ABC_Z_1, + &mut self.buffer_primary.ABC_Z_2, + )?; + + let r_bools = r.to_le_bits().iter().map(|b| Some(*b)).collect::>(); + + let comm_T = Commitment::::decompress(&nifs_primary.comm_T)?; + let E_new = self.r_U_primary.comm_E + comm_T * r; + + let W_new = self.l_u_primary.comm_W + self.r_U_primary.comm_W * r; + + let mut cs_cyclefold_E = SatisfyingAssignment::::with_capacity( + pp.circuit_shape_cyclefold.r1cs_shape.num_io + 1, + pp.circuit_shape_cyclefold.r1cs_shape.num_vars, + ); + + let inputs_cyclefold_E: CyclefoldCircuitInputs = CyclefoldCircuitInputs::new( + Some(self.r_U_primary.comm_E), + Some(comm_T), + Some(E_new), + r_bools.clone(), + ); + + let circuit_cyclefold_E: CyclefoldCircuit = CyclefoldCircuit::new(Some(inputs_cyclefold_E)); + + let _output_cyclefold_E = circuit_cyclefold_E.synthesize(&mut cs_cyclefold_E); + + let (l_u_cyclefold_E, l_w_cyclefold_E) = cs_cyclefold_E + .r1cs_instance_and_witness(&pp.circuit_shape_cyclefold.r1cs_shape, &pp.ck_cyclefold) + .map_err(|_| NovaError::UnSat)?; + + let (nifs_cyclefold_E, (r_U_cyclefold_E, r_W_cyclefold_E), _) = NIFS::prove( + &pp.ck_cyclefold, + &pp.ro_consts_cyclefold, + &scalar_as_base::(pp.digest()), + &pp.circuit_shape_cyclefold.r1cs_shape, + &self.r_U_cyclefold, + &self.r_W_cyclefold, + &l_u_cyclefold_E, + &l_w_cyclefold_E, + )?; + + let comm_T_E = Commitment::::decompress(&nifs_cyclefold_E.comm_T)?; + + let mut cs_cyclefold_W = SatisfyingAssignment::::with_capacity( + pp.circuit_shape_cyclefold.r1cs_shape.num_io + 1, + pp.circuit_shape_cyclefold.r1cs_shape.num_vars, + ); + + let inputs_cyclefold_W: CyclefoldCircuitInputs = CyclefoldCircuitInputs::new( + Some(self.r_U_primary.comm_W), + Some(self.l_u_primary.comm_W), + Some(W_new), + r_bools, + ); + + let circuit_cyclefold_W: CyclefoldCircuit = CyclefoldCircuit::new(Some(inputs_cyclefold_W)); + + let _output_cyclefold_W = circuit_cyclefold_W.synthesize(&mut cs_cyclefold_W); + + let (l_u_cyclefold_W, l_w_cyclefold_W) = cs_cyclefold_W + .r1cs_instance_and_witness(&pp.circuit_shape_cyclefold.r1cs_shape, &pp.ck_cyclefold) + .map_err(|_| NovaError::UnSat)?; + + let (nifs_cyclefold_E, (r_U_cyclefold_W, r_W_cyclefold_W), _) = NIFS::prove( + &pp.ck_cyclefold, + &pp.ro_consts_cyclefold, + &scalar_as_base::(pp.digest()), + &pp.circuit_shape_cyclefold.r1cs_shape, + &r_U_cyclefold_E, + &r_W_cyclefold_E, + &l_u_cyclefold_W, + &l_w_cyclefold_W, + )?; + + let comm_T_W = Commitment::::decompress(&nifs_cyclefold_E.comm_T)?; + + let mut cs_primary = SatisfyingAssignment::::with_capacity( + pp.circuit_shape_primary.r1cs_shape.num_io + 1, + pp.circuit_shape_primary.r1cs_shape.num_vars, + ); + + // TODO: Finish this method + + todo!() + } + + /// TODO: docs + pub fn verify( + &self, + _pp: &PublicParams, + _num_steps: usize, + _z0_primary: &[E1::Scalar], + ) -> Result, NovaError> { + todo!() + } +} + +#[cfg(test)] +mod test { + // use super::*; +} diff --git a/src/lib.rs b/src/lib.rs index 1b53e0552..db41e3b36 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,9 +25,12 @@ pub mod r1cs; pub mod spartan; pub mod traits; +pub mod cyclefold; pub mod supernova; use once_cell::sync::OnceCell; +use traits::circuit::TrivialCircuit; +use traits::recursive::RecursiveSNARKTrait; use traits::{CurveCycleEquipped, Dual}; use crate::digest::{DigestComputer, SimpleDigestible}; @@ -538,7 +541,7 @@ where let l_u_secondary_i = self.l_u_secondary.clone(); // fold the secondary circuit's instance - let nifs_secondary = NIFS::prove_mut( + let (nifs_secondary, _) = NIFS::prove_mut( &*pp.ck_secondary, &pp.ro_consts_secondary, &scalar_as_base::(pp.digest()), @@ -579,7 +582,7 @@ where cs_primary.r1cs_instance_and_witness(&pp.circuit_shape_primary.r1cs_shape, &pp.ck_primary)?; // fold the primary circuit's instance - let nifs_primary = NIFS::prove_mut( + let (nifs_primary, _) = NIFS::prove_mut( &*pp.ck_primary, &pp.ro_consts_primary, &pp.digest(), @@ -757,6 +760,92 @@ where } } +impl RecursiveSNARKTrait + for RecursiveSNARK> +where + E1: Engine::Scalar>, + E2: Engine::Scalar>, + C: StepCircuit, +{ + type PublicParams = PublicParams>; + + type Proof = Self; + + fn new( + pp: &Self::PublicParams, + c_primary: &C, + z0_primary: &[::Scalar], + ) -> Result { + let c_secondary = TrivialCircuit::<::Scalar>::default(); + let z0_secondary = vec![::Scalar::ZERO; pp.F_arity_secondary]; + + Self::Proof::new(&pp, c_primary, &c_secondary, z0_primary, &z0_secondary) + } + + fn prove_step( + proof: &mut Self::Proof, + pp: &Self::PublicParams, + c_primary: &C, + ) -> Result<(), NovaError> { + let c_secondary = TrivialCircuit::default(); + proof.prove_step(&pp, c_primary, &c_secondary) + } + + fn verify( + proof: &Self::Proof, + pp: &Self::PublicParams, + z0_primary: &[::Scalar], + ) -> Result::Scalar>, NovaError> { + let z0_secondary = vec![::Scalar::ZERO; pp.F_arity_secondary]; + proof + .verify(&pp, proof.num_steps(), z0_primary, &z0_secondary) + .map(|(zi_primary, _)| zi_primary) + } +} + +impl RecursiveSNARKTrait + for RecursiveSNARK> +where + E1: Engine::Scalar>, + E2: Engine::Scalar>, + C: StepCircuit, +{ + type PublicParams = PublicParams>; + + type Proof = Self; + + fn new( + pp: &Self::PublicParams, + c_primary: &C, + z0_primary: &[::Scalar], + ) -> Result { + let c_secondary = TrivialCircuit::<::Scalar>::default(); + let z0_secondary = vec![::Scalar::ZERO; pp.F_arity_secondary]; + + Self::Proof::new(&pp, c_primary, &c_secondary, z0_primary, &z0_secondary) + } + + fn prove_step( + proof: &mut Self::Proof, + pp: &Self::PublicParams, + c_primary: &C, + ) -> Result<(), NovaError> { + let c_secondary = TrivialCircuit::default(); + proof.prove_step(&pp, c_primary, &c_secondary) + } + + fn verify( + proof: &Self::Proof, + pp: &Self::PublicParams, + z0_primary: &[::Scalar], + ) -> Result::Scalar>, NovaError> { + let z0_secondary = vec![::Scalar::ZERO; pp.F_arity_secondary]; + proof + .verify(&pp, proof.num_steps(), z0_primary, &z0_secondary) + .map(|(zi_primary, _)| zi_primary) + } +} + /// A type that holds the prover key for `CompressedSNARK` #[derive(Clone, Debug)] pub struct ProverKey @@ -850,7 +939,7 @@ where recursive_snark: &RecursiveSNARK, ) -> Result { // fold the secondary circuit's instance with its running instance - let (nifs_secondary, (f_U_secondary, f_W_secondary)) = NIFS::prove( + let (nifs_secondary, (f_U_secondary, f_W_secondary), _) = NIFS::prove( &*pp.ck_secondary, &pp.ro_consts_secondary, &scalar_as_base::(pp.digest()), diff --git a/src/nifs.rs b/src/nifs.rs index 566397587..9f7bc958a 100644 --- a/src/nifs.rs +++ b/src/nifs.rs @@ -48,7 +48,14 @@ impl NIFS { W1: &RelaxedR1CSWitness, U2: &R1CSInstance, W2: &R1CSWitness, - ) -> Result<(Self, (RelaxedR1CSInstance, RelaxedR1CSWitness)), NovaError> { + ) -> Result< + ( + Self, + (RelaxedR1CSInstance, RelaxedR1CSWitness), + E::Scalar, + ), + NovaError, + > { // initialize a new RO let mut ro = E::RO::new(ro_consts.clone(), NUM_FE_FOR_RO); @@ -79,6 +86,7 @@ impl NIFS { comm_T: comm_T.compress(), }, (U, W), + r, )) } @@ -101,7 +109,7 @@ impl NIFS { T: &mut Vec, ABC_Z_1: &mut R1CSResult, ABC_Z_2: &mut R1CSResult, - ) -> Result { + ) -> Result<(Self, E::Scalar), NovaError> { // initialize a new RO let mut ro = E::RO::new(ro_consts.clone(), NUM_FE_FOR_RO); @@ -127,9 +135,12 @@ impl NIFS { W1.fold_mut(W2, T, &r)?; // return the commitment - Ok(Self { - comm_T: comm_T.compress(), - }) + Ok(( + Self { + comm_T: comm_T.compress(), + }, + r, + )) } /// Takes as input a relaxed R1CS instance `U1` and R1CS instance `U2` @@ -279,7 +290,7 @@ mod tests { // produce a step SNARK with (W1, U1) as the first incoming witness-instance pair let res = NIFS::prove(ck, ro_consts, pp_digest, shape, &r_U, &r_W, U1, W1); assert!(res.is_ok()); - let (nifs, (_U, W)) = res.unwrap(); + let (nifs, (_U, W), _) = res.unwrap(); // verify the step SNARK with U1 as the first incoming instance let res = nifs.verify(ro_consts, pp_digest, &r_U, U1); @@ -295,7 +306,7 @@ mod tests { // produce a step SNARK with (W2, U2) as the second incoming witness-instance pair let res = NIFS::prove(ck, ro_consts, pp_digest, shape, &r_U, &r_W, U2, W2); assert!(res.is_ok()); - let (nifs, (_U, W)) = res.unwrap(); + let (nifs, (_U, W), _) = res.unwrap(); // verify the step SNARK with U1 as the first incoming instance let res = nifs.verify(ro_consts, pp_digest, &r_U, U2); diff --git a/src/supernova/mod.rs b/src/supernova/mod.rs index 26e9414d6..4efd0b7a2 100644 --- a/src/supernova/mod.rs +++ b/src/supernova/mod.rs @@ -14,6 +14,7 @@ use crate::{ scalar_as_base, traits::{ commitment::{CommitmentEngineTrait, CommitmentTrait}, + recursive::RecursiveSNARKTrait, AbsorbInROTrait, CurveCycleEquipped, Dual, Engine, ROConstants, ROConstantsCircuit, ROTrait, }, Commitment, CommitmentKey, R1CSWithArity, @@ -749,7 +750,7 @@ where assert_eq!(self.program_counter, E1::Scalar::from(circuit_index as u64)); // fold the secondary circuit's instance - let nifs_secondary = NIFS::prove_mut( + let (nifs_secondary, _) = NIFS::prove_mut( &*pp.ck_secondary, &pp.ro_consts_secondary, &scalar_as_base::(self.pp_digest), @@ -823,7 +824,7 @@ where ) }; - let nifs_primary = NIFS::prove_mut( + let (nifs_primary, _) = NIFS::prove_mut( &*pp.ck_primary, &pp.ro_consts_primary, &self.pp_digest, @@ -1112,6 +1113,136 @@ where } } +/// TODO: docs +pub struct PublicParamsWithNonUniformCircuit +where + E1: Engine::Scalar>, + E2: Engine::Scalar>, + C0: NonUniformCircuit, + C1: StepCircuit, + C2: StepCircuit, +{ + pp: PublicParams, + non_uniform_circuit: C0, +} + +impl RecursiveSNARKTrait for RecursiveSNARK +where + E1: Engine::Scalar>, + E2: Engine::Scalar>, + C: NonUniformCircuit>, + C: StepCircuit, +{ + type PublicParams = + PublicParamsWithNonUniformCircuit>; + + type Proof = Self; + + fn new( + pp: &Self::PublicParams, + c_primary: &C, + z0_primary: &[::Scalar], + ) -> Result { + let c_secondary = TrivialSecondaryCircuit::default(); + let z0_secondary = vec![E2::Scalar::ZERO; pp.pp.circuit_shape_secondary.F_arity]; + Self::Proof::new( + &pp.pp, + &pp.non_uniform_circuit, + c_primary, + &c_secondary, + z0_primary, + &z0_secondary, + ) + .map_err(|_| NovaError::InternalError) // TODO: better error + } + + fn prove_step( + proof: &mut Self::Proof, + pp: &Self::PublicParams, + c_primary: &C, + ) -> Result<(), NovaError> { + proof + .prove_step(&pp.pp, c_primary, &TrivialSecondaryCircuit::default()) + .map_err(|_| NovaError::InternalError) // TODO: better error + } + + fn verify( + proof: &Self::Proof, + pp: &Self::PublicParams, + z0_primary: &[::Scalar], + ) -> Result::Scalar>, NovaError> { + proof + .verify(&pp.pp, z0_primary, &vec![E2::Scalar::ZERO]) + .map(|(zi_primary, _)| zi_primary) + .map_err(|_| NovaError::InternalError) // TODO: better error + } +} + +/// TODO: docs +pub struct PublicParamsWithNonUniformCircuit +where + E1: Engine::Scalar>, + E2: Engine::Scalar>, + C0: NonUniformCircuit, + C1: StepCircuit, + C2: StepCircuit, +{ + pp: PublicParams, + non_uniform_circuit: C0, +} + +impl RecursiveSNARKTrait for RecursiveSNARK +where + E1: Engine::Scalar>, + E2: Engine::Scalar>, + C: NonUniformCircuit>, + C: StepCircuit, +{ + type PublicParams = + PublicParamsWithNonUniformCircuit>; + + type Proof = Self; + + fn new( + pp: &Self::PublicParams, + c_primary: &C, + z0_primary: &[::Scalar], + ) -> Result { + let c_secondary = TrivialSecondaryCircuit::default(); + let z0_secondary = vec![E2::Scalar::ZERO; pp.pp.circuit_shape_secondary.F_arity]; + Self::Proof::new( + &pp.pp, + &pp.non_uniform_circuit, + c_primary, + &c_secondary, + z0_primary, + &z0_secondary, + ) + .map_err(|_| NovaError::InternalError) // TODO: better error + } + + fn prove_step( + proof: &mut Self::Proof, + pp: &Self::PublicParams, + c_primary: &C, + ) -> Result<(), NovaError> { + proof + .prove_step(&pp.pp, c_primary, &TrivialSecondaryCircuit::default()) + .map_err(|_| NovaError::InternalError) // TODO: better error + } + + fn verify( + proof: &Self::Proof, + pp: &Self::PublicParams, + z0_primary: &[::Scalar], + ) -> Result::Scalar>, NovaError> { + proof + .verify(&pp.pp, z0_primary, &vec![E2::Scalar::ZERO]) + .map(|(zi_primary, _)| zi_primary) + .map_err(|_| NovaError::InternalError) // TODO: better error + } +} + /// SuperNova helper trait, for implementors that provide sets of sub-circuits to be proved via NIVC. `C1` must be a /// type (likely an `Enum`) for which a potentially-distinct instance can be supplied for each `index` below /// `self.num_circuits()`. diff --git a/src/supernova/snark.rs b/src/supernova/snark.rs index cba4ad6eb..5574c902f 100644 --- a/src/supernova/snark.rs +++ b/src/supernova/snark.rs @@ -109,7 +109,7 @@ where &recursive_snark.l_w_secondary, ); - let (nifs_secondary, (f_U_secondary, f_W_secondary)) = res_secondary?; + let (nifs_secondary, (f_U_secondary, f_W_secondary), _) = res_secondary?; // Prepare the list of primary Relaxed R1CS instances (a default instance is provided for // uninitialized circuits) diff --git a/src/traits/mod.rs b/src/traits/mod.rs index 1786f734f..23e8629c4 100644 --- a/src/traits/mod.rs +++ b/src/traits/mod.rs @@ -172,4 +172,5 @@ impl> TranscriptReprTrait for &[T] { pub mod circuit; pub mod evaluation; +pub mod recursive; pub mod snark; diff --git a/src/traits/recursive.rs b/src/traits/recursive.rs new file mode 100644 index 000000000..30f5f76ca --- /dev/null +++ b/src/traits/recursive.rs @@ -0,0 +1,42 @@ +//! This module defines the `RecursiveSNARKTrait` to abstract over the different implementations of +//! RecursiveSNARK +//! +//! NOTE: This trait is probably going to be changed in the future, it's a first guess on what it +//! should look like. +//! + +use crate::{errors::NovaError, traits::Engine}; + +/// TODO: docs +pub trait RecursiveSNARKTrait +where + E1: Engine::Scalar>, + E2: Engine::Scalar>, +{ + /// TODO: docs + type PublicParams; + + /// TODO: docs + type Proof; // FIXME: This shouldn't have to be here, but size of `Self` not known at compile time + + /// TODO: docs + fn new( + pp: &Self::PublicParams, + c_primary: &C, + z0_primary: &[E1::Scalar], + ) -> Result; + + /// TODO: docs + fn prove_step( + proof: &mut Self::Proof, + pp: &Self::PublicParams, + c_primary: &C, + ) -> Result<(), NovaError>; + + /// TODO: docs + fn verify( + proof: &Self::Proof, + pp: &Self::PublicParams, + z0_primary: &[E1::Scalar], + ) -> Result, NovaError>; +} From 3f1c2bcf409815d93b65c8dc9b601169f8e3e418 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Fri, 19 Jan 2024 13:55:15 -0500 Subject: [PATCH 02/77] (wip) scalar <-> base confusion --- src/cyclefold/nova_circuit.rs | 75 ++++++++++++++++------------------- src/cyclefold/snark.rs | 48 ++++++++++++++++++++-- 2 files changed, 80 insertions(+), 43 deletions(-) diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index de3b01fb2..9fdc9d722 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -33,6 +33,20 @@ impl AugmentedCircuitParams { } } +#[derive(Debug, Serialize, Deserialize)] +#[serde(bound = "")] +pub struct FoldingData { + U: RelaxedR1CSInstance, + u: R1CSInstance, + T: Commitment, +} + +impl FoldingData { + pub fn new(U: RelaxedR1CSInstance, u: R1CSInstance, T: Commitment) -> Self { + Self { U, u, T } + } +} + /// TODO: Docs #[derive(Debug, Serialize, Deserialize)] #[serde(bound = "")] @@ -43,23 +57,16 @@ where { pp_digest: E1::Scalar, i: E1::Base, - z0: Vec, - - zi: Option>, - U_p: Option>, - u_p: Option>, - T_p: Option>, + z0: Vec, - U_c: Option>, - u_c_1: Option>, - T_c_1: Option>, + zi: Option>, + data_p: Option>, - U_c_1: Option>, - u_c_2: Option>, - T_c_2: Option>, + data_c_1: Option>, + data_c_2: Option>, - E_new: Option>, - W_new: Option>, + E_new: Option>, + W_new: Option>, } impl AugmentedCircuitInputs @@ -70,34 +77,22 @@ where pub fn new( pp_digest: E1::Scalar, i: E1::Base, - z0: Vec, - zi: Option>, - U_p: Option>, - u_p: Option>, - T_p: Option>, - U_c: Option>, - u_c_1: Option>, - T_c_1: Option>, - U_c_1: Option>, - u_c_2: Option>, - T_c_2: Option>, - E_new: Option>, - W_new: Option>, + z0: Vec, + zi: Option>, + data_p: Option>, + data_c_1: Option>, + data_c_2: Option>, + E_new: Option>, + W_new: Option>, ) -> Self { Self { pp_digest, i, z0, zi, - U_p, - u_p, - T_p, - U_c, - u_c_1, - T_c_1, - U_c_1, - u_c_2, - T_c_2, + data_p, + data_c_1, + data_c_2, E_new, W_new, } @@ -108,7 +103,7 @@ pub struct AugmentedCircuit<'a, E1, E2, SC> where E1: Engine::Scalar>, E2: Engine::Scalar>, - SC: StepCircuit, + SC: StepCircuit, { params: &'a AugmentedCircuitParams, ro_consts: ROConstantsCircuit, @@ -120,7 +115,7 @@ impl<'a, E1, E2, SC> AugmentedCircuit<'a, E1, E2, SC> where E1: Engine::Scalar>, E2: Engine::Scalar>, - SC: StepCircuit, + SC: StepCircuit, { pub const fn new( params: &'a AugmentedCircuitParams, @@ -164,10 +159,10 @@ where todo!() } - fn synthesize::Base>>( + pub fn synthesize::Base>>( self, cs: &mut CS, - ) -> Result>, SynthesisError> { + ) -> Result>, SynthesisError> { // TODO: It's written down here https://hackmd.io/@mpenciak/HybHrnNFT todo!() } diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index db0366874..21bfdd91d 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -15,10 +15,13 @@ use crate::{ SimpleDigestible, }; -use super::nova_circuit::{AugmentedCircuit, AugmentedCircuitInputs, AugmentedCircuitParams}; +use super::nova_circuit::{ + AugmentedCircuit, AugmentedCircuitInputs, AugmentedCircuitParams, FoldingData, +}; use abomonation::Abomonation; use abomonation_derive::Abomonation; +use bellpepper_core::SynthesisError; use ff::{PrimeField, PrimeFieldBits}; use once_cell::sync::OnceCell; use serde::{Deserialize, Serialize}; @@ -249,9 +252,48 @@ where pp.circuit_shape_primary.r1cs_shape.num_vars, ); - // TODO: Finish this method + let data_p = FoldingData::new(self.r_U_primary.clone(), self.l_u_primary.clone(), comm_T); + let data_c_E = FoldingData::new(r_U_cyclefold_E, l_u_cyclefold_E, comm_T_E); + let data_c_W = FoldingData::new(r_U_cyclefold_W, l_u_cyclefold_W, comm_T_W); + + let inputs_primary = AugmentedCircuitInputs::new( + pp.digest(), + ::Scalar::from(self.i as u64), + self.z0_primary.clone(), + Some(self.zi_primary.clone()), + Some(data_p), + Some(data_c_E), + Some(data_c_W), + Some(E_new), + Some(W_new), + ); - todo!() + let circuit_primary: AugmentedCircuit<'_, E2, E1, C1> = AugmentedCircuit::new( + &pp.augmented_circuit_params_primary, + pp.ro_consts_circuit_primary.clone(), + Some(inputs_primary), + c_primary, + ); + + let zi_primary = circuit_primary.synthesize(&mut cs_primary)?; + + let (l_u_primary, l_w_primary) = cs_primary + .r1cs_instance_and_witness(&pp.circuit_shape_primary.r1cs_shape, &pp.ck_primary) + .map_err(|_| NovaError::UnSat)?; + + self.zi_primary = zi_primary + .iter() + .map(|v| v.get_value().ok_or(SynthesisError::AssignmentMissing)) + .collect::, _>>()?; + + self.l_u_primary = l_u_primary; + self.l_w_primary = l_w_primary; + self.r_U_cyclefold = r_U_cyclefold_W; + self.r_W_cyclefold = r_W_cyclefold_W; + + self.i += 1; + + Ok(()) } /// TODO: docs From 24eb5991d4b297fb31a47779da20e58629c8a253 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Mon, 22 Jan 2024 13:09:34 -0500 Subject: [PATCH 03/77] (wip) fix W_new def --- src/cyclefold/snark.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 21bfdd91d..566a75972 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -179,7 +179,7 @@ where let comm_T = Commitment::::decompress(&nifs_primary.comm_T)?; let E_new = self.r_U_primary.comm_E + comm_T * r; - let W_new = self.l_u_primary.comm_W + self.r_U_primary.comm_W * r; + let W_new = self.r_U_primary.comm_W + self.l_u_primary.comm_W * r; let mut cs_cyclefold_E = SatisfyingAssignment::::with_capacity( pp.circuit_shape_cyclefold.r1cs_shape.num_io + 1, From 9b0c6a93e66e97573295b8971285caca97dd6669 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Tue, 23 Jan 2024 12:23:41 -0500 Subject: [PATCH 04/77] (wip) fixed base <-> scalar confusion --- src/cyclefold/nova_circuit.rs | 37 ++++++++++++++++------------------- src/cyclefold/snark.rs | 8 ++++---- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index 9fdc9d722..c3fb04c9c 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -17,7 +17,6 @@ use bellpepper::gadgets::num::AllocatedNum; use bellpepper_core::{ConstraintSystem, SynthesisError}; use serde::{Deserialize, Serialize}; -/// TODO: docs #[derive(Clone, PartialEq, Serialize, Deserialize, Abomonation)] pub struct AugmentedCircuitParams { limb_width: usize, @@ -47,7 +46,6 @@ impl FoldingData { } } -/// TODO: Docs #[derive(Debug, Serialize, Deserialize)] #[serde(bound = "")] pub struct AugmentedCircuitInputs @@ -57,16 +55,16 @@ where { pp_digest: E1::Scalar, i: E1::Base, - z0: Vec, + z0: Vec, - zi: Option>, - data_p: Option>, + zi: Option>, + data_p: Option>, - data_c_1: Option>, - data_c_2: Option>, + data_c_1: Option>, + data_c_2: Option>, - E_new: Option>, - W_new: Option>, + E_new: Option>, + W_new: Option>, } impl AugmentedCircuitInputs @@ -77,13 +75,13 @@ where pub fn new( pp_digest: E1::Scalar, i: E1::Base, - z0: Vec, - zi: Option>, - data_p: Option>, - data_c_1: Option>, - data_c_2: Option>, - E_new: Option>, - W_new: Option>, + z0: Vec, + zi: Option>, + data_p: Option>, + data_c_1: Option>, + data_c_2: Option>, + E_new: Option>, + W_new: Option>, ) -> Self { Self { pp_digest, @@ -98,12 +96,11 @@ where } } } - pub struct AugmentedCircuit<'a, E1, E2, SC> where E1: Engine::Scalar>, E2: Engine::Scalar>, - SC: StepCircuit, + SC: StepCircuit, { params: &'a AugmentedCircuitParams, ro_consts: ROConstantsCircuit, @@ -115,7 +112,7 @@ impl<'a, E1, E2, SC> AugmentedCircuit<'a, E1, E2, SC> where E1: Engine::Scalar>, E2: Engine::Scalar>, - SC: StepCircuit, + SC: StepCircuit, { pub const fn new( params: &'a AugmentedCircuitParams, @@ -162,7 +159,7 @@ where pub fn synthesize::Base>>( self, cs: &mut CS, - ) -> Result>, SynthesisError> { + ) -> Result>, SynthesisError> { // TODO: It's written down here https://hackmd.io/@mpenciak/HybHrnNFT todo!() } diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 566a75972..f764fdae9 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -254,11 +254,11 @@ where let data_p = FoldingData::new(self.r_U_primary.clone(), self.l_u_primary.clone(), comm_T); let data_c_E = FoldingData::new(r_U_cyclefold_E, l_u_cyclefold_E, comm_T_E); - let data_c_W = FoldingData::new(r_U_cyclefold_W, l_u_cyclefold_W, comm_T_W); + let data_c_W = FoldingData::new(r_U_cyclefold_W.clone(), l_u_cyclefold_W, comm_T_W); - let inputs_primary = AugmentedCircuitInputs::new( - pp.digest(), - ::Scalar::from(self.i as u64), + let inputs_primary: AugmentedCircuitInputs = AugmentedCircuitInputs::new( + scalar_as_base::(pp.digest()), + ::Base::from(self.i as u64), self.z0_primary.clone(), Some(self.zi_primary.clone()), Some(data_p), From 49fe8e44461468f532caef7bd85c43e874c24a4a Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Wed, 24 Jan 2024 00:11:07 -0500 Subject: [PATCH 05/77] (wip) new gadget scaffolding and allocated witnesses --- src/cyclefold/gadgets.rs | 108 ++++++++++++++++++++++++++++ src/cyclefold/mod.rs | 1 + src/cyclefold/nova_circuit.rs | 130 ++++++++++++++++++++++++++++------ 3 files changed, 218 insertions(+), 21 deletions(-) create mode 100644 src/cyclefold/gadgets.rs diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs new file mode 100644 index 000000000..6ce7b6572 --- /dev/null +++ b/src/cyclefold/gadgets.rs @@ -0,0 +1,108 @@ +use super::nova_circuit::FoldingData; + +use crate::{ + gadgets::{ + ecc::AllocatedPoint, + r1cs::{AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance}, + }, + traits::Engine, +}; + +use bellpepper_core::{ConstraintSystem, SynthesisError}; +pub struct AllocatedFoldingData { + U: AllocatedRelaxedR1CSInstance, + u: AllocatedR1CSInstance, + T: AllocatedPoint, +} + +impl AllocatedFoldingData { + pub fn alloc::Base>>( + mut cs: CS, + inst: Option<&FoldingData>, + ) -> Result { + todo!() + } + + pub fn absorb_in_ro(&self, ro: &mut E::ROCircuit) { + todo!() + } +} + +pub mod emulated { + use std::marker::PhantomData; + + use super::*; + use crate::gadgets::nonnative::bignat::BigNat; + + pub struct AllocatedPoint + where + E1: Engine::Scalar>, + E2: Engine::Scalar>, + { + x: BigNat, + y: BigNat, + is_infinity: BigNat, + _p: PhantomData, + } + + impl AllocatedPoint + where + E1: Engine::Scalar>, + E2: Engine::Scalar>, + { + pub fn alloc( + mut cs: CS, + coords: Option<(E2::Base, E2::Base, bool)>, + ) -> Result + where + CS: ConstraintSystem<::Base>, + { + todo!() + } + + pub fn absorb_in_ro(&self, ro: &mut E1::ROCircuit) { + todo!() + } + + pub fn check_on_curve(&self, mut cs: CS) -> Result<(), SynthesisError> + where + CS: ConstraintSystem<::Base>, + { + todo!() + } + } + + pub struct AllocatedFoldingData + where + E1: Engine::Scalar>, + E2: Engine::Scalar>, + { + U_W: AllocatedPoint, + U_E: AllocatedPoint, + U_u: BigNat, + U_X0: BigNat, + U_X1: BigNat, + u_W: AllocatedPoint, + u_x0: BigNat, + u_x1: BigNat, + T: AllocatedPoint, + _p: PhantomData, + } + + impl AllocatedFoldingData + where + E1: Engine::Scalar>, + E2: Engine::Scalar>, + { + pub fn alloc(mut cs: CS, inst: Option<&FoldingData>) -> Result + where + CS: ConstraintSystem<::Base>, + { + todo!() + } + + pub fn absorb_in_ro(&self, ro: &mut E1::ROCircuit) { + todo!() + } + } +} diff --git a/src/cyclefold/mod.rs b/src/cyclefold/mod.rs index b21169935..b515df75b 100644 --- a/src/cyclefold/mod.rs +++ b/src/cyclefold/mod.rs @@ -1,6 +1,7 @@ //! This module defines Cyclefold stuff mod circuit; +mod gadgets; mod nova_circuit; pub mod compressed; diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index c3fb04c9c..6d329cd03 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -1,22 +1,25 @@ //! This module defines the Nova augmented circuit used for Cyclefold -// FIXME: Make an NIFS instance/proof struct to reduce input and output sizes - use crate::{ gadgets::{ ecc::AllocatedPoint, r1cs::{AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance}, + utils::alloc_scalar_as_base, }, r1cs::{R1CSInstance, RelaxedR1CSInstance}, - traits::{circuit::StepCircuit, Engine, ROConstantsCircuit}, + traits::{circuit::StepCircuit, commitment::CommitmentTrait, Engine, ROConstantsCircuit}, Commitment, }; use abomonation_derive::Abomonation; -use bellpepper::gadgets::num::AllocatedNum; +use bellpepper::gadgets::{num::AllocatedNum, Assignment}; use bellpepper_core::{ConstraintSystem, SynthesisError}; +use ff::Field; use serde::{Deserialize, Serialize}; +use super::gadgets::emulated; +use super::gadgets::AllocatedFoldingData; + #[derive(Clone, PartialEq, Serialize, Deserialize, Abomonation)] pub struct AugmentedCircuitParams { limb_width: usize, @@ -34,7 +37,7 @@ impl AugmentedCircuitParams { #[derive(Debug, Serialize, Deserialize)] #[serde(bound = "")] -pub struct FoldingData { +pub(crate) struct FoldingData { U: RelaxedR1CSInstance, u: R1CSInstance, T: Commitment, @@ -128,31 +131,116 @@ where } } - // FIXME: Need to actually figure out how to encode wrong-field points and instances fn alloc_witness::Base>>( &self, mut cs: CS, arity: usize, ) -> Result< ( - AllocatedNum, // pp_digest - AllocatedNum, // i - Vec>, // z0 - Vec>, // zi - AllocatedRelaxedR1CSInstance, // U_p - AllocatedR1CSInstance, // u_p - AllocatedPoint, // T_p - AllocatedRelaxedR1CSInstance, // U_c - AllocatedR1CSInstance, // u_c_1 - AllocatedPoint, // T_c_1 - AllocatedRelaxedR1CSInstance, // U_c_1 - AllocatedR1CSInstance, // u_c_2 - AllocatedPoint, // T_c_2 - AllocatedPoint, // E_new - AllocatedPoint, // W_new + AllocatedNum, // pp_digest + AllocatedNum, // i + Vec>, // z0 + Vec>, // zi + emulated::AllocatedFoldingData, //data_p + AllocatedFoldingData, // data_c_1 + AllocatedFoldingData, // data_c_2 + emulated::AllocatedPoint, // E_new + emulated::AllocatedPoint, // W_new ), SynthesisError, > { + let pp_digest = alloc_scalar_as_base::( + cs.namespace(|| "params"), + self.inputs.as_ref().map(|inputs| inputs.pp_digest), + )?; + + let i = AllocatedNum::alloc(cs.namespace(|| "i"), || { + Ok( + self + .inputs + .as_ref() + .ok_or(SynthesisError::AssignmentMissing)? + .i, + ) + })?; + + let z_0 = (0..arity) + .map(|i| { + AllocatedNum::alloc(cs.namespace(|| format!("z0_{i}")), || { + Ok(self.inputs.get()?.z0[i]) + }) + }) + .collect::>, _>>()?; + + // Allocate zi. If inputs.zi is not provided (base case) allocate default value 0 + let zero = vec![E1::Base::ZERO; arity]; + let z_i = (0..arity) + .map(|i| { + AllocatedNum::alloc(cs.namespace(|| format!("zi_{i}")), || { + Ok(self.inputs.get()?.zi.as_ref().unwrap_or(&zero)[i]) + }) + }) + .collect::>, _>>()?; + + let data_p = emulated::AllocatedFoldingData::alloc( + cs.namespace(|| "data_p"), + self + .inputs + .as_ref() + .and_then(|inputs| inputs.data_p.as_ref()), + )?; + + let data_c_1 = AllocatedFoldingData::alloc( + cs.namespace(|| "data_c_1"), + self + .inputs + .as_ref() + .and_then(|inputs| inputs.data_c_1.as_ref()), + )?; + + let data_c_2 = AllocatedFoldingData::alloc( + cs.namespace(|| "data_c_2"), + self + .inputs + .as_ref() + .and_then(|inputs| inputs.data_c_2.as_ref()), + )?; + + let E_new = emulated::AllocatedPoint::alloc( + cs.namespace(|| "E_new"), + self + .inputs + .as_ref() + .and_then(|inputs| inputs.E_new.as_ref()) + .map(|E_new| E_new.to_coordinates()), + )?; + + let W_new = emulated::AllocatedPoint::alloc( + cs.namespace(|| "W_new"), + self + .inputs + .as_ref() + .and_then(|inputs| inputs.W_new.as_ref()) + .map(|W_new| W_new.to_coordinates()), + )?; + + Ok(( + pp_digest, i, z_0, z_i, data_p, data_c_1, data_c_2, E_new, W_new, + )) + } + + pub fn synthesize_base_case::Base>>( + self, + cs: &mut CS, + ) -> Result<(), SynthesisError> { + todo!() + } + + pub fn synthesize_non_base_case::Base>>( + self, + cs: &mut CS, + ) -> Result<(), SynthesisError> { + // TODO: It's written down here https://hackmd.io/@mpenciak/HybHrnNFT todo!() } From 600e601124503c89dae89312ae045c06a1d204a1 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Wed, 24 Jan 2024 09:50:51 -0500 Subject: [PATCH 06/77] (wip) fix prove_step --- src/cyclefold/snark.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index f764fdae9..80318bbca 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -160,6 +160,8 @@ where return Ok(()); } + let r_U_primary_i = self.r_U_primary.clone(); + let (nifs_primary, r) = NIFS::prove_mut( &pp.ck_primary, &pp.ro_consts_primary, @@ -201,6 +203,7 @@ where .r1cs_instance_and_witness(&pp.circuit_shape_cyclefold.r1cs_shape, &pp.ck_cyclefold) .map_err(|_| NovaError::UnSat)?; + // TODO: check if this is better or worse than `prove_mut` with a clone of `self.r_U_cyclefold` let (nifs_cyclefold_E, (r_U_cyclefold_E, r_W_cyclefold_E), _) = NIFS::prove( &pp.ck_cyclefold, &pp.ro_consts_cyclefold, @@ -234,7 +237,8 @@ where .r1cs_instance_and_witness(&pp.circuit_shape_cyclefold.r1cs_shape, &pp.ck_cyclefold) .map_err(|_| NovaError::UnSat)?; - let (nifs_cyclefold_E, (r_U_cyclefold_W, r_W_cyclefold_W), _) = NIFS::prove( + // TODO: check if this is better or worse than `prove_mut` with a clone of r_U_cyclefold_E + let (nifs_cyclefold_W, (r_U_cyclefold_W, r_W_cyclefold_W), _) = NIFS::prove( &pp.ck_cyclefold, &pp.ro_consts_cyclefold, &scalar_as_base::(pp.digest()), @@ -245,16 +249,16 @@ where &l_w_cyclefold_W, )?; - let comm_T_W = Commitment::::decompress(&nifs_cyclefold_E.comm_T)?; + let comm_T_W = Commitment::::decompress(&nifs_cyclefold_W.comm_T)?; let mut cs_primary = SatisfyingAssignment::::with_capacity( pp.circuit_shape_primary.r1cs_shape.num_io + 1, pp.circuit_shape_primary.r1cs_shape.num_vars, ); - let data_p = FoldingData::new(self.r_U_primary.clone(), self.l_u_primary.clone(), comm_T); - let data_c_E = FoldingData::new(r_U_cyclefold_E, l_u_cyclefold_E, comm_T_E); - let data_c_W = FoldingData::new(r_U_cyclefold_W.clone(), l_u_cyclefold_W, comm_T_W); + let data_p = FoldingData::new(r_U_primary_i, self.l_u_primary.clone(), comm_T); + let data_c_E = FoldingData::new(self.r_U_cyclefold.clone(), l_u_cyclefold_E, comm_T_E); + let data_c_W = FoldingData::new(r_U_cyclefold_E, l_u_cyclefold_W, comm_T_W); let inputs_primary: AugmentedCircuitInputs = AugmentedCircuitInputs::new( scalar_as_base::(pp.digest()), From 58c528fb8188feb21f720ec693903620902cf6b0 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Wed, 24 Jan 2024 21:56:57 -0500 Subject: [PATCH 07/77] (wip) synthesize_non_base_case --- src/cyclefold/gadgets.rs | 84 ++++++++++++++---- src/cyclefold/nova_circuit.rs | 153 ++++++++++++++++++++++++++++++-- src/gadgets/nonnative/bignat.rs | 10 ++- src/gadgets/nonnative/util.rs | 1 + 4 files changed, 226 insertions(+), 22 deletions(-) diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index 6ce7b6572..3b066e85d 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -10,9 +10,9 @@ use crate::{ use bellpepper_core::{ConstraintSystem, SynthesisError}; pub struct AllocatedFoldingData { - U: AllocatedRelaxedR1CSInstance, - u: AllocatedR1CSInstance, - T: AllocatedPoint, + pub U: AllocatedRelaxedR1CSInstance, + pub u: AllocatedR1CSInstance, + pub T: AllocatedPoint, } impl AllocatedFoldingData { @@ -29,10 +29,13 @@ impl AllocatedFoldingData { } pub mod emulated { + use super::*; + use std::marker::PhantomData; - use super::*; use crate::gadgets::nonnative::bignat::BigNat; + use crate::traits::ROConstantsCircuit; + use crate::RelaxedR1CSInstance; pub struct AllocatedPoint where @@ -60,7 +63,10 @@ pub mod emulated { todo!() } - pub fn absorb_in_ro(&self, ro: &mut E1::ROCircuit) { + pub fn absorb_in_ro(&self, cs: CS, ro: &mut E1::ROCircuit) + where + CS: ConstraintSystem<::Base>, + { todo!() } @@ -72,20 +78,65 @@ pub mod emulated { } } + pub struct AllocatedRelaxedR1CSInstance + where + E1: Engine::Scalar>, + E2: Engine::Scalar>, + { + W: AllocatedPoint, + E: AllocatedPoint, + u: BigNat, + x0: BigNat, + x1: BigNat, + pub _p: PhantomData, + } + + impl AllocatedRelaxedR1CSInstance + where + E1: Engine::Scalar>, + E2: Engine::Scalar>, + { + pub fn alloc( + mut cs: CS, + inst: Option<&RelaxedR1CSInstance>, + ) -> Result + where + CS: ConstraintSystem<::Base>, + { + todo!() + } + + pub fn fold_with_r1cs::Base>>( + &self, + mut cs: CS, + W_new: AllocatedPoint, + E_new: AllocatedPoint, + x0: BigNat, + x1: BigNat, + ro_consts: ROConstantsCircuit, + limb_width: usize, + n_limbs: usize, + ) -> Result { + todo!() + } + + pub fn absorb_in_ro(&self, cs: CS, ro: &mut E1::ROCircuit) + where + CS: ConstraintSystem<::Base>, + { + todo!() + } + } pub struct AllocatedFoldingData where E1: Engine::Scalar>, E2: Engine::Scalar>, { - U_W: AllocatedPoint, - U_E: AllocatedPoint, - U_u: BigNat, - U_X0: BigNat, - U_X1: BigNat, - u_W: AllocatedPoint, - u_x0: BigNat, - u_x1: BigNat, - T: AllocatedPoint, + pub U: AllocatedRelaxedR1CSInstance, + pub u_W: AllocatedPoint, + pub u_x0: BigNat, + pub u_x1: BigNat, + pub T: AllocatedPoint, _p: PhantomData, } @@ -101,7 +152,10 @@ pub mod emulated { todo!() } - pub fn absorb_in_ro(&self, ro: &mut E1::ROCircuit) { + pub fn absorb_in_ro(&self, cs: CS, ro: &mut E1::ROCircuit) + where + CS: ConstraintSystem<::Base>, + { todo!() } } diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index 6d329cd03..c0ef186ac 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -1,10 +1,11 @@ //! This module defines the Nova augmented circuit used for Cyclefold use crate::{ + constants::{NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_BITS}, gadgets::{ ecc::AllocatedPoint, r1cs::{AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance}, - utils::alloc_scalar_as_base, + utils::{alloc_num_equals, alloc_scalar_as_base, alloc_zero, le_bits_to_num}, }, r1cs::{R1CSInstance, RelaxedR1CSInstance}, traits::{circuit::StepCircuit, commitment::CommitmentTrait, Engine, ROConstantsCircuit}, @@ -13,7 +14,7 @@ use crate::{ use abomonation_derive::Abomonation; use bellpepper::gadgets::{num::AllocatedNum, Assignment}; -use bellpepper_core::{ConstraintSystem, SynthesisError}; +use bellpepper_core::{boolean::AllocatedBit, ConstraintSystem, SynthesisError}; use ff::Field; use serde::{Deserialize, Serialize}; @@ -49,6 +50,7 @@ impl FoldingData { } } +// TODO: This needs to take in the initial cyclefold relaxed R1CS instance as well #[derive(Debug, Serialize, Deserialize)] #[serde(bound = "")] pub struct AugmentedCircuitInputs @@ -232,16 +234,147 @@ where pub fn synthesize_base_case::Base>>( self, cs: &mut CS, - ) -> Result<(), SynthesisError> { + ) -> Result< + ( + AllocatedRelaxedR1CSInstance, + emulated::AllocatedRelaxedR1CSInstance, + ), + SynthesisError, + > { todo!() } pub fn synthesize_non_base_case::Base>>( self, + pp_digest: &AllocatedNum, + i: &AllocatedNum, + z_0: &[AllocatedNum], + z_i: &[AllocatedNum], + data_p: &emulated::AllocatedFoldingData, + data_c_1: &AllocatedFoldingData, + data_c_2: &AllocatedFoldingData, + E_new: &emulated::AllocatedPoint, + W_new: &emulated::AllocatedPoint, + arity: usize, cs: &mut CS, - ) -> Result<(), SynthesisError> { - // TODO: It's written down here https://hackmd.io/@mpenciak/HybHrnNFT - todo!() + ) -> Result< + ( + AllocatedRelaxedR1CSInstance, + emulated::AllocatedRelaxedR1CSInstance, + AllocatedBit, + ), + SynthesisError, + > { + // Follows the outline written down here https://hackmd.io/@mpenciak/HybHrnNFT + let mut ro_p = E1::ROCircuit::new( + self.ro_consts.clone(), + NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * arity, + ); + + ro_p.absorb(pp_digest); + ro_p.absorb(i); + for e in z_0 { + ro_p.absorb(e) + } + for e in z_i { + ro_p.absorb(e) + } + data_p + .U + .absorb_in_ro(cs.namespace(|| "absorb U_p"), &mut ro_p); + + let hash_bits_p = ro_p.squeeze(cs.namespace(|| "primary hash bits"), NUM_HASH_BITS); + let hash_p = le_bits_to_num(cs.namespace(|| "primary hash"), hash_bits_p)?; + + let x_0 = data_p + .u_x0 + .as_allocated_num(cs.namespace(|| "u.x[0] as AllocatedNum"))?; + + let check_primary = alloc_num_equals( + cs.namespace(|| "u.X[0] = H(params, i, z0, zi, U_p)"), + &x_0, + &hash_p, + )?; + + let mut ro_c = E1::ROCircuit::new(self.ro_consts.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF); + + ro_c.absorb(pp_digest); + data_c_1 + .U + .absorb_in_ro(cs.namespace(|| "absorb U_c"), &mut ro_c); + let hash_c_bits = ro_c.squeeze(cs.namespace(|| "cyclefold hash bits"), NUM_HASH_BITS); + let hash_c = le_bits_to_num(cs.namespace(|| "cyclefold hash"), hash_c_bits)?; + + let x_1 = data_p + .u_x1 + .as_allocated_num(cs.namespace(|| "u.x[1] as AllocatedNum"))?; + + let check_cyclefold = + alloc_num_equals(cs.namespace(|| "u.X[1] = H(params, U_c)"), &x_1, &hash_c)?; + + let check_io = AllocatedBit::and( + cs.namespace(|| "both IOs match"), + &check_primary, + &check_cyclefold, + )?; + + // Run NIVC.V on U_c, u_c_1, T_c_1 + let U_int = data_c_1.U.fold_with_r1cs( + cs.namespace(|| "U_int = fold U_c with u_c_1"), + pp_digest, + &data_c_1.u, + &data_c_1.T, + self.ro_consts.clone(), + self.params.limb_width, + self.params.n_limbs, + )?; + + // Calculate h_int = H(pp, U_c_int) + let mut ro_c_int = E1::ROCircuit::new(self.ro_consts.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF); + ro_c_int.absorb(pp_digest); + U_int.absorb_in_ro(cs.namespace(|| "absorb U_c_int"), &mut ro_c_int); + let h_c_int_bits = ro_c_int.squeeze(cs.namespace(|| "intermediate hash bits"), NUM_HASH_BITS); + let h_c_int = le_bits_to_num(cs.namespace(|| "intermediate hash"), h_c_int_bits)?; + + // Calculate h_1 = H(pp, U_c_1) + let mut ro_c_1 = E1::ROCircuit::new(self.ro_consts.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF); + ro_c_1.absorb(pp_digest); + data_c_2 + .U + .absorb_in_ro(cs.namespace(|| "absorb U_c_1"), &mut ro_c_1); + let h_c_1_bits = ro_c_1.squeeze(cs.namespace(|| "cyclefold_1 hash bits"), NUM_HASH_BITS); + let h_c_1 = le_bits_to_num(cs.namespace(|| "cyclefold_1 hash"), h_c_1_bits)?; + + let check_cyclefold_int = alloc_num_equals(cs.namespace(|| "h_int = h_c_1"), &h_c_int, &h_c_1)?; + + let checks_pass = AllocatedBit::and( + cs.namespace(|| "all checks passed"), + &check_io, + &check_cyclefold_int, + )?; + + let U_c = data_c_2.U.fold_with_r1cs( + cs.namespace(|| "U_c = fold U_c_1 with u_c_2"), + pp_digest, + &data_c_2.u, + &data_c_2.T, + self.ro_consts.clone(), + self.params.limb_width, + self.params.n_limbs, + )?; + + let U_p = data_p.U.fold_with_r1cs( + cs.namespace(|| "fold u_p into U_p"), + *W_new, + *E_new, + data_p.u_x0, + data_p.u_x1, + self.ro_consts.clone(), + self.params.limb_width, + self.params.n_limbs, + )?; + + Ok((U_c, U_p, checks_pass)) } pub fn synthesize::Base>>( @@ -249,6 +382,14 @@ where cs: &mut CS, ) -> Result>, SynthesisError> { // TODO: It's written down here https://hackmd.io/@mpenciak/HybHrnNFT + let (pp_digest, i, z_0, z_i, data_p, data_c_1, data_c_2, E_new, W_new) = + self.alloc_witness(cs.namespace(|| "alloc_witness"), self.params.n_limbs)?; + + let zero = alloc_zero(cs.namespace(|| "zero")); + let is_base_case = alloc_num_equals(cs.namespace(|| "is base case"), &i, &zero)?; + + // TODO: Here we synthesize either the base case or non-base-case + todo!() } } diff --git a/src/gadgets/nonnative/bignat.rs b/src/gadgets/nonnative/bignat.rs index 66dead141..c81bdca42 100644 --- a/src/gadgets/nonnative/bignat.rs +++ b/src/gadgets/nonnative/bignat.rs @@ -4,7 +4,7 @@ use super::{ }, OptionExt, }; -use bellpepper_core::{ConstraintSystem, LinearCombination, SynthesisError}; +use bellpepper_core::{num::AllocatedNum, ConstraintSystem, LinearCombination, SynthesisError}; use ff::PrimeField; use itertools::Itertools as _; use num_bigint::BigInt; @@ -257,6 +257,14 @@ impl BigNat { limbs } + /// explodes if it can't fit into an AllocatedNum + pub fn as_allocated_num>( + &self, + mut cs: CS, + ) -> Result, SynthesisError> { + todo!() + } + pub fn assert_well_formed>( &self, mut cs: CS, diff --git a/src/gadgets/nonnative/util.rs b/src/gadgets/nonnative/util.rs index 3a4f30aa8..08f4938b9 100644 --- a/src/gadgets/nonnative/util.rs +++ b/src/gadgets/nonnative/util.rs @@ -1,5 +1,6 @@ use super::{BitAccess, OptionExt}; use bellpepper_core::{ + boolean::AllocatedBit, num::AllocatedNum, {ConstraintSystem, LinearCombination, SynthesisError, Variable}, }; From 3b373c8d026b864cadd3a9aace1ff441044a34b3 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Wed, 24 Jan 2024 23:13:56 -0500 Subject: [PATCH 08/77] (wip) synthesize --- src/cyclefold/gadgets.rs | 18 ++++- src/cyclefold/nova_circuit.rs | 140 +++++++++++++++++++++++++++++----- 2 files changed, 134 insertions(+), 24 deletions(-) diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index 3b066e85d..07f9805dc 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -29,6 +29,8 @@ impl AllocatedFoldingData { } pub mod emulated { + use bellpepper_core::boolean::Boolean; + use super::*; use std::marker::PhantomData; @@ -37,6 +39,7 @@ pub mod emulated { use crate::traits::ROConstantsCircuit; use crate::RelaxedR1CSInstance; + #[derive(Clone)] pub struct AllocatedPoint where E1: Engine::Scalar>, @@ -111,8 +114,8 @@ pub mod emulated { mut cs: CS, W_new: AllocatedPoint, E_new: AllocatedPoint, - x0: BigNat, - x1: BigNat, + x0: &BigNat, + x1: &BigNat, ro_consts: ROConstantsCircuit, limb_width: usize, n_limbs: usize, @@ -120,12 +123,21 @@ pub mod emulated { todo!() } - pub fn absorb_in_ro(&self, cs: CS, ro: &mut E1::ROCircuit) + pub fn absorb_in_ro(&self, cs: CS, ro: &mut E1::ROCircuit) -> Result<(), SynthesisError> where CS: ConstraintSystem<::Base>, { todo!() } + + pub fn conditionally_select::Base>>( + &self, + cs: CS, + other: &Self, + condition: &Boolean, + ) -> Result { + todo!() + } } pub struct AllocatedFoldingData where diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index c0ef186ac..19695e9c3 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -5,15 +5,19 @@ use crate::{ gadgets::{ ecc::AllocatedPoint, r1cs::{AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance}, - utils::{alloc_num_equals, alloc_scalar_as_base, alloc_zero, le_bits_to_num}, + utils::{ + alloc_num_equals, alloc_scalar_as_base, alloc_zero, conditionally_select_vec, le_bits_to_num, + }, }, r1cs::{R1CSInstance, RelaxedR1CSInstance}, - traits::{circuit::StepCircuit, commitment::CommitmentTrait, Engine, ROConstantsCircuit}, + traits::{ + circuit::StepCircuit, commitment::CommitmentTrait, Engine, ROCircuitTrait, ROConstantsCircuit, + }, Commitment, }; use abomonation_derive::Abomonation; -use bellpepper::gadgets::{num::AllocatedNum, Assignment}; +use bellpepper::gadgets::{boolean::Boolean, num::AllocatedNum, Assignment}; use bellpepper_core::{boolean::AllocatedBit, ConstraintSystem, SynthesisError}; use ff::Field; use serde::{Deserialize, Serialize}; @@ -232,8 +236,8 @@ where } pub fn synthesize_base_case::Base>>( - self, - cs: &mut CS, + &self, + mut cs: CS, ) -> Result< ( AllocatedRelaxedR1CSInstance, @@ -245,7 +249,8 @@ where } pub fn synthesize_non_base_case::Base>>( - self, + &self, + mut cs: CS, pp_digest: &AllocatedNum, i: &AllocatedNum, z_0: &[AllocatedNum], @@ -256,7 +261,6 @@ where E_new: &emulated::AllocatedPoint, W_new: &emulated::AllocatedPoint, arity: usize, - cs: &mut CS, ) -> Result< ( AllocatedRelaxedR1CSInstance, @@ -283,8 +287,8 @@ where .U .absorb_in_ro(cs.namespace(|| "absorb U_p"), &mut ro_p); - let hash_bits_p = ro_p.squeeze(cs.namespace(|| "primary hash bits"), NUM_HASH_BITS); - let hash_p = le_bits_to_num(cs.namespace(|| "primary hash"), hash_bits_p)?; + let hash_bits_p = ro_p.squeeze(cs.namespace(|| "primary hash bits"), NUM_HASH_BITS)?; + let hash_p = le_bits_to_num(cs.namespace(|| "primary hash"), &hash_bits_p)?; let x_0 = data_p .u_x0 @@ -302,8 +306,8 @@ where data_c_1 .U .absorb_in_ro(cs.namespace(|| "absorb U_c"), &mut ro_c); - let hash_c_bits = ro_c.squeeze(cs.namespace(|| "cyclefold hash bits"), NUM_HASH_BITS); - let hash_c = le_bits_to_num(cs.namespace(|| "cyclefold hash"), hash_c_bits)?; + let hash_c_bits = ro_c.squeeze(cs.namespace(|| "cyclefold hash bits"), NUM_HASH_BITS)?; + let hash_c = le_bits_to_num(cs.namespace(|| "cyclefold hash"), &hash_c_bits)?; let x_1 = data_p .u_x1 @@ -333,8 +337,9 @@ where let mut ro_c_int = E1::ROCircuit::new(self.ro_consts.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF); ro_c_int.absorb(pp_digest); U_int.absorb_in_ro(cs.namespace(|| "absorb U_c_int"), &mut ro_c_int); - let h_c_int_bits = ro_c_int.squeeze(cs.namespace(|| "intermediate hash bits"), NUM_HASH_BITS); - let h_c_int = le_bits_to_num(cs.namespace(|| "intermediate hash"), h_c_int_bits)?; + let h_c_int_bits = + ro_c_int.squeeze(cs.namespace(|| "intermediate hash bits"), NUM_HASH_BITS)?; + let h_c_int = le_bits_to_num(cs.namespace(|| "intermediate hash"), &h_c_int_bits)?; // Calculate h_1 = H(pp, U_c_1) let mut ro_c_1 = E1::ROCircuit::new(self.ro_consts.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF); @@ -342,8 +347,8 @@ where data_c_2 .U .absorb_in_ro(cs.namespace(|| "absorb U_c_1"), &mut ro_c_1); - let h_c_1_bits = ro_c_1.squeeze(cs.namespace(|| "cyclefold_1 hash bits"), NUM_HASH_BITS); - let h_c_1 = le_bits_to_num(cs.namespace(|| "cyclefold_1 hash"), h_c_1_bits)?; + let h_c_1_bits = ro_c_1.squeeze(cs.namespace(|| "cyclefold_1 hash bits"), NUM_HASH_BITS)?; + let h_c_1 = le_bits_to_num(cs.namespace(|| "cyclefold_1 hash"), &h_c_1_bits)?; let check_cyclefold_int = alloc_num_equals(cs.namespace(|| "h_int = h_c_1"), &h_c_int, &h_c_1)?; @@ -365,10 +370,10 @@ where let U_p = data_p.U.fold_with_r1cs( cs.namespace(|| "fold u_p into U_p"), - *W_new, - *E_new, - data_p.u_x0, - data_p.u_x1, + W_new.clone(), + E_new.clone(), + &data_p.u_x0, + &data_p.u_x1, self.ro_consts.clone(), self.params.limb_width, self.params.n_limbs, @@ -382,14 +387,107 @@ where cs: &mut CS, ) -> Result>, SynthesisError> { // TODO: It's written down here https://hackmd.io/@mpenciak/HybHrnNFT + let arity = self.step_circuit.arity(); + let (pp_digest, i, z_0, z_i, data_p, data_c_1, data_c_2, E_new, W_new) = self.alloc_witness(cs.namespace(|| "alloc_witness"), self.params.n_limbs)?; let zero = alloc_zero(cs.namespace(|| "zero")); let is_base_case = alloc_num_equals(cs.namespace(|| "is base case"), &i, &zero)?; - // TODO: Here we synthesize either the base case or non-base-case + let (Unew_c_base, Unew_p_base) = self.synthesize_base_case(cs.namespace(|| "base case"))?; + + let (Unew_c_non_base, Unew_p_non_base, check_non_base_pass) = self.synthesize_non_base_case( + cs.namespace(|| "synthesize non base case"), + &pp_digest, + &i, + &z_0, + &z_i, + &data_p, + &data_c_1, + &data_c_2, + &E_new, + &W_new, + arity, + )?; - todo!() + 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, + ); + + let is_base_case_bool = Boolean::from(is_base_case.clone()); + + let Unew_p = Unew_p_base.conditionally_select( + cs.namespace(|| "compute Unew_p"), + &Unew_p_non_base, + &is_base_case_bool, + )?; + + let Unew_c = Unew_c_base.conditionally_select( + cs.namespace(|| "compute Unew_c"), + &Unew_c_non_base, + &is_base_case_bool, + )?; + + // Compute i + 1 + let i_new = AllocatedNum::alloc(cs.namespace(|| "i + 1"), || { + Ok(*i.get_value().get()? + E1::Base::ONE) + })?; + cs.enforce( + || "check i + 1", + |lc| lc, + |lc| lc, + |lc| lc + i_new.get_variable() - CS::one() - i.get_variable(), + ); + + // Compute z_{i+1} + let z_input = conditionally_select_vec( + cs.namespace(|| "select input to F"), + &z_0, + &z_i, + &Boolean::from(is_base_case), + )?; + + let z_next = self + .step_circuit + .synthesize(&mut cs.namespace(|| "F"), &z_input)?; + + if z_next.len() != arity { + return Err(SynthesisError::IncompatibleLengthVector( + "z_next".to_string(), + )); + } + + let mut ro_p = E1::ROCircuit::new(self.ro_consts, NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * arity); + ro_p.absorb(&pp_digest); + ro_p.absorb(&i_new); + for e in &z_0 { + ro_p.absorb(e); + } + for e in &z_next { + ro_p.absorb(e); + } + Unew_p.absorb_in_ro(cs.namespace(|| "absorb Unew_p"), &mut ro_p)?; + let hash_p_bits = ro_p.squeeze(cs.namespace(|| "hash_p_bits"), NUM_HASH_BITS)?; + let hash_p = le_bits_to_num(cs.namespace(|| "hash_p"), &hash_p_bits)?; + + let mut ro_c = E1::ROCircuit::new(self.ro_consts, NUM_FE_WITHOUT_IO_FOR_CRHF); + ro_c.absorb(&pp_digest); + Unew_c.absorb_in_ro(cs.namespace(|| "absorb Unew_c"), &mut ro_c)?; + let hash_c_bits = ro_c.squeeze(cs.namespace(|| "hash_c_bits"), NUM_HASH_BITS)?; + let hash_c = le_bits_to_num(cs.namespace(|| "hash_c"), &hash_c_bits)?; + + hash_p.inputize(cs.namespace(|| "u_p.x[0] = hash_p"))?; + hash_c.inputize(cs.namespace(|| "u_p.x[1] = hash_c"))?; + + Ok(z_next) } } From 5ec288205f4ceaa04e2240e7423573ac78d75c1c Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Wed, 24 Jan 2024 23:35:21 -0500 Subject: [PATCH 09/77] (wip) synthesize_base_case --- src/cyclefold/gadgets.rs | 8 ++++++++ src/cyclefold/nova_circuit.rs | 20 +++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index 07f9805dc..686022006 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -138,6 +138,14 @@ pub mod emulated { ) -> Result { todo!() } + + pub fn default::Base>>( + mut cs: CS, + limb_width: usize, + n_limbs: usize, + ) -> Result { + todo!() + } } pub struct AllocatedFoldingData where diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index 19695e9c3..2e234bbd8 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -3,8 +3,7 @@ use crate::{ constants::{NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_BITS}, gadgets::{ - ecc::AllocatedPoint, - r1cs::{AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance}, + r1cs::AllocatedRelaxedR1CSInstance, utils::{ alloc_num_equals, alloc_scalar_as_base, alloc_zero, conditionally_select_vec, le_bits_to_num, }, @@ -22,8 +21,7 @@ use bellpepper_core::{boolean::AllocatedBit, ConstraintSystem, SynthesisError}; use ff::Field; use serde::{Deserialize, Serialize}; -use super::gadgets::emulated; -use super::gadgets::AllocatedFoldingData; +use super::gadgets::{emulated, AllocatedFoldingData}; #[derive(Clone, PartialEq, Serialize, Deserialize, Abomonation)] pub struct AugmentedCircuitParams { @@ -245,7 +243,19 @@ where ), SynthesisError, > { - todo!() + let U_c_default = AllocatedRelaxedR1CSInstance::default( + cs.namespace(|| "Allocate U_c_default"), + self.params.limb_width, + self.params.n_limbs, + )?; + + let U_p_default = emulated::AllocatedRelaxedR1CSInstance::default( + cs.namespace(|| "Allocated U_p_default"), + self.params.limb_width, + self.params.n_limbs, + )?; + + Ok((U_c_default, U_p_default)) } pub fn synthesize_non_base_case::Base>>( From e1adb35ab3f4f98b13328589819172243a776f96 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Wed, 24 Jan 2024 23:35:27 -0500 Subject: [PATCH 10/77] (wip) fix absorbs --- src/cyclefold/gadgets.rs | 6 +++--- src/cyclefold/nova_circuit.rs | 13 ++++++++----- src/gadgets/nonnative/util.rs | 1 - 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index 686022006..7f3bb28bd 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -23,7 +23,7 @@ impl AllocatedFoldingData { todo!() } - pub fn absorb_in_ro(&self, ro: &mut E::ROCircuit) { + pub fn absorb_in_ro(&self, ro: &mut E::ROCircuit) -> Result<(), SynthesisError> { todo!() } } @@ -66,7 +66,7 @@ pub mod emulated { todo!() } - pub fn absorb_in_ro(&self, cs: CS, ro: &mut E1::ROCircuit) + pub fn absorb_in_ro(&self, cs: CS, ro: &mut E1::ROCircuit) -> Result<(), SynthesisError> where CS: ConstraintSystem<::Base>, { @@ -172,7 +172,7 @@ pub mod emulated { todo!() } - pub fn absorb_in_ro(&self, cs: CS, ro: &mut E1::ROCircuit) + pub fn absorb_in_ro(&self, cs: CS, ro: &mut E1::ROCircuit) -> Result<(), SynthesisError> where CS: ConstraintSystem<::Base>, { diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index 2e234bbd8..82a0afcb5 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -295,7 +295,7 @@ where } data_p .U - .absorb_in_ro(cs.namespace(|| "absorb U_p"), &mut ro_p); + .absorb_in_ro(cs.namespace(|| "absorb U_p"), &mut ro_p)?; let hash_bits_p = ro_p.squeeze(cs.namespace(|| "primary hash bits"), NUM_HASH_BITS)?; let hash_p = le_bits_to_num(cs.namespace(|| "primary hash"), &hash_bits_p)?; @@ -315,7 +315,7 @@ where ro_c.absorb(pp_digest); data_c_1 .U - .absorb_in_ro(cs.namespace(|| "absorb U_c"), &mut ro_c); + .absorb_in_ro(cs.namespace(|| "absorb U_c"), &mut ro_c)?; let hash_c_bits = ro_c.squeeze(cs.namespace(|| "cyclefold hash bits"), NUM_HASH_BITS)?; let hash_c = le_bits_to_num(cs.namespace(|| "cyclefold hash"), &hash_c_bits)?; @@ -346,7 +346,7 @@ where // Calculate h_int = H(pp, U_c_int) let mut ro_c_int = E1::ROCircuit::new(self.ro_consts.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF); ro_c_int.absorb(pp_digest); - U_int.absorb_in_ro(cs.namespace(|| "absorb U_c_int"), &mut ro_c_int); + U_int.absorb_in_ro(cs.namespace(|| "absorb U_c_int"), &mut ro_c_int)?; let h_c_int_bits = ro_c_int.squeeze(cs.namespace(|| "intermediate hash bits"), NUM_HASH_BITS)?; let h_c_int = le_bits_to_num(cs.namespace(|| "intermediate hash"), &h_c_int_bits)?; @@ -356,7 +356,7 @@ where ro_c_1.absorb(pp_digest); data_c_2 .U - .absorb_in_ro(cs.namespace(|| "absorb U_c_1"), &mut ro_c_1); + .absorb_in_ro(cs.namespace(|| "absorb U_c_1"), &mut ro_c_1)?; let h_c_1_bits = ro_c_1.squeeze(cs.namespace(|| "cyclefold_1 hash bits"), NUM_HASH_BITS)?; let h_c_1 = le_bits_to_num(cs.namespace(|| "cyclefold_1 hash"), &h_c_1_bits)?; @@ -476,7 +476,10 @@ where )); } - let mut ro_p = E1::ROCircuit::new(self.ro_consts, NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * arity); + let mut ro_p = E1::ROCircuit::new( + self.ro_consts.clone(), + NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * arity, + ); ro_p.absorb(&pp_digest); ro_p.absorb(&i_new); for e in &z_0 { diff --git a/src/gadgets/nonnative/util.rs b/src/gadgets/nonnative/util.rs index 08f4938b9..3a4f30aa8 100644 --- a/src/gadgets/nonnative/util.rs +++ b/src/gadgets/nonnative/util.rs @@ -1,6 +1,5 @@ use super::{BitAccess, OptionExt}; use bellpepper_core::{ - boolean::AllocatedBit, num::AllocatedNum, {ConstraintSystem, LinearCombination, SynthesisError, Variable}, }; From fd5f5b93fdac3756502e5ba5dd54d9eefde53832 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Thu, 25 Jan 2024 00:55:23 -0500 Subject: [PATCH 11/77] (wip) stuck --- src/cyclefold/nova_circuit.rs | 2 +- src/cyclefold/snark.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index 82a0afcb5..7b32b2de2 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -352,7 +352,7 @@ where let h_c_int = le_bits_to_num(cs.namespace(|| "intermediate hash"), &h_c_int_bits)?; // Calculate h_1 = H(pp, U_c_1) - let mut ro_c_1 = E1::ROCircuit::new(self.ro_consts.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF); + let mut ro_c_1 = E1::ROCircuit::new(self.ro_consts, NUM_FE_WITHOUT_IO_FOR_CRHF); ro_c_1.absorb(pp_digest); data_c_2 .U diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 80318bbca..089a80eec 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -162,6 +162,7 @@ where let r_U_primary_i = self.r_U_primary.clone(); + // FIXME: This is wrong. The NIFS prover field needs to match the eventual NIFS verifier field let (nifs_primary, r) = NIFS::prove_mut( &pp.ck_primary, &pp.ro_consts_primary, From e32410911285974301a69f1048c45009ac071173 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Mon, 29 Jan 2024 17:20:03 -0500 Subject: [PATCH 12/77] (wip) CommitmentKey macro --- src/cyclefold/mod.rs | 1 + src/cyclefold/nifs.rs | 79 ++++++++++++++++++++++++++++++++++++++++++ src/cyclefold/snark.rs | 34 ++++++++++++------ src/nifs.rs | 5 +-- 4 files changed, 104 insertions(+), 15 deletions(-) create mode 100644 src/cyclefold/nifs.rs diff --git a/src/cyclefold/mod.rs b/src/cyclefold/mod.rs index b515df75b..61fd35d9a 100644 --- a/src/cyclefold/mod.rs +++ b/src/cyclefold/mod.rs @@ -5,4 +5,5 @@ mod gadgets; mod nova_circuit; pub mod compressed; +pub mod nifs; pub mod snark; diff --git a/src/cyclefold/nifs.rs b/src/cyclefold/nifs.rs new file mode 100644 index 000000000..6cec23e05 --- /dev/null +++ b/src/cyclefold/nifs.rs @@ -0,0 +1,79 @@ +//! This module defines the needed wrong-field NIFS prover + +use std::marker::PhantomData; + +use crate::{ + constants::{NUM_CHALLENGE_BITS, NUM_FE_FOR_RO}, + errors::NovaError, + gadgets::utils::scalar_as_base, + r1cs::{R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness}, + traits::{commitment::CommitmentTrait, AbsorbInROTrait, Engine, ROConstants, ROTrait}, + CommitmentKey, CompressedCommitment, +}; + +pub struct NIFS +where + E1: Engine::Scalar>, + E2: Engine::Scalar>, +{ + pub(crate) comm_T: CompressedCommitment, + _p: PhantomData, +} + +impl NIFS +where + E1: Engine::Scalar>, + E2: Engine::Scalar>, +{ + pub fn prove( + ck: &CommitmentKey, + ro_consts: &ROConstants, + pp_digest: &E1::Scalar, + S: &R1CSShape, + U1: &RelaxedR1CSInstance, + W1: &RelaxedR1CSWitness, + U2: &R1CSInstance, + W2: &R1CSWitness, + ) -> Result< + ( + Self, + (RelaxedR1CSInstance, RelaxedR1CSWitness), + E1::Scalar, + ), + NovaError, + > { + let mut ro = E2::RO::new(ro_consts.clone(), NUM_FE_FOR_RO); + + ro.absorb(*pp_digest); + + // FIXME + // Want to write: U2.absorb_in_ro(&mut ro); + // But can't because U2 is a R1CSInstance and not a R1CSInstance + + let (T, comm_T) = S.commit_T(ck, U1, W1, U2, W2)?; + + // FIXME + // Want to write: comm_T.absorb_in_ro(&mut ro); + // But can't because comm_T is a CompressedCommitment and not a CompressedCommitment + + let r = scalar_as_base::(ro.squeeze(NUM_CHALLENGE_BITS)); + + let U = U1.fold(U2, &comm_T, &r); + + let W = W1.fold(W2, &T, &r)?; + + Ok(( + Self { + comm_T: comm_T.compress(), + _p: PhantomData, + }, + (U, W), + r, + )) + } +} + +#[cfg(test)] +mod test { + // use super::*; +} diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 089a80eec..c48f973ff 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -44,7 +44,7 @@ where C1: StepCircuit, { F_arity_primary: usize, - ro_consts_primary: ROConstants, + ro_consts_primary: ROConstants, ro_consts_circuit_primary: ROConstantsCircuit, ck_primary: CommitmentKey, circuit_shape_primary: R1CSWithArity, @@ -160,23 +160,33 @@ where return Ok(()); } - let r_U_primary_i = self.r_U_primary.clone(); - - // FIXME: This is wrong. The NIFS prover field needs to match the eventual NIFS verifier field - let (nifs_primary, r) = NIFS::prove_mut( + let (nifs_primary, (r_U_primary, r_W_primary), r) = super::nifs::NIFS::::prove( &pp.ck_primary, &pp.ro_consts_primary, &pp.digest(), &pp.circuit_shape_primary.r1cs_shape, - &mut self.r_U_primary, - &mut self.r_W_primary, + &self.r_U_primary, + &self.r_W_primary, &self.l_u_primary, &self.l_w_primary, - &mut self.buffer_primary.T, - &mut self.buffer_primary.ABC_Z_1, - &mut self.buffer_primary.ABC_Z_2, )?; + // TODO: Delete this + // NOTE: This replaces the following code: + // let (nifs_primary, r) = NIFS::prove_mut( + // &pp.ck_primary, + // &pp.ro_consts_primary, + // &pp.digest(), + // &pp.circuit_shape_primary.r1cs_shape, + // &mut self.r_U_primary, + // &mut self.r_W_primary, + // &self.l_u_primary, + // &self.l_w_primary, + // &mut self.buffer_primary.T, + // &mut self.buffer_primary.ABC_Z_1, + // &mut self.buffer_primary.ABC_Z_2, + // )?; + let r_bools = r.to_le_bits().iter().map(|b| Some(*b)).collect::>(); let comm_T = Commitment::::decompress(&nifs_primary.comm_T)?; @@ -257,7 +267,7 @@ where pp.circuit_shape_primary.r1cs_shape.num_vars, ); - let data_p = FoldingData::new(r_U_primary_i, self.l_u_primary.clone(), comm_T); + let data_p = FoldingData::new(self.r_U_primary.clone(), self.l_u_primary.clone(), comm_T); let data_c_E = FoldingData::new(self.r_U_cyclefold.clone(), l_u_cyclefold_E, comm_T_E); let data_c_W = FoldingData::new(r_U_cyclefold_E, l_u_cyclefold_W, comm_T_W); @@ -291,6 +301,8 @@ where .map(|v| v.get_value().ok_or(SynthesisError::AssignmentMissing)) .collect::, _>>()?; + self.r_U_primary = r_U_primary; + self.r_W_primary = r_W_primary; self.l_u_primary = l_u_primary; self.l_w_primary = l_w_primary; self.r_U_cyclefold = r_U_cyclefold_W; diff --git a/src/nifs.rs b/src/nifs.rs index 9f7bc958a..9522e07a8 100644 --- a/src/nifs.rs +++ b/src/nifs.rs @@ -8,7 +8,7 @@ use crate::{ R1CSInstance, R1CSResult, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness, }, scalar_as_base, - traits::{commitment::CommitmentTrait, AbsorbInROTrait, Engine, ROTrait}, + traits::{commitment::CommitmentTrait, AbsorbInROTrait, Engine, ROConstants, ROTrait}, Commitment, CommitmentKey, CompressedCommitment, }; use serde::{Deserialize, Serialize}; @@ -21,9 +21,6 @@ pub struct NIFS { pub(crate) comm_T: CompressedCommitment, } -type ROConstants = - <::RO as ROTrait<::Base, ::Scalar>>::Constants; - impl NIFS { /// Takes as input a Relaxed R1CS instance-witness tuple `(U1, W1)` and /// an R1CS instance-witness tuple `(U2, W2)` with the same structure `shape` From 9b68ff5825a72c698a267af9f34c1c7c7e9160b8 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Wed, 31 Jan 2024 13:33:26 -0500 Subject: [PATCH 13/77] (wip) some gadgets filled --- src/cyclefold/gadgets.rs | 31 +++++++++++++++--- src/cyclefold/nifs.rs | 57 ++++++++++++++++++++++++++------- src/cyclefold/nova_circuit.rs | 12 ++++--- src/gadgets/nonnative/bignat.rs | 7 ++-- src/gadgets/nonnative/util.rs | 38 +++++++++++++++++++++- 5 files changed, 123 insertions(+), 22 deletions(-) diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index 7f3bb28bd..75a54cac0 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -5,7 +5,7 @@ use crate::{ ecc::AllocatedPoint, r1cs::{AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance}, }, - traits::Engine, + traits::{commitment::CommitmentTrait, Engine, ROCircuitTrait}, }; use bellpepper_core::{ConstraintSystem, SynthesisError}; @@ -19,12 +19,35 @@ impl AllocatedFoldingData { pub fn alloc::Base>>( mut cs: CS, inst: Option<&FoldingData>, + limb_width: usize, + n_limbs: usize, ) -> Result { - todo!() + let U = AllocatedRelaxedR1CSInstance::alloc( + cs.namespace(|| "U"), + inst.map(|x| &x.U), + limb_width, + n_limbs, + )?; + + let u = AllocatedR1CSInstance::alloc(cs.namespace(|| "U"), inst.map(|x| &x.u))?; + + let T = AllocatedPoint::alloc(cs.namespace(|| "T"), inst.map(|x| x.T.to_coordinates()))?; + T.check_on_curve(cs.namespace(|| "T on curve"))?; + + Ok(Self { U, u, T }) } - pub fn absorb_in_ro(&self, ro: &mut E::ROCircuit) -> Result<(), SynthesisError> { - todo!() + pub fn absorb_in_ro::Base>>( + &self, + mut cs: CS, + ro: &mut E::ROCircuit, + ) -> Result<(), SynthesisError> { + self.U.absorb_in_ro(cs.namespace(|| "absorb U"), ro)?; + self.u.absorb_in_ro(ro); + ro.absorb(&self.T.x); + ro.absorb(&self.T.y); + ro.absorb(&self.T.is_infinity); + Ok(()) } } diff --git a/src/cyclefold/nifs.rs b/src/cyclefold/nifs.rs index 6cec23e05..a4e1920d6 100644 --- a/src/cyclefold/nifs.rs +++ b/src/cyclefold/nifs.rs @@ -2,15 +2,54 @@ use std::marker::PhantomData; +use ff::Field; + use crate::{ - constants::{NUM_CHALLENGE_BITS, NUM_FE_FOR_RO}, + constants::{BN_LIMB_WIDTH, BN_N_LIMBS, NUM_CHALLENGE_BITS, NUM_FE_FOR_RO}, errors::NovaError, - gadgets::utils::scalar_as_base, + gadgets::{ + nonnative::{bignat::nat_to_limbs, util::f_to_nat}, + utils::scalar_as_base, + }, r1cs::{R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness}, - traits::{commitment::CommitmentTrait, AbsorbInROTrait, Engine, ROConstants, ROTrait}, - CommitmentKey, CompressedCommitment, + traits::{commitment::CommitmentTrait, /*AbsorbInROTrait,*/ Engine, ROConstants, ROTrait}, + Commitment, CommitmentKey, CompressedCommitment, }; +fn absorb_commitment(comm: &Commitment, ro: &mut E2::RO) +where + E1: Engine::Scalar>, + E2: Engine::Scalar>, +{ + let (x, y, is_infinity) = comm.to_coordinates(); + + let x_limbs = nat_to_limbs(&f_to_nat(&x), BN_LIMB_WIDTH, BN_N_LIMBS).unwrap(); + let y_limbs = nat_to_limbs(&f_to_nat(&y), BN_LIMB_WIDTH, BN_N_LIMBS).unwrap(); + + for limb in x_limbs { + ro.absorb(scalar_as_base::(limb)); + } + for limb in y_limbs { + ro.absorb(scalar_as_base::(limb)); + } + if is_infinity { + ro.absorb(::Scalar::ONE); + } else { + ro.absorb(::Scalar::ZERO); + } +} + +fn absorb_r1cs(u: &R1CSInstance, ro: &mut E2::RO) +where + E1: Engine::Scalar>, + E2: Engine::Scalar>, +{ + absorb_commitment::(&u.comm_W, ro); + for x in &u.X { + ro.absorb(*x); + } +} + pub struct NIFS where E1: Engine::Scalar>, @@ -25,7 +64,7 @@ where E1: Engine::Scalar>, E2: Engine::Scalar>, { - pub fn prove( + pub fn prove( ck: &CommitmentKey, ro_consts: &ROConstants, pp_digest: &E1::Scalar, @@ -46,15 +85,11 @@ where ro.absorb(*pp_digest); - // FIXME - // Want to write: U2.absorb_in_ro(&mut ro); - // But can't because U2 is a R1CSInstance and not a R1CSInstance + absorb_r1cs::(U2, &mut ro); let (T, comm_T) = S.commit_T(ck, U1, W1, U2, W2)?; - // FIXME - // Want to write: comm_T.absorb_in_ro(&mut ro); - // But can't because comm_T is a CompressedCommitment and not a CompressedCommitment + absorb_commitment::(&comm_T, &mut ro); let r = scalar_as_base::(ro.squeeze(NUM_CHALLENGE_BITS)); diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index 7b32b2de2..b48570fb7 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -41,9 +41,9 @@ impl AugmentedCircuitParams { #[derive(Debug, Serialize, Deserialize)] #[serde(bound = "")] pub(crate) struct FoldingData { - U: RelaxedR1CSInstance, - u: R1CSInstance, - T: Commitment, + pub U: RelaxedR1CSInstance, + pub u: R1CSInstance, + pub T: Commitment, } impl FoldingData { @@ -200,6 +200,8 @@ where .inputs .as_ref() .and_then(|inputs| inputs.data_c_1.as_ref()), + self.params.limb_width, + self.params.n_limbs, )?; let data_c_2 = AllocatedFoldingData::alloc( @@ -208,6 +210,8 @@ where .inputs .as_ref() .and_then(|inputs| inputs.data_c_2.as_ref()), + self.params.limb_width, + self.params.n_limbs, )?; let E_new = emulated::AllocatedPoint::alloc( @@ -352,7 +356,7 @@ where let h_c_int = le_bits_to_num(cs.namespace(|| "intermediate hash"), &h_c_int_bits)?; // Calculate h_1 = H(pp, U_c_1) - let mut ro_c_1 = E1::ROCircuit::new(self.ro_consts, NUM_FE_WITHOUT_IO_FOR_CRHF); + let mut ro_c_1 = E1::ROCircuit::new(self.ro_consts.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF); ro_c_1.absorb(pp_digest); data_c_2 .U diff --git a/src/gadgets/nonnative/bignat.rs b/src/gadgets/nonnative/bignat.rs index c81bdca42..cbae26a67 100644 --- a/src/gadgets/nonnative/bignat.rs +++ b/src/gadgets/nonnative/bignat.rs @@ -257,12 +257,15 @@ impl BigNat { limbs } - /// explodes if it can't fit into an AllocatedNum + // NOTE: This is quick and dirty and is bad + /// Transforms a BigNat into an AllocatedNum (makes no check on whether it's bigger than the field) pub fn as_allocated_num>( &self, mut cs: CS, ) -> Result, SynthesisError> { - todo!() + let bits = self.decompose(cs.namespace(|| "decompse as bits"))?; + let num = Num::from_bits(cs.namespace(|| "num from bits"), &bits)?; + num.as_allocated_num(cs.namespace(|| "as_allocated_num")) } pub fn assert_well_formed>( diff --git a/src/gadgets/nonnative/util.rs b/src/gadgets/nonnative/util.rs index 3a4f30aa8..af1a07271 100644 --- a/src/gadgets/nonnative/util.rs +++ b/src/gadgets/nonnative/util.rs @@ -156,7 +156,43 @@ impl Num { } /// Computes the natural number represented by an array of bits. - /// Checks if the natural number equals `self` + pub fn from_bits>( + mut cs: CS, + bits: &Bitvector, + ) -> Result { + let allocations = bits.allocations.clone(); + let mut f = Scalar::ONE; + let num = allocations + .iter() + .fold(LinearCombination::zero(), |lc, bit| { + let l = lc + (f, &bit.bit); + f = f.double(); + l + }); + let mut f = Scalar::ONE; + let value = bits.values.as_ref().map(|vs| { + vs.iter().fold(Scalar::ZERO, |mut acc, b| { + if *b { + acc += f; + } + f = f.double(); + acc + }) + }); + + let val = value.grab()?; + + cs.enforce( + || "field element equals bits", + |lc| lc + &num, + |lc| lc + CS::one(), + |lc| lc + (*val, CS::one()), + ); + + Ok(Self::new(value, num)) + } + + /// Checks if the natural number equals an array of bits. pub fn is_equal>(&self, mut cs: CS, other: &Bitvector) { let mut f = Scalar::ONE; let sum = other From dd60109e0f2ef65d0f62e9db9817c22d9620ade8 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Thu, 1 Feb 2024 18:08:12 -0500 Subject: [PATCH 14/77] (wip) some more progress --- src/cyclefold/gadgets.rs | 267 +++++++++++++++++++++++++++++--- src/cyclefold/nifs.rs | 4 + src/cyclefold/nova_circuit.rs | 26 ++-- src/gadgets/nonnative/bignat.rs | 33 ++-- 4 files changed, 281 insertions(+), 49 deletions(-) diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index 75a54cac0..68a903899 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -52,15 +52,27 @@ impl AllocatedFoldingData { } pub mod emulated { - use bellpepper_core::boolean::Boolean; + use bellpepper::gadgets::Assignment; + use bellpepper_core::{ + boolean::{AllocatedBit, Boolean}, + num::AllocatedNum, + }; use super::*; use std::marker::PhantomData; - use crate::gadgets::nonnative::bignat::BigNat; - use crate::traits::ROConstantsCircuit; - use crate::RelaxedR1CSInstance; + use crate::{ + constants::{NUM_CHALLENGE_BITS, NUM_FE_FOR_RO}, + gadgets::{ + nonnative::{bignat::BigNat, util::f_to_nat}, + utils::{alloc_bignat_constant, alloc_scalar_as_base, le_bits_to_num, scalar_as_base}, + }, + traits::{Group, ROConstantsCircuit}, + RelaxedR1CSInstance, + }; + + use ff::Field; #[derive(Clone)] pub struct AllocatedPoint @@ -70,7 +82,7 @@ pub mod emulated { { x: BigNat, y: BigNat, - is_infinity: BigNat, + is_infinity: AllocatedNum, _p: PhantomData, } @@ -82,25 +94,144 @@ pub mod emulated { pub fn alloc( mut cs: CS, coords: Option<(E2::Base, E2::Base, bool)>, + limb_width: usize, + n_limbs: usize, ) -> Result where CS: ConstraintSystem<::Base>, { - todo!() + let x = BigNat::alloc_from_nat( + cs.namespace(|| "x"), + || { + Ok(f_to_nat( + &coords.ok_or(SynthesisError::AssignmentMissing)?.0, + )) + }, + limb_width, + n_limbs, + )?; + + let y = BigNat::alloc_from_nat( + cs.namespace(|| "y"), + || { + Ok(f_to_nat( + &coords.ok_or(SynthesisError::AssignmentMissing)?.1, + )) + }, + limb_width, + n_limbs, + )?; + + let is_infinity = AllocatedNum::alloc(cs.namespace(|| "is_infinity"), || { + Ok(if coords.map_or(true, |c| c.2) { + E1::Base::ONE + } else { + E1::Base::ZERO + }) + })?; + cs.enforce( + || "is_infinity is bit", + |lc| lc + is_infinity.get_variable(), + |lc| lc + CS::one() - is_infinity.get_variable(), + |lc| lc, + ); + Ok(Self { x, y, is_infinity }) } - pub fn absorb_in_ro(&self, cs: CS, ro: &mut E1::ROCircuit) -> Result<(), SynthesisError> + pub fn absorb_in_ro(&self, mut cs: CS, ro: &mut E1::ROCircuit) -> Result<(), SynthesisError> where CS: ConstraintSystem<::Base>, { - todo!() + let x_bn = self + .x + .as_limbs() + .iter() + .enumerate() + .map(|(i, limb)| { + limb.as_allocated_num(cs.namespace(|| format!("convert limb {i} of X_r[0] to num"))) + }) + .collect::>, _>>()?; + + for limb in x_bn { + ro.absorb(&limb) + } + + let y_bn = self + .y + .as_limbs() + .iter() + .enumerate() + .map(|(i, limb)| { + limb.as_allocated_num(cs.namespace(|| format!("convert limb {i} of X_r[0] to num"))) + }) + .collect::>, _>>()?; + + for limb in y_bn { + ro.absorb(&limb) + } + + ro.absorb(&self.is_infinity); + + Ok(()) } - pub fn check_on_curve(&self, mut cs: CS) -> Result<(), SynthesisError> + pub fn check_on_curve( + &self, + mut cs: CS, + limb_width: usize, + n_limbs: usize, + ) -> Result<(), SynthesisError> where CS: ConstraintSystem<::Base>, { - todo!() + let m_bn = alloc_bignat_constant( + cs.namespace(|| "alloc m"), + &E1::GE::group_params().3, + limb_width, + n_limbs, + )?; + + let A_bn = alloc_bignat_constant( + cs.namespace(|| "alloc A"), + &f_to_nat(&E1::GE::group_params().0), + limb_width, + n_limbs, + )?; + + let B_bn = alloc_bignat_constant( + cs.namespace(|| "alloc B"), + &f_to_nat(&E1::GE::group_params().1), + limb_width, + n_limbs, + )?; + + let (_, A_x) = A_bn.mult_mod(cs.namespace(|| "A_x"), &self.x, &m_bn)?; + + let (_, x_sq) = self.x.mult_mod(cs.namespace(|| "x_sq"), &self.x, &m_bn)?; + let (_, x_cu) = self.x.mult_mod(cs.namespace(|| "x_cu"), &x_sq, &m_bn)?; + + let rhs = x_cu.add(&A_x)?.add(&B_bn)?; + + let (_, y_sq) = self.y.mult_mod(cs.namespace(|| "y_sq"), &self.y, &m_bn)?; + + let always_equal = AllocatedBit::alloc( + cs.namespace(|| "always_equal = 1 - is_infinity"), + self + .is_infinity + .get_value() + .map(|is_infinity| is_infinity == E1::Base::ONE), + )?; + + cs.enforce( + || "always_equal = 1 - is_infinity", + |lc| lc, + |lc| lc, + |lc| lc + always_equal.get_variable() - CS::one() + self.is_infinity.get_variable(), + ); + + y_sq.equal_when_carried_regroup(cs.namespace(|| "y_sq = rhs"), &rhs, &always_equal)?; + + Ok(()) } } @@ -109,11 +240,11 @@ pub mod emulated { E1: Engine::Scalar>, E2: Engine::Scalar>, { - W: AllocatedPoint, - E: AllocatedPoint, - u: BigNat, - x0: BigNat, - x1: BigNat, + comm_W: AllocatedPoint, + comm_E: AllocatedPoint, + u: AllocatedNum, + x0: AllocatedNum, + x1: AllocatedNum, pub _p: PhantomData, } @@ -125,25 +256,115 @@ pub mod emulated { pub fn alloc( mut cs: CS, inst: Option<&RelaxedR1CSInstance>, + limb_width: usize, + n_limbs: usize, ) -> Result where CS: ConstraintSystem<::Base>, { - todo!() + let comm_W = AllocatedPoint::alloc( + cs.namespace(|| "allocate comm_W"), + inst.map(|x| x.comm_W.to_coordinates()), + limb_width, + n_limbs, + )?; + + let comm_E = AllocatedPoint::alloc( + cs.namespace(|| "allocate comm_E"), + inst.map(|x| x.comm_E.to_coordinates()), + limb_width, + n_limbs, + )?; + + let u = AllocatedNum::alloc(cs.namespace(|| "allocate u"), || { + inst.map_or(Ok(E1::Base::ZERO), |inst| Ok(inst.u)) + })?; + + let x0 = AllocatedNum::alloc(cs.namespace(|| "allocate x0"), || { + inst.map_or(Ok(E1::Base::ZERO), |inst| Ok(inst.X[0])) + })?; + + let x1 = AllocatedNum::alloc(cs.namespace(|| "allocate x1"), || { + inst.map_or(Ok(E1::Base::ZERO), |inst| Ok(inst.X[1])) + })?; + + Ok(Self { + comm_W, + comm_E, + u, + x0, + x1, + _p: PhantomData, + }) } pub fn fold_with_r1cs::Base>>( &self, mut cs: CS, + pp_digest: &AllocatedNum, W_new: AllocatedPoint, E_new: AllocatedPoint, - x0: &BigNat, - x1: &BigNat, + u_W: &AllocatedPoint, + u_x0: &AllocatedNum, + u_x1: &AllocatedNum, + comm_T: &AllocatedPoint, ro_consts: ROConstantsCircuit, - limb_width: usize, - n_limbs: usize, ) -> Result { - todo!() + let mut ro = E1::ROCircuit::new(ro_consts, NUM_FE_FOR_RO); + + ro.absorb(pp_digest); + + // Absorb u + // Absorb the witness + u_W.absorb_in_ro(cs.namespace(|| "absorb u_W"), &mut ro)?; + // Absorb public IO + ro.absorb(u_x0); + ro.absorb(u_x1); + + // Absorb comm_T + comm_T.absorb_in_ro(cs.namespace(|| "absorb comm_T"), &mut ro)?; + + let r_bits = ro.squeeze(cs.namespace(|| "r bits"), NUM_CHALLENGE_BITS)?; + let r = le_bits_to_num(cs.namespace(|| "r"), &r_bits)?; + + let u_fold = AllocatedNum::alloc(cs.namespace(|| "u"), || { + Ok(*self.u.get_value().get()? + r.get_value().get()?) + })?; + cs.enforce( + || "u_fold = u + r", + |lc| lc, + |lc| lc, + |lc| lc + u_fold.get_variable() - self.u.get_variable() - r.get_variable(), + ); + + let x0_fold = AllocatedNum::alloc(cs.namespace(|| "x0"), || { + Ok(*self.x0.get_value().get()? + *r.get_value().get()? * *u_x0.get_value().get()?) + })?; + cs.enforce( + || "x0_fold = x0 + r * u_x0", + |lc| lc + r.get_variable(), + |lc| lc + u_x0.get_variable(), + |lc| lc + x0_fold.get_variable() - self.x0.get_variable(), + ); + + let x1_fold = AllocatedNum::alloc(cs.namespace(|| "x1"), || { + Ok(*self.x1.get_value().get()? + *r.get_value().get()? * *u_x1.get_value().get()?) + })?; + cs.enforce( + || "x1_fold = x1 + r * u_x1", + |lc| lc + r.get_variable(), + |lc| lc + u_x1.get_variable(), + |lc| lc + x1_fold.get_variable() - self.x1.get_variable(), + ); + + Ok(Self { + comm_W: W_new, + comm_E: E_new, + u: u_fold, + x0: x0_fold, + x1: x1_fold, + _p: PhantomData, + }) } pub fn absorb_in_ro(&self, cs: CS, ro: &mut E1::ROCircuit) -> Result<(), SynthesisError> @@ -177,8 +398,8 @@ pub mod emulated { { pub U: AllocatedRelaxedR1CSInstance, pub u_W: AllocatedPoint, - pub u_x0: BigNat, - pub u_x1: BigNat, + pub u_x0: AllocatedNum, + pub u_x1: AllocatedNum, pub T: AllocatedPoint, _p: PhantomData, } diff --git a/src/cyclefold/nifs.rs b/src/cyclefold/nifs.rs index a4e1920d6..d9a927180 100644 --- a/src/cyclefold/nifs.rs +++ b/src/cyclefold/nifs.rs @@ -50,6 +50,7 @@ where } } +/// A SNARK that holds the proof of a step of an incremental computation pub struct NIFS where E1: Engine::Scalar>, @@ -64,6 +65,9 @@ where E1: Engine::Scalar>, E2: Engine::Scalar>, { + /// Takes a relaxed R1CS instance-witness pair (U1, W1) and an R1CS instance-witness pair (U2, W) + /// and folds them into a new relaxed R1CS instance-witness pair (U, W) and a commitment to the + /// cross term T. It also provides the challenge r used to fold the instances. pub fn prove( ck: &CommitmentKey, ro_consts: &ROConstants, diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index b48570fb7..3d839c2a5 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -221,6 +221,8 @@ where .as_ref() .and_then(|inputs| inputs.E_new.as_ref()) .map(|E_new| E_new.to_coordinates()), + self.params.limb_width, + self.params.n_limbs, )?; let W_new = emulated::AllocatedPoint::alloc( @@ -230,6 +232,8 @@ where .as_ref() .and_then(|inputs| inputs.W_new.as_ref()) .map(|W_new| W_new.to_coordinates()), + self.params.limb_width, + self.params.n_limbs, )?; Ok(( @@ -304,13 +308,9 @@ where let hash_bits_p = ro_p.squeeze(cs.namespace(|| "primary hash bits"), NUM_HASH_BITS)?; let hash_p = le_bits_to_num(cs.namespace(|| "primary hash"), &hash_bits_p)?; - let x_0 = data_p - .u_x0 - .as_allocated_num(cs.namespace(|| "u.x[0] as AllocatedNum"))?; - let check_primary = alloc_num_equals( cs.namespace(|| "u.X[0] = H(params, i, z0, zi, U_p)"), - &x_0, + &data_p.u_x0, &hash_p, )?; @@ -323,12 +323,11 @@ where let hash_c_bits = ro_c.squeeze(cs.namespace(|| "cyclefold hash bits"), NUM_HASH_BITS)?; let hash_c = le_bits_to_num(cs.namespace(|| "cyclefold hash"), &hash_c_bits)?; - let x_1 = data_p - .u_x1 - .as_allocated_num(cs.namespace(|| "u.x[1] as AllocatedNum"))?; - - let check_cyclefold = - alloc_num_equals(cs.namespace(|| "u.X[1] = H(params, U_c)"), &x_1, &hash_c)?; + let check_cyclefold = alloc_num_equals( + cs.namespace(|| "u.X[1] = H(params, U_c)"), + &data_p.u_x1, + &hash_c, + )?; let check_io = AllocatedBit::and( cs.namespace(|| "both IOs match"), @@ -384,13 +383,14 @@ where let U_p = data_p.U.fold_with_r1cs( cs.namespace(|| "fold u_p into U_p"), + &pp_digest, W_new.clone(), E_new.clone(), + &data_p.u_W, &data_p.u_x0, &data_p.u_x1, + &data_p.T, self.ro_consts.clone(), - self.params.limb_width, - self.params.n_limbs, )?; Ok((U_c, U_p, checks_pass)) diff --git a/src/gadgets/nonnative/bignat.rs b/src/gadgets/nonnative/bignat.rs index cbae26a67..9830f4e57 100644 --- a/src/gadgets/nonnative/bignat.rs +++ b/src/gadgets/nonnative/bignat.rs @@ -4,7 +4,9 @@ use super::{ }, OptionExt, }; -use bellpepper_core::{num::AllocatedNum, ConstraintSystem, LinearCombination, SynthesisError}; +use bellpepper_core::{ + boolean::AllocatedBit, num::AllocatedNum, ConstraintSystem, LinearCombination, SynthesisError, +}; use ff::PrimeField; use itertools::Itertools as _; use num_bigint::BigInt; @@ -357,10 +359,11 @@ impl BigNat { &self, mut cs: CS, other: &Self, + always_equal: &AllocatedBit, ) -> Result<(), SynthesisError> { self.enforce_limb_width_agreement(other, "equal_when_carried")?; - // We'll propegate carries over the first `n` limbs. + // We'll propagate carries over the first `n` limbs. let n = min(self.limbs.len(), other.limbs.len()); let target_base = BigInt::from(1u8) << self.params.limb_width as u32; let mut accumulated_extra = BigInt::from(0usize); @@ -387,8 +390,7 @@ impl BigNat { cs.enforce( || format!("carry {i}"), - |lc| lc, - |lc| lc, + |lc| lc + CS::one() - always_equal.get_variable(), |lc| { lc + &carry_in.num + &self.limbs[i] - &other.limbs[i] + (nat_to_f(max_word).unwrap(), CS::one()) @@ -398,6 +400,7 @@ impl BigNat { CS::one(), ) }, + |lc| lc, ); accumulated_extra /= &target_base; @@ -407,9 +410,9 @@ impl BigNat { } else { cs.enforce( || format!("carry {i} is out"), - |lc| lc, - |lc| lc, + |lc| lc + CS::one() - always_equal.get_variable(), |lc| lc + &carry.num - (nat_to_f(&accumulated_extra).unwrap(), CS::one()), + |lc| lc, ); } carry_in = carry; @@ -418,17 +421,17 @@ impl BigNat { for (i, zero_limb) in self.limbs.iter().enumerate().skip(n) { cs.enforce( || format!("zero self {i}"), - |lc| lc, - |lc| lc, + |lc| lc + CS::one() - always_equal.get_variable(), |lc| lc + zero_limb, + |lc| lc, ); } for (i, zero_limb) in other.limbs.iter().enumerate().skip(n) { cs.enforce( || format!("zero other {i}"), - |lc| lc, - |lc| lc, + |lc| lc + CS::one() - always_equal.get_variable(), |lc| lc + zero_limb, + |lc| lc, ); } Ok(()) @@ -441,6 +444,7 @@ impl BigNat { &self, mut cs: CS, other: &Self, + always_equal: &AllocatedBit, ) -> Result<(), SynthesisError> { self.enforce_limb_width_agreement(other, "equal_when_carried_regroup")?; let max_word = max(&self.params.max_word, &other.params.max_word); @@ -454,7 +458,7 @@ impl BigNat { let self_grouped = self.group_limbs(limbs_per_group); let other_grouped = other.group_limbs(limbs_per_group); - self_grouped.equal_when_carried(cs.namespace(|| "grouped"), &other_grouped) + self_grouped.equal_when_carried(cs.namespace(|| "grouped"), &other_grouped, always_equal) } pub fn add(&self, other: &Self) -> Result { @@ -568,7 +572,9 @@ impl BigNat { let left_int = Self::from_poly(left, limb_width, left_max_word); let right_int = Self::from_poly(right, limb_width, right_max_word); - left_int.equal_when_carried_regroup(cs.namespace(|| "carry"), &right_int)?; + + let always_equal = AllocatedBit::alloc(cs.namespace(|| "always_equal = false"), Some(false))?; + left_int.equal_when_carried_regroup(cs.namespace(|| "carry"), &right_int, &always_equal)?; Ok((quotient, remainder)) } @@ -613,7 +619,8 @@ impl BigNat { }; let right_int = Self::from_poly(right, limb_width, right_max_word); - self.equal_when_carried_regroup(cs.namespace(|| "carry"), &right_int)?; + let always_equal = AllocatedBit::alloc(cs.namespace(|| "always_equal = false"), Some(false))?; + self.equal_when_carried_regroup(cs.namespace(|| "carry"), &right_int, &always_equal)?; Ok(remainder) } From de9fbbed9f0113f56b998fa6a508e7ac39bc55f1 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Sun, 4 Feb 2024 18:19:36 -0500 Subject: [PATCH 15/77] (wip) Gadgets done, some cleanup --- src/cyclefold/gadgets.rs | 225 ++++++++++++++++++++++++++++---- src/cyclefold/nova_circuit.rs | 14 ++ src/cyclefold/snark.rs | 68 ++++++++-- src/gadgets/nonnative/bignat.rs | 24 ++-- src/gadgets/nonnative/util.rs | 67 +++++----- 5 files changed, 317 insertions(+), 81 deletions(-) diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index 68a903899..4580fa59a 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -37,18 +37,19 @@ impl AllocatedFoldingData { Ok(Self { U, u, T }) } - pub fn absorb_in_ro::Base>>( - &self, - mut cs: CS, - ro: &mut E::ROCircuit, - ) -> Result<(), SynthesisError> { - self.U.absorb_in_ro(cs.namespace(|| "absorb U"), ro)?; - self.u.absorb_in_ro(ro); - ro.absorb(&self.T.x); - ro.absorb(&self.T.y); - ro.absorb(&self.T.is_infinity); - Ok(()) - } + // TODO: Delete if not needed, or uncomment if this saves time + // pub fn absorb_in_ro::Base>>( + // &self, + // mut cs: CS, + // ro: &mut E::ROCircuit, + // ) -> Result<(), SynthesisError> { + // self.U.absorb_in_ro(cs.namespace(|| "absorb U"), ro)?; + // self.u.absorb_in_ro(ro); + // ro.absorb(&self.T.x); + // ro.absorb(&self.T.y); + // ro.absorb(&self.T.is_infinity); + // Ok(()) + // } } pub mod emulated { @@ -66,7 +67,10 @@ pub mod emulated { constants::{NUM_CHALLENGE_BITS, NUM_FE_FOR_RO}, gadgets::{ nonnative::{bignat::BigNat, util::f_to_nat}, - utils::{alloc_bignat_constant, alloc_scalar_as_base, le_bits_to_num, scalar_as_base}, + utils::{ + alloc_bignat_constant, alloc_one, alloc_zero, conditionally_select, + conditionally_select_bignat, le_bits_to_num, /*scalar_as_base,*/ + }, }, traits::{Group, ROConstantsCircuit}, RelaxedR1CSInstance, @@ -233,6 +237,60 @@ pub mod emulated { Ok(()) } + + fn conditionally_select::Base>>( + &self, + mut cs: CS, + other: &Self, + condition: &Boolean, + ) -> Result { + let x = conditionally_select_bignat( + cs.namespace(|| "x = cond ? self.x : other.x"), + &self.x, + &other.x, + condition, + )?; + + let y = conditionally_select_bignat( + cs.namespace(|| "y = cond ? self.y : other.y"), + &self.y, + &other.y, + condition, + )?; + + let is_infinity = conditionally_select( + cs.namespace(|| "is_infinity = cond ? self.is_infinity : other.is_infinity"), + &self.is_infinity, + &other.is_infinity, + condition, + )?; + + Ok(Self { x, y, is_infinity }) + } + + pub fn default::Base>>( + mut cs: CS, + limb_width: usize, + n_limbs: usize, + ) -> Result { + let x = BigNat::alloc_from_nat( + cs.namespace(|| "allocate x_default = 0"), + || Ok(f_to_nat(&E1::Scalar::ZERO)), + limb_width, + n_limbs, + )?; + + let y = BigNat::alloc_from_nat( + cs.namespace(|| "allocate y_default = 0"), + || Ok(f_to_nat(&E1::Scalar::ZERO)), + limb_width, + n_limbs, + )?; + + let is_infinity = alloc_one(cs.namespace(|| "one")); + + Ok(Self { x, y, is_infinity }) + } } pub struct AllocatedRelaxedR1CSInstance @@ -367,20 +425,71 @@ pub mod emulated { }) } - pub fn absorb_in_ro(&self, cs: CS, ro: &mut E1::ROCircuit) -> Result<(), SynthesisError> + pub fn absorb_in_ro(&self, mut cs: CS, ro: &mut E1::ROCircuit) -> Result<(), SynthesisError> where CS: ConstraintSystem<::Base>, { - todo!() + self + .comm_E + .absorb_in_ro(cs.namespace(|| "absorb comm_E"), ro)?; + self + .comm_W + .absorb_in_ro(cs.namespace(|| "absorb comm_W"), ro)?; + + ro.absorb(&self.u); + ro.absorb(&self.x0); + ro.absorb(&self.x1); + + Ok(()) } pub fn conditionally_select::Base>>( &self, - cs: CS, + mut cs: CS, other: &Self, condition: &Boolean, ) -> Result { - todo!() + let comm_W = self.comm_W.conditionally_select( + cs.namespace(|| "comm_W = cond ? self.comm_W : other.comm_W"), + &other.comm_W, + condition, + )?; + + let comm_E = self.comm_E.conditionally_select( + cs.namespace(|| "comm_E = cond? self.comm_E : other.comm_E"), + &other.comm_E, + condition, + )?; + + let u = conditionally_select( + cs.namespace(|| "u = cond ? self.u : other.u"), + &self.u, + &other.u, + condition, + )?; + + let x0 = conditionally_select( + cs.namespace(|| "x0 = cond ? self.x0 : other.x0"), + &self.x0, + &other.x0, + condition, + )?; + + let x1 = conditionally_select( + cs.namespace(|| "x1 = cond ? self.x1 : other.x1"), + &self.x1, + &other.x1, + condition, + )?; + + Ok(Self { + comm_W, + comm_E, + u, + x0, + x1, + _p: PhantomData, + }) } pub fn default::Base>>( @@ -388,7 +497,22 @@ pub mod emulated { limb_width: usize, n_limbs: usize, ) -> Result { - todo!() + let comm_W = AllocatedPoint::default(cs.namespace(|| "default comm_W"), limb_width, n_limbs)?; + let comm_E = comm_W.clone(); + + let u = alloc_zero(cs.namespace(|| "u = 0")); + + let x0 = u.clone(); + let x1 = u.clone(); + + Ok(Self { + comm_W, + comm_E, + u, + x0, + x1, + _p: PhantomData, + }) } } pub struct AllocatedFoldingData @@ -409,18 +533,67 @@ pub mod emulated { E1: Engine::Scalar>, E2: Engine::Scalar>, { - pub fn alloc(mut cs: CS, inst: Option<&FoldingData>) -> Result + pub fn alloc( + mut cs: CS, + inst: Option<&FoldingData>, + limb_width: usize, + n_limbs: usize, + ) -> Result where CS: ConstraintSystem<::Base>, { - todo!() - } + let U = AllocatedRelaxedR1CSInstance::alloc( + cs.namespace(|| "allocate U"), + inst.map(|x| &x.U), + limb_width, + n_limbs, + )?; - pub fn absorb_in_ro(&self, cs: CS, ro: &mut E1::ROCircuit) -> Result<(), SynthesisError> - where - CS: ConstraintSystem<::Base>, - { - todo!() + let u_W = AllocatedPoint::alloc( + cs.namespace(|| "allocate u_W"), + inst.map(|x| x.u.comm_W.to_coordinates()), + limb_width, + n_limbs, + )?; + + let u_x0 = AllocatedNum::alloc(cs.namespace(|| "allocate u_x0"), || { + inst.map_or(Ok(E1::Base::ZERO), |inst| Ok(inst.u.X[0])) + })?; + + let u_x1 = AllocatedNum::alloc(cs.namespace(|| "allocate u_x1"), || { + inst.map_or(Ok(E1::Base::ZERO), |inst| Ok(inst.u.X[1])) + })?; + + let T = AllocatedPoint::alloc( + cs.namespace(|| "allocate T"), + inst.map(|x| x.T.to_coordinates()), + limb_width, + n_limbs, + )?; + + T.check_on_curve(cs.namespace(|| "T on curve"), limb_width, n_limbs)?; + + Ok(Self { + U, + u_W, + u_x0, + u_x1, + T, + _p: PhantomData, + }) } + + // TODO: Delete if not needed + // pub fn absorb_in_ro(&self, mut cs: CS, ro: &mut E1::ROCircuit) -> Result<(), SynthesisError> + // where + // CS: ConstraintSystem<::Base>, + // { + // self.U.absorb_in_ro(cs.namespace(|| "absorb U"), ro)?; + // self.u_W.absorb_in_ro(cs.namespace(|| "absorb u_W"), ro)?; + // ro.absorb(&self.u_x0); + // ro.absorb(&self.u_x1); + // self.T.absorb_in_ro(cs.namespace(|| "absorb T"), ro)?; + // Ok(()) + // } } } diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index 3d839c2a5..e66e7f278 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -192,6 +192,8 @@ where .inputs .as_ref() .and_then(|inputs| inputs.data_p.as_ref()), + self.params.limb_width, + self.params.n_limbs, )?; let data_c_1 = AllocatedFoldingData::alloc( @@ -225,6 +227,12 @@ where self.params.n_limbs, )?; + E_new.check_on_curve( + cs.namespace(|| "E_new on curve"), + self.params.limb_width, + self.params.n_limbs, + )?; + let W_new = emulated::AllocatedPoint::alloc( cs.namespace(|| "W_new"), self @@ -236,6 +244,12 @@ where self.params.n_limbs, )?; + W_new.check_on_curve( + cs.namespace(|| "W_new on curve"), + self.params.limb_width, + self.params.n_limbs, + )?; + Ok(( pp_digest, i, z_0, z_i, data_p, data_c_1, data_c_2, E_new, W_new, )) diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index c48f973ff..3bde97fac 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -4,7 +4,12 @@ use std::marker::PhantomData; use crate::{ - bellpepper::{r1cs::NovaWitness, solver::SatisfyingAssignment}, + bellpepper::{ + r1cs::{NovaShape, NovaWitness}, + shape_cs::ShapeCS, + solver::SatisfyingAssignment, + }, + constants::{BN_LIMB_WIDTH, BN_N_LIMBS}, cyclefold::circuit::{CyclefoldCircuit, CyclefoldCircuitInputs}, errors::NovaError, gadgets::utils::scalar_as_base, @@ -48,7 +53,7 @@ where ro_consts_circuit_primary: ROConstantsCircuit, ck_primary: CommitmentKey, circuit_shape_primary: R1CSWithArity, - augmented_circuit_params_primary: AugmentedCircuitParams, + augmented_circuit_params: AugmentedCircuitParams, ro_consts_cyclefold: ROConstants, ck_cyclefold: CommitmentKey, @@ -67,11 +72,46 @@ where { /// TODO: docs pub fn setup( - _c_primary: &C1, - _ck_hint1: &CommitmentKeyHint, - _ck_hint_cyclefold: &CommitmentKeyHint, + c_primary: &C1, + ck_hint1: &CommitmentKeyHint, + ck_hint_cyclefold: &CommitmentKeyHint, ) -> Self { - todo!() + let F_arity_primary = c_primary.arity(); + let ro_consts_primary = ROConstants::::default(); + let ro_consts_circuit_primary = ROConstantsCircuit::::default(); + + let augmented_circuit_params = AugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS); + let circuit_primary: AugmentedCircuit<'_, E2, E1, C1> = AugmentedCircuit::new( + &augmented_circuit_params, + ro_consts_circuit_primary.clone(), + None, + c_primary, + ); + let mut cs: ShapeCS = ShapeCS::new(); + let _ = circuit_primary.synthesize(&mut cs); + let (r1cs_shape_primary, ck_primary) = cs.r1cs_shape_and_key(ck_hint1); + let circuit_shape_primary = R1CSWithArity::new(r1cs_shape_primary, F_arity_primary); + + let ro_consts_cyclefold = ROConstants::::default(); + let mut cs: ShapeCS = ShapeCS::new(); + let circuit_cyclefold: CyclefoldCircuit = CyclefoldCircuit::new(None); + let _ = circuit_cyclefold.synthesize(&mut cs); + let (r1cs_shape_cyclefold, ck_cyclefold) = cs.r1cs_shape_and_key(ck_hint_cyclefold); + let circuit_shape_cyclefold = R1CSWithArity::new(r1cs_shape_cyclefold, 0); + + Self { + F_arity_primary, + ro_consts_primary, + ro_consts_circuit_primary, + ck_primary, + circuit_shape_primary, + augmented_circuit_params, + ro_consts_cyclefold, + ck_cyclefold, + circuit_shape_cyclefold, + digest: OnceCell::new(), + _p: PhantomData, + } } /// TODO: docs @@ -84,13 +124,19 @@ where } /// TODO: docs - pub const fn num_constraints(&self) -> usize { - todo!() + pub const fn num_constraints(&self) -> (usize, usize) { + ( + self.circuit_shape_primary.r1cs_shape.num_cons, + self.circuit_shape_cyclefold.r1cs_shape.num_cons, + ) } /// TODO: docs - pub const fn num_variables(&self) -> usize { - todo!() + pub const fn num_variables(&self) -> (usize, usize) { + ( + self.circuit_shape_primary.r1cs_shape.num_vars, + self.circuit_shape_cyclefold.r1cs_shape.num_vars, + ) } } @@ -284,7 +330,7 @@ where ); let circuit_primary: AugmentedCircuit<'_, E2, E1, C1> = AugmentedCircuit::new( - &pp.augmented_circuit_params_primary, + &pp.augmented_circuit_params, pp.ro_consts_circuit_primary.clone(), Some(inputs_primary), c_primary, diff --git a/src/gadgets/nonnative/bignat.rs b/src/gadgets/nonnative/bignat.rs index 9830f4e57..be4062b98 100644 --- a/src/gadgets/nonnative/bignat.rs +++ b/src/gadgets/nonnative/bignat.rs @@ -5,7 +5,8 @@ use super::{ OptionExt, }; use bellpepper_core::{ - boolean::AllocatedBit, num::AllocatedNum, ConstraintSystem, LinearCombination, SynthesisError, + boolean::AllocatedBit, /*num::AllocatedNum,*/ ConstraintSystem, LinearCombination, + SynthesisError, }; use ff::PrimeField; use itertools::Itertools as _; @@ -259,16 +260,17 @@ impl BigNat { limbs } - // NOTE: This is quick and dirty and is bad - /// Transforms a BigNat into an AllocatedNum (makes no check on whether it's bigger than the field) - pub fn as_allocated_num>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let bits = self.decompose(cs.namespace(|| "decompse as bits"))?; - let num = Num::from_bits(cs.namespace(|| "num from bits"), &bits)?; - num.as_allocated_num(cs.namespace(|| "as_allocated_num")) - } + // TODO: This probably can be deleted + // // NOTE: This is quick and dirty and is bad + // /// Transforms a BigNat into an AllocatedNum (makes no check on whether it's bigger than the field) + // pub fn as_allocated_num>( + // &self, + // mut cs: CS, + // ) -> Result, SynthesisError> { + // let bits = self.decompose(cs.namespace(|| "decompse as bits"))?; + // let num = Num::from_bits(cs.namespace(|| "num from bits"), &bits)?; + // num.as_allocated_num(cs.namespace(|| "as_allocated_num")) + // } pub fn assert_well_formed>( &self, diff --git a/src/gadgets/nonnative/util.rs b/src/gadgets/nonnative/util.rs index af1a07271..4a59c0cc5 100644 --- a/src/gadgets/nonnative/util.rs +++ b/src/gadgets/nonnative/util.rs @@ -155,42 +155,43 @@ impl Num { Ok(()) } - /// Computes the natural number represented by an array of bits. - pub fn from_bits>( - mut cs: CS, - bits: &Bitvector, - ) -> Result { - let allocations = bits.allocations.clone(); - let mut f = Scalar::ONE; - let num = allocations - .iter() - .fold(LinearCombination::zero(), |lc, bit| { - let l = lc + (f, &bit.bit); - f = f.double(); - l - }); - let mut f = Scalar::ONE; - let value = bits.values.as_ref().map(|vs| { - vs.iter().fold(Scalar::ZERO, |mut acc, b| { - if *b { - acc += f; - } - f = f.double(); - acc - }) - }); + // TOOD: Can probably delete this + // /// Computes the natural number represented by an array of bits. + // pub fn from_bits>( + // mut cs: CS, + // bits: &Bitvector, + // ) -> Result { + // let allocations = bits.allocations.clone(); + // let mut f = Scalar::ONE; + // let num = allocations + // .iter() + // .fold(LinearCombination::zero(), |lc, bit| { + // let l = lc + (f, &bit.bit); + // f = f.double(); + // l + // }); + // let mut f = Scalar::ONE; + // let value = bits.values.as_ref().map(|vs| { + // vs.iter().fold(Scalar::ZERO, |mut acc, b| { + // if *b { + // acc += f; + // } + // f = f.double(); + // acc + // }) + // }); - let val = value.grab()?; + // let val = value.grab()?; - cs.enforce( - || "field element equals bits", - |lc| lc + &num, - |lc| lc + CS::one(), - |lc| lc + (*val, CS::one()), - ); + // cs.enforce( + // || "field element equals bits", + // |lc| lc + &num, + // |lc| lc + CS::one(), + // |lc| lc + (*val, CS::one()), + // ); - Ok(Self::new(value, num)) - } + // Ok(Self::new(value, num)) + // } /// Checks if the natural number equals an array of bits. pub fn is_equal>(&self, mut cs: CS, other: &Bitvector) { From bdcd68dcf929cc77ce2a754d0231a2afeb72cf53 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Mon, 5 Feb 2024 12:21:36 -0500 Subject: [PATCH 16/77] (wip) Scalar <-> Base confusion in verify --- src/cyclefold/snark.rs | 183 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 171 insertions(+), 12 deletions(-) diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 3bde97fac..9a41b93ca 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -9,13 +9,19 @@ use crate::{ shape_cs::ShapeCS, solver::SatisfyingAssignment, }, - constants::{BN_LIMB_WIDTH, BN_N_LIMBS}, + constants::{BN_LIMB_WIDTH, BN_N_LIMBS, NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_BITS}, cyclefold::circuit::{CyclefoldCircuit, CyclefoldCircuitInputs}, errors::NovaError, gadgets::utils::scalar_as_base, nifs::NIFS, - r1cs::{CommitmentKeyHint, R1CSInstance, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness}, - traits::{circuit::StepCircuit, commitment::CommitmentTrait, Engine, ROConstantsCircuit}, + r1cs::{ + self, CommitmentKeyHint, R1CSInstance, R1CSResult, R1CSWitness, RelaxedR1CSInstance, + RelaxedR1CSWitness, + }, + traits::{ + circuit::StepCircuit, commitment::CommitmentTrait, AbsorbInROTrait, Engine, ROConstantsCircuit, + ROTrait, + }, Commitment, CommitmentKey, DigestComputer, R1CSWithArity, ROConstants, ResourceBuffer, SimpleDigestible, }; @@ -26,7 +32,7 @@ use super::nova_circuit::{ use abomonation::Abomonation; use abomonation_derive::Abomonation; -use bellpepper_core::SynthesisError; +use bellpepper_core::{ConstraintSystem, SynthesisError}; use ff::{PrimeField, PrimeFieldBits}; use once_cell::sync::OnceCell; use serde::{Deserialize, Serialize}; @@ -188,11 +194,83 @@ where { /// TODO: docs pub fn new( - _pp: &PublicParams, - _c_primary: &C1, - _z0_primary: &[E1::Scalar], + pp: &PublicParams, + c_primary: &C1, + z0_primary: &[E1::Scalar], ) -> Result { - todo!() + if z0_primary.len() != pp.F_arity_primary { + return Err(NovaError::InvalidInitialInputLength); + } + + let r1cs_primary = &pp.circuit_shape_primary.r1cs_shape; + let r1cs_cyclefold = &pp.circuit_shape_cyclefold.r1cs_shape; + + let r_U_cyclefold = RelaxedR1CSInstance::default(&pp.ck_cyclefold, r1cs_cyclefold); + let r_W_cyclefold = RelaxedR1CSWitness::default(r1cs_cyclefold); + + let mut cs_primary = SatisfyingAssignment::::new(); + let inputs_primary: AugmentedCircuitInputs = AugmentedCircuitInputs::new( + scalar_as_base::(pp.digest()), + ::Base::from(0u64), + z0_primary.to_vec(), + None, + None, + None, + None, + None, + None, + ); + + let circuit_primary = AugmentedCircuit::new( + &pp.augmented_circuit_params, + pp.ro_consts_circuit_primary.clone(), + Some(inputs_primary), + c_primary, + ); + + let zi_primary = circuit_primary.synthesize(&mut cs_primary)?; + let (l_u_primary, l_w_primary) = + cs_primary.r1cs_instance_and_witness(r1cs_primary, &pp.ck_primary)?; + + let r_U_primary = + RelaxedR1CSInstance::from_r1cs_instance(&pp.ck_primary, r1cs_primary, l_u_primary); + let r_W_primary = RelaxedR1CSWitness::from_r1cs_witness(r1cs_primary, l_w_primary); + + let zi_primary = zi_primary + .iter() + .map(|v| v.get_value().ok_or(SynthesisError::AssignmentMissing)) + .collect::, _>>()?; + + let buffer_primary = ResourceBuffer { + l_w: None, + l_u: None, + ABC_Z_1: R1CSResult::default(r1cs_primary.num_cons), + ABC_Z_2: R1CSResult::default(r1cs_primary.num_cons), + T: r1cs::default_T::(r1cs_primary.num_cons), + }; + + let buffer_cyclefold = ResourceBuffer { + l_w: None, + l_u: None, + ABC_Z_1: R1CSResult::default(r1cs_cyclefold.num_cons), + ABC_Z_2: R1CSResult::default(r1cs_cyclefold.num_cons), + T: r1cs::default_T::(r1cs_cyclefold.num_cons), + }; + + Ok(Self { + z0_primary: z0_primary.to_vec(), + r_W_primary, + r_U_primary, + l_w_primary, + l_u_primary, + r_W_cyclefold, + r_U_cyclefold, + buffer_primary, + buffer_cyclefold, + i: 1, + zi_primary, + _p: PhantomData, + }) } /// TODO: docs @@ -362,11 +440,92 @@ where /// TODO: docs pub fn verify( &self, - _pp: &PublicParams, - _num_steps: usize, - _z0_primary: &[E1::Scalar], + pp: &PublicParams, + num_steps: usize, + z0_primary: &[E1::Scalar], ) -> Result, NovaError> { - todo!() + // number of steps cannot be zero + let is_num_steps_zero = num_steps == 0; + + // check if the provided proof has executed num_steps + let is_num_steps_not_match = self.i != num_steps; + + // check if the initial inputs match + let is_inputs_not_match = self.z0_primary != z0_primary; + + // check if the (relaxed) R1CS instances have two public outputs + let is_instance_has_two_outpus = self.r_U_primary.X.len() != 2; + + if is_num_steps_zero + || is_num_steps_not_match + || is_inputs_not_match + || is_instance_has_two_outpus + { + return Err(NovaError::ProofVerifyError); + } + + let (hash_primary, hash_cyclefold) = { + let mut hasher = ::RO::new( + pp.ro_consts_primary.clone(), + NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * pp.F_arity_primary, + ); + hasher.absorb(pp.digest()); + hasher.absorb(E1::Scalar::from(num_steps as u64)); + for e in z0_primary { + hasher.absorb(*e); + } + for e in &self.zi_primary { + hasher.absorb(*e); + } + self.r_U_primary.absorb_in_ro(&mut hasher); + let hash_primary = hasher.squeeze(NUM_HASH_BITS); + + let mut hasher = + ::RO::new(pp.ro_consts_cyclefold.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF); + hasher.absorb(pp.digest()); + self.r_U_cyclefold.absorb_in_ro(&mut hasher); + let hash_cyclefold = hasher.squeeze(NUM_HASH_BITS); + + (hash_primary, hash_cyclefold) + }; + + if hash_primary != self.l_u_primary.X[0] || hash_cyclefold != self.l_u_primary.X[1] { + return Err(NovaError::ProofVerifyError); + } + + let (res_r_primary, (res_l_primary, res_r_cyclefold)) = rayon::join( + || { + pp.circuit_shape_primary.r1cs_shape.is_sat_relaxed( + &pp.ck_primary, + &self.r_U_primary, + &self.r_W_primary, + ) + }, + || { + rayon::join( + || { + pp.circuit_shape_primary.r1cs_shape.is_sat( + &pp.ck_primary, + &self.l_u_primary, + &self.l_w_primary, + ) + }, + || { + pp.circuit_shape_cyclefold.r1cs_shape.is_sat_relaxed( + &pp.ck_cyclefold, + &self.r_U_cyclefold, + &self.r_W_cyclefold, + ) + }, + ) + }, + ); + + res_r_primary?; + res_l_primary?; + res_r_cyclefold?; + + Ok(self.zi_primary.clone()) } } From ea699367f7110afdd1a698889d73050c833f9f56 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Mon, 5 Feb 2024 16:42:22 -0500 Subject: [PATCH 17/77] it builds! --- src/cyclefold/nifs.rs | 3 ++- src/cyclefold/snark.rs | 34 ++++++++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/cyclefold/nifs.rs b/src/cyclefold/nifs.rs index d9a927180..f9660e503 100644 --- a/src/cyclefold/nifs.rs +++ b/src/cyclefold/nifs.rs @@ -16,7 +16,8 @@ use crate::{ Commitment, CommitmentKey, CompressedCommitment, }; -fn absorb_commitment(comm: &Commitment, ro: &mut E2::RO) +/// TODO: Docs +pub fn absorb_commitment(comm: &Commitment, ro: &mut E2::RO) where E1: Engine::Scalar>, E2: Engine::Scalar>, diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 9a41b93ca..17e89e17d 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -26,8 +26,9 @@ use crate::{ SimpleDigestible, }; -use super::nova_circuit::{ - AugmentedCircuit, AugmentedCircuitInputs, AugmentedCircuitParams, FoldingData, +use super::{ + nifs::absorb_commitment, + nova_circuit::{AugmentedCircuit, AugmentedCircuitInputs, AugmentedCircuitParams, FoldingData}, }; use abomonation::Abomonation; @@ -233,8 +234,8 @@ where cs_primary.r1cs_instance_and_witness(r1cs_primary, &pp.ck_primary)?; let r_U_primary = - RelaxedR1CSInstance::from_r1cs_instance(&pp.ck_primary, r1cs_primary, l_u_primary); - let r_W_primary = RelaxedR1CSWitness::from_r1cs_witness(r1cs_primary, l_w_primary); + RelaxedR1CSInstance::from_r1cs_instance(&pp.ck_primary, r1cs_primary, l_u_primary.clone()); + let r_W_primary = RelaxedR1CSWitness::from_r1cs_witness(r1cs_primary, l_w_primary.clone()); let zi_primary = zi_primary .iter() @@ -465,7 +466,7 @@ where } let (hash_primary, hash_cyclefold) = { - let mut hasher = ::RO::new( + let mut hasher = ::RO::new( pp.ro_consts_primary.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * pp.F_arity_primary, ); @@ -477,11 +478,11 @@ where for e in &self.zi_primary { hasher.absorb(*e); } - self.r_U_primary.absorb_in_ro(&mut hasher); + absorb_relaxed_r1cs::(&self.r_U_primary, &mut hasher); let hash_primary = hasher.squeeze(NUM_HASH_BITS); let mut hasher = - ::RO::new(pp.ro_consts_cyclefold.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF); + ::RO::new(pp.ro_consts_cyclefold.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF); hasher.absorb(pp.digest()); self.r_U_cyclefold.absorb_in_ro(&mut hasher); let hash_cyclefold = hasher.squeeze(NUM_HASH_BITS); @@ -489,7 +490,11 @@ where (hash_primary, hash_cyclefold) }; - if hash_primary != self.l_u_primary.X[0] || hash_cyclefold != self.l_u_primary.X[1] { + // TODO: This seems like it might be a bad sign, I don't know if I should need to use + // `scalar_as_base` here + if scalar_as_base::(hash_primary) != self.l_u_primary.X[0] + || scalar_as_base::(hash_cyclefold) != self.l_u_primary.X[1] + { return Err(NovaError::ProofVerifyError); } @@ -529,6 +534,19 @@ where } } +fn absorb_relaxed_r1cs(U: &RelaxedR1CSInstance, ro: &mut E2::RO) +where + E1: Engine::Scalar>, + E2: Engine::Scalar>, +{ + absorb_commitment::(&U.comm_W, ro); + absorb_commitment::(&U.comm_E, ro); + for e in &U.X { + ro.absorb(*e); + } + ro.absorb(U.u); +} + #[cfg(test)] mod test { // use super::*; From 56e0b598bab4710a02f3214528c0cbdf12cafa66 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Mon, 5 Feb 2024 16:58:55 -0500 Subject: [PATCH 18/77] make clippy happy --- src/cyclefold/nova_circuit.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index e66e7f278..5b2642786 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -23,7 +23,7 @@ use serde::{Deserialize, Serialize}; use super::gadgets::{emulated, AllocatedFoldingData}; -#[derive(Clone, PartialEq, Serialize, Deserialize, Abomonation)] +#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Abomonation)] pub struct AugmentedCircuitParams { limb_width: usize, n_limbs: usize, @@ -397,7 +397,7 @@ where let U_p = data_p.U.fold_with_r1cs( cs.namespace(|| "fold u_p into U_p"), - &pp_digest, + pp_digest, W_new.clone(), E_new.clone(), &data_p.u_W, From bf01a0b526fd05b3232d8fc2ebdafe98eff10c99 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Mon, 5 Feb 2024 17:21:01 -0500 Subject: [PATCH 19/77] Delete `RecursiveSNARKTrait` and cleanup --- src/cyclefold/nifs.rs | 1 + src/cyclefold/nova_circuit.rs | 2 +- src/cyclefold/snark.rs | 2 +- src/lib.rs | 88 ----------------------- src/supernova/mod.rs | 130 ---------------------------------- src/traits/mod.rs | 1 - src/traits/recursive.rs | 42 ----------- 7 files changed, 3 insertions(+), 263 deletions(-) delete mode 100644 src/traits/recursive.rs diff --git a/src/cyclefold/nifs.rs b/src/cyclefold/nifs.rs index f9660e503..c6cf0cb67 100644 --- a/src/cyclefold/nifs.rs +++ b/src/cyclefold/nifs.rs @@ -52,6 +52,7 @@ where } /// A SNARK that holds the proof of a step of an incremental computation +#[derive(Debug)] pub struct NIFS where E1: Engine::Scalar>, diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index 5b2642786..aa5a5d15a 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -23,7 +23,7 @@ use serde::{Deserialize, Serialize}; use super::gadgets::{emulated, AllocatedFoldingData}; -#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Abomonation)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Abomonation)] pub struct AugmentedCircuitParams { limb_width: usize, n_limbs: usize, diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 17e89e17d..d0acb2173 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -39,7 +39,7 @@ use once_cell::sync::OnceCell; use serde::{Deserialize, Serialize}; /// TODO: docs -#[derive(Clone, PartialEq, Serialize, Deserialize, Abomonation)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Abomonation)] #[serde(bound = "")] #[abomonation_bounds( where diff --git a/src/lib.rs b/src/lib.rs index db41e3b36..6907770c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,8 +29,6 @@ pub mod cyclefold; pub mod supernova; use once_cell::sync::OnceCell; -use traits::circuit::TrivialCircuit; -use traits::recursive::RecursiveSNARKTrait; use traits::{CurveCycleEquipped, Dual}; use crate::digest::{DigestComputer, SimpleDigestible}; @@ -760,92 +758,6 @@ where } } -impl RecursiveSNARKTrait - for RecursiveSNARK> -where - E1: Engine::Scalar>, - E2: Engine::Scalar>, - C: StepCircuit, -{ - type PublicParams = PublicParams>; - - type Proof = Self; - - fn new( - pp: &Self::PublicParams, - c_primary: &C, - z0_primary: &[::Scalar], - ) -> Result { - let c_secondary = TrivialCircuit::<::Scalar>::default(); - let z0_secondary = vec![::Scalar::ZERO; pp.F_arity_secondary]; - - Self::Proof::new(&pp, c_primary, &c_secondary, z0_primary, &z0_secondary) - } - - fn prove_step( - proof: &mut Self::Proof, - pp: &Self::PublicParams, - c_primary: &C, - ) -> Result<(), NovaError> { - let c_secondary = TrivialCircuit::default(); - proof.prove_step(&pp, c_primary, &c_secondary) - } - - fn verify( - proof: &Self::Proof, - pp: &Self::PublicParams, - z0_primary: &[::Scalar], - ) -> Result::Scalar>, NovaError> { - let z0_secondary = vec![::Scalar::ZERO; pp.F_arity_secondary]; - proof - .verify(&pp, proof.num_steps(), z0_primary, &z0_secondary) - .map(|(zi_primary, _)| zi_primary) - } -} - -impl RecursiveSNARKTrait - for RecursiveSNARK> -where - E1: Engine::Scalar>, - E2: Engine::Scalar>, - C: StepCircuit, -{ - type PublicParams = PublicParams>; - - type Proof = Self; - - fn new( - pp: &Self::PublicParams, - c_primary: &C, - z0_primary: &[::Scalar], - ) -> Result { - let c_secondary = TrivialCircuit::<::Scalar>::default(); - let z0_secondary = vec![::Scalar::ZERO; pp.F_arity_secondary]; - - Self::Proof::new(&pp, c_primary, &c_secondary, z0_primary, &z0_secondary) - } - - fn prove_step( - proof: &mut Self::Proof, - pp: &Self::PublicParams, - c_primary: &C, - ) -> Result<(), NovaError> { - let c_secondary = TrivialCircuit::default(); - proof.prove_step(&pp, c_primary, &c_secondary) - } - - fn verify( - proof: &Self::Proof, - pp: &Self::PublicParams, - z0_primary: &[::Scalar], - ) -> Result::Scalar>, NovaError> { - let z0_secondary = vec![::Scalar::ZERO; pp.F_arity_secondary]; - proof - .verify(&pp, proof.num_steps(), z0_primary, &z0_secondary) - .map(|(zi_primary, _)| zi_primary) - } -} - /// A type that holds the prover key for `CompressedSNARK` #[derive(Clone, Debug)] pub struct ProverKey diff --git a/src/supernova/mod.rs b/src/supernova/mod.rs index 4efd0b7a2..b47bd121c 100644 --- a/src/supernova/mod.rs +++ b/src/supernova/mod.rs @@ -14,7 +14,6 @@ use crate::{ scalar_as_base, traits::{ commitment::{CommitmentEngineTrait, CommitmentTrait}, - recursive::RecursiveSNARKTrait, AbsorbInROTrait, CurveCycleEquipped, Dual, Engine, ROConstants, ROConstantsCircuit, ROTrait, }, Commitment, CommitmentKey, R1CSWithArity, @@ -1114,135 +1113,6 @@ where } /// TODO: docs -pub struct PublicParamsWithNonUniformCircuit -where - E1: Engine::Scalar>, - E2: Engine::Scalar>, - C0: NonUniformCircuit, - C1: StepCircuit, - C2: StepCircuit, -{ - pp: PublicParams, - non_uniform_circuit: C0, -} - -impl RecursiveSNARKTrait for RecursiveSNARK -where - E1: Engine::Scalar>, - E2: Engine::Scalar>, - C: NonUniformCircuit>, - C: StepCircuit, -{ - type PublicParams = - PublicParamsWithNonUniformCircuit>; - - type Proof = Self; - - fn new( - pp: &Self::PublicParams, - c_primary: &C, - z0_primary: &[::Scalar], - ) -> Result { - let c_secondary = TrivialSecondaryCircuit::default(); - let z0_secondary = vec![E2::Scalar::ZERO; pp.pp.circuit_shape_secondary.F_arity]; - Self::Proof::new( - &pp.pp, - &pp.non_uniform_circuit, - c_primary, - &c_secondary, - z0_primary, - &z0_secondary, - ) - .map_err(|_| NovaError::InternalError) // TODO: better error - } - - fn prove_step( - proof: &mut Self::Proof, - pp: &Self::PublicParams, - c_primary: &C, - ) -> Result<(), NovaError> { - proof - .prove_step(&pp.pp, c_primary, &TrivialSecondaryCircuit::default()) - .map_err(|_| NovaError::InternalError) // TODO: better error - } - - fn verify( - proof: &Self::Proof, - pp: &Self::PublicParams, - z0_primary: &[::Scalar], - ) -> Result::Scalar>, NovaError> { - proof - .verify(&pp.pp, z0_primary, &vec![E2::Scalar::ZERO]) - .map(|(zi_primary, _)| zi_primary) - .map_err(|_| NovaError::InternalError) // TODO: better error - } -} - -/// TODO: docs -pub struct PublicParamsWithNonUniformCircuit -where - E1: Engine::Scalar>, - E2: Engine::Scalar>, - C0: NonUniformCircuit, - C1: StepCircuit, - C2: StepCircuit, -{ - pp: PublicParams, - non_uniform_circuit: C0, -} - -impl RecursiveSNARKTrait for RecursiveSNARK -where - E1: Engine::Scalar>, - E2: Engine::Scalar>, - C: NonUniformCircuit>, - C: StepCircuit, -{ - type PublicParams = - PublicParamsWithNonUniformCircuit>; - - type Proof = Self; - - fn new( - pp: &Self::PublicParams, - c_primary: &C, - z0_primary: &[::Scalar], - ) -> Result { - let c_secondary = TrivialSecondaryCircuit::default(); - let z0_secondary = vec![E2::Scalar::ZERO; pp.pp.circuit_shape_secondary.F_arity]; - Self::Proof::new( - &pp.pp, - &pp.non_uniform_circuit, - c_primary, - &c_secondary, - z0_primary, - &z0_secondary, - ) - .map_err(|_| NovaError::InternalError) // TODO: better error - } - - fn prove_step( - proof: &mut Self::Proof, - pp: &Self::PublicParams, - c_primary: &C, - ) -> Result<(), NovaError> { - proof - .prove_step(&pp.pp, c_primary, &TrivialSecondaryCircuit::default()) - .map_err(|_| NovaError::InternalError) // TODO: better error - } - - fn verify( - proof: &Self::Proof, - pp: &Self::PublicParams, - z0_primary: &[::Scalar], - ) -> Result::Scalar>, NovaError> { - proof - .verify(&pp.pp, z0_primary, &vec![E2::Scalar::ZERO]) - .map(|(zi_primary, _)| zi_primary) - .map_err(|_| NovaError::InternalError) // TODO: better error - } -} - /// SuperNova helper trait, for implementors that provide sets of sub-circuits to be proved via NIVC. `C1` must be a /// type (likely an `Enum`) for which a potentially-distinct instance can be supplied for each `index` below /// `self.num_circuits()`. diff --git a/src/traits/mod.rs b/src/traits/mod.rs index 23e8629c4..1786f734f 100644 --- a/src/traits/mod.rs +++ b/src/traits/mod.rs @@ -172,5 +172,4 @@ impl> TranscriptReprTrait for &[T] { pub mod circuit; pub mod evaluation; -pub mod recursive; pub mod snark; diff --git a/src/traits/recursive.rs b/src/traits/recursive.rs deleted file mode 100644 index 30f5f76ca..000000000 --- a/src/traits/recursive.rs +++ /dev/null @@ -1,42 +0,0 @@ -//! This module defines the `RecursiveSNARKTrait` to abstract over the different implementations of -//! RecursiveSNARK -//! -//! NOTE: This trait is probably going to be changed in the future, it's a first guess on what it -//! should look like. -//! - -use crate::{errors::NovaError, traits::Engine}; - -/// TODO: docs -pub trait RecursiveSNARKTrait -where - E1: Engine::Scalar>, - E2: Engine::Scalar>, -{ - /// TODO: docs - type PublicParams; - - /// TODO: docs - type Proof; // FIXME: This shouldn't have to be here, but size of `Self` not known at compile time - - /// TODO: docs - fn new( - pp: &Self::PublicParams, - c_primary: &C, - z0_primary: &[E1::Scalar], - ) -> Result; - - /// TODO: docs - fn prove_step( - proof: &mut Self::Proof, - pp: &Self::PublicParams, - c_primary: &C, - ) -> Result<(), NovaError>; - - /// TODO: docs - fn verify( - proof: &Self::Proof, - pp: &Self::PublicParams, - z0_primary: &[E1::Scalar], - ) -> Result, NovaError>; -} From 787addaa3e0b89b082ae9efbe59184b823b6e053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= Date: Sun, 11 Feb 2024 14:28:07 -0500 Subject: [PATCH 20/77] refactor: Refactor data types and function signatures in Cyclefold module - Updated and harmonized data type usage in `cyclefold/gadgets.rs`, `cyclefold/circuit.rs`, and `cyclefold/nova_circuit.rs` - Replaced `AllocatedPoint` with `AllocatedEmulPoint`, `AllocatedRelaxedR1CSInstance` with `AllocatedEmulRelaxedR1CSInstance` to avoid confusion, and simplified required bounds, - Refactored `cyclefold/gadgets.rs` to remove use of needless bounds, - no `CurveCycleEquipped` bound was used anywhere in this commit --- src/cyclefold/circuit.rs | 8 +- src/cyclefold/gadgets.rs | 172 +++++++++++++++------------------- src/cyclefold/nova_circuit.rs | 34 +++---- 3 files changed, 99 insertions(+), 115 deletions(-) diff --git a/src/cyclefold/circuit.rs b/src/cyclefold/circuit.rs index 6f9216d2e..0a28f8c23 100644 --- a/src/cyclefold/circuit.rs +++ b/src/cyclefold/circuit.rs @@ -49,9 +49,9 @@ impl CyclefoldCircuit { mut cs: CS, ) -> Result< ( - AllocatedPoint, - AllocatedPoint, - AllocatedPoint, + AllocatedPoint, + AllocatedPoint, + AllocatedPoint, Vec, ), SynthesisError, @@ -97,7 +97,7 @@ impl CyclefoldCircuit { pub fn synthesize::Base>>( &self, mut cs: CS, - ) -> Result, SynthesisError> { + ) -> Result, SynthesisError> { let (C_1, C_2, result, r) = self.alloc_witness(cs.namespace(|| "allocate circuit witness"))?; // Calculate C_final diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index 4580fa59a..a03d9fc25 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -12,7 +12,7 @@ use bellpepper_core::{ConstraintSystem, SynthesisError}; pub struct AllocatedFoldingData { pub U: AllocatedRelaxedR1CSInstance, pub u: AllocatedR1CSInstance, - pub T: AllocatedPoint, + pub T: AllocatedPoint, } impl AllocatedFoldingData { @@ -61,8 +61,6 @@ pub mod emulated { use super::*; - use std::marker::PhantomData; - use crate::{ constants::{NUM_CHALLENGE_BITS, NUM_FE_FOR_RO}, gadgets::{ @@ -79,30 +77,27 @@ pub mod emulated { use ff::Field; #[derive(Clone)] - pub struct AllocatedPoint + pub struct AllocatedEmulPoint where - E1: Engine::Scalar>, - E2: Engine::Scalar>, + G: Group, { - x: BigNat, - y: BigNat, - is_infinity: AllocatedNum, - _p: PhantomData, + x: BigNat, + y: BigNat, + is_infinity: AllocatedNum, } - impl AllocatedPoint + impl AllocatedEmulPoint where - E1: Engine::Scalar>, - E2: Engine::Scalar>, + G: Group, { pub fn alloc( mut cs: CS, - coords: Option<(E2::Base, E2::Base, bool)>, + coords: Option<(G::Scalar, G::Scalar, bool)>, limb_width: usize, n_limbs: usize, ) -> Result where - CS: ConstraintSystem<::Base>, + CS: ConstraintSystem<::Base>, { let x = BigNat::alloc_from_nat( cs.namespace(|| "x"), @@ -128,9 +123,9 @@ pub mod emulated { let is_infinity = AllocatedNum::alloc(cs.namespace(|| "is_infinity"), || { Ok(if coords.map_or(true, |c| c.2) { - E1::Base::ONE + G::Base::ONE } else { - E1::Base::ZERO + G::Base::ZERO }) })?; cs.enforce( @@ -142,9 +137,13 @@ pub mod emulated { Ok(Self { x, y, is_infinity }) } - pub fn absorb_in_ro(&self, mut cs: CS, ro: &mut E1::ROCircuit) -> Result<(), SynthesisError> + pub fn absorb_in_ro( + &self, + mut cs: CS, + ro: &mut impl ROCircuitTrait, + ) -> Result<(), SynthesisError> where - CS: ConstraintSystem<::Base>, + CS: ConstraintSystem, { let x_bn = self .x @@ -154,7 +153,7 @@ pub mod emulated { .map(|(i, limb)| { limb.as_allocated_num(cs.namespace(|| format!("convert limb {i} of X_r[0] to num"))) }) - .collect::>, _>>()?; + .collect::>, _>>()?; for limb in x_bn { ro.absorb(&limb) @@ -168,7 +167,7 @@ pub mod emulated { .map(|(i, limb)| { limb.as_allocated_num(cs.namespace(|| format!("convert limb {i} of X_r[0] to num"))) }) - .collect::>, _>>()?; + .collect::>, _>>()?; for limb in y_bn { ro.absorb(&limb) @@ -186,25 +185,25 @@ pub mod emulated { n_limbs: usize, ) -> Result<(), SynthesisError> where - CS: ConstraintSystem<::Base>, + CS: ConstraintSystem, { let m_bn = alloc_bignat_constant( cs.namespace(|| "alloc m"), - &E1::GE::group_params().3, + &G::group_params().3, limb_width, n_limbs, )?; let A_bn = alloc_bignat_constant( cs.namespace(|| "alloc A"), - &f_to_nat(&E1::GE::group_params().0), + &f_to_nat(&G::group_params().0), limb_width, n_limbs, )?; let B_bn = alloc_bignat_constant( cs.namespace(|| "alloc B"), - &f_to_nat(&E1::GE::group_params().1), + &f_to_nat(&G::group_params().1), limb_width, n_limbs, )?; @@ -223,7 +222,7 @@ pub mod emulated { self .is_infinity .get_value() - .map(|is_infinity| is_infinity == E1::Base::ONE), + .map(|is_infinity| is_infinity == G::Base::ONE), )?; cs.enforce( @@ -238,7 +237,7 @@ pub mod emulated { Ok(()) } - fn conditionally_select::Base>>( + fn conditionally_select>( &self, mut cs: CS, other: &Self, @@ -268,21 +267,21 @@ pub mod emulated { Ok(Self { x, y, is_infinity }) } - pub fn default::Base>>( + pub fn default>( mut cs: CS, limb_width: usize, n_limbs: usize, ) -> Result { let x = BigNat::alloc_from_nat( cs.namespace(|| "allocate x_default = 0"), - || Ok(f_to_nat(&E1::Scalar::ZERO)), + || Ok(f_to_nat(&G::Scalar::ZERO)), limb_width, n_limbs, )?; let y = BigNat::alloc_from_nat( cs.namespace(|| "allocate y_default = 0"), - || Ok(f_to_nat(&E1::Scalar::ZERO)), + || Ok(f_to_nat(&G::Scalar::ZERO)), limb_width, n_limbs, )?; @@ -293,41 +292,35 @@ pub mod emulated { } } - pub struct AllocatedRelaxedR1CSInstance - where - E1: Engine::Scalar>, - E2: Engine::Scalar>, - { - comm_W: AllocatedPoint, - comm_E: AllocatedPoint, - u: AllocatedNum, - x0: AllocatedNum, - x1: AllocatedNum, - pub _p: PhantomData, + pub struct AllocatedEmulRelaxedR1CSInstance { + comm_W: AllocatedEmulPoint, + comm_E: AllocatedEmulPoint, + u: AllocatedNum, + x0: AllocatedNum, + x1: AllocatedNum, } - impl AllocatedRelaxedR1CSInstance + impl AllocatedEmulRelaxedR1CSInstance where - E1: Engine::Scalar>, - E2: Engine::Scalar>, + E: Engine, { - pub fn alloc( + pub fn alloc>( mut cs: CS, inst: Option<&RelaxedR1CSInstance>, limb_width: usize, n_limbs: usize, ) -> Result where - CS: ConstraintSystem<::Base>, + CS: ConstraintSystem<::Base>, { - let comm_W = AllocatedPoint::alloc( + let comm_W = AllocatedEmulPoint::alloc( cs.namespace(|| "allocate comm_W"), inst.map(|x| x.comm_W.to_coordinates()), limb_width, n_limbs, )?; - let comm_E = AllocatedPoint::alloc( + let comm_E = AllocatedEmulPoint::alloc( cs.namespace(|| "allocate comm_E"), inst.map(|x| x.comm_E.to_coordinates()), limb_width, @@ -335,15 +328,15 @@ pub mod emulated { )?; let u = AllocatedNum::alloc(cs.namespace(|| "allocate u"), || { - inst.map_or(Ok(E1::Base::ZERO), |inst| Ok(inst.u)) + inst.map_or(Ok(E::Base::ZERO), |inst| Ok(inst.u)) })?; let x0 = AllocatedNum::alloc(cs.namespace(|| "allocate x0"), || { - inst.map_or(Ok(E1::Base::ZERO), |inst| Ok(inst.X[0])) + inst.map_or(Ok(E::Base::ZERO), |inst| Ok(inst.X[0])) })?; let x1 = AllocatedNum::alloc(cs.namespace(|| "allocate x1"), || { - inst.map_or(Ok(E1::Base::ZERO), |inst| Ok(inst.X[1])) + inst.map_or(Ok(E::Base::ZERO), |inst| Ok(inst.X[1])) })?; Ok(Self { @@ -352,23 +345,22 @@ pub mod emulated { u, x0, x1, - _p: PhantomData, }) } - pub fn fold_with_r1cs::Base>>( + pub fn fold_with_r1cs::Base>>( &self, mut cs: CS, - pp_digest: &AllocatedNum, - W_new: AllocatedPoint, - E_new: AllocatedPoint, - u_W: &AllocatedPoint, - u_x0: &AllocatedNum, - u_x1: &AllocatedNum, - comm_T: &AllocatedPoint, - ro_consts: ROConstantsCircuit, + pp_digest: &AllocatedNum, + W_new: AllocatedEmulPoint, + E_new: AllocatedEmulPoint, + u_W: &AllocatedEmulPoint, + u_x0: &AllocatedNum, + u_x1: &AllocatedNum, + comm_T: &AllocatedEmulPoint, + ro_consts: ROConstantsCircuit, ) -> Result { - let mut ro = E1::ROCircuit::new(ro_consts, NUM_FE_FOR_RO); + let mut ro = E::ROCircuit::new(ro_consts, NUM_FE_FOR_RO); ro.absorb(pp_digest); @@ -421,13 +413,16 @@ pub mod emulated { u: u_fold, x0: x0_fold, x1: x1_fold, - _p: PhantomData, }) } - pub fn absorb_in_ro(&self, mut cs: CS, ro: &mut E1::ROCircuit) -> Result<(), SynthesisError> + pub fn absorb_in_ro( + &self, + mut cs: CS, + ro: &mut impl ROCircuitTrait, + ) -> Result<(), SynthesisError> where - CS: ConstraintSystem<::Base>, + CS: ConstraintSystem<::Base>, { self .comm_E @@ -443,7 +438,7 @@ pub mod emulated { Ok(()) } - pub fn conditionally_select::Base>>( + pub fn conditionally_select::Base>>( &self, mut cs: CS, other: &Self, @@ -488,16 +483,16 @@ pub mod emulated { u, x0, x1, - _p: PhantomData, }) } - pub fn default::Base>>( + pub fn default::Base>>( mut cs: CS, limb_width: usize, n_limbs: usize, ) -> Result { - let comm_W = AllocatedPoint::default(cs.namespace(|| "default comm_W"), limb_width, n_limbs)?; + let comm_W = + AllocatedEmulPoint::default(cs.namespace(|| "default comm_W"), limb_width, n_limbs)?; let comm_E = comm_W.clone(); let u = alloc_zero(cs.namespace(|| "u = 0")); @@ -511,45 +506,35 @@ pub mod emulated { u, x0, x1, - _p: PhantomData, }) } } - pub struct AllocatedFoldingData - where - E1: Engine::Scalar>, - E2: Engine::Scalar>, - { - pub U: AllocatedRelaxedR1CSInstance, - pub u_W: AllocatedPoint, - pub u_x0: AllocatedNum, - pub u_x1: AllocatedNum, - pub T: AllocatedPoint, - _p: PhantomData, + pub struct AllocatedFoldingData { + pub U: AllocatedEmulRelaxedR1CSInstance, + pub u_W: AllocatedEmulPoint, + pub u_x0: AllocatedNum, + pub u_x1: AllocatedNum, + pub T: AllocatedEmulPoint, } - impl AllocatedFoldingData - where - E1: Engine::Scalar>, - E2: Engine::Scalar>, - { - pub fn alloc( + impl AllocatedFoldingData { + pub fn alloc>( mut cs: CS, inst: Option<&FoldingData>, limb_width: usize, n_limbs: usize, ) -> Result where - CS: ConstraintSystem<::Base>, + CS: ConstraintSystem<::Base>, { - let U = AllocatedRelaxedR1CSInstance::alloc( + let U = AllocatedEmulRelaxedR1CSInstance::alloc( cs.namespace(|| "allocate U"), inst.map(|x| &x.U), limb_width, n_limbs, )?; - let u_W = AllocatedPoint::alloc( + let u_W = AllocatedEmulPoint::alloc( cs.namespace(|| "allocate u_W"), inst.map(|x| x.u.comm_W.to_coordinates()), limb_width, @@ -557,14 +542,14 @@ pub mod emulated { )?; let u_x0 = AllocatedNum::alloc(cs.namespace(|| "allocate u_x0"), || { - inst.map_or(Ok(E1::Base::ZERO), |inst| Ok(inst.u.X[0])) + inst.map_or(Ok(E::Base::ZERO), |inst| Ok(inst.u.X[0])) })?; let u_x1 = AllocatedNum::alloc(cs.namespace(|| "allocate u_x1"), || { - inst.map_or(Ok(E1::Base::ZERO), |inst| Ok(inst.u.X[1])) + inst.map_or(Ok(E::Base::ZERO), |inst| Ok(inst.u.X[1])) })?; - let T = AllocatedPoint::alloc( + let T = AllocatedEmulPoint::alloc( cs.namespace(|| "allocate T"), inst.map(|x| x.T.to_coordinates()), limb_width, @@ -579,7 +564,6 @@ pub mod emulated { u_x0, u_x1, T, - _p: PhantomData, }) } diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index aa5a5d15a..9f4ca2f53 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -141,15 +141,15 @@ where arity: usize, ) -> Result< ( - AllocatedNum, // pp_digest - AllocatedNum, // i - Vec>, // z0 - Vec>, // zi - emulated::AllocatedFoldingData, //data_p - AllocatedFoldingData, // data_c_1 - AllocatedFoldingData, // data_c_2 - emulated::AllocatedPoint, // E_new - emulated::AllocatedPoint, // W_new + AllocatedNum, // pp_digest + AllocatedNum, // i + Vec>, // z0 + Vec>, // zi + emulated::AllocatedFoldingData, //data_p + AllocatedFoldingData, // data_c_1 + AllocatedFoldingData, // data_c_2 + emulated::AllocatedEmulPoint, // E_new + emulated::AllocatedEmulPoint, // W_new ), SynthesisError, > { @@ -216,7 +216,7 @@ where self.params.n_limbs, )?; - let E_new = emulated::AllocatedPoint::alloc( + let E_new = emulated::AllocatedEmulPoint::alloc( cs.namespace(|| "E_new"), self .inputs @@ -233,7 +233,7 @@ where self.params.n_limbs, )?; - let W_new = emulated::AllocatedPoint::alloc( + let W_new = emulated::AllocatedEmulPoint::alloc( cs.namespace(|| "W_new"), self .inputs @@ -261,7 +261,7 @@ where ) -> Result< ( AllocatedRelaxedR1CSInstance, - emulated::AllocatedRelaxedR1CSInstance, + emulated::AllocatedEmulRelaxedR1CSInstance, ), SynthesisError, > { @@ -271,7 +271,7 @@ where self.params.n_limbs, )?; - let U_p_default = emulated::AllocatedRelaxedR1CSInstance::default( + let U_p_default = emulated::AllocatedEmulRelaxedR1CSInstance::default( cs.namespace(|| "Allocated U_p_default"), self.params.limb_width, self.params.n_limbs, @@ -287,16 +287,16 @@ where i: &AllocatedNum, z_0: &[AllocatedNum], z_i: &[AllocatedNum], - data_p: &emulated::AllocatedFoldingData, + data_p: &emulated::AllocatedFoldingData, data_c_1: &AllocatedFoldingData, data_c_2: &AllocatedFoldingData, - E_new: &emulated::AllocatedPoint, - W_new: &emulated::AllocatedPoint, + E_new: &emulated::AllocatedEmulPoint, + W_new: &emulated::AllocatedEmulPoint, arity: usize, ) -> Result< ( AllocatedRelaxedR1CSInstance, - emulated::AllocatedRelaxedR1CSInstance, + emulated::AllocatedEmulRelaxedR1CSInstance, AllocatedBit, ), SynthesisError, From ecdc1c2c939dfd223f00873210567c7e59f1b752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= Date: Sun, 11 Feb 2024 14:41:24 -0500 Subject: [PATCH 21/77] refactor: remove uneeded params in PublicParams, RecursiveSNARK --- src/cyclefold/snark.rs | 121 +++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 72 deletions(-) diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index d0acb2173..97429d78f 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -1,8 +1,6 @@ //! This module defines the Cyclefold `RecursiveSNARK` type //! -use std::marker::PhantomData; - use crate::{ bellpepper::{ r1cs::{NovaShape, NovaWitness}, @@ -19,8 +17,8 @@ use crate::{ RelaxedR1CSWitness, }, traits::{ - circuit::StepCircuit, commitment::CommitmentTrait, AbsorbInROTrait, Engine, ROConstantsCircuit, - ROTrait, + circuit::StepCircuit, commitment::CommitmentTrait, AbsorbInROTrait, CurveCycleEquipped, Dual, + Engine, ROConstantsCircuit, ROTrait, }, Commitment, CommitmentKey, DigestComputer, R1CSWithArity, ROConstants, ResourceBuffer, SimpleDigestible, @@ -43,52 +41,45 @@ use serde::{Deserialize, Serialize}; #[serde(bound = "")] #[abomonation_bounds( where - E1: Engine::Scalar>, - E2: Engine::Scalar>, - C1: StepCircuit, + E1: CurveCycleEquipped, ::Repr: Abomonation, - ::Repr: Abomonation, + < as Engine>::Scalar as PrimeField>::Repr: Abomonation, )] -pub struct PublicParams +pub struct PublicParams where - E1: Engine::Scalar>, - E2: Engine::Scalar>, - C1: StepCircuit, + E1: CurveCycleEquipped, { F_arity_primary: usize, - ro_consts_primary: ROConstants, - ro_consts_circuit_primary: ROConstantsCircuit, + ro_consts_primary: ROConstants>, + ro_consts_circuit_primary: ROConstantsCircuit>, ck_primary: CommitmentKey, circuit_shape_primary: R1CSWithArity, augmented_circuit_params: AugmentedCircuitParams, - ro_consts_cyclefold: ROConstants, - ck_cyclefold: CommitmentKey, - circuit_shape_cyclefold: R1CSWithArity, + ro_consts_cyclefold: ROConstants>, + ck_cyclefold: CommitmentKey>, + circuit_shape_cyclefold: R1CSWithArity>, #[abomonation_skip] #[serde(skip, default = "OnceCell::new")] digest: OnceCell, - _p: PhantomData<(E1, E2, C1)>, } -impl PublicParams +impl PublicParams where - E1: Engine::Scalar>, - E2: Engine::Scalar>, - C1: StepCircuit, + E1: CurveCycleEquipped, { /// TODO: docs - pub fn setup( + pub fn setup>( c_primary: &C1, ck_hint1: &CommitmentKeyHint, - ck_hint_cyclefold: &CommitmentKeyHint, + ck_hint_cyclefold: &CommitmentKeyHint>, ) -> Self { let F_arity_primary = c_primary.arity(); - let ro_consts_primary = ROConstants::::default(); - let ro_consts_circuit_primary = ROConstantsCircuit::::default(); + let ro_consts_primary = ROConstants::>::default(); + let ro_consts_circuit_primary = ROConstantsCircuit::>::default(); let augmented_circuit_params = AugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS); - let circuit_primary: AugmentedCircuit<'_, E2, E1, C1> = AugmentedCircuit::new( + let circuit_primary: AugmentedCircuit<'_, Dual, E1, C1> = AugmentedCircuit::new( &augmented_circuit_params, ro_consts_circuit_primary.clone(), None, @@ -99,8 +90,8 @@ where let (r1cs_shape_primary, ck_primary) = cs.r1cs_shape_and_key(ck_hint1); let circuit_shape_primary = R1CSWithArity::new(r1cs_shape_primary, F_arity_primary); - let ro_consts_cyclefold = ROConstants::::default(); - let mut cs: ShapeCS = ShapeCS::new(); + let ro_consts_cyclefold = ROConstants::>::default(); + let mut cs: ShapeCS> = ShapeCS::new(); let circuit_cyclefold: CyclefoldCircuit = CyclefoldCircuit::new(None); let _ = circuit_cyclefold.synthesize(&mut cs); let (r1cs_shape_cyclefold, ck_cyclefold) = cs.r1cs_shape_and_key(ck_hint_cyclefold); @@ -117,7 +108,6 @@ where ck_cyclefold, circuit_shape_cyclefold, digest: OnceCell::new(), - _p: PhantomData, } } @@ -147,22 +137,14 @@ where } } -impl SimpleDigestible for PublicParams -where - E1: Engine::Scalar>, - E2: Engine::Scalar>, - C1: StepCircuit, -{ -} +impl SimpleDigestible for PublicParams where E1: CurveCycleEquipped {} /// TODO: docs #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(bound = "")] -pub struct RecursiveSNARK +pub struct RecursiveSNARK where - E1: Engine::Scalar>, - E2: Engine::Scalar>, - C1: StepCircuit, + E1: CurveCycleEquipped, { // Input z0_primary: Vec, @@ -174,28 +156,24 @@ where l_u_primary: R1CSInstance, // cyclefold circuit data - r_W_cyclefold: RelaxedR1CSWitness, - r_U_cyclefold: RelaxedR1CSInstance, + r_W_cyclefold: RelaxedR1CSWitness>, + r_U_cyclefold: RelaxedR1CSInstance>, // memory buffers for folding steps buffer_primary: ResourceBuffer, - buffer_cyclefold: ResourceBuffer, + buffer_cyclefold: ResourceBuffer>, i: usize, zi_primary: Vec, - - _p: PhantomData, } -impl RecursiveSNARK +impl RecursiveSNARK where - E1: Engine::Scalar>, - E2: Engine::Scalar>, - C1: StepCircuit, + E1: CurveCycleEquipped, { /// TODO: docs - pub fn new( - pp: &PublicParams, + pub fn new>( + pp: &PublicParams, c_primary: &C1, z0_primary: &[E1::Scalar], ) -> Result { @@ -210,9 +188,9 @@ where let r_W_cyclefold = RelaxedR1CSWitness::default(r1cs_cyclefold); let mut cs_primary = SatisfyingAssignment::::new(); - let inputs_primary: AugmentedCircuitInputs = AugmentedCircuitInputs::new( + let inputs_primary: AugmentedCircuitInputs, E1> = AugmentedCircuitInputs::new( scalar_as_base::(pp.digest()), - ::Base::from(0u64), + as Engine>::Base::from(0u64), z0_primary.to_vec(), None, None, @@ -255,7 +233,7 @@ where l_u: None, ABC_Z_1: R1CSResult::default(r1cs_cyclefold.num_cons), ABC_Z_2: R1CSResult::default(r1cs_cyclefold.num_cons), - T: r1cs::default_T::(r1cs_cyclefold.num_cons), + T: r1cs::default_T::>(r1cs_cyclefold.num_cons), }; Ok(Self { @@ -270,14 +248,13 @@ where buffer_cyclefold, i: 1, zi_primary, - _p: PhantomData, }) } /// TODO: docs - pub fn prove_step( + pub fn prove_step>( &mut self, - pp: &PublicParams, + pp: &PublicParams, c_primary: &C1, ) -> Result<(), NovaError> { if self.i == 0 { @@ -285,7 +262,7 @@ where return Ok(()); } - let (nifs_primary, (r_U_primary, r_W_primary), r) = super::nifs::NIFS::::prove( + let (nifs_primary, (r_U_primary, r_W_primary), r) = super::nifs::NIFS::>::prove( &pp.ck_primary, &pp.ro_consts_primary, &pp.digest(), @@ -319,7 +296,7 @@ where let W_new = self.r_U_primary.comm_W + self.l_u_primary.comm_W * r; - let mut cs_cyclefold_E = SatisfyingAssignment::::with_capacity( + let mut cs_cyclefold_E = SatisfyingAssignment::>::with_capacity( pp.circuit_shape_cyclefold.r1cs_shape.num_io + 1, pp.circuit_shape_cyclefold.r1cs_shape.num_vars, ); @@ -351,9 +328,9 @@ where &l_w_cyclefold_E, )?; - let comm_T_E = Commitment::::decompress(&nifs_cyclefold_E.comm_T)?; + let comm_T_E = Commitment::>::decompress(&nifs_cyclefold_E.comm_T)?; - let mut cs_cyclefold_W = SatisfyingAssignment::::with_capacity( + let mut cs_cyclefold_W = SatisfyingAssignment::>::with_capacity( pp.circuit_shape_cyclefold.r1cs_shape.num_io + 1, pp.circuit_shape_cyclefold.r1cs_shape.num_vars, ); @@ -385,7 +362,7 @@ where &l_w_cyclefold_W, )?; - let comm_T_W = Commitment::::decompress(&nifs_cyclefold_W.comm_T)?; + let comm_T_W = Commitment::>::decompress(&nifs_cyclefold_W.comm_T)?; let mut cs_primary = SatisfyingAssignment::::with_capacity( pp.circuit_shape_primary.r1cs_shape.num_io + 1, @@ -396,9 +373,9 @@ where let data_c_E = FoldingData::new(self.r_U_cyclefold.clone(), l_u_cyclefold_E, comm_T_E); let data_c_W = FoldingData::new(r_U_cyclefold_E, l_u_cyclefold_W, comm_T_W); - let inputs_primary: AugmentedCircuitInputs = AugmentedCircuitInputs::new( + let inputs_primary: AugmentedCircuitInputs, E1> = AugmentedCircuitInputs::new( scalar_as_base::(pp.digest()), - ::Base::from(self.i as u64), + as Engine>::Base::from(self.i as u64), self.z0_primary.clone(), Some(self.zi_primary.clone()), Some(data_p), @@ -408,7 +385,7 @@ where Some(W_new), ); - let circuit_primary: AugmentedCircuit<'_, E2, E1, C1> = AugmentedCircuit::new( + let circuit_primary: AugmentedCircuit<'_, Dual, E1, C1> = AugmentedCircuit::new( &pp.augmented_circuit_params, pp.ro_consts_circuit_primary.clone(), Some(inputs_primary), @@ -441,7 +418,7 @@ where /// TODO: docs pub fn verify( &self, - pp: &PublicParams, + pp: &PublicParams, num_steps: usize, z0_primary: &[E1::Scalar], ) -> Result, NovaError> { @@ -466,7 +443,7 @@ where } let (hash_primary, hash_cyclefold) = { - let mut hasher = ::RO::new( + let mut hasher = as Engine>::RO::new( pp.ro_consts_primary.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * pp.F_arity_primary, ); @@ -478,11 +455,11 @@ where for e in &self.zi_primary { hasher.absorb(*e); } - absorb_relaxed_r1cs::(&self.r_U_primary, &mut hasher); + absorb_relaxed_r1cs::>(&self.r_U_primary, &mut hasher); let hash_primary = hasher.squeeze(NUM_HASH_BITS); let mut hasher = - ::RO::new(pp.ro_consts_cyclefold.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF); + as Engine>::RO::new(pp.ro_consts_cyclefold.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF); hasher.absorb(pp.digest()); self.r_U_cyclefold.absorb_in_ro(&mut hasher); let hash_cyclefold = hasher.squeeze(NUM_HASH_BITS); @@ -492,8 +469,8 @@ where // TODO: This seems like it might be a bad sign, I don't know if I should need to use // `scalar_as_base` here - if scalar_as_base::(hash_primary) != self.l_u_primary.X[0] - || scalar_as_base::(hash_cyclefold) != self.l_u_primary.X[1] + if scalar_as_base::>(hash_primary) != self.l_u_primary.X[0] + || scalar_as_base::>(hash_cyclefold) != self.l_u_primary.X[1] { return Err(NovaError::ProofVerifyError); } From b09059abed02b538a32b341d6b72fff6892f269a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= Date: Sun, 11 Feb 2024 14:59:17 -0500 Subject: [PATCH 22/77] refactor: Remove option in CycleFoldInputs, remove a few needless types in tests --- src/cyclefold/circuit.rs | 65 +++++++++++++++++++--------------------- src/cyclefold/nifs.rs | 10 ++++--- src/cyclefold/snark.rs | 7 +++-- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/cyclefold/circuit.rs b/src/cyclefold/circuit.rs index 0a28f8c23..56e6a9ee5 100644 --- a/src/cyclefold/circuit.rs +++ b/src/cyclefold/circuit.rs @@ -16,6 +16,17 @@ pub struct CyclefoldCircuitInputs { scalar: Vec>, } +impl Default for CyclefoldCircuitInputs { + fn default() -> Self { + Self { + commit_1: None, + commit_2: None, + result: None, + scalar: vec![], + } + } +} + impl CyclefoldCircuitInputs { /// TODO pub fn new( @@ -35,12 +46,12 @@ impl CyclefoldCircuitInputs { /// TODO: docs pub struct CyclefoldCircuit { - inputs: Option>, + inputs: CyclefoldCircuitInputs, } impl CyclefoldCircuit { /// TODO: docs - pub fn new(inputs: Option>) -> Self { + pub fn new(inputs: CyclefoldCircuitInputs) -> Self { Self { inputs } } @@ -58,32 +69,21 @@ impl CyclefoldCircuit { > { let commit_1 = AllocatedPoint::alloc( cs.namespace(|| "allocate C_1"), - self - .inputs - .as_ref() - .and_then(|inputs| inputs.commit_1.map(|C_1| C_1.to_coordinates())), + self.inputs.commit_1.map(|C_1| C_1.to_coordinates()), )?; let commit_2 = AllocatedPoint::alloc( cs.namespace(|| "allocate C_2"), - self - .inputs - .as_ref() - .and_then(|inputs| inputs.commit_2.map(|C_2| C_2.to_coordinates())), + self.inputs.commit_2.map(|C_2| C_2.to_coordinates()), )?; let result = AllocatedPoint::alloc( cs.namespace(|| "allocate C_1 + r * C_2"), - self - .inputs - .as_ref() - .and_then(|inputs| inputs.result.map(|result| result.to_coordinates())), + self.inputs.result.map(|result| result.to_coordinates()), )?; let scalar = self .inputs - .as_ref() - .ok_or(SynthesisError::AssignmentMissing)? .scalar .iter() .enumerate() @@ -140,45 +140,42 @@ mod tests { use crate::{ bellpepper::{solver::SatisfyingAssignment, test_shape_cs::TestShapeCS}, - provider::{ - Bn256Engine, GrumpkinEngine, PallasEngine, Secp256k1Engine, Secq256k1Engine, VestaEngine, - }, - traits::commitment::CommitmentEngineTrait, + provider::{Bn256Engine, PallasEngine, Secp256k1Engine}, + traits::{commitment::CommitmentEngineTrait, CurveCycleEquipped, Dual}, }; use super::*; - fn test_cyclefold_circuit_size_with(expected_constraints: usize, expected_vars: usize) + fn test_cyclefold_circuit_size_with(expected_constraints: usize, expected_vars: usize) where - E1: Engine::Scalar>, - E2: Engine::Scalar>, + E1: CurveCycleEquipped, { let rng = OsRng; - let ck = <::CE as CommitmentEngineTrait>::setup(b"hi", 5); + let ck = < as Engine>::CE as CommitmentEngineTrait>>::setup(b"hi", 5); let v1 = (0..5) - .map(|_| ::random(rng)) + .map(|_| < as Engine>::Scalar as Field>::random(rng)) .collect::>(); let v2 = (0..5) - .map(|_| ::random(rng)) + .map(|_| < as Engine>::Scalar as Field>::random(rng)) .collect::>(); - let C_1 = <::CE as CommitmentEngineTrait>::commit(&ck, &v1); + let C_1 = < as Engine>::CE as CommitmentEngineTrait>>::commit(&ck, &v1); - let C_2 = <::CE as CommitmentEngineTrait>::commit(&ck, &v2); + let C_2 = < as Engine>::CE as CommitmentEngineTrait>>::commit(&ck, &v2); - let r = ::random(rng); + let r = < as Engine>::Scalar as Field>::random(rng); let result = C_1 + C_2 * r; let r_bits = r.to_le_bits().iter().map(|b| Some(*b)).collect::>(); - let inputs: CyclefoldCircuitInputs = + let inputs: CyclefoldCircuitInputs> = CyclefoldCircuitInputs::new(Some(C_1), Some(C_2), Some(result), r_bits); - let circuit: CyclefoldCircuit<_> = CyclefoldCircuit::new(Some(inputs)); + let circuit: CyclefoldCircuit<_> = CyclefoldCircuit::new(inputs); // Test the circuit size does not change let mut cs: TestShapeCS = TestShapeCS::default(); @@ -215,8 +212,8 @@ mod tests { #[test] fn test_cyclefold_circuit_size() { - test_cyclefold_circuit_size_with::(2735, 2728); - test_cyclefold_circuit_size_with::(2769, 2760); - test_cyclefold_circuit_size_with::(2701, 2696); + test_cyclefold_circuit_size_with::(2735, 2728); + test_cyclefold_circuit_size_with::(2769, 2760); + test_cyclefold_circuit_size_with::(2701, 2696); } } diff --git a/src/cyclefold/nifs.rs b/src/cyclefold/nifs.rs index c6cf0cb67..58e45b2b3 100644 --- a/src/cyclefold/nifs.rs +++ b/src/cyclefold/nifs.rs @@ -13,12 +13,14 @@ use crate::{ }, r1cs::{R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness}, traits::{commitment::CommitmentTrait, /*AbsorbInROTrait,*/ Engine, ROConstants, ROTrait}, - Commitment, CommitmentKey, CompressedCommitment, + CommitmentKey, CompressedCommitment, }; /// TODO: Docs -pub fn absorb_commitment(comm: &Commitment, ro: &mut E2::RO) -where +pub fn absorb_commitment( + comm: &impl CommitmentTrait, + ro: &mut impl ROTrait, +) where E1: Engine::Scalar>, E2: Engine::Scalar>, { @@ -40,7 +42,7 @@ where } } -fn absorb_r1cs(u: &R1CSInstance, ro: &mut E2::RO) +fn absorb_r1cs(u: &R1CSInstance, ro: &mut impl ROTrait) where E1: Engine::Scalar>, E2: Engine::Scalar>, diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 97429d78f..07f039e33 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -92,7 +92,8 @@ where let ro_consts_cyclefold = ROConstants::>::default(); let mut cs: ShapeCS> = ShapeCS::new(); - let circuit_cyclefold: CyclefoldCircuit = CyclefoldCircuit::new(None); + let circuit_cyclefold: CyclefoldCircuit = + CyclefoldCircuit::new(CyclefoldCircuitInputs::default()); let _ = circuit_cyclefold.synthesize(&mut cs); let (r1cs_shape_cyclefold, ck_cyclefold) = cs.r1cs_shape_and_key(ck_hint_cyclefold); let circuit_shape_cyclefold = R1CSWithArity::new(r1cs_shape_cyclefold, 0); @@ -308,7 +309,7 @@ where r_bools.clone(), ); - let circuit_cyclefold_E: CyclefoldCircuit = CyclefoldCircuit::new(Some(inputs_cyclefold_E)); + let circuit_cyclefold_E: CyclefoldCircuit = CyclefoldCircuit::new(inputs_cyclefold_E); let _output_cyclefold_E = circuit_cyclefold_E.synthesize(&mut cs_cyclefold_E); @@ -342,7 +343,7 @@ where r_bools, ); - let circuit_cyclefold_W: CyclefoldCircuit = CyclefoldCircuit::new(Some(inputs_cyclefold_W)); + let circuit_cyclefold_W: CyclefoldCircuit = CyclefoldCircuit::new(inputs_cyclefold_W); let _output_cyclefold_W = circuit_cyclefold_W.synthesize(&mut cs_cyclefold_W); From a169a23d1e806c530314f10d8e13078233d2517b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= Date: Sun, 11 Feb 2024 15:02:47 -0500 Subject: [PATCH 23/77] test: use expect_test on brittle test --- src/cyclefold/circuit.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/cyclefold/circuit.rs b/src/cyclefold/circuit.rs index 56e6a9ee5..975b2fc80 100644 --- a/src/cyclefold/circuit.rs +++ b/src/cyclefold/circuit.rs @@ -135,6 +135,7 @@ impl CyclefoldCircuit { #[cfg(test)] mod tests { + use expect_test::{expect, Expect}; use ff::{Field, PrimeFieldBits}; use rand_core::OsRng; @@ -146,7 +147,7 @@ mod tests { use super::*; - fn test_cyclefold_circuit_size_with(expected_constraints: usize, expected_vars: usize) + fn test_cyclefold_circuit_size_with(expected_constraints: &Expect, expected_vars: &Expect) where E1: CurveCycleEquipped, { @@ -185,8 +186,8 @@ mod tests { let num_variables = cs.num_aux(); - assert_eq!(expected_constraints, num_constraints); - assert_eq!(expected_vars, num_variables); + expected_constraints.assert_eq(&num_constraints.to_string()); + expected_vars.assert_eq(&num_variables.to_string()); // Test the circuit calculation matches weighted sum of commitments let mut cs = SatisfyingAssignment::::new(); @@ -212,8 +213,8 @@ mod tests { #[test] fn test_cyclefold_circuit_size() { - test_cyclefold_circuit_size_with::(2735, 2728); - test_cyclefold_circuit_size_with::(2769, 2760); - test_cyclefold_circuit_size_with::(2701, 2696); + test_cyclefold_circuit_size_with::(&expect!("2735"), &expect!("2728")); + test_cyclefold_circuit_size_with::(&expect!("2769"), &expect!("2760")); + test_cyclefold_circuit_size_with::(&expect!("2701"), &expect!("2696")); } } From 40952a2e0715318f6a3ce643d5b46a231636331b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= Date: Sun, 11 Feb 2024 15:19:17 -0500 Subject: [PATCH 24/77] chore: update expect tests --- src/circuit.rs | 12 ++++++------ src/supernova/circuit.rs | 12 ++++++------ src/supernova/test.rs | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/circuit.rs b/src/circuit.rs index b5f4473b0..fadade590 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -459,8 +459,8 @@ mod tests { ¶ms2, ro_consts1, ro_consts2, - &expect!["9817"], - &expect!["10349"], + &expect!["9821"], + &expect!["10353"], ); } @@ -476,8 +476,8 @@ mod tests { ¶ms2, ro_consts1, ro_consts2, - &expect!["9985"], - &expect!["10538"], + &expect!["9989"], + &expect!["10542"], ); } @@ -493,8 +493,8 @@ mod tests { ¶ms2, ro_consts1, ro_consts2, - &expect!["10264"], - &expect!["10961"], + &expect!["10268"], + &expect!["10965"], ); } } diff --git a/src/supernova/circuit.rs b/src/supernova/circuit.rs index b0cff4743..3e23c9c7b 100644 --- a/src/supernova/circuit.rs +++ b/src/supernova/circuit.rs @@ -836,8 +836,8 @@ mod tests { ¶ms2, ro_consts1, ro_consts2, - &expect!["9836"], - &expect!["10384"], + &expect!["9840"], + &expect!["10388"], 1, ); // TODO: extend to num_augmented_circuits >= 2 @@ -855,8 +855,8 @@ mod tests { ¶ms2, ro_consts1, ro_consts2, - &expect!["10004"], - &expect!["10573"], + &expect!["10008"], + &expect!["10577"], 1, ); // TODO: extend to num_augmented_circuits >= 2 @@ -874,8 +874,8 @@ mod tests { ¶ms2, ro_consts1, ro_consts2, - &expect!["10283"], - &expect!["10996"], + &expect!["10287"], + &expect!["11000"], 1, ); // TODO: extend to num_augmented_circuits >= 2 diff --git a/src/supernova/test.rs b/src/supernova/test.rs index 51c89c07e..a909d3266 100644 --- a/src/supernova/test.rs +++ b/src/supernova/test.rs @@ -564,8 +564,8 @@ fn test_recursive_circuit() { ¶ms2, ro_consts1, ro_consts2, - &expect!["9836"], - &expect!["12017"], + &expect!["9840"], + &expect!["12021"], ); } From 14dc4f1914f4120b59b2fcc2295a3d8a66b14c1e Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Mon, 12 Feb 2024 19:05:30 -0500 Subject: [PATCH 25/77] (wip) addressing comments --- src/constants.rs | 1 + src/cyclefold/circuit.rs | 153 ++++++++++++---------------- src/cyclefold/compressed.rs | 2 - src/cyclefold/gadgets.rs | 170 ++++++++++++++++++++------------ src/cyclefold/mod.rs | 1 - src/cyclefold/nifs.rs | 7 +- src/cyclefold/nova_circuit.rs | 74 ++++++++++---- src/cyclefold/snark.rs | 33 +++---- src/gadgets/nonnative/bignat.rs | 1 + 9 files changed, 249 insertions(+), 193 deletions(-) delete mode 100644 src/cyclefold/compressed.rs diff --git a/src/constants.rs b/src/constants.rs index acda5628a..c22cf1e98 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -6,6 +6,7 @@ pub(crate) const BN_N_LIMBS: usize = 4; pub(crate) const NUM_FE_WITHOUT_IO_FOR_CRHF: usize = 9 + NIO_NOVA_FOLD * BN_N_LIMBS; pub(crate) const NUM_FE_FOR_RO: usize = 9; pub(crate) const NIO_NOVA_FOLD: usize = 2; +pub(crate) const NUM_FE_IN_EMULATED_POINT: usize = 2 * BN_N_LIMBS + 1; /// Bit size of Nova field element hashes pub const NUM_HASH_BITS: usize = 250; diff --git a/src/cyclefold/circuit.rs b/src/cyclefold/circuit.rs index 975b2fc80..7128b7e9c 100644 --- a/src/cyclefold/circuit.rs +++ b/src/cyclefold/circuit.rs @@ -3,64 +3,47 @@ use bellpepper_core::{boolean::AllocatedBit, ConstraintSystem, SynthesisError}; use crate::{ - gadgets::ecc::AllocatedPoint, + constants::{NUM_CHALLENGE_BITS, NUM_HASH_BITS}, + gadgets::{ecc::AllocatedPoint, utils::le_bits_to_num}, traits::{commitment::CommitmentTrait, Engine}, Commitment, }; /// TODO: docs -pub struct CyclefoldCircuitInputs { +pub struct CyclefoldCircuit { commit_1: Option>, commit_2: Option>, - result: Option>, - scalar: Vec>, + scalar: Option<[bool; NUM_CHALLENGE_BITS]>, } -impl Default for CyclefoldCircuitInputs { +impl Default for CyclefoldCircuit { fn default() -> Self { Self { commit_1: None, commit_2: None, - result: None, - scalar: vec![], + scalar: None, } } } - -impl CyclefoldCircuitInputs { - /// TODO +impl CyclefoldCircuit { + /// TODO: docs pub fn new( commit_1: Option>, commit_2: Option>, - result: Option>, - scalar: Vec>, + scalar: Option<[bool; NUM_CHALLENGE_BITS]>, ) -> Self { Self { commit_1, commit_2, - result, scalar, } } -} - -/// TODO: docs -pub struct CyclefoldCircuit { - inputs: CyclefoldCircuitInputs, -} - -impl CyclefoldCircuit { - /// TODO: docs - pub fn new(inputs: CyclefoldCircuitInputs) -> Self { - Self { inputs } - } fn alloc_witness::Base>>( &self, mut cs: CS, ) -> Result< ( - AllocatedPoint, AllocatedPoint, AllocatedPoint, Vec, @@ -69,74 +52,70 @@ impl CyclefoldCircuit { > { let commit_1 = AllocatedPoint::alloc( cs.namespace(|| "allocate C_1"), - self.inputs.commit_1.map(|C_1| C_1.to_coordinates()), + self.commit_1.map(|C_1| C_1.to_coordinates()), )?; + commit_1.check_on_curve(cs.namespace(|| "commit_1 on curve"))?; let commit_2 = AllocatedPoint::alloc( cs.namespace(|| "allocate C_2"), - self.inputs.commit_2.map(|C_2| C_2.to_coordinates()), + self.commit_2.map(|C_2| C_2.to_coordinates()), )?; - - let result = AllocatedPoint::alloc( - cs.namespace(|| "allocate C_1 + r * C_2"), - self.inputs.result.map(|result| result.to_coordinates()), - )?; - - let scalar = self - .inputs - .scalar - .iter() - .enumerate() - .map(|(idx, b)| AllocatedBit::alloc(cs.namespace(|| format!("scalar bit {}", idx)), *b)) - .collect::>()?; - - Ok((commit_1, commit_2, result, scalar)) + commit_2.check_on_curve(cs.namespace(|| "commit_2 on curve"))?; + + let false_bit = AllocatedBit::alloc(cs.namespace(|| "allocated false bit"), Some(false))?; + let scalar: Vec = + self + .scalar + .map_or(Ok(vec![false_bit; NUM_HASH_BITS]), |bits| { + bits + .iter() + .enumerate() + .map(|(idx, bit)| { + AllocatedBit::alloc(cs.namespace(|| format!("scalar bit {idx}")), Some(*bit)) + }) + .collect::, _>>() + })?; + + Ok((commit_1, commit_2, scalar)) } /// TODO: docs pub fn synthesize::Base>>( &self, mut cs: CS, - ) -> Result, SynthesisError> { - let (C_1, C_2, result, r) = self.alloc_witness(cs.namespace(|| "allocate circuit witness"))?; + ) -> Result<(), SynthesisError> { + let (C_1, C_2, r) = self.alloc_witness(cs.namespace(|| "allocate circuit witness"))?; // Calculate C_final let r_C_2 = C_2.scalar_mul(cs.namespace(|| "r * C_2"), &r)?; let C_final = C_1.add(cs.namespace(|| "C_1 + r * C_2"), &r_C_2)?; - let (res_x, res_y, res_inf) = result.get_coordinates(); - - // enforce C_final = result - cs.enforce( - || "C_final_x = res_x", - |lc| lc + res_x.get_variable(), - |lc| lc + CS::one(), - |lc| lc + C_final.x.get_variable(), - ); - - cs.enforce( - || "C_final_y = res_y", - |lc| lc + res_y.get_variable(), - |lc| lc + CS::one(), - |lc| lc + C_final.y.get_variable(), - ); - - cs.enforce( - || "C_final_inf = res_inf", - |lc| lc + res_inf.get_variable(), - |lc| lc + CS::one(), - |lc| lc + C_final.is_infinity.get_variable(), - ); - - Ok(C_final) + let mut inputize_point = + |point: &AllocatedPoint, name: &str| -> Result<(), SynthesisError> { + point.x.inputize(cs.namespace(|| format!("{name}.x")))?; + point.y.inputize(cs.namespace(|| format!("{name}.y")))?; + point + .is_infinity + .inputize(cs.namespace(|| format!("{name}.is_infinity")))?; + Ok(()) + }; + inputize_point(&C_1, "commit_1")?; + inputize_point(&C_2, "commit_2")?; + inputize_point(&C_final, "result")?; + + let scalar = le_bits_to_num(cs.namespace(|| "get scalar"), &r)?; + + scalar.inputize(cs.namespace(|| "scalar"))?; + + Ok(()) } } #[cfg(test)] mod tests { use expect_test::{expect, Expect}; - use ff::{Field, PrimeFieldBits}; + use ff::{Field, PrimeField, PrimeFieldBits}; use rand_core::OsRng; use crate::{ @@ -167,16 +146,20 @@ mod tests { let C_2 = < as Engine>::CE as CommitmentEngineTrait>>::commit(&ck, &v2); - let r = < as Engine>::Scalar as Field>::random(rng); + let val: u128 = rand::random(); - let result = C_1 + C_2 * r; + let r = < as Engine>::Scalar as PrimeField>::from_u128(val); - let r_bits = r.to_le_bits().iter().map(|b| Some(*b)).collect::>(); + let _result = C_1 + C_2 * r; - let inputs: CyclefoldCircuitInputs> = - CyclefoldCircuitInputs::new(Some(C_1), Some(C_2), Some(result), r_bits); + let r_bits = r + .to_le_bits() + .iter() + .map(|b| Some(*b)) + .collect::>>() + .map(|v| v.try_into().unwrap()); - let circuit: CyclefoldCircuit<_> = CyclefoldCircuit::new(inputs); + let circuit: CyclefoldCircuit> = CyclefoldCircuit::new(Some(C_1), Some(C_2), r_bits); // Test the circuit size does not change let mut cs: TestShapeCS = TestShapeCS::default(); @@ -192,23 +175,13 @@ mod tests { // Test the circuit calculation matches weighted sum of commitments let mut cs = SatisfyingAssignment::::new(); - let P = circuit + let _ = circuit .synthesize(cs.namespace(|| "synthesizing witness")) .unwrap(); let r_C_2 = C_2 * r; - let C_final = C_1 + r_C_2; - - let P_x = P.x.get_value().unwrap(); - let P_y = P.y.get_value().unwrap(); - let P_infty = P.is_infinity.get_value().unwrap(); - - let (x, y, infty) = C_final.to_coordinates(); - - assert_eq!(x, P_x); - assert_eq!(y, P_y); - assert_eq!(infty, P_infty == ::ONE); + let _C_final = C_1 + r_C_2; } #[test] @@ -217,4 +190,6 @@ mod tests { test_cyclefold_circuit_size_with::(&expect!("2769"), &expect!("2760")); test_cyclefold_circuit_size_with::(&expect!("2701"), &expect!("2696")); } + + // TODO: add test for circuit satisfiability } diff --git a/src/cyclefold/compressed.rs b/src/cyclefold/compressed.rs deleted file mode 100644 index 4e19fd2c9..000000000 --- a/src/cyclefold/compressed.rs +++ /dev/null @@ -1,2 +0,0 @@ -//! This module defines the cyclefold `CompressedSNARK` type -//! diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index a03d9fc25..32307038a 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -23,7 +23,7 @@ impl AllocatedFoldingData { n_limbs: usize, ) -> Result { let U = AllocatedRelaxedR1CSInstance::alloc( - cs.namespace(|| "U"), + cs.namespace(|| "u"), inst.map(|x| &x.U), limb_width, n_limbs, @@ -62,12 +62,12 @@ pub mod emulated { use super::*; use crate::{ - constants::{NUM_CHALLENGE_BITS, NUM_FE_FOR_RO}, + constants::{NUM_CHALLENGE_BITS, NUM_FE_IN_EMULATED_POINT}, gadgets::{ nonnative::{bignat::BigNat, util::f_to_nat}, utils::{ - alloc_bignat_constant, alloc_one, alloc_zero, conditionally_select, - conditionally_select_bignat, le_bits_to_num, /*scalar_as_base,*/ + alloc_zero, conditionally_select, conditionally_select_allocated_bit, + conditionally_select_bignat, le_bits_to_num, }, }, traits::{Group, ROConstantsCircuit}, @@ -76,6 +76,63 @@ pub mod emulated { use ff::Field; + pub struct EmulatedCurveParams + where + G: Group, + { + pub A: BigNat, + pub B: BigNat, + pub m: BigNat, + } + + impl EmulatedCurveParams { + #[allow(unused)] + pub fn alloc( + mut cs: CS, + params: Option<&(G::Scalar, G::Scalar, G::Scalar)>, + limb_width: usize, + n_limbs: usize, + ) -> Result + where + CS: ConstraintSystem, + { + let A = BigNat::alloc_from_nat( + cs.namespace(|| "allocate A"), + || { + Ok(f_to_nat( + ¶ms.ok_or(SynthesisError::AssignmentMissing)?.0, + )) + }, + limb_width, + n_limbs, + )?; + + let B = BigNat::alloc_from_nat( + cs.namespace(|| "allocate B"), + || { + Ok(f_to_nat( + ¶ms.ok_or(SynthesisError::AssignmentMissing)?.1, + )) + }, + limb_width, + n_limbs, + )?; + + let m = BigNat::alloc_from_nat( + cs.namespace(|| "allocate m"), + || { + Ok(f_to_nat( + ¶ms.ok_or(SynthesisError::AssignmentMissing)?.2, + )) + }, + limb_width, + n_limbs, + )?; + + Ok(Self { A, B, m }) + } + } + #[derive(Clone)] pub struct AllocatedEmulPoint where @@ -83,7 +140,7 @@ pub mod emulated { { x: BigNat, y: BigNat, - is_infinity: AllocatedNum, + is_infinity: AllocatedBit, } impl AllocatedEmulPoint @@ -121,19 +178,11 @@ pub mod emulated { n_limbs, )?; - let is_infinity = AllocatedNum::alloc(cs.namespace(|| "is_infinity"), || { - Ok(if coords.map_or(true, |c| c.2) { - G::Base::ONE - } else { - G::Base::ZERO - }) - })?; - cs.enforce( - || "is_infinity is bit", - |lc| lc + is_infinity.get_variable(), - |lc| lc + CS::one() - is_infinity.get_variable(), - |lc| lc, - ); + let is_infinity = AllocatedBit::alloc( + cs.namespace(|| "alloc is_infinity"), + coords.map(|(_, _, is_infinity)| is_infinity), + )?; + Ok(Self { x, y, is_infinity }) } @@ -151,7 +200,7 @@ pub mod emulated { .iter() .enumerate() .map(|(i, limb)| { - limb.as_allocated_num(cs.namespace(|| format!("convert limb {i} of X_r[0] to num"))) + limb.as_allocated_num(cs.namespace(|| format!("convert limb {i} of x to num"))) }) .collect::>, _>>()?; @@ -165,7 +214,7 @@ pub mod emulated { .iter() .enumerate() .map(|(i, limb)| { - limb.as_allocated_num(cs.namespace(|| format!("convert limb {i} of X_r[0] to num"))) + limb.as_allocated_num(cs.namespace(|| format!("convert limb {i} of y to num"))) }) .collect::>, _>>()?; @@ -173,40 +222,48 @@ pub mod emulated { ro.absorb(&limb) } - ro.absorb(&self.is_infinity); + let is_infinity_num: AllocatedNum = + AllocatedNum::alloc(cs.namespace(|| "is_infinity"), || { + self + .is_infinity + .get_value() + .map_or(Err(SynthesisError::AssignmentMissing), |bit| { + if bit { + Ok(G::Base::ONE) + } else { + Ok(G::Base::ZERO) + } + }) + })?; + + cs.enforce( + || "constrain num equals bit", + |lc| lc, + |lc| lc, + |lc| lc + is_infinity_num.get_variable() - self.is_infinity.get_variable(), + ); + + ro.absorb(&is_infinity_num); Ok(()) } + #[allow(unused)] pub fn check_on_curve( &self, mut cs: CS, - limb_width: usize, - n_limbs: usize, + curve_params: &EmulatedCurveParams, + _limb_width: usize, + _n_limbs: usize, ) -> Result<(), SynthesisError> where CS: ConstraintSystem, { - let m_bn = alloc_bignat_constant( - cs.namespace(|| "alloc m"), - &G::group_params().3, - limb_width, - n_limbs, - )?; - - let A_bn = alloc_bignat_constant( - cs.namespace(|| "alloc A"), - &f_to_nat(&G::group_params().0), - limb_width, - n_limbs, - )?; - - let B_bn = alloc_bignat_constant( - cs.namespace(|| "alloc B"), - &f_to_nat(&G::group_params().1), - limb_width, - n_limbs, - )?; + let (m_bn, A_bn, B_bn) = ( + curve_params.m.clone(), + curve_params.A.clone(), + curve_params.B.clone(), + ); let (_, A_x) = A_bn.mult_mod(cs.namespace(|| "A_x"), &self.x, &m_bn)?; @@ -217,20 +274,7 @@ pub mod emulated { let (_, y_sq) = self.y.mult_mod(cs.namespace(|| "y_sq"), &self.y, &m_bn)?; - let always_equal = AllocatedBit::alloc( - cs.namespace(|| "always_equal = 1 - is_infinity"), - self - .is_infinity - .get_value() - .map(|is_infinity| is_infinity == G::Base::ONE), - )?; - - cs.enforce( - || "always_equal = 1 - is_infinity", - |lc| lc, - |lc| lc, - |lc| lc + always_equal.get_variable() - CS::one() + self.is_infinity.get_variable(), - ); + let always_equal = self.is_infinity.clone(); y_sq.equal_when_carried_regroup(cs.namespace(|| "y_sq = rhs"), &rhs, &always_equal)?; @@ -257,7 +301,7 @@ pub mod emulated { condition, )?; - let is_infinity = conditionally_select( + let is_infinity = conditionally_select_allocated_bit( cs.namespace(|| "is_infinity = cond ? self.is_infinity : other.is_infinity"), &self.is_infinity, &other.is_infinity, @@ -286,7 +330,7 @@ pub mod emulated { n_limbs, )?; - let is_infinity = alloc_one(cs.namespace(|| "one")); + let is_infinity = AllocatedBit::alloc(cs.namespace(|| "allocate is_infinity"), Some(true))?; Ok(Self { x, y, is_infinity }) } @@ -360,8 +404,10 @@ pub mod emulated { comm_T: &AllocatedEmulPoint, ro_consts: ROConstantsCircuit, ) -> Result { - let mut ro = E::ROCircuit::new(ro_consts, NUM_FE_FOR_RO); - + let mut ro = E::ROCircuit::new( + ro_consts, + 1 + NUM_FE_IN_EMULATED_POINT + 2 + NUM_FE_IN_EMULATED_POINT, // pp_digest + u.W + u.x + comm_T + ); ro.absorb(pp_digest); // Absorb u @@ -556,8 +602,6 @@ pub mod emulated { n_limbs, )?; - T.check_on_curve(cs.namespace(|| "T on curve"), limb_width, n_limbs)?; - Ok(Self { U, u_W, diff --git a/src/cyclefold/mod.rs b/src/cyclefold/mod.rs index 61fd35d9a..1352e1983 100644 --- a/src/cyclefold/mod.rs +++ b/src/cyclefold/mod.rs @@ -4,6 +4,5 @@ mod circuit; mod gadgets; mod nova_circuit; -pub mod compressed; pub mod nifs; pub mod snark; diff --git a/src/cyclefold/nifs.rs b/src/cyclefold/nifs.rs index 58e45b2b3..9021e8be3 100644 --- a/src/cyclefold/nifs.rs +++ b/src/cyclefold/nifs.rs @@ -5,7 +5,7 @@ use std::marker::PhantomData; use ff::Field; use crate::{ - constants::{BN_LIMB_WIDTH, BN_N_LIMBS, NUM_CHALLENGE_BITS, NUM_FE_FOR_RO}, + constants::{BN_LIMB_WIDTH, BN_N_LIMBS, NUM_CHALLENGE_BITS, NUM_FE_IN_EMULATED_POINT}, errors::NovaError, gadgets::{ nonnative::{bignat::nat_to_limbs, util::f_to_nat}, @@ -89,7 +89,10 @@ where ), NovaError, > { - let mut ro = E2::RO::new(ro_consts.clone(), NUM_FE_FOR_RO); + let mut ro = E2::RO::new( + ro_consts.clone(), + 1 + NUM_FE_IN_EMULATED_POINT + 2 + NUM_FE_IN_EMULATED_POINT, // pp_digest + u.W + u.X + T + ); ro.absorb(*pp_digest); diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index 9f4ca2f53..e8aed6fd8 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -1,7 +1,7 @@ //! This module defines the Nova augmented circuit used for Cyclefold use crate::{ - constants::{NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_BITS}, + constants::{NUM_FE_IN_EMULATED_POINT, NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_BITS}, gadgets::{ r1cs::AllocatedRelaxedR1CSInstance, utils::{ @@ -227,12 +227,6 @@ where self.params.n_limbs, )?; - E_new.check_on_curve( - cs.namespace(|| "E_new on curve"), - self.params.limb_width, - self.params.n_limbs, - )?; - let W_new = emulated::AllocatedEmulPoint::alloc( cs.namespace(|| "W_new"), self @@ -244,12 +238,6 @@ where self.params.n_limbs, )?; - W_new.check_on_curve( - cs.namespace(|| "W_new on curve"), - self.params.limb_width, - self.params.n_limbs, - )?; - Ok(( pp_digest, i, z_0, z_i, data_p, data_c_1, data_c_2, E_new, W_new, )) @@ -304,7 +292,7 @@ where // Follows the outline written down here https://hackmd.io/@mpenciak/HybHrnNFT let mut ro_p = E1::ROCircuit::new( self.ro_consts.clone(), - NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * arity, + 2 + 2 * arity + 2 * NUM_FE_IN_EMULATED_POINT + 3, ); ro_p.absorb(pp_digest); @@ -331,6 +319,7 @@ where let mut ro_c = E1::ROCircuit::new(self.ro_consts.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF); ro_c.absorb(pp_digest); + ro_c.absorb(i); data_c_1 .U .absorb_in_ro(cs.namespace(|| "absorb U_c"), &mut ro_c)?; @@ -361,7 +350,7 @@ where )?; // Calculate h_int = H(pp, U_c_int) - let mut ro_c_int = E1::ROCircuit::new(self.ro_consts.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF); + let mut ro_c_int = E1::ROCircuit::new(self.ro_consts.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF - 1); ro_c_int.absorb(pp_digest); U_int.absorb_in_ro(cs.namespace(|| "absorb U_c_int"), &mut ro_c_int)?; let h_c_int_bits = @@ -369,7 +358,7 @@ where let h_c_int = le_bits_to_num(cs.namespace(|| "intermediate hash"), &h_c_int_bits)?; // Calculate h_1 = H(pp, U_c_1) - let mut ro_c_1 = E1::ROCircuit::new(self.ro_consts.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF); + let mut ro_c_1 = E1::ROCircuit::new(self.ro_consts.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF - 1); ro_c_1.absorb(pp_digest); data_c_2 .U @@ -414,11 +403,11 @@ where self, cs: &mut CS, ) -> Result>, SynthesisError> { - // TODO: It's written down here https://hackmd.io/@mpenciak/HybHrnNFT + // TODO: It's written down here https://hackmd.io/SBvAur_2RQmaduDi7gYbhw let arity = self.step_circuit.arity(); let (pp_digest, i, z_0, z_i, data_p, data_c_1, data_c_2, E_new, W_new) = - self.alloc_witness(cs.namespace(|| "alloc_witness"), self.params.n_limbs)?; + self.alloc_witness(cs.namespace(|| "alloc_witness"), arity)?; let zero = alloc_zero(cs.namespace(|| "zero")); let is_base_case = alloc_num_equals(cs.namespace(|| "is base case"), &i, &zero)?; @@ -496,7 +485,7 @@ where let mut ro_p = E1::ROCircuit::new( self.ro_consts.clone(), - NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * arity, + 2 + 2 * arity + 2 * NUM_FE_IN_EMULATED_POINT + 3, ); ro_p.absorb(&pp_digest); ro_p.absorb(&i_new); @@ -512,6 +501,7 @@ where let mut ro_c = E1::ROCircuit::new(self.ro_consts, NUM_FE_WITHOUT_IO_FOR_CRHF); ro_c.absorb(&pp_digest); + ro_c.absorb(&i_new); Unew_c.absorb_in_ro(cs.namespace(|| "absorb Unew_c"), &mut ro_c)?; let hash_c_bits = ro_c.squeeze(cs.namespace(|| "hash_c_bits"), NUM_HASH_BITS)?; let hash_c = le_bits_to_num(cs.namespace(|| "hash_c"), &hash_c_bits)?; @@ -522,3 +512,49 @@ where Ok(z_next) } } + +#[cfg(test)] +mod test { + use crate::{ + bellpepper::test_shape_cs::TestShapeCS, + constants::{BN_LIMB_WIDTH, BN_N_LIMBS}, + provider::{Bn256Engine, PallasEngine, Secp256k1Engine}, + traits::{circuit::TrivialCircuit, CurveCycleEquipped, Dual}, + }; + + use super::*; + + fn test_circuit_size_with() + where + E: CurveCycleEquipped, + { + let params = AugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS); + + let ro_consts = ROConstantsCircuit::::default(); + + let step_circuit = TrivialCircuit::::default(); + + let circuit = AugmentedCircuit::, TrivialCircuit>::new( + ¶ms, + ro_consts, + None, + &step_circuit, + ); + let mut cs: TestShapeCS> = TestShapeCS::default(); + + let _ = circuit.synthesize(&mut cs); + + let num_constraints = cs.num_constraints(); + let num_variables = cs.num_aux(); + + assert_eq!(num_constraints, 0); + assert_eq!(num_variables, 0); + } + + #[test] + fn test_circuit_size() { + test_circuit_size_with::(); + test_circuit_size_with::(); + test_circuit_size_with::(); + } +} diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 07f039e33..b64e4cfc3 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -7,8 +7,10 @@ use crate::{ shape_cs::ShapeCS, solver::SatisfyingAssignment, }, - constants::{BN_LIMB_WIDTH, BN_N_LIMBS, NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_BITS}, - cyclefold::circuit::{CyclefoldCircuit, CyclefoldCircuitInputs}, + constants::{ + BN_LIMB_WIDTH, BN_N_LIMBS, NUM_CHALLENGE_BITS, NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_BITS, + }, + cyclefold::circuit::CyclefoldCircuit, errors::NovaError, gadgets::utils::scalar_as_base, nifs::NIFS, @@ -92,8 +94,7 @@ where let ro_consts_cyclefold = ROConstants::>::default(); let mut cs: ShapeCS> = ShapeCS::new(); - let circuit_cyclefold: CyclefoldCircuit = - CyclefoldCircuit::new(CyclefoldCircuitInputs::default()); + let circuit_cyclefold: CyclefoldCircuit = CyclefoldCircuit::default(); let _ = circuit_cyclefold.synthesize(&mut cs); let (r1cs_shape_cyclefold, ck_cyclefold) = cs.r1cs_shape_and_key(ck_hint_cyclefold); let circuit_shape_cyclefold = R1CSWithArity::new(r1cs_shape_cyclefold, 0); @@ -290,7 +291,14 @@ where // &mut self.buffer_primary.ABC_Z_2, // )?; - let r_bools = r.to_le_bits().iter().map(|b| Some(*b)).collect::>(); + let r_bools: Option<[bool; NUM_CHALLENGE_BITS]> = r + .to_le_bits() + .iter() + .map(|b| Some(*b)) + .collect::>>() + .unwrap() + .try_into() + .ok(); let comm_T = Commitment::::decompress(&nifs_primary.comm_T)?; let E_new = self.r_U_primary.comm_E + comm_T * r; @@ -302,14 +310,8 @@ where pp.circuit_shape_cyclefold.r1cs_shape.num_vars, ); - let inputs_cyclefold_E: CyclefoldCircuitInputs = CyclefoldCircuitInputs::new( - Some(self.r_U_primary.comm_E), - Some(comm_T), - Some(E_new), - r_bools.clone(), - ); - - let circuit_cyclefold_E: CyclefoldCircuit = CyclefoldCircuit::new(inputs_cyclefold_E); + let circuit_cyclefold_E: CyclefoldCircuit = + CyclefoldCircuit::new(Some(self.r_U_primary.comm_E), Some(comm_T), r_bools.clone()); let _output_cyclefold_E = circuit_cyclefold_E.synthesize(&mut cs_cyclefold_E); @@ -336,15 +338,12 @@ where pp.circuit_shape_cyclefold.r1cs_shape.num_vars, ); - let inputs_cyclefold_W: CyclefoldCircuitInputs = CyclefoldCircuitInputs::new( + let circuit_cyclefold_W: CyclefoldCircuit = CyclefoldCircuit::new( Some(self.r_U_primary.comm_W), Some(self.l_u_primary.comm_W), - Some(W_new), r_bools, ); - let circuit_cyclefold_W: CyclefoldCircuit = CyclefoldCircuit::new(inputs_cyclefold_W); - let _output_cyclefold_W = circuit_cyclefold_W.synthesize(&mut cs_cyclefold_W); let (l_u_cyclefold_W, l_w_cyclefold_W) = cs_cyclefold_W diff --git a/src/gadgets/nonnative/bignat.rs b/src/gadgets/nonnative/bignat.rs index be4062b98..bcfc79691 100644 --- a/src/gadgets/nonnative/bignat.rs +++ b/src/gadgets/nonnative/bignat.rs @@ -357,6 +357,7 @@ impl BigNat { } /// Constrain `self` to be equal to `other`, after carrying both. + /// The constraint is always satisfied if `always_equal` is true. pub fn equal_when_carried>( &self, mut cs: CS, From 72a25ccc4989bb1b0a45b92f2daaa3bceba4a862 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Tue, 13 Feb 2024 11:57:49 -0500 Subject: [PATCH 26/77] (wip) add recursive_snark test --- src/cyclefold/circuit.rs | 5 ++- src/cyclefold/nifs.rs | 8 ++++- src/cyclefold/nova_circuit.rs | 10 +++--- src/cyclefold/snark.rs | 58 ++++++++++++++++++++++++++++++++++- 4 files changed, 73 insertions(+), 8 deletions(-) diff --git a/src/cyclefold/circuit.rs b/src/cyclefold/circuit.rs index 7128b7e9c..1d13d1937 100644 --- a/src/cyclefold/circuit.rs +++ b/src/cyclefold/circuit.rs @@ -157,7 +157,10 @@ mod tests { .iter() .map(|b| Some(*b)) .collect::>>() - .map(|v| v.try_into().unwrap()); + .map(|mut vec| { + vec.resize_with(128, || false); + vec.try_into().unwrap() + }); let circuit: CyclefoldCircuit> = CyclefoldCircuit::new(Some(C_1), Some(C_2), r_bits); diff --git a/src/cyclefold/nifs.rs b/src/cyclefold/nifs.rs index 9021e8be3..5f17d6245 100644 --- a/src/cyclefold/nifs.rs +++ b/src/cyclefold/nifs.rs @@ -89,9 +89,15 @@ where ), NovaError, > { + let arity = U1.X.len(); + + if arity != U2.X.len() { + return Err(NovaError::InvalidInputLength); + } + let mut ro = E2::RO::new( ro_consts.clone(), - 1 + NUM_FE_IN_EMULATED_POINT + 2 + NUM_FE_IN_EMULATED_POINT, // pp_digest + u.W + u.X + T + 1 + NUM_FE_IN_EMULATED_POINT + arity + NUM_FE_IN_EMULATED_POINT, // pp_digest + u.W + u.X + T ); ro.absorb(*pp_digest); diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index e8aed6fd8..4a4b49043 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -524,7 +524,7 @@ mod test { use super::*; - fn test_circuit_size_with() + fn test_augmented_circuit_size_with() where E: CurveCycleEquipped, { @@ -552,9 +552,9 @@ mod test { } #[test] - fn test_circuit_size() { - test_circuit_size_with::(); - test_circuit_size_with::(); - test_circuit_size_with::(); + fn test_augmented_circuit_size() { + test_augmented_circuit_size_with::(); + test_augmented_circuit_size_with::(); + test_augmented_circuit_size_with::(); } } diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index b64e4cfc3..1d65d8571 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -526,5 +526,61 @@ where #[cfg(test)] mod test { - // use super::*; + use std::marker::PhantomData; + + use bellpepper_core::num::AllocatedNum; + + use super::*; + use crate::{ + provider::{Bn256Engine, PallasEngine, Secp256k1Engine}, + traits::snark::default_ck_hint, + }; + + #[derive(Clone)] + struct SquareCircuit { + _p: PhantomData, + } + + impl StepCircuit for SquareCircuit { + fn arity(&self) -> usize { + 1 + } + + fn synthesize>( + &self, + cs: &mut CS, + z: &[AllocatedNum], + ) -> Result>, SynthesisError> { + let x = &z[0]; + let x_sq = x.square(cs.namespace(|| "x_sq"))?; + + Ok(vec![x_sq]) + } + } + + fn test_trivial_cyclefold_prove_verify_with() { + let num_steps = 1; + let primary_circuit = SquareCircuit:: { _p: PhantomData }; + + let pp = PublicParams::::setup(&primary_circuit, &*default_ck_hint(), &*default_ck_hint()); + + let z0 = vec![E::Scalar::from(2u64)]; + + let mut recursive_snark = RecursiveSNARK::new(&pp, &primary_circuit, &z0).unwrap(); + + let res = recursive_snark.prove_step(&pp, &primary_circuit); + + assert!(res.is_ok()); + + let res = recursive_snark.verify(&pp, num_steps, &z0); + + assert!(res.is_ok()); + } + + #[test] + fn test_cyclefold_prove_verify() { + test_trivial_cyclefold_prove_verify_with::(); + test_trivial_cyclefold_prove_verify_with::(); + test_trivial_cyclefold_prove_verify_with::(); + } } From d7e30cfefdcb4e7ac82b3a0c8732a67ba9d405bb Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Thu, 15 Feb 2024 14:25:49 -0500 Subject: [PATCH 27/77] (wip) add generic bounds for allocatedr1csinstances --- src/constants.rs | 1 + src/cyclefold/gadgets.rs | 8 ++++---- src/cyclefold/nova_circuit.rs | 30 ++++++++++++++++-------------- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index c22cf1e98..d3d878cfe 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -7,6 +7,7 @@ pub(crate) const NUM_FE_WITHOUT_IO_FOR_CRHF: usize = 9 + NIO_NOVA_FOLD * BN_N_LI pub(crate) const NUM_FE_FOR_RO: usize = 9; pub(crate) const NIO_NOVA_FOLD: usize = 2; pub(crate) const NUM_FE_IN_EMULATED_POINT: usize = 2 * BN_N_LIMBS + 1; +pub(crate) const NIO_CYCLE_FOLD: usize = 4; /// Bit size of Nova field element hashes pub const NUM_HASH_BITS: usize = 250; diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index 32307038a..dff5ec3af 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -9,13 +9,13 @@ use crate::{ }; use bellpepper_core::{ConstraintSystem, SynthesisError}; -pub struct AllocatedFoldingData { - pub U: AllocatedRelaxedR1CSInstance, - pub u: AllocatedR1CSInstance, +pub struct AllocatedFoldingData { + pub U: AllocatedRelaxedR1CSInstance, + pub u: AllocatedR1CSInstance, pub T: AllocatedPoint, } -impl AllocatedFoldingData { +impl AllocatedFoldingData { pub fn alloc::Base>>( mut cs: CS, inst: Option<&FoldingData>, diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index 4a4b49043..9db6fad9c 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -1,7 +1,9 @@ //! This module defines the Nova augmented circuit used for Cyclefold use crate::{ - constants::{NUM_FE_IN_EMULATED_POINT, NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_BITS}, + constants::{ + NIO_CYCLE_FOLD, NUM_FE_IN_EMULATED_POINT, NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_BITS, + }, gadgets::{ r1cs::AllocatedRelaxedR1CSInstance, utils::{ @@ -141,15 +143,15 @@ where arity: usize, ) -> Result< ( - AllocatedNum, // pp_digest - AllocatedNum, // i - Vec>, // z0 - Vec>, // zi - emulated::AllocatedFoldingData, //data_p - AllocatedFoldingData, // data_c_1 - AllocatedFoldingData, // data_c_2 - emulated::AllocatedEmulPoint, // E_new - emulated::AllocatedEmulPoint, // W_new + AllocatedNum, // pp_digest + AllocatedNum, // i + Vec>, // z0 + Vec>, // zi + emulated::AllocatedFoldingData, //data_p + AllocatedFoldingData, // data_c_1 + AllocatedFoldingData, // data_c_2 + emulated::AllocatedEmulPoint, // E_new + emulated::AllocatedEmulPoint, // W_new ), SynthesisError, > { @@ -248,7 +250,7 @@ where mut cs: CS, ) -> Result< ( - AllocatedRelaxedR1CSInstance, + AllocatedRelaxedR1CSInstance, emulated::AllocatedEmulRelaxedR1CSInstance, ), SynthesisError, @@ -276,14 +278,14 @@ where z_0: &[AllocatedNum], z_i: &[AllocatedNum], data_p: &emulated::AllocatedFoldingData, - data_c_1: &AllocatedFoldingData, - data_c_2: &AllocatedFoldingData, + data_c_1: &AllocatedFoldingData, + data_c_2: &AllocatedFoldingData, E_new: &emulated::AllocatedEmulPoint, W_new: &emulated::AllocatedEmulPoint, arity: usize, ) -> Result< ( - AllocatedRelaxedR1CSInstance, + AllocatedRelaxedR1CSInstance, emulated::AllocatedEmulRelaxedR1CSInstance, AllocatedBit, ), From 7f488bcd267a7a9df3440f197b33cc2afbe283b7 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Thu, 15 Feb 2024 15:10:49 -0500 Subject: [PATCH 28/77] make the cyclefold test more **fun** --- src/cyclefold/circuit.rs | 58 ++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/src/cyclefold/circuit.rs b/src/cyclefold/circuit.rs index 1d13d1937..a19ca0c03 100644 --- a/src/cyclefold/circuit.rs +++ b/src/cyclefold/circuit.rs @@ -68,10 +68,10 @@ impl CyclefoldCircuit { .scalar .map_or(Ok(vec![false_bit; NUM_HASH_BITS]), |bits| { bits - .iter() + .into_iter() .enumerate() .map(|(idx, bit)| { - AllocatedBit::alloc(cs.namespace(|| format!("scalar bit {idx}")), Some(*bit)) + AllocatedBit::alloc(cs.namespace(|| format!("scalar bit {idx}")), Some(bit)) }) .collect::, _>>() })?; @@ -119,20 +119,25 @@ mod tests { use rand_core::OsRng; use crate::{ - bellpepper::{solver::SatisfyingAssignment, test_shape_cs::TestShapeCS}, + bellpepper::{ + r1cs::{NovaShape, NovaWitness}, + shape_cs::ShapeCS, + solver::SatisfyingAssignment, + }, provider::{Bn256Engine, PallasEngine, Secp256k1Engine}, - traits::{commitment::CommitmentEngineTrait, CurveCycleEquipped, Dual}, + traits::{commitment::CommitmentEngineTrait, snark::default_ck_hint, CurveCycleEquipped, Dual}, }; use super::*; + // TODO: Split this test up into multiple tests fn test_cyclefold_circuit_size_with(expected_constraints: &Expect, expected_vars: &Expect) where E1: CurveCycleEquipped, { let rng = OsRng; - let ck = < as Engine>::CE as CommitmentEngineTrait>>::setup(b"hi", 5); + let ck = < as Engine>::CE as CommitmentEngineTrait>>::setup(b"test", 5); let v1 = (0..5) .map(|_| < as Engine>::Scalar as Field>::random(rng)) @@ -150,12 +155,14 @@ mod tests { let r = < as Engine>::Scalar as PrimeField>::from_u128(val); - let _result = C_1 + C_2 * r; + let native_result = C_1 + C_2 * r; + + let (res_X, res_Y, res_is_infinity) = native_result.to_coordinates(); let r_bits = r .to_le_bits() - .iter() - .map(|b| Some(*b)) + .into_iter() + .map(|b| Some(b)) .collect::>>() .map(|mut vec| { vec.resize_with(128, || false); @@ -164,34 +171,51 @@ mod tests { let circuit: CyclefoldCircuit> = CyclefoldCircuit::new(Some(C_1), Some(C_2), r_bits); - // Test the circuit size does not change - let mut cs: TestShapeCS = TestShapeCS::default(); + let mut cs: ShapeCS = ShapeCS::new(); let _ = circuit.synthesize(cs.namespace(|| "synthesizing shape")); let num_constraints = cs.num_constraints(); - let num_variables = cs.num_aux(); + let num_io = cs.num_inputs(); expected_constraints.assert_eq(&num_constraints.to_string()); expected_vars.assert_eq(&num_variables.to_string()); + assert_eq!(num_io, 11); + + let (shape, ck) = cs.r1cs_shape_and_key(&*default_ck_hint()); - // Test the circuit calculation matches weighted sum of commitments let mut cs = SatisfyingAssignment::::new(); let _ = circuit .synthesize(cs.namespace(|| "synthesizing witness")) .unwrap(); - let r_C_2 = C_2 * r; + let (instance, witness) = cs.r1cs_instance_and_witness(&shape, &ck).unwrap(); + + let X = &instance.X; + + let circuit_res_X = X[6]; + let circuit_res_Y = X[7]; + let circuit_res_is_infinity = X[8]; + + dbg!(res_X, res_Y, res_is_infinity); + dbg!(X); + + assert_eq!(res_X, circuit_res_X); + assert_eq!(res_Y, circuit_res_Y); + assert_eq!( + res_is_infinity, + circuit_res_is_infinity == as Engine>::Base::ONE + ); - let _C_final = C_1 + r_C_2; + assert!(shape.is_sat(&ck, &instance, &witness).is_ok()); } #[test] fn test_cyclefold_circuit_size() { - test_cyclefold_circuit_size_with::(&expect!("2735"), &expect!("2728")); - test_cyclefold_circuit_size_with::(&expect!("2769"), &expect!("2760")); - test_cyclefold_circuit_size_with::(&expect!("2701"), &expect!("2696")); + test_cyclefold_circuit_size_with::(&expect!("1371"), &expect!("1359")); + test_cyclefold_circuit_size_with::(&expect!("1371"), &expect!("1359")); + test_cyclefold_circuit_size_with::(&expect!("1371"), &expect!("1359")); } // TODO: add test for circuit satisfiability From ca4f46efce5f2e41534f53700f434342cfcddedf Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Tue, 20 Feb 2024 10:27:03 -0500 Subject: [PATCH 29/77] fix cyclefold circuit IO --- src/cyclefold/circuit.rs | 148 ++++++++++++++++++++++++++++++++------- src/gadgets/utils.rs | 16 +++++ 2 files changed, 140 insertions(+), 24 deletions(-) diff --git a/src/cyclefold/circuit.rs b/src/cyclefold/circuit.rs index a19ca0c03..7f01b4c6b 100644 --- a/src/cyclefold/circuit.rs +++ b/src/cyclefold/circuit.rs @@ -1,10 +1,15 @@ //! This module defines Cyclefold stuff -use bellpepper_core::{boolean::AllocatedBit, ConstraintSystem, SynthesisError}; +use bellpepper::gadgets::Assignment; +use bellpepper_core::{boolean::AllocatedBit, num::AllocatedNum, ConstraintSystem, SynthesisError}; +use ff::PrimeField; use crate::{ constants::{NUM_CHALLENGE_BITS, NUM_HASH_BITS}, - gadgets::{ecc::AllocatedPoint, utils::le_bits_to_num}, + gadgets::{ + ecc::AllocatedPoint, + utils::{alloc_constant, le_bits_to_num}, + }, traits::{commitment::CommitmentTrait, Engine}, Commitment, }; @@ -91,18 +96,9 @@ impl CyclefoldCircuit { let C_final = C_1.add(cs.namespace(|| "C_1 + r * C_2"), &r_C_2)?; - let mut inputize_point = - |point: &AllocatedPoint, name: &str| -> Result<(), SynthesisError> { - point.x.inputize(cs.namespace(|| format!("{name}.x")))?; - point.y.inputize(cs.namespace(|| format!("{name}.y")))?; - point - .is_infinity - .inputize(cs.namespace(|| format!("{name}.is_infinity")))?; - Ok(()) - }; - inputize_point(&C_1, "commit_1")?; - inputize_point(&C_2, "commit_2")?; - inputize_point(&C_final, "result")?; + inputize_point::(&C_1, cs.namespace(|| "inputize C_1"))?; + inputize_point::(&C_2, cs.namespace(|| "inputize C_2"))?; + inputize_point::(&C_final, cs.namespace(|| "inputize C_final"))?; let scalar = le_bits_to_num(cs.namespace(|| "get scalar"), &r)?; @@ -112,6 +108,85 @@ impl CyclefoldCircuit { } } +fn inputize_point(point: &AllocatedPoint, mut cs: CS) -> Result<(), SynthesisError> +where + E: Engine, + CS: ConstraintSystem, +{ + let (lower_x, upper_x) = split_field_element(&point.x, cs.namespace(|| "split x"))?; + let (lower_y, upper_y) = split_field_element(&point.y, cs.namespace(|| "split y"))?; + lower_x.inputize(cs.namespace(|| "inputize lower_x"))?; + upper_x.inputize(cs.namespace(|| "inputize upper_x"))?; + lower_y.inputize(cs.namespace(|| "inputize lower_y"))?; + upper_y.inputize(cs.namespace(|| "inputize upper_y"))?; + point + .is_infinity + .inputize(cs.namespace(|| "inputize is_infinity"))?; + Ok(()) +} + +// TODO: Clean this up +fn split_field_element( + num: &AllocatedNum, + mut cs: CS, +) -> Result<(AllocatedNum, AllocatedNum), SynthesisError> +where + Scalar: PrimeField, + CS: ConstraintSystem, +{ + let lower_allocated_num = AllocatedNum::alloc(cs.namespace(|| "alloc lower"), || { + let repr = num.get_value().get()?.to_repr(); + let bytes = repr.as_ref(); + let (lower, _) = bytes.split_at(16); + Ok(Scalar::from_u128(u128::from_le_bytes( + (*lower).try_into().unwrap(), + ))) + })?; + let upper_allocated_num = AllocatedNum::alloc(cs.namespace(|| "alloc upper"), || { + let repr = num.get_value().get()?.to_repr(); + let bytes = repr.as_ref(); + let (_, upper) = bytes.split_at(16); + Ok(Scalar::from_u128(u128::from_le_bytes( + (*upper).try_into().unwrap(), + ))) + })?; + + let shift = alloc_constant( + Scalar::from_u128(u128::MAX) + Scalar::ONE, + cs.namespace(|| "alloc shift"), + ); + + let repr = num.get_value().get().map(|v| v.to_repr()); + + let shifted_upper_num = repr.map(|v| { + (0..128).fold( + Scalar::from_u128(u128::from_le_bytes( + (*v.as_ref().split_at(16).1).try_into().unwrap(), + )), + |acc, _| acc.double(), + ) + }); + + let shifted_upper_allocated_num = + AllocatedNum::alloc(cs.namespace(|| "alloc shifted_upper"), || shifted_upper_num)?; + + cs.enforce( + || "enforce shifted_upper is valid", + |lc| lc + upper_allocated_num.get_variable(), + |lc| lc + shift.get_variable(), + |lc| lc + shifted_upper_allocated_num.get_variable(), + ); + + cs.enforce( + || "enforce split", + |lc| lc + CS::one(), + |lc| lc + num.get_variable(), + |lc| lc + lower_allocated_num.get_variable() + shifted_upper_allocated_num.get_variable(), + ); + + Ok((lower_allocated_num, upper_allocated_num)) +} + #[cfg(test)] mod tests { use expect_test::{expect, Expect}; @@ -130,6 +205,22 @@ mod tests { use super::*; + fn test_split_field_elt_with() { + let rng = OsRng; + let rando = ::random(rng); + + let mut cs: SatisfyingAssignment = SatisfyingAssignment::::new(); + + let rando_num = AllocatedNum::alloc(cs.namespace(|| "alloc num"), || Ok(rando)).unwrap(); + + assert!(split_field_element(&rando_num, cs.namespace(|| "split num")).is_ok()); + } + + #[test] + fn test_split_field_elt() { + test_split_field_elt_with::() + } + // TODO: Split this test up into multiple tests fn test_cyclefold_circuit_size_with(expected_constraints: &Expect, expected_vars: &Expect) where @@ -176,11 +267,11 @@ mod tests { let num_constraints = cs.num_constraints(); let num_variables = cs.num_aux(); - let num_io = cs.num_inputs(); + let _num_io = cs.num_inputs(); expected_constraints.assert_eq(&num_constraints.to_string()); expected_vars.assert_eq(&num_variables.to_string()); - assert_eq!(num_io, 11); + // assert_eq!(num_io, 17); // 5 per point (15) + scalar (1) + 1 (1) let (shape, ck) = cs.r1cs_shape_and_key(&*default_ck_hint()); @@ -194,12 +285,21 @@ mod tests { let X = &instance.X; - let circuit_res_X = X[6]; - let circuit_res_Y = X[7]; - let circuit_res_is_infinity = X[8]; + let recombine_scalar = |lower: E1::Scalar, upper: E1::Scalar| -> E1::Scalar { + let mut upper = upper.clone(); + (0..128).for_each(|_| upper = upper.double()); + lower + upper + }; + + let circuit_res_X_lower = X[10]; + let circuit_res_X_upper = X[11]; + let circuit_res_X = recombine_scalar(circuit_res_X_lower, circuit_res_X_upper); + + let circuit_res_Y_lower = X[12]; + let circuit_res_Y_upper = X[13]; + let circuit_res_Y = recombine_scalar(circuit_res_Y_lower, circuit_res_Y_upper); - dbg!(res_X, res_Y, res_is_infinity); - dbg!(X); + let circuit_res_is_infinity = X[14]; assert_eq!(res_X, circuit_res_X); assert_eq!(res_Y, circuit_res_Y); @@ -213,9 +313,9 @@ mod tests { #[test] fn test_cyclefold_circuit_size() { - test_cyclefold_circuit_size_with::(&expect!("1371"), &expect!("1359")); - test_cyclefold_circuit_size_with::(&expect!("1371"), &expect!("1359")); - test_cyclefold_circuit_size_with::(&expect!("1371"), &expect!("1359")); + test_cyclefold_circuit_size_with::(&expect!("1395"), &expect!("1383")); + test_cyclefold_circuit_size_with::(&expect!("1395"), &expect!("1383")); + test_cyclefold_circuit_size_with::(&expect!("1395"), &expect!("1383")); } // TODO: add test for circuit satisfiability diff --git a/src/gadgets/utils.rs b/src/gadgets/utils.rs index 1b256c9d7..d251f4922 100644 --- a/src/gadgets/utils.rs +++ b/src/gadgets/utils.rs @@ -68,6 +68,22 @@ pub fn alloc_one>(mut cs: CS) -> Allocate one } +/// Allocate a constant and constrain it +pub fn alloc_constant>( + val: F, + mut cs: CS, +) -> AllocatedNum { + let allocated_val = AllocatedNum::alloc_infallible(cs.namespace(|| "alloc"), || val); + cs.enforce( + || "check val is valid", + |lc| lc, + |lc| lc, + |lc| lc + allocated_val.get_variable() - (val, CS::one()), + ); + + allocated_val +} + /// Allocate a scalar as a base. Only to be used is the scalar fits in base! pub fn alloc_scalar_as_base( mut cs: CS, From a3a722d8b2fd2a1b7fdc49cd252f0155b7d8146b Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Tue, 20 Feb 2024 10:42:22 -0500 Subject: [PATCH 30/77] uncomment num_io cyclefold check --- src/cyclefold/circuit.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cyclefold/circuit.rs b/src/cyclefold/circuit.rs index 7f01b4c6b..8d429e8ac 100644 --- a/src/cyclefold/circuit.rs +++ b/src/cyclefold/circuit.rs @@ -267,11 +267,11 @@ mod tests { let num_constraints = cs.num_constraints(); let num_variables = cs.num_aux(); - let _num_io = cs.num_inputs(); + let num_io = cs.num_inputs(); expected_constraints.assert_eq(&num_constraints.to_string()); expected_vars.assert_eq(&num_variables.to_string()); - // assert_eq!(num_io, 17); // 5 per point (15) + scalar (1) + 1 (1) + assert_eq!(num_io, 17); // 5 per point (15) + scalar (1) + 1 (1) let (shape, ck) = cs.r1cs_shape_and_key(&*default_ck_hint()); From 6b464495c79c334157ea87050020bfeeea38eb83 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Tue, 20 Feb 2024 11:21:40 -0500 Subject: [PATCH 31/77] update constant --- src/constants.rs | 2 +- src/cyclefold/circuit.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index d3d878cfe..9b164ed01 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -7,7 +7,7 @@ pub(crate) const NUM_FE_WITHOUT_IO_FOR_CRHF: usize = 9 + NIO_NOVA_FOLD * BN_N_LI pub(crate) const NUM_FE_FOR_RO: usize = 9; pub(crate) const NIO_NOVA_FOLD: usize = 2; pub(crate) const NUM_FE_IN_EMULATED_POINT: usize = 2 * BN_N_LIMBS + 1; -pub(crate) const NIO_CYCLE_FOLD: usize = 4; +pub(crate) const NIO_CYCLE_FOLD: usize = 16; // 5 per point (15) + 1 scalar /// Bit size of Nova field element hashes pub const NUM_HASH_BITS: usize = 250; diff --git a/src/cyclefold/circuit.rs b/src/cyclefold/circuit.rs index 8d429e8ac..85329e3de 100644 --- a/src/cyclefold/circuit.rs +++ b/src/cyclefold/circuit.rs @@ -199,6 +199,7 @@ mod tests { shape_cs::ShapeCS, solver::SatisfyingAssignment, }, + constants::NIO_CYCLE_FOLD, provider::{Bn256Engine, PallasEngine, Secp256k1Engine}, traits::{commitment::CommitmentEngineTrait, snark::default_ck_hint, CurveCycleEquipped, Dual}, }; @@ -271,7 +272,7 @@ mod tests { expected_constraints.assert_eq(&num_constraints.to_string()); expected_vars.assert_eq(&num_variables.to_string()); - assert_eq!(num_io, 17); // 5 per point (15) + scalar (1) + 1 (1) + assert_eq!(num_io, NIO_CYCLE_FOLD + 1); // includes 1 let (shape, ck) = cs.r1cs_shape_and_key(&*default_ck_hint()); From eb92e4ef710e8bae680049da4b7d6e1accee41df Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Tue, 20 Feb 2024 11:21:53 -0500 Subject: [PATCH 32/77] fix link --- src/cyclefold/nova_circuit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index 9db6fad9c..3c82c422a 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -291,7 +291,7 @@ where ), SynthesisError, > { - // Follows the outline written down here https://hackmd.io/@mpenciak/HybHrnNFT + // Follows the outline written down here https://hackmd.io/@lurk-lab/HybHrnNFT let mut ro_p = E1::ROCircuit::new( self.ro_consts.clone(), 2 + 2 * arity + 2 * NUM_FE_IN_EMULATED_POINT + 3, From 87d1695e75eeb9b47bdb770f5b37d48fa5b5a62c Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Tue, 20 Feb 2024 11:22:06 -0500 Subject: [PATCH 33/77] cleanup output --- src/cyclefold/snark.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 1d65d8571..054864b59 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -313,7 +313,7 @@ where let circuit_cyclefold_E: CyclefoldCircuit = CyclefoldCircuit::new(Some(self.r_U_primary.comm_E), Some(comm_T), r_bools.clone()); - let _output_cyclefold_E = circuit_cyclefold_E.synthesize(&mut cs_cyclefold_E); + let _ = circuit_cyclefold_E.synthesize(&mut cs_cyclefold_E); let (l_u_cyclefold_E, l_w_cyclefold_E) = cs_cyclefold_E .r1cs_instance_and_witness(&pp.circuit_shape_cyclefold.r1cs_shape, &pp.ck_cyclefold) @@ -344,7 +344,7 @@ where r_bools, ); - let _output_cyclefold_W = circuit_cyclefold_W.synthesize(&mut cs_cyclefold_W); + let _ = circuit_cyclefold_W.synthesize(&mut cs_cyclefold_W); let (l_u_cyclefold_W, l_w_cyclefold_W) = cs_cyclefold_W .r1cs_instance_and_witness(&pp.circuit_shape_cyclefold.r1cs_shape, &pp.ck_cyclefold) From 7697e642aff8b680776208f434a61fd616e976e0 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Tue, 20 Feb 2024 16:20:09 -0500 Subject: [PATCH 34/77] last couple circuit pieces --- src/cyclefold/gadgets.rs | 6 +-- src/cyclefold/nova_circuit.rs | 84 ++++++++++++++++++++++++++++++++++- 2 files changed, 85 insertions(+), 5 deletions(-) diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index dff5ec3af..7c719ab84 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -138,9 +138,9 @@ pub mod emulated { where G: Group, { - x: BigNat, - y: BigNat, - is_infinity: AllocatedBit, + pub x: BigNat, + pub y: BigNat, + pub is_infinity: AllocatedBit, } impl AllocatedEmulPoint diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index 3c82c422a..c3c686365 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -2,10 +2,10 @@ use crate::{ constants::{ - NIO_CYCLE_FOLD, NUM_FE_IN_EMULATED_POINT, NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_BITS, + BN_N_LIMBS, NIO_CYCLE_FOLD, NUM_FE_IN_EMULATED_POINT, NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_BITS, }, gadgets::{ - r1cs::AllocatedRelaxedR1CSInstance, + r1cs::{AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance}, utils::{ alloc_num_equals, alloc_scalar_as_base, alloc_zero, conditionally_select_vec, le_bits_to_num, }, @@ -21,6 +21,7 @@ use abomonation_derive::Abomonation; use bellpepper::gadgets::{boolean::Boolean, num::AllocatedNum, Assignment}; use bellpepper_core::{boolean::AllocatedBit, ConstraintSystem, SynthesisError}; use ff::Field; +use itertools::{chain, Itertools}; use serde::{Deserialize, Serialize}; use super::gadgets::{emulated, AllocatedFoldingData}; @@ -334,6 +335,10 @@ where &hash_c, )?; + // TODO: Add `cyclefold_invocation_check`s here after I change the circuit IO + // cyclefold_invocation_check(cs, C_1, C_2, C_res, instance); + // cyclefold_invocation_check(cs, C_1, C_2, C_res, instance); + let check_io = AllocatedBit::and( cs.namespace(|| "both IOs match"), &check_primary, @@ -515,6 +520,81 @@ where } } +// TODO: Clean this up +pub fn emulated_point_check::Base>>( + mut cs: CS, + point: &emulated::AllocatedEmulPoint, + io_limbs: &[AllocatedNum<::Base>], +) -> Result<(), SynthesisError> { + let x_limbs = point + .x + .group_limbs(BN_N_LIMBS / 2) + .as_limbs() + .iter() + .enumerate() + .map(|(idx, limb)| limb.as_allocated_num(cs.namespace(|| format!("alloc x_limb[{idx}]")))) + .collect::, _>>()?; + + let y_limbs = point + .y + .group_limbs(BN_N_LIMBS / 2) + .as_limbs() + .iter() + .enumerate() + .map(|(idx, limb)| limb.as_allocated_num(cs.namespace(|| format!("alloc y_limb[{idx}]")))) + .collect::, _>>()?; + + let is_infinity = AllocatedNum::alloc(cs.namespace(|| "allocate is_infinity"), || { + if *point.is_infinity.get_value().get()? { + Ok(::Base::ONE) + } else { + Ok(::Base::ZERO) + } + })?; + + cs.enforce( + || "enforcing is_infinity", + |lc| lc, + |lc| lc, + |lc| lc + is_infinity.get_variable() - point.is_infinity.get_variable(), + ); + + let all_variables = chain!(x_limbs, y_limbs, vec![is_infinity]); + + all_variables + .into_iter() + .zip_eq(io_limbs) + .enumerate() + .try_for_each(|(idx, (var, limb))| -> Result<(), SynthesisError> { + cs.enforce( + || format!("enforcing equality {idx}"), + |lc| lc, + |lc| lc, + |lc| lc + var.get_variable() - limb.get_variable(), + ); + + Ok(()) + })?; + + Ok(()) +} + +// TODO: Clean this up +pub fn cyclefold_invocation_check::Base>>( + mut cs: CS, + C_1: &emulated::AllocatedEmulPoint, + C_2: &emulated::AllocatedEmulPoint, + C_res: &emulated::AllocatedEmulPoint, + instance: AllocatedR1CSInstance, +) -> Result<(), SynthesisError> { + let (point_1_io, point_23_io) = instance.X.split_at(5); + let (point_2_io, point_3_io) = point_23_io.split_at(5); + emulated_point_check::(cs.namespace(|| "check point C_1"), C_1, point_1_io)?; + emulated_point_check::(cs.namespace(|| "check point C_2"), C_2, point_2_io)?; + emulated_point_check::(cs.namespace(|| "check point C_res"), C_res, point_3_io)?; + + Ok(()) +} #[cfg(test)] mod test { use crate::{ From 6463bd91059b3e701b478bf41c96d30a0a368179 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Tue, 20 Feb 2024 17:49:03 -0500 Subject: [PATCH 35/77] add comm_T to inputs and add cyclefold IO checks --- src/cyclefold/gadgets.rs | 4 +-- src/cyclefold/nova_circuit.rs | 46 +++++++++++++++++++++++++++++++---- src/cyclefold/snark.rs | 2 ++ 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index 7c719ab84..80de30e4c 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -337,8 +337,8 @@ pub mod emulated { } pub struct AllocatedEmulRelaxedR1CSInstance { - comm_W: AllocatedEmulPoint, - comm_E: AllocatedEmulPoint, + pub comm_W: AllocatedEmulPoint, + pub comm_E: AllocatedEmulPoint, u: AllocatedNum, x0: AllocatedNum, x1: AllocatedNum, diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index c3c686365..55708f0c2 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -73,6 +73,8 @@ where data_c_1: Option>, data_c_2: Option>, + comm_T: Option>, + E_new: Option>, W_new: Option>, } @@ -90,6 +92,7 @@ where data_p: Option>, data_c_1: Option>, data_c_2: Option>, + comm_T: Option>, E_new: Option>, W_new: Option>, ) -> Self { @@ -101,6 +104,7 @@ where data_p, data_c_1, data_c_2, + comm_T, E_new, W_new, } @@ -151,6 +155,7 @@ where emulated::AllocatedFoldingData, //data_p AllocatedFoldingData, // data_c_1 AllocatedFoldingData, // data_c_2 + emulated::AllocatedEmulPoint, // comm_T emulated::AllocatedEmulPoint, // E_new emulated::AllocatedEmulPoint, // W_new ), @@ -219,6 +224,17 @@ where self.params.n_limbs, )?; + let comm_T = emulated::AllocatedEmulPoint::alloc( + cs.namespace(|| "comm_T"), + self + .inputs + .as_ref() + .and_then(|inputs| inputs.comm_T.as_ref()) + .map(|comm_T| comm_T.to_coordinates()), + self.params.limb_width, + self.params.n_limbs, + )?; + let E_new = emulated::AllocatedEmulPoint::alloc( cs.namespace(|| "E_new"), self @@ -242,7 +258,7 @@ where )?; Ok(( - pp_digest, i, z_0, z_i, data_p, data_c_1, data_c_2, E_new, W_new, + pp_digest, i, z_0, z_i, data_p, data_c_1, data_c_2, comm_T, E_new, W_new, )) } @@ -281,6 +297,7 @@ where data_p: &emulated::AllocatedFoldingData, data_c_1: &AllocatedFoldingData, data_c_2: &AllocatedFoldingData, + comm_T: &emulated::AllocatedEmulPoint, E_new: &emulated::AllocatedEmulPoint, W_new: &emulated::AllocatedEmulPoint, arity: usize, @@ -335,9 +352,27 @@ where &hash_c, )?; + let u_E = &data_c_1.u; + let E_1 = &data_p.U.comm_E; // TODO: Add `cyclefold_invocation_check`s here after I change the circuit IO - // cyclefold_invocation_check(cs, C_1, C_2, C_res, instance); - // cyclefold_invocation_check(cs, C_1, C_2, C_res, instance); + cyclefold_invocation_check( + cs.namespace(|| "cyclefold invocation check E"), + &E_1, + comm_T, + E_new, + u_E, + )?; + + let u_W = &data_c_2.u; + let W_1 = &data_p.U.comm_W; + let W_2 = &data_p.u_W; + cyclefold_invocation_check( + cs.namespace(|| "cyclefold invocation check W"), + &W_1, + &W_2, + W_new, + u_W, + )?; let check_io = AllocatedBit::and( cs.namespace(|| "both IOs match"), @@ -413,7 +448,7 @@ where // TODO: It's written down here https://hackmd.io/SBvAur_2RQmaduDi7gYbhw let arity = self.step_circuit.arity(); - let (pp_digest, i, z_0, z_i, data_p, data_c_1, data_c_2, E_new, W_new) = + let (pp_digest, i, z_0, z_i, data_p, data_c_1, data_c_2, comm_T, E_new, W_new) = self.alloc_witness(cs.namespace(|| "alloc_witness"), arity)?; let zero = alloc_zero(cs.namespace(|| "zero")); @@ -430,6 +465,7 @@ where &data_p, &data_c_1, &data_c_2, + &comm_T, &E_new, &W_new, arity, @@ -585,7 +621,7 @@ pub fn cyclefold_invocation_check, C_2: &emulated::AllocatedEmulPoint, C_res: &emulated::AllocatedEmulPoint, - instance: AllocatedR1CSInstance, + instance: &AllocatedR1CSInstance, ) -> Result<(), SynthesisError> { let (point_1_io, point_23_io) = instance.X.split_at(5); let (point_2_io, point_3_io) = point_23_io.split_at(5); diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 054864b59..78b9a3dac 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -200,6 +200,7 @@ where None, None, None, + None, ); let circuit_primary = AugmentedCircuit::new( @@ -381,6 +382,7 @@ where Some(data_p), Some(data_c_E), Some(data_c_W), + Some(comm_T), Some(E_new), Some(W_new), ); From 7cb4a105af4b93746baad31173b13e9d7a4eebcf Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Tue, 20 Feb 2024 18:15:42 -0500 Subject: [PATCH 36/77] fix ro absorb numbers --- src/cyclefold/nova_circuit.rs | 14 ++++++++------ src/gadgets/r1cs.rs | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index 55708f0c2..465adc5de 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -2,7 +2,8 @@ use crate::{ constants::{ - BN_N_LIMBS, NIO_CYCLE_FOLD, NUM_FE_IN_EMULATED_POINT, NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_BITS, + BN_N_LIMBS, NIO_CYCLE_FOLD, NUM_FE_IN_EMULATED_POINT, + /*NUM_FE_WITHOUT_IO_FOR_CRHF,*/ NUM_HASH_BITS, }, gadgets::{ r1cs::{AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance}, @@ -336,7 +337,7 @@ where &hash_p, )?; - let mut ro_c = E1::ROCircuit::new(self.ro_consts.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF); + let mut ro_c = E1::ROCircuit::new(self.ro_consts.clone(), 73); // TODO: Be more dilligent about this ro_c.absorb(pp_digest); ro_c.absorb(i); @@ -392,7 +393,7 @@ where )?; // Calculate h_int = H(pp, U_c_int) - let mut ro_c_int = E1::ROCircuit::new(self.ro_consts.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF - 1); + let mut ro_c_int = E1::ROCircuit::new(self.ro_consts.clone(), 72); // TODO: Fix this ro_c_int.absorb(pp_digest); U_int.absorb_in_ro(cs.namespace(|| "absorb U_c_int"), &mut ro_c_int)?; let h_c_int_bits = @@ -400,7 +401,7 @@ where let h_c_int = le_bits_to_num(cs.namespace(|| "intermediate hash"), &h_c_int_bits)?; // Calculate h_1 = H(pp, U_c_1) - let mut ro_c_1 = E1::ROCircuit::new(self.ro_consts.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF - 1); + let mut ro_c_1 = E1::ROCircuit::new(self.ro_consts.clone(), 72); // TODO: Fix this ro_c_1.absorb(pp_digest); data_c_2 .U @@ -542,7 +543,7 @@ where let hash_p_bits = ro_p.squeeze(cs.namespace(|| "hash_p_bits"), NUM_HASH_BITS)?; let hash_p = le_bits_to_num(cs.namespace(|| "hash_p"), &hash_p_bits)?; - let mut ro_c = E1::ROCircuit::new(self.ro_consts, NUM_FE_WITHOUT_IO_FOR_CRHF); + let mut ro_c = E1::ROCircuit::new(self.ro_consts, 72); // TODO: Fix this ro_c.absorb(&pp_digest); ro_c.absorb(&i_new); Unew_c.absorb_in_ro(cs.namespace(|| "absorb Unew_c"), &mut ro_c)?; @@ -624,7 +625,8 @@ pub fn cyclefold_invocation_check, ) -> Result<(), SynthesisError> { let (point_1_io, point_23_io) = instance.X.split_at(5); - let (point_2_io, point_3_io) = point_23_io.split_at(5); + let (point_2_io, point_3_io_plus_scalar) = point_23_io.split_at(5); + let point_3_io = point_3_io_plus_scalar.split_at(5).0; emulated_point_check::(cs.namespace(|| "check point C_1"), C_1, point_1_io)?; emulated_point_check::(cs.namespace(|| "check point C_2"), C_2, point_2_io)?; emulated_point_check::(cs.namespace(|| "check point C_res"), C_res, point_3_io)?; diff --git a/src/gadgets/r1cs.rs b/src/gadgets/r1cs.rs index 85b87f7c2..a19becd35 100644 --- a/src/gadgets/r1cs.rs +++ b/src/gadgets/r1cs.rs @@ -4,7 +4,7 @@ use super::nonnative::{ util::{f_to_nat, Num}, }; use crate::{ - constants::{NUM_CHALLENGE_BITS, NUM_FE_FOR_RO}, + constants::NUM_CHALLENGE_BITS, gadgets::{ ecc::AllocatedPoint, utils::{ @@ -229,7 +229,7 @@ impl AllocatedRelaxedR1CSInstance { n_limbs: usize, ) -> Result { // Compute r: - let mut ro = E::ROCircuit::new(ro_consts, NUM_FE_FOR_RO); + let mut ro = E::ROCircuit::new(ro_consts, 7 + N); ro.absorb(params); // running instance `U` does not need to absorbed since u.X[0] = Hash(params, U, i, z0, zi) From ee8cf699ed42d243804d4ef845c2206004cf45da Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Wed, 21 Feb 2024 16:55:23 -0500 Subject: [PATCH 37/77] remove extra unneeded comm_T allocation --- src/cyclefold/nova_circuit.rs | 24 +++--------------------- src/cyclefold/snark.rs | 2 -- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index 465adc5de..3dcb1f139 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -74,8 +74,6 @@ where data_c_1: Option>, data_c_2: Option>, - comm_T: Option>, - E_new: Option>, W_new: Option>, } @@ -93,7 +91,6 @@ where data_p: Option>, data_c_1: Option>, data_c_2: Option>, - comm_T: Option>, E_new: Option>, W_new: Option>, ) -> Self { @@ -105,7 +102,6 @@ where data_p, data_c_1, data_c_2, - comm_T, E_new, W_new, } @@ -156,7 +152,6 @@ where emulated::AllocatedFoldingData, //data_p AllocatedFoldingData, // data_c_1 AllocatedFoldingData, // data_c_2 - emulated::AllocatedEmulPoint, // comm_T emulated::AllocatedEmulPoint, // E_new emulated::AllocatedEmulPoint, // W_new ), @@ -225,17 +220,6 @@ where self.params.n_limbs, )?; - let comm_T = emulated::AllocatedEmulPoint::alloc( - cs.namespace(|| "comm_T"), - self - .inputs - .as_ref() - .and_then(|inputs| inputs.comm_T.as_ref()) - .map(|comm_T| comm_T.to_coordinates()), - self.params.limb_width, - self.params.n_limbs, - )?; - let E_new = emulated::AllocatedEmulPoint::alloc( cs.namespace(|| "E_new"), self @@ -259,7 +243,7 @@ where )?; Ok(( - pp_digest, i, z_0, z_i, data_p, data_c_1, data_c_2, comm_T, E_new, W_new, + pp_digest, i, z_0, z_i, data_p, data_c_1, data_c_2, E_new, W_new, )) } @@ -298,7 +282,6 @@ where data_p: &emulated::AllocatedFoldingData, data_c_1: &AllocatedFoldingData, data_c_2: &AllocatedFoldingData, - comm_T: &emulated::AllocatedEmulPoint, E_new: &emulated::AllocatedEmulPoint, W_new: &emulated::AllocatedEmulPoint, arity: usize, @@ -359,7 +342,7 @@ where cyclefold_invocation_check( cs.namespace(|| "cyclefold invocation check E"), &E_1, - comm_T, + &data_p.T, E_new, u_E, )?; @@ -449,7 +432,7 @@ where // TODO: It's written down here https://hackmd.io/SBvAur_2RQmaduDi7gYbhw let arity = self.step_circuit.arity(); - let (pp_digest, i, z_0, z_i, data_p, data_c_1, data_c_2, comm_T, E_new, W_new) = + let (pp_digest, i, z_0, z_i, data_p, data_c_1, data_c_2, E_new, W_new) = self.alloc_witness(cs.namespace(|| "alloc_witness"), arity)?; let zero = alloc_zero(cs.namespace(|| "zero")); @@ -466,7 +449,6 @@ where &data_p, &data_c_1, &data_c_2, - &comm_T, &E_new, &W_new, arity, diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 78b9a3dac..054864b59 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -200,7 +200,6 @@ where None, None, None, - None, ); let circuit_primary = AugmentedCircuit::new( @@ -382,7 +381,6 @@ where Some(data_p), Some(data_c_E), Some(data_c_W), - Some(comm_T), Some(E_new), Some(W_new), ); From 315d1ce6daa3a841d856e8aaa6f1a7d28e1c1ad0 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Wed, 21 Feb 2024 17:28:22 -0500 Subject: [PATCH 38/77] properly constrain `always_equal` in `mult_mod` and `red_mod` --- src/gadgets/nonnative/bignat.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/gadgets/nonnative/bignat.rs b/src/gadgets/nonnative/bignat.rs index bcfc79691..3935a7770 100644 --- a/src/gadgets/nonnative/bignat.rs +++ b/src/gadgets/nonnative/bignat.rs @@ -577,6 +577,13 @@ impl BigNat { let right_int = Self::from_poly(right, limb_width, right_max_word); let always_equal = AllocatedBit::alloc(cs.namespace(|| "always_equal = false"), Some(false))?; + cs.enforce( + || "enforce always_equal", + |lc| lc, + |lc| lc, + |lc| lc + always_equal.get_variable(), + ); + left_int.equal_when_carried_regroup(cs.namespace(|| "carry"), &right_int, &always_equal)?; Ok((quotient, remainder)) } @@ -623,6 +630,13 @@ impl BigNat { let right_int = Self::from_poly(right, limb_width, right_max_word); let always_equal = AllocatedBit::alloc(cs.namespace(|| "always_equal = false"), Some(false))?; + cs.enforce( + || "enforce always_equal", + |lc| lc, + |lc| lc, + |lc| lc + always_equal.get_variable(), + ); + self.equal_when_carried_regroup(cs.namespace(|| "carry"), &right_int, &always_equal)?; Ok(remainder) } From 42830f56ca8c19b3ad033d8e4af8addd68182cd2 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Mon, 26 Feb 2024 17:41:48 -0500 Subject: [PATCH 39/77] some allocation fixes --- src/cyclefold/gadgets.rs | 6 +++--- src/cyclefold/nova_circuit.rs | 16 +++------------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index 80de30e4c..46d9a1a32 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -160,7 +160,7 @@ pub mod emulated { cs.namespace(|| "x"), || { Ok(f_to_nat( - &coords.ok_or(SynthesisError::AssignmentMissing)?.0, + &coords.map_or(::ZERO, |val| val.0), )) }, limb_width, @@ -171,7 +171,7 @@ pub mod emulated { cs.namespace(|| "y"), || { Ok(f_to_nat( - &coords.ok_or(SynthesisError::AssignmentMissing)?.1, + &coords.map_or(::ZERO, |val| val.1), )) }, limb_width, @@ -180,7 +180,7 @@ pub mod emulated { let is_infinity = AllocatedBit::alloc( cs.namespace(|| "alloc is_infinity"), - coords.map(|(_, _, is_infinity)| is_infinity), + coords.map_or(Some(true), |(_, _, is_infinity)| Some(is_infinity)), )?; Ok(Self { x, y, is_infinity }) diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index 3dcb1f139..4dd5277fd 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -162,15 +162,7 @@ where self.inputs.as_ref().map(|inputs| inputs.pp_digest), )?; - let i = AllocatedNum::alloc(cs.namespace(|| "i"), || { - Ok( - self - .inputs - .as_ref() - .ok_or(SynthesisError::AssignmentMissing)? - .i, - ) - })?; + let i = AllocatedNum::alloc(cs.namespace(|| "i"), || Ok(self.inputs.get()?.i))?; let z_0 = (0..arity) .map(|i| { @@ -466,18 +458,16 @@ where |lc| lc, ); - let is_base_case_bool = Boolean::from(is_base_case.clone()); - let Unew_p = Unew_p_base.conditionally_select( cs.namespace(|| "compute Unew_p"), &Unew_p_non_base, - &is_base_case_bool, + &Boolean::from(is_base_case.clone()), )?; let Unew_c = Unew_c_base.conditionally_select( cs.namespace(|| "compute Unew_c"), &Unew_c_non_base, - &is_base_case_bool, + &Boolean::from(is_base_case.clone()), )?; // Compute i + 1 From 178bb1f170146e4a4cfca12af3b69a5306b571d1 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Mon, 26 Feb 2024 17:42:56 -0500 Subject: [PATCH 40/77] eliminate clone --- src/cyclefold/nova_circuit.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index 4dd5277fd..fd8b1b98e 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -274,8 +274,8 @@ where data_p: &emulated::AllocatedFoldingData, data_c_1: &AllocatedFoldingData, data_c_2: &AllocatedFoldingData, - E_new: &emulated::AllocatedEmulPoint, - W_new: &emulated::AllocatedEmulPoint, + E_new: emulated::AllocatedEmulPoint, + W_new: emulated::AllocatedEmulPoint, arity: usize, ) -> Result< ( @@ -335,7 +335,7 @@ where cs.namespace(|| "cyclefold invocation check E"), &E_1, &data_p.T, - E_new, + &E_new, u_E, )?; @@ -346,7 +346,7 @@ where cs.namespace(|| "cyclefold invocation check W"), &W_1, &W_2, - W_new, + &W_new, u_W, )?; @@ -405,8 +405,8 @@ where let U_p = data_p.U.fold_with_r1cs( cs.namespace(|| "fold u_p into U_p"), pp_digest, - W_new.clone(), - E_new.clone(), + W_new, + E_new, &data_p.u_W, &data_p.u_x0, &data_p.u_x1, @@ -441,8 +441,8 @@ where &data_p, &data_c_1, &data_c_2, - &E_new, - &W_new, + E_new, + W_new, arity, )?; From ceb9c6bfc7389ec5c88cf5eaaadbbe5f6a6e73cb Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Mon, 26 Feb 2024 17:45:50 -0500 Subject: [PATCH 41/77] make RO absorb numbers make sense --- src/cyclefold/nova_circuit.rs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index fd8b1b98e..dc8a55a4b 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -312,7 +312,10 @@ where &hash_p, )?; - let mut ro_c = E1::ROCircuit::new(self.ro_consts.clone(), 73); // TODO: Be more dilligent about this + let mut ro_c = E1::ROCircuit::new( + self.ro_consts.clone(), + 1 + 1 + 3 + 3 + 1 + NIO_CYCLE_FOLD * BN_N_LIMBS, // pp + i + W + E + u + X + ); ro_c.absorb(pp_digest); ro_c.absorb(i); @@ -330,7 +333,6 @@ where let u_E = &data_c_1.u; let E_1 = &data_p.U.comm_E; - // TODO: Add `cyclefold_invocation_check`s here after I change the circuit IO cyclefold_invocation_check( cs.namespace(|| "cyclefold invocation check E"), &E_1, @@ -368,7 +370,10 @@ where )?; // Calculate h_int = H(pp, U_c_int) - let mut ro_c_int = E1::ROCircuit::new(self.ro_consts.clone(), 72); // TODO: Fix this + let mut ro_c_int = E1::ROCircuit::new( + self.ro_consts.clone(), + 1 + 3 + 3 + 1 + NIO_CYCLE_FOLD * BN_N_LIMBS, // pp + W + E + u + X + ); ro_c_int.absorb(pp_digest); U_int.absorb_in_ro(cs.namespace(|| "absorb U_c_int"), &mut ro_c_int)?; let h_c_int_bits = @@ -376,7 +381,11 @@ where let h_c_int = le_bits_to_num(cs.namespace(|| "intermediate hash"), &h_c_int_bits)?; // Calculate h_1 = H(pp, U_c_1) - let mut ro_c_1 = E1::ROCircuit::new(self.ro_consts.clone(), 72); // TODO: Fix this + let mut ro_c_1 = E1::ROCircuit::new( + self.ro_consts.clone(), + 1 + 3 + 3 + 1 + NIO_CYCLE_FOLD * BN_N_LIMBS, // pp + W + E + u + X + ); + ro_c_1.absorb(pp_digest); data_c_2 .U @@ -515,7 +524,10 @@ where let hash_p_bits = ro_p.squeeze(cs.namespace(|| "hash_p_bits"), NUM_HASH_BITS)?; let hash_p = le_bits_to_num(cs.namespace(|| "hash_p"), &hash_p_bits)?; - let mut ro_c = E1::ROCircuit::new(self.ro_consts, 72); // TODO: Fix this + let mut ro_c = E1::ROCircuit::new( + self.ro_consts, + 1 + 1 + 3 + 3 + 1 + NIO_CYCLE_FOLD * BN_N_LIMBS, + ); // pp + i + W + E + u + X ro_c.absorb(&pp_digest); ro_c.absorb(&i_new); Unew_c.absorb_in_ro(cs.namespace(|| "absorb Unew_c"), &mut ro_c)?; From 6a63178e1b2aac03c44fc6b3661db9e0d1d2c997 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Mon, 26 Feb 2024 20:59:03 -0500 Subject: [PATCH 42/77] fix cyclefold scalar alloc --- src/cyclefold/circuit.rs | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/cyclefold/circuit.rs b/src/cyclefold/circuit.rs index 85329e3de..e9f7e0032 100644 --- a/src/cyclefold/circuit.rs +++ b/src/cyclefold/circuit.rs @@ -5,7 +5,7 @@ use bellpepper_core::{boolean::AllocatedBit, num::AllocatedNum, ConstraintSystem use ff::PrimeField; use crate::{ - constants::{NUM_CHALLENGE_BITS, NUM_HASH_BITS}, + constants::NUM_CHALLENGE_BITS, gadgets::{ ecc::AllocatedPoint, utils::{alloc_constant, le_bits_to_num}, @@ -67,19 +67,15 @@ impl CyclefoldCircuit { )?; commit_2.check_on_curve(cs.namespace(|| "commit_2 on curve"))?; - let false_bit = AllocatedBit::alloc(cs.namespace(|| "allocated false bit"), Some(false))?; - let scalar: Vec = - self - .scalar - .map_or(Ok(vec![false_bit; NUM_HASH_BITS]), |bits| { - bits - .into_iter() - .enumerate() - .map(|(idx, bit)| { - AllocatedBit::alloc(cs.namespace(|| format!("scalar bit {idx}")), Some(bit)) - }) - .collect::, _>>() - })?; + let scalar: Vec = self + .scalar + .unwrap_or([false; NUM_CHALLENGE_BITS]) + .into_iter() + .enumerate() + .map(|(idx, bit)| { + AllocatedBit::alloc(cs.namespace(|| format!("scalar bit {idx}")), Some(bit)) + }) + .collect::, _>>()?; Ok((commit_1, commit_2, scalar)) } From 734dead3e9a3f2911d5922cba2a9eb5c17da26b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= Date: Tue, 27 Feb 2024 08:41:54 -0500 Subject: [PATCH 43/77] chore: update Bn256Engine -> Bn256EngineKZG --- src/cyclefold/circuit.rs | 6 +++--- src/cyclefold/nova_circuit.rs | 4 ++-- src/cyclefold/snark.rs | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cyclefold/circuit.rs b/src/cyclefold/circuit.rs index e9f7e0032..78cf989d7 100644 --- a/src/cyclefold/circuit.rs +++ b/src/cyclefold/circuit.rs @@ -196,7 +196,7 @@ mod tests { solver::SatisfyingAssignment, }, constants::NIO_CYCLE_FOLD, - provider::{Bn256Engine, PallasEngine, Secp256k1Engine}, + provider::{Bn256EngineKZG, PallasEngine, Secp256k1Engine}, traits::{commitment::CommitmentEngineTrait, snark::default_ck_hint, CurveCycleEquipped, Dual}, }; @@ -215,7 +215,7 @@ mod tests { #[test] fn test_split_field_elt() { - test_split_field_elt_with::() + test_split_field_elt_with::() } // TODO: Split this test up into multiple tests @@ -311,7 +311,7 @@ mod tests { #[test] fn test_cyclefold_circuit_size() { test_cyclefold_circuit_size_with::(&expect!("1395"), &expect!("1383")); - test_cyclefold_circuit_size_with::(&expect!("1395"), &expect!("1383")); + test_cyclefold_circuit_size_with::(&expect!("1395"), &expect!("1383")); test_cyclefold_circuit_size_with::(&expect!("1395"), &expect!("1383")); } diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index dc8a55a4b..667844c8d 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -622,7 +622,7 @@ mod test { use crate::{ bellpepper::test_shape_cs::TestShapeCS, constants::{BN_LIMB_WIDTH, BN_N_LIMBS}, - provider::{Bn256Engine, PallasEngine, Secp256k1Engine}, + provider::{Bn256EngineKZG, PallasEngine, Secp256k1Engine}, traits::{circuit::TrivialCircuit, CurveCycleEquipped, Dual}, }; @@ -659,6 +659,6 @@ mod test { fn test_augmented_circuit_size() { test_augmented_circuit_size_with::(); test_augmented_circuit_size_with::(); - test_augmented_circuit_size_with::(); + test_augmented_circuit_size_with::(); } } diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 054864b59..f8368fd94 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -532,7 +532,7 @@ mod test { use super::*; use crate::{ - provider::{Bn256Engine, PallasEngine, Secp256k1Engine}, + provider::{Bn256EngineKZG, PallasEngine, Secp256k1Engine}, traits::snark::default_ck_hint, }; @@ -580,7 +580,7 @@ mod test { #[test] fn test_cyclefold_prove_verify() { test_trivial_cyclefold_prove_verify_with::(); - test_trivial_cyclefold_prove_verify_with::(); + test_trivial_cyclefold_prove_verify_with::(); test_trivial_cyclefold_prove_verify_with::(); } } From fb85877c8e9208e2512dc8b1ae7a40d27d76fe77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= Date: Tue, 27 Feb 2024 08:43:03 -0500 Subject: [PATCH 44/77] chore: run clippy --- src/cyclefold/circuit.rs | 10 +++++----- src/cyclefold/nova_circuit.rs | 6 +++--- src/cyclefold/snark.rs | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/cyclefold/circuit.rs b/src/cyclefold/circuit.rs index 78cf989d7..f21c06e51 100644 --- a/src/cyclefold/circuit.rs +++ b/src/cyclefold/circuit.rs @@ -210,7 +210,7 @@ mod tests { let rando_num = AllocatedNum::alloc(cs.namespace(|| "alloc num"), || Ok(rando)).unwrap(); - assert!(split_field_element(&rando_num, cs.namespace(|| "split num")).is_ok()); + split_field_element(&rando_num, cs.namespace(|| "split num")).unwrap(); } #[test] @@ -250,7 +250,7 @@ mod tests { let r_bits = r .to_le_bits() .into_iter() - .map(|b| Some(b)) + .map(Some) .collect::>>() .map(|mut vec| { vec.resize_with(128, || false); @@ -274,7 +274,7 @@ mod tests { let mut cs = SatisfyingAssignment::::new(); - let _ = circuit + circuit .synthesize(cs.namespace(|| "synthesizing witness")) .unwrap(); @@ -283,7 +283,7 @@ mod tests { let X = &instance.X; let recombine_scalar = |lower: E1::Scalar, upper: E1::Scalar| -> E1::Scalar { - let mut upper = upper.clone(); + let mut upper = upper; (0..128).for_each(|_| upper = upper.double()); lower + upper }; @@ -305,7 +305,7 @@ mod tests { circuit_res_is_infinity == as Engine>::Base::ONE ); - assert!(shape.is_sat(&ck, &instance, &witness).is_ok()); + shape.is_sat(&ck, &instance, &witness).unwrap(); } #[test] diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index 667844c8d..435e7bd8a 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -335,7 +335,7 @@ where let E_1 = &data_p.U.comm_E; cyclefold_invocation_check( cs.namespace(|| "cyclefold invocation check E"), - &E_1, + E_1, &data_p.T, &E_new, u_E, @@ -346,8 +346,8 @@ where let W_2 = &data_p.u_W; cyclefold_invocation_check( cs.namespace(|| "cyclefold invocation check W"), - &W_1, - &W_2, + W_1, + W_2, &W_new, u_W, )?; diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index f8368fd94..3d529202c 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -311,7 +311,7 @@ where ); let circuit_cyclefold_E: CyclefoldCircuit = - CyclefoldCircuit::new(Some(self.r_U_primary.comm_E), Some(comm_T), r_bools.clone()); + CyclefoldCircuit::new(Some(self.r_U_primary.comm_E), Some(comm_T), r_bools); let _ = circuit_cyclefold_E.synthesize(&mut cs_cyclefold_E); @@ -570,11 +570,11 @@ mod test { let res = recursive_snark.prove_step(&pp, &primary_circuit); - assert!(res.is_ok()); + res.unwrap(); let res = recursive_snark.verify(&pp, num_steps, &z0); - assert!(res.is_ok()); + res.unwrap(); } #[test] From ab73fa955183fb76dde37a46447b70f61fe38c27 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Tue, 27 Feb 2024 22:40:57 -0500 Subject: [PATCH 45/77] update `expect_test`s --- src/circuit.rs | 12 ++++++------ src/cyclefold/circuit.rs | 6 +++--- src/lib.rs | 12 ++++++------ src/supernova/circuit.rs | 12 ++++++------ src/supernova/test.rs | 10 +++++----- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/circuit.rs b/src/circuit.rs index fadade590..e49ffcbae 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -459,8 +459,8 @@ mod tests { ¶ms2, ro_consts1, ro_consts2, - &expect!["9821"], - &expect!["10353"], + &expect!["9825"], + &expect!["10357"], ); } @@ -476,8 +476,8 @@ mod tests { ¶ms2, ro_consts1, ro_consts2, - &expect!["9989"], - &expect!["10542"], + &expect!["9993"], + &expect!["10546"], ); } @@ -493,8 +493,8 @@ mod tests { ¶ms2, ro_consts1, ro_consts2, - &expect!["10268"], - &expect!["10965"], + &expect!["10272"], + &expect!["10969"], ); } } diff --git a/src/cyclefold/circuit.rs b/src/cyclefold/circuit.rs index f21c06e51..bcf0ba7ad 100644 --- a/src/cyclefold/circuit.rs +++ b/src/cyclefold/circuit.rs @@ -310,9 +310,9 @@ mod tests { #[test] fn test_cyclefold_circuit_size() { - test_cyclefold_circuit_size_with::(&expect!("1395"), &expect!("1383")); - test_cyclefold_circuit_size_with::(&expect!("1395"), &expect!("1383")); - test_cyclefold_circuit_size_with::(&expect!("1395"), &expect!("1383")); + test_cyclefold_circuit_size_with::(&expect!("1394"), &expect!("1382")); + test_cyclefold_circuit_size_with::(&expect!("1394"), &expect!("1382")); + test_cyclefold_circuit_size_with::(&expect!("1394"), &expect!("1382")); } // TODO: add test for circuit satisfiability diff --git a/src/lib.rs b/src/lib.rs index 6907770c4..80aa267bd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1121,36 +1121,36 @@ mod tests { test_pp_digest_with::, EE<_>>( &TrivialCircuit::default(), &TrivialCircuit::default(), - &expect!["e5a6a85b77f3fb958b69722a5a21bf656fd21a6b5a012708a4b086b6be6d2b03"], + &expect!["b948575a34e25b1f306a0fef795d4a809d536828281465f4ccbf990f126c2f03"], ); test_pp_digest_with::, EE<_>>( &CubicCircuit::default(), &TrivialCircuit::default(), - &expect!["ec707a8b822baebca114b6e61b238374f9ed358c542dd37ee73febb47832cd01"], + &expect!["a3ad9ddd33c45764340638b1d33988efd212de2df74551d37a3e8cb6802fe001"], ); test_pp_digest_with::, EE<_>>( &TrivialCircuit::default(), &TrivialCircuit::default(), - &expect!["df52de22456157eb056003d4dc580a167ab8ce40a151c9944ea09a6fd0028600"], + &expect!["9e2dac4031303a8e677c1d8aa05954019e3dc979e104fb332b3308cffdbe4600"], ); test_pp_digest_with::, EE<_>>( &CubicCircuit::default(), &TrivialCircuit::default(), - &expect!["b3ad0f4b734c5bd2ab9e83be8ee0cbaaa120e5cd0270b51cb9d7778a33f0b801"], + &expect!["8dea874c5cf2acffda1eba370193880f02e6bc0d95e3d1c2cce540060b606302"], ); test_pp_digest_with::, EE<_>>( &TrivialCircuit::default(), &TrivialCircuit::default(), - &expect!["e1feca53664212ee750da857c726b2a09bb30b2964f22ea85a19b58c9eaf5701"], + &expect!["6ec0ef50b2fc4af3ec40a5f62de8dafe6c84acda41e1a29cac0e18233e1be602"], ); test_pp_digest_with::, EE<_>>( &CubicCircuit::default(), &TrivialCircuit::default(), - &expect!["4ad6b10b6fd24fecba49f08d35bc874a6da9c77735bc0bcf4b78b1914a97e602"], + &expect!["da34ff488965a4aebab886745189729cbb6d4c23417fa6763943a7ce57324f03"], ); } diff --git a/src/supernova/circuit.rs b/src/supernova/circuit.rs index 3e23c9c7b..1b8a31155 100644 --- a/src/supernova/circuit.rs +++ b/src/supernova/circuit.rs @@ -836,8 +836,8 @@ mod tests { ¶ms2, ro_consts1, ro_consts2, - &expect!["9840"], - &expect!["10388"], + &expect!["9844"], + &expect!["10392"], 1, ); // TODO: extend to num_augmented_circuits >= 2 @@ -855,8 +855,8 @@ mod tests { ¶ms2, ro_consts1, ro_consts2, - &expect!["10008"], - &expect!["10577"], + &expect!["10012"], + &expect!["10581"], 1, ); // TODO: extend to num_augmented_circuits >= 2 @@ -874,8 +874,8 @@ mod tests { ¶ms2, ro_consts1, ro_consts2, - &expect!["10287"], - &expect!["11000"], + &expect!["10291"], + &expect!["11004"], 1, ); // TODO: extend to num_augmented_circuits >= 2 diff --git a/src/supernova/test.rs b/src/supernova/test.rs index a909d3266..ce80efcc7 100644 --- a/src/supernova/test.rs +++ b/src/supernova/test.rs @@ -564,8 +564,8 @@ fn test_recursive_circuit() { ¶ms2, ro_consts1, ro_consts2, - &expect!["9840"], - &expect!["12021"], + &expect!["9844"], + &expect!["12025"], ); } @@ -606,7 +606,7 @@ fn test_supernova_pp_digest() { test_pp_digest_with::( &test_rom, - &expect!["698b3592bf271c0cc53245aee71ec3f8e0d16486b3efc73be290a0af27605b01"], + &expect!["ca24639b674ad70ed5ae5f2d2048e01990bc1974bf9ddf6240e3f885d55a8c03"], ); let rom = vec![ @@ -617,7 +617,7 @@ fn test_supernova_pp_digest() { test_pp_digest_with::( &test_rom_grumpkin, - &expect!["30418e576c11dd698054a6cc69d1b1e43ddf0f562abfb50b777147afad741a01"], + &expect!["6ca2ed5fd9b755e97559024eadfa5c2e48d8cb231c35bfaf9c377af6bebac203"], ); let rom = vec![ @@ -628,7 +628,7 @@ fn test_supernova_pp_digest() { test_pp_digest_with::( &test_rom_secp, - &expect!["c94ee4e2870e34d6d057aa66157f8315879ecf2692ab9d1e2567c5830bed1103"], + &expect!["5e86ba0fd6f343752ebb56bfaa4fe3d52dc5bd7e6fffb2fd130ec43ce4ab1301"], ); } From d1afdea41c0fea7c2b9766c99eafb0e271a6c295 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Tue, 27 Feb 2024 22:44:37 -0500 Subject: [PATCH 46/77] fix EmulatedCurveParam allocation --- src/cyclefold/gadgets.rs | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index 46d9a1a32..4de439af9 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -89,7 +89,7 @@ pub mod emulated { #[allow(unused)] pub fn alloc( mut cs: CS, - params: Option<&(G::Scalar, G::Scalar, G::Scalar)>, + params: &(G::Scalar, G::Scalar, G::Scalar), limb_width: usize, n_limbs: usize, ) -> Result @@ -98,33 +98,21 @@ pub mod emulated { { let A = BigNat::alloc_from_nat( cs.namespace(|| "allocate A"), - || { - Ok(f_to_nat( - ¶ms.ok_or(SynthesisError::AssignmentMissing)?.0, - )) - }, + || Ok(f_to_nat(¶ms.0)), limb_width, n_limbs, )?; let B = BigNat::alloc_from_nat( cs.namespace(|| "allocate B"), - || { - Ok(f_to_nat( - ¶ms.ok_or(SynthesisError::AssignmentMissing)?.1, - )) - }, + || Ok(f_to_nat(¶ms.1)), limb_width, n_limbs, )?; let m = BigNat::alloc_from_nat( cs.namespace(|| "allocate m"), - || { - Ok(f_to_nat( - ¶ms.ok_or(SynthesisError::AssignmentMissing)?.2, - )) - }, + || Ok(f_to_nat(¶ms.2)), limb_width, n_limbs, )?; From e47d8ae03a1bdd89f6c7e61fb313ebf92e651090 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Tue, 27 Feb 2024 22:47:05 -0500 Subject: [PATCH 47/77] fix nifs RO absorb count --- src/nifs.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/nifs.rs b/src/nifs.rs index 9522e07a8..0571409e0 100644 --- a/src/nifs.rs +++ b/src/nifs.rs @@ -53,8 +53,14 @@ impl NIFS { ), NovaError, > { + // Check `U1` and `U2` have the same arity + let io_arity = U1.X.len(); + if io_arity != U2.X.len() { + return Err(NovaError::InvalidInputLength); + } + // initialize a new RO - let mut ro = E::RO::new(ro_consts.clone(), NUM_FE_FOR_RO); + let mut ro = E::RO::new(ro_consts.clone(), 7 + io_arity); // append the digest of pp to the transcript ro.absorb(scalar_as_base::(*pp_digest)); From 3a3558c350aef1d78d4838fdf82e28ea16f707ed Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Tue, 27 Feb 2024 23:42:39 -0500 Subject: [PATCH 48/77] make `test_augmented_circuit_size` an `expect_test` --- src/cyclefold/nova_circuit.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index 435e7bd8a..e241abb74 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -626,9 +626,11 @@ mod test { traits::{circuit::TrivialCircuit, CurveCycleEquipped, Dual}, }; + use expect_test::{expect, Expect}; + use super::*; - fn test_augmented_circuit_size_with() + fn test_augmented_circuit_size_with(expected_cons: &Expect, expected_var: &Expect) where E: CurveCycleEquipped, { @@ -651,14 +653,14 @@ mod test { let num_constraints = cs.num_constraints(); let num_variables = cs.num_aux(); - assert_eq!(num_constraints, 0); - assert_eq!(num_variables, 0); + expected_cons.assert_eq(&num_constraints.to_string()); + expected_var.assert_eq(&num_variables.to_string()); } #[test] fn test_augmented_circuit_size() { - test_augmented_circuit_size_with::(); - test_augmented_circuit_size_with::(); - test_augmented_circuit_size_with::(); + test_augmented_circuit_size_with::(&expect!["86258"], &expect!["86079"]); + test_augmented_circuit_size_with::(&expect!["88094"], &expect!["87915"]); + test_augmented_circuit_size_with::(&expect!["86825"], &expect!["86646"]); } } From 8d2379588533e5bded308f32faa5f31df79add22 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Wed, 28 Feb 2024 08:11:53 -0500 Subject: [PATCH 49/77] fix: typo --- src/cyclefold/snark.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 3d529202c..b689fabe0 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -432,12 +432,12 @@ where let is_inputs_not_match = self.z0_primary != z0_primary; // check if the (relaxed) R1CS instances have two public outputs - let is_instance_has_two_outpus = self.r_U_primary.X.len() != 2; + let is_instance_has_two_outputs = self.r_U_primary.X.len() != 2; if is_num_steps_zero || is_num_steps_not_match || is_inputs_not_match - || is_instance_has_two_outpus + || is_instance_has_two_outputs { return Err(NovaError::ProofVerifyError); } From f630fafbcc03f1fd2244174360237a51d5de1c09 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Wed, 28 Feb 2024 08:12:34 -0500 Subject: [PATCH 50/77] fix: `RecursiveSNARK::new` sets i to 0 --- src/cyclefold/snark.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index b689fabe0..ffae6951a 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -248,7 +248,7 @@ where r_U_cyclefold, buffer_primary, buffer_cyclefold, - i: 1, + i: 0, zi_primary, }) } From 695ff0c15e661fafb290628bbdddd138cfa97cd5 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Wed, 28 Feb 2024 08:13:27 -0500 Subject: [PATCH 51/77] fix: Fix RO absorb count in verify --- src/cyclefold/snark.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index ffae6951a..5f684399e 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -8,7 +8,8 @@ use crate::{ solver::SatisfyingAssignment, }, constants::{ - BN_LIMB_WIDTH, BN_N_LIMBS, NUM_CHALLENGE_BITS, NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_BITS, + BN_LIMB_WIDTH, BN_N_LIMBS, NIO_CYCLE_FOLD, NUM_CHALLENGE_BITS, NUM_FE_IN_EMULATED_POINT, + NUM_HASH_BITS, }, cyclefold::circuit::CyclefoldCircuit, errors::NovaError, @@ -445,7 +446,7 @@ where let (hash_primary, hash_cyclefold) = { let mut hasher = as Engine>::RO::new( pp.ro_consts_primary.clone(), - NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * pp.F_arity_primary, + 2 + 2 * pp.F_arity_primary + 2 * NUM_FE_IN_EMULATED_POINT + 3, ); hasher.absorb(pp.digest()); hasher.absorb(E1::Scalar::from(num_steps as u64)); @@ -458,9 +459,12 @@ where absorb_relaxed_r1cs::>(&self.r_U_primary, &mut hasher); let hash_primary = hasher.squeeze(NUM_HASH_BITS); - let mut hasher = - as Engine>::RO::new(pp.ro_consts_cyclefold.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF); + let mut hasher = as Engine>::RO::new( + pp.ro_consts_cyclefold.clone(), + 1 + 1 + 3 + 3 + 1 + NIO_CYCLE_FOLD * BN_N_LIMBS, + ); hasher.absorb(pp.digest()); + hasher.absorb(E1::Scalar::from(num_steps as u64)); self.r_U_cyclefold.absorb_in_ro(&mut hasher); let hash_cyclefold = hasher.squeeze(NUM_HASH_BITS); From bd3076d97aca11ea985d15e5b0e4e572d61e842e Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Wed, 28 Feb 2024 08:14:10 -0500 Subject: [PATCH 52/77] fix: `RecursiveSNARK::new` should set r_*_primary to default values --- src/cyclefold/snark.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 5f684399e..ac9f5f432 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -214,9 +214,8 @@ where let (l_u_primary, l_w_primary) = cs_primary.r1cs_instance_and_witness(r1cs_primary, &pp.ck_primary)?; - let r_U_primary = - RelaxedR1CSInstance::from_r1cs_instance(&pp.ck_primary, r1cs_primary, l_u_primary.clone()); - let r_W_primary = RelaxedR1CSWitness::from_r1cs_witness(r1cs_primary, l_w_primary.clone()); + let r_U_primary = RelaxedR1CSInstance::default(&pp.ck_primary, &r1cs_primary); + let r_W_primary = RelaxedR1CSWitness::default(&r1cs_primary); let zi_primary = zi_primary .iter() From 2aebeb3665fca866a14f7abeb6285ca7d0a996df Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Fri, 1 Mar 2024 17:11:18 -0500 Subject: [PATCH 53/77] u <-> U in folding data Alloc --- src/cyclefold/gadgets.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index 4de439af9..4799c26db 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -23,13 +23,13 @@ impl AllocatedFoldingData { n_limbs: usize, ) -> Result { let U = AllocatedRelaxedR1CSInstance::alloc( - cs.namespace(|| "u"), + cs.namespace(|| "U"), inst.map(|x| &x.U), limb_width, n_limbs, )?; - let u = AllocatedR1CSInstance::alloc(cs.namespace(|| "U"), inst.map(|x| &x.u))?; + let u = AllocatedR1CSInstance::alloc(cs.namespace(|| "u"), inst.map(|x| &x.u))?; let T = AllocatedPoint::alloc(cs.namespace(|| "T"), inst.map(|x| x.T.to_coordinates()))?; T.check_on_curve(cs.namespace(|| "T on curve"))?; From bccabb66acc321f52961797537ee9be4f4ec2fdb Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Fri, 1 Mar 2024 17:12:26 -0500 Subject: [PATCH 54/77] fix: add trivialization for cyclefold checks in base case --- src/cyclefold/nova_circuit.rs | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index e241abb74..9972d513e 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -277,6 +277,7 @@ where E_new: emulated::AllocatedEmulPoint, W_new: emulated::AllocatedEmulPoint, arity: usize, + is_base_case: &AllocatedBit, ) -> Result< ( AllocatedRelaxedR1CSInstance, @@ -339,6 +340,7 @@ where &data_p.T, &E_new, u_E, + is_base_case, )?; let u_W = &data_c_2.u; @@ -350,6 +352,7 @@ where W_2, &W_new, u_W, + is_base_case, )?; let check_io = AllocatedBit::and( @@ -453,6 +456,7 @@ where E_new, W_new, arity, + &is_base_case, )?; let should_be_false = AllocatedBit::nor( @@ -546,6 +550,7 @@ pub fn emulated_point_check::Bas mut cs: CS, point: &emulated::AllocatedEmulPoint, io_limbs: &[AllocatedNum<::Base>], + always_equal: &AllocatedBit, ) -> Result<(), SynthesisError> { let x_limbs = point .x @@ -589,9 +594,9 @@ pub fn emulated_point_check::Bas .try_for_each(|(idx, (var, limb))| -> Result<(), SynthesisError> { cs.enforce( || format!("enforcing equality {idx}"), - |lc| lc, - |lc| lc, + |lc| lc + CS::one() - always_equal.get_variable(), |lc| lc + var.get_variable() - limb.get_variable(), + |lc| lc, ); Ok(()) @@ -607,13 +612,29 @@ pub fn cyclefold_invocation_check, C_res: &emulated::AllocatedEmulPoint, instance: &AllocatedR1CSInstance, + is_base_case: &AllocatedBit, ) -> Result<(), SynthesisError> { let (point_1_io, point_23_io) = instance.X.split_at(5); let (point_2_io, point_3_io_plus_scalar) = point_23_io.split_at(5); let point_3_io = point_3_io_plus_scalar.split_at(5).0; - emulated_point_check::(cs.namespace(|| "check point C_1"), C_1, point_1_io)?; - emulated_point_check::(cs.namespace(|| "check point C_2"), C_2, point_2_io)?; - emulated_point_check::(cs.namespace(|| "check point C_res"), C_res, point_3_io)?; + emulated_point_check::( + cs.namespace(|| "check point C_1"), + C_1, + point_1_io, + is_base_case, + )?; + emulated_point_check::( + cs.namespace(|| "check point C_2"), + C_2, + point_2_io, + is_base_case, + )?; + emulated_point_check::( + cs.namespace(|| "check point C_res"), + C_res, + point_3_io, + is_base_case, + )?; Ok(()) } From 8cdfea39f6e2091bfc62b9e0ce5c402a8f262407 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Fri, 1 Mar 2024 17:12:51 -0500 Subject: [PATCH 55/77] make prove_verify test more fun --- src/cyclefold/snark.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index ac9f5f432..5006bbbc9 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -562,7 +562,6 @@ mod test { } fn test_trivial_cyclefold_prove_verify_with() { - let num_steps = 1; let primary_circuit = SquareCircuit:: { _p: PhantomData }; let pp = PublicParams::::setup(&primary_circuit, &*default_ck_hint(), &*default_ck_hint()); @@ -575,7 +574,15 @@ mod test { res.unwrap(); - let res = recursive_snark.verify(&pp, num_steps, &z0); + let res = recursive_snark.verify(&pp, 1, &z0); + + res.unwrap(); + + let res = recursive_snark.prove_step(&pp, &primary_circuit); + + res.unwrap(); + + let res = recursive_snark.verify(&pp, 2, &z0); res.unwrap(); } From a2d1ed8bce2854bfce8b0ee512ea08b5965ff8ac Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Sat, 2 Mar 2024 08:05:39 -0500 Subject: [PATCH 56/77] fix: fix hashing order --- src/cyclefold/gadgets.rs | 6 +++--- src/cyclefold/snark.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index 4799c26db..460c1f487 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -458,12 +458,12 @@ pub mod emulated { where CS: ConstraintSystem<::Base>, { - self - .comm_E - .absorb_in_ro(cs.namespace(|| "absorb comm_E"), ro)?; self .comm_W .absorb_in_ro(cs.namespace(|| "absorb comm_W"), ro)?; + self + .comm_E + .absorb_in_ro(cs.namespace(|| "absorb comm_E"), ro)?; ro.absorb(&self.u); ro.absorb(&self.x0); diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 5006bbbc9..08b26d415 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -521,10 +521,10 @@ where { absorb_commitment::(&U.comm_W, ro); absorb_commitment::(&U.comm_E, ro); + ro.absorb(U.u); for e in &U.X { ro.absorb(*e); } - ro.absorb(U.u); } #[cfg(test)] From 7445a599abac040fdc5bf79ffb9f176fc4f3d178 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Sun, 3 Mar 2024 17:37:12 -0500 Subject: [PATCH 57/77] fix: r_bools was always None --- src/cyclefold/snark.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 08b26d415..9727081e9 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -291,14 +291,13 @@ where // &mut self.buffer_primary.ABC_Z_2, // )?; - let r_bools: Option<[bool; NUM_CHALLENGE_BITS]> = r + let r_bools = r .to_le_bits() .iter() .map(|b| Some(*b)) + .take(NUM_CHALLENGE_BITS) .collect::>>() - .unwrap() - .try_into() - .ok(); + .map(|v| v.try_into().unwrap()); let comm_T = Commitment::::decompress(&nifs_primary.comm_T)?; let E_new = self.r_U_primary.comm_E + comm_T * r; From 4fab7cb2aace7bfb08de26cbb0b071622a3a801c Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Sun, 3 Mar 2024 17:39:31 -0500 Subject: [PATCH 58/77] make prove_verify test longer --- src/cyclefold/snark.rs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 9727081e9..414ecc170 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -569,21 +569,13 @@ mod test { let mut recursive_snark = RecursiveSNARK::new(&pp, &primary_circuit, &z0).unwrap(); - let res = recursive_snark.prove_step(&pp, &primary_circuit); + (1..5).for_each(|iter| { + let res_proof = recursive_snark.prove_step(&pp, &primary_circuit); + res_proof.unwrap(); - res.unwrap(); - - let res = recursive_snark.verify(&pp, 1, &z0); - - res.unwrap(); - - let res = recursive_snark.prove_step(&pp, &primary_circuit); - - res.unwrap(); - - let res = recursive_snark.verify(&pp, 2, &z0); - - res.unwrap(); + let res_verify = recursive_snark.verify(&pp, iter, &z0); + res_verify.unwrap(); + }); } #[test] From 051bab822d37c74b5c447d317ed307ce096276d1 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Sun, 3 Mar 2024 17:39:39 -0500 Subject: [PATCH 59/77] clippy --- src/cyclefold/snark.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 414ecc170..6b1a4729f 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -214,8 +214,8 @@ where let (l_u_primary, l_w_primary) = cs_primary.r1cs_instance_and_witness(r1cs_primary, &pp.ck_primary)?; - let r_U_primary = RelaxedR1CSInstance::default(&pp.ck_primary, &r1cs_primary); - let r_W_primary = RelaxedR1CSWitness::default(&r1cs_primary); + let r_U_primary = RelaxedR1CSInstance::default(&pp.ck_primary, r1cs_primary); + let r_W_primary = RelaxedR1CSWitness::default(r1cs_primary); let zi_primary = zi_primary .iter() From b4dc789a5cd5f94e209f9377e71bd4d535c82ff9 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Sun, 3 Mar 2024 17:40:14 -0500 Subject: [PATCH 60/77] test_augmented_circuit_size should check synthesis works --- src/cyclefold/nova_circuit.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index 9972d513e..402bd56f6 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -669,7 +669,9 @@ mod test { ); let mut cs: TestShapeCS> = TestShapeCS::default(); - let _ = circuit.synthesize(&mut cs); + let res = circuit.synthesize(&mut cs); + + res.unwrap(); let num_constraints = cs.num_constraints(); let num_variables = cs.num_aux(); From 40abbec23dbcea928d861f6450cc643037f46646 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Sun, 3 Mar 2024 17:56:21 -0500 Subject: [PATCH 61/77] chore: fix rebase --- src/cyclefold/circuit.rs | 5 +---- src/cyclefold/gadgets.rs | 12 +++--------- src/cyclefold/nifs.rs | 5 +---- src/cyclefold/nova_circuit.rs | 6 ++---- src/cyclefold/snark.rs | 2 +- src/gadgets/mod.rs | 2 +- 6 files changed, 9 insertions(+), 23 deletions(-) diff --git a/src/cyclefold/circuit.rs b/src/cyclefold/circuit.rs index bcf0ba7ad..71caa4c0c 100644 --- a/src/cyclefold/circuit.rs +++ b/src/cyclefold/circuit.rs @@ -6,10 +6,7 @@ use ff::PrimeField; use crate::{ constants::NUM_CHALLENGE_BITS, - gadgets::{ - ecc::AllocatedPoint, - utils::{alloc_constant, le_bits_to_num}, - }, + gadgets::{alloc_constant, le_bits_to_num, AllocatedPoint}, traits::{commitment::CommitmentTrait, Engine}, Commitment, }; diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index 460c1f487..8e15e05bf 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -1,10 +1,7 @@ use super::nova_circuit::FoldingData; use crate::{ - gadgets::{ - ecc::AllocatedPoint, - r1cs::{AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance}, - }, + gadgets::{AllocatedPoint, AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance}, traits::{commitment::CommitmentTrait, Engine, ROCircuitTrait}, }; @@ -64,11 +61,8 @@ pub mod emulated { use crate::{ constants::{NUM_CHALLENGE_BITS, NUM_FE_IN_EMULATED_POINT}, gadgets::{ - nonnative::{bignat::BigNat, util::f_to_nat}, - utils::{ - alloc_zero, conditionally_select, conditionally_select_allocated_bit, - conditionally_select_bignat, le_bits_to_num, - }, + alloc_zero, conditionally_select, conditionally_select_allocated_bit, + conditionally_select_bignat, f_to_nat, le_bits_to_num, BigNat, }, traits::{Group, ROConstantsCircuit}, RelaxedR1CSInstance, diff --git a/src/cyclefold/nifs.rs b/src/cyclefold/nifs.rs index 5f17d6245..6a4627576 100644 --- a/src/cyclefold/nifs.rs +++ b/src/cyclefold/nifs.rs @@ -7,10 +7,7 @@ use ff::Field; use crate::{ constants::{BN_LIMB_WIDTH, BN_N_LIMBS, NUM_CHALLENGE_BITS, NUM_FE_IN_EMULATED_POINT}, errors::NovaError, - gadgets::{ - nonnative::{bignat::nat_to_limbs, util::f_to_nat}, - utils::scalar_as_base, - }, + gadgets::{f_to_nat, nat_to_limbs, scalar_as_base}, r1cs::{R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness}, traits::{commitment::CommitmentTrait, /*AbsorbInROTrait,*/ Engine, ROConstants, ROTrait}, CommitmentKey, CompressedCommitment, diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index 402bd56f6..f7826aaa1 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -6,10 +6,8 @@ use crate::{ /*NUM_FE_WITHOUT_IO_FOR_CRHF,*/ NUM_HASH_BITS, }, gadgets::{ - r1cs::{AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance}, - utils::{ - alloc_num_equals, alloc_scalar_as_base, alloc_zero, conditionally_select_vec, le_bits_to_num, - }, + alloc_num_equals, alloc_scalar_as_base, alloc_zero, conditionally_select_vec, le_bits_to_num, + AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance, }, r1cs::{R1CSInstance, RelaxedR1CSInstance}, traits::{ diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 6b1a4729f..132b8cfec 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -13,7 +13,7 @@ use crate::{ }, cyclefold::circuit::CyclefoldCircuit, errors::NovaError, - gadgets::utils::scalar_as_base, + gadgets::scalar_as_base, nifs::NIFS, r1cs::{ self, CommitmentKeyHint, R1CSInstance, R1CSResult, R1CSWitness, RelaxedR1CSInstance, diff --git a/src/gadgets/mod.rs b/src/gadgets/mod.rs index 7022edcf2..1d3af3288 100644 --- a/src/gadgets/mod.rs +++ b/src/gadgets/mod.rs @@ -3,7 +3,7 @@ mod ecc; pub(crate) use ecc::AllocatedPoint; mod nonnative; -pub(crate) use nonnative::{bignat::nat_to_limbs, util::f_to_nat}; +pub(crate) use nonnative::{bignat::nat_to_limbs, bignat::BigNat, util::f_to_nat}; mod r1cs; pub(crate) use r1cs::{ From 07d92c90142be72a23c6d6f3646d9a7c1de4b1dc Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Sun, 3 Mar 2024 18:17:16 -0500 Subject: [PATCH 62/77] chore: bump msrv --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ef6bf8523..d86b2b4f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ readme = "README.md" repository = "https://github.com/lurk-lab/arecibo" license-file = "LICENSE" keywords = ["zkSNARKs", "cryptography", "proofs"] -rust-version="1.71.0" +rust-version="1.74.1" [dependencies] bellpepper-core = { version = "0.4.0", default-features = false } From 90cc20528687014983c90ecb89336d8a51833871 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Tue, 5 Mar 2024 19:36:00 -0500 Subject: [PATCH 63/77] chore: cleanup, documentation, and split cyclefold test --- src/cyclefold/circuit.rs | 104 ++++++++++++++++---------------- src/cyclefold/gadgets.rs | 27 --------- src/cyclefold/mod.rs | 2 +- src/cyclefold/nifs.rs | 9 +-- src/cyclefold/nova_circuit.rs | 8 +-- src/cyclefold/snark.rs | 44 +++++--------- src/gadgets/nonnative/bignat.rs | 12 ---- src/supernova/mod.rs | 1 - 8 files changed, 73 insertions(+), 134 deletions(-) diff --git a/src/cyclefold/circuit.rs b/src/cyclefold/circuit.rs index 71caa4c0c..9412dfe7c 100644 --- a/src/cyclefold/circuit.rs +++ b/src/cyclefold/circuit.rs @@ -11,7 +11,7 @@ use crate::{ Commitment, }; -/// TODO: docs +/// A structure containing the CycleFold circuit inputs and implementing the synthesize function pub struct CyclefoldCircuit { commit_1: Option>, commit_2: Option>, @@ -28,7 +28,7 @@ impl Default for CyclefoldCircuit { } } impl CyclefoldCircuit { - /// TODO: docs + /// Create a new CycleFold circuit with the given inputs pub fn new( commit_1: Option>, commit_2: Option>, @@ -77,7 +77,7 @@ impl CyclefoldCircuit { Ok((commit_1, commit_2, scalar)) } - /// TODO: docs + /// Synthesize the CycleFold circuit pub fn synthesize::Base>>( &self, mut cs: CS, @@ -199,51 +199,55 @@ mod tests { use super::*; - fn test_split_field_elt_with() { - let rng = OsRng; - let rando = ::random(rng); + fn test_cyclefold_circuit_size_with(expected_constraints: &Expect, expected_vars: &Expect) + where + E1: CurveCycleEquipped, + { + // Instantiate the circuit with trivial inputs + let circuit: CyclefoldCircuit> = CyclefoldCircuit::new(None, None, None); - let mut cs: SatisfyingAssignment = SatisfyingAssignment::::new(); + // Synthesize the R1CS shape + let mut cs: ShapeCS = ShapeCS::new(); + let _ = circuit.synthesize(cs.namespace(|| "synthesizing shape")); - let rando_num = AllocatedNum::alloc(cs.namespace(|| "alloc num"), || Ok(rando)).unwrap(); + // Extract the number of constraints and variables + let num_constraints = cs.num_constraints(); + let num_variables = cs.num_aux(); + let num_io = cs.num_inputs(); - split_field_element(&rando_num, cs.namespace(|| "split num")).unwrap(); + // Check the number of constraints and variables match the expected values + expected_constraints.assert_eq(&num_constraints.to_string()); + expected_vars.assert_eq(&num_variables.to_string()); + assert_eq!(num_io, NIO_CYCLE_FOLD + 1); // includes 1 } #[test] - fn test_split_field_elt() { - test_split_field_elt_with::() + fn test_cyclefold_circuit_size() { + test_cyclefold_circuit_size_with::(&expect!("1394"), &expect!("1382")); + test_cyclefold_circuit_size_with::(&expect!("1394"), &expect!("1382")); + test_cyclefold_circuit_size_with::(&expect!("1394"), &expect!("1382")); } - // TODO: Split this test up into multiple tests - fn test_cyclefold_circuit_size_with(expected_constraints: &Expect, expected_vars: &Expect) - where - E1: CurveCycleEquipped, - { + fn test_cyclefold_circuit_sat_with() { let rng = OsRng; - let ck = < as Engine>::CE as CommitmentEngineTrait>>::setup(b"test", 5); + let ck = < as Engine>::CE as CommitmentEngineTrait>>::setup(b"test", 5); + // Generate random vectors to commit to let v1 = (0..5) - .map(|_| < as Engine>::Scalar as Field>::random(rng)) + .map(|_| < as Engine>::Scalar as Field>::random(rng)) .collect::>(); - let v2 = (0..5) - .map(|_| < as Engine>::Scalar as Field>::random(rng)) + .map(|_| < as Engine>::Scalar as Field>::random(rng)) .collect::>(); - let C_1 = < as Engine>::CE as CommitmentEngineTrait>>::commit(&ck, &v1); - - let C_2 = < as Engine>::CE as CommitmentEngineTrait>>::commit(&ck, &v2); + // Calculate the random commitments + let C_1 = < as Engine>::CE as CommitmentEngineTrait>>::commit(&ck, &v1); + let C_2 = < as Engine>::CE as CommitmentEngineTrait>>::commit(&ck, &v2); + // Generate a random scalar let val: u128 = rand::random(); - - let r = < as Engine>::Scalar as PrimeField>::from_u128(val); - - let native_result = C_1 + C_2 * r; - - let (res_X, res_Y, res_is_infinity) = native_result.to_coordinates(); - + let r = < as Engine>::Scalar as PrimeField>::from_u128(val); let r_bits = r .to_le_bits() .into_iter() @@ -254,23 +258,19 @@ mod tests { vec.try_into().unwrap() }); - let circuit: CyclefoldCircuit> = CyclefoldCircuit::new(Some(C_1), Some(C_2), r_bits); - - let mut cs: ShapeCS = ShapeCS::new(); - let _ = circuit.synthesize(cs.namespace(|| "synthesizing shape")); - - let num_constraints = cs.num_constraints(); - let num_variables = cs.num_aux(); - let num_io = cs.num_inputs(); + // Calculate the result out of circuit + let native_result = C_1 + C_2 * r; + let (res_X, res_Y, res_is_infinity) = native_result.to_coordinates(); - expected_constraints.assert_eq(&num_constraints.to_string()); - expected_vars.assert_eq(&num_variables.to_string()); - assert_eq!(num_io, NIO_CYCLE_FOLD + 1); // includes 1 + let circuit: CyclefoldCircuit> = CyclefoldCircuit::new(Some(C_1), Some(C_2), r_bits); + // Generate the R1CS shape and commitment key + let mut cs: ShapeCS = ShapeCS::new(); + let _ = circuit.synthesize(cs.namespace(|| "synthesizing shape")); let (shape, ck) = cs.r1cs_shape_and_key(&*default_ck_hint()); - let mut cs = SatisfyingAssignment::::new(); - + // Synthesize the R1CS circuit on the random inputs + let mut cs = SatisfyingAssignment::::new(); circuit .synthesize(cs.namespace(|| "synthesizing witness")) .unwrap(); @@ -279,12 +279,14 @@ mod tests { let X = &instance.X; - let recombine_scalar = |lower: E1::Scalar, upper: E1::Scalar| -> E1::Scalar { + // Recombine the scalar from the two 128 bit limbs in the public IO of the cyclefold circuit + let recombine_scalar = |lower: E::Scalar, upper: E::Scalar| -> E::Scalar { let mut upper = upper; (0..128).for_each(|_| upper = upper.double()); lower + upper }; + // Parse the public IO as the circuit values let circuit_res_X_lower = X[10]; let circuit_res_X_upper = X[11]; let circuit_res_X = recombine_scalar(circuit_res_X_lower, circuit_res_X_upper); @@ -295,22 +297,22 @@ mod tests { let circuit_res_is_infinity = X[14]; + // Check the out of circuit result matches the circuit result assert_eq!(res_X, circuit_res_X); assert_eq!(res_Y, circuit_res_Y); assert_eq!( res_is_infinity, - circuit_res_is_infinity == as Engine>::Base::ONE + circuit_res_is_infinity == as Engine>::Base::ONE ); + // Check the R1CS equation is satisfied shape.is_sat(&ck, &instance, &witness).unwrap(); } #[test] - fn test_cyclefold_circuit_size() { - test_cyclefold_circuit_size_with::(&expect!("1394"), &expect!("1382")); - test_cyclefold_circuit_size_with::(&expect!("1394"), &expect!("1382")); - test_cyclefold_circuit_size_with::(&expect!("1394"), &expect!("1382")); + fn test_cyclefold_circuit_sat() { + test_cyclefold_circuit_sat_with::(); + test_cyclefold_circuit_sat_with::(); + test_cyclefold_circuit_sat_with::(); } - - // TODO: add test for circuit satisfiability } diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index 8e15e05bf..c14561a38 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -33,20 +33,6 @@ impl AllocatedFoldingData { Ok(Self { U, u, T }) } - - // TODO: Delete if not needed, or uncomment if this saves time - // pub fn absorb_in_ro::Base>>( - // &self, - // mut cs: CS, - // ro: &mut E::ROCircuit, - // ) -> Result<(), SynthesisError> { - // self.U.absorb_in_ro(cs.namespace(|| "absorb U"), ro)?; - // self.u.absorb_in_ro(ro); - // ro.absorb(&self.T.x); - // ro.absorb(&self.T.y); - // ro.absorb(&self.T.is_infinity); - // Ok(()) - // } } pub mod emulated { @@ -592,18 +578,5 @@ pub mod emulated { T, }) } - - // TODO: Delete if not needed - // pub fn absorb_in_ro(&self, mut cs: CS, ro: &mut E1::ROCircuit) -> Result<(), SynthesisError> - // where - // CS: ConstraintSystem<::Base>, - // { - // self.U.absorb_in_ro(cs.namespace(|| "absorb U"), ro)?; - // self.u_W.absorb_in_ro(cs.namespace(|| "absorb u_W"), ro)?; - // ro.absorb(&self.u_x0); - // ro.absorb(&self.u_x1); - // self.T.absorb_in_ro(cs.namespace(|| "absorb T"), ro)?; - // Ok(()) - // } } } diff --git a/src/cyclefold/mod.rs b/src/cyclefold/mod.rs index 1352e1983..004afad5b 100644 --- a/src/cyclefold/mod.rs +++ b/src/cyclefold/mod.rs @@ -1,4 +1,4 @@ -//! This module defines Cyclefold stuff +//! This module defines Cyclefold folding scheme and its related functions. mod circuit; mod gadgets; diff --git a/src/cyclefold/nifs.rs b/src/cyclefold/nifs.rs index 6a4627576..ac42527bf 100644 --- a/src/cyclefold/nifs.rs +++ b/src/cyclefold/nifs.rs @@ -9,11 +9,11 @@ use crate::{ errors::NovaError, gadgets::{f_to_nat, nat_to_limbs, scalar_as_base}, r1cs::{R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness}, - traits::{commitment::CommitmentTrait, /*AbsorbInROTrait,*/ Engine, ROConstants, ROTrait}, + traits::{commitment::CommitmentTrait, Engine, ROConstants, ROTrait}, CommitmentKey, CompressedCommitment, }; -/// TODO: Docs +/// Absorb a commitment over engine `E1` into an RO over engine `E2` by absorbing the limbs pub fn absorb_commitment( comm: &impl CommitmentTrait, ro: &mut impl ROTrait, @@ -121,8 +121,3 @@ where )) } } - -#[cfg(test)] -mod test { - // use super::*; -} diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index f7826aaa1..d3aac2f13 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -1,10 +1,7 @@ //! This module defines the Nova augmented circuit used for Cyclefold use crate::{ - constants::{ - BN_N_LIMBS, NIO_CYCLE_FOLD, NUM_FE_IN_EMULATED_POINT, - /*NUM_FE_WITHOUT_IO_FOR_CRHF,*/ NUM_HASH_BITS, - }, + constants::{BN_N_LIMBS, NIO_CYCLE_FOLD, NUM_FE_IN_EMULATED_POINT, NUM_HASH_BITS}, gadgets::{ alloc_num_equals, alloc_scalar_as_base, alloc_zero, conditionally_select_vec, le_bits_to_num, AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance, @@ -54,7 +51,6 @@ impl FoldingData { } } -// TODO: This needs to take in the initial cyclefold relaxed R1CS instance as well #[derive(Debug, Serialize, Deserialize)] #[serde(bound = "")] pub struct AugmentedCircuitInputs @@ -431,7 +427,7 @@ where self, cs: &mut CS, ) -> Result>, SynthesisError> { - // TODO: It's written down here https://hackmd.io/SBvAur_2RQmaduDi7gYbhw + // Circuit is documented here: https://hackmd.io/SBvAur_2RQmaduDi7gYbhw let arity = self.step_circuit.arity(); let (pp_digest, i, z_0, z_i, data_p, data_c_1, data_c_2, E_new, W_new) = diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 132b8cfec..10332493b 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -39,7 +39,7 @@ use ff::{PrimeField, PrimeFieldBits}; use once_cell::sync::OnceCell; use serde::{Deserialize, Serialize}; -/// TODO: docs +/// The public parameters used in the CycleFold recursive SNARK proof and verification #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Abomonation)] #[serde(bound = "")] #[abomonation_bounds( @@ -71,10 +71,13 @@ impl PublicParams where E1: CurveCycleEquipped, { - /// TODO: docs + /// Builds the public parameters for the circuit `C1`. + /// The same note for public parameter hints apply as in the case for Nova's public parameters: + /// For some final compressing SNARKs the size of the commitment key must be larger, so we include + /// `ck_hint_primary` and `ck_hint_cyclefold` parameters to accommodate this. pub fn setup>( c_primary: &C1, - ck_hint1: &CommitmentKeyHint, + ck_hint_primary: &CommitmentKeyHint, ck_hint_cyclefold: &CommitmentKeyHint>, ) -> Self { let F_arity_primary = c_primary.arity(); @@ -90,7 +93,7 @@ where ); let mut cs: ShapeCS = ShapeCS::new(); let _ = circuit_primary.synthesize(&mut cs); - let (r1cs_shape_primary, ck_primary) = cs.r1cs_shape_and_key(ck_hint1); + let (r1cs_shape_primary, ck_primary) = cs.r1cs_shape_and_key(ck_hint_primary); let circuit_shape_primary = R1CSWithArity::new(r1cs_shape_primary, F_arity_primary); let ro_consts_cyclefold = ROConstants::>::default(); @@ -114,7 +117,7 @@ where } } - /// TODO: docs + /// Calculate the digest of the public parameters. pub fn digest(&self) -> E1::Scalar { self .digest @@ -123,7 +126,7 @@ where .expect("Failure in retrieving digest") } - /// TODO: docs + /// Returns the number of constraints in the primary and cyclefold circuits pub const fn num_constraints(&self) -> (usize, usize) { ( self.circuit_shape_primary.r1cs_shape.num_cons, @@ -131,7 +134,7 @@ where ) } - /// TODO: docs + /// Returns the number of variables in the primary and cyclefold circuits pub const fn num_variables(&self) -> (usize, usize) { ( self.circuit_shape_primary.r1cs_shape.num_vars, @@ -142,7 +145,8 @@ where impl SimpleDigestible for PublicParams where E1: CurveCycleEquipped {} -/// TODO: docs +/// A SNARK that proves the correct execution of an incremental computation in the CycleFold folding +/// scheme. #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(bound = "")] pub struct RecursiveSNARK @@ -174,7 +178,7 @@ impl RecursiveSNARK where E1: CurveCycleEquipped, { - /// TODO: docs + /// Create a new instance of a recursive SNARK pub fn new>( pp: &PublicParams, c_primary: &C1, @@ -253,7 +257,7 @@ where }) } - /// TODO: docs + /// Update the `RecursiveSNARK` by proving a step of the incremental computation. pub fn prove_step>( &mut self, pp: &PublicParams, @@ -275,22 +279,6 @@ where &self.l_w_primary, )?; - // TODO: Delete this - // NOTE: This replaces the following code: - // let (nifs_primary, r) = NIFS::prove_mut( - // &pp.ck_primary, - // &pp.ro_consts_primary, - // &pp.digest(), - // &pp.circuit_shape_primary.r1cs_shape, - // &mut self.r_U_primary, - // &mut self.r_W_primary, - // &self.l_u_primary, - // &self.l_w_primary, - // &mut self.buffer_primary.T, - // &mut self.buffer_primary.ABC_Z_1, - // &mut self.buffer_primary.ABC_Z_2, - // )?; - let r_bools = r .to_le_bits() .iter() @@ -414,7 +402,7 @@ where Ok(()) } - /// TODO: docs + /// Verify the correctness of the `RecursiveSNARK` pub fn verify( &self, pp: &PublicParams, @@ -469,8 +457,6 @@ where (hash_primary, hash_cyclefold) }; - // TODO: This seems like it might be a bad sign, I don't know if I should need to use - // `scalar_as_base` here if scalar_as_base::>(hash_primary) != self.l_u_primary.X[0] || scalar_as_base::>(hash_cyclefold) != self.l_u_primary.X[1] { diff --git a/src/gadgets/nonnative/bignat.rs b/src/gadgets/nonnative/bignat.rs index 3935a7770..e4e48a048 100644 --- a/src/gadgets/nonnative/bignat.rs +++ b/src/gadgets/nonnative/bignat.rs @@ -260,18 +260,6 @@ impl BigNat { limbs } - // TODO: This probably can be deleted - // // NOTE: This is quick and dirty and is bad - // /// Transforms a BigNat into an AllocatedNum (makes no check on whether it's bigger than the field) - // pub fn as_allocated_num>( - // &self, - // mut cs: CS, - // ) -> Result, SynthesisError> { - // let bits = self.decompose(cs.namespace(|| "decompse as bits"))?; - // let num = Num::from_bits(cs.namespace(|| "num from bits"), &bits)?; - // num.as_allocated_num(cs.namespace(|| "as_allocated_num")) - // } - pub fn assert_well_formed>( &self, mut cs: CS, diff --git a/src/supernova/mod.rs b/src/supernova/mod.rs index b47bd121c..a1f1fd31a 100644 --- a/src/supernova/mod.rs +++ b/src/supernova/mod.rs @@ -1112,7 +1112,6 @@ where } } -/// TODO: docs /// SuperNova helper trait, for implementors that provide sets of sub-circuits to be proved via NIVC. `C1` must be a /// type (likely an `Enum`) for which a potentially-distinct instance can be supplied for each `index` below /// `self.num_circuits()`. From 13d146f10abc6262cf657f34f2787d37364cd1a2 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Thu, 7 Mar 2024 00:04:56 -0500 Subject: [PATCH 64/77] fix: Fix CycleFold circuit IO to only have arity 4 Also fix CycleFold folding soundness issue by absorbing running instance --- src/constants.rs | 2 +- src/cyclefold/circuit.rs | 203 +++++++++++++++------------------- src/cyclefold/gadgets.rs | 200 +++++++++++++++++++++++++++++++-- src/cyclefold/nifs.rs | 77 ++++++++++++- src/cyclefold/nova_circuit.rs | 161 ++++----------------------- src/cyclefold/snark.rs | 9 +- src/gadgets/mod.rs | 2 +- src/gadgets/utils.rs | 30 ++--- 8 files changed, 389 insertions(+), 295 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index 9b164ed01..7ed79d2f0 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -7,7 +7,7 @@ pub(crate) const NUM_FE_WITHOUT_IO_FOR_CRHF: usize = 9 + NIO_NOVA_FOLD * BN_N_LI pub(crate) const NUM_FE_FOR_RO: usize = 9; pub(crate) const NIO_NOVA_FOLD: usize = 2; pub(crate) const NUM_FE_IN_EMULATED_POINT: usize = 2 * BN_N_LIMBS + 1; -pub(crate) const NIO_CYCLE_FOLD: usize = 16; // 5 per point (15) + 1 scalar +pub(crate) const NIO_CYCLE_FOLD: usize = 4; // 1 per point (3) + scalar /// Bit size of Nova field element hashes pub const NUM_HASH_BITS: usize = 250; diff --git a/src/cyclefold/circuit.rs b/src/cyclefold/circuit.rs index 9412dfe7c..1987e0b0f 100644 --- a/src/cyclefold/circuit.rs +++ b/src/cyclefold/circuit.rs @@ -1,12 +1,15 @@ //! This module defines Cyclefold stuff -use bellpepper::gadgets::Assignment; -use bellpepper_core::{boolean::AllocatedBit, num::AllocatedNum, ConstraintSystem, SynthesisError}; -use ff::PrimeField; +use bellpepper_core::{ + boolean::{AllocatedBit, Boolean}, + ConstraintSystem, SynthesisError, +}; +use ff::Field; +use neptune::{circuit2::poseidon_hash_allocated, poseidon::PoseidonConstants}; use crate::{ constants::NUM_CHALLENGE_BITS, - gadgets::{alloc_constant, le_bits_to_num, AllocatedPoint}, + gadgets::{alloc_zero, conditionally_select, le_bits_to_num, AllocatedPoint}, traits::{commitment::CommitmentTrait, Engine}, Commitment, }; @@ -16,14 +19,17 @@ pub struct CyclefoldCircuit { commit_1: Option>, commit_2: Option>, scalar: Option<[bool; NUM_CHALLENGE_BITS]>, + poseidon_constants: PoseidonConstants, } impl Default for CyclefoldCircuit { fn default() -> Self { + let poseidon_constants = PoseidonConstants::new(); Self { commit_1: None, commit_2: None, scalar: None, + poseidon_constants, } } } @@ -34,10 +40,12 @@ impl CyclefoldCircuit { commit_2: Option>, scalar: Option<[bool; NUM_CHALLENGE_BITS]>, ) -> Self { + let poseidon_constants = PoseidonConstants::new(); Self { commit_1, commit_2, scalar, + poseidon_constants, } } @@ -46,9 +54,9 @@ impl CyclefoldCircuit { mut cs: CS, ) -> Result< ( - AllocatedPoint, - AllocatedPoint, - Vec, + AllocatedPoint, // commit_1 + AllocatedPoint, // commit_2 + Vec, // scalar ), SynthesisError, > { @@ -89,9 +97,9 @@ impl CyclefoldCircuit { let C_final = C_1.add(cs.namespace(|| "C_1 + r * C_2"), &r_C_2)?; - inputize_point::(&C_1, cs.namespace(|| "inputize C_1"))?; - inputize_point::(&C_2, cs.namespace(|| "inputize C_2"))?; - inputize_point::(&C_final, cs.namespace(|| "inputize C_final"))?; + self.inputize_point(C_1, cs.namespace(|| "inputize C_1"))?; + self.inputize_point(C_2, cs.namespace(|| "inputize C_2"))?; + self.inputize_point(C_final, cs.namespace(|| "inputize C_final"))?; let scalar = le_bits_to_num(cs.namespace(|| "get scalar"), &r)?; @@ -99,91 +107,64 @@ impl CyclefoldCircuit { Ok(()) } -} -fn inputize_point(point: &AllocatedPoint, mut cs: CS) -> Result<(), SynthesisError> -where - E: Engine, - CS: ConstraintSystem, -{ - let (lower_x, upper_x) = split_field_element(&point.x, cs.namespace(|| "split x"))?; - let (lower_y, upper_y) = split_field_element(&point.y, cs.namespace(|| "split y"))?; - lower_x.inputize(cs.namespace(|| "inputize lower_x"))?; - upper_x.inputize(cs.namespace(|| "inputize upper_x"))?; - lower_y.inputize(cs.namespace(|| "inputize lower_y"))?; - upper_y.inputize(cs.namespace(|| "inputize upper_y"))?; - point - .is_infinity - .inputize(cs.namespace(|| "inputize is_infinity"))?; - Ok(()) -} + // Represent the point in the public IO as its 2-ary Poseidon hash + fn inputize_point( + &self, + point: AllocatedPoint, + mut cs: CS, + ) -> Result<(), SynthesisError> + where + E: Engine, + CS: ConstraintSystem, + { + let (x, y, is_infinity) = point.get_coordinates(); + let preimage = vec![x.clone(), y.clone()]; + let val = poseidon_hash_allocated( + cs.namespace(|| "hash point"), + preimage, + &self.poseidon_constants, + )?; + + let zero = alloc_zero(cs.namespace(|| "zero")); + + let is_infinity_bit = AllocatedBit::alloc( + cs.namespace(|| "is_infinity"), + Some( + if is_infinity.get_value().unwrap_or(E::Base::ONE) == E::Base::ONE { + true + } else { + false + }, + ), + )?; + + cs.enforce( + || "infinity_bit matches", + |lc| lc, + |lc| lc, + |lc| lc + is_infinity_bit.get_variable() - is_infinity.get_variable(), + ); + + // Output 0 when it is the point at infinity + let output = conditionally_select( + cs.namespace(|| "select output"), + &zero, + &val, + &Boolean::from(is_infinity_bit), + )?; + + output.inputize(cs.namespace(|| "inputize hash"))?; -// TODO: Clean this up -fn split_field_element( - num: &AllocatedNum, - mut cs: CS, -) -> Result<(AllocatedNum, AllocatedNum), SynthesisError> -where - Scalar: PrimeField, - CS: ConstraintSystem, -{ - let lower_allocated_num = AllocatedNum::alloc(cs.namespace(|| "alloc lower"), || { - let repr = num.get_value().get()?.to_repr(); - let bytes = repr.as_ref(); - let (lower, _) = bytes.split_at(16); - Ok(Scalar::from_u128(u128::from_le_bytes( - (*lower).try_into().unwrap(), - ))) - })?; - let upper_allocated_num = AllocatedNum::alloc(cs.namespace(|| "alloc upper"), || { - let repr = num.get_value().get()?.to_repr(); - let bytes = repr.as_ref(); - let (_, upper) = bytes.split_at(16); - Ok(Scalar::from_u128(u128::from_le_bytes( - (*upper).try_into().unwrap(), - ))) - })?; - - let shift = alloc_constant( - Scalar::from_u128(u128::MAX) + Scalar::ONE, - cs.namespace(|| "alloc shift"), - ); - - let repr = num.get_value().get().map(|v| v.to_repr()); - - let shifted_upper_num = repr.map(|v| { - (0..128).fold( - Scalar::from_u128(u128::from_le_bytes( - (*v.as_ref().split_at(16).1).try_into().unwrap(), - )), - |acc, _| acc.double(), - ) - }); - - let shifted_upper_allocated_num = - AllocatedNum::alloc(cs.namespace(|| "alloc shifted_upper"), || shifted_upper_num)?; - - cs.enforce( - || "enforce shifted_upper is valid", - |lc| lc + upper_allocated_num.get_variable(), - |lc| lc + shift.get_variable(), - |lc| lc + shifted_upper_allocated_num.get_variable(), - ); - - cs.enforce( - || "enforce split", - |lc| lc + CS::one(), - |lc| lc + num.get_variable(), - |lc| lc + lower_allocated_num.get_variable() + shifted_upper_allocated_num.get_variable(), - ); - - Ok((lower_allocated_num, upper_allocated_num)) + Ok(()) + } } #[cfg(test)] mod tests { use expect_test::{expect, Expect}; use ff::{Field, PrimeField, PrimeFieldBits}; + use neptune::Poseidon; use rand_core::OsRng; use crate::{ @@ -193,6 +174,7 @@ mod tests { solver::SatisfyingAssignment, }, constants::NIO_CYCLE_FOLD, + gadgets::scalar_as_base, provider::{Bn256EngineKZG, PallasEngine, Secp256k1Engine}, traits::{commitment::CommitmentEngineTrait, snark::default_ck_hint, CurveCycleEquipped, Dual}, }; @@ -204,7 +186,7 @@ mod tests { E1: CurveCycleEquipped, { // Instantiate the circuit with trivial inputs - let circuit: CyclefoldCircuit> = CyclefoldCircuit::new(None, None, None); + let circuit: CyclefoldCircuit> = CyclefoldCircuit::default(); // Synthesize the R1CS shape let mut cs: ShapeCS = ShapeCS::new(); @@ -258,11 +240,10 @@ mod tests { vec.try_into().unwrap() }); + let circuit: CyclefoldCircuit> = CyclefoldCircuit::new(Some(C_1), Some(C_2), r_bits); + // Calculate the result out of circuit let native_result = C_1 + C_2 * r; - let (res_X, res_Y, res_is_infinity) = native_result.to_coordinates(); - - let circuit: CyclefoldCircuit> = CyclefoldCircuit::new(Some(C_1), Some(C_2), r_bits); // Generate the R1CS shape and commitment key let mut cs: ShapeCS = ShapeCS::new(); @@ -276,34 +257,28 @@ mod tests { .unwrap(); let (instance, witness) = cs.r1cs_instance_and_witness(&shape, &ck).unwrap(); - let X = &instance.X; - // Recombine the scalar from the two 128 bit limbs in the public IO of the cyclefold circuit - let recombine_scalar = |lower: E::Scalar, upper: E::Scalar| -> E::Scalar { - let mut upper = upper; - (0..128).for_each(|_| upper = upper.double()); - lower + upper - }; - - // Parse the public IO as the circuit values - let circuit_res_X_lower = X[10]; - let circuit_res_X_upper = X[11]; - let circuit_res_X = recombine_scalar(circuit_res_X_lower, circuit_res_X_upper); + // Helper functio to calculate the hash + let compute_hash = |P: Commitment>| -> E::Scalar { + let (x, y, is_infinity) = P.to_coordinates(); + if is_infinity { + return E::Scalar::ZERO; + } - let circuit_res_Y_lower = X[12]; - let circuit_res_Y_upper = X[13]; - let circuit_res_Y = recombine_scalar(circuit_res_Y_lower, circuit_res_Y_upper); + let mut hasher = Poseidon::new_with_preimage(&vec![x, y], &circuit.poseidon_constants); - let circuit_res_is_infinity = X[14]; + hasher.hash() + }; - // Check the out of circuit result matches the circuit result - assert_eq!(res_X, circuit_res_X); - assert_eq!(res_Y, circuit_res_Y); - assert_eq!( - res_is_infinity, - circuit_res_is_infinity == as Engine>::Base::ONE - ); + // Check the circuit calculates the right thing + let hash_1 = compute_hash(C_1); + assert_eq!(hash_1, X[0]); + let hash_2 = compute_hash(C_2); + assert_eq!(hash_2, X[1]); + let hash_res = compute_hash(native_result); + assert_eq!(hash_res, X[2]); + assert_eq!(r, scalar_as_base::(X[3])); // Check the R1CS equation is satisfied shape.is_sat(&ck, &instance, &witness).unwrap(); diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index c14561a38..9700f0836 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -1,19 +1,102 @@ use super::nova_circuit::FoldingData; use crate::{ - gadgets::{AllocatedPoint, AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance}, - traits::{commitment::CommitmentTrait, Engine, ROCircuitTrait}, + constants::{NIO_CYCLE_FOLD, NUM_CHALLENGE_BITS}, + gadgets::{ + alloc_bignat_constant, f_to_nat, le_bits_to_num, AllocatedPoint, AllocatedRelaxedR1CSInstance, + BigNat, Num, + }, + r1cs::R1CSInstance, + traits::{commitment::CommitmentTrait, Engine, Group, ROCircuitTrait, ROConstantsCircuit}, }; -use bellpepper_core::{ConstraintSystem, SynthesisError}; -pub struct AllocatedFoldingData { - pub U: AllocatedRelaxedR1CSInstance, - pub u: AllocatedR1CSInstance, +use bellpepper::gadgets::Assignment; +use bellpepper_core::{num::AllocatedNum, ConstraintSystem, SynthesisError}; +use ff::Field; +use itertools::Itertools; + +pub struct AllocatedCycleFoldInstance { + W: AllocatedPoint, + X: [BigNat; NIO_CYCLE_FOLD], +} + +impl AllocatedCycleFoldInstance { + pub fn alloc>( + mut cs: CS, + inst: Option<&R1CSInstance>, + limb_width: usize, + n_limbs: usize, + ) -> Result { + let W = AllocatedPoint::alloc( + cs.namespace(|| "allocate W"), + inst.map(|u| u.comm_W.to_coordinates()), + )?; + W.check_on_curve(cs.namespace(|| "check W on curve"))?; + + if let Some(inst) = inst { + if inst.X.len() != NIO_CYCLE_FOLD { + return Err(SynthesisError::IncompatibleLengthVector(String::from( + "R1CS instance has wrong arity", + ))); + } + } + + let X: [BigNat; NIO_CYCLE_FOLD] = (0..NIO_CYCLE_FOLD) + .map(|idx| { + BigNat::alloc_from_nat( + cs.namespace(|| format!("allocating IO {idx}")), + || Ok(f_to_nat(inst.map_or(&E::Scalar::ZERO, |inst| &inst.X[idx]))), + limb_width, + n_limbs, + ) + }) + .collect::, _>>()? + .try_into() + .map_err(|err: Vec<_>| { + SynthesisError::IncompatibleLengthVector(format!("{} != {NIO_CYCLE_FOLD}", err.len())) + })?; + + Ok(Self { W, X }) + } + + pub fn absorb_in_ro( + &self, + mut cs: CS, + ro: &mut impl ROCircuitTrait, + ) -> Result<(), SynthesisError> + where + CS: ConstraintSystem, + { + ro.absorb(&self.W.x); + ro.absorb(&self.W.y); + ro.absorb(&self.W.is_infinity); + self + .X + .iter() + .enumerate() + .try_for_each(|(io_idx, x)| -> Result<(), SynthesisError> { + x.as_limbs().iter().enumerate().try_for_each( + |(limb_idx, limb)| -> Result<(), SynthesisError> { + ro.absorb(&limb.as_allocated_num( + cs.namespace(|| format!("convert limb {limb_idx} of X[{io_idx}] to num")), + )?); + Ok(()) + }, + ) + })?; + + Ok(()) + } +} + +pub struct AllocatedCycleFoldData { + pub U: AllocatedRelaxedR1CSInstance, + pub u: AllocatedCycleFoldInstance, pub T: AllocatedPoint, } -impl AllocatedFoldingData { - pub fn alloc::Base>>( +impl AllocatedCycleFoldData { + pub fn alloc>( mut cs: CS, inst: Option<&FoldingData>, limb_width: usize, @@ -26,13 +109,105 @@ impl AllocatedFoldingData { n_limbs, )?; - let u = AllocatedR1CSInstance::alloc(cs.namespace(|| "u"), inst.map(|x| &x.u))?; + let u = AllocatedCycleFoldInstance::alloc( + cs.namespace(|| "u"), + inst.map(|x| &x.u), + limb_width, + n_limbs, + )?; let T = AllocatedPoint::alloc(cs.namespace(|| "T"), inst.map(|x| x.T.to_coordinates()))?; T.check_on_curve(cs.namespace(|| "T on curve"))?; Ok(Self { U, u, T }) } + + pub fn apply_fold( + &self, + mut cs: CS, + params: &AllocatedNum, + ro_consts: ROConstantsCircuit, + limb_width: usize, + n_limbs: usize, + ) -> Result, SynthesisError> + where + CS: ConstraintSystem, + { + // Compute r: + let mut ro = E::ROCircuit::new(ro_consts, 7 + NIO_CYCLE_FOLD); + ro.absorb(params); + + self.U.absorb_in_ro( + cs.namespace(|| "absorb cyclefold running instance"), + &mut ro, + )?; + // running instance `U` does not need to absorbed since u.X[0] = Hash(params, U, i, z0, zi) + self + .u + .absorb_in_ro(cs.namespace(|| "absorb cyclefold instance"), &mut ro)?; + + ro.absorb(&self.T.x); + ro.absorb(&self.T.y); + ro.absorb(&self.T.is_infinity); + let r_bits = ro.squeeze(cs.namespace(|| "r bits"), NUM_CHALLENGE_BITS)?; + let r = le_bits_to_num(cs.namespace(|| "r"), &r_bits)?; + + // W_fold = self.W + r * u.W + let rW = self.u.W.scalar_mul(cs.namespace(|| "r * u.W"), &r_bits)?; + let W_fold = self.U.W.add(cs.namespace(|| "self.W + r * u.W"), &rW)?; + + // E_fold = self.E + r * T + let rT = self.T.scalar_mul(cs.namespace(|| "r * T"), &r_bits)?; + let E_fold = self.U.E.add(cs.namespace(|| "self.E + r * T"), &rT)?; + + // u_fold = u_r + r + let u_fold = AllocatedNum::alloc(cs.namespace(|| "u_fold"), || { + Ok(*self.U.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.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), + 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"), + &E::GE::group_params().2, + limb_width, + n_limbs, + )?; + + let mut X_fold = vec![]; + + for (idx, (X, x)) in self.U.X.iter().zip_eq(self.u.X.iter()).enumerate() { + let (_, r) = x.mult_mod(cs.namespace(|| format!("r*u.X[{idx}]")), &r_bn, &m_bn)?; + let r_new = X.add(&r)?; + let X_i_fold = r_new.red_mod(cs.namespace(|| format!("reduce folded X[{idx}]")), &m_bn)?; + X_fold.push(X_i_fold); + } + + let X_fold = X_fold.try_into().map_err(|err: Vec<_>| { + SynthesisError::IncompatibleLengthVector(format!("{} != {NIO_CYCLE_FOLD}", err.len())) + })?; + + Ok(AllocatedRelaxedR1CSInstance { + W: W_fold, + E: E_fold, + u: u_fold, + X: X_fold, + }) + } } pub mod emulated { @@ -40,20 +215,21 @@ pub mod emulated { use bellpepper_core::{ boolean::{AllocatedBit, Boolean}, num::AllocatedNum, + ConstraintSystem, SynthesisError, }; - use super::*; - use crate::{ constants::{NUM_CHALLENGE_BITS, NUM_FE_IN_EMULATED_POINT}, gadgets::{ alloc_zero, conditionally_select, conditionally_select_allocated_bit, conditionally_select_bignat, f_to_nat, le_bits_to_num, BigNat, }, - traits::{Group, ROConstantsCircuit}, + traits::{commitment::CommitmentTrait, Engine, Group, ROCircuitTrait, ROConstantsCircuit}, RelaxedR1CSInstance, }; + use super::FoldingData; + use ff::Field; pub struct EmulatedCurveParams diff --git a/src/cyclefold/nifs.rs b/src/cyclefold/nifs.rs index ac42527bf..099a9e7b7 100644 --- a/src/cyclefold/nifs.rs +++ b/src/cyclefold/nifs.rs @@ -9,7 +9,7 @@ use crate::{ errors::NovaError, gadgets::{f_to_nat, nat_to_limbs, scalar_as_base}, r1cs::{R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness}, - traits::{commitment::CommitmentTrait, Engine, ROConstants, ROTrait}, + traits::{commitment::CommitmentTrait, AbsorbInROTrait, Engine, ROConstants, ROTrait}, CommitmentKey, CompressedCommitment, }; @@ -50,9 +50,11 @@ where } } -/// A SNARK that holds the proof of a step of an incremental computation +/// A SNARK that holds the proof of a step of an incremental computation of the primary circuit +/// in the CycleFold folding scheme. +/// The difference of this folding scheme from the Nova NIFS in `src/nifs.rs` is that this #[derive(Debug)] -pub struct NIFS +pub struct PrimaryNIFS where E1: Engine::Scalar>, E2: Engine::Scalar>, @@ -61,12 +63,12 @@ where _p: PhantomData, } -impl NIFS +impl PrimaryNIFS where E1: Engine::Scalar>, E2: Engine::Scalar>, { - /// Takes a relaxed R1CS instance-witness pair (U1, W1) and an R1CS instance-witness pair (U2, W) + /// Takes a relaxed R1CS instance-witness pair (U1, W1) and an R1CS instance-witness pair (U2, W2) /// and folds them into a new relaxed R1CS instance-witness pair (U, W) and a commitment to the /// cross term T. It also provides the challenge r used to fold the instances. pub fn prove( @@ -121,3 +123,68 @@ where )) } } + +/// A SNARK that holds the proof of a step of an incremental computation of the CycleFold circuit +/// The difference of this folding scheme from the Nova NIFS in `src/nifs.rs` is that this folding +/// prover and verifier must fold in the `RelaxedR1CSInstance` accumulator because the optimization +/// in the +pub struct CycleFoldNIFS { + pub(crate) comm_T: CompressedCommitment, +} + +impl CycleFoldNIFS { + /// Folds an R1CS instance/witness pair (U2, W2) into a relaxed R1CS instance/witness (U1, W1) + /// returning the new folded accumulator and a commitment to the cross-term. + pub fn prove( + ck: &CommitmentKey, + ro_consts: &ROConstants, + pp_digest: &E::Scalar, + S: &R1CSShape, + U1: &RelaxedR1CSInstance, + W1: &RelaxedR1CSWitness, + U2: &R1CSInstance, + W2: &R1CSWitness, + ) -> Result<(Self, (RelaxedR1CSInstance, RelaxedR1CSWitness)), NovaError> { + // Check `U1` and `U2` have the same arity + let io_arity = U1.X.len(); + if io_arity != U2.X.len() { + return Err(NovaError::InvalidInputLength); + } + + // initialize a new RO + let mut ro = E::RO::new(ro_consts.clone(), 7 + io_arity); + + // append the digest of pp to the transcript + ro.absorb(scalar_as_base::(*pp_digest)); + + // append U1 to the transcript. + // NOTE: this must be here because the IO for `U2` does not have the data of the hash of `U1` + U1.absorb_in_ro(&mut ro); + + // append U2 to transcript + U2.absorb_in_ro(&mut ro); + + // compute a commitment to the cross-term + let (T, comm_T) = S.commit_T(ck, U1, W1, U2, W2)?; + + // append `comm_T` to the transcript and obtain a challenge + comm_T.absorb_in_ro(&mut ro); + + // compute a challenge from the RO + let r = ro.squeeze(NUM_CHALLENGE_BITS); + + // fold the instance using `r` and `comm_T` + let U = U1.fold(U2, &comm_T, &r); + + // fold the witness using `r` and `T` + let W = W1.fold(W2, &T, &r)?; + + // return the folded instance and witness + Ok(( + Self { + comm_T: comm_T.compress(), + }, + (U, W), + )) + } +} diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index d3aac2f13..dc78f9456 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -4,7 +4,7 @@ use crate::{ constants::{BN_N_LIMBS, NIO_CYCLE_FOLD, NUM_FE_IN_EMULATED_POINT, NUM_HASH_BITS}, gadgets::{ alloc_num_equals, alloc_scalar_as_base, alloc_zero, conditionally_select_vec, le_bits_to_num, - AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance, + AllocatedRelaxedR1CSInstance, }, r1cs::{R1CSInstance, RelaxedR1CSInstance}, traits::{ @@ -17,10 +17,9 @@ use abomonation_derive::Abomonation; use bellpepper::gadgets::{boolean::Boolean, num::AllocatedNum, Assignment}; use bellpepper_core::{boolean::AllocatedBit, ConstraintSystem, SynthesisError}; use ff::Field; -use itertools::{chain, Itertools}; use serde::{Deserialize, Serialize}; -use super::gadgets::{emulated, AllocatedFoldingData}; +use super::gadgets::{emulated, AllocatedCycleFoldData}; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Abomonation)] pub struct AugmentedCircuitParams { @@ -139,15 +138,15 @@ where arity: usize, ) -> Result< ( - AllocatedNum, // pp_digest - AllocatedNum, // i - Vec>, // z0 - Vec>, // zi - emulated::AllocatedFoldingData, //data_p - AllocatedFoldingData, // data_c_1 - AllocatedFoldingData, // data_c_2 - emulated::AllocatedEmulPoint, // E_new - emulated::AllocatedEmulPoint, // W_new + AllocatedNum, // pp_digest + AllocatedNum, // i + Vec>, // z0 + Vec>, // zi + emulated::AllocatedFoldingData, //data_p + AllocatedCycleFoldData, // data_c_1 + AllocatedCycleFoldData, // data_c_2 + emulated::AllocatedEmulPoint, // E_new + emulated::AllocatedEmulPoint, // W_new ), SynthesisError, > { @@ -186,7 +185,7 @@ where self.params.n_limbs, )?; - let data_c_1 = AllocatedFoldingData::alloc( + let data_c_1 = AllocatedCycleFoldData::alloc( cs.namespace(|| "data_c_1"), self .inputs @@ -196,7 +195,7 @@ where self.params.n_limbs, )?; - let data_c_2 = AllocatedFoldingData::alloc( + let data_c_2 = AllocatedCycleFoldData::alloc( cs.namespace(|| "data_c_2"), self .inputs @@ -266,12 +265,11 @@ where z_0: &[AllocatedNum], z_i: &[AllocatedNum], data_p: &emulated::AllocatedFoldingData, - data_c_1: &AllocatedFoldingData, - data_c_2: &AllocatedFoldingData, + data_c_1: &AllocatedCycleFoldData, + data_c_2: &AllocatedCycleFoldData, E_new: emulated::AllocatedEmulPoint, W_new: emulated::AllocatedEmulPoint, arity: usize, - is_base_case: &AllocatedBit, ) -> Result< ( AllocatedRelaxedR1CSInstance, @@ -326,29 +324,6 @@ where &hash_c, )?; - let u_E = &data_c_1.u; - let E_1 = &data_p.U.comm_E; - cyclefold_invocation_check( - cs.namespace(|| "cyclefold invocation check E"), - E_1, - &data_p.T, - &E_new, - u_E, - is_base_case, - )?; - - let u_W = &data_c_2.u; - let W_1 = &data_p.U.comm_W; - let W_2 = &data_p.u_W; - cyclefold_invocation_check( - cs.namespace(|| "cyclefold invocation check W"), - W_1, - W_2, - &W_new, - u_W, - is_base_case, - )?; - let check_io = AllocatedBit::and( cs.namespace(|| "both IOs match"), &check_primary, @@ -356,11 +331,9 @@ where )?; // Run NIVC.V on U_c, u_c_1, T_c_1 - let U_int = data_c_1.U.fold_with_r1cs( - cs.namespace(|| "U_int = fold U_c with u_c_1"), + let U_int = data_c_1.apply_fold( + cs.namespace(|| "fold u_c_1 into U_c"), pp_digest, - &data_c_1.u, - &data_c_1.T, self.ro_consts.clone(), self.params.limb_width, self.params.n_limbs, @@ -398,11 +371,9 @@ where &check_cyclefold_int, )?; - let U_c = data_c_2.U.fold_with_r1cs( - cs.namespace(|| "U_c = fold U_c_1 with u_c_2"), + let U_c = data_c_2.apply_fold( + cs.namespace(|| "fold u_c_2 into U_c_1"), pp_digest, - &data_c_2.u, - &data_c_2.T, self.ro_consts.clone(), self.params.limb_width, self.params.n_limbs, @@ -450,7 +421,6 @@ where E_new, W_new, arity, - &is_base_case, )?; let should_be_false = AllocatedBit::nor( @@ -539,99 +509,6 @@ where } } -// TODO: Clean this up -pub fn emulated_point_check::Base>>( - mut cs: CS, - point: &emulated::AllocatedEmulPoint, - io_limbs: &[AllocatedNum<::Base>], - always_equal: &AllocatedBit, -) -> Result<(), SynthesisError> { - let x_limbs = point - .x - .group_limbs(BN_N_LIMBS / 2) - .as_limbs() - .iter() - .enumerate() - .map(|(idx, limb)| limb.as_allocated_num(cs.namespace(|| format!("alloc x_limb[{idx}]")))) - .collect::, _>>()?; - - let y_limbs = point - .y - .group_limbs(BN_N_LIMBS / 2) - .as_limbs() - .iter() - .enumerate() - .map(|(idx, limb)| limb.as_allocated_num(cs.namespace(|| format!("alloc y_limb[{idx}]")))) - .collect::, _>>()?; - - let is_infinity = AllocatedNum::alloc(cs.namespace(|| "allocate is_infinity"), || { - if *point.is_infinity.get_value().get()? { - Ok(::Base::ONE) - } else { - Ok(::Base::ZERO) - } - })?; - - cs.enforce( - || "enforcing is_infinity", - |lc| lc, - |lc| lc, - |lc| lc + is_infinity.get_variable() - point.is_infinity.get_variable(), - ); - - let all_variables = chain!(x_limbs, y_limbs, vec![is_infinity]); - - all_variables - .into_iter() - .zip_eq(io_limbs) - .enumerate() - .try_for_each(|(idx, (var, limb))| -> Result<(), SynthesisError> { - cs.enforce( - || format!("enforcing equality {idx}"), - |lc| lc + CS::one() - always_equal.get_variable(), - |lc| lc + var.get_variable() - limb.get_variable(), - |lc| lc, - ); - - Ok(()) - })?; - - Ok(()) -} - -// TODO: Clean this up -pub fn cyclefold_invocation_check::Base>>( - mut cs: CS, - C_1: &emulated::AllocatedEmulPoint, - C_2: &emulated::AllocatedEmulPoint, - C_res: &emulated::AllocatedEmulPoint, - instance: &AllocatedR1CSInstance, - is_base_case: &AllocatedBit, -) -> Result<(), SynthesisError> { - let (point_1_io, point_23_io) = instance.X.split_at(5); - let (point_2_io, point_3_io_plus_scalar) = point_23_io.split_at(5); - let point_3_io = point_3_io_plus_scalar.split_at(5).0; - emulated_point_check::( - cs.namespace(|| "check point C_1"), - C_1, - point_1_io, - is_base_case, - )?; - emulated_point_check::( - cs.namespace(|| "check point C_2"), - C_2, - point_2_io, - is_base_case, - )?; - emulated_point_check::( - cs.namespace(|| "check point C_res"), - C_res, - point_3_io, - is_base_case, - )?; - - Ok(()) -} #[cfg(test)] mod test { use crate::{ diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 10332493b..deab86dd7 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -14,7 +14,6 @@ use crate::{ cyclefold::circuit::CyclefoldCircuit, errors::NovaError, gadgets::scalar_as_base, - nifs::NIFS, r1cs::{ self, CommitmentKeyHint, R1CSInstance, R1CSResult, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness, @@ -28,7 +27,7 @@ use crate::{ }; use super::{ - nifs::absorb_commitment, + nifs::{absorb_commitment, CycleFoldNIFS, PrimaryNIFS}, nova_circuit::{AugmentedCircuit, AugmentedCircuitInputs, AugmentedCircuitParams, FoldingData}, }; @@ -268,7 +267,7 @@ where return Ok(()); } - let (nifs_primary, (r_U_primary, r_W_primary), r) = super::nifs::NIFS::>::prove( + let (nifs_primary, (r_U_primary, r_W_primary), r) = PrimaryNIFS::>::prove( &pp.ck_primary, &pp.ro_consts_primary, &pp.digest(), @@ -307,7 +306,7 @@ where .map_err(|_| NovaError::UnSat)?; // TODO: check if this is better or worse than `prove_mut` with a clone of `self.r_U_cyclefold` - let (nifs_cyclefold_E, (r_U_cyclefold_E, r_W_cyclefold_E), _) = NIFS::prove( + let (nifs_cyclefold_E, (r_U_cyclefold_E, r_W_cyclefold_E)) = CycleFoldNIFS::prove( &pp.ck_cyclefold, &pp.ro_consts_cyclefold, &scalar_as_base::(pp.digest()), @@ -338,7 +337,7 @@ where .map_err(|_| NovaError::UnSat)?; // TODO: check if this is better or worse than `prove_mut` with a clone of r_U_cyclefold_E - let (nifs_cyclefold_W, (r_U_cyclefold_W, r_W_cyclefold_W), _) = NIFS::prove( + let (nifs_cyclefold_W, (r_U_cyclefold_W, r_W_cyclefold_W)) = CycleFoldNIFS::prove( &pp.ck_cyclefold, &pp.ro_consts_cyclefold, &scalar_as_base::(pp.digest()), diff --git a/src/gadgets/mod.rs b/src/gadgets/mod.rs index 1d3af3288..e5f31dae0 100644 --- a/src/gadgets/mod.rs +++ b/src/gadgets/mod.rs @@ -3,7 +3,7 @@ mod ecc; pub(crate) use ecc::AllocatedPoint; mod nonnative; -pub(crate) use nonnative::{bignat::nat_to_limbs, bignat::BigNat, util::f_to_nat}; +pub(crate) use nonnative::{bignat::nat_to_limbs, bignat::BigNat, util::f_to_nat, util::Num}; mod r1cs; pub(crate) use r1cs::{ diff --git a/src/gadgets/utils.rs b/src/gadgets/utils.rs index d251f4922..4474c4ece 100644 --- a/src/gadgets/utils.rs +++ b/src/gadgets/utils.rs @@ -68,21 +68,21 @@ pub fn alloc_one>(mut cs: CS) -> Allocate one } -/// Allocate a constant and constrain it -pub fn alloc_constant>( - val: F, - mut cs: CS, -) -> AllocatedNum { - let allocated_val = AllocatedNum::alloc_infallible(cs.namespace(|| "alloc"), || val); - cs.enforce( - || "check val is valid", - |lc| lc, - |lc| lc, - |lc| lc + allocated_val.get_variable() - (val, CS::one()), - ); - - allocated_val -} +// /// Allocate a constant and constrain it +// pub fn alloc_constant>( +// val: F, +// mut cs: CS, +// ) -> AllocatedNum { +// let allocated_val = AllocatedNum::alloc_infallible(cs.namespace(|| "alloc"), || val); +// cs.enforce( +// || "check val is valid", +// |lc| lc, +// |lc| lc, +// |lc| lc + allocated_val.get_variable() - (val, CS::one()), +// ); + +// allocated_val +// } /// Allocate a scalar as a base. Only to be used is the scalar fits in base! pub fn alloc_scalar_as_base( From 59577f64a35ca7d0bf7a3d7e7e3503b00e40c355 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Thu, 7 Mar 2024 00:49:23 -0500 Subject: [PATCH 65/77] chore: update cyclefold_circuit_size expecttest --- src/cyclefold/circuit.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cyclefold/circuit.rs b/src/cyclefold/circuit.rs index 1987e0b0f..c5fc9e090 100644 --- a/src/cyclefold/circuit.rs +++ b/src/cyclefold/circuit.rs @@ -205,9 +205,9 @@ mod tests { #[test] fn test_cyclefold_circuit_size() { - test_cyclefold_circuit_size_with::(&expect!("1394"), &expect!("1382")); - test_cyclefold_circuit_size_with::(&expect!("1394"), &expect!("1382")); - test_cyclefold_circuit_size_with::(&expect!("1394"), &expect!("1382")); + test_cyclefold_circuit_size_with::(&expect!("2090"), &expect!("2081")); + test_cyclefold_circuit_size_with::(&expect!("2090"), &expect!("2081")); + test_cyclefold_circuit_size_with::(&expect!("2090"), &expect!("2081")); } fn test_cyclefold_circuit_sat_with() { From e735e9eae4705134805df315a54ff5e90fcd5acc Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Thu, 7 Mar 2024 00:50:46 -0500 Subject: [PATCH 66/77] fix: fix RO absorb counts and logic --- src/cyclefold/gadgets.rs | 7 +++++-- src/cyclefold/nifs.rs | 34 ++++++++++++++++++++++++---------- src/cyclefold/snark.rs | 10 +++++----- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index 9700f0836..5e301ea4d 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -1,7 +1,7 @@ use super::nova_circuit::FoldingData; use crate::{ - constants::{NIO_CYCLE_FOLD, NUM_CHALLENGE_BITS}, + constants::{BN_N_LIMBS, NIO_CYCLE_FOLD, NUM_CHALLENGE_BITS}, gadgets::{ alloc_bignat_constant, f_to_nat, le_bits_to_num, AllocatedPoint, AllocatedRelaxedR1CSInstance, BigNat, Num, @@ -134,7 +134,10 @@ impl AllocatedCycleFoldData { CS: ConstraintSystem, { // Compute r: - let mut ro = E::ROCircuit::new(ro_consts, 7 + NIO_CYCLE_FOLD); + let mut ro = E::ROCircuit::new( + ro_consts, + 1 + (3 + 3 + 1 + NIO_CYCLE_FOLD * BN_N_LIMBS) + (3 + NIO_CYCLE_FOLD * BN_N_LIMBS) + 3, // digest + (U) + (u) + T + ); ro.absorb(params); self.U.absorb_in_ro( diff --git a/src/cyclefold/nifs.rs b/src/cyclefold/nifs.rs index 099a9e7b7..deca5f16d 100644 --- a/src/cyclefold/nifs.rs +++ b/src/cyclefold/nifs.rs @@ -5,7 +5,9 @@ use std::marker::PhantomData; use ff::Field; use crate::{ - constants::{BN_LIMB_WIDTH, BN_N_LIMBS, NUM_CHALLENGE_BITS, NUM_FE_IN_EMULATED_POINT}, + constants::{ + BN_LIMB_WIDTH, BN_N_LIMBS, NIO_CYCLE_FOLD, NUM_CHALLENGE_BITS, NUM_FE_IN_EMULATED_POINT, + }, errors::NovaError, gadgets::{f_to_nat, nat_to_limbs, scalar_as_base}, r1cs::{R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness}, @@ -14,7 +16,7 @@ use crate::{ }; /// Absorb a commitment over engine `E1` into an RO over engine `E2` by absorbing the limbs -pub fn absorb_commitment( +pub fn absorb_primary_commitment( comm: &impl CommitmentTrait, ro: &mut impl ROTrait, ) where @@ -39,17 +41,27 @@ pub fn absorb_commitment( } } -fn absorb_r1cs(u: &R1CSInstance, ro: &mut impl ROTrait) +fn absorb_primary_r1cs(u: &R1CSInstance, ro: &mut impl ROTrait) where E1: Engine::Scalar>, E2: Engine::Scalar>, { - absorb_commitment::(&u.comm_W, ro); + absorb_primary_commitment::(&u.comm_W, ro); for x in &u.X { ro.absorb(*x); } } +fn absorb_cyclefold_r1cs(u: &R1CSInstance, ro: &mut E::RO) { + u.comm_W.absorb_in_ro(ro); + u.X.iter().for_each(|x| { + let limbs: Vec = nat_to_limbs(&f_to_nat(x), BN_LIMB_WIDTH, BN_N_LIMBS).unwrap(); + limbs + .into_iter() + .for_each(|limb| ro.absorb(scalar_as_base::(limb))); + }); +} + /// A SNARK that holds the proof of a step of an incremental computation of the primary circuit /// in the CycleFold folding scheme. /// The difference of this folding scheme from the Nova NIFS in `src/nifs.rs` is that this @@ -101,11 +113,11 @@ where ro.absorb(*pp_digest); - absorb_r1cs::(U2, &mut ro); + absorb_primary_r1cs::(U2, &mut ro); let (T, comm_T) = S.commit_T(ck, U1, W1, U2, W2)?; - absorb_commitment::(&comm_T, &mut ro); + absorb_primary_commitment::(&comm_T, &mut ro); let r = scalar_as_base::(ro.squeeze(NUM_CHALLENGE_BITS)); @@ -146,13 +158,15 @@ impl CycleFoldNIFS { W2: &R1CSWitness, ) -> Result<(Self, (RelaxedR1CSInstance, RelaxedR1CSWitness)), NovaError> { // Check `U1` and `U2` have the same arity - let io_arity = U1.X.len(); - if io_arity != U2.X.len() { + if U2.X.len() != NIO_CYCLE_FOLD || U1.X.len() != NIO_CYCLE_FOLD { return Err(NovaError::InvalidInputLength); } // initialize a new RO - let mut ro = E::RO::new(ro_consts.clone(), 7 + io_arity); + let mut ro = E::RO::new( + ro_consts.clone(), + 46, // 1 + (3 + 3 + 1 + NIO_CYCLE_FOLD * BN_N_LIMBS) + (3 + NIO_CYCLE_FOLD * BN_N_LIMBS) + 3, // digest + (U) + (u) + T + ); // append the digest of pp to the transcript ro.absorb(scalar_as_base::(*pp_digest)); @@ -162,7 +176,7 @@ impl CycleFoldNIFS { U1.absorb_in_ro(&mut ro); // append U2 to transcript - U2.absorb_in_ro(&mut ro); + absorb_cyclefold_r1cs(U2, &mut ro); // compute a commitment to the cross-term let (T, comm_T) = S.commit_T(ck, U1, W1, U2, W2)?; diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index deab86dd7..5f5a34002 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -27,7 +27,7 @@ use crate::{ }; use super::{ - nifs::{absorb_commitment, CycleFoldNIFS, PrimaryNIFS}, + nifs::{absorb_primary_commitment, CycleFoldNIFS, PrimaryNIFS}, nova_circuit::{AugmentedCircuit, AugmentedCircuitInputs, AugmentedCircuitParams, FoldingData}, }; @@ -441,7 +441,7 @@ where for e in &self.zi_primary { hasher.absorb(*e); } - absorb_relaxed_r1cs::>(&self.r_U_primary, &mut hasher); + absorb_primary_relaxed_r1cs::>(&self.r_U_primary, &mut hasher); let hash_primary = hasher.squeeze(NUM_HASH_BITS); let mut hasher = as Engine>::RO::new( @@ -498,13 +498,13 @@ where } } -fn absorb_relaxed_r1cs(U: &RelaxedR1CSInstance, ro: &mut E2::RO) +fn absorb_primary_relaxed_r1cs(U: &RelaxedR1CSInstance, ro: &mut E2::RO) where E1: Engine::Scalar>, E2: Engine::Scalar>, { - absorb_commitment::(&U.comm_W, ro); - absorb_commitment::(&U.comm_E, ro); + absorb_primary_commitment::(&U.comm_W, ro); + absorb_primary_commitment::(&U.comm_E, ro); ro.absorb(U.u); for e in &U.X { ro.absorb(*e); From 6381ccf5d1bc50726d880bcccb4c326e8b834bac Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Thu, 7 Mar 2024 00:51:05 -0500 Subject: [PATCH 67/77] chore: update cyclefold primary circuit size expecttest --- src/cyclefold/nova_circuit.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index dc78f9456..20992c493 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -553,8 +553,8 @@ mod test { #[test] fn test_augmented_circuit_size() { - test_augmented_circuit_size_with::(&expect!["86258"], &expect!["86079"]); - test_augmented_circuit_size_with::(&expect!["88094"], &expect!["87915"]); - test_augmented_circuit_size_with::(&expect!["86825"], &expect!["86646"]); + test_augmented_circuit_size_with::(&expect!["33312"], &expect!["33339"]); + test_augmented_circuit_size_with::(&expect!["35148"], &expect!["35175"]); + test_augmented_circuit_size_with::(&expect!["33879"], &expect!["33906"]); } } From 0fafbeb2a5d3747642e4818e17ebc4730a542bfa Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Thu, 7 Mar 2024 17:02:16 -0500 Subject: [PATCH 68/77] chore: cleanup and docs --- src/cyclefold/circuit.rs | 2 +- src/cyclefold/gadgets.rs | 96 +++++++---------------------------- src/cyclefold/mod.rs | 3 +- src/cyclefold/nifs.rs | 55 ++------------------ src/cyclefold/nova_circuit.rs | 43 +++++++++------- src/cyclefold/snark.rs | 25 +++------ src/cyclefold/util.rs | 89 ++++++++++++++++++++++++++++++++ 7 files changed, 144 insertions(+), 169 deletions(-) create mode 100644 src/cyclefold/util.rs diff --git a/src/cyclefold/circuit.rs b/src/cyclefold/circuit.rs index c5fc9e090..122b5b1e8 100644 --- a/src/cyclefold/circuit.rs +++ b/src/cyclefold/circuit.rs @@ -1,4 +1,4 @@ -//! This module defines Cyclefold stuff +//! This module defines Cyclefold circuit use bellpepper_core::{ boolean::{AllocatedBit, Boolean}, diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index 5e301ea4d..ab0577b37 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -1,4 +1,6 @@ -use super::nova_circuit::FoldingData; +//! This module defines many of the gadgets needed in the primary folding circuit + +use super::util::FoldingData; use crate::{ constants::{BN_N_LIMBS, NIO_CYCLE_FOLD, NUM_CHALLENGE_BITS}, @@ -15,6 +17,7 @@ use bellpepper_core::{num::AllocatedNum, ConstraintSystem, SynthesisError}; use ff::Field; use itertools::Itertools; +// An allocated version of the R1CS instance obtained from a single cyclefold invocation pub struct AllocatedCycleFoldInstance { W: AllocatedPoint, X: [BigNat; NIO_CYCLE_FOLD], @@ -89,6 +92,7 @@ impl AllocatedCycleFoldInstance { } } +/// An circuit allocated version of the `FoldingData` coming from a CycleFold invocation. pub struct AllocatedCycleFoldData { pub U: AllocatedRelaxedR1CSInstance, pub u: AllocatedCycleFoldInstance, @@ -122,6 +126,7 @@ impl AllocatedCycleFoldData { Ok(Self { U, u, T }) } + /// The NIFS verifier which folds the CycleFold instance into a running relaxed R1CS instance. pub fn apply_fold( &self, mut cs: CS, @@ -193,6 +198,7 @@ impl AllocatedCycleFoldData { let mut X_fold = vec![]; + // Calculate the for (idx, (X, x)) in self.U.X.iter().zip_eq(self.u.X.iter()).enumerate() { let (_, r) = x.mult_mod(cs.namespace(|| format!("r*u.X[{idx}]")), &r_bn, &m_bn)?; let r_new = X.add(&r)?; @@ -235,51 +241,7 @@ pub mod emulated { use ff::Field; - pub struct EmulatedCurveParams - where - G: Group, - { - pub A: BigNat, - pub B: BigNat, - pub m: BigNat, - } - - impl EmulatedCurveParams { - #[allow(unused)] - pub fn alloc( - mut cs: CS, - params: &(G::Scalar, G::Scalar, G::Scalar), - limb_width: usize, - n_limbs: usize, - ) -> Result - where - CS: ConstraintSystem, - { - let A = BigNat::alloc_from_nat( - cs.namespace(|| "allocate A"), - || Ok(f_to_nat(¶ms.0)), - limb_width, - n_limbs, - )?; - - let B = BigNat::alloc_from_nat( - cs.namespace(|| "allocate B"), - || Ok(f_to_nat(¶ms.1)), - limb_width, - n_limbs, - )?; - - let m = BigNat::alloc_from_nat( - cs.namespace(|| "allocate m"), - || Ok(f_to_nat(¶ms.2)), - limb_width, - n_limbs, - )?; - - Ok(Self { A, B, m }) - } - } - + /// An allocated version of a curve point from the non-native curve #[derive(Clone)] pub struct AllocatedEmulPoint where @@ -395,39 +357,6 @@ pub mod emulated { Ok(()) } - #[allow(unused)] - pub fn check_on_curve( - &self, - mut cs: CS, - curve_params: &EmulatedCurveParams, - _limb_width: usize, - _n_limbs: usize, - ) -> Result<(), SynthesisError> - where - CS: ConstraintSystem, - { - let (m_bn, A_bn, B_bn) = ( - curve_params.m.clone(), - curve_params.A.clone(), - curve_params.B.clone(), - ); - - let (_, A_x) = A_bn.mult_mod(cs.namespace(|| "A_x"), &self.x, &m_bn)?; - - let (_, x_sq) = self.x.mult_mod(cs.namespace(|| "x_sq"), &self.x, &m_bn)?; - let (_, x_cu) = self.x.mult_mod(cs.namespace(|| "x_cu"), &x_sq, &m_bn)?; - - let rhs = x_cu.add(&A_x)?.add(&B_bn)?; - - let (_, y_sq) = self.y.mult_mod(cs.namespace(|| "y_sq"), &self.y, &m_bn)?; - - let always_equal = self.is_infinity.clone(); - - y_sq.equal_when_carried_regroup(cs.namespace(|| "y_sq = rhs"), &rhs, &always_equal)?; - - Ok(()) - } - fn conditionally_select>( &self, mut cs: CS, @@ -483,6 +412,8 @@ pub mod emulated { } } + /// A non-native circuit version of a `RelaxedR1CSInstance`. This is used for the in-circuit + /// representation of the primary running instance pub struct AllocatedEmulRelaxedR1CSInstance { pub comm_W: AllocatedEmulPoint, pub comm_E: AllocatedEmulPoint, @@ -539,6 +470,11 @@ pub mod emulated { }) } + /// Performs a folding of a primary R1CS instance (`u_W`, `u_x0`, `u_x1`) into a running + /// `AllocatedEmulRelaxedR1CSInstance` + /// As the curve operations are performed in the CycleFold circuit and provided to the primary + /// circuit as non-deterministic advice, this folding simply sets those values as the new witness + /// and error vector commitments. pub fn fold_with_r1cs::Base>>( &self, mut cs: CS, @@ -702,6 +638,8 @@ pub mod emulated { }) } } + + /// The in-circuit representation of the primary folding data. pub struct AllocatedFoldingData { pub U: AllocatedEmulRelaxedR1CSInstance, pub u_W: AllocatedEmulPoint, diff --git a/src/cyclefold/mod.rs b/src/cyclefold/mod.rs index 004afad5b..ae5f97bab 100644 --- a/src/cyclefold/mod.rs +++ b/src/cyclefold/mod.rs @@ -1,8 +1,9 @@ -//! This module defines Cyclefold folding scheme and its related functions. +//! This module defines CycleFold folding scheme and its related functions. mod circuit; mod gadgets; mod nova_circuit; +mod util; pub mod nifs; pub mod snark; diff --git a/src/cyclefold/nifs.rs b/src/cyclefold/nifs.rs index deca5f16d..3df6d652c 100644 --- a/src/cyclefold/nifs.rs +++ b/src/cyclefold/nifs.rs @@ -2,65 +2,16 @@ use std::marker::PhantomData; -use ff::Field; - use crate::{ - constants::{ - BN_LIMB_WIDTH, BN_N_LIMBS, NIO_CYCLE_FOLD, NUM_CHALLENGE_BITS, NUM_FE_IN_EMULATED_POINT, - }, + constants::{NIO_CYCLE_FOLD, NUM_CHALLENGE_BITS, NUM_FE_IN_EMULATED_POINT}, errors::NovaError, - gadgets::{f_to_nat, nat_to_limbs, scalar_as_base}, + gadgets::scalar_as_base, r1cs::{R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness}, traits::{commitment::CommitmentTrait, AbsorbInROTrait, Engine, ROConstants, ROTrait}, CommitmentKey, CompressedCommitment, }; -/// Absorb a commitment over engine `E1` into an RO over engine `E2` by absorbing the limbs -pub fn absorb_primary_commitment( - comm: &impl CommitmentTrait, - ro: &mut impl ROTrait, -) where - E1: Engine::Scalar>, - E2: Engine::Scalar>, -{ - let (x, y, is_infinity) = comm.to_coordinates(); - - let x_limbs = nat_to_limbs(&f_to_nat(&x), BN_LIMB_WIDTH, BN_N_LIMBS).unwrap(); - let y_limbs = nat_to_limbs(&f_to_nat(&y), BN_LIMB_WIDTH, BN_N_LIMBS).unwrap(); - - for limb in x_limbs { - ro.absorb(scalar_as_base::(limb)); - } - for limb in y_limbs { - ro.absorb(scalar_as_base::(limb)); - } - if is_infinity { - ro.absorb(::Scalar::ONE); - } else { - ro.absorb(::Scalar::ZERO); - } -} - -fn absorb_primary_r1cs(u: &R1CSInstance, ro: &mut impl ROTrait) -where - E1: Engine::Scalar>, - E2: Engine::Scalar>, -{ - absorb_primary_commitment::(&u.comm_W, ro); - for x in &u.X { - ro.absorb(*x); - } -} - -fn absorb_cyclefold_r1cs(u: &R1CSInstance, ro: &mut E::RO) { - u.comm_W.absorb_in_ro(ro); - u.X.iter().for_each(|x| { - let limbs: Vec = nat_to_limbs(&f_to_nat(x), BN_LIMB_WIDTH, BN_N_LIMBS).unwrap(); - limbs - .into_iter() - .for_each(|limb| ro.absorb(scalar_as_base::(limb))); - }); -} +use super::util::{absorb_cyclefold_r1cs, absorb_primary_commitment, absorb_primary_r1cs}; /// A SNARK that holds the proof of a step of an incremental computation of the primary circuit /// in the CycleFold folding scheme. diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index 20992c493..c595902d6 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -6,7 +6,6 @@ use crate::{ alloc_num_equals, alloc_scalar_as_base, alloc_zero, conditionally_select_vec, le_bits_to_num, AllocatedRelaxedR1CSInstance, }, - r1cs::{R1CSInstance, RelaxedR1CSInstance}, traits::{ circuit::StepCircuit, commitment::CommitmentTrait, Engine, ROCircuitTrait, ROConstantsCircuit, }, @@ -19,7 +18,10 @@ use bellpepper_core::{boolean::AllocatedBit, ConstraintSystem, SynthesisError}; use ff::Field; use serde::{Deserialize, Serialize}; -use super::gadgets::{emulated, AllocatedCycleFoldData}; +use super::{ + gadgets::{emulated, AllocatedCycleFoldData}, + util::FoldingData, +}; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Abomonation)] pub struct AugmentedCircuitParams { @@ -36,20 +38,6 @@ impl AugmentedCircuitParams { } } -#[derive(Debug, Serialize, Deserialize)] -#[serde(bound = "")] -pub(crate) struct FoldingData { - pub U: RelaxedR1CSInstance, - pub u: R1CSInstance, - pub T: Commitment, -} - -impl FoldingData { - pub fn new(U: RelaxedR1CSInstance, u: R1CSInstance, T: Commitment) -> Self { - Self { U, u, T } - } -} - #[derive(Debug, Serialize, Deserialize)] #[serde(bound = "")] pub struct AugmentedCircuitInputs @@ -254,6 +242,8 @@ where self.params.n_limbs, )?; + // In the first folding step return the default relaxed instances for both the CycleFold and + // primary running accumulators Ok((U_c_default, U_p_default)) } @@ -279,6 +269,8 @@ where SynthesisError, > { // Follows the outline written down here https://hackmd.io/@lurk-lab/HybHrnNFT + + // Calculate the hash of the non-deterministic advice for the primary circuit let mut ro_p = E1::ROCircuit::new( self.ro_consts.clone(), 2 + 2 * arity + 2 * NUM_FE_IN_EMULATED_POINT + 3, @@ -299,12 +291,14 @@ where let hash_bits_p = ro_p.squeeze(cs.namespace(|| "primary hash bits"), NUM_HASH_BITS)?; let hash_p = le_bits_to_num(cs.namespace(|| "primary hash"), &hash_bits_p)?; + // check the hash matches the public IO from the last primary instance let check_primary = alloc_num_equals( cs.namespace(|| "u.X[0] = H(params, i, z0, zi, U_p)"), &data_p.u_x0, &hash_p, )?; + // Calculate the hash of the non-dterministic advice for the secondary circuit let mut ro_c = E1::ROCircuit::new( self.ro_consts.clone(), 1 + 1 + 3 + 3 + 1 + NIO_CYCLE_FOLD * BN_N_LIMBS, // pp + i + W + E + u + X @@ -318,6 +312,7 @@ where let hash_c_bits = ro_c.squeeze(cs.namespace(|| "cyclefold hash bits"), NUM_HASH_BITS)?; let hash_c = le_bits_to_num(cs.namespace(|| "cyclefold hash"), &hash_c_bits)?; + // check the hash matches the public IO from the last primary instance let check_cyclefold = alloc_num_equals( cs.namespace(|| "u.X[1] = H(params, U_c)"), &data_p.u_x1, @@ -363,6 +358,7 @@ where let h_c_1_bits = ro_c_1.squeeze(cs.namespace(|| "cyclefold_1 hash bits"), NUM_HASH_BITS)?; let h_c_1 = le_bits_to_num(cs.namespace(|| "cyclefold_1 hash"), &h_c_1_bits)?; + // Check the intermediate-calculated running instance matches the non-deterministic advice provided to the prover let check_cyclefold_int = alloc_num_equals(cs.namespace(|| "h_int = h_c_1"), &h_c_int, &h_c_1)?; let checks_pass = AllocatedBit::and( @@ -371,6 +367,7 @@ where &check_cyclefold_int, )?; + // calculate the folded CycleFold accumulator let U_c = data_c_2.apply_fold( cs.namespace(|| "fold u_c_2 into U_c_1"), pp_digest, @@ -379,6 +376,7 @@ where self.params.n_limbs, )?; + // calculate the folded primary circuit accumulator let U_p = data_p.U.fold_with_r1cs( cs.namespace(|| "fold u_p into U_p"), pp_digest, @@ -401,6 +399,7 @@ where // Circuit is documented here: https://hackmd.io/SBvAur_2RQmaduDi7gYbhw let arity = self.step_circuit.arity(); + // Allocate the witness let (pp_digest, i, z_0, z_i, data_p, data_c_1, data_c_2, E_new, W_new) = self.alloc_witness(cs.namespace(|| "alloc_witness"), arity)?; @@ -435,12 +434,14 @@ where |lc| lc, ); + // select the new running primary instance let Unew_p = Unew_p_base.conditionally_select( cs.namespace(|| "compute Unew_p"), &Unew_p_non_base, &Boolean::from(is_base_case.clone()), )?; + // select the new running CycleFold instance let Unew_c = Unew_c_base.conditionally_select( cs.namespace(|| "compute Unew_c"), &Unew_c_non_base, @@ -476,9 +477,11 @@ where )); } + // Calculate the first component of the public IO as the hash of the calculated primary running + // instance let mut ro_p = E1::ROCircuit::new( self.ro_consts.clone(), - 2 + 2 * arity + 2 * NUM_FE_IN_EMULATED_POINT + 3, + 2 + 2 * arity + (2 * NUM_FE_IN_EMULATED_POINT + 3), // pp + i + z_0 + z_next + (U_p) ); ro_p.absorb(&pp_digest); ro_p.absorb(&i_new); @@ -492,10 +495,12 @@ where let hash_p_bits = ro_p.squeeze(cs.namespace(|| "hash_p_bits"), NUM_HASH_BITS)?; let hash_p = le_bits_to_num(cs.namespace(|| "hash_p"), &hash_p_bits)?; + // Calculate the second component of the public IO as the hash of the calculated CycleFold running + // instance let mut ro_c = E1::ROCircuit::new( self.ro_consts, - 1 + 1 + 3 + 3 + 1 + NIO_CYCLE_FOLD * BN_N_LIMBS, - ); // pp + i + W + E + u + X + 1 + 1 + 3 + 3 + 1 + NIO_CYCLE_FOLD * BN_N_LIMBS, // pp + i + W + E + u + X + ); ro_c.absorb(&pp_digest); ro_c.absorb(&i_new); Unew_c.absorb_in_ro(cs.namespace(|| "absorb Unew_c"), &mut ro_c)?; diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 5f5a34002..229126ebc 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -1,5 +1,5 @@ -//! This module defines the Cyclefold `RecursiveSNARK` type -//! +//! This module defines the Cyclefold `RecursiveSNARK` type with its `new`, `prove_step`, and +//! `verify` methods. use crate::{ bellpepper::{ @@ -27,8 +27,9 @@ use crate::{ }; use super::{ - nifs::{absorb_primary_commitment, CycleFoldNIFS, PrimaryNIFS}, - nova_circuit::{AugmentedCircuit, AugmentedCircuitInputs, AugmentedCircuitParams, FoldingData}, + nifs::{CycleFoldNIFS, PrimaryNIFS}, + nova_circuit::{AugmentedCircuit, AugmentedCircuitInputs, AugmentedCircuitParams}, + util::{absorb_primary_relaxed_r1cs, FoldingData}, }; use abomonation::Abomonation; @@ -428,6 +429,7 @@ where return Err(NovaError::ProofVerifyError); } + // Calculate the hashes of the primary running instance and cyclefold running instance let (hash_primary, hash_cyclefold) = { let mut hasher = as Engine>::RO::new( pp.ro_consts_primary.clone(), @@ -456,12 +458,14 @@ where (hash_primary, hash_cyclefold) }; + // Verify the hashes equal the public IO for the final primary instance if scalar_as_base::>(hash_primary) != self.l_u_primary.X[0] || scalar_as_base::>(hash_cyclefold) != self.l_u_primary.X[1] { return Err(NovaError::ProofVerifyError); } + // Verify the satisfiability of running relaxed instances, and the final primary instance. let (res_r_primary, (res_l_primary, res_r_cyclefold)) = rayon::join( || { pp.circuit_shape_primary.r1cs_shape.is_sat_relaxed( @@ -498,19 +502,6 @@ where } } -fn absorb_primary_relaxed_r1cs(U: &RelaxedR1CSInstance, ro: &mut E2::RO) -where - E1: Engine::Scalar>, - E2: Engine::Scalar>, -{ - absorb_primary_commitment::(&U.comm_W, ro); - absorb_primary_commitment::(&U.comm_E, ro); - ro.absorb(U.u); - for e in &U.X { - ro.absorb(*e); - } -} - #[cfg(test)] mod test { use std::marker::PhantomData; diff --git a/src/cyclefold/util.rs b/src/cyclefold/util.rs new file mode 100644 index 000000000..34e8f89d7 --- /dev/null +++ b/src/cyclefold/util.rs @@ -0,0 +1,89 @@ +//! This module defines some useful utilities for RO absorbing, and the Folding data used in the +//! CycleFold folding scheme. + +use crate::{ + constants::{BN_LIMB_WIDTH, BN_N_LIMBS}, + gadgets::{f_to_nat, nat_to_limbs, scalar_as_base}, + r1cs::{R1CSInstance, RelaxedR1CSInstance}, + traits::{commitment::CommitmentTrait, AbsorbInROTrait, Engine, ROTrait}, + Commitment, +}; + +use ff::Field; +use serde::{Deserialize, Serialize}; + +/// Absorb a commitment over engine `E1` into an RO over engine `E2` by absorbing the limbs +pub(super) fn absorb_primary_commitment( + comm: &impl CommitmentTrait, + ro: &mut impl ROTrait, +) where + E1: Engine::Scalar>, + E2: Engine::Scalar>, +{ + let (x, y, is_infinity) = comm.to_coordinates(); + + let x_limbs = nat_to_limbs(&f_to_nat(&x), BN_LIMB_WIDTH, BN_N_LIMBS).unwrap(); + let y_limbs = nat_to_limbs(&f_to_nat(&y), BN_LIMB_WIDTH, BN_N_LIMBS).unwrap(); + + for limb in x_limbs { + ro.absorb(scalar_as_base::(limb)); + } + for limb in y_limbs { + ro.absorb(scalar_as_base::(limb)); + } + if is_infinity { + ro.absorb(::Scalar::ONE); + } else { + ro.absorb(::Scalar::ZERO); + } +} + +pub(super) fn absorb_primary_r1cs( + u: &R1CSInstance, + ro: &mut impl ROTrait, +) where + E1: Engine::Scalar>, + E2: Engine::Scalar>, +{ + absorb_primary_commitment::(&u.comm_W, ro); + for x in &u.X { + ro.absorb(*x); + } +} + +pub(super) fn absorb_cyclefold_r1cs(u: &R1CSInstance, ro: &mut E::RO) { + u.comm_W.absorb_in_ro(ro); + u.X.iter().for_each(|x| { + let limbs: Vec = nat_to_limbs(&f_to_nat(x), BN_LIMB_WIDTH, BN_N_LIMBS).unwrap(); + limbs + .into_iter() + .for_each(|limb| ro.absorb(scalar_as_base::(limb))); + }); +} + +pub(super) fn absorb_primary_relaxed_r1cs(U: &RelaxedR1CSInstance, ro: &mut E2::RO) +where + E1: Engine::Scalar>, + E2: Engine::Scalar>, +{ + absorb_primary_commitment::(&U.comm_W, ro); + absorb_primary_commitment::(&U.comm_E, ro); + ro.absorb(U.u); + for e in &U.X { + ro.absorb(*e); + } +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(bound = "")] +pub(super) struct FoldingData { + pub U: RelaxedR1CSInstance, + pub u: R1CSInstance, + pub T: Commitment, +} + +impl FoldingData { + pub fn new(U: RelaxedR1CSInstance, u: R1CSInstance, T: Commitment) -> Self { + Self { U, u, T } + } +} From f912350c9013bb46cef5effaa193615f88bc89c2 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Thu, 7 Mar 2024 17:25:36 -0500 Subject: [PATCH 69/77] fix: fix rebase --- src/cyclefold/circuit.rs | 3 ++- src/cyclefold/gadgets.rs | 6 +++--- src/cyclefold/nova_circuit.rs | 8 +++++--- src/gadgets/mod.rs | 3 ++- src/gadgets/utils.rs | 27 +++++++++++++++++++++++++++ 5 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/cyclefold/circuit.rs b/src/cyclefold/circuit.rs index 122b5b1e8..d5ff2ae85 100644 --- a/src/cyclefold/circuit.rs +++ b/src/cyclefold/circuit.rs @@ -9,10 +9,11 @@ use neptune::{circuit2::poseidon_hash_allocated, poseidon::PoseidonConstants}; use crate::{ constants::NUM_CHALLENGE_BITS, - gadgets::{alloc_zero, conditionally_select, le_bits_to_num, AllocatedPoint}, + gadgets::{alloc_zero, le_bits_to_num, AllocatedPoint}, traits::{commitment::CommitmentTrait, Engine}, Commitment, }; +use bellpepper::gadgets::boolean_utils::conditionally_select; /// A structure containing the CycleFold circuit inputs and implementing the synthesize function pub struct CyclefoldCircuit { diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index ab0577b37..b1b8c1880 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -220,7 +220,7 @@ impl AllocatedCycleFoldData { } pub mod emulated { - use bellpepper::gadgets::Assignment; + use bellpepper::gadgets::{boolean_utils::conditionally_select, Assignment}; use bellpepper_core::{ boolean::{AllocatedBit, Boolean}, num::AllocatedNum, @@ -230,8 +230,8 @@ pub mod emulated { use crate::{ constants::{NUM_CHALLENGE_BITS, NUM_FE_IN_EMULATED_POINT}, gadgets::{ - alloc_zero, conditionally_select, conditionally_select_allocated_bit, - conditionally_select_bignat, f_to_nat, le_bits_to_num, BigNat, + alloc_zero, conditionally_select_allocated_bit, conditionally_select_bignat, f_to_nat, + le_bits_to_num, BigNat, }, traits::{commitment::CommitmentTrait, Engine, Group, ROCircuitTrait, ROConstantsCircuit}, RelaxedR1CSInstance, diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index c595902d6..a917420d7 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -3,7 +3,7 @@ use crate::{ constants::{BN_N_LIMBS, NIO_CYCLE_FOLD, NUM_FE_IN_EMULATED_POINT, NUM_HASH_BITS}, gadgets::{ - alloc_num_equals, alloc_scalar_as_base, alloc_zero, conditionally_select_vec, le_bits_to_num, + alloc_num_equals, alloc_scalar_as_base, alloc_zero, le_bits_to_num, AllocatedRelaxedR1CSInstance, }, traits::{ @@ -13,7 +13,9 @@ use crate::{ }; use abomonation_derive::Abomonation; -use bellpepper::gadgets::{boolean::Boolean, num::AllocatedNum, Assignment}; +use bellpepper::gadgets::{ + boolean::Boolean, boolean_utils::conditionally_select_slice, num::AllocatedNum, Assignment, +}; use bellpepper_core::{boolean::AllocatedBit, ConstraintSystem, SynthesisError}; use ff::Field; use serde::{Deserialize, Serialize}; @@ -460,7 +462,7 @@ where ); // Compute z_{i+1} - let z_input = conditionally_select_vec( + let z_input = conditionally_select_slice( cs.namespace(|| "select input to F"), &z_0, &z_i, diff --git a/src/gadgets/mod.rs b/src/gadgets/mod.rs index e5f31dae0..5841f6ec2 100644 --- a/src/gadgets/mod.rs +++ b/src/gadgets/mod.rs @@ -15,5 +15,6 @@ mod utils; #[cfg(test)] pub(crate) use utils::alloc_one; pub(crate) use utils::{ - alloc_num_equals, alloc_scalar_as_base, alloc_zero, le_bits_to_num, scalar_as_base, + alloc_bignat_constant, alloc_num_equals, alloc_scalar_as_base, alloc_zero, + conditionally_select_allocated_bit, conditionally_select_bignat, le_bits_to_num, scalar_as_base, }; diff --git a/src/gadgets/utils.rs b/src/gadgets/utils.rs index 4474c4ece..411dfc348 100644 --- a/src/gadgets/utils.rs +++ b/src/gadgets/utils.rs @@ -185,6 +185,33 @@ pub fn alloc_num_equals>( Ok(r) } +// TODO: Figure out if this can be done better +pub fn conditionally_select_allocated_bit>( + mut cs: CS, + a: &AllocatedBit, + b: &AllocatedBit, + condition: &Boolean, +) -> Result { + let c = AllocatedBit::alloc( + cs.namespace(|| "conditionally select result"), + if condition.get_value().unwrap_or(false) { + a.get_value() + } else { + b.get_value() + }, + )?; + + // a * condition + b*(1-condition) = c -> + // a * condition - b*condition = c - b + cs.enforce( + || "conditional select constraint", + |lc| lc + a.get_variable() - b.get_variable(), + |_| condition.lc(CS::one(), F::ONE), + |lc| lc + c.get_variable() - b.get_variable(), + ); + + Ok(c) +} /// If condition return a otherwise b where a and b are `BigNats` pub fn conditionally_select_bignat>( mut cs: CS, From 0232b8da2802330da4b85d001a60af0c47323430 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Thu, 7 Mar 2024 17:59:41 -0500 Subject: [PATCH 70/77] chore: fix clippy lints --- src/cyclefold/circuit.rs | 18 ++++++------------ src/cyclefold/nifs.rs | 1 + 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/cyclefold/circuit.rs b/src/cyclefold/circuit.rs index d5ff2ae85..a554f9b05 100644 --- a/src/cyclefold/circuit.rs +++ b/src/cyclefold/circuit.rs @@ -98,9 +98,9 @@ impl CyclefoldCircuit { let C_final = C_1.add(cs.namespace(|| "C_1 + r * C_2"), &r_C_2)?; - self.inputize_point(C_1, cs.namespace(|| "inputize C_1"))?; - self.inputize_point(C_2, cs.namespace(|| "inputize C_2"))?; - self.inputize_point(C_final, cs.namespace(|| "inputize C_final"))?; + self.inputize_point(&C_1, cs.namespace(|| "inputize C_1"))?; + self.inputize_point(&C_2, cs.namespace(|| "inputize C_2"))?; + self.inputize_point(&C_final, cs.namespace(|| "inputize C_final"))?; let scalar = le_bits_to_num(cs.namespace(|| "get scalar"), &r)?; @@ -112,7 +112,7 @@ impl CyclefoldCircuit { // Represent the point in the public IO as its 2-ary Poseidon hash fn inputize_point( &self, - point: AllocatedPoint, + point: &AllocatedPoint, mut cs: CS, ) -> Result<(), SynthesisError> where @@ -131,13 +131,7 @@ impl CyclefoldCircuit { let is_infinity_bit = AllocatedBit::alloc( cs.namespace(|| "is_infinity"), - Some( - if is_infinity.get_value().unwrap_or(E::Base::ONE) == E::Base::ONE { - true - } else { - false - }, - ), + Some(is_infinity.get_value().unwrap_or(E::Base::ONE) == E::Base::ONE), )?; cs.enforce( @@ -267,7 +261,7 @@ mod tests { return E::Scalar::ZERO; } - let mut hasher = Poseidon::new_with_preimage(&vec![x, y], &circuit.poseidon_constants); + let mut hasher = Poseidon::new_with_preimage(&[x, y], &circuit.poseidon_constants); hasher.hash() }; diff --git a/src/cyclefold/nifs.rs b/src/cyclefold/nifs.rs index 3df6d652c..1fd66a9f8 100644 --- a/src/cyclefold/nifs.rs +++ b/src/cyclefold/nifs.rs @@ -91,6 +91,7 @@ where /// The difference of this folding scheme from the Nova NIFS in `src/nifs.rs` is that this folding /// prover and verifier must fold in the `RelaxedR1CSInstance` accumulator because the optimization /// in the +#[derive(Debug)] pub struct CycleFoldNIFS { pub(crate) comm_T: CompressedCommitment, } From a123ba39f65f828157d708f3779bf0a3b103dee1 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Fri, 8 Mar 2024 22:40:30 +0000 Subject: [PATCH 71/77] fix: constrain `AllocatedEmulPoint::default` --- src/cyclefold/gadgets.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index b1b8c1880..318978946 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -230,8 +230,8 @@ pub mod emulated { use crate::{ constants::{NUM_CHALLENGE_BITS, NUM_FE_IN_EMULATED_POINT}, gadgets::{ - alloc_zero, conditionally_select_allocated_bit, conditionally_select_bignat, f_to_nat, - le_bits_to_num, BigNat, + alloc_bignat_constant, alloc_zero, conditionally_select_allocated_bit, + conditionally_select_bignat, f_to_nat, le_bits_to_num, BigNat, }, traits::{commitment::CommitmentTrait, Engine, Group, ROCircuitTrait, ROConstantsCircuit}, RelaxedR1CSInstance, @@ -392,21 +392,26 @@ pub mod emulated { limb_width: usize, n_limbs: usize, ) -> Result { - let x = BigNat::alloc_from_nat( + let x = alloc_bignat_constant( cs.namespace(|| "allocate x_default = 0"), - || Ok(f_to_nat(&G::Scalar::ZERO)), + &f_to_nat(&G::Base::ZERO), limb_width, n_limbs, )?; - - let y = BigNat::alloc_from_nat( + let y = alloc_bignat_constant( cs.namespace(|| "allocate y_default = 0"), - || Ok(f_to_nat(&G::Scalar::ZERO)), + &f_to_nat(&G::Base::ZERO), limb_width, n_limbs, )?; let is_infinity = AllocatedBit::alloc(cs.namespace(|| "allocate is_infinity"), Some(true))?; + cs.enforce( + || "is_infinity = 1", + |lc| lc, + |lc| lc, + |lc| lc + CS::one() - is_infinity.get_variable(), + ); Ok(Self { x, y, is_infinity }) } From 8f0f4252fb12d7f84d4aa052a2520f7936b0dd0d Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Fri, 8 Mar 2024 22:40:52 +0000 Subject: [PATCH 72/77] tidy: use `AllocatedNum::add` --- src/cyclefold/gadgets.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/cyclefold/gadgets.rs b/src/cyclefold/gadgets.rs index 318978946..fe20163ce 100644 --- a/src/cyclefold/gadgets.rs +++ b/src/cyclefold/gadgets.rs @@ -511,16 +511,7 @@ pub mod emulated { let r_bits = ro.squeeze(cs.namespace(|| "r bits"), NUM_CHALLENGE_BITS)?; let r = le_bits_to_num(cs.namespace(|| "r"), &r_bits)?; - let u_fold = AllocatedNum::alloc(cs.namespace(|| "u"), || { - Ok(*self.u.get_value().get()? + r.get_value().get()?) - })?; - cs.enforce( - || "u_fold = u + r", - |lc| lc, - |lc| lc, - |lc| lc + u_fold.get_variable() - self.u.get_variable() - r.get_variable(), - ); - + let u_fold = self.u.add(cs.namespace(|| "u_fold = u + r"), &r)?; let x0_fold = AllocatedNum::alloc(cs.namespace(|| "x0"), || { Ok(*self.x0.get_value().get()? + *r.get_value().get()? * *u_x0.get_value().get()?) })?; From 67f9a925df59e42057ae6f4027b1901a1870c283 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Fri, 8 Mar 2024 22:41:45 +0000 Subject: [PATCH 73/77] chore: fix rebase errors in `src/gadgets` --- src/gadgets/nonnative/bignat.rs | 47 +++++++++------------------------ src/gadgets/nonnative/util.rs | 38 -------------------------- src/gadgets/utils.rs | 16 ----------- 3 files changed, 12 insertions(+), 89 deletions(-) diff --git a/src/gadgets/nonnative/bignat.rs b/src/gadgets/nonnative/bignat.rs index e4e48a048..2b9d12cc6 100644 --- a/src/gadgets/nonnative/bignat.rs +++ b/src/gadgets/nonnative/bignat.rs @@ -4,10 +4,7 @@ use super::{ }, OptionExt, }; -use bellpepper_core::{ - boolean::AllocatedBit, /*num::AllocatedNum,*/ ConstraintSystem, LinearCombination, - SynthesisError, -}; +use bellpepper_core::{boolean::AllocatedBit, ConstraintSystem, LinearCombination, SynthesisError}; use ff::PrimeField; use itertools::Itertools as _; use num_bigint::BigInt; @@ -345,12 +342,10 @@ impl BigNat { } /// Constrain `self` to be equal to `other`, after carrying both. - /// The constraint is always satisfied if `always_equal` is true. pub fn equal_when_carried>( &self, mut cs: CS, other: &Self, - always_equal: &AllocatedBit, ) -> Result<(), SynthesisError> { self.enforce_limb_width_agreement(other, "equal_when_carried")?; @@ -381,7 +376,8 @@ impl BigNat { cs.enforce( || format!("carry {i}"), - |lc| lc + CS::one() - always_equal.get_variable(), + |lc| lc, + |lc| lc, |lc| { lc + &carry_in.num + &self.limbs[i] - &other.limbs[i] + (nat_to_f(max_word).unwrap(), CS::one()) @@ -391,7 +387,6 @@ impl BigNat { CS::one(), ) }, - |lc| lc, ); accumulated_extra /= &target_base; @@ -401,9 +396,9 @@ impl BigNat { } else { cs.enforce( || format!("carry {i} is out"), - |lc| lc + CS::one() - always_equal.get_variable(), - |lc| lc + &carry.num - (nat_to_f(&accumulated_extra).unwrap(), CS::one()), |lc| lc, + |lc| lc, + |lc| lc + &carry.num - (nat_to_f(&accumulated_extra).unwrap(), CS::one()), ); } carry_in = carry; @@ -412,17 +407,17 @@ impl BigNat { for (i, zero_limb) in self.limbs.iter().enumerate().skip(n) { cs.enforce( || format!("zero self {i}"), - |lc| lc + CS::one() - always_equal.get_variable(), - |lc| lc + zero_limb, |lc| lc, + |lc| lc, + |lc| lc + zero_limb, ); } for (i, zero_limb) in other.limbs.iter().enumerate().skip(n) { cs.enforce( || format!("zero other {i}"), - |lc| lc + CS::one() - always_equal.get_variable(), - |lc| lc + zero_limb, |lc| lc, + |lc| lc, + |lc| lc + zero_limb, ); } Ok(()) @@ -435,7 +430,6 @@ impl BigNat { &self, mut cs: CS, other: &Self, - always_equal: &AllocatedBit, ) -> Result<(), SynthesisError> { self.enforce_limb_width_agreement(other, "equal_when_carried_regroup")?; let max_word = max(&self.params.max_word, &other.params.max_word); @@ -449,7 +443,7 @@ impl BigNat { let self_grouped = self.group_limbs(limbs_per_group); let other_grouped = other.group_limbs(limbs_per_group); - self_grouped.equal_when_carried(cs.namespace(|| "grouped"), &other_grouped, always_equal) + self_grouped.equal_when_carried(cs.namespace(|| "grouped"), &other_grouped) } pub fn add(&self, other: &Self) -> Result { @@ -563,16 +557,7 @@ impl BigNat { let left_int = Self::from_poly(left, limb_width, left_max_word); let right_int = Self::from_poly(right, limb_width, right_max_word); - - let always_equal = AllocatedBit::alloc(cs.namespace(|| "always_equal = false"), Some(false))?; - cs.enforce( - || "enforce always_equal", - |lc| lc, - |lc| lc, - |lc| lc + always_equal.get_variable(), - ); - - left_int.equal_when_carried_regroup(cs.namespace(|| "carry"), &right_int, &always_equal)?; + left_int.equal_when_carried_regroup(cs.namespace(|| "carry"), &right_int)?; Ok((quotient, remainder)) } @@ -617,15 +602,7 @@ impl BigNat { }; let right_int = Self::from_poly(right, limb_width, right_max_word); - let always_equal = AllocatedBit::alloc(cs.namespace(|| "always_equal = false"), Some(false))?; - cs.enforce( - || "enforce always_equal", - |lc| lc, - |lc| lc, - |lc| lc + always_equal.get_variable(), - ); - - self.equal_when_carried_regroup(cs.namespace(|| "carry"), &right_int, &always_equal)?; + self.equal_when_carried_regroup(cs.namespace(|| "carry"), &right_int)?; Ok(remainder) } diff --git a/src/gadgets/nonnative/util.rs b/src/gadgets/nonnative/util.rs index 4a59c0cc5..c63108f07 100644 --- a/src/gadgets/nonnative/util.rs +++ b/src/gadgets/nonnative/util.rs @@ -155,44 +155,6 @@ impl Num { Ok(()) } - // TOOD: Can probably delete this - // /// Computes the natural number represented by an array of bits. - // pub fn from_bits>( - // mut cs: CS, - // bits: &Bitvector, - // ) -> Result { - // let allocations = bits.allocations.clone(); - // let mut f = Scalar::ONE; - // let num = allocations - // .iter() - // .fold(LinearCombination::zero(), |lc, bit| { - // let l = lc + (f, &bit.bit); - // f = f.double(); - // l - // }); - // let mut f = Scalar::ONE; - // let value = bits.values.as_ref().map(|vs| { - // vs.iter().fold(Scalar::ZERO, |mut acc, b| { - // if *b { - // acc += f; - // } - // f = f.double(); - // acc - // }) - // }); - - // let val = value.grab()?; - - // cs.enforce( - // || "field element equals bits", - // |lc| lc + &num, - // |lc| lc + CS::one(), - // |lc| lc + (*val, CS::one()), - // ); - - // Ok(Self::new(value, num)) - // } - /// Checks if the natural number equals an array of bits. pub fn is_equal>(&self, mut cs: CS, other: &Bitvector) { let mut f = Scalar::ONE; diff --git a/src/gadgets/utils.rs b/src/gadgets/utils.rs index 411dfc348..fcae1350e 100644 --- a/src/gadgets/utils.rs +++ b/src/gadgets/utils.rs @@ -68,22 +68,6 @@ pub fn alloc_one>(mut cs: CS) -> Allocate one } -// /// Allocate a constant and constrain it -// pub fn alloc_constant>( -// val: F, -// mut cs: CS, -// ) -> AllocatedNum { -// let allocated_val = AllocatedNum::alloc_infallible(cs.namespace(|| "alloc"), || val); -// cs.enforce( -// || "check val is valid", -// |lc| lc, -// |lc| lc, -// |lc| lc + allocated_val.get_variable() - (val, CS::one()), -// ); - -// allocated_val -// } - /// Allocate a scalar as a base. Only to be used is the scalar fits in base! pub fn alloc_scalar_as_base( mut cs: CS, From 5d48fc64dbcbcebc2f428bfa180d88945e91123a Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Fri, 8 Mar 2024 17:50:17 -0500 Subject: [PATCH 74/77] fix: fix import --- src/gadgets/nonnative/bignat.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gadgets/nonnative/bignat.rs b/src/gadgets/nonnative/bignat.rs index 2b9d12cc6..e1bc32df4 100644 --- a/src/gadgets/nonnative/bignat.rs +++ b/src/gadgets/nonnative/bignat.rs @@ -4,7 +4,7 @@ use super::{ }, OptionExt, }; -use bellpepper_core::{boolean::AllocatedBit, ConstraintSystem, LinearCombination, SynthesisError}; +use bellpepper_core::{ConstraintSystem, LinearCombination, SynthesisError}; use ff::PrimeField; use itertools::Itertools as _; use num_bigint::BigInt; From b83d57cc7cbde307d330003264542b338e79ca91 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Fri, 8 Mar 2024 17:50:33 -0500 Subject: [PATCH 75/77] chore: update expecttests --- src/circuit.rs | 12 ++++++------ src/cyclefold/nova_circuit.rs | 6 +++--- src/lib.rs | 12 ++++++------ src/supernova/circuit.rs | 12 ++++++------ src/supernova/test.rs | 10 +++++----- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/circuit.rs b/src/circuit.rs index e49ffcbae..b5f4473b0 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -459,8 +459,8 @@ mod tests { ¶ms2, ro_consts1, ro_consts2, - &expect!["9825"], - &expect!["10357"], + &expect!["9817"], + &expect!["10349"], ); } @@ -476,8 +476,8 @@ mod tests { ¶ms2, ro_consts1, ro_consts2, - &expect!["9993"], - &expect!["10546"], + &expect!["9985"], + &expect!["10538"], ); } @@ -493,8 +493,8 @@ mod tests { ¶ms2, ro_consts1, ro_consts2, - &expect!["10272"], - &expect!["10969"], + &expect!["10264"], + &expect!["10961"], ); } } diff --git a/src/cyclefold/nova_circuit.rs b/src/cyclefold/nova_circuit.rs index a917420d7..9ffd259be 100644 --- a/src/cyclefold/nova_circuit.rs +++ b/src/cyclefold/nova_circuit.rs @@ -560,8 +560,8 @@ mod test { #[test] fn test_augmented_circuit_size() { - test_augmented_circuit_size_with::(&expect!["33312"], &expect!["33339"]); - test_augmented_circuit_size_with::(&expect!["35148"], &expect!["35175"]); - test_augmented_circuit_size_with::(&expect!["33879"], &expect!["33906"]); + test_augmented_circuit_size_with::(&expect!["33289"], &expect!["33323"]); + test_augmented_circuit_size_with::(&expect!["35125"], &expect!["35159"]); + test_augmented_circuit_size_with::(&expect!["33856"], &expect!["33890"]); } } diff --git a/src/lib.rs b/src/lib.rs index 80aa267bd..6907770c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1121,36 +1121,36 @@ mod tests { test_pp_digest_with::, EE<_>>( &TrivialCircuit::default(), &TrivialCircuit::default(), - &expect!["b948575a34e25b1f306a0fef795d4a809d536828281465f4ccbf990f126c2f03"], + &expect!["e5a6a85b77f3fb958b69722a5a21bf656fd21a6b5a012708a4b086b6be6d2b03"], ); test_pp_digest_with::, EE<_>>( &CubicCircuit::default(), &TrivialCircuit::default(), - &expect!["a3ad9ddd33c45764340638b1d33988efd212de2df74551d37a3e8cb6802fe001"], + &expect!["ec707a8b822baebca114b6e61b238374f9ed358c542dd37ee73febb47832cd01"], ); test_pp_digest_with::, EE<_>>( &TrivialCircuit::default(), &TrivialCircuit::default(), - &expect!["9e2dac4031303a8e677c1d8aa05954019e3dc979e104fb332b3308cffdbe4600"], + &expect!["df52de22456157eb056003d4dc580a167ab8ce40a151c9944ea09a6fd0028600"], ); test_pp_digest_with::, EE<_>>( &CubicCircuit::default(), &TrivialCircuit::default(), - &expect!["8dea874c5cf2acffda1eba370193880f02e6bc0d95e3d1c2cce540060b606302"], + &expect!["b3ad0f4b734c5bd2ab9e83be8ee0cbaaa120e5cd0270b51cb9d7778a33f0b801"], ); test_pp_digest_with::, EE<_>>( &TrivialCircuit::default(), &TrivialCircuit::default(), - &expect!["6ec0ef50b2fc4af3ec40a5f62de8dafe6c84acda41e1a29cac0e18233e1be602"], + &expect!["e1feca53664212ee750da857c726b2a09bb30b2964f22ea85a19b58c9eaf5701"], ); test_pp_digest_with::, EE<_>>( &CubicCircuit::default(), &TrivialCircuit::default(), - &expect!["da34ff488965a4aebab886745189729cbb6d4c23417fa6763943a7ce57324f03"], + &expect!["4ad6b10b6fd24fecba49f08d35bc874a6da9c77735bc0bcf4b78b1914a97e602"], ); } diff --git a/src/supernova/circuit.rs b/src/supernova/circuit.rs index 1b8a31155..b0cff4743 100644 --- a/src/supernova/circuit.rs +++ b/src/supernova/circuit.rs @@ -836,8 +836,8 @@ mod tests { ¶ms2, ro_consts1, ro_consts2, - &expect!["9844"], - &expect!["10392"], + &expect!["9836"], + &expect!["10384"], 1, ); // TODO: extend to num_augmented_circuits >= 2 @@ -855,8 +855,8 @@ mod tests { ¶ms2, ro_consts1, ro_consts2, - &expect!["10012"], - &expect!["10581"], + &expect!["10004"], + &expect!["10573"], 1, ); // TODO: extend to num_augmented_circuits >= 2 @@ -874,8 +874,8 @@ mod tests { ¶ms2, ro_consts1, ro_consts2, - &expect!["10291"], - &expect!["11004"], + &expect!["10283"], + &expect!["10996"], 1, ); // TODO: extend to num_augmented_circuits >= 2 diff --git a/src/supernova/test.rs b/src/supernova/test.rs index ce80efcc7..51c89c07e 100644 --- a/src/supernova/test.rs +++ b/src/supernova/test.rs @@ -564,8 +564,8 @@ fn test_recursive_circuit() { ¶ms2, ro_consts1, ro_consts2, - &expect!["9844"], - &expect!["12025"], + &expect!["9836"], + &expect!["12017"], ); } @@ -606,7 +606,7 @@ fn test_supernova_pp_digest() { test_pp_digest_with::( &test_rom, - &expect!["ca24639b674ad70ed5ae5f2d2048e01990bc1974bf9ddf6240e3f885d55a8c03"], + &expect!["698b3592bf271c0cc53245aee71ec3f8e0d16486b3efc73be290a0af27605b01"], ); let rom = vec![ @@ -617,7 +617,7 @@ fn test_supernova_pp_digest() { test_pp_digest_with::( &test_rom_grumpkin, - &expect!["6ca2ed5fd9b755e97559024eadfa5c2e48d8cb231c35bfaf9c377af6bebac203"], + &expect!["30418e576c11dd698054a6cc69d1b1e43ddf0f562abfb50b777147afad741a01"], ); let rom = vec![ @@ -628,7 +628,7 @@ fn test_supernova_pp_digest() { test_pp_digest_with::( &test_rom_secp, - &expect!["5e86ba0fd6f343752ebb56bfaa4fe3d52dc5bd7e6fffb2fd130ec43ce4ab1301"], + &expect!["c94ee4e2870e34d6d057aa66157f8315879ecf2692ab9d1e2567c5830bed1103"], ); } From 938877b15a3cef7cc5fd92a8ff6b6ebaa885bec5 Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Fri, 22 Mar 2024 08:11:17 -0400 Subject: [PATCH 76/77] tidy: add new constant --- src/constants.rs | 1 + src/gadgets/r1cs.rs | 4 ++-- src/nifs.rs | 7 +++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index 7ed79d2f0..2504cb4e5 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -4,6 +4,7 @@ pub(crate) const NUM_CHALLENGE_BITS: usize = 128; pub(crate) const BN_LIMB_WIDTH: usize = 64; pub(crate) const BN_N_LIMBS: usize = 4; pub(crate) const NUM_FE_WITHOUT_IO_FOR_CRHF: usize = 9 + NIO_NOVA_FOLD * BN_N_LIMBS; +pub(crate) const NUM_FE_WITHOUT_IO_FOR_NOVA_FOLD: usize = 7; pub(crate) const NUM_FE_FOR_RO: usize = 9; pub(crate) const NIO_NOVA_FOLD: usize = 2; pub(crate) const NUM_FE_IN_EMULATED_POINT: usize = 2 * BN_N_LIMBS + 1; diff --git a/src/gadgets/r1cs.rs b/src/gadgets/r1cs.rs index a19becd35..3314393f9 100644 --- a/src/gadgets/r1cs.rs +++ b/src/gadgets/r1cs.rs @@ -4,7 +4,7 @@ use super::nonnative::{ util::{f_to_nat, Num}, }; use crate::{ - constants::NUM_CHALLENGE_BITS, + constants::{NUM_CHALLENGE_BITS, NUM_FE_WITHOUT_IO_FOR_NOVA_FOLD}, gadgets::{ ecc::AllocatedPoint, utils::{ @@ -229,7 +229,7 @@ impl AllocatedRelaxedR1CSInstance { n_limbs: usize, ) -> Result { // Compute r: - let mut ro = E::ROCircuit::new(ro_consts, 7 + N); + let mut ro = E::ROCircuit::new(ro_consts, NUM_FE_WITHOUT_IO_FOR_NOVA_FOLD + N); ro.absorb(params); // running instance `U` does not need to absorbed since u.X[0] = Hash(params, U, i, z0, zi) diff --git a/src/nifs.rs b/src/nifs.rs index 0571409e0..fe6236cc1 100644 --- a/src/nifs.rs +++ b/src/nifs.rs @@ -2,7 +2,7 @@ #![allow(non_snake_case)] use crate::{ - constants::{NUM_CHALLENGE_BITS, NUM_FE_FOR_RO}, + constants::{NUM_CHALLENGE_BITS, NUM_FE_FOR_RO, NUM_FE_WITHOUT_IO_FOR_NOVA_FOLD}, errors::NovaError, r1cs::{ R1CSInstance, R1CSResult, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness, @@ -60,7 +60,10 @@ impl NIFS { } // initialize a new RO - let mut ro = E::RO::new(ro_consts.clone(), 7 + io_arity); + let mut ro = E::RO::new( + ro_consts.clone(), + NUM_FE_WITHOUT_IO_FOR_NOVA_FOLD + io_arity, + ); // append the digest of pp to the transcript ro.absorb(scalar_as_base::(*pp_digest)); From 497aef7dafa52adbe07858aff778d4756e232f6c Mon Sep 17 00:00:00 2001 From: Matej Penciak Date: Fri, 22 Mar 2024 08:11:26 -0400 Subject: [PATCH 77/77] tidy: Cyclefold -> CycleFold --- src/cyclefold/circuit.rs | 23 +++++++++++------------ src/cyclefold/snark.rs | 10 +++++----- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/cyclefold/circuit.rs b/src/cyclefold/circuit.rs index a554f9b05..46e3c4dbb 100644 --- a/src/cyclefold/circuit.rs +++ b/src/cyclefold/circuit.rs @@ -16,14 +16,14 @@ use crate::{ use bellpepper::gadgets::boolean_utils::conditionally_select; /// A structure containing the CycleFold circuit inputs and implementing the synthesize function -pub struct CyclefoldCircuit { +pub struct CycleFoldCircuit { commit_1: Option>, commit_2: Option>, scalar: Option<[bool; NUM_CHALLENGE_BITS]>, poseidon_constants: PoseidonConstants, } -impl Default for CyclefoldCircuit { +impl Default for CycleFoldCircuit { fn default() -> Self { let poseidon_constants = PoseidonConstants::new(); Self { @@ -34,7 +34,7 @@ impl Default for CyclefoldCircuit { } } } -impl CyclefoldCircuit { +impl CycleFoldCircuit { /// Create a new CycleFold circuit with the given inputs pub fn new( commit_1: Option>, @@ -181,7 +181,7 @@ mod tests { E1: CurveCycleEquipped, { // Instantiate the circuit with trivial inputs - let circuit: CyclefoldCircuit> = CyclefoldCircuit::default(); + let circuit: CycleFoldCircuit> = CycleFoldCircuit::default(); // Synthesize the R1CS shape let mut cs: ShapeCS = ShapeCS::new(); @@ -228,14 +228,13 @@ mod tests { let r_bits = r .to_le_bits() .into_iter() - .map(Some) - .collect::>>() - .map(|mut vec| { - vec.resize_with(128, || false); - vec.try_into().unwrap() - }); - - let circuit: CyclefoldCircuit> = CyclefoldCircuit::new(Some(C_1), Some(C_2), r_bits); + .take(128) + .collect::>() + .try_into() + .unwrap(); + + let circuit: CycleFoldCircuit> = + CycleFoldCircuit::new(Some(C_1), Some(C_2), Some(r_bits)); // Calculate the result out of circuit let native_result = C_1 + C_2 * r; diff --git a/src/cyclefold/snark.rs b/src/cyclefold/snark.rs index 229126ebc..930823333 100644 --- a/src/cyclefold/snark.rs +++ b/src/cyclefold/snark.rs @@ -11,7 +11,7 @@ use crate::{ BN_LIMB_WIDTH, BN_N_LIMBS, NIO_CYCLE_FOLD, NUM_CHALLENGE_BITS, NUM_FE_IN_EMULATED_POINT, NUM_HASH_BITS, }, - cyclefold::circuit::CyclefoldCircuit, + cyclefold::circuit::CycleFoldCircuit, errors::NovaError, gadgets::scalar_as_base, r1cs::{ @@ -98,7 +98,7 @@ where let ro_consts_cyclefold = ROConstants::>::default(); let mut cs: ShapeCS> = ShapeCS::new(); - let circuit_cyclefold: CyclefoldCircuit = CyclefoldCircuit::default(); + let circuit_cyclefold: CycleFoldCircuit = CycleFoldCircuit::default(); let _ = circuit_cyclefold.synthesize(&mut cs); let (r1cs_shape_cyclefold, ck_cyclefold) = cs.r1cs_shape_and_key(ck_hint_cyclefold); let circuit_shape_cyclefold = R1CSWithArity::new(r1cs_shape_cyclefold, 0); @@ -297,8 +297,8 @@ where pp.circuit_shape_cyclefold.r1cs_shape.num_vars, ); - let circuit_cyclefold_E: CyclefoldCircuit = - CyclefoldCircuit::new(Some(self.r_U_primary.comm_E), Some(comm_T), r_bools); + let circuit_cyclefold_E: CycleFoldCircuit = + CycleFoldCircuit::new(Some(self.r_U_primary.comm_E), Some(comm_T), r_bools); let _ = circuit_cyclefold_E.synthesize(&mut cs_cyclefold_E); @@ -325,7 +325,7 @@ where pp.circuit_shape_cyclefold.r1cs_shape.num_vars, ); - let circuit_cyclefold_W: CyclefoldCircuit = CyclefoldCircuit::new( + let circuit_cyclefold_W: CycleFoldCircuit = CycleFoldCircuit::new( Some(self.r_U_primary.comm_W), Some(self.l_u_primary.comm_W), r_bools,