refactor(rln): improve circuit loading performances and tests fix (#26)

* refactor(rln): change witness generation logic to improve performance

* rln(refactor): - change circuit loading logic; - update tests with tree size 20
This commit is contained in:
G 2022-07-29 07:40:16 +02:00 committed by GitHub
parent 9ca9c227e4
commit c1db14dd7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 350 additions and 131 deletions

View File

@ -11,7 +11,7 @@ crate-type = ["cdylib", "rlib", "staticlib"]
[dependencies]
# WASM operations
# wasmer = { version = "2.0" }
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 }

View File

@ -8,34 +8,35 @@ use num_bigint::BigUint;
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::path::Path;
use std::str::FromStr;
use once_cell::sync::{Lazy, OnceCell};
use std::sync::Mutex;
use wasmer::{Module, Store};
const ZKEY_FILENAME: &str = "rln_final.zkey";
const VK_FILENAME: &str = "verifying_key.json";
const R1CS_FILENAME: &str = "rln.r1cs";
const WASM_FILENAME: &str = "rln.wasm";
// These parameters are used for tests
// Note that the circuit and keys in TEST_RESOURCES_FOLDER are compiled for Merkle trees of height 16 and 20 (including leaves level)
// Changing these parameters to other value than these pairs of defaults will cause zkSNARK proof verification to fail
// All tests should pass for TEST_TREE_HEIGHT = 16
// The following tests fails for TEST_TREE_HEIGHT = 20 : ffi::test::test_merkle_proof_ffi, public::test::test_merkle_proof, test::test_merkle_proof, test::test_witness_from_json
// TODO: tests containing hardcoded values for TEST_TREE_HEIGHT = 16 need to be extended for the case TEST_TREE_HEIGHT = 20 in order to pass
pub const TEST_TREE_HEIGHT: usize = 16;
pub const TEST_RESOURCES_FOLDER: &str = "./resources/tree_height_16/";
//pub const TEST_TREE_HEIGHT: usize = 20;
//pub const TEST_RESOURCES_FOLDER: &str = "./resources/tree_height_20/";
// Changing these parameters to other value than these twp defaults will cause zkSNARK proof verification to fail
//pub const TEST_TREE_HEIGHT: usize = 16;
//pub const TEST_RESOURCES_FOLDER: &str = "./resources/tree_height_16/";
pub const TEST_TREE_HEIGHT: usize = 20;
pub const TEST_RESOURCES_FOLDER: &str = "./resources/tree_height_20/";
#[allow(non_snake_case)]
pub fn ZKEY(resources_folder: &str) -> Result<ProvingKey<Bn254>> {
pub fn ZKEY(resources_folder: &str) -> Result<(ProvingKey<Bn254>, ConstraintMatrices<Fr>)> {
let zkey_path = format!("{resources_folder}{ZKEY_FILENAME}");
if Path::new(&zkey_path).exists() {
let mut file = File::open(&zkey_path).unwrap();
let (proving_key, _matrices) = read_zkey(&mut file).unwrap();
Ok(proving_key)
let proving_key_and_matrices = read_zkey(&mut file).unwrap();
Ok(proving_key_and_matrices)
} else {
Err(Error::new(ErrorKind::NotFound, "No proving key found!"))
}
@ -52,7 +53,8 @@ pub fn VK(resources_folder: &str) -> Result<VerifyingKey<Bn254>> {
verifying_key = vk_from_json(&vk_path);
Ok(verifying_key)
} else if Path::new(&zkey_path).exists() {
verifying_key = ZKEY(resources_folder).unwrap().vk;
let (proving_key, _matrices) = ZKEY(resources_folder).unwrap();
verifying_key = proving_key.vk;
Ok(verifying_key)
} else {
Err(Error::new(
@ -62,16 +64,30 @@ pub fn VK(resources_folder: &str) -> Result<VerifyingKey<Bn254>> {
}
}
#[allow(non_snake_case)]
pub fn CIRCOM(resources_folder: &str) -> Option<CircomBuilder<Bn254>> {
static WITNESS_CALCULATOR: OnceCell<Mutex<WitnessCalculator>> = OnceCell::new();
fn read_wasm(resources_folder: &str) -> Vec<u8> {
let wasm_path = format!("{resources_folder}{WASM_FILENAME}");
let r1cs_path = format!("{resources_folder}{R1CS_FILENAME}");
let mut wasm_file = File::open(&wasm_path).expect("no file found");
let metadata = std::fs::metadata(&wasm_path).expect("unable to read metadata");
let mut wasm_buffer = vec![0; metadata.len() as usize];
wasm_file
.read_exact(&mut wasm_buffer)
.expect("buffer overflow");
wasm_buffer
}
// Load the WASM and R1CS for witness and proof generation
let cfg = CircomConfig::<Bn254>::new(&wasm_path, &r1cs_path).unwrap();
// We build and return the circuit
Some(CircomBuilder::new(cfg))
#[allow(non_snake_case)]
pub fn CIRCOM(resources_folder: &str) -> &'static Mutex<WitnessCalculator> {
WITNESS_CALCULATOR.get_or_init(|| {
// We read the wasm file
let wasm_buffer = read_wasm(resources_folder);
let store = Store::default();
let module = Module::from_binary(&store, &wasm_buffer).expect("wasm should be valid");
let result =
WitnessCalculator::from_module(module).expect("Failed to create witness calculator");
Mutex::new(result)
})
}
// TODO: all the following implementations are taken from a public github project: find reference for them
@ -165,8 +181,6 @@ 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>) {
let zkey = ZKEY(resources_folder);
if zkey.is_ok() {
assert_eq!(zkey.unwrap().vk, verifying_key);
}
let (proving_key, _matrices) = ZKEY(resources_folder).unwrap();
assert_eq!(proving_key.vk, verifying_key);
}

View File

@ -255,6 +255,7 @@ mod test {
use serde::{Deserialize, Serialize};
use std::io::Cursor;
use std::mem::MaybeUninit;
use std::time::{Duration, Instant};
#[test]
// We test merkle batch Merkle tree additions
@ -371,8 +372,6 @@ mod test {
#[test]
// This test is similar to the one in lib, but uses only public C API
// This test contains hardcoded values!
// TODO: expand this test to work with tree_height = 20
fn test_merkle_proof_ffi() {
let tree_height = TEST_TREE_HEIGHT;
let leaf_index = 3;
@ -415,7 +414,7 @@ mod test {
let (identity_path_index, _) = bytes_le_to_vec_u8(&result_data[read..].to_vec());
// We check correct computation of the path and indexes
let expected_path_elements = vec![
let mut expected_path_elements = vec![
Field::from_str("0x0000000000000000000000000000000000000000000000000000000000000000")
.unwrap(),
Field::from_str("0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864")
@ -448,9 +447,32 @@ mod test {
.unwrap(),
];
let expected_identity_path_index: Vec<u8> =
let mut expected_identity_path_index: Vec<u8> =
vec![1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
// We add the remaining elements for the case TEST_TREE_HEIGHT = 20
if TEST_TREE_HEIGHT == 20 {
expected_path_elements.append(&mut vec![
Field::from_str(
"0x22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92",
)
.unwrap(),
Field::from_str(
"0x2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323",
)
.unwrap(),
Field::from_str(
"0x2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992",
)
.unwrap(),
Field::from_str(
"0x0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f",
)
.unwrap(),
]);
expected_identity_path_index.append(&mut vec![0, 0, 0, 0]);
}
assert_eq!(path_elements, expected_path_elements);
assert_eq!(identity_path_index, expected_identity_path_index);
@ -462,7 +484,7 @@ mod test {
}
#[test]
fn test_groth16_proof_ffi() {
fn test_100_groth16_proofs_ffi() {
let tree_height = TEST_TREE_HEIGHT;
// We create a RLN instance
@ -472,32 +494,54 @@ mod test {
assert!(success, "RLN object creation failed");
let rln_pointer = unsafe { &mut *rln_pointer.assume_init() };
// We generate random witness instances and relative proof values
let rln_witness = random_rln_witness(tree_height);
let proof_values = proof_values_from_witness(&rln_witness);
// We compute some benchmarks regarding proof and verify API calls
// Note that circuit loading requires some initial overhead.
// Once the circuit is loaded (i.e., when the RLN object is created), proof generation and verification times should be similar at each call.
let sample_size = 100;
let mut prove_time: u128 = 0;
let mut verify_time: u128 = 0;
// We prepare id_commitment and we set the leaf at provided index
let rln_witness_ser = serialize_witness(&rln_witness);
let input_buffer = &Buffer::from(rln_witness_ser.as_ref());
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
let success = prove(rln_pointer, input_buffer, output_buffer.as_mut_ptr());
assert!(success, "prove call failed");
let output_buffer = unsafe { output_buffer.assume_init() };
for _ in 0..sample_size {
// We generate random witness instances and relative proof values
let rln_witness = random_rln_witness(tree_height);
let proof_values = proof_values_from_witness(&rln_witness);
// We read the returned proof and we append proof values for verify
let serialized_proof = <&[u8]>::from(&output_buffer).to_vec();
let serialized_proof_values = serialize_proof_values(&proof_values);
let mut verify_data = Vec::<u8>::new();
verify_data.extend(&serialized_proof);
verify_data.extend(&serialized_proof_values);
// We prepare id_commitment and we set the leaf at provided index
let rln_witness_ser = serialize_witness(&rln_witness);
let input_buffer = &Buffer::from(rln_witness_ser.as_ref());
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
let now = Instant::now();
let success = prove(rln_pointer, input_buffer, output_buffer.as_mut_ptr());
prove_time += now.elapsed().as_nanos();
assert!(success, "prove call failed");
let output_buffer = unsafe { output_buffer.assume_init() };
// We prepare input proof values and we call verify
let input_buffer = &Buffer::from(verify_data.as_ref());
let mut proof_is_valid: bool = false;
let proof_is_valid_ptr = &mut proof_is_valid as *mut bool;
let success = verify(rln_pointer, input_buffer, proof_is_valid_ptr);
assert!(success, "verify call failed");
assert_eq!(proof_is_valid, true);
// We read the returned proof and we append proof values for verify
let serialized_proof = <&[u8]>::from(&output_buffer).to_vec();
let serialized_proof_values = serialize_proof_values(&proof_values);
let mut verify_data = Vec::<u8>::new();
verify_data.extend(&serialized_proof);
verify_data.extend(&serialized_proof_values);
// We prepare input proof values and we call verify
let input_buffer = &Buffer::from(verify_data.as_ref());
let mut proof_is_valid: bool = false;
let proof_is_valid_ptr = &mut proof_is_valid as *mut bool;
let now = Instant::now();
let success = verify(rln_pointer, input_buffer, proof_is_valid_ptr);
verify_time += now.elapsed().as_nanos();
assert!(success, "verify call failed");
assert_eq!(proof_is_valid, true);
}
println!(
"Average prove API call time: {:?}",
Duration::from_nanos((prove_time / sample_size).try_into().unwrap())
);
println!(
"Average verify API call time: {:?}",
Duration::from_nanos((verify_time / sample_size).try_into().unwrap())
);
}
#[test]

View File

@ -25,7 +25,7 @@ mod test {
use semaphore::{hash::Hash, identity::Identity, poseidon_hash, Field};
// Input generated with https://github.com/oskarth/zk-kit/commit/b6a872f7160c7c14e10a0ea40acab99cbb23c9a8
const WITNESS_JSON: &str = r#"
const WITNESS_JSON_16: &str = r#"
{
"identity_secret": "12825549237505733615964533204745049909430608936689388901883576945030025938736",
"path_elements": [
@ -68,10 +68,60 @@ mod test {
}
"#;
// Input generated with protocol::random_rln_witness
const WITNESS_JSON_20: &str = r#"
{
"identity_secret": "922538810348594125658702672067738675294669207539999802857585668079702330450",
"path_elements": [
"16059714054680148404543504061485737353203416489071538960876865983954285286166",
"3041470753871943901334053763207316028823782848445723460227667780327106380356",
"2557297527793326315072058421057853700096944625924483912548759909801348042183",
"6677578602456189582427063963562590713054668181987223110955234085327917303436",
"2250827150965576973906150764756422151438812678308727218463995574869267980301",
"1895457427602709606993445561553433669787657053834360973759981803464906070980",
"11033689991077061346803816826729204895841441316315304395980565540264104346466",
"18588752216879570844240300406954267039026327526134910835334500497981810174976",
"19346480964028499661277403659363466542857230928032088490855656809181891953123",
"21460193770370072688835316363068413651465631481105148051902686770759127189327",
"20906347653364838502964722817589315918082261023317339146393355650507243340078",
"13466599592974387800162739317046838825289754472645703919149409009404541432954",
"9617165663598957201253074168824246164494443748556931540348223968573884172285",
"6936463137584425684797785981770877165377386163416057257854261010817156666898",
"369902028235468424790098825415813437044876310542601948037281422841675126849",
"13510969869821080499683463562609720931680005714401083864659516045615497273644",
"2567921390740781421487331055530491683313154421589525170472201828596388395736",
"14360870889466292805403568662660511177232987619663547772298178013674025998478",
"4735344599616284973799984501493858013178071155960162022656706545116168334293"
],
"identity_path_index": [
1,
0,
1,
0,
1,
1,
0,
0,
1,
1,
1,
0,
0,
0,
1,
0,
1,
1,
0
],
"x": "6427050788896290028100534859169645070970780055911091444144195464808120686416",
"epoch": "0x2bd155d9f85c741044da6909d144f9cc5ce8e0d545a9ed4921b156e8b8569bab",
"rln_identifier": "2193983000213424579594329476781986065965849144986973472766961413131458022566"
}
"#;
#[test]
// We test Merkle Tree generation, proofs and verification
// This test contains hardcoded values!
// TODO: expand this test to work with tree_height = 20
fn test_merkle_proof() {
let tree_height = TEST_TREE_HEIGHT;
let leaf_index = 3;
@ -89,18 +139,32 @@ mod test {
// We check correct computation of the root
let root = tree.root();
assert_eq!(
root,
Field::from_str("0x27401a4559ce263630907ce3b77c570649e28ede22d2a7f5296839627a16e870")
if TEST_TREE_HEIGHT == 16 {
assert_eq!(
root,
Field::from_str(
"0x27401a4559ce263630907ce3b77c570649e28ede22d2a7f5296839627a16e870"
)
.unwrap()
);
);
} else if TEST_TREE_HEIGHT == 20 {
assert_eq!(
root,
Field::from_str(
"0x302920b5e5af8bf5f4bf32995f1ac5933d9a4b6f74803fdde84b8b9a761a2991"
)
.unwrap()
);
}
let merkle_proof = tree.proof(leaf_index).expect("proof should exist");
let path_elements = merkle_proof.get_path_elements();
let identity_path_index = merkle_proof.get_path_index();
// We check correct computation of the path and indexes
let expected_path_elements = vec![
// These values refers to TEST_TREE_HEIGHT == 16
let mut expected_path_elements = vec![
Field::from_str("0x0000000000000000000000000000000000000000000000000000000000000000")
.unwrap(),
Field::from_str("0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864")
@ -133,9 +197,32 @@ mod test {
.unwrap(),
];
let expected_identity_path_index: Vec<u8> =
let mut expected_identity_path_index: Vec<u8> =
vec![1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
// We add the remaining elements for the case TEST_TREE_HEIGHT = 20
if TEST_TREE_HEIGHT == 20 {
expected_path_elements.append(&mut vec![
Field::from_str(
"0x22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92",
)
.unwrap(),
Field::from_str(
"0x2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323",
)
.unwrap(),
Field::from_str(
"0x2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992",
)
.unwrap(),
Field::from_str(
"0x0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f",
)
.unwrap(),
]);
expected_identity_path_index.append(&mut vec![0, 0, 0, 0]);
}
assert_eq!(path_elements, expected_path_elements);
assert_eq!(identity_path_index, expected_identity_path_index);
@ -145,16 +232,22 @@ mod test {
#[test]
// We test a RLN proof generation and verification
// This test contains hardcoded values!
// TODO: expand this test to work with tree_height = 20
fn test_witness_from_json() {
// We generate all relevant keys
let proving_key = ZKEY(TEST_RESOURCES_FOLDER).unwrap();
let verification_key = VK(TEST_RESOURCES_FOLDER).unwrap();
let builder = CIRCOM(TEST_RESOURCES_FOLDER).unwrap();
let builder = CIRCOM(TEST_RESOURCES_FOLDER);
// We compute witness from the json input example
let rln_witness = rln_witness_from_json(WITNESS_JSON);
let mut witness_json: &str = "";
if TEST_TREE_HEIGHT == 16 {
witness_json = WITNESS_JSON_16;
} else if TEST_TREE_HEIGHT == 20 {
witness_json = WITNESS_JSON_20;
}
let rln_witness = rln_witness_from_json(witness_json);
// Let's generate a zkSNARK proof
let proof = generate_proof(builder, &proving_key, &rln_witness).unwrap();
@ -200,7 +293,7 @@ mod test {
// We generate all relevant keys
let proving_key = ZKEY(TEST_RESOURCES_FOLDER).unwrap();
let verification_key = VK(TEST_RESOURCES_FOLDER).unwrap();
let builder = CIRCOM(TEST_RESOURCES_FOLDER).unwrap();
let builder = CIRCOM(TEST_RESOURCES_FOLDER);
// Let's generate a zkSNARK proof
let proof = generate_proof(builder, &proving_key, &rln_witness).unwrap();
@ -216,7 +309,16 @@ mod test {
#[test]
fn test_serialization() {
// We test witness serialization
let rln_witness = rln_witness_from_json(WITNESS_JSON);
let mut witness_json: &str = "";
if TEST_TREE_HEIGHT == 16 {
witness_json = WITNESS_JSON_16;
} else if TEST_TREE_HEIGHT == 20 {
witness_json = WITNESS_JSON_20;
}
let rln_witness = rln_witness_from_json(witness_json);
let ser = serialize_witness(&rln_witness);
let (deser, _) = deserialize_witness(&ser);
assert_eq!(rln_witness, deser);

View File

@ -1,8 +1,8 @@
use crate::circuit::{VK, ZKEY};
use crate::circuit::{CIRCOM, VK, ZKEY};
use crate::merkle_tree::{self, Branch};
use crate::poseidon_tree::PoseidonHash;
use ark_bn254::{Bn254, Fr, Parameters};
use ark_circom::{read_zkey, CircomBuilder, CircomConfig, CircomReduction};
use ark_circom::{read_zkey, CircomBuilder, CircomConfig, CircomReduction, WitnessCalculator};
use ark_ec::bn::Bn;
use ark_ff::{bytes::ToBytes, Fp256, PrimeField};
use ark_groth16::{
@ -10,6 +10,7 @@ use ark_groth16::{
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};
@ -21,6 +22,7 @@ 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;
@ -433,50 +435,66 @@ pub enum ProofError {
///
/// Returns a [`ProofError`] if proving fails.
pub fn generate_proof(
mut builder: CircomBuilder<Bn254>,
proving_key: &ProvingKey<Bn254>,
witness_calculator: &Mutex<WitnessCalculator>,
proving_key: &(ProvingKey<Bn254>, ConstraintMatrices<Fr>),
rln_witness: &RLNWitnessInput,
) -> Result<Proof, ProofError> {
// We confert the path indexes to field elements
// TODO: check if necessary
let mut path_elements: Vec<BigInt> = Vec::new();
for v in rln_witness.path_elements.iter() {
path_elements.push(BigInt::from(*v));
}
let mut identity_path_index: Vec<BigInt> = Vec::new();
for v in rln_witness.identity_path_index.iter() {
identity_path_index.push(BigInt::from(*v));
}
let inputs = [
(
"identity_secret",
vec![BigInt::from(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)]),
(
"rln_identifier",
vec![BigInt::from(rln_witness.rln_identifier)],
),
];
let inputs = inputs
.into_iter()
.map(|(name, values)| (name.to_string(), values));
let now = Instant::now();
builder.push_input("identity_secret", BigInt::from(rln_witness.identity_secret));
for v in rln_witness.path_elements.iter() {
builder.push_input("path_elements", BigInt::from(*v));
}
for v in rln_witness.identity_path_index.iter() {
builder.push_input("identity_path_index", BigInt::from(*v));
}
builder.push_input("x", BigInt::from(rln_witness.x));
builder.push_input("epoch", BigInt::from(rln_witness.epoch));
builder.push_input("rln_identifier", BigInt::from(rln_witness.rln_identifier));
let circom = builder.build().unwrap();
// This can be checked against proof_values_from_witness
// Get the populated instance of the circuit with the witness
//let inputs = vec_to_field(circom.get_public_inputs().unwrap());
let full_assignment = witness_calculator
.lock()
.expect("witness_calculator mutex should not get poisoned")
.calculate_witness_element::<Bn254, _>(inputs, false)
.map_err(ProofError::WitnessError)?;
println!("witness generation took: {:.2?}", now.elapsed());
let now = Instant::now();
// Generate a random proof
// Random Values
let mut rng = thread_rng();
let r = ark_bn254::Fr::rand(&mut rng);
let s = ark_bn254::Fr::rand(&mut rng);
let ark_proof = create_random_proof_with_reduction::<_, _, _, CircomReduction>(
circom,
proving_key,
&mut rng,
)
.unwrap();
let now = Instant::now();
let ark_proof = create_proof_with_reduction_and_matrices::<_, CircomReduction>(
&proving_key.0,
r,
s,
&proving_key.1,
proving_key.1.num_instance_variables,
proving_key.1.num_constraints,
full_assignment.as_slice(),
)?;
let proof = ark_proof.into();
println!("proof generation took: {:.2?}", now.elapsed());
Ok(proof)
@ -506,7 +524,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 now = Instant::now();
let verified = ark_verify_proof(&pvk, &pr, &vec_to_fr(&inputs))?;
println!("verify took: {:.2?}", now.elapsed());
Ok(verified)
}

View File

@ -3,9 +3,10 @@ use crate::poseidon_tree::PoseidonTree;
/// used by tests etc as well
///
use ark_bn254::{Bn254, Fr};
use ark_circom::{CircomBuilder, CircomCircuit, CircomConfig};
use ark_circom::{CircomBuilder, CircomCircuit, CircomConfig, 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;
@ -16,6 +17,7 @@ use std::default::Default;
use std::io::Cursor;
use std::io::{self, Error, ErrorKind, Result}; //default read/write
use std::option::Option;
use std::sync::Mutex;
// For the ToBytes implementation of groth16::Proof
use ark_ec::bn::Bn;
@ -31,23 +33,23 @@ pub const RLN_IDENTIFIER: &[u8] = b"zerokit/rln/010203040506070809";
// TODO Add Engine here? i.e. <E: Engine> not <Bn254>
// TODO Assuming we want to use IncrementalMerkleTree, figure out type/trait conversions
pub struct RLN {
pub circom: Option<CircomBuilder<Bn254>>,
pub proving_key: Result<ProvingKey<Bn254>>,
pub verification_key: Result<VerifyingKey<Bn254>>,
pub tree: PoseidonTree,
pub resources_folder: String,
pub struct RLN<'a> {
witness_calculator: &'a Mutex<WitnessCalculator>,
proving_key: Result<(ProvingKey<Bn254>, ConstraintMatrices<Fr>)>,
verification_key: Result<VerifyingKey<Bn254>>,
tree: PoseidonTree,
resources_folder: String,
}
impl RLN {
pub fn new<R: Read>(tree_height: usize, mut input_data: R) -> RLN {
impl RLN<'_> {
pub fn new<R: Read>(tree_height: usize, mut input_data: R) -> RLN<'static> {
// We read input
let mut input: Vec<u8> = Vec::new();
input_data.read_to_end(&mut input).unwrap();
let resources_folder = String::from_utf8(input).expect("Found invalid UTF-8");
let circom = None::<CircomBuilder<Bn254>>; //CIRCOM();
let witness_calculator = CIRCOM(&resources_folder);
let proving_key = ZKEY(&resources_folder);
let verification_key = VK(&resources_folder);
@ -57,7 +59,7 @@ impl RLN {
let tree = PoseidonTree::new(tree_height, leaf);
RLN {
circom,
witness_calculator,
proving_key,
verification_key,
tree,
@ -163,12 +165,14 @@ impl RLN {
input_data.read_to_end(&mut serialized)?;
let (rln_witness, _) = deserialize_witness(&serialized);
if self.circom.is_none() {
self.circom = CIRCOM(&self.resources_folder);
/*
if self.witness_calculator.is_none() {
self.witness_calculator = CIRCOM(&self.resources_folder);
}
*/
let proof = generate_proof(
self.circom.as_ref().unwrap().clone(),
self.witness_calculator,
self.proving_key.as_ref().unwrap(),
&rln_witness,
)
@ -215,12 +219,14 @@ impl RLN {
let (rln_witness, _) = proof_inputs_to_rln_witness(&mut self.tree, &witness_byte);
let proof_values = proof_values_from_witness(&rln_witness);
if self.circom.is_none() {
self.circom = CIRCOM(&self.resources_folder);
/*
if self.witness_calculator.is_none() {
self.witness_calculator = CIRCOM(&self.resources_folder);
}
*/
let proof = generate_proof(
self.circom.as_ref().unwrap().clone(),
self.witness_calculator,
self.proving_key.as_ref().unwrap(),
&rln_witness,
)
@ -294,7 +300,7 @@ impl RLN {
}
}
impl Default for RLN {
impl Default for RLN<'_> {
fn default() -> Self {
let tree_height = TEST_TREE_HEIGHT;
let buffer = Cursor::new(TEST_RESOURCES_FOLDER);
@ -401,8 +407,6 @@ mod test {
#[test]
// This test is similar to the one in lib, but uses only public API
// This test contains hardcoded values!
// TODO: expand this test to work with tree_height = 20
fn test_merkle_proof() {
let tree_height = TEST_TREE_HEIGHT;
let leaf_index = 3;
@ -425,11 +429,23 @@ mod test {
rln.get_root(&mut buffer).unwrap();
let (root, _) = bytes_le_to_field(&buffer.into_inner());
assert_eq!(
root,
Field::from_str("0x27401a4559ce263630907ce3b77c570649e28ede22d2a7f5296839627a16e870")
if TEST_TREE_HEIGHT == 16 {
assert_eq!(
root,
Field::from_str(
"0x27401a4559ce263630907ce3b77c570649e28ede22d2a7f5296839627a16e870"
)
.unwrap()
);
);
} else if TEST_TREE_HEIGHT == 20 {
assert_eq!(
root,
Field::from_str(
"0x302920b5e5af8bf5f4bf32995f1ac5933d9a4b6f74803fdde84b8b9a761a2991"
)
.unwrap()
);
}
// We check correct computation of merkle proof
let mut buffer = Cursor::new(Vec::<u8>::new());
@ -440,7 +456,7 @@ mod test {
let (identity_path_index, _) = bytes_le_to_vec_u8(&buffer_inner[read..].to_vec());
// We check correct computation of the path and indexes
let expected_path_elements = vec![
let mut expected_path_elements = vec![
Field::from_str("0x0000000000000000000000000000000000000000000000000000000000000000")
.unwrap(),
Field::from_str("0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864")
@ -473,9 +489,32 @@ mod test {
.unwrap(),
];
let expected_identity_path_index: Vec<u8> =
let mut expected_identity_path_index: Vec<u8> =
vec![1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
// We add the remaining elements for the case TEST_TREE_HEIGHT = 20
if TEST_TREE_HEIGHT == 20 {
expected_path_elements.append(&mut vec![
Field::from_str(
"0x22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92",
)
.unwrap(),
Field::from_str(
"0x2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323",
)
.unwrap(),
Field::from_str(
"0x2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992",
)
.unwrap(),
Field::from_str(
"0x0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f",
)
.unwrap(),
]);
expected_identity_path_index.append(&mut vec![0, 0, 0, 0]);
}
assert_eq!(path_elements, expected_path_elements);
assert_eq!(identity_path_index, expected_identity_path_index);