arnaucube
arnaucube/sonobePublic Self-hosted repository
sonobe/examples/multi_inputs.rs
examples/multi_inputs.rsRaw
  1#![allow(non_snake_case)]
  2#![allow(non_upper_case_globals)]
  3#![allow(non_camel_case_types)]
  4#![allow(clippy::upper_case_acronyms)]
  5
  6use ark_ff::PrimeField;
  7use ark_r1cs_std::alloc::AllocVar;
  8use ark_r1cs_std::fields::fp::FpVar;
  9use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
 10use core::marker::PhantomData;
 11use std::time::Instant;
 12
 13use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective};
 14use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2};
 15
 16use folding_schemes::commitment::{kzg::KZG, pedersen::Pedersen};
 17use folding_schemes::folding::nova::Nova;
 18use folding_schemes::frontend::FCircuit;
 19use folding_schemes::{Error, FoldingScheme};
 20mod utils;
 21use utils::init_nova_ivc_params;
 22
 23/// This is the circuit that we want to fold, it implements the FCircuit trait. The parameter z_i
 24/// denotes the current state which contains 5 elements, and z_{i+1} denotes the next state which
 25/// we get by applying the step.
 26/// In this example we set z_i and z_{i+1} to have five elements, and at each step we do different
 27/// operations on each of them.
 28#[derive(Clone, Copy, Debug)]
 29pub struct MultiInputsFCircuit<F: PrimeField> {
 30    _f: PhantomData<F>,
 31}
 32impl<F: PrimeField> FCircuit<F> for MultiInputsFCircuit<F> {
 33    type Params = ();
 34
 35    fn new(_params: Self::Params) -> Result<Self, Error> {
 36        Ok(Self { _f: PhantomData })
 37    }
 38    fn state_len(&self) -> usize {
 39        5
 40    }
 41    fn external_inputs_len(&self) -> usize {
 42        0
 43    }
 44
 45    /// computes the next state values in place, assigning z_{i+1} into z_i, and computing the new
 46    /// z_{i+1}
 47    fn step_native(
 48        &self,
 49        _i: usize,
 50        z_i: Vec<F>,
 51        _external_inputs: Vec<F>,
 52    ) -> Result<Vec<F>, Error> {
 53        let a = z_i[0] + F::from(4_u32);
 54        let b = z_i[1] + F::from(40_u32);
 55        let c = z_i[2] * F::from(4_u32);
 56        let d = z_i[3] * F::from(40_u32);
 57        let e = z_i[4] + F::from(100_u32);
 58
 59        Ok(vec![a, b, c, d, e])
 60    }
 61
 62    /// generates the constraints for the step of F for the given z_i
 63    fn generate_step_constraints(
 64        &self,
 65        cs: ConstraintSystemRef<F>,
 66        _i: usize,
 67        z_i: Vec<FpVar<F>>,
 68        _external_inputs: Vec<FpVar<F>>,
 69    ) -> Result<Vec<FpVar<F>>, SynthesisError> {
 70        let four = FpVar::<F>::new_constant(cs.clone(), F::from(4u32))?;
 71        let forty = FpVar::<F>::new_constant(cs.clone(), F::from(40u32))?;
 72        let onehundred = FpVar::<F>::new_constant(cs.clone(), F::from(100u32))?;
 73        let a = z_i[0].clone() + four.clone();
 74        let b = z_i[1].clone() + forty.clone();
 75        let c = z_i[2].clone() * four;
 76        let d = z_i[3].clone() * forty;
 77        let e = z_i[4].clone() + onehundred;
 78
 79        Ok(vec![a, b, c, d, e])
 80    }
 81}
 82
 83/// cargo test --example multi_inputs
 84#[cfg(test)]
 85pub mod tests {
 86    use super::*;
 87    use ark_r1cs_std::{alloc::AllocVar, R1CSVar};
 88    use ark_relations::r1cs::ConstraintSystem;
 89
 90    // test to check that the MultiInputsFCircuit computes the same values inside and outside the circuit
 91    #[test]
 92    fn test_f_circuit() {
 93        let cs = ConstraintSystem::<Fr>::new_ref();
 94
 95        let circuit = MultiInputsFCircuit::<Fr>::new(()).unwrap();
 96        let z_i = vec![
 97            Fr::from(1_u32),
 98            Fr::from(1_u32),
 99            Fr::from(1_u32),
100            Fr::from(1_u32),
101            Fr::from(1_u32),
102        ];
103
104        let z_i1 = circuit.step_native(0, z_i.clone(), vec![]).unwrap();
105
106        let z_iVar = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(z_i)).unwrap();
107        let computed_z_i1Var = circuit
108            .generate_step_constraints(cs.clone(), 0, z_iVar.clone(), vec![])
109            .unwrap();
110        assert_eq!(computed_z_i1Var.value().unwrap(), z_i1);
111    }
112}
113
114/// cargo run --release --example multi_inputs
115fn main() {
116    let num_steps = 10;
117    let initial_state = vec![
118        Fr::from(1_u32),
119        Fr::from(1_u32),
120        Fr::from(1_u32),
121        Fr::from(1_u32),
122        Fr::from(1_u32),
123    ];
124
125    let F_circuit = MultiInputsFCircuit::<Fr>::new(()).unwrap();
126
127    println!("Prepare Nova ProverParams & VerifierParams");
128    let (prover_params, verifier_params, _) =
129        init_nova_ivc_params::<MultiInputsFCircuit<Fr>>(F_circuit);
130
131    /// The idea here is that eventually we could replace the next line chunk that defines the
132    /// `type NOVA = Nova<...>` by using another folding scheme that fulfills the `FoldingScheme`
133    /// trait, and the rest of our code would be working without needing to be updated.
134    type NOVA = Nova<
135        Projective,
136        GVar,
137        Projective2,
138        GVar2,
139        MultiInputsFCircuit<Fr>,
140        KZG<'static, Bn254>,
141        Pedersen<Projective2>,
142    >;
143
144    println!("Initialize FoldingScheme");
145    let mut folding_scheme = NOVA::init(&prover_params, F_circuit, initial_state.clone()).unwrap();
146
147    // compute a step of the IVC
148    for i in 0..num_steps {
149        let start = Instant::now();
150        folding_scheme.prove_step(vec![]).unwrap();
151        println!("Nova::prove_step {}: {:?}", i, start.elapsed());
152    }
153
154    let (running_instance, incoming_instance, cyclefold_instance) = folding_scheme.instances();
155
156    println!("Run the Nova's IVC verifier");
157    NOVA::verify(
158        verifier_params,
159        initial_state.clone(),
160        folding_scheme.state(), // latest state
161        Fr::from(num_steps as u32),
162        running_instance,
163        incoming_instance,
164        cyclefold_instance,
165    )
166    .unwrap();
167}