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.

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