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.

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