This commit is contained in:
decanus 2021-09-13 13:39:11 +02:00
parent 22acfd7a2d
commit 297782c92e
No known key found for this signature in database
GPG Key ID: 3730AAF5D6589867
16 changed files with 0 additions and 2316 deletions

8
lib/rln/.gitignore vendored
View File

@ -1,8 +0,0 @@
/target
/pkg
/examples/www
node_modules
*.key
Cargo.lock
.cargo
tmp_wasm

View File

@ -1,58 +0,0 @@
[package]
name = "rln"
version = "0.1.0"
authors = ["Onur Kılıç <kiliconu@itu.edu.tr>"]
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

View File

@ -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
```

View File

@ -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::<Bn256>(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<E: Engine>(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::<E>::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::<E> {
inputs: RLNInputs::<E>::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();
}
}

View File

@ -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<E: Engine>(
merkle_depth: usize,
poseidon_params: PoseidonParams<E>,
) -> ProverBenchResult {
RLNTest::new(merkle_depth, Some(poseidon_params)).run_prover_bench()
}
pub struct RLNTest<E>
where
E: Engine,
{
rln: RLN<E>,
merkle_depth: usize,
}
impl<E> RLNTest<E>
where
E: Engine,
{
fn rng() -> XorShiftRng {
XorShiftRng::from_seed([0x3dbe6258, 0x8d313d76, 0x3237db17, 0xe5bc0654])
}
fn empty_inputs(merkle_depth: usize) -> RLNInputs<E> {
RLNInputs::<E> {
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<PoseidonParams<E>>) -> RLNTest<E> {
RLNTest {
rln: RLN::new(merkle_depth, poseidon_params),
merkle_depth,
}
}
pub fn hasher(&self) -> PoseidonHasher<E> {
self.rln.hasher()
}
pub fn valid_inputs(&self) -> RLNInputs<E> {
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::<E> {
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::<E> {
inputs: inputs.clone(),
hasher: hasher.clone(),
};
let mut cs = TestConstraintSystem::<E>::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<u8> = Vec::new();
let inputs = self.valid_inputs();
inputs.write(&mut raw_inputs).unwrap();
let mut proof: Vec<u8> = 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<u8> = 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<u8> = 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<W: Write>(&self, w: W) -> io::Result<()> {
self.rln.export_circuit_parameters(w)
}
}

View File

@ -1,6 +0,0 @@
mod polynomial;
pub mod poseidon;
pub mod rln;
#[cfg(any(test, feature = "bench"))]
pub mod bench;

View File

@ -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<CS, E>(
mut cs: CS,
a1: &num::AllocatedNum<E>,
x: &num::AllocatedNum<E>,
a0: &num::AllocatedNum<E>,
) -> Result<num::AllocatedNum<E>, SynthesisError>
where
E: Engine,
CS: ConstraintSystem<E>,
{
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)
}

View File

@ -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<E>
where
E: Engine,
{
an: Option<num::AllocatedNum<E>>,
nu: Option<num::Num<E>>,
}
enum RoundType {
Full,
Partial,
Exhausted,
}
struct RoundCtx<'a, E>
where
E: Engine,
{
number: usize,
params: &'a PoseidonParams<E>,
}
struct State<E>
where
E: Engine,
{
elements: Vec<Element<E>>,
}
#[derive(Clone)]
pub struct PoseidonCircuit<E>
where
E: Engine,
{
params: PoseidonParams<E>,
}
impl<E> Element<E>
where
E: Engine,
{
pub fn new_from_alloc(an: num::AllocatedNum<E>) -> Self {
return Element {
an: Some(an),
nu: None,
};
}
pub fn new_from_num(nu: num::Num<E>) -> 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<E>) {
self.an = Some(an);
self.nu = None;
}
pub fn update_with_num(&mut self, nu: num::Num<E>) {
self.nu = Some(nu);
self.an = None;
}
pub fn num(&self) -> num::Num<E> {
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<CS: ConstraintSystem<E>>(
&self,
mut cs: CS,
) -> Result<num::AllocatedNum<E>, 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<num::AllocatedNum<E>> {
self.an.clone()
}
}
impl<'a, E> RoundCtx<'a, E>
where
E: Engine,
{
pub fn new(params: &'a PoseidonParams<E>) -> 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<E::Fr> {
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<E> State<E>
where
E: Engine,
{
pub fn new(elements: Vec<Element<E>>) -> Self {
Self { elements }
}
pub fn first_allocated<CS: ConstraintSystem<E>>(
&mut self,
mut cs: CS,
) -> Result<num::AllocatedNum<E>, 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<CS: ConstraintSystem<E>>(
&mut self,
mut cs: CS,
ctx: &mut RoundCtx<E>,
) -> 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<CS: ConstraintSystem<E>>(
&mut self,
ctx: &mut RoundCtx<E>,
) -> 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<num::Num<E>> = Vec::new();
let w = ctx.width();
for i in 0..w {
let row = ctx.mds_matrix_row(i);
let mut acc = num::Num::<E>::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::<E>::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<E> PoseidonCircuit<E>
where
E: Engine,
{
pub fn new(params: PoseidonParams<E>) -> Self {
Self { params: params }
}
pub fn width(&self) -> usize {
self.params.width()
}
pub fn alloc<CS: ConstraintSystem<E>>(
&self,
mut cs: CS,
input: Vec<num::AllocatedNum<E>>,
) -> Result<num::AllocatedNum<E>, SynthesisError> {
assert!(input.len() < self.params.width());
let mut elements: Vec<Element<E>> = 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::<CS>(&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::<Bn256>::new();
let params = PoseidonParams::new(8, 55, 3, None, None, None);
let inputs: Vec<Fr> = ["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::<Bn256>::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()
);
}

View File

@ -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<E>
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<E::Fr>,
pub share_y: Option<E::Fr>,
// epoch is the external nullifier
// we derive the line equation and the nullifier from epoch
pub epoch: Option<E::Fr>,
// nullifier
pub nullifier: Option<E::Fr>,
// root is the current state of membership set
pub root: Option<E::Fr>,
// 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<E::Fr>,
// authentication path of the member
pub auth_path: Vec<Option<(E::Fr, bool)>>,
}
impl<E> RLNInputs<E>
where
E: Engine,
{
pub fn public_inputs(&self) -> Vec<E::Fr> {
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<E> {
RLNInputs::<E> {
share_x: None,
share_y: None,
epoch: None,
nullifier: None,
root: None,
id_key: None,
auth_path: vec![None; merkle_depth],
}
}
pub fn read<R: Read>(mut reader: R) -> io::Result<RLNInputs<E>> {
let mut buf = <E::Fr as PrimeField>::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<W: 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<R: Read>(mut reader: R) -> io::Result<Vec<E::Fr>> {
let mut buf = <E::Fr as PrimeField>::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<W: Write>(&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<W: Write>(
mut writer: W,
auth_path: Vec<Option<(E::Fr, bool)>>,
) -> 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<R: Read>(mut reader: R) -> io::Result<Vec<Option<(E::Fr, bool)>>> {
let mut byte_buf = vec![0u8; 1];
let mut el_buf = <E::Fr as PrimeField>::Repr::default();
let mut auth_path: Vec<Option<(E::Fr, bool)>> = 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<E>
where
E: Engine,
{
pub inputs: RLNInputs<E>,
pub hasher: PoseidonCircuit<E>,
}
impl<E> Circuit<E> for RLNCircuit<E>
where
E: Engine,
{
fn synthesize<CS: ConstraintSystem<E>>(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<E: Engine> {
merkle_depth: usize,
poseidon_parameters: PoseidonParams<E>,
}
fn cases<E: Engine>() -> Vec<TestSuite<E>> {
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::<Bn256>();
for case in cases.iter() {
let rln_test = bench::RLNTest::<Bn256>::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::<Bn256> {
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<u8> = Vec::new();
input0.write(&mut raw_inputs).unwrap();
let mut reader = raw_inputs.as_slice();
let input1 = RLNInputs::<Bn256>::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);
}
}

View File

@ -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<Bn256>,
) -> bool {
let buffer = <&[u8]>::from(unsafe { &*parameters_buffer });
let rln = match RLN::<Bn256>::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<Bn256>,
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<u8> = 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<Bn256>,
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<Bn256>,
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<u8> = 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<Bn256>, keypair_buffer: *mut Buffer) -> bool {
let rln = unsafe { &*ctx };
let mut output_data: Vec<u8> = 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<Bn256> {
let merkle_depth = merkle_depth();
let poseidon_params = PoseidonParams::<Bn256>::new(8, 55, 3, None, None, None);
let rln_test = bench::RLNTest::<Bn256>::new(merkle_depth, Some(poseidon_params));
rln_test
}
fn rln_pointer(circuit_parameters: Vec<u8>) -> MaybeUninit<*mut RLN<Bn256>> {
// 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<Bn256>>::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<u8> = 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<u8> = 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::<Buffer>::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<u8> = 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<u8> = 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<u8> = Vec::new();
let inputs: Vec<Fr> = ["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<u8> = Vec::new();
expected.into_repr().write_le(&mut expected_data).unwrap();
let mut result_buffer = MaybeUninit::<Buffer>::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<u8> = 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::<Buffer>::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 = <Fr as PrimeField>::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);
}
}

View File

@ -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;

View File

@ -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<E>
where
E: Engine,
{
pub hasher: Hasher<E>,
zero: Vec<E::Fr>,
depth: usize,
nodes: HashMap<(usize, usize), E::Fr>,
}
impl<E> MerkleTree<E>
where
E: Engine,
{
pub fn empty(mut hasher: Hasher<E>, depth: usize) -> Self {
let mut zero: Vec<E::Fr> = 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<E::Fr>) {
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<Fr> = (0..8)
.map(|s| Fr::from_str(&format!("{}", s)).unwrap())
.collect();
use sapling_crypto::bellman::pairing::bn256::{Bn256, Fr, FrRepr};
let params = PoseidonParams::<Bn256>::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::<Bn256>::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());
}

View File

@ -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<E: Engine> {
rf: usize,
rp: usize,
t: usize,
round_constants: Vec<E::Fr>,
mds_matrix: Vec<E::Fr>,
}
#[derive(Clone)]
pub struct Poseidon<E: Engine> {
state: Vec<E::Fr>,
round: usize,
params: PoseidonParams<E>,
}
impl<E: Engine> PoseidonParams<E> {
pub fn new(
rf: usize,
rp: usize,
t: usize,
round_constants: Option<Vec<E::Fr>>,
mds_matrix: Option<Vec<E::Fr>>,
seed: Option<Vec<u8>>,
) -> PoseidonParams<E> {
let seed = match seed {
Some(seed) => seed,
None => b"".to_vec(),
};
let _round_constants = match round_constants {
Some(round_constants) => round_constants,
None => PoseidonParams::<E>::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::<E>::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<E::Fr> {
let w = self.width();
self.mds_matrix[i * w..(i + 1) * w].to_vec()
}
pub fn mds_matrix(&self) -> Vec<E::Fr> {
self.mds_matrix.clone()
}
pub fn generate_mds_matrix(persona: &[u8; 8], seed: Vec<u8>, t: usize) -> Vec<E::Fr> {
let v: Vec<E::Fr> = PoseidonParams::<E>::generate_constants(persona, seed, t * 2);
let mut matrix: Vec<E::Fr> = 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<u8>, len: usize) -> Vec<E::Fr> {
let mut constants: Vec<E::Fr> = 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 = <E::Fr as PrimeField>::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<E: Engine> Poseidon<E> {
pub fn new(params: PoseidonParams<E>) -> Poseidon<E> {
Poseidon {
round: 0,
state: Vec::new(),
params,
}
}
fn new_state(&mut self, inputs: Vec<E::Fr>) {
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>) -> 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::<Bn256>::new(8, 55, 3, None, None, None);
let mut hasher = Poseidon::<Bn256>::new(params);
let input1: Vec<Fr> = ["0"].iter().map(|e| Fr::from_str(e).unwrap()).collect();
let r1: Fr = hasher.hash(input1.to_vec());
let input2: Vec<Fr> = ["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");
}

View File

@ -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<E>
where
E: Engine,
{
circuit_parameters: Parameters<E>,
poseidon_params: PoseidonParams<E>,
merkle_depth: usize,
}
impl<E> RLN<E>
where
E: Engine,
{
fn default_poseidon_params() -> PoseidonParams<E> {
PoseidonParams::<E>::new(8, 55, 3, None, None, None)
}
fn new_circuit(merkle_depth: usize, poseidon_params: PoseidonParams<E>) -> Parameters<E> {
let mut rng = XorShiftRng::from_seed([0x3dbe6258, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let inputs = RLNInputs::<E>::empty(merkle_depth);
let circuit = RLNCircuit::<E> {
inputs,
hasher: PoseidonCircuit::new(poseidon_params.clone()),
};
generate_random_parameters(circuit, &mut rng).unwrap()
}
fn new_with_params(
merkle_depth: usize,
circuit_parameters: Parameters<E>,
poseidon_params: PoseidonParams<E>,
) -> RLN<E> {
RLN {
circuit_parameters,
poseidon_params,
merkle_depth,
}
}
pub fn poseidon_params(&self) -> PoseidonParams<E> {
self.poseidon_params.clone()
}
pub fn new(merkle_depth: usize, poseidon_params: Option<PoseidonParams<E>>) -> RLN<E> {
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<R: Read>(
merkle_depth: usize,
raw_circuit_parameters: R,
poseidon_params: Option<PoseidonParams<E>>,
) -> io::Result<RLN<E>> {
let circuit_parameters = Parameters::<E>::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<E> {
PoseidonHasher::new(self.poseidon_params.clone())
}
pub fn hash<R: Read, W: Write>(&self, input: R, n: usize, mut output: W) -> io::Result<()> {
let mut hasher = self.hasher();
let input: Vec<E::Fr> = read_inputs::<R, E>(input, n)?;
let result = hasher.hash(input);
// let mut output_data: Vec<u8> = Vec::new();
result.into_repr().write_le(&mut output)?;
Ok(())
}
pub fn generate_proof<R: Read, W: Write>(&self, input: R, mut output: W) -> io::Result<()> {
use rand::chacha::ChaChaRng;
use rand::SeedableRng;
let mut rng = ChaChaRng::new_unseeded();
let inputs = RLNInputs::<E>::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<R: Read>(&self, uncompresed_proof: R, raw_public_inputs: R) -> io::Result<bool> {
let proof = read_uncompressed_proof(uncompresed_proof)?;
// let proof = Proof::read(uncompresed_proof).unwrap();
let public_inputs = RLNInputs::<E>::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<W: Write>(&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<W: Write>(&self, w: W) -> io::Result<()> {
self.circuit_parameters.vk.write(w)
}
pub fn export_circuit_parameters<W: Write>(&self, w: W) -> io::Result<()> {
self.circuit_parameters.write(w)
}
}

View File

@ -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<R: Read, E: Engine>(mut reader: R, n: usize) -> io::Result<Vec<E::Fr>> {
let mut out: Vec<E::Fr> = Vec::new();
let mut buf = <E::Fr as PrimeField>::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<W: Write, E: Engine>(
proof: Proof<E>,
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<R: Read, E: Engine>(mut reader: R) -> io::Result<Proof<E>> {
let mut g1_repr = <E::G1Affine as CurveAffine>::Uncompressed::empty();
let mut g2_repr = <E::G2Affine as CurveAffine>::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 })
}

View File

@ -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<Bn256>,
}
#[wasm_bindgen]
impl RLNWasm {
#[wasm_bindgen]
pub fn new(merkle_depth: usize) -> RLNWasm {
set_panic_hook();
RLNWasm {
api: RLN::<Bn256>::new(merkle_depth, None),
}
}
#[wasm_bindgen]
pub fn new_with_raw_params(
merkle_depth: usize,
raw_circuit_parameters: &[u8],
) -> Result<RLNWasm, JsValue> {
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<Vec<u8>, 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<bool, JsValue> {
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<Vec<u8>, JsValue> {
let mut output: Vec<u8> = 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<Vec<u8>, JsValue> {
let mut output: Vec<u8> = 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::<Bn256>::new(8, 55, 3, None, None, None);
let rln_test = bench::RLNTest::<Bn256>::new(merkle_depth, Some(poseidon_params));
let rln_wasm = super::RLNWasm::new(merkle_depth);
let mut raw_inputs: Vec<u8> = 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<u8> = 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
);
}
}