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:
G 2022-09-14 11:48:48 +02:00 committed by GitHub
parent f5897eb752
commit a85454cfd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 676 additions and 629 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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())
);
}

View File

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

View File

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

View File

@ -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)))
}
*/