You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

222 lines
7.5 KiB

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