mirror of
https://github.com/arnaucube/sonobe.git
synced 2026-01-08 15:01:30 +01:00
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:
@@ -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()?;
|
||||
|
||||
|
||||
@@ -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::<
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -10,4 +10,3 @@ template Example () {
|
||||
}
|
||||
|
||||
component main {public [ivc_input]} = Example();
|
||||
|
||||
|
||||
@@ -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();
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>;
|
||||
|
||||
Reference in New Issue
Block a user