mirror of https://github.com/vacp2p/rfc.git
fix(32/RLN): update RFC according to design changes (#561)
* fix(rln): update rfc according to design changes * fix(rln): formatting table * fix(rln): format table * fix(rln): typos, distinction epoch/external nullifier, rust code, removed obsolete stuff * fix(rln): wrong capital letter in link * fix(rln): specify internal_nullifier depends on app
This commit is contained in:
parent
2b72238134
commit
66d73a27f4
|
@ -33,7 +33,7 @@ Registration to the group is mandatory for signaling in the application.
|
|||
After registration, group members can generate Zero-knowledge Proof of membership for their signals and can participate in the application.
|
||||
Usually, the membership requires a financial or social stake which
|
||||
is beneficial for the prevention of Sybil attacks and double-signaling.
|
||||
Group members are allowed to send one signal per external nullifier (an identifier that groups signals and can be thought of as a voting booth. Usually a timestamp or time interval in the RLN apps).
|
||||
Group members are allowed to send one signal per external nullifier (an identifier that groups signals and can be thought of as a voting booth).
|
||||
If a user generates more signals than allowed,
|
||||
the user risks being slashed - by revealing his membership secret credentials.
|
||||
If the financial stake is put in place, the user also risks his stake being taken.
|
||||
|
@ -52,7 +52,7 @@ Depending on the application requirements, the registration can be implemented i
|
|||
- decentralized registrations, by using a smart contract
|
||||
|
||||
What is important is that the users' identity commitments (explained in section [User Indetity](#user-identity)) are stored in a Merkle tree,
|
||||
and the users can obtain a merkle proof proving that they are part of the group.
|
||||
and the users can obtain a Merkle proof proving that they are part of the group.
|
||||
|
||||
Also depending on the application requirements,
|
||||
usually a financial or social stake is introduced.
|
||||
|
@ -76,7 +76,7 @@ The user's identity is composed of:
|
|||
```
|
||||
|
||||
For registration, the user needs to submit their `identity_commitment` (along with any additional registration requirements) to the registry.
|
||||
Upon registration, they should receive `leaf_index` value which represents their position in the merkle tree.
|
||||
Upon registration, they should receive `leaf_index` value which represents their position in the Merkle tree.
|
||||
Receiving a `leaf_index` is not a hard requirement and is application specific.
|
||||
The other way around is the users calculating the `leaf_index` themselves upon successful registration.
|
||||
|
||||
|
@ -90,8 +90,8 @@ they need to generate a ZK-Proof by using the circuit with the specification des
|
|||
For generating a proof,
|
||||
the users need to obtain the required parameters or compute them themselves,
|
||||
depending on the application implementation and client libraries supported by the application.
|
||||
For example the users can store the membership merkle tree on their end and
|
||||
generate a merkle proof whenever they want to generate a signal.
|
||||
For example the users can store the membership Merkle tree on their end and
|
||||
generate a Merkle proof whenever they want to generate a signal.
|
||||
|
||||
### Implementation notes
|
||||
|
||||
|
@ -101,14 +101,18 @@ The signal hash can be generated by hashing the raw signal (or content) using th
|
|||
|
||||
#### External nullifier
|
||||
|
||||
The external nullifier can be generated by hashing a raw string (i.e UNIX timestamp) value using `keccak256`.
|
||||
The external nullifier MUST be computed as the Poseidon hash of the current epoch (e.g. a value equal to or derived from the current UNIX timestamp divided by the epoch length) and the RLN identifier.
|
||||
|
||||
#### Obtaining merkle proof
|
||||
```
|
||||
external_nullifier = poseidonHash([epoch, rln_identifier])
|
||||
```
|
||||
|
||||
The merkle proof should be obtained locally or from a trusted third party.
|
||||
By using the [incremental merkle tree algorithm](https://github.com/appliedzkp/incrementalquintree/blob/master/ts/IncrementalQuinTree.ts),
|
||||
the merkle can be obtained by providing the `leaf_index` of the `identity_commitment`.
|
||||
The proof (`merkle_proof`) is composed of the following fields:
|
||||
#### Obtaining Merkle proof
|
||||
|
||||
The Merkle proof should be obtained locally or from a trusted third party.
|
||||
By using the [incremental Merkle tree algorithm](https://github.com/appliedzkp/incrementalquintree/blob/master/ts/IncrementalQuinTree.ts),
|
||||
the Merkle can be obtained by providing the `leaf_index` of the `identity_commitment`.
|
||||
The proof (`Merkle_proof`) is composed of the following fields:
|
||||
|
||||
```
|
||||
{
|
||||
|
@ -119,8 +123,8 @@ The proof (`merkle_proof`) is composed of the following fields:
|
|||
```
|
||||
|
||||
1. **root** - The root of membership group Merkle tree at the time of publishing the message
|
||||
2. **indices** - The index fields of the leafs in the merkle tree - used by the merkle tree algorithm for verification
|
||||
3. **path_elements** - Auxiliary data structure used for storing the path to the leaf - used by the merkle proof algorithm for verificaton
|
||||
2. **indices** - The index fields of the leafs in the Merkle tree - used by the Merkle tree algorithm for verification
|
||||
3. **path_elements** - Auxiliary data structure used for storing the path to the leaf - used by the Merkle proof algorithm for verificaton
|
||||
|
||||
|
||||
#### Generating proof
|
||||
|
@ -131,10 +135,10 @@ the user need to submit the following fields to the circuit:
|
|||
```
|
||||
{
|
||||
identity_secret: identity_secret_hash,
|
||||
path_elements: merkle_proof.path_elements,
|
||||
identity_path_index: merkle_proof.indices,
|
||||
path_elements: Merkle_proof.path_elements,
|
||||
identity_path_index: Merkle_proof.indices,
|
||||
x: signal_hash,
|
||||
external_nullifier: external_nullifier,
|
||||
epoch: epoch,
|
||||
rln_identifier: rln_identifier
|
||||
}
|
||||
```
|
||||
|
@ -145,13 +149,13 @@ the user need to submit the following fields to the circuit:
|
|||
The proof output is calculated locally,
|
||||
in order for the required fields for proof verification to be sent along with the proof.
|
||||
The proof output is composed of the `y` share of the secret equation and the `internal_nullifier`.
|
||||
The `internal_nullifier` represents an unique fingerprint for the user for the `external_nullifier`.
|
||||
The `internal_nullifier` represents a unique fingerprint of a user for a given `epoch` and app.
|
||||
The following fields are needed for proof output calculation:
|
||||
|
||||
```
|
||||
{
|
||||
identity_secret_hash: bigint,
|
||||
external_nullifier: bigint,
|
||||
epoch: bigint,
|
||||
rln_identifier: bigint,
|
||||
x: bigint,
|
||||
}
|
||||
|
@ -160,12 +164,14 @@ The following fields are needed for proof output calculation:
|
|||
The output `[y, internal_nullifier]` is calculated in the following way:
|
||||
|
||||
```
|
||||
a_0 = identity_secret
|
||||
external_nullifier = poseidonHash([epoch, rln_identifier])
|
||||
|
||||
a_0 = identity_secret_hash
|
||||
a_1 = poseidonHash([a0, external_nullifier])
|
||||
|
||||
y = a_0 + x * a_1
|
||||
|
||||
internal_nullifier = poseidonHash([a_1, rln_identifier])
|
||||
internal_nullifier = poseidonHash([a_1])
|
||||
```
|
||||
|
||||
It relies on the properties of the [Shamir's Secret sharing scheme](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing).
|
||||
|
@ -191,8 +197,8 @@ the following fields might be required:
|
|||
|
||||
```
|
||||
{
|
||||
root: merkle_proof.root,
|
||||
external_nullifier: external_nullifier
|
||||
root: Merkle_proof.root,
|
||||
epoch: epoch
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -209,9 +215,9 @@ the slashing will be implemented on each user's client.
|
|||
### Implementation notes
|
||||
|
||||
Each user of the protocol (server or otherwise) will need to store metadata for each message received by each user,
|
||||
for the given `external_nullifier`.
|
||||
The data can be deleted when the `external_nullifier` passes.
|
||||
Storing metadata is required, so that if a user sends more than one unique signal per `external_nullifier`,
|
||||
for the given `epoch`.
|
||||
The data can be deleted when the `epoch` passes.
|
||||
Storing metadata is required, so that if a user sends more than one unique signal per `epoch`,
|
||||
they can be slashed and removed from the protocol.
|
||||
The metadata stored contains the `x`, `y` shares and the `internal_nullifier` for the user for each message.
|
||||
If enough such shares are present, the user's secret can be retreived.
|
||||
|
@ -238,7 +244,7 @@ The output message verification consists of the following steps:
|
|||
- spam verification
|
||||
|
||||
**1. `external_nullifier` correctness**
|
||||
Upon received `output_message`, first the `external_nullifier` field is checked,
|
||||
Upon received `output_message`, first the `epoch` and `rln_identifier` fields are checked,
|
||||
to ensure that the message matches the current `external_nullifier`.
|
||||
If the `external_nullifier` is correct the verification continues, otherwise, the message is discarded.
|
||||
|
||||
|
@ -256,10 +262,10 @@ The `zk_proof` should be verified by providing the `zk_proof` field to the circu
|
|||
```
|
||||
[
|
||||
y,
|
||||
merkle_proof.root,
|
||||
Merkle_proof.root,
|
||||
internal_nullifier,
|
||||
signal_hash,
|
||||
external_nullifier,
|
||||
x, # signal_hash
|
||||
epoch,
|
||||
rln_identifier
|
||||
]
|
||||
```
|
||||
|
@ -280,13 +286,13 @@ The secret can be retreived by the properties of the Shamir's secret sharing sch
|
|||
In particular the secret (`a_0`) can be retrieved by computing [Lagrange polynomials](https://en.wikipedia.org/wiki/Lagrange_polynomial).
|
||||
|
||||
After the secret is retreived,
|
||||
the user's `identity_commitment` can be generated from the secret and it can be used for removing the user from the membership merkle tree (zeroing out the leaf that contains the user's `identity_commitment`).
|
||||
the user's `identity_commitment` can be generated from the secret and it can be used for removing the user from the membership Merkle tree (zeroing out the leaf that contains the user's `identity_commitment`).
|
||||
Additionally, depending on the application the `identity_secret_hash` can be used for taking the user's provided stake.
|
||||
|
||||
# Technical overview
|
||||
|
||||
The main RLN construct is implemented using a [ZK-SNARK](https://z.cash/technology/zksnarks/) circuit.
|
||||
However it is helpful to describe the other necessary outside components for interaction with the circuit,
|
||||
However, it is helpful to describe the other necessary outside components for interaction with the circuit,
|
||||
which together with the ZK-SNARK circuit enable the above mentioned features.
|
||||
|
||||
|
||||
|
@ -302,8 +308,8 @@ which together with the ZK-SNARK circuit enable the above mentioned features.
|
|||
| **Identity secret hash** | The hash of the identity secret, obtained using the Poseidon hash function. It is used for deriving the identity commitment of the user, and as a private input for zk proof generation. The secret hash should be kept private by the user. |
|
||||
| **Identity commitment** | Hash obtained from the `Identity secret hash` by using the poseidon hash function. It is used by the users for registering in the protocol. |
|
||||
| **Signal** | The message generated by a user. It is an arbitrary bit string that may represent a chat message, a URL request, protobuf message, etc. |
|
||||
| **Signal hash** | Keccak hash of the signal, used as an input in the RLN circuit. |
|
||||
| **RLN Identifier** | Random finite field value unique per RLN app. It is used for additional cross-application security. The role of the RLN identifier is protection of the user secrets being compromised if signals are being generated with the same credentials at different apps. |
|
||||
| **Signal hash** | Keccak256 hash of the signal modulo circuit's field characteristic, used as an input in the RLN circuit. |
|
||||
| **RLN Identifier** | Random finite field value unique per RLN app. It is used for additional cross-application security. The role of the RLN identifier is protection of the user secrets from being compromised when signals are being generated with the same credentials in different apps. |
|
||||
| **RLN membership tree** | Merkle tree data structure, filled with identity commitments of the users. Serves as a data structure that ensures user registrations. |
|
||||
| **Merkle proof** | Proof that a user is member of the RLN membership tree. |
|
||||
|
||||
|
@ -316,8 +322,8 @@ which together with the ZK-SNARK circuit enable the above mentioned features.
|
|||
| **A0** | The identity secret hash. |
|
||||
| **A1** | Poseidon hash of [A0, External nullifier] (see about External nullifier below). |
|
||||
| **y** | The result of the polynomial equation (y = a0 + a1*x). The public output of the circuit. |
|
||||
| **External nullifier** | `keccak256` hash of a string. An identifier that groups signals and can be thought of as a voting booth. Usually a timestamp or time interval in the RLN apps. |
|
||||
| **Internal nullifier** | Poseidon hash of [A1, RLN Identifier]. This field ensures that a user can send only one valid signal per external nullifier without risking being slashed. Public input of the circuit. |
|
||||
| **External nullifier** | Poseidon hash of [Epoch, RLN Identifier]. An identifier that groups signals and can be thought of as a voting booth. |
|
||||
| **Internal nullifier** | Poseidon hash of [A1]. This field ensures that a user can send only one valid signal per external nullifier without risking being slashed. Public input of the circuit. |
|
||||
|
||||
|
||||
|
||||
|
@ -325,7 +331,7 @@ which together with the ZK-SNARK circuit enable the above mentioned features.
|
|||
|
||||
Anonymous signaling with a controlled rate limit is enabled by proving that the user is part of a group which has high barriers to entry (form of stake) and
|
||||
enabling secret reveal if more than 1 unique signal is produced per external nullifier.
|
||||
The membership part is implemented using membership [merkle trees](https://en.wikipedia.org/wiki/Merkle_tree) and merkle proofs,
|
||||
The membership part is implemented using membership [Merkle trees](https://en.wikipedia.org/wiki/Merkle_tree) and Merkle proofs,
|
||||
while the secret reveal part is enabled by using the Shamir's Secret Sharing scheme.
|
||||
Essentially the protocol requires the users to generate zero-knowledge proof to be able to send signals and participate in the application.
|
||||
The zero knowledge proof proves that the user is member of a group,
|
||||
|
@ -339,14 +345,14 @@ using the [circomlib](https://docs.circom.io/) library.
|
|||
|
||||
### System parameters
|
||||
|
||||
- `n_levels` - merkle tree depth
|
||||
- `n_levels` - Merkle tree depth
|
||||
|
||||
|
||||
### Circuit parameters
|
||||
|
||||
**Public Inputs**
|
||||
- `x`
|
||||
- `external_nullifier`
|
||||
- `epoch`
|
||||
- `rln_identifier`
|
||||
|
||||
**Private Inputs**
|
||||
|
@ -365,9 +371,17 @@ Canonical [Poseidon hash implementation](https://eprint.iacr.org/2019/458.pdf) i
|
|||
as implemented in the [circomlib library](https://github.com/iden3/circomlib/blob/master/circuits/poseidon.circom), according to the Poseidon paper.
|
||||
This Poseidon hash version (canonical implementation) uses the following parameters:
|
||||
|
||||
- `t`: 3
|
||||
- `RF`: 8
|
||||
- `RP`: 57
|
||||
| Hash inputs | `t` | `RF` | `RP`|
|
||||
|:---:|:---:|:---:|:---:|
|
||||
|1 | 2 | 8 | 56|
|
||||
|2 | 3 | 8 | 57|
|
||||
|3 | 4 | 8 | 56|
|
||||
|4 | 5 | 8 | 60|
|
||||
|5 | 6 | 8 | 60|
|
||||
|6 | 7 | 8 | 63|
|
||||
|7 | 8 | 8 | 64|
|
||||
|8 | 9 | 8 | 63|
|
||||
|
||||
|
||||
### Membership implementation
|
||||
|
||||
|
@ -376,9 +390,9 @@ Membership is proven by providing a membership proof (witness).
|
|||
The fields from the membership proof required for the verification are:
|
||||
`path_elements` and `identity_path_index`.
|
||||
|
||||
[IncrementalQuinTree](https://github.com/appliedzkp/incrementalquintree) algorithm is used for constructing the Membership merkle tree.
|
||||
[IncrementalQuinTree](https://github.com/appliedzkp/incrementalquintree) algorithm is used for constructing the Membership Merkle tree.
|
||||
The circuits are reused from this repository.
|
||||
You can find out more details about the IncrementalQuinTree algorithm [here](https://ethresear.ch/t/gas-and-circuit-constraint-benchmarks-of-binary-and-quinary-incremental-merkle-trees-using-the-poseidon-hash-function/7446).
|
||||
You can find out more details about the IncrementalQuinTree algorithm [here](https://ethresear.ch/t/gas-and-circuit-constraint-benchmarks-of-binary-and-quinary-incremental-Merkle-trees-using-the-poseidon-hash-function/7446).
|
||||
|
||||
### Slashing and Shamir's Secret Sharing
|
||||
|
||||
|
@ -387,19 +401,21 @@ In order to produce a valid proof, `identity_secret_hash` as a private input to
|
|||
Then a secret equation is created in the form of:
|
||||
|
||||
```
|
||||
y = a_0 + x*a_1,
|
||||
y = a_0 + x * a_1,
|
||||
```
|
||||
|
||||
where `a_0` is the `identity_secret_hash` and `a_1 = hash(a_0, external nullifier)`.
|
||||
Along with the generated proof,
|
||||
the users need to provide a (x, y) share which satisfies the line equation,
|
||||
the users need to provide a `(x, y)` share which satisfies the line equation,
|
||||
in order for their proof to be verified.
|
||||
`x` is the hashed signal, while the `y` is the circuit output.
|
||||
With more than one pair of unique shares, anyone can derive `a_0`, the `identity_secret_hash` .
|
||||
The hash of a signal will be evaluation point `x`.
|
||||
So that a member who sends more than one unique signal per `external_nullifier` risks their identity secret being revealed.
|
||||
With more than one pair of unique shares, anyone can derive `a_0`, i.e. the `identity_secret_hash` .
|
||||
The hash of a signal will be the evaluation point `x`.
|
||||
In this way, a member who sends more than one unique signal per `external_nullifier` risks their identity secret being revealed.
|
||||
|
||||
Note that shares used for different external nullifiers and different RLN apps cannot be used to derive the secret key.
|
||||
Note that shares used in different epochs and different RLN apps cannot be used to derive the identity secret hash.
|
||||
|
||||
Thanks to the `external_nullifier` definition, also shares computed from same secret within same epoch but in different RLN apps cannot be used to derive the identity secret hash.
|
||||
|
||||
The `rln_identifier` is a random value from a finite field,
|
||||
unique per RLN app,
|
||||
|
@ -410,14 +426,11 @@ then their user signals can be grouped by the `internal_nullifier` which could l
|
|||
This is because two separate signals under the same `internal_nullifier` can be treated as rate limiting violation.
|
||||
With adding the `rln_identifier` field we obscure the `internal_nullifier`,
|
||||
so this kind of attack can be hardened because we don't have the same `internal_nullifier` anymore.
|
||||
The only kind of attack that is possible is if we have an entity with a global view of all messages,
|
||||
and they try to brute force different combinations of `x` and `y` shares for different `internal_nullifier`s.
|
||||
|
||||
|
||||
## Identity credentials generation
|
||||
|
||||
In order to be able to generate valid proofs, the users need to be part of the identity membership merkle tree.
|
||||
They are part of the identity membership merkle tree if their `identity_commitment` is placed in a leaf in the tree.
|
||||
In order to be able to generate valid proofs, the users need to be part of the identity membership Merkle tree.
|
||||
They are part of the identity membership Merkle tree if their `identity_commitment` is placed in a leaf in the tree.
|
||||
|
||||
The identity credentials of a user are composed of:
|
||||
|
||||
|
@ -518,85 +531,108 @@ Also for RLN we do a single secret component input for the circuit.
|
|||
Thus we need to hash the secret array (two components) to a secret hash,
|
||||
and we use that as a secret component input.
|
||||
|
||||
# Apendix C: Auxiliary tooling
|
||||
# Appendix C: Auxiliary tooling
|
||||
|
||||
There are few additional tools implemented for easier integrations and usage of the RLN protocol.
|
||||
|
||||
[`zerokit`](https://github.com/vacp2p/zerokit) is a set of Zero Knowledge modules, written in Rust and designed to be used in many different environments.
|
||||
Among different modules, it supports `Semaphore` and `RLN`.
|
||||
|
||||
[`zk-kit`](https://github.com/appliedzkp/zk-kit) is a typescript library which exposes APIs for identity credentials generation,
|
||||
as well as proof generation.
|
||||
It supports various protocols (`Semaphore`, `RLN`),.
|
||||
It supports various protocols (`Semaphore`, `RLN`).
|
||||
|
||||
[`zk-keeper`](https://github.com/akinovak/zk-keeper) is a browser plugin which allows for safe credential storing and proof generation.
|
||||
You can think of MetaMask for ZK-Proofs.
|
||||
It uses `zk-kit` under the hood.
|
||||
|
||||
# Apendix D: Example usage
|
||||
# Appendix D: Example usage
|
||||
|
||||
The following examples are code snippets using the `zk-kit` library.
|
||||
The examples are written in [typescript](https://www.typescriptlang.org/).
|
||||
The following examples are code snippets using the `zerokit` RLN module.
|
||||
The examples are written in [rust](https://www.rust-lang.org/).
|
||||
|
||||
## Creating a RLN object
|
||||
|
||||
```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);
|
||||
```
|
||||
|
||||
## Generating identity credentials
|
||||
|
||||
```typescript
|
||||
import { ZkIdentity } from "@zk-kit/identity"
|
||||
```rust
|
||||
// We generate an identity tuple
|
||||
let mut buffer = Cursor::new(Vec::<u8>::new());
|
||||
rln.extended_key_gen(&mut buffer).unwrap();
|
||||
|
||||
const identity = new ZkIdentity()
|
||||
const identityCommitment = identity.genIdentityCommitment()
|
||||
const secretHash = identity.getSecretHash()
|
||||
// We deserialize the keygen output to obtain
|
||||
// the identiy_secret and id_commitment
|
||||
let (identity_trapdoor, identity_nullifier, identity_secret_hash, id_commitment) = deserialize_identity_tuple(buffer.into_inner());
|
||||
```
|
||||
|
||||
## Adding 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();
|
||||
```
|
||||
|
||||
## Setting epoch and signal
|
||||
|
||||
```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");
|
||||
|
||||
// We set our signal
|
||||
let signal = b"RLN is awesome";
|
||||
```
|
||||
|
||||
## Generating proof
|
||||
|
||||
```typescript
|
||||
import { RLN, MerkleProof, FullProof, genSignalHash, generateMerkleProof, genExternalNullifier } from '@zk-kit/protocols'
|
||||
import { ZkIdentity } from '@zk-kit/identity'
|
||||
import { bigintToHex, hexToBigint } from 'bigint-conversion'
|
||||
```rust
|
||||
// We prepare input to the proof generation routine
|
||||
let proof_input = prepare_prove_input(identity_secret, id_index, epoch, signal);
|
||||
|
||||
const ZERO_VALUE = BigInt(0);
|
||||
const TREE_DEPTH = 15;
|
||||
const LEAVES_PER_NODE = 2;
|
||||
const LEAVES = [...]; // leaves should be an array of the leaf values of the membership merkle tree
|
||||
// the identity commitment generated below should be present in the LEAVES array
|
||||
// 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();
|
||||
|
||||
// this is for illustrative purposes only. The identityCommitment should be present in the LEAVES array above.
|
||||
const identity = new ZkIdentity()
|
||||
const secretHash = identity.getSecretHash()
|
||||
const identityCommitment = identity.genIdentityCommitment()
|
||||
|
||||
const signal = "hey"
|
||||
const signalHash = genSignalHash(signal)
|
||||
const epoch = genExternalNullifier("test-epoch")
|
||||
const rlnIdentifier = RLN.genIdentifier()
|
||||
|
||||
const merkleProof = generateMerkleProof(TREE_DEPTH, ZERO_VALUE, LEAVES_PER_NODE, LEAVES, identityCommitment)
|
||||
const witness = RLN.genWitness(secretHash, merkleProof, epoch, signal, rlnIdentifier)
|
||||
|
||||
const [y, nullifier] = RLN.calculateOutput(secretHash, BigInt(epoch), rlnIdentifier, signalHash)
|
||||
const publicSignals = [y, merkleProof.root, nullifier, signalHash, epoch, rlnIdentifier]
|
||||
|
||||
const wasmFilePath = path.join(zkeyFiles, "rln", "rln.wasm") // path to the WASM compiled circuit
|
||||
const finalZkeyPath = path.join(zkeyFiles, "rln", "rln_final.zkey") // path to the prover key
|
||||
|
||||
const fullProof = await RLN.genProof(witness, wasmFilePath, finalZkeyPath)
|
||||
// We get the public outputs returned by the circuit evaluation
|
||||
let proof_data = out_buffer.into_inner();
|
||||
```
|
||||
|
||||
## Verifiying proof
|
||||
|
||||
```typescript
|
||||
import { RLN } from '@zk-kit/protocols'
|
||||
```rust
|
||||
// We prepare input to the proof verification routine
|
||||
let verify_data = prepare_verify_input(proof_data, signal);
|
||||
|
||||
// Public signal and the proof are received from the proving party
|
||||
// const publicSignals = [y, merkleProof.root, nullifier, signalHash, epoch, rlnIdentifier]
|
||||
// const proof = (await RLN.genProof(witness, wasmFilePath, finalZkeyPath)).proof
|
||||
// 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();
|
||||
|
||||
const vkeyPath = path.join(zkeyFiles, "rln", "verification_key.json") // Path to the verifier key
|
||||
const vKey = JSON.parse(fs.readFileSync(vkeyPath, "utf-8")) // The verifier key
|
||||
|
||||
const response = await RLN.verifyProof(vKey, { proof: proof, publicSignals })
|
||||
// We ensure the proof is valid
|
||||
assert!(verified);
|
||||
```
|
||||
|
||||
For more details please visit the [`zk-kit`](https://github.com/appliedzkp/zk-kit) library.
|
||||
For more details please visit the [`zerokit`](https://github.com/vacp2p/zerokit) library.
|
||||
|
||||
# Copyright
|
||||
|
||||
|
|
Loading…
Reference in New Issue