feat(rln-wasm): seperate rln wasm parallel package (#360)

This commit is contained in:
Vinh Trịnh
2025-12-18 16:48:10 +07:00
committed by GitHub
parent 2fc079d633
commit c0769395bd
6 changed files with 133 additions and 158 deletions

View File

@@ -115,17 +115,16 @@ jobs:
run: make installdeps
- name: Build rln-wasm package
run: |
if [[ ${{ matrix.feature }} == *parallel* ]]; then
if [[ ${{ matrix.feature }} == "parallel" ]]; then
env CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUSTFLAGS="-C target-feature=+atomics,+bulk-memory,+mutable-globals -C link-arg=--shared-memory -C link-arg=--max-memory=1073741824 -C link-arg=--import-memory -C link-arg=--export=__wasm_init_tls -C link-arg=--export=__tls_size -C link-arg=--export=__tls_align -C link-arg=--export=__tls_base" \
rustup run nightly wasm-pack build --release --target web --scope waku \
--features ${{ matrix.feature }} -Z build-std=panic_abort,std
else
wasm-pack build --release --target web --scope waku
fi
if [[ ${{ matrix.feature }} == "utils" ]]; then
--features parallel -Z build-std=panic_abort,std
sed -i.bak 's/rln-wasm/zerokit-rln-wasm-parallel/g' pkg/package.json && rm pkg/package.json.bak
elif [[ ${{ matrix.feature }} == "utils" ]]; then
wasm-pack build --release --target web --scope waku --no-default-features --features utils
sed -i.bak 's/rln-wasm/zerokit-rln-wasm-utils/g' pkg/package.json && rm pkg/package.json.bak
else
wasm-pack build --release --target web --scope waku
sed -i.bak 's/rln-wasm/zerokit-rln-wasm/g' pkg/package.json && rm pkg/package.json.bak
fi

View File

@@ -4,7 +4,11 @@ dependencies = ["pack_build", "pack_rename", "pack_add_keywords"]
[tasks.build_parallel]
clear = true
dependencies = ["pack_build_parallel", "pack_rename", "pack_add_keywords"]
dependencies = [
"pack_build_parallel",
"pack_rename_parallel",
"pack_add_keywords",
]
[tasks.build_utils]
clear = true
@@ -14,6 +18,9 @@ dependencies = ["pack_build_utils", "pack_rename_utils", "pack_add_keywords"]
command = "wasm-pack"
args = ["build", "--release", "--target", "web", "--scope", "waku"]
[tasks.pack_rename]
script = "sed -i.bak 's/rln-wasm/zerokit-rln-wasm/g' pkg/package.json && rm pkg/package.json.bak"
[tasks.pack_build_parallel]
command = "env"
args = [
@@ -34,8 +41,8 @@ args = [
"build-std=panic_abort,std",
]
[tasks.pack_rename]
script = "sed -i.bak 's/rln-wasm/zerokit-rln-wasm/g' pkg/package.json && rm pkg/package.json.bak"
[tasks.pack_rename_parallel]
script = "sed -i.bak 's/rln-wasm/zerokit-rln-wasm-parallel/g' pkg/package.json && rm pkg/package.json.bak"
[tasks.pack_build_utils]
command = "wasm-pack"

View File

@@ -14,145 +14,92 @@ RLN functionality in JavaScript/TypeScript applications.
> [!NOTE]
> This project requires the following tools:
>
> - `wasm-pack` - for compiling Rust to WebAssembly
> - `wasm-pack` (v0.13.1) - for compiling Rust to WebAssembly
> - `cargo-make` - for running build commands
> - `nvm` - to install and manage Node.js
>
> Ensure all dependencies are installed before proceeding.
> - `nvm` - to install and manage Node.js (v22.14.0+)
### Manually
#### Install `wasm-pack`
```bash
cargo install wasm-pack --version=0.13.1
```
#### Install `cargo-make`
```bash
cargo install cargo-make
```
#### Install `Node.js`
If you don't have `nvm` (Node Version Manager), install it by following
the [installation instructions](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script).
After installing `nvm`, install and use Node.js `v22.14.0`:
```bash
nvm install 22.14.0
nvm use 22.14.0
nvm alias default 22.14.0
```
If you already have Node.js installed,
check your version with `node -v` command — the version must be strictly greater than 22.
### Or install everything
You can run the following command from the root of the repository to install all required dependencies for `zerokit`
### Quick Install
```bash
make installdeps
```
## Building the library
### Manual Installation
First, navigate to the rln-wasm directory:
```bash
# Install wasm-pack
cargo install wasm-pack --version=0.13.1
# Install cargo-make
cargo install cargo-make
# Install Node.js via nvm
nvm install 22.14.0
nvm use 22.14.0
nvm alias default 22.14.0
```
## Building the Library
Navigate to the rln-wasm directory:
```bash
cd rln-wasm
```
Compile zerokit for `wasm32-unknown-unknown`:
Build commands:
```bash
cargo make build
cargo make build # Default → @waku/zerokit-rln-wasm
cargo make build_parallel # Parallel → @waku/zerokit-rln-wasm-parallel (requires nightly Rust)
cargo make build_utils # Utils only → @waku/zerokit-rln-wasm-utils
```
Or you can build the utility functions only without RLN proof generation and verification:
All packages output to `pkg/` directory.
## Running Tests and Benchmarks
```bash
cargo make build_utils
```
## Running tests and benchmarks
```bash
cargo make test
```
If you want to run the tests in browser headless mode, you can use the following command:
```bash
cargo make test_browser
```
If you want to test only the utility functions after running `cargo make build_utils`, you can use the following command:
```bash
cargo make test_utils
cargo make test # Standard tests
cargo make test_browser # Browser headless mode
cargo make test_utils # Utils-only tests
cargo make test_parallel # Parallel tests
```
## Examples
Working examples demonstrating proof generation, proof verification and slashing:
See [Node example](./examples/index.js) and [README](./examples/Readme.md) for proof generation, verification, and slashing.
- [Node example](./examples/index.js) and [README](./examples/Readme.md)
## Parallel Computation
## Parallel computation
The library supports parallel computation using the `wasm-bindgen-rayon` crate,
enabling multi-threaded execution in the browser.
Enables multi-threaded browser execution using `wasm-bindgen-rayon`.
> [!NOTE]
> Parallel support is not enabled by default due to WebAssembly and browser limitations. \
> Compiling this feature requires `nightly` Rust.
>
> - Parallel support is not enabled by default due to WebAssembly and browser limitations.
> - Requires `nightly` Rust: `rustup install nightly`
> - Browser-only (not compatible with Node.js)
> - Requires HTTP headers for `SharedArrayBuffer`:
> - `Cross-Origin-Opener-Policy: same-origin`
> - `Cross-Origin-Embedder-Policy: require-corp`
### Build Setup
### Usage
#### Install `nightly` Rust
Direct usage (modern browsers with WebAssembly threads support):
```bash
rustup install nightly
```js
import * as wasmPkg from '@waku/zerokit-rln-wasm-parallel';
await wasmPkg.default();
await wasmPkg.initThreadPool(navigator.hardwareConcurrency);
wasmPkg.nowCallAnyExportedFuncs();
```
### Build Commands
### Feature Detection for Older Browsers
To enable parallel computation for WebAssembly threads, you can use the following command:
If you're targeting [older browser versions that didn't support WebAssembly threads yet](https://webassembly.org/roadmap/), you'll want to use both builds - the parallel version for modern browsers and the default version as a fallback. Use feature detection to choose the appropriate build on the JavaScript side.
```bash
cargo make build_parallel
```
### Running parallel tests and benchmarks
If you want to run the parallel tests in browser headless mode, you can use the following command:
```bash
cargo make test_parallel
```
### WebAssembly Threads Support
Most modern browsers support WebAssembly threads,
but they require the following headers to enable `SharedArrayBuffer`, which is necessary for multithreading:
- Cross-Origin-Opener-Policy: same-origin
- Cross-Origin-Embedder-Policy: require-corp
Without these, the application will fall back to single-threaded mode.
## Feature detection
If you're targeting [older browser versions that didn't support WebAssembly threads yet](https://webassembly.org/roadmap/),
you'll likely want to create two builds - one with threads support and one without -
and use feature detection to choose the right one on the JavaScript side.
You can use [wasm-feature-detect](https://github.com/GoogleChromeLabs/wasm-feature-detect) library for this purpose.
For example, your code might look like this:
You can use the [wasm-feature-detect](https://github.com/GoogleChromeLabs/wasm-feature-detect) library for this purpose:
```js
import { threads } from 'wasm-feature-detect';
@@ -160,11 +107,11 @@ import { threads } from 'wasm-feature-detect';
let wasmPkg;
if (await threads()) {
wasmPkg = await import('./pkg-with-threads/index.js');
wasmPkg = await import('@waku/zerokit-rln-wasm-parallel');
await wasmPkg.default();
await wasmPkg.initThreadPool(navigator.hardwareConcurrency);
} else {
wasmPkg = await import('./pkg-without-threads/index.js');
wasmPkg = await import('@waku/zerokit-rln-wasm');
await wasmPkg.default();
}

View File

@@ -1,6 +1,8 @@
# Compile and Run
# RLN WASM Node Examples
## Build the rln-wasm package at the root of rln-wasm
This example demonstrates how to use the RLN WASM package in a Node.js environment.
## Build the @waku/zerokit-rln-wasm package at the root of rln-wasm module
```bash
cargo make build

View File

@@ -1,4 +1,6 @@
# Compile and Run
# RLN FFI C example
This example demonstrates how to use the RLN C FFI in both stateless and non-stateless modes.
## Non-stateless mode

View File

@@ -1,10 +1,13 @@
# RLN Nim FFI example
# RLN FFI Nim example
This example shows how to use the RLN C FFI from Nim in stateless mode. It demonstrates:
This example demonstrates how to use the RLN C FFI from Nim in both stateless and non-stateless modes. It covers:
- Creating an RLN handle using the stateless constructor
- Building a witness for a mock Merkle path (no exported tree APIs)
- Generating a proof and verifying it
- Creating an RLN handle (stateless or with Merkle tree backend)
- Generating identity keys and commitments
- Building a witness (mock Merkle path in stateless mode, real Merkle proof in non-stateless mode)
- Generating and verifying a proof
- Serializing/deserializing FFI objects (CFr, Vec\<CFr>, RLNWitnessInput, RLNProof, RLNProofValues)
- Simulating a double-signaling attack and recovering the identity secret
## Build the RLN library
@@ -29,10 +32,10 @@ This produces the shared library in `target/release`:
From this directory:
```bash
# Stateless (uses local mock path, no tree exports)
# Stateless mode (no tree APIs, uses mock Merkle path)
nim c -d:release -d:ffiStateless main.nim
# Non-stateless (uses exported tree APIs to insert leaf and fetch proof)
# Non-stateless mode (uses exported tree APIs to insert leaf and fetch proof)
nim c -d:release main.nim
```
@@ -66,41 +69,56 @@ $env:PATH = "$PWD\..\..\target\release;$env:PATH"
./main.exe
```
You should see output similar to:
You should see detailed output showing each step, for example:
```powershell
RLN created
Witness built
Proof generated
Verify: OK
```text
Creating RLN instance
RLN instance created successfully
Generating identity keys
Identity generated
- identity_secret = ...
- id_commitment = ...
Creating message limit
- user_message_limit = ...
Computing rate commitment
- rate_commitment = ...
CFr serialization: CFr <-> bytes
- serialized rate_commitment = ...
- deserialized rate_commitment = ...
Vec<CFr> serialization: Vec<CFr> <-> bytes
- serialized keys = ...
- deserialized keys = ...
... (Merkle path, hashing, witness, proof, verification, and slashing steps) ...
Proof verified successfully
Slashing successful: Identity is recovered!
```
## What the example does (stateless mode)
## What the example does
1) Creates an RLN handle via the stateless constructor.
### Stateless mode
2) Generates identity keys and sets a `user_message_limit` and `message_id`.
1. Creates an RLN handle via the stateless constructor.
2. Generates identity keys, sets a `user_message_limit` and `message_id`.
3. Hashes a signal, epoch, and RLN identifier to field elements.
4. Computes `rateCommitment = Poseidon(id_commitment, user_message_limit)`.
5. Builds a mock Merkle path for an empty depth-20 tree at index 0 (no exported tree APIs):
- Path siblings: level 0 sibling is `0`, then each level uses precomputed default hashes `H(0,0)`, `H(H(0,0),H(0,0))`, ...
- Path indices: all zeros (left at every level)
- Root: folds the path upwards with `rateCommitment` at index 0
6. Builds the witness, generates the proof, and verifies it with `ffi_verify_with_roots`, passing a one-element roots vector containing the computed root.
7. Simulates a double-signaling attack and recovers the identity secret from two proofs.
3) Hashes a signal and external nullifier (`ffi_hash`).
### Non-stateless mode
4) Computes `rateCommitment = Poseidon(id_commitment, user_message_limit)` using `ffi_poseidon_hash`.
5) Builds a mock Merkle path for an empty depth-20 tree at index 0 (no exported tree APIs):
- Path siblings: level 0 sibling is `0`,
then each level uses precomputed default hashes `H(0,0)`, `H(H(0,0),H(0,0))`, ...
- Path indices: all zeros (left at every level)
- Root: folds the path upwards with `rateCommitment` at index 0
6) Builds the witness, generates the proof, and verifies it with `ffi_verify_with_roots`,
passing a one-element roots vector containing the computed root (length must be 1).
## What the example does (non-stateless mode)
1) Creates an RLN handle with a Merkle tree backend and configuration.
2) Generates identity keys and computes `rateCommitment = Poseidon(id_commitment, user_message_limit)`.
3) Inserts the leaf with `ffi_set_next_leaf` and fetches a real Merkle path for index 0 via `ffi_get_merkle_proof`.
4) Builds the witness from the exported proof, generates the proof, and verifies with `ffi_verify_rln_proof` using the current tree root.
1. Creates an RLN handle with a Merkle tree backend and configuration.
2. Generates identity keys and computes `rateCommitment = Poseidon(id_commitment, user_message_limit)`.
3. Inserts the leaf with `ffi_set_next_leaf` and fetches a real Merkle path for index 0 via `ffi_get_merkle_proof`.
4. Builds the witness from the exported proof, generates the proof, and verifies with `ffi_verify_rln_proof` using the current tree root.
5. Simulates a double-signaling attack and recovers the identity secret from two proofs.