diff --git a/.gitignore b/.gitignore index e2e3afdc..d3ba383d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,12 +2,12 @@ Cargo.lock # Circom generated files -frontends/src/circom/test_folder/*_js/ +experimental-frontends/src/circom/test_folder/*_js/ *.r1cs *.sym # Noir generated files -frontends/src/noir/test_folder/*/target/* +experimental-frontends/src/noir/test_folder/*/target/* # generated contracts data solidity-verifiers/generated diff --git a/benches/common.rs b/benches/common.rs index 6c632b4b..163dbbfc 100644 --- a/benches/common.rs +++ b/benches/common.rs @@ -29,7 +29,7 @@ pub(crate) fn bench_ivc_opt< // warmup steps for _ in 0..5 { - fs.prove_step(rng, vec![], None)?; + fs.prove_step(rng, (), None)?; } let mut group = c.benchmark_group(format!( @@ -38,7 +38,7 @@ pub(crate) fn bench_ivc_opt< )); group.significance_level(0.1).sample_size(10); group.bench_function("prove_step", |b| { - b.iter(|| -> Result<_, _> { black_box(fs.clone()).prove_step(rng, vec![], None) }) + b.iter(|| -> Result<_, _> { black_box(fs.clone()).prove_step(rng, (), None) }) }); // verify the IVCProof diff --git a/examples/circom_full_flow.rs b/examples/circom_full_flow.rs index b0d92fb2..67aaad43 100644 --- a/examples/circom_full_flow.rs +++ b/examples/circom_full_flow.rs @@ -17,7 +17,7 @@ use ark_grumpkin::Projective as G2; use std::path::PathBuf; use std::time::Instant; -use experimental_frontends::circom::CircomFCircuit; +use experimental_frontends::{circom::CircomFCircuit, utils::VecF}; use folding_schemes::{ commitment::{kzg::KZG, pedersen::Pedersen}, folding::{ @@ -64,14 +64,16 @@ fn main() -> Result<(), Error> { "./experimental-frontends/src/circom/test_folder/with_external_inputs_js/with_external_inputs.wasm", ); - let f_circuit_params = (r1cs_path.into(), wasm_path.into(), 1, 2); - let f_circuit = CircomFCircuit::::new(f_circuit_params)?; + let f_circuit_params = (r1cs_path.into(), wasm_path.into(), 1); // state len = 1 + const EXT_INP_LEN: usize = 2; // external inputs len = 2 + let f_circuit = CircomFCircuit::::new(f_circuit_params)?; - pub type N = Nova, KZG<'static, Bn254>, Pedersen, false>; + pub type N = + Nova, KZG<'static, Bn254>, Pedersen, false>; pub type D = DeciderEth< G1, G2, - CircomFCircuit, + CircomFCircuit, KZG<'static, Bn254>, Pedersen, Groth16, @@ -94,7 +96,7 @@ fn main() -> Result<(), Error> { // run n steps of the folding iteration for (i, external_inputs_at_step) in external_inputs.iter().enumerate() { let start = Instant::now(); - nova.prove_step(rng, external_inputs_at_step.clone(), None)?; + nova.prove_step(rng, VecF(external_inputs_at_step.clone()), None)?; println!("Nova::prove_step {}: {:?}", i, start.elapsed()); } diff --git a/examples/external_inputs.rs b/examples/external_inputs.rs index 60427b00..8bcad20c 100644 --- a/examples/external_inputs.rs +++ b/examples/external_inputs.rs @@ -74,6 +74,8 @@ where F: Absorb, { type Params = PoseidonConfig; + type ExternalInputs = VecF; + type ExternalInputsVar = VecFpVar; fn new(params: Self::Params) -> Result { Ok(Self { @@ -84,9 +86,6 @@ where fn state_len(&self) -> usize { 1 } - fn external_inputs_len(&self) -> usize { - 1 - } /// generates the constraints and returns the next state value for the step of F for the given /// z_i and external_inputs fn generate_step_constraints( @@ -94,16 +93,51 @@ where cs: ConstraintSystemRef, _i: usize, z_i: Vec>, - external_inputs: Vec>, + external_inputs: Self::ExternalInputsVar, ) -> Result>, SynthesisError> { + let ei: VecFpVar = external_inputs.into(); let crh_params = CRHParametersVar::::new_constant(cs.clone(), self.poseidon_config.clone())?; - let hash_input: [FpVar; 2] = [z_i[0].clone(), external_inputs[0].clone()]; + let hash_input: [FpVar; 2] = [z_i[0].clone(), ei.0[0].clone()]; let h = CRHGadget::::evaluate(&crh_params, &hash_input)?; Ok(vec![h]) } } +#[derive(Clone, Debug)] +pub struct VecF(Vec); +impl Default for VecF { + fn default() -> Self { + VecF(vec![F::zero()]) + } +} + +use ark_r1cs_std::alloc::AllocationMode; +use ark_relations::r1cs::Namespace; +use core::borrow::Borrow; +#[derive(Clone, Debug)] +pub struct VecFpVar(Vec>); +impl AllocVar, F> for VecFpVar { + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + f().and_then(|val| { + let cs = cs.into(); + + let v = Vec::>::new_variable(cs.clone(), || Ok(val.borrow().0.clone()), mode)?; + + Ok(VecFpVar(v)) + }) + } +} +impl Default for VecFpVar { + fn default() -> Self { + VecFpVar(vec![FpVar::::Constant(F::zero())]) + } +} + /// cargo test --example external_inputs #[cfg(test)] pub mod tests { @@ -139,8 +173,12 @@ pub mod tests { let z_iVar = Vec::>::new_witness(cs.clone(), || Ok(z_i))?; let external_inputsVar = Vec::>::new_witness(cs.clone(), || Ok(external_inputs))?; - let computed_z_i1Var = - circuit.generate_step_constraints(cs.clone(), 0, z_iVar, external_inputsVar)?; + let computed_z_i1Var = circuit.generate_step_constraints( + cs.clone(), + 0, + z_iVar, + VecFpVar(external_inputsVar), + )?; assert_eq!(computed_z_i1Var.value()?, z_i1); Ok(()) } @@ -153,11 +191,11 @@ fn main() -> Result<(), Error> { // prepare the external inputs to be used at each folding step let external_inputs = vec![ - vec![Fr::from(3_u32)], - vec![Fr::from(33_u32)], - vec![Fr::from(73_u32)], - vec![Fr::from(103_u32)], - vec![Fr::from(125_u32)], + VecF(vec![Fr::from(3_u32)]), + VecF(vec![Fr::from(33_u32)]), + VecF(vec![Fr::from(73_u32)]), + VecF(vec![Fr::from(103_u32)]), + VecF(vec![Fr::from(125_u32)]), ]; assert_eq!(external_inputs.len(), num_steps); diff --git a/examples/full_flow.rs b/examples/full_flow.rs index 648171f0..4d84bd01 100644 --- a/examples/full_flow.rs +++ b/examples/full_flow.rs @@ -46,21 +46,21 @@ pub struct CubicFCircuit { } impl FCircuit for CubicFCircuit { type Params = (); + type ExternalInputs = (); + type ExternalInputsVar = (); + fn new(_params: Self::Params) -> Result { Ok(Self { _f: PhantomData }) } fn state_len(&self) -> usize { 1 } - fn external_inputs_len(&self) -> usize { - 0 - } fn generate_step_constraints( &self, cs: ConstraintSystemRef, _i: usize, z_i: Vec>, - _external_inputs: Vec>, + _external_inputs: Self::ExternalInputsVar, ) -> Result>, SynthesisError> { let five = FpVar::::new_constant(cs.clone(), F::from(5u32))?; let z_i = z_i[0].clone(); @@ -96,7 +96,7 @@ fn main() -> Result<(), Error> { // run n steps of the folding iteration for i in 0..n_steps { let start = Instant::now(); - nova.prove_step(rng, vec![], None)?; + nova.prove_step(rng, (), None)?; println!("Nova::prove_step {}: {:?}", i, start.elapsed()); } diff --git a/examples/multi_inputs.rs b/examples/multi_inputs.rs index 67b32cf4..ff56bd9d 100644 --- a/examples/multi_inputs.rs +++ b/examples/multi_inputs.rs @@ -30,6 +30,8 @@ pub struct MultiInputsFCircuit { } impl FCircuit for MultiInputsFCircuit { type Params = (); + type ExternalInputs = (); + type ExternalInputsVar = (); fn new(_params: Self::Params) -> Result { Ok(Self { _f: PhantomData }) @@ -37,16 +39,13 @@ impl FCircuit for MultiInputsFCircuit { fn state_len(&self) -> usize { 5 } - fn external_inputs_len(&self) -> usize { - 0 - } /// generates the constraints for the step of F for the given z_i fn generate_step_constraints( &self, cs: ConstraintSystemRef, _i: usize, z_i: Vec>, - _external_inputs: Vec>, + _external_inputs: Self::ExternalInputsVar, ) -> Result>, SynthesisError> { let four = FpVar::::new_constant(cs.clone(), F::from(4u32))?; let forty = FpVar::::new_constant(cs.clone(), F::from(40u32))?; @@ -96,7 +95,7 @@ pub mod tests { let z_iVar = Vec::>::new_witness(cs.clone(), || Ok(z_i))?; let computed_z_i1Var = - circuit.generate_step_constraints(cs.clone(), 0, z_iVar.clone(), vec![])?; + circuit.generate_step_constraints(cs.clone(), 0, z_iVar.clone(), ())?; assert_eq!(computed_z_i1Var.value()?, z_i1); Ok(()) } @@ -140,7 +139,7 @@ fn main() -> Result<(), Error> { // compute a step of the IVC for i in 0..num_steps { let start = Instant::now(); - folding_scheme.prove_step(rng, vec![], None)?; + folding_scheme.prove_step(rng, (), None)?; println!("Nova::prove_step {}: {:?}", i, start.elapsed()); } diff --git a/examples/noir_full_flow.rs b/examples/noir_full_flow.rs index 147903e1..17c1f919 100644 --- a/examples/noir_full_flow.rs +++ b/examples/noir_full_flow.rs @@ -14,7 +14,7 @@ use ark_bn254::{Bn254, Fr, G1Projective as G1}; use ark_groth16::Groth16; use ark_grumpkin::Projective as G2; -use experimental_frontends::noir::NoirFCircuit; +use experimental_frontends::{noir::NoirFCircuit, utils::VecF}; use folding_schemes::{ commitment::{kzg::KZG, pedersen::Pedersen}, folding::{ @@ -42,16 +42,23 @@ fn main() -> Result<(), Error> { let z_0 = vec![Fr::from(1)]; // initialize the noir fcircuit - let f_circuit = NoirFCircuit::new(( + const EXT_INP_LEN: usize = 0; + let f_circuit = NoirFCircuit::::new(( Path::new("./experimental-frontends/src/noir/test_folder/test_mimc/target/test_mimc.json") .into(), 1, - 0, ))?; - pub type N = Nova, KZG<'static, Bn254>, Pedersen>; - pub type D = - DeciderEth, KZG<'static, Bn254>, Pedersen, Groth16, N>; + pub type N = Nova, KZG<'static, Bn254>, Pedersen>; + pub type D = DeciderEth< + G1, + G2, + NoirFCircuit, + KZG<'static, Bn254>, + Pedersen, + Groth16, + N, + >; let poseidon_config = poseidon_canonical_config::(); let mut rng = ark_std::rand::rngs::OsRng; @@ -69,7 +76,7 @@ fn main() -> Result<(), Error> { // run n steps of the folding iteration for i in 0..5 { let start = Instant::now(); - nova.prove_step(rng, vec![], None)?; + nova.prove_step(rng, VecF(vec![]), None)?; println!("Nova::prove_step {}: {:?}", i, start.elapsed()); } // verify the last IVC proof diff --git a/examples/noname_full_flow.rs b/examples/noname_full_flow.rs index 6bfb80bd..e42244ef 100644 --- a/examples/noname_full_flow.rs +++ b/examples/noname_full_flow.rs @@ -15,7 +15,7 @@ use noname::backends::r1cs::R1csBn254Field; use ark_groth16::Groth16; use ark_grumpkin::Projective as G2; -use experimental_frontends::noname::NonameFCircuit; +use experimental_frontends::{noname::NonameFCircuit, utils::VecF}; use folding_schemes::{ commitment::{kzg::KZG, pedersen::Pedersen}, folding::{ @@ -58,15 +58,21 @@ fn main() -> Result<(), Error> { ]; // initialize the noname circuit - let f_circuit_params = (NONAME_CIRCUIT_EXTERNAL_INPUTS.to_owned(), 2, 2); - let f_circuit = NonameFCircuit::::new(f_circuit_params)?; + let f_circuit_params = (NONAME_CIRCUIT_EXTERNAL_INPUTS.to_owned(), 2); // state len = 2 + const EXT_INP_LEN: usize = 2; + let f_circuit = NonameFCircuit::::new(f_circuit_params)?; - pub type N = - Nova, KZG<'static, Bn254>, Pedersen>; + pub type N = Nova< + G1, + G2, + NonameFCircuit, + KZG<'static, Bn254>, + Pedersen, + >; pub type D = DeciderEth< G1, G2, - NonameFCircuit, + NonameFCircuit, KZG<'static, Bn254>, Pedersen, Groth16, @@ -89,7 +95,7 @@ fn main() -> Result<(), Error> { // run n steps of the folding iteration for (i, external_inputs_at_step) in external_inputs.iter().enumerate() { let start = Instant::now(); - nova.prove_step(rng, external_inputs_at_step.clone(), None)?; + nova.prove_step(rng, VecF(external_inputs_at_step.clone()), None)?; println!("Nova::prove_step {}: {:?}", i, start.elapsed()); } diff --git a/examples/sha256.rs b/examples/sha256.rs index 95f75d16..22a5a104 100644 --- a/examples/sha256.rs +++ b/examples/sha256.rs @@ -36,6 +36,8 @@ pub struct Sha256FCircuit { } impl FCircuit for Sha256FCircuit { type Params = (); + type ExternalInputs = (); + type ExternalInputsVar = (); fn new(_params: Self::Params) -> Result { Ok(Self { _f: PhantomData }) @@ -43,16 +45,13 @@ impl FCircuit for Sha256FCircuit { fn state_len(&self) -> usize { 1 } - fn external_inputs_len(&self) -> usize { - 0 - } /// generates the constraints for the step of F for the given z_i fn generate_step_constraints( &self, _cs: ConstraintSystemRef, _i: usize, z_i: Vec>, - _external_inputs: Vec>, + _external_inputs: Self::ExternalInputsVar, ) -> Result>, SynthesisError> { let unit_var = UnitVar::default(); let out_bytes = Sha256Gadget::evaluate(&unit_var, &z_i[0].to_bytes_le()?)?; @@ -89,7 +88,7 @@ pub mod tests { let z_iVar = Vec::>::new_witness(cs.clone(), || Ok(z_i))?; let computed_z_i1Var = - circuit.generate_step_constraints(cs.clone(), 0, z_iVar.clone(), vec![])?; + circuit.generate_step_constraints(cs.clone(), 0, z_iVar.clone(), ())?; assert_eq!(computed_z_i1Var.value()?, z_i1); Ok(()) } @@ -126,7 +125,7 @@ fn main() -> Result<(), Error> { // compute a step of the IVC for i in 0..num_steps { let start = Instant::now(); - folding_scheme.prove_step(rng, vec![], None)?; + folding_scheme.prove_step(rng, (), None)?; println!("Nova::prove_step {}: {:?}", i, start.elapsed()); } diff --git a/experimental-frontends/src/circom/mod.rs b/experimental-frontends/src/circom/mod.rs index 4c62e01f..ed7a76a7 100644 --- a/experimental-frontends/src/circom/mod.rs +++ b/experimental-frontends/src/circom/mod.rs @@ -10,30 +10,32 @@ use folding_schemes::{frontend::FCircuit, utils::PathOrBin, Error}; use num_bigint::BigInt; pub mod utils; +use crate::utils::{VecF, VecFpVar}; use utils::CircomWrapper; -/// Define CircomFCircuit +/// Define CircomFCircuit. The parameter `L` indicates the length of the ExternalInputs vector of +/// field elements. #[derive(Clone, Debug)] -pub struct CircomFCircuit { +pub struct CircomFCircuit { circom_wrapper: CircomWrapper, pub state_len: usize, - pub external_inputs_len: usize, r1cs: CircomR1CS, } -impl FCircuit for CircomFCircuit { - /// (r1cs_path, wasm_path, state_len, external_inputs_len) - type Params = (PathOrBin, PathOrBin, usize, usize); +impl FCircuit for CircomFCircuit { + /// (r1cs_path, wasm_path, state_len) + type Params = (PathOrBin, PathOrBin, usize); + type ExternalInputs = VecF; + type ExternalInputsVar = VecFpVar; fn new(params: Self::Params) -> Result { - let (r1cs_path, wasm_path, state_len, external_inputs_len) = params; + let (r1cs_path, wasm_path, state_len) = params; let circom_wrapper = CircomWrapper::new(r1cs_path, wasm_path)?; let r1cs = circom_wrapper.extract_r1cs()?; Ok(Self { circom_wrapper, state_len, - external_inputs_len, r1cs, }) } @@ -41,27 +43,24 @@ impl FCircuit for CircomFCircuit { fn state_len(&self) -> usize { self.state_len } - fn external_inputs_len(&self) -> usize { - self.external_inputs_len - } fn generate_step_constraints( &self, cs: ConstraintSystemRef, _i: usize, z_i: Vec>, - external_inputs: Vec>, + external_inputs: Self::ExternalInputsVar, ) -> Result>, SynthesisError> { #[cfg(test)] assert_eq!(z_i.len(), self.state_len()); #[cfg(test)] - assert_eq!(external_inputs.len(), self.external_inputs_len()); + assert_eq!(external_inputs.0.len(), L); let input_values = self.fpvars_to_bigints(&z_i)?; let mut inputs_map = vec![("ivc_input".to_string(), input_values)]; - if self.external_inputs_len() > 0 { - let external_inputs_bi = self.fpvars_to_bigints(&external_inputs)?; + if L > 0 { + let external_inputs_bi = self.fpvars_to_bigints(&external_inputs.0)?; inputs_map.push(("external_inputs".to_string(), external_inputs_bi)); } @@ -106,7 +105,7 @@ impl FCircuit for CircomFCircuit { } } -impl CircomFCircuit { +impl CircomFCircuit { fn fpvars_to_bigints(&self, fpvars: &[FpVar]) -> Result, SynthesisError> { let mut input_values = Vec::new(); // converts each FpVar to PrimeField value, then to num_bigint::BigInt. @@ -171,14 +170,15 @@ pub mod tests { PathBuf::from("./src/circom/test_folder/cubic_circuit_js/cubic_circuit.wasm"); let circom_fcircuit = - CircomFCircuit::::new((r1cs_path.into(), wasm_path.into(), 1, 0))?; // state_len:1, external_inputs_len:0 + CircomFCircuit::::new((r1cs_path.into(), wasm_path.into(), 1))?; // state_len:1, external_inputs_len:0 let cs = ConstraintSystem::::new_ref(); let z_i = vec![Fr::from(3u32)]; let z_i_var = Vec::>::new_witness(cs.clone(), || Ok(z_i))?; - let z_i1_var = circom_fcircuit.generate_step_constraints(cs.clone(), 1, z_i_var, vec![])?; + let z_i1_var = + circom_fcircuit.generate_step_constraints(cs.clone(), 1, z_i_var, VecFpVar(vec![]))?; assert_eq!(z_i1_var.value()?, vec![Fr::from(35u32)]); Ok(()) } @@ -191,7 +191,7 @@ pub mod tests { PathBuf::from("./src/circom/test_folder/cubic_circuit_js/cubic_circuit.wasm"); let circom_fcircuit = - CircomFCircuit::::new((r1cs_path.into(), wasm_path.into(), 1, 0))?; // state_len:1, external_inputs_len:0 + CircomFCircuit::::new((r1cs_path.into(), wasm_path.into(), 1))?; // state_len:1, external_inputs_len:0 // Allocates z_i1 by using step_native function. let z_i = vec![Fr::from(3_u32)]; @@ -215,7 +215,7 @@ pub mod tests { "./src/circom/test_folder/with_external_inputs_js/with_external_inputs.wasm", ); let circom_fcircuit = - CircomFCircuit::::new((r1cs_path.into(), wasm_path.into(), 1, 2))?; // state_len:1, external_inputs_len:2 + CircomFCircuit::::new((r1cs_path.into(), wasm_path.into(), 1))?; // state_len:1, external_inputs_len:2 let cs = ConstraintSystem::::new_ref(); let z_i = vec![Fr::from(3u32)]; let external_inputs = vec![Fr::from(6u32), Fr::from(7u32)]; @@ -231,7 +231,7 @@ pub mod tests { cs.clone(), 1, z_i_var, - external_inputs_var, + VecFpVar(external_inputs_var), )?; assert_eq!(z_i1_var.value()?, z_i1_native); @@ -246,7 +246,7 @@ pub mod tests { cs.clone(), 1, wrong_z_i_var, - external_inputs_var, + VecFpVar(external_inputs_var), ); // TODO:: https://github.com/privacy-scaling-explorations/sonobe/issues/104 // Disable check for now @@ -260,7 +260,7 @@ pub mod tests { let wasm_path = PathBuf::from("./src/circom/test_folder/no_external_inputs_js/no_external_inputs.wasm"); let circom_fcircuit = - CircomFCircuit::::new((r1cs_path.into(), wasm_path.into(), 3, 0))?; + CircomFCircuit::::new((r1cs_path.into(), wasm_path.into(), 3))?; let cs = ConstraintSystem::::new_ref(); let z_i = vec![Fr::from(3u32), Fr::from(4u32), Fr::from(5u32)]; let z_i_var = Vec::>::new_witness(cs.clone(), || Ok(z_i.clone()))?; @@ -269,7 +269,8 @@ pub mod tests { let z_i1_native = no_external_inputs_step_native(z_i.clone()); // run gadget step - let z_i1_var = circom_fcircuit.generate_step_constraints(cs.clone(), 1, z_i_var, vec![])?; + let z_i1_var = + circom_fcircuit.generate_step_constraints(cs.clone(), 1, z_i_var, VecFpVar(vec![]))?; assert_eq!(z_i1_var.value()?, z_i1_native); @@ -277,8 +278,12 @@ pub mod tests { let cs = ConstraintSystem::::new_ref(); let wrong_z_i = vec![Fr::from(0u32), Fr::from(4u32), Fr::from(5u32)]; let wrong_z_i_var = Vec::>::new_witness(cs.clone(), || Ok(wrong_z_i))?; - let _z_i1_var = - circom_fcircuit.generate_step_constraints(cs.clone(), 1, wrong_z_i_var, vec![]); + let _z_i1_var = circom_fcircuit.generate_step_constraints( + cs.clone(), + 1, + wrong_z_i_var, + VecFpVar(vec![]), + ); // TODO:: https://github.com/privacy-scaling-explorations/sonobe/issues/104 // Disable check for now // assert!(z_i1_var.is_err()) @@ -292,7 +297,7 @@ pub mod tests { PathBuf::from("./src/circom/test_folder/cubic_circuit_js/cubic_circuit.wasm"); let circom_fcircuit = - CircomFCircuit::::new((r1cs_path.into(), wasm_path.into(), 1, 0))?; // state_len:1, external_inputs_len:0 + CircomFCircuit::::new((r1cs_path.into(), wasm_path.into(), 1))?; // state_len:1, external_inputs_len:0 // Allocates z_i1 by using step_native function. let z_i = vec![Fr::from(3_u32)]; diff --git a/experimental-frontends/src/lib.rs b/experimental-frontends/src/lib.rs index f6b56f35..da1a1786 100644 --- a/experimental-frontends/src/lib.rs +++ b/experimental-frontends/src/lib.rs @@ -1,3 +1,4 @@ pub mod circom; pub mod noir; pub mod noname; +pub mod utils; diff --git a/experimental-frontends/src/noir/mod.rs b/experimental-frontends/src/noir/mod.rs index 1fcaa5b8..83228c8e 100644 --- a/experimental-frontends/src/noir/mod.rs +++ b/experimental-frontends/src/noir/mod.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use acvm::{ acir::{ acir_field::GenericFieldElement, @@ -12,18 +10,19 @@ use acvm::{ use ark_ff::PrimeField; use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar, R1CSVar}; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError}; -use folding_schemes::{frontend::FCircuit, utils::PathOrBin, Error}; use serde::{self, Deserialize, Serialize}; +use std::collections::HashMap; use self::bridge::AcirCircuitSonobe; +use crate::utils::{VecF, VecFpVar}; +use folding_schemes::{frontend::FCircuit, utils::PathOrBin, Error}; mod bridge; #[derive(Clone, Debug)] -pub struct NoirFCircuit { +pub struct NoirFCircuit { pub circuit: Circuit>, pub state_len: usize, - pub external_inputs_len: usize, } #[derive(Clone, Serialize, Deserialize, Debug)] @@ -35,11 +34,13 @@ pub struct ProgramArtifactGeneric { pub bytecode: Program>, } -impl FCircuit for NoirFCircuit { - type Params = (PathOrBin, usize, usize); +impl FCircuit for NoirFCircuit { + type Params = (PathOrBin, usize); + type ExternalInputs = VecF; + type ExternalInputsVar = VecFpVar; fn new(params: Self::Params) -> Result { - let (source, state_len, external_inputs_len) = params; + let (source, state_len) = params; let input_string = match source { PathOrBin::Path(path) => { let file_path = path.with_extension("json"); @@ -62,27 +63,19 @@ impl FCircuit for NoirFCircuit { )); } - Ok(NoirFCircuit { - circuit, - state_len, - external_inputs_len, - }) + Ok(NoirFCircuit { circuit, state_len }) } fn state_len(&self) -> usize { self.state_len } - fn external_inputs_len(&self) -> usize { - self.external_inputs_len - } - fn generate_step_constraints( &self, cs: ConstraintSystemRef, _i: usize, z_i: Vec>, - external_inputs: Vec>, // inputs that are not part of the state + external_inputs: Self::ExternalInputsVar, // inputs that are not part of the state ) -> Result>, SynthesisError> { let mut acvm = ACVM::new( &StubbedBlackBoxSolver, @@ -123,9 +116,9 @@ impl FCircuit for NoirFCircuit { .map(|witness| { let idx = witness.as_usize() - z_i.len(); let witness = AcvmWitness(witness.witness_index()); - already_assigned_witness_values.insert(witness, &external_inputs[idx]); + already_assigned_witness_values.insert(witness, &external_inputs.0[idx]); - let val = external_inputs[idx].value()?; + let val = external_inputs.0[idx].value()?; let value = if val == F::zero() { "0".to_string() } else { @@ -178,6 +171,7 @@ mod tests { use std::env; use crate::noir::NoirFCircuit; + use crate::utils::VecFpVar; /// Native implementation of `src/noir/test_folder/test_circuit` fn external_inputs_step_native(z_i: Vec, external_inputs: Vec) -> Vec { @@ -198,17 +192,22 @@ mod tests { fn test_step_constraints() -> Result<(), Error> { let cs = ConstraintSystem::::new_ref(); let cur_path = env::current_dir()?; - let noirfcircuit = NoirFCircuit::new(( + // external inputs length: 2, state length: 2 + let noirfcircuit = NoirFCircuit::::new(( cur_path .join("src/noir/test_folder/test_circuit/target/test_circuit.json") .into(), 2, - 2, ))?; let inputs = vec![Fr::from(2), Fr::from(5)]; let z_i = Vec::>::new_witness(cs.clone(), || Ok(inputs.clone()))?; let external_inputs = Vec::>::new_witness(cs.clone(), || Ok(inputs))?; - let output = noirfcircuit.generate_step_constraints(cs.clone(), 0, z_i, external_inputs)?; + let output = noirfcircuit.generate_step_constraints( + cs.clone(), + 0, + z_i, + VecFpVar(external_inputs), + )?; assert_eq!(output[0].value()?, Fr::from(4)); assert_eq!(output[1].value()?, Fr::from(25)); Ok(()) @@ -218,18 +217,23 @@ mod tests { fn test_step_constraints_no_external_inputs() -> Result<(), Error> { let cs = ConstraintSystem::::new_ref(); let cur_path = env::current_dir()?; - let noirfcircuit = NoirFCircuit::new(( + // external inputs length: 0, state length: 2 + let noirfcircuit = NoirFCircuit::::new(( cur_path .join("src/noir/test_folder/test_no_external_inputs/target/test_no_external_inputs.json") .into(), 2, - 0, )) ?; let inputs = vec![Fr::from(2), Fr::from(5)]; let z_i = Vec::>::new_witness(cs.clone(), || Ok(inputs.clone()))?; let external_inputs = vec![]; - let output = noirfcircuit.generate_step_constraints(cs.clone(), 0, z_i, external_inputs)?; + let output = noirfcircuit.generate_step_constraints( + cs.clone(), + 0, + z_i, + VecFpVar(external_inputs), + )?; assert_eq!(output[0].value()?, Fr::from(4)); assert_eq!(output[1].value()?, Fr::from(25)); Ok(()) diff --git a/experimental-frontends/src/noname/mod.rs b/experimental-frontends/src/noname/mod.rs index 5ec6ed18..897b49b0 100644 --- a/experimental-frontends/src/noname/mod.rs +++ b/experimental-frontends/src/noname/mod.rs @@ -1,39 +1,41 @@ +use ark_ff::PrimeField; use ark_r1cs_std::alloc::AllocVar; use ark_r1cs_std::fields::fp::FpVar; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError}; +use noname::backends::{r1cs::R1CS as R1CSNoname, BackendField}; +use noname::witness::CompiledCircuit; use num_bigint::BigUint; use std::marker::PhantomData; -use self::bridge::NonameSonobeCircuit; -use self::utils::{compile_source_code, NonameInputs}; - -use ark_ff::PrimeField; use folding_schemes::{frontend::FCircuit, Error}; -use noname::backends::{r1cs::R1CS as R1CSNoname, BackendField}; -use noname::witness::CompiledCircuit; pub mod bridge; pub mod utils; +use crate::utils::{VecF, VecFpVar}; +use self::bridge::NonameSonobeCircuit; +use self::utils::{compile_source_code, NonameInputs}; + +// `L` indicates the length of the ExternalInputs vector of field elements. #[derive(Debug, Clone)] -pub struct NonameFCircuit { +pub struct NonameFCircuit { pub state_len: usize, - pub external_inputs_len: usize, pub circuit: CompiledCircuit>, _f: PhantomData, } -impl FCircuit for NonameFCircuit { - type Params = (String, usize, usize); +impl FCircuit for NonameFCircuit { + type Params = (String, usize); + type ExternalInputs = VecF; + type ExternalInputsVar = VecFpVar; fn new(params: Self::Params) -> Result { - let (code, state_len, external_inputs_len) = params; + let (code, state_len) = params; let compiled_circuit = compile_source_code::(&code).map_err(|_| { Error::Other("Encountered an error while compiling a noname circuit".to_owned()) })?; Ok(NonameFCircuit { state_len, - external_inputs_len, circuit: compiled_circuit, _f: PhantomData, }) @@ -43,19 +45,15 @@ impl FCircuit for NonameFCircuit { self.state_len } - fn external_inputs_len(&self) -> usize { - self.external_inputs_len - } - fn generate_step_constraints( &self, cs: ConstraintSystemRef, _i: usize, z_i: Vec>, - external_inputs: Vec>, + external_inputs: Self::ExternalInputsVar, ) -> Result>, SynthesisError> { let wtns_external_inputs = - NonameInputs::from_fpvars((&external_inputs, "external_inputs".to_string()))?; + NonameInputs::from_fpvars((&external_inputs.0, "external_inputs".to_string()))?; let wtns_ivc_inputs = NonameInputs::from_fpvars((&z_i, "ivc_inputs".to_string()))?; let noname_witness = self .circuit @@ -78,7 +76,7 @@ impl FCircuit for NonameFCircuit { compiled_circuit: self.circuit.clone(), witness: noname_witness, assigned_z_i: &z_i, - assigned_external_inputs: &external_inputs, + assigned_external_inputs: &external_inputs.0, assigned_z_i1: &assigned_z_i1, }; noname_circuit.generate_constraints(cs.clone())?; @@ -89,16 +87,16 @@ impl FCircuit for NonameFCircuit { #[cfg(test)] mod tests { - use ark_bn254::Fr; use ark_ff::PrimeField; use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar, R1CSVar}; + use ark_relations::r1cs::ConstraintSystem; use noname::backends::r1cs::R1csBn254Field; use folding_schemes::{frontend::FCircuit, Error}; use super::NonameFCircuit; - use ark_relations::r1cs::ConstraintSystem; + use crate::utils::VecFpVar; /// Native implementation of `NONAME_CIRCUIT_EXTERNAL_INPUTS` fn external_inputs_step_native(z_i: Vec, external_inputs: Vec) -> Vec { @@ -125,8 +123,9 @@ mod tests { #[test] fn test_step_native() -> Result<(), Error> { let cs = ConstraintSystem::::new_ref(); - let params = (NONAME_CIRCUIT_EXTERNAL_INPUTS.to_owned(), 2, 2); - let circuit = NonameFCircuit::::new(params)?; + // state length = 2, external inputs length= 2 + let params = (NONAME_CIRCUIT_EXTERNAL_INPUTS.to_owned(), 2); + let circuit = NonameFCircuit::::new(params)?; let inputs_public = vec![Fr::from(2), Fr::from(5)]; let inputs_private = vec![Fr::from(8), Fr::from(2)]; @@ -139,7 +138,7 @@ mod tests { cs.clone(), 0, ivc_inputs_var, - external_inputs_var, + VecFpVar(external_inputs_var), )?; let z_i1_native = external_inputs_step_native(inputs_public, inputs_private); @@ -151,8 +150,9 @@ mod tests { #[test] fn test_step_constraints() -> Result<(), Error> { let cs = ConstraintSystem::::new_ref(); - let params = (NONAME_CIRCUIT_EXTERNAL_INPUTS.to_owned(), 2, 2); - let circuit = NonameFCircuit::::new(params)?; + // external inputs length= 2 + let params = (NONAME_CIRCUIT_EXTERNAL_INPUTS.to_owned(), 2); + let circuit = NonameFCircuit::::new(params)?; let inputs_public = vec![Fr::from(2), Fr::from(5)]; let inputs_private = vec![Fr::from(8), Fr::from(2)]; @@ -163,7 +163,7 @@ mod tests { cs.clone(), 0, ivc_inputs_var, - external_inputs_var, + VecFpVar(external_inputs_var), )?; assert!(cs.is_satisfied()?); assert_eq!(z_i1[0].value()?, Fr::from(10_u8)); @@ -174,13 +174,14 @@ mod tests { #[test] fn test_generate_constraints_no_external_inputs() -> Result<(), Error> { let cs = ConstraintSystem::::new_ref(); - let params = (NONAME_CIRCUIT_NO_EXTERNAL_INPUTS.to_owned(), 2, 0); + let params = (NONAME_CIRCUIT_NO_EXTERNAL_INPUTS.to_owned(), 2); // state length = 2 let inputs_public = vec![Fr::from(2), Fr::from(5)]; let ivc_inputs_var = Vec::>::new_witness(cs.clone(), || Ok(inputs_public))?; - let f_circuit = NonameFCircuit::::new(params)?; - f_circuit.generate_step_constraints(cs.clone(), 0, ivc_inputs_var, vec![])?; + // external inputs length = 0 + let f_circuit = NonameFCircuit::::new(params)?; + f_circuit.generate_step_constraints(cs.clone(), 0, ivc_inputs_var, VecFpVar(vec![]))?; assert!(cs.is_satisfied()?); Ok(()) } diff --git a/experimental-frontends/src/utils.rs b/experimental-frontends/src/utils.rs new file mode 100644 index 00000000..769c5ae7 --- /dev/null +++ b/experimental-frontends/src/utils.rs @@ -0,0 +1,38 @@ +use ark_ff::PrimeField; +use ark_r1cs_std::{ + alloc::{AllocVar, AllocationMode}, + fields::fp::FpVar, +}; +use ark_relations::r1cs::{Namespace, SynthesisError}; +use ark_std::fmt::Debug; +use core::borrow::Borrow; + +#[derive(Clone, Debug)] +pub struct VecF(pub Vec); +impl Default for VecF { + fn default() -> Self { + VecF(vec![F::zero(); L]) + } +} +#[derive(Clone, Debug)] +pub struct VecFpVar(pub Vec>); +impl AllocVar, F> for VecFpVar { + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + f().and_then(|val| { + let cs = cs.into(); + + let v = Vec::>::new_variable(cs.clone(), || Ok(val.borrow().0.clone()), mode)?; + + Ok(VecFpVar(v)) + }) + } +} +impl Default for VecFpVar { + fn default() -> Self { + VecFpVar(vec![FpVar::::Constant(F::zero()); L]) + } +} diff --git a/folding-schemes/src/folding/circuits/nonnative/uint.rs b/folding-schemes/src/folding/circuits/nonnative/uint.rs index 033244f2..eafd8fa4 100644 --- a/folding-schemes/src/folding/circuits/nonnative/uint.rs +++ b/folding-schemes/src/folding/circuits/nonnative/uint.rs @@ -203,8 +203,8 @@ impl NonNativeUintVar { // Thus, 55 allows us to compute `Az∘Bz` without the expensive alignment // operation. // - // TODO (@winderica): either make it a global const, or compute an - // optimal value based on the modulus size + // TODO: either make it a global const, or compute an optimal value + // based on the modulus size. 55 } } diff --git a/folding-schemes/src/folding/hypernova/circuits.rs b/folding-schemes/src/folding/hypernova/circuits.rs index f7b34690..808ea49f 100644 --- a/folding-schemes/src/folding/hypernova/circuits.rs +++ b/folding-schemes/src/folding/hypernova/circuits.rs @@ -479,7 +479,7 @@ pub struct AugmentedFCircuit< pub(super) i_usize: Option, pub(super) z_0: Option>, pub(super) z_i: Option>, - pub(super) external_inputs: Option>, + pub(super) external_inputs: Option, pub(super) U_i: Option>, pub(super) Us: Option>>, // other U_i's to be folded that are not the main running instance pub(super) u_i_C: Option, // u_i.C @@ -602,7 +602,7 @@ where i_usize: Some(0), z_0: Some(z_0.clone()), z_i: Some(z_0.clone()), - external_inputs: Some(vec![C1::ScalarField::zero(); self.F.external_inputs_len()]), + external_inputs: Some(FC::ExternalInputs::default()), U_i: Some(U_i.clone()), Us: Some(Us), u_i_C: Some(u_i.C), @@ -681,10 +681,8 @@ where .z_i .unwrap_or(vec![CF1::::zero(); self.F.state_len()])) })?; - let external_inputs = Vec::>>::new_witness(cs.clone(), || { - Ok(self - .external_inputs - .unwrap_or(vec![CF1::::zero(); self.F.external_inputs_len()])) + let external_inputs = FC::ExternalInputsVar::new_witness(cs.clone(), || { + Ok(self.external_inputs.unwrap_or_default()) })?; let U_dummy = LCCCS::::dummy(&self.ccs); @@ -1278,7 +1276,7 @@ mod tests { i_usize: Some(0), z_0: Some(z_0.clone()), z_i: Some(z_i.clone()), - external_inputs: Some(vec![]), + external_inputs: Some(()), U_i: Some(U_i.clone()), Us: Some(Us.clone()), u_i_C: Some(u_i.C), @@ -1362,7 +1360,7 @@ mod tests { i_usize: Some(i), z_0: Some(z_0.clone()), z_i: Some(z_i.clone()), - external_inputs: Some(vec![]), + external_inputs: Some(()), U_i: Some(U_i.clone()), Us: Some(Us.clone()), u_i_C: Some(u_i.C), diff --git a/folding-schemes/src/folding/hypernova/decider_eth.rs b/folding-schemes/src/folding/hypernova/decider_eth.rs index 1a3346fc..d4bffd77 100644 --- a/folding-schemes/src/folding/hypernova/decider_eth.rs +++ b/folding-schemes/src/folding/hypernova/decider_eth.rs @@ -260,8 +260,8 @@ pub mod tests { let hypernova_params = HN::preprocess(&mut rng, &prep_param)?; let mut hypernova = HN::init(&hypernova_params, F_circuit, z_0.clone())?; - hypernova.prove_step(&mut rng, vec![], Some((vec![], vec![])))?; - hypernova.prove_step(&mut rng, vec![], Some((vec![], vec![])))?; // do a 2nd step + hypernova.prove_step(&mut rng, (), Some((vec![], vec![])))?; + hypernova.prove_step(&mut rng, (), Some((vec![], vec![])))?; // do a 2nd step // prepare the Decider prover & verifier params let (decider_pp, decider_vp) = @@ -356,8 +356,8 @@ pub mod tests { let hypernova_params = (hypernova_pp_deserialized, hypernova_vp_deserialized); let mut hypernova = HN::init(&hypernova_params, F_circuit, z_0.clone())?; - hypernova.prove_step(&mut rng, vec![], Some((vec![], vec![])))?; - hypernova.prove_step(&mut rng, vec![], Some((vec![], vec![])))?; + hypernova.prove_step(&mut rng, (), Some((vec![], vec![])))?; + hypernova.prove_step(&mut rng, (), Some((vec![], vec![])))?; // decider proof generation let proof = D::prove(rng, decider_pp, hypernova.clone())?; diff --git a/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs b/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs index 067c5501..099e2146 100644 --- a/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs +++ b/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs @@ -301,7 +301,7 @@ pub mod tests { // generate a Nova instance and do a step of it let mut hypernova = HN::init(&hn_params, F_circuit, z_0.clone())?; - hypernova.prove_step(&mut rng, vec![], None)?; + hypernova.prove_step(&mut rng, (), None)?; let ivc_proof = hypernova.ivc_proof(); HN::verify(hn_params.1, ivc_proof)?; diff --git a/folding-schemes/src/folding/hypernova/mod.rs b/folding-schemes/src/folding/hypernova/mod.rs index ecba05a4..e6b5ae31 100644 --- a/folding-schemes/src/folding/hypernova/mod.rs +++ b/folding-schemes/src/folding/hypernova/mod.rs @@ -279,7 +279,7 @@ where &self, mut rng: impl RngCore, state: Vec, - external_inputs: Vec, + external_inputs: FC::ExternalInputs, ) -> Result { let r1cs_z = self.new_instance_generic(state, external_inputs)?; // compute committed instances, w_{i+1}, u_{i+1}, which will be used as w_i, u_i, so we @@ -301,7 +301,7 @@ where &self, mut rng: impl RngCore, state: Vec, - external_inputs: Vec, + external_inputs: FC::ExternalInputs, ) -> Result { let r1cs_z = self.new_instance_generic(state, external_inputs)?; // compute committed instances, w_{i+1}, u_{i+1}, which will be used as w_i, u_i, so we @@ -332,7 +332,7 @@ where fn new_instance_generic( &self, state: Vec, - external_inputs: Vec, + external_inputs: FC::ExternalInputs, ) -> Result, Error> { // prepare the initial dummy instances let U_i = LCCCS::::dummy(&self.ccs); @@ -599,7 +599,7 @@ where fn prove_step( &mut self, mut rng: impl RngCore, - external_inputs: Vec, + external_inputs: FC::ExternalInputs, other_instances: Option, ) -> Result<(), Error> { // ensure that commitments are blinding if user has specified so. @@ -665,14 +665,6 @@ where self.F.state_len(), )); } - if external_inputs.len() != self.F.external_inputs_len() { - return Err(Error::NotSameLength( - "F.external_inputs_len()".to_string(), - self.F.external_inputs_len(), - "external_inputs.len()".to_string(), - external_inputs.len(), - )); - } if self.i > C1::ScalarField::from_le_bytes_mod_order(&usize::MAX.to_le_bytes()) { return Err(Error::MaxStep); @@ -1045,17 +1037,17 @@ mod tests { let mut lcccs = vec![]; for j in 0..MU - 1 { let instance_state = vec![Fr::from(j as u32 + 85_u32)]; - let (U, W) = hypernova.new_running_instance(&mut rng, instance_state, vec![])?; + let (U, W) = hypernova.new_running_instance(&mut rng, instance_state, ())?; lcccs.push((U, W)); } let mut cccs = vec![]; for j in 0..NU - 1 { let instance_state = vec![Fr::from(j as u32 + 15_u32)]; - let (u, w) = hypernova.new_incoming_instance(&mut rng, instance_state, vec![])?; + let (u, w) = hypernova.new_incoming_instance(&mut rng, instance_state, ())?; cccs.push((u, w)); } - hypernova.prove_step(&mut rng, vec![], Some((lcccs, cccs)))?; + hypernova.prove_step(&mut rng, (), Some((lcccs, cccs)))?; } assert_eq!(Fr::from(num_steps as u32), hypernova.i); diff --git a/folding-schemes/src/folding/mod.rs b/folding-schemes/src/folding/mod.rs index 3de2751c..a2b23a8d 100644 --- a/folding-schemes/src/folding/mod.rs +++ b/folding-schemes/src/folding/mod.rs @@ -77,7 +77,7 @@ pub mod tests { // perform multiple IVC steps (internally folding) let num_steps: usize = 3; for _ in 0..num_steps { - fs.prove_step(&mut rng, vec![], None)?; + fs.prove_step(&mut rng, FC::ExternalInputs::default(), None)?; } // verify the IVCProof @@ -121,8 +121,8 @@ pub mod tests { // serialization new FS instance let num_steps: usize = 3; for _ in 0..num_steps { - new_fs.prove_step(&mut rng, vec![], None)?; - fs.prove_step(&mut rng, vec![], None)?; + new_fs.prove_step(&mut rng, FC::ExternalInputs::default(), None)?; + fs.prove_step(&mut rng, FC::ExternalInputs::default(), None)?; } // check that the IVCProofs from both FS instances are equal diff --git a/folding-schemes/src/folding/nova/circuits.rs b/folding-schemes/src/folding/nova/circuits.rs index 1989148c..6b7f8fa1 100644 --- a/folding-schemes/src/folding/nova/circuits.rs +++ b/folding-schemes/src/folding/nova/circuits.rs @@ -55,7 +55,7 @@ pub struct AugmentedFCircuit>> { pub(super) i_usize: Option, pub(super) z_0: Option>, pub(super) z_i: Option>, - pub(super) external_inputs: Option>, + pub(super) external_inputs: Option, pub(super) u_i_cmW: Option, pub(super) U_i: Option>, pub(super) U_i1_cmE: Option, @@ -125,10 +125,8 @@ where .z_i .unwrap_or(vec![CF1::::zero(); self.F.state_len()])) })?; - let external_inputs = Vec::>>::new_witness(cs.clone(), || { - Ok(self - .external_inputs - .unwrap_or(vec![CF1::::zero(); self.F.external_inputs_len()])) + let external_inputs = FC::ExternalInputsVar::new_witness(cs.clone(), || { + Ok(self.external_inputs.unwrap_or_default()) })?; let u_dummy = CommittedInstance::dummy(2); diff --git a/folding-schemes/src/folding/nova/decider.rs b/folding-schemes/src/folding/nova/decider.rs index f270aefa..8068dec4 100644 --- a/folding-schemes/src/folding/nova/decider.rs +++ b/folding-schemes/src/folding/nova/decider.rs @@ -371,9 +371,9 @@ pub mod tests { let mut nova = N::init(&nova_params, F_circuit, z_0.clone())?; println!("Nova initialized, {:?}", start.elapsed()); let start = Instant::now(); - nova.prove_step(&mut rng, vec![], None)?; + nova.prove_step(&mut rng, (), None)?; println!("prove_step, {:?}", start.elapsed()); - nova.prove_step(&mut rng, vec![], None)?; // do a 2nd step + nova.prove_step(&mut rng, (), None)?; // do a 2nd step let mut rng = rand::rngs::OsRng; diff --git a/folding-schemes/src/folding/nova/decider_circuits.rs b/folding-schemes/src/folding/nova/decider_circuits.rs index f91537fa..db9a686d 100644 --- a/folding-schemes/src/folding/nova/decider_circuits.rs +++ b/folding-schemes/src/folding/nova/decider_circuits.rs @@ -192,7 +192,7 @@ pub mod tests { // generate a Nova instance and do a step of it let mut nova = N::init(&nova_params, F_circuit, z_0.clone())?; - nova.prove_step(&mut rng, vec![], None)?; + nova.prove_step(&mut rng, (), None)?; // verify the IVC let ivc_proof = nova.ivc_proof(); N::verify(nova_params.1, ivc_proof)?; diff --git a/folding-schemes/src/folding/nova/decider_eth.rs b/folding-schemes/src/folding/nova/decider_eth.rs index b7a4a79c..55d05a94 100644 --- a/folding-schemes/src/folding/nova/decider_eth.rs +++ b/folding-schemes/src/folding/nova/decider_eth.rs @@ -319,9 +319,9 @@ pub mod tests { let (decider_pp, decider_vp) = D::preprocess(&mut rng, nova_params, nova.clone())?; let start = Instant::now(); - nova.prove_step(&mut rng, vec![], None)?; + nova.prove_step(&mut rng, (), None)?; println!("prove_step, {:?}", start.elapsed()); - nova.prove_step(&mut rng, vec![], None)?; // do a 2nd step + nova.prove_step(&mut rng, (), None)?; // do a 2nd step // decider proof generation let start = Instant::now(); @@ -431,9 +431,9 @@ pub mod tests { let mut nova = N::init(&nova_params, F_circuit, z_0)?; let start = Instant::now(); - nova.prove_step(&mut rng, vec![], None)?; + nova.prove_step(&mut rng, (), None)?; println!("prove_step, {:?}", start.elapsed()); - nova.prove_step(&mut rng, vec![], None)?; // do a 2nd step + nova.prove_step(&mut rng, (), None)?; // do a 2nd step // decider proof generation let start = Instant::now(); diff --git a/folding-schemes/src/folding/nova/decider_eth_circuit.rs b/folding-schemes/src/folding/nova/decider_eth_circuit.rs index b11c9b05..e3f7fdba 100644 --- a/folding-schemes/src/folding/nova/decider_eth_circuit.rs +++ b/folding-schemes/src/folding/nova/decider_eth_circuit.rs @@ -240,7 +240,7 @@ pub mod tests { // generate a Nova instance and do a step of it let mut nova = N::init(&nova_params, F_circuit, z_0.clone())?; - nova.prove_step(&mut rng, vec![], None)?; + nova.prove_step(&mut rng, (), None)?; let ivc_proof = nova.ivc_proof(); N::verify(nova_params.1, ivc_proof)?; diff --git a/folding-schemes/src/folding/nova/mod.rs b/folding-schemes/src/folding/nova/mod.rs index d1e1540e..cae1782a 100644 --- a/folding-schemes/src/folding/nova/mod.rs +++ b/folding-schemes/src/folding/nova/mod.rs @@ -620,8 +620,8 @@ where fn prove_step( &mut self, mut rng: impl RngCore, - external_inputs: Vec, - // Nova does not support multi-instances folding + external_inputs: FC::ExternalInputs, + // Nova does not support multi-instances folding (by design) _other_instances: Option, ) -> Result<(), Error> { // ensure that commitments are blinding if user has specified so. @@ -659,14 +659,6 @@ where self.F.state_len(), )); } - if external_inputs.len() != self.F.external_inputs_len() { - return Err(Error::NotSameLength( - "F.external_inputs_len()".to_string(), - self.F.external_inputs_len(), - "external_inputs.len()".to_string(), - external_inputs.len(), - )); - } if self.i > C1::ScalarField::from_le_bytes_mod_order(&usize::MAX.to_le_bytes()) { return Err(Error::MaxStep); @@ -1123,7 +1115,7 @@ pub mod tests { )?; for _ in 0..num_steps { - nova.prove_step(&mut rng, vec![], None)?; + nova.prove_step(&mut rng, (), None)?; } assert_eq!(Fr::from(num_steps as u32), nova.i); diff --git a/folding-schemes/src/folding/protogalaxy/circuits.rs b/folding-schemes/src/folding/protogalaxy/circuits.rs index d46afac1..3bfac847 100644 --- a/folding-schemes/src/folding/protogalaxy/circuits.rs +++ b/folding-schemes/src/folding/protogalaxy/circuits.rs @@ -238,7 +238,7 @@ pub struct AugmentedFCircuit>> { pub(super) i_usize: usize, pub(super) z_0: Vec>, pub(super) z_i: Vec>, - pub(super) external_inputs: Vec>, + pub(super) external_inputs: FC::ExternalInputs, pub(super) F: FC, // F circuit pub(super) u_i_phi: C1, pub(super) U_i: CommittedInstance, @@ -274,7 +274,7 @@ impl>> AugmentedFCircuit i_usize: 0, z_0: vec![CF1::::zero(); F_circuit.state_len()], z_i: vec![CF1::::zero(); F_circuit.state_len()], - external_inputs: vec![CF1::::zero(); F_circuit.external_inputs_len()], + external_inputs: FC::ExternalInputs::default(), u_i_phi: C1::zero(), U_i: u_dummy, U_i1_phi: C1::zero(), @@ -307,7 +307,7 @@ where let z_0 = Vec::>>::new_witness(cs.clone(), || Ok(self.z_0))?; let z_i = Vec::>>::new_witness(cs.clone(), || Ok(self.z_i))?; let external_inputs = - Vec::>>::new_witness(cs.clone(), || Ok(self.external_inputs))?; + FC::ExternalInputsVar::new_witness(cs.clone(), || Ok(self.external_inputs))?; let u_dummy = CommittedInstance::::dummy((2, self.U_i.betas.len())); let U_i = CommittedInstanceVar::::new_witness(cs.clone(), || Ok(self.U_i))?; diff --git a/folding-schemes/src/folding/protogalaxy/decider_eth.rs b/folding-schemes/src/folding/protogalaxy/decider_eth.rs index ddb843bc..4ea7e07a 100644 --- a/folding-schemes/src/folding/protogalaxy/decider_eth.rs +++ b/folding-schemes/src/folding/protogalaxy/decider_eth.rs @@ -285,8 +285,8 @@ pub mod tests { let start = Instant::now(); let mut protogalaxy = PG::init(&protogalaxy_params, F_circuit, z_0.clone())?; println!("ProtoGalaxy initialized, {:?}", start.elapsed()); - protogalaxy.prove_step(&mut rng, vec![], None)?; - protogalaxy.prove_step(&mut rng, vec![], None)?; // do a 2nd step + protogalaxy.prove_step(&mut rng, (), None)?; + protogalaxy.prove_step(&mut rng, (), None)?; // do a 2nd step // prepare the Decider prover & verifier params let (decider_pp, decider_vp) = @@ -360,8 +360,8 @@ pub mod tests { let start = Instant::now(); let mut protogalaxy = PG::init(&protogalaxy_params, F_circuit, z_0.clone())?; println!("ProtoGalaxy initialized, {:?}", start.elapsed()); - protogalaxy.prove_step(&mut rng, vec![], None)?; - protogalaxy.prove_step(&mut rng, vec![], None)?; // do a 2nd step + protogalaxy.prove_step(&mut rng, (), None)?; + protogalaxy.prove_step(&mut rng, (), None)?; // do a 2nd step // prepare the Decider prover & verifier params let (decider_pp, decider_vp) = @@ -402,9 +402,9 @@ pub mod tests { let mut protogalaxy = PG::init(&protogalaxy_params, F_circuit, z_0)?; let start = Instant::now(); - protogalaxy.prove_step(&mut rng, vec![], None)?; + protogalaxy.prove_step(&mut rng, (), None)?; println!("prove_step, {:?}", start.elapsed()); - protogalaxy.prove_step(&mut rng, vec![], None)?; // do a 2nd step + protogalaxy.prove_step(&mut rng, (), None)?; // do a 2nd step // decider proof generation let start = Instant::now(); diff --git a/folding-schemes/src/folding/protogalaxy/decider_eth_circuit.rs b/folding-schemes/src/folding/protogalaxy/decider_eth_circuit.rs index 483d7f9b..da9ce4e3 100644 --- a/folding-schemes/src/folding/protogalaxy/decider_eth_circuit.rs +++ b/folding-schemes/src/folding/protogalaxy/decider_eth_circuit.rs @@ -222,7 +222,7 @@ pub mod tests { // generate a Nova instance and do a step of it let mut protogalaxy = PG::init(&pg_params, F_circuit, z_0.clone())?; - protogalaxy.prove_step(&mut rng, vec![], None)?; + protogalaxy.prove_step(&mut rng, (), None)?; let ivc_proof = protogalaxy.ivc_proof(); PG::verify(pg_params.1, ivc_proof)?; diff --git a/folding-schemes/src/folding/protogalaxy/mod.rs b/folding-schemes/src/folding/protogalaxy/mod.rs index 8e1e3090..36b776c9 100644 --- a/folding-schemes/src/folding/protogalaxy/mod.rs +++ b/folding-schemes/src/folding/protogalaxy/mod.rs @@ -455,9 +455,8 @@ where { /// returns the hash of the public parameters of ProtoGalaxy pub fn pp_hash(&self) -> Result { - // TODO (@winderica): support hiding commitments in ProtoGalaxy. - // For now, `H` is set to false. - // Tracking issue: https://github.com/privacy-scaling-explorations/sonobe/issues/82 + // TODO: support hiding commitments in ProtoGalaxy. For now, `H` is set to false. Tracking + // issue: https://github.com/privacy-scaling-explorations/sonobe/issues/82 pp_hash::( &self.r1cs, &self.cf_r1cs, @@ -557,7 +556,6 @@ where // For `t_lower_bound`, we configure `F'` with `t = 1` and compute log2 // of the size of `F'`. let state_len = F.state_len(); - let external_inputs_len = F.external_inputs_len(); // `F'` includes `F` and `ProtoGalaxy.V`, where `F` might be costly. // Observing that the cost of `F` is constant with respect to `t`, we @@ -569,14 +567,13 @@ where cs.clone(), 0, Vec::new_witness(cs.clone(), || Ok(vec![Zero::zero(); state_len]))?, - Vec::new_witness(cs.clone(), || Ok(vec![Zero::zero(); external_inputs_len]))?, + FC::ExternalInputsVar::default(), )?; let step_constraints = cs.num_constraints(); // Create a dummy circuit with the same state length and external inputs // length as `F`, which replaces `F` in the augmented circuit `F'`. - let dummy_circuit: DummyCircuit = - FCircuit::::new((state_len, external_inputs_len))?; + let dummy_circuit: DummyCircuit = FCircuit::::new(state_len)?; // Compute `augmentation_constraints`, the size of `F'` without `F`. let cs = ConstraintSystem::::new_ref(); @@ -700,9 +697,9 @@ where ) -> Result<(Self::ProverParam, Self::VerifierParam), Error> { // We fix `k`, the number of incoming instances, to 1, because // multi-instances folding is not supported yet. - // TODO (@winderica): Support multi-instances folding and make `k` a - // constant generic parameter (as in HyperNova) - // Tracking issue: https://github.com/privacy-scaling-explorations/sonobe/issues/82 + // TODO: Support multi-instances folding and make `k` a constant generic parameter (as in + // HyperNova). Tracking issue: + // https://github.com/privacy-scaling-explorations/sonobe/issues/82 let k = 1; // `d`, the degree of the constraint system, is set to 2, as we only // support R1CS for now, whose highest degree is 2. @@ -788,7 +785,7 @@ where fn prove_step( &mut self, mut rng: impl RngCore, - external_inputs: Vec, + external_inputs: FC::ExternalInputs, _other_instances: Option, ) -> Result<(), Error> { // Multi-instances folding is not supported yet. @@ -797,9 +794,9 @@ where } // We fix `k`, the number of incoming instances, to 1, because // multi-instances folding is not supported yet. - // TODO (@winderica): Support multi-instances folding and make `k` a - // constant generic parameter (as in HyperNova) - // Tracking issue: https://github.com/privacy-scaling-explorations/sonobe/issues/82 + // TODO: Support multi-instances folding and make `k` a constant generic parameter (as in + // HyperNova). Tracking issue: + // https://github.com/privacy-scaling-explorations/sonobe/issues/82 let k = 1; // `d`, the degree of the constraint system, is set to 2, as we only // support R1CS for now, whose highest degree is 2. @@ -820,14 +817,6 @@ where self.F.state_len(), )); } - if external_inputs.len() != self.F.external_inputs_len() { - return Err(Error::NotSameLength( - "F.external_inputs_len()".to_string(), - self.F.external_inputs_len(), - "external_inputs.len()".to_string(), - external_inputs.len(), - )); - } let i_bn: BigUint = self.i.into(); let i_usize: usize = i_bn.try_into().map_err(|_| Error::MaxStep)?; @@ -847,7 +836,7 @@ where .external_inputs .clone_from(&external_inputs); - // There is no need to update `self.U_i` etc. as they are unchanged. + // There is no need to update `self.U_i` etc. as they are unchanged. } else { // Primary part: // Compute `U_{i+1}` by folding `u_i` into `U_i`. @@ -1167,7 +1156,7 @@ mod tests { let num_steps: usize = 3; for _ in 0..num_steps { - protogalaxy.prove_step(&mut test_rng(), vec![], None)?; + protogalaxy.prove_step(&mut test_rng(), (), None)?; } assert_eq!(Fr::from(num_steps as u32), protogalaxy.i); @@ -1184,32 +1173,28 @@ mod tests { let poseidon_config = poseidon_canonical_config::(); for state_len in [1, 10, 100] { - for external_inputs_len in [1, 10, 100] { - let dummy_circuit: DummyCircuit = - FCircuit::::new((state_len, external_inputs_len))?; - - let costs: Vec = (1..32) - .into_par_iter() - .map(|t| { - let cs = ConstraintSystem::::new_ref(); - AugmentedFCircuit::::empty( - &poseidon_config, - dummy_circuit.clone(), - t, - d, - k, - ) - .generate_constraints(cs.clone())?; - Ok(cs.num_constraints()) - }) - .collect::, Error>>()?; - - for t_lower_bound in log2(costs[0]) as usize..32 { - let num_constraints = - (1 << t_lower_bound) - costs[0] + costs[t_lower_bound - 1]; - let t = log2(num_constraints) as usize; - assert!(t == t_lower_bound || t == t_lower_bound + 1); - } + let dummy_circuit: DummyCircuit = FCircuit::::new(state_len)?; + + let costs: Vec = (1..32) + .into_par_iter() + .map(|t| { + let cs = ConstraintSystem::::new_ref(); + AugmentedFCircuit::::empty( + &poseidon_config, + dummy_circuit.clone(), + t, + d, + k, + ) + .generate_constraints(cs.clone())?; + Ok(cs.num_constraints()) + }) + .collect::, Error>>()?; + + for t_lower_bound in log2(costs[0]) as usize..32 { + let num_constraints = (1 << t_lower_bound) - costs[0] + costs[t_lower_bound - 1]; + let t = log2(num_constraints) as usize; + assert!(t == t_lower_bound || t == t_lower_bound + 1); } } Ok(()) diff --git a/folding-schemes/src/frontend/mod.rs b/folding-schemes/src/frontend/mod.rs index b5a352f5..3cafced3 100644 --- a/folding-schemes/src/frontend/mod.rs +++ b/folding-schemes/src/frontend/mod.rs @@ -1,6 +1,6 @@ use crate::Error; use ark_ff::PrimeField; -use ark_r1cs_std::fields::fp::FpVar; +use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar}; use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError}; use ark_std::fmt::Debug; @@ -10,8 +10,14 @@ pub mod utils; /// inside the agmented F' function). /// The parameter z_i denotes the current state, and z_{i+1} denotes the next state after applying /// the step. +/// Note that the external inputs for the specific circuit are defined at the implementation of +/// both `FCircuit::ExternalInputs` and `FCircuit::ExternalInputsVar`, where the `Default` trait +/// implementation. For example if the external inputs are just an array of field elements, their +/// `Default` trait implementation must return an array of the expected size. pub trait FCircuit: Clone + Debug { type Params: Debug; + type ExternalInputs: Clone + Default + Debug; + type ExternalInputsVar: Clone + Default + Debug + AllocVar; /// returns a new FCircuit instance fn new(params: Self::Params) -> Result; @@ -20,10 +26,6 @@ pub trait FCircuit: Clone + Debug { /// FCircuit inputs. fn state_len(&self) -> usize; - /// returns the number of elements in the external inputs used by the FCircuit. External inputs - /// are optional, and in case no external inputs are used, this method should return 0. - fn external_inputs_len(&self) -> usize; - /// generates the constraints for the step of F for the given z_i fn generate_step_constraints( // this method uses self, so that each FCircuit implementation (and different frontends) @@ -32,7 +34,7 @@ pub trait FCircuit: Clone + Debug { cs: ConstraintSystemRef, i: usize, z_i: Vec>, - external_inputs: Vec>, // inputs that are not part of the state + external_inputs: Self::ExternalInputsVar, // inputs that are not part of the state ) -> Result>, SynthesisError>; } diff --git a/folding-schemes/src/frontend/utils.rs b/folding-schemes/src/frontend/utils.rs index e4d3d749..b4fcb00d 100644 --- a/folding-schemes/src/frontend/utils.rs +++ b/folding-schemes/src/frontend/utils.rs @@ -10,35 +10,29 @@ use ark_std::{fmt::Debug, Zero}; use super::FCircuit; use crate::Error; -/// DummyCircuit is a circuit that has dummy state and external inputs whose -/// lengths are specified in the `state_len` and `external_inputs_len` -/// parameters, without any constraints. +/// DummyCircuit is a circuit that has dummy state whose length is specified in the `state_len` +/// parameter, without any constraints. #[derive(Clone, Debug)] pub struct DummyCircuit { state_len: usize, - external_inputs_len: usize, } impl FCircuit for DummyCircuit { - type Params = (usize, usize); + type Params = usize; + type ExternalInputs = (); + type ExternalInputsVar = (); - fn new((state_len, external_inputs_len): Self::Params) -> Result { - Ok(Self { - state_len, - external_inputs_len, - }) + fn new(state_len: Self::Params) -> Result { + Ok(Self { state_len }) } fn state_len(&self) -> usize { self.state_len } - fn external_inputs_len(&self) -> usize { - self.external_inputs_len - } fn generate_step_constraints( &self, cs: ConstraintSystemRef, _i: usize, _z_i: Vec>, - _external_inputs: Vec>, + _external_inputs: Self::ExternalInputsVar, ) -> Result>, SynthesisError> { Vec::new_witness(cs.clone(), || Ok(vec![Zero::zero(); self.state_len])) } @@ -57,21 +51,21 @@ pub struct CubicFCircuit { #[cfg(test)] impl FCircuit for CubicFCircuit { type Params = (); + type ExternalInputs = (); + type ExternalInputsVar = (); + fn new(_params: Self::Params) -> Result { Ok(Self { _f: PhantomData }) } fn state_len(&self) -> usize { 1 } - fn external_inputs_len(&self) -> usize { - 0 - } fn generate_step_constraints( &self, cs: ConstraintSystemRef, _i: usize, z_i: Vec>, - _external_inputs: Vec>, + _external_inputs: Self::ExternalInputsVar, ) -> Result>, SynthesisError> { let five = FpVar::::new_constant(cs.clone(), F::from(5u32))?; let z_i = z_i[0].clone(); @@ -97,6 +91,8 @@ pub struct CustomFCircuit { impl FCircuit for CustomFCircuit { type Params = usize; + type ExternalInputs = (); + type ExternalInputsVar = (); fn new(params: Self::Params) -> Result { Ok(Self { @@ -107,15 +103,12 @@ impl FCircuit for CustomFCircuit { fn state_len(&self) -> usize { 1 } - fn external_inputs_len(&self) -> usize { - 0 - } fn generate_step_constraints( &self, _cs: ConstraintSystemRef, _i: usize, z_i: Vec>, - _external_inputs: Vec>, + _external_inputs: Self::ExternalInputsVar, ) -> Result>, SynthesisError> { let mut z_i1 = z_i[0].clone(); for _ in 0..self.n_constraints - 1 { @@ -153,9 +146,12 @@ impl> ConstraintSynthesizer for WrapperCircuit Vec::>::new_witness(cs.clone(), || Ok(self.z_i.unwrap_or(vec![F::zero()])))?; let z_i1 = Vec::>::new_input(cs.clone(), || Ok(self.z_i1.unwrap_or(vec![F::zero()])))?; - let computed_z_i1 = - self.FC - .generate_step_constraints(cs.clone(), 0, z_i.clone(), vec![])?; + let computed_z_i1 = self.FC.generate_step_constraints( + cs.clone(), + 0, + z_i.clone(), + FC::ExternalInputsVar::default(), + )?; use ark_r1cs_std::eq::EqGadget; computed_z_i1.enforce_equal(&z_i1)?; diff --git a/folding-schemes/src/lib.rs b/folding-schemes/src/lib.rs index aeb97c41..b3b132a6 100644 --- a/folding-schemes/src/lib.rs +++ b/folding-schemes/src/lib.rs @@ -197,7 +197,7 @@ pub trait FoldingScheme< fn prove_step( &mut self, rng: impl RngCore, - external_inputs: Vec, + external_inputs: FC::ExternalInputs, other_instances: Option, ) -> Result<(), Error>; @@ -237,7 +237,7 @@ pub trait MultiFolding< &self, rng: impl RngCore, state: Vec, - external_inputs: Vec, + external_inputs: FC::ExternalInputs, ) -> Result; /// Creates a new IncomingInstance for the given state, to be folded in the multi-folding step. @@ -245,7 +245,7 @@ pub trait MultiFolding< &self, rng: impl RngCore, state: Vec, - external_inputs: Vec, + external_inputs: FC::ExternalInputs, ) -> Result; } diff --git a/solidity-verifiers/Cargo.toml b/solidity-verifiers/Cargo.toml index 2228d5cd..db26d064 100644 --- a/solidity-verifiers/Cargo.toml +++ b/solidity-verifiers/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] ark-groth16 = "^0.5.0" -ark-bn254 = "^0.5.0" +ark-bn254 = { version = "^0.5.0", default-features = false, features = ["r1cs"] } ark-poly-commit = "^0.5.0" ark-serialize = "^0.5.0" askama = { version = "0.12.0", features = ["config"], default-features = false } @@ -22,7 +22,7 @@ ark-crypto-primitives = { version = "^0.5.0", default-features = false, features ark-snark = { version = "^0.5.0", default-features = false } ark-relations = { version = "^0.5.0", default-features = false } ark-r1cs-std = { version = "^0.5.0", default-features = false, features = ["parallel"] } -ark-grumpkin = { version = "^0.5.0", default-features = false } +ark-grumpkin = { version = "^0.5.0", default-features = false, features = ["r1cs"] } folding-schemes = { path = "../folding-schemes/", features=["light-test"]} experimental-frontends = { path = "../experimental-frontends/"} noname = { git = "https://github.com/dmpierre/noname" } diff --git a/solidity-verifiers/src/verifiers/nova_cyclefold.rs b/solidity-verifiers/src/verifiers/nova_cyclefold.rs index 95a9b794..26a6397a 100644 --- a/solidity-verifiers/src/verifiers/nova_cyclefold.rs +++ b/solidity-verifiers/src/verifiers/nova_cyclefold.rs @@ -190,21 +190,20 @@ mod tests { } impl FCircuit for CubicFCircuit { type Params = (); + type ExternalInputs = (); + type ExternalInputsVar = (); fn new(_params: Self::Params) -> Result { Ok(Self { _f: PhantomData }) } fn state_len(&self) -> usize { 1 } - fn external_inputs_len(&self) -> usize { - 0 - } fn generate_step_constraints( &self, cs: ConstraintSystemRef, _i: usize, z_i: Vec>, - _external_inputs: Vec>, + _external_inputs: Self::ExternalInputsVar, ) -> Result>, SynthesisError> { let five = FpVar::::new_constant(cs.clone(), F::from(5u32))?; let z_i = z_i[0].clone(); @@ -224,6 +223,8 @@ mod tests { } impl FCircuit for MultiInputsFCircuit { type Params = (); + type ExternalInputs = (); + type ExternalInputsVar = (); fn new(_params: Self::Params) -> Result { Ok(Self { _f: PhantomData }) @@ -231,16 +232,13 @@ mod tests { fn state_len(&self) -> usize { 5 } - fn external_inputs_len(&self) -> usize { - 0 - } /// generates the constraints for the step of F for the given z_i fn generate_step_constraints( &self, cs: ConstraintSystemRef, _i: usize, z_i: Vec>, - _external_inputs: Vec>, + _external_inputs: Self::ExternalInputsVar, ) -> Result>, SynthesisError> { let four = FpVar::::new_constant(cs.clone(), F::from(4u32))?; let forty = FpVar::::new_constant(cs.clone(), F::from(40u32))?; @@ -344,7 +342,8 @@ mod tests { let mut nova = NOVA::::init(&fs_params, f_circuit, z_0).unwrap(); for _ in 0..n_steps { - nova.prove_step(&mut rng, vec![], None).unwrap(); + nova.prove_step(&mut rng, FC::ExternalInputs::default(), None) + .unwrap(); } let start = Instant::now();