From 39d4ee327418e3be94e8da73f2973efcd8acb4a3 Mon Sep 17 00:00:00 2001 From: CPerezz Date: Tue, 15 Oct 2024 17:32:07 +0200 Subject: [PATCH 01/12] feat: Enable load_witness functionallity within FCircuit Since the witness is generated in the browser we need a way to store it such that it can flow from `prove_step` into `generate_constraints` due to the API restrictions. --- folding-schemes/src/folding/nova/circuits.rs | 3 +++ folding-schemes/src/frontend/mod.rs | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/folding-schemes/src/folding/nova/circuits.rs b/folding-schemes/src/folding/nova/circuits.rs index b657b61e..6778a4c5 100644 --- a/folding-schemes/src/folding/nova/circuits.rs +++ b/folding-schemes/src/folding/nova/circuits.rs @@ -410,6 +410,9 @@ where // get z_{i+1} from the F circuit let i_usize = self.i_usize.unwrap_or(0); + + // If we are in the browser-frontend case. The witness is already loaded within + // self.F.witness. This was done at `self.prove_step()` fn. let z_i1 = self .F .generate_step_constraints(cs.clone(), i_usize, z_i, external_inputs)?; diff --git a/folding-schemes/src/frontend/mod.rs b/folding-schemes/src/frontend/mod.rs index 7cf9e432..adffe409 100644 --- a/folding-schemes/src/frontend/mod.rs +++ b/folding-schemes/src/frontend/mod.rs @@ -48,6 +48,13 @@ pub trait FCircuit: Clone + Debug { z_i: Vec>, external_inputs: Vec>, // inputs that are not part of the state ) -> Result>, SynthesisError>; + + /// Allows to load pre-generated witness into the FCircuit implementor. + /// This is only needed by the browser use cases where we have already computed our + /// witness there. And we need a way to load it into the FCircuit since it's already computed. + /// + /// By default this method will simply do nothing. Only in the browser FCircuit implementors this will have usage. + fn load_witness(&mut self, _witness: Vec) {} } #[cfg(test)] From 039a40035e32c8958cc9a09064ea24b7008f1fe7 Mon Sep 17 00:00:00 2001 From: CPerezz Date: Tue, 15 Oct 2024 17:41:55 +0200 Subject: [PATCH 02/12] feat: Enable browser-folding with passed-in witness This commit introduces CircomFCircuitBrowser, a structure that facilitates folding of Circom-generated circuits in the browser via WASM. It loads R1CS data, handles witness inputs, and generates constraints for the folding process. Resolves: #155 --- folding-schemes/src/folding/nova/mod.rs | 21 +++ .../src/frontend/circom/browser.rs | 177 ++++++++++++++++++ folding-schemes/src/frontend/circom/mod.rs | 6 +- 3 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 folding-schemes/src/frontend/circom/browser.rs diff --git a/folding-schemes/src/folding/nova/mod.rs b/folding-schemes/src/folding/nova/mod.rs index d67c33b9..dea5bd85 100644 --- a/folding-schemes/src/folding/nova/mod.rs +++ b/folding-schemes/src/folding/nova/mod.rs @@ -642,10 +642,31 @@ where fn prove_step( &mut self, mut rng: impl RngCore, + // This contains the full witness when we're in the browser-frontend case external_inputs: Vec, // Nova does not support multi-instances folding _other_instances: Option, ) -> Result<(), Error> { + // Slice and separate between external inputs and frontend witness. + let (frontend_witness, external_inputs) = + if external_inputs.len() > self.F.external_inputs_len() { + ( + // Full circom witness trace + Some(external_inputs[..].to_vec()), + // Extracted external inputs from circom trace + external_inputs[1 + self.F.state_len() * 2 + ..1 + self.F.state_len() * 2 + self.F.external_inputs_len()] + .to_vec(), + ) + } else { + (None, external_inputs) + }; + + // If we are in the browser-case (frontend_witness = Some(witness)) then we load the witness into the FCircuit. + if let Some(witness) = frontend_witness { + self.F.load_witness(witness) + }; + // ensure that commitments are blinding if user has specified so. if H && self.i >= C1::ScalarField::one() { let blinding_commitments = if self.i == C1::ScalarField::one() { diff --git a/folding-schemes/src/frontend/circom/browser.rs b/folding-schemes/src/frontend/circom/browser.rs new file mode 100644 index 00000000..7ee29c77 --- /dev/null +++ b/folding-schemes/src/frontend/circom/browser.rs @@ -0,0 +1,177 @@ +use crate::frontend::FCircuit; +use crate::frontend::FpVar::Var; +use crate::Error; +use ark_circom::circom::{CircomCircuit, R1CS as CircomR1CS}; +use ark_circom::circom::{R1CSFile, R1CS}; +use ark_ff::{BigInteger, PrimeField}; +use ark_r1cs_std::alloc::AllocVar; +use ark_r1cs_std::fields::fp::FpVar; +use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError}; +use ark_std::fmt::Debug; +use byteorder::{LittleEndian, ReadBytesExt}; +use std::io::{BufReader, Cursor, Read}; +use std::usize; + +/// This circuit is the one we will use in order to fold from the browser. +/// +/// A clear example on how to do all of this can be seen at: +/// +#[derive(Clone, Debug)] +pub struct CircomFCircuitBrowser { + pub state_len: usize, + pub external_inputs_len: usize, + pub witness: Vec, + pub r1cs: CircomR1CS, +} + +impl FCircuit for CircomFCircuitBrowser { + /// (r1cs_file_bytes, state_len, external_inputs_len) + type Params = (Vec, usize, usize); + + fn new(params: Self::Params) -> Result { + let (r1cs_bytes, state_len, external_inputs_len) = params; + let reader = BufReader::new(Cursor::new(&r1cs_bytes[..])); + + let mut r1cs: R1CS = R1CSFile::::new(reader) + .expect("Error reading R1cs") + .into(); + + // That's some weird magic. Ask @dmpierre + r1cs.wire_mapping = None; + + Ok(Self { + state_len, + external_inputs_len, + witness: vec![F::zero(); r1cs.num_variables], + r1cs, + }) + } + + fn state_len(&self) -> usize { + self.state_len + } + fn external_inputs_len(&self) -> usize { + self.external_inputs_len + } + + fn step_native( + &self, + _i: usize, + z_i: Vec, + external_inputs: Vec, + ) -> Result, Error> { + // Should we keep these? + assert_eq!(z_i.len(), self.state_len()); + assert_eq!(external_inputs.len(), self.external_inputs_len()); + + // Extracts the z_i1(next state) from the witness vector and external inputs. + let z_i1 = z_i[..self.state_len()].to_vec(); + Ok(z_i1) + } + + fn generate_step_constraints( + &self, + cs: ConstraintSystemRef, + _i: usize, + z_i: Vec>, + // This in reality contains the `witness` passed from the `prove_step` call from the + // browser. + _external_inputs: Vec>, + ) -> Result>, SynthesisError> { + // Since public inputs are already allocated variables, we will tell `circom-compat` to not re-allocate those + let mut already_allocated_public_inputs = vec![]; + for var in z_i.iter() { + match var { + Var(var) => already_allocated_public_inputs.push(var.variable), + _ => return Err(SynthesisError::Unsatisfiable), // allocated z_i should be Var + } + } + + // Initializes the CircomCircuit. + let circom_circuit = CircomCircuit { + r1cs: self.r1cs.clone(), + witness: Some(self.witness.clone()), + public_inputs_indexes: already_allocated_public_inputs, + // Since public inputs are already allocated variables, we will tell `circom-compat` to not re-allocate those + allocate_inputs_as_witnesses: true, + }; + + // Generates the constraints for the circom_circuit. + circom_circuit.generate_constraints(cs.clone())?; + + // Extracts the z_i1(next state) from the witness vector. + let z_i1: Vec> = Vec::>::new_witness(cs.clone(), || { + Ok(self.witness[1..1 + self.state_len()].to_vec()) + })?; + + Ok(z_i1) + } + + fn load_witness(&mut self, witness: Vec) { + self.witness = witness; + } +} + +/// Load Circom-generated witness from u8 array by a reader. +/// +/// This fn has been taken from +pub fn load_witness_from_bin_reader(mut reader: R) -> Vec { + let mut wtns_header = [0u8; 4]; + reader.read_exact(&mut wtns_header).expect("read_error"); + if wtns_header != [119, 116, 110, 115] { + // ruby -e 'p "wtns".bytes' => [119, 116, 110, 115] + panic!("invalid file header"); + } + let version = reader.read_u32::().expect("read_error"); + // println!("wtns version {}", version); + if version > 2 { + panic!("unsupported file version"); + } + let num_sections = reader.read_u32::().expect("read_error"); + if num_sections != 2 { + panic!("invalid num sections"); + } + // read the first section + let sec_type = reader.read_u32::().expect("read_error"); + if sec_type != 1 { + panic!("invalid section type"); + } + let sec_size = reader.read_u64::().expect("read_error"); + if sec_size != 4 + 32 + 4 { + panic!("invalid section len") + } + let field_size: u32 = reader.read_u32::().expect("read_error"); + if field_size != 32 { + panic!("invalid field byte size"); + } + let mut prime = vec![0u8; field_size as usize]; + reader.read_exact(&mut prime).expect("read_error"); + // if prime != hex!("010000f093f5e1439170b97948e833285d588181b64550b829a031e1724e6430") { + // panic!("invalid curve prime {:?}", prime); + // } + let witness_len: u32 = reader.read_u32::().expect("read_error"); + // println!("witness len {}", witness_len); + let sec_type = reader.read_u32::().expect("read_error"); + if sec_type != 2 { + panic!("invalid section type"); + } + let sec_size = reader.read_u64::().expect("read_error"); + if sec_size != (witness_len * field_size) as u64 { + panic!("invalid witness section size"); + } + let mut result = Vec::with_capacity(witness_len as usize); + for _ in 0..witness_len { + result.push(read_field::<&mut R, F>(&mut reader).expect("read_error")); + } + result +} + +fn read_field( + mut reader: R, +) -> Result { + let mut repr: Vec = F::ZERO.into_bigint().to_bytes_le(); + for digit in repr.iter_mut() { + *digit = reader.read_u8()?; + } + Ok(F::from_le_bytes_mod_order(&repr[..])) +} diff --git a/folding-schemes/src/frontend/circom/mod.rs b/folding-schemes/src/frontend/circom/mod.rs index a261f722..00473e43 100644 --- a/folding-schemes/src/frontend/circom/mod.rs +++ b/folding-schemes/src/frontend/circom/mod.rs @@ -13,7 +13,10 @@ use num_bigint::BigInt; use std::rc::Rc; use std::{fmt, usize}; +pub mod browser; pub mod utils; + +pub use browser::{load_witness_from_bin_reader, CircomFCircuitBrowser}; use utils::CircomWrapper; type ClosurePointer = Rc, Vec) -> Result, Error>>; @@ -33,7 +36,8 @@ impl fmt::Debug for CustomStepNative { } } -/// Define CircomFCircuit +/// This circuit is the one we will use in order to fold circom circuits +/// from a non-browser environment. #[derive(Clone, Debug)] pub struct CircomFCircuit { circom_wrapper: CircomWrapper, From 67a066dee59721d5d81468045f837e69eaa563d5 Mon Sep 17 00:00:00 2001 From: CPerezz Date: Tue, 15 Oct 2024 17:52:31 +0200 Subject: [PATCH 03/12] chore: Add "browser" feature and leave CircomFCircuitBrowser under it --- folding-schemes/Cargo.toml | 66 +++++++++++++++------- folding-schemes/src/frontend/circom/mod.rs | 2 + 2 files changed, 49 insertions(+), 19 deletions(-) diff --git a/folding-schemes/Cargo.toml b/folding-schemes/Cargo.toml index d7c45482..d0773bf8 100644 --- a/folding-schemes/Cargo.toml +++ b/folding-schemes/Cargo.toml @@ -4,20 +4,40 @@ version = "0.1.0" edition = "2021" [dependencies] -ark-ec = { version = "^0.4.0", default-features = false, features = ["parallel"] } -ark-ff = { version = "^0.4.0", default-features = false, features = ["parallel", "asm"] } -ark-poly = { version = "^0.4.0", default-features = false, features = ["parallel"] } -ark-std = { version = "^0.4.0", default-features = false, features = ["parallel"] } -ark-crypto-primitives = { version = "^0.4.0", default-features = false, features = ["r1cs", "sponge", "crh", "parallel"] } +ark-ec = { version = "^0.4.0", default-features = false, features = [ + "parallel", +] } +ark-ff = { version = "^0.4.0", default-features = false, features = [ + "parallel", + "asm", +] } +ark-poly = { version = "^0.4.0", default-features = false, features = [ + "parallel", +] } +ark-std = { version = "^0.4.0", default-features = false, features = [ + "parallel", +] } +ark-crypto-primitives = { version = "^0.4.0", default-features = false, features = [ + "r1cs", + "sponge", + "crh", + "parallel", +] } ark-grumpkin = { version = "0.4.0", default-features = false } -ark-poly-commit = { version = "^0.4.0", default-features = false, features = ["parallel"] } +ark-poly-commit = { version = "^0.4.0", default-features = false, features = [ + "parallel", +] } ark-relations = { version = "^0.4.0", default-features = false } # this is patched at the workspace level -ark-r1cs-std = { version = "0.4.0", default-features = false, features = ["parallel"] } +ark-r1cs-std = { version = "0.4.0", default-features = false, features = [ + "parallel", +] } ark-snark = { version = "^0.4.0", default-features = false } ark-serialize = { version = "^0.4.0", default-features = false } ark-circom = { git = "https://github.com/arnaucube/circom-compat", default-features = false } -ark-groth16 = { version = "^0.4.0", default-features = false, features = ["parallel"]} +ark-groth16 = { version = "^0.4.0", default-features = false, features = [ + "parallel", +] } ark-bn254 = { version = "^0.4.0", default-features = false } thiserror = "1.0" rayon = "1" @@ -27,26 +47,30 @@ color-eyre = "=0.6.2" sha3 = "0.10" ark-noname = { git = "https://github.com/dmpierre/ark-noname", branch = "feat/sonobe-integration" } noname = { git = "https://github.com/dmpierre/noname" } -serde_json = "1.0.85" # to (de)serialize JSON +serde_json = "1.0.85" # to (de)serialize JSON serde = "1.0.203" -acvm = { git = "https://github.com/noir-lang/noir", rev="2b4853e", default-features = false } -noir_arkworks_backend = { package="arkworks_backend", git = "https://github.com/dmpierre/arkworks_backend", branch = "feat/sonobe-integration" } +acvm = { git = "https://github.com/noir-lang/noir", rev = "2b4853e", default-features = false } +noir_arkworks_backend = { package = "arkworks_backend", git = "https://github.com/dmpierre/arkworks_backend", branch = "feat/sonobe-integration" } log = "0.4" # tmp import for espresso's sumcheck -espresso_subroutines = {git="https://github.com/EspressoSystems/hyperplonk", package="subroutines"} +espresso_subroutines = { git = "https://github.com/EspressoSystems/hyperplonk", package = "subroutines" } +# Dependencies needed for browser folding +byteorder = { version = "1.4.3", optional = true } [dev-dependencies] -ark-pallas = {version="0.4.0", features=["r1cs"]} -ark-vesta = {version="0.4.0", features=["r1cs"]} -ark-bn254 = {version="0.4.0", features=["r1cs"]} -ark-grumpkin = {version="0.4.0", features=["r1cs"]} +ark-pallas = { version = "0.4.0", features = ["r1cs"] } +ark-vesta = { version = "0.4.0", features = ["r1cs"] } +ark-bn254 = { version = "0.4.0", features = ["r1cs"] } +ark-grumpkin = { version = "0.4.0", features = ["r1cs"] } # Note: do not use the MNTx_298 curves in practice due security reasons, here # we only use them in the tests. -ark-mnt4-298 = {version="0.4.0", features=["r1cs"]} -ark-mnt6-298 = {version="0.4.0", features=["r1cs"]} +ark-mnt4-298 = { version = "0.4.0", features = ["r1cs"] } +ark-mnt6-298 = { version = "0.4.0", features = ["r1cs"] } rand = "0.8.5" -tracing = { version = "0.1", default-features = false, features = [ "attributes" ] } +tracing = { version = "0.1", default-features = false, features = [ + "attributes", +] } tracing-subscriber = { version = "0.2" } # This allows the crate to be built when targeting WASM. @@ -58,6 +82,7 @@ getrandom = { version = "0.2", features = ["js"] } default = ["ark-circom/default", "parallel"] parallel = [] wasm = ["ark-circom/wasm"] +browser = ["wasm", "byteorder"] light-test = [] @@ -72,3 +97,6 @@ path = "../examples/multi_inputs.rs" [[example]] name = "external_inputs" path = "../examples/external_inputs.rs" + +[lib] +crate-type = ["cdylib", "rlib"] diff --git a/folding-schemes/src/frontend/circom/mod.rs b/folding-schemes/src/frontend/circom/mod.rs index 00473e43..cb70eb43 100644 --- a/folding-schemes/src/frontend/circom/mod.rs +++ b/folding-schemes/src/frontend/circom/mod.rs @@ -13,9 +13,11 @@ use num_bigint::BigInt; use std::rc::Rc; use std::{fmt, usize}; +#[cfg(feature = "browser")] pub mod browser; pub mod utils; +#[cfg(feature = "browser")] pub use browser::{load_witness_from_bin_reader, CircomFCircuitBrowser}; use utils::CircomWrapper; From 9bcf84c30f90fffa0a7459182ed5a9895c370949 Mon Sep 17 00:00:00 2001 From: CPerezz Date: Wed, 16 Oct 2024 16:12:41 +0200 Subject: [PATCH 04/12] chore: Address review leftovers --- folding-schemes/src/frontend/circom/browser.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/folding-schemes/src/frontend/circom/browser.rs b/folding-schemes/src/frontend/circom/browser.rs index 7ee29c77..3817f8b0 100644 --- a/folding-schemes/src/frontend/circom/browser.rs +++ b/folding-schemes/src/frontend/circom/browser.rs @@ -119,11 +119,9 @@ pub fn load_witness_from_bin_reader(mut reader: R) -> Ve let mut wtns_header = [0u8; 4]; reader.read_exact(&mut wtns_header).expect("read_error"); if wtns_header != [119, 116, 110, 115] { - // ruby -e 'p "wtns".bytes' => [119, 116, 110, 115] panic!("invalid file header"); } let version = reader.read_u32::().expect("read_error"); - // println!("wtns version {}", version); if version > 2 { panic!("unsupported file version"); } @@ -146,11 +144,7 @@ pub fn load_witness_from_bin_reader(mut reader: R) -> Ve } let mut prime = vec![0u8; field_size as usize]; reader.read_exact(&mut prime).expect("read_error"); - // if prime != hex!("010000f093f5e1439170b97948e833285d588181b64550b829a031e1724e6430") { - // panic!("invalid curve prime {:?}", prime); - // } let witness_len: u32 = reader.read_u32::().expect("read_error"); - // println!("witness len {}", witness_len); let sec_type = reader.read_u32::().expect("read_error"); if sec_type != 2 { panic!("invalid section type"); From 206984cd9ec5e548eaaa24d9f06dff533c94d5d4 Mon Sep 17 00:00:00 2001 From: CPerezz Date: Thu, 17 Oct 2024 12:26:59 +0200 Subject: [PATCH 05/12] chore: Ammend review comments --- folding-schemes/src/folding/nova/mod.rs | 39 ++++++++++--------- .../src/frontend/circom/browser.rs | 2 - 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/folding-schemes/src/folding/nova/mod.rs b/folding-schemes/src/folding/nova/mod.rs index dea5bd85..dcb3e46d 100644 --- a/folding-schemes/src/folding/nova/mod.rs +++ b/folding-schemes/src/folding/nova/mod.rs @@ -647,25 +647,28 @@ where // Nova does not support multi-instances folding _other_instances: Option, ) -> Result<(), Error> { - // Slice and separate between external inputs and frontend witness. - let (frontend_witness, external_inputs) = - if external_inputs.len() > self.F.external_inputs_len() { - ( - // Full circom witness trace - Some(external_inputs[..].to_vec()), - // Extracted external inputs from circom trace - external_inputs[1 + self.F.state_len() * 2 - ..1 + self.F.state_len() * 2 + self.F.external_inputs_len()] - .to_vec(), - ) - } else { - (None, external_inputs) + #[cfg(feature = "browser")] + { + // Slice and separate between external inputs and frontend witness. + let (frontend_witness, external_inputs) = + if external_inputs.len() > self.F.external_inputs_len() { + ( + // Full circom witness trace + Some(external_inputs[..].to_vec()), + // Extracted external inputs from circom trace + external_inputs[1 + self.F.state_len() * 2 + ..1 + self.F.state_len() * 2 + self.F.external_inputs_len()] + .to_vec(), + ) + } else { + (None, external_inputs) + }; + + // If we are in the browser-case (frontend_witness = Some(witness)) then we load the witness into the FCircuit. + if let Some(witness) = frontend_witness { + self.F.load_witness(witness) }; - - // If we are in the browser-case (frontend_witness = Some(witness)) then we load the witness into the FCircuit. - if let Some(witness) = frontend_witness { - self.F.load_witness(witness) - }; + } // ensure that commitments are blinding if user has specified so. if H && self.i >= C1::ScalarField::one() { diff --git a/folding-schemes/src/frontend/circom/browser.rs b/folding-schemes/src/frontend/circom/browser.rs index 3817f8b0..8185200a 100644 --- a/folding-schemes/src/frontend/circom/browser.rs +++ b/folding-schemes/src/frontend/circom/browser.rs @@ -74,8 +74,6 @@ impl FCircuit for CircomFCircuitBrowser { cs: ConstraintSystemRef, _i: usize, z_i: Vec>, - // This in reality contains the `witness` passed from the `prove_step` call from the - // browser. _external_inputs: Vec>, ) -> Result>, SynthesisError> { // Since public inputs are already allocated variables, we will tell `circom-compat` to not re-allocate those From 0cf1aaeaf4978df56c6ae48286e63639afa76800 Mon Sep 17 00:00:00 2001 From: CPerezz Date: Fri, 18 Oct 2024 11:19:29 +0200 Subject: [PATCH 06/12] fix: leftover `parallel` feature code --- .../src/utils/espresso/sum_check/prover.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/folding-schemes/src/utils/espresso/sum_check/prover.rs b/folding-schemes/src/utils/espresso/sum_check/prover.rs index 24440d24..007a1cc4 100644 --- a/folding-schemes/src/utils/espresso/sum_check/prover.rs +++ b/folding-schemes/src/utils/espresso/sum_check/prover.rs @@ -102,14 +102,15 @@ impl SumCheckProver for IOPProverState { self.challenges.push(*chal); let r = self.challenges[self.round - 1]; - // #[cfg(feature = "parallel")] + #[cfg(feature = "parallel")] flattened_ml_extensions .par_iter_mut() .for_each(|mle| *mle = fix_variables(mle, &[r])); - // #[cfg(not(feature = "parallel"))] - // flattened_ml_extensions - // .iter_mut() - // .for_each(|mle| *mle = fix_variables(mle, &[r])); + + #[cfg(not(feature = "parallel"))] + flattened_ml_extensions + .iter_mut() + .for_each(|mle| *mle = fix_variables(mle, &[r])); } else if self.round > 0 { return Err(PolyIOPErrors::InvalidProver( "verifier message is empty".to_string(), From 47d908a749331f837afcf583e9ef353535e05cc4 Mon Sep 17 00:00:00 2001 From: CPerezz Date: Fri, 18 Oct 2024 11:22:16 +0200 Subject: [PATCH 07/12] update: Remove `wasm` feature As @arnaucube correctly suggested to me, the `wasm` feature can be omitted as the `wasmer` loader does not require it. Therefore, feature set, CI and code has been updated accordingly. Also, the browser feature has been renamed to `circom-browser`. --- .github/scripts/wasm-target-test-build.sh | 2 +- .github/workflows/ci.yml | 12 ++++++------ folding-schemes/Cargo.toml | 3 +-- folding-schemes/src/folding/nova/circuits.rs | 2 +- folding-schemes/src/folding/nova/mod.rs | 6 +++--- folding-schemes/src/frontend/circom/mod.rs | 6 +++--- folding-schemes/src/frontend/mod.rs | 4 ++-- 7 files changed, 17 insertions(+), 18 deletions(-) diff --git a/.github/scripts/wasm-target-test-build.sh b/.github/scripts/wasm-target-test-build.sh index e86ef832..026fac82 100644 --- a/.github/scripts/wasm-target-test-build.sh +++ b/.github/scripts/wasm-target-test-build.sh @@ -15,7 +15,7 @@ cp "${GIT_ROOT}/rust-toolchain" . rustup target add wasm32-unknown-unknown wasm32-wasi # add dependencies -cargo add --path "${GIT_ROOT}/folding-schemes" --features wasm, parallel +cargo add --path "${GIT_ROOT}/folding-schemes" --features circom-browser cargo add getrandom --features js --target wasm32-unknown-unknown # test build for wasm32-* targets diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7ef464e..d731e6c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: feature_set: [basic] include: - feature_set: basic - features: --features default,light-test + features: --features default, light-test steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 @@ -99,7 +99,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: build - args: -p folding-schemes --no-default-features --target ${{ matrix.target }} --features "wasm, parallel" + args: -p folding-schemes --target ${{ matrix.target }} --features circom-frontend - name: Run wasm-compat script run: | chmod +x .github/scripts/wasm-target-test-build.sh @@ -156,13 +156,13 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - feature_set: [basic, wasm] + feature_set: [basic, circom-browser] include: - feature_set: basic features: --features default,light-test - # We only want to test `folding-schemes` package with `wasm` feature. - - feature_set: wasm - features: -p folding-schemes --features wasm,parallel --target wasm32-unknown-unknown + # We only want to test `folding-schemes` package with `circom-browser` feature. + - feature_set: circom-browser + features: -p folding-schemes --features circom-browser, parallel --target wasm32-unknown-unknown steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 diff --git a/folding-schemes/Cargo.toml b/folding-schemes/Cargo.toml index d0773bf8..470df177 100644 --- a/folding-schemes/Cargo.toml +++ b/folding-schemes/Cargo.toml @@ -81,8 +81,7 @@ getrandom = { version = "0.2", features = ["js"] } [features] default = ["ark-circom/default", "parallel"] parallel = [] -wasm = ["ark-circom/wasm"] -browser = ["wasm", "byteorder"] +circom-browser = ["ark-circom/wasm", "byteorder"] light-test = [] diff --git a/folding-schemes/src/folding/nova/circuits.rs b/folding-schemes/src/folding/nova/circuits.rs index 6778a4c5..2697d95f 100644 --- a/folding-schemes/src/folding/nova/circuits.rs +++ b/folding-schemes/src/folding/nova/circuits.rs @@ -411,7 +411,7 @@ where // get z_{i+1} from the F circuit let i_usize = self.i_usize.unwrap_or(0); - // If we are in the browser-frontend case. The witness is already loaded within + // If we are in the circom-browser-frontend case. The witness is already loaded within // self.F.witness. This was done at `self.prove_step()` fn. let z_i1 = self .F diff --git a/folding-schemes/src/folding/nova/mod.rs b/folding-schemes/src/folding/nova/mod.rs index dcb3e46d..62c9d913 100644 --- a/folding-schemes/src/folding/nova/mod.rs +++ b/folding-schemes/src/folding/nova/mod.rs @@ -642,12 +642,12 @@ where fn prove_step( &mut self, mut rng: impl RngCore, - // This contains the full witness when we're in the browser-frontend case + // This contains the full witness when we're in the circom-browser-frontend case external_inputs: Vec, // Nova does not support multi-instances folding _other_instances: Option, ) -> Result<(), Error> { - #[cfg(feature = "browser")] + #[cfg(feature = "circom-browser")] { // Slice and separate between external inputs and frontend witness. let (frontend_witness, external_inputs) = @@ -664,7 +664,7 @@ where (None, external_inputs) }; - // If we are in the browser-case (frontend_witness = Some(witness)) then we load the witness into the FCircuit. + // If we are in the circom-browser-case (frontend_witness = Some(witness)) then we load the witness into the FCircuit. if let Some(witness) = frontend_witness { self.F.load_witness(witness) }; diff --git a/folding-schemes/src/frontend/circom/mod.rs b/folding-schemes/src/frontend/circom/mod.rs index cb70eb43..8b9982a0 100644 --- a/folding-schemes/src/frontend/circom/mod.rs +++ b/folding-schemes/src/frontend/circom/mod.rs @@ -13,11 +13,11 @@ use num_bigint::BigInt; use std::rc::Rc; use std::{fmt, usize}; -#[cfg(feature = "browser")] +#[cfg(feature = "circom-browser")] pub mod browser; pub mod utils; -#[cfg(feature = "browser")] +#[cfg(feature = "circom-browser")] pub use browser::{load_witness_from_bin_reader, CircomFCircuitBrowser}; use utils::CircomWrapper; @@ -38,7 +38,7 @@ impl fmt::Debug for CustomStepNative { } } -/// This circuit is the one we will use in order to fold circom circuits +/// This circuit is the one we will use in order to fold Circom circuits /// from a non-browser environment. #[derive(Clone, Debug)] pub struct CircomFCircuit { diff --git a/folding-schemes/src/frontend/mod.rs b/folding-schemes/src/frontend/mod.rs index adffe409..e68c971f 100644 --- a/folding-schemes/src/frontend/mod.rs +++ b/folding-schemes/src/frontend/mod.rs @@ -50,10 +50,10 @@ pub trait FCircuit: Clone + Debug { ) -> Result>, SynthesisError>; /// Allows to load pre-generated witness into the FCircuit implementor. - /// This is only needed by the browser use cases where we have already computed our + /// This is only needed by the circom-browser use cases where we have already computed our /// witness there. And we need a way to load it into the FCircuit since it's already computed. /// - /// By default this method will simply do nothing. Only in the browser FCircuit implementors this will have usage. + /// By default this method will simply do nothing. Only in the circom-browser FCircuit implementors this will have usage. fn load_witness(&mut self, _witness: Vec) {} } From 5c678493efa6be5071316c5d484a08a30bf75bb1 Mon Sep 17 00:00:00 2001 From: CPerezz Date: Fri, 18 Oct 2024 11:34:27 +0200 Subject: [PATCH 08/12] fix: Exclude constant 1 from zi_1 returned in browser::step_native --- folding-schemes/src/frontend/circom/browser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/folding-schemes/src/frontend/circom/browser.rs b/folding-schemes/src/frontend/circom/browser.rs index 8185200a..9c3ac20f 100644 --- a/folding-schemes/src/frontend/circom/browser.rs +++ b/folding-schemes/src/frontend/circom/browser.rs @@ -65,7 +65,7 @@ impl FCircuit for CircomFCircuitBrowser { assert_eq!(external_inputs.len(), self.external_inputs_len()); // Extracts the z_i1(next state) from the witness vector and external inputs. - let z_i1 = z_i[..self.state_len()].to_vec(); + let z_i1 = z_i[1..1 + self.state_len()].to_vec(); Ok(z_i1) } From 29ba23e25ad895cf7f8dae9262cbb0f0fec8b48a Mon Sep 17 00:00:00 2001 From: CPerezz Date: Fri, 18 Oct 2024 11:36:19 +0200 Subject: [PATCH 09/12] fix: Avoid whitespaces within features passed to CLI commands --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d731e6c5..1add8edc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: feature_set: [basic] include: - feature_set: basic - features: --features default, light-test + features: --features default,light-test steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 @@ -162,7 +162,7 @@ jobs: features: --features default,light-test # We only want to test `folding-schemes` package with `circom-browser` feature. - feature_set: circom-browser - features: -p folding-schemes --features circom-browser, parallel --target wasm32-unknown-unknown + features: -p folding-schemes --features circom-browser,parallel --target wasm32-unknown-unknown steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 From 291082e1f68ca63de7639346701906eee44af64e Mon Sep 17 00:00:00 2001 From: CPerezz Date: Mon, 21 Oct 2024 15:28:17 +0200 Subject: [PATCH 10/12] fix: Clippy warns --- folding-schemes/src/folding/nova/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/folding-schemes/src/folding/nova/mod.rs b/folding-schemes/src/folding/nova/mod.rs index 62c9d913..1cb7b208 100644 --- a/folding-schemes/src/folding/nova/mod.rs +++ b/folding-schemes/src/folding/nova/mod.rs @@ -648,7 +648,7 @@ where _other_instances: Option, ) -> Result<(), Error> { #[cfg(feature = "circom-browser")] - { + let external_inputs = { // Slice and separate between external inputs and frontend witness. let (frontend_witness, external_inputs) = if external_inputs.len() > self.F.external_inputs_len() { @@ -668,7 +668,9 @@ where if let Some(witness) = frontend_witness { self.F.load_witness(witness) }; - } + + external_inputs + }; // ensure that commitments are blinding if user has specified so. if H && self.i >= C1::ScalarField::one() { From d2d543f9ca91c4d4a1d1d0bf82f2c89451cce62b Mon Sep 17 00:00:00 2001 From: CPerezz Date: Mon, 21 Oct 2024 15:33:42 +0200 Subject: [PATCH 11/12] fix: CI feature typo --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1add8edc..64749770 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -99,7 +99,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: build - args: -p folding-schemes --target ${{ matrix.target }} --features circom-frontend + args: -p folding-schemes --target ${{ matrix.target }} --features circom-browser - name: Run wasm-compat script run: | chmod +x .github/scripts/wasm-target-test-build.sh From 63f599ece3943c11f47804429eb01eb84b7688b7 Mon Sep 17 00:00:00 2001 From: CPerezz Date: Mon, 21 Oct 2024 23:15:13 +0200 Subject: [PATCH 12/12] fix: Update CI package to test for WASM build to `frontends` --- .github/workflows/ci.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 49eeef6f..92a4f44b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -99,7 +99,12 @@ jobs: uses: actions-rs/cargo@v1 with: command: build - args: -p folding-schemes --target ${{ matrix.target }} --features circom-browser + args: -p frontends --no-default-features --target ${{ matrix.target }} --features "circom-browser, parallel" + - name: Wasm-compat folding-schemes build + uses: actions-rs/cargo@v1 + with: + command: build + args: -p folding-schemes --no-default-features --target ${{ matrix.target }} --features "default,light-test" - name: Run wasm-compat script run: | chmod +x .github/scripts/wasm-target-test-build.sh @@ -156,13 +161,13 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - feature_set: [basic, circom-browser] + feature_set: [basic, wasm] include: - feature_set: basic features: --features default,light-test - # We only want to test `folding-schemes` package with `circom-browser` feature. - - feature_set: circom-browser - features: -p folding-schemes --features circom-browser,parallel --target wasm32-unknown-unknown + # We only want to test `frontends` package with `wasm` feature. + - feature_set: wasm + features: -p frontends --features circom-browser,parallel --target wasm32-unknown-unknown steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1