diff --git a/.github/workflows/nightly-release.yml b/.github/workflows/nightly-release.yml index 02aea94..e1bfc82 100644 --- a/.github/workflows/nightly-release.yml +++ b/.github/workflows/nightly-release.yml @@ -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 diff --git a/rln-wasm/Makefile.toml b/rln-wasm/Makefile.toml index 139d868..8e39b80 100644 --- a/rln-wasm/Makefile.toml +++ b/rln-wasm/Makefile.toml @@ -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" diff --git a/rln-wasm/README.md b/rln-wasm/README.md index 2d64bd1..0164315 100644 --- a/rln-wasm/README.md +++ b/rln-wasm/README.md @@ -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(); } diff --git a/rln-wasm/examples/README.md b/rln-wasm/examples/README.md index fc62425..a6f7721 100644 --- a/rln-wasm/examples/README.md +++ b/rln-wasm/examples/README.md @@ -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 diff --git a/rln/ffi_c_examples/Readme.md b/rln/ffi_c_examples/Readme.md index 89ba74a..fc3c7b2 100644 --- a/rln/ffi_c_examples/Readme.md +++ b/rln/ffi_c_examples/Readme.md @@ -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 diff --git a/rln/ffi_nim_examples/README.md b/rln/ffi_nim_examples/README.md index 97533f8..e27ecb8 100644 --- a/rln/ffi_nim_examples/README.md +++ b/rln/ffi_nim_examples/README.md @@ -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\, 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 serialization: Vec <-> 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.