use rand::thread_rng;
use std::time::{Duration, Instant};
use bellperson::bls::Engine;
use ff::{Field, ScalarEngine};
use bellperson::bls::Bls12;
use bellperson::{Circuit, ConstraintSystem, SynthesisError};
use bellperson::groth16::{
create_random_proof, create_random_proof_batch, generate_random_parameters,
prepare_verifying_key, verify_proof, verify_proofs_batch, 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
}
#[derive(Clone)]
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(())
}
}
#[test]
fn test_mimc() {
let rng = &mut thread_rng();
let constants = (0..MIMC_ROUNDS)
.map(|_| <Bls12 as ScalarEngine>::Fr::random(rng))
.collect::<Vec<_>>();
println!("Creating parameters...");
let params = {
let c = MiMCDemo::<Bls12> {
xl: None,
xr: None,
constants: &constants,
};
generate_random_parameters(c, rng).unwrap()
};
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![];
let mut proofs = vec![];
let mut images = vec![];
for _ in 0..SAMPLES {
let xl = <Bls12 as ScalarEngine>::Fr::random(rng);
let xr = <Bls12 as ScalarEngine>::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, ¶ms, 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();
proofs.push(proof);
images.push(vec![image]);
}
println!("Creating batch proofs...");
let proving_batch = Instant::now();
{
let xl = <Bls12 as ScalarEngine>::Fr::random(rng);
let xr = <Bls12 as ScalarEngine>::Fr::random(rng);
let c = MiMCDemo {
xl: Some(xl),
xr: Some(xr),
constants: &constants,
};
let proofs = create_random_proof_batch(vec![c; SAMPLES as usize], ¶ms, rng).unwrap();
assert_eq!(proofs.len(), 50);
}
let proving_batch = proving_batch.elapsed().subsec_nanos() as f64 / 1_000_000_000f64;
println!(
"Proving time batch: {:04}s ({:04}s / proof)",
proving_batch,
proving_batch / SAMPLES as f64,
);
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: {:08}s", proving_avg);
println!("Average verifying time: {:08}s", verifying_avg);
{
let pvk = prepare_verifying_key(¶ms.vk);
let start = Instant::now();
let proofs: Vec<_> = proofs.iter().collect();
let valid = verify_proofs_batch(&pvk, &mut rand::rngs::OsRng, &proofs, &images).unwrap();
println!(
"Batch verification of {} proofs: {:04}s ({:04}s/proof)",
proofs.len(),
(start.elapsed().subsec_nanos() as f64) / 1_000_000_000f64,
((start.elapsed().subsec_nanos() as f64) / 1_000_000_000f64) / proofs.len() as f64,
);
assert!(valid, "failed batch verification");
let mut bad_proofs = proofs
.iter()
.map(|p| (*p).clone())
.collect::<Vec<Proof<_>>>();
for i in 0..proofs.len() {
use groupy::CurveProjective;
let p = &mut bad_proofs[i];
let mut a: <Bls12 as Engine>::G1 = p.a.into();
a.add_assign(&<Bls12 as Engine>::G1::one());
p.a = a.into_affine();
}
let bad_proofs_ref = bad_proofs.iter().collect::<Vec<_>>();
assert!(
!verify_proofs_batch(&pvk, &mut rand::rngs::OsRng, &bad_proofs_ref[..], &images)
.unwrap()
);
}
}