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 + + + + + + + + + + + +
+ + + + + + + +
+
+

+ + Custom Circuit Configuration + NOIR_COMPATIBLE_V1 +

+ +
+ +

Drag & drop circuit files

+

Required: prover.pkp, verifier.pkv, inputs.json

+ + +
+
+ +
+

REQUIREMENT CHECKLIST

+ +
+
+
-
+ prover.pkp +
+
Waiting...
+
+ +
+
+
-
+ verifier.pkv +
+
Waiting...
+
+ +
+
+
-
+ inputs.json / Prover.toml +
+
Waiting...
+
+
+
+ + +
+ + +
+

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...
+
+
+ +
+ +
+ + +
+
+ + +
+
+
+
+ SYSTEM LOG OUTPUT +
+
+ +
+
+
+ + + + +
+
+ +
+ + +
+
+
TOTAL TIME (WITNESS + PROOF)
+
-
+
+
+
PROOF SIZE
+
-
+
+
+
CONSTRAINTS
+
-
+
+
+
WITNESSES
+
-
+
+
+ + + + + + + + + + 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> = RefCell::new(None); + + map.for_each(&mut |value: JsValue, key: JsValue| { + if err.borrow().is_some() { + return; + } + + let key_str = if let Some(n) = key.as_f64() { + (n as u32).to_string() + } else if let Some(s) = key.as_string() { + s + } else if let Ok(inner) = js_sys::Reflect::get(&key, &"inner".into()) { + if let Some(n) = inner.as_f64() { + (n as u32).to_string() + } else { + *err.borrow_mut() = Some(format!("Map key has non-numeric .inner property")); + return; + } + } else { + *err.borrow_mut() = Some(format!("Unsupported Map key type")); + return; + }; + + let val_str = match value.as_string() { + Some(s) => s, + None => { + *err.borrow_mut() = Some(format!("Map value at key {key_str} is not a string")); + return; + } + }; + + result.insert(key_str, val_str); + }); + + if let Some(msg) = err.into_inner() { + return Err(JsError::new(&msg)); + } + Ok(result) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn witness_map_from_pairs(pairs: &[(&str, &str)]) -> BTreeMap { + pairs + .iter() + .map(|(k, v)| ((*k).to_owned(), (*v).to_owned())) + .collect() + } + + #[test] + fn max_field_element_bytes_is_32() { + assert_eq!(MAX_FIELD_ELEMENT_BYTES, 32); + } + + #[test] + fn parse_witness_map_entries_parses_valid_hex_values() { + let input = witness_map_from_pairs(&[("1", "0x01"), ("2", "ff")]); + + let parsed = parse_witness_map_entries(input).unwrap(); + + assert_eq!(parsed.get(&Witness(1)), Some(&FieldElement::from(1_u128))); + assert_eq!(parsed.get(&Witness(2)), Some(&FieldElement::from(255_u128))); + } + + #[test] + fn parse_witness_map_entries_rejects_empty_map() { + let err = parse_witness_map_entries_impl(BTreeMap::new()).unwrap_err(); + assert!(err.contains("Witness map is empty")); + } + + #[test] + fn parse_witness_map_entries_rejects_invalid_index() { + let input = witness_map_from_pairs(&[("abc", "0x01")]); + + let err = parse_witness_map_entries_impl(input).unwrap_err(); + assert!(err.contains("Failed to parse witness index 'abc'")); + } + + #[test] + fn parse_witness_map_entries_rejects_invalid_hex() { + let input = witness_map_from_pairs(&[("1", "0xzz")]); + + let err = parse_witness_map_entries_impl(input).unwrap_err(); + assert!(err.contains("Failed to parse hex string at index 1")); + } + + #[test] + fn parse_witness_map_entries_rejects_too_many_bytes() { + let too_long_hex = format!("0x{}", "11".repeat(MAX_FIELD_ELEMENT_BYTES + 1)); + let mut input = BTreeMap::new(); + input.insert("5".to_owned(), too_long_hex); + + let err = parse_witness_map_entries_impl(input).unwrap_err(); + assert!(err.contains("exceeds BN254 field element size (32 bytes)")); + } +} diff --git a/tooling/provekit-wasm/src/verifier.rs b/tooling/provekit-wasm/src/verifier.rs new file mode 100644 index 000000000..85b555cab --- /dev/null +++ b/tooling/provekit-wasm/src/verifier.rs @@ -0,0 +1,60 @@ +use { + crate::format::parse_binary_verifier, + provekit_common::{ + binary_format::{HEADER_SIZE, MAGIC_BYTES}, + NoirProof, Verifier as VerifierCore, + }, + provekit_verifier::Verify, + wasm_bindgen::prelude::*, +}; + +/// WASM bindings for proof verification. Reusable across multiple proofs. +#[wasm_bindgen] +pub struct Verifier { + inner: VerifierCore, +} + +#[wasm_bindgen] +impl Verifier { + /// Creates a new verifier from a `.pkv` verifier artifact. + #[wasm_bindgen(constructor)] + pub fn new(verifier_data: &[u8]) -> Result { + let is_binary = verifier_data.len() >= HEADER_SIZE && &verifier_data[..8] == MAGIC_BYTES; + + let inner = if is_binary { + parse_binary_verifier(verifier_data)? + } else { + serde_json::from_slice(verifier_data) + .map_err(|err| JsError::new(&format!("Failed to parse verifier JSON: {err}")))? + }; + Ok(Self { inner }) + } + + /// Verifies a proof provided as JSON bytes. The verifier is **not** + /// consumed. + #[wasm_bindgen(js_name = verifyBytes)] + pub fn verify_bytes(&self, proof_json: &[u8]) -> Result<(), JsError> { + let proof: NoirProof = serde_json::from_slice(proof_json) + .map_err(|err| JsError::new(&format!("Failed to parse proof JSON: {err}")))?; + self.verify_proof(&proof) + } + + /// Verifies a proof provided as a JavaScript object. The verifier is + /// **not** consumed. + #[wasm_bindgen(js_name = verifyJs)] + pub fn verify_js(&self, proof: JsValue) -> Result<(), JsError> { + let proof: NoirProof = serde_wasm_bindgen::from_value(proof) + .map_err(|err| JsError::new(&format!("Failed to parse proof: {err}")))?; + self.verify_proof(&proof) + } +} + +impl Verifier { + fn verify_proof(&self, proof: &NoirProof) -> Result<(), JsError> { + // Clone so the core verifier's .take() consumption doesn't prevent reuse. + let mut verifier = self.inner.clone(); + verifier + .verify(proof) + .map_err(|err| JsError::new(&format!("{err:#}"))) + } +}