mimc.rs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. // For randomness (during paramgen and proof generation)
  2. use rand::thread_rng;
  3. // For benchmarking
  4. use std::time::{Duration, Instant};
  5. // Bring in some tools for using finite fiels
  6. use ff::{Field, PrimeField};
  7. // We're going to use the BLS12-381 pairing-friendly elliptic curve.
  8. use bls12_381::{Bls12, Scalar};
  9. // We'll use these interfaces to construct our circuit.
  10. use bellman::{Circuit, ConstraintSystem, SynthesisError};
  11. // We're going to use the Groth16 proving system.
  12. use bellman::groth16::{
  13. create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof, Proof,
  14. };
  15. const MIMC_ROUNDS: usize = 322;
  16. /// This is an implementation of MiMC, specifically a
  17. /// variant named `LongsightF322p3` for BLS12-381.
  18. /// See http://eprint.iacr.org/2016/492 for more
  19. /// information about this construction.
  20. ///
  21. /// ```
  22. /// function LongsightF322p3(xL ⦂ Fp, xR ⦂ Fp) {
  23. /// for i from 0 up to 321 {
  24. /// xL, xR := xR + (xL + Ci)^3, xL
  25. /// }
  26. /// return xL
  27. /// }
  28. /// ```
  29. fn mimc<Scalar: PrimeField>(mut xl: Scalar, mut xr: Scalar, constants: &[Scalar]) -> Scalar {
  30. assert_eq!(constants.len(), MIMC_ROUNDS);
  31. for i in 0..MIMC_ROUNDS {
  32. let mut tmp1 = xl;
  33. tmp1.add_assign(&constants[i]);
  34. let mut tmp2 = tmp1.square();
  35. tmp2.mul_assign(&tmp1);
  36. tmp2.add_assign(&xr);
  37. xr = xl;
  38. xl = tmp2;
  39. }
  40. xl
  41. }
  42. /// This is our demo circuit for proving knowledge of the
  43. /// preimage of a MiMC hash invocation.
  44. struct MiMCDemo<'a, Scalar: PrimeField> {
  45. xl: Option<Scalar>,
  46. xr: Option<Scalar>,
  47. constants: &'a [Scalar],
  48. }
  49. /// Our demo circuit implements this `Circuit` trait which
  50. /// is used during paramgen and proving in order to
  51. /// synthesize the constraint system.
  52. impl<'a, Scalar: PrimeField> Circuit<Scalar> for MiMCDemo<'a, Scalar> {
  53. fn synthesize<CS: ConstraintSystem<Scalar>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
  54. assert_eq!(self.constants.len(), MIMC_ROUNDS);
  55. // Allocate the first component of the preimage.
  56. let mut xl_value = self.xl;
  57. let mut xl = cs.alloc(
  58. || "preimage xl",
  59. || xl_value.ok_or(SynthesisError::AssignmentMissing),
  60. )?;
  61. // Allocate the second component of the preimage.
  62. let mut xr_value = self.xr;
  63. let mut xr = cs.alloc(
  64. || "preimage xr",
  65. || xr_value.ok_or(SynthesisError::AssignmentMissing),
  66. )?;
  67. for i in 0..MIMC_ROUNDS {
  68. // xL, xR := xR + (xL + Ci)^3, xL
  69. let cs = &mut cs.namespace(|| format!("round {}", i));
  70. // tmp = (xL + Ci)^2
  71. let tmp_value = xl_value.map(|mut e| {
  72. e.add_assign(&self.constants[i]);
  73. e.square()
  74. });
  75. let tmp = cs.alloc(
  76. || "tmp",
  77. || tmp_value.ok_or(SynthesisError::AssignmentMissing),
  78. )?;
  79. cs.enforce(
  80. || "tmp = (xL + Ci)^2",
  81. |lc| lc + xl + (self.constants[i], CS::one()),
  82. |lc| lc + xl + (self.constants[i], CS::one()),
  83. |lc| lc + tmp,
  84. );
  85. // new_xL = xR + (xL + Ci)^3
  86. // new_xL = xR + tmp * (xL + Ci)
  87. // new_xL - xR = tmp * (xL + Ci)
  88. let new_xl_value = xl_value.map(|mut e| {
  89. e.add_assign(&self.constants[i]);
  90. e.mul_assign(&tmp_value.unwrap());
  91. e.add_assign(&xr_value.unwrap());
  92. e
  93. });
  94. let new_xl = if i == (MIMC_ROUNDS - 1) {
  95. // This is the last round, xL is our image and so
  96. // we allocate a public input.
  97. cs.alloc_input(
  98. || "image",
  99. || new_xl_value.ok_or(SynthesisError::AssignmentMissing),
  100. )?
  101. } else {
  102. cs.alloc(
  103. || "new_xl",
  104. || new_xl_value.ok_or(SynthesisError::AssignmentMissing),
  105. )?
  106. };
  107. cs.enforce(
  108. || "new_xL = xR + (xL + Ci)^3",
  109. |lc| lc + tmp,
  110. |lc| lc + xl + (self.constants[i], CS::one()),
  111. |lc| lc + new_xl - xr,
  112. );
  113. // xR = xL
  114. xr = xl;
  115. xr_value = xl_value;
  116. // xL = new_xL
  117. xl = new_xl;
  118. xl_value = new_xl_value;
  119. }
  120. Ok(())
  121. }
  122. }
  123. fn main() {
  124. // This may not be cryptographically safe, use
  125. // `OsRng` (for example) in production software.
  126. let rng = &mut thread_rng();
  127. // Generate the MiMC round constants
  128. let constants = (0..MIMC_ROUNDS)
  129. .map(|_| Scalar::random(rng))
  130. .collect::<Vec<_>>();
  131. println!("Creating parameters...");
  132. // Create parameters for our circuit
  133. let params = {
  134. let c = MiMCDemo {
  135. xl: None,
  136. xr: None,
  137. constants: &constants,
  138. };
  139. generate_random_parameters::<Bls12, _, _>(c, rng).unwrap()
  140. };
  141. // Prepare the verification key (for proof verification)
  142. let pvk = prepare_verifying_key(&params.vk);
  143. println!("Creating proofs...");
  144. // Let's benchmark stuff!
  145. const SAMPLES: u32 = 10;
  146. let mut total_proving = Duration::new(0, 0);
  147. let mut total_verifying = Duration::new(0, 0);
  148. // Just a place to put the proof data, so we can
  149. // benchmark deserialization.
  150. let mut proof_vec = vec![];
  151. for _ in 0..SAMPLES {
  152. // Generate a random preimage and compute the image
  153. let xl = Scalar::random(rng);
  154. let xr = Scalar::random(rng);
  155. let image = mimc(xl, xr, &constants);
  156. proof_vec.truncate(0);
  157. let start = Instant::now();
  158. {
  159. // Create an instance of our circuit (with the
  160. // witness)
  161. let c = MiMCDemo {
  162. xl: Some(xl),
  163. xr: Some(xr),
  164. constants: &constants,
  165. };
  166. // Create a groth16 proof with our parameters.
  167. let proof = create_random_proof(c, &params, rng).unwrap();
  168. proof.write(&mut proof_vec).unwrap();
  169. }
  170. total_proving += start.elapsed();
  171. let start = Instant::now();
  172. let proof = Proof::read(&proof_vec[..]).unwrap();
  173. // Check the proof
  174. assert!(verify_proof(&pvk, &proof, &[image]).is_ok());
  175. total_verifying += start.elapsed();
  176. }
  177. let proving_avg = total_proving / SAMPLES;
  178. //let proving_avg =
  179. // proving_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (proving_avg.as_secs() as f64);
  180. let verifying_avg = total_verifying / SAMPLES;
  181. //let verifying_avg =
  182. // verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (verifying_avg.as_secs() as f64);
  183. println!("Average proving time: {:?} seconds", proving_avg);
  184. println!("Average verifying time: {:?} seconds", verifying_avg);
  185. }