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.

169 lines
5.6 KiB

  1. #![allow(non_snake_case)]
  2. #![allow(non_upper_case_globals)]
  3. #![allow(non_camel_case_types)]
  4. #![allow(clippy::upper_case_acronyms)]
  5. use ark_ff::PrimeField;
  6. use ark_r1cs_std::alloc::AllocVar;
  7. use ark_r1cs_std::fields::fp::FpVar;
  8. use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
  9. use core::marker::PhantomData;
  10. use std::time::Instant;
  11. use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective};
  12. use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2};
  13. use folding_schemes::commitment::{kzg::KZG, pedersen::Pedersen};
  14. use folding_schemes::folding::nova::{Nova, PreprocessorParam};
  15. use folding_schemes::frontend::FCircuit;
  16. use folding_schemes::transcript::poseidon::poseidon_canonical_config;
  17. use folding_schemes::{Error, FoldingScheme};
  18. /// This is the circuit that we want to fold, it implements the FCircuit trait. The parameter z_i
  19. /// denotes the current state which contains 5 elements, and z_{i+1} denotes the next state which
  20. /// we get by applying the step.
  21. /// In this example we set z_i and z_{i+1} to have five elements, and at each step we do different
  22. /// operations on each of them.
  23. #[derive(Clone, Copy, Debug)]
  24. pub struct MultiInputsFCircuit<F: PrimeField> {
  25. _f: PhantomData<F>,
  26. }
  27. impl<F: PrimeField> FCircuit<F> for MultiInputsFCircuit<F> {
  28. type Params = ();
  29. fn new(_params: Self::Params) -> Result<Self, Error> {
  30. Ok(Self { _f: PhantomData })
  31. }
  32. fn state_len(&self) -> usize {
  33. 5
  34. }
  35. fn external_inputs_len(&self) -> usize {
  36. 0
  37. }
  38. /// computes the next state values in place, assigning z_{i+1} into z_i, and computing the new
  39. /// z_{i+1}
  40. fn step_native(
  41. &self,
  42. _i: usize,
  43. z_i: Vec<F>,
  44. _external_inputs: Vec<F>,
  45. ) -> Result<Vec<F>, Error> {
  46. let a = z_i[0] + F::from(4_u32);
  47. let b = z_i[1] + F::from(40_u32);
  48. let c = z_i[2] * F::from(4_u32);
  49. let d = z_i[3] * F::from(40_u32);
  50. let e = z_i[4] + F::from(100_u32);
  51. Ok(vec![a, b, c, d, e])
  52. }
  53. /// generates the constraints for the step of F for the given z_i
  54. fn generate_step_constraints(
  55. &self,
  56. cs: ConstraintSystemRef<F>,
  57. _i: usize,
  58. z_i: Vec<FpVar<F>>,
  59. _external_inputs: Vec<FpVar<F>>,
  60. ) -> Result<Vec<FpVar<F>>, SynthesisError> {
  61. let four = FpVar::<F>::new_constant(cs.clone(), F::from(4u32))?;
  62. let forty = FpVar::<F>::new_constant(cs.clone(), F::from(40u32))?;
  63. let onehundred = FpVar::<F>::new_constant(cs.clone(), F::from(100u32))?;
  64. let a = z_i[0].clone() + four.clone();
  65. let b = z_i[1].clone() + forty.clone();
  66. let c = z_i[2].clone() * four;
  67. let d = z_i[3].clone() * forty;
  68. let e = z_i[4].clone() + onehundred;
  69. Ok(vec![a, b, c, d, e])
  70. }
  71. }
  72. /// cargo test --example multi_inputs
  73. #[cfg(test)]
  74. pub mod tests {
  75. use super::*;
  76. use ark_r1cs_std::{alloc::AllocVar, R1CSVar};
  77. use ark_relations::r1cs::ConstraintSystem;
  78. // test to check that the MultiInputsFCircuit computes the same values inside and outside the circuit
  79. #[test]
  80. fn test_f_circuit() {
  81. let cs = ConstraintSystem::<Fr>::new_ref();
  82. let circuit = MultiInputsFCircuit::<Fr>::new(()).unwrap();
  83. let z_i = vec![
  84. Fr::from(1_u32),
  85. Fr::from(1_u32),
  86. Fr::from(1_u32),
  87. Fr::from(1_u32),
  88. Fr::from(1_u32),
  89. ];
  90. let z_i1 = circuit.step_native(0, z_i.clone(), vec![]).unwrap();
  91. let z_iVar = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(z_i)).unwrap();
  92. let computed_z_i1Var = circuit
  93. .generate_step_constraints(cs.clone(), 0, z_iVar.clone(), vec![])
  94. .unwrap();
  95. assert_eq!(computed_z_i1Var.value().unwrap(), z_i1);
  96. }
  97. }
  98. /// cargo run --release --example multi_inputs
  99. fn main() {
  100. let num_steps = 10;
  101. let initial_state = vec![
  102. Fr::from(1_u32),
  103. Fr::from(1_u32),
  104. Fr::from(1_u32),
  105. Fr::from(1_u32),
  106. Fr::from(1_u32),
  107. ];
  108. let F_circuit = MultiInputsFCircuit::<Fr>::new(()).unwrap();
  109. let poseidon_config = poseidon_canonical_config::<Fr>();
  110. let mut rng = rand::rngs::OsRng;
  111. /// The idea here is that eventually we could replace the next line chunk that defines the
  112. /// `type N = Nova<...>` by using another folding scheme that fulfills the `FoldingScheme`
  113. /// trait, and the rest of our code would be working without needing to be updated.
  114. type N = Nova<
  115. Projective,
  116. GVar,
  117. Projective2,
  118. GVar2,
  119. MultiInputsFCircuit<Fr>,
  120. KZG<'static, Bn254>,
  121. Pedersen<Projective2>,
  122. >;
  123. println!("Prepare Nova ProverParams & VerifierParams");
  124. let nova_preprocess_params = PreprocessorParam::new(poseidon_config, F_circuit);
  125. let (nova_pp, nova_vp) = N::preprocess(&mut rng, &nova_preprocess_params).unwrap();
  126. println!("Initialize FoldingScheme");
  127. let mut folding_scheme = N::init(&nova_pp, F_circuit, initial_state.clone()).unwrap();
  128. // compute a step of the IVC
  129. for i in 0..num_steps {
  130. let start = Instant::now();
  131. folding_scheme.prove_step(rng, vec![]).unwrap();
  132. println!("Nova::prove_step {}: {:?}", i, start.elapsed());
  133. }
  134. let (running_instance, incoming_instance, cyclefold_instance) = folding_scheme.instances();
  135. println!("Run the Nova's IVC verifier");
  136. N::verify(
  137. nova_vp,
  138. initial_state.clone(),
  139. folding_scheme.state(), // latest state
  140. Fr::from(num_steps as u32),
  141. running_instance,
  142. incoming_instance,
  143. cyclefold_instance,
  144. )
  145. .unwrap();
  146. }