diff --git a/.cargo/config.toml b/.cargo/config.toml
index 34f42c464..bba341ef1 100644
--- a/.cargo/config.toml
+++ b/.cargo/config.toml
@@ -7,4 +7,16 @@ rustflags = ["-C", "target-feature=+simd128,+relaxed-simd"]
[target.wasm32-wasip1]
runner = "wasmtime run --dir . "
-rustflags = ["-C", "target-feature=+simd128,+relaxed-simd"]
\ No newline at end of file
+rustflags = ["-C", "target-feature=+simd128,+relaxed-simd"]
+
+[target.wasm32-unknown-unknown]
+rustflags = [
+ "-C", "target-feature=+atomics,+bulk-memory,+mutable-globals,+simd128,+relaxed-simd,-reference-types",
+ "-C", "link-arg=--shared-memory",
+ "-C", "link-arg=--import-memory",
+ "-C", "link-arg=--max-memory=4294967296",
+ "-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",
+]
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 1e7e1bad6..ad7ca8c30 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,8 @@
*.json
# Allow JSON files in csca_registry
!**/csca_registry/**/*.json
+# Allow package.json files
+!**/package.json
*.gz
*.bin
*.nps
@@ -46,6 +48,10 @@ target/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
circuit_stats_examples/
+# Node.js
+node_modules/
+# wasm packages
+tooling/provekit-wasm/pkg/*
# logs for passport circuit benchmarks
-noir-examples/noir-passport/merkle_age_check/benchmark-inputs/logs/
\ No newline at end of file
+noir-examples/noir-passport/merkle_age_check/benchmark-inputs/logs/
diff --git a/Cargo.lock b/Cargo.lock
index 41f9f3811..1de6eee21 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -7,10 +7,10 @@ name = "acir"
version = "1.0.0-beta.11"
source = "git+https://github.com/noir-lang/noir?rev=v1.0.0-beta.11#fd3925aaaeb76c76319f44590d135498ef41ea6c"
dependencies = [
- "acir_field 1.0.0-beta.11",
+ "acir_field",
"base64",
"bincode 2.0.1",
- "brillig 1.0.0-beta.11",
+ "brillig",
"color-eyre",
"flate2",
"noir_protobuf",
@@ -21,32 +21,11 @@ dependencies = [
"rmp-serde",
"serde",
"serde-big-array",
- "strum 0.24.1",
- "strum_macros 0.24.3",
+ "strum",
+ "strum_macros",
"thiserror 1.0.69",
]
-[[package]]
-name = "acir"
-version = "1.0.0-beta.19"
-source = "git+https://github.com/noir-lang/noir.git?branch=master#efd7f97e84c659a92cc8b7c501c578fbb718df37"
-dependencies = [
- "acir_field 1.0.0-beta.19",
- "base64",
- "brillig 1.0.0-beta.19",
- "flate2",
- "noirc_span",
- "num-bigint",
- "num-traits",
- "num_enum",
- "rmp-serde",
- "serde",
- "serde-big-array",
- "strum 0.26.3",
- "strum_macros 0.26.4",
- "thiserror 2.0.18",
-]
-
[[package]]
name = "acir_field"
version = "1.0.0-beta.11"
@@ -61,28 +40,14 @@ dependencies = [
"serde",
]
-[[package]]
-name = "acir_field"
-version = "1.0.0-beta.19"
-source = "git+https://github.com/noir-lang/noir.git?branch=master#efd7f97e84c659a92cc8b7c501c578fbb718df37"
-dependencies = [
- "ark-bn254",
- "ark-ff 0.5.0",
- "ark-std 0.5.0",
- "cfg-if",
- "hex",
- "num-bigint",
- "serde",
-]
-
[[package]]
name = "acvm"
version = "1.0.0-beta.11"
source = "git+https://github.com/noir-lang/noir?rev=v1.0.0-beta.11#fd3925aaaeb76c76319f44590d135498ef41ea6c"
dependencies = [
- "acir 1.0.0-beta.11",
- "acvm_blackbox_solver 1.0.0-beta.11",
- "brillig_vm 1.0.0-beta.11",
+ "acir",
+ "acvm_blackbox_solver",
+ "brillig_vm",
"fxhash",
"indexmap 2.13.0",
"serde",
@@ -90,57 +55,24 @@ dependencies = [
"tracing",
]
-[[package]]
-name = "acvm"
-version = "1.0.0-beta.19"
-source = "git+https://github.com/noir-lang/noir.git?branch=master#efd7f97e84c659a92cc8b7c501c578fbb718df37"
-dependencies = [
- "acir 1.0.0-beta.19",
- "acvm_blackbox_solver 1.0.0-beta.19",
- "brillig_vm 1.0.0-beta.19",
- "indexmap 2.13.0",
- "rustc-hash",
- "serde",
- "thiserror 2.0.18",
- "tracing",
-]
-
[[package]]
name = "acvm_blackbox_solver"
version = "1.0.0-beta.11"
source = "git+https://github.com/noir-lang/noir?rev=v1.0.0-beta.11#fd3925aaaeb76c76319f44590d135498ef41ea6c"
dependencies = [
- "acir 1.0.0-beta.11",
- "blake2 0.10.6",
+ "acir",
+ "blake2",
"blake3",
- "k256 0.13.4",
- "keccak 0.1.6",
+ "k256",
+ "keccak",
"libaes",
"log",
"num-bigint",
- "p256 0.13.2",
- "sha2 0.10.9",
+ "p256",
+ "sha2",
"thiserror 1.0.69",
]
-[[package]]
-name = "acvm_blackbox_solver"
-version = "1.0.0-beta.19"
-source = "git+https://github.com/noir-lang/noir.git?branch=master#efd7f97e84c659a92cc8b7c501c578fbb718df37"
-dependencies = [
- "acir 1.0.0-beta.19",
- "aes",
- "blake2 0.11.0-rc.5",
- "blake3",
- "cbc",
- "k256 0.14.0-rc.7",
- "keccak 0.2.0-rc.2",
- "log",
- "p256 0.14.0-rc.7",
- "sha2 0.11.0-rc.5",
- "thiserror 2.0.18",
-]
-
[[package]]
name = "addr2line"
version = "0.25.1"
@@ -156,17 +88,6 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
-[[package]]
-name = "aes"
-version = "0.8.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
-dependencies = [
- "cfg-if",
- "cipher",
- "cpufeatures 0.2.17",
-]
-
[[package]]
name = "ahash"
version = "0.8.12"
@@ -326,41 +247,6 @@ dependencies = [
"ark-std 0.5.0",
]
-[[package]]
-name = "ark-crypto-primitives"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e0c292754729c8a190e50414fd1a37093c786c709899f29c9f7daccecfa855e"
-dependencies = [
- "ahash",
- "ark-crypto-primitives-macros",
- "ark-ec",
- "ark-ff 0.5.0",
- "ark-relations",
- "ark-serialize 0.5.0",
- "ark-snark",
- "ark-std 0.5.0",
- "blake2 0.10.6",
- "derivative",
- "digest 0.10.7",
- "fnv",
- "hashbrown 0.14.5",
- "merlin",
- "rayon",
- "sha2 0.10.9",
-]
-
-[[package]]
-name = "ark-crypto-primitives-macros"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7e89fe77d1f0f4fe5b96dfc940923d88d17b6a773808124f21e764dfb063c6a"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.117",
-]
-
[[package]]
name = "ark-ec"
version = "0.5.0"
@@ -379,7 +265,6 @@ dependencies = [
"num-bigint",
"num-integer",
"num-traits",
- "rayon",
"zeroize",
]
@@ -438,7 +323,6 @@ dependencies = [
"num-bigint",
"num-traits",
"paste",
- "rayon",
"zeroize",
]
@@ -537,18 +421,6 @@ dependencies = [
"hashbrown 0.15.5",
]
-[[package]]
-name = "ark-relations"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec46ddc93e7af44bcab5230937635b06fb5744464dd6a7e7b083e80ebd274384"
-dependencies = [
- "ark-ff 0.5.0",
- "ark-std 0.5.0",
- "tracing",
- "tracing-subscriber 0.2.25",
-]
-
[[package]]
name = "ark-serialize"
version = "0.3.0"
@@ -581,7 +453,6 @@ dependencies = [
"arrayvec",
"digest 0.10.7",
"num-bigint",
- "rayon",
]
[[package]]
@@ -595,18 +466,6 @@ dependencies = [
"syn 2.0.117",
]
-[[package]]
-name = "ark-snark"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d368e2848c2d4c129ce7679a7d0d2d612b6a274d3ea6a13bad4445d61b381b88"
-dependencies = [
- "ark-ff 0.5.0",
- "ark-relations",
- "ark-serialize 0.5.0",
- "ark-std 0.5.0",
-]
-
[[package]]
name = "ark-std"
version = "0.3.0"
@@ -635,7 +494,6 @@ checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a"
dependencies = [
"num-traits",
"rand 0.8.5",
- "rayon",
]
[[package]]
@@ -653,6 +511,45 @@ dependencies = [
"zeroize",
]
+[[package]]
+name = "asn1-rs"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048"
+dependencies = [
+ "asn1-rs-derive",
+ "asn1-rs-impl",
+ "displaydoc",
+ "nom",
+ "num-traits",
+ "rusticata-macros",
+ "thiserror 1.0.69",
+ "time",
+]
+
+[[package]]
+name = "asn1-rs-derive"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+ "synstructure",
+]
+
+[[package]]
+name = "asn1-rs-impl"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
[[package]]
name = "async-lsp"
version = "0.2.3"
@@ -789,12 +686,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
-[[package]]
-name = "base16ct"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6"
-
[[package]]
name = "base64"
version = "0.22.1"
@@ -891,21 +782,22 @@ dependencies = [
]
[[package]]
-name = "blake2"
-version = "0.10.6"
+name = "bitvec-nom2"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
+checksum = "d988fcc40055ceaa85edc55875a08f8abd29018582647fd82ad6128dba14a5f0"
dependencies = [
- "digest 0.10.7",
+ "bitvec",
+ "nom",
]
[[package]]
name = "blake2"
-version = "0.11.0-rc.5"
+version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d52965399b470437fc7f4d4b51134668dbc96573fea6f1b83318a420e4605745"
+checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
- "digest 0.11.1",
+ "digest 0.10.7",
]
[[package]]
@@ -919,7 +811,7 @@ dependencies = [
"cc",
"cfg-if",
"constant_time_eq",
- "cpufeatures 0.2.17",
+ "cpufeatures",
"digest 0.10.7",
"zeroize",
]
@@ -933,24 +825,6 @@ dependencies = [
"generic-array",
]
-[[package]]
-name = "block-buffer"
-version = "0.12.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be"
-dependencies = [
- "hybrid-array",
-]
-
-[[package]]
-name = "block-padding"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
-dependencies = [
- "generic-array",
-]
-
[[package]]
name = "bn254-multiplier"
version = "0.1.0"
@@ -979,8 +853,8 @@ name = "bn254_blackbox_solver"
version = "1.0.0-beta.11"
source = "git+https://github.com/noir-lang/noir?rev=v1.0.0-beta.11#fd3925aaaeb76c76319f44590d135498ef41ea6c"
dependencies = [
- "acir 1.0.0-beta.11",
- "acvm_blackbox_solver 1.0.0-beta.11",
+ "acir",
+ "acvm_blackbox_solver",
"ark-bn254",
"ark-ec",
"ark-ff 0.5.0",
@@ -1004,16 +878,7 @@ name = "brillig"
version = "1.0.0-beta.11"
source = "git+https://github.com/noir-lang/noir?rev=v1.0.0-beta.11#fd3925aaaeb76c76319f44590d135498ef41ea6c"
dependencies = [
- "acir_field 1.0.0-beta.11",
- "serde",
-]
-
-[[package]]
-name = "brillig"
-version = "1.0.0-beta.19"
-source = "git+https://github.com/noir-lang/noir.git?branch=master#efd7f97e84c659a92cc8b7c501c578fbb718df37"
-dependencies = [
- "acir_field 1.0.0-beta.19",
+ "acir_field",
"serde",
]
@@ -1022,25 +887,13 @@ name = "brillig_vm"
version = "1.0.0-beta.11"
source = "git+https://github.com/noir-lang/noir?rev=v1.0.0-beta.11#fd3925aaaeb76c76319f44590d135498ef41ea6c"
dependencies = [
- "acir 1.0.0-beta.11",
- "acvm_blackbox_solver 1.0.0-beta.11",
+ "acir",
+ "acvm_blackbox_solver",
"num-bigint",
"num-traits",
"thiserror 1.0.69",
]
-[[package]]
-name = "brillig_vm"
-version = "1.0.0-beta.19"
-source = "git+https://github.com/noir-lang/noir.git?branch=master#efd7f97e84c659a92cc8b7c501c578fbb718df37"
-dependencies = [
- "acir 1.0.0-beta.19",
- "acvm_blackbox_solver 1.0.0-beta.19",
- "num-bigint",
- "num-traits",
- "thiserror 2.0.18",
-]
-
[[package]]
name = "bstr"
version = "1.12.1"
@@ -1092,15 +945,6 @@ version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
-[[package]]
-name = "cbc"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
-dependencies = [
- "cipher",
-]
-
[[package]]
name = "cc"
version = "1.2.56"
@@ -1172,16 +1016,6 @@ dependencies = [
"half",
]
-[[package]]
-name = "cipher"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
-dependencies = [
- "crypto-common 0.1.7",
- "inout",
-]
-
[[package]]
name = "clap"
version = "4.5.60"
@@ -1243,12 +1077,6 @@ dependencies = [
"winapi",
]
-[[package]]
-name = "cmov"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de0758edba32d61d1fd9f4d69491b47604b91ee2f7e6b33de7e54ca4ebe55dc3"
-
[[package]]
name = "cobs"
version = "0.3.0"
@@ -1352,16 +1180,20 @@ dependencies = [
]
[[package]]
-name = "const-oid"
-version = "0.9.6"
+name = "console_error_panic_hook"
+version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
+checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen",
+]
[[package]]
name = "const-oid"
-version = "0.10.2"
+version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
+checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "const_format"
@@ -1383,6 +1215,15 @@ dependencies = [
"unicode-xid",
]
+[[package]]
+name = "const_panic"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e262cdaac42494e3ae34c43969f9cdeb7da178bdb4b66fa6a1ea2edb4c8ae652"
+dependencies = [
+ "typewit",
+]
+
[[package]]
name = "constant_time_eq"
version = "0.4.2"
@@ -1424,12 +1265,6 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
-[[package]]
-name = "cpubits"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ef0c543070d296ea414df2dd7625d1b24866ce206709d8a4a424f28377f5861"
-
[[package]]
name = "cpufeatures"
version = "0.2.17"
@@ -1440,14 +1275,20 @@ dependencies = [
]
[[package]]
-name = "cpufeatures"
-version = "0.3.0"
+name = "crc"
+version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
+checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d"
dependencies = [
- "libc",
+ "crc-catalog",
]
+[[package]]
+name = "crc-catalog"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
+
[[package]]
name = "crc32fast"
version = "1.5.0"
@@ -1515,22 +1356,6 @@ dependencies = [
"zeroize",
]
-[[package]]
-name = "crypto-bigint"
-version = "0.7.0-rc.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96dacf199529fb801ae62a9aafdc01b189e9504c0d1ee1512a4c16bcd8666a93"
-dependencies = [
- "cpubits",
- "ctutils",
- "getrandom 0.4.2",
- "hybrid-array",
- "num-traits",
- "rand_core 0.10.0",
- "subtle",
- "zeroize",
-]
-
[[package]]
name = "crypto-common"
version = "0.1.7"
@@ -1541,17 +1366,6 @@ dependencies = [
"typenum",
]
-[[package]]
-name = "crypto-common"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710"
-dependencies = [
- "getrandom 0.4.2",
- "hybrid-array",
- "rand_core 0.10.0",
-]
-
[[package]]
name = "csv"
version = "1.4.0"
@@ -1573,16 +1387,6 @@ dependencies = [
"memchr",
]
-[[package]]
-name = "ctutils"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1005a6d4446f5120ef475ad3d2af2b30c49c2c9c6904258e3bb30219bebed5e4"
-dependencies = [
- "cmov",
- "subtle",
-]
-
[[package]]
name = "dap"
version = "0.4.1-alpha1"
@@ -1629,26 +1433,35 @@ dependencies = [
"syn 2.0.117",
]
+[[package]]
+name = "data-encoding"
+version = "2.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea"
+
[[package]]
name = "der"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
dependencies = [
- "const-oid 0.9.6",
- "pem-rfc7468 0.7.0",
+ "const-oid",
+ "pem-rfc7468",
"zeroize",
]
[[package]]
-name = "der"
-version = "0.8.0"
+name = "der-parser"
+version = "9.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b"
+checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553"
dependencies = [
- "const-oid 0.10.2",
- "pem-rfc7468 1.0.0",
- "zeroize",
+ "asn1-rs",
+ "displaydoc",
+ "nom",
+ "num-bigint",
+ "num-traits",
+ "rusticata-macros",
]
[[package]]
@@ -1698,24 +1511,12 @@ version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
- "block-buffer 0.10.4",
- "const-oid 0.9.6",
- "crypto-common 0.1.7",
+ "block-buffer",
+ "const-oid",
+ "crypto-common",
"subtle",
]
-[[package]]
-name = "digest"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "285743a676ccb6b3e116bc14cc69319b957867930ae9c4822f8e0f54509d7243"
-dependencies = [
- "block-buffer 0.12.0",
- "const-oid 0.10.2",
- "crypto-common 0.2.1",
- "ctutils",
-]
-
[[package]]
name = "dirs"
version = "4.0.0"
@@ -1793,6 +1594,12 @@ dependencies = [
"syn 2.0.117",
]
+[[package]]
+name = "doc-comment"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "780955b8b195a21ab8e4ac6b60dd1dbdcec1dc6c51c0617964b08c81785e12c9"
+
[[package]]
name = "dyn-clone"
version = "1.0.20"
@@ -1820,27 +1627,12 @@ version = "0.16.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
dependencies = [
- "der 0.7.10",
+ "der",
"digest 0.10.7",
- "elliptic-curve 0.13.8",
- "rfc6979 0.4.0",
- "signature 2.2.0",
- "spki 0.7.3",
-]
-
-[[package]]
-name = "ecdsa"
-version = "0.17.0-rc.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91bbdd377139884fafcad8dc43a760a3e1e681aa26db910257fa6535b70e1829"
-dependencies = [
- "der 0.8.0",
- "digest 0.11.1",
- "elliptic-curve 0.14.0-rc.28",
- "rfc6979 0.5.0-rc.5",
- "signature 3.0.0-rc.10",
- "spki 0.8.0-rc.4",
- "zeroize",
+ "elliptic-curve",
+ "rfc6979",
+ "signature",
+ "spki",
]
[[package]]
@@ -1867,38 +1659,16 @@ version = "0.13.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
dependencies = [
- "base16ct 0.2.0",
- "crypto-bigint 0.5.5",
+ "base16ct",
+ "crypto-bigint",
"digest 0.10.7",
"ff",
"generic-array",
"group",
- "pem-rfc7468 0.7.0",
- "pkcs8 0.10.2",
+ "pem-rfc7468",
+ "pkcs8",
"rand_core 0.6.4",
- "sec1 0.7.3",
- "subtle",
- "zeroize",
-]
-
-[[package]]
-name = "elliptic-curve"
-version = "0.14.0-rc.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bde7860544606d222fd6bd6d9f9a0773321bf78072a637e1d560a058c0031978"
-dependencies = [
- "base16ct 1.0.0",
- "crypto-bigint 0.7.0-rc.28",
- "crypto-common 0.2.1",
- "digest 0.11.1",
- "hybrid-array",
- "once_cell",
- "pem-rfc7468 1.0.0",
- "pkcs8 0.11.0-rc.11",
- "rand_core 0.10.0",
- "rustcrypto-ff",
- "rustcrypto-group",
- "sec1 0.8.0-rc.13",
+ "sec1",
"subtle",
"zeroize",
]
@@ -2130,7 +1900,7 @@ version = "1.0.0-beta.11"
source = "git+https://github.com/noir-lang/noir?rev=v1.0.0-beta.11#fd3925aaaeb76c76319f44590d135498ef41ea6c"
dependencies = [
"codespan-reporting",
- "iter-extended 1.0.0-beta.11",
+ "iter-extended",
"serde",
]
@@ -2329,8 +2099,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
+ "js-sys",
"libc",
"wasi",
+ "wasm-bindgen",
]
[[package]]
@@ -2340,9 +2112,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
+ "js-sys",
"libc",
"r-efi 5.3.0",
"wasip2",
+ "wasm-bindgen",
]
[[package]]
@@ -2421,15 +2195,6 @@ version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
-[[package]]
-name = "hashbrown"
-version = "0.14.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
-dependencies = [
- "allocator-api2",
-]
-
[[package]]
name = "hashbrown"
version = "0.15.5"
@@ -2508,15 +2273,6 @@ dependencies = [
"digest 0.10.7",
]
-[[package]]
-name = "hmac"
-version = "0.13.0-rc.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef451d73f36d8a3f93ad32c332ea01146c9650e1ec821a9b0e46c01277d544f8"
-dependencies = [
- "digest 0.11.1",
-]
-
[[package]]
name = "http"
version = "1.4.0"
@@ -2562,17 +2318,6 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
-[[package]]
-name = "hybrid-array"
-version = "0.4.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1b229d73f5803b562cc26e4da0396c8610a4ee209f4fac8fa4f8d709166dc45"
-dependencies = [
- "subtle",
- "typenum",
- "zeroize",
-]
-
[[package]]
name = "hyper"
version = "1.8.1"
@@ -2885,16 +2630,6 @@ dependencies = [
"libc",
]
-[[package]]
-name = "inout"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
-dependencies = [
- "block-padding",
- "generic-array",
-]
-
[[package]]
name = "inplace-vec-builder"
version = "0.1.1"
@@ -2942,11 +2677,6 @@ name = "iter-extended"
version = "1.0.0-beta.11"
source = "git+https://github.com/noir-lang/noir?rev=v1.0.0-beta.11#fd3925aaaeb76c76319f44590d135498ef41ea6c"
-[[package]]
-name = "iter-extended"
-version = "1.0.0-beta.19"
-source = "git+https://github.com/noir-lang/noir.git?branch=master#efd7f97e84c659a92cc8b7c501c578fbb718df37"
-
[[package]]
name = "itertools"
version = "0.10.5"
@@ -3105,6 +2835,12 @@ dependencies = [
"thiserror 2.0.18",
]
+[[package]]
+name = "jzon"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17ab85f84ca42c5ec520e6f3c9966ba1fd62909ce260f8837e248857d2560509"
+
[[package]]
name = "k256"
version = "0.13.4"
@@ -3112,42 +2848,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b"
dependencies = [
"cfg-if",
- "ecdsa 0.16.9",
- "elliptic-curve 0.13.8",
+ "ecdsa",
+ "elliptic-curve",
"once_cell",
- "sha2 0.10.9",
- "signature 2.2.0",
+ "sha2",
+ "signature",
]
[[package]]
-name = "k256"
-version = "0.14.0-rc.7"
+name = "keccak"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83da23da11f0b5db6f23d9280a84b3a33a746aa43ebb9270d6b445991da9cee3"
+checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653"
dependencies = [
- "cpubits",
- "ecdsa 0.17.0-rc.16",
- "elliptic-curve 0.14.0-rc.28",
- "sha2 0.11.0-rc.5",
- "signature 3.0.0-rc.10",
+ "cpufeatures",
]
[[package]]
-name = "keccak"
-version = "0.1.6"
+name = "konst"
+version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653"
+checksum = "4381b9b00c55f251f2ebe9473aef7c117e96828def1a7cb3bd3f0f903c6894e9"
dependencies = [
- "cpufeatures 0.2.17",
+ "const_panic",
+ "konst_kernel",
+ "typewit",
]
[[package]]
-name = "keccak"
-version = "0.2.0-rc.2"
+name = "konst_kernel"
+version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "882b69cb15b1f78b51342322a97ccd16f5123d1dc8a3da981a95244f488e8692"
+checksum = "e4b1eb7788f3824c629b1116a7a9060d6e898c358ebff59070093d51103dcc3c"
dependencies = [
- "cpufeatures 0.3.0",
+ "typewit",
]
[[package]]
@@ -3175,6 +2909,9 @@ name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+dependencies = [
+ "spin 0.9.8",
+]
[[package]]
name = "leb128fmt"
@@ -3266,7 +3003,7 @@ dependencies = [
"generator",
"scoped-tls",
"tracing",
- "tracing-subscriber 0.3.22",
+ "tracing-subscriber",
]
[[package]]
@@ -3304,6 +3041,16 @@ dependencies = [
"url",
]
+[[package]]
+name = "lzma-rs"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e"
+dependencies = [
+ "byteorder",
+ "crc",
+]
+
[[package]]
name = "lzma-sys"
version = "0.1.20"
@@ -3333,11 +3080,10 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
[[package]]
name = "mavros-artifacts"
version = "0.1.0"
-source = "git+https://github.com/reilabs/mavros?branch=split_main#2fe1fe3cafb2df3c46a220f3aa78f47acf63eeef"
+source = "git+https://github.com/reilabs/mavros?rev=3e47fd58001a0109a0314bc080b5246fd807ba04#3e47fd58001a0109a0314bc080b5246fd807ba04"
dependencies = [
"ark-bn254",
"ark-ff 0.5.0",
- "bincode 1.3.3",
"serde",
"tracing",
]
@@ -3345,14 +3091,11 @@ dependencies = [
[[package]]
name = "mavros-vm"
version = "0.1.0"
-source = "git+https://github.com/reilabs/mavros?branch=split_main#2fe1fe3cafb2df3c46a220f3aa78f47acf63eeef"
+source = "git+https://github.com/reilabs/mavros?rev=3e47fd58001a0109a0314bc080b5246fd807ba04#3e47fd58001a0109a0314bc080b5246fd807ba04"
dependencies = [
- "ark-bn254",
"ark-ff 0.5.0",
"mavros-artifacts",
- "noirc_abi 1.0.0-beta.19",
"opcode-gen",
- "serde",
"tracing",
]
@@ -3371,24 +3114,18 @@ dependencies = [
"autocfg",
]
-[[package]]
-name = "merlin"
-version = "3.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d"
-dependencies = [
- "byteorder",
- "keccak 0.1.6",
- "rand_core 0.6.4",
- "zeroize",
-]
-
[[package]]
name = "mime"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
[[package]]
name = "miniz_oxide"
version = "0.8.9"
@@ -3433,16 +3170,16 @@ name = "nargo"
version = "1.0.0-beta.11"
source = "git+https://github.com/noir-lang/noir?rev=v1.0.0-beta.11#fd3925aaaeb76c76319f44590d135498ef41ea6c"
dependencies = [
- "acvm 1.0.0-beta.11",
+ "acvm",
"fm",
- "iter-extended 1.0.0-beta.11",
+ "iter-extended",
"jsonrpsee",
"noir_greybox_fuzzer",
- "noirc_abi 1.0.0-beta.11",
+ "noirc_abi",
"noirc_driver",
"noirc_errors",
"noirc_frontend",
- "noirc_printable_type 1.0.0-beta.11",
+ "noirc_printable_type",
"rand 0.8.5",
"rayon",
"serde",
@@ -3459,7 +3196,7 @@ name = "nargo_cli"
version = "1.0.0-beta.11"
source = "git+https://github.com/noir-lang/noir?rev=v1.0.0-beta.11#fd3925aaaeb76c76319f44590d135498ef41ea6c"
dependencies = [
- "acvm 1.0.0-beta.11",
+ "acvm",
"async-lsp",
"bn254_blackbox_solver",
"build-data",
@@ -3471,7 +3208,7 @@ dependencies = [
"fm",
"fs2",
"fxhash",
- "iter-extended 1.0.0-beta.11",
+ "iter-extended",
"nargo",
"nargo_expand",
"nargo_fmt",
@@ -3480,7 +3217,7 @@ dependencies = [
"noir_ast_fuzzer",
"noir_debugger",
"noir_lsp",
- "noirc_abi 1.0.0-beta.11",
+ "noirc_abi",
"noirc_artifacts",
"noirc_artifacts_info",
"noirc_driver",
@@ -3503,7 +3240,7 @@ dependencies = [
"tower",
"tracing",
"tracing-appender",
- "tracing-subscriber 0.3.22",
+ "tracing-subscriber",
]
[[package]]
@@ -3589,30 +3326,43 @@ dependencies = [
"memoffset",
]
+[[package]]
+name = "noir-bignum-paramgen"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a2f214558ab24dd9af1d905187b42024370600b9458be46ab34c9af2f11e441"
+dependencies = [
+ "hex",
+ "itoa",
+ "num-bigint-dig",
+ "num-integer",
+ "num-traits",
+]
+
[[package]]
name = "noir_artifact_cli"
version = "1.0.0-beta.11"
source = "git+https://github.com/noir-lang/noir?rev=v1.0.0-beta.11#fd3925aaaeb76c76319f44590d135498ef41ea6c"
dependencies = [
- "acir 1.0.0-beta.11",
- "acvm 1.0.0-beta.11",
+ "acir",
+ "acvm",
"bn254_blackbox_solver",
"clap",
"color-eyre",
"const_format",
"fm",
"nargo",
- "noirc_abi 1.0.0-beta.11",
+ "noirc_abi",
"noirc_artifacts",
"noirc_artifacts_info",
"noirc_driver",
"noirc_errors",
- "noirc_printable_type 1.0.0-beta.11",
+ "noirc_printable_type",
"serde",
"serde_json",
"thiserror 1.0.69",
"toml 0.7.8",
- "tracing-subscriber 0.3.22",
+ "tracing-subscriber",
]
[[package]]
@@ -3620,18 +3370,18 @@ name = "noir_ast_fuzzer"
version = "1.0.0-beta.11"
source = "git+https://github.com/noir-lang/noir?rev=v1.0.0-beta.11#fd3925aaaeb76c76319f44590d135498ef41ea6c"
dependencies = [
- "acir 1.0.0-beta.11",
- "acvm 1.0.0-beta.11",
+ "acir",
+ "acvm",
"arbitrary",
"bn254_blackbox_solver",
"build-data",
"color-eyre",
"im",
- "iter-extended 1.0.0-beta.11",
+ "iter-extended",
"log",
"nargo",
"noir_greybox_fuzzer",
- "noirc_abi 1.0.0-beta.11",
+ "noirc_abi",
"noirc_driver",
"noirc_errors",
"noirc_evaluator",
@@ -3639,7 +3389,7 @@ dependencies = [
"proptest",
"rand 0.8.5",
"regex",
- "strum 0.24.1",
+ "strum",
]
[[package]]
@@ -3647,7 +3397,7 @@ name = "noir_debugger"
version = "1.0.0-beta.11"
source = "git+https://github.com/noir-lang/noir?rev=v1.0.0-beta.11#fd3925aaaeb76c76319f44590d135498ef41ea6c"
dependencies = [
- "acvm 1.0.0-beta.11",
+ "acvm",
"bn254_blackbox_solver",
"build-data",
"codespan-reporting",
@@ -3658,7 +3408,7 @@ dependencies = [
"noirc_artifacts",
"noirc_driver",
"noirc_errors",
- "noirc_printable_type 1.0.0-beta.11",
+ "noirc_printable_type",
"owo-colors",
"thiserror 1.0.69",
]
@@ -3668,10 +3418,10 @@ name = "noir_greybox_fuzzer"
version = "1.0.0-beta.11"
source = "git+https://github.com/noir-lang/noir?rev=v1.0.0-beta.11#fd3925aaaeb76c76319f44590d135498ef41ea6c"
dependencies = [
- "acvm 1.0.0-beta.11",
+ "acvm",
"build-data",
"fm",
- "noirc_abi 1.0.0-beta.11",
+ "noirc_abi",
"noirc_artifacts",
"num-traits",
"proptest",
@@ -3688,14 +3438,14 @@ name = "noir_lsp"
version = "1.0.0-beta.11"
source = "git+https://github.com/noir-lang/noir?rev=v1.0.0-beta.11#fd3925aaaeb76c76319f44590d135498ef41ea6c"
dependencies = [
- "acvm 1.0.0-beta.11",
+ "acvm",
"async-lsp",
"codespan-lsp",
"convert_case",
"fm",
"fuzzy-matcher",
"fxhash",
- "iter-extended 1.0.0-beta.11",
+ "iter-extended",
"nargo",
"nargo_expand",
"nargo_fmt",
@@ -3707,7 +3457,7 @@ dependencies = [
"rayon",
"serde",
"serde_json",
- "strum 0.24.1",
+ "strum",
"thiserror 1.0.69",
"tower",
"wasm-bindgen",
@@ -3727,9 +3477,9 @@ name = "noirc_abi"
version = "1.0.0-beta.11"
source = "git+https://github.com/noir-lang/noir?rev=v1.0.0-beta.11#fd3925aaaeb76c76319f44590d135498ef41ea6c"
dependencies = [
- "acvm 1.0.0-beta.11",
- "iter-extended 1.0.0-beta.11",
- "noirc_printable_type 1.0.0-beta.11",
+ "acvm",
+ "iter-extended",
+ "noirc_printable_type",
"num-bigint",
"num-traits",
"serde",
@@ -3738,22 +3488,6 @@ dependencies = [
"toml 0.7.8",
]
-[[package]]
-name = "noirc_abi"
-version = "1.0.0-beta.19"
-source = "git+https://github.com/noir-lang/noir.git?branch=master#efd7f97e84c659a92cc8b7c501c578fbb718df37"
-dependencies = [
- "acvm 1.0.0-beta.19",
- "iter-extended 1.0.0-beta.19",
- "noirc_printable_type 1.0.0-beta.19",
- "num-bigint",
- "num-traits",
- "serde",
- "serde_json",
- "thiserror 2.0.18",
- "toml 0.8.23",
-]
-
[[package]]
name = "noirc_arena"
version = "1.0.0-beta.11"
@@ -3764,13 +3498,13 @@ name = "noirc_artifacts"
version = "1.0.0-beta.11"
source = "git+https://github.com/noir-lang/noir?rev=v1.0.0-beta.11#fd3925aaaeb76c76319f44590d135498ef41ea6c"
dependencies = [
- "acvm 1.0.0-beta.11",
+ "acvm",
"codespan-reporting",
"fm",
- "noirc_abi 1.0.0-beta.11",
+ "noirc_abi",
"noirc_driver",
"noirc_errors",
- "noirc_printable_type 1.0.0-beta.11",
+ "noirc_printable_type",
"serde",
]
@@ -3779,10 +3513,10 @@ name = "noirc_artifacts_info"
version = "1.0.0-beta.11"
source = "git+https://github.com/noir-lang/noir?rev=v1.0.0-beta.11#fd3925aaaeb76c76319f44590d135498ef41ea6c"
dependencies = [
- "acir 1.0.0-beta.11",
- "acvm 1.0.0-beta.11",
+ "acir",
+ "acvm",
"clap",
- "iter-extended 1.0.0-beta.11",
+ "iter-extended",
"noirc_artifacts",
"prettytable-rs",
"rayon",
@@ -3795,13 +3529,13 @@ name = "noirc_driver"
version = "1.0.0-beta.11"
source = "git+https://github.com/noir-lang/noir?rev=v1.0.0-beta.11#fd3925aaaeb76c76319f44590d135498ef41ea6c"
dependencies = [
- "acvm 1.0.0-beta.11",
+ "acvm",
"build-data",
"clap",
"fm",
"fxhash",
- "iter-extended 1.0.0-beta.11",
- "noirc_abi 1.0.0-beta.11",
+ "iter-extended",
+ "noirc_abi",
"noirc_errors",
"noirc_evaluator",
"noirc_frontend",
@@ -3815,14 +3549,14 @@ name = "noirc_errors"
version = "1.0.0-beta.11"
source = "git+https://github.com/noir-lang/noir?rev=v1.0.0-beta.11#fd3925aaaeb76c76319f44590d135498ef41ea6c"
dependencies = [
- "acvm 1.0.0-beta.11",
+ "acvm",
"base64",
"codespan",
"codespan-reporting",
"flate2",
"fm",
"fxhash",
- "noirc_printable_type 1.0.0-beta.11",
+ "noirc_printable_type",
"serde",
"serde_json",
"tracing",
@@ -3833,17 +3567,17 @@ name = "noirc_evaluator"
version = "1.0.0-beta.11"
source = "git+https://github.com/noir-lang/noir?rev=v1.0.0-beta.11#fd3925aaaeb76c76319f44590d135498ef41ea6c"
dependencies = [
- "acvm 1.0.0-beta.11",
+ "acvm",
"bn254_blackbox_solver",
"cfg-if",
"chrono",
"fm",
"fxhash",
"im",
- "iter-extended 1.0.0-beta.11",
+ "iter-extended",
"noirc_errors",
"noirc_frontend",
- "noirc_printable_type 1.0.0-beta.11",
+ "noirc_printable_type",
"num-bigint",
"num-integer",
"num-traits",
@@ -3863,16 +3597,16 @@ name = "noirc_frontend"
version = "1.0.0-beta.11"
source = "git+https://github.com/noir-lang/noir?rev=v1.0.0-beta.11#fd3925aaaeb76c76319f44590d135498ef41ea6c"
dependencies = [
- "acvm 1.0.0-beta.11",
+ "acvm",
"bn254_blackbox_solver",
"cfg-if",
"fm",
"fxhash",
"im",
- "iter-extended 1.0.0-beta.11",
+ "iter-extended",
"noirc_arena",
"noirc_errors",
- "noirc_printable_type 1.0.0-beta.11",
+ "noirc_printable_type",
"num-bigint",
"num-traits",
"petgraph 0.8.3",
@@ -3882,8 +3616,8 @@ dependencies = [
"serde_json",
"small-ord-set",
"smol_str",
- "strum 0.24.1",
- "strum_macros 0.24.3",
+ "strum",
+ "strum_macros",
"thiserror 1.0.69",
"tracing",
]
@@ -3893,30 +3627,20 @@ name = "noirc_printable_type"
version = "1.0.0-beta.11"
source = "git+https://github.com/noir-lang/noir?rev=v1.0.0-beta.11#fd3925aaaeb76c76319f44590d135498ef41ea6c"
dependencies = [
- "acvm 1.0.0-beta.11",
- "iter-extended 1.0.0-beta.11",
+ "acvm",
+ "iter-extended",
"serde",
"serde_json",
]
[[package]]
-name = "noirc_printable_type"
-version = "1.0.0-beta.19"
-source = "git+https://github.com/noir-lang/noir.git?branch=master#efd7f97e84c659a92cc8b7c501c578fbb718df37"
-dependencies = [
- "acvm 1.0.0-beta.19",
- "iter-extended 1.0.0-beta.19",
- "serde",
- "serde_json",
-]
-
-[[package]]
-name = "noirc_span"
-version = "1.0.0-beta.19"
-source = "git+https://github.com/noir-lang/noir.git?branch=master#efd7f97e84c659a92cc8b7c501c578fbb718df37"
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
- "codespan",
- "serde",
+ "memchr",
+ "minimal-lexical",
]
[[package]]
@@ -3982,6 +3706,22 @@ dependencies = [
"num-traits",
]
+[[package]]
+name = "num-bigint-dig"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7"
+dependencies = [
+ "lazy_static",
+ "libm",
+ "num-integer",
+ "num-iter",
+ "num-traits",
+ "rand 0.8.5",
+ "smallvec",
+ "zeroize",
+]
+
[[package]]
name = "num-conv"
version = "0.2.0"
@@ -3997,6 +3737,17 @@ dependencies = [
"num-traits",
]
+[[package]]
+name = "num-iter"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
[[package]]
name = "num-traits"
version = "0.2.19"
@@ -4044,6 +3795,15 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "oid-registry"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9"
+dependencies = [
+ "asn1-rs",
+]
+
[[package]]
name = "once_cell"
version = "1.21.3"
@@ -4059,7 +3819,7 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "opcode-gen"
version = "0.1.0"
-source = "git+https://github.com/reilabs/mavros?branch=split_main#2fe1fe3cafb2df3c46a220f3aa78f47acf63eeef"
+source = "git+https://github.com/reilabs/mavros?rev=3e47fd58001a0109a0314bc080b5246fd807ba04#3e47fd58001a0109a0314bc080b5246fd807ba04"
dependencies = [
"proc-macro2",
"quote",
@@ -4134,23 +3894,10 @@ version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
dependencies = [
- "ecdsa 0.16.9",
- "elliptic-curve 0.13.8",
- "primeorder 0.13.6",
- "sha2 0.10.9",
-]
-
-[[package]]
-name = "p256"
-version = "0.14.0-rc.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "018bfbb86e05fd70a83e985921241035ee09fcd369c4a2c3680b389a01d2ad28"
-dependencies = [
- "ecdsa 0.17.0-rc.16",
- "elliptic-curve 0.14.0-rc.28",
- "primefield",
- "primeorder 0.14.0-rc.7",
- "sha2 0.11.0-rc.5",
+ "ecdsa",
+ "elliptic-curve",
+ "primeorder",
+ "sha2",
]
[[package]]
@@ -4355,6 +4102,37 @@ dependencies = [
"windows-link",
]
+[[package]]
+name = "passport-input-gen"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "argh",
+ "ark-bn254",
+ "ark-ff 0.5.0",
+ "base64",
+ "chrono",
+ "hex",
+ "lazy_static",
+ "noir-bignum-paramgen",
+ "noirc_abi",
+ "poseidon2",
+ "provekit-common",
+ "provekit-prover",
+ "rasn",
+ "rasn-cms",
+ "rasn-pkix",
+ "rsa",
+ "serde",
+ "serde_json",
+ "sha2",
+ "signature",
+ "thiserror 2.0.18",
+ "tracing",
+ "tracing-subscriber",
+ "x509-parser",
+]
+
[[package]]
name = "paste"
version = "1.0.15"
@@ -4363,18 +4141,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pem-rfc7468"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
-dependencies = [
- "base64ct",
-]
-
-[[package]]
-name = "pem-rfc7468"
-version = "1.0.0"
+version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a6305423e0e7738146434843d1694d621cce767262b2a86910beab705e4493d9"
+checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
dependencies = [
"base64ct",
]
@@ -4450,23 +4219,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
-name = "pkcs8"
-version = "0.10.2"
+name = "pkcs1"
+version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
+checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
dependencies = [
- "der 0.7.10",
- "spki 0.7.3",
+ "der",
+ "pkcs8",
+ "spki",
]
[[package]]
name = "pkcs8"
-version = "0.11.0-rc.11"
+version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12922b6296c06eb741b02d7b5161e3aaa22864af38dfa025a1a3ba3f68c84577"
+checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
dependencies = [
- "der 0.8.0",
- "spki 0.8.0-rc.4",
+ "der",
+ "spki",
]
[[package]]
@@ -4481,6 +4251,15 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
+[[package]]
+name = "poseidon2"
+version = "0.1.0"
+dependencies = [
+ "ark-bn254",
+ "ark-ff 0.5.0",
+ "ark-std 0.5.0",
+]
+
[[package]]
name = "postcard"
version = "1.1.3"
@@ -4542,36 +4321,13 @@ dependencies = [
"unicode-width",
]
-[[package]]
-name = "primefield"
-version = "0.14.0-rc.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93401c13cc7ff24684571cfca9d3cf9ebabfaf3d4b7b9963ade41ec54da196b5"
-dependencies = [
- "crypto-bigint 0.7.0-rc.28",
- "crypto-common 0.2.1",
- "rand_core 0.10.0",
- "rustcrypto-ff",
- "subtle",
- "zeroize",
-]
-
[[package]]
name = "primeorder"
version = "0.13.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
dependencies = [
- "elliptic-curve 0.13.8",
-]
-
-[[package]]
-name = "primeorder"
-version = "0.14.0-rc.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0c5c8a39bcd764bfedf456e8d55e115fe86dda3e0f555371849f2a41cbc9706"
-dependencies = [
- "elliptic-curve 0.14.0-rc.28",
+ "elliptic-curve",
]
[[package]]
@@ -4754,38 +4510,31 @@ name = "provekit-bench"
version = "0.1.0"
dependencies = [
"anyhow",
- "ark-ff 0.5.0",
- "ark-std 0.5.0",
"divan",
"nargo",
"nargo_cli",
"nargo_toml",
- "noir_artifact_cli",
- "noirc_abi 1.0.0-beta.11",
- "noirc_artifacts",
"noirc_driver",
"provekit-common",
"provekit-prover",
"provekit-r1cs-compiler",
"provekit-verifier",
- "rand 0.9.2",
"serde",
"test-case",
"toml 0.8.23",
- "whir",
]
[[package]]
name = "provekit-cli"
version = "0.1.0"
dependencies = [
- "acir 1.0.0-beta.11",
+ "acir",
"anyhow",
"argh",
"ark-ff 0.5.0",
"base64",
"hex",
- "noirc_abi 1.0.0-beta.11",
+ "noirc_abi",
"postcard",
"provekit-common",
"provekit-gnark",
@@ -4793,11 +4542,10 @@ dependencies = [
"provekit-r1cs-compiler",
"provekit-verifier",
"rayon",
- "serde",
"serde_json",
"tikv-jemallocator",
"tracing",
- "tracing-subscriber 0.3.22",
+ "tracing-subscriber",
"tracing-tracy",
]
@@ -4805,7 +4553,7 @@ dependencies = [
name = "provekit-common"
version = "0.1.0"
dependencies = [
- "acir 1.0.0-beta.11",
+ "acir",
"anyhow",
"ark-bn254",
"ark-ff 0.5.0",
@@ -4817,10 +4565,8 @@ dependencies = [
"itertools 0.14.0",
"mavros-artifacts",
"mavros-vm",
- "noirc_abi 1.0.0-beta.11",
+ "noirc_abi",
"postcard",
- "rand 0.8.5",
- "rand 0.9.2",
"rayon",
"ruint",
"serde",
@@ -4832,7 +4578,6 @@ dependencies = [
"whir",
"xz2",
"zerocopy",
- "zeroize",
"zstd",
]
@@ -4840,10 +4585,8 @@ dependencies = [
name = "provekit-ffi"
version = "0.1.0"
dependencies = [
- "acir 1.0.0-beta.11",
"anyhow",
"libc",
- "noirc_abi 1.0.0-beta.11",
"parking_lot",
"provekit-common",
"provekit-prover",
@@ -4866,9 +4609,8 @@ dependencies = [
name = "provekit-prover"
version = "0.1.0"
dependencies = [
- "acir 1.0.0-beta.11",
+ "acir",
"anyhow",
- "ark-crypto-primitives",
"ark-ff 0.5.0",
"ark-std 0.5.0",
"bn254_blackbox_solver",
@@ -4876,14 +4618,9 @@ dependencies = [
"mavros-vm",
"nargo",
"noir_artifact_cli",
- "noirc_abi 1.0.0-beta.11",
+ "noirc_abi",
"postcard",
"provekit-common",
- "rand 0.9.2",
- "rayon",
- "skyscraper",
- "spongefish",
- "spongefish-pow",
"tracing",
"whir",
]
@@ -4892,17 +4629,17 @@ dependencies = [
name = "provekit-r1cs-compiler"
version = "0.1.0"
dependencies = [
- "acir 1.0.0-beta.11",
+ "acir",
"anyhow",
"ark-bn254",
- "ark-crypto-primitives",
"ark-ff 0.5.0",
"ark-std 0.5.0",
"bincode 1.3.3",
"mavros-artifacts",
- "noirc_abi 1.0.0-beta.11",
+ "noirc_abi",
"noirc_artifacts",
"ntt",
+ "poseidon2",
"postcard",
"provekit-common",
"serde",
@@ -4923,6 +4660,30 @@ dependencies = [
"whir",
]
+[[package]]
+name = "provekit-wasm"
+version = "0.1.0"
+dependencies = [
+ "acir",
+ "anyhow",
+ "base64",
+ "console_error_panic_hook",
+ "getrandom 0.2.17",
+ "getrandom 0.3.4",
+ "hex",
+ "js-sys",
+ "lzma-rs",
+ "postcard",
+ "provekit-common",
+ "provekit-prover",
+ "provekit-verifier",
+ "ruzstd",
+ "serde-wasm-bindgen",
+ "serde_json",
+ "wasm-bindgen",
+ "wasm-bindgen-rayon",
+]
+
[[package]]
name = "quick-error"
version = "1.2.3"
@@ -5098,6 +4859,63 @@ version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68"
+[[package]]
+name = "rasn"
+version = "0.15.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5379b720091e4bf4a9f118eb46f4ffb67bb8b7551649528c89e265cf880e748"
+dependencies = [
+ "arrayvec",
+ "bitvec",
+ "bitvec-nom2",
+ "bytes",
+ "chrono",
+ "either",
+ "jzon",
+ "konst",
+ "nom",
+ "num-bigint",
+ "num-integer",
+ "num-traits",
+ "once_cell",
+ "rasn-derive",
+ "snafu",
+]
+
+[[package]]
+name = "rasn-cms"
+version = "0.15.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee9c688bd3aa3db270834720ab22b2862cd07ed094c4b2262bfb74a91008681e"
+dependencies = [
+ "rasn",
+ "rasn-pkix",
+]
+
+[[package]]
+name = "rasn-derive"
+version = "0.15.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e521162112419405837a6590b327f24707ce9f9b3ac9c9c4a4d10673b63abcd8"
+dependencies = [
+ "either",
+ "itertools 0.10.5",
+ "proc-macro2",
+ "quote",
+ "rayon",
+ "syn 1.0.109",
+ "uuid",
+]
+
+[[package]]
+name = "rasn-pkix"
+version = "0.15.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9f74a31343c2fd11da94025b8dcbeb96bfb207b4d480db99ad5554c117448fa"
+dependencies = [
+ "rasn",
+]
+
[[package]]
name = "rayon"
version = "1.11.0"
@@ -5106,6 +4924,7 @@ checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
dependencies = [
"either",
"rayon-core",
+ "wasm_sync",
]
[[package]]
@@ -5116,6 +4935,7 @@ checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
+ "wasm_sync",
]
[[package]]
@@ -5263,17 +5083,7 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
dependencies = [
- "hmac 0.12.1",
- "subtle",
-]
-
-[[package]]
-name = "rfc6979"
-version = "0.5.0-rc.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23a3127ee32baec36af75b4107082d9bd823501ec14a4e016be4b6b37faa74ae"
-dependencies = [
- "hmac 0.13.0-rc.5",
+ "hmac",
"subtle",
]
@@ -5320,6 +5130,27 @@ dependencies = [
"serde",
]
+[[package]]
+name = "rsa"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d"
+dependencies = [
+ "const-oid",
+ "digest 0.10.7",
+ "num-bigint-dig",
+ "num-integer",
+ "num-traits",
+ "pkcs1",
+ "pkcs8",
+ "rand_core 0.6.4",
+ "sha2",
+ "signature",
+ "spki",
+ "subtle",
+ "zeroize",
+]
+
[[package]]
name = "ruint"
version = "1.17.2"
@@ -5384,7 +5215,7 @@ version = "8.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1"
dependencies = [
- "sha2 0.10.9",
+ "sha2",
"walkdir",
]
@@ -5425,24 +5256,12 @@ dependencies = [
]
[[package]]
-name = "rustcrypto-ff"
-version = "0.14.0-rc.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5db129183b2c139d7d87d08be57cba626c715789db17aec65c8866bfd767d1f"
-dependencies = [
- "rand_core 0.10.0",
- "subtle",
-]
-
-[[package]]
-name = "rustcrypto-group"
-version = "0.14.0-rc.0"
+name = "rusticata-macros"
+version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57c4b1463f274a3ff6fb2f44da43e576cb9424367bd96f185ead87b52fe00523"
+checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632"
dependencies = [
- "rand_core 0.10.0",
- "rustcrypto-ff",
- "subtle",
+ "nom",
]
[[package]]
@@ -5597,6 +5416,15 @@ dependencies = [
"syn 1.0.109",
]
+[[package]]
+name = "ruzstd"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5ff0cc5e135c8870a775d3320910cd9b564ec036b4dc0b8741629020be63f01"
+dependencies = [
+ "twox-hash",
+]
+
[[package]]
name = "ryu"
version = "1.0.23"
@@ -5710,24 +5538,10 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
dependencies = [
- "base16ct 0.2.0",
- "der 0.7.10",
+ "base16ct",
+ "der",
"generic-array",
- "pkcs8 0.10.2",
- "subtle",
- "zeroize",
-]
-
-[[package]]
-name = "sec1"
-version = "0.8.0-rc.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a2400ed44a13193820aa528a19f376c3843141a8ce96ff34b11104cc79763f2"
-dependencies = [
- "base16ct 1.0.0",
- "ctutils",
- "der 0.8.0",
- "hybrid-array",
+ "pkcs8",
"subtle",
"zeroize",
]
@@ -5804,6 +5618,17 @@ dependencies = [
"serde",
]
+[[package]]
+name = "serde-wasm-bindgen"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b"
+dependencies = [
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+]
+
[[package]]
name = "serde_core"
version = "1.0.228"
@@ -5918,22 +5743,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
- "cpufeatures 0.2.17",
+ "cpufeatures",
"digest 0.10.7",
"sha2-asm",
]
-[[package]]
-name = "sha2"
-version = "0.11.0-rc.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c5f3b1e2dc8aad28310d8410bd4d7e180eca65fca176c52ab00d364475d0024"
-dependencies = [
- "cfg-if",
- "cpufeatures 0.2.17",
- "digest 0.11.1",
-]
-
[[package]]
name = "sha2-asm"
version = "0.6.4"
@@ -5952,7 +5766,7 @@ dependencies = [
"async-trait",
"bytes",
"hex",
- "sha2 0.10.9",
+ "sha2",
]
[[package]]
@@ -5962,7 +5776,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
dependencies = [
"digest 0.10.7",
- "keccak 0.1.6",
+ "keccak",
]
[[package]]
@@ -6006,16 +5820,6 @@ dependencies = [
"rand_core 0.6.4",
]
-[[package]]
-name = "signature"
-version = "3.0.0-rc.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f1880df446116126965eeec169136b2e0251dba37c6223bcc819569550edea3"
-dependencies = [
- "digest 0.11.1",
- "rand_core 0.10.0",
-]
-
[[package]]
name = "simd-adler32"
version = "0.3.8"
@@ -6108,6 +5912,29 @@ dependencies = [
"serde_core",
]
+[[package]]
+name = "snafu"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6"
+dependencies = [
+ "backtrace",
+ "doc-comment",
+ "snafu-derive",
+]
+
+[[package]]
+name = "snafu-derive"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf"
+dependencies = [
+ "heck 0.4.1",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
[[package]]
name = "socket2"
version = "0.6.2"
@@ -6149,17 +5976,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
dependencies = [
"base64ct",
- "der 0.7.10",
-]
-
-[[package]]
-name = "spki"
-version = "0.8.0-rc.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8baeff88f34ed0691978ec34440140e1572b68c7dd4a495fd14a3dc1944daa80"
-dependencies = [
- "base64ct",
- "der 0.8.0",
+ "der",
]
[[package]]
@@ -6171,10 +5988,10 @@ dependencies = [
"ark-serialize 0.5.0",
"blake3",
"digest 0.10.7",
- "keccak 0.1.6",
+ "keccak",
"p3-koala-bear",
"rand 0.8.5",
- "sha2 0.10.9",
+ "sha2",
"sha3",
"zeroize",
]
@@ -6186,7 +6003,7 @@ source = "git+https://github.com/arkworks-rs/spongefish?rev=fcc277f8a857fdeeadd7
dependencies = [
"blake3",
"bytemuck",
- "keccak 0.1.6",
+ "keccak",
"rand 0.8.5",
"rayon",
"spongefish",
@@ -6234,12 +6051,6 @@ version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
-[[package]]
-name = "strum"
-version = "0.26.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
-
[[package]]
name = "strum_macros"
version = "0.24.3"
@@ -6253,19 +6064,6 @@ dependencies = [
"syn 1.0.109",
]
-[[package]]
-name = "strum_macros"
-version = "0.26.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
-dependencies = [
- "heck 0.5.0",
- "proc-macro2",
- "quote",
- "rustversion",
- "syn 2.0.117",
-]
-
[[package]]
name = "subtle"
version = "2.6.1"
@@ -6777,7 +6575,7 @@ dependencies = [
"crossbeam-channel",
"thiserror 2.0.18",
"time",
- "tracing-subscriber 0.3.22",
+ "tracing-subscriber",
]
[[package]]
@@ -6808,7 +6606,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db"
dependencies = [
"tracing",
- "tracing-subscriber 0.3.22",
+ "tracing-subscriber",
]
[[package]]
@@ -6832,15 +6630,6 @@ dependencies = [
"tracing-core",
]
-[[package]]
-name = "tracing-subscriber"
-version = "0.2.25"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71"
-dependencies = [
- "tracing-core",
-]
-
[[package]]
name = "tracing-subscriber"
version = "0.3.22"
@@ -6869,7 +6658,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eaa1852afa96e0fe9e44caa53dc0bd2d9d05e0f2611ce09f97f8677af56e4ba"
dependencies = [
"tracing-core",
- "tracing-subscriber 0.3.22",
+ "tracing-subscriber",
"tracy-client",
]
@@ -6919,12 +6708,33 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+[[package]]
+name = "twox-hash"
+version = "2.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c"
+
[[package]]
name = "typenum"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
+[[package]]
+name = "typewit"
+version = "1.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8c1ae7cc0fdb8b842d65d127cb981574b0d2b249b74d1c7a2986863dc134f71"
+dependencies = [
+ "typewit_proc_macros",
+]
+
+[[package]]
+name = "typewit_proc_macros"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6"
+
[[package]]
name = "ucd-trie"
version = "0.1.7"
@@ -7028,6 +6838,15 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+[[package]]
+name = "uuid"
+version = "1.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37"
+dependencies = [
+ "getrandom 0.4.2",
+]
+
[[package]]
name = "valuable"
version = "0.1.1"
@@ -7067,13 +6886,13 @@ dependencies = [
"reqwest",
"serde",
"serde_json",
- "sha2 0.10.9",
+ "sha2",
"tokio",
"tokio-util",
"tower",
"tower-http",
"tracing",
- "tracing-subscriber 0.3.22",
+ "tracing-subscriber",
]
[[package]]
@@ -7214,6 +7033,18 @@ dependencies = [
"wasm-bindgen-shared",
]
+[[package]]
+name = "wasm-bindgen-rayon"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a16c60a56c81e4dc3b9c43d76ba5633e1c0278211d59a9cb07d61b6cd1c6583"
+dependencies = [
+ "crossbeam-channel",
+ "js-sys",
+ "rayon",
+ "wasm-bindgen",
+]
+
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.100"
@@ -7245,6 +7076,17 @@ dependencies = [
"wasmparser",
]
+[[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]]
name = "wasmparser"
version = "0.244.0"
@@ -7298,7 +7140,7 @@ dependencies = [
"blake3",
"ciborium",
"clap",
- "const-oid 0.9.6",
+ "const-oid",
"derive-where",
"digest 0.10.7",
"hex",
@@ -7308,7 +7150,7 @@ dependencies = [
"rayon",
"serde",
"serde_json",
- "sha2 0.10.9",
+ "sha2",
"sha3",
"spongefish",
"static_assertions",
@@ -7835,6 +7677,23 @@ dependencies = [
"tap",
]
+[[package]]
+name = "x509-parser"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69"
+dependencies = [
+ "asn1-rs",
+ "data-encoding",
+ "der-parser",
+ "lazy_static",
+ "nom",
+ "oid-registry",
+ "rusticata-macros",
+ "thiserror 1.0.69",
+ "time",
+]
+
[[package]]
name = "xz2"
version = "0.1.7"
diff --git a/Cargo.toml b/Cargo.toml
index ca25762d1..5d5d93063 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,6 +14,7 @@ members = [
"tooling/provekit-bench",
"tooling/provekit-ffi",
"tooling/provekit-gnark",
+ "tooling/provekit-wasm",
"tooling/verifier-server",
"ntt",
"poseidon2",
@@ -95,10 +96,11 @@ provekit-cli = { path = "tooling/cli" }
provekit-common = { path = "provekit/common" }
provekit-ffi = { path = "tooling/provekit-ffi" }
provekit-gnark = { path = "tooling/provekit-gnark" }
-provekit-prover = { path = "provekit/prover" }
+provekit-prover = { path = "provekit/prover", default-features = false }
provekit-r1cs-compiler = { path = "provekit/r1cs-compiler" }
provekit-verifier = { path = "provekit/verifier" }
provekit-verifier-server = { path = "tooling/verifier-server" }
+provekit-wasm = { path = "tooling/provekit-wasm" }
# 3rd party
anyhow = "1.0.93"
@@ -128,8 +130,6 @@ seq-macro = "0.3.6"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sha2 = { version = "0.10.9", features = ["asm"] }
-sha3 = "0.11.0-rc.3"
-blake3 = "1.5.6"
test-case = "3.3.1"
tikv-jemallocator = "0.6"
toml = "0.8.8"
@@ -147,6 +147,17 @@ zerocopy = "0.8.25"
zeroize = "1.8.1"
zstd = "0.13"
+# WASM-specific dependencies
+js-sys = "0.3"
+lzma-rs = "0.3"
+ruzstd = "0.8"
+wasm-bindgen-rayon = "1.2"
+wasm-bindgen = "0.2"
+serde-wasm-bindgen = "0.6"
+console_error_panic_hook = "0.1"
+getrandom = { version = "0.2", features = ["js"] }
+getrandom03 = { package = "getrandom", version = "0.3", features = ["wasm_js"] }
+
# Noir language dependencies
acir = { git = "https://github.com/noir-lang/noir", rev = "v1.0.0-beta.11" }
bn254_blackbox_solver = { git = "https://github.com/noir-lang/noir", rev = "v1.0.0-beta.11" }
@@ -162,7 +173,6 @@ noirc_driver = { git = "https://github.com/noir-lang/noir", rev = "v1.0.0-beta.1
ark-bn254 = { version = "0.5.0", default-features = false, features = [
"scalar_field",
] }
-ark-crypto-primitives = { version = "0.5", features = ["merkle_tree", "parallel"] }
ark-ff = { version = "0.5", features = ["asm", "std"] }
ark-poly = "0.5"
ark-serialize = "0.5"
diff --git a/noir-examples/noir_sha256/Prover.toml b/noir-examples/noir_sha256/Prover.toml
index e69de29bb..d8f7e9e8a 100644
--- a/noir-examples/noir_sha256/Prover.toml
+++ b/noir-examples/noir_sha256/Prover.toml
@@ -0,0 +1,3 @@
+input = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+
+expected = [102, 104, 122, 173, 248, 98, 189, 119, 108, 143, 193, 139, 142, 159, 142, 32, 8, 151, 20, 133, 110, 226, 51, 179, 144, 42, 89, 29, 13, 95, 41, 37]
diff --git a/noir-examples/poseidon-rounds/Prover.toml b/noir-examples/poseidon-rounds/Prover.toml
index 1794b6151..1a2fd79b2 100644
--- a/noir-examples/poseidon-rounds/Prover.toml
+++ b/noir-examples/poseidon-rounds/Prover.toml
@@ -1,4 +1,4 @@
-plains = [1, 2]
+plains = [1, 2, 3, 4]
# Value for 100 rounds
# result = '0x0b50a50fda9459e4eda4b0e9382fcbb5d00c6b387bd6b79b9efb3ff95c2a32b6'
diff --git a/noir-examples/poseidon-rounds/src/main.nr b/noir-examples/poseidon-rounds/src/main.nr
index 646d1d429..c50f886c9 100644
--- a/noir-examples/poseidon-rounds/src/main.nr
+++ b/noir-examples/poseidon-rounds/src/main.nr
@@ -1,11 +1,8 @@
-use dep::poseidon2;
-
-fn main(plains: [Field; 2], result: Field) {
- let mut hash = poseidon2::bn254::hash_2(plains);
+fn main(plains: [Field; 4], result: Field) -> pub [Field; 4] {
+ let mut hash = std::hash::poseidon2_permutation(plains, 4);
let rounds = 1000;
for _ in 0..rounds {
- hash = poseidon2::bn254::hash_1([hash]);
+ hash = std::hash::poseidon2_permutation(hash, 4);
}
- println(hash);
- assert(hash == result);
+ hash
}
diff --git a/playground/wasm-demo/.gitignore b/playground/wasm-demo/.gitignore
new file mode 100644
index 000000000..fe0c8ba76
--- /dev/null
+++ b/playground/wasm-demo/.gitignore
@@ -0,0 +1,15 @@
+# Dependencies
+node_modules/
+vendor/
+
+# Generated artifacts (created by setup script)
+artifacts/
+pkg/
+pkg-web/
+
+# Build outputs
+*.wasm
+!src/**/*.wasm
+
+pnpm-lock.yaml
+package-lock.json
\ No newline at end of file
diff --git a/playground/wasm-demo/_headers b/playground/wasm-demo/_headers
new file mode 100644
index 000000000..3412e41ef
--- /dev/null
+++ b/playground/wasm-demo/_headers
@@ -0,0 +1,4 @@
+/*
+ Cross-Origin-Opener-Policy: same-origin
+ Cross-Origin-Embedder-Policy: require-corp
+ Access-Control-Allow-Origin: *
diff --git a/playground/wasm-demo/index.html b/playground/wasm-demo/index.html
new file mode 100644
index 000000000..6822d4c0f
--- /dev/null
+++ b/playground/wasm-demo/index.html
@@ -0,0 +1,1223 @@
+
+
+
+
+
+ ProveKit WASM Browser Demo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Drag & drop circuit files
+
Required: prover.pkp, verifier.pkv, inputs.json
+
+
+
+
+
+
+
REQUIREMENT CHECKLIST
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
PROOF GENERATION STEPS
+
+
+
+
+
1
+
+
Load WASM Modules
+
Status: Waiting...
+
+
+
+
+
2
+
+
Load Circuit & Artifacts
+
Status: Waiting...
+
+
+
+
+
3
+
+
Generate Witness (noir_js)
+
Status: Waiting...
+
+
+
+
+
4
+
+
Generate Proof (ProveKit WASM)
+
Status: Waiting...
+
+
+
+
+
5
+
+
Verify Proof (ProveKit WASM)
+
Status: Waiting...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
TOTAL TIME (WITNESS + PROOF)
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/playground/wasm-demo/noir-web/noir-init.mjs b/playground/wasm-demo/noir-web/noir-init.mjs
new file mode 100644
index 000000000..fdcfab778
--- /dev/null
+++ b/playground/wasm-demo/noir-web/noir-init.mjs
@@ -0,0 +1,82 @@
+/**
+ * noir_js browser initialization wrapper
+ *
+ * This module handles loading and initializing the Noir WASM modules
+ * for browser usage. It uses the web builds of acvm_js and noirc_abi.
+ */
+
+// Import web builds (resolved via import map)
+import initACVM, * as acvm from '@noir-lang/acvm_js';
+import initNoirC, * as noirc_abi from '@noir-lang/noirc_abi';
+
+let initialized = false;
+
+/**
+ * Decode base64 string to Uint8Array (browser implementation)
+ */
+function base64Decode(input) {
+ return Uint8Array.from(atob(input), (c) => c.charCodeAt(0));
+}
+
+// Simple Noir class implementation for browser
+// Based on the official noir_js implementation
+export class Noir {
+ constructor(circuit) {
+ this.circuit = circuit;
+ }
+
+ async execute(inputs, foreignCallHandler) {
+ if (!initialized) {
+ throw new Error('Call initNoir() before executing');
+ }
+
+ // Default foreign call handler
+ const defaultHandler = async (name, args) => {
+ if (name === 'print') {
+ return [];
+ }
+ throw new Error(`Unexpected oracle during execution: ${name}(${args.join(', ')})`);
+ };
+
+ const handler = foreignCallHandler || defaultHandler;
+
+ // Encode inputs using noirc_abi
+ const witnessMap = noirc_abi.abiEncode(this.circuit.abi, inputs);
+
+ // Decode bytecode from base64 and execute
+ const decodedBytecode = base64Decode(this.circuit.bytecode);
+ const witnessStack = await acvm.executeProgram(decodedBytecode, witnessMap, handler);
+
+ // Compress the witness stack
+ const witness = acvm.compressWitnessStack(witnessStack);
+
+ return { witness };
+ }
+}
+
+/**
+ * Initialize the Noir WASM modules.
+ * Must be called before using Noir or decompressWitness.
+ */
+export async function initNoir() {
+ if (initialized) return;
+
+ // Initialize ACVM and NoirC WASM modules in parallel
+ await Promise.all([
+ initACVM(),
+ initNoirC()
+ ]);
+
+ initialized = true;
+}
+
+/**
+ * Decompress a witness from compressed format.
+ * Note: This returns a witness stack, use [0].witness for the main witness.
+ */
+export function decompressWitness(compressed) {
+ if (!initialized) {
+ throw new Error('Call initNoir() before using decompressWitness');
+ }
+ return acvm.decompressWitnessStack(compressed);
+}
diff --git a/playground/wasm-demo/package.json b/playground/wasm-demo/package.json
new file mode 100644
index 000000000..b3a2db1cb
--- /dev/null
+++ b/playground/wasm-demo/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "provekit-wasm-demo",
+ "version": "1.0.0",
+ "description": "ProveKit WASM browser demo",
+ "type": "module",
+ "scripts": {
+ "setup": "node scripts/setup.mjs",
+ "demo:web": "node scripts/serve.mjs",
+ "serve": "node scripts/serve.mjs",
+ "clean": "rm -rf artifacts pkg pkg-web"
+ },
+ "dependencies": {
+ "@noir-lang/acvm_js": "1.0.0-beta.11",
+ "@noir-lang/noir_js": "1.0.0-beta.11",
+ "@noir-lang/noirc_abi": "1.0.0-beta.11"
+ }
+}
diff --git a/playground/wasm-demo/scripts/serve.mjs b/playground/wasm-demo/scripts/serve.mjs
new file mode 100644
index 000000000..d81e88829
--- /dev/null
+++ b/playground/wasm-demo/scripts/serve.mjs
@@ -0,0 +1,139 @@
+#!/usr/bin/env node
+/**
+ * Simple HTTP server for the web demo with Cross-Origin Isolation.
+ *
+ * Serves static files with proper MIME types and required headers for:
+ * - SharedArrayBuffer (needed for wasm-bindgen-rayon thread pool)
+ * - Cross-Origin Isolation (COOP + COEP headers)
+ */
+
+import { createServer } from "http";
+import { readFile, stat } from "fs/promises";
+import { extname, join, resolve } from "path";
+import { fileURLToPath } from "url";
+
+const __dirname = fileURLToPath(new URL(".", import.meta.url));
+const ROOT = resolve(__dirname, "..");
+const START_PORT = parseInt(process.env.PORT || "8080");
+
+const MIME_TYPES = {
+ ".html": "text/html",
+ ".js": "text/javascript",
+ ".mjs": "text/javascript",
+ ".css": "text/css",
+ ".json": "application/json",
+ ".wasm": "application/wasm",
+ ".toml": "text/plain",
+ ".png": "image/png",
+ ".jpg": "image/jpeg",
+ ".svg": "image/svg+xml",
+};
+
+async function serveFile(res, filePath) {
+ try {
+ const data = await readFile(filePath);
+ const ext = extname(filePath).toLowerCase();
+ const contentType = MIME_TYPES[ext] || "application/octet-stream";
+
+ res.writeHead(200, {
+ "Content-Type": contentType,
+ "Access-Control-Allow-Origin": "*",
+ // Cross-Origin Isolation headers required for SharedArrayBuffer
+ // These enable wasm-bindgen-rayon's Web Worker-based parallelism
+ "Cross-Origin-Opener-Policy": "same-origin",
+ "Cross-Origin-Embedder-Policy": "require-corp",
+ });
+ res.end(data);
+ } catch (err) {
+ if (err.code === "ENOENT") {
+ res.writeHead(404, { "Content-Type": "text/plain" });
+ res.end("Not Found");
+ } else {
+ console.error(err);
+ res.writeHead(500, { "Content-Type": "text/plain" });
+ res.end("Internal Server Error");
+ }
+ }
+}
+
+async function handleRequest(req, res) {
+ let urlPath = req.url.split("?")[0];
+
+ // Default to index.html
+ if (urlPath === "/") {
+ urlPath = "/index.html";
+ }
+
+ const filePath = join(ROOT, urlPath);
+
+ // Security: prevent directory traversal
+ if (!filePath.startsWith(ROOT)) {
+ res.writeHead(403, { "Content-Type": "text/plain" });
+ res.end("Forbidden");
+ return;
+ }
+
+ // Check if it's a directory
+ try {
+ const stats = await stat(filePath);
+ if (stats.isDirectory()) {
+ // For the pkg/ directory, serve the WASM JS entry point.
+ // wasm-bindgen-rayon workers do `import('../../..')` which resolves to
+ // the pkg/ directory. Browsers can't import directories, so we serve
+ // the main JS module directly.
+ const pkgEntry = join(filePath, "provekit_wasm.js");
+ try {
+ await stat(pkgEntry);
+ await serveFile(res, pkgEntry);
+ return;
+ } catch (_) {
+ // No provekit_wasm.js — fall back to index.html
+ }
+ await serveFile(res, join(filePath, "index.html"));
+ } else {
+ await serveFile(res, filePath);
+ }
+ } catch (err) {
+ if (err.code === "ENOENT") {
+ res.writeHead(404, { "Content-Type": "text/plain" });
+ res.end("Not Found");
+ } else {
+ console.error(err);
+ res.writeHead(500, { "Content-Type": "text/plain" });
+ res.end("Internal Server Error");
+ }
+ }
+}
+
+async function startServer(port, maxAttempts = 10) {
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
+ const currentPort = port + attempt;
+ try {
+ await new Promise((resolve, reject) => {
+ const server = createServer(handleRequest);
+ server.once("error", reject);
+ server.listen(currentPort, () => {
+ console.log(`\n🌐 ProveKit WASM Web Demo (with parallelism)`);
+ console.log(` Server running at http://localhost:${currentPort}`);
+ console.log(`\n Cross-Origin Isolation: ENABLED`);
+ console.log(` SharedArrayBuffer: AVAILABLE`);
+ console.log(` Thread pool: SUPPORTED`);
+ console.log(`\n Open the URL above in your browser to run the demo.`);
+ console.log(` Press Ctrl+C to stop.\n`);
+ resolve();
+ });
+ });
+ return; // Success
+ } catch (err) {
+ if (err.code === "EADDRINUSE") {
+ console.log(`Port ${currentPort} is in use, trying ${currentPort + 1}...`);
+ } else {
+ throw err;
+ }
+ }
+ }
+ console.error(`Could not find an available port after ${maxAttempts} attempts`);
+ process.exit(1);
+}
+
+startServer(START_PORT);
diff --git a/playground/wasm-demo/scripts/setup.mjs b/playground/wasm-demo/scripts/setup.mjs
new file mode 100644
index 000000000..84eb7ca37
--- /dev/null
+++ b/playground/wasm-demo/scripts/setup.mjs
@@ -0,0 +1,556 @@
+#!/usr/bin/env node
+/**
+ * Setup script for ProveKit WASM browser demo.
+ *
+ * Usage:
+ * node scripts/setup.mjs
+ *
+ * Builds WASM + CLI once, then prepares both SHA256 and Poseidon circuits
+ * into artifacts/sha256/ and artifacts/poseidon/ respectively.
+ */
+
+import { execSync, spawnSync } from "child_process";
+import {
+ existsSync,
+ mkdirSync,
+ copyFileSync,
+ readFileSync,
+ writeFileSync,
+ readdirSync,
+} from "fs";
+import { dirname, join, resolve } from "path";
+import { fileURLToPath } from "url";
+
+const __dirname = dirname(fileURLToPath(import.meta.url));
+const ROOT_DIR = resolve(__dirname, "../../..");
+const DEMO_DIR = resolve(__dirname, "..");
+const WASM_PKG_DIR = join(ROOT_DIR, "tooling/provekit-wasm/pkg");
+
+const CIRCUITS = [
+ { name: "sha256", path: join(ROOT_DIR, "noir-examples/noir_sha256") },
+ { name: "poseidon", path: join(ROOT_DIR, "noir-examples/poseidon-rounds") },
+];
+
+// Colors for console output
+const colors = {
+ reset: "\x1b[0m",
+ bright: "\x1b[1m",
+ green: "\x1b[32m",
+ yellow: "\x1b[33m",
+ blue: "\x1b[34m",
+ red: "\x1b[31m",
+};
+
+function log(msg, color = colors.reset) {
+ console.log(`${color}${msg}${colors.reset}`);
+}
+
+function logStep(step, msg) {
+ console.log(
+ `\n${colors.blue}[${step}]${colors.reset} ${colors.bright}${msg}${colors.reset}`
+ );
+}
+
+function logSuccess(msg) {
+ console.log(`${colors.green}✓${colors.reset} ${msg}`);
+}
+
+function logError(msg) {
+ console.error(`${colors.red}✗ ${msg}${colors.reset}`);
+}
+
+function run(cmd, opts = {}) {
+ log(` $ ${cmd}`, colors.yellow);
+ try {
+ execSync(cmd, { stdio: "inherit", ...opts });
+ return true;
+ } catch (e) {
+ logError(`Command failed: ${cmd}`);
+ return false;
+ }
+}
+
+function checkCommand(cmd, name) {
+ const result = spawnSync("which", [cmd], { stdio: "pipe" });
+ if (result.status !== 0) {
+ logError(`${name} not found. Please install it first.`);
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Get circuit name from Nargo.toml
+ */
+function getCircuitName(circuitDir) {
+ const nargoToml = join(circuitDir, "Nargo.toml");
+ if (!existsSync(nargoToml)) {
+ throw new Error(`Nargo.toml not found in ${circuitDir}`);
+ }
+
+ const content = readFileSync(nargoToml, "utf-8");
+ const match = content.match(/^name\s*=\s*"([^"]+)"/m);
+ if (!match) {
+ throw new Error("Could not find circuit name in Nargo.toml");
+ }
+ return match[1];
+}
+
+/**
+ * Parse a TOML value (handles strings, arrays, inline tables)
+ */
+function parseTomlValue(valueStr) {
+ valueStr = valueStr.trim();
+
+ // String
+ if (valueStr.startsWith('"') && valueStr.endsWith('"')) {
+ return valueStr.slice(1, -1);
+ }
+
+ // Single-quoted literal string (TOML literal strings)
+ if (valueStr.startsWith("'") && valueStr.endsWith("'")) {
+ return valueStr.slice(1, -1);
+ }
+
+ // Inline table { key = "value", ... }
+ if (valueStr.startsWith("{") && valueStr.endsWith("}")) {
+ const inner = valueStr.slice(1, -1).trim();
+ const obj = {};
+ // Parse key = value pairs, handling nested structures
+ let depth = 0;
+ let currentKey = "";
+ let currentValue = "";
+ let inKey = true;
+ let inString = false;
+
+ for (let i = 0; i < inner.length; i++) {
+ const char = inner[i];
+
+ if (char === '"' && inner[i - 1] !== "\\") {
+ inString = !inString;
+ }
+
+ if (!inString) {
+ if (char === "{" || char === "[") depth++;
+ if (char === "}" || char === "]") depth--;
+
+ if (char === "=" && depth === 0 && inKey) {
+ inKey = false;
+ continue;
+ }
+
+ if (char === "," && depth === 0) {
+ if (currentKey.trim() && currentValue.trim()) {
+ obj[currentKey.trim()] = parseTomlValue(currentValue.trim());
+ }
+ currentKey = "";
+ currentValue = "";
+ inKey = true;
+ continue;
+ }
+ }
+
+ if (inKey) {
+ currentKey += char;
+ } else {
+ currentValue += char;
+ }
+ }
+
+ // Handle last key-value pair
+ if (currentKey.trim() && currentValue.trim()) {
+ obj[currentKey.trim()] = parseTomlValue(currentValue.trim());
+ }
+
+ return obj;
+ }
+
+ // Array [ ... ]
+ if (valueStr.startsWith("[") && valueStr.endsWith("]")) {
+ const inner = valueStr.slice(1, -1).trim();
+ if (!inner) return [];
+
+ const items = [];
+ let depth = 0;
+ let current = "";
+ let inString = false;
+
+ for (let i = 0; i < inner.length; i++) {
+ const char = inner[i];
+
+ if (char === '"' && inner[i - 1] !== "\\") {
+ inString = !inString;
+ }
+
+ if (!inString) {
+ if (char === "{" || char === "[") depth++;
+ if (char === "}" || char === "]") depth--;
+
+ if (char === "," && depth === 0) {
+ if (current.trim()) {
+ items.push(parseTomlValue(current.trim()));
+ }
+ current = "";
+ continue;
+ }
+ }
+
+ current += char;
+ }
+
+ if (current.trim()) {
+ items.push(parseTomlValue(current.trim()));
+ }
+
+ return items;
+ }
+
+ // Number or bare string
+ return valueStr;
+}
+
+/**
+ * Check if brackets are balanced in a string
+ */
+function areBracketsBalanced(str) {
+ let depth = 0;
+ let inString = false;
+ for (let i = 0; i < str.length; i++) {
+ const char = str[i];
+ if (char === '"' && str[i - 1] !== "\\") {
+ inString = !inString;
+ }
+ if (!inString) {
+ if (char === "[" || char === "{") depth++;
+ if (char === "]" || char === "}") depth--;
+ }
+ }
+ return depth === 0;
+}
+
+/**
+ * Parse Prover.toml to JSON for browser demo
+ */
+function parseProverToml(content) {
+ const result = {};
+ const lines = content.split("\n");
+ let currentSection = null;
+ let pendingLine = "";
+
+ for (let i = 0; i < lines.length; i++) {
+ let line = lines[i].trim();
+
+ // Skip comments and empty lines (unless we're accumulating a multi-line value)
+ if (!pendingLine && (!line || line.startsWith("#"))) continue;
+
+ // If we have a pending line, append this line to it
+ if (pendingLine) {
+ // Skip comment lines within multi-line values
+ if (line.startsWith("#")) continue;
+ pendingLine += " " + line;
+ line = pendingLine;
+
+ // Check if brackets are balanced now
+ if (!areBracketsBalanced(line)) {
+ continue; // Keep accumulating
+ }
+ pendingLine = "";
+ }
+
+ // Section header [section]
+ const sectionMatch = line.match(/^\[([^\]]+)\]$/);
+ if (sectionMatch) {
+ currentSection = sectionMatch[1];
+ continue;
+ }
+
+ // Key = value (find first = that's not inside a string or nested structure)
+ const eqIndex = findTopLevelEquals(line);
+ if (eqIndex !== -1) {
+ const key = line.slice(0, eqIndex).trim();
+ const valueStr = line.slice(eqIndex + 1).trim();
+
+ // Check if this is an incomplete multi-line value
+ if (!areBracketsBalanced(valueStr)) {
+ pendingLine = line;
+ continue;
+ }
+
+ const value = parseTomlValue(valueStr);
+
+ const fullKey = currentSection ? `${currentSection}.${key}` : key;
+ setNestedValue(result, fullKey, value);
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Find the first = that's not inside quotes or nested structures
+ */
+function findTopLevelEquals(line) {
+ let inString = false;
+ let depth = 0;
+
+ for (let i = 0; i < line.length; i++) {
+ const char = line[i];
+
+ if (char === '"' && line[i - 1] !== "\\") {
+ inString = !inString;
+ }
+
+ if (!inString) {
+ if (char === "{" || char === "[") depth++;
+ if (char === "}" || char === "]") depth--;
+ if (char === "=" && depth === 0) {
+ return i;
+ }
+ }
+ }
+
+ return -1;
+}
+
+function setNestedValue(obj, path, value) {
+ const parts = path.split(".");
+ let current = obj;
+ for (let i = 0; i < parts.length - 1; i++) {
+ if (!(parts[i] in current)) {
+ current[parts[i]] = {};
+ }
+ current = current[parts[i]];
+ }
+ current[parts[parts.length - 1]] = value;
+}
+
+async function buildShared() {
+ log("\n🔧 ProveKit WASM Demo Setup\n", colors.bright);
+
+ // Check prerequisites
+ logStep("1/5", "Checking prerequisites...");
+
+ if (!checkCommand("nargo", "Noir (nargo)")) {
+ log(
+ "\nInstall Noir:\n curl -L https://raw.githubusercontent.com/noir-lang/noirup/refs/heads/main/install | bash"
+ );
+ log(" noirup --version v1.0.0-beta.11");
+ process.exit(1);
+ }
+ logSuccess("nargo found");
+
+ if (!checkCommand("wasm-bindgen", "wasm-bindgen-cli")) {
+ log("\nInstall wasm-bindgen-cli:\n cargo install wasm-bindgen-cli");
+ process.exit(1);
+ }
+ logSuccess("wasm-bindgen found");
+
+ if (!checkCommand("cargo", "Rust (cargo)")) {
+ log("\nInstall Rust: https://rustup.rs");
+ process.exit(1);
+ }
+ logSuccess("cargo found");
+
+ // Install npm deps and copy vendor files for browser import map
+ logStep("2/5", "Installing noir-lang npm packages...");
+ if (!run("npm install --legacy-peer-deps", { cwd: DEMO_DIR })) {
+ process.exit(1);
+ }
+ const vendorDir = join(DEMO_DIR, "vendor");
+ const vendorMappings = [
+ { pkg: "@noir-lang/acvm_js", dest: "acvm_js" },
+ { pkg: "@noir-lang/noirc_abi", dest: "noirc_abi" },
+ ];
+ for (const { pkg, dest } of vendorMappings) {
+ const srcDir = join(DEMO_DIR, "node_modules", pkg);
+ const destDir = join(vendorDir, dest);
+ if (!existsSync(destDir)) mkdirSync(destDir, { recursive: true });
+ for (const entry of readdirSync(srcDir)) {
+ if (entry.endsWith(".js") || entry.endsWith(".wasm") || entry.endsWith(".d.ts")) {
+ copyFileSync(join(srcDir, entry), join(destDir, entry));
+ }
+ }
+ }
+ logSuccess("Vendor files copied from node_modules");
+
+ // Build WASM package with thread support (requires -Z build-std for atomics)
+ logStep("3/5", "Building WASM package with thread support...");
+
+ // cargo build with -Z build-std to rebuild std with atomics support
+ // RUSTFLAGS for atomics/shared-memory are in .cargo/config.toml
+ if (!run(
+ `cargo build --release --target wasm32-unknown-unknown -p provekit-wasm -Z build-std=panic_abort,std`,
+ { cwd: ROOT_DIR }
+ )) {
+ process.exit(1);
+ }
+
+ // Generate JS bindings from the built .wasm
+ if (!run(
+ `wasm-bindgen --target web --out-dir tooling/provekit-wasm/pkg target/wasm32-unknown-unknown/release/provekit_wasm.wasm`,
+ { cwd: ROOT_DIR }
+ )) {
+ process.exit(1);
+ }
+ logSuccess("WASM package built");
+
+ // Copy WASM package to demo/pkg
+ const wasmDestDir = join(DEMO_DIR, "pkg");
+ if (!existsSync(wasmDestDir)) {
+ mkdirSync(wasmDestDir, { recursive: true });
+ }
+
+ for (const file of [
+ "provekit_wasm_bg.wasm",
+ "provekit_wasm.js",
+ "provekit_wasm.d.ts",
+ "package.json",
+ ]) {
+ const src = join(WASM_PKG_DIR, file);
+ const dest = join(wasmDestDir, file);
+ if (existsSync(src)) {
+ copyFileSync(src, dest);
+ }
+ }
+
+ // Copy snippets directory (for wasm-bindgen-rayon worker helpers)
+ const snippetsDir = join(WASM_PKG_DIR, "snippets");
+ if (existsSync(snippetsDir)) {
+ const snippetsDestDir = join(wasmDestDir, "snippets");
+ if (!existsSync(snippetsDestDir)) {
+ mkdirSync(snippetsDestDir, { recursive: true });
+ }
+ function copyDirRecursive(src, dest) {
+ if (!existsSync(dest)) mkdirSync(dest, { recursive: true });
+ for (const entry of readdirSync(src, { withFileTypes: true })) {
+ const srcPath = join(src, entry.name);
+ const destPath = join(dest, entry.name);
+ if (entry.isDirectory()) {
+ copyDirRecursive(srcPath, destPath);
+ } else {
+ copyFileSync(srcPath, destPath);
+ }
+ }
+ }
+ copyDirRecursive(snippetsDir, snippetsDestDir);
+ logSuccess("WASM snippets copied (for thread pool)");
+
+ function patchWorkerHelpers(dir) {
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
+ const fullPath = join(dir, entry.name);
+ if (entry.isDirectory()) {
+ patchWorkerHelpers(fullPath);
+ } else if (entry.name === "workerHelpers.js") {
+ let content = readFileSync(fullPath, "utf-8");
+ content = content.replace(
+ "import('../../..')",
+ "import('../../../provekit_wasm.js')"
+ );
+ writeFileSync(fullPath, content);
+ }
+ }
+ }
+ patchWorkerHelpers(snippetsDestDir);
+ logSuccess("Worker helpers patched for browser imports");
+ }
+ logSuccess("WASM package copied to demo/pkg");
+
+ // Build native CLI
+ logStep("4/5", "Building native CLI...");
+ if (!run("cargo build --profile release-fast --bin provekit-cli", { cwd: ROOT_DIR })) {
+ process.exit(1);
+ }
+ logSuccess("Native CLI built");
+}
+
+async function prepareCircuit({ name, path: circuitDir }) {
+ const artifactsDir = join(DEMO_DIR, "artifacts", name);
+ if (!existsSync(artifactsDir)) {
+ mkdirSync(artifactsDir, { recursive: true });
+ }
+
+ // Validate circuit directory
+ if (!existsSync(circuitDir)) {
+ logError(`Circuit directory not found: ${circuitDir}`);
+ process.exit(1);
+ }
+
+ const circuitName = getCircuitName(circuitDir);
+ log(`\n📦 Preparing circuit: ${name} (${circuitName})`, colors.bright);
+ log(` Path: ${circuitDir}`);
+
+ // Compile Noir circuit
+ logStep(`${name}`, `Compiling Noir circuit (${circuitName})...`);
+ if (!run("nargo compile", { cwd: circuitDir })) {
+ process.exit(1);
+ }
+ logSuccess("Circuit compiled");
+
+ // Copy compiled circuit
+ const circuitSrc = join(circuitDir, `target/${circuitName}.json`);
+ const circuitDest = join(artifactsDir, "circuit.json");
+ if (!existsSync(circuitSrc)) {
+ logError(`Compiled circuit not found: ${circuitSrc}`);
+ process.exit(1);
+ }
+ copyFileSync(circuitSrc, circuitDest);
+ logSuccess(`Circuit artifact copied (${circuitName}.json -> circuit.json)`);
+
+ // Prepare prover/verifier artifacts
+ logStep(`${name}`, "Preparing prover/verifier artifacts...");
+ const cliPath = join(ROOT_DIR, "target/release-fast/provekit-cli");
+ const proverBinPath = join(artifactsDir, "prover.pkp");
+ const verifierBinPath = join(artifactsDir, "verifier.pkv");
+
+ if (
+ !run(
+ `${cliPath} prepare ${circuitDest} --pkp ${proverBinPath} --pkv ${verifierBinPath} --hash blake3`,
+ { cwd: artifactsDir }
+ )
+ ) {
+ process.exit(1);
+ }
+ logSuccess("prover.pkp and verifier.pkv created");
+
+
+ // Copy Prover.toml and convert to inputs.json
+ logStep(`${name}`, "Preparing inputs...");
+ const proverTomlSrc = join(circuitDir, "Prover.toml");
+ const proverTomlDest = join(artifactsDir, "Prover.toml");
+ copyFileSync(proverTomlSrc, proverTomlDest);
+ logSuccess("Prover.toml copied");
+
+ // Convert Prover.toml to inputs.json for browser demo
+ const tomlContent = readFileSync(proverTomlSrc, "utf-8");
+ const inputs = parseProverToml(tomlContent);
+ const inputsJsonPath = join(artifactsDir, "inputs.json");
+ writeFileSync(inputsJsonPath, JSON.stringify(inputs, null, 2));
+ logSuccess("inputs.json created");
+
+ // Save circuit metadata
+ const metadataPath = join(artifactsDir, "metadata.json");
+ writeFileSync(
+ metadataPath,
+ JSON.stringify({ name: circuitName, path: circuitDir }, null, 2)
+ );
+ logSuccess("metadata.json created");
+}
+
+async function main() {
+ await buildShared();
+
+ logStep("5/5", `Preparing ${CIRCUITS.length} circuits...`);
+ for (const circuit of CIRCUITS) {
+ await prepareCircuit(circuit);
+ }
+
+ log("\n\u2705 Setup complete!\n", colors.green + colors.bright);
+ log("Run the demo with:", colors.bright);
+ log(" node scripts/serve.mjs # Start browser demo server");
+ log(" # Open http://localhost:8080\n");
+}
+
+main().catch((err) => {
+ logError(err.message);
+ process.exit(1);
+});
diff --git a/playground/wasm-demo/src/demo-web.mjs b/playground/wasm-demo/src/demo-web.mjs
new file mode 100644
index 000000000..c329f626c
--- /dev/null
+++ b/playground/wasm-demo/src/demo-web.mjs
@@ -0,0 +1,566 @@
+/**
+ * ProveKit WASM Browser Demo
+ *
+ * Demonstrates zero-knowledge proof generation using ProveKit WASM bindings in the browser:
+ * 1. Load compiled Noir circuit
+ * 2. Generate witness using @noir-lang/noir_js (local web bundles)
+ * 3. Generate proof using ProveKit WASM
+ */
+
+// DOM elements
+const logContainer = document.getElementById("logContainer");
+const runBtn = document.getElementById("runBtn");
+const verifyBtn = document.getElementById("verifyBtn");
+
+function log(msg, type = "info") {
+ const line = document.createElement("div");
+ line.className = `log-line log-${type}`;
+ line.textContent = msg;
+ logContainer.appendChild(line);
+ logContainer.scrollTop = logContainer.scrollHeight;
+}
+
+function updateStep(step, status, statusClass = "") {
+ const el = document.getElementById(`step${step}-status`);
+ if (el) {
+ el.innerHTML = status;
+ el.className = `step-status ${statusClass}`;
+ }
+}
+
+function logMemory(label, extras = {}) {
+ let msg = `📊 ${label}`;
+
+ for (const [name, obj] of Object.entries(extras)) {
+ if (obj instanceof ArrayBuffer) {
+ msg += ` | ${name}: ${(obj.byteLength / 1024 / 1024).toFixed(2)} MB`;
+ } else if (obj instanceof Uint8Array) {
+ msg += ` | ${name}: ${(obj.byteLength / 1024 / 1024).toFixed(2)} MB`;
+ } else if (typeof obj === 'object' && obj !== null) {
+ const jsonSize = JSON.stringify(obj).length;
+ msg += ` | ${name}: ~${(jsonSize / 1024).toFixed(0)} KB`;
+ }
+ }
+
+ if (performance.memory) {
+ const used = (performance.memory.usedJSHeapSize / 1024 / 1024).toFixed(1);
+ msg += ` | heap: ${used} MB`;
+ }
+
+ log(msg, "info");
+}
+
+/**
+ * Convert a Noir witness map to the format expected by ProveKit WASM.
+ */
+function convertWitnessMap(witnessMap) {
+ const result = {};
+ if (witnessMap instanceof Map) {
+ for (const [index, value] of witnessMap.entries()) {
+ result[index] = value;
+ }
+ } else if (typeof witnessMap === "object" && witnessMap !== null) {
+ for (const [index, value] of Object.entries(witnessMap)) {
+ result[Number(index)] = value;
+ }
+ } else {
+ throw new Error(`Unexpected witness map type: ${typeof witnessMap}`);
+ }
+ return result;
+}
+
+function getArtifactBase() {
+ const circuit = window.activeCircuit || 'sha256';
+ return `artifacts/${circuit}/`;
+}
+
+async function loadInputs() {
+ const response = await fetch(getArtifactBase() + "inputs.json");
+ if (!response.ok) {
+ throw new Error("inputs.json not found. Run setup first.");
+ }
+ return response.json();
+}
+
+/**
+ * TOML parser for Noir Prover.toml files (browser-side).
+ * Handles inline tables { k = "v" }, arrays of inline tables,
+ * multi-line arrays, dotted keys (a.b.c), and [section] headers.
+ */
+
+/** Split string by delimiter, respecting quoted strings and nested {} [] */
+function splitTopLevel(str, delimiter) {
+ const parts = [];
+ let current = '';
+ let inStr = false;
+ let strCh = '';
+ let braces = 0;
+ let brackets = 0;
+ for (let i = 0; i < str.length; i++) {
+ const ch = str[i];
+ if (inStr) {
+ current += ch;
+ if (ch === strCh && str[i - 1] !== '\\') inStr = false;
+ continue;
+ }
+ if (ch === '"' || ch === "'") { inStr = true; strCh = ch; current += ch; continue; }
+ if (ch === '{') braces++;
+ if (ch === '}') braces--;
+ if (ch === '[') brackets++;
+ if (ch === ']') brackets--;
+ if (ch === delimiter && braces === 0 && brackets === 0) {
+ parts.push(current);
+ current = '';
+ continue;
+ }
+ current += ch;
+ }
+ if (current.trim()) parts.push(current);
+ return parts;
+}
+
+function parseInlineTable(str) {
+ const inner = str.slice(1, -1).trim();
+ if (!inner) return {};
+ const result = {};
+ for (const pair of splitTopLevel(inner, ',')) {
+ const t = pair.trim();
+ if (!t) continue;
+ const eq = t.indexOf('=');
+ if (eq === -1) continue;
+ result[t.slice(0, eq).trim()] = parseTomlValue(t.slice(eq + 1).trim());
+ }
+ return result;
+}
+
+function parseTomlArray(str) {
+ const inner = str.slice(1, -1).trim();
+ if (!inner) return [];
+ return splitTopLevel(inner, ',')
+ .map(el => el.trim())
+ .filter(el => el.length > 0)
+ .map(el => parseTomlValue(el));
+}
+
+function parseTomlValue(raw) {
+ if (raw.startsWith('{') && raw.endsWith('}')) return parseInlineTable(raw);
+ if (raw.startsWith('[') && raw.endsWith(']')) return parseTomlArray(raw);
+ if ((raw.startsWith('"') && raw.endsWith('"')) ||
+ (raw.startsWith("'") && raw.endsWith("'"))) return raw.slice(1, -1);
+ return raw; // keep as string — noir_js expects string numbers
+}
+
+/** Set a value at a nested dotted path, creating intermediate objects */
+function setNested(obj, path, val) {
+ let cur = obj;
+ for (let i = 0; i < path.length - 1; i++) {
+ if (!(path[i] in cur) || typeof cur[path[i]] !== 'object' ||
+ cur[path[i]] === null || Array.isArray(cur[path[i]])) {
+ cur[path[i]] = {};
+ }
+ cur = cur[path[i]];
+ }
+ cur[path[path.length - 1]] = val;
+}
+
+function parseSimpleToml(content) {
+ // Phase 1: Join multi-line arrays into single logical lines
+ const logicalLines = [];
+ const rawLines = content.split('\n');
+ let buffer = '';
+ let depth = 0;
+ for (const rawLine of rawLines) {
+ const stripped = rawLine.trim();
+ if (!stripped || stripped.startsWith('#')) continue;
+ if (depth > 0) {
+ buffer += ' ' + stripped;
+ for (const ch of stripped) {
+ if (ch === '[') depth++;
+ else if (ch === ']') depth--;
+ }
+ if (depth <= 0) { logicalLines.push(buffer); buffer = ''; depth = 0; }
+ continue;
+ }
+ const eqIdx = stripped.indexOf('=');
+ if (eqIdx !== -1) {
+ const valPart = stripped.slice(eqIdx + 1).trim();
+ let d = 0;
+ for (const ch of valPart) { if (ch === '[') d++; else if (ch === ']') d--; }
+ if (d > 0) { buffer = stripped; depth = d; continue; }
+ }
+ logicalLines.push(stripped);
+ }
+
+ // Phase 2: Parse logical lines into nested object
+ const result = {};
+ let section = null;
+ for (const line of logicalLines) {
+ const trimmed = line.trim();
+ if (!trimmed || trimmed.startsWith('#')) continue;
+ const secMatch = trimmed.match(/^\[([^\]]+)\]$/);
+ if (secMatch) { section = secMatch[1]; continue; }
+ const eqIdx = trimmed.indexOf('=');
+ if (eqIdx === -1) continue;
+ const key = trimmed.slice(0, eqIdx).trim();
+ const val = parseTomlValue(trimmed.slice(eqIdx + 1).trim());
+ const fullPath = section ? [section, ...key.split('.')] : key.split('.');
+ setNested(result, fullPath, val);
+ }
+ return result;
+}
+
+// Global state
+let provekit = null;
+let circuitJson = null;
+let proverBin = null;
+let verifierBin = null;
+let lastProofBytes = null;
+
+async function initWasm() {
+ try {
+ updateStep(1, '
Loading...', "running");
+ log("Loading ProveKit WASM module...");
+
+ const wasmModule = await import("../pkg/provekit_wasm.js");
+ const wasmBinary = await fetch("pkg/provekit_wasm_bg.wasm");
+ const wasmBytes = await wasmBinary.arrayBuffer();
+ await wasmModule.default(wasmBytes);
+
+ if (wasmModule.initPanicHook) {
+ wasmModule.initPanicHook();
+ }
+
+ const isIOS = /iPhone|iPad|iPod/.test(navigator.userAgent);
+ const isAndroid = /Android/.test(navigator.userAgent);
+ const isMobile = isIOS || isAndroid;
+ const maxThreads = navigator.hardwareConcurrency || 4;
+ const threadCountEl = document.getElementById("threadCount");
+ const hasSharedArrayBuffer = typeof SharedArrayBuffer !== 'undefined';
+
+ // iOS WebKit has unreliable WASM threading — don't even try
+ if (isIOS) {
+ log("📱 iOS detected - WebKit WASM threading is unreliable");
+ log("Running in single-threaded mode (optimized for iOS)");
+ if (threadCountEl) threadCountEl.textContent = 1;
+ } else if (isAndroid && hasSharedArrayBuffer) {
+ const androidThreads = Math.min(maxThreads, 4);
+ log(`📱 Android detected, trying ${androidThreads} threads...`);
+ try {
+ await wasmModule.initThreadPool(androidThreads);
+ log(`Thread pool ready (${androidThreads} workers)`);
+ if (threadCountEl) threadCountEl.textContent = androidThreads;
+ } catch (e) {
+ log(`Thread pool failed: ${e.message}`, "warn");
+ log("Falling back to single-threaded mode", "warn");
+ if (threadCountEl) threadCountEl.textContent = 1;
+ }
+ } else if (!isMobile && hasSharedArrayBuffer) {
+ try {
+ log(`Initializing thread pool with ${maxThreads} workers...`);
+ await wasmModule.initThreadPool(maxThreads);
+ log(`Thread pool ready (${maxThreads} workers)`);
+ if (threadCountEl) threadCountEl.textContent = maxThreads;
+ } catch (e) {
+ log(`Thread pool failed: ${e.message}`, "warn");
+ log("Falling back to single-threaded mode", "warn");
+ if (threadCountEl) threadCountEl.textContent = 1;
+ }
+ } else {
+ if (!isMobile) {
+ log("SharedArrayBuffer not available, running single-threaded", "warn");
+ } else {
+ log("Mobile: running in single-threaded mode");
+ }
+ if (threadCountEl) threadCountEl.textContent = 1;
+ }
+
+ provekit = wasmModule;
+ log("Initializing noir_js WASM modules...");
+
+ let attempts = 0;
+ while (!window.Noir && attempts < 50) {
+ await new Promise((r) => setTimeout(r, 100));
+ attempts++;
+ }
+
+ if (!window.Noir) {
+ throw new Error("Failed to load noir_js");
+ }
+
+ if (window.initNoir) {
+ await window.initNoir();
+ }
+
+ log("noir_js initialized");
+ updateStep(1, "Loaded", "success");
+ runBtn.disabled = false;
+ window.wasmReady = true;
+
+ } catch (error) {
+ log(`Error initializing WASM: ${error.message}`, "error");
+ console.error(error);
+ updateStep(1, "Failed", "error");
+ }
+}
+
+async function runDemo() {
+ runBtn.disabled = true;
+ const logLines = logContainer.querySelectorAll('.log-line, .log-error-block');
+ logLines.forEach(el => el.remove());
+
+ for (let i = 2; i <= 5; i++) {
+ updateStep(i, "Waiting...");
+ }
+ verifyBtn.disabled = true;
+ lastProofBytes = null;
+
+ let witnessTime = 0;
+ let proofTime = 0;
+ let witnessSize = 0;
+ let proofSize = 0;
+ let numConstraints = 0;
+ let numWitnesses = 0;
+ let inputs = {};
+ let prover = null;
+ try {
+ updateStep(2, '
Loading artifacts...', "running");
+ const isCustom = window.activeCircuit === 'custom';
+
+ // --- Load artifacts (mode-specific) ---
+ if (isCustom && window.customFiles) {
+ log("Loading uploaded prover and verifier artifacts...");
+ logMemory("Before loading artifacts");
+ proverBin = await window.customFiles.prover.arrayBuffer();
+ verifierBin = await window.customFiles.verifier.arrayBuffer();
+ } else {
+ const base = getArtifactBase();
+ let circuitName = "unknown";
+ try {
+ const metadataResponse = await fetch(base + "metadata.json");
+ if (metadataResponse.ok) {
+ const metadata = await metadataResponse.json();
+ circuitName = metadata.name || "unknown";
+ }
+ } catch (e) {
+ // metadata.json is optional
+ }
+ log(`Circuit: ${circuitName}`);
+ log("Loading prover (.pkp) and verifier (.pkv) artifacts...");
+ logMemory("Before loading artifacts");
+ const [proverResponse, verifierResponse] = await Promise.all([
+ fetch(base + "prover.pkp"),
+ fetch(base + "verifier.pkv"),
+ ]);
+ proverBin = await proverResponse.arrayBuffer();
+ verifierBin = await verifierResponse.arrayBuffer();
+ }
+
+ // --- Common: log sizes, create prover, extract circuit ---
+ log(`Prover artifact: ${(proverBin.byteLength / 1024 / 1024).toFixed(2)} MB`);
+ log(`Verifier artifact: ${(verifierBin.byteLength / 1024 / 1024).toFixed(2)} MB`);
+ logMemory("After loading artifacts", { proverBin, verifierBin });
+
+ log("Creating Prover instance...");
+ prover = new provekit.Prover(new Uint8Array(proverBin));
+ proverBin = null;
+
+ numConstraints = prover.getNumConstraints();
+ numWitnesses = prover.getNumWitnesses();
+ log(`Circuit: ${numConstraints.toLocaleString()} constraints, ${numWitnesses.toLocaleString()} witnesses`);
+
+ const circuitBytes = prover.getCircuit();
+ circuitJson = JSON.parse(new TextDecoder().decode(circuitBytes));
+ log("Circuit extracted from prover artifact");
+ logMemory("After creating Prover (freed proverBin)");
+ updateStep(2, "Loaded", "success");
+
+ // --- Load inputs (mode-specific) ---
+ updateStep(3, '
Generating witness...', "running");
+ if (isCustom && window.customFiles) {
+ const inputsFile = window.customFiles.inputs;
+ if (inputsFile.name.endsWith('.toml')) {
+ log("Parsing Prover.toml...");
+ const tomlText = await inputsFile.text();
+ inputs = parseSimpleToml(tomlText);
+ } else {
+ const inputsText = await inputsFile.text();
+ inputs = JSON.parse(inputsText);
+ }
+ } else {
+ log("Loading inputs...");
+ inputs = await loadInputs();
+ }
+ log(`Inputs loaded (${Object.keys(inputs).length} top-level keys)`);
+
+ // --- Generate witness ---
+ log("Generating witness using noir_js...");
+ logMemory("Before witness generation", { circuitJson, inputs });
+
+ await new Promise((r) => setTimeout(r, 50)); // Let UI update
+
+ const witnessStart = performance.now();
+ const noir = new window.Noir(circuitJson);
+ const { witness: compressedWitness } = await noir.execute(inputs);
+ const witnessStack = window.decompressWitness(compressedWitness);
+ const witnessMap = witnessStack[0].witness;
+ witnessTime = performance.now() - witnessStart;
+
+ const witnessObjSize = witnessMap instanceof Map
+ ? witnessMap.size * 64
+ : Object.keys(witnessMap).length * 64;
+ log(`\u{1F4CA} Witness object: ~${(witnessObjSize / 1024).toFixed(0)} KB estimated`);
+ logMemory("After witness generation");
+
+ witnessSize =
+ witnessMap instanceof Map
+ ? witnessMap.size
+ : Object.keys(witnessMap).length;
+ log(`Witness size: ${witnessSize} elements`);
+ log(`Witness generation time: ${witnessTime.toFixed(0)}ms`);
+
+ updateStep(3, `Done (${witnessTime.toFixed(0)}ms)`, "success");
+
+ // --- Generate proof ---
+ updateStep(4, '
Generating proof...', "running");
+ log("Converting witness format...");
+
+ const convertedWitness = convertWitnessMap(witnessMap);
+ log(`Converted ${Object.keys(convertedWitness).length} witness entries`);
+
+ log("Generating proof (this may take a while)...");
+ logMemory("Before proveBytes");
+
+ await new Promise((r) => setTimeout(r, 50)); // Let UI update
+
+ const proofStart = performance.now();
+ log("Starting proof computation...");
+ const proofBytes = prover.proveBytes(convertedWitness);
+ logMemory("After proveBytes");
+ proofTime = performance.now() - proofStart;
+
+ proofSize = proofBytes.length;
+ log(`Proof size: ${(proofSize / 1024).toFixed(1)} KB`);
+ log(`Proving time: ${(proofTime / 1000).toFixed(2)}s`);
+
+ try {
+ const proofJson = JSON.parse(new TextDecoder().decode(proofBytes));
+ const pi = proofJson.public_inputs;
+ if (pi && pi.length > 0) {
+ const values = pi.map(hex => {
+ const be = hex.match(/.{2}/g).slice().reverse().join('');
+ return BigInt('0x' + be);
+ });
+ const allBytes = values.every(v => v < 256n);
+ if (allBytes) {
+ const hexStr = values.map(v => v.toString(16).padStart(2, '0')).join('');
+ log(`Public inputs (${pi.length}): 0x${hexStr}`);
+ } else {
+ log(`Public inputs (${pi.length}):`);
+ for (let i = 0; i < values.length; i++) {
+ log(` [${i}]: 0x${values[i].toString(16)}`);
+ }
+ }
+ }
+ } catch (_) {
+ // non-critical — proof bytes may not be JSON in all modes
+ }
+
+ updateStep(4, `Done (${(proofTime / 1000).toFixed(2)}s)`, "success");
+
+ lastProofBytes = proofBytes;
+
+ const totalTimeMs = witnessTime + proofTime;
+ document.getElementById("totalTimeUi").textContent =
+ `${(totalTimeMs / 1000).toFixed(2)}s`;
+ document.getElementById("proofSizeUi").textContent =
+ `${(proofSize / 1024).toFixed(1)} KB`;
+ document.getElementById("constraintsUi").textContent =
+ numConstraints.toLocaleString();
+ document.getElementById("witnessesUi").textContent =
+ numWitnesses.toLocaleString();
+
+ const proofText = new TextDecoder().decode(proofBytes);
+ const truncated =
+ proofText.length > 2000
+ ? proofText.substring(0, 2000) + "..."
+ : proofText;
+ const proofOutputEl = document.getElementById("proofOutput");
+ if (proofOutputEl) proofOutputEl.textContent = truncated;
+ const proofCardEl = document.getElementById("proofCard");
+ if (proofCardEl) proofCardEl.style.display = "block";
+
+ updateStep(5, "Ready \u2014 click Verify Proof");
+ verifyBtn.disabled = false;
+ } catch (error) {
+ log(`Error: ${error.message}`, "error");
+ console.error(error);
+
+ for (let i = 2; i <= 4; i++) {
+ const el = document.getElementById(`step${i}-status`);
+ if (el && el.classList.contains("running")) {
+ updateStep(i, "Failed", "error");
+ break;
+ }
+ }
+ } finally {
+ runBtn.disabled = false;
+ }
+}
+
+async function verifyProof() {
+ if (!lastProofBytes || !verifierBin || !provekit) {
+ log("No proof available. Generate a proof first.", "error");
+ return;
+ }
+
+ verifyBtn.disabled = true;
+
+ try {
+ updateStep(5, '
Verifying...', "running");
+ log("Creating verifier instance...");
+ const verifier = new provekit.Verifier(new Uint8Array(verifierBin));
+ log("Verifying proof...");
+
+ await new Promise((r) => setTimeout(r, 50)); // Let UI update
+
+ const verifyStart = performance.now();
+ verifier.verifyBytes(lastProofBytes);
+ const verifyTime = performance.now() - verifyStart;
+ log(`Verification time: ${verifyTime.toFixed(0)}ms`);
+ log("Proof verified successfully!", "success");
+ updateStep(5, `Verified ✓ (${verifyTime.toFixed(0)}ms)`, "success");
+ } catch (error) {
+ log(`Verification error: ${error.message}`, "error");
+ console.error(error);
+ updateStep(5, "Failed", "error");
+ } finally {
+ verifyBtn.disabled = false;
+ }
+}
+
+function onCircuitChanged(circuit) {
+ circuitJson = null;
+ proverBin = null;
+ verifierBin = null;
+ lastProofBytes = null;
+
+ for (let i = 2; i <= 5; i++) {
+ updateStep(i, "Status: Waiting...");
+ }
+
+ document.getElementById("totalTimeUi").textContent = "-";
+ document.getElementById("proofSizeUi").textContent = "-";
+ document.getElementById("constraintsUi").textContent = "-";
+ document.getElementById("witnessesUi").textContent = "-";
+
+ if (provekit) runBtn.disabled = false;
+ verifyBtn.disabled = true;
+
+ log(`Switched to ${circuit.toUpperCase()} circuit`, "info");
+}
+
+initWasm();
+
+window.runDemo = runDemo;
+window.verifyProof = verifyProof;
+window.onCircuitChanged = onCircuitChanged;
diff --git a/provekit/common/Cargo.toml b/provekit/common/Cargo.toml
index ed5547761..5621d0c1e 100644
--- a/provekit/common/Cargo.toml
+++ b/provekit/common/Cargo.toml
@@ -9,7 +9,8 @@ homepage.workspace = true
repository.workspace = true
[features]
-default = []
+default = ["parallel"]
+parallel = []
[dependencies]
# Workspace crates
@@ -40,11 +41,14 @@ ruint.workspace = true
serde.workspace = true
serde_json.workspace = true
tracing.workspace = true
-xz2.workspace = true
zerocopy.workspace = true
+
+# Target-specific dependencies: only on non-WASM targets
+[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
+zstd.workspace = true
mavros-vm.workspace = true
mavros-artifacts.workspace = true
-zstd.workspace = true
+xz2.workspace = true
[lints]
workspace = true
diff --git a/provekit/common/src/file/binary_format.rs b/provekit/common/src/file/binary_format.rs
new file mode 100644
index 000000000..44ff55717
--- /dev/null
+++ b/provekit/common/src/file/binary_format.rs
@@ -0,0 +1,27 @@
+pub const MAGIC_BYTES: &[u8] = b"\xDC\xDFOZkp\x01\x00";
+
+/// Header layout: MAGIC(8) + FORMAT(8) + MAJOR(2) + MINOR(2) + HASH_CONFIG(1) =
+/// 21 bytes
+pub const HEADER_SIZE: usize = 21;
+
+/// Zstd magic number: `28 B5 2F FD`.
+pub const ZSTD_MAGIC: [u8; 4] = [0x28, 0xb5, 0x2f, 0xfd];
+
+/// XZ magic number: `FD 37 7A 58 5A 00`.
+pub const XZ_MAGIC: [u8; 6] = [0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00];
+
+// ---------------------------------------------------------------------------
+// Per-format identifiers and versions
+// ---------------------------------------------------------------------------
+
+pub const PROVER_FORMAT: [u8; 8] = *b"PrvKitPr";
+pub const PROVER_VERSION: (u16, u16) = (1, 2);
+
+pub const VERIFIER_FORMAT: [u8; 8] = *b"PrvKitVr";
+pub const VERIFIER_VERSION: (u16, u16) = (1, 3);
+
+pub const NOIR_PROOF_SCHEME_FORMAT: [u8; 8] = *b"NrProScm";
+pub const NOIR_PROOF_SCHEME_VERSION: (u16, u16) = (1, 2);
+
+pub const NOIR_PROOF_FORMAT: [u8; 8] = *b"NPSProof";
+pub const NOIR_PROOF_VERSION: (u16, u16) = (1, 1);
diff --git a/provekit/common/src/file/bin.rs b/provekit/common/src/file/io/bin.rs
similarity index 93%
rename from provekit/common/src/file/bin.rs
rename to provekit/common/src/file/io/bin.rs
index 09cb66350..cb5ff2f48 100644
--- a/provekit/common/src/file/bin.rs
+++ b/provekit/common/src/file/io/bin.rs
@@ -1,6 +1,10 @@
use {
super::BufExt as _,
- crate::{utils::human, HashConfig},
+ crate::{
+ binary_format::{HEADER_SIZE, MAGIC_BYTES, XZ_MAGIC, ZSTD_MAGIC},
+ utils::human,
+ HashConfig,
+ },
anyhow::{ensure, Context as _, Result},
bytes::{Buf, BufMut as _, Bytes, BytesMut},
serde::{Deserialize, Serialize},
@@ -12,20 +16,10 @@ use {
tracing::{info, instrument},
};
-/// Header layout: MAGIC(8) + FORMAT(8) + MAJOR(2) + MINOR(2) + HASH_CONFIG(1) =
-/// 21 bytes
-const HEADER_SIZE: usize = 21;
-const MAGIC_BYTES: &[u8] = b"\xDC\xDFOZkp\x01\x00";
/// Byte offset where hash config is stored: MAGIC(8) + FORMAT(8) + MAJOR(2) +
/// MINOR(2) = 20
const HASH_CONFIG_OFFSET: usize = 20;
-/// Zstd magic number: `28 B5 2F FD`.
-const ZSTD_MAGIC: [u8; 4] = [0x28, 0xb5, 0x2f, 0xfd];
-
-/// XZ magic number: `FD 37 7A 58 5A 00`.
-const XZ_MAGIC: [u8; 6] = [0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00];
-
/// Compression algorithm for binary file output.
#[derive(Debug, Clone, Copy)]
pub enum Compression {
diff --git a/provekit/common/src/file/buf_ext.rs b/provekit/common/src/file/io/buf_ext.rs
similarity index 100%
rename from provekit/common/src/file/buf_ext.rs
rename to provekit/common/src/file/io/buf_ext.rs
diff --git a/provekit/common/src/file/counting_writer.rs b/provekit/common/src/file/io/counting_writer.rs
similarity index 100%
rename from provekit/common/src/file/counting_writer.rs
rename to provekit/common/src/file/io/counting_writer.rs
diff --git a/provekit/common/src/file/json.rs b/provekit/common/src/file/io/json.rs
similarity index 100%
rename from provekit/common/src/file/json.rs
rename to provekit/common/src/file/io/json.rs
diff --git a/provekit/common/src/file/io/mod.rs b/provekit/common/src/file/io/mod.rs
new file mode 100644
index 000000000..4d04680a3
--- /dev/null
+++ b/provekit/common/src/file/io/mod.rs
@@ -0,0 +1,156 @@
+mod bin;
+mod buf_ext;
+mod counting_writer;
+mod json;
+
+use {
+ self::{
+ bin::{read_bin, read_hash_config as read_hash_config_bin, write_bin, Compression},
+ buf_ext::BufExt,
+ counting_writer::CountingWriter,
+ json::{read_json, write_json},
+ },
+ crate::{HashConfig, NoirProof, NoirProofScheme, Prover, Verifier},
+ anyhow::Result,
+ serde::{Deserialize, Serialize},
+ std::{ffi::OsStr, path::Path},
+ tracing::instrument,
+};
+
+/// Trait for structures that can be serialized to and deserialized from files.
+pub trait FileFormat: Serialize + for<'a> Deserialize<'a> {
+ const FORMAT: [u8; 8];
+ const EXTENSION: &'static str;
+ const VERSION: (u16, u16);
+ const COMPRESSION: Compression;
+}
+
+/// Helper trait to optionally extract hash config.
+pub(crate) trait MaybeHashAware {
+ fn maybe_hash_config(&self) -> Option
;
+}
+
+/// Impl for Prover (has hash config).
+impl MaybeHashAware for Prover {
+ fn maybe_hash_config(&self) -> Option {
+ match self {
+ Prover::Noir(p) => Some(p.hash_config),
+ Prover::Mavros(p) => Some(p.hash_config),
+ }
+ }
+}
+
+/// Impl for Verifier (has hash config).
+impl MaybeHashAware for Verifier {
+ fn maybe_hash_config(&self) -> Option {
+ Some(self.hash_config)
+ }
+}
+
+/// Impl for NoirProof (no hash config).
+impl MaybeHashAware for NoirProof {
+ fn maybe_hash_config(&self) -> Option {
+ None
+ }
+}
+
+/// Impl for NoirProofScheme (has hash config).
+impl MaybeHashAware for NoirProofScheme {
+ fn maybe_hash_config(&self) -> Option {
+ match self {
+ NoirProofScheme::Noir(d) => Some(d.hash_config),
+ NoirProofScheme::Mavros(d) => Some(d.hash_config),
+ }
+ }
+}
+
+impl FileFormat for NoirProofScheme {
+ const FORMAT: [u8; 8] = crate::binary_format::NOIR_PROOF_SCHEME_FORMAT;
+ const EXTENSION: &'static str = "nps";
+ const VERSION: (u16, u16) = crate::binary_format::NOIR_PROOF_SCHEME_VERSION;
+ const COMPRESSION: Compression = Compression::Zstd;
+}
+
+impl FileFormat for Prover {
+ const FORMAT: [u8; 8] = crate::binary_format::PROVER_FORMAT;
+ const EXTENSION: &'static str = "pkp";
+ const VERSION: (u16, u16) = crate::binary_format::PROVER_VERSION;
+ const COMPRESSION: Compression = Compression::Xz;
+}
+
+impl FileFormat for Verifier {
+ const FORMAT: [u8; 8] = crate::binary_format::VERIFIER_FORMAT;
+ const EXTENSION: &'static str = "pkv";
+ const VERSION: (u16, u16) = crate::binary_format::VERIFIER_VERSION;
+ const COMPRESSION: Compression = Compression::Zstd;
+}
+
+impl FileFormat for NoirProof {
+ const FORMAT: [u8; 8] = crate::binary_format::NOIR_PROOF_FORMAT;
+ const EXTENSION: &'static str = "np";
+ const VERSION: (u16, u16) = crate::binary_format::NOIR_PROOF_VERSION;
+ const COMPRESSION: Compression = Compression::Zstd;
+}
+
+/// Write a file with format determined from extension.
+#[allow(private_bounds)]
+#[instrument(skip(value))]
+pub fn write(value: &T, path: &Path) -> Result<()> {
+ match path.extension().and_then(OsStr::to_str) {
+ Some("json") => write_json(value, path),
+ Some(ext) if ext == T::EXTENSION => {
+ write_bin_with_hash_config(value, path, T::FORMAT, T::VERSION, T::COMPRESSION)
+ }
+ _ => Err(anyhow::anyhow!(
+ "Unsupported file extension, please specify .{} or .json",
+ T::EXTENSION
+ )),
+ }
+}
+
+/// Helper to write binary files with hash_config if T implements
+/// MaybeHashAware.
+fn write_bin_with_hash_config(
+ value: &T,
+ path: &Path,
+ format: [u8; 8],
+ version: (u16, u16),
+ compression: Compression,
+) -> Result<()> {
+ let hash_config = value.maybe_hash_config();
+ write_bin(value, path, format, version, compression, hash_config)
+}
+
+/// Read a file with format determined from extension.
+#[instrument()]
+pub fn read(path: &Path) -> Result {
+ match path.extension().and_then(OsStr::to_str) {
+ Some("json") => read_json(path),
+ Some(ext) if ext == T::EXTENSION => read_bin(path, T::FORMAT, T::VERSION),
+ _ => Err(anyhow::anyhow!(
+ "Unsupported file extension, please specify .{} or .json",
+ T::EXTENSION
+ )),
+ }
+}
+
+/// Read just the hash configuration from a file.
+#[instrument()]
+pub fn read_hash_config(path: &Path) -> Result {
+ match path.extension().and_then(OsStr::to_str) {
+ Some("json") => {
+ // For JSON, parse and extract hash_config field (required)
+ let json_str = std::fs::read_to_string(path)?;
+ let value: serde_json::Value = serde_json::from_str(&json_str)?;
+ value
+ .get("hash_config")
+ .and_then(|v| serde_json::from_value(v.clone()).ok())
+ .ok_or_else(|| anyhow::anyhow!("Missing hash_config field in JSON file"))
+ }
+ Some(ext) if ext == T::EXTENSION => read_hash_config_bin(path, T::FORMAT, T::VERSION),
+ _ => Err(anyhow::anyhow!(
+ "Unsupported file extension, please specify .{} or .json",
+ T::EXTENSION
+ )),
+ }
+}
diff --git a/provekit/common/src/file/mod.rs b/provekit/common/src/file/mod.rs
index 06f6f34f9..d8a733da0 100644
--- a/provekit/common/src/file/mod.rs
+++ b/provekit/common/src/file/mod.rs
@@ -1,156 +1,7 @@
-mod bin;
-mod buf_ext;
-mod counting_writer;
-mod json;
+pub mod binary_format;
-use {
- self::{
- bin::{read_bin, read_hash_config as read_hash_config_bin, write_bin, Compression},
- buf_ext::BufExt,
- counting_writer::CountingWriter,
- json::{read_json, write_json},
- },
- crate::{HashConfig, NoirProof, NoirProofScheme, Prover, Verifier},
- anyhow::Result,
- serde::{Deserialize, Serialize},
- std::{ffi::OsStr, path::Path},
- tracing::instrument,
-};
+#[cfg(not(target_arch = "wasm32"))]
+mod io;
-/// Trait for structures that can be serialized to and deserialized from files.
-pub trait FileFormat: Serialize + for<'a> Deserialize<'a> {
- const FORMAT: [u8; 8];
- const EXTENSION: &'static str;
- const VERSION: (u16, u16);
- const COMPRESSION: Compression;
-}
-
-/// Helper trait to optionally extract hash config.
-pub(crate) trait MaybeHashAware {
- fn maybe_hash_config(&self) -> Option;
-}
-
-/// Impl for Prover (has hash config).
-impl MaybeHashAware for Prover {
- fn maybe_hash_config(&self) -> Option {
- match self {
- Prover::Noir(p) => Some(p.hash_config),
- Prover::Mavros(p) => Some(p.hash_config),
- }
- }
-}
-
-/// Impl for Verifier (has hash config).
-impl MaybeHashAware for Verifier {
- fn maybe_hash_config(&self) -> Option {
- Some(self.hash_config)
- }
-}
-
-/// Impl for NoirProof (no hash config).
-impl MaybeHashAware for NoirProof {
- fn maybe_hash_config(&self) -> Option {
- None
- }
-}
-
-/// Impl for NoirProofScheme (has hash config).
-impl MaybeHashAware for NoirProofScheme {
- fn maybe_hash_config(&self) -> Option {
- match self {
- NoirProofScheme::Noir(d) => Some(d.hash_config),
- NoirProofScheme::Mavros(d) => Some(d.hash_config),
- }
- }
-}
-
-impl FileFormat for NoirProofScheme {
- const FORMAT: [u8; 8] = *b"NrProScm";
- const EXTENSION: &'static str = "nps";
- const VERSION: (u16, u16) = (1, 2);
- const COMPRESSION: Compression = Compression::Zstd;
-}
-
-impl FileFormat for Prover {
- const FORMAT: [u8; 8] = *b"PrvKitPr";
- const EXTENSION: &'static str = "pkp";
- const VERSION: (u16, u16) = (1, 2);
- const COMPRESSION: Compression = Compression::Xz;
-}
-
-impl FileFormat for Verifier {
- const FORMAT: [u8; 8] = *b"PrvKitVr";
- const EXTENSION: &'static str = "pkv";
- const VERSION: (u16, u16) = (1, 3);
- const COMPRESSION: Compression = Compression::Zstd;
-}
-
-impl FileFormat for NoirProof {
- const FORMAT: [u8; 8] = *b"NPSProof";
- const EXTENSION: &'static str = "np";
- const VERSION: (u16, u16) = (1, 1);
- const COMPRESSION: Compression = Compression::Zstd;
-}
-
-/// Write a file with format determined from extension.
-#[allow(private_bounds)]
-#[instrument(skip(value))]
-pub fn write(value: &T, path: &Path) -> Result<()> {
- match path.extension().and_then(OsStr::to_str) {
- Some("json") => write_json(value, path),
- Some(ext) if ext == T::EXTENSION => {
- write_bin_with_hash_config(value, path, T::FORMAT, T::VERSION, T::COMPRESSION)
- }
- _ => Err(anyhow::anyhow!(
- "Unsupported file extension, please specify .{} or .json",
- T::EXTENSION
- )),
- }
-}
-
-/// Helper to write binary files with hash_config if T implements
-/// MaybeHashAware.
-fn write_bin_with_hash_config(
- value: &T,
- path: &Path,
- format: [u8; 8],
- version: (u16, u16),
- compression: Compression,
-) -> Result<()> {
- let hash_config = value.maybe_hash_config();
- write_bin(value, path, format, version, compression, hash_config)
-}
-
-/// Read a file with format determined from extension.
-#[instrument()]
-pub fn read(path: &Path) -> Result {
- match path.extension().and_then(OsStr::to_str) {
- Some("json") => read_json(path),
- Some(ext) if ext == T::EXTENSION => read_bin(path, T::FORMAT, T::VERSION),
- _ => Err(anyhow::anyhow!(
- "Unsupported file extension, please specify .{} or .json",
- T::EXTENSION
- )),
- }
-}
-
-/// Read just the hash configuration from a file.
-#[instrument()]
-pub fn read_hash_config(path: &Path) -> Result {
- match path.extension().and_then(OsStr::to_str) {
- Some("json") => {
- // For JSON, parse and extract hash_config field (required)
- let json_str = std::fs::read_to_string(path)?;
- let value: serde_json::Value = serde_json::from_str(&json_str)?;
- value
- .get("hash_config")
- .and_then(|v| serde_json::from_value(v.clone()).ok())
- .ok_or_else(|| anyhow::anyhow!("Missing hash_config field in JSON file"))
- }
- Some(ext) if ext == T::EXTENSION => read_hash_config_bin(path, T::FORMAT, T::VERSION),
- _ => Err(anyhow::anyhow!(
- "Unsupported file extension, please specify .{} or .json",
- T::EXTENSION
- )),
- }
-}
+#[cfg(not(target_arch = "wasm32"))]
+pub use io::*;
diff --git a/provekit/common/src/lib.rs b/provekit/common/src/lib.rs
index 29ec40b5f..a2376382a 100644
--- a/provekit/common/src/lib.rs
+++ b/provekit/common/src/lib.rs
@@ -1,6 +1,8 @@
pub mod file;
+pub use file::binary_format;
pub mod hash_config;
mod interner;
+mod mavros;
mod noir_proof_scheme;
pub mod optimize;
pub mod prefix_covector;
@@ -22,9 +24,10 @@ pub use {
acir::FieldElement as NoirElement,
ark_bn254::Fr as FieldElement,
hash_config::HashConfig,
- noir_proof_scheme::{MavrosSchemeData, NoirProof, NoirProofScheme, NoirSchemeData},
+ mavros::{MavrosProver, MavrosSchemeData},
+ noir_proof_scheme::{NoirProof, NoirProofScheme, NoirSchemeData},
prefix_covector::{OffsetCovector, PrefixCovector},
- prover::{MavrosProver, NoirProver, Prover},
+ prover::{NoirProver, Prover},
r1cs::R1CS,
transcript_sponge::TranscriptSponge,
verifier::Verifier,
diff --git a/provekit/common/src/mavros.rs b/provekit/common/src/mavros.rs
new file mode 100644
index 000000000..64e023bec
--- /dev/null
+++ b/provekit/common/src/mavros.rs
@@ -0,0 +1,60 @@
+#[cfg(target_arch = "wasm32")]
+pub use self::wasm_stubs::{ConstraintsLayout, WitnessLayout};
+#[cfg(not(target_arch = "wasm32"))]
+pub use mavros_vm::{ConstraintsLayout, WitnessLayout};
+use {
+ crate::{whir_r1cs::WhirR1CSScheme, HashConfig, R1CS},
+ noirc_abi::Abi,
+ serde::{Deserialize, Serialize},
+};
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct MavrosProver {
+ #[serde(with = "crate::utils::serde_jsonify")]
+ pub abi: Abi,
+ pub num_public_inputs: usize,
+ pub whir_for_witness: WhirR1CSScheme,
+ pub witgen_binary: Vec,
+ pub ad_binary: Vec,
+ pub constraints_layout: ConstraintsLayout,
+ pub witness_layout: WitnessLayout,
+ pub hash_config: HashConfig,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct MavrosSchemeData {
+ #[serde(with = "crate::utils::serde_jsonify")]
+ pub abi: Abi,
+ pub num_public_inputs: usize,
+ pub r1cs: R1CS,
+ pub whir_for_witness: WhirR1CSScheme,
+ pub witgen_binary: Vec,
+ pub ad_binary: Vec,
+ pub constraints_layout: ConstraintsLayout,
+ pub witness_layout: WitnessLayout,
+ pub hash_config: HashConfig,
+}
+
+// Wire-compatible stubs for WASM targets where mavros_vm (C bindings) is
+// unavailable. Field names, types, and ordering MUST match the real mavros_vm
+// types exactly.
+#[cfg(target_arch = "wasm32")]
+mod wasm_stubs {
+ use serde::{Deserialize, Serialize};
+
+ #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
+ pub struct WitnessLayout {
+ pub algebraic_size: usize,
+ pub multiplicities_size: usize,
+ pub challenges_size: usize,
+ pub tables_data_size: usize,
+ pub lookups_data_size: usize,
+ }
+
+ #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
+ pub struct ConstraintsLayout {
+ pub algebraic_size: usize,
+ pub tables_data_size: usize,
+ pub lookups_data_size: usize,
+ }
+}
diff --git a/provekit/common/src/noir_proof_scheme.rs b/provekit/common/src/noir_proof_scheme.rs
index ee52e9599..a43377eae 100644
--- a/provekit/common/src/noir_proof_scheme.rs
+++ b/provekit/common/src/noir_proof_scheme.rs
@@ -2,11 +2,9 @@ use {
crate::{
whir_r1cs::{WhirR1CSProof, WhirR1CSScheme},
witness::{NoirWitnessGenerator, SplitWitnessBuilders},
- HashConfig, NoirElement, PublicInputs, R1CS,
+ HashConfig, MavrosSchemeData, NoirElement, PublicInputs, R1CS,
},
acir::circuit::Program,
- mavros_vm::{ConstraintsLayout, WitnessLayout},
- noirc_abi::Abi,
serde::{Deserialize, Serialize},
};
@@ -20,20 +18,9 @@ pub struct NoirSchemeData {
pub hash_config: HashConfig,
}
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct MavrosSchemeData {
- #[serde(with = "crate::utils::serde_jsonify")]
- pub abi: Abi,
- pub num_public_inputs: usize,
- pub r1cs: R1CS,
- pub whir_for_witness: WhirR1CSScheme,
- pub witgen_binary: Vec,
- pub ad_binary: Vec,
- pub constraints_layout: ConstraintsLayout,
- pub witness_layout: WitnessLayout,
- pub hash_config: HashConfig,
-}
-
+// INVARIANT: Variant order is wire-format-critical (postcard uses positional
+// discriminants). Do not reorder, cfg-gate, or insert variants without
+// verifying cross-target deserialization (native <-> WASM).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum NoirProofScheme {
Noir(NoirSchemeData),
diff --git a/provekit/common/src/prover.rs b/provekit/common/src/prover.rs
index 974850607..a8d09c9d2 100644
--- a/provekit/common/src/prover.rs
+++ b/provekit/common/src/prover.rs
@@ -3,11 +3,9 @@ use {
noir_proof_scheme::NoirProofScheme,
whir_r1cs::WhirR1CSScheme,
witness::{NoirWitnessGenerator, SplitWitnessBuilders},
- HashConfig, NoirElement, R1CS,
+ HashConfig, MavrosProver, NoirElement, R1CS,
},
acir::circuit::Program,
- mavros_vm::{ConstraintsLayout, WitnessLayout},
- noirc_abi::Abi,
serde::{Deserialize, Serialize},
};
@@ -21,19 +19,9 @@ pub struct NoirProver {
pub whir_for_witness: WhirR1CSScheme,
}
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct MavrosProver {
- #[serde(with = "crate::utils::serde_jsonify")]
- pub abi: Abi,
- pub num_public_inputs: usize,
- pub whir_for_witness: WhirR1CSScheme,
- pub witgen_binary: Vec,
- pub ad_binary: Vec,
- pub constraints_layout: ConstraintsLayout,
- pub witness_layout: WitnessLayout,
- pub hash_config: HashConfig,
-}
-
+// INVARIANT: Variant order is wire-format-critical (postcard uses positional
+// discriminants). Do not reorder, cfg-gate, or insert variants without
+// verifying cross-target deserialization (native <-> WASM).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Prover {
Noir(NoirProver),
@@ -41,6 +29,7 @@ pub enum Prover {
}
impl Prover {
+ /// Convert a compilation output into the on-disk prover format.
pub fn from_noir_proof_scheme(scheme: NoirProofScheme) -> Self {
match scheme {
NoirProofScheme::Noir(d) => Prover::Noir(NoirProver {
diff --git a/provekit/common/src/utils/mod.rs b/provekit/common/src/utils/mod.rs
index 9dbfafdf4..c73189d95 100644
--- a/provekit/common/src/utils/mod.rs
+++ b/provekit/common/src/utils/mod.rs
@@ -161,6 +161,7 @@ pub fn batch_inverse_montgomery(values: &[FieldElement]) -> Vec {
inverses
}
+#[cfg(not(target_arch = "wasm32"))]
pub fn convert_mavros_r1cs_to_provekit(mavros_r1cs: &mavros_artifacts::R1CS) -> crate::R1CS {
let num_witnesses = mavros_r1cs.witness_layout.size();
let num_constraints = mavros_r1cs.constraints.len();
diff --git a/provekit/common/src/utils/sumcheck.rs b/provekit/common/src/utils/sumcheck.rs
index f6f6ec543..207d76be4 100644
--- a/provekit/common/src/utils/sumcheck.rs
+++ b/provekit/common/src/utils/sumcheck.rs
@@ -5,7 +5,6 @@ use {
FieldElement, R1CS,
},
ark_std::{One, Zero},
- rayon::iter::{IndexedParallelIterator as _, IntoParallelRefIterator, ParallelIterator as _},
std::array,
tracing::instrument,
};
@@ -223,6 +222,7 @@ pub fn multiply_transposed_by_eq_alpha(
},
|| ct.hydrate(interner) * eq_alpha.as_slice(),
);
+
[a, b, c]
}
diff --git a/provekit/prover/Cargo.toml b/provekit/prover/Cargo.toml
index f8ab4e06e..cc6b3654f 100644
--- a/provekit/prover/Cargo.toml
+++ b/provekit/prover/Cargo.toml
@@ -9,7 +9,9 @@ homepage.workspace = true
repository.workspace = true
[features]
-default = []
+default = ["witness-generation", "parallel"]
+witness-generation = ["nargo", "bn254_blackbox_solver", "noir_artifact_cli"]
+parallel = ["provekit-common/parallel"]
[dependencies]
# Workspace crates
@@ -17,9 +19,6 @@ provekit-common.workspace = true
# Noir language
acir.workspace = true
-bn254_blackbox_solver.workspace = true
-nargo.workspace = true
-noir_artifact_cli.workspace = true
noirc_abi.workspace = true
# Cryptography and proof systems
@@ -31,6 +30,12 @@ whir.workspace = true
anyhow.workspace = true
postcard.workspace = true
tracing.workspace = true
+
+# Target-specific dependencies: only on non-WASM targets
+[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
+bn254_blackbox_solver = { workspace = true, optional = true }
+nargo = { workspace = true, optional = true }
+noir_artifact_cli = { workspace = true, optional = true }
mavros-vm.workspace = true
mavros-artifacts.workspace = true
diff --git a/provekit/prover/src/input_utils.rs b/provekit/prover/src/input_utils.rs
index 4e3d1833a..b8c2e7768 100644
--- a/provekit/prover/src/input_utils.rs
+++ b/provekit/prover/src/input_utils.rs
@@ -1,4 +1,5 @@
use {
+ anyhow::{Context, Result},
mavros_artifacts::InputValueOrdered,
noirc_abi::{
input_parser::{Format, InputValue},
@@ -18,11 +19,13 @@ pub fn read_prover_inputs(
.unwrap_or_default();
let Some(format) = Format::from_ext(ext) else {
- return Err(anyhow::anyhow!("Unsupported input extension: {}", ext));
+ anyhow::bail!("Unsupported input extension: {}", ext);
};
let inputs_src = fs::read_to_string(&file_path)?;
- let inputs: BTreeMap = format.parse(&inputs_src, abi).unwrap();
+ let inputs: BTreeMap = format
+ .parse(&inputs_src, abi)
+ .context("while parsing Prover.toml inputs")?;
Ok(inputs)
}
@@ -30,51 +33,58 @@ pub fn read_prover_inputs(
pub fn ordered_params_from_btreemap(
abi: &noirc_abi::Abi,
unordered_params: &BTreeMap,
-) -> Vec {
+) -> Result> {
let mut ordered_params = Vec::new();
for param in &abi.parameters {
let param_value = unordered_params
.get(¶m.name)
- .expect("Parameter not found in unordered params");
+ .ok_or_else(|| anyhow::anyhow!("Parameter '{}' not found in inputs", param.name))?;
- ordered_params.push(ordered_param(¶m.typ, param_value));
+ ordered_params.push(ordered_param(¶m.typ, param_value)?);
}
if let Some(return_type) = &abi.return_type {
if let Some(return_value) = unordered_params.get(MAIN_RETURN_NAME) {
- ordered_params.push(ordered_param(&return_type.abi_type, return_value));
+ ordered_params.push(ordered_param(&return_type.abi_type, return_value)?);
}
}
- ordered_params
+ Ok(ordered_params)
}
-fn ordered_param(abi_type: &AbiType, value: &InputValue) -> InputValueOrdered {
+fn ordered_param(abi_type: &AbiType, value: &InputValue) -> Result {
match (value, abi_type) {
- (InputValue::Field(elem), _) => InputValueOrdered::Field(elem.into_repr()),
+ (InputValue::Field(elem), _) => Ok(InputValueOrdered::Field(elem.into_repr())),
- (InputValue::Vec(vec_elements), AbiType::Array { typ, .. }) => InputValueOrdered::Vec(
- vec_elements
+ (InputValue::Vec(vec_elements), AbiType::Array { typ, .. }) => {
+ let items = vec_elements
.iter()
.map(|elem| ordered_param(typ, elem))
- .collect(),
- ),
- (InputValue::Struct(object), AbiType::Struct { fields, .. }) => InputValueOrdered::Struct(
- fields
+ .collect::>>()?;
+ Ok(InputValueOrdered::Vec(items))
+ }
+ (InputValue::Struct(object), AbiType::Struct { fields, .. }) => {
+ let items = fields
.iter()
.map(|(field_name, field_type)| {
- let field_value = object.get(field_name).expect("Field not found in struct");
- (field_name.clone(), ordered_param(field_type, field_value))
+ let field_value = object.get(field_name).ok_or_else(|| {
+ anyhow::anyhow!("Field '{}' not found in struct input", field_name)
+ })?;
+ Ok((field_name.clone(), ordered_param(field_type, field_value)?))
})
- .collect::>(),
- ),
- (InputValue::String(_string), _) => {
- panic!("Strings are not supported in ordered params");
+ .collect::>>()?;
+ Ok(InputValueOrdered::Struct(items))
}
-
- (InputValue::Vec(_vec_elements), AbiType::Tuple { fields: _fields }) => {
- panic!("Tuples are not supported in ordered params");
+ (InputValue::String(_), _) => {
+ anyhow::bail!("String inputs are not supported for ordered params")
+ }
+ (InputValue::Vec(_), AbiType::Tuple { .. }) => {
+ anyhow::bail!("Tuple inputs are not supported for ordered params")
}
- _ => unreachable!("value should have already been checked to match abi type"),
+ _ => anyhow::bail!(
+ "Input value does not match ABI type: {:?} vs {:?}",
+ value,
+ abi_type
+ ),
}
}
diff --git a/provekit/prover/src/lib.rs b/provekit/prover/src/lib.rs
index bbac4de0a..5f1f165de 100644
--- a/provekit/prover/src/lib.rs
+++ b/provekit/prover/src/lib.rs
@@ -7,32 +7,45 @@ use {
},
acir::native_types::WitnessMap,
anyhow::{Context, Result},
- bn254_blackbox_solver::Bn254BlackBoxSolver,
- mavros_vm::interpreter as mavros_interpreter,
- nargo::foreign_calls::DefaultForeignCallBuilder,
- noir_artifact_cli::fs::inputs::read_inputs_from_file,
- noirc_abi::InputMap,
provekit_common::{
- FieldElement, MavrosProver, NoirElement, NoirProof, NoirProver, Prover, PublicInputs,
- TranscriptSponge,
+ FieldElement, NoirElement, NoirProof, NoirProver, Prover, PublicInputs, TranscriptSponge,
},
- std::{mem::size_of, path::Path},
+ std::mem::size_of,
tracing::{debug, info_span, instrument},
- whir::transcript::{codecs::Empty, ProverState, VerifierMessage},
+ whir::transcript::{codecs::Empty, ProverState},
+};
+#[cfg(all(feature = "witness-generation", not(target_arch = "wasm32")))]
+use {
+ bn254_blackbox_solver::Bn254BlackBoxSolver, nargo::foreign_calls::DefaultForeignCallBuilder,
+ noir_artifact_cli::fs::inputs::read_inputs_from_file, noirc_abi::InputMap,
+};
+#[cfg(not(target_arch = "wasm32"))]
+use {
+ mavros_vm::interpreter as mavros_interpreter, provekit_common::MavrosProver, std::path::Path,
+ whir::transcript::VerifierMessage,
};
+#[cfg(not(target_arch = "wasm32"))]
pub mod input_utils;
mod r1cs;
mod whir_r1cs;
mod witness;
+/// `prove` and `prove_with_toml` are native-only (cfg-gated out on wasm32).
+/// `prove_with_witness` is available on all targets. `MavrosProver` does not
+/// support `prove_with_witness` (errors at runtime).
pub trait Prove {
+ #[cfg(all(feature = "witness-generation", not(target_arch = "wasm32")))]
fn prove(self, input_map: InputMap) -> Result;
+ #[cfg(all(feature = "witness-generation", not(target_arch = "wasm32")))]
fn prove_with_toml(self, prover_toml: impl AsRef) -> Result;
+
+ fn prove_with_witness(self, witness: WitnessMap) -> Result;
}
#[instrument(skip_all)]
+#[cfg(all(feature = "witness-generation", not(target_arch = "wasm32")))]
fn generate_noir_witness(
prover: &mut NoirProver,
input_map: InputMap,
@@ -64,11 +77,28 @@ fn generate_noir_witness(
}
impl Prove for NoirProver {
+ #[cfg(all(feature = "witness-generation", not(target_arch = "wasm32")))]
#[instrument(skip_all)]
fn prove(mut self, input_map: InputMap) -> Result {
+ let witness = generate_noir_witness(&mut self, input_map)?;
+ self.prove_with_witness(witness)
+ }
+
+ #[cfg(all(feature = "witness-generation", not(target_arch = "wasm32")))]
+ #[instrument(skip_all)]
+ fn prove_with_toml(self, prover_toml: impl AsRef) -> Result {
+ let (input_map, _return_value) =
+ read_inputs_from_file(prover_toml.as_ref(), self.witness_generator.abi())?;
+ self.prove(input_map)
+ }
+
+ #[instrument(skip_all)]
+ fn prove_with_witness(
+ self,
+ acir_witness_idx_to_value_map: WitnessMap,
+ ) -> Result {
provekit_common::register_ntt();
- let acir_witness_idx_to_value_map = generate_noir_witness(&mut self, input_map)?;
let num_public_inputs = self.program.functions[0].public_inputs().indices().len();
drop(self.program);
drop(self.witness_generator);
@@ -205,21 +235,15 @@ impl Prove for NoirProver {
whir_r1cs_proof,
})
}
-
- #[instrument(skip_all)]
- fn prove_with_toml(self, prover_toml: impl AsRef) -> Result {
- let (input_map, _return_value) =
- read_inputs_from_file(prover_toml.as_ref(), self.witness_generator.abi())?;
- self.prove(input_map)
- }
}
+#[cfg(not(target_arch = "wasm32"))]
impl Prove for MavrosProver {
- #[instrument(skip_all)]
+ #[cfg(feature = "witness-generation")]
fn prove(mut self, input_map: InputMap) -> Result {
provekit_common::register_ntt();
- let params = crate::input_utils::ordered_params_from_btreemap(&self.abi, &input_map);
+ let params = crate::input_utils::ordered_params_from_btreemap(&self.abi, &input_map)?;
let phase1 = mavros_interpreter::run_phase1(
&mut self.witgen_binary,
self.witness_layout,
@@ -304,6 +328,7 @@ impl Prove for MavrosProver {
})
}
+ #[cfg(feature = "witness-generation")]
#[instrument(skip_all)]
fn prove_with_toml(self, prover_toml: impl AsRef) -> Result {
let project_path = prover_toml
@@ -315,22 +340,40 @@ impl Prove for MavrosProver {
crate::input_utils::read_prover_inputs(&project_path.to_path_buf(), &self.abi)?;
self.prove(input_map)
}
+
+ fn prove_with_witness(self, _witness: WitnessMap) -> Result {
+ Err(anyhow::anyhow!(
+ "prove_with_witness is not supported for Mavros prover"
+ ))
+ }
}
impl Prove for Prover {
+ #[cfg(all(feature = "witness-generation", not(target_arch = "wasm32")))]
fn prove(self, input_map: InputMap) -> Result {
match self {
Prover::Noir(p) => p.prove(input_map),
Prover::Mavros(p) => p.prove(input_map),
}
}
+
+ #[cfg(all(feature = "witness-generation", not(target_arch = "wasm32")))]
fn prove_with_toml(self, prover_toml: impl AsRef) -> Result {
match self {
Prover::Noir(p) => p.prove_with_toml(prover_toml),
Prover::Mavros(p) => p.prove_with_toml(prover_toml),
}
}
-}
-#[cfg(test)]
-mod tests {}
+ fn prove_with_witness(self, witness: WitnessMap) -> Result {
+ match self {
+ Prover::Noir(p) => p.prove_with_witness(witness),
+ #[cfg(not(target_arch = "wasm32"))]
+ Prover::Mavros(p) => p.prove_with_witness(witness),
+ #[cfg(target_arch = "wasm32")]
+ Prover::Mavros(_) => {
+ anyhow::bail!("Mavros prover is not supported on WASM")
+ }
+ }
+ }
+}
diff --git a/provekit/prover/src/whir_r1cs.rs b/provekit/prover/src/whir_r1cs.rs
index 08920f3bd..6532a772a 100644
--- a/provekit/prover/src/whir_r1cs.rs
+++ b/provekit/prover/src/whir_r1cs.rs
@@ -2,8 +2,6 @@ use {
anyhow::{ensure, Result},
ark_ff::UniformRand,
ark_std::{One, Zero},
- mavros_artifacts::{ConstraintsLayout, WitnessLayout},
- mavros_vm::interpreter::Phase1Result,
provekit_common::{
prefix_covector::{
build_prefix_covectors, compute_alpha_evals, compute_public_eval, expand_powers,
@@ -29,6 +27,11 @@ use {
transcript::{ProverState, VerifierMessage},
},
};
+#[cfg(not(target_arch = "wasm32"))]
+use {
+ mavros_artifacts::{ConstraintsLayout, WitnessLayout},
+ mavros_vm::interpreter::Phase1Result,
+};
pub struct BlindingState {
pub polynomial: Vec<[FieldElement; 4]>,
@@ -60,6 +63,7 @@ pub trait WhirR1CSProver {
public_inputs: &PublicInputs,
) -> Result;
+ #[cfg(not(target_arch = "wasm32"))]
fn prove_mavros(
&self,
merlin: ProverState,
@@ -181,6 +185,7 @@ impl WhirR1CSProver for WhirR1CSScheme {
)
}
+ #[cfg(not(target_arch = "wasm32"))]
#[instrument(skip_all)]
fn prove_mavros(
&self,
@@ -199,10 +204,11 @@ impl WhirR1CSProver for WhirR1CSScheme {
.as_ref()
.expect("c1 must carry blinding state");
+ let [a, b, c] = [phase1.out_a, phase1.out_b, phase1.out_c];
let (alpha, blinding_eval) = run_zk_sumcheck_prover(
- phase1.out_a,
- phase1.out_b,
- phase1.out_c,
+ a,
+ b,
+ c,
&mut merlin,
self.m_0,
&blinding.polynomial,
diff --git a/provekit/r1cs-compiler/Cargo.toml b/provekit/r1cs-compiler/Cargo.toml
index 8752fd2cf..05630a4dd 100644
--- a/provekit/r1cs-compiler/Cargo.toml
+++ b/provekit/r1cs-compiler/Cargo.toml
@@ -8,9 +8,6 @@ license.workspace = true
homepage.workspace = true
repository.workspace = true
-[features]
-default = []
-
[dependencies]
# Workspace crates
provekit-common.workspace = true
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
index 343270f65..d895a33cf 100644
--- a/rust-toolchain.toml
+++ b/rust-toolchain.toml
@@ -1,3 +1,4 @@
[toolchain]
channel = "nightly-2026-03-04"
-components = ["rustfmt", "rust-analyzer", "clippy"]
+components = ["rustfmt", "rust-analyzer", "clippy", "rust-src"]
+targets = ["wasm32-unknown-unknown"]
diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml
index 4e88e0918..59369b84d 100644
--- a/tooling/cli/Cargo.toml
+++ b/tooling/cli/Cargo.toml
@@ -12,7 +12,7 @@ repository.workspace = true
# Workspace crates
provekit-common.workspace = true
provekit-gnark.workspace = true
-provekit-prover.workspace = true
+provekit-prover = { workspace = true, features = ["witness-generation", "parallel"] }
provekit-r1cs-compiler.workspace = true
provekit-verifier.workspace = true
diff --git a/tooling/cli/src/cmd/prepare.rs b/tooling/cli/src/cmd/prepare.rs
index a491e69bc..814371eb3 100644
--- a/tooling/cli/src/cmd/prepare.rs
+++ b/tooling/cli/src/cmd/prepare.rs
@@ -84,13 +84,11 @@ impl Command for Args {
}
};
- write(
- &Prover::from_noir_proof_scheme(scheme.clone()),
- &self.pkp_path,
- )
- .context("while writing Provekit Prover")?;
- write(&Verifier::from_noir_proof_scheme(scheme), &self.pkv_path)
- .context("while writing Provekit Verifier")?;
+ let prover = Prover::from_noir_proof_scheme(scheme.clone());
+ let verifier = Verifier::from_noir_proof_scheme(scheme);
+
+ write(&prover, &self.pkp_path).context("while writing Provekit Prover")?;
+ write(&verifier, &self.pkv_path).context("while writing Provekit Verifier")?;
Ok(())
}
}
diff --git a/tooling/provekit-bench/Cargo.toml b/tooling/provekit-bench/Cargo.toml
index 52b52b57d..043abb280 100644
--- a/tooling/provekit-bench/Cargo.toml
+++ b/tooling/provekit-bench/Cargo.toml
@@ -11,7 +11,7 @@ repository.workspace = true
[dependencies]
# Workspace crates
provekit-common.workspace = true
-provekit-prover.workspace = true
+provekit-prover = { workspace = true, features = ["witness-generation", "parallel"] }
provekit-r1cs-compiler.workspace = true
provekit-verifier.workspace = true
@@ -35,5 +35,3 @@ workspace = true
name = "bench"
harness = false
-[features]
-default = []
\ No newline at end of file
diff --git a/tooling/provekit-ffi/Cargo.toml b/tooling/provekit-ffi/Cargo.toml
index e699ea6a9..e0d1fe318 100644
--- a/tooling/provekit-ffi/Cargo.toml
+++ b/tooling/provekit-ffi/Cargo.toml
@@ -14,7 +14,7 @@ crate-type = ["staticlib"]
[dependencies]
# Workspace crates
provekit-common.workspace = true
-provekit-prover.workspace = true
+provekit-prover = { workspace = true, features = ["witness-generation", "parallel"] }
# 3rd party
anyhow.workspace = true
diff --git a/tooling/provekit-wasm/Cargo.toml b/tooling/provekit-wasm/Cargo.toml
new file mode 100644
index 000000000..44c7b271d
--- /dev/null
+++ b/tooling/provekit-wasm/Cargo.toml
@@ -0,0 +1,55 @@
+# WASM build:
+# cargo build --release --target wasm32-unknown-unknown -p provekit-wasm -Z build-std=panic_abort,std
+# wasm-bindgen --target web --out-dir tooling/provekit-wasm/pkg target/wasm32-unknown-unknown/release/provekit_wasm.wasm
+# wasm-opt -O3 --enable-simd --enable-threads --enable-bulk-memory --enable-mutable-globals --enable-nontrapping-float-to-int --enable-sign-ext --fast-math --low-memory-unused -o pkg/provekit_wasm_bg.wasm pkg/provekit_wasm_bg.wasm
+#
+# RUSTFLAGS and linker args are in .cargo/config.toml under [target.wasm32-unknown-unknown].
+# The browser must serve with Cross-Origin-Opener-Policy: same-origin and
+# Cross-Origin-Embedder-Policy: require-corp headers for SharedArrayBuffer.
+
+[package]
+name = "provekit-wasm"
+version = "0.1.0"
+edition.workspace = true
+rust-version.workspace = true
+authors.workspace = true
+license.workspace = true
+homepage.workspace = true
+repository.workspace = true
+
+[lib]
+crate-type = ["cdylib", "rlib"]
+
+[dependencies]
+# Workspace crates - enable parallel features with wasm-bindgen-rayon
+provekit-common.workspace = true
+provekit-prover = { workspace = true, default-features = false, features = ["parallel"] }
+provekit-verifier.workspace = true
+
+# Noir language
+acir.workspace = true
+
+
+# 3rd party
+anyhow.workspace = true
+base64.workspace = true
+console_error_panic_hook.workspace = true
+getrandom.workspace = true # Enable js feature for WASM via feature unification (v0.2)
+getrandom03.workspace = true # Enable wasm_js feature for WASM via feature unification (v0.3)
+hex.workspace = true
+js-sys.workspace = true
+lzma-rs.workspace = true
+postcard.workspace = true
+ruzstd.workspace = true # Pure-Rust Zstd decompressor (native crate zstd uses C bindings, incompatible with WASM)
+serde_json.workspace = true
+serde-wasm-bindgen.workspace = true
+wasm-bindgen.workspace = true
+
+# WASM parallelism via Web Workers
+wasm-bindgen-rayon.workspace = true
+
+[package.metadata.cargo-machete]
+ignored = ["getrandom", "getrandom03"]
+
+[lints]
+workspace = true
diff --git a/tooling/provekit-wasm/src/format.rs b/tooling/provekit-wasm/src/format.rs
new file mode 100644
index 000000000..b96b36595
--- /dev/null
+++ b/tooling/provekit-wasm/src/format.rs
@@ -0,0 +1,210 @@
+use {
+ provekit_common::{
+ binary_format::{
+ HEADER_SIZE, MAGIC_BYTES, PROVER_FORMAT, PROVER_VERSION, VERIFIER_FORMAT,
+ VERIFIER_VERSION, XZ_MAGIC, ZSTD_MAGIC,
+ },
+ Prover, Verifier,
+ },
+ wasm_bindgen::prelude::*,
+};
+
+pub(crate) fn parse_binary_header<'a>(
+ data: &'a [u8],
+ expected_format: &[u8; 8],
+ (expected_major, min_minor): (u16, u16),
+ label: &str,
+) -> Result<&'a [u8], JsError> {
+ parse_binary_header_impl(data, expected_format, (expected_major, min_minor), label)
+ .map_err(|msg| JsError::new(&msg))
+}
+
+fn parse_binary_header_impl<'a>(
+ data: &'a [u8],
+ expected_format: &[u8; 8],
+ (expected_major, min_minor): (u16, u16),
+ label: &str,
+) -> Result<&'a [u8], String> {
+ if data.len() < HEADER_SIZE {
+ return Err(format!("{label} data too short for binary format"));
+ }
+ if &data[..8] != MAGIC_BYTES {
+ return Err(format!("Invalid magic bytes in {label} data"));
+ }
+ if &data[8..16] != expected_format {
+ return Err(format!("Invalid format identifier in {label} data"));
+ }
+
+ let major = u16::from_le_bytes([data[16], data[17]]);
+ let minor = u16::from_le_bytes([data[18], data[19]]);
+ if major != expected_major {
+ return Err(format!(
+ "Incompatible {label} format: major version {major}, expected {expected_major}"
+ ));
+ }
+ if minor < min_minor {
+ return Err(format!(
+ "Incompatible {label} format: minor version {minor}, expected >= {min_minor}"
+ ));
+ }
+
+ Ok(&data[HEADER_SIZE..])
+}
+
+pub(crate) fn decompress(data: &[u8]) -> Result, JsError> {
+ decompress_impl(data).map_err(|msg| JsError::new(&msg))
+}
+
+fn decompress_impl(data: &[u8]) -> Result, String> {
+ if data.len() >= 4 && data[..4] == ZSTD_MAGIC {
+ let mut decoder = ruzstd::decoding::StreamingDecoder::new(std::io::Cursor::new(data))
+ .map_err(|e| format!("Failed to init Zstd decoder: {e}"))?;
+ let hint = usize::try_from(decoder.decoder.content_size()).unwrap_or(0);
+ let mut out = Vec::with_capacity(hint);
+ std::io::Read::read_to_end(&mut decoder, &mut out)
+ .map_err(|e| format!("Failed to decompress Zstd data: {e}"))?;
+ Ok(out)
+ } else if data.len() >= 6 && data[..6] == XZ_MAGIC {
+ let mut out = Vec::new();
+ lzma_rs::xz_decompress(&mut std::io::Cursor::new(data), &mut out)
+ .map_err(|e| format!("Failed to decompress XZ data: {e}"))?;
+ Ok(out)
+ } else {
+ Err(format!(
+ "Unknown compression format (first bytes: {:02X?})",
+ &data[..data.len().min(6)]
+ ))
+ }
+}
+
+/// Parses a binary prover artifact (.pkp format).
+pub fn parse_binary_prover(data: &[u8]) -> Result {
+ let payload = parse_binary_header(data, &PROVER_FORMAT, PROVER_VERSION, "prover")?;
+ let decompressed = decompress(payload)?;
+ postcard::from_bytes(&decompressed)
+ .map_err(|err| JsError::new(&format!("Failed to deserialize prover data: {err}")))
+}
+
+/// Parses a binary verifier artifact (.pkv format).
+pub fn parse_binary_verifier(data: &[u8]) -> Result {
+ let payload = parse_binary_header(data, &VERIFIER_FORMAT, VERIFIER_VERSION, "verifier")?;
+ let decompressed = decompress(payload)?;
+ postcard::from_bytes(&decompressed)
+ .map_err(|err| JsError::new(&format!("Failed to deserialize verifier data: {err}")))
+}
+
+#[cfg(test)]
+mod tests {
+ use {
+ super::*,
+ ruzstd::encoding::{compress_to_vec, CompressionLevel},
+ };
+
+ fn build_header(
+ format: [u8; 8],
+ version: (u16, u16),
+ hash_config: u8,
+ payload: &[u8],
+ ) -> Vec {
+ let mut data = Vec::with_capacity(HEADER_SIZE + payload.len());
+ data.extend_from_slice(MAGIC_BYTES);
+ data.extend_from_slice(&format);
+ data.extend_from_slice(&version.0.to_le_bytes());
+ data.extend_from_slice(&version.1.to_le_bytes());
+ data.push(hash_config);
+ data.extend_from_slice(payload);
+ data
+ }
+
+ #[test]
+ fn parse_binary_header_accepts_valid_header() {
+ let payload = b"payload-bytes";
+ let data = build_header(PROVER_FORMAT, PROVER_VERSION, 0xff, payload);
+
+ let parsed = parse_binary_header(&data, &PROVER_FORMAT, PROVER_VERSION, "prover").unwrap();
+
+ assert_eq!(parsed, payload);
+ }
+
+ #[test]
+ fn parse_binary_header_rejects_magic_mismatch() {
+ let mut data = build_header(PROVER_FORMAT, PROVER_VERSION, 0xff, b"x");
+ data[0] ^= 0x01;
+
+ let err =
+ parse_binary_header_impl(&data, &PROVER_FORMAT, PROVER_VERSION, "prover").unwrap_err();
+ assert!(err.contains("Invalid magic bytes in prover data"));
+ }
+
+ #[test]
+ fn parse_binary_header_rejects_format_mismatch() {
+ let data = build_header(VERIFIER_FORMAT, PROVER_VERSION, 0xff, b"x");
+
+ let err =
+ parse_binary_header_impl(&data, &PROVER_FORMAT, PROVER_VERSION, "prover").unwrap_err();
+ assert!(err.contains("Invalid format identifier in prover data"));
+ }
+
+ #[test]
+ fn parse_binary_header_rejects_major_version_mismatch() {
+ let bad_major = (PROVER_VERSION.0 + 1, PROVER_VERSION.1);
+ let data = build_header(PROVER_FORMAT, bad_major, 0xff, b"x");
+
+ let err =
+ parse_binary_header_impl(&data, &PROVER_FORMAT, PROVER_VERSION, "prover").unwrap_err();
+ assert!(err.contains("Incompatible prover format: major version"));
+ }
+
+ #[test]
+ fn parse_binary_header_rejects_minor_version_too_low() {
+ let min_minor = PROVER_VERSION.1 + 1;
+ let data = build_header(PROVER_FORMAT, PROVER_VERSION, 0xff, b"x");
+
+ let err = parse_binary_header_impl(
+ &data,
+ &PROVER_FORMAT,
+ (PROVER_VERSION.0, min_minor),
+ "prover",
+ )
+ .unwrap_err();
+ assert!(err.contains("Incompatible prover format: minor version"));
+ }
+
+ #[test]
+ fn parse_binary_header_rejects_data_too_short() {
+ let too_short = vec![0_u8; HEADER_SIZE - 1];
+
+ let err = parse_binary_header_impl(&too_short, &PROVER_FORMAT, PROVER_VERSION, "prover")
+ .unwrap_err();
+ assert!(err.contains("prover data too short for binary format"));
+ }
+
+ #[test]
+ fn decompress_rejects_unknown_magic() {
+ let err = decompress_impl(b"\x01\x02\x03\x04").unwrap_err();
+ assert!(err.contains("Unknown compression format"));
+ }
+
+ #[test]
+ fn decompress_roundtrips_zstd_data() {
+ let payload = b"provekit-zstd-roundtrip";
+ let compressed = compress_to_vec(payload.as_slice(), CompressionLevel::Fastest);
+
+ assert_eq!(&compressed[..4], ZSTD_MAGIC.as_slice());
+
+ let decompressed = decompress_impl(&compressed).unwrap();
+ assert_eq!(decompressed, payload);
+ }
+
+ #[test]
+ fn decompress_roundtrips_xz_data() {
+ let payload = b"provekit-xz-roundtrip";
+ let mut compressed = Vec::new();
+ lzma_rs::xz_compress(&mut std::io::Cursor::new(payload), &mut compressed).unwrap();
+
+ assert_eq!(&compressed[..6], XZ_MAGIC.as_slice());
+
+ let decompressed = decompress_impl(&compressed).unwrap();
+ assert_eq!(decompressed, payload);
+ }
+}
diff --git a/tooling/provekit-wasm/src/lib.rs b/tooling/provekit-wasm/src/lib.rs
new file mode 100644
index 000000000..884d02632
--- /dev/null
+++ b/tooling/provekit-wasm/src/lib.rs
@@ -0,0 +1,32 @@
+//! WebAssembly bindings for ProveKit.
+//!
+//! # Example
+//!
+//! ```javascript
+//! import { initPanicHook, initThreadPool, Prover } from "./pkg/provekit_wasm.js";
+//!
+//! // Initialize panic hook and thread pool
+//! initPanicHook();
+//! await initThreadPool(navigator.hardwareConcurrency);
+//!
+//! // Load prover artifact (.pkp file — same as native CLI uses)
+//! const proverBin = new Uint8Array(await (await fetch("/prover.pkp")).arrayBuffer());
+//! const prover = new Prover(proverBin);
+//!
+//! // Extract circuit for noir_js witness generation
+//! const circuitJson = JSON.parse(new TextDecoder().decode(prover.getCircuit()));
+//! const noir = new Noir(circuitJson);
+//! const { witness } = await noir.execute(inputs);
+//! const proof = prover.proveBytes(decompressWitness(witness)[0].witness);
+//! ```
+
+mod format;
+mod prover;
+mod verifier;
+
+pub use wasm_bindgen_rayon::init_thread_pool;
+
+#[wasm_bindgen::prelude::wasm_bindgen(js_name = initPanicHook)]
+pub fn init_panic_hook() {
+ console_error_panic_hook::set_once();
+}
diff --git a/tooling/provekit-wasm/src/prover.rs b/tooling/provekit-wasm/src/prover.rs
new file mode 100644
index 000000000..e14d70a52
--- /dev/null
+++ b/tooling/provekit-wasm/src/prover.rs
@@ -0,0 +1,285 @@
+use {
+ crate::format::parse_binary_prover,
+ acir::{
+ circuit::Program,
+ native_types::{Witness, WitnessMap},
+ AcirField, FieldElement,
+ },
+ anyhow::Context,
+ base64::{engine::general_purpose::STANDARD as BASE64, Engine as _},
+ provekit_common::{
+ binary_format::{HEADER_SIZE, MAGIC_BYTES},
+ NoirElement, NoirProof, Prover as ProverCore,
+ },
+ provekit_prover::Prove,
+ std::{cell::RefCell, collections::BTreeMap},
+ wasm_bindgen::prelude::*,
+};
+
+/// WASM bindings for proof generation. Consumed after `proveBytes`/`proveJs`.
+#[wasm_bindgen]
+pub struct Prover {
+ inner: Option,
+}
+
+#[wasm_bindgen]
+impl Prover {
+ #[wasm_bindgen(constructor)]
+ pub fn new(prover_data: &[u8]) -> Result {
+ let is_binary = prover_data.len() >= HEADER_SIZE && &prover_data[..8] == MAGIC_BYTES;
+
+ let inner = if is_binary {
+ parse_binary_prover(prover_data)?
+ } else {
+ serde_json::from_slice(prover_data).map_err(|err| {
+ JsError::new(&format!(
+ "Failed to parse prover JSON: {err}. Data length: {}, first bytes: {:02X?}",
+ prover_data.len(),
+ &prover_data[..prover_data.len().min(20)]
+ ))
+ })?
+ };
+ Ok(Self { inner: Some(inner) })
+ }
+
+ /// `witness_map`: JS `Map` or plain object `{ "0": "0xhex…"
+ /// }`.
+ #[wasm_bindgen(js_name = proveBytes)]
+ pub fn prove_bytes(&mut self, witness_map: JsValue) -> Result, JsError> {
+ let proof = self.prove_inner(witness_map)?;
+ serde_json::to_vec(&proof)
+ .map(|bytes| bytes.into_boxed_slice())
+ .map_err(|err| JsError::new(&format!("Failed to serialize proof to JSON: {err}")))
+ }
+
+ #[wasm_bindgen(js_name = proveJs)]
+ pub fn prove_js(&mut self, witness_map: JsValue) -> Result {
+ let proof = self.prove_inner(witness_map)?;
+ serde_wasm_bindgen::to_value(&proof)
+ .map_err(|err| JsError::new(&format!("Failed to convert proof to JsValue: {err}")))
+ }
+
+ /// Returns circuit JSON for `@noir-lang/noir_js`.
+ ///
+ /// ```js
+ /// const prover = new Prover(pkpBytes);
+ /// const circuitJson = JSON.parse(new TextDecoder().decode(prover.getCircuit()));
+ /// const noir = new Noir(circuitJson);
+ /// ```
+ #[wasm_bindgen(js_name = getCircuit)]
+ pub fn get_circuit(&self) -> Result, JsError> {
+ let noir_prover = match self.inner_ref()? {
+ ProverCore::Noir(p) => p,
+ ProverCore::Mavros(_) => {
+ return Err(JsError::new("Only Noir provers are supported in WASM"))
+ }
+ };
+
+ let program_bytes = Program::::serialize_program(&noir_prover.program);
+ let bytecode_b64 = BASE64.encode(&program_bytes);
+
+ let abi_json = serde_json::to_value(&noir_prover.witness_generator.abi)
+ .map_err(|e| JsError::new(&format!("Failed to serialize ABI: {e}")))?;
+
+ let circuit = serde_json::json!({
+ "abi": abi_json,
+ "bytecode": bytecode_b64,
+ });
+
+ serde_json::to_vec(&circuit)
+ .map(|b| b.into_boxed_slice())
+ .map_err(|e| JsError::new(&format!("Failed to serialize circuit JSON: {e}")))
+ }
+
+ #[wasm_bindgen(js_name = getNumConstraints)]
+ pub fn get_num_constraints(&self) -> Result {
+ Ok(self.inner_ref()?.size().0)
+ }
+
+ #[wasm_bindgen(js_name = getNumWitnesses)]
+ pub fn get_num_witnesses(&self) -> Result {
+ Ok(self.inner_ref()?.size().1)
+ }
+}
+
+impl Prover {
+ fn inner_ref(&self) -> Result<&ProverCore, JsError> {
+ self.inner
+ .as_ref()
+ .ok_or_else(|| JsError::new("Prover has been consumed by a previous prove() call"))
+ }
+
+ fn prove_inner(&mut self, witness_map: JsValue) -> Result {
+ let witness = parse_witness_map(witness_map)?;
+ let inner = self
+ .inner
+ .take()
+ .ok_or_else(|| JsError::new("Prover has been consumed by a previous prove() call"))?;
+ inner
+ .prove_with_witness(witness)
+ .context("Failed to generate proof")
+ .map_err(|err| JsError::new(&format!("{err:#}")))
+ }
+}
+
+/// Max byte length for a BN254 field element (32 bytes = 64 hex chars).
+pub(crate) const MAX_FIELD_ELEMENT_BYTES: usize = 32;
+
+/// Accepts a JS `Map` or a plain object `{ "idx":
+/// "0xhex…" }`.
+pub(crate) fn parse_witness_map(js_value: JsValue) -> Result, JsError> {
+ let map: BTreeMap = if js_value.is_instance_of::() {
+ js_map_to_btree(&js_sys::Map::from(js_value))?
+ } else {
+ serde_wasm_bindgen::from_value(js_value).map_err(|err| {
+ JsError::new(&format!(
+ "Expected a Map or plain object mapping witness indices to hex strings: {err}"
+ ))
+ })?
+ };
+
+ parse_witness_map_entries(map)
+}
+
+fn parse_witness_map_entries(
+ map: BTreeMap,
+) -> Result, JsError> {
+ parse_witness_map_entries_impl(map).map_err(|msg| JsError::new(&msg))
+}
+
+fn parse_witness_map_entries_impl(
+ map: BTreeMap,
+) -> Result, String> {
+ if map.is_empty() {
+ return Err("Witness map is empty".to_owned());
+ }
+
+ let mut witness_map = WitnessMap::new();
+
+ for (index_str, hex_value) in map {
+ let index: u32 = index_str
+ .parse()
+ .map_err(|err| format!("Failed to parse witness index '{index_str}': {err}"))?;
+
+ let hex_str = hex_value.trim_start_matches("0x");
+
+ let bytes = hex::decode(hex_str)
+ .map_err(|err| format!("Failed to parse hex string at index {index}: {err}"))?;
+
+ if bytes.len() > MAX_FIELD_ELEMENT_BYTES {
+ return Err(format!(
+ "Hex value at index {index} is {} bytes, exceeds BN254 field element size (32 \
+ bytes)",
+ bytes.len()
+ ));
+ }
+
+ let field_element = FieldElement::from_be_bytes_reduce(&bytes);
+ witness_map.insert(Witness(index), field_element);
+ }
+
+ Ok(witness_map)
+}
+
+/// Converts a JS `Map` to `BTreeMap`, handling numeric and
+/// string keys and Witness objects with an `inner` property.
+fn js_map_to_btree(map: &js_sys::Map) -> Result, JsError> {
+ let mut result = BTreeMap::new();
+ let err: RefCell