diff --git a/rln/Cargo.toml b/rln/Cargo.toml index 7fbc791..7c47806 100644 --- a/rln/Cargo.toml +++ b/rln/Cargo.toml @@ -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 } diff --git a/rln/resources/tree_height_16/rln.r1cs b/rln/resources/tree_height_16/rln.r1cs deleted file mode 100644 index dad7d2a..0000000 Binary files a/rln/resources/tree_height_16/rln.r1cs and /dev/null differ diff --git a/rln/resources/tree_height_20/rln.r1cs b/rln/resources/tree_height_20/rln.r1cs deleted file mode 100644 index e009f2b..0000000 Binary files a/rln/resources/tree_height_20/rln.r1cs and /dev/null differ diff --git a/rln/src/circuit.rs b/rln/src/circuit.rs index 777e0c9..5209ea1 100644 --- a/rln/src/circuit.rs +++ b/rln/src/circuit.rs @@ -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> { +pub fn ZKEY(resources_folder: &str) -> Result<(ProvingKey, ConstraintMatrices)> { 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> { 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> { } } -#[allow(non_snake_case)] -pub fn CIRCOM(resources_folder: &str) -> Option> { +static WITNESS_CALCULATOR: OnceCell> = OnceCell::new(); + +fn read_wasm(resources_folder: &str) -> Vec { 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::::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 { + 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 { // Checks verification key to be correct with respect to proving key pub fn check_vk_from_zkey(resources_folder: &str, verifying_key: VerifyingKey) { - 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); } diff --git a/rln/src/ffi.rs b/rln/src/ffi.rs index 1423b24..8ffec64 100644 --- a/rln/src/ffi.rs +++ b/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 = + let mut expected_identity_path_index: Vec = 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::::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::::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::::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::::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] diff --git a/rln/src/lib.rs b/rln/src/lib.rs index ef14d95..a966547 100644 --- a/rln/src/lib.rs +++ b/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 = + let mut expected_identity_path_index: Vec = 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); diff --git a/rln/src/protocol.rs b/rln/src/protocol.rs index 2b07352..62f4875 100644 --- a/rln/src/protocol.rs +++ b/rln/src/protocol.rs @@ -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, - proving_key: &ProvingKey, + witness_calculator: &Mutex, + proving_key: &(ProvingKey, ConstraintMatrices), rln_witness: &RLNWitnessInput, ) -> Result { + // We confert the path indexes to field elements + // TODO: check if necessary + let mut path_elements: Vec = Vec::new(); + for v in rln_witness.path_elements.iter() { + path_elements.push(BigInt::from(*v)); + } + + let mut identity_path_index: Vec = 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::(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 = (*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) } diff --git a/rln/src/public.rs b/rln/src/public.rs index 2d82681..6aeee1f 100644 --- a/rln/src/public.rs +++ b/rln/src/public.rs @@ -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. not // TODO Assuming we want to use IncrementalMerkleTree, figure out type/trait conversions -pub struct RLN { - pub circom: Option>, - pub proving_key: Result>, - pub verification_key: Result>, - pub tree: PoseidonTree, - pub resources_folder: String, +pub struct RLN<'a> { + witness_calculator: &'a Mutex, + proving_key: Result<(ProvingKey, ConstraintMatrices)>, + verification_key: Result>, + tree: PoseidonTree, + resources_folder: String, } -impl RLN { - pub fn new(tree_height: usize, mut input_data: R) -> RLN { +impl RLN<'_> { + pub fn new(tree_height: usize, mut input_data: R) -> RLN<'static> { // We read input let mut input: Vec = 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::>; //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::::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 = + let mut expected_identity_path_index: Vec = 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);