mirror of https://github.com/vacp2p/zerokit.git
Use arkworks arithmetic only (#43)
* 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
This commit is contained in:
parent
f5897eb752
commit
a85454cfd7
|
@ -3,76 +3,36 @@ name = "rln"
|
|||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib", "staticlib"]
|
||||
|
||||
[dependencies]
|
||||
|
||||
# WASM operations
|
||||
wasmer = { version = "2.0" }
|
||||
# fnv = { version = "1.0.3", default-features = false }
|
||||
# num = { version = "0.4.0" }
|
||||
# num-traits = { version = "0.2.0", default-features = false }
|
||||
num-bigint = { version = "0.4", default-features = false, features = ["rand"] }
|
||||
|
||||
# ZKP Generation
|
||||
ark-ec = { version = "0.3.0", default-features = false, features = ["parallel"] }
|
||||
ark-ff = { version = "0.3.0", default-features = false, features = ["parallel", "asm"] }
|
||||
#ark-ff = { git = "https://github.com/arkworks-rs/algebra/"}
|
||||
ark-std = { version = "0.3.0", default-features = false, features = ["parallel"] }
|
||||
ark-bn254 = { version = "0.3.0" }
|
||||
ark-groth16 = { git = "https://github.com/arkworks-rs/groth16", rev = "765817f", features = ["parallel"] }
|
||||
# ark-poly = { version = "^0.3.0", default-features = false, features = ["parallel"] }
|
||||
#ark-relations = { version = "0.3.0", default-features = false, path = "../../../arkworks-rs/snark/relations", features = [ "std" ] }
|
||||
ark-relations = { version = "0.3.0", default-features = false, features = [ "std" ] }
|
||||
ark-serialize = { version = "0.3.0", default-features = false }
|
||||
|
||||
ark-circom = { git = "https://github.com/gakonst/ark-circom", features = ["circom-2"] }
|
||||
#ark-circom = { features = ["circom-2"], path = "../../../gakonst/ark-circom" }
|
||||
wasmer = { version = "2.0" }
|
||||
|
||||
# error handling
|
||||
# thiserror = "1.0.26"
|
||||
color-eyre = "0.5"
|
||||
|
||||
# decoding of data
|
||||
hex = "0.4.3"
|
||||
# byteorder = "1.4.3"
|
||||
hex-literal = "0.3"
|
||||
|
||||
# tracing
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.2"
|
||||
|
||||
# json
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
serde_json = "1.0.48"
|
||||
bincode = "1.3.3"
|
||||
|
||||
once_cell = "1.8"
|
||||
poseidon-rs = "0.0.8"
|
||||
primitive-types = "0.11.1"
|
||||
sha2 = "0.10.1"
|
||||
|
||||
ff = { package="ff_ce", version="0.11"}
|
||||
|
||||
zkp-u256 = { version = "0.2", optional = true }
|
||||
ethers = { git = "https://github.com/gakonst/ethers-rs" }
|
||||
|
||||
tiny-keccak = "2.0.2"
|
||||
|
||||
blake2 = "0.8.1"
|
||||
|
||||
# TODO Remove this and use arkworks instead
|
||||
sapling-crypto = { package = "sapling-crypto_ce", version = "0.1.3", default-features = false }
|
||||
bellman = { package = "bellman_ce", version = "0.3.4", default-features = false }
|
||||
|
||||
#semaphore = { git = "https://github.com/oskarth/semaphore-rs" }
|
||||
semaphore = { git = "https://github.com/worldcoin/semaphore-rs", rev = "d462a43"}
|
||||
|
||||
rand = "0.8"
|
||||
|
||||
tempfile = "3.3.0"
|
||||
|
||||
thiserror = "1.0.0"
|
||||
|
||||
# utilities
|
||||
hex-literal = "0.3"
|
||||
num-bigint = { version = "0.4", default-features = false, features = ["rand"] }
|
||||
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"] }
|
||||
serde_json = "1.0.48"
|
|
@ -1,22 +1,19 @@
|
|||
// This crate provides interfaces for the zero-knowledge circuit and keys
|
||||
|
||||
use ark_bn254::{Bn254, Fq, Fq2, Fr, G1Affine, G1Projective, G2Affine, G2Projective};
|
||||
use ark_circom::{read_zkey, CircomBuilder, CircomConfig, WitnessCalculator};
|
||||
use ark_ff::BigInteger256;
|
||||
use ark_bn254::{
|
||||
Bn254, Fq as ArkFq, Fq2 as ArkFq2, Fr as ArkFr, G1Affine as ArkG1Affine,
|
||||
G1Projective as ArkG1Projective, G2Affine as ArkG2Affine, G2Projective as ArkG2Projective,
|
||||
};
|
||||
use ark_circom::{read_zkey, WitnessCalculator};
|
||||
use ark_groth16::{ProvingKey, VerifyingKey};
|
||||
use ark_relations::r1cs::ConstraintMatrices;
|
||||
use core::include_bytes;
|
||||
use num_bigint::BigUint;
|
||||
use once_cell::sync::OnceCell;
|
||||
use serde_json::Value;
|
||||
use std::convert::TryFrom;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::io::{Cursor, Error, ErrorKind, Result, Write};
|
||||
use std::option::Option;
|
||||
use std::io::{Error, ErrorKind, Read, Result};
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use once_cell::sync::{Lazy, OnceCell};
|
||||
use std::sync::Mutex;
|
||||
use wasmer::{Module, Store};
|
||||
|
||||
|
@ -34,8 +31,21 @@ const WASM_FILENAME: &str = "rln.wasm";
|
|||
pub const TEST_TREE_HEIGHT: usize = 20;
|
||||
pub const TEST_RESOURCES_FOLDER: &str = "./resources/tree_height_20/";
|
||||
|
||||
// The following types define the pairing friendly elliptic curve, the underlying finite fields and groups default to this module
|
||||
// Note that proofs are serialized assuming Fr to be 4x8 = 32 bytes in size. Hence, changing to a curve with different encoding will make proof verification to fail
|
||||
|
||||
pub type Curve = Bn254;
|
||||
pub type Fr = ArkFr;
|
||||
pub type Fq = ArkFq;
|
||||
pub type Fq2 = ArkFq2;
|
||||
pub type G1Affine = ArkG1Affine;
|
||||
pub type G1Projective = ArkG1Projective;
|
||||
pub type G2Affine = ArkG2Affine;
|
||||
pub type G2Projective = ArkG2Projective;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn ZKEY(resources_folder: &str) -> Result<(ProvingKey<Bn254>, ConstraintMatrices<Fr>)> {
|
||||
// Loads the proving key
|
||||
pub fn ZKEY(resources_folder: &str) -> Result<(ProvingKey<Curve>, ConstraintMatrices<Fr>)> {
|
||||
let zkey_path = format!("{resources_folder}{ZKEY_FILENAME}");
|
||||
if Path::new(&zkey_path).exists() {
|
||||
let mut file = File::open(&zkey_path)?;
|
||||
|
@ -47,11 +57,12 @@ pub fn ZKEY(resources_folder: &str) -> Result<(ProvingKey<Bn254>, ConstraintMatr
|
|||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn VK(resources_folder: &str) -> Result<VerifyingKey<Bn254>> {
|
||||
// Loads the verification key
|
||||
pub fn VK(resources_folder: &str) -> Result<VerifyingKey<Curve>> {
|
||||
let vk_path = format!("{resources_folder}{VK_FILENAME}");
|
||||
let zkey_path = format!("{resources_folder}{ZKEY_FILENAME}");
|
||||
|
||||
let verifying_key: VerifyingKey<Bn254>;
|
||||
let verifying_key: VerifyingKey<Curve>;
|
||||
|
||||
if Path::new(&vk_path).exists() {
|
||||
verifying_key = vk_from_json(&vk_path);
|
||||
|
@ -70,6 +81,7 @@ pub fn VK(resources_folder: &str) -> Result<VerifyingKey<Bn254>> {
|
|||
|
||||
static WITNESS_CALCULATOR: OnceCell<Mutex<WitnessCalculator>> = OnceCell::new();
|
||||
|
||||
// Loads the circuit WASM
|
||||
fn read_wasm(resources_folder: &str) -> Vec<u8> {
|
||||
let wasm_path = format!("{resources_folder}{WASM_FILENAME}");
|
||||
let mut wasm_file = File::open(&wasm_path).expect("no file found");
|
||||
|
@ -82,6 +94,7 @@ fn read_wasm(resources_folder: &str) -> Vec<u8> {
|
|||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
// Initializes the witness calculator
|
||||
pub fn CIRCOM(resources_folder: &str) -> &'static Mutex<WitnessCalculator> {
|
||||
WITNESS_CALCULATOR.get_or_init(|| {
|
||||
// We read the wasm file
|
||||
|
@ -94,7 +107,7 @@ pub fn CIRCOM(resources_folder: &str) -> &'static Mutex<WitnessCalculator> {
|
|||
})
|
||||
}
|
||||
|
||||
// TODO: all the following implementations are taken from a public github project: find reference for them
|
||||
// The following function implementations are taken/adapted from https://github.com/gakonst/ark-circom/blob/1732e15d6313fe176b0b1abb858ac9e095d0dbd7/src/zkey.rs
|
||||
|
||||
// Utilities to convert a json verification key in a groth16::VerificationKey
|
||||
fn fq_from_str(s: &str) -> Fq {
|
||||
|
@ -170,7 +183,7 @@ fn json_to_g2(json: &Value, key: &str) -> G2Affine {
|
|||
}
|
||||
|
||||
// Computes the verification key from its JSON serialization
|
||||
fn vk_from_json(vk_path: &str) -> VerifyingKey<Bn254> {
|
||||
fn vk_from_json(vk_path: &str) -> VerifyingKey<Curve> {
|
||||
let json = std::fs::read_to_string(vk_path).unwrap();
|
||||
let json: Value = serde_json::from_str(&json).unwrap();
|
||||
|
||||
|
@ -184,7 +197,7 @@ fn vk_from_json(vk_path: &str) -> VerifyingKey<Bn254> {
|
|||
}
|
||||
|
||||
// Checks verification key to be correct with respect to proving key
|
||||
pub fn check_vk_from_zkey(resources_folder: &str, verifying_key: VerifyingKey<Bn254>) {
|
||||
pub fn check_vk_from_zkey(resources_folder: &str, verifying_key: VerifyingKey<Curve>) {
|
||||
let (proving_key, _matrices) = ZKEY(resources_folder).unwrap();
|
||||
assert_eq!(proving_key.vk, verifying_key);
|
||||
}
|
||||
|
|
184
rln/src/ffi.rs
184
rln/src/ffi.rs
|
@ -1,8 +1,9 @@
|
|||
// This crate implements the public Foreign Function Interface (FFI) for the RLN module
|
||||
|
||||
use crate::public::RLN;
|
||||
use std::slice;
|
||||
|
||||
use crate::public::RLN;
|
||||
|
||||
/// Buffer struct is taken from
|
||||
/// https://github.com/celo-org/celo-threshold-bls-rs/blob/master/crates/threshold-bls-ffi/src/ffi.rs
|
||||
///
|
||||
|
@ -30,7 +31,7 @@ impl<'a> From<&Buffer> for &'a [u8] {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: check if there are security implications for this clippy. It seems we should have pub unsafe extern "C" fn ...
|
||||
// TODO: check if there are security implications by using this clippy
|
||||
// #[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
|
@ -245,17 +246,11 @@ pub extern "C" fn hash(
|
|||
mod test {
|
||||
use super::*;
|
||||
use crate::circuit::*;
|
||||
use crate::poseidon_tree::poseidon_hash;
|
||||
use crate::protocol::*;
|
||||
use crate::utils::*;
|
||||
use ark_bn254::{Bn254, Fr};
|
||||
use ark_groth16::Proof as ArkProof;
|
||||
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
|
||||
use ark_std::str::FromStr;
|
||||
use ark_std::{rand::thread_rng, UniformRand};
|
||||
use rand::Rng;
|
||||
use semaphore::{identity::Identity, poseidon_hash, Field};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io::Cursor;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
|
@ -266,10 +261,10 @@ mod test {
|
|||
let no_of_leaves = 256;
|
||||
|
||||
// We generate a vector of random leaves
|
||||
let mut leaves: Vec<Field> = Vec::new();
|
||||
let mut leaves: Vec<Fr> = Vec::new();
|
||||
let mut rng = thread_rng();
|
||||
for _ in 0..no_of_leaves {
|
||||
leaves.push(to_field(&Fr::rand(&mut rng)));
|
||||
leaves.push(Fr::rand(&mut rng));
|
||||
}
|
||||
|
||||
// We create a RLN instance
|
||||
|
@ -282,7 +277,7 @@ mod test {
|
|||
// We first add leaves one by one specifying the index
|
||||
for (i, leaf) in leaves.iter().enumerate() {
|
||||
// We prepare id_commitment and we set the leaf at provided index
|
||||
let leaf_ser = field_to_bytes_le(&leaf);
|
||||
let leaf_ser = fr_to_bytes_le(&leaf);
|
||||
let input_buffer = &Buffer::from(leaf_ser.as_ref());
|
||||
let success = set_leaf(rln_pointer, i, input_buffer);
|
||||
assert!(success, "set leaf call failed");
|
||||
|
@ -294,7 +289,7 @@ mod test {
|
|||
assert!(success, "get root call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let result_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
let (root_single, _) = bytes_le_to_field(&result_data);
|
||||
let (root_single, _) = bytes_le_to_fr(&result_data);
|
||||
|
||||
// We reset the tree to default
|
||||
let success = set_tree(rln_pointer, tree_height);
|
||||
|
@ -302,7 +297,7 @@ mod test {
|
|||
|
||||
// We add leaves one by one using the internal index (new leaves goes in next available position)
|
||||
for leaf in &leaves {
|
||||
let leaf_ser = field_to_bytes_le(&leaf);
|
||||
let leaf_ser = fr_to_bytes_le(&leaf);
|
||||
let input_buffer = &Buffer::from(leaf_ser.as_ref());
|
||||
let success = set_next_leaf(rln_pointer, input_buffer);
|
||||
assert!(success, "set next leaf call failed");
|
||||
|
@ -314,7 +309,7 @@ mod test {
|
|||
assert!(success, "get root call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let result_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
let (root_next, _) = bytes_le_to_field(&result_data);
|
||||
let (root_next, _) = bytes_le_to_fr(&result_data);
|
||||
|
||||
// We check if roots are the same
|
||||
assert_eq!(root_single, root_next);
|
||||
|
@ -324,7 +319,7 @@ mod test {
|
|||
assert!(success, "set tree call failed");
|
||||
|
||||
// We add leaves in a batch into the tree
|
||||
let leaves_ser = vec_field_to_bytes_le(&leaves);
|
||||
let leaves_ser = vec_fr_to_bytes_le(&leaves);
|
||||
let input_buffer = &Buffer::from(leaves_ser.as_ref());
|
||||
let success = set_leaves(rln_pointer, input_buffer);
|
||||
assert!(success, "set leaves call failed");
|
||||
|
@ -335,7 +330,7 @@ mod test {
|
|||
assert!(success, "get root call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let result_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
let (root_batch, _) = bytes_le_to_field(&result_data);
|
||||
let (root_batch, _) = bytes_le_to_fr(&result_data);
|
||||
|
||||
// We check if roots are the same
|
||||
assert_eq!(root_single, root_batch);
|
||||
|
@ -354,7 +349,7 @@ mod test {
|
|||
assert!(success, "get root call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let result_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
let (root_delete, _) = bytes_le_to_field(&result_data);
|
||||
let (root_delete, _) = bytes_le_to_fr(&result_data);
|
||||
|
||||
// We reset the tree to default
|
||||
let success = set_tree(rln_pointer, tree_height);
|
||||
|
@ -366,7 +361,7 @@ mod test {
|
|||
assert!(success, "get root call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let result_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
let (root_empty, _) = bytes_le_to_field(&result_data);
|
||||
let (root_empty, _) = bytes_le_to_fr(&result_data);
|
||||
|
||||
// We check if roots are the same
|
||||
assert_eq!(root_delete, root_empty);
|
||||
|
@ -386,13 +381,11 @@ mod test {
|
|||
let rln_pointer = unsafe { &mut *rln_pointer.assume_init() };
|
||||
|
||||
// generate identity
|
||||
// We follow zk-kit approach for identity generation
|
||||
let id = Identity::from_seed(b"test-merkle-proof");
|
||||
let identity_secret = poseidon_hash(&vec![id.trapdoor, id.nullifier]);
|
||||
let identity_secret = hash_to_field(b"test-merkle-proof");
|
||||
let id_commitment = poseidon_hash(&vec![identity_secret]);
|
||||
|
||||
// We prepare id_commitment and we set the leaf at provided index
|
||||
let leaf_ser = field_to_bytes_le(&id_commitment);
|
||||
let leaf_ser = fr_to_bytes_le(&id_commitment);
|
||||
let input_buffer = &Buffer::from(leaf_ser.as_ref());
|
||||
let success = set_leaf(rln_pointer, leaf_index, input_buffer);
|
||||
assert!(success, "set leaf call failed");
|
||||
|
@ -403,7 +396,7 @@ mod test {
|
|||
assert!(success, "get root call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let result_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
let (root, _) = bytes_le_to_field(&result_data);
|
||||
let (root, _) = bytes_le_to_fr(&result_data);
|
||||
|
||||
// We obtain the Merkle tree root
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
|
@ -412,41 +405,71 @@ mod test {
|
|||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let result_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
|
||||
let (path_elements, read) = bytes_le_to_vec_field(&result_data);
|
||||
let (path_elements, read) = bytes_le_to_vec_fr(&result_data);
|
||||
let (identity_path_index, _) = bytes_le_to_vec_u8(&result_data[read..].to_vec());
|
||||
|
||||
// We check correct computation of the path and indexes
|
||||
let mut expected_path_elements = vec![
|
||||
Field::from_str("0x0000000000000000000000000000000000000000000000000000000000000000")
|
||||
.unwrap(),
|
||||
Field::from_str("0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864")
|
||||
.unwrap(),
|
||||
Field::from_str("0x1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1")
|
||||
.unwrap(),
|
||||
Field::from_str("0x18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238")
|
||||
.unwrap(),
|
||||
Field::from_str("0x07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a")
|
||||
.unwrap(),
|
||||
Field::from_str("0x2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55")
|
||||
.unwrap(),
|
||||
Field::from_str("0x2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78")
|
||||
.unwrap(),
|
||||
Field::from_str("0x078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d")
|
||||
.unwrap(),
|
||||
Field::from_str("0x2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61")
|
||||
.unwrap(),
|
||||
Field::from_str("0x0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747")
|
||||
.unwrap(),
|
||||
Field::from_str("0x1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2")
|
||||
.unwrap(),
|
||||
Field::from_str("0x1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636")
|
||||
.unwrap(),
|
||||
Field::from_str("0x2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a")
|
||||
.unwrap(),
|
||||
Field::from_str("0x14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0")
|
||||
.unwrap(),
|
||||
Field::from_str("0x190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c")
|
||||
.unwrap(),
|
||||
str_to_fr(
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c",
|
||||
16,
|
||||
),
|
||||
];
|
||||
|
||||
let mut expected_identity_path_index: Vec<u8> =
|
||||
|
@ -455,31 +478,31 @@ mod test {
|
|||
// We add the remaining elements for the case TEST_TREE_HEIGHT = 19
|
||||
if TEST_TREE_HEIGHT == 19 || TEST_TREE_HEIGHT == 20 {
|
||||
expected_path_elements.append(&mut vec![
|
||||
Field::from_str(
|
||||
str_to_fr(
|
||||
"0x22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92",
|
||||
)
|
||||
.unwrap(),
|
||||
Field::from_str(
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323",
|
||||
)
|
||||
.unwrap(),
|
||||
Field::from_str(
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992",
|
||||
)
|
||||
.unwrap(),
|
||||
Field::from_str(
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f",
|
||||
)
|
||||
.unwrap(),
|
||||
16,
|
||||
),
|
||||
]);
|
||||
expected_identity_path_index.append(&mut vec![0, 0, 0, 0]);
|
||||
}
|
||||
|
||||
if TEST_TREE_HEIGHT == 20 {
|
||||
expected_path_elements.append(&mut vec![Field::from_str(
|
||||
expected_path_elements.append(&mut vec![str_to_fr(
|
||||
"0x1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca",
|
||||
)
|
||||
.unwrap()]);
|
||||
16,
|
||||
)]);
|
||||
expected_identity_path_index.append(&mut vec![0]);
|
||||
}
|
||||
|
||||
|
@ -494,6 +517,7 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
// Benchmarks proof generation and verification
|
||||
fn test_groth16_proofs_performance_ffi() {
|
||||
let tree_height = TEST_TREE_HEIGHT;
|
||||
|
||||
|
@ -555,15 +579,16 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
// Computes and verifies an RLN ZK proof using FFI APIs
|
||||
fn test_rln_proof_ffi() {
|
||||
let tree_height = TEST_TREE_HEIGHT;
|
||||
let no_of_leaves = 256;
|
||||
|
||||
// We generate a vector of random leaves
|
||||
let mut leaves: Vec<Field> = Vec::new();
|
||||
let mut leaves: Vec<Fr> = Vec::new();
|
||||
let mut rng = thread_rng();
|
||||
for _ in 0..no_of_leaves {
|
||||
leaves.push(to_field(&Fr::rand(&mut rng)));
|
||||
leaves.push(Fr::rand(&mut rng));
|
||||
}
|
||||
|
||||
// We create a RLN instance
|
||||
|
@ -574,7 +599,7 @@ mod test {
|
|||
let rln_pointer = unsafe { &mut *rln_pointer.assume_init() };
|
||||
|
||||
// We add leaves in a batch into the tree
|
||||
let leaves_ser = vec_field_to_bytes_le(&leaves);
|
||||
let leaves_ser = vec_fr_to_bytes_le(&leaves);
|
||||
let input_buffer = &Buffer::from(leaves_ser.as_ref());
|
||||
let success = set_leaves(rln_pointer, input_buffer);
|
||||
assert!(success, "set leaves call failed");
|
||||
|
@ -585,11 +610,11 @@ mod test {
|
|||
assert!(success, "key gen call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let result_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
let (identity_secret, read) = bytes_le_to_field(&result_data);
|
||||
let (id_commitment, _) = bytes_le_to_field(&result_data[read..].to_vec());
|
||||
let (identity_secret, read) = bytes_le_to_fr(&result_data);
|
||||
let (id_commitment, _) = bytes_le_to_fr(&result_data[read..].to_vec());
|
||||
|
||||
// We set as leaf id_commitment, its index would be equal to no_of_leaves
|
||||
let leaf_ser = field_to_bytes_le(&id_commitment);
|
||||
let leaf_ser = fr_to_bytes_le(&id_commitment);
|
||||
let input_buffer = &Buffer::from(leaf_ser.as_ref());
|
||||
let success = set_next_leaf(rln_pointer, input_buffer);
|
||||
assert!(success, "set next leaf call failed");
|
||||
|
@ -607,9 +632,9 @@ mod test {
|
|||
// We prepare input for generate_rln_proof API
|
||||
// input_data is [ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal<var> ]
|
||||
let mut serialized: Vec<u8> = Vec::new();
|
||||
serialized.append(&mut field_to_bytes_le(&identity_secret));
|
||||
serialized.append(&mut fr_to_bytes_le(&identity_secret));
|
||||
serialized.append(&mut identity_index.to_le_bytes().to_vec());
|
||||
serialized.append(&mut field_to_bytes_le(&epoch));
|
||||
serialized.append(&mut fr_to_bytes_le(&epoch));
|
||||
serialized.append(&mut signal_len.to_le_bytes().to_vec());
|
||||
serialized.append(&mut signal.to_vec());
|
||||
|
||||
|
@ -638,6 +663,7 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
// Tests hash to field using FFI APIs
|
||||
fn test_hash_to_field_ffi() {
|
||||
let tree_height = TEST_TREE_HEIGHT;
|
||||
|
||||
|
@ -660,7 +686,7 @@ mod test {
|
|||
|
||||
// We read the returned proof and we append proof values for verify
|
||||
let serialized_hash = <&[u8]>::from(&output_buffer).to_vec();
|
||||
let (hash1, _) = bytes_le_to_field(&serialized_hash);
|
||||
let (hash1, _) = bytes_le_to_fr(&serialized_hash);
|
||||
|
||||
let hash2 = hash_to_field(&signal);
|
||||
|
||||
|
|
166
rln/src/lib.rs
166
rln/src/lib.rs
|
@ -1,10 +1,4 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use crate::circuit::{CIRCOM, VK, ZKEY};
|
||||
use ark_bn254::{Fr, Parameters};
|
||||
use ark_ec::bn::Bn;
|
||||
use ark_std::str::FromStr;
|
||||
|
||||
pub mod circuit;
|
||||
pub mod ffi;
|
||||
|
@ -16,13 +10,11 @@ pub mod utils;
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::circuit::{TEST_RESOURCES_FOLDER, TEST_TREE_HEIGHT};
|
||||
use crate::poseidon_tree::PoseidonTree;
|
||||
|
||||
use crate::circuit::{Fr, CIRCOM, TEST_RESOURCES_FOLDER, TEST_TREE_HEIGHT, VK, ZKEY};
|
||||
use crate::poseidon_tree::{poseidon_hash, PoseidonTree};
|
||||
use crate::protocol::*;
|
||||
use hex_literal::hex;
|
||||
use num_bigint::BigInt;
|
||||
use semaphore::{hash::Hash, identity::Identity, poseidon_hash, Field};
|
||||
use crate::utils::str_to_fr;
|
||||
|
||||
// Input generated with https://github.com/oskarth/zk-kit/commit/b6a872f7160c7c14e10a0ea40acab99cbb23c9a8
|
||||
const WITNESS_JSON_15: &str = r#"
|
||||
|
@ -175,19 +167,17 @@ mod test {
|
|||
"#;
|
||||
|
||||
#[test]
|
||||
// We test Merkle Tree generation, proofs and verification
|
||||
// We test Merkle tree generation, proofs and verification
|
||||
fn test_merkle_proof() {
|
||||
let tree_height = TEST_TREE_HEIGHT;
|
||||
let leaf_index = 3;
|
||||
|
||||
// generate identity
|
||||
// We follow zk-kit approach for identity generation
|
||||
let id = Identity::from_seed(b"test-merkle-proof");
|
||||
let identity_secret = poseidon_hash(&vec![id.trapdoor, id.nullifier]);
|
||||
let identity_secret = hash_to_field(b"test-merkle-proof");
|
||||
let id_commitment = poseidon_hash(&vec![identity_secret]);
|
||||
|
||||
// generate merkle tree
|
||||
let default_leaf = Field::from(0);
|
||||
let default_leaf = Fr::from(0);
|
||||
let mut tree = PoseidonTree::new(tree_height, default_leaf);
|
||||
tree.set(leaf_index, id_commitment.into()).unwrap();
|
||||
|
||||
|
@ -197,26 +187,26 @@ mod test {
|
|||
if TEST_TREE_HEIGHT == 15 {
|
||||
assert_eq!(
|
||||
root,
|
||||
Field::from_str(
|
||||
"0x27401a4559ce263630907ce3b77c570649e28ede22d2a7f5296839627a16e870"
|
||||
str_to_fr(
|
||||
"0x1984f2e01184aef5cb974640898a5f5c25556554e2b06d99d4841badb8b198cd",
|
||||
16
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
} else if TEST_TREE_HEIGHT == 19 {
|
||||
assert_eq!(
|
||||
root,
|
||||
Field::from_str(
|
||||
"0x302920b5e5af8bf5f4bf32995f1ac5933d9a4b6f74803fdde84b8b9a761a2991"
|
||||
str_to_fr(
|
||||
"0x219ceb53f2b1b7a6cf74e80d50d44d68ecb4a53c6cc65b25593c8d56343fb1fe",
|
||||
16
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
} else if TEST_TREE_HEIGHT == 20 {
|
||||
assert_eq!(
|
||||
root,
|
||||
Field::from_str(
|
||||
"0x0c33d9be0ca0dd96c64d92107886de4235108e0fee203bb044ac4ee210a3dfea"
|
||||
str_to_fr(
|
||||
"0x21947ffd0bce0c385f876e7c97d6a42eec5b1fe935aab2f01c1f8a8cbcc356d2",
|
||||
16
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -227,36 +217,66 @@ mod test {
|
|||
// We check correct computation of the path and indexes
|
||||
// These values refers to TEST_TREE_HEIGHT == 16
|
||||
let mut expected_path_elements = vec![
|
||||
Field::from_str("0x0000000000000000000000000000000000000000000000000000000000000000")
|
||||
.unwrap(),
|
||||
Field::from_str("0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864")
|
||||
.unwrap(),
|
||||
Field::from_str("0x1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1")
|
||||
.unwrap(),
|
||||
Field::from_str("0x18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238")
|
||||
.unwrap(),
|
||||
Field::from_str("0x07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a")
|
||||
.unwrap(),
|
||||
Field::from_str("0x2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55")
|
||||
.unwrap(),
|
||||
Field::from_str("0x2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78")
|
||||
.unwrap(),
|
||||
Field::from_str("0x078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d")
|
||||
.unwrap(),
|
||||
Field::from_str("0x2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61")
|
||||
.unwrap(),
|
||||
Field::from_str("0x0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747")
|
||||
.unwrap(),
|
||||
Field::from_str("0x1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2")
|
||||
.unwrap(),
|
||||
Field::from_str("0x1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636")
|
||||
.unwrap(),
|
||||
Field::from_str("0x2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a")
|
||||
.unwrap(),
|
||||
Field::from_str("0x14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0")
|
||||
.unwrap(),
|
||||
Field::from_str("0x190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c")
|
||||
.unwrap(),
|
||||
str_to_fr(
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c",
|
||||
16,
|
||||
),
|
||||
];
|
||||
|
||||
let mut expected_identity_path_index: Vec<u8> =
|
||||
|
@ -265,31 +285,31 @@ mod test {
|
|||
// We add the remaining elements for the case TEST_TREE_HEIGHT = 20
|
||||
if TEST_TREE_HEIGHT == 19 || TEST_TREE_HEIGHT == 20 {
|
||||
expected_path_elements.append(&mut vec![
|
||||
Field::from_str(
|
||||
str_to_fr(
|
||||
"0x22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92",
|
||||
)
|
||||
.unwrap(),
|
||||
Field::from_str(
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323",
|
||||
)
|
||||
.unwrap(),
|
||||
Field::from_str(
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992",
|
||||
)
|
||||
.unwrap(),
|
||||
Field::from_str(
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f",
|
||||
)
|
||||
.unwrap(),
|
||||
16,
|
||||
),
|
||||
]);
|
||||
expected_identity_path_index.append(&mut vec![0, 0, 0, 0]);
|
||||
}
|
||||
|
||||
if TEST_TREE_HEIGHT == 20 {
|
||||
expected_path_elements.append(&mut vec![Field::from_str(
|
||||
expected_path_elements.append(&mut vec![str_to_fr(
|
||||
"0x1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca",
|
||||
)
|
||||
.unwrap()]);
|
||||
16,
|
||||
)]);
|
||||
expected_identity_path_index.append(&mut vec![0]);
|
||||
}
|
||||
|
||||
|
@ -342,7 +362,7 @@ mod test {
|
|||
let (identity_secret, id_commitment) = keygen();
|
||||
|
||||
//// generate merkle tree
|
||||
let default_leaf = Field::from(0);
|
||||
let default_leaf = Fr::from(0);
|
||||
let mut tree = PoseidonTree::new(tree_height, default_leaf);
|
||||
tree.set(leaf_index, id_commitment.into()).unwrap();
|
||||
|
||||
|
@ -379,7 +399,7 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
fn test_witness_serialization() {
|
||||
// We test witness serialization
|
||||
let mut witness_json: &str = "";
|
||||
|
||||
|
|
|
@ -11,22 +11,21 @@
|
|||
//! # To do
|
||||
//!
|
||||
//! * Disk based storage backend (using mmaped files should be easy)
|
||||
//! * Implement serialization for tree and Merkle proof
|
||||
|
||||
use ark_std::str::FromStr;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io::{self, Error, ErrorKind};
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::{
|
||||
cmp::max,
|
||||
fmt::Debug,
|
||||
iter::{once, repeat, successors},
|
||||
};
|
||||
use std::{collections::HashMap, hash::Hash};
|
||||
|
||||
/// In the Hasher trait we define the node type, the default leaf
|
||||
/// and the hash function used to initialize a Merkle Tree implementation
|
||||
pub trait Hasher {
|
||||
/// Type of the leaf and tree node
|
||||
type Fr: Copy + Clone + Eq + Serialize;
|
||||
type Fr: Copy + Clone + Eq;
|
||||
|
||||
/// Returns the default tree leaf
|
||||
fn default_leaf() -> Self::Fr;
|
||||
|
@ -40,6 +39,7 @@ pub trait Hasher {
|
|||
////////////////////////////////////////////////////////////
|
||||
|
||||
/// The Merkle tree structure
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct OptimalMerkleTree<H>
|
||||
where
|
||||
H: Hasher,
|
||||
|
@ -63,7 +63,7 @@ where
|
|||
|
||||
/// The Merkle proof
|
||||
/// Contains a vector of (node, branch_index) that defines the proof path elements and branch direction (1 or 0)
|
||||
#[derive(Clone, PartialEq, Eq, Serialize)]
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct OptimalMerkleProof<H: Hasher>(pub Vec<(H::Fr, u8)>);
|
||||
|
||||
/// Implementations
|
||||
|
@ -293,7 +293,7 @@ pub struct FullMerkleTree<H: Hasher> {
|
|||
}
|
||||
|
||||
/// Element of a Merkle proof
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum FullMerkleBranch<H: Hasher> {
|
||||
/// Left branch taken, value is the right sibling hash.
|
||||
Left(H::Fr),
|
||||
|
@ -303,7 +303,7 @@ pub enum FullMerkleBranch<H: Hasher> {
|
|||
}
|
||||
|
||||
/// Merkle proof path, bottom to top.
|
||||
#[derive(Clone, PartialEq, Eq, Serialize)]
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct FullMerkleProof<H: Hasher>(pub Vec<FullMerkleBranch<H>>);
|
||||
|
||||
/// Implementations
|
||||
|
@ -546,7 +546,8 @@ where
|
|||
////////////////////////////////////////////////////////////
|
||||
|
||||
// Tests adapted from https://github.com/worldcoin/semaphore-rs/blob/d462a4372f1fd9c27610f2acfe4841fab1d396aa/src/merkle_tree.rs
|
||||
pub mod test {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use hex_literal::hex;
|
||||
use tiny_keccak::{Hasher as _, Keccak};
|
||||
|
|
|
@ -1,26 +1,25 @@
|
|||
// This crate defines RLN module default Merkle tree implementation and Hasher
|
||||
// Implementation inspired by https://github.com/worldcoin/semaphore-rs/blob/d462a4372f1fd9c27610f2acfe4841fab1d396aa/src/poseidon_tree.rs (no differences)
|
||||
|
||||
use crate::merkle_tree::{
|
||||
FullMerkleProof, FullMerkleTree, Hasher, OptimalMerkleProof, OptimalMerkleTree,
|
||||
};
|
||||
use semaphore::{poseidon_hash, Field};
|
||||
use serde::{Deserialize, Serialize};
|
||||
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;
|
||||
|
||||
// The zerokit RLN default Merkle tree implementation.
|
||||
// To switch to FullMerkleTree implementation it is enough to redefine the following two types
|
||||
#[allow(dead_code)]
|
||||
pub type PoseidonTree = OptimalMerkleTree<PoseidonHash>;
|
||||
pub type MerkleProof = OptimalMerkleProof<PoseidonHash>;
|
||||
//pub type PoseidonTree = FullMerkleTree<PoseidonHash>;
|
||||
//pub type MerkleProof = FullMerkleProof<PoseidonHash>;
|
||||
|
||||
// The zerokit RLN default Hasher
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct PoseidonHash;
|
||||
|
||||
impl Hasher for PoseidonHash {
|
||||
type Fr = Field;
|
||||
type Fr = Fr;
|
||||
|
||||
fn default_leaf() -> Self::Fr {
|
||||
Self::Fr::from(0)
|
||||
|
@ -31,77 +30,91 @@ 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
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#[test]
|
||||
/// A basic performance comparison between the two supported Merkle Tree implementations
|
||||
fn test_merkle_implementations_performances() {
|
||||
use std::time::{Duration, Instant};
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
let tree_height = 20;
|
||||
let sample_size = 100;
|
||||
#[test]
|
||||
/// A basic performance comparison between the two supported Merkle Tree implementations
|
||||
fn test_merkle_implementations_performances() {
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
let leaves: Vec<Field> = (0..sample_size).map(|s| Field::from(s)).collect();
|
||||
let tree_height = 20;
|
||||
let sample_size = 100;
|
||||
|
||||
let mut gen_time_full: u128 = 0;
|
||||
let mut upd_time_full: u128 = 0;
|
||||
let mut gen_time_opt: u128 = 0;
|
||||
let mut upd_time_opt: u128 = 0;
|
||||
let leaves: Vec<Fr> = (0..sample_size).map(|s| Fr::from(s)).collect();
|
||||
|
||||
for _ in 0..sample_size.try_into().unwrap() {
|
||||
let now = Instant::now();
|
||||
FullMerkleTree::<PoseidonHash>::default(tree_height);
|
||||
gen_time_full += now.elapsed().as_nanos();
|
||||
let mut gen_time_full: u128 = 0;
|
||||
let mut upd_time_full: u128 = 0;
|
||||
let mut gen_time_opt: u128 = 0;
|
||||
let mut upd_time_opt: u128 = 0;
|
||||
|
||||
let now = Instant::now();
|
||||
OptimalMerkleTree::<PoseidonHash>::default(tree_height);
|
||||
gen_time_opt += now.elapsed().as_nanos();
|
||||
for _ in 0..sample_size.try_into().unwrap() {
|
||||
let now = Instant::now();
|
||||
FullMerkleTree::<PoseidonHash>::default(tree_height);
|
||||
gen_time_full += now.elapsed().as_nanos();
|
||||
|
||||
let now = Instant::now();
|
||||
OptimalMerkleTree::<PoseidonHash>::default(tree_height);
|
||||
gen_time_opt += now.elapsed().as_nanos();
|
||||
}
|
||||
|
||||
let mut tree_full = FullMerkleTree::<PoseidonHash>::default(tree_height);
|
||||
let mut tree_opt = OptimalMerkleTree::<PoseidonHash>::default(tree_height);
|
||||
for i in 0..sample_size.try_into().unwrap() {
|
||||
let now = Instant::now();
|
||||
tree_full.set(i, leaves[i]).unwrap();
|
||||
upd_time_full += now.elapsed().as_nanos();
|
||||
let proof = tree_full.proof(i).expect("index should be set");
|
||||
assert_eq!(proof.leaf_index(), i);
|
||||
|
||||
let now = Instant::now();
|
||||
tree_opt.set(i, leaves[i]).unwrap();
|
||||
upd_time_opt += now.elapsed().as_nanos();
|
||||
let proof = tree_opt.proof(i).expect("index should be set");
|
||||
assert_eq!(proof.leaf_index(), i);
|
||||
}
|
||||
|
||||
println!("Average tree generation time:");
|
||||
println!(
|
||||
" - Full Merkle Tree: {:?}",
|
||||
Duration::from_nanos((gen_time_full / sample_size).try_into().unwrap())
|
||||
);
|
||||
println!(
|
||||
" - Optimal Merkle Tree: {:?}",
|
||||
Duration::from_nanos((gen_time_opt / sample_size).try_into().unwrap())
|
||||
);
|
||||
|
||||
println!("Average update_next execution time:");
|
||||
println!(
|
||||
" - Full Merkle Tree: {:?}",
|
||||
Duration::from_nanos((upd_time_full / sample_size).try_into().unwrap())
|
||||
);
|
||||
|
||||
println!(
|
||||
" - Optimal Merkle Tree: {:?}",
|
||||
Duration::from_nanos((upd_time_opt / sample_size).try_into().unwrap())
|
||||
);
|
||||
}
|
||||
|
||||
let mut tree_full = FullMerkleTree::<PoseidonHash>::default(tree_height);
|
||||
let mut tree_opt = OptimalMerkleTree::<PoseidonHash>::default(tree_height);
|
||||
for i in 0..sample_size.try_into().unwrap() {
|
||||
let now = Instant::now();
|
||||
tree_full.set(i, leaves[i]).unwrap();
|
||||
upd_time_full += now.elapsed().as_nanos();
|
||||
let proof = tree_full.proof(i).expect("index should be set");
|
||||
assert_eq!(proof.leaf_index(), i);
|
||||
|
||||
let now = Instant::now();
|
||||
tree_opt.set(i, leaves[i]).unwrap();
|
||||
upd_time_opt += now.elapsed().as_nanos();
|
||||
let proof = tree_opt.proof(i).expect("index should be set");
|
||||
assert_eq!(proof.leaf_index(), i);
|
||||
}
|
||||
|
||||
println!("Average tree generation time:");
|
||||
println!(
|
||||
" - Full Merkle Tree: {:?}",
|
||||
Duration::from_nanos(
|
||||
(gen_time_full / u128::from(sample_size))
|
||||
.try_into()
|
||||
.unwrap()
|
||||
)
|
||||
);
|
||||
println!(
|
||||
" - Optimal Merkle Tree: {:?}",
|
||||
Duration::from_nanos((gen_time_opt / u128::from(sample_size)).try_into().unwrap())
|
||||
);
|
||||
|
||||
println!("Average update_next execution time:");
|
||||
println!(
|
||||
" - Full Merkle Tree: {:?}",
|
||||
Duration::from_nanos(
|
||||
(upd_time_full / u128::from(sample_size))
|
||||
.try_into()
|
||||
.unwrap()
|
||||
)
|
||||
);
|
||||
|
||||
println!(
|
||||
" - Optimal Merkle Tree: {:?}",
|
||||
Duration::from_nanos((upd_time_opt / u128::from(sample_size)).try_into().unwrap())
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,34 +1,25 @@
|
|||
// This crate collects all the underlying primitives used to implement RLN
|
||||
|
||||
use crate::circuit::{CIRCOM, VK, ZKEY};
|
||||
use ark_bn254::{Bn254, Fr, Parameters};
|
||||
use ark_circom::{read_zkey, CircomBuilder, CircomConfig, CircomReduction, WitnessCalculator};
|
||||
use ark_ec::bn::Bn;
|
||||
use ark_ff::{bytes::ToBytes, Fp256, PrimeField};
|
||||
use ark_circom::{CircomReduction, WitnessCalculator};
|
||||
use ark_groth16::{
|
||||
create_proof_with_reduction_and_matrices, create_random_proof_with_reduction,
|
||||
prepare_verifying_key, verify_proof as ark_verify_proof, Proof as ArkProof, ProvingKey,
|
||||
VerifyingKey,
|
||||
create_proof_with_reduction_and_matrices, prepare_verifying_key,
|
||||
verify_proof as ark_verify_proof, Proof as ArkProof, ProvingKey, VerifyingKey,
|
||||
};
|
||||
use ark_relations::r1cs::ConstraintMatrices;
|
||||
use ark_relations::r1cs::SynthesisError;
|
||||
use ark_serialize::*;
|
||||
use ark_std::{rand::thread_rng, str::FromStr, UniformRand};
|
||||
use ark_std::{rand::thread_rng, UniformRand};
|
||||
use color_eyre::Result;
|
||||
use ethers::core::utils::keccak256;
|
||||
use num_bigint::{BigInt, BigUint, ToBigInt};
|
||||
use primitive_types::U256;
|
||||
use num_bigint::BigInt;
|
||||
use rand::Rng;
|
||||
use semaphore::{identity::Identity, poseidon_hash, Field};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io::Write;
|
||||
use std::sync::Mutex;
|
||||
use std::time::Instant;
|
||||
use thiserror::Error;
|
||||
use tiny_keccak::{Hasher as _, Keccak};
|
||||
|
||||
use crate::circuit::{Curve, Fr};
|
||||
use crate::poseidon_tree::*;
|
||||
use crate::public::{RLN, RLN_IDENTIFIER};
|
||||
pub use crate::utils::*;
|
||||
use crate::public::RLN_IDENTIFIER;
|
||||
use crate::utils::*;
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// RLN Witness data structure and utility functions
|
||||
|
@ -36,35 +27,35 @@ pub use crate::utils::*;
|
|||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct RLNWitnessInput {
|
||||
identity_secret: Field,
|
||||
path_elements: Vec<Field>,
|
||||
identity_secret: Fr,
|
||||
path_elements: Vec<Fr>,
|
||||
identity_path_index: Vec<u8>,
|
||||
x: Field,
|
||||
epoch: Field,
|
||||
rln_identifier: Field,
|
||||
x: Fr,
|
||||
epoch: Fr,
|
||||
rln_identifier: Fr,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct RLNProofValues {
|
||||
// Public outputs:
|
||||
pub y: Field,
|
||||
pub nullifier: Field,
|
||||
pub root: Field,
|
||||
pub y: Fr,
|
||||
pub nullifier: Fr,
|
||||
pub root: Fr,
|
||||
// Public Inputs:
|
||||
pub x: Field,
|
||||
pub epoch: Field,
|
||||
pub rln_identifier: Field,
|
||||
pub x: Fr,
|
||||
pub epoch: Fr,
|
||||
pub rln_identifier: Fr,
|
||||
}
|
||||
|
||||
pub fn serialize_witness(rln_witness: &RLNWitnessInput) -> Vec<u8> {
|
||||
let mut serialized: Vec<u8> = Vec::new();
|
||||
|
||||
serialized.append(&mut field_to_bytes_le(&rln_witness.identity_secret));
|
||||
serialized.append(&mut vec_field_to_bytes_le(&rln_witness.path_elements));
|
||||
serialized.append(&mut fr_to_bytes_le(&rln_witness.identity_secret));
|
||||
serialized.append(&mut vec_fr_to_bytes_le(&rln_witness.path_elements));
|
||||
serialized.append(&mut vec_u8_to_bytes_le(&rln_witness.identity_path_index));
|
||||
serialized.append(&mut field_to_bytes_le(&rln_witness.x));
|
||||
serialized.append(&mut field_to_bytes_le(&rln_witness.epoch));
|
||||
serialized.append(&mut field_to_bytes_le(&rln_witness.rln_identifier));
|
||||
serialized.append(&mut fr_to_bytes_le(&rln_witness.x));
|
||||
serialized.append(&mut fr_to_bytes_le(&rln_witness.epoch));
|
||||
serialized.append(&mut fr_to_bytes_le(&rln_witness.rln_identifier));
|
||||
|
||||
serialized
|
||||
}
|
||||
|
@ -72,22 +63,22 @@ pub fn serialize_witness(rln_witness: &RLNWitnessInput) -> Vec<u8> {
|
|||
pub fn deserialize_witness(serialized: &[u8]) -> (RLNWitnessInput, usize) {
|
||||
let mut all_read: usize = 0;
|
||||
|
||||
let (identity_secret, read) = bytes_le_to_field(&serialized[all_read..].to_vec());
|
||||
let (identity_secret, read) = bytes_le_to_fr(&serialized[all_read..].to_vec());
|
||||
all_read += read;
|
||||
|
||||
let (path_elements, read) = bytes_le_to_vec_field(&serialized[all_read..].to_vec());
|
||||
let (path_elements, read) = bytes_le_to_vec_fr(&serialized[all_read..].to_vec());
|
||||
all_read += read;
|
||||
|
||||
let (identity_path_index, read) = bytes_le_to_vec_u8(&serialized[all_read..].to_vec());
|
||||
all_read += read;
|
||||
|
||||
let (x, read) = bytes_le_to_field(&serialized[all_read..].to_vec());
|
||||
let (x, read) = bytes_le_to_fr(&serialized[all_read..].to_vec());
|
||||
all_read += read;
|
||||
|
||||
let (epoch, read) = bytes_le_to_field(&serialized[all_read..].to_vec());
|
||||
let (epoch, read) = bytes_le_to_fr(&serialized[all_read..].to_vec());
|
||||
all_read += read;
|
||||
|
||||
let (rln_identifier, read) = bytes_le_to_field(&serialized[all_read..].to_vec());
|
||||
let (rln_identifier, read) = bytes_le_to_fr(&serialized[all_read..].to_vec());
|
||||
all_read += read;
|
||||
|
||||
// TODO: check rln_identifier against public::RLN_IDENTIFIER
|
||||
|
@ -116,13 +107,13 @@ pub fn proof_inputs_to_rln_witness(
|
|||
) -> (RLNWitnessInput, usize) {
|
||||
let mut all_read: usize = 0;
|
||||
|
||||
let (identity_secret, read) = bytes_le_to_field(&serialized[all_read..].to_vec());
|
||||
let (identity_secret, read) = bytes_le_to_fr(&serialized[all_read..].to_vec());
|
||||
all_read += read;
|
||||
|
||||
let id_index = u64::from_le_bytes(serialized[all_read..all_read + 8].try_into().unwrap());
|
||||
all_read += 8;
|
||||
|
||||
let (epoch, read) = bytes_le_to_field(&serialized[all_read..].to_vec());
|
||||
let (epoch, read) = bytes_le_to_fr(&serialized[all_read..].to_vec());
|
||||
all_read += read;
|
||||
|
||||
let signal_len = u64::from_le_bytes(serialized[all_read..all_read + 8].try_into().unwrap());
|
||||
|
@ -157,12 +148,12 @@ pub fn proof_inputs_to_rln_witness(
|
|||
pub fn serialize_proof_values(rln_proof_values: &RLNProofValues) -> Vec<u8> {
|
||||
let mut serialized: Vec<u8> = Vec::new();
|
||||
|
||||
serialized.append(&mut field_to_bytes_le(&rln_proof_values.root));
|
||||
serialized.append(&mut field_to_bytes_le(&rln_proof_values.epoch));
|
||||
serialized.append(&mut field_to_bytes_le(&rln_proof_values.x));
|
||||
serialized.append(&mut field_to_bytes_le(&rln_proof_values.y));
|
||||
serialized.append(&mut field_to_bytes_le(&rln_proof_values.nullifier));
|
||||
serialized.append(&mut field_to_bytes_le(&rln_proof_values.rln_identifier));
|
||||
serialized.append(&mut fr_to_bytes_le(&rln_proof_values.root));
|
||||
serialized.append(&mut fr_to_bytes_le(&rln_proof_values.epoch));
|
||||
serialized.append(&mut fr_to_bytes_le(&rln_proof_values.x));
|
||||
serialized.append(&mut fr_to_bytes_le(&rln_proof_values.y));
|
||||
serialized.append(&mut fr_to_bytes_le(&rln_proof_values.nullifier));
|
||||
serialized.append(&mut fr_to_bytes_le(&rln_proof_values.rln_identifier));
|
||||
|
||||
serialized
|
||||
}
|
||||
|
@ -170,22 +161,22 @@ pub fn serialize_proof_values(rln_proof_values: &RLNProofValues) -> Vec<u8> {
|
|||
pub fn deserialize_proof_values(serialized: &[u8]) -> (RLNProofValues, usize) {
|
||||
let mut all_read: usize = 0;
|
||||
|
||||
let (root, read) = bytes_le_to_field(&serialized[all_read..].to_vec());
|
||||
let (root, read) = bytes_le_to_fr(&serialized[all_read..].to_vec());
|
||||
all_read += read;
|
||||
|
||||
let (epoch, read) = bytes_le_to_field(&serialized[all_read..].to_vec());
|
||||
let (epoch, read) = bytes_le_to_fr(&serialized[all_read..].to_vec());
|
||||
all_read += read;
|
||||
|
||||
let (x, read) = bytes_le_to_field(&serialized[all_read..].to_vec());
|
||||
let (x, read) = bytes_le_to_fr(&serialized[all_read..].to_vec());
|
||||
all_read += read;
|
||||
|
||||
let (y, read) = bytes_le_to_field(&serialized[all_read..].to_vec());
|
||||
let (y, read) = bytes_le_to_fr(&serialized[all_read..].to_vec());
|
||||
all_read += read;
|
||||
|
||||
let (nullifier, read) = bytes_le_to_field(&serialized[all_read..].to_vec());
|
||||
let (nullifier, read) = bytes_le_to_fr(&serialized[all_read..].to_vec());
|
||||
all_read += read;
|
||||
|
||||
let (rln_identifier, read) = bytes_le_to_field(&serialized[all_read..].to_vec());
|
||||
let (rln_identifier, read) = bytes_le_to_fr(&serialized[all_read..].to_vec());
|
||||
all_read += read;
|
||||
|
||||
(
|
||||
|
@ -205,13 +196,13 @@ pub fn rln_witness_from_json(input_json_str: &str) -> RLNWitnessInput {
|
|||
let input_json: serde_json::Value =
|
||||
serde_json::from_str(input_json_str).expect("JSON was not well-formatted");
|
||||
|
||||
let identity_secret = str_to_field(input_json["identity_secret"].to_string(), 10);
|
||||
let identity_secret = str_to_fr(&input_json["identity_secret"].to_string(), 10);
|
||||
|
||||
let path_elements = input_json["path_elements"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|v| str_to_field(v.to_string(), 10))
|
||||
.map(|v| str_to_fr(&v.to_string(), 10))
|
||||
.collect();
|
||||
|
||||
let identity_path_index = input_json["identity_path_index"]
|
||||
|
@ -221,11 +212,11 @@ pub fn rln_witness_from_json(input_json_str: &str) -> RLNWitnessInput {
|
|||
.map(|v| v.as_u64().unwrap() as u8)
|
||||
.collect();
|
||||
|
||||
let x = str_to_field(input_json["x"].to_string(), 10);
|
||||
let x = str_to_fr(&input_json["x"].to_string(), 10);
|
||||
|
||||
let epoch = str_to_field(input_json["epoch"].to_string(), 16);
|
||||
let epoch = str_to_fr(&input_json["epoch"].to_string(), 16);
|
||||
|
||||
let rln_identifier = str_to_field(input_json["rln_identifier"].to_string(), 10);
|
||||
let rln_identifier = str_to_fr(&input_json["rln_identifier"].to_string(), 10);
|
||||
|
||||
// TODO: check rln_identifier against public::RLN_IDENTIFIER
|
||||
|
||||
|
@ -240,11 +231,11 @@ pub fn rln_witness_from_json(input_json_str: &str) -> RLNWitnessInput {
|
|||
}
|
||||
|
||||
pub fn rln_witness_from_values(
|
||||
identity_secret: Field,
|
||||
identity_secret: Fr,
|
||||
merkle_proof: &MerkleProof,
|
||||
x: Field,
|
||||
epoch: Field,
|
||||
//rln_identifier: Field,
|
||||
x: Fr,
|
||||
epoch: Fr,
|
||||
//rln_identifier: Fr,
|
||||
) -> RLNWitnessInput {
|
||||
let path_elements = merkle_proof.get_path_elements();
|
||||
let identity_path_index = merkle_proof.get_path_index();
|
||||
|
@ -268,7 +259,7 @@ pub fn random_rln_witness(tree_height: usize) -> RLNWitnessInput {
|
|||
let epoch = hash_to_field(&rng.gen::<[u8; 32]>());
|
||||
let rln_identifier = hash_to_field(RLN_IDENTIFIER); //hash_to_field(&rng.gen::<[u8; 32]>());
|
||||
|
||||
let mut path_elements: Vec<Field> = Vec::new();
|
||||
let mut path_elements: Vec<Fr> = Vec::new();
|
||||
let mut identity_path_index: Vec<u8> = Vec::new();
|
||||
|
||||
for _ in 0..tree_height {
|
||||
|
@ -290,8 +281,8 @@ pub fn proof_values_from_witness(rln_witness: &RLNWitnessInput) -> RLNProofValue
|
|||
// y share
|
||||
let a_0 = rln_witness.identity_secret;
|
||||
let a_1 = poseidon_hash(&[a_0, rln_witness.epoch]);
|
||||
let y = mul(&rln_witness.x, &a_1);
|
||||
let y = add(&y, &a_0);
|
||||
let y = rln_witness.x * a_1;
|
||||
let y = y + a_0;
|
||||
|
||||
// Nullifier
|
||||
let nullifier = poseidon_hash(&[a_1, rln_witness.rln_identifier]);
|
||||
|
@ -319,11 +310,11 @@ pub fn proof_values_from_witness(rln_witness: &RLNWitnessInput) -> RLNProofValue
|
|||
///////////////////////////////////////////////////////
|
||||
|
||||
pub fn compute_tree_root(
|
||||
leaf: &Field,
|
||||
path_elements: &[Field],
|
||||
leaf: &Fr,
|
||||
path_elements: &[Fr],
|
||||
identity_path_index: &[u8],
|
||||
hash_leaf: bool,
|
||||
) -> Field {
|
||||
) -> Fr {
|
||||
let mut root = *leaf;
|
||||
if hash_leaf {
|
||||
root = poseidon_hash(&[root]);
|
||||
|
@ -343,71 +334,30 @@ pub fn compute_tree_root(
|
|||
///////////////////////////////////////////////////////
|
||||
// Signal/nullifier utility functions
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
// Generates a tupe (identity_secret, id_commitment) where
|
||||
// identity_secret is random and id_commitment = PoseidonHash(identity_secret)
|
||||
pub fn keygen() -> (Field, Field) {
|
||||
pub fn keygen() -> (Fr, Fr) {
|
||||
let mut rng = thread_rng();
|
||||
let identity_secret = to_field(&Fr::rand(&mut rng));
|
||||
let identity_secret = Fr::rand(&mut rng);
|
||||
let id_commitment = poseidon_hash(&[identity_secret]);
|
||||
(identity_secret, id_commitment)
|
||||
}
|
||||
|
||||
pub fn hash_to_field(signal: &[u8]) -> Field {
|
||||
let hash = keccak256(signal);
|
||||
let (el, _) = bytes_le_to_field(hash.as_ref());
|
||||
// Hashes arbitrary signal to the underlying prime field
|
||||
pub fn hash_to_field(signal: &[u8]) -> Fr {
|
||||
// We hash the input signal using Keccak256
|
||||
// (note that a bigger curve order might require a bigger hash blocksize)
|
||||
let mut hash = [0; 32];
|
||||
let mut hasher = Keccak::v256();
|
||||
hasher.update(signal);
|
||||
hasher.finalize(&mut hash);
|
||||
|
||||
// We export the hash as a field element
|
||||
let (el, _) = bytes_le_to_fr(hash.as_ref());
|
||||
el
|
||||
}
|
||||
|
||||
/// Generates the nullifier hash
|
||||
#[must_use]
|
||||
pub fn generate_nullifier_hash(identity: &Identity, external_nullifier: Field) -> Field {
|
||||
poseidon_hash(&[external_nullifier, identity.nullifier])
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// Proof data structure and utility functions
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
// Matches the private G1Tup type in ark-circom.
|
||||
pub type G1 = (U256, U256);
|
||||
|
||||
// Matches the private G2Tup type in ark-circom.
|
||||
pub type G2 = ([U256; 2], [U256; 2]);
|
||||
|
||||
/// Wrap a proof object so we have serde support
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Proof(G1, G2, G1);
|
||||
|
||||
impl From<ArkProof<Bn<Parameters>>> for Proof {
|
||||
fn from(proof: ArkProof<Bn<Parameters>>) -> Self {
|
||||
let proof = ark_circom::ethereum::Proof::from(proof);
|
||||
let (a, b, c) = proof.as_tuple();
|
||||
Self(a, b, c)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Proof> for ArkProof<Bn<Parameters>> {
|
||||
fn from(proof: Proof) -> Self {
|
||||
let eth_proof = ark_circom::ethereum::Proof {
|
||||
a: ark_circom::ethereum::G1 {
|
||||
x: proof.0 .0,
|
||||
y: proof.0 .1,
|
||||
},
|
||||
#[rustfmt::skip] // Rustfmt inserts some confusing spaces
|
||||
b: ark_circom::ethereum::G2 {
|
||||
// The order of coefficients is flipped.
|
||||
x: [proof.1.0[1], proof.1.0[0]],
|
||||
y: [proof.1.1[1], proof.1.1[0]],
|
||||
},
|
||||
c: ark_circom::ethereum::G1 {
|
||||
x: proof.2 .0,
|
||||
y: proof.2 .1,
|
||||
},
|
||||
};
|
||||
eth_proof.into()
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// zkSNARK utility functions
|
||||
///////////////////////////////////////////////////////
|
||||
|
@ -429,16 +379,16 @@ pub enum ProofError {
|
|||
/// Returns a [`ProofError`] if proving fails.
|
||||
pub fn generate_proof(
|
||||
witness_calculator: &Mutex<WitnessCalculator>,
|
||||
proving_key: &(ProvingKey<Bn254>, ConstraintMatrices<Fr>),
|
||||
proving_key: &(ProvingKey<Curve>, ConstraintMatrices<Fr>),
|
||||
rln_witness: &RLNWitnessInput,
|
||||
) -> Result<Proof, ProofError> {
|
||||
) -> Result<ArkProof<Curve>, ProofError> {
|
||||
// We confert the path indexes to field elements
|
||||
// TODO: check if necessary
|
||||
let mut path_elements = Vec::new();
|
||||
rln_witness
|
||||
.path_elements
|
||||
.iter()
|
||||
.for_each(|v| path_elements.push(BigInt::from(*v)));
|
||||
.for_each(|v| path_elements.push(to_bigint(v)));
|
||||
|
||||
let mut identity_path_index = Vec::new();
|
||||
rln_witness
|
||||
|
@ -449,15 +399,15 @@ pub fn generate_proof(
|
|||
let inputs = [
|
||||
(
|
||||
"identity_secret",
|
||||
vec![BigInt::from(rln_witness.identity_secret)],
|
||||
vec![to_bigint(&rln_witness.identity_secret)],
|
||||
),
|
||||
("path_elements", path_elements),
|
||||
("identity_path_index", identity_path_index),
|
||||
("x", vec![BigInt::from(rln_witness.x)]),
|
||||
("epoch", vec![BigInt::from(rln_witness.epoch)]),
|
||||
("x", vec![to_bigint(&rln_witness.x)]),
|
||||
("epoch", vec![to_bigint(&rln_witness.epoch)]),
|
||||
(
|
||||
"rln_identifier",
|
||||
vec![BigInt::from(rln_witness.rln_identifier)],
|
||||
vec![to_bigint(&rln_witness.rln_identifier)],
|
||||
),
|
||||
];
|
||||
let inputs = inputs
|
||||
|
@ -469,18 +419,18 @@ pub fn generate_proof(
|
|||
let full_assignment = witness_calculator
|
||||
.lock()
|
||||
.expect("witness_calculator mutex should not get poisoned")
|
||||
.calculate_witness_element::<Bn254, _>(inputs, false)
|
||||
.calculate_witness_element::<Curve, _>(inputs, false)
|
||||
.map_err(ProofError::WitnessError)?;
|
||||
|
||||
println!("witness generation took: {:.2?}", now.elapsed());
|
||||
|
||||
// Random Values
|
||||
let mut rng = thread_rng();
|
||||
let r = ark_bn254::Fr::rand(&mut rng);
|
||||
let s = ark_bn254::Fr::rand(&mut rng);
|
||||
let r = Fr::rand(&mut rng);
|
||||
let s = Fr::rand(&mut rng);
|
||||
|
||||
let now = Instant::now();
|
||||
let ark_proof = create_proof_with_reduction_and_matrices::<_, CircomReduction>(
|
||||
let proof = create_proof_with_reduction_and_matrices::<_, CircomReduction>(
|
||||
&proving_key.0,
|
||||
r,
|
||||
s,
|
||||
|
@ -489,7 +439,6 @@ pub fn generate_proof(
|
|||
proving_key.1.num_constraints,
|
||||
full_assignment.as_slice(),
|
||||
)?;
|
||||
let proof = ark_proof.into();
|
||||
println!("proof generation took: {:.2?}", now.elapsed());
|
||||
|
||||
Ok(proof)
|
||||
|
@ -502,8 +451,8 @@ pub fn generate_proof(
|
|||
/// Returns a [`ProofError`] if verifying fails. Verification failure does not
|
||||
/// necessarily mean the proof is incorrect.
|
||||
pub fn verify_proof(
|
||||
verifying_key: &VerifyingKey<Bn254>,
|
||||
proof: &Proof,
|
||||
verifying_key: &VerifyingKey<Curve>,
|
||||
proof: &ArkProof<Curve>,
|
||||
proof_values: &RLNProofValues,
|
||||
) -> Result<bool, ProofError> {
|
||||
// We re-arrange proof-values according to the circuit specification
|
||||
|
@ -518,9 +467,9 @@ pub fn verify_proof(
|
|||
|
||||
// Check that the proof is valid
|
||||
let pvk = prepare_verifying_key(verifying_key);
|
||||
let pr: ArkProof<Bn254> = (*proof).into();
|
||||
//let pr: ArkProof<Curve> = (*proof).into();
|
||||
let now = Instant::now();
|
||||
let verified = ark_verify_proof(&pvk, &pr, &vec_to_fr(&inputs))?;
|
||||
let verified = ark_verify_proof(&pvk, &proof, &inputs)?;
|
||||
println!("verify took: {:.2?}", now.elapsed());
|
||||
|
||||
Ok(verified)
|
||||
|
|
|
@ -1,42 +1,29 @@
|
|||
/// This is the main public API for RLN module. It is used by the FFI, and should be
|
||||
/// used by tests etc as well
|
||||
///
|
||||
use ark_bn254::{Bn254, Fr};
|
||||
use ark_circom::{CircomBuilder, CircomCircuit, CircomConfig, WitnessCalculator};
|
||||
use ark_circom::WitnessCalculator;
|
||||
use ark_groth16::Proof as ArkProof;
|
||||
use ark_groth16::{ProvingKey, VerifyingKey};
|
||||
use ark_relations::r1cs::ConstraintMatrices;
|
||||
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
|
||||
use ark_std::{rand::thread_rng, str::FromStr, UniformRand};
|
||||
use num_bigint::BigInt;
|
||||
use semaphore::{identity::Identity, Field};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json;
|
||||
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, Write};
|
||||
use std::default::Default;
|
||||
use std::io::Cursor;
|
||||
use std::io::{self, Error, ErrorKind, Result}; //default read/write
|
||||
use std::option::Option;
|
||||
use std::io::{self, Result};
|
||||
use std::sync::Mutex;
|
||||
|
||||
// For the ToBytes implementation of groth16::Proof
|
||||
use ark_ec::bn::Bn;
|
||||
use ark_ff::bytes::ToBytes;
|
||||
use ark_serialize::{Read, Write};
|
||||
|
||||
use crate::circuit::{CIRCOM, TEST_RESOURCES_FOLDER, TEST_TREE_HEIGHT, VK, ZKEY};
|
||||
use crate::circuit::{Curve, Fr, CIRCOM, TEST_RESOURCES_FOLDER, TEST_TREE_HEIGHT, VK, ZKEY};
|
||||
use crate::poseidon_tree::PoseidonTree;
|
||||
use crate::protocol::{self, *};
|
||||
use crate::protocol::*;
|
||||
use crate::utils::*;
|
||||
|
||||
// Application specific RLN identifier
|
||||
pub const RLN_IDENTIFIER: &[u8] = b"zerokit/rln/010203040506070809";
|
||||
|
||||
// TODO Add Engine here? i.e. <E: Engine> not <Bn254>
|
||||
// TODO Add Engine here? i.e. <E: Engine> not <Curve>
|
||||
// TODO Assuming we want to use IncrementalMerkleTree, figure out type/trait conversions
|
||||
pub struct RLN<'a> {
|
||||
witness_calculator: &'a Mutex<WitnessCalculator>,
|
||||
proving_key: Result<(ProvingKey<Bn254>, ConstraintMatrices<Fr>)>,
|
||||
verification_key: Result<VerifyingKey<Bn254>>,
|
||||
proving_key: Result<(ProvingKey<Curve>, ConstraintMatrices<Fr>)>,
|
||||
verification_key: Result<VerifyingKey<Curve>>,
|
||||
tree: PoseidonTree,
|
||||
resources_folder: String,
|
||||
}
|
||||
|
@ -82,7 +69,7 @@ impl RLN<'_> {
|
|||
input_data.read_to_end(&mut leaf_byte)?;
|
||||
|
||||
// We set the leaf at input index
|
||||
let (leaf, _) = bytes_le_to_field(&leaf_byte);
|
||||
let (leaf, _) = bytes_le_to_fr(&leaf_byte);
|
||||
self.tree.set(index, leaf)?;
|
||||
|
||||
Ok(())
|
||||
|
@ -94,7 +81,7 @@ impl RLN<'_> {
|
|||
let mut leaves_byte: Vec<u8> = Vec::new();
|
||||
input_data.read_to_end(&mut leaves_byte)?;
|
||||
|
||||
let (leaves, _) = bytes_le_to_vec_field(&leaves_byte);
|
||||
let (leaves, _) = bytes_le_to_vec_fr(&leaves_byte);
|
||||
|
||||
// We set the leaves
|
||||
for (i, leaf) in leaves.iter().enumerate() {
|
||||
|
@ -111,7 +98,7 @@ impl RLN<'_> {
|
|||
input_data.read_to_end(&mut leaf_byte)?;
|
||||
|
||||
// We set the leaf at input index
|
||||
let (leaf, _) = bytes_le_to_field(&leaf_byte);
|
||||
let (leaf, _) = bytes_le_to_fr(&leaf_byte);
|
||||
self.tree.update_next(leaf)?;
|
||||
|
||||
Ok(())
|
||||
|
@ -127,7 +114,7 @@ impl RLN<'_> {
|
|||
/// * `root` is a scalar field element in 32 bytes
|
||||
pub fn get_root<W: Write>(&self, mut output_data: W) -> io::Result<()> {
|
||||
let root = self.tree.root();
|
||||
output_data.write_all(&field_to_bytes_le(&root))?;
|
||||
output_data.write_all(&fr_to_bytes_le(&root))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -139,7 +126,7 @@ impl RLN<'_> {
|
|||
let path_elements = merkle_proof.get_path_elements();
|
||||
let identity_path_index = merkle_proof.get_path_index();
|
||||
|
||||
output_data.write_all(&vec_field_to_bytes_le(&path_elements))?;
|
||||
output_data.write_all(&vec_fr_to_bytes_le(&path_elements))?;
|
||||
output_data.write_all(&vec_u8_to_bytes_le(&identity_path_index))?;
|
||||
|
||||
Ok(())
|
||||
|
@ -172,20 +159,18 @@ impl RLN<'_> {
|
|||
.unwrap();
|
||||
|
||||
// Note: we export a serialization of ark-groth16::Proof not semaphore::Proof
|
||||
ArkProof::from(proof).serialize(&mut output_data).unwrap();
|
||||
proof.serialize(&mut output_data).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn verify<R: Read>(&self, mut input_data: R) -> io::Result<bool> {
|
||||
// Input data is serialized for Bn254 as:
|
||||
// Input data is serialized for Curve as:
|
||||
// serialized_proof (compressed, 4*32 bytes) || serialized_proof_values (6*32 bytes)
|
||||
let mut input_byte: Vec<u8> = Vec::new();
|
||||
input_data.read_to_end(&mut input_byte)?;
|
||||
let proof: protocol::Proof =
|
||||
ArkProof::deserialize(&mut Cursor::new(&input_byte[..128].to_vec()))
|
||||
.unwrap()
|
||||
.into();
|
||||
let proof = ArkProof::deserialize(&mut Cursor::new(&input_byte[..128].to_vec())).unwrap();
|
||||
|
||||
let (proof_values, _) = deserialize_proof_values(&input_byte[128..].to_vec());
|
||||
|
||||
let verified = verify_proof(
|
||||
|
@ -221,22 +206,19 @@ impl RLN<'_> {
|
|||
|
||||
// Note: we export a serialization of ark-groth16::Proof not semaphore::Proof
|
||||
// This proof is compressed, i.e. 128 bytes long
|
||||
ArkProof::from(proof).serialize(&mut output_data).unwrap();
|
||||
proof.serialize(&mut output_data).unwrap();
|
||||
output_data.write_all(&serialize_proof_values(&proof_values))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Input data is serialized for Bn254 as:
|
||||
// Input data is serialized for Curve as:
|
||||
// [ proof<128> | share_y<32> | nullifier<32> | root<32> | epoch<32> | share_x<32> | rln_identifier<32> | signal_len<8> | signal<var> ]
|
||||
pub fn verify_rln_proof<R: Read>(&self, mut input_data: R) -> io::Result<bool> {
|
||||
let mut serialized: Vec<u8> = Vec::new();
|
||||
input_data.read_to_end(&mut serialized)?;
|
||||
let mut all_read = 0;
|
||||
let proof: protocol::Proof =
|
||||
ArkProof::deserialize(&mut Cursor::new(&serialized[..128].to_vec()))
|
||||
.unwrap()
|
||||
.into();
|
||||
let proof = ArkProof::deserialize(&mut Cursor::new(&serialized[..128].to_vec())).unwrap();
|
||||
all_read += 128;
|
||||
let (proof_values, read) = deserialize_proof_values(&serialized[all_read..].to_vec());
|
||||
all_read += read;
|
||||
|
@ -270,8 +252,8 @@ impl RLN<'_> {
|
|||
|
||||
pub fn key_gen<W: Write>(&self, mut output_data: W) -> io::Result<()> {
|
||||
let (id_key, id_commitment_key) = keygen();
|
||||
output_data.write_all(&field_to_bytes_le(&id_key))?;
|
||||
output_data.write_all(&field_to_bytes_le(&id_commitment_key))?;
|
||||
output_data.write_all(&fr_to_bytes_le(&id_key))?;
|
||||
output_data.write_all(&fr_to_bytes_le(&id_commitment_key))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -281,7 +263,7 @@ impl RLN<'_> {
|
|||
input_data.read_to_end(&mut serialized)?;
|
||||
|
||||
let hash = hash_to_field(&serialized);
|
||||
output_data.write_all(&field_to_bytes_le(&hash))?;
|
||||
output_data.write_all(&fr_to_bytes_le(&hash))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -298,9 +280,9 @@ impl Default for RLN<'_> {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use ark_std::str::FromStr;
|
||||
use crate::poseidon_tree::poseidon_hash;
|
||||
use ark_std::{rand::thread_rng, UniformRand};
|
||||
use rand::Rng;
|
||||
use semaphore::poseidon_hash;
|
||||
|
||||
#[test]
|
||||
// We test merkle batch Merkle tree additions
|
||||
|
@ -309,10 +291,10 @@ mod test {
|
|||
let no_of_leaves = 256;
|
||||
|
||||
// We generate a vector of random leaves
|
||||
let mut leaves: Vec<Field> = Vec::new();
|
||||
let mut leaves: Vec<Fr> = Vec::new();
|
||||
let mut rng = thread_rng();
|
||||
for _ in 0..no_of_leaves {
|
||||
leaves.push(to_field(&Fr::rand(&mut rng)));
|
||||
leaves.push(Fr::rand(&mut rng));
|
||||
}
|
||||
|
||||
// We create a new tree
|
||||
|
@ -324,21 +306,21 @@ mod test {
|
|||
// We check if the number of leaves set is consistent
|
||||
assert_eq!(rln.tree.leaves_set(), i);
|
||||
|
||||
let mut buffer = Cursor::new(field_to_bytes_le(&leaf));
|
||||
let mut buffer = Cursor::new(fr_to_bytes_le(&leaf));
|
||||
rln.set_leaf(i, &mut buffer).unwrap();
|
||||
}
|
||||
|
||||
// We get the root of the tree obtained adding one leaf per time
|
||||
let mut buffer = Cursor::new(Vec::<u8>::new());
|
||||
rln.get_root(&mut buffer).unwrap();
|
||||
let (root_single, _) = bytes_le_to_field(&buffer.into_inner());
|
||||
let (root_single, _) = bytes_le_to_fr(&buffer.into_inner());
|
||||
|
||||
// We reset the tree to default
|
||||
rln.set_tree(tree_height).unwrap();
|
||||
|
||||
// We add leaves one by one using the internal index (new leaves goes in next available position)
|
||||
for leaf in &leaves {
|
||||
let mut buffer = Cursor::new(field_to_bytes_le(&leaf));
|
||||
let mut buffer = Cursor::new(fr_to_bytes_le(&leaf));
|
||||
rln.set_next_leaf(&mut buffer).unwrap();
|
||||
}
|
||||
|
||||
|
@ -348,7 +330,7 @@ mod test {
|
|||
// We get the root of the tree obtained adding leaves using the internal index
|
||||
let mut buffer = Cursor::new(Vec::<u8>::new());
|
||||
rln.get_root(&mut buffer).unwrap();
|
||||
let (root_next, _) = bytes_le_to_field(&buffer.into_inner());
|
||||
let (root_next, _) = bytes_le_to_fr(&buffer.into_inner());
|
||||
|
||||
assert_eq!(root_single, root_next);
|
||||
|
||||
|
@ -356,7 +338,7 @@ mod test {
|
|||
rln.set_tree(tree_height).unwrap();
|
||||
|
||||
// We add leaves in a batch into the tree
|
||||
let mut buffer = Cursor::new(vec_field_to_bytes_le(&leaves));
|
||||
let mut buffer = Cursor::new(vec_fr_to_bytes_le(&leaves));
|
||||
rln.set_leaves(&mut buffer).unwrap();
|
||||
|
||||
// We check if number of leaves set is consistent
|
||||
|
@ -365,7 +347,7 @@ mod test {
|
|||
// We get the root of the tree obtained adding leaves in batch
|
||||
let mut buffer = Cursor::new(Vec::<u8>::new());
|
||||
rln.get_root(&mut buffer).unwrap();
|
||||
let (root_batch, _) = bytes_le_to_field(&buffer.into_inner());
|
||||
let (root_batch, _) = bytes_le_to_fr(&buffer.into_inner());
|
||||
|
||||
assert_eq!(root_single, root_batch);
|
||||
|
||||
|
@ -380,14 +362,14 @@ mod test {
|
|||
|
||||
let mut buffer = Cursor::new(Vec::<u8>::new());
|
||||
rln.get_root(&mut buffer).unwrap();
|
||||
let (root_delete, _) = bytes_le_to_field(&buffer.into_inner());
|
||||
let (root_delete, _) = bytes_le_to_fr(&buffer.into_inner());
|
||||
|
||||
// We reset the tree to default
|
||||
rln.set_tree(tree_height).unwrap();
|
||||
|
||||
let mut buffer = Cursor::new(Vec::<u8>::new());
|
||||
rln.get_root(&mut buffer).unwrap();
|
||||
let (root_empty, _) = bytes_le_to_field(&buffer.into_inner());
|
||||
let (root_empty, _) = bytes_le_to_fr(&buffer.into_inner());
|
||||
|
||||
assert_eq!(root_delete, root_empty);
|
||||
}
|
||||
|
@ -402,43 +384,41 @@ mod test {
|
|||
let mut rln = RLN::new(tree_height, input_buffer);
|
||||
|
||||
// generate identity
|
||||
// We follow zk-kit approach for identity generation
|
||||
let id = Identity::from_seed(b"test-merkle-proof");
|
||||
let identity_secret = poseidon_hash(&vec![id.trapdoor, id.nullifier]);
|
||||
let identity_secret = hash_to_field(b"test-merkle-proof");
|
||||
let id_commitment = poseidon_hash(&vec![identity_secret]);
|
||||
|
||||
// We pass id_commitment as Read buffer to RLN's set_leaf
|
||||
let mut buffer = Cursor::new(field_to_bytes_le(&id_commitment));
|
||||
let mut buffer = Cursor::new(fr_to_bytes_le(&id_commitment));
|
||||
rln.set_leaf(leaf_index, &mut buffer).unwrap();
|
||||
|
||||
// We check correct computation of the root
|
||||
let mut buffer = Cursor::new(Vec::<u8>::new());
|
||||
rln.get_root(&mut buffer).unwrap();
|
||||
let (root, _) = bytes_le_to_field(&buffer.into_inner());
|
||||
let (root, _) = bytes_le_to_fr(&buffer.into_inner());
|
||||
|
||||
if TEST_TREE_HEIGHT == 15 {
|
||||
assert_eq!(
|
||||
root,
|
||||
Field::from_str(
|
||||
"0x27401a4559ce263630907ce3b77c570649e28ede22d2a7f5296839627a16e870"
|
||||
str_to_fr(
|
||||
"0x1984f2e01184aef5cb974640898a5f5c25556554e2b06d99d4841badb8b198cd",
|
||||
16
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
} else if TEST_TREE_HEIGHT == 19 {
|
||||
assert_eq!(
|
||||
root,
|
||||
Field::from_str(
|
||||
"0x302920b5e5af8bf5f4bf32995f1ac5933d9a4b6f74803fdde84b8b9a761a2991"
|
||||
str_to_fr(
|
||||
"0x219ceb53f2b1b7a6cf74e80d50d44d68ecb4a53c6cc65b25593c8d56343fb1fe",
|
||||
16
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
} else if TEST_TREE_HEIGHT == 20 {
|
||||
assert_eq!(
|
||||
root,
|
||||
Field::from_str(
|
||||
"0x0c33d9be0ca0dd96c64d92107886de4235108e0fee203bb044ac4ee210a3dfea"
|
||||
str_to_fr(
|
||||
"0x21947ffd0bce0c385f876e7c97d6a42eec5b1fe935aab2f01c1f8a8cbcc356d2",
|
||||
16
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -447,41 +427,71 @@ mod test {
|
|||
rln.get_proof(leaf_index, &mut buffer).unwrap();
|
||||
|
||||
let buffer_inner = buffer.into_inner();
|
||||
let (path_elements, read) = bytes_le_to_vec_field(&buffer_inner);
|
||||
let (path_elements, read) = bytes_le_to_vec_fr(&buffer_inner);
|
||||
let (identity_path_index, _) = bytes_le_to_vec_u8(&buffer_inner[read..].to_vec());
|
||||
|
||||
// We check correct computation of the path and indexes
|
||||
let mut expected_path_elements = vec![
|
||||
Field::from_str("0x0000000000000000000000000000000000000000000000000000000000000000")
|
||||
.unwrap(),
|
||||
Field::from_str("0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864")
|
||||
.unwrap(),
|
||||
Field::from_str("0x1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1")
|
||||
.unwrap(),
|
||||
Field::from_str("0x18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238")
|
||||
.unwrap(),
|
||||
Field::from_str("0x07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a")
|
||||
.unwrap(),
|
||||
Field::from_str("0x2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55")
|
||||
.unwrap(),
|
||||
Field::from_str("0x2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78")
|
||||
.unwrap(),
|
||||
Field::from_str("0x078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d")
|
||||
.unwrap(),
|
||||
Field::from_str("0x2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61")
|
||||
.unwrap(),
|
||||
Field::from_str("0x0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747")
|
||||
.unwrap(),
|
||||
Field::from_str("0x1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2")
|
||||
.unwrap(),
|
||||
Field::from_str("0x1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636")
|
||||
.unwrap(),
|
||||
Field::from_str("0x2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a")
|
||||
.unwrap(),
|
||||
Field::from_str("0x14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0")
|
||||
.unwrap(),
|
||||
Field::from_str("0x190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c")
|
||||
.unwrap(),
|
||||
str_to_fr(
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0",
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c",
|
||||
16,
|
||||
),
|
||||
];
|
||||
|
||||
let mut expected_identity_path_index: Vec<u8> =
|
||||
|
@ -490,31 +500,31 @@ mod test {
|
|||
// We add the remaining elements for the case TEST_TREE_HEIGHT = 20
|
||||
if TEST_TREE_HEIGHT == 19 || TEST_TREE_HEIGHT == 20 {
|
||||
expected_path_elements.append(&mut vec![
|
||||
Field::from_str(
|
||||
str_to_fr(
|
||||
"0x22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92",
|
||||
)
|
||||
.unwrap(),
|
||||
Field::from_str(
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323",
|
||||
)
|
||||
.unwrap(),
|
||||
Field::from_str(
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992",
|
||||
)
|
||||
.unwrap(),
|
||||
Field::from_str(
|
||||
16,
|
||||
),
|
||||
str_to_fr(
|
||||
"0x0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f",
|
||||
)
|
||||
.unwrap(),
|
||||
16,
|
||||
),
|
||||
]);
|
||||
expected_identity_path_index.append(&mut vec![0, 0, 0, 0]);
|
||||
}
|
||||
|
||||
if TEST_TREE_HEIGHT == 20 {
|
||||
expected_path_elements.append(&mut vec![Field::from_str(
|
||||
expected_path_elements.append(&mut vec![str_to_fr(
|
||||
"0x1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca",
|
||||
)
|
||||
.unwrap()]);
|
||||
16,
|
||||
)]);
|
||||
expected_identity_path_index.append(&mut vec![0]);
|
||||
}
|
||||
|
||||
|
@ -547,9 +557,7 @@ mod test {
|
|||
let serialized_proof = output_buffer.into_inner();
|
||||
|
||||
// Before checking public verify API, we check that the (deserialized) proof generated by prove is actually valid
|
||||
let proof: protocol::Proof = ArkProof::deserialize(&mut Cursor::new(&serialized_proof))
|
||||
.unwrap()
|
||||
.into();
|
||||
let proof = ArkProof::deserialize(&mut Cursor::new(&serialized_proof)).unwrap();
|
||||
let verified = verify_proof(
|
||||
&rln.verification_key.as_ref().unwrap(),
|
||||
&proof,
|
||||
|
@ -576,10 +584,10 @@ mod test {
|
|||
let no_of_leaves = 256;
|
||||
|
||||
// We generate a vector of random leaves
|
||||
let mut leaves: Vec<Field> = Vec::new();
|
||||
let mut leaves: Vec<Fr> = Vec::new();
|
||||
let mut rng = thread_rng();
|
||||
for _ in 0..no_of_leaves {
|
||||
leaves.push(to_field(&Fr::rand(&mut rng)));
|
||||
leaves.push(Fr::rand(&mut rng));
|
||||
}
|
||||
|
||||
// We create a new RLN instance
|
||||
|
@ -587,7 +595,7 @@ mod test {
|
|||
let mut rln = RLN::new(tree_height, input_buffer);
|
||||
|
||||
// We add leaves in a batch into the tree
|
||||
let mut buffer = Cursor::new(vec_field_to_bytes_le(&leaves));
|
||||
let mut buffer = Cursor::new(vec_fr_to_bytes_le(&leaves));
|
||||
rln.set_leaves(&mut buffer).unwrap();
|
||||
|
||||
// Generate identity pair
|
||||
|
@ -595,7 +603,7 @@ mod test {
|
|||
|
||||
// We set as leaf id_commitment after storing its index
|
||||
let identity_index = u64::try_from(rln.tree.leaves_set()).unwrap();
|
||||
let mut buffer = Cursor::new(field_to_bytes_le(&id_commitment));
|
||||
let mut buffer = Cursor::new(fr_to_bytes_le(&id_commitment));
|
||||
rln.set_next_leaf(&mut buffer).unwrap();
|
||||
|
||||
// We generate a random signal
|
||||
|
@ -609,9 +617,9 @@ mod test {
|
|||
// We prepare input for generate_rln_proof API
|
||||
// input_data is [ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal<var> ]
|
||||
let mut serialized: Vec<u8> = Vec::new();
|
||||
serialized.append(&mut field_to_bytes_le(&identity_secret));
|
||||
serialized.append(&mut fr_to_bytes_le(&identity_secret));
|
||||
serialized.append(&mut identity_index.to_le_bytes().to_vec());
|
||||
serialized.append(&mut field_to_bytes_le(&epoch));
|
||||
serialized.append(&mut fr_to_bytes_le(&epoch));
|
||||
serialized.append(&mut signal_len.to_le_bytes().to_vec());
|
||||
serialized.append(&mut signal.to_vec());
|
||||
|
||||
|
@ -647,7 +655,7 @@ mod test {
|
|||
|
||||
rln.hash(&mut input_buffer, &mut output_buffer).unwrap();
|
||||
let serialized_hash = output_buffer.into_inner();
|
||||
let (hash1, _) = bytes_le_to_field(&serialized_hash);
|
||||
let (hash1, _) = bytes_le_to_fr(&serialized_hash);
|
||||
|
||||
let hash2 = hash_to_field(&signal);
|
||||
|
||||
|
|
185
rln/src/utils.rs
185
rln/src/utils.rs
|
@ -1,11 +1,11 @@
|
|||
// This crate provides cross-module useful utilities (mainly type conversions) not necessarily specific to RLN
|
||||
|
||||
use ark_bn254::{Bn254, Fr, Parameters};
|
||||
use ark_ff::{BigInteger, Field as ArkField, FpParameters, PrimeField};
|
||||
use ark_std::str::FromStr;
|
||||
use ethers::core::utils::keccak256;
|
||||
use num_bigint::{BigInt, BigUint, ToBigInt};
|
||||
use semaphore::{identity::Identity, Field};
|
||||
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 {
|
||||
|
@ -15,36 +15,17 @@ pub fn modulus_bit_size() -> usize {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn to_bigint(el: &Fr) -> BigInt {
|
||||
let res: BigUint = (*el).try_into().unwrap();
|
||||
res.try_into().unwrap()
|
||||
}
|
||||
|
||||
pub fn fr_byte_size() -> usize {
|
||||
let mbs = modulus_bit_size();
|
||||
(mbs + 64 - (mbs % 64)) / 8
|
||||
}
|
||||
|
||||
pub fn to_fr(el: &Field) -> Fr {
|
||||
Fr::try_from(*el).unwrap()
|
||||
}
|
||||
|
||||
pub fn to_field(el: &Fr) -> Field {
|
||||
(*el).try_into().unwrap()
|
||||
}
|
||||
|
||||
pub fn vec_to_fr(v: &[Field]) -> Vec<Fr> {
|
||||
v.iter().map(|el| to_fr(el)).collect()
|
||||
}
|
||||
|
||||
pub fn vec_to_field(v: &[Fr]) -> Vec<Field> {
|
||||
v.iter().map(|el| to_field(el)).collect()
|
||||
}
|
||||
|
||||
pub fn vec_fr_to_field(input: &[Fr]) -> Vec<Field> {
|
||||
input.iter().map(|el| to_field(el)).collect()
|
||||
}
|
||||
|
||||
pub fn vec_field_to_fr(input: &[Field]) -> Vec<Fr> {
|
||||
input.iter().map(|el| to_fr(el)).collect()
|
||||
}
|
||||
|
||||
pub fn str_to_field(input: String, radix: i32) -> Field {
|
||||
pub fn str_to_fr(input: &str, radix: u32) -> Fr {
|
||||
assert!((radix == 10) || (radix == 16));
|
||||
|
||||
// We remove any quote present and we trim
|
||||
|
@ -53,15 +34,16 @@ pub fn str_to_field(input: String, radix: i32) -> Field {
|
|||
let input_clean = input_clean.trim();
|
||||
|
||||
if radix == 10 {
|
||||
Field::from_str(&format!(
|
||||
"{:01$x}",
|
||||
BigUint::from_str(input_clean).unwrap(),
|
||||
64
|
||||
))
|
||||
.unwrap()
|
||||
BigUint::from_str_radix(&input_clean, radix)
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap()
|
||||
} else {
|
||||
let input_clean = input_clean.replace("0x", "");
|
||||
Field::from_str(&format!("{:0>64}", &input_clean)).unwrap()
|
||||
BigUint::from_str_radix(&input_clean, radix)
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,16 +63,6 @@ pub fn bytes_be_to_fr(input: &[u8]) -> (Fr, usize) {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn bytes_le_to_field(input: &[u8]) -> (Field, usize) {
|
||||
let (fr_el, read) = bytes_le_to_fr(input);
|
||||
(to_field(&fr_el), read)
|
||||
}
|
||||
|
||||
pub fn bytes_be_to_field(input: &[u8]) -> (Field, usize) {
|
||||
let (fr_el, read) = bytes_be_to_fr(input);
|
||||
(to_field(&fr_el), read)
|
||||
}
|
||||
|
||||
pub fn fr_to_bytes_le(input: &Fr) -> Vec<u8> {
|
||||
let input_biguint: BigUint = (*input).into();
|
||||
let mut res = input_biguint.to_bytes_le();
|
||||
|
@ -112,14 +84,6 @@ pub fn fr_to_bytes_be(input: &Fr) -> Vec<u8> {
|
|||
res
|
||||
}
|
||||
|
||||
pub fn field_to_bytes_le(input: &Field) -> Vec<u8> {
|
||||
fr_to_bytes_le(&to_fr(input))
|
||||
}
|
||||
|
||||
pub fn field_to_bytes_be(input: &Field) -> Vec<u8> {
|
||||
fr_to_bytes_be(&to_fr(input))
|
||||
}
|
||||
|
||||
pub fn vec_fr_to_bytes_le(input: &[Fr]) -> Vec<u8> {
|
||||
let mut bytes: Vec<u8> = Vec::new();
|
||||
//We store the vector length
|
||||
|
@ -140,14 +104,6 @@ pub fn vec_fr_to_bytes_be(input: &[Fr]) -> Vec<u8> {
|
|||
bytes
|
||||
}
|
||||
|
||||
pub fn vec_field_to_bytes_le(input: &[Field]) -> Vec<u8> {
|
||||
vec_fr_to_bytes_le(&vec_field_to_fr(input))
|
||||
}
|
||||
|
||||
pub fn vec_field_to_bytes_be(input: &[Field]) -> Vec<u8> {
|
||||
vec_fr_to_bytes_be(&vec_field_to_fr(input))
|
||||
}
|
||||
|
||||
pub fn vec_u8_to_bytes_le(input: &[u8]) -> Vec<u8> {
|
||||
let mut bytes: Vec<u8> = Vec::new();
|
||||
//We store the vector length
|
||||
|
@ -223,6 +179,106 @@ 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)
|
||||
|
||||
pub fn fr_to_posfr(value: Fr) -> PosFr {
|
||||
let mut bytes = [0_u8; 32];
|
||||
let byte_vec = value.into_repr().to_bytes_be();
|
||||
bytes.copy_from_slice(&byte_vec[..]);
|
||||
let mut repr = <PosFr as ff::PrimeField>::Repr::default();
|
||||
repr.read_be(&bytes[..])
|
||||
.expect("read from correctly sized slice always succeeds");
|
||||
PosFr::from_repr(repr).expect("value is always in range")
|
||||
}
|
||||
|
||||
pub fn posfr_to_fr(value: PosFr) -> Fr {
|
||||
let mut bytes = [0u8; 32];
|
||||
value
|
||||
.into_repr()
|
||||
.write_be(&mut bytes[..])
|
||||
.expect("write to correctly sized slice always succeeds");
|
||||
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 {
|
||||
Fr::try_from(*el).unwrap()
|
||||
}
|
||||
|
||||
pub fn to_field(el: &Fr) -> Field {
|
||||
(*el).try_into().unwrap()
|
||||
}
|
||||
|
||||
pub fn vec_to_fr(v: &[Field]) -> Vec<Fr> {
|
||||
v.iter().map(|el| to_fr(el)).collect()
|
||||
}
|
||||
|
||||
pub fn vec_to_field(v: &[Fr]) -> Vec<Field> {
|
||||
v.iter().map(|el| to_field(el)).collect()
|
||||
}
|
||||
|
||||
pub fn vec_fr_to_field(input: &[Fr]) -> Vec<Field> {
|
||||
input.iter().map(|el| to_field(el)).collect()
|
||||
}
|
||||
|
||||
pub fn vec_field_to_fr(input: &[Field]) -> Vec<Fr> {
|
||||
input.iter().map(|el| to_fr(el)).collect()
|
||||
}
|
||||
|
||||
pub fn str_to_field(input: String, radix: i32) -> Field {
|
||||
assert!((radix == 10) || (radix == 16));
|
||||
|
||||
// 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();
|
||||
|
||||
if radix == 10 {
|
||||
Field::from_str(&format!(
|
||||
"{:01$x}",
|
||||
BigUint::from_str(input_clean).unwrap(),
|
||||
64
|
||||
))
|
||||
.unwrap()
|
||||
} else {
|
||||
let input_clean = input_clean.replace("0x", "");
|
||||
Field::from_str(&format!("{:0>64}", &input_clean)).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bytes_le_to_field(input: &[u8]) -> (Field, usize) {
|
||||
let (fr_el, read) = bytes_le_to_fr(input);
|
||||
(to_field(&fr_el), read)
|
||||
}
|
||||
|
||||
pub fn bytes_be_to_field(input: &[u8]) -> (Field, usize) {
|
||||
let (fr_el, read) = bytes_be_to_fr(input);
|
||||
(to_field(&fr_el), read)
|
||||
}
|
||||
|
||||
|
||||
pub fn field_to_bytes_le(input: &Field) -> Vec<u8> {
|
||||
fr_to_bytes_le(&to_fr(input))
|
||||
}
|
||||
|
||||
pub fn field_to_bytes_be(input: &Field) -> Vec<u8> {
|
||||
fr_to_bytes_be(&to_fr(input))
|
||||
}
|
||||
|
||||
|
||||
pub fn vec_field_to_bytes_le(input: &[Field]) -> Vec<u8> {
|
||||
vec_fr_to_bytes_le(&vec_field_to_fr(input))
|
||||
}
|
||||
|
||||
pub fn vec_field_to_bytes_be(input: &[Field]) -> Vec<u8> {
|
||||
vec_fr_to_bytes_be(&vec_field_to_fr(input))
|
||||
}
|
||||
|
||||
|
||||
pub fn bytes_le_to_vec_field(input: &[u8]) -> (Vec<Field>, usize) {
|
||||
let (vec_fr, read) = bytes_le_to_vec_fr(input);
|
||||
(vec_fr_to_field(&vec_fr), read)
|
||||
|
@ -250,3 +306,4 @@ pub fn div(a: &Field, b: &Field) -> Field {
|
|||
pub fn inv(a: &Field) -> Field {
|
||||
to_field(&(Fr::from(1) / to_fr(a)))
|
||||
}
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue