From a4905c8a062258d38b987ec05b663f21b1c2571b Mon Sep 17 00:00:00 2001 From: arnaucube Date: Mon, 11 Mar 2024 12:32:50 +0100 Subject: [PATCH] Add external inputs logic to F function/circuit. Add an example of usage with external inputs too. (#78) * Add external inputs logic to F function/circuit. Add an example of usage with external inputs too. * Add examples run into CI --- .github/workflows/ci.yml | 13 ++ folding-schemes/examples/external_inputs.rs | 213 ++++++++++++++++++ folding-schemes/examples/multi_inputs.rs | 15 +- folding-schemes/examples/sha256.rs | 15 +- .../src/folding/hypernova/lcccs.rs | 2 +- folding-schemes/src/folding/nova/circuits.rs | 7 +- .../src/folding/nova/decider_eth_circuit.rs | 6 +- folding-schemes/src/folding/nova/mod.rs | 21 +- folding-schemes/src/frontend/mod.rs | 30 ++- folding-schemes/src/lib.rs | 2 + 10 files changed, 287 insertions(+), 37 deletions(-) create mode 100644 folding-schemes/examples/external_inputs.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c1ecc6..ffab90f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,6 +72,18 @@ jobs: run: | cargo test --doc + examples: + if: github.event.pull_request.draft == false + name: Run examples & examples tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + - name: Run examples tests + run: cargo test --examples + - name: Run examples + run: cargo run --release --example 2>&1 | grep -E '^ ' | xargs -n1 cargo run --release --example + fmt: if: github.event.pull_request.draft == false name: Rustfmt @@ -104,6 +116,7 @@ jobs: args: --all-targets --all-features -- -D warnings typos: + if: github.event.pull_request.draft == false name: Spell Check with Typos runs-on: ubuntu-latest steps: diff --git a/folding-schemes/examples/external_inputs.rs b/folding-schemes/examples/external_inputs.rs new file mode 100644 index 0000000..437c1fd --- /dev/null +++ b/folding-schemes/examples/external_inputs.rs @@ -0,0 +1,213 @@ +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(clippy::upper_case_acronyms)] + +use ark_crypto_primitives::{ + crh::{ + poseidon::constraints::{CRHGadget, CRHParametersVar}, + poseidon::CRH, + CRHScheme, CRHSchemeGadget, + }, + sponge::{poseidon::PoseidonConfig, Absorb}, +}; +use ark_ff::PrimeField; +use ark_pallas::{constraints::GVar, Fr, Projective}; +use ark_r1cs_std::fields::fp::FpVar; +use ark_r1cs_std::{alloc::AllocVar, fields::FieldVar}; +use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError}; +use ark_std::Zero; +use ark_vesta::{constraints::GVar as GVar2, Projective as Projective2}; +use core::marker::PhantomData; +use std::time::Instant; + +use folding_schemes::commitment::pedersen::Pedersen; +use folding_schemes::folding::nova::Nova; +use folding_schemes::frontend::FCircuit; +use folding_schemes::{Error, FoldingScheme}; +mod utils; +use folding_schemes::transcript::poseidon::poseidon_test_config; +use utils::test_nova_setup; + +/// This is the circuit that we want to fold, it implements the FCircuit trait. The parameter z_i +/// denotes the current state which contains 2 elements, and z_{i+1} denotes the next state which +/// we get by applying the step. +/// +/// In this example we set the state to be the previous state together with an external input, and +/// the new state is an array which contains the new state and a zero which will be ignored. +/// +/// This is useful for example if we want to fold multiple verifications of signatures, where the +/// circuit F checks the signature and is folded for each of the signatures and public keys. To +/// keep things simpler, the following example does not verify signatures but does a similar +/// approach with a chain of hashes, where each iteration hashes the previous step output (z_i) +/// together with an external input (w_i). +/// +/// w_1 w_2 w_3 w_4 +/// │ │ │ │ +/// ▼ ▼ ▼ ▼ +/// ┌─┐ ┌─┐ ┌─┐ ┌─┐ +/// ─────►│F├────►│F├────►│F├────►│F├────► +/// z_1 └─┘ z_2 └─┘ z_3 └─┘ z_4 └─┘ z_5 +/// +/// +/// where each F is: +/// w_i +/// │ ┌────────────────────┐ +/// │ │FCircuit │ +/// │ │ │ +/// └────►│ h =Hash(z_i[0],w_i)│ +/// │ │ =Hash(v, w_i) │ +/// ────────►│ │ ├───────► +/// z_i=[v,0] │ └──►z_{i+1}=[h, 0] │ z_{i+1}=[h,0] +/// │ │ +/// └────────────────────┘ +/// +/// where each w_i value is set at the external_inputs array. +/// +/// The last state z_i is used together with the external input w_i as inputs to compute the new +/// state z_{i+1}. +/// The function F will output the new state in an array of two elements, where the second element +/// is a 0. In other words, z_{i+1} = [F([z_i, w_i]), 0], and the 0 will be replaced by w_{i+1} in +/// the next iteration, so z_{i+2} = [F([z_{i+1}, w_{i+1}]), 0]. +#[derive(Clone, Debug)] +pub struct ExternalInputsCircuits +where + F: Absorb, +{ + _f: PhantomData, + poseidon_config: PoseidonConfig, + external_inputs: Vec, +} +impl FCircuit for ExternalInputsCircuits +where + F: Absorb, +{ + type Params = (PoseidonConfig, Vec); // where Vec contains the external inputs + + fn new(params: Self::Params) -> Self { + Self { + _f: PhantomData, + poseidon_config: params.0, + external_inputs: params.1, + } + } + fn state_len(&self) -> usize { + 2 + } + + /// computes the next state values in place, assigning z_{i+1} into z_i, and computing the new + /// z_{i+1} + fn step_native(&self, i: usize, z_i: Vec) -> Result, Error> { + let input: [F; 2] = [z_i[0], self.external_inputs[i]]; + let h = CRH::::evaluate(&self.poseidon_config, input).unwrap(); + Ok(vec![h, F::zero()]) + } + + /// 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>, + ) -> Result>, SynthesisError> { + let crh_params = + CRHParametersVar::::new_constant(cs.clone(), self.poseidon_config.clone())?; + let external_inputVar = + FpVar::::new_witness(cs.clone(), || Ok(self.external_inputs[i])).unwrap(); + let input: [FpVar; 2] = [z_i[0].clone(), external_inputVar.clone()]; + let h = CRHGadget::::evaluate(&crh_params, &input)?; + Ok(vec![h, FpVar::::zero()]) + } +} + +/// cargo test --example external_inputs +#[cfg(test)] +pub mod tests { + use super::*; + use ark_r1cs_std::R1CSVar; + use ark_relations::r1cs::ConstraintSystem; + + // test to check that the ExternalInputsCircuits computes the same values inside and outside the circuit + #[test] + fn test_f_circuit() { + let poseidon_config = poseidon_test_config::(); + + let cs = ConstraintSystem::::new_ref(); + + let circuit = ExternalInputsCircuits::::new((poseidon_config, vec![Fr::from(3_u32)])); + let z_i = vec![Fr::from(1_u32), Fr::zero()]; + + let z_i1 = circuit.step_native(0, z_i.clone()).unwrap(); + + let z_iVar = Vec::>::new_witness(cs.clone(), || Ok(z_i)).unwrap(); + let computed_z_i1Var = circuit + .generate_step_constraints(cs.clone(), 0, z_iVar.clone()) + .unwrap(); + assert_eq!(computed_z_i1Var.value().unwrap(), z_i1); + } +} + +/// cargo run --release --example external_inputs +fn main() { + let num_steps = 5; + let initial_state = vec![Fr::from(1_u32), Fr::zero()]; + + // set the external inputs to be used at each folding step + let external_inputs = vec![ + Fr::from(3_u32), + Fr::from(33_u32), + Fr::from(73_u32), + Fr::from(103_u32), + Fr::from(125_u32), + ]; + assert_eq!(external_inputs.len(), num_steps); + + let poseidon_config = poseidon_test_config::(); + let F_circuit = ExternalInputsCircuits::::new((poseidon_config, external_inputs)); + + println!("Prepare Nova ProverParams & VerifierParams"); + let (prover_params, verifier_params) = + test_nova_setup::>(F_circuit.clone()); + + /// The idea here is that eventually we could replace the next line chunk that defines the + /// `type NOVA = Nova<...>` by using another folding scheme that fulfills the `FoldingScheme` + /// trait, and the rest of our code would be working without needing to be updated. + type NOVA = Nova< + Projective, + GVar, + Projective2, + GVar2, + ExternalInputsCircuits, + Pedersen, + Pedersen, + >; + + println!("Initialize FoldingScheme"); + let mut folding_scheme = NOVA::init(&prover_params, F_circuit, initial_state.clone()).unwrap(); + + // compute a step of the IVC + for i in 0..num_steps { + let start = Instant::now(); + folding_scheme.prove_step().unwrap(); + println!("Nova::prove_step {}: {:?}", i, start.elapsed()); + } + println!( + "state at last step (after {} iterations): {:?}", + num_steps, + folding_scheme.state() + ); + + let (running_instance, incoming_instance, cyclefold_instance) = folding_scheme.instances(); + + println!("Run the Nova's IVC verifier"); + NOVA::verify( + verifier_params, + initial_state.clone(), + folding_scheme.state(), // latest state + Fr::from(num_steps as u32), + running_instance, + incoming_instance, + cyclefold_instance, + ) + .unwrap(); +} diff --git a/folding-schemes/examples/multi_inputs.rs b/folding-schemes/examples/multi_inputs.rs index fea5292..e7c296e 100644 --- a/folding-schemes/examples/multi_inputs.rs +++ b/folding-schemes/examples/multi_inputs.rs @@ -35,13 +35,13 @@ impl FCircuit for MultiInputsFCircuit { fn new(_params: Self::Params) -> Self { Self { _f: PhantomData } } - fn state_len(self) -> usize { + fn state_len(&self) -> usize { 5 } /// computes the next state values in place, assigning z_{i+1} into z_i, and computing the new /// z_{i+1} - fn step_native(self, z_i: Vec) -> Result, Error> { + fn step_native(&self, _i: usize, z_i: Vec) -> Result, Error> { let a = z_i[0] + F::from(4_u32); let b = z_i[1] + F::from(40_u32); let c = z_i[2] * F::from(4_u32); @@ -53,8 +53,9 @@ impl FCircuit for MultiInputsFCircuit { /// generates the constraints for the step of F for the given z_i fn generate_step_constraints( - self, + &self, cs: ConstraintSystemRef, + _i: usize, z_i: Vec>, ) -> Result>, SynthesisError> { let four = FpVar::::new_constant(cs.clone(), F::from(4u32))?; @@ -74,12 +75,12 @@ impl FCircuit for MultiInputsFCircuit { #[cfg(test)] pub mod tests { use super::*; - use ark_r1cs_std::alloc::AllocVar; + use ark_r1cs_std::{alloc::AllocVar, R1CSVar}; use ark_relations::r1cs::ConstraintSystem; // test to check that the MultiInputsFCircuit computes the same values inside and outside the circuit #[test] - fn test_add_f_circuit() { + fn test_f_circuit() { let cs = ConstraintSystem::::new_ref(); let circuit = MultiInputsFCircuit::::new(()); @@ -91,11 +92,11 @@ pub mod tests { Fr::from(1_u32), ]; - let z_i1 = circuit.step_native(z_i.clone()).unwrap(); + let z_i1 = circuit.step_native(0, z_i.clone()).unwrap(); let z_iVar = Vec::>::new_witness(cs.clone(), || Ok(z_i)).unwrap(); let computed_z_i1Var = circuit - .generate_step_constraints(cs.clone(), z_iVar.clone()) + .generate_step_constraints(cs.clone(), 0, z_iVar.clone()) .unwrap(); assert_eq!(computed_z_i1Var.value().unwrap(), z_i1); } diff --git a/folding-schemes/examples/sha256.rs b/folding-schemes/examples/sha256.rs index a1f8e82..ee5c147 100644 --- a/folding-schemes/examples/sha256.rs +++ b/folding-schemes/examples/sha256.rs @@ -41,13 +41,13 @@ impl FCircuit for Sha256FCircuit { fn new(_params: Self::Params) -> Self { Self { _f: PhantomData } } - fn state_len(self) -> usize { + fn state_len(&self) -> usize { 1 } /// computes the next state values in place, assigning z_{i+1} into z_i, and computing the new /// z_{i+1} - fn step_native(self, z_i: Vec) -> Result, Error> { + fn step_native(&self, _i: usize, z_i: Vec) -> Result, Error> { let out_bytes = Sha256::evaluate(&(), z_i[0].into_bigint().to_bytes_le()).unwrap(); let out: Vec = out_bytes.to_field_elements().unwrap(); @@ -56,8 +56,9 @@ impl FCircuit for Sha256FCircuit { /// generates the constraints for the step of F for the given z_i fn generate_step_constraints( - self, + &self, _cs: ConstraintSystemRef, + _i: usize, z_i: Vec>, ) -> Result>, SynthesisError> { let unit_var = UnitVar::default(); @@ -71,22 +72,22 @@ impl FCircuit for Sha256FCircuit { #[cfg(test)] pub mod tests { use super::*; - use ark_r1cs_std::alloc::AllocVar; + use ark_r1cs_std::{alloc::AllocVar, R1CSVar}; use ark_relations::r1cs::ConstraintSystem; // test to check that the Sha256FCircuit computes the same values inside and outside the circuit #[test] - fn test_sha256_f_circuit() { + fn test_f_circuit() { let cs = ConstraintSystem::::new_ref(); let circuit = Sha256FCircuit::::new(()); let z_i = vec![Fr::from(1_u32)]; - let z_i1 = circuit.step_native(z_i.clone()).unwrap(); + let z_i1 = circuit.step_native(0, z_i.clone()).unwrap(); let z_iVar = Vec::>::new_witness(cs.clone(), || Ok(z_i)).unwrap(); let computed_z_i1Var = circuit - .generate_step_constraints(cs.clone(), z_iVar.clone()) + .generate_step_constraints(cs.clone(), 0, z_iVar.clone()) .unwrap(); assert_eq!(computed_z_i1Var.value().unwrap(), z_i1); } diff --git a/folding-schemes/src/folding/hypernova/lcccs.rs b/folding-schemes/src/folding/hypernova/lcccs.rs index 66d47ed..9656b32 100644 --- a/folding-schemes/src/folding/hypernova/lcccs.rs +++ b/folding-schemes/src/folding/hypernova/lcccs.rs @@ -96,7 +96,7 @@ impl LCCCS { w: &Witness, ) -> Result<(), Error> { // check that C is the commitment of w. Notice that this is not verifying a Pedersen - // opening, but checking that the Commmitment comes from committing to the witness. + // opening, but checking that the Commitment comes from committing to the witness. if self.C != Pedersen::::commit(pedersen_params, &w.w, &w.r_w)? { return Err(Error::NotSatisfied); } diff --git a/folding-schemes/src/folding/nova/circuits.rs b/folding-schemes/src/folding/nova/circuits.rs index b14db99..e3a890c 100644 --- a/folding-schemes/src/folding/nova/circuits.rs +++ b/folding-schemes/src/folding/nova/circuits.rs @@ -245,6 +245,7 @@ pub struct AugmentedFCircuit< pub _gc2: PhantomData, pub poseidon_config: PoseidonConfig>, pub i: Option>, + pub i_usize: Option, pub z_0: Option>, pub z_i: Option>, pub u_i: Option>, @@ -278,6 +279,7 @@ where _gc2: PhantomData, poseidon_config: poseidon_config.clone(), i: None, + i_usize: None, z_0: None, z_i: None, u_i: None, @@ -349,7 +351,10 @@ where )?; // get z_{i+1} from the F circuit - let z_i1 = self.F.generate_step_constraints(cs.clone(), z_i.clone())?; + let i_usize = self.i_usize.unwrap_or(0); + let z_i1 = self + .F + .generate_step_constraints(cs.clone(), i_usize, z_i.clone())?; let zero = FpVar::>::new_constant(cs.clone(), CF1::::zero())?; let is_not_basecase = i.is_neq(&zero)?; diff --git a/folding-schemes/src/folding/nova/decider_eth_circuit.rs b/folding-schemes/src/folding/nova/decider_eth_circuit.rs index b9fa400..1c54447 100644 --- a/folding-schemes/src/folding/nova/decider_eth_circuit.rs +++ b/folding-schemes/src/folding/nova/decider_eth_circuit.rs @@ -508,7 +508,7 @@ pub mod tests { let circuit = WrapperCircuit::> { FC: cubic_circuit, z_i: Some(z_i.clone()), - z_i1: Some(cubic_circuit.step_native(z_i).unwrap()), + z_i1: Some(cubic_circuit.step_native(0, z_i).unwrap()), }; test_relaxed_r1cs_gadget(circuit); @@ -551,7 +551,7 @@ pub mod tests { let circuit = WrapperCircuit::> { FC: custom_circuit, z_i: Some(z_i.clone()), - z_i1: Some(custom_circuit.step_native(z_i).unwrap()), + z_i1: Some(custom_circuit.step_native(0, z_i).unwrap()), }; test_relaxed_r1cs_gadget(circuit); } @@ -567,7 +567,7 @@ pub mod tests { let circuit = WrapperCircuit::> { FC: custom_circuit, z_i: Some(z_i.clone()), - z_i1: Some(custom_circuit.step_native(z_i).unwrap()), + z_i1: Some(custom_circuit.step_native(0, z_i).unwrap()), }; circuit.generate_constraints(cs.clone()).unwrap(); cs.finalize(); diff --git a/folding-schemes/src/folding/nova/mod.rs b/folding-schemes/src/folding/nova/mod.rs index 13066ee..4f2648e 100644 --- a/folding-schemes/src/folding/nova/mod.rs +++ b/folding-schemes/src/folding/nova/mod.rs @@ -227,7 +227,7 @@ where let (prover_params, F_circuit) = prep_param; let (r1cs, cf_r1cs) = - get_r1cs::(&prover_params.poseidon_config, *F_circuit)?; + get_r1cs::(&prover_params.poseidon_config, F_circuit.clone())?; let verifier_params = VerifierParams:: { poseidon_config: prover_params.poseidon_config.clone(), @@ -244,7 +244,7 @@ where let cs2 = ConstraintSystem::::new_ref(); let augmented_F_circuit = - AugmentedFCircuit::::empty(&pp.poseidon_config, F); + AugmentedFCircuit::::empty(&pp.poseidon_config, F.clone()); let cf_circuit = CycleFoldCircuit::::empty(); augmented_F_circuit.generate_constraints(cs.clone())?; @@ -292,7 +292,14 @@ where let cfW_circuit: CycleFoldCircuit; let cfE_circuit: CycleFoldCircuit; - let z_i1 = self.F.step_native(self.z_i.clone())?; + if self.i > C1::ScalarField::from_le_bytes_mod_order(&std::usize::MAX.to_le_bytes()) { + return Err(Error::MaxStep); + } + let mut i_bytes: [u8; 8] = [0; 8]; + i_bytes.copy_from_slice(&self.i.into_bigint().to_bytes_le()[..8]); + let i_usize: usize = usize::from_le_bytes(i_bytes); + + let z_i1 = self.F.step_native(i_usize, self.z_i.clone())?; // compute T and cmT for AugmentedFCircuit let (T, cmT) = self.compute_cmT()?; @@ -327,13 +334,14 @@ where _gc2: PhantomData, poseidon_config: self.poseidon_config.clone(), i: Some(C1::ScalarField::zero()), // = i=0 - z_0: Some(self.z_0.clone()), // = z_i + i_usize: Some(0), + z_0: Some(self.z_0.clone()), // = z_i z_i: Some(self.z_i.clone()), u_i: Some(self.u_i.clone()), // = dummy U_i: Some(self.U_i.clone()), // = dummy U_i1: Some(U_i1.clone()), cmT: Some(cmT), - F: self.F, + F: self.F.clone(), x: Some(u_i1_x), cf1_u_i: None, cf2_u_i: None, @@ -399,13 +407,14 @@ where _gc2: PhantomData, poseidon_config: self.poseidon_config.clone(), i: Some(self.i), + i_usize: Some(i_usize), z_0: Some(self.z_0.clone()), z_i: Some(self.z_i.clone()), u_i: Some(self.u_i.clone()), U_i: Some(self.U_i.clone()), U_i1: Some(U_i1.clone()), cmT: Some(cmT), - F: self.F, + F: self.F.clone(), x: Some(u_i1_x), // cyclefold values cf1_u_i: Some(cfW_u_i.clone()), diff --git a/folding-schemes/src/frontend/mod.rs b/folding-schemes/src/frontend/mod.rs index 890f3a9..3990f46 100644 --- a/folding-schemes/src/frontend/mod.rs +++ b/folding-schemes/src/frontend/mod.rs @@ -10,7 +10,7 @@ use ark_std::fmt::Debug; /// 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. -pub trait FCircuit: Clone + Copy + Debug { +pub trait FCircuit: Clone + Debug { type Params: Debug; /// returns a new FCircuit instance @@ -18,14 +18,15 @@ pub trait FCircuit: Clone + Copy + Debug { /// returns the number of elements in the state of the FCircuit, which corresponds to the /// FCircuit inputs. - fn state_len(self) -> usize; + fn state_len(&self) -> usize; /// computes the next state values in place, assigning z_{i+1} into z_i, and computing the new /// z_{i+1} fn step_native( // this method uses self, so that each FCircuit implementation (and different frontends) // can hold a state if needed to store data to compute the next state. - self, + &self, + i: usize, z_i: Vec, ) -> Result, Error>; @@ -33,8 +34,9 @@ pub trait FCircuit: Clone + Copy + Debug { fn generate_step_constraints( // this method uses self, so that each FCircuit implementation (and different frontends) // can hold a state if needed to store data to generate the constraints. - self, + &self, cs: ConstraintSystemRef, + i: usize, z_i: Vec>, ) -> Result>, SynthesisError>; } @@ -63,15 +65,16 @@ pub mod tests { fn new(_params: Self::Params) -> Self { Self { _f: PhantomData } } - fn state_len(self) -> usize { + fn state_len(&self) -> usize { 1 } - fn step_native(self, z_i: Vec) -> Result, Error> { + fn step_native(&self, _i: usize, z_i: Vec) -> Result, Error> { Ok(vec![z_i[0] * z_i[0] * z_i[0] + z_i[0] + F::from(5_u32)]) } fn generate_step_constraints( - self, + &self, cs: ConstraintSystemRef, + _i: usize, z_i: Vec>, ) -> Result>, SynthesisError> { let five = FpVar::::new_constant(cs.clone(), F::from(5u32))?; @@ -97,10 +100,10 @@ pub mod tests { n_constraints: params, } } - fn state_len(self) -> usize { + fn state_len(&self) -> usize { 1 } - fn step_native(self, z_i: Vec) -> Result, Error> { + fn step_native(&self, _i: usize, z_i: Vec) -> Result, Error> { let mut z_i1 = F::one(); for _ in 0..self.n_constraints - 1 { z_i1 *= z_i[0]; @@ -108,8 +111,9 @@ pub mod tests { Ok(vec![z_i1]) } fn generate_step_constraints( - self, + &self, cs: ConstraintSystemRef, + _i: usize, z_i: Vec>, ) -> Result>, SynthesisError> { let mut z_i1 = FpVar::::new_witness(cs.clone(), || Ok(F::one()))?; @@ -143,7 +147,9 @@ pub mod tests { 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(), z_i.clone())?; + let computed_z_i1 = self + .FC + .generate_step_constraints(cs.clone(), 0, z_i.clone())?; computed_z_i1.enforce_equal(&z_i1)?; Ok(()) @@ -173,7 +179,7 @@ pub mod tests { let wrapper_circuit = WrapperCircuit::> { FC: custom_circuit, z_i: Some(z_i.clone()), - z_i1: Some(custom_circuit.step_native(z_i).unwrap()), + z_i1: Some(custom_circuit.step_native(0, z_i).unwrap()), }; wrapper_circuit.generate_constraints(cs.clone()).unwrap(); assert_eq!(cs.num_constraints(), n_constraints); diff --git a/folding-schemes/src/lib.rs b/folding-schemes/src/lib.rs index 9d1d12e..3ff2e1e 100644 --- a/folding-schemes/src/lib.rs +++ b/folding-schemes/src/lib.rs @@ -66,6 +66,8 @@ pub enum Error { NewDomainFail, #[error("Feature '{0}' not supported yet")] NotSupportedYet(String), + #[error("max i-th step reached (usize limit reached)")] + MaxStep, #[error(transparent)] ProtoGalaxy(folding::protogalaxy::ProtoGalaxyError),