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.

284 lines
8.6 KiB

  1. //! Demonstrates how to use Nova to produce a recursive proof of the correct execution of
  2. //! iterations of the MinRoot function, thereby realizing a Nova-based verifiable delay function (VDF).
  3. //! We execute a configurable number of iterations of the MinRoot function per step of Nova's recursion.
  4. type G1 = pasta_curves::pallas::Point;
  5. type G2 = pasta_curves::vesta::Point;
  6. use ::bellperson::{gadgets::num::AllocatedNum, ConstraintSystem, SynthesisError};
  7. use ff::PrimeField;
  8. use nova_snark::{
  9. traits::{
  10. circuit::{StepCircuit, TrivialTestCircuit},
  11. Group,
  12. },
  13. CompressedSNARK, PublicParams, RecursiveSNARK,
  14. };
  15. use num_bigint::BigUint;
  16. use std::time::Instant;
  17. #[derive(Clone, Debug)]
  18. struct MinRootIteration<F: PrimeField> {
  19. x_i: F,
  20. y_i: F,
  21. x_i_plus_1: F,
  22. y_i_plus_1: F,
  23. }
  24. impl<F: PrimeField> MinRootIteration<F> {
  25. // produces a sample non-deterministic advice, executing one invocation of MinRoot per step
  26. fn new(num_iters: usize, x_0: &F, y_0: &F) -> (Vec<F>, Vec<Self>) {
  27. // although this code is written generically, it is tailored to Pallas' scalar field
  28. // (p - 3 / 5)
  29. let exp = BigUint::parse_bytes(
  30. b"23158417847463239084714197001737581570690445185553317903743794198714690358477",
  31. 10,
  32. )
  33. .unwrap();
  34. let mut res = Vec::new();
  35. let mut x_i = *x_0;
  36. let mut y_i = *y_0;
  37. for _i in 0..num_iters {
  38. let x_i_plus_1 = (x_i + y_i).pow_vartime(exp.to_u64_digits()); // computes the fifth root of x_i + y_i
  39. // sanity check
  40. let sq = x_i_plus_1 * x_i_plus_1;
  41. let quad = sq * sq;
  42. let fifth = quad * x_i_plus_1;
  43. debug_assert_eq!(fifth, x_i + y_i);
  44. let y_i_plus_1 = x_i;
  45. res.push(Self {
  46. x_i,
  47. y_i,
  48. x_i_plus_1,
  49. y_i_plus_1,
  50. });
  51. x_i = x_i_plus_1;
  52. y_i = y_i_plus_1;
  53. }
  54. let z0 = vec![*x_0, *y_0];
  55. (z0, res)
  56. }
  57. }
  58. #[derive(Clone, Debug)]
  59. struct MinRootCircuit<F: PrimeField> {
  60. seq: Vec<MinRootIteration<F>>,
  61. }
  62. impl<F> StepCircuit<F> for MinRootCircuit<F>
  63. where
  64. F: PrimeField,
  65. {
  66. fn arity(&self) -> usize {
  67. 2
  68. }
  69. fn synthesize<CS: ConstraintSystem<F>>(
  70. &self,
  71. cs: &mut CS,
  72. z: &[AllocatedNum<F>],
  73. ) -> Result<Vec<AllocatedNum<F>>, SynthesisError> {
  74. let mut z_out: Result<Vec<AllocatedNum<F>>, SynthesisError> =
  75. Err(SynthesisError::AssignmentMissing);
  76. // use the provided inputs
  77. let x_0 = z[0].clone();
  78. let y_0 = z[1].clone();
  79. // variables to hold running x_i and y_i
  80. let mut x_i = x_0;
  81. let mut y_i = y_0;
  82. for i in 0..self.seq.len() {
  83. // non deterministic advice
  84. let x_i_plus_1 =
  85. AllocatedNum::alloc(cs.namespace(|| format!("x_i_plus_1_iter_{}", i)), || {
  86. Ok(self.seq[i].x_i_plus_1)
  87. })?;
  88. // check the following conditions hold:
  89. // (i) x_i_plus_1 = (x_i + y_i)^{1/5}, which can be more easily checked with x_i_plus_1^5 = x_i + y_i
  90. // (ii) y_i_plus_1 = x_i
  91. // (1) constraints for condition (i) are below
  92. // (2) constraints for condition (ii) is avoided because we just used x_i wherever y_i_plus_1 is used
  93. let x_i_plus_1_sq =
  94. x_i_plus_1.square(cs.namespace(|| format!("x_i_plus_1_sq_iter_{}", i)))?;
  95. let x_i_plus_1_quad =
  96. x_i_plus_1_sq.square(cs.namespace(|| format!("x_i_plus_1_quad_{}", i)))?;
  97. cs.enforce(
  98. || format!("x_i_plus_1_quad * x_i_plus_1 = x_i + y_i_iter_{}", i),
  99. |lc| lc + x_i_plus_1_quad.get_variable(),
  100. |lc| lc + x_i_plus_1.get_variable(),
  101. |lc| lc + x_i.get_variable() + y_i.get_variable(),
  102. );
  103. // return hash(x_i_plus_1, y_i_plus_1) since Nova circuits expect a single output
  104. if i == self.seq.len() - 1 {
  105. z_out = Ok(vec![x_i_plus_1.clone(), x_i.clone()]);
  106. }
  107. // update x_i and y_i for the next iteration
  108. y_i = x_i;
  109. x_i = x_i_plus_1;
  110. }
  111. z_out
  112. }
  113. fn output(&self, z: &[F]) -> Vec<F> {
  114. // sanity check
  115. debug_assert_eq!(z[0], self.seq[0].x_i);
  116. debug_assert_eq!(z[1], self.seq[0].y_i);
  117. // compute output using advice
  118. vec![
  119. self.seq[self.seq.len() - 1].x_i_plus_1,
  120. self.seq[self.seq.len() - 1].y_i_plus_1,
  121. ]
  122. }
  123. }
  124. fn main() {
  125. println!("Nova-based VDF with MinRoot delay function");
  126. println!("=========================================================");
  127. let num_steps = 10;
  128. for num_iters_per_step in [1024, 2048, 4096, 8192, 16384, 32768, 65535] {
  129. // number of iterations of MinRoot per Nova's recursive step
  130. let circuit_primary = MinRootCircuit {
  131. seq: vec![
  132. MinRootIteration {
  133. x_i: <G1 as Group>::Scalar::zero(),
  134. y_i: <G1 as Group>::Scalar::zero(),
  135. x_i_plus_1: <G1 as Group>::Scalar::zero(),
  136. y_i_plus_1: <G1 as Group>::Scalar::zero(),
  137. };
  138. num_iters_per_step
  139. ],
  140. };
  141. let circuit_secondary = TrivialTestCircuit::default();
  142. println!(
  143. "Proving {} iterations of MinRoot per step",
  144. num_iters_per_step
  145. );
  146. // produce public parameters
  147. println!("Producing public parameters...");
  148. let pp = PublicParams::<
  149. G1,
  150. G2,
  151. MinRootCircuit<<G1 as Group>::Scalar>,
  152. TrivialTestCircuit<<G2 as Group>::Scalar>,
  153. >::setup(circuit_primary, circuit_secondary.clone());
  154. println!(
  155. "Number of constraints per step (primary circuit): {}",
  156. pp.num_constraints().0
  157. );
  158. println!(
  159. "Number of constraints per step (secondary circuit): {}",
  160. pp.num_constraints().1
  161. );
  162. println!(
  163. "Number of variables per step (primary circuit): {}",
  164. pp.num_variables().0
  165. );
  166. println!(
  167. "Number of variables per step (secondary circuit): {}",
  168. pp.num_variables().1
  169. );
  170. // produce non-deterministic advice
  171. let (z0_primary, minroot_iterations) = MinRootIteration::new(
  172. num_iters_per_step * num_steps,
  173. &<G1 as Group>::Scalar::zero(),
  174. &<G1 as Group>::Scalar::one(),
  175. );
  176. let minroot_circuits = (0..num_steps)
  177. .map(|i| MinRootCircuit {
  178. seq: (0..num_iters_per_step)
  179. .map(|j| MinRootIteration {
  180. x_i: minroot_iterations[i * num_iters_per_step + j].x_i,
  181. y_i: minroot_iterations[i * num_iters_per_step + j].y_i,
  182. x_i_plus_1: minroot_iterations[i * num_iters_per_step + j].x_i_plus_1,
  183. y_i_plus_1: minroot_iterations[i * num_iters_per_step + j].y_i_plus_1,
  184. })
  185. .collect::<Vec<_>>(),
  186. })
  187. .collect::<Vec<_>>();
  188. let z0_secondary = vec![<G2 as Group>::Scalar::zero()];
  189. type C1 = MinRootCircuit<<G1 as Group>::Scalar>;
  190. type C2 = TrivialTestCircuit<<G2 as Group>::Scalar>;
  191. // produce a recursive SNARK
  192. println!("Generating a RecursiveSNARK...");
  193. let mut recursive_snark: Option<RecursiveSNARK<G1, G2, C1, C2>> = None;
  194. for (i, circuit_primary) in minroot_circuits.iter().take(num_steps).enumerate() {
  195. let start = Instant::now();
  196. let res = RecursiveSNARK::prove_step(
  197. &pp,
  198. recursive_snark,
  199. circuit_primary.clone(),
  200. circuit_secondary.clone(),
  201. z0_primary.clone(),
  202. z0_secondary.clone(),
  203. );
  204. assert!(res.is_ok());
  205. println!(
  206. "RecursiveSNARK::prove_step {}: {:?}, took {:?} ",
  207. i,
  208. res.is_ok(),
  209. start.elapsed()
  210. );
  211. recursive_snark = Some(res.unwrap());
  212. }
  213. assert!(recursive_snark.is_some());
  214. let recursive_snark = recursive_snark.unwrap();
  215. // verify the recursive SNARK
  216. println!("Verifying a RecursiveSNARK...");
  217. let start = Instant::now();
  218. let res = recursive_snark.verify(&pp, num_steps, z0_primary.clone(), z0_secondary.clone());
  219. println!(
  220. "RecursiveSNARK::verify: {:?}, took {:?}",
  221. res.is_ok(),
  222. start.elapsed()
  223. );
  224. assert!(res.is_ok());
  225. // produce a compressed SNARK
  226. println!("Generating a CompressedSNARK using Spartan with IPA-PC...");
  227. let start = Instant::now();
  228. type S1 = nova_snark::spartan_with_ipa_pc::RelaxedR1CSSNARK<G1>;
  229. type S2 = nova_snark::spartan_with_ipa_pc::RelaxedR1CSSNARK<G2>;
  230. let res = CompressedSNARK::<_, _, _, _, S1, S2>::prove(&pp, &recursive_snark);
  231. println!(
  232. "CompressedSNARK::prove: {:?}, took {:?}",
  233. res.is_ok(),
  234. start.elapsed()
  235. );
  236. assert!(res.is_ok());
  237. let compressed_snark = res.unwrap();
  238. // verify the compressed SNARK
  239. println!("Verifying a CompressedSNARK...");
  240. let start = Instant::now();
  241. let res = compressed_snark.verify(&pp, num_steps, z0_primary, z0_secondary);
  242. println!(
  243. "CompressedSNARK::verify: {:?}, took {:?}",
  244. res.is_ok(),
  245. start.elapsed()
  246. );
  247. assert!(res.is_ok());
  248. println!("=========================================================");
  249. }
  250. }