diff --git a/Cargo.lock b/Cargo.lock index 0885f80..6301484 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2340,6 +2340,7 @@ dependencies = [ "wasm-bindgen-test", "wasmer", "web-sys", + "zerokit_utils", ] [[package]] diff --git a/rln-wasm/Cargo.toml b/rln-wasm/Cargo.toml index 4685a2b..4af0016 100644 --- a/rln-wasm/Cargo.toml +++ b/rln-wasm/Cargo.toml @@ -11,7 +11,10 @@ crate-type = ["cdylib", "rlib"] default = ["console_error_panic_hook"] [dependencies] -rln = { path = "../rln", default-features = false, features = ["wasm"] } +rln = { path = "../rln", default-features = false, features = [ + "wasm", + "stateless", +] } num-bigint = { version = "0.4", default-features = false, features = [ "rand", "serde", @@ -29,6 +32,7 @@ serde_json = "1.0.85" # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for # code size when deploying. console_error_panic_hook = { version = "0.1.7", optional = true } +zerokit_utils = { version = "0.5.1", path = "../utils" } [dev-dependencies] wasm-bindgen-test = "0.3.13" diff --git a/rln-wasm/src/lib.rs b/rln-wasm/src/lib.rs index c6b0702..fe242d6 100644 --- a/rln-wasm/src/lib.rs +++ b/rln-wasm/src/lib.rs @@ -182,88 +182,15 @@ impl<'a> ProcessArg for &'a [u8] { #[allow(clippy::not_unsafe_ptr_arg_deref)] #[wasm_bindgen(js_name = newRLN)] pub fn wasm_new( - tree_height: usize, zkey: Uint8Array, vk: Uint8Array, ) -> Result<*mut RLNWrapper, String> { - let instance = RLN::new_with_params(tree_height, zkey.to_vec(), vk.to_vec()) + let instance = RLN::new_with_params(zkey.to_vec(), vk.to_vec()) .map_err(|err| format!("{:#?}", err))?; let wrapper = RLNWrapper { instance }; Ok(Box::into_raw(Box::new(wrapper))) } -#[allow(clippy::not_unsafe_ptr_arg_deref)] -#[wasm_bindgen(js_name = getSerializedRLNWitness)] -pub fn wasm_get_serialized_rln_witness( - ctx: *mut RLNWrapper, - input: Uint8Array, -) -> Result { - let rln_witness = call!(ctx, get_serialized_rln_witness, &input.to_vec()[..]) - .map_err(|err| format!("{:#?}", err))?; - Ok(Uint8Array::from(&rln_witness[..])) -} - -#[allow(clippy::not_unsafe_ptr_arg_deref)] -#[wasm_bindgen(js_name = insertMember)] -pub fn wasm_set_next_leaf(ctx: *mut RLNWrapper, input: Uint8Array) -> Result<(), String> { - call_with_error_msg!( - ctx, - set_next_leaf, - "could not insert member into merkle tree".to_string(), - &input.to_vec()[..] - ) -} - -#[allow(clippy::not_unsafe_ptr_arg_deref)] -#[wasm_bindgen(js_name = setLeavesFrom)] -pub fn wasm_set_leaves_from( - ctx: *mut RLNWrapper, - index: usize, - input: Uint8Array, -) -> Result<(), String> { - call_with_error_msg!( - ctx, - set_leaves_from, - "could not set multiple leaves".to_string(), - index, - &*input.to_vec() - ) -} - -#[allow(clippy::not_unsafe_ptr_arg_deref)] -#[wasm_bindgen(js_name = deleteLeaf)] -pub fn wasm_delete_leaf(ctx: *mut RLNWrapper, index: usize) -> Result<(), String> { - call_with_error_msg!(ctx, delete_leaf, "could not delete leaf".to_string(), index) -} - -#[allow(clippy::not_unsafe_ptr_arg_deref)] -#[wasm_bindgen(js_name = setMetadata)] -pub fn wasm_set_metadata(ctx: *mut RLNWrapper, input: Uint8Array) -> Result<(), String> { - call_with_error_msg!( - ctx, - set_metadata, - "could not set metadata".to_string(), - &*input.to_vec() - ) -} - -#[allow(clippy::not_unsafe_ptr_arg_deref)] -#[wasm_bindgen(js_name = getMetadata)] -pub fn wasm_get_metadata(ctx: *mut RLNWrapper) -> Result { - call_with_output_and_error_msg!(ctx, get_metadata, "could not get metadata".to_string()) -} - -#[allow(clippy::not_unsafe_ptr_arg_deref)] -#[wasm_bindgen(js_name = initTreeWithLeaves)] -pub fn wasm_init_tree_with_leaves(ctx: *mut RLNWrapper, input: Uint8Array) -> Result<(), String> { - call_with_error_msg!( - ctx, - init_tree_with_leaves, - "could not init merkle tree".to_string(), - &*input.to_vec() - ) -} - #[allow(clippy::not_unsafe_ptr_arg_deref)] #[wasm_bindgen(js_name = RLNWitnessToJson)] pub fn rln_witness_to_json( @@ -362,17 +289,6 @@ pub fn wasm_recover_id_secret( ) } -#[allow(clippy::not_unsafe_ptr_arg_deref)] -#[wasm_bindgen(js_name = verifyRLNProof)] -pub fn wasm_verify_rln_proof(ctx: *const RLNWrapper, proof: Uint8Array) -> Result { - call_bool_method_with_error_msg!( - ctx, - verify_rln_proof, - "error while verifying rln proof".to_string(), - &proof.to_vec()[..] - ) -} - #[allow(clippy::not_unsafe_ptr_arg_deref)] #[wasm_bindgen(js_name = verifyWithRoots)] pub fn wasm_verify_with_roots( @@ -389,12 +305,6 @@ pub fn wasm_verify_with_roots( ) } -#[allow(clippy::not_unsafe_ptr_arg_deref)] -#[wasm_bindgen(js_name = getRoot)] -pub fn wasm_get_root(ctx: *const RLNWrapper) -> Result { - call_with_output_and_error_msg!(ctx, get_root, "could not obtain root") -} - #[wasm_bindgen(js_name = hash)] pub fn wasm_hash(input: Uint8Array) -> Result { fn_call_with_output_and_error_msg!(hash, "could not generate hash", &input.to_vec()[..]) diff --git a/rln-wasm/tests/rln-wasm.rs b/rln-wasm/tests/rln-wasm.rs index 1c30707..5e08769 100644 --- a/rln-wasm/tests/rln-wasm.rs +++ b/rln-wasm/tests/rln-wasm.rs @@ -7,8 +7,13 @@ mod tests { use rln::hashers::{hash_to_field, poseidon_hash}; use rln::utils::{bytes_le_to_fr, fr_to_bytes_le, normalize_usize}; use rln_wasm::*; + use rln::poseidon_tree::PoseidonTree; + use rln::utils::vec_fr_to_bytes_le; use wasm_bindgen::{prelude::*, JsValue}; use wasm_bindgen_test::wasm_bindgen_test; + use zerokit_utils::merkle_tree::merkle_tree::ZerokitMerkleTree; + use zerokit_utils::ZerokitMerkleProof; + use rln::utils::vec_u8_to_bytes_le; #[wasm_bindgen(module = "src/utils.js")] extern "C" { @@ -30,7 +35,9 @@ mod tests { let vk = read_file(&vk_path).unwrap(); // Creating an instance of RLN - let rln_instance = wasm_new(tree_height, zkey, vk).unwrap(); + let rln_instance = wasm_new(zkey, vk).unwrap(); + + let mut tree = PoseidonTree::default(TEST_TREE_HEIGHT).unwrap(); // Creating membership key let mem_keys = wasm_key_gen(rln_instance).unwrap(); @@ -40,7 +47,7 @@ mod tests { // Prepare the message let signal = b"Hello World"; - let identity_index: usize = 0; + let identity_index = tree.leaves_set(); // Setting up the epoch and rln_identifier let epoch = hash_to_field(b"test-epoch"); let rln_identifier = hash_to_field(b"test-rln-identifier"); @@ -53,31 +60,28 @@ mod tests { let (id_commitment_fr, _) = bytes_le_to_fr(&id_commitment.to_vec()[..]); let rate_commitment = poseidon_hash(&[id_commitment_fr, user_message_limit]); + tree.update_next(rate_commitment).unwrap(); + + let x = hash_to_field(signal); + let merkle_proof = tree.proof(identity_index).expect("proof should exist"); + let path_elements = merkle_proof.get_path_elements(); + let identity_path_index = merkle_proof.get_path_index(); - // Insert PK - wasm_set_next_leaf( - rln_instance, - Uint8Array::from(fr_to_bytes_le(&rate_commitment).as_slice()), - ) - .unwrap(); // Serializing the message let mut serialized_vec: Vec = Vec::new(); serialized_vec.append(&mut id_key.to_vec()); - serialized_vec.append(&mut normalize_usize(identity_index)); serialized_vec.append(&mut fr_to_bytes_le(&user_message_limit).to_vec()); serialized_vec.append(&mut message_id.to_vec()); + serialized_vec.append(&mut vec_fr_to_bytes_le(&path_elements).unwrap()); + serialized_vec.append(&mut vec_u8_to_bytes_le(&identity_path_index).unwrap()); + serialized_vec.append(&mut fr_to_bytes_le(&x)); serialized_vec.append(&mut external_nullifier.to_vec()); - serialized_vec.append(&mut normalize_usize(signal.len())); - serialized_vec.append(&mut signal.to_vec()); let serialized_message = Uint8Array::from(&serialized_vec[..]); - let serialized_rln_witness = - wasm_get_serialized_rln_witness(rln_instance, serialized_message).unwrap(); - // Obtaining inputs that should be sent to circom witness calculator let json_inputs = - rln_witness_to_json(rln_instance, serialized_rln_witness.clone()).unwrap(); + rln_witness_to_json(rln_instance, serialized_message.clone()).unwrap(); // Calculating witness with JS // (Using a JSON since wasm_bindgen does not like Result,JsValue>) @@ -97,7 +101,7 @@ mod tests { let proof = generate_rln_proof_with_witness( rln_instance, calculated_witness.into(), - serialized_rln_witness, + serialized_message, ) .unwrap(); @@ -107,42 +111,13 @@ mod tests { proof_bytes.append(&mut signal.to_vec()); let proof_with_signal = Uint8Array::from(&proof_bytes[..]); - // Validate Proof - let is_proof_valid = wasm_verify_rln_proof(rln_instance, proof_with_signal); - - assert!( - is_proof_valid.unwrap(), - "validating proof generated with wasm failed" - ); - // Validating Proof with Roots - let root = wasm_get_root(rln_instance).unwrap(); - let roots = Uint8Array::from(&root.to_vec()[..]); + let root = tree.root(); + let root_le = fr_to_bytes_le(&root); + let roots = Uint8Array::from(&root_le[..]); let proof_with_signal = Uint8Array::from(&proof_bytes[..]); let is_proof_valid = wasm_verify_with_roots(rln_instance, proof_with_signal, roots); assert!(is_proof_valid.unwrap(), "verifying proof with roots failed"); } - - #[wasm_bindgen_test] - fn test_metadata() { - let tree_height = TEST_TREE_HEIGHT; - let zkey_path = format!("../rln/resources/tree_height_{TEST_TREE_HEIGHT}/rln_final.zkey"); - let vk_path = - format!("../rln/resources/tree_height_{TEST_TREE_HEIGHT}/verification_key.arkvkey"); - let zkey = read_file(&zkey_path).unwrap(); - let vk = read_file(&vk_path).unwrap(); - - // Creating an instance of RLN - let rln_instance = wasm_new(tree_height, zkey, vk).unwrap(); - - let test_metadata = Uint8Array::new(&JsValue::from_str("test")); - // Inserting random metadata - wasm_set_metadata(rln_instance, test_metadata.clone()).unwrap(); - - // Getting metadata - let metadata = wasm_get_metadata(rln_instance).unwrap(); - - assert_eq!(metadata.to_vec(), test_metadata.to_vec()); - } } diff --git a/rln/src/public.rs b/rln/src/public.rs index 6f333b4..ca575d4 100644 --- a/rln/src/public.rs +++ b/rln/src/public.rs @@ -1,10 +1,9 @@ use crate::circuit::{vk_from_raw, zkey_from_raw, Curve, Fr}; use crate::hashers::{hash_to_field, poseidon_hash as utils_poseidon_hash}; -use crate::poseidon_tree::PoseidonTree; use crate::protocol::*; use crate::utils::*; /// This is the main public API for RLN module. It is used by the FFI, and should be -/// used by tests etc as well +/// used by tests etc. as well use ark_groth16::Proof as ArkProof; use ark_groth16::{ProvingKey, VerifyingKey}; use ark_relations::r1cs::ConstraintMatrices; @@ -20,6 +19,7 @@ cfg_if! { use std::sync::Mutex; use crate::circuit::{circom_from_folder, vk_from_folder, circom_from_raw, zkey_from_folder, TEST_TREE_HEIGHT}; use ark_circom::WitnessCalculator; + use crate::poseidon_tree::PoseidonTree; use serde_json::{json, Value}; use utils::{Hasher}; use std::sync::Arc; @@ -111,9 +111,12 @@ impl RLN { } /// Creates a new stateless RLN object by loading circuit resources from a folder. + /// + /// Example: + /// /// ``` /// // We create a new RLN instance - /// let mut rln = RLN::new(input); + /// let mut rln = RLN::new(); /// ``` #[cfg_attr(docsrs, doc(cfg(feature = "stateless")))] #[cfg(all(not(target_arch = "wasm32"), feature = "stateless"))] @@ -211,26 +214,6 @@ impl RLN { }) } - #[cfg(all(target_arch = "wasm32", not(feature = "stateless")))] - pub fn new_with_params(tree_height: usize, zkey_vec: Vec, vk_vec: Vec) -> Result { - // TODO: check this lines while update rln-wasm - #[cfg(not(target_arch = "wasm32"))] - let witness_calculator = circom_from_raw(circom_vec)?; - - let proving_key = zkey_from_raw(&zkey_vec)?; - let verification_key = vk_from_raw(&vk_vec, &zkey_vec)?; - - // We compute a default empty tree - let tree = PoseidonTree::default(tree_height)?; - - Ok(RLN { - proving_key, - verification_key, - tree, - _marker: PhantomData, - }) - } - /// Creates a new stateless RLN object by passing circuit resources as byte vectors. /// /// Input parameters are @@ -305,9 +288,6 @@ impl RLN { /// ``` #[cfg(all(target_arch = "wasm32", feature = "stateless"))] pub fn new_with_params(zkey_vec: Vec, vk_vec: Vec) -> Result { - #[cfg(not(target_arch = "wasm32"))] - let witness_calculator = circom_from_raw(circom_vec)?; - let proving_key = zkey_from_raw(&zkey_vec)?; let verification_key = vk_from_raw(&vk_vec, &zkey_vec)?; @@ -590,7 +570,7 @@ impl RLN { Ok(()) } - /// Sets the value of the leaf at position index to the harcoded default value. + /// Sets the value of the leaf at position index to the hardcoded default value. /// /// This function does not change the internal Merkle tree `next_index` value. /// @@ -998,7 +978,7 @@ impl RLN { /// /// The function returns true if the zkSNARK proof is valid with respect to the provided circuit output values and signal. Returns false otherwise. /// - /// Note that contrary to [`verify`](crate::public::RLN::verify), this function takes additionaly as input the signal and further verifies if + /// Note that contrary to [`verify`](crate::public::RLN::verify), this function takes additionally as input the signal and further verifies if /// - the Merkle tree root corresponds to the root provided as input; /// - the input signal corresponds to the Shamir's x coordinate provided as input /// - the hardcoded application [RLN identifier](crate::public::RLN_IDENTIFIER) corresponds to the RLN identifier provided as input @@ -1047,7 +1027,7 @@ impl RLN { /// /// Input values are: /// - `input_data`: a reader for the serialization of the RLN zkSNARK proof concatenated with a serialization of the circuit output values and the signal information, i.e. `[ proof<128> | root<32> | external_nullifier<32> | x<32> | y<32> | nullifier<32> | signal_len<8> | signal]` - /// - `roots_data`: a reader for the serialization of a vector of roots, i.e. `[ number_of_roots<8> | root_1<32> | ... | root_n<32> ]` (number_of_roots is a uint64 in little-endian, roots are serialized using `rln::utils::fr_to_bytes_le`)) + /// - `roots_data`: a reader for the serialization of a vector of roots, i.e. `[ number_of_roots<8> | root_1<32> | ... | root_n<32> ]` (number_of_roots is an uint64 in little-endian, roots are serialized using `rln::utils::fr_to_bytes_le`) /// /// The function returns true if the zkSNARK proof is valid with respect to the provided circuit output values, signal and roots. Returns false otherwise. /// @@ -1192,7 +1172,7 @@ impl RLN { /// Generated credentials are compatible with [Semaphore](https://semaphore.appliedzkp.org/docs/guides/identities)'s credentials. /// /// Output values are: - /// - `output_data`: a writer receiving the serialization of the identity tapdoor, identity nullifier, identity secret and identity commitment (serialization done with `rln::utils::fr_to_bytes_le`) + /// - `output_data`: a writer receiving the serialization of the identity trapdoor, identity nullifier, identity secret and identity commitment (serialization done with `rln::utils::fr_to_bytes_le`) /// /// Example /// ``` @@ -1267,7 +1247,7 @@ impl RLN { /// - `input_data`: a reader for the byte vector containing the seed /// /// Output values are: - /// - `output_data`: a writer receiving the serialization of the identity tapdoor, identity nullifier, identity secret and identity commitment (serialization done with `rln::utils::fr_to_bytes_le`) + /// - `output_data`: a writer receiving the serialization of the identity trapdoor, identity nullifier, identity secret and identity commitment (serialization done with `rln::utils::fr_to_bytes_le`) /// /// Example /// ``` @@ -1341,7 +1321,7 @@ impl RLN { mut input_proof_data_2: R, mut output_data: W, ) -> Result<()> { - // We serialize_compressed the two proofs and we get the corresponding RLNProofValues objects + // We serialize_compressed the two proofs, and we get the corresponding RLNProofValues objects let mut serialized: Vec = Vec::new(); input_proof_data_1.read_to_end(&mut serialized)?; // We skip deserialization of the zk-proof at the beginning @@ -1357,7 +1337,7 @@ impl RLN { // We continue only if the proof values are for the same external nullifier (which includes epoch and rln identifier) // The idea is that proof values that go as input to this function are verified first (with zk-proof verify), hence ensuring validity of external nullifier and other fields. // Only in case all fields are valid, an external_nullifier for the message will be stored (otherwise signal/proof will be simply discarded) - // If the nullifier matches one already seen, we can recovery of identity secret. + // If the nullifier matches one already seen, we can recover of identity secret. if external_nullifier_1 == external_nullifier_2 { // We extract the two shares let share1 = (proof_values_1.x, proof_values_1.y); @@ -1382,7 +1362,7 @@ impl RLN { /// Input values are: /// - `input_data`: a reader for the serialization of `[ identity_secret<32> | id_index<8> | user_message_limit<32> | message_id<32> | external_nullifier<32> | signal_len<8> | signal ]` /// - /// The function returns the corresponding [`RLNWitnessInput`](crate::protocol::RLNWitnessInput) object serialized using [`rln::protocol::serialize_witness`](crate::protocol::serialize_witness)). + /// The function returns the corresponding [`RLNWitnessInput`](crate::protocol::RLNWitnessInput) object serialized using [`rln::protocol::serialize_witness`](crate::protocol::serialize_witness). #[cfg(not(feature = "stateless"))] pub fn get_serialized_rln_witness(&mut self, mut input_data: R) -> Result> { // We read input RLN witness and we serialize_compressed it @@ -1405,7 +1385,7 @@ impl RLN { } /// Converts a byte serialization of a [`RLNWitnessInput`](crate::protocol::RLNWitnessInput) object to the corresponding JSON serialization. - /// Before serialisation the data will be translated into big int for further calculation in the witness calculator. + /// Before serialization the data will be translated into big int for further calculation in the witness calculator. /// /// Input values are: /// - `serialized_witness`: the byte serialization of a [`RLNWitnessInput`](crate::protocol::RLNWitnessInput) object (serialization done with [`rln::protocol::serialize_witness`](crate::protocol::serialize_witness)).