diff --git a/lib/rln/.gitignore b/lib/rln/.gitignore deleted file mode 100644 index d9d206e..0000000 --- a/lib/rln/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -/target -/pkg -/examples/www -node_modules -*.key -Cargo.lock -.cargo -tmp_wasm \ No newline at end of file diff --git a/lib/rln/Cargo.toml b/lib/rln/Cargo.toml deleted file mode 100644 index 01a8185..0000000 --- a/lib/rln/Cargo.toml +++ /dev/null @@ -1,58 +0,0 @@ -[package] -name = "rln" -version = "0.1.0" -authors = ["Onur Kılıç "] -edition = "2018" - -[lib] -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 } -# sapling-crypto = {package = "sapling-crypto_ce", path = "../sapling-crypto", default-features = false } -bellman = { package = "bellman_ce", version = "0.3.4", default-features = false } -# bellman = {package = "bellman_ce", path = "../bellman", default-features = false } - -[target.'cfg(target_arch = "wasm32")'.dependencies] -hex = "0.4" -console_error_panic_hook = { version = "0.1.1" } -wasm-bindgen = "=0.2.60" -# wee_alloc = "0.4.5" -web-sys = {version = "0.3", features = ["console", "Performance", "Window"]} -js-sys = "0.3.37" - -[target.'cfg(target_arch = "wasm32")'.dev-dependencies] -wasm-bindgen-test = "0.3" - -[profile.release] -opt-level = 3 -lto = "thin" -incremental = true - -# build all our deps in release mode -[profile.dev.package."*"] -opt-level = 3 - -[profile.bench] -opt-level = 3 -debug = false -rpath = false -lto = "thin" -incremental = true -debug-assertions = false - - -[profile.test] -opt-level = 3 -incremental = true -debug-assertions = true -debug = true - - diff --git a/lib/rln/README.md b/lib/rln/README.md deleted file mode 100644 index 6a9e75b..0000000 --- a/lib/rln/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# RLN - -This is the development repo of rate limit nullifier zkSNARK circuits. - -For details, see work in progress document [here](https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view) - -## Test - -``` -cargo test --release --features multicore rln_32 -- --nocapture -``` - -## Generate Test Keys - -``` -cargo run --release --example export_test_keys -``` - -## Wasm Support - -### Build - -``` -wasm-pack build --release --target=nodejs --scope=rln --out-name=$PACKAGE --out-dir=$PACKAGE_DIR -- --features wasm -``` - -### Test - -With wasm-pack: - -``` -wasm-pack test --release --node -- --features wasm -``` - -With cargo: - -Follow the steps [here](https://rustwasm.github.io/docs/wasm-bindgen/wasm-bindgen-test/usage.html#appendix-using-wasm-bindgen-test-without-wasm-pack) before running the test, then run: - -``` -cargo test --release --target wasm32-unknown-unknown --features wasm -``` \ No newline at end of file diff --git a/lib/rln/examples/export_test_keys/main.rs b/lib/rln/examples/export_test_keys/main.rs deleted file mode 100644 index 14d352d..0000000 --- a/lib/rln/examples/export_test_keys/main.rs +++ /dev/null @@ -1,38 +0,0 @@ -#[cfg(not(target_arch = "wasm32"))] -fn main() { - use sapling_crypto::bellman::pairing::bn256::Bn256; - let merkle_depth = 32usize; - test_keys::export::(merkle_depth); -} - -#[cfg(target_arch = "wasm32")] -fn main() { - panic!("should not be run in wasm"); -} - -#[cfg(not(target_arch = "wasm32"))] -mod test_keys { - use sapling_crypto::bellman::pairing::Engine; - pub fn export(merkle_depth: usize) { - use rand::{SeedableRng, XorShiftRng}; - use rln::circuit::poseidon::PoseidonCircuit; - use rln::circuit::rln::{RLNCircuit, RLNInputs}; - use rln::poseidon::PoseidonParams; - use sapling_crypto::bellman::groth16::generate_random_parameters; - use std::fs::File; - - let poseidon_params = PoseidonParams::::new(8, 55, 3, None, None, None); - let mut rng = XorShiftRng::from_seed([0x3dbe6258, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - let hasher = PoseidonCircuit::new(poseidon_params.clone()); - let circuit = RLNCircuit:: { - inputs: RLNInputs::::empty(merkle_depth), - hasher: hasher.clone(), - }; - let parameters = generate_random_parameters(circuit, &mut rng).unwrap(); - let mut file_vk = File::create("verifier.key").unwrap(); - let vk = parameters.vk.clone(); - vk.write(&mut file_vk).unwrap(); - let mut file_paramaters = File::create("parameters.key").unwrap(); - parameters.write(&mut file_paramaters).unwrap(); - } -} diff --git a/lib/rln/src/circuit/bench.rs b/lib/rln/src/circuit/bench.rs deleted file mode 100644 index 099c8e6..0000000 --- a/lib/rln/src/circuit/bench.rs +++ /dev/null @@ -1,198 +0,0 @@ -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::io::{self, ErrorKind, Read, Write}; -use std::thread::sleep; -use std::time::{Duration, Instant}; - -use crate::public::RLN; - -pub struct ProverBenchResult { - pub prover_key_size: usize, - pub prover_time: f64, -} - -impl ProverBenchResult { - pub fn new() -> ProverBenchResult { - ProverBenchResult { - prover_key_size: 0, - prover_time: 0f64, - } - } -} - -pub fn run_rln_prover_bench( - merkle_depth: usize, - poseidon_params: PoseidonParams, -) -> ProverBenchResult { - RLNTest::new(merkle_depth, Some(poseidon_params)).run_prover_bench() -} - -pub struct RLNTest -where - E: Engine, -{ - rln: RLN, - merkle_depth: usize, -} - -impl RLNTest -where - E: Engine, -{ - fn rng() -> XorShiftRng { - XorShiftRng::from_seed([0x3dbe6258, 0x8d313d76, 0x3237db17, 0xe5bc0654]) - } - - fn empty_inputs(merkle_depth: usize) -> RLNInputs { - RLNInputs:: { - share_x: None, - share_y: None, - epoch: None, - nullifier: None, - root: None, - id_key: None, - auth_path: vec![None; merkle_depth], - } - } - - pub fn new(merkle_depth: usize, poseidon_params: Option>) -> RLNTest { - RLNTest { - rln: RLN::new(merkle_depth, poseidon_params), - merkle_depth, - } - } - - pub fn hasher(&self) -> PoseidonHasher { - self.rln.hasher() - } - - pub fn valid_inputs(&self) -> RLNInputs { - let mut rng = Self::rng(); - let mut hasher = self.rln.hasher(); - - // 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: E::Fr = 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: E::Fr = 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 - } - - pub fn synthesize(&self) -> usize { - let hasher = PoseidonCircuit::new(self.rln.poseidon_params()); - let inputs = self.valid_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()); - cs.num_constraints() - } - - pub fn run_prover_bench(&self) -> ProverBenchResult { - let mut raw_inputs: Vec = Vec::new(); - let inputs = self.valid_inputs(); - inputs.write(&mut raw_inputs).unwrap(); - - let mut proof: Vec = Vec::new(); - let now = Instant::now(); - self.rln - .generate_proof(raw_inputs.as_slice(), &mut proof) - .unwrap(); - let prover_time = now.elapsed().as_millis() as f64 / 1000.0; - - let mut raw_public_inputs: Vec = Vec::new(); - inputs.write_public_inputs(&mut raw_public_inputs).unwrap(); - - assert!( - self.rln - .verify(proof.as_slice(), raw_public_inputs.as_slice()) - .unwrap(), - true - ); - - let mut circuit_parameters: Vec = Vec::new(); - self.rln - .export_circuit_parameters(&mut circuit_parameters) - .unwrap(); - let prover_key_size = circuit_parameters.len(); - - ProverBenchResult { - prover_time, - prover_key_size, - } - } - - pub fn export_circuit_parameters(&self, w: W) -> io::Result<()> { - self.rln.export_circuit_parameters(w) - } -} diff --git a/lib/rln/src/circuit/mod.rs b/lib/rln/src/circuit/mod.rs deleted file mode 100644 index 7779873..0000000 --- a/lib/rln/src/circuit/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod polynomial; -pub mod poseidon; -pub mod rln; - -#[cfg(any(test, feature = "bench"))] -pub mod bench; diff --git a/lib/rln/src/circuit/polynomial.rs b/lib/rln/src/circuit/polynomial.rs deleted file mode 100644 index d49801c..0000000 --- a/lib/rln/src/circuit/polynomial.rs +++ /dev/null @@ -1,46 +0,0 @@ -use sapling_crypto::bellman::pairing::ff::{Field, PrimeField, PrimeFieldRepr}; -use sapling_crypto::bellman::pairing::Engine; -use sapling_crypto::bellman::{Circuit, ConstraintSystem, SynthesisError, Variable}; -use sapling_crypto::circuit::{boolean, ecc, num, Assignment}; - -// helper for horner evaluation methods -// b = a_0 + a_1 * x -pub fn allocate_add_with_coeff( - mut cs: CS, - a1: &num::AllocatedNum, - x: &num::AllocatedNum, - a0: &num::AllocatedNum, -) -> Result, SynthesisError> -where - E: Engine, - CS: ConstraintSystem, -{ - let ax = num::AllocatedNum::alloc(cs.namespace(|| "a1x"), || { - let mut ax_val = *a1.get_value().get()?; - let x_val = *x.get_value().get()?; - ax_val.mul_assign(&x_val); - Ok(ax_val) - })?; - - cs.enforce( - || "a1*x", - |lc| lc + a1.get_variable(), - |lc| lc + x.get_variable(), - |lc| lc + ax.get_variable(), - ); - - let y = num::AllocatedNum::alloc(cs.namespace(|| "y"), || { - let ax_val = *ax.get_value().get()?; - let mut y_val = *a0.get_value().get()?; - y_val.add_assign(&ax_val); - Ok(y_val) - })?; - - cs.enforce( - || "enforce y", - |lc| lc + ax.get_variable() + a0.get_variable(), - |lc| lc + CS::one(), - |lc| lc + y.get_variable(), - ); - Ok(y) -} diff --git a/lib/rln/src/circuit/poseidon.rs b/lib/rln/src/circuit/poseidon.rs deleted file mode 100644 index c86cbfd..0000000 --- a/lib/rln/src/circuit/poseidon.rs +++ /dev/null @@ -1,403 +0,0 @@ -use crate::poseidon::{Poseidon as PoseidonHasher, PoseidonParams}; -use sapling_crypto::bellman::pairing::ff::{Field, PrimeField, PrimeFieldRepr}; -use sapling_crypto::bellman::pairing::Engine; -use sapling_crypto::bellman::{Circuit, ConstraintSystem, LinearCombination, SynthesisError}; -use sapling_crypto::circuit::{boolean, ecc, num, Assignment}; - -#[derive(Clone)] -struct Element -where - E: Engine, -{ - an: Option>, - nu: Option>, -} - -enum RoundType { - Full, - Partial, - Exhausted, -} - -struct RoundCtx<'a, E> -where - E: Engine, -{ - number: usize, - params: &'a PoseidonParams, -} - -struct State -where - E: Engine, -{ - elements: Vec>, -} - -#[derive(Clone)] -pub struct PoseidonCircuit -where - E: Engine, -{ - params: PoseidonParams, -} - -impl Element -where - E: Engine, -{ - pub fn new_from_alloc(an: num::AllocatedNum) -> Self { - return Element { - an: Some(an), - nu: None, - }; - } - - pub fn new_from_num(nu: num::Num) -> Self { - return Element { - an: None, - nu: Some(nu), - }; - } - - pub fn is_allocated(&self) -> bool { - return self.an.is_some(); - } - - pub fn is_number(&self) -> bool { - return self.nu.is_some(); - } - - pub fn update_with_allocated(&mut self, an: num::AllocatedNum) { - self.an = Some(an); - self.nu = None; - } - - pub fn update_with_num(&mut self, nu: num::Num) { - self.nu = Some(nu); - self.an = None; - } - - pub fn num(&self) -> num::Num { - if let Some(nu) = self.nu.clone() { - nu - } else { - match self.an.clone() { - Some(an) => num::Num::from(an), - None => panic!("element not exist"), - } - } - } - - pub fn allocate>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - match self.nu.clone() { - Some(nu) => { - let v = num::AllocatedNum::alloc(cs.namespace(|| "allocate num"), || { - nu.get_value() - .ok_or_else(|| SynthesisError::AssignmentMissing) - })?; - cs.enforce( - || format!("enforce allocated"), - |_| nu.lc(E::Fr::one()), - |lc| lc + CS::one(), - |lc| lc + v.get_variable(), - ); - Ok(v) - } - None => panic!(""), - } - } - - pub fn allocated(&self) -> Option> { - self.an.clone() - } -} - -impl<'a, E> RoundCtx<'a, E> -where - E: Engine, -{ - pub fn new(params: &'a PoseidonParams) -> Self { - RoundCtx { - params, - number: 0usize, - } - } - - pub fn width(&self) -> usize { - self.params.width() - } - - pub fn round_number(&self) -> usize { - self.number - } - - pub fn is_full_round(&self) -> bool { - match self.round_type() { - RoundType::Full => true, - _ => false, - } - } - - pub fn is_exhausted(&self) -> bool { - match self.round_type() { - RoundType::Exhausted => true, - _ => false, - } - } - - pub fn is_last_round(&self) -> bool { - self.number == self.params.total_rounds() - 1 - } - - pub fn in_transition(&self) -> bool { - let a1 = self.params.full_round_half_len(); - let a2 = a1 + self.params.partial_round_len(); - self.number == a1 - 1 || self.number == a2 - 1 - } - - pub fn round_constant(&self) -> E::Fr { - self.params.round_constant(self.number) - } - - pub fn mds_matrix_row(&self, i: usize) -> Vec { - let w = self.width(); - let matrix = self.params.mds_matrix(); - matrix[i * w..(i + 1) * w].to_vec() - } - - pub fn round_type(&self) -> RoundType { - let a1 = self.params.full_round_half_len(); - let (a2, a3) = ( - a1 + self.params.partial_round_len(), - self.params.total_rounds(), - ); - if self.number < a1 { - RoundType::Full - } else if self.number >= a1 && self.number < a2 { - RoundType::Partial - } else if self.number >= a2 && self.number < a3 { - RoundType::Full - } else { - RoundType::Exhausted - } - } - - pub fn round_end(&mut self) { - self.number += 1; - } -} - -impl State -where - E: Engine, -{ - pub fn new(elements: Vec>) -> Self { - Self { elements } - } - - pub fn first_allocated>( - &mut self, - mut cs: CS, - ) -> Result, SynthesisError> { - let el = match self.elements[0].allocated() { - Some(an) => an, - None => self.elements[0].allocate(cs.namespace(|| format!("alloc first")))?, - }; - Ok(el) - } - - fn sbox>( - &mut self, - mut cs: CS, - ctx: &mut RoundCtx, - ) -> Result<(), SynthesisError> { - assert_eq!(ctx.width(), self.elements.len()); - - for i in 0..if ctx.is_full_round() { ctx.width() } else { 1 } { - let round_constant = ctx.round_constant(); - let si = { - match self.elements[i].allocated() { - Some(an) => an, - None => self.elements[i] - .allocate(cs.namespace(|| format!("alloc sbox input {}", i)))?, - } - }; - let si2 = num::AllocatedNum::alloc( - cs.namespace(|| format!("square with round constant {}", i)), - || { - let mut val = *si.get_value().get()?; - val.add_assign(&round_constant); - val.square(); - Ok(val) - }, - )?; - cs.enforce( - || format!("constraint square with round constant {}", i), - |lc| lc + si.get_variable() + (round_constant, CS::one()), - |lc| lc + si.get_variable() + (round_constant, CS::one()), - |lc| lc + si2.get_variable(), - ); - let si4 = si2.square(cs.namespace(|| format!("si^4 {}", i)))?; - let si5 = num::AllocatedNum::alloc(cs.namespace(|| format!("si^5 {}", i)), || { - let mut val = *si4.get_value().get()?; - let mut si_val = *si.get_value().get()?; - si_val.add_assign(&round_constant); - val.mul_assign(&si_val); - Ok(val) - })?; - cs.enforce( - || format!("constraint sbox result {}", i), - |lc| lc + si.get_variable() + (round_constant, CS::one()), - |lc| lc + si4.get_variable(), - |lc| lc + si5.get_variable(), - ); - self.elements[i].update_with_allocated(si5); - } - - Ok(()) - } - - fn mul_mds_matrix>( - &mut self, - ctx: &mut RoundCtx, - ) -> Result<(), SynthesisError> { - assert_eq!(ctx.width(), self.elements.len()); - - if !ctx.is_last_round() { - // skip mds multiplication in last round - - let mut new_state: Vec> = Vec::new(); - let w = ctx.width(); - - for i in 0..w { - let row = ctx.mds_matrix_row(i); - let mut acc = num::Num::::zero(); - for j in 0..w { - let mut r = self.elements[j].num(); - r.scale(row[j]); - acc.add_assign(&r); - } - new_state.push(acc); - } - - // round ends here - let is_full_round = ctx.is_full_round(); - let in_transition = ctx.in_transition(); - ctx.round_end(); - - // add round constants just after mds if - // first full round has just ended - // or in partial rounds expect the last one. - if in_transition == is_full_round { - // add round constants for elements in {1, t} - let round_constant = ctx.round_constant(); - for i in 1..w { - let mut constant_as_num = num::Num::::zero(); - constant_as_num = constant_as_num.add_bool_with_coeff( - CS::one(), - &boolean::Boolean::Constant(true), - round_constant, - ); - new_state[i].add_assign(&constant_as_num); - } - } - - for (s0, s1) in self.elements.iter_mut().zip(new_state) { - s0.update_with_num(s1); - } - } else { - // terminates hades - ctx.round_end(); - } - Ok(()) - } -} - -impl PoseidonCircuit -where - E: Engine, -{ - pub fn new(params: PoseidonParams) -> Self { - Self { params: params } - } - - pub fn width(&self) -> usize { - self.params.width() - } - - pub fn alloc>( - &self, - mut cs: CS, - input: Vec>, - ) -> Result, SynthesisError> { - assert!(input.len() < self.params.width()); - - let mut elements: Vec> = input - .iter() - .map(|el| Element::new_from_alloc(el.clone())) - .collect(); - elements.resize(self.width(), Element::new_from_num(num::Num::zero())); - - let mut state = State::new(elements); - let mut ctx = RoundCtx::new(&self.params); - loop { - match ctx.round_type() { - RoundType::Exhausted => { - break; - } - _ => { - let round_number = ctx.round_number(); - state.sbox(cs.namespace(|| format!("sbox {}", round_number)), &mut ctx)?; - state.mul_mds_matrix::(&mut ctx)?; - } - } - } - state.first_allocated(cs.namespace(|| format!("allocate result"))) - } -} - -#[test] -fn test_poseidon_circuit() { - use sapling_crypto::bellman::pairing::bn256::{Bn256, Fr}; - use sapling_crypto::bellman::pairing::ff::{Field, PrimeField, PrimeFieldRepr}; - use sapling_crypto::circuit::test::TestConstraintSystem; - - let mut cs = TestConstraintSystem::::new(); - let params = PoseidonParams::new(8, 55, 3, None, None, None); - - let inputs: Vec = ["0", "0"] - .iter() - .map(|e| Fr::from_str(e).unwrap()) - .collect(); - let allocated_inputs = inputs - .clone() - .into_iter() - .enumerate() - .map(|(i, e)| { - let a = num::AllocatedNum::alloc(cs.namespace(|| format!("input {}", i)), || Ok(e)); - a.unwrap() - }) - .collect(); - - let circuit = PoseidonCircuit::::new(params.clone()); - let res_allocated = circuit - .alloc(cs.namespace(|| "hash alloc"), allocated_inputs) - .unwrap(); - let result = res_allocated.get_value().unwrap(); - let mut poseidon = PoseidonHasher::new(params.clone()); - let expected = poseidon.hash(inputs); - - assert_eq!(result, expected); - assert!(cs.is_satisfied()); - println!( - "number of constraints for (t: {}, rf: {}, rp: {}), {}", - params.width(), - params.full_round_half_len() * 2, - params.partial_round_len(), - cs.num_constraints() - ); -} diff --git a/lib/rln/src/circuit/rln.rs b/lib/rln/src/circuit/rln.rs deleted file mode 100644 index 0067e05..0000000 --- a/lib/rln/src/circuit/rln.rs +++ /dev/null @@ -1,480 +0,0 @@ -use crate::circuit::polynomial::allocate_add_with_coeff; -use crate::circuit::poseidon::PoseidonCircuit; -use crate::poseidon::{Poseidon as PoseidonHasher, PoseidonParams}; -use sapling_crypto::bellman::pairing::ff::{Field, PrimeField, PrimeFieldRepr}; -use sapling_crypto::bellman::pairing::Engine; -use sapling_crypto::bellman::{Circuit, ConstraintSystem, SynthesisError, Variable}; -use sapling_crypto::circuit::{boolean, ecc, num, Assignment}; -use sapling_crypto::jubjub::{JubjubEngine, JubjubParams, PrimeOrder}; - -use std::io::{self, Read, Write}; - -// Rate Limit Nullifier - -#[derive(Clone)] -pub struct RLNInputs -where - E: Engine, -{ - // Public inputs - - // share, (x, y), - // where x should be hash of the signal - // and y is the evaluation - pub share_x: Option, - pub share_y: Option, - - // epoch is the external nullifier - // we derive the line equation and the nullifier from epoch - pub epoch: Option, - - // nullifier - pub nullifier: Option, - - // root is the current state of membership set - pub root: Option, - - // Private inputs - - // id_key must be a preimage of a leaf in membership tree. - // id_key also together with epoch will be used to construct - // a secret line equation together with the epoch - pub id_key: Option, - - // authentication path of the member - pub auth_path: Vec>, -} - -impl RLNInputs -where - E: Engine, -{ - pub fn public_inputs(&self) -> Vec { - vec![ - self.root.unwrap(), - self.epoch.unwrap(), - self.share_x.unwrap(), - self.share_y.unwrap(), - self.nullifier.unwrap(), - ] - } - - pub fn merkle_depth(&self) -> usize { - self.auth_path.len() - } - - pub fn empty(merkle_depth: usize) -> RLNInputs { - RLNInputs:: { - share_x: None, - share_y: None, - epoch: None, - nullifier: None, - root: None, - id_key: None, - auth_path: vec![None; merkle_depth], - } - } - - pub fn read(mut reader: R) -> io::Result> { - let mut buf = ::Repr::default(); - - buf.read_le(&mut reader)?; - let share_x = - E::Fr::from_repr(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - - - buf.read_le(&mut reader)?; - let share_y = - E::Fr::from_repr(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - buf.read_le(&mut reader)?; - let epoch = - E::Fr::from_repr(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - buf.read_le(&mut reader)?; - let nullifier = - E::Fr::from_repr(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - buf.read_le(&mut reader)?; - let root = - E::Fr::from_repr(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - buf.read_le(&mut reader)?; - let id_key = - E::Fr::from_repr(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - let auth_path = Self::decode_auth_path(&mut reader)?; - Ok(RLNInputs { - share_x: Some(share_x), - share_y: Some(share_y), - epoch: Some(epoch), - nullifier: Some(nullifier), - root: Some(root), - id_key: Some(id_key), - auth_path, - }) - } - - pub fn write(&self, mut writer: W) -> io::Result<()> { - self.share_x - .unwrap() - .into_repr() - .write_le(&mut writer) - .unwrap(); - self.share_y - .unwrap() - .into_repr() - .write_le(&mut writer) - .unwrap(); - self.epoch - .unwrap() - .into_repr() - .write_le(&mut writer) - .unwrap(); - self.nullifier - .unwrap() - .into_repr() - .write_le(&mut writer) - .unwrap(); - self.root - .unwrap() - .into_repr() - .write_le(&mut writer) - .unwrap(); - self.id_key - .unwrap() - .into_repr() - .write_le(&mut writer) - .unwrap(); - Self::encode_auth_path(&mut writer, self.auth_path.clone()).unwrap(); - Ok(()) - } - - pub fn read_public_inputs(mut reader: R) -> io::Result> { - let mut buf = ::Repr::default(); - buf.read_le(&mut reader)?; - let root = - E::Fr::from_repr(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - buf.read_le(&mut reader)?; - let epoch = - E::Fr::from_repr(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - buf.read_le(&mut reader)?; - let share_x = - E::Fr::from_repr(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - buf.read_le(&mut reader)?; - let share_y = - E::Fr::from_repr(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - buf.read_le(&mut reader)?; - let nullifier = - E::Fr::from_repr(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - Ok(vec![root, epoch, share_x, share_y, nullifier]) - } - - pub fn write_public_inputs(&self, mut writer: W) -> io::Result<()> { - self.root.unwrap().into_repr().write_le(&mut writer)?; - self.epoch.unwrap().into_repr().write_le(&mut writer)?; - self.share_x.unwrap().into_repr().write_le(&mut writer)?; - self.share_y.unwrap().into_repr().write_le(&mut writer)?; - self.nullifier.unwrap().into_repr().write_le(&mut writer)?; - Ok(()) - } - - pub fn encode_auth_path( - mut writer: W, - auth_path: Vec>, - ) -> io::Result<()> { - let path_len = auth_path.len() as u8; - writer.write(&[path_len])?; - for el in auth_path.iter() { - let c = el.unwrap(); - if c.1 { - writer.write(&[1])?; - } else { - writer.write(&[0])?; - } - c.0.into_repr().write_le(&mut writer).unwrap(); - } - Ok(()) - } - - pub fn decode_auth_path(mut reader: R) -> io::Result>> { - let mut byte_buf = vec![0u8; 1]; - let mut el_buf = ::Repr::default(); - let mut auth_path: Vec> = vec![]; - reader.read_exact(&mut byte_buf)?; - let path_len = byte_buf[0]; - if path_len < 2 { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "invalid path length", - )); - } - for _ in 0..path_len { - reader.read_exact(&mut byte_buf)?; - let path_dir = match byte_buf[0] { - 0u8 => false, - 1u8 => true, - _ => { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "invalid path direction", - )) - } - }; - el_buf.read_le(&mut reader)?; - let node = E::Fr::from_repr(el_buf) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - auth_path.push(Some((node, path_dir))); - } - Ok(auth_path) - } -} - -#[derive(Clone)] -pub struct RLNCircuit -where - E: Engine, -{ - pub inputs: RLNInputs, - pub hasher: PoseidonCircuit, -} - -impl Circuit for RLNCircuit -where - E: Engine, -{ - fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { - // 1. Part - // Membership constraints - // root == merkle_proof(auth_path, preimage_of_leaf) - - let root = num::AllocatedNum::alloc(cs.namespace(|| "root"), || { - let value = self.inputs.root.clone(); - Ok(*value.get()?) - })?; - root.inputize(cs.namespace(|| "root is public"))?; - - let preimage = num::AllocatedNum::alloc(cs.namespace(|| "preimage"), || { - let value = self.inputs.id_key; - Ok(*value.get()?) - })?; - - // identity is a leaf of membership tree - - let identity = self - .hasher - .alloc(cs.namespace(|| "identity"), vec![preimage.clone()])?; - - // accumulator up to the root - - let mut acc = identity.clone(); - - // ascend the tree - - let auth_path_witness = self.inputs.auth_path.clone(); - for (i, e) in auth_path_witness.into_iter().enumerate() { - let cs = &mut cs.namespace(|| format!("auth path {}", i)); - let position = boolean::Boolean::from(boolean::AllocatedBit::alloc( - cs.namespace(|| "position bit"), - e.map(|e| e.1), - )?); - let path_element = - num::AllocatedNum::alloc(cs.namespace(|| "path element"), || Ok(e.get()?.0))?; - - let (xr, xl) = num::AllocatedNum::conditionally_reverse( - cs.namespace(|| "conditional reversal of preimage"), - &acc, - &path_element, - &position, - )?; - - acc = self - .hasher - .alloc(cs.namespace(|| "hash couple"), vec![xl, xr])?; - } - - // see if it is a member - - cs.enforce( - || "enforce membership", - |lc| lc + acc.get_variable(), - |lc| lc + CS::one(), - |lc| lc + root.get_variable(), - ); - - // 2. Part - // Line Equation Constaints - // a_1 = hash(a_0, epoch) - // share_y == a_0 + a_1 * share_x - - let epoch = num::AllocatedNum::alloc(cs.namespace(|| "epoch"), || { - let value = self.inputs.epoch.clone(); - Ok(*value.get()?) - })?; - epoch.inputize(cs.namespace(|| "epoch is public"))?; - - let a_0 = preimage.clone(); - - // a_1 == h(a_0, epoch) - - let a_1 = self - .hasher - .alloc(cs.namespace(|| "a_1"), vec![a_0.clone(), epoch])?; - - let share_x = num::AllocatedNum::alloc(cs.namespace(|| "share x"), || { - let value = self.inputs.share_x.clone(); - Ok(*value.get()?) - })?; - share_x.inputize(cs.namespace(|| "share x is public"))?; - - // constaint the evaluation the line equation - - let eval = allocate_add_with_coeff(cs.namespace(|| "eval"), &a_1, &share_x, &a_0)?; - - let share_y = num::AllocatedNum::alloc(cs.namespace(|| "share y"), || { - let value = self.inputs.share_y.clone(); - Ok(*value.get()?) - })?; - share_y.inputize(cs.namespace(|| "share y is public"))?; - - // see if share satisfies the line equation - - cs.enforce( - || "enforce lookup", - |lc| lc + share_y.get_variable(), - |lc| lc + CS::one(), - |lc| lc + eval.get_variable(), - ); - - // 3. Part - // Nullifier constraints - - // hashing secret twice with epoch ingredient - // a_1 == hash(a_0, epoch) is already constrained - - // nullifier == hash(a_1) - - let nullifier_calculated = self - .hasher - .alloc(cs.namespace(|| "calculated nullifier"), vec![a_1.clone()])?; - - let nullifier = num::AllocatedNum::alloc(cs.namespace(|| "nullifier"), || { - let value = self.inputs.nullifier.clone(); - Ok(*value.get()?) - })?; - nullifier.inputize(cs.namespace(|| "nullifier is public"))?; - - // check if correct nullifier supplied - - cs.enforce( - || "enforce nullifier", - |lc| lc + nullifier_calculated.get_variable(), - |lc| lc + CS::one(), - |lc| lc + nullifier.get_variable(), - ); - - Ok(()) - } -} - -#[cfg(test)] -mod test { - - use super::RLNInputs; - use crate::circuit::bench; - use crate::poseidon::PoseidonParams; - use sapling_crypto::bellman::pairing::bls12_381::Bls12; - use sapling_crypto::bellman::pairing::bn256::Bn256; - use sapling_crypto::bellman::pairing::Engine; - - struct TestSuite { - merkle_depth: usize, - poseidon_parameters: PoseidonParams, - } - - fn cases() -> Vec> { - vec![ - TestSuite { - merkle_depth: 3, - poseidon_parameters: PoseidonParams::new(8, 55, 3, None, None, None), - }, - TestSuite { - merkle_depth: 24, - poseidon_parameters: PoseidonParams::new(8, 55, 3, None, None, None), - }, - TestSuite { - merkle_depth: 32, - poseidon_parameters: PoseidonParams::new(8, 55, 3, None, None, None), - }, - TestSuite { - merkle_depth: 16, - poseidon_parameters: PoseidonParams::new(8, 33, 3, None, None, None), - }, - TestSuite { - merkle_depth: 24, - poseidon_parameters: PoseidonParams::new(8, 33, 3, None, None, None), - }, - TestSuite { - merkle_depth: 32, - poseidon_parameters: PoseidonParams::new(8, 33, 3, None, None, None), - }, - ] - } - - #[test] - fn test_rln_bn() { - use sapling_crypto::bellman::pairing::bn256::Bn256; - let cases = cases::(); - for case in cases.iter() { - let rln_test = bench::RLNTest::::new( - case.merkle_depth, - Some(case.poseidon_parameters.clone()), - ); - let num_constraints = rln_test.synthesize(); - let result = rln_test.run_prover_bench(); - println!( - "bn256, t: {}, rf: {}, rp: {}, merkle depth: {}", - case.poseidon_parameters.width(), - case.poseidon_parameters.full_round_half_len() * 2, - case.poseidon_parameters.partial_round_len(), - case.merkle_depth, - ); - println!("number of constatins:\t{}", num_constraints); - println!("prover key size:\t{}", result.prover_key_size); - println!("prover time:\t{}", result.prover_time); - } - } - - #[test] - fn test_input_serialization() { - use sapling_crypto::bellman::pairing::bn256::{Bn256, Fr}; - use sapling_crypto::bellman::pairing::ff::{Field, PrimeField, PrimeFieldRepr}; - let share_x = Fr::from_str("1").unwrap(); - let share_y = Fr::from_str("2").unwrap(); - let epoch = Fr::from_str("3").unwrap(); - let nullifier = Fr::from_str("4").unwrap(); - let root = Fr::from_str("5").unwrap(); - let id_key = Fr::from_str("6").unwrap(); - let auth_path = vec![ - Some((Fr::from_str("20").unwrap(), false)), - Some((Fr::from_str("21").unwrap(), true)), - Some((Fr::from_str("22").unwrap(), true)), - Some((Fr::from_str("23").unwrap(), false)), - ]; - let input0 = RLNInputs:: { - share_x: Some(share_x), - share_y: Some(share_y), - epoch: Some(epoch), - nullifier: Some(nullifier), - root: Some(root), - id_key: Some(id_key), - auth_path, - }; - let mut raw_inputs: Vec = Vec::new(); - input0.write(&mut raw_inputs).unwrap(); - let mut reader = raw_inputs.as_slice(); - let input1 = RLNInputs::::read(&mut reader).unwrap(); - assert_eq!(input0.share_x, input1.share_x); - assert_eq!(input0.share_y, input1.share_y); - assert_eq!(input0.epoch, input1.epoch); - assert_eq!(input0.nullifier, input1.nullifier); - assert_eq!(input0.root, input1.root); - assert_eq!(input0.id_key, input1.id_key); - assert_eq!(input0.auth_path, input1.auth_path); - } -} diff --git a/lib/rln/src/ffi.rs b/lib/rln/src/ffi.rs deleted file mode 100644 index 5a22a32..0000000 --- a/lib/rln/src/ffi.rs +++ /dev/null @@ -1,273 +0,0 @@ -use crate::{circuit::rln, public::RLN}; -use bellman::pairing::bn256::Bn256; -use std::slice; - -/// Buffer struct is taken from -/// https://github.com/celo-org/celo-threshold-bls-rs/blob/master/crates/threshold-bls-ffi/src/ffi.rs - -#[repr(C)] -#[derive(Clone, Debug, PartialEq)] -pub struct Buffer { - pub ptr: *const u8, - pub len: usize, -} - -impl From<&[u8]> for Buffer { - fn from(src: &[u8]) -> Self { - Self { - ptr: &src[0] as *const u8, - len: src.len(), - } - } -} - -impl<'a> From<&Buffer> for &'a [u8] { - fn from(src: &Buffer) -> &'a [u8] { - unsafe { slice::from_raw_parts(src.ptr, src.len) } - } -} - -#[no_mangle] -pub extern "C" fn new_circuit_from_params( - merkle_depth: usize, - parameters_buffer: *const Buffer, - ctx: *mut *mut RLN, -) -> bool { - let buffer = <&[u8]>::from(unsafe { &*parameters_buffer }); - let rln = match RLN::::new_with_raw_params(merkle_depth, buffer, None) { - Ok(rln) => rln, - Err(_) => return false, - }; - unsafe { *ctx = Box::into_raw(Box::new(rln)) }; - true -} - -#[no_mangle] -pub extern "C" fn generate_proof( - ctx: *const RLN, - input_buffer: *const Buffer, - output_buffer: *mut Buffer, -) -> bool { - let rln = unsafe { &*ctx }; - let input_data = <&[u8]>::from(unsafe { &*input_buffer }); - let mut output_data: Vec = Vec::new(); - match rln.generate_proof(input_data, &mut output_data) { - Ok(proof_data) => proof_data, - Err(_) => return false, - }; - unsafe { *output_buffer = Buffer::from(&output_data[..]) }; - std::mem::forget(output_data); - true -} - -#[no_mangle] -pub extern "C" fn verify( - ctx: *const RLN, - proof_buffer: *const Buffer, - public_inputs_buffer: *const Buffer, - result_ptr: *mut u32, -) -> bool { - let rln = unsafe { &*ctx }; - let proof_data = <&[u8]>::from(unsafe { &*proof_buffer }); - let public_inputs_data = <&[u8]>::from(unsafe { &*public_inputs_buffer }); - if match rln.verify(proof_data, public_inputs_data) { - Ok(verified) => verified, - Err(_) => return false, - } { - unsafe { *result_ptr = 0 }; - } else { - unsafe { *result_ptr = 1 }; - }; - true -} - -#[no_mangle] -pub extern "C" fn hash( - ctx: *const RLN, - inputs_buffer: *const Buffer, - input_len: *const usize, - output_buffer: *mut Buffer, -) -> bool { - let rln = unsafe { &*ctx }; - let input_data = <&[u8]>::from(unsafe { &*inputs_buffer }); - let n: usize = unsafe { *input_len }; - let mut output_data: Vec = Vec::new(); - match rln.hash(input_data, n, &mut output_data) { - Ok(output_data) => output_data, - Err(_) => return false, - }; - unsafe { *output_buffer = Buffer::from(&output_data[..]) }; - std::mem::forget(output_data); - true -} - -#[no_mangle] -pub extern "C" fn key_gen(ctx: *const RLN, keypair_buffer: *mut Buffer) -> bool { - let rln = unsafe { &*ctx }; - let mut output_data: Vec = Vec::new(); - match rln.key_gen(&mut output_data) { - Ok(_) => (), - Err(_) => return false, - } - unsafe { *keypair_buffer = Buffer::from(&output_data[..]) }; - std::mem::forget(output_data); - true -} - -use sapling_crypto::bellman::pairing::ff::{Field, PrimeField, PrimeFieldRepr}; -use sapling_crypto::bellman::pairing::Engine; -use std::io::{self, Read, Write}; - -#[cfg(test)] -mod tests { - use crate::circuit::bench; - use crate::poseidon::PoseidonParams; - use bellman::pairing::bn256::{Bn256, Fr}; - - use super::*; - use std::mem::MaybeUninit; - - fn merkle_depth() -> usize { - 3usize - } - - fn rln_test() -> bench::RLNTest { - let merkle_depth = merkle_depth(); - let poseidon_params = PoseidonParams::::new(8, 55, 3, None, None, None); - let rln_test = bench::RLNTest::::new(merkle_depth, Some(poseidon_params)); - rln_test - } - - fn rln_pointer(circuit_parameters: Vec) -> MaybeUninit<*mut RLN> { - // restore this new curcuit with bindings - let merkle_depth = merkle_depth(); - let circuit_parameters_buffer = &Buffer::from(circuit_parameters.as_ref()); - let mut rln_pointer = MaybeUninit::<*mut RLN>::uninit(); - unsafe { - new_circuit_from_params( - merkle_depth, - circuit_parameters_buffer, - rln_pointer.as_mut_ptr(), - ) - }; - - rln_pointer - } - - #[test] - fn test_proof_ffi() { - let rln_test = rln_test(); - - let mut circuit_parameters: Vec = Vec::new(); - rln_test - .export_circuit_parameters(&mut circuit_parameters) - .unwrap(); - - let rln_pointer = rln_pointer(circuit_parameters); - let rln_pointer = unsafe { &*rln_pointer.assume_init() }; - - let mut inputs_data: Vec = Vec::new(); - let inputs = rln_test.valid_inputs(); - inputs.write(&mut inputs_data).unwrap(); - let inputs_buffer = &Buffer::from(inputs_data.as_ref()); - - let mut proof_buffer = MaybeUninit::::uninit(); - - let success = - unsafe { generate_proof(rln_pointer, inputs_buffer, proof_buffer.as_mut_ptr()) }; - assert!(success, "proof generation failed"); - - let proof_buffer = unsafe { proof_buffer.assume_init() }; - - let mut public_inputs_data: Vec = Vec::new(); - inputs.write_public_inputs(&mut public_inputs_data).unwrap(); - let public_inputs_buffer = &Buffer::from(public_inputs_data.as_ref()); - - let mut result = 0u32; - let result_ptr = &mut result as *mut u32; - - let success = - unsafe { verify(rln_pointer, &proof_buffer, public_inputs_buffer, result_ptr) }; - assert!(success, "verification operation failed"); - assert_eq!(0, result); - } - - #[test] - fn test_hash_ffi() { - let rln_test = rln_test(); - - let mut circuit_parameters: Vec = Vec::new(); - rln_test - .export_circuit_parameters(&mut circuit_parameters) - .unwrap(); - let mut hasher = rln_test.hasher(); - - let rln_pointer = rln_pointer(circuit_parameters); - let rln_pointer = unsafe { &*rln_pointer.assume_init() }; - - let mut input_data: Vec = Vec::new(); - - let inputs: Vec = ["1", "2"] - .iter() - .map(|e| Fr::from_str(e).unwrap()) - .collect(); - inputs.iter().for_each(|e| { - e.into_repr().write_le(&mut input_data).unwrap(); - }); - let input_buffer = &Buffer::from(input_data.as_ref()); - - let input_len: usize = 2; - let input_len_pointer = &input_len as *const usize; - - let expected = hasher.hash(inputs); - let mut expected_data: Vec = Vec::new(); - expected.into_repr().write_le(&mut expected_data).unwrap(); - - let mut result_buffer = MaybeUninit::::uninit(); - - let success = unsafe { - hash( - rln_pointer, - input_buffer, - input_len_pointer, - result_buffer.as_mut_ptr(), - ) - }; - assert!(success, "hash ffi call failed"); - - let result_buffer = unsafe { result_buffer.assume_init() }; - let result_data = <&[u8]>::from(&result_buffer); - assert_eq!(expected_data.as_slice(), result_data); - } - - #[test] - fn test_keygen_ffi() { - let rln_test = rln_test(); - - let mut circuit_parameters: Vec = Vec::new(); - rln_test - .export_circuit_parameters(&mut circuit_parameters) - .unwrap(); - let mut hasher = rln_test.hasher(); - - let rln_pointer = rln_pointer(circuit_parameters); - let rln_pointer = unsafe { &*rln_pointer.assume_init() }; - - let mut keypair_buffer = MaybeUninit::::uninit(); - - let success = unsafe { key_gen(rln_pointer, keypair_buffer.as_mut_ptr()) }; - assert!(success, "proof generation failed"); - - let keypair_buffer = unsafe { keypair_buffer.assume_init() }; - let mut keypair_data = <&[u8]>::from(&keypair_buffer); - - let mut buf = ::Repr::default(); - buf.read_le(&mut keypair_data).unwrap(); - let secret = Fr::from_repr(buf).unwrap(); - buf.read_le(&mut keypair_data).unwrap(); - let public = Fr::from_repr(buf).unwrap(); - let expected_public: Fr = hasher.hash(vec![secret]); - - assert_eq!(public, expected_public); - } -} diff --git a/lib/rln/src/lib.rs b/lib/rln/src/lib.rs deleted file mode 100644 index 266691e..0000000 --- a/lib/rln/src/lib.rs +++ /dev/null @@ -1,14 +0,0 @@ -#![allow(dead_code)] -#![allow(unused_imports)] - -pub mod circuit; -pub mod merkle; -pub mod poseidon; -pub mod public; -mod utils; - -#[cfg(not(target_arch = "wasm32"))] -pub mod ffi; - -#[cfg(target_arch = "wasm32")] -mod wasm; diff --git a/lib/rln/src/merkle.rs b/lib/rln/src/merkle.rs deleted file mode 100644 index 9964511..0000000 --- a/lib/rln/src/merkle.rs +++ /dev/null @@ -1,154 +0,0 @@ -use crate::poseidon::{Poseidon as Hasher, PoseidonParams}; -use sapling_crypto::bellman::pairing::ff::{Field, PrimeField, PrimeFieldRepr}; -use sapling_crypto::bellman::pairing::Engine; -use std::collections::HashMap; - -pub struct MerkleTree -where - E: Engine, -{ - pub hasher: Hasher, - zero: Vec, - depth: usize, - nodes: HashMap<(usize, usize), E::Fr>, -} - -impl MerkleTree -where - E: Engine, -{ - pub fn empty(mut hasher: Hasher, depth: usize) -> Self { - let mut zero: Vec = Vec::with_capacity(depth + 1); - zero.push(E::Fr::from_str("0").unwrap()); - for i in 0..depth { - zero.push(hasher.hash([zero[i]; 2].to_vec())); - } - zero.reverse(); - MerkleTree { - hasher: hasher, - zero: zero.clone(), - depth: depth, - nodes: HashMap::new(), - } - } - - fn get_node(&self, depth: usize, index: usize) -> E::Fr { - *self - .nodes - .get(&(depth, index)) - .unwrap_or_else(|| &self.zero[depth]) - } - - fn hash_couple(&mut self, depth: usize, index: usize) -> E::Fr { - let b = index & !1; - self.hasher - .hash([self.get_node(depth, b), self.get_node(depth, b + 1)].to_vec()) - } - - fn recalculate_from(&mut self, leaf_index: usize) { - let mut i = leaf_index; - let mut depth = self.depth; - loop { - let h = self.hash_couple(depth, i); - i >>= 1; - depth -= 1; - self.nodes.insert((depth, i), h); - if depth == 0 { - break; - } - } - assert_eq!(depth, 0); - assert_eq!(i, 0); - } - - pub fn insert(&mut self, leaf_index: usize, new: E::Fr, old: Option) { - let d = self.depth; - { - if old.is_some() { - let old = old.unwrap(); - let t = self.get_node(d, leaf_index); - if t.is_zero() { - assert!(old.is_zero()); - } else { - assert!(t.eq(&self.hasher.hash(vec![old]))); - } - } - }; - let leaf = self.hasher.hash(vec![new]); - self.update(leaf_index, leaf); - } - - pub fn update(&mut self, leaf_index: usize, leaf: E::Fr) { - self.nodes.insert((self.depth, leaf_index), leaf); - self.recalculate_from(leaf_index); - } - - pub fn root(&self) -> E::Fr { - return self.get_node(0, 0); - } - - pub fn witness(&mut self, leaf_index: usize) -> Vec<(E::Fr, bool)> { - let mut witness = Vec::<(E::Fr, bool)>::with_capacity(self.depth); - let mut i = leaf_index; - let mut depth = self.depth; - loop { - i ^= 1; - witness.push((self.get_node(depth, i), (i & 1 == 1))); - i >>= 1; - depth -= 1; - if depth == 0 { - break; - } - } - assert_eq!(i, 0); - witness - } - - pub fn check_inclusion( - &mut self, - witness: Vec<(E::Fr, bool)>, - leaf_index: usize, - data: E::Fr, - ) -> bool { - let mut acc = self.hasher.hash(vec![data]); - { - assert!(self.get_node(self.depth, leaf_index).eq(&acc)); - } - for w in witness.into_iter() { - if w.1 { - acc = self.hasher.hash(vec![acc, w.0]); - } else { - acc = self.hasher.hash(vec![w.0, acc]); - } - } - acc.eq(&self.root()) - } -} - -#[test] -fn test_merkle_set() { - let zero = Some(Fr::zero()); - let data: Vec = (0..8) - .map(|s| Fr::from_str(&format!("{}", s)).unwrap()) - .collect(); - use sapling_crypto::bellman::pairing::bn256::{Bn256, Fr, FrRepr}; - let params = PoseidonParams::::new(8, 55, 3, None, None, None); - let hasher = Hasher::new(params); - let mut set = MerkleTree::empty(hasher, 3); - let leaf_index = 6; - set.insert(leaf_index, data[0], zero); - let witness = set.witness(leaf_index); - assert!(set.check_inclusion(witness, leaf_index, data[0])); -} - -#[test] -fn test_merkle_zeros() { - use sapling_crypto::bellman::pairing::bn256::{Bn256, Fr, FrRepr}; - let params = PoseidonParams::::new(8, 55, 3, None, None, None); - let hasher = Hasher::new(params); - let mut set = MerkleTree::empty(hasher, 32); - set.insert(5, Fr::from_str("1").unwrap(), Some(Fr::zero())); - println!("{}", set.root()); - set.insert(6, Fr::from_str("2").unwrap(), Some(Fr::zero())); - println!("{}", set.root()); -} diff --git a/lib/rln/src/poseidon.rs b/lib/rln/src/poseidon.rs deleted file mode 100644 index dadd1ee..0000000 --- a/lib/rln/src/poseidon.rs +++ /dev/null @@ -1,245 +0,0 @@ -use blake2::{Blake2s, Digest}; - -use sapling_crypto::bellman::pairing::ff::{Field, PrimeField, PrimeFieldRepr}; -use sapling_crypto::bellman::pairing::Engine; - -#[derive(Clone)] -pub struct PoseidonParams { - rf: usize, - rp: usize, - t: usize, - round_constants: Vec, - mds_matrix: Vec, -} - -#[derive(Clone)] -pub struct Poseidon { - state: Vec, - round: usize, - params: PoseidonParams, -} - -impl PoseidonParams { - pub fn new( - rf: usize, - rp: usize, - t: usize, - round_constants: Option>, - mds_matrix: Option>, - seed: Option>, - ) -> PoseidonParams { - let seed = match seed { - Some(seed) => seed, - None => b"".to_vec(), - }; - - let _round_constants = match round_constants { - Some(round_constants) => round_constants, - None => PoseidonParams::::generate_constants(b"drlnhdsc", seed.clone(), rf + rp), - }; - assert_eq!(rf + rp, _round_constants.len()); - - let _mds_matrix = match mds_matrix { - Some(mds_matrix) => mds_matrix, - None => PoseidonParams::::generate_mds_matrix(b"drlnhdsm", seed.clone(), t), - }; - PoseidonParams { - rf, - rp, - t, - round_constants: _round_constants, - mds_matrix: _mds_matrix, - } - } - - pub fn width(&self) -> usize { - return self.t; - } - - pub fn partial_round_len(&self) -> usize { - return self.rp; - } - - pub fn full_round_half_len(&self) -> usize { - return self.rf / 2; - } - - pub fn total_rounds(&self) -> usize { - return self.rf + self.rp; - } - - pub fn round_constant(&self, round: usize) -> E::Fr { - return self.round_constants[round]; - } - - pub fn mds_matrix_row(&self, i: usize) -> Vec { - let w = self.width(); - self.mds_matrix[i * w..(i + 1) * w].to_vec() - } - - pub fn mds_matrix(&self) -> Vec { - self.mds_matrix.clone() - } - - pub fn generate_mds_matrix(persona: &[u8; 8], seed: Vec, t: usize) -> Vec { - let v: Vec = PoseidonParams::::generate_constants(persona, seed, t * 2); - let mut matrix: Vec = Vec::with_capacity(t * t); - for i in 0..t { - for j in 0..t { - let mut tmp = v[i]; - tmp.add_assign(&v[t + j]); - let entry = tmp.inverse().unwrap(); - matrix.insert((i * t) + j, entry); - } - } - matrix - } - - pub fn generate_constants(persona: &[u8; 8], seed: Vec, len: usize) -> Vec { - let mut constants: Vec = Vec::new(); - let mut source = seed.clone(); - loop { - let mut hasher = Blake2s::new(); - hasher.input(persona); - hasher.input(source); - source = hasher.result().to_vec(); - let mut candidate_repr = ::Repr::default(); - candidate_repr.read_le(&source[..]).unwrap(); - if let Ok(candidate) = E::Fr::from_repr(candidate_repr) { - constants.push(candidate); - if constants.len() == len { - break; - } - } - } - constants - } -} - -impl Poseidon { - pub fn new(params: PoseidonParams) -> Poseidon { - Poseidon { - round: 0, - state: Vec::new(), - params, - } - } - - fn new_state(&mut self, inputs: Vec) { - let t = self.t(); - self.state = inputs.clone(); - self.state.resize(t, E::Fr::zero()); - } - - fn clear(&mut self) { - self.round = 0; - } - - fn t(&self) -> usize { - self.params.t - } - - fn result(&self) -> E::Fr { - self.state[0] - } - - pub fn hash(&mut self, inputs: Vec) -> E::Fr { - self.new_state(inputs); - loop { - self.round(self.round); - self.round += 1; - if self.round == self.params.total_rounds() { - break; - } - } - let r = self.result(); - self.clear(); - r - } - - fn round(&mut self, round: usize) { - let a1 = self.params.full_round_half_len(); - let a2 = a1 + self.params.partial_round_len(); - let a3 = self.params.total_rounds(); - if round < a1 { - self.full_round(round); - } else if round >= a1 && round < a2 { - self.partial_round(round); - } else if round >= a2 && round < a3 { - if round == a3 - 1 { - self.full_round_last(); - } else { - self.full_round(round); - } - } else { - panic!("should not be here") - } - } - - fn full_round(&mut self, round: usize) { - self.add_round_constants(round); - self.apply_quintic_sbox(true); - self.mul_mds_matrix(); - } - - fn full_round_last(&mut self) { - let last_round = self.params.total_rounds() - 1; - self.add_round_constants(last_round); - self.apply_quintic_sbox(true); - } - - fn partial_round(&mut self, round: usize) { - self.add_round_constants(round); - self.apply_quintic_sbox(false); - self.mul_mds_matrix(); - } - - fn add_round_constants(&mut self, round: usize) { - for (_, b) in self.state.iter_mut().enumerate() { - let c = self.params.round_constants[round]; - b.add_assign(&c); - } - } - - fn apply_quintic_sbox(&mut self, full: bool) { - for s in self.state.iter_mut() { - let mut b = s.clone(); - b.square(); - b.square(); - s.mul_assign(&b); - if !full { - break; - } - } - } - - fn mul_mds_matrix(&mut self) { - let w = self.params.t; - let mut new_state = vec![E::Fr::zero(); w]; - for (i, ns) in new_state.iter_mut().enumerate() { - for (j, s) in self.state.iter().enumerate() { - let mut tmp = s.clone(); - tmp.mul_assign(&self.params.mds_matrix[i * w + j]); - ns.add_assign(&tmp); - } - } - self.state = new_state; - } -} - -#[test] -fn test_poseidon_hash() { - use sapling_crypto::bellman::pairing::bn256; - use sapling_crypto::bellman::pairing::bn256::{Bn256, Fr}; - let params = PoseidonParams::::new(8, 55, 3, None, None, None); - let mut hasher = Poseidon::::new(params); - let input1: Vec = ["0"].iter().map(|e| Fr::from_str(e).unwrap()).collect(); - let r1: Fr = hasher.hash(input1.to_vec()); - let input2: Vec = ["0", "0"] - .iter() - .map(|e| Fr::from_str(e).unwrap()) - .collect(); - let r2: Fr = hasher.hash(input2.to_vec()); - // println!("{:?}", r1); - assert_eq!(r1, r2, "just to see if internal state resets"); -} diff --git a/lib/rln/src/public.rs b/lib/rln/src/public.rs deleted file mode 100644 index c7232f7..0000000 --- a/lib/rln/src/public.rs +++ /dev/null @@ -1,140 +0,0 @@ -use crate::circuit::poseidon::PoseidonCircuit; -use crate::circuit::rln::{RLNCircuit, RLNInputs}; -use crate::merkle::MerkleTree; -use crate::poseidon::{Poseidon as PoseidonHasher, PoseidonParams}; -use crate::utils::{read_inputs, read_uncompressed_proof, write_uncompressed_proof}; -use bellman::groth16::generate_random_parameters; -use bellman::groth16::{create_proof, prepare_verifying_key, verify_proof}; -use bellman::groth16::{create_random_proof, Parameters, Proof}; -use bellman::pairing::ff::{Field, PrimeField, PrimeFieldRepr}; -use bellman::pairing::{CurveAffine, EncodedPoint, Engine}; -use bellman::{Circuit, ConstraintSystem, SynthesisError}; -use rand::{Rand, SeedableRng, XorShiftRng}; -use std::io::{self, Error, ErrorKind, Read, Write}; - -pub struct RLN -where - E: Engine, -{ - circuit_parameters: Parameters, - poseidon_params: PoseidonParams, - merkle_depth: usize, -} - -impl RLN -where - E: Engine, -{ - fn default_poseidon_params() -> PoseidonParams { - PoseidonParams::::new(8, 55, 3, None, None, None) - } - - fn new_circuit(merkle_depth: usize, poseidon_params: PoseidonParams) -> Parameters { - let mut rng = XorShiftRng::from_seed([0x3dbe6258, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - let inputs = RLNInputs::::empty(merkle_depth); - let circuit = RLNCircuit:: { - inputs, - hasher: PoseidonCircuit::new(poseidon_params.clone()), - }; - generate_random_parameters(circuit, &mut rng).unwrap() - } - - fn new_with_params( - merkle_depth: usize, - circuit_parameters: Parameters, - poseidon_params: PoseidonParams, - ) -> RLN { - RLN { - circuit_parameters, - poseidon_params, - merkle_depth, - } - } - - pub fn poseidon_params(&self) -> PoseidonParams { - self.poseidon_params.clone() - } - - pub fn new(merkle_depth: usize, poseidon_params: Option>) -> RLN { - let poseidon_params = match poseidon_params { - Some(params) => params, - None => Self::default_poseidon_params(), - }; - let circuit_parameters = Self::new_circuit(merkle_depth, poseidon_params.clone()); - Self::new_with_params(merkle_depth, circuit_parameters, poseidon_params) - } - - pub fn new_with_raw_params( - merkle_depth: usize, - raw_circuit_parameters: R, - poseidon_params: Option>, - ) -> io::Result> { - let circuit_parameters = Parameters::::read(raw_circuit_parameters, true)?; - let poseidon_params = match poseidon_params { - Some(params) => params, - None => Self::default_poseidon_params(), - }; - Ok(Self::new_with_params( - merkle_depth, - circuit_parameters, - poseidon_params, - )) - } - - pub fn hasher(&self) -> PoseidonHasher { - PoseidonHasher::new(self.poseidon_params.clone()) - } - - pub fn hash(&self, input: R, n: usize, mut output: W) -> io::Result<()> { - let mut hasher = self.hasher(); - let input: Vec = read_inputs::(input, n)?; - let result = hasher.hash(input); - // let mut output_data: Vec = Vec::new(); - result.into_repr().write_le(&mut output)?; - Ok(()) - } - - pub fn generate_proof(&self, input: R, mut output: W) -> io::Result<()> { - use rand::chacha::ChaChaRng; - use rand::SeedableRng; - let mut rng = ChaChaRng::new_unseeded(); - let inputs = RLNInputs::::read(input)?; - assert_eq!(self.merkle_depth, inputs.merkle_depth()); - let circuit_hasher = PoseidonCircuit::new(self.poseidon_params.clone()); - let circuit = RLNCircuit { - inputs: inputs.clone(), - hasher: circuit_hasher.clone(), - }; - let proof = create_random_proof(circuit, &self.circuit_parameters, &mut rng).unwrap(); - write_uncompressed_proof(proof, &mut output)?; - // proof.write(&mut w).unwrap(); - Ok(()) - } - - pub fn verify(&self, uncompresed_proof: R, raw_public_inputs: R) -> io::Result { - let proof = read_uncompressed_proof(uncompresed_proof)?; - // let proof = Proof::read(uncompresed_proof).unwrap(); - let public_inputs = RLNInputs::::read_public_inputs(raw_public_inputs)?; - let verifing_key = prepare_verifying_key(&self.circuit_parameters.vk); - let success = verify_proof(&verifing_key, &proof, &public_inputs).unwrap(); - Ok(success) - } - - pub fn key_gen(&self, mut w: W) -> io::Result<()> { - let mut rng = XorShiftRng::from_seed([0x3dbe6258, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - let mut hasher = self.hasher(); - let secret = E::Fr::rand(&mut rng); - let public: E::Fr = hasher.hash(vec![secret.clone()]); - secret.into_repr().write_le(&mut w)?; - public.into_repr().write_le(&mut w)?; - Ok(()) - } - - pub fn export_verifier_key(&self, w: W) -> io::Result<()> { - self.circuit_parameters.vk.write(w) - } - - pub fn export_circuit_parameters(&self, w: W) -> io::Result<()> { - self.circuit_parameters.write(w) - } -} diff --git a/lib/rln/src/utils.rs b/lib/rln/src/utils.rs deleted file mode 100644 index 7f64ebe..0000000 --- a/lib/rln/src/utils.rs +++ /dev/null @@ -1,80 +0,0 @@ -use bellman::groth16::Proof; -use bellman::pairing::ff::{Field, PrimeField, PrimeFieldRepr}; -use bellman::pairing::{CurveAffine, EncodedPoint, Engine}; - -use rand::{Rand, SeedableRng, XorShiftRng}; -use std::io::{self, Error, ErrorKind, Read, Write}; - -pub fn read_inputs(mut reader: R, n: usize) -> io::Result> { - let mut out: Vec = Vec::new(); - let mut buf = ::Repr::default(); - for _ in 0..n { - buf.read_le(&mut reader)?; - let input = - E::Fr::from_repr(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - out.push(input); - } - Ok(out) -} - -pub fn write_uncompressed_proof( - proof: Proof, - mut writer: W, -) -> io::Result<()> { - writer.write_all(proof.a.into_uncompressed().as_ref())?; - writer.write_all(proof.b.into_uncompressed().as_ref())?; - writer.write_all(proof.c.into_uncompressed().as_ref())?; - Ok(()) -} - -pub fn read_uncompressed_proof(mut reader: R) -> io::Result> { - let mut g1_repr = ::Uncompressed::empty(); - let mut g2_repr = ::Uncompressed::empty(); - - reader.read_exact(g1_repr.as_mut())?; - let a = g1_repr - .into_affine() - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) - .and_then(|e| { - if e.is_zero() { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "point at infinity", - )) - } else { - Ok(e) - } - })?; - - reader.read_exact(g2_repr.as_mut())?; - let b = g2_repr - .into_affine() - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) - .and_then(|e| { - if e.is_zero() { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "point at infinity", - )) - } else { - Ok(e) - } - })?; - - reader.read_exact(g1_repr.as_mut())?; - let c = g1_repr - .into_affine() - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) - .and_then(|e| { - if e.is_zero() { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "point at infinity", - )) - } else { - Ok(e) - } - })?; - - Ok(Proof { a, b, c }) -} diff --git a/lib/rln/src/wasm.rs b/lib/rln/src/wasm.rs deleted file mode 100644 index 4123d97..0000000 --- a/lib/rln/src/wasm.rs +++ /dev/null @@ -1,132 +0,0 @@ -use crate::public::RLN; - -use std::io::{self, Error, ErrorKind, Read, Write}; -use wasm_bindgen::prelude::*; - -use js_sys::Array; -use sapling_crypto::bellman::pairing::bn256::{Bn256, Fr}; - -pub fn set_panic_hook() { - // When the `console_error_panic_hook` feature is enabled, we can call the - // `set_panic_hook` function at least once during initialization, and then - // we will get better error messages if our code ever panics. - // - // For more details see - // https://github.com/rustwasm/console_error_panic_hook#readme - // #[cfg(feature = "console_error_panic_hook")] - console_error_panic_hook::set_once(); -} - -#[wasm_bindgen] -pub struct RLNWasm { - api: RLN, -} - -#[wasm_bindgen] -impl RLNWasm { - #[wasm_bindgen] - pub fn new(merkle_depth: usize) -> RLNWasm { - set_panic_hook(); - RLNWasm { - api: RLN::::new(merkle_depth, None), - } - } - - #[wasm_bindgen] - pub fn new_with_raw_params( - merkle_depth: usize, - raw_circuit_parameters: &[u8], - ) -> Result { - set_panic_hook(); - let api = match RLN::new_with_raw_params(merkle_depth, raw_circuit_parameters, None) { - Ok(api) => api, - Err(e) => return Err(e.to_string().into()), - }; - Ok(RLNWasm { api }) - } - - #[wasm_bindgen] - pub fn generate_proof(&self, input: &[u8]) -> Result, JsValue> { - let proof = match self.api.generate_proof(input) { - Ok(proof) => proof, - Err(e) => return Err(e.to_string().into()), - }; - Ok(proof) - } - - #[wasm_bindgen] - pub fn verify( - &self, - uncompresed_proof: &[u8], - raw_public_inputs: &[u8], - ) -> Result { - let success = match self.api.verify(uncompresed_proof, raw_public_inputs) { - Ok(success) => success, - Err(e) => return Err(e.to_string().into()), - }; - Ok(success) - } - - #[wasm_bindgen] - pub fn export_verifier_key(&self) -> Result, JsValue> { - let mut output: Vec = Vec::new(); - match self.api.export_verifier_key(&mut output) { - Ok(_) => (), - Err(e) => return Err(e.to_string().into()), - }; - Ok(output) - } - - #[wasm_bindgen] - pub fn export_circuit_parameters(&self) -> Result, JsValue> { - let mut output: Vec = Vec::new(); - match self.api.export_circuit_parameters(&mut output) { - Ok(_) => (), - Err(e) => return Err(e.to_string().into()), - }; - Ok(output) - } -} - -#[cfg(test)] -mod test { - - use crate::circuit::bench; - use wasm_bindgen_test::*; - - use crate::circuit::poseidon::PoseidonCircuit; - use crate::circuit::rln::{RLNCircuit, RLNInputs}; - use crate::merkle::MerkleTree; - use crate::poseidon::{Poseidon as PoseidonHasher, PoseidonParams}; - use bellman::groth16::{generate_random_parameters, Parameters, Proof}; - use bellman::pairing::bn256::{Bn256, Fr}; - use bellman::pairing::ff::{Field, PrimeField, PrimeFieldRepr}; - use rand::{Rand, SeedableRng, XorShiftRng}; - - #[wasm_bindgen_test] - fn test_rln_wasm() { - let merkle_depth = 3usize; - let poseidon_params = PoseidonParams::::new(8, 55, 3, None, None, None); - let rln_test = bench::RLNTest::::new(merkle_depth, Some(poseidon_params)); - - let rln_wasm = super::RLNWasm::new(merkle_depth); - - let mut raw_inputs: Vec = Vec::new(); - let inputs = rln_test.valid_inputs(); - inputs.write(&mut raw_inputs); - - // let now = Instant::now(); - let proof = rln_wasm.generate_proof(raw_inputs.as_slice()).unwrap(); - // let prover_time = now.elapsed().as_millis() as f64 / 1000.0; - - let mut raw_public_inputs: Vec = Vec::new(); - inputs.write_public_inputs(&mut raw_public_inputs); - - assert_eq!( - rln_wasm - .verify(proof.as_slice(), raw_public_inputs.as_slice()) - .unwrap(), - true - ); - } -}