forked from zkcrypto/bellman
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
17 changed files
with
2,441 additions
and
2,011 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
extern crate bellman; | ||
extern crate pairing; | ||
extern crate rand; | ||
|
||
// For randomness (during paramgen and proof generation) | ||
use rand::{thread_rng, Rng}; | ||
|
||
// For benchmarking | ||
use std::time::{Duration, Instant}; | ||
|
||
// Bring in some tools for using pairing-friendly curves | ||
use pairing::{ | ||
Engine, | ||
Field | ||
}; | ||
|
||
// We're going to use the BLS12-381 pairing-friendly elliptic curve. | ||
use pairing::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::{ | ||
generate_random_parameters, | ||
prepare_verifying_key, | ||
create_random_proof, | ||
verify_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<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 | ||
} | ||
|
||
/// This is our demo circuit for proving knowledge of the | ||
/// preimage of a MiMC hash invocation. | ||
struct MiMCDemo<'a, E: Engine> { | ||
xl: Option<E::Fr>, | ||
xr: Option<E::Fr>, | ||
constants: &'a [E::Fr] | ||
} | ||
|
||
/// Our demo circuit implements this `Circuit` trait which | ||
/// is used during paramgen and proving in order to | ||
/// synthesize the constraint system. | ||
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); | ||
|
||
// 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 mut tmp_value = xl_value.map(|mut e| { | ||
e.add_assign(&self.constants[i]); | ||
e.square(); | ||
e | ||
}); | ||
let mut 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 mut 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 mut 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 | ||
); | ||
|
||
// xR = xL | ||
xr = xl; | ||
xr_value = xl_value; | ||
|
||
// xL = new_xL | ||
xl = new_xl; | ||
xl_value = new_xl_value; | ||
} | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
fn main() { | ||
// This may not be cryptographically safe, use | ||
// `OsRng` (for example) in production software. | ||
let rng = &mut thread_rng(); | ||
|
||
// Generate the MiMC round constants | ||
let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>(); | ||
|
||
println!("Creating parameters..."); | ||
|
||
// Create parameters for our circuit | ||
let params = { | ||
let c = MiMCDemo::<Bls12> { | ||
xl: None, | ||
xr: None, | ||
constants: &constants | ||
}; | ||
|
||
generate_random_parameters(c, rng).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 = 50; | ||
let mut total_proving = Duration::new(0, 0); | ||
let mut total_verifying = Duration::new(0, 0); | ||
|
||
for _ in 0..SAMPLES { | ||
// Generate a random preimage and compute the image | ||
let xl = rng.gen(); | ||
let xr = rng.gen(); | ||
let image = mimc::<Bls12>(xl, xr, &constants); | ||
|
||
let start = Instant::now(); | ||
let proof = { | ||
// 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. | ||
create_random_proof(c, ¶ms, rng).unwrap() | ||
}; | ||
total_proving += start.elapsed(); | ||
|
||
let start = Instant::now(); | ||
// Check the proof | ||
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); | ||
} |
Oops, something went wrong.