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.

174 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 =
  79. Nova<G1, GVar, G2, GVar2, CubicFCircuit<Fr>, KZG<'static, Bn254>, Pedersen<G2>, false>;
  80. pub type D = DeciderEth<
  81. G1,
  82. GVar,
  83. G2,
  84. GVar2,
  85. CubicFCircuit<Fr>,
  86. KZG<'static, Bn254>,
  87. Pedersen<G2>,
  88. Groth16<Bn254>,
  89. N,
  90. >;
  91. let poseidon_config = poseidon_canonical_config::<Fr>();
  92. let mut rng = rand::rngs::OsRng;
  93. // prepare the Nova prover & verifier params
  94. let nova_preprocess_params = PreprocessorParam::new(poseidon_config.clone(), f_circuit);
  95. let nova_params = N::preprocess(&mut rng, &nova_preprocess_params).unwrap();
  96. // initialize the folding scheme engine, in our case we use Nova
  97. let mut nova = N::init(&nova_params, f_circuit, z_0).unwrap();
  98. // prepare the Decider prover & verifier params
  99. let (decider_pp, decider_vp) = D::preprocess(&mut rng, &nova_params, nova.clone()).unwrap();
  100. // run n steps of the folding iteration
  101. for i in 0..n_steps {
  102. let start = Instant::now();
  103. nova.prove_step(rng, vec![], None).unwrap();
  104. println!("Nova::prove_step {}: {:?}", i, start.elapsed());
  105. }
  106. let start = Instant::now();
  107. let proof = D::prove(rng, decider_pp, nova.clone()).unwrap();
  108. println!("generated Decider proof: {:?}", start.elapsed());
  109. let verified = D::verify(
  110. decider_vp.clone(),
  111. nova.i,
  112. nova.z_0.clone(),
  113. nova.z_i.clone(),
  114. &nova.U_i,
  115. &nova.u_i,
  116. &proof,
  117. )
  118. .unwrap();
  119. assert!(verified);
  120. println!("Decider proof verification: {}", verified);
  121. // Now, let's generate the Solidity code that verifies this Decider final proof
  122. let function_selector =
  123. get_function_selector_for_nova_cyclefold_verifier(nova.z_0.len() * 2 + 1);
  124. let calldata: Vec<u8> = prepare_calldata(
  125. function_selector,
  126. nova.i,
  127. nova.z_0,
  128. nova.z_i,
  129. &nova.U_i,
  130. &nova.u_i,
  131. proof,
  132. )
  133. .unwrap();
  134. // prepare the setup params for the solidity verifier
  135. let nova_cyclefold_vk = NovaCycleFoldVerifierKey::from((decider_vp, f_circuit.state_len()));
  136. // generate the solidity code
  137. let decider_solidity_code = get_decider_template_for_cyclefold_decider(nova_cyclefold_vk);
  138. // verify the proof against the solidity code in the EVM
  139. let nova_cyclefold_verifier_bytecode = compile_solidity(&decider_solidity_code, "NovaDecider");
  140. let mut evm = Evm::default();
  141. let verifier_address = evm.create(nova_cyclefold_verifier_bytecode);
  142. let (_, output) = evm.call(verifier_address, calldata.clone());
  143. assert_eq!(*output.last().unwrap(), 1);
  144. // save smart contract and the calldata
  145. println!("storing nova-verifier.sol and the calldata into files");
  146. use std::fs;
  147. fs::write(
  148. "./examples/nova-verifier.sol",
  149. decider_solidity_code.clone(),
  150. )
  151. .unwrap();
  152. fs::write("./examples/solidity-calldata.calldata", calldata.clone()).unwrap();
  153. let s = solidity_verifiers::utils::get_formatted_calldata(calldata.clone());
  154. fs::write("./examples/solidity-calldata.inputs", s.join(",\n")).expect("");
  155. }