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}