feat(RLN): integrate Poseidon Hash (#44)

* refactor(rln): removing unused crates/dependencies

* cargo fmt

* refactor(rln): removed more dependencies; curve/fields as parameters

* refactor(rln): use poseidon-rs hash instead of semaphore-rs poseidon

* chore(rln): remove deps

* refactor(rln): use exclusively arkworks Fr

* refactor(rln): integrate poseidon-rs implementation to work with arkworks arithmetic

* fix(rln): remove previous poseidon-rs wrapper
This commit is contained in:
G 2022-09-15 05:06:16 +02:00 committed by GitHub
parent fb34ebe63c
commit 1131b76a66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 3608 additions and 34 deletions

View File

@ -30,8 +30,6 @@ once_cell = "1.8"
rand = "0.8"
tiny-keccak = "2.0.2"
num-traits = "0.2.15"
ff = { package="ff_ce", version="0.11"}
poseidon-rs = "0.0.8"
# serialization
serde = { version = "1.0.103", default-features = false, features = ["derive"] }

View File

@ -246,7 +246,7 @@ pub extern "C" fn hash(
mod test {
use super::*;
use crate::circuit::*;
use crate::poseidon_tree::poseidon_hash;
use crate::poseidon_hash::poseidon_hash;
use crate::protocol::*;
use crate::utils::*;
use ark_std::{rand::thread_rng, UniformRand};

View File

@ -3,6 +3,8 @@
pub mod circuit;
pub mod ffi;
pub mod merkle_tree;
pub mod poseidon_constants;
pub mod poseidon_hash;
pub mod poseidon_tree;
pub mod protocol;
pub mod public;
@ -12,7 +14,8 @@ pub mod utils;
mod test {
use crate::circuit::{Fr, CIRCOM, TEST_RESOURCES_FOLDER, TEST_TREE_HEIGHT, VK, ZKEY};
use crate::poseidon_tree::{poseidon_hash, PoseidonTree};
use crate::poseidon_hash::poseidon_hash;
use crate::poseidon_tree::PoseidonTree;
use crate::protocol::*;
use crate::utils::str_to_fr;

File diff suppressed because it is too large Load Diff

129
rln/src/poseidon_hash.rs Normal file
View File

@ -0,0 +1,129 @@
// This crate implements the Poseidon hash algorithm https://eprint.iacr.org/2019/458.pdf
// The implementation is taken from https://github.com/arnaucube/poseidon-rs/blob/233027d6075a637c29ad84a8a44f5653b81f0410/src/lib.rs
// and slightly adapted to work over arkworks field data type
use crate::circuit::Fr;
use crate::poseidon_constants::constants;
use crate::utils::*;
use ark_std::Zero;
use once_cell::sync::Lazy;
#[derive(Debug)]
pub struct Constants {
pub c: Vec<Vec<Fr>>,
pub m: Vec<Vec<Vec<Fr>>>,
pub n_rounds_f: usize,
pub n_rounds_p: Vec<usize>,
}
pub fn load_constants() -> Constants {
let (c_str, m_str) = constants();
let mut c: Vec<Vec<Fr>> = Vec::new();
for i in 0..c_str.len() {
let mut cci: Vec<Fr> = Vec::new();
for j in 0..c_str[i].len() {
let b: Fr = str_to_fr(c_str[i][j], 10);
cci.push(b);
}
c.push(cci);
}
let mut m: Vec<Vec<Vec<Fr>>> = Vec::new();
for i in 0..m_str.len() {
let mut mi: Vec<Vec<Fr>> = Vec::new();
for j in 0..m_str[i].len() {
let mut mij: Vec<Fr> = Vec::new();
for k in 0..m_str[i][j].len() {
let b: Fr = str_to_fr(m_str[i][j][k], 10);
mij.push(b);
}
mi.push(mij);
}
m.push(mi);
}
Constants {
c: c,
m: m,
n_rounds_f: 8,
n_rounds_p: vec![56, 57, 56, 60, 60, 63, 64, 63],
}
}
pub struct Poseidon {
constants: Constants,
}
impl Poseidon {
pub fn new() -> Poseidon {
Poseidon {
constants: load_constants(),
}
}
pub fn ark(&self, state: &mut [Fr], c: &[Fr], it: usize) {
for i in 0..state.len() {
state[i] += c[it + i];
}
}
pub fn sbox(&self, n_rounds_f: usize, n_rounds_p: usize, state: &mut [Fr], i: usize) {
if (i < n_rounds_f / 2) || (i >= n_rounds_f / 2 + n_rounds_p) {
for j in 0..state.len() {
let aux = state[j];
state[j] *= state[j];
state[j] *= state[j];
state[j] *= aux;
}
} else {
let aux = state[0];
state[0] *= state[0];
state[0] *= state[0];
state[0] *= aux;
}
}
pub fn mix(&self, state: &[Fr], m: &[Vec<Fr>]) -> Vec<Fr> {
let mut new_state: Vec<Fr> = Vec::new();
for i in 0..state.len() {
new_state.push(Fr::zero());
for j in 0..state.len() {
let mut mij = m[i][j];
mij *= state[j];
new_state[i] += mij;
}
}
new_state.clone()
}
pub fn hash(&self, inp: Vec<Fr>) -> Result<Fr, String> {
let t = inp.len() + 1;
if inp.is_empty() || (inp.len() >= self.constants.n_rounds_p.len() - 1) {
return Err("Wrong inputs length".to_string());
}
let n_rounds_f = self.constants.n_rounds_f;
let n_rounds_p = self.constants.n_rounds_p[t - 2];
let mut state = vec![Fr::zero(); t];
state[1..].clone_from_slice(&inp);
for i in 0..(n_rounds_f + n_rounds_p) {
self.ark(&mut state, &self.constants.c[t - 2], i * t);
self.sbox(n_rounds_f, n_rounds_p, &mut state, i);
state = self.mix(&state, &self.constants.m[t - 2]);
}
Ok(state[0])
}
}
impl Default for Poseidon {
fn default() -> Self {
Self::new()
}
}
// Poseidon Hash wrapper over above implementation. Adapted from semaphore-rs poseidon hash wrapper.
static POSEIDON: Lazy<Poseidon> = Lazy::new(Poseidon::new);
pub fn poseidon_hash(input: &[Fr]) -> Fr {
POSEIDON
.hash(input.to_vec())
.expect("hash with fixed input size can't fail")
}

View File

@ -1,11 +1,10 @@
// This crate defines RLN module default Merkle tree implementation and Hasher
// This crate defines the RLN module default Merkle tree implementation and its Hasher
// Implementation inspired by https://github.com/worldcoin/semaphore-rs/blob/d462a4372f1fd9c27610f2acfe4841fab1d396aa/src/poseidon_tree.rs (no differences)
use crate::circuit::Fr;
use crate::merkle_tree::*;
use crate::utils::{fr_to_posfr, posfr_to_fr};
use once_cell::sync::Lazy;
use poseidon_rs::Poseidon;
use crate::poseidon_hash::poseidon_hash;
// The zerokit RLN default Merkle tree implementation.
// To switch to FullMerkleTree implementation it is enough to redefine the following two types
@ -30,23 +29,6 @@ impl Hasher for PoseidonHash {
}
}
// Poseidon Hash wrapper over poseidon-rs implementation. Adapted from semaphore-rs poseidon hash wrapper.
// TODO: integrate poseidon hash in zerokit
static POSEIDON: Lazy<Poseidon> = Lazy::new(Poseidon::new);
pub fn poseidon_hash(input: &[Fr]) -> Fr {
let input = input
.iter()
.copied()
.map(|x| fr_to_posfr(x))
.collect::<Vec<_>>();
POSEIDON
.hash(input)
.map(|x| posfr_to_fr(x))
.expect("hash with fixed input size can't fail")
}
////////////////////////////////////////////////////////////
/// Tests
////////////////////////////////////////////////////////////

View File

@ -17,6 +17,7 @@ use thiserror::Error;
use tiny_keccak::{Hasher as _, Keccak};
use crate::circuit::{Curve, Fr};
use crate::poseidon_hash::poseidon_hash;
use crate::poseidon_tree::*;
use crate::public::RLN_IDENTIFIER;
use crate::utils::*;
@ -469,7 +470,7 @@ pub fn verify_proof(
let pvk = prepare_verifying_key(verifying_key);
//let pr: ArkProof<Curve> = (*proof).into();
let now = Instant::now();
let verified = ark_verify_proof(&pvk, &proof, &inputs)?;
let verified = ark_verify_proof(&pvk, proof, &inputs)?;
println!("verify took: {:.2?}", now.elapsed());
Ok(verified)

View File

@ -280,7 +280,7 @@ impl Default for RLN<'_> {
#[cfg(test)]
mod test {
use super::*;
use crate::poseidon_tree::poseidon_hash;
use crate::poseidon_hash::poseidon_hash;
use ark_std::{rand::thread_rng, UniformRand};
use rand::Rng;

View File

@ -2,10 +2,8 @@
use crate::circuit::Fr;
use ark_ff::{BigInteger, FpParameters, PrimeField};
use ff::{PrimeField as _, PrimeFieldRepr as _};
use num_bigint::{BigInt, BigUint};
use num_traits::Num;
use poseidon_rs::Fr as PosFr;
use std::iter::Extend;
pub fn modulus_bit_size() -> usize {
@ -30,8 +28,8 @@ pub fn str_to_fr(input: &str, radix: u32) -> Fr {
// We remove any quote present and we trim
let single_quote: char = '\"';
let input_clean = input.replace(single_quote, "");
let input_clean = input_clean.trim();
let mut input_clean = input.replace(single_quote, "");
input_clean = input_clean.trim().to_string();
if radix == 10 {
BigUint::from_str_radix(&input_clean, radix)
@ -39,7 +37,7 @@ pub fn str_to_fr(input: &str, radix: u32) -> Fr {
.try_into()
.unwrap()
} else {
let input_clean = input_clean.replace("0x", "");
input_clean = input_clean.replace("0x", "");
BigUint::from_str_radix(&input_clean, radix)
.unwrap()
.try_into()
@ -179,7 +177,12 @@ pub fn bytes_be_to_vec_fr(input: &[u8]) -> (Vec<Fr>, usize) {
(res, read)
}
// Conversion Utilities between poseidon-rs Field and arkworks Fr (in order to call directly poseidon-rs poseidon_hash)
/* Old conversion utilities between different libraries data types
// Conversion Utilities between poseidon-rs Field and arkworks Fr (in order to call directly poseidon-rs' poseidon_hash)
use ff::{PrimeField as _, PrimeFieldRepr as _};
use poseidon_rs::Fr as PosFr;
pub fn fr_to_posfr(value: Fr) -> PosFr {
let mut bytes = [0_u8; 32];
@ -200,9 +203,9 @@ pub fn posfr_to_fr(value: PosFr) -> Fr {
Fr::from_be_bytes_mod_order(&bytes)
}
// Conversion Utilities between semaphore-rs Field and arkworks Fr
/*
use semaphore::Field;
pub fn to_fr(el: &Field) -> Fr {