diff --git a/rln/Cargo.toml b/rln/Cargo.toml index 7e7f514..08cd48f 100644 --- a/rln/Cargo.toml +++ b/rln/Cargo.toml @@ -6,6 +6,9 @@ edition = "2021" [lib] crate-type = ["cdylib", "rlib", "staticlib"] +# This flag disable cargo doctests, i.e. testing example code-snippets in documentation +doctest = false + [dependencies] diff --git a/rln/README.md b/rln/README.md index 7080c50..dac9f16 100644 --- a/rln/README.md +++ b/rln/README.md @@ -2,15 +2,22 @@ This module provides APIs to manage, compute and verify [RLN](https://rfc.vac.dev/spec/32/) zkSNARK proofs and RLN primitives. -Currently, this module comes with three [pre-compiled](https://github.com/vacp2p/zerokit/tree/master/rln/resources) RLN circuits having Merkle tree of height `15`, `19` and `20`, respectively. +## Pre-requisites +### Install +```sh +git clone https://github.com/vacp2p/zerokit.git +cd zerokit/rln +``` Implemented tests can be executed by running within the module folder `cargo test --release` -## Compiling circuits +### Compile ZK circuits -`rln` (https://github.com/privacy-scaling-explorations/rln) repo with Circuits is contained as a submodule. +The `rln` (https://github.com/privacy-scaling-explorations/rln) repository, which contains the RLN circuit implementation is a submodule of zerokit RLN. + +To compile the RLN circuit ``` sh # Update submodules @@ -27,9 +34,9 @@ cp build/zkeyFiles/rln-final.zkey ../../resources/tree_height_15 cp build/zkeyFiles/rln.wasm ../../resources/tree_height_15 ``` -Note that the above code snippet will compile a RLN circuit with a Merkle tree of height equal `15` based on the default value set in `rln/circuit/rln.circom`. +Note that the above code snippet will compile a RLN circuit with a Merkle tree of height equal `15` based on the default value set in `vendor/rln/circuit/rln.circom`. -To compile a RLN circuit with Merkle tree height `N`, it suffices to change `rln/circuit/rln.circom` to +In order to compile a RLN circuit with Merkle tree height `N`, it suffices to change `vendor/rln/circuit/rln.circom` to ``` pragma circom 2.0.0; @@ -40,4 +47,144 @@ component main {public [x, epoch, rln_identifier ]} = RLN(N); ``` However, if `N` is too big, this might require a bigger Powers of Tau ceremony than the one hardcoded in `./scripts/build-circuits.sh`, which is `2^14`. -In such case we refer to the official [Circom documentation](https://docs.circom.io/getting-started/proving-circuits/#powers-of-tau) for instructions on how to run an appropriate Powers of Tau ceremony and Phase 2 in order to compile the desired circuit. \ No newline at end of file +In such case we refer to the official [Circom documentation](https://docs.circom.io/getting-started/proving-circuits/#powers-of-tau) for instructions on how to run an appropriate Powers of Tau ceremony and Phase 2 in order to compile the desired circuit. + + +Currently, the `rln` module comes with three [pre-compiled](https://github.com/vacp2p/zerokit/tree/master/rln/resources) RLN circuits having Merkle tree of height `15`, `19` and `20`, respectively. + +## Getting started + +### Add RLN as dependency + +We start by adding zerokit RLN to our `Cargo.toml` + +```toml +[dependencies] +rln = { git = "https://github.com/vacp2p/zerokit" } +``` + +### Create a RLN object + +First, we need to create a RLN object for a chosen input Merkle tree size. + +Note that we need to pass to RLN object constructor the path where the circuit (`rln.wasm`, built for the input tree size), the corresponding proving key (`rln_final.zkey`) and verification key (`verification_key.json`, optional) are found. + +In the following we will use [cursors](https://doc.rust-lang.org/std/io/struct.Cursor.html) as readers/writers for interfacing with RLN public APIs. + +```rust +use rln::protocol::*; +use rln::public::*; +use std::io::Cursor; + +// We set the RLN parameters: +// - the tree height; +// - the circuit resource folder (requires a trailing "/"). +let tree_height = 20; +let resources = Cursor::new("../zerokit/rln/resources/tree_height_20/"); + +// We create a new RLN instance +let mut rln = RLN::new(tree_height, resources); +``` + +### Generate an identity keypair + +We generate an identity keypair + +```rust +// We generate an identity pair +let mut buffer = Cursor::new(Vec::::new()); +rln.key_gen(&mut buffer).unwrap(); + +// We deserialize the keygen output to obtain +// the identiy_secret and id_commitment +let (identity_secret, id_commitment) = deserialize_identity_pair(buffer.into_inner()); +``` + +### Add ID commitment to the RLN Merkle tree + +```rust +// We define the tree index where id_commitment will be added +let id_index = 10; + +// We serialize id_commitment and pass it to set_leaf +let mut buffer = Cursor::new(serialize_field_element(id_commitment)); +rln.set_leaf(id_index, &mut buffer).unwrap(); +``` + +Note that when tree leaves are not explicitly set by the user (in this example, all those with index less and greater than `10`), their values is set to an hardcoded default (all-`0` bytes in current implementation). + +### Set epoch + +The epoch, sometimes referred to as _external nullifier_, is used to identify messages received in a certain time frame. It usually corresponds to the current UNIX time but can also be set to a random value or generated by a seed, provided that it corresponds to a field element. + +```rust +// We generate epoch from a date seed and we ensure is +// mapped to a field element by hashing-to-field its content +let epoch = hash_to_field(b"Today at noon, this year"); +``` +### Set signal + +The signal is the message for which we are computing a RLN proof. + +```rust +// We set our signal +let signal = b"RLN is awesome"; +``` + +### Generate a RLN proof + +We prepare the input to the proof generation routine. + +Input buffer is serialized as `[ identity_key | id_index | epoch | signal_len | signal ]`. + +```rust +// We prepare input to the proof generation routine +let proof_input = prepare_prove_input(identity_secret, id_index, epoch, signal); +``` + +We are now ready to generate a RLN ZK proof along with the _public outputs_ of the ZK circuit evaluation. + +```rust + +// We generate a RLN proof for proof_input +let mut in_buffer = Cursor::new(proof_input); +let mut out_buffer = Cursor::new(Vec::::new()); +rln.generate_rln_proof(&mut in_buffer, &mut out_buffer) + .unwrap(); + +// We get the public outputs returned by the circuit evaluation +let proof_data = out_buffer.into_inner(); +``` + +The byte vector `proof_data` is serialized as `[ zk-proof | tree_root | epoch | share_x | share_y | nullifier | rln_identifier ]`. + + +### Verify a RLN proof + +We prepare the input to the proof verification routine. + +Input buffer is serialized as `[proof_data | signal_len | signal ]`, where `proof_data` is (computed as) the output obtained by `generate_rln_proof`. + +```rust +// We prepare input to the proof verification routine +let verify_data = prepare_verify_input(proof_data, signal); + +// We verify the zk-proof against the provided proof values +let mut in_buffer = Cursor::new(verify_data); +let verified = rln.verify(&mut in_buffer).unwrap(); +``` + +We check if the proof verification was successful: +```rust +// We ensure the proof is valid +assert!(verified); +``` + +## Get involved! +Zerokit RLN public and FFI APIs allow interaction with many more features than what briefly showcased above. + +We invite you to check our API documentation by running +```rust +cargo doc --no-deps +``` +and look at unit tests to have an hint on how to interface and use them. \ No newline at end of file diff --git a/rln/src/ffi.rs b/rln/src/ffi.rs index 5c0f4db..3aaa727 100644 --- a/rln/src/ffi.rs +++ b/rln/src/ffi.rs @@ -5,9 +5,9 @@ use std::slice; use crate::public::RLN; /// Buffer struct is taken from -/// https://github.com/celo-org/celo-threshold-bls-rs/blob/master/crates/threshold-bls-ffi/src/ffi.rs +/// /// -/// Also heavily inspired by https://github.com/kilic/rln/blob/master/src/ffi.rs +/// Also heavily inspired by #[repr(C)] #[derive(Clone, Debug, PartialEq)] diff --git a/rln/src/public.rs b/rln/src/public.rs index 00e91f9..aec0da3 100644 --- a/rln/src/public.rs +++ b/rln/src/public.rs @@ -24,11 +24,16 @@ cfg_if! { } } -// Application specific RLN identifier +/// The application-specific RLN identifier. +/// +/// Prevents a RLN ZK proof generated for one application to be re-used in another one. 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 +/// The RLN object. +/// +/// It implements the methods required to update the internal Merkle Tree, generate and verify RLN ZK proofs. +/// +/// I/O is mostly done using writers and readers implementing `std::io::Write` and `std::io::Read`, respectively. pub struct RLN<'a> { proving_key: Result<(ProvingKey, ConstraintMatrices)>, verification_key: Result>, @@ -44,6 +49,22 @@ pub struct RLN<'a> { } impl RLN<'_> { + /// Creates a new RLN object by loading circuit resources from a folder. + /// + /// Input parameters are + /// - `tree_height`: the height of the internal Merkle tree + /// - `input_data`: a reader for the string path of the resource folder containing the ZK circuit (`rln.wasm`), the proving key (`rln_final.zkey`) and the verification key (`verification_key.json`). + /// + /// Example: + /// ``` + /// use std::io::Cursor; + /// + /// let tree_height = 20; + /// let resources = Cursor::new("./resources/tree_height_20/"); + /// + /// // We create a new RLN instance + /// let mut rln = RLN::new(tree_height, resources); + /// ``` #[cfg(not(target_arch = "wasm32"))] pub fn new(tree_height: usize, mut input_data: R) -> RLN<'static> { // We read input @@ -70,6 +91,39 @@ impl RLN<'_> { } } + /// Creates a new RLN object by passing circuit resources as byte vectors. + /// + /// Input parameters are + /// - `tree_height`: the height of the internal Merkle tree + /// - `circom_vec`: a byte vector containing the ZK circuit (`rln.wasm`) as binary file + /// - `zkey_vec`: a byte vector containing to the proving key (`rln_final.zkey`) as binary file + /// - `vk_vec`: a byte vector containing to the verification key (`verification_key.json`) as binary file + /// + /// Example: + /// ``` + /// use std::fs::File; + /// use std::io::Read; + /// + /// let tree_height = 20; + /// let resources_folder = "./resources/tree_height_20/"; + /// + /// let mut resources: Vec> = Vec::new(); + /// for filename in ["rln.wasm", "rln_final.zkey", "verification_key.json"] { + /// let fullpath = format!("{resources_folder}{filename}"); + /// let mut file = File::open(&fullpath).expect("no file found"); + /// let metadata = std::fs::metadata(&fullpath).expect("unable to read metadata"); + /// let mut buffer = vec![0; metadata.len() as usize]; + /// file.read_exact(&mut buffer).expect("buffer overflow"); + /// resources.push(buffer); + /// } + /// + /// let mut rln = RLN::new_with_params( + /// tree_height, + /// resources[0].clone(), + /// resources[1].clone(), + /// resources[2].clone(), + /// ); + /// ``` pub fn new_with_params( tree_height: usize, #[cfg(not(target_arch = "wasm32"))] circom_vec: Vec, @@ -99,6 +153,12 @@ impl RLN<'_> { //////////////////////////////////////////////////////// // Merkle-tree APIs //////////////////////////////////////////////////////// + /// Initializes the internal Merkle tree. + /// + /// Leaves are set to the default value implemented in PoseidonTree implementation. + /// + /// Input values are: + /// - `tree_height`: the height of the Merkle tree. pub fn set_tree(&mut self, tree_height: usize) -> io::Result<()> { // We compute a default empty tree of desired height self.tree = PoseidonTree::default(tree_height); @@ -106,6 +166,26 @@ impl RLN<'_> { Ok(()) } + /// Sets a leaf value at position index in the internal Merkle tree. + /// + /// Input values are: + /// - `index`: the index of the leaf + /// - `input_data`: a reader for the serialization of the leaf value (serialization done with [`rln::utils::fr_to_bytes_le`](crate::utils::fr_to_bytes_le)) + /// + /// Example: + /// ``` + /// use crate::protocol::*; + /// + /// // We generate a random id secret and commitment pair + /// let (identity_secret, id_commitment) = keygen(); + /// + /// // We define the tree index where id_commitment will be added + /// let id_index = 10; + /// + /// // We serialize id_commitment and pass it to set_leaf + /// let mut buffer = Cursor::new(serialize_field_element(id_commitment)); + /// rln.set_leaf(id_index, &mut buffer).unwrap(); + /// ``` pub fn set_leaf(&mut self, index: usize, mut input_data: R) -> io::Result<()> { // We read input let mut leaf_byte: Vec = Vec::new(); @@ -118,6 +198,36 @@ impl RLN<'_> { Ok(()) } + /// Sets multiple leaves starting from position index in the internal Merkle tree. + /// + /// If n leaves are passed as input, these will be set at positions `index`, `index+1`, ..., `index+n-1` respectively. + /// + /// This function updates the internal Merkle tree `next_index value indicating the next available index corresponding to a never-set leaf as `next_index = max(next_index, index + n)`. + /// + /// Input values are: + /// - `index`: the index of the first leaf to be set + /// - `input_data`: a reader for the serialization of multiple leaf values (serialization done with [`rln::utils::vec_fr_to_bytes_le`](crate::utils::vec_fr_to_bytes_le)) + /// + /// Example: + /// ``` + /// use rln::circuit::Fr; + /// use rln::utils::*; + /// + /// let start_index = 10; + /// let no_of_leaves = 256; + /// + /// // We generate a vector of random leaves + /// let mut leaves: Vec = Vec::new(); + /// let mut rng = thread_rng(); + /// for _ in 0..no_of_leaves { + /// let (_, id_commitment) = keygen(); + /// leaves.push(id_commitment); + /// } + /// + /// // We add leaves in a batch into the tree + /// let mut buffer = Cursor::new(vec_fr_to_bytes_le(&leaves)); + /// rln.set_leaves_from(index, &mut buffer).unwrap(); + /// ``` pub fn set_leaves_from(&mut self, index: usize, mut input_data: R) -> io::Result<()> { // We read input let mut leaves_byte: Vec = Vec::new(); @@ -129,6 +239,12 @@ impl RLN<'_> { return self.tree.set_range(index, leaves); } + /// Resets the tree state to default and sets multiple leaves starting from index 0. + /// + /// In contrast to [`set_leaves_from`](crate::public::RLN::set_leaves_from), this function resets to 0 the internal `next_index` value, before setting the input leaves values. + /// + /// Input values are: + /// - `input_data`: a reader for the serialization of multiple leaf values (serialization done with [`rln::utils::vec_fr_to_bytes_le`](crate::utils::vec_fr_to_bytes_le)) pub fn init_tree_with_leaves(&mut self, input_data: R) -> io::Result<()> { // reset the tree // NOTE: this requires the tree to be initialized with the correct height initially @@ -137,7 +253,47 @@ impl RLN<'_> { return self.set_leaves_from(0, input_data); } - // Set input leaf to the next available index + /// Sets a leaf value at the next available never-set leaf index. + /// + /// This function updates the internal Merkle tree `next_index` value indicating the next available index corresponding to a never-set leaf as `next_index = next_index + 1`. + /// + /// Input values are: + /// - `input_data`: a reader for the serialization of multiple leaf values (serialization done with [`rln::utils::vec_fr_to_bytes_le`](crate::utils::vec_fr_to_bytes_le)) + /// + /// Example: + /// ``` + /// use rln::circuit::Fr; + /// use rln::utils::*; + /// + /// let tree_height = 20; + /// let start_index = 10; + /// let no_of_leaves = 256; + /// + /// // We reset the tree + /// rln.set_tree(tree_height).unwrap(); + /// + /// // Internal Merkle tree next_index value is now 0 + /// + /// // We generate a vector of random leaves + /// let mut leaves: Vec = Vec::new(); + /// let mut rng = thread_rng(); + /// for _ in 0..no_of_leaves { + /// let (_, id_commitment) = keygen(); + /// leaves.push(id_commitment); + /// } + /// + /// // We add leaves in a batch into the tree + /// let mut buffer = Cursor::new(vec_fr_to_bytes_le(&leaves)); + /// rln.set_leaves_from(index, &mut buffer).unwrap(); + /// + /// // We set 256 leaves starting from index 10: next_index value is now max(0, 256+10) = 266 + /// + /// // We set a leaf on next available index + /// // id_commitment will be set at index 266 + /// let (_, id_commitment) = keygen(); + /// let mut buffer = Cursor::new(fr_to_bytes_le(&id_commitment)); + /// rln.set_next_leaf(&mut buffer).unwrap(); + /// ``` pub fn set_next_leaf(&mut self, mut input_data: R) -> io::Result<()> { // We read input let mut leaf_byte: Vec = Vec::new(); @@ -150,14 +306,37 @@ impl RLN<'_> { Ok(()) } - // Deleting a leaf corresponds to set its value to the default 0 leaf + /// Sets the value of the leaf at position index to the harcoded default value. + /// + /// This function does not change the internal Merkle tree `next_index` value. + /// + /// Input values are: + /// - `index`: the index of the leaf whose value will be reset + /// + /// Example + /// ``` + /// + /// let index = 10; + /// rln.delete_leaf(index).unwrap(); + /// ``` pub fn delete_leaf(&mut self, index: usize) -> io::Result<()> { self.tree.delete(index)?; Ok(()) } - /// returns current membership root - /// * `root` is a scalar field element in 32 bytes + /// Returns the Merkle tree root + /// + /// Output values are: + /// - `output_data`: a writer receiving the serialization of the root value (serialization done with [`rln::utils::fr_to_bytes_le`](crate::utils::fr_to_bytes_le)) + /// + /// Example + /// ``` + /// use rln::utils::*; + /// + /// let mut buffer = Cursor::new(Vec::::new()); + /// rln.get_root(&mut buffer).unwrap(); + /// let (root, _) = bytes_le_to_fr(&buffer.into_inner()); + /// ``` pub fn get_root(&self, mut output_data: W) -> io::Result<()> { let root = self.tree.root(); output_data.write_all(&fr_to_bytes_le(&root))?; @@ -165,8 +344,27 @@ impl RLN<'_> { Ok(()) } - /// returns current membership root - /// * `root` is a scalar field element in 32 bytes + /// Returns the Merkle proof of the leaf at position index + /// + /// Input values are: + /// - `index`: the index of the leaf + /// + /// Output values are: + /// - `output_data`: a writer receiving the serialization of the path elements and path indexes (serialization done with [`rln::utils::vec_fr_to_bytes_le`](crate::utils::vec_fr_to_bytes_le) and [`rln::utils::vec_u8_to_bytes_le`](crate::utils::vec_u8_to_bytes_le), respectively) + /// + /// Example + /// ``` + /// use rln::utils::*; + /// + /// let index = 10; + /// + /// let mut buffer = Cursor::new(Vec::::new()); + /// rln.get_proof(index, &mut buffer).unwrap(); + /// + /// let buffer_inner = buffer.into_inner(); + /// let (path_elements, read) = bytes_le_to_vec_fr(&buffer_inner); + /// let (identity_path_index, _) = bytes_le_to_vec_u8(&buffer_inner[read..].to_vec()); + /// ``` pub fn get_proof(&self, index: usize, mut output_data: W) -> io::Result<()> { let merkle_proof = self.tree.proof(index).expect("proof should exist"); let path_elements = merkle_proof.get_path_elements(); @@ -181,6 +379,27 @@ impl RLN<'_> { //////////////////////////////////////////////////////// // zkSNARK APIs //////////////////////////////////////////////////////// + /// Computes a zkSNARK RLN proof using a [`RLNWitnessInput`](crate::protocol::RLNWitnessInput). + /// + /// Input values are: + /// - `input_data`: a reader for the serialization of a [`RLNWitnessInput`](crate::protocol::RLNWitnessInput) object, containing the public and private inputs to the ZK circuits (serialization done using [`rln::protocol::serialize_witness`](crate::protocol::serialize_witness)) + /// + /// Output values are: + /// - `output_data`: a writer receiving the serialization of the zkSNARK proof + /// + /// Example: + /// ``` + /// use rln::protocol::*; + /// + /// let rln_witness = random_rln_witness(tree_height); + /// let proof_values = proof_values_from_witness(&rln_witness); + /// + /// // We compute a Groth16 proof + /// let mut input_buffer = Cursor::new(serialize_witness(&rln_witness)); + /// let mut output_buffer = Cursor::new(Vec::::new()); + /// rln.prove(&mut input_buffer, &mut output_buffer).unwrap(); + /// let zk_proof = output_buffer.into_inner(); + /// ``` #[cfg(not(target_arch = "wasm32"))] pub fn prove( &mut self, @@ -211,6 +430,41 @@ impl RLN<'_> { Ok(()) } + /// Verifies a zkSNARK RLN proof. + /// + /// Input values are: + /// - `input_data`: a reader for the serialization of the RLN zkSNARK proof concatenated with a serialization of the circuit output values, i.e. `[ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> ]`, where <_> indicates the byte length. + /// + /// The function returns true if the zkSNARK proof is valid with respect to the provided circuit output values, false otherwise. + /// + /// Example: + /// ``` + /// use rln::protocol::*; + /// + /// let rln_witness = random_rln_witness(tree_height); + /// + /// // We compute a Groth16 proof + /// let mut input_buffer = Cursor::new(serialize_witness(&rln_witness)); + /// let mut output_buffer = Cursor::new(Vec::::new()); + /// rln.prove(&mut input_buffer, &mut output_buffer).unwrap(); + /// let zk_proof = output_buffer.into_inner(); + /// + /// // We prepare the input to prove API, consisting of zk_proof (compressed, 4*32 bytes) || proof_values (6*32 bytes) + /// // In this example, we compute proof values directly from witness using the utility proof_values_from_witness + /// let proof_values = proof_values_from_witness(&rln_witness); + /// let serialized_proof_values = serialize_proof_values(&proof_values); + /// + /// // We build the input to the verify method + /// let mut verify_data = Vec::::new(); + /// verify_data.extend(&zk_proof); + /// verify_data.extend(&proof_values); + /// let mut input_buffer = Cursor::new(verify_data); + /// + /// // We verify the Groth16 proof against the provided zk-proof and proof values + /// let verified = rln.verify(&mut input_buffer).unwrap(); + /// + /// assert!(verified); + /// ``` pub fn verify(&self, mut input_data: R) -> io::Result { // Input data is serialized for Curve as: // serialized_proof (compressed, 4*32 bytes) || serialized_proof_values (6*32 bytes), i.e. @@ -231,28 +485,52 @@ impl RLN<'_> { Ok(verified) } - /// Get the serialized rln_witness for some input - pub fn get_serialized_rln_witness(&mut self, mut input_data: R) -> Vec { - // We read input RLN witness and we deserialize it - let mut witness_byte: Vec = Vec::new(); - input_data.read_to_end(&mut witness_byte).unwrap(); - let (rln_witness, _) = proof_inputs_to_rln_witness(&mut self.tree, &witness_byte); - - serialize_witness(&rln_witness) - } - - /// Get JSON inputs for serialized RLN witness - pub fn get_rln_witness_json( - &mut self, - serialized_witness: &[u8], - ) -> io::Result { - let (rln_witness, _) = deserialize_witness(serialized_witness); - Ok(get_json_inputs(&rln_witness)) - } - - // This API keeps partial compatibility with kilic's rln public API https://github.com/kilic/rln/blob/7ac74183f8b69b399e3bc96c1ae8ab61c026dc43/src/public.rs#L148 - // input_data is [ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal ] - // output_data is [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> ] + /// Computes a zkSNARK RLN proof from the identity secret, the Merkle tree index, the epoch and signal. + /// + /// Input values are: + /// - `input_data`: a reader for the serialization of `[ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal ]` + /// + /// Output values are: + /// - `output_data`: a writer receiving the serialization of the zkSNARK proof and the circuit evaluations outputs, i.e. `[ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> ]` + /// + /// Example + /// ``` + /// use rln::protocol::*: + /// use rln::utils::*; + /// + /// // Generate identity pair + /// let (identity_secret, id_commitment) = keygen(); + /// + /// // We set as leaf id_commitment after storing its index + /// let identity_index = 10; + /// let mut buffer = Cursor::new(fr_to_bytes_le(&id_commitment)); + /// rln.set_leaf(identity_index, &mut buffer).unwrap(); + /// + /// // We generate a random signal + /// let mut rng = rand::thread_rng(); + /// let signal: [u8; 32] = rng.gen(); + /// let signal_len = u64::try_from(signal.len()).unwrap(); + /// + /// // We generate a random epoch + /// let epoch = hash_to_field(b"test-epoch"); + /// + /// // We prepare input for generate_rln_proof API + /// // input_data is [ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal ] + /// let mut serialized: Vec = Vec::new(); + /// serialized.append(&mut fr_to_bytes_le(&identity_secret)); + /// serialized.append(&mut identity_index.to_le_bytes().to_vec()); + /// serialized.append(&mut fr_to_bytes_le(&epoch)); + /// serialized.append(&mut signal_len.to_le_bytes().to_vec()); + /// serialized.append(&mut signal.to_vec()); + /// + /// let mut input_buffer = Cursor::new(serialized); + /// let mut output_buffer = Cursor::new(Vec::::new()); + /// rln.generate_rln_proof(&mut input_buffer, &mut output_buffer) + /// .unwrap(); + /// + /// // proof_data is [ proof<128> | share_y<32> | nullifier<32> | root<32> | epoch<32> | share_x<32> | rln_identifier<32> ] + /// let mut proof_data = output_buffer.into_inner(); + /// ``` #[cfg(not(target_arch = "wasm32"))] pub fn generate_rln_proof( &mut self, @@ -280,9 +558,12 @@ impl RLN<'_> { Ok(()) } - /// Generate RLN Proof using a witness calculated from outside zerokit - /// - /// output_data is [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> ] + // TODO: this function seems to use redundant witness (as bigint and serialized) and should be refactored + // Generate RLN Proof using a witness calculated from outside zerokit + // + // output_data is [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> ] + // we skip it from documentation for now + #[doc(hidden)] pub fn generate_rln_proof_with_witness( &mut self, calculated_witness: Vec, @@ -303,9 +584,33 @@ impl RLN<'_> { Ok(()) } - // Input data is serialized for Curve as: - // [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> | signal_len<8> | signal ] - // Note that in contrast to verify, this function takes as input the signal and further verifies: the tree root, the X coordinate, the RLN identifier + /// Verifies a zkSNARK RLN proof against the provided proof values and the state of the internal Merkle tree. + /// + /// 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> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> | signal_len<8> | signal ]` + /// + /// 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 + /// - 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 + /// + /// Example + /// ``` + /// // proof_data is computed as in the example code snippet provided for rln::public::RLN::generate_rln_proof + /// + /// // We prepare input for verify_rln_proof API + /// // input_data is [ proof<128> | share_y<32> | nullifier<32> | root<32> | epoch<32> | share_x<32> | rln_identifier<32> | signal_len<8> | signal ] + /// // that is [ proof_data || signal_len<8> | signal ] + /// proof_data.append(&mut signal_len.to_le_bytes().to_vec()); + /// proof_data.append(&mut signal.to_vec()); + /// + /// let mut input_buffer = Cursor::new(proof_data); + /// let verified = rln.verify_rln_proof(&mut input_buffer).unwrap(); + /// + /// assert!(verified); + /// ``` pub fn verify_rln_proof(&self, mut input_data: R) -> io::Result { let mut serialized: Vec = Vec::new(); input_data.read_to_end(&mut serialized)?; @@ -336,10 +641,57 @@ impl RLN<'_> { && (proof_values.rln_identifier == hash_to_field(RLN_IDENTIFIER))) } - // This function verifies a proof against a sequence of input valid roots to allow external validation of the tree state. - // Input data is serialized for Curve as: - // [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> | signal_len<8> | signal ] - // roots_data is serialized as (arbitrary long, even empty) sequence of Fr elements serialized in little endian (32 bytes each for Bn254) + /// Verifies a zkSNARK RLN proof against the provided proof values and a set of allowed Merkle tree roots. + /// + /// 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> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<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`)) + /// + /// The function returns true if the zkSNARK proof is valid with respect to the provided circuit output values, signal and roots. Returns false otherwise. + /// + /// Note that contrary to [`verify_rln_proof`](crate::public::RLN::verify_rln_proof), this function does not check if the internal Merkle tree root corresponds to the root provided as input, but rather checks if the root provided as input in `input_data` corresponds to one of the roots serialized in `roots_data`. + /// + /// If `roots_data` contains no root (is empty), root validation is skipped and the proof will be correctly verified only if the other proof values results valid (i.e., zk-proof, signal, x-coordinate, RLN identifier) + /// + /// Example + /// ``` + /// // proof_data is computed as in the example code snippet provided for rln::public::RLN::generate_rln_proof + /// + /// // If no roots is provided, proof validation is skipped and if the remaining proof values are valid, the proof will be correctly verified + /// let mut input_buffer = Cursor::new(proof_data); + /// let mut roots_serialized: Vec = Vec::new(); + /// let mut roots_buffer = Cursor::new(roots_serialized.clone()); + /// let verified = rln + /// .verify_with_roots(&mut input_buffer.clone(), &mut roots_buffer) + /// .unwrap(); + /// + /// assert!(verified); + /// + /// // We serialize in the roots buffer some random values and we check that the proof is not verified since doesn't contain the correct root the proof refers to + /// for _ in 0..5 { + /// roots_serialized.append(&mut fr_to_bytes_le(&Fr::rand(&mut rng))); + /// } + /// roots_buffer = Cursor::new(roots_serialized.clone()); + /// let verified = rln + /// .verify_with_roots(&mut input_buffer.clone(), &mut roots_buffer) + /// .unwrap(); + /// + /// assert!(verified == false); + /// + /// // We get the root of the tree obtained adding one leaf per time + /// let mut buffer = Cursor::new(Vec::::new()); + /// rln.get_root(&mut buffer).unwrap(); + /// let (root, _) = bytes_le_to_fr(&buffer.into_inner()); + /// + /// // We add the real root and we check if now the proof is verified + /// roots_serialized.append(&mut fr_to_bytes_le(&root)); + /// roots_buffer = Cursor::new(roots_serialized.clone()); + /// let verified = rln + /// .verify_with_roots(&mut input_buffer.clone(), &mut roots_buffer) + /// .unwrap(); + /// + /// assert!(verified); + /// ``` pub fn verify_with_roots( &self, mut input_data: R, @@ -413,6 +765,24 @@ impl RLN<'_> { // Utils //////////////////////////////////////////////////////// + /// Returns an identity secret and identity commitment pair. + /// + /// The identity commitment is the Poseidon hash of the identity secret. + /// + /// Output values are: + /// - `output_data`: a writer receiving the serialization of the identity secret and identity commitment (serialization done with `rln::utils::fr_to_bytes_le`) + /// + /// Example + /// ``` + /// use rln::protocol::*; + /// + /// // We generate an identity pair + /// let mut buffer = Cursor::new(Vec::::new()); + /// rln.key_gen(&mut buffer).unwrap(); + /// + /// // We deserialize the keygen output + /// let (identity_secret, id_commitment) = deserialize_identity_pair(buffer.into_inner()); + /// ``` pub fn key_gen(&self, mut output_data: W) -> io::Result<()> { let (id_key, id_commitment_key) = keygen(); output_data.write_all(&fr_to_bytes_le(&id_key))?; @@ -421,6 +791,30 @@ impl RLN<'_> { Ok(()) } + /// Returns an identity secret and identity commitment pair generated using a seed. + /// + /// The identity commitment is the Poseidon hash of the identity secret. + /// + /// Input values are: + /// - `input_data`: a reader for the byte vector containing the seed + /// + /// Output values are: + /// - `output_data`: a writer receiving the serialization of the identity secret and identity commitment (serialization done with [`rln::utils::fr_to_bytes_le`](crate::utils::fr_to_bytes_le)) + /// + /// Example + /// ``` + /// use rln::protocol::*; + /// + /// let seed_bytes: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + /// + /// let mut input_buffer = Cursor::new(&seed_bytes); + /// let mut output_buffer = Cursor::new(Vec::::new()); + /// rln.seeded_key_gen(&mut input_buffer, &mut output_buffer) + /// .unwrap(); + /// + /// // We deserialize the keygen output + /// let (identity_secret, id_commitment) = deserialize_identity_pair(output_buffer.into_inner()); + /// ``` pub fn seeded_key_gen( &self, mut input_data: R, @@ -436,6 +830,28 @@ impl RLN<'_> { Ok(()) } + /// Hashes an input signal to an element in the working prime field. + /// + /// The result is computed as the Keccak256 of the input signal modulo the prime field characteristic. + /// + /// Input values are: + /// - `input_data`: a reader for the byte vector containing the input signal. + /// + /// Output values are: + /// - `output_data`: a writer receiving the serialization of the resulting field element (serialization done with [`rln::utils::fr_to_bytes_le`](crate::utils::fr_to_bytes_le)) + /// + /// Example + /// ``` + /// let signal: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + /// + /// let mut input_buffer = Cursor::new(&signal); + /// let mut output_buffer = Cursor::new(Vec::::new()); + /// rln.hash(&mut input_buffer, &mut output_buffer) + /// .unwrap(); + /// + /// // We deserialize the keygen output + /// let field_element = deserialize_field_element(output_buffer.into_inner()); + /// ``` pub fn hash(&self, mut input_data: R, mut output_data: W) -> io::Result<()> { let mut serialized: Vec = Vec::new(); input_data.read_to_end(&mut serialized)?; @@ -445,6 +861,35 @@ impl RLN<'_> { Ok(()) } + + /// Returns the serialization of a [`RLNWitnessInput`](crate::protocol::RLNWitnessInput) populated from the identity secret, the Merkle tree index, the epoch and signal. + /// + /// Input values are: + /// - `input_data`: a reader for the serialization of `[ id_key<32> | id_index<8> | epoch<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)). + pub fn get_serialized_rln_witness(&mut self, mut input_data: R) -> Vec { + // We read input RLN witness and we deserialize it + let mut witness_byte: Vec = Vec::new(); + input_data.read_to_end(&mut witness_byte).unwrap(); + let (rln_witness, _) = proof_inputs_to_rln_witness(&mut self.tree, &witness_byte); + + serialize_witness(&rln_witness) + } + + /// Converts a byte serialization of a [`RLNWitnessInput`](crate::protocol::RLNWitnessInput) object to the corresponding JSON serialization. + /// + /// 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)). + /// + /// The function returns the corresponding JSON encoding of the input [`RLNWitnessInput`](crate::protocol::RLNWitnessInput) object. + pub fn get_rln_witness_json( + &mut self, + serialized_witness: &[u8], + ) -> io::Result { + let (rln_witness, _) = deserialize_witness(serialized_witness); + Ok(get_json_inputs(&rln_witness)) + } } #[cfg(not(target_arch = "wasm32"))]