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.

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