# Changes - Unified the `RLN` struct and core protocol types across public, FFI, and WASM so everything works consistently. - Fully refactored `protocol.rs` and `public.rs` to clean up the API surface and make the flow easier to work with. - Added (de)serialization for `RLN_Proof` and `RLN_ProofValues`, and matched all C, Nim, WASM, and Node.js examples. - Aligned FFI and WASM behavior, added missing APIs, and standardized how witness are created and passed around. - Reworked the error types, added clearer verification messages, and simplified the overall error structure. - Updated variable names, README, Rust docs, and examples across the repo, updated outdated RLN RFC link. - Refactored `rln-cli` to use the new public API, removed serialize-based cli example, and dropped the `eyre` crate. - Bumped dependencies, fixed CI, fixed `+atomic` flags for latest nightly Rust and added `Clippy.toml` for better fmt. - Added a `prelude.rs` file for easier use, cleaned up public access for types and types import across zerokit modules. - Separated keygen, proof handling, slashing logic, and witness into protocol folder.
2.9 KiB
RLN Nim FFI example
This example shows how to use the RLN C FFI from Nim in stateless mode. It demonstrates:
- 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
Build the RLN library
From the repository root:
# Stateless build (no tree APIs)
cargo build -p rln --release --no-default-features --features stateless
# Non-stateless build (with tree APIs)
cargo build -p rln --release
This produces the shared library in target/release:
- macOS:
librln.dylib - Linux:
librln.so - Windows:
rln.dll
Build the Nim example (two modes)
From this directory:
# Stateless (uses local mock path, no tree exports)
nim c -d:release -d:ffiStateless main.nim
# Non-stateless (uses exported tree APIs to insert leaf and fetch proof)
nim c -d:release main.nim
Notes:
- The example links dynamically. If your OS linker cannot find the library at runtime, set an rpath or environment variable as shown below.
- The example auto-picks a platform-specific default library name.
You can override it with
-d:RLN_LIB:"/absolute/path/to/lib"if needed.
Run the example
Ensure the dynamic loader can find the RLN library, then run the binary.
macOS:
DYLD_LIBRARY_PATH=../../target/release ./main
Linux:
LD_LIBRARY_PATH=../../target/release ./main
Windows (PowerShell):
$env:PATH = "$PWD\..\..\target\release;$env:PATH"
./main.exe
You should see output similar to:
RLN created
Witness built
Proof generated
Verify: OK
What the example does (stateless mode)
-
Creates an RLN handle via the stateless constructor.
-
Generates identity keys and sets a
user_message_limitandmessage_id. -
Hashes a signal and external nullifier (
ffi_hash). -
Computes
rateCommitment = Poseidon(id_commitment, user_message_limit)usingffi_poseidon_hash. -
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 hashesH(0,0),H(H(0,0),H(0,0)), ... - Path indices: all zeros (left at every level)
- Root: folds the path upwards with
rateCommitmentat index 0
- Path siblings: level 0 sibling is
-
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)
-
Creates an RLN handle with a Merkle tree backend and configuration.
-
Generates identity keys and computes
rateCommitment = Poseidon(id_commitment, user_message_limit). -
Inserts the leaf with
ffi_set_next_leafand fetches a real Merkle path for index 0 viaffi_get_merkle_proof. -
Builds the witness from the exported proof, generates the proof, and verifies with
ffi_verify_rln_proofusing the current tree root.