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_crypto_primitives::snark::SNARK;
14use ark_ff::PrimeField;
15use ark_groth16::VerifyingKey as G16VerifierKey;
16use ark_groth16::{Groth16, ProvingKey};
17use ark_grumpkin::{constraints::GVar as GVar2, Projective as G2};
18use ark_poly_commit::kzg10::VerifierKey as KZGVerifierKey;
19use ark_r1cs_std::alloc::AllocVar;
20use ark_r1cs_std::fields::fp::FpVar;
21use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
22use ark_std::Zero;
23use std::marker::PhantomData;
24use std::time::Instant;
25
26use folding_schemes::{
27 commitment::{
28 kzg::{ProverKey as KZGProverKey, KZG},
29 pedersen::Pedersen,
30 CommitmentScheme,
31 },
32 folding::nova::{
33 decider_eth::{prepare_calldata, Decider as DeciderEth},
34 decider_eth_circuit::DeciderEthCircuit,
35 get_cs_params_len, Nova, ProverParams,
36 },
37 frontend::FCircuit,
38 transcript::poseidon::poseidon_test_config,
39 Decider, Error, FoldingScheme,
40};
41use solidity_verifiers::{
42 evm::{compile_solidity, Evm},
43 utils::get_function_selector_for_nova_cyclefold_verifier,
44 verifiers::nova_cyclefold::get_decider_template_for_cyclefold_decider,
45 NovaCycleFoldVerifierKey,
46};
47
48/// Test circuit to be folded
49#[derive(Clone, Copy, Debug)]
50pub struct CubicFCircuit<F: PrimeField> {
51 _f: PhantomData<F>,
52}
53impl<F: PrimeField> FCircuit<F> for CubicFCircuit<F> {
54 type Params = ();
55 fn new(_params: Self::Params) -> Self {
56 Self { _f: PhantomData }
57 }
58 fn state_len(&self) -> usize {
59 1
60 }
61 fn step_native(&self, _i: usize, z_i: Vec<F>) -> Result<Vec<F>, Error> {
62 Ok(vec![z_i[0] * z_i[0] * z_i[0] + z_i[0] + F::from(5_u32)])
63 }
64 fn generate_step_constraints(
65 &self,
66 cs: ConstraintSystemRef<F>,
67 _i: usize,
68 z_i: Vec<FpVar<F>>,
69 ) -> Result<Vec<FpVar<F>>, SynthesisError> {
70 let five = FpVar::<F>::new_constant(cs.clone(), F::from(5u32))?;
71 let z_i = z_i[0].clone();
72
73 Ok(vec![&z_i * &z_i * &z_i + &z_i + &five])
74 }
75}
76
77#[allow(clippy::type_complexity)]
78fn init_test_prover_params<FC: FCircuit<Fr, Params = ()>>() -> (
79 ProverParams<G1, G2, KZG<'static, Bn254>, Pedersen<G2>>,
80 KZGVerifierKey<Bn254>,
81) {
82 let mut rng = ark_std::test_rng();
83 let poseidon_config = poseidon_test_config::<Fr>();
84 let f_circuit = FC::new(());
85 let (cs_len, cf_cs_len) =
86 get_cs_params_len::<G1, GVar, G2, GVar2, FC>(&poseidon_config, f_circuit).unwrap();
87 let (kzg_pk, kzg_vk): (KZGProverKey<G1>, KZGVerifierKey<Bn254>) =
88 KZG::<Bn254>::setup(&mut rng, cs_len).unwrap();
89 let (cf_pedersen_params, _) = Pedersen::<G2>::setup(&mut rng, cf_cs_len).unwrap();
90 let fs_prover_params = ProverParams::<G1, G2, KZG<Bn254>, Pedersen<G2>> {
91 poseidon_config: poseidon_config.clone(),
92 cs_params: kzg_pk.clone(),
93 cf_cs_params: cf_pedersen_params,
94 };
95 (fs_prover_params, kzg_vk)
96}
97/// Initializes Nova parameters and DeciderEth parameters. Only for test purposes.
98#[allow(clippy::type_complexity)]
99fn init_params<FC: FCircuit<Fr, Params = ()>>() -> (
100 ProverParams<G1, G2, KZG<'static, Bn254>, Pedersen<G2>>,
101 KZGVerifierKey<Bn254>,
102 ProvingKey<Bn254>,
103 G16VerifierKey<Bn254>,
104) {
105 let mut rng = rand::rngs::OsRng;
106 let start = Instant::now();
107 let (fs_prover_params, kzg_vk) = init_test_prover_params::<FC>();
108 println!("generated Nova folding params: {:?}", start.elapsed());
109 let f_circuit = FC::new(());
110
111 pub type NOVA<FC> = Nova<G1, GVar, G2, GVar2, FC, KZG<'static, Bn254>, Pedersen<G2>>;
112 let z_0 = vec![Fr::zero(); f_circuit.state_len()];
113 let nova = NOVA::init(&fs_prover_params, f_circuit, z_0.clone()).unwrap();
114
115 let decider_circuit =
116 DeciderEthCircuit::<G1, GVar, G2, GVar2, KZG<Bn254>, Pedersen<G2>>::from_nova::<FC>(
117 nova.clone(),
118 )
119 .unwrap();
120 let start = Instant::now();
121 let (g16_pk, g16_vk) =
122 Groth16::<Bn254>::circuit_specific_setup(decider_circuit.clone(), &mut rng).unwrap();
123 println!(
124 "generated G16 (Decider circuit) params: {:?}",
125 start.elapsed()
126 );
127 (fs_prover_params, kzg_vk, g16_pk, g16_vk)
128}
129
130fn main() {
131 let n_steps = 10;
132 // set the initial state
133 let z_0 = vec![Fr::from(3_u32)];
134
135 let (fs_prover_params, kzg_vk, g16_pk, g16_vk) = init_params::<CubicFCircuit<Fr>>();
136
137 pub type NOVA = Nova<G1, GVar, G2, GVar2, CubicFCircuit<Fr>, KZG<'static, Bn254>, Pedersen<G2>>;
138 pub type DECIDERETH_FCircuit = DeciderEth<
139 G1,
140 GVar,
141 G2,
142 GVar2,
143 CubicFCircuit<Fr>,
144 KZG<'static, Bn254>,
145 Pedersen<G2>,
146 Groth16<Bn254>,
147 NOVA,
148 >;
149 let f_circuit = CubicFCircuit::<Fr>::new(());
150
151 // initialize the folding scheme engine, in our case we use Nova
152 let mut nova = NOVA::init(&fs_prover_params, f_circuit, z_0).unwrap();
153 // run n steps of the folding iteration
154 for i in 0..n_steps {
155 let start = Instant::now();
156 nova.prove_step().unwrap();
157 println!("Nova::prove_step {}: {:?}", i, start.elapsed());
158 }
159
160 let rng = rand::rngs::OsRng;
161 let start = Instant::now();
162 let proof = DECIDERETH_FCircuit::prove(
163 (g16_pk, fs_prover_params.cs_params.clone()),
164 rng,
165 nova.clone(),
166 )
167 .unwrap();
168 println!("generated Decider proof: {:?}", start.elapsed());
169
170 let verified = DECIDERETH_FCircuit::verify(
171 (g16_vk.clone(), kzg_vk.clone()),
172 nova.i,
173 nova.z_0.clone(),
174 nova.z_i.clone(),
175 &nova.U_i,
176 &nova.u_i,
177 &proof,
178 )
179 .unwrap();
180 assert!(verified);
181 println!("Decider proof verification: {}", verified);
182
183 // Now, let's generate the Solidity code that verifies this Decider final proof
184 let function_selector =
185 get_function_selector_for_nova_cyclefold_verifier(nova.z_0.len() * 2 + 1);
186
187 let calldata: Vec<u8> = prepare_calldata(
188 function_selector,
189 nova.i,
190 nova.z_0,
191 nova.z_i,
192 &nova.U_i,
193 &nova.u_i,
194 proof,
195 )
196 .unwrap();
197
198 // prepare the setup params for the solidity verifier
199 let nova_cyclefold_vk = NovaCycleFoldVerifierKey::from((g16_vk, kzg_vk, f_circuit.state_len()));
200
201 // generate the solidity code
202 let decider_solidity_code = get_decider_template_for_cyclefold_decider(nova_cyclefold_vk);
203
204 // verify the proof against the solidity code in the EVM
205 let nova_cyclefold_verifier_bytecode = compile_solidity(&decider_solidity_code, "NovaDecider");
206 let mut evm = Evm::default();
207 let verifier_address = evm.create(nova_cyclefold_verifier_bytecode);
208 let (_, output) = evm.call(verifier_address, calldata.clone());
209 assert_eq!(*output.last().unwrap(), 1);
210
211 // save smart contract and the calldata
212 println!("storing nova-verifier.sol and the calldata into files");
213 use std::fs;
214 fs::write(
215 "./examples/nova-verifier.sol",
216 decider_solidity_code.clone(),
217 )
218 .unwrap();
219 fs::write("./examples/solidity-calldata.calldata", calldata.clone()).unwrap();
220 let s = solidity_verifiers::utils::get_formatted_calldata(calldata.clone());
221 fs::write("./examples/solidity-calldata.inputs", s.join(",\n")).expect("");
222}