arnaucube
arnaucube/sonobePublic Self-hosted repository
sonobe/examples/full_flow.rs
examples/full_flow.rsRaw
  1#![allow(non_snake_case)]
  2#![allow(non_camel_case_types)]
  3#![allow(clippy::upper_case_acronyms)]
  4///
  5/// This example performs the full flow:
  6/// - define the circuit to be folded
  7/// - fold the circuit with Nova+CycleFold's IVC
  8/// - generate a DeciderEthCircuit final proof
  9/// - generate the Solidity contract that verifies the proof
 10/// - verify the proof in the EVM
 11///
 12use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as G1};
 13use ark_ff::PrimeField;
 14use ark_groth16::Groth16;
 15use ark_grumpkin::{constraints::GVar as GVar2, Projective as G2};
 16use ark_r1cs_std::alloc::AllocVar;
 17use ark_r1cs_std::fields::fp::FpVar;
 18use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
 19use std::marker::PhantomData;
 20use std::time::Instant;
 21
 22mod utils;
 23use utils::init_ivc_and_decider_params;
 24
 25use folding_schemes::{
 26    commitment::{kzg::KZG, pedersen::Pedersen},
 27    folding::nova::{
 28        decider_eth::{prepare_calldata, Decider as DeciderEth},
 29        Nova,
 30    },
 31    frontend::FCircuit,
 32    Decider, Error, FoldingScheme,
 33};
 34use solidity_verifiers::{
 35    evm::{compile_solidity, Evm},
 36    utils::get_function_selector_for_nova_cyclefold_verifier,
 37    verifiers::nova_cyclefold::get_decider_template_for_cyclefold_decider,
 38    NovaCycleFoldVerifierKey,
 39};
 40
 41/// Test circuit to be folded
 42#[derive(Clone, Copy, Debug)]
 43pub struct CubicFCircuit<F: PrimeField> {
 44    _f: PhantomData<F>,
 45}
 46impl<F: PrimeField> FCircuit<F> for CubicFCircuit<F> {
 47    type Params = ();
 48    fn new(_params: Self::Params) -> Result<Self, Error> {
 49        Ok(Self { _f: PhantomData })
 50    }
 51    fn state_len(&self) -> usize {
 52        1
 53    }
 54    fn external_inputs_len(&self) -> usize {
 55        0
 56    }
 57    fn step_native(
 58        &self,
 59        _i: usize,
 60        z_i: Vec<F>,
 61        _external_inputs: Vec<F>,
 62    ) -> Result<Vec<F>, Error> {
 63        Ok(vec![z_i[0] * z_i[0] * z_i[0] + z_i[0] + F::from(5_u32)])
 64    }
 65    fn generate_step_constraints(
 66        &self,
 67        cs: ConstraintSystemRef<F>,
 68        _i: usize,
 69        z_i: Vec<FpVar<F>>,
 70        _external_inputs: Vec<FpVar<F>>,
 71    ) -> Result<Vec<FpVar<F>>, SynthesisError> {
 72        let five = FpVar::<F>::new_constant(cs.clone(), F::from(5u32))?;
 73        let z_i = z_i[0].clone();
 74
 75        Ok(vec![&z_i * &z_i * &z_i + &z_i + &five])
 76    }
 77}
 78
 79fn main() {
 80    let n_steps = 10;
 81    // set the initial state
 82    let z_0 = vec![Fr::from(3_u32)];
 83
 84    let f_circuit = CubicFCircuit::<Fr>::new(()).unwrap();
 85    let (fs_prover_params, kzg_vk, g16_pk, g16_vk) =
 86        init_ivc_and_decider_params::<CubicFCircuit<Fr>>(f_circuit);
 87
 88    pub type NOVA = Nova<G1, GVar, G2, GVar2, CubicFCircuit<Fr>, KZG<'static, Bn254>, Pedersen<G2>>;
 89    pub type DECIDERETH_FCircuit = DeciderEth<
 90        G1,
 91        GVar,
 92        G2,
 93        GVar2,
 94        CubicFCircuit<Fr>,
 95        KZG<'static, Bn254>,
 96        Pedersen<G2>,
 97        Groth16<Bn254>,
 98        NOVA,
 99    >;
100
101    // initialize the folding scheme engine, in our case we use Nova
102    let mut nova = NOVA::init(&fs_prover_params, f_circuit, z_0).unwrap();
103    // run n steps of the folding iteration
104    for i in 0..n_steps {
105        let start = Instant::now();
106        nova.prove_step(vec![]).unwrap();
107        println!("Nova::prove_step {}: {:?}", i, start.elapsed());
108    }
109
110    let rng = rand::rngs::OsRng;
111    let start = Instant::now();
112    let proof = DECIDERETH_FCircuit::prove(
113        (g16_pk, fs_prover_params.cs_params.clone()),
114        rng,
115        nova.clone(),
116    )
117    .unwrap();
118    println!("generated Decider proof: {:?}", start.elapsed());
119
120    let verified = DECIDERETH_FCircuit::verify(
121        (g16_vk.clone(), kzg_vk.clone()),
122        nova.i,
123        nova.z_0.clone(),
124        nova.z_i.clone(),
125        &nova.U_i,
126        &nova.u_i,
127        &proof,
128    )
129    .unwrap();
130    assert!(verified);
131    println!("Decider proof verification: {}", verified);
132
133    // Now, let's generate the Solidity code that verifies this Decider final proof
134    let function_selector =
135        get_function_selector_for_nova_cyclefold_verifier(nova.z_0.len() * 2 + 1);
136
137    let calldata: Vec<u8> = prepare_calldata(
138        function_selector,
139        nova.i,
140        nova.z_0,
141        nova.z_i,
142        &nova.U_i,
143        &nova.u_i,
144        proof,
145    )
146    .unwrap();
147
148    // prepare the setup params for the solidity verifier
149    let nova_cyclefold_vk = NovaCycleFoldVerifierKey::from((g16_vk, kzg_vk, f_circuit.state_len()));
150
151    // generate the solidity code
152    let decider_solidity_code = get_decider_template_for_cyclefold_decider(nova_cyclefold_vk);
153
154    // verify the proof against the solidity code in the EVM
155    let nova_cyclefold_verifier_bytecode = compile_solidity(&decider_solidity_code, "NovaDecider");
156    let mut evm = Evm::default();
157    let verifier_address = evm.create(nova_cyclefold_verifier_bytecode);
158    let (_, output) = evm.call(verifier_address, calldata.clone());
159    assert_eq!(*output.last().unwrap(), 1);
160
161    // save smart contract and the calldata
162    println!("storing nova-verifier.sol and the calldata into files");
163    use std::fs;
164    fs::write(
165        "./examples/nova-verifier.sol",
166        decider_solidity_code.clone(),
167    )
168    .unwrap();
169    fs::write("./examples/solidity-calldata.calldata", calldata.clone()).unwrap();
170    let s = solidity_verifiers::utils::get_formatted_calldata(calldata.clone());
171    fs::write("./examples/solidity-calldata.inputs", s.join(",\n")).expect("");
172}