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);