mirror of
https://github.com/vacp2p/zerokit.git
synced 2026-01-09 13:47:58 -05:00
Enable parallel execution for rln-wasm module (#296)
## Changes
- Enabled parallelism in the browser for `rln-wasm` with the
`multithread` feature flag.
- Added browser tests for both single-threaded and multi-threaded modes.
- Enabled browser tests in the CI workflow.
- Pending: resolving hanging issue with `wasm-bindgen-rayon`
([comment](https://github.com/RReverser/wasm-bindgen-rayon/issues/6#issuecomment-2814372940)).
- Forked [this
commit](42887c80e6)
into a separate
[branch](https://github.com/vacp2p/zerokit/tree/benchmark-v0.8.0), which
includes an HTML benchmark file and a test case for the multithreaded
feature in `rln-wasm`.
- The test case still has the known issue above, so it's temporarily
disabled in this PR and will be addressed in the future.
- Improve the `make installdeps` which resolves the issue of NVM not
enabling Node.js in the current terminal session.
- Reduce the build size of the `.wasm` blob using the `wasm-opt` tool
from [Binaryen](https://github.com/WebAssembly/binaryen).
- Maybe we can close this draft
[PR](https://github.com/vacp2p/zerokit/pull/226), which is already very
outdated?
This commit is contained in:
91
.github/workflows/ci.yml
vendored
91
.github/workflows/ci.yml
vendored
@@ -24,8 +24,8 @@ jobs:
|
|||||||
utils-test:
|
utils-test:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
platform: [ ubuntu-latest, macos-latest ]
|
platform: [ubuntu-latest, macos-latest]
|
||||||
crate: [ utils ]
|
crate: [utils]
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
|
|
||||||
@@ -50,9 +50,9 @@ jobs:
|
|||||||
rln-test:
|
rln-test:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
platform: [ ubuntu-latest, macos-latest ]
|
platform: [ubuntu-latest, macos-latest]
|
||||||
crate: [ rln ]
|
crate: [rln]
|
||||||
feature: [ "default", "arkzkey", "stateless" ]
|
feature: ["default", "arkzkey", "stateless"]
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
|
|
||||||
@@ -78,15 +78,16 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
working-directory: ${{ matrix.crate }}
|
working-directory: ${{ matrix.crate }}
|
||||||
|
|
||||||
rln-wasm:
|
rln-wasm-test:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
platform: [ ubuntu-latest, macos-latest ]
|
platform: [ubuntu-latest, macos-latest]
|
||||||
feature: [ "default", "arkzkey" ]
|
crate: [rln-wasm]
|
||||||
|
feature: ["default", "arkzkey"]
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
|
|
||||||
name: test - rln-wasm - ${{ matrix.platform }} - ${{ matrix.feature }}
|
name: test - ${{ matrix.crate }} - ${{ matrix.platform }} - ${{ matrix.feature }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Install stable toolchain
|
- name: Install stable toolchain
|
||||||
@@ -105,7 +106,7 @@ jobs:
|
|||||||
else
|
else
|
||||||
cargo make build_${{ matrix.feature }}
|
cargo make build_${{ matrix.feature }}
|
||||||
fi
|
fi
|
||||||
working-directory: rln-wasm
|
working-directory: ${{ matrix.crate }}
|
||||||
- name: cargo-make test
|
- name: cargo-make test
|
||||||
run: |
|
run: |
|
||||||
if [ ${{ matrix.feature }} == default ]; then
|
if [ ${{ matrix.feature }} == default ]; then
|
||||||
@@ -113,14 +114,62 @@ jobs:
|
|||||||
else
|
else
|
||||||
cargo make test_${{ matrix.feature }} --release
|
cargo make test_${{ matrix.feature }} --release
|
||||||
fi
|
fi
|
||||||
working-directory: rln-wasm
|
working-directory: ${{ matrix.crate }}
|
||||||
|
- name: cargo-make test browser
|
||||||
|
run: |
|
||||||
|
if [ ${{ matrix.feature }} == default ]; then
|
||||||
|
cargo make test_browser --release
|
||||||
|
else
|
||||||
|
cargo make test_browser_${{ matrix.feature }} --release
|
||||||
|
fi
|
||||||
|
working-directory: ${{ matrix.crate }}
|
||||||
|
|
||||||
|
# rln-wasm-multihread-test:
|
||||||
|
# strategy:
|
||||||
|
# matrix:
|
||||||
|
# platform: [ubuntu-latest, macos-latest]
|
||||||
|
# crate: [rln-wasm]
|
||||||
|
# feature: ["multithread", "multithread_arkzkey"]
|
||||||
|
# runs-on: ${{ matrix.platform }}
|
||||||
|
# timeout-minutes: 60
|
||||||
|
|
||||||
|
# name: test - ${{ matrix.crate }} - ${{ matrix.platform }} - ${{ matrix.feature }}
|
||||||
|
# steps:
|
||||||
|
# - uses: actions/checkout@v3
|
||||||
|
# - name: Install nightly toolchain
|
||||||
|
# uses: actions-rs/toolchain@v1
|
||||||
|
# with:
|
||||||
|
# profile: minimal
|
||||||
|
# toolchain: nightly
|
||||||
|
# override: true
|
||||||
|
# components: rust-src
|
||||||
|
# target: wasm32-unknown-unknown
|
||||||
|
# - uses: Swatinem/rust-cache@v2
|
||||||
|
# - name: Install Dependencies
|
||||||
|
# run: make installdeps
|
||||||
|
# - name: cargo-make build
|
||||||
|
# run: |
|
||||||
|
# if [ ${{ matrix.feature }} == default ]; then
|
||||||
|
# cargo make build
|
||||||
|
# else
|
||||||
|
# cargo make build_${{ matrix.feature }}
|
||||||
|
# fi
|
||||||
|
# working-directory: ${{ matrix.crate }}
|
||||||
|
# - name: cargo-make test
|
||||||
|
# run: |
|
||||||
|
# if [ ${{ matrix.feature }} == default ]; then
|
||||||
|
# cargo make test --release
|
||||||
|
# else
|
||||||
|
# cargo make test_${{ matrix.feature }} --release
|
||||||
|
# fi
|
||||||
|
# working-directory: ${{ matrix.crate }}
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
# we run lint tests only on ubuntu
|
# we run lint tests only on ubuntu
|
||||||
platform: [ ubuntu-latest ]
|
platform: [ubuntu-latest]
|
||||||
crate: [ rln, rln-wasm, utils ]
|
crate: [rln, rln-wasm, utils]
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
|
|
||||||
@@ -154,12 +203,12 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
# we run benchmark tests only on ubuntu
|
# we run benchmark tests only on ubuntu
|
||||||
platform: [ ubuntu-latest ]
|
platform: [ubuntu-latest]
|
||||||
crate: [ utils ]
|
crate: [utils]
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
|
|
||||||
name: benchmark - ${{ matrix.platform }} - ${{ matrix.crate }}
|
name: benchmark - ${{ matrix.crate }} - ${{ matrix.platform }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@@ -175,13 +224,13 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
# we run benchmark tests only on ubuntu
|
# we run benchmark tests only on ubuntu
|
||||||
platform: [ ubuntu-latest ]
|
platform: [ubuntu-latest]
|
||||||
crate: [ rln ]
|
crate: [rln]
|
||||||
feature: [ "default", "arkzkey" ]
|
feature: ["default", "arkzkey"]
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
|
|
||||||
name: benchmark - ${{ matrix.platform }} - ${{ matrix.crate }} - ${{ matrix.feature }}
|
name: benchmark - ${{ matrix.crate }} - ${{ matrix.platform }} - ${{ matrix.feature }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@@ -190,4 +239,4 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
branchName: ${{ github.base_ref }}
|
branchName: ${{ github.base_ref }}
|
||||||
cwd: ${{ matrix.crate }}
|
cwd: ${{ matrix.crate }}
|
||||||
features: ${{ matrix.feature }}
|
features: ${{ matrix.feature }}
|
||||||
|
|||||||
35
Cargo.lock
generated
35
Cargo.lock
generated
@@ -802,6 +802,15 @@ dependencies = [
|
|||||||
"itertools 0.10.5",
|
"itertools 0.10.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.5.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-deque"
|
name = "crossbeam-deque"
|
||||||
version = "0.8.6"
|
version = "0.8.6"
|
||||||
@@ -1638,6 +1647,7 @@ checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-deque",
|
"crossbeam-deque",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
|
"wasm_sync",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1737,7 +1747,9 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
|
"wasm-bindgen-rayon",
|
||||||
"wasm-bindgen-test",
|
"wasm-bindgen-test",
|
||||||
|
"web-sys",
|
||||||
"zerokit_utils",
|
"zerokit_utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2292,6 +2304,18 @@ dependencies = [
|
|||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-rayon"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9beda8dfdfaf2e0ec0b47e130a0794d18188fba4da8a2155dcc3bbeb7e0d454"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-channel",
|
||||||
|
"js-sys",
|
||||||
|
"rayon-core",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-shared"
|
name = "wasm-bindgen-shared"
|
||||||
version = "0.2.100"
|
version = "0.2.100"
|
||||||
@@ -2325,6 +2349,17 @@ dependencies = [
|
|||||||
"syn 2.0.100",
|
"syn 2.0.100",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm_sync"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cff360cade7fec41ff0e9d2cda57fe58258c5f16def0e21302394659e6bbb0ea"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.77"
|
version = "0.3.77"
|
||||||
|
|||||||
29
Makefile
29
Makefile
@@ -1,6 +1,6 @@
|
|||||||
.PHONY: all installdeps build test bench clean
|
.PHONY: all installdeps build test bench clean
|
||||||
|
|
||||||
all: .pre-build build
|
all: installdeps build
|
||||||
|
|
||||||
.fetch-submodules:
|
.fetch-submodules:
|
||||||
@git submodule update --init --recursive
|
@git submodule update --init --recursive
|
||||||
@@ -13,30 +13,23 @@ endif
|
|||||||
|
|
||||||
installdeps: .pre-build
|
installdeps: .pre-build
|
||||||
ifeq ($(shell uname),Darwin)
|
ifeq ($(shell uname),Darwin)
|
||||||
@brew update
|
@brew install cmake ninja binaryen
|
||||||
@brew install cmake ninja
|
|
||||||
else ifeq ($(shell uname),Linux)
|
else ifeq ($(shell uname),Linux)
|
||||||
@sudo apt-get update
|
@sudo apt-get install -y cmake ninja-build binaryen
|
||||||
@sudo apt-get install -y cmake ninja-build
|
|
||||||
endif
|
endif
|
||||||
@if [ ! -d "$$HOME/.nvm" ]; then \
|
@which wasm-pack > /dev/null && wasm-pack --version | grep -q "0.13.1" || cargo install wasm-pack --version=0.13.1
|
||||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | bash; \
|
@which wasm-bindgen > /dev/null && wasm-bindgen --version | grep -q "0.2.100" || cargo install wasm-bindgen-cli --version=0.2.100
|
||||||
fi
|
@test -s "$$HOME/.nvm/nvm.sh" || curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | bash
|
||||||
@bash -c 'export NVM_DIR="$$HOME/.nvm" && \
|
@bash -c '. "$$HOME/.nvm/nvm.sh"; [ "$$(node -v 2>/dev/null)" = "v22.14.0" ] || nvm install 22.14.0; nvm use 22.14.0; nvm alias default 22.14.0'
|
||||||
[ -s "$$NVM_DIR/nvm.sh" ] && \. "$$NVM_DIR/nvm.sh" && \
|
|
||||||
nvm install 22.14.0 && \
|
|
||||||
nvm use 22.14.0'
|
|
||||||
@curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
|
||||||
@echo "\033[1;32m>>> Now run this command to activate Node.js 22.14.0: \033[1;33msource $$HOME/.nvm/nvm.sh && nvm use 22.14.0\033[0m"
|
|
||||||
|
|
||||||
build: .pre-build
|
build: installdeps
|
||||||
@cargo make build
|
@cargo make build
|
||||||
|
|
||||||
test: .pre-build
|
test: build
|
||||||
@cargo make test
|
@cargo make test
|
||||||
|
|
||||||
bench: .pre-build
|
bench: build
|
||||||
@cargo make bench
|
@cargo make bench
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@cargo clean
|
@cargo clean
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ num-bigint = { version = "0.4.6", default-features = false, features = [
|
|||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
] }
|
] }
|
||||||
|
js-sys = "0.3.77"
|
||||||
wasm-bindgen = "0.2.100"
|
wasm-bindgen = "0.2.100"
|
||||||
serde-wasm-bindgen = "0.6.5"
|
serde-wasm-bindgen = "0.6.5"
|
||||||
js-sys = "0.3.77"
|
wasm-bindgen-rayon = { version = "1.2.0", optional = true }
|
||||||
serde_json = "1.0"
|
|
||||||
|
|
||||||
# The `console_error_panic_xhook` crate provides better debugging of panics by
|
# The `console_error_panic_xhook` crate provides better debugging of panics by
|
||||||
# logging them with `console.error`. This is great for development, but requires
|
# logging them with `console.error`. This is great for development, but requires
|
||||||
@@ -30,10 +30,16 @@ zerokit_utils = { path = "../utils" }
|
|||||||
getrandom = { version = "0.2.15", features = ["js"] }
|
getrandom = { version = "0.2.15", features = ["js"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
serde_json = "1.0"
|
||||||
wasm-bindgen-test = "0.3.50"
|
wasm-bindgen-test = "0.3.50"
|
||||||
wasm-bindgen-futures = "0.4.50"
|
wasm-bindgen-futures = "0.4.50"
|
||||||
|
|
||||||
|
[dev-dependencies.web-sys]
|
||||||
|
version = "0.3.77"
|
||||||
|
features = ["Window", "Navigator"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["console_error_panic_hook"]
|
default = ["console_error_panic_hook"]
|
||||||
stateless = ["rln/stateless"]
|
stateless = ["rln/stateless"]
|
||||||
arkzkey = ["rln/arkzkey"]
|
arkzkey = ["rln/arkzkey"]
|
||||||
|
multithread = ["wasm-bindgen-rayon"]
|
||||||
|
|||||||
@@ -1,24 +1,118 @@
|
|||||||
[tasks.build]
|
[tasks.build]
|
||||||
clear = true
|
clear = true
|
||||||
dependencies = ["pack_build", "pack_rename"]
|
dependencies = ["pack_build", "pack_rename", "pack_resize"]
|
||||||
|
|
||||||
[tasks.build_arkzkey]
|
[tasks.build_arkzkey]
|
||||||
clear = true
|
clear = true
|
||||||
dependencies = ["pack_build_arkzkey", "pack_rename"]
|
dependencies = ["pack_build_arkzkey", "pack_rename", "pack_resize"]
|
||||||
|
|
||||||
|
[tasks.build_multithread]
|
||||||
|
clear = true
|
||||||
|
dependencies = [
|
||||||
|
"pack_build_multithread",
|
||||||
|
"post_build_multithread",
|
||||||
|
"pack_rename",
|
||||||
|
"pack_resize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tasks.build_multithread_arkzkey]
|
||||||
|
clear = true
|
||||||
|
dependencies = [
|
||||||
|
"pack_build_multithread_arkzkey",
|
||||||
|
"post_build_multithread",
|
||||||
|
"pack_rename",
|
||||||
|
"pack_resize",
|
||||||
|
]
|
||||||
|
|
||||||
[tasks.pack_build]
|
[tasks.pack_build]
|
||||||
command = "wasm-pack"
|
command = "wasm-pack"
|
||||||
args = ["build", "--release", "--target", "web", "--scope", "waku"]
|
args = [
|
||||||
env = { "RUSTFLAGS" = "--cfg feature=\"stateless\"" }
|
"build",
|
||||||
|
"--release",
|
||||||
|
"--target",
|
||||||
|
"web",
|
||||||
|
"--scope",
|
||||||
|
"waku",
|
||||||
|
"--features",
|
||||||
|
"stateless",
|
||||||
|
]
|
||||||
|
|
||||||
[tasks.pack_build_arkzkey]
|
[tasks.pack_build_arkzkey]
|
||||||
command = "wasm-pack"
|
command = "wasm-pack"
|
||||||
args = ["build", "--release", "--target", "web", "--scope", "waku"]
|
args = [
|
||||||
env = { "RUSTFLAGS" = "--cfg feature=\"stateless\" --cfg feature=\"arkzkey\"" }
|
"build",
|
||||||
|
"--release",
|
||||||
|
"--target",
|
||||||
|
"web",
|
||||||
|
"--scope",
|
||||||
|
"waku",
|
||||||
|
"--features",
|
||||||
|
"stateless,arkzkey",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tasks.pack_build_multithread]
|
||||||
|
command = "env"
|
||||||
|
args = [
|
||||||
|
"RUSTFLAGS=-C target-feature=+atomics,+bulk-memory,+mutable-globals",
|
||||||
|
"rustup",
|
||||||
|
"run",
|
||||||
|
"nightly",
|
||||||
|
"wasm-pack",
|
||||||
|
"build",
|
||||||
|
"--release",
|
||||||
|
"--target",
|
||||||
|
"web",
|
||||||
|
"--scope",
|
||||||
|
"waku",
|
||||||
|
"--features",
|
||||||
|
"stateless,multithread",
|
||||||
|
"-Z",
|
||||||
|
"build-std=panic_abort,std",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tasks.pack_build_multithread_arkzkey]
|
||||||
|
command = "env"
|
||||||
|
args = [
|
||||||
|
"RUSTFLAGS=-C target-feature=+atomics,+bulk-memory,+mutable-globals",
|
||||||
|
"rustup",
|
||||||
|
"run",
|
||||||
|
"nightly",
|
||||||
|
"wasm-pack",
|
||||||
|
"build",
|
||||||
|
"--release",
|
||||||
|
"--target",
|
||||||
|
"web",
|
||||||
|
"--scope",
|
||||||
|
"waku",
|
||||||
|
"--features",
|
||||||
|
"stateless,multithread,arkzkey",
|
||||||
|
"-Z",
|
||||||
|
"build-std=panic_abort,std",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tasks.post_build_multithread]
|
||||||
|
script = '''
|
||||||
|
wasm-bindgen --target web --split-linked-modules --out-dir ./pkg ../target/wasm32-unknown-unknown/release/rln_wasm.wasm && \
|
||||||
|
find ./pkg/snippets -name "workerHelpers.worker.js" -exec sed -i.bak 's|from '\''\.\.\/\.\.\/\.\.\/'\'';|from "../../../rln_wasm.js";|g' {} \; -exec rm -f {}.bak \; && \
|
||||||
|
find ./pkg/snippets -name "workerHelpers.worker.js" -exec sed -i.bak 's|await initWbg(module, memory);|await initWbg({ module, memory });|g' {} \; -exec rm -f {}.bak \;
|
||||||
|
'''
|
||||||
|
|
||||||
[tasks.pack_rename]
|
[tasks.pack_rename]
|
||||||
script = "sed -i.bak 's/rln-wasm/zerokit-rln-wasm/g' pkg/package.json && rm pkg/package.json.bak"
|
script = "sed -i.bak 's/rln-wasm/zerokit-rln-wasm/g' pkg/package.json && rm pkg/package.json.bak"
|
||||||
|
|
||||||
|
[tasks.pack_resize]
|
||||||
|
command = "wasm-opt"
|
||||||
|
args = [
|
||||||
|
"pkg/rln_wasm_bg.wasm",
|
||||||
|
"-Oz",
|
||||||
|
"--strip-debug",
|
||||||
|
"--strip-dwarf",
|
||||||
|
"--remove-unused-module-elements",
|
||||||
|
"--vacuum",
|
||||||
|
"-o",
|
||||||
|
"pkg/rln_wasm_bg.wasm",
|
||||||
|
]
|
||||||
|
|
||||||
[tasks.test]
|
[tasks.test]
|
||||||
command = "wasm-pack"
|
command = "wasm-pack"
|
||||||
args = [
|
args = [
|
||||||
@@ -27,10 +121,12 @@ args = [
|
|||||||
"--node",
|
"--node",
|
||||||
"--target",
|
"--target",
|
||||||
"wasm32-unknown-unknown",
|
"wasm32-unknown-unknown",
|
||||||
|
"--features",
|
||||||
|
"stateless",
|
||||||
"--",
|
"--",
|
||||||
"--nocapture",
|
"--nocapture",
|
||||||
]
|
]
|
||||||
env = { "RUSTFLAGS" = "--cfg feature=\"stateless\"" }
|
dependencies = ["build"]
|
||||||
|
|
||||||
[tasks.test_arkzkey]
|
[tasks.test_arkzkey]
|
||||||
command = "wasm-pack"
|
command = "wasm-pack"
|
||||||
@@ -40,12 +136,99 @@ args = [
|
|||||||
"--node",
|
"--node",
|
||||||
"--target",
|
"--target",
|
||||||
"wasm32-unknown-unknown",
|
"wasm32-unknown-unknown",
|
||||||
|
"--features",
|
||||||
|
"stateless,arkzkey",
|
||||||
"--",
|
"--",
|
||||||
"--nocapture",
|
"--nocapture",
|
||||||
]
|
]
|
||||||
env = { "RUSTFLAGS" = "--cfg feature=\"stateless\" --cfg feature=\"arkzkey\"" }
|
|
||||||
dependencies = ["build_arkzkey"]
|
dependencies = ["build_arkzkey"]
|
||||||
|
|
||||||
|
[tasks.test_browser]
|
||||||
|
command = "wasm-pack"
|
||||||
|
args = [
|
||||||
|
"test",
|
||||||
|
"--release",
|
||||||
|
"--chrome",
|
||||||
|
# "--firefox",
|
||||||
|
# "--safari",
|
||||||
|
"--headless",
|
||||||
|
"--target",
|
||||||
|
"wasm32-unknown-unknown",
|
||||||
|
"--features",
|
||||||
|
"stateless",
|
||||||
|
"--",
|
||||||
|
"--nocapture",
|
||||||
|
]
|
||||||
|
dependencies = ["build"]
|
||||||
|
|
||||||
|
[tasks.test_browser_arkzkey]
|
||||||
|
command = "wasm-pack"
|
||||||
|
args = [
|
||||||
|
"test",
|
||||||
|
"--release",
|
||||||
|
"--chrome",
|
||||||
|
# "--firefox",
|
||||||
|
# "--safari",
|
||||||
|
"--headless",
|
||||||
|
"--target",
|
||||||
|
"wasm32-unknown-unknown",
|
||||||
|
"--features",
|
||||||
|
"stateless,arkzkey",
|
||||||
|
"--",
|
||||||
|
"--nocapture",
|
||||||
|
]
|
||||||
|
dependencies = ["build_arkzkey"]
|
||||||
|
|
||||||
|
[tasks.test_multithread]
|
||||||
|
command = "env"
|
||||||
|
args = [
|
||||||
|
"RUSTFLAGS=-C target-feature=+atomics,+bulk-memory,+mutable-globals",
|
||||||
|
"rustup",
|
||||||
|
"run",
|
||||||
|
"nightly",
|
||||||
|
"wasm-pack",
|
||||||
|
"test",
|
||||||
|
"--release",
|
||||||
|
"--chrome",
|
||||||
|
# "--firefox",
|
||||||
|
# "--safari",
|
||||||
|
"--headless",
|
||||||
|
"--target",
|
||||||
|
"wasm32-unknown-unknown",
|
||||||
|
"--features",
|
||||||
|
"stateless,multithread",
|
||||||
|
"-Z",
|
||||||
|
"build-std=panic_abort,std",
|
||||||
|
"--",
|
||||||
|
"--nocapture",
|
||||||
|
]
|
||||||
|
dependencies = ["build_multithread"]
|
||||||
|
|
||||||
|
[tasks.test_multithread_arkzkey]
|
||||||
|
command = "env"
|
||||||
|
args = [
|
||||||
|
"RUSTFLAGS=-C target-feature=+atomics,+bulk-memory,+mutable-globals",
|
||||||
|
"rustup",
|
||||||
|
"run",
|
||||||
|
"nightly",
|
||||||
|
"wasm-pack",
|
||||||
|
"test",
|
||||||
|
"--release",
|
||||||
|
"--chrome",
|
||||||
|
# "--firefox",
|
||||||
|
# "--safari",
|
||||||
|
"--headless",
|
||||||
|
"--target",
|
||||||
|
"wasm32-unknown-unknown",
|
||||||
|
"--features",
|
||||||
|
"stateless,multithread,arkzkey",
|
||||||
|
"-Z",
|
||||||
|
"build-std=panic_abort,std",
|
||||||
|
"--",
|
||||||
|
"--nocapture",
|
||||||
|
]
|
||||||
|
dependencies = ["build_multithread_arkzkey"]
|
||||||
|
|
||||||
[tasks.bench]
|
[tasks.bench]
|
||||||
disabled = true
|
disabled = true
|
||||||
|
|
||||||
|
|||||||
@@ -2,21 +2,48 @@
|
|||||||
|
|
||||||
This library is used in [waku-org/js-rln](https://github.com/waku-org/js-rln/)
|
This library is used in [waku-org/js-rln](https://github.com/waku-org/js-rln/)
|
||||||
|
|
||||||
> **Note**: This project requires `wasm-pack` for compiling Rust to WebAssembly and `cargo-make` for running the build commands. Make sure both are installed before proceeding.
|
## Install Dependencies
|
||||||
|
|
||||||
Install `wasm-pack`:
|
> [!NOTE]
|
||||||
|
> This project requires the following tools:
|
||||||
|
>
|
||||||
|
> - `wasm-pack` (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.
|
||||||
|
|
||||||
|
### Manually
|
||||||
|
|
||||||
|
#### Install `wasm-pack`
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
cargo install wasm-pack --version=0.13.1
|
||||||
```
|
```
|
||||||
|
|
||||||
Install `cargo-make`
|
#### Install `cargo-make`
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo install cargo-make
|
cargo install cargo-make
|
||||||
```
|
```
|
||||||
|
|
||||||
Or install everything needed for `zerokit` at the root of the repository:
|
#### 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`
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make installdeps
|
make installdeps
|
||||||
@@ -53,3 +80,78 @@ Or test with the **arkzkey** feature enabled
|
|||||||
```bash
|
```bash
|
||||||
cargo make test_arkzkey
|
cargo make test_arkzkey
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you want to run the tests in browser headless mode, you can use the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo make test_browser
|
||||||
|
cargo make test_browser_arkzkey
|
||||||
|
```
|
||||||
|
|
||||||
|
## Parallel computation
|
||||||
|
|
||||||
|
The library supports parallel computation using the `wasm-bindgen-rayon` crate, enabling multi-threaded execution in the browser.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Parallel support is not enabled by default due to WebAssembly and browser limitations. \
|
||||||
|
> Compiling this feature requires `nightly` Rust and the `wasm-bindgen-cli` tool.
|
||||||
|
|
||||||
|
### Build Setup
|
||||||
|
|
||||||
|
#### Install `nightly` Rust
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rustup install nightly
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Install `wasm-bindgen-cli`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo install wasm-bindgen-cli --version=0.2.100
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build Commands
|
||||||
|
|
||||||
|
To enable parallel computation for WebAssembly threads, you can use the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo make build_multithread
|
||||||
|
```
|
||||||
|
|
||||||
|
Or with the **arkzkey** feature enabled:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo make build_multithread_arkzkey
|
||||||
|
```
|
||||||
|
|
||||||
|
### WebAssembly Threading 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 thread 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:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { threads } from 'wasm-feature-detect';
|
||||||
|
|
||||||
|
let wasmPkg;
|
||||||
|
|
||||||
|
if (await threads()) {
|
||||||
|
wasmPkg = await import('./pkg-with-threads/index.js');
|
||||||
|
await wasmPkg.default();
|
||||||
|
await wasmPkg.initThreadPool(navigator.hardwareConcurrency);
|
||||||
|
} else {
|
||||||
|
wasmPkg = await import('./pkg-without-threads/index.js');
|
||||||
|
await wasmPkg.default();
|
||||||
|
}
|
||||||
|
|
||||||
|
wasmPkg.nowCallAnyExportedFuncs();
|
||||||
|
```
|
||||||
|
|||||||
335
rln-wasm/resources/witness_calculator_browser.js
Normal file
335
rln-wasm/resources/witness_calculator_browser.js
Normal file
@@ -0,0 +1,335 @@
|
|||||||
|
// Browser compatible witness calculator
|
||||||
|
(function (global) {
|
||||||
|
async function builder(code, options) {
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
let wasmModule;
|
||||||
|
try {
|
||||||
|
wasmModule = await WebAssembly.compile(code);
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
console.log(
|
||||||
|
"\nTry to run circom --c in order to generate c++ code instead\n"
|
||||||
|
);
|
||||||
|
throw new Error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
let wc;
|
||||||
|
|
||||||
|
let errStr = "";
|
||||||
|
let msgStr = "";
|
||||||
|
|
||||||
|
const instance = await WebAssembly.instantiate(wasmModule, {
|
||||||
|
runtime: {
|
||||||
|
exceptionHandler: function (code) {
|
||||||
|
let err;
|
||||||
|
if (code == 1) {
|
||||||
|
err = "Signal not found.\n";
|
||||||
|
} else if (code == 2) {
|
||||||
|
err = "Too many signals set.\n";
|
||||||
|
} else if (code == 3) {
|
||||||
|
err = "Signal already set.\n";
|
||||||
|
} else if (code == 4) {
|
||||||
|
err = "Assert Failed.\n";
|
||||||
|
} else if (code == 5) {
|
||||||
|
err = "Not enough memory.\n";
|
||||||
|
} else if (code == 6) {
|
||||||
|
err = "Input signal array access exceeds the size.\n";
|
||||||
|
} else {
|
||||||
|
err = "Unknown error.\n";
|
||||||
|
}
|
||||||
|
throw new Error(err + errStr);
|
||||||
|
},
|
||||||
|
printErrorMessage: function () {
|
||||||
|
errStr += getMessage() + "\n";
|
||||||
|
// console.error(getMessage());
|
||||||
|
},
|
||||||
|
writeBufferMessage: function () {
|
||||||
|
const msg = getMessage();
|
||||||
|
// Any calls to `log()` will always end with a `\n`, so that's when we print and reset
|
||||||
|
if (msg === "\n") {
|
||||||
|
console.log(msgStr);
|
||||||
|
msgStr = "";
|
||||||
|
} else {
|
||||||
|
// If we've buffered other content, put a space in between the items
|
||||||
|
if (msgStr !== "") {
|
||||||
|
msgStr += " ";
|
||||||
|
}
|
||||||
|
// Then append the message to the message we are creating
|
||||||
|
msgStr += msg;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showSharedRWMemory: function () {
|
||||||
|
printSharedRWMemory();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const sanityCheck = options;
|
||||||
|
// options &&
|
||||||
|
// (
|
||||||
|
// options.sanityCheck ||
|
||||||
|
// options.logGetSignal ||
|
||||||
|
// options.logSetSignal ||
|
||||||
|
// options.logStartComponent ||
|
||||||
|
// options.logFinishComponent
|
||||||
|
// );
|
||||||
|
|
||||||
|
wc = new WitnessCalculator(instance, sanityCheck);
|
||||||
|
return wc;
|
||||||
|
|
||||||
|
function getMessage() {
|
||||||
|
var message = "";
|
||||||
|
var c = instance.exports.getMessageChar();
|
||||||
|
while (c != 0) {
|
||||||
|
message += String.fromCharCode(c);
|
||||||
|
c = instance.exports.getMessageChar();
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
function printSharedRWMemory() {
|
||||||
|
const shared_rw_memory_size = instance.exports.getFieldNumLen32();
|
||||||
|
const arr = new Uint32Array(shared_rw_memory_size);
|
||||||
|
for (let j = 0; j < shared_rw_memory_size; j++) {
|
||||||
|
arr[shared_rw_memory_size - 1 - j] =
|
||||||
|
instance.exports.readSharedRWMemory(j);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we've buffered other content, put a space in between the items
|
||||||
|
if (msgStr !== "") {
|
||||||
|
msgStr += " ";
|
||||||
|
}
|
||||||
|
// Then append the value to the message we are creating
|
||||||
|
msgStr += fromArray32(arr).toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WitnessCalculator {
|
||||||
|
constructor(instance, sanityCheck) {
|
||||||
|
this.instance = instance;
|
||||||
|
|
||||||
|
this.version = this.instance.exports.getVersion();
|
||||||
|
this.n32 = this.instance.exports.getFieldNumLen32();
|
||||||
|
|
||||||
|
this.instance.exports.getRawPrime();
|
||||||
|
const arr = new Uint32Array(this.n32);
|
||||||
|
for (let i = 0; i < this.n32; i++) {
|
||||||
|
arr[this.n32 - 1 - i] = this.instance.exports.readSharedRWMemory(i);
|
||||||
|
}
|
||||||
|
this.prime = fromArray32(arr);
|
||||||
|
|
||||||
|
this.witnessSize = this.instance.exports.getWitnessSize();
|
||||||
|
|
||||||
|
this.sanityCheck = sanityCheck;
|
||||||
|
}
|
||||||
|
|
||||||
|
circom_version() {
|
||||||
|
return this.instance.exports.getVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
async _doCalculateWitness(input, sanityCheck) {
|
||||||
|
//input is assumed to be a map from signals to arrays of bigints
|
||||||
|
this.instance.exports.init(this.sanityCheck || sanityCheck ? 1 : 0);
|
||||||
|
const keys = Object.keys(input);
|
||||||
|
var input_counter = 0;
|
||||||
|
keys.forEach((k) => {
|
||||||
|
const h = fnvHash(k);
|
||||||
|
const hMSB = parseInt(h.slice(0, 8), 16);
|
||||||
|
const hLSB = parseInt(h.slice(8, 16), 16);
|
||||||
|
const fArr = flatArray(input[k]);
|
||||||
|
let signalSize = this.instance.exports.getInputSignalSize(hMSB, hLSB);
|
||||||
|
if (signalSize < 0) {
|
||||||
|
throw new Error(`Signal ${k} not found\n`);
|
||||||
|
}
|
||||||
|
if (fArr.length < signalSize) {
|
||||||
|
throw new Error(`Not enough values for input signal ${k}\n`);
|
||||||
|
}
|
||||||
|
if (fArr.length > signalSize) {
|
||||||
|
throw new Error(`Too many values for input signal ${k}\n`);
|
||||||
|
}
|
||||||
|
for (let i = 0; i < fArr.length; i++) {
|
||||||
|
const arrFr = toArray32(BigInt(fArr[i]) % this.prime, this.n32);
|
||||||
|
for (let j = 0; j < this.n32; j++) {
|
||||||
|
this.instance.exports.writeSharedRWMemory(
|
||||||
|
j,
|
||||||
|
arrFr[this.n32 - 1 - j]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
this.instance.exports.setInputSignal(hMSB, hLSB, i);
|
||||||
|
input_counter++;
|
||||||
|
} catch (err) {
|
||||||
|
// console.log(`After adding signal ${i} of ${k}`)
|
||||||
|
throw new Error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (input_counter < this.instance.exports.getInputSize()) {
|
||||||
|
throw new Error(
|
||||||
|
`Not all inputs have been set. Only ${input_counter} out of ${this.instance.exports.getInputSize()}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async calculateWitness(input, sanityCheck) {
|
||||||
|
const w = [];
|
||||||
|
|
||||||
|
await this._doCalculateWitness(input, sanityCheck);
|
||||||
|
|
||||||
|
for (let i = 0; i < this.witnessSize; i++) {
|
||||||
|
this.instance.exports.getWitness(i);
|
||||||
|
const arr = new Uint32Array(this.n32);
|
||||||
|
for (let j = 0; j < this.n32; j++) {
|
||||||
|
arr[this.n32 - 1 - j] = this.instance.exports.readSharedRWMemory(j);
|
||||||
|
}
|
||||||
|
w.push(fromArray32(arr));
|
||||||
|
}
|
||||||
|
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
async calculateBinWitness(input, sanityCheck) {
|
||||||
|
const buff32 = new Uint32Array(this.witnessSize * this.n32);
|
||||||
|
const buff = new Uint8Array(buff32.buffer);
|
||||||
|
await this._doCalculateWitness(input, sanityCheck);
|
||||||
|
|
||||||
|
for (let i = 0; i < this.witnessSize; i++) {
|
||||||
|
this.instance.exports.getWitness(i);
|
||||||
|
const pos = i * this.n32;
|
||||||
|
for (let j = 0; j < this.n32; j++) {
|
||||||
|
buff32[pos + j] = this.instance.exports.readSharedRWMemory(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buff;
|
||||||
|
}
|
||||||
|
|
||||||
|
async calculateWTNSBin(input, sanityCheck) {
|
||||||
|
const buff32 = new Uint32Array(
|
||||||
|
this.witnessSize * this.n32 + this.n32 + 11
|
||||||
|
);
|
||||||
|
const buff = new Uint8Array(buff32.buffer);
|
||||||
|
await this._doCalculateWitness(input, sanityCheck);
|
||||||
|
|
||||||
|
//"wtns"
|
||||||
|
buff[0] = "w".charCodeAt(0);
|
||||||
|
buff[1] = "t".charCodeAt(0);
|
||||||
|
buff[2] = "n".charCodeAt(0);
|
||||||
|
buff[3] = "s".charCodeAt(0);
|
||||||
|
|
||||||
|
//version 2
|
||||||
|
buff32[1] = 2;
|
||||||
|
|
||||||
|
//number of sections: 2
|
||||||
|
buff32[2] = 2;
|
||||||
|
|
||||||
|
//id section 1
|
||||||
|
buff32[3] = 1;
|
||||||
|
|
||||||
|
const n8 = this.n32 * 4;
|
||||||
|
//id section 1 length in 64bytes
|
||||||
|
const idSection1length = 8 + n8;
|
||||||
|
const idSection1lengthHex = idSection1length.toString(16);
|
||||||
|
buff32[4] = parseInt(idSection1lengthHex.slice(0, 8), 16);
|
||||||
|
buff32[5] = parseInt(idSection1lengthHex.slice(8, 16), 16);
|
||||||
|
|
||||||
|
//this.n32
|
||||||
|
buff32[6] = n8;
|
||||||
|
|
||||||
|
//prime number
|
||||||
|
this.instance.exports.getRawPrime();
|
||||||
|
|
||||||
|
var pos = 7;
|
||||||
|
for (let j = 0; j < this.n32; j++) {
|
||||||
|
buff32[pos + j] = this.instance.exports.readSharedRWMemory(j);
|
||||||
|
}
|
||||||
|
pos += this.n32;
|
||||||
|
|
||||||
|
// witness size
|
||||||
|
buff32[pos] = this.witnessSize;
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
//id section 2
|
||||||
|
buff32[pos] = 2;
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
// section 2 length
|
||||||
|
const idSection2length = n8 * this.witnessSize;
|
||||||
|
const idSection2lengthHex = idSection2length.toString(16);
|
||||||
|
buff32[pos] = parseInt(idSection2lengthHex.slice(0, 8), 16);
|
||||||
|
buff32[pos + 1] = parseInt(idSection2lengthHex.slice(8, 16), 16);
|
||||||
|
|
||||||
|
pos += 2;
|
||||||
|
for (let i = 0; i < this.witnessSize; i++) {
|
||||||
|
this.instance.exports.getWitness(i);
|
||||||
|
for (let j = 0; j < this.n32; j++) {
|
||||||
|
buff32[pos + j] = this.instance.exports.readSharedRWMemory(j);
|
||||||
|
}
|
||||||
|
pos += this.n32;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toArray32(rem, size) {
|
||||||
|
const res = []; //new Uint32Array(size); //has no unshift
|
||||||
|
const radix = BigInt(0x100000000);
|
||||||
|
while (rem) {
|
||||||
|
res.unshift(Number(rem % radix));
|
||||||
|
rem = rem / radix;
|
||||||
|
}
|
||||||
|
if (size) {
|
||||||
|
var i = size - res.length;
|
||||||
|
while (i > 0) {
|
||||||
|
res.unshift(0);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fromArray32(arr) {
|
||||||
|
//returns a BigInt
|
||||||
|
var res = BigInt(0);
|
||||||
|
const radix = BigInt(0x100000000);
|
||||||
|
for (let i = 0; i < arr.length; i++) {
|
||||||
|
res = res * radix + BigInt(arr[i]);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function flatArray(a) {
|
||||||
|
var res = [];
|
||||||
|
fillArray(res, a);
|
||||||
|
return res;
|
||||||
|
|
||||||
|
function fillArray(res, a) {
|
||||||
|
if (Array.isArray(a)) {
|
||||||
|
for (let i = 0; i < a.length; i++) {
|
||||||
|
fillArray(res, a[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.push(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fnvHash(str) {
|
||||||
|
const uint64_max = BigInt(2) ** BigInt(64);
|
||||||
|
let hash = BigInt("0xCBF29CE484222325");
|
||||||
|
for (var i = 0; i < str.length; i++) {
|
||||||
|
hash ^= BigInt(str[i].charCodeAt());
|
||||||
|
hash *= BigInt(0x100000001b3);
|
||||||
|
hash %= uint64_max;
|
||||||
|
}
|
||||||
|
let shash = hash.toString(16);
|
||||||
|
let n = 16 - shash.length;
|
||||||
|
shash = "0".repeat(n).concat(shash);
|
||||||
|
return shash;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make it globally available
|
||||||
|
global.witnessCalculatorBuilder = builder;
|
||||||
|
})(typeof self !== "undefined" ? self : window);
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// Node.js module compatible witness calculator
|
||||||
module.exports = async function builder(code, options) {
|
module.exports = async function builder(code, options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
|
||||||
@@ -6,6 +6,9 @@ use rln::public::{hash, poseidon_hash, RLN};
|
|||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "multithread")]
|
||||||
|
pub use wasm_bindgen_rayon::init_thread_pool;
|
||||||
|
|
||||||
#[wasm_bindgen(js_name = initPanicHook)]
|
#[wasm_bindgen(js_name = initPanicHook)]
|
||||||
pub fn init_panic_hook() {
|
pub fn init_panic_hook() {
|
||||||
console_error_panic_hook::set_once();
|
console_error_panic_hook::set_once();
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
const fs = require("fs");
|
|
||||||
|
|
||||||
// Utils functions for loading circom witness calculator and reading files from test
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
read_file: function (path) {
|
|
||||||
return fs.readFileSync(path);
|
|
||||||
},
|
|
||||||
|
|
||||||
calculateWitness: async function (circom_path, inputs) {
|
|
||||||
const wc = require("../resources/witness_calculator.js");
|
|
||||||
const wasmFile = fs.readFileSync(circom_path);
|
|
||||||
const wasmFileBuffer = wasmFile.slice(
|
|
||||||
wasmFile.byteOffset,
|
|
||||||
wasmFile.byteOffset + wasmFile.byteLength
|
|
||||||
);
|
|
||||||
const witnessCalculator = await wc(wasmFileBuffer);
|
|
||||||
const calculatedWitness = await witnessCalculator.calculateWitness(
|
|
||||||
inputs,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
return JSON.stringify(calculatedWitness, (key, value) =>
|
|
||||||
typeof value === "bigint" ? value.toString() : value
|
|
||||||
);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
257
rln-wasm/tests/browser.rs
Normal file
257
rln-wasm/tests/browser.rs
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
#![cfg(target_arch = "wasm32")]
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use js_sys::{BigInt as JsBigInt, Date, Object, Uint8Array};
|
||||||
|
use rln::circuit::{Fr, TEST_TREE_HEIGHT};
|
||||||
|
use rln::hashers::{hash_to_field, poseidon_hash};
|
||||||
|
use rln::poseidon_tree::PoseidonTree;
|
||||||
|
use rln::protocol::{prepare_verify_input, rln_witness_from_values, serialize_witness};
|
||||||
|
use rln::utils::{bytes_le_to_fr, fr_to_bytes_le};
|
||||||
|
use rln_wasm::{
|
||||||
|
wasm_generate_rln_proof_with_witness, wasm_key_gen, wasm_new, wasm_rln_witness_to_json,
|
||||||
|
wasm_verify_with_roots,
|
||||||
|
};
|
||||||
|
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
|
||||||
|
use wasm_bindgen_test::{console_log, wasm_bindgen_test, wasm_bindgen_test_configure};
|
||||||
|
use zerokit_utils::merkle_tree::merkle_tree::ZerokitMerkleTree;
|
||||||
|
|
||||||
|
#[cfg(feature = "multithread")]
|
||||||
|
use {rln_wasm::init_thread_pool, wasm_bindgen_futures::JsFuture, web_sys::window};
|
||||||
|
|
||||||
|
#[wasm_bindgen(inline_js = r#"
|
||||||
|
export function isThreadpoolSupported() {
|
||||||
|
return typeof SharedArrayBuffer !== 'undefined' &&
|
||||||
|
typeof Atomics !== 'undefined' &&
|
||||||
|
typeof crossOriginIsolated !== 'undefined' &&
|
||||||
|
crossOriginIsolated;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initWitnessCalculator(jsCode) {
|
||||||
|
eval(jsCode);
|
||||||
|
if (typeof window.witnessCalculatorBuilder !== 'function') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function readFile(data) {
|
||||||
|
return new Uint8Array(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function calculateWitness(circom_data, inputs) {
|
||||||
|
const wasmBuffer = circom_data instanceof Uint8Array ? circom_data : new Uint8Array(circom_data);
|
||||||
|
const witnessCalculator = await window.witnessCalculatorBuilder(wasmBuffer);
|
||||||
|
const calculatedWitness = await witnessCalculator.calculateWitness(inputs, false);
|
||||||
|
return JSON.stringify(calculatedWitness, (key, value) =>
|
||||||
|
typeof value === "bigint" ? value.toString() : value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
"#)]
|
||||||
|
extern "C" {
|
||||||
|
#[wasm_bindgen(catch)]
|
||||||
|
fn isThreadpoolSupported() -> Result<bool, JsValue>;
|
||||||
|
|
||||||
|
#[wasm_bindgen(catch)]
|
||||||
|
fn initWitnessCalculator(js: &str) -> Result<bool, JsValue>;
|
||||||
|
|
||||||
|
#[wasm_bindgen(catch)]
|
||||||
|
fn readFile(data: &[u8]) -> Result<Uint8Array, JsValue>;
|
||||||
|
|
||||||
|
#[wasm_bindgen(catch)]
|
||||||
|
async fn calculateWitness(circom_data: &[u8], inputs: Object) -> Result<JsValue, JsValue>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const WITNESS_CALCULATOR_JS: &str = include_str!("../resources/witness_calculator_browser.js");
|
||||||
|
|
||||||
|
#[cfg(feature = "arkzkey")]
|
||||||
|
const ZKEY_BYTES: &[u8] =
|
||||||
|
include_bytes!("../../rln/resources/tree_height_20/rln_final.arkzkey");
|
||||||
|
#[cfg(not(feature = "arkzkey"))]
|
||||||
|
const ZKEY_BYTES: &[u8] = include_bytes!("../../rln/resources/tree_height_20/rln_final.zkey");
|
||||||
|
|
||||||
|
const CIRCOM_BYTES: &[u8] = include_bytes!("../../rln/resources/tree_height_20/rln.wasm");
|
||||||
|
|
||||||
|
wasm_bindgen_test_configure!(run_in_browser);
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
pub async fn rln_wasm_benchmark() {
|
||||||
|
// Check if thread pool is supported
|
||||||
|
#[cfg(feature = "multithread")]
|
||||||
|
if !isThreadpoolSupported().expect("Failed to check thread pool support") {
|
||||||
|
panic!("Thread pool is NOT supported");
|
||||||
|
} else {
|
||||||
|
// Initialize thread pool
|
||||||
|
let cpu_count = window()
|
||||||
|
.expect("Failed to get window")
|
||||||
|
.navigator()
|
||||||
|
.hardware_concurrency() as usize;
|
||||||
|
JsFuture::from(init_thread_pool(cpu_count))
|
||||||
|
.await
|
||||||
|
.expect("Failed to initialize thread pool");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the witness calculator
|
||||||
|
initWitnessCalculator(WITNESS_CALCULATOR_JS)
|
||||||
|
.expect("Failed to initialize witness calculator");
|
||||||
|
|
||||||
|
let mut results = String::from("\nbenchmarks:\n");
|
||||||
|
let iterations = 10;
|
||||||
|
|
||||||
|
let zkey = readFile(&ZKEY_BYTES).expect("Failed to read zkey file");
|
||||||
|
|
||||||
|
// Benchmark wasm_new
|
||||||
|
let start_wasm_new = Date::now();
|
||||||
|
for _ in 0..iterations {
|
||||||
|
let _ = wasm_new(zkey.clone()).expect("Failed to create RLN instance");
|
||||||
|
}
|
||||||
|
let wasm_new_result = Date::now() - start_wasm_new;
|
||||||
|
|
||||||
|
// Create RLN instance for other benchmarks
|
||||||
|
let rln_instance = wasm_new(zkey).expect("Failed to create RLN instance");
|
||||||
|
let mut tree = PoseidonTree::default(TEST_TREE_HEIGHT).expect("Failed to create tree");
|
||||||
|
|
||||||
|
// Benchmark wasm_key_gen
|
||||||
|
let start_wasm_key_gen = Date::now();
|
||||||
|
for _ in 0..iterations {
|
||||||
|
let _ = wasm_key_gen(rln_instance).expect("Failed to generate keys");
|
||||||
|
}
|
||||||
|
let wasm_key_gen_result = Date::now() - start_wasm_key_gen;
|
||||||
|
|
||||||
|
// Generate identity pair for other benchmarks
|
||||||
|
let mem_keys = wasm_key_gen(rln_instance).expect("Failed to generate keys");
|
||||||
|
let id_key = mem_keys.subarray(0, 32);
|
||||||
|
let (identity_secret_hash, _) = bytes_le_to_fr(&id_key.to_vec());
|
||||||
|
let (id_commitment, _) = bytes_le_to_fr(&mem_keys.subarray(32, 64).to_vec());
|
||||||
|
|
||||||
|
let epoch = hash_to_field(b"test-epoch");
|
||||||
|
let rln_identifier = hash_to_field(b"test-rln-identifier");
|
||||||
|
let external_nullifier = poseidon_hash(&[epoch, rln_identifier]);
|
||||||
|
|
||||||
|
let identity_index = tree.leaves_set();
|
||||||
|
|
||||||
|
let user_message_limit = Fr::from(100);
|
||||||
|
|
||||||
|
let rate_commitment = poseidon_hash(&[id_commitment, user_message_limit]);
|
||||||
|
tree.update_next(rate_commitment)
|
||||||
|
.expect("Failed to update tree");
|
||||||
|
|
||||||
|
let message_id = Fr::from(0);
|
||||||
|
let signal: [u8; 32] = [0; 32];
|
||||||
|
let x = hash_to_field(&signal);
|
||||||
|
|
||||||
|
let merkle_proof = tree
|
||||||
|
.proof(identity_index)
|
||||||
|
.expect("Failed to generate merkle proof");
|
||||||
|
|
||||||
|
let rln_witness = rln_witness_from_values(
|
||||||
|
identity_secret_hash,
|
||||||
|
&merkle_proof,
|
||||||
|
x,
|
||||||
|
external_nullifier,
|
||||||
|
user_message_limit,
|
||||||
|
message_id,
|
||||||
|
)
|
||||||
|
.expect("Failed to create RLN witness");
|
||||||
|
|
||||||
|
let serialized_witness =
|
||||||
|
serialize_witness(&rln_witness).expect("Failed to serialize witness");
|
||||||
|
let witness_buffer = Uint8Array::from(&serialized_witness[..]);
|
||||||
|
|
||||||
|
let json_inputs = wasm_rln_witness_to_json(rln_instance, witness_buffer.clone())
|
||||||
|
.expect("Failed to convert witness to JSON");
|
||||||
|
|
||||||
|
// Benchmark calculateWitness
|
||||||
|
let start_calculate_witness = Date::now();
|
||||||
|
for _ in 0..iterations {
|
||||||
|
let _ = calculateWitness(&CIRCOM_BYTES, json_inputs.clone())
|
||||||
|
.await
|
||||||
|
.expect("Failed to calculate witness");
|
||||||
|
}
|
||||||
|
let calculate_witness_result = Date::now() - start_calculate_witness;
|
||||||
|
|
||||||
|
// Calculate witness for other benchmarks
|
||||||
|
let calculated_witness_json = calculateWitness(&CIRCOM_BYTES, json_inputs)
|
||||||
|
.await
|
||||||
|
.expect("Failed to calculate witness")
|
||||||
|
.as_string()
|
||||||
|
.expect("Failed to convert calculated witness to string");
|
||||||
|
let calculated_witness_vec_str: Vec<String> =
|
||||||
|
serde_json::from_str(&calculated_witness_json).expect("Failed to parse JSON");
|
||||||
|
let calculated_witness: Vec<JsBigInt> = calculated_witness_vec_str
|
||||||
|
.iter()
|
||||||
|
.map(|x| JsBigInt::new(&x.into()).expect("Failed to create JsBigInt"))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Benchmark wasm_generate_rln_proof_with_witness
|
||||||
|
let start_wasm_generate_rln_proof_with_witness = Date::now();
|
||||||
|
for _ in 0..iterations {
|
||||||
|
let _ = wasm_generate_rln_proof_with_witness(
|
||||||
|
rln_instance,
|
||||||
|
calculated_witness.clone(),
|
||||||
|
witness_buffer.clone(),
|
||||||
|
)
|
||||||
|
.expect("Failed to generate proof");
|
||||||
|
}
|
||||||
|
let wasm_generate_rln_proof_with_witness_result =
|
||||||
|
Date::now() - start_wasm_generate_rln_proof_with_witness;
|
||||||
|
|
||||||
|
// Generate a proof for other benchmarks
|
||||||
|
let proof =
|
||||||
|
wasm_generate_rln_proof_with_witness(rln_instance, calculated_witness, witness_buffer)
|
||||||
|
.expect("Failed to generate proof");
|
||||||
|
|
||||||
|
let proof_data = proof.to_vec();
|
||||||
|
let verify_input = prepare_verify_input(proof_data, &signal);
|
||||||
|
let input_buffer = Uint8Array::from(&verify_input[..]);
|
||||||
|
|
||||||
|
let root = tree.root();
|
||||||
|
let roots_serialized = fr_to_bytes_le(&root);
|
||||||
|
let roots_buffer = Uint8Array::from(&roots_serialized[..]);
|
||||||
|
|
||||||
|
// Benchmark wasm_verify_with_roots
|
||||||
|
let start_wasm_verify_with_roots = Date::now();
|
||||||
|
for _ in 0..iterations {
|
||||||
|
let _ =
|
||||||
|
wasm_verify_with_roots(rln_instance, input_buffer.clone(), roots_buffer.clone())
|
||||||
|
.expect("Failed to verify proof");
|
||||||
|
}
|
||||||
|
let wasm_verify_with_roots_result = Date::now() - start_wasm_verify_with_roots;
|
||||||
|
|
||||||
|
// Verify the proof with the root
|
||||||
|
let is_proof_valid = wasm_verify_with_roots(rln_instance, input_buffer, roots_buffer)
|
||||||
|
.expect("Failed to verify proof");
|
||||||
|
assert!(is_proof_valid, "verification failed");
|
||||||
|
|
||||||
|
// Format and display results
|
||||||
|
let format_duration = |duration_ms: f64| -> String {
|
||||||
|
let avg_ms = duration_ms / (iterations as f64);
|
||||||
|
if avg_ms >= 1000.0 {
|
||||||
|
format!("{:.3} s", avg_ms / 1000.0)
|
||||||
|
} else {
|
||||||
|
format!("{:.3} ms", avg_ms)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
results.push_str(&format!("wasm_new: {}\n", format_duration(wasm_new_result)));
|
||||||
|
results.push_str(&format!(
|
||||||
|
"wasm_key_gen: {}\n",
|
||||||
|
format_duration(wasm_key_gen_result)
|
||||||
|
));
|
||||||
|
results.push_str(&format!(
|
||||||
|
"calculateWitness: {}\n",
|
||||||
|
format_duration(calculate_witness_result)
|
||||||
|
));
|
||||||
|
results.push_str(&format!(
|
||||||
|
"wasm_generate_rln_proof_with_witness: {}\n",
|
||||||
|
format_duration(wasm_generate_rln_proof_with_witness_result)
|
||||||
|
));
|
||||||
|
results.push_str(&format!(
|
||||||
|
"wasm_verify_with_roots: {}\n",
|
||||||
|
format_duration(wasm_verify_with_roots_result)
|
||||||
|
));
|
||||||
|
|
||||||
|
// Log the results
|
||||||
|
console_log!("{results}");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#![cfg(not(feature = "multithread"))]
|
||||||
#![cfg(target_arch = "wasm32")]
|
#![cfg(target_arch = "wasm32")]
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -8,15 +9,43 @@ mod tests {
|
|||||||
use rln::poseidon_tree::PoseidonTree;
|
use rln::poseidon_tree::PoseidonTree;
|
||||||
use rln::protocol::{prepare_verify_input, rln_witness_from_values, serialize_witness};
|
use rln::protocol::{prepare_verify_input, rln_witness_from_values, serialize_witness};
|
||||||
use rln::utils::{bytes_le_to_fr, fr_to_bytes_le};
|
use rln::utils::{bytes_le_to_fr, fr_to_bytes_le};
|
||||||
use rln_wasm::*;
|
use rln_wasm::{
|
||||||
use wasm_bindgen::{prelude::*, JsValue};
|
wasm_generate_rln_proof_with_witness, wasm_key_gen, wasm_new, wasm_rln_witness_to_json,
|
||||||
use wasm_bindgen_test::wasm_bindgen_test;
|
wasm_verify_with_roots,
|
||||||
|
};
|
||||||
|
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
|
||||||
|
use wasm_bindgen_test::{console_log, wasm_bindgen_test};
|
||||||
use zerokit_utils::merkle_tree::merkle_tree::ZerokitMerkleTree;
|
use zerokit_utils::merkle_tree::merkle_tree::ZerokitMerkleTree;
|
||||||
|
|
||||||
#[wasm_bindgen(module = "src/utils.js")]
|
#[wasm_bindgen(inline_js = r#"
|
||||||
|
const fs = require("fs");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
readFile: function (path) {
|
||||||
|
return fs.readFileSync(path);
|
||||||
|
},
|
||||||
|
|
||||||
|
calculateWitness: async function (circom_path, inputs) {
|
||||||
|
const wc = require("resources/witness_calculator_node.js");
|
||||||
|
const wasmFile = fs.readFileSync(circom_path);
|
||||||
|
const wasmFileBuffer = wasmFile.slice(
|
||||||
|
wasmFile.byteOffset,
|
||||||
|
wasmFile.byteOffset + wasmFile.byteLength
|
||||||
|
);
|
||||||
|
const witnessCalculator = await wc(wasmFileBuffer);
|
||||||
|
const calculatedWitness = await witnessCalculator.calculateWitness(
|
||||||
|
inputs,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
return JSON.stringify(calculatedWitness, (key, value) =>
|
||||||
|
typeof value === "bigint" ? value.toString() : value
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
"#)]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[wasm_bindgen(catch)]
|
#[wasm_bindgen(catch)]
|
||||||
fn read_file(path: &str) -> Result<Uint8Array, JsValue>;
|
fn readFile(path: &str) -> Result<Uint8Array, JsValue>;
|
||||||
|
|
||||||
#[wasm_bindgen(catch)]
|
#[wasm_bindgen(catch)]
|
||||||
async fn calculateWitness(circom_path: &str, input: Object) -> Result<JsValue, JsValue>;
|
async fn calculateWitness(circom_path: &str, input: Object) -> Result<JsValue, JsValue>;
|
||||||
@@ -34,7 +63,7 @@ mod tests {
|
|||||||
let mut results = String::from("\nbenchmarks:\n");
|
let mut results = String::from("\nbenchmarks:\n");
|
||||||
let iterations = 10;
|
let iterations = 10;
|
||||||
|
|
||||||
let zkey = read_file(&ZKEY_PATH).expect("Failed to read zkey file");
|
let zkey = readFile(&ZKEY_PATH).expect("Failed to read zkey file");
|
||||||
|
|
||||||
// Benchmark wasm_new
|
// Benchmark wasm_new
|
||||||
let start_wasm_new = Date::now();
|
let start_wasm_new = Date::now();
|
||||||
@@ -175,7 +204,7 @@ mod tests {
|
|||||||
format_duration(wasm_key_gen_result)
|
format_duration(wasm_key_gen_result)
|
||||||
));
|
));
|
||||||
results.push_str(&format!(
|
results.push_str(&format!(
|
||||||
"calculateWitness: {}\n",
|
"calculate_witness: {}\n",
|
||||||
format_duration(calculate_witness_result)
|
format_duration(calculate_witness_result)
|
||||||
));
|
));
|
||||||
results.push_str(&format!(
|
results.push_str(&format!(
|
||||||
@@ -188,102 +217,6 @@ mod tests {
|
|||||||
));
|
));
|
||||||
|
|
||||||
// Log the results
|
// Log the results
|
||||||
wasm_bindgen_test::console_log!("{results}");
|
console_log!("{results}");
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
|
||||||
pub async fn rln_wasm_test() {
|
|
||||||
// Read the zkey file
|
|
||||||
let zkey = read_file(&ZKEY_PATH).expect("Failed to read zkey file");
|
|
||||||
|
|
||||||
// Create RLN instance and separated tree
|
|
||||||
let rln_instance = wasm_new(zkey).expect("Failed to create RLN instance");
|
|
||||||
let mut tree = PoseidonTree::default(TEST_TREE_HEIGHT).expect("Failed to create tree");
|
|
||||||
|
|
||||||
// Setting up the epoch and rln_identifier
|
|
||||||
let epoch = hash_to_field(b"test-epoch");
|
|
||||||
let rln_identifier = hash_to_field(b"test-rln-identifier");
|
|
||||||
let external_nullifier = poseidon_hash(&[epoch, rln_identifier]);
|
|
||||||
|
|
||||||
// Generate identity pair
|
|
||||||
let mem_keys = wasm_key_gen(rln_instance).expect("Failed to generate keys");
|
|
||||||
let (identity_secret_hash, _) = bytes_le_to_fr(&mem_keys.subarray(0, 32).to_vec());
|
|
||||||
let (id_commitment, _) = bytes_le_to_fr(&mem_keys.subarray(32, 64).to_vec());
|
|
||||||
|
|
||||||
// Get index of the identity
|
|
||||||
let identity_index = tree.leaves_set();
|
|
||||||
|
|
||||||
// Setting up the user message limit
|
|
||||||
let user_message_limit = Fr::from(100);
|
|
||||||
|
|
||||||
// Updating the tree with the rate commitment
|
|
||||||
let rate_commitment = poseidon_hash(&[id_commitment, user_message_limit]);
|
|
||||||
tree.update_next(rate_commitment)
|
|
||||||
.expect("Failed to update tree");
|
|
||||||
|
|
||||||
// Generate merkle proof
|
|
||||||
let merkle_proof = tree
|
|
||||||
.proof(identity_index)
|
|
||||||
.expect("Failed to generate merkle proof");
|
|
||||||
|
|
||||||
// Create message id and signal
|
|
||||||
let message_id = Fr::from(0);
|
|
||||||
let signal: [u8; 32] = [0; 32];
|
|
||||||
let x = hash_to_field(&signal);
|
|
||||||
|
|
||||||
// Prepare input for witness calculation
|
|
||||||
let rln_witness = rln_witness_from_values(
|
|
||||||
identity_secret_hash,
|
|
||||||
&merkle_proof,
|
|
||||||
x,
|
|
||||||
external_nullifier,
|
|
||||||
user_message_limit,
|
|
||||||
message_id,
|
|
||||||
)
|
|
||||||
.expect("Failed to create RLN witness");
|
|
||||||
|
|
||||||
// Serialize the rln witness
|
|
||||||
let serialized_witness =
|
|
||||||
serialize_witness(&rln_witness).expect("Failed to serialize witness");
|
|
||||||
// Convert the serialized witness to a Uint8Array
|
|
||||||
let witness_buffer = Uint8Array::from(&serialized_witness[..]);
|
|
||||||
|
|
||||||
// Obtaining inputs that should be sent to circom witness calculator
|
|
||||||
let json_inputs = wasm_rln_witness_to_json(rln_instance, witness_buffer.clone())
|
|
||||||
.expect("Failed to convert witness to JSON");
|
|
||||||
|
|
||||||
// Calculating witness with JS
|
|
||||||
// (Using a JSON since wasm_bindgen does not like Result<Vec<JsBigInt>,JsValue>)
|
|
||||||
let calculated_witness_json = calculateWitness(&CIRCOM_PATH, json_inputs)
|
|
||||||
.await
|
|
||||||
.expect("Failed to calculate witness")
|
|
||||||
.as_string()
|
|
||||||
.expect("Failed to convert calculated witness to string");
|
|
||||||
let calculated_witness_vec_str: Vec<String> =
|
|
||||||
serde_json::from_str(&calculated_witness_json).expect("Failed to parse JSON");
|
|
||||||
let calculated_witness: Vec<JsBigInt> = calculated_witness_vec_str
|
|
||||||
.iter()
|
|
||||||
.map(|x| JsBigInt::new(&x.into()).expect("Failed to create JsBigInt"))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Generate a proof from the calculated witness
|
|
||||||
let proof =
|
|
||||||
wasm_generate_rln_proof_with_witness(rln_instance, calculated_witness, witness_buffer)
|
|
||||||
.expect("Failed to generate proof");
|
|
||||||
|
|
||||||
// Prepare the root for verification
|
|
||||||
let root = tree.root();
|
|
||||||
let roots_serialized = fr_to_bytes_le(&root);
|
|
||||||
let roots_buffer = Uint8Array::from(&roots_serialized[..]);
|
|
||||||
|
|
||||||
// Prepare input for proof verification
|
|
||||||
let proof_data = proof.to_vec();
|
|
||||||
let verify_input = prepare_verify_input(proof_data, &signal);
|
|
||||||
let input_buffer = Uint8Array::from(&verify_input[..]);
|
|
||||||
|
|
||||||
// Verify the proof with the root
|
|
||||||
let is_proof_valid = wasm_verify_with_roots(rln_instance, input_buffer, roots_buffer)
|
|
||||||
.expect("Failed to verify proof");
|
|
||||||
assert!(is_proof_valid, "verification failed");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user