diff --git a/Cargo.toml b/Cargo.toml index ef1bfd7..3b915d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,12 +10,15 @@ crate-type = ["cdylib", "rlib"] [features] multicore = ["sapling-crypto/multicore", "bellman/multicore"] wasm = ["sapling-crypto/wasm", "bellman/wasm", "bellman/nolog"] +bench = [] [dependencies] rand = "0.4" blake2 = "0.8.1" -sapling-crypto = { package = "sapling-crypto_ce", version = "0.1.3", default-features = false } -bellman = { package = "bellman_ce", version = "0.3.4", default-features = false } +# sapling-crypto = { package = "sapling-crypto_ce", version = "0.1.3", default-features = false } +sapling-crypto = {package = "sapling-crypto_ce", path = "../sapling-crypto" } +# bellman = { package = "bellman_ce", version = "0.3.4", default-features = false } +bellman = {package = "bellman_ce", path = "../bellman" } [target.'cfg(target_arch = "wasm32")'.dependencies] console_error_panic_hook = { version = "0.1.1" } @@ -24,4 +27,4 @@ wee_alloc = "0.4.5" web-sys = {version = "0.3", features = ["console", "Performance", "Window"]} [target.'cfg(target_arch = "wasm32")'.dev-dependencies] -wasm-bindgen-test = "0.3" +wasm-bindgen-test = "0.3" \ No newline at end of file diff --git a/src/circuit/bench.rs b/src/circuit/bench.rs new file mode 100644 index 0000000..f46208c --- /dev/null +++ b/src/circuit/bench.rs @@ -0,0 +1,196 @@ +use crate::circuit::poseidon::PoseidonCircuit; +use crate::circuit::rln::{RLNCircuit, RLNInputs}; +use crate::merkle::MerkleTree; +use crate::poseidon::{Poseidon as PoseidonHasher, PoseidonParams}; +use rand::{Rand, SeedableRng, XorShiftRng}; +use sapling_crypto::bellman::groth16::*; +use sapling_crypto::bellman::pairing::ff::{Field, PrimeField, PrimeFieldRepr}; +use sapling_crypto::bellman::pairing::Engine; +use sapling_crypto::bellman::Circuit; +use sapling_crypto::circuit::test::TestConstraintSystem; +use std::error::Error; +use std::thread::sleep; +use std::time::{Duration, Instant}; + +pub struct BenchResult { + pub number_of_constaints: usize, + pub prover_key_size: usize, + pub prover_time: f64, +} + +impl BenchResult { + pub fn new() -> BenchResult { + BenchResult { + number_of_constaints: 0, + prover_key_size: 0, + prover_time: 0f64, + } + } + + pub fn print(&self) { + println!("number of constraints\n{}", self.number_of_constaints); + println!("prover key size\n{}", self.prover_key_size); + println!("prover time\n{}", self.prover_time); + } +} + +// TODO make it composable: curve, poseidon, merkle +pub fn run_rln_bn_24() -> BenchResult { + use sapling_crypto::bellman::pairing::bn256::Bn256; + let poseidon_params = PoseidonParams::::default(); + let rln_test = RLNTest::new(poseidon_params, 24); + rln_test.run() +} +pub fn run_rln_bn_32() -> BenchResult { + use sapling_crypto::bellman::pairing::bn256::Bn256; + let poseidon_params = PoseidonParams::::default(); + let rln_test = RLNTest::new(poseidon_params, 32); + rln_test.run() +} + +pub struct RLNTest +where + E: Engine, +{ + merkle_depth: usize, + poseidon_params: PoseidonParams, +} + +impl RLNTest +where + E: Engine, +{ + pub fn new(poseidon_params: PoseidonParams, merkle_depth: usize) -> RLNTest { + // let cs = TestConstraintSystem::::new(); + RLNTest:: { + poseidon_params, + merkle_depth, + } + } + + fn inputs(&self) -> RLNInputs { + let mut rng = XorShiftRng::from_seed([0x3dbe6258, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut hasher = PoseidonHasher::new(self.poseidon_params.clone()); + // Initialize empty merkle tree + let merkle_depth = self.merkle_depth; + let mut membership_tree = MerkleTree::empty(hasher.clone(), merkle_depth); + + // A. setup an identity + + let id_key = E::Fr::rand(&mut rng); + let id_comm = hasher.hash(vec![id_key.clone()]); + + // B. insert to the membership tree + + let id_index = 6; // any number below 2^depth will work + membership_tree.update(id_index, id_comm); + + // C.1 get membership witness + + let auth_path = membership_tree.witness(id_index); + assert!(membership_tree.check_inclusion(auth_path.clone(), id_index, id_key.clone())); + + // C.2 prepare sss + + // get current epoch + let epoch = E::Fr::rand(&mut rng); + + let signal_hash = E::Fr::rand(&mut rng); + // evaluation point is the signal_hash + let share_x = signal_hash.clone(); + + // calculate current line equation + let a_0 = id_key.clone(); + let a_1 = hasher.hash(vec![a_0, epoch]); + + // evaluate line equation + let mut share_y = a_1.clone(); + share_y.mul_assign(&share_x); + share_y.add_assign(&a_0); + + // calculate nullfier + let nullifier = hasher.hash(vec![a_1]); + + // compose the circuit + + let inputs = RLNInputs:: { + share_x: Some(share_x), + share_y: Some(share_y), + epoch: Some(epoch), + nullifier: Some(nullifier), + root: Some(membership_tree.root()), + id_key: Some(id_key), + auth_path: auth_path.into_iter().map(|w| Some(w)).collect(), + }; + + inputs + } + + fn empty_inputs(&self) -> RLNInputs { + RLNInputs:: { + share_x: None, + share_y: None, + epoch: None, + nullifier: None, + root: None, + id_key: None, + auth_path: vec![None; self.merkle_depth], + } + } + + pub fn run(&self) -> BenchResult { + let mut rng = XorShiftRng::from_seed([0x3dbe6258, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let hasher = PoseidonCircuit::new(self.poseidon_params.clone()); + let inputs = self.inputs(); + let circuit = RLNCircuit:: { + inputs: inputs.clone(), + hasher: hasher.clone(), + }; + + let mut result = BenchResult::new(); + + let mut cs = TestConstraintSystem::::new(); + { + let circuit = circuit.clone(); + circuit.synthesize(&mut cs).unwrap(); + let unsatisfied = cs.which_is_unsatisfied(); + if unsatisfied.is_some() { + panic!("unsatisfied\n{}", unsatisfied.unwrap()); + } + let unconstrained = cs.find_unconstrained(); + if !unconstrained.is_empty() { + panic!("unconstrained\n{}", unconstrained); + } + assert!(cs.is_satisfied()); + result.number_of_constaints = cs.num_constraints(); + } + + { + let parameters = { + let inputs = self.empty_inputs(); + let circuit = RLNCircuit:: { + inputs, + hasher: hasher.clone(), + }; + let parameters = generate_random_parameters(circuit, &mut rng).unwrap(); + parameters + }; + + let mut v = vec![]; + parameters.write(&mut v).unwrap(); + + result.prover_key_size = v.len(); + + let now = Instant::now(); + let proof = create_random_proof(circuit, ¶meters, &mut rng).unwrap(); + + result.prover_time = now.elapsed().as_millis() as f64 / 1000.0; + + let verifing_key = prepare_verifying_key(¶meters.vk); + assert!(verify_proof(&verifing_key, &proof, &inputs.public_inputs()).unwrap()); + } + + result + } +} diff --git a/src/circuit/mod.rs b/src/circuit/mod.rs index 6262123..7779873 100644 --- a/src/circuit/mod.rs +++ b/src/circuit/mod.rs @@ -1,3 +1,6 @@ mod polynomial; pub mod poseidon; pub mod rln; + +#[cfg(any(test, feature = "bench"))] +pub mod bench; diff --git a/src/circuit/rln.rs b/src/circuit/rln.rs index 68e8478..bfc5728 100644 --- a/src/circuit/rln.rs +++ b/src/circuit/rln.rs @@ -49,7 +49,7 @@ impl RLNInputs where E: Engine, { - fn public_inputs(&self) -> Vec { + pub fn public_inputs(&self) -> Vec { vec![ self.root.unwrap(), self.epoch.unwrap(), @@ -371,187 +371,20 @@ where #[cfg(test)] mod test { - use super::{RLNCircuit, RLNInputs}; - use crate::circuit::poseidon::PoseidonCircuit; - use crate::merkle::MerkleTree; - use crate::poseidon::{Poseidon as PoseidonHasher, PoseidonParams}; - use rand::{Rand, SeedableRng, XorShiftRng}; - // use sapling_crypto::bellman::groth16::{ - // create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof, Parameters, - // }; - use sapling_crypto::bellman::groth16::*; - use sapling_crypto::bellman::pairing::ff::{Field, PrimeField, PrimeFieldRepr}; - use sapling_crypto::bellman::pairing::Engine; - use sapling_crypto::bellman::Circuit; - use sapling_crypto::circuit::test::TestConstraintSystem; - use std::thread::sleep; - use std::time::{Duration, Instant}; - struct RLNTest - where - E: Engine, - { - // cs: TestConstraintSystem, - merkle_depth: usize, - poseidon_params: PoseidonParams, - } + use super::RLNInputs; + use crate::circuit::bench; - impl RLNTest - where - E: Engine, - { - pub fn new(poseidon_params: PoseidonParams, merkle_depth: usize) -> RLNTest { - // let cs = TestConstraintSystem::::new(); - RLNTest:: { - poseidon_params, - merkle_depth, - } - } - - fn inputs(&self) -> RLNInputs { - let mut rng = XorShiftRng::from_seed([0x3dbe6258, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - let mut hasher = PoseidonHasher::new(self.poseidon_params.clone()); - // Initialize empty merkle tree - let merkle_depth = self.merkle_depth; - let mut membership_tree = MerkleTree::empty(hasher.clone(), merkle_depth); - - // A. setup an identity - - let id_key = E::Fr::rand(&mut rng); - let id_comm = hasher.hash(vec![id_key.clone()]); - - // B. insert to the membership tree - - let id_index = 6; // any number below 2^depth will work - membership_tree.update(id_index, id_comm); - - // C.1 get membership witness - - let auth_path = membership_tree.witness(id_index); - assert!(membership_tree.check_inclusion(auth_path.clone(), id_index, id_key.clone())); - - // C.2 prepare sss - - // get current epoch - let epoch = E::Fr::rand(&mut rng); - - let signal_hash = E::Fr::rand(&mut rng); - // evaluation point is the signal_hash - let share_x = signal_hash.clone(); - - // calculate current line equation - let a_0 = id_key.clone(); - let a_1 = hasher.hash(vec![a_0, epoch]); - - // evaluate line equation - let mut share_y = a_1.clone(); - share_y.mul_assign(&share_x); - share_y.add_assign(&a_0); - - // calculate nullfier - let nullifier = hasher.hash(vec![a_1]); - - // compose the circuit - - let inputs = RLNInputs:: { - share_x: Some(share_x), - share_y: Some(share_y), - epoch: Some(epoch), - nullifier: Some(nullifier), - root: Some(membership_tree.root()), - id_key: Some(id_key), - auth_path: auth_path.into_iter().map(|w| Some(w)).collect(), - }; - - inputs - } - - fn empty_inputs(&self) -> RLNInputs { - RLNInputs:: { - share_x: None, - share_y: None, - epoch: None, - nullifier: None, - root: None, - id_key: None, - auth_path: vec![None; self.merkle_depth], - } - } - - pub fn run(&self) { - let mut rng = XorShiftRng::from_seed([0x3dbe6258, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - - let hasher = PoseidonCircuit::new(self.poseidon_params.clone()); - let inputs = self.inputs(); - let circuit = RLNCircuit:: { - inputs: inputs.clone(), - hasher: hasher.clone(), - }; - let mut cs = TestConstraintSystem::::new(); - { - let circuit = circuit.clone(); - circuit.synthesize(&mut cs).unwrap(); - let unsatisfied = cs.which_is_unsatisfied(); - if unsatisfied.is_some() { - panic!("unsatisfied\n{}", unsatisfied.unwrap()); - } - let unconstrained = cs.find_unconstrained(); - if !unconstrained.is_empty() { - panic!("unconstrained\n{}", unconstrained); - } - assert!(cs.is_satisfied()); - println!("number of constaints: {}", cs.num_constraints()); - } - - { - let parameters = { - let inputs = self.empty_inputs(); - let circuit = RLNCircuit:: { - inputs, - hasher: hasher.clone(), - }; - let parameters = generate_random_parameters(circuit, &mut rng).unwrap(); - parameters - }; - - // let mut key_size = 0; - // let point_size = (((E::Fr::NUM_BITS / 64) + 1) * 8 * 2) as usize; - // key_size += parameters.a.len() * point_size; - // key_size += parameters.l.len() * point_size; - // key_size += parameters.h.len() * point_size; - // key_size += parameters.b_g1.len() * point_size; - // key_size += parameters.b_g2.len() * point_size * 2; - // println!("prover key size in bytes: {}", key_size); - - let mut v = vec![]; - parameters.write(&mut v).unwrap(); - - println!("circuit parameter size in bytes: {}", v.len()); - - let now = Instant::now(); - let proof = create_random_proof(circuit, ¶meters, &mut rng).unwrap(); - println!("prover time: {}", now.elapsed().as_millis() as f64 / 1000.0); - - let verifing_key = prepare_verifying_key(¶meters.vk); - assert!(verify_proof(&verifing_key, &proof, &inputs.public_inputs()).unwrap()); - } - } + #[test] + fn test_rln_bn_24() { + let result = bench::run_rln_bn_24(); + result.print(); } #[test] - fn test_rln_24() { - use sapling_crypto::bellman::pairing::bn256::Bn256; - let poseidon_params = PoseidonParams::::default(); - let rln_test = RLNTest::new(poseidon_params, 24); - rln_test.run(); - } - - #[test] - fn test_rln_32() { - use sapling_crypto::bellman::pairing::bn256::Bn256; - let poseidon_params = PoseidonParams::::default(); - let rln_test = RLNTest::new(poseidon_params, 32); - rln_test.run(); + fn test_rln_bn_32() { + let result = bench::run_rln_bn_32(); + result.print(); } #[test]