mirror of https://github.com/vacp2p/zerokit.git
196 lines
6.2 KiB
Markdown
196 lines
6.2 KiB
Markdown
# Zerokit RLN Module
|
|
|
|
This module provides APIs to manage, compute and verify [RLN](https://rfc.vac.dev/spec/32/) zkSNARK proofs and RLN primitives.
|
|
|
|
## Pre-requisites
|
|
### Install dependencies and clone repo
|
|
|
|
```sh
|
|
make installdeps
|
|
git clone https://github.com/vacp2p/zerokit.git
|
|
cd zerokit/rln
|
|
```
|
|
|
|
### Build and Test
|
|
|
|
To build and test, run the following commands within the module folder
|
|
```bash
|
|
cargo make build
|
|
cargo make test
|
|
```
|
|
|
|
### Compile ZK circuits
|
|
|
|
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
|
|
git submodule update --init --recursive
|
|
|
|
# Install rln dependencies
|
|
cd vendor/rln/ && npm install
|
|
|
|
# Build circuits
|
|
./scripts/build-circuits.sh rln
|
|
|
|
# Copy over assets
|
|
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 `vendor/rln/circuit/rln.circom`.
|
|
|
|
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;
|
|
|
|
include "./rln-base.circom";
|
|
|
|
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.
|
|
|
|
|
|
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::<u8>::new());
|
|
rln.key_gen(&mut buffer).unwrap();
|
|
|
|
// We deserialize the keygen output to obtain
|
|
// the identiy_secret and id_commitment
|
|
let (identity_secret_hash, 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_hash, 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::<u8>::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. |