hash-chain-sonobe/src/lib.rs
src/lib.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///
12
13#[cfg(test)]
14mod tests {
15 use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as G1};
16 use ark_grumpkin::{constraints::GVar as GVar2, Projective as G2};
17
18 use ark_crypto_primitives::snark::SNARK;
19 use ark_groth16::{Groth16, ProvingKey, VerifyingKey as G16VerifierKey};
20 use ark_poly_commit::kzg10::VerifierKey as KZGVerifierKey;
21
22 use ark_std::Zero;
23
24 use std::path::PathBuf;
25 use std::time::Instant;
26
27 use folding_schemes::{
28 commitment::{
29 kzg::{ProverKey as KZGProverKey, KZG},
30 pedersen::Pedersen,
31 CommitmentScheme,
32 },
33 folding::nova::{
34 decider_eth::{prepare_calldata, Decider as DeciderEth},
35 decider_eth_circuit::DeciderEthCircuit,
36 get_r1cs, Nova, ProverParams, VerifierParams,
37 },
38 frontend::{circom::CircomFCircuit, FCircuit},
39 transcript::poseidon::poseidon_test_config,
40 Decider, FoldingScheme,
41 };
42 use solidity_verifiers::{
43 evm::{compile_solidity, Evm},
44 utils::get_function_selector_for_nova_cyclefold_verifier,
45 verifiers::nova_cyclefold::get_decider_template_for_cyclefold_decider,
46 NovaCycleFoldVerifierKey,
47 };
48
49 // This method computes the Nova's Prover & Verifier parameters for the example.
50 // Warning: this method is only for testing purposes. For a real world use case those parameters
51 // should be generated carefully (both the PoseidonConfig and the PedersenParams).
52 #[allow(clippy::type_complexity)]
53 fn init_nova_ivc_params<FC: FCircuit<Fr>>(
54 F_circuit: FC,
55 ) -> (
56 ProverParams<G1, G2, KZG<'static, Bn254>, Pedersen<G2>>,
57 VerifierParams<G1, G2>,
58 KZGVerifierKey<Bn254>,
59 ) {
60 let mut rng = ark_std::test_rng();
61 let poseidon_config = poseidon_test_config::<Fr>();
62
63 // get the CM & CF_CM len
64 let (r1cs, cf_r1cs) =
65 get_r1cs::<G1, GVar, G2, GVar2, FC>(&poseidon_config, F_circuit).unwrap();
66 let cs_len = r1cs.A.n_rows;
67 let cf_cs_len = cf_r1cs.A.n_rows;
68
69 // let (pedersen_params, _) = Pedersen::<G1>::setup(&mut rng, cf_len).unwrap();
70 let (kzg_pk, kzg_vk): (KZGProverKey<G1>, KZGVerifierKey<Bn254>) =
71 KZG::<Bn254>::setup(&mut rng, cs_len).unwrap();
72 let (cf_pedersen_params, _) = Pedersen::<G2>::setup(&mut rng, cf_cs_len).unwrap();
73
74 let fs_prover_params = ProverParams::<G1, G2, KZG<Bn254>, Pedersen<G2>> {
75 poseidon_config: poseidon_config.clone(),
76 cs_params: kzg_pk.clone(),
77 cf_cs_params: cf_pedersen_params,
78 };
79 let fs_verifier_params = VerifierParams::<G1, G2> {
80 poseidon_config: poseidon_config.clone(),
81 r1cs,
82 cf_r1cs,
83 };
84 (fs_prover_params, fs_verifier_params, kzg_vk)
85 }
86
87 /// Initializes Nova parameters and DeciderEth parameters. Only for test purposes.
88 #[allow(clippy::type_complexity)]
89 fn init_ivc_and_decider_params<FC: FCircuit<Fr>>(
90 f_circuit: FC,
91 ) -> (
92 ProverParams<G1, G2, KZG<'static, Bn254>, Pedersen<G2>>,
93 KZGVerifierKey<Bn254>,
94 ProvingKey<Bn254>,
95 G16VerifierKey<Bn254>,
96 ) {
97 let mut rng = rand::rngs::OsRng;
98 let start = Instant::now();
99 let (fs_prover_params, _, kzg_vk) = init_nova_ivc_params::<FC>(f_circuit.clone());
100 println!("generated Nova folding params: {:?}", start.elapsed());
101
102 pub type NOVA<FC> = Nova<G1, GVar, G2, GVar2, FC, KZG<'static, Bn254>, Pedersen<G2>>;
103 let z_0 = vec![Fr::zero(); f_circuit.state_len()];
104 let nova = NOVA::init(&fs_prover_params, f_circuit, z_0.clone()).unwrap();
105
106 let decider_circuit =
107 DeciderEthCircuit::<G1, GVar, G2, GVar2, KZG<Bn254>, Pedersen<G2>>::from_nova::<FC>(
108 nova.clone(),
109 )
110 .unwrap();
111 let start = Instant::now();
112 let (g16_pk, g16_vk) =
113 Groth16::<Bn254>::circuit_specific_setup(decider_circuit.clone(), &mut rng).unwrap();
114 println!(
115 "generated G16 (Decider circuit) params: {:?}",
116 start.elapsed()
117 );
118 (fs_prover_params, kzg_vk, g16_pk, g16_vk)
119 }
120
121 #[test]
122 fn full_flow() {
123 // set the initial state
124 let z_0_aux: Vec<u32> = vec![0_u32; 32 * 8];
125 let z_0: Vec<Fr> = z_0_aux.iter().map(|v| Fr::from(*v)).collect::<Vec<Fr>>();
126
127 // initialize the Circom circuit
128 let r1cs_path = PathBuf::from("./circuit/keccak-chain.r1cs");
129 let wasm_path = PathBuf::from("./circuit/keccak-chain_js/keccak-chain.wasm");
130
131 let f_circuit_params = (r1cs_path, wasm_path, 32 * 8, 0);
132 let f_circuit = CircomFCircuit::<Fr>::new(f_circuit_params).unwrap();
133
134 let (fs_prover_params, kzg_vk, g16_pk, g16_vk) =
135 init_ivc_and_decider_params::<CircomFCircuit<Fr>>(f_circuit.clone());
136
137 pub type NOVA =
138 Nova<G1, GVar, G2, GVar2, CircomFCircuit<Fr>, KZG<'static, Bn254>, Pedersen<G2>>;
139 pub type DECIDERETH_FCircuit = DeciderEth<
140 G1,
141 GVar,
142 G2,
143 GVar2,
144 CircomFCircuit<Fr>,
145 KZG<'static, Bn254>,
146 Pedersen<G2>,
147 Groth16<Bn254>,
148 NOVA,
149 >;
150
151 // initialize the folding scheme engine, in our case we use Nova
152 let mut nova = NOVA::init(&fs_prover_params, f_circuit.clone(), z_0).unwrap();
153 // run n steps of the folding iteration
154 for i in 0..10 {
155 let start = Instant::now();
156 nova.prove_step(vec![]).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 =
200 NovaCycleFoldVerifierKey::from((g16_vk, kzg_vk, f_circuit.state_len()));
201
202 // generate the solidity code
203 let decider_solidity_code = get_decider_template_for_cyclefold_decider(nova_cyclefold_vk);
204
205 // verify the proof against the solidity code in the EVM
206 let nova_cyclefold_verifier_bytecode =
207 compile_solidity(&decider_solidity_code, "NovaDecider");
208 let mut evm = Evm::default();
209 let verifier_address = evm.create(nova_cyclefold_verifier_bytecode);
210 let (_, output) = evm.call(verifier_address, calldata.clone());
211 assert_eq!(*output.last().unwrap(), 1);
212
213 // save smart contract and the calldata
214 println!("storing nova-verifier.sol and the calldata into files");
215 use std::fs;
216 fs::write(
217 "./examples/nova-verifier.sol",
218 decider_solidity_code.clone(),
219 )
220 .unwrap();
221 fs::write("./examples/solidity-calldata.calldata", calldata.clone()).unwrap();
222 let s = solidity_verifiers::utils::get_formatted_calldata(calldata.clone());
223 fs::write("./examples/solidity-calldata.inputs", s.join(",\n")).expect("");
224 }
225}