| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- // For randomness (during paramgen and proof generation)
- //use rand::thread_rng;
- // For benchmarking
- use std::time::{Duration, Instant};
- // from string scalar
- use drk::bls_extensions::BlsStringConversion;
- // Bring in some tools for using finite fiels
- use ff::PrimeField;
- // mimc constants
- //mod mimc_constants;
- //use mimc_constants::mimc_constants;
- // We're going to use the BLS12-381 pairing-friendly elliptic curve.
- use bls12_381::Bls12;
- // We'll use these interfaces to construct our circuit.
- use bellman::{Circuit, ConstraintSystem, SynthesisError};
- // We're going to use the Groth16 proving system.
- use bellman::groth16::{
- create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof, Proof,
- };
- const MIMC_ROUNDS: usize = 322;
- /// This is an implementation of MiMC, specifically a
- /// variant named `LongsightF322p3` for BLS12-381.
- /// See http://eprint.iacr.org/2016/492 for more
- /// information about this construction.
- ///
- /// ```
- /// function LongsightF322p3(xL ⦂ Fp, xR ⦂ Fp) {
- /// for i from 0 up to 321 {
- /// xL, xR := xR + (xL + Ci)^3, xL
- /// }
- /// return xL
- /// }
- /// ```
- fn mimc<Scalar: PrimeField>(mut xl: Scalar, mut xr: Scalar, constants: &[Scalar]) -> Scalar {
- assert_eq!(constants.len(), MIMC_ROUNDS);
- for i in 0..MIMC_ROUNDS {
- let mut tmp1 = xl;
- tmp1.add_assign(&constants[i]);
- let mut tmp2 = tmp1.square();
- tmp2.mul_assign(&tmp1);
- tmp2.add_assign(&xr);
- xr = xl;
- xl = tmp2;
- }
- xl
- }
- //macro_rules! from_slice {
- // ($data:expr, $len:literal) => {{
- // let mut array = [0; $len];
- // // panics if not enough data
- // let bytes = &$data[..array.len()];
- // assert_eq!(bytes.len(), array.len());
- // for (a, b) in array.iter_mut().rev().zip(bytes.iter()) {
- // *a = *b;
- // }
- // //array.copy_from_slice(bytes.iter().rev());
- // array
- // }};
- //}
- /// This is our demo circuit for proving knowledge of the
- /// preimage of a MiMC hash invocation.
- struct MiMCDemo<'a, Scalar: PrimeField> {
- xl: Option<Scalar>,
- xr: Option<Scalar>,
- constants: &'a [Scalar],
- }
- /// Our demo circuit implements this `Circuit` trait which
- /// is used during paramgen and proving in order to
- /// synthesize the constraint system.
- impl<'a, Scalar: PrimeField> Circuit<Scalar> for MiMCDemo<'a, Scalar> {
- fn synthesize<CS: ConstraintSystem<Scalar>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
- assert_eq!(self.constants.len(), MIMC_ROUNDS);
- // Allocate the first component of the preimage.
- let mut xl_value = self.xl;
- let mut xl = cs.alloc(
- || "preimage xl",
- || xl_value.ok_or(SynthesisError::AssignmentMissing),
- )?;
- // Allocate the second component of the preimage.
- let mut xr_value = self.xr;
- let mut xr = cs.alloc(
- || "preimage xr",
- || xr_value.ok_or(SynthesisError::AssignmentMissing),
- )?;
- for i in 0..MIMC_ROUNDS {
- // xL, xR := xR + (xL + Ci)^3, xL
- let cs = &mut cs.namespace(|| format!("round {}", i));
- // tmp = (xL + Ci)^2
- let tmp_value = xl_value.map(|mut e| {
- println!("{:?}", e);
- e.add_assign(&self.constants[i]);
- e.square()
- });
- // println!("tmp_value {:?} {:?}", self.constants[i], tmp_value);
- let tmp = cs.alloc(
- || "tmp",
- || tmp_value.ok_or(SynthesisError::AssignmentMissing),
- )?;
- cs.enforce(
- || "tmp = (xL + Ci)^2",
- |lc| lc + xl + (self.constants[i], CS::one()),
- |lc| lc + xl + (self.constants[i], CS::one()),
- |lc| lc + tmp,
- );
- // new_xL = xR + (xL + Ci)^3
- // new_xL = xR + tmp * (xL + Ci)
- // new_xL - xR = tmp * (xL + Ci)
- let new_xl_value = xl_value.map(|mut e| {
- e.add_assign(&self.constants[i]);
- e.mul_assign(&tmp_value.unwrap());
- e.add_assign(&xr_value.unwrap());
- e
- });
- let new_xl = if i == (MIMC_ROUNDS - 1) {
- // This is the last round, xL is our image and so
- // we allocate a public input.
- cs.alloc_input(
- || "image",
- || new_xl_value.ok_or(SynthesisError::AssignmentMissing),
- )?
- } else {
- cs.alloc(
- || "new_xl",
- || new_xl_value.ok_or(SynthesisError::AssignmentMissing),
- )?
- };
- cs.enforce(
- || "new_xL = xR + (xL + Ci)^3",
- |lc| lc + tmp,
- |lc| lc + xl + (self.constants[i], CS::one()),
- |lc| lc + new_xl - xr,
- );
- println!("{:?}", i);
- println!("{:?} {:?}", xl_value, xr_value);
- println!("{:?}", new_xl_value);
- // xR = xL
- xr = xl;
- xr_value = xl_value;
- // xL = new_xL
- xl = new_xl;
- xl_value = new_xl_value;
- }
- Ok(())
- }
- }
- fn main() {
- use rand::rngs::OsRng;
- // // Generate the MiMC round constants
- // let constants = (0..MIMC_ROUNDS)
- // .map(|_| Scalar::random(&mut OsRng))
- // .collect::<Vec<_>>();
- let constants = Vec::new();
- /*
- for const_str in mimc_constants() {
- let bytes = from_slice!(&hex::decode(const_str).unwrap(), 32);
- assert_eq!(bytes.len(), 32);
- let constant = Scalar::from_bytes(&bytes).unwrap();
- constants.push(constant);
- }
- */
- println!("Creating parameters...");
- // Create parameters for our circuit
- let params = {
- let c = MiMCDemo {
- xl: None,
- xr: None,
- constants: &constants,
- };
- generate_random_parameters::<Bls12, _, _>(c, &mut OsRng).unwrap()
- };
- // Prepare the verification key (for proof verification)
- let pvk = prepare_verifying_key(¶ms.vk);
- println!("Creating proofs...");
- // Let's benchmark stuff!
- const SAMPLES: u32 = 1;
- let mut total_proving = Duration::new(0, 0);
- let mut total_verifying = Duration::new(0, 0);
- // Just a place to put the proof data, so we can
- // benchmark deserialization.
- let mut proof_vec = vec![];
- for _ in 0..SAMPLES {
- // Generate a random preimage and compute the image
- // let xl = Scalar::random(&mut OsRng);
- // let xr = Scalar::random(&mut OsRng);
- let xl = bls12_381::Scalar::from_string(
- "15a36d1f0f390d8852a35a8c1908dd87a361ee3fd48fdf77b9819dc82d90607e",
- );
- let xr = bls12_381::Scalar::from_string(
- "015d8c7f5b43fe33f7891142c001d9251f3abeeb98fad3e87b0dc53c4ebf1891",
- );
- let image = mimc(xl, xr, &constants);
- proof_vec.truncate(0);
- let start = Instant::now();
- {
- // Create an instance of our circuit (with the
- // witness)
- let c = MiMCDemo {
- xl: Some(xl),
- xr: Some(xr),
- constants: &constants,
- };
- // Create a groth16 proof with our parameters.
- let proof = create_random_proof(c, ¶ms, &mut OsRng).unwrap();
- proof.write(&mut proof_vec).unwrap();
- }
- total_proving += start.elapsed();
- let start = Instant::now();
- let proof = Proof::read(&proof_vec[..]).unwrap();
- // Check the proof
- assert!(verify_proof(&pvk, &proof, &[image]).is_ok());
- total_verifying += start.elapsed();
- }
- let proving_avg = total_proving / SAMPLES;
- //let proving_avg =
- // proving_avg.subsec_nanos() as f64 / 1_000_000_000f64 +
- // (proving_avg.as_secs() as f64);
- let verifying_avg = total_verifying / SAMPLES;
- //let verifying_avg =
- // verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64 +
- // (verifying_avg.as_secs() as f64);
- println!("Average proving time: {:?} seconds", proving_avg);
- println!("Average verifying time: {:?} seconds", verifying_avg);
- }
|