Circom external inputs (#91)

* circom: add external_inputs

* adapt new external_inputs interface to the FoldingScheme trait and Nova impl

* adapt examples to new FCircuit external_inputs interface

* add state_len & external_inputs_len params to CircomFCircuit

* add examples/circom_full_flow.rs

* merge the params initializer functions, clippy

* circom: move r1cs reading to FCircuit::new instead of each step

* CI/examples: add circom so it can run the circom_full_flow example
This commit is contained in:
2024-05-06 16:06:08 +02:00
parent 9bbdfc5a85
commit d5c1e5f72a
21 changed files with 632 additions and 261 deletions

View File

@@ -258,6 +258,7 @@ pub struct AugmentedFCircuit<
pub i_usize: Option<usize>,
pub z_0: Option<Vec<C1::ScalarField>>,
pub z_i: Option<Vec<C1::ScalarField>>,
pub external_inputs: Option<Vec<C1::ScalarField>>,
pub u_i_cmW: Option<C1>,
pub U_i: Option<CommittedInstance<C1>>,
pub U_i1_cmE: Option<C1>,
@@ -290,6 +291,7 @@ where
i_usize: None,
z_0: None,
z_i: None,
external_inputs: None,
u_i_cmW: None,
U_i: None,
U_i1_cmE: None,
@@ -335,6 +337,11 @@ where
.z_i
.unwrap_or(vec![CF1::<C1>::zero(); self.F.state_len()]))
})?;
let external_inputs = Vec::<FpVar<CF1<C1>>>::new_witness(cs.clone(), || {
Ok(self
.external_inputs
.unwrap_or(vec![CF1::<C1>::zero(); self.F.external_inputs_len()]))
})?;
let u_dummy = CommittedInstance::dummy(2);
let U_i = CommittedInstanceVar::<C1>::new_witness(cs.clone(), || {
@@ -364,9 +371,9 @@ where
// get z_{i+1} from the F circuit
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 z_i1 =
self.F
.generate_step_constraints(cs.clone(), i_usize, z_i.clone(), external_inputs)?;
let is_basecase = i.is_zero()?;

View File

@@ -321,7 +321,7 @@ pub mod tests {
let mut rng = ark_std::test_rng();
let poseidon_config = poseidon_test_config::<Fr>();
let F_circuit = CubicFCircuit::<Fr>::new(());
let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap();
let z_0 = vec![Fr::from(3_u32)];
let (cs_len, cf_cs_len) =
@@ -347,9 +347,9 @@ pub mod tests {
let mut nova = NOVA::init(&prover_params, F_circuit, z_0.clone()).unwrap();
println!("Nova initialized, {:?}", start.elapsed());
let start = Instant::now();
nova.prove_step().unwrap();
nova.prove_step(vec![]).unwrap();
println!("prove_step, {:?}", start.elapsed());
nova.prove_step().unwrap(); // do a 2nd step
nova.prove_step(vec![]).unwrap(); // do a 2nd step
// generate Groth16 setup
let circuit = DeciderEthCircuit::<

View File

@@ -671,11 +671,11 @@ pub mod tests {
#[test]
fn test_relaxed_r1cs_small_gadget_arkworks() {
let z_i = vec![Fr::from(3_u32)];
let cubic_circuit = CubicFCircuit::<Fr>::new(());
let cubic_circuit = CubicFCircuit::<Fr>::new(()).unwrap();
let circuit = WrapperCircuit::<Fr, CubicFCircuit<Fr>> {
FC: cubic_circuit,
z_i: Some(z_i.clone()),
z_i1: Some(cubic_circuit.step_native(0, z_i).unwrap()),
z_i1: Some(cubic_circuit.step_native(0, z_i, vec![]).unwrap()),
};
test_relaxed_r1cs_gadget(circuit);
@@ -713,12 +713,12 @@ pub mod tests {
#[test]
fn test_relaxed_r1cs_custom_circuit() {
let n_constraints = 10_000;
let custom_circuit = CustomFCircuit::<Fr>::new(n_constraints);
let custom_circuit = CustomFCircuit::<Fr>::new(n_constraints).unwrap();
let z_i = vec![Fr::from(5_u32)];
let circuit = WrapperCircuit::<Fr, CustomFCircuit<Fr>> {
FC: custom_circuit,
z_i: Some(z_i.clone()),
z_i1: Some(custom_circuit.step_native(0, z_i).unwrap()),
z_i1: Some(custom_circuit.step_native(0, z_i, vec![]).unwrap()),
};
test_relaxed_r1cs_gadget(circuit);
}
@@ -729,12 +729,12 @@ pub mod tests {
// in practice we would use CycleFoldCircuit, but is a very big circuit (when computed
// non-natively inside the RelaxedR1CS circuit), so in order to have a short test we use a
// custom circuit.
let custom_circuit = CustomFCircuit::<Fq>::new(10);
let custom_circuit = CustomFCircuit::<Fq>::new(10).unwrap();
let z_i = vec![Fq::from(5_u32)];
let circuit = WrapperCircuit::<Fq, CustomFCircuit<Fq>> {
FC: custom_circuit,
z_i: Some(z_i.clone()),
z_i1: Some(custom_circuit.step_native(0, z_i).unwrap()),
z_i1: Some(custom_circuit.step_native(0, z_i, vec![]).unwrap()),
};
circuit.generate_constraints(cs.clone()).unwrap();
cs.finalize();
@@ -770,7 +770,7 @@ pub mod tests {
let mut rng = ark_std::test_rng();
let poseidon_config = poseidon_test_config::<Fr>();
let F_circuit = CubicFCircuit::<Fr>::new(());
let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap();
let z_0 = vec![Fr::from(3_u32)];
// get the CS & CF_CS len
@@ -802,7 +802,7 @@ pub mod tests {
// generate a Nova instance and do a step of it
let mut nova = NOVA::init(&prover_params, F_circuit, z_0.clone()).unwrap();
nova.prove_step().unwrap();
nova.prove_step(vec![]).unwrap();
let ivc_v = nova.clone();
let verifier_params = VerifierParams::<Projective, Projective2> {
poseidon_config: poseidon_config.clone(),

View File

@@ -339,9 +339,26 @@ where
}
/// Implements IVC.P of Nova+CycleFold
fn prove_step(&mut self) -> Result<(), Error> {
fn prove_step(&mut self, external_inputs: Vec<C1::ScalarField>) -> Result<(), Error> {
let augmented_F_circuit: AugmentedFCircuit<C1, C2, GC2, FC>;
if self.z_i.len() != self.F.state_len() {
return Err(Error::NotSameLength(
"z_i.len()".to_string(),
self.z_i.len(),
"F.state_len()".to_string(),
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);
}
@@ -349,7 +366,9 @@ where
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())?;
let z_i1 = self
.F
.step_native(i_usize, self.z_i.clone(), external_inputs.clone())?;
// compute T and cmT for AugmentedFCircuit
let (T, cmT) = self.compute_cmT()?;
@@ -392,6 +411,7 @@ where
i_usize: Some(0),
z_0: Some(self.z_0.clone()), // = z_i
z_i: Some(self.z_i.clone()),
external_inputs: Some(external_inputs.clone()),
u_i_cmW: Some(self.u_i.cmW), // = dummy
U_i: Some(self.U_i.clone()), // = dummy
U_i1_cmE: Some(U_i1.cmE),
@@ -464,6 +484,7 @@ where
i_usize: Some(i_usize),
z_0: Some(self.z_0.clone()),
z_i: Some(self.z_i.clone()),
external_inputs: Some(external_inputs.clone()),
u_i_cmW: Some(self.u_i.cmW),
U_i: Some(self.U_i.clone()),
U_i1_cmE: Some(U_i1.cmE),
@@ -803,7 +824,7 @@ pub mod tests {
let mut rng = ark_std::test_rng();
let poseidon_config = poseidon_test_config::<Fr>();
let F_circuit = CubicFCircuit::<Fr>::new(());
let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap();
let (cs_len, cf_cs_len) =
get_cs_params_len::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>>(
@@ -854,7 +875,7 @@ pub mod tests {
let num_steps: usize = 3;
for _ in 0..num_steps {
nova.prove_step().unwrap();
nova.prove_step(vec![]).unwrap();
}
assert_eq!(Fr::from(num_steps as u32), nova.i);

View File

@@ -1,4 +1,4 @@
use ark_circom::circom::CircomCircuit;
use ark_circom::circom::{CircomCircuit, R1CS as CircomR1CS};
use ark_ff::PrimeField;
use ark_r1cs_std::alloc::AllocVar;
use ark_r1cs_std::fields::fp::FpVar;
@@ -18,32 +18,64 @@ use utils::CircomWrapper;
#[derive(Clone, Debug)]
pub struct CircomFCircuit<F: PrimeField> {
circom_wrapper: CircomWrapper<F>,
state_len: usize,
external_inputs_len: usize,
r1cs: CircomR1CS<F>,
}
impl<F: PrimeField> FCircuit<F> for CircomFCircuit<F> {
type Params = (PathBuf, PathBuf);
/// (r1cs_path, wasm_path, state_len, external_inputs_len)
type Params = (PathBuf, PathBuf, usize, usize);
fn new(params: Self::Params) -> Self {
let (r1cs_path, wasm_path) = params;
fn new(params: Self::Params) -> Result<Self, Error> {
let (r1cs_path, wasm_path, state_len, external_inputs_len) = params;
let circom_wrapper = CircomWrapper::new(r1cs_path, wasm_path);
Self { circom_wrapper }
let r1cs = circom_wrapper.extract_r1cs()?;
Ok(Self {
circom_wrapper,
state_len,
external_inputs_len,
r1cs,
})
}
fn state_len(&self) -> usize {
1
self.state_len
}
fn external_inputs_len(&self) -> usize {
self.external_inputs_len
}
fn step_native(&self, _i: usize, z_i: Vec<F>) -> Result<Vec<F>, Error> {
// Converts PrimeField values to BigInt for computing witness.
let input_num_bigint = z_i
fn step_native(
&self,
_i: usize,
z_i: Vec<F>,
external_inputs: Vec<F>,
) -> Result<Vec<F>, Error> {
#[cfg(test)]
assert_eq!(z_i.len(), self.state_len());
#[cfg(test)]
assert_eq!(external_inputs.len(), self.external_inputs_len());
let inputs_bi = z_i
.iter()
.map(|val| self.circom_wrapper.ark_primefield_to_num_bigint(*val))
.collect::<Vec<BigInt>>();
let mut inputs_map = vec![("ivc_input".to_string(), inputs_bi)];
if self.external_inputs_len() > 0 {
let external_inputs_bi = external_inputs
.iter()
.map(|val| self.circom_wrapper.ark_primefield_to_num_bigint(*val))
.collect::<Vec<BigInt>>();
inputs_map.push(("external_inputs".to_string(), external_inputs_bi));
}
// Computes witness
let witness = self
.circom_wrapper
.extract_witness(&[("ivc_input".to_string(), input_num_bigint)])
.extract_witness(&inputs_map)
.map_err(|e| {
Error::WitnessCalculationError(format!("Failed to calculate witness: {}", e))
})?;
@@ -58,54 +90,67 @@ impl<F: PrimeField> FCircuit<F> for CircomFCircuit<F> {
cs: ConstraintSystemRef<F>,
_i: usize,
z_i: Vec<FpVar<F>>,
external_inputs: Vec<FpVar<F>>,
) -> Result<Vec<FpVar<F>>, SynthesisError> {
let mut input_values = Vec::new();
// Converts each FpVar to PrimeField value, then to num_bigint::BigInt.
for fp_var in z_i.iter() {
// Extracts the PrimeField value from FpVar.
let primefield_value = fp_var.value()?;
// Converts the PrimeField value to num_bigint::BigInt.
let num_bigint_value = self
.circom_wrapper
.ark_primefield_to_num_bigint(primefield_value);
input_values.push(num_bigint_value);
#[cfg(test)]
assert_eq!(z_i.len(), self.state_len());
#[cfg(test)]
assert_eq!(external_inputs.len(), self.external_inputs_len());
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)?;
inputs_map.push(("external_inputs".to_string(), external_inputs_bi));
}
let num_bigint_inputs = vec![("ivc_input".to_string(), input_values)];
// Extracts R1CS and witness.
let (r1cs, witness) = self
let witness = self
.circom_wrapper
.extract_r1cs_and_witness(&num_bigint_inputs)
.extract_witness(&inputs_map)
.map_err(|_| SynthesisError::AssignmentMissing)?;
// Initializes the CircomCircuit.
let circom_circuit = CircomCircuit {
r1cs,
witness: witness.clone(),
r1cs: self.r1cs.clone(),
witness: Some(witness.clone()),
inputs_already_allocated: true,
};
// Generates the constraints for the circom_circuit.
circom_circuit
.generate_constraints(cs.clone())
.map_err(|_| SynthesisError::AssignmentMissing)?;
circom_circuit.generate_constraints(cs.clone())?;
// Checks for constraint satisfaction.
if !cs.is_satisfied().unwrap() {
return Err(SynthesisError::Unsatisfiable);
}
let w = witness.ok_or(SynthesisError::Unsatisfiable)?;
// Extracts the z_i1(next state) from the witness vector.
let z_i1: Vec<FpVar<F>> =
Vec::<FpVar<F>>::new_witness(cs.clone(), || Ok(w[1..1 + self.state_len()].to_vec()))?;
let z_i1: Vec<FpVar<F>> = Vec::<FpVar<F>>::new_witness(cs.clone(), || {
Ok(witness[1..1 + self.state_len()].to_vec())
})?;
Ok(z_i1)
}
}
impl<F: PrimeField> CircomFCircuit<F> {
fn fpvars_to_bigints(&self, fpVars: Vec<FpVar<F>>) -> Result<Vec<BigInt>, SynthesisError> {
let mut input_values = Vec::new();
// converts each FpVar to PrimeField value, then to num_bigint::BigInt.
for fp_var in fpVars.iter() {
// extracts the PrimeField value from FpVar.
let primefield_value = fp_var.value()?;
// converts the PrimeField value to num_bigint::BigInt.
let num_bigint_value = self
.circom_wrapper
.ark_primefield_to_num_bigint(primefield_value);
input_values.push(num_bigint_value);
}
Ok(input_values)
}
}
#[cfg(test)]
pub mod tests {
use super::*;
@@ -120,10 +165,10 @@ pub mod tests {
let wasm_path =
PathBuf::from("./src/frontend/circom/test_folder/cubic_circuit_js/cubic_circuit.wasm");
let circom_fcircuit = CircomFCircuit::<Fr>::new((r1cs_path, wasm_path));
let circom_fcircuit = CircomFCircuit::<Fr>::new((r1cs_path, wasm_path, 1, 0)).unwrap(); // state_len:1, external_inputs_len:0
let z_i = vec![Fr::from(3u32)];
let z_i1 = circom_fcircuit.step_native(1, z_i).unwrap();
let z_i1 = circom_fcircuit.step_native(1, z_i, vec![]).unwrap();
assert_eq!(z_i1, vec![Fr::from(35u32)]);
}
@@ -134,7 +179,7 @@ pub mod tests {
let wasm_path =
PathBuf::from("./src/frontend/circom/test_folder/cubic_circuit_js/cubic_circuit.wasm");
let circom_fcircuit = CircomFCircuit::<Fr>::new((r1cs_path, wasm_path));
let circom_fcircuit = CircomFCircuit::<Fr>::new((r1cs_path, wasm_path, 1, 0)).unwrap(); // state_len:1, external_inputs_len:0
let cs = ConstraintSystem::<Fr>::new_ref();
@@ -144,7 +189,7 @@ pub mod tests {
let cs = ConstraintSystem::<Fr>::new_ref();
let z_i1_var = circom_fcircuit
.generate_step_constraints(cs.clone(), 1, z_i_var)
.generate_step_constraints(cs.clone(), 1, z_i_var, vec![])
.unwrap();
assert_eq!(z_i1_var.value().unwrap(), vec![Fr::from(35u32)]);
}
@@ -156,14 +201,14 @@ pub mod tests {
let wasm_path =
PathBuf::from("./src/frontend/circom/test_folder/cubic_circuit_js/cubic_circuit.wasm");
let circom_fcircuit = CircomFCircuit::<Fr>::new((r1cs_path, wasm_path));
let circom_fcircuit = CircomFCircuit::<Fr>::new((r1cs_path, wasm_path, 1, 0)).unwrap(); // state_len:1, external_inputs_len:0
// Allocates z_i1 by using step_native function.
let z_i = vec![Fr::from(3_u32)];
let wrapper_circuit = crate::frontend::tests::WrapperCircuit {
FC: circom_fcircuit.clone(),
z_i: Some(z_i.clone()),
z_i1: Some(circom_fcircuit.step_native(0, z_i.clone()).unwrap()),
z_i1: Some(circom_fcircuit.step_native(0, z_i.clone(), vec![]).unwrap()),
};
let cs = ConstraintSystem::<Fr>::new_ref();
@@ -174,4 +219,35 @@ pub mod tests {
"Constraint system is not satisfied"
);
}
#[test]
fn test_circom_external_inputs() {
let r1cs_path = PathBuf::from("./src/frontend/circom/test_folder/external_inputs.r1cs");
let wasm_path = PathBuf::from(
"./src/frontend/circom/test_folder/external_inputs_js/external_inputs.wasm",
);
let circom_fcircuit = CircomFCircuit::<Fr>::new((r1cs_path, wasm_path, 1, 2)).unwrap(); // state_len:1, external_inputs_len:2
let cs = ConstraintSystem::<Fr>::new_ref();
let z_i = vec![Fr::from(3u32)];
let external_inputs = vec![Fr::from(6u32), Fr::from(7u32)];
// run native step
let z_i1 = circom_fcircuit
.step_native(1, z_i.clone(), external_inputs.clone())
.unwrap();
assert_eq!(z_i1, vec![Fr::from(52u32)]);
// run gadget step
let z_i_var = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(z_i)).unwrap();
let external_inputs_var =
Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(external_inputs)).unwrap();
let z_i1_var = circom_fcircuit
.generate_step_constraints(cs.clone(), 1, z_i_var, external_inputs_var)
.unwrap();
assert_eq!(z_i1_var.value().unwrap(), vec![Fr::from(52u32)]);
}
}

View File

@@ -1,2 +1,4 @@
#!/bin/bash
circom ./folding-schemes/src/frontend/circom/test_folder/cubic_circuit.circom --r1cs --wasm --prime bn128 --output ./folding-schemes/src/frontend/circom/test_folder/
circom ./folding-schemes/src/frontend/circom/test_folder/cubic_circuit.circom --r1cs --sym --wasm --prime bn128 --output ./folding-schemes/src/frontend/circom/test_folder/
circom ./folding-schemes/src/frontend/circom/test_folder/external_inputs.circom --r1cs --sym --wasm --prime bn128 --output ./folding-schemes/src/frontend/circom/test_folder/

View File

@@ -10,4 +10,3 @@ template Example () {
}
component main {public [ivc_input]} = Example();

View File

@@ -0,0 +1,20 @@
pragma circom 2.0.3;
/*
z_{i+1} == z_i^3 + z_i * external_input[0] + external_input[1]
*/
template Example () {
signal input ivc_input[1]; // IVC state
signal input external_inputs[2]; // not state
signal output ivc_output[1]; // next IVC state
signal temp1;
signal temp2;
temp1 <== ivc_input[0] * ivc_input[0];
temp2 <== ivc_input[0] * external_inputs[0];
ivc_output[0] <== temp1 * ivc_input[0] + temp2 + external_inputs[1];
}
component main {public [ivc_input]} = Example();

View File

@@ -45,6 +45,13 @@ impl<F: PrimeField> CircomWrapper<F> {
Ok((r1cs, Some(witness_vec)))
}
pub fn extract_r1cs(&self) -> Result<R1CS<F>, Error> {
let file = File::open(&self.r1cs_filepath)?;
let reader = BufReader::new(file);
let r1cs_file = r1cs_reader::R1CSFile::<F>::new(reader)?;
Ok(r1cs_reader::R1CS::<F>::from(r1cs_file))
}
// Extracts the witness vector as a vector of PrimeField elements.
pub fn extract_witness(&self, inputs: &[(String, Vec<BigInt>)]) -> Result<Vec<F>, Error> {
let witness_bigint = self.calculate_witness(inputs)?;

View File

@@ -14,12 +14,16 @@ pub trait FCircuit<F: PrimeField>: Clone + Debug {
type Params: Debug;
/// returns a new FCircuit instance
fn new(params: Self::Params) -> Self;
fn new(params: Self::Params) -> Result<Self, Error>;
/// returns the number of elements in the state of the FCircuit, which corresponds to the
/// 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;
/// computes the next state values in place, assigning z_{i+1} into z_i, and computing the new
/// z_{i+1}
fn step_native(
@@ -28,6 +32,7 @@ pub trait FCircuit<F: PrimeField>: Clone + Debug {
&self,
i: usize,
z_i: Vec<F>,
external_inputs: Vec<F>, // inputs that are not part of the state
) -> Result<Vec<F>, Error>;
/// generates the constraints for the step of F for the given z_i
@@ -38,6 +43,7 @@ pub trait FCircuit<F: PrimeField>: Clone + Debug {
cs: ConstraintSystemRef<F>,
i: usize,
z_i: Vec<FpVar<F>>,
external_inputs: Vec<FpVar<F>>, // inputs that are not part of the state
) -> Result<Vec<FpVar<F>>, SynthesisError>;
}
@@ -61,13 +67,21 @@ pub mod tests {
}
impl<F: PrimeField> FCircuit<F> for CubicFCircuit<F> {
type Params = ();
fn new(_params: Self::Params) -> Self {
Self { _f: PhantomData }
fn new(_params: Self::Params) -> Result<Self, Error> {
Ok(Self { _f: PhantomData })
}
fn state_len(&self) -> usize {
1
}
fn step_native(&self, _i: usize, z_i: Vec<F>) -> Result<Vec<F>, Error> {
fn external_inputs_len(&self) -> usize {
0
}
fn step_native(
&self,
_i: usize,
z_i: Vec<F>,
_external_inputs: Vec<F>,
) -> Result<Vec<F>, Error> {
Ok(vec![z_i[0] * z_i[0] * z_i[0] + z_i[0] + F::from(5_u32)])
}
fn generate_step_constraints(
@@ -75,6 +89,7 @@ pub mod tests {
cs: ConstraintSystemRef<F>,
_i: usize,
z_i: Vec<FpVar<F>>,
_external_inputs: Vec<FpVar<F>>,
) -> Result<Vec<FpVar<F>>, SynthesisError> {
let five = FpVar::<F>::new_constant(cs.clone(), F::from(5u32))?;
let z_i = z_i[0].clone();
@@ -93,16 +108,24 @@ pub mod tests {
impl<F: PrimeField> FCircuit<F> for CustomFCircuit<F> {
type Params = usize;
fn new(params: Self::Params) -> Self {
Self {
fn new(params: Self::Params) -> Result<Self, Error> {
Ok(Self {
_f: PhantomData,
n_constraints: params,
}
})
}
fn state_len(&self) -> usize {
1
}
fn step_native(&self, _i: usize, z_i: Vec<F>) -> Result<Vec<F>, Error> {
fn external_inputs_len(&self) -> usize {
0
}
fn step_native(
&self,
_i: usize,
z_i: Vec<F>,
_external_inputs: Vec<F>,
) -> Result<Vec<F>, Error> {
let mut z_i1 = F::one();
for _ in 0..self.n_constraints - 1 {
z_i1 *= z_i[0];
@@ -114,6 +137,7 @@ pub mod tests {
cs: ConstraintSystemRef<F>,
_i: usize,
z_i: Vec<FpVar<F>>,
_external_inputs: Vec<FpVar<F>>,
) -> Result<Vec<FpVar<F>>, SynthesisError> {
let mut z_i1 = FpVar::<F>::new_witness(cs.clone(), || Ok(F::one()))?;
for _ in 0..self.n_constraints - 1 {
@@ -146,9 +170,9 @@ pub mod tests {
let z_i1 = Vec::<FpVar<F>>::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())?;
let computed_z_i1 =
self.FC
.generate_step_constraints(cs.clone(), 0, z_i.clone(), vec![])?;
computed_z_i1.enforce_equal(&z_i1)?;
Ok(())
@@ -158,7 +182,7 @@ pub mod tests {
#[test]
fn test_testfcircuit() {
let cs = ConstraintSystem::<Fr>::new_ref();
let F_circuit = CubicFCircuit::<Fr>::new(());
let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap();
let wrapper_circuit = WrapperCircuit::<Fr, CubicFCircuit<Fr>> {
FC: F_circuit,
@@ -173,12 +197,12 @@ pub mod tests {
fn test_customtestfcircuit() {
let cs = ConstraintSystem::<Fr>::new_ref();
let n_constraints = 1000;
let custom_circuit = CustomFCircuit::<Fr>::new(n_constraints);
let custom_circuit = CustomFCircuit::<Fr>::new(n_constraints).unwrap();
let z_i = vec![Fr::from(5_u32)];
let wrapper_circuit = WrapperCircuit::<Fr, CustomFCircuit<Fr>> {
FC: custom_circuit,
z_i: Some(z_i.clone()),
z_i1: Some(custom_circuit.step_native(0, z_i).unwrap()),
z_i1: Some(custom_circuit.step_native(0, z_i, vec![]).unwrap()),
};
wrapper_circuit.generate_constraints(cs.clone()).unwrap();
assert_eq!(cs.num_constraints(), n_constraints);

View File

@@ -124,7 +124,7 @@ where
z_0: Vec<C1::ScalarField>, // initial state
) -> Result<Self, Error>;
fn prove_step(&mut self) -> Result<(), Error>;
fn prove_step(&mut self, external_inputs: Vec<C1::ScalarField>) -> Result<(), Error>;
// returns the state at the current step
fn state(&self) -> Vec<C1::ScalarField>;