extern crate bellperson;
extern crate ff;
extern crate paired;
extern crate phase21;
extern crate rand;
use rand::thread_rng;
use std::time::{Duration, Instant};
use paired::Engine;
use ff::Field;
use paired::bls12_381::{Bls12, Fr};
use bellperson::{Circuit, ConstraintSystem, SynthesisError};
use bellperson::groth16::{create_random_proof, prepare_verifying_key, verify_proof, Proof};
const MIMC_ROUNDS: usize = 322;
fn mimc<E: Engine>(mut xl: E::Fr, mut xr: E::Fr, constants: &[E::Fr]) -> E::Fr {
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;
tmp2.square();
tmp2.mul_assign(&tmp1);
tmp2.add_assign(&xr);
xr = xl;
xl = tmp2;
}
xl
}
struct MiMCDemo<'a, E: Engine> {
xl: Option<E::Fr>,
xr: Option<E::Fr>,
constants: &'a [E::Fr],
}
impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> {
fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
assert_eq!(self.constants.len(), MIMC_ROUNDS);
let mut xl_value = self.xl;
let mut xl = cs.alloc(
|| "preimage xl",
|| xl_value.ok_or(SynthesisError::AssignmentMissing),
)?;
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 {
let cs = &mut cs.namespace(|| format!("round {}", i));
let tmp_value = xl_value.map(|mut e| {
e.add_assign(&self.constants[i]);
e.square();
e
});
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,
);
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) {
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,
);
xr = xl;
xr_value = xl_value;
xl = new_xl;
xl_value = new_xl_value;
}
Ok(())
}
}
fn main() {
let rng = &mut thread_rng();
let constants = (0..MIMC_ROUNDS)
.map(|_| Fr::random(rng))
.collect::<Vec<_>>();
println!("Creating parameters...");
let mut params = {
let c = MiMCDemo::<Bls12> {
xl: None,
xr: None,
constants: &constants,
};
phase21::MPCParameters::new(c).unwrap()
};
let old_params = params.clone();
params.contribute(rng);
let first_contrib = phase21::verify_contribution(&old_params, ¶ms).expect("should verify");
let old_params = params.clone();
params.contribute(rng);
let second_contrib = phase21::verify_contribution(&old_params, ¶ms).expect("should verify");
let verification_result = params
.verify(MiMCDemo::<Bls12> {
xl: None,
xr: None,
constants: &constants,
})
.unwrap();
assert!(phase21::contains_contribution(
&verification_result,
&first_contrib
));
assert!(phase21::contains_contribution(
&verification_result,
&second_contrib
));
let params = params.get_params();
let pvk = prepare_verifying_key(¶ms.vk);
println!("Creating proofs...");
const SAMPLES: u32 = 50;
let mut total_proving = Duration::new(0, 0);
let mut total_verifying = Duration::new(0, 0);
let mut proof_vec = vec![];
for _ in 0..SAMPLES {
let xl = Fr::random(rng);
let xr = Fr::random(rng);
let image = mimc::<Bls12>(xl, xr, &constants);
proof_vec.truncate(0);
let start = Instant::now();
{
let c = MiMCDemo {
xl: Some(xl),
xr: Some(xr),
constants: &constants,
};
let proof = create_random_proof(c, params, rng).unwrap();
proof.write(&mut proof_vec).unwrap();
}
total_proving += start.elapsed();
let start = Instant::now();
let proof = Proof::read(&proof_vec[..]).unwrap();
assert!(verify_proof(&pvk, &proof, &[image]).unwrap());
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);
}