mirror of https://github.com/vacp2p/zerokit.git
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:
parent
9ca9c227e4
commit
c1db14dd7c
|
@ -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 }
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -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);
|
||||
}
|
||||
|
|
100
rln/src/ffi.rs
100
rln/src/ffi.rs
|
@ -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]
|
||||
|
|
132
rln/src/lib.rs
132
rln/src/lib.rs
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue