From 26b308446808ae1c3470f367435cf81236bce2d9 Mon Sep 17 00:00:00 2001 From: Cedoor Date: Mon, 27 Oct 2025 12:21:15 +0000 Subject: [PATCH 1/4] chore: update crisp crates in settings/workspace --- .vscode/settings.json | 13 +++++-------- Cargo.toml | 1 - 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 9158da957e..ca80b93a0c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,20 +1,17 @@ { "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, - "prettier.documentSelectors": [ - "**/*.sol" - ], + "prettier.documentSelectors": ["**/*.sol"], "solidity.formatter": "prettier", "typescript.tsdk": "node_modules/typescript/lib", - "rust-analyzer.cargo.extraArgs": [ - "--locked" - ], + "rust-analyzer.cargo.extraArgs": ["--locked"], "rust-analyzer.linkedProjects": [ "crates/evm/Cargo.toml", "crates/evm-helpers/Cargo.toml", "examples/CRISP/program/Cargo.toml", "examples/CRISP/server/Cargo.toml", - "examples/CRISP/wasm-crypto/Cargo.toml" + "examples/CRISP/crates/zk-inputs/Cargo.toml", + "examples/CRISP/crates/zk-inputs-wasm/Cargo.toml" ], "[solidity]": { "editor.defaultFormatter": "NomicFoundation.hardhat-solidity" @@ -22,4 +19,4 @@ "[rust]": { "editor.defaultFormatter": "rust-lang.rust-analyzer" } -} \ No newline at end of file +} diff --git a/Cargo.toml b/Cargo.toml index 1c4e6e4e1a..fea1f73cc6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,6 @@ members = [ exclude = [ "examples/CRISP/server", "examples/CRISP/program", - "examples/CRISP/wasm-crypto", "examples/default/client/wasm", "examples/default", # client needs to be able to build crates/support independently From 797631469c6a66de370868f472e99c68679268fb Mon Sep 17 00:00:00 2001 From: Cedoor Date: Mon, 27 Oct 2025 12:22:23 +0000 Subject: [PATCH 2/4] feat: add zk-inputs-wasm crate --- examples/CRISP/Cargo.lock | 450 +++++++++++------- examples/CRISP/Cargo.toml | 3 +- .../CRISP/crates/zk-inputs-wasm/Cargo.toml | 20 + .../CRISP/crates/zk-inputs-wasm/src/lib.rs | 140 ++++++ .../{generator => zk-inputs}/Cargo.toml | 2 +- .../src/ciphertext_addition.rs | 0 .../{generator => zk-inputs}/src/lib.rs | 115 ++--- .../src/serialization.rs | 18 +- examples/CRISP/docker-compose.yaml | 4 +- examples/CRISP/package.json | 3 +- .../crisp-contracts/deployed_contracts.json | 80 +++- .../CRISP/packages/crisp-sdk/package.json | 6 +- .../crisp-sdk/tests/fixtures/pubkey.bin | Bin 27676 -> 0 bytes .../packages/crisp-sdk/tests/vote.test.ts | 10 +- .../CRISP/packages/crisp-sdk/vite.config.ts | 9 + examples/CRISP/server/Dockerfile | 4 +- pnpm-lock.yaml | 17 +- pnpm-workspace.yaml | 1 + 18 files changed, 616 insertions(+), 266 deletions(-) create mode 100644 examples/CRISP/crates/zk-inputs-wasm/Cargo.toml create mode 100644 examples/CRISP/crates/zk-inputs-wasm/src/lib.rs rename examples/CRISP/crates/{generator => zk-inputs}/Cargo.toml (96%) rename examples/CRISP/crates/{generator => zk-inputs}/src/ciphertext_addition.rs (100%) rename examples/CRISP/crates/{generator => zk-inputs}/src/lib.rs (77%) rename examples/CRISP/crates/{generator => zk-inputs}/src/serialization.rs (96%) delete mode 100644 examples/CRISP/packages/crisp-sdk/tests/fixtures/pubkey.bin create mode 100644 examples/CRISP/packages/crisp-sdk/vite.config.ts diff --git a/examples/CRISP/Cargo.lock b/examples/CRISP/Cargo.lock index 7d809e3bf5..05848091ff 100644 --- a/examples/CRISP/Cargo.lock +++ b/examples/CRISP/Cargo.lock @@ -79,7 +79,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ - "quote", + "quote 1.0.41", "syn 2.0.108", ] @@ -90,7 +90,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" dependencies = [ "bytestring", - "cfg-if", + "cfg-if 1.0.4", "http 0.2.12", "regex", "regex-lite", @@ -162,7 +162,7 @@ dependencies = [ "actix-web-codegen", "bytes", "bytestring", - "cfg-if", + "cfg-if 1.0.4", "cookie", "derive_more", "encoding_rs", @@ -195,8 +195,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" dependencies = [ "actix-router", - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -221,7 +221,7 @@ version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "once_cell", "version_check", "zerocopy", @@ -352,7 +352,7 @@ dependencies = [ "alloy-rpc-types-eth", "alloy-sol-types", "alloy-transport", - "futures", + "futures 0.3.31", "futures-util", "serde_json", "thiserror 2.0.17", @@ -568,7 +568,7 @@ checksum = "3cfebde8c581a5d37b678d0a48a32decb51efd7a63a08ce2517ddec26db705c8" dependencies = [ "alloy-rlp", "bytes", - "cfg-if", + "cfg-if 1.0.4", "const-hex", "derive_more", "foldhash", @@ -619,7 +619,7 @@ dependencies = [ "auto_impl", "dashmap", "either", - "futures", + "futures 0.3.31", "futures-utils-wasm", "lru", "parking_lot 0.12.5", @@ -645,7 +645,7 @@ dependencies = [ "alloy-transport", "auto_impl", "bimap", - "futures", + "futures 0.3.31", "parking_lot 0.12.5", "serde", "serde_json", @@ -673,8 +673,8 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -691,7 +691,7 @@ dependencies = [ "alloy-transport-http", "alloy-transport-ipc", "alloy-transport-ws", - "futures", + "futures 0.3.31", "pin-project", "reqwest", "serde", @@ -871,8 +871,8 @@ dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -888,8 +888,8 @@ dependencies = [ "heck", "indexmap 2.12.0", "proc-macro-error2", - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", "syn-solidity", "tiny-keccak", @@ -906,8 +906,8 @@ dependencies = [ "dunce", "heck", "macro-string", - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "serde_json", "syn 2.0.108", "syn-solidity", @@ -946,7 +946,7 @@ dependencies = [ "auto_impl", "base64 0.22.1", "derive_more", - "futures", + "futures 0.3.31", "futures-utils-wasm", "parking_lot 0.12.5", "serde", @@ -984,7 +984,7 @@ dependencies = [ "alloy-pubsub", "alloy-transport", "bytes", - "futures", + "futures 0.3.31", "interprocess", "pin-project", "serde", @@ -1002,7 +1002,7 @@ checksum = "9476a36a34e2fb51b6746d009c53d309a186a825aa95435407f0e07149f4ad2d" dependencies = [ "alloy-pubsub", "alloy-transport", - "futures", + "futures 0.3.31", "http 1.3.1", "rustls", "serde_json", @@ -1036,8 +1036,8 @@ checksum = "f8e52276fdb553d3c11563afad2898f4085165e4093604afe3d78b69afbf408f" dependencies = [ "alloy-primitives", "darling", - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -1204,7 +1204,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" dependencies = [ - "quote", + "quote 1.0.41", "syn 1.0.109", ] @@ -1214,7 +1214,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" dependencies = [ - "quote", + "quote 1.0.41", "syn 1.0.109", ] @@ -1224,7 +1224,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ - "quote", + "quote 1.0.41", "syn 2.0.108", ] @@ -1236,7 +1236,7 @@ checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" dependencies = [ "num-bigint", "num-traits", - "quote", + "quote 1.0.41", "syn 1.0.109", ] @@ -1248,8 +1248,8 @@ checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ "num-bigint", "num-traits", - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 1.0.109", ] @@ -1261,8 +1261,8 @@ checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" dependencies = [ "num-bigint", "num-traits", - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -1319,8 +1319,8 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 1.0.109", ] @@ -1386,8 +1386,8 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -1397,8 +1397,8 @@ version = "0.1.88" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -1408,7 +1408,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" dependencies = [ - "futures", + "futures 0.3.31", "pharos", "rustc_version 0.4.1", ] @@ -1425,8 +1425,8 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -1443,7 +1443,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ "addr2line", - "cfg-if", + "cfg-if 1.0.4", "libc", "miniz_oxide", "object", @@ -1581,7 +1581,7 @@ dependencies = [ "arrayref", "arrayvec", "cc", - "cfg-if", + "cfg-if 1.0.4", "constant_time_eq", ] @@ -1690,6 +1690,12 @@ dependencies = [ "shlex", ] +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "cfg-if" version = "1.0.4" @@ -1746,8 +1752,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" dependencies = [ "heck", - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -1796,13 +1802,23 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if 1.0.4", + "wasm-bindgen", +] + [[package]] name = "const-hex" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bb320cac8a0750d7f25280aa97b09c26edfe161164238ecbbb31092b079e735" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "cpufeatures", "proptest", "serde_core", @@ -1849,9 +1865,9 @@ version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", + "proc-macro2 1.0.103", + "quote 1.0.41", + "unicode-xid 0.2.6", ] [[package]] @@ -1926,7 +1942,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", ] [[package]] @@ -1974,22 +1990,12 @@ dependencies = [ name = "crisp-zk-inputs" version = "0.1.0" dependencies = [ - "bigint-poly", - "eyre", - "fhe", - "fhe-math", - "fhe-traits", - "hex", - "itertools 0.14.0", - "num-bigint", - "num-integer", - "num-traits", - "rand 0.8.5", - "rayon", - "serde", - "serde_json", - "zkfhe-greco", - "zkfhe-shared", + "getrandom 0.2.16", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-test", + "web-sys", + "zk-inputs", ] [[package]] @@ -2063,8 +2069,8 @@ checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" dependencies = [ "fnv", "ident_case", - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "serde", "strsim", "syn 2.0.108", @@ -2077,7 +2083,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", - "quote", + "quote 1.0.41", "syn 2.0.108", ] @@ -2087,7 +2093,7 @@ version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "crossbeam-utils", "hashbrown 0.14.5", "lock_api", @@ -2127,8 +2133,8 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 1.0.109", ] @@ -2147,10 +2153,10 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", - "unicode-xid", + "unicode-xid 0.2.6", ] [[package]] @@ -2194,8 +2200,8 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -2292,7 +2298,7 @@ dependencies = [ "alloy", "async-trait", "eyre", - "futures", + "futures 0.3.31", "futures-util", "once_cell", "tokio", @@ -2379,8 +2385,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" dependencies = [ "enum-ordinalize", - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -2425,7 +2431,7 @@ version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", ] [[package]] @@ -2443,8 +2449,8 @@ version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -2696,6 +2702,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "futures" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" + [[package]] name = "futures" version = "0.3.31" @@ -2750,8 +2762,8 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -2826,7 +2838,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "js-sys", "libc", "wasi", @@ -2839,7 +2851,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "js-sys", "libc", "r-efi", @@ -3279,8 +3291,8 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -3319,7 +3331,7 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", ] [[package]] @@ -3344,7 +3356,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" dependencies = [ "bitflags 2.10.0", - "cfg-if", + "cfg-if 1.0.4", "libc", ] @@ -3431,8 +3443,8 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -3473,7 +3485,7 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "ecdsa", "elliptic-curve", "once_cell", @@ -3630,8 +3642,8 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -3856,8 +3868,8 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -3868,7 +3880,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c4b5ecbd0beec843101bffe848217f770e8b8da81d8355b7d6e226f2199b3dc" dependencies = [ "alloy-rlp", - "cfg-if", + "cfg-if 1.0.4", "proptest", "ruint", "serde", @@ -3903,7 +3915,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24ad14dd45412269e1a30f52ad8f0664f0f4f4a89ee8fe28c3b3527021ebb654" dependencies = [ "bitflags 2.10.0", - "cfg-if", + "cfg-if 1.0.4", "foreign-types", "libc", "once_cell", @@ -3917,8 +3929,8 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -3973,8 +3985,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" dependencies = [ "proc-macro-crate", - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -4005,7 +4017,7 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "instant", "libc", "redox_syscall 0.2.16", @@ -4019,7 +4031,7 @@ version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "libc", "redox_syscall 0.5.18", "smallvec", @@ -4072,8 +4084,8 @@ checksum = "49b401d98f5757ebe97a26085998d6c0eecec4995cad6ab7fc30ffdf4b052843" dependencies = [ "pest", "pest_meta", - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -4103,7 +4115,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" dependencies = [ - "futures", + "futures 0.3.31", "rustc_version 0.4.1", ] @@ -4122,8 +4134,8 @@ version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -4200,7 +4212,7 @@ version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ - "proc-macro2", + "proc-macro2 1.0.103", "syn 2.0.108", ] @@ -4241,8 +4253,8 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", ] [[package]] @@ -4252,11 +4264,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" dependencies = [ "proc-macro-error-attr2", - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid 0.1.0", +] + [[package]] name = "proc-macro2" version = "1.0.103" @@ -4324,8 +4345,8 @@ checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", "itertools 0.12.1", - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -4399,13 +4420,22 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +dependencies = [ + "proc-macro2 0.4.30", +] + [[package]] name = "quote" version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ - "proc-macro2", + "proc-macro2 1.0.103", ] [[package]] @@ -4566,8 +4596,8 @@ version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -4645,7 +4675,7 @@ dependencies = [ "tower-service", "url", "wasm-bindgen", - "wasm-bindgen-futures", + "wasm-bindgen-futures 0.4.54", "web-sys", "webpki-roots 1.0.3", ] @@ -4667,7 +4697,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", - "cfg-if", + "cfg-if 1.0.4", "getrandom 0.2.16", "libc", "untrusted", @@ -4736,7 +4766,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "ordered-multimap", ] @@ -4881,6 +4911,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -5001,8 +5037,8 @@ version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -5066,8 +5102,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327ada00f7d64abaac1e55a6911e90cf665aa051b9a561c7006c157f4633135e" dependencies = [ "darling", - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -5087,7 +5123,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "cpufeatures", "digest 0.10.7", ] @@ -5098,7 +5134,7 @@ version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "cpufeatures", "digest 0.10.7", ] @@ -5120,7 +5156,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" dependencies = [ "cc", - "cfg-if", + "cfg-if 1.0.4", ] [[package]] @@ -5261,8 +5297,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck", - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -5278,8 +5314,8 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "unicode-ident", ] @@ -5289,8 +5325,8 @@ version = "2.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "unicode-ident", ] @@ -5301,8 +5337,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff790eb176cc81bb8936aed0f7b9f14fc4670069a2d371b3e3b0ecce908b2cb3" dependencies = [ "paste", - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -5321,8 +5357,8 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -5390,8 +5426,8 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -5401,8 +5437,8 @@ version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -5412,7 +5448,7 @@ version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", ] [[package]] @@ -5515,8 +5551,8 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -5715,8 +5751,8 @@ version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -5802,6 +5838,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -5901,7 +5943,7 @@ version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "once_cell", "rustversion", "wasm-bindgen-macro", @@ -5916,19 +5958,32 @@ checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" dependencies = [ "bumpalo", "log", - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83420b37346c311b9ed822af41ec2e82839bfe99867ec6c54e2da43b7538771c" +dependencies = [ + "cfg-if 0.1.10", + "futures 0.1.31", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-futures" version = "0.4.54" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "js-sys", "once_cell", "wasm-bindgen", @@ -5941,7 +5996,7 @@ version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" dependencies = [ - "quote", + "quote 1.0.41", "wasm-bindgen-macro-support", ] @@ -5951,8 +6006,8 @@ version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", "wasm-bindgen-backend", "wasm-bindgen-shared", @@ -5967,13 +6022,38 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-bindgen-test" +version = "0.2.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2d9693b63a742d481c7f80587e057920e568317b2806988c59cd71618bc26c1" +dependencies = [ + "console_error_panic_hook", + "futures 0.1.31", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures 0.3.27", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.2.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0789dac148a8840bbcf9efe13905463b733fa96543bfbf263790535c11af7ba5" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", +] + [[package]] name = "wasmtimer" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c598d6b99ea013e35844697fc4670d08339d5cda15588f193c6beedd12f644b" dependencies = [ - "futures", + "futures 0.3.31", "js-sys", "parking_lot 0.12.5", "pin-utils", @@ -6066,8 +6146,8 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -6077,8 +6157,8 @@ version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -6334,7 +6414,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c173014acad22e83f16403ee360115b38846fe754e735c5d9d3803fe70c6abc" dependencies = [ "async_io_stream", - "futures", + "futures 0.3.31", "js-sys", "log", "pharos", @@ -6342,7 +6422,7 @@ dependencies = [ "send_wrapper", "thiserror 2.0.17", "wasm-bindgen", - "wasm-bindgen-futures", + "wasm-bindgen-futures 0.4.54", "web-sys", ] @@ -6382,8 +6462,8 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", "synstructure", ] @@ -6403,8 +6483,8 @@ version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -6423,8 +6503,8 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", "synstructure", ] @@ -6444,8 +6524,8 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] @@ -6477,11 +6557,33 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.103", + "quote 1.0.41", "syn 2.0.108", ] +[[package]] +name = "zk-inputs" +version = "0.1.0" +dependencies = [ + "bigint-poly", + "eyre", + "fhe", + "fhe-math", + "fhe-traits", + "hex", + "itertools 0.14.0", + "num-bigint", + "num-integer", + "num-traits", + "rand 0.8.5", + "rayon", + "serde", + "serde_json", + "zkfhe-greco", + "zkfhe-shared", +] + [[package]] name = "zkfhe-greco" version = "0.1.0" diff --git a/examples/CRISP/Cargo.toml b/examples/CRISP/Cargo.toml index c7096547cf..7177a0e5d1 100644 --- a/examples/CRISP/Cargo.toml +++ b/examples/CRISP/Cargo.toml @@ -3,7 +3,8 @@ members = [ "server", ".enclave/support/dev", "program", - "crates/generator" + "crates/zk-inputs", + "crates/zk-inputs-wasm" ] resolver = "3" diff --git a/examples/CRISP/crates/zk-inputs-wasm/Cargo.toml b/examples/CRISP/crates/zk-inputs-wasm/Cargo.toml new file mode 100644 index 0000000000..33a4f26c72 --- /dev/null +++ b/examples/CRISP/crates/zk-inputs-wasm/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "crisp-zk-inputs" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +description = "Core logic to pre-compute CRISP ZK inputs (WASM/JavaScript bindings)." + +[lib] +crate-type = ["cdylib"] + +[dependencies] +zk-inputs = { path = "../zk-inputs" } +wasm-bindgen = "0.2" +js-sys = "0.3" +web-sys = "0.3" +getrandom = { version = "0.2", features = ["js"] } + +[dev-dependencies] +wasm-bindgen-test = "0.2" diff --git a/examples/CRISP/crates/zk-inputs-wasm/src/lib.rs b/examples/CRISP/crates/zk-inputs-wasm/src/lib.rs new file mode 100644 index 0000000000..93c7b9e9cb --- /dev/null +++ b/examples/CRISP/crates/zk-inputs-wasm/src/lib.rs @@ -0,0 +1,140 @@ +//! JavaScript library with WASM bindings for CRISP ZK inputs generation. +//! +//! This crate provides JavaScript bindings for the CRISP ZK inputs generator using WASM. + +use js_sys; +use wasm_bindgen::prelude::*; +use zk_inputs::ZKInputsGenerator as CoreZKInputsGenerator; + +/// JavaScript-compatible CRISP ZK inputs generator. +#[wasm_bindgen] +pub struct ZKInputsGenerator { + generator: CoreZKInputsGenerator, +} + +#[wasm_bindgen] +impl ZKInputsGenerator { + /// Create a new JavaScript CRISP ZK inputs generator with the specified BFV parameters. + /// + /// # Arguments + /// - `degree`: Polynomial degree + /// - `plaintext_modulus`: Plaintext modulus + /// - `moduli`: Array of moduli + #[wasm_bindgen(constructor)] + pub fn new( + degree: usize, + plaintext_modulus: u64, + moduli: Vec, + ) -> Result { + let generator = CoreZKInputsGenerator::new(degree, plaintext_modulus, &moduli); + Ok(ZKInputsGenerator { generator }) + } + + /// Create a new JavaScript CRISP ZK inputs generator with default BFV parameters. + #[wasm_bindgen(js_name = "withDefaults")] + pub fn with_defaults() -> ZKInputsGenerator { + let generator = CoreZKInputsGenerator::with_defaults(); + ZKInputsGenerator { generator } + } + + /// Generate a CRISP ZK inputs from JavaScript. + #[wasm_bindgen(js_name = "generateInputs")] + pub fn generate_inputs( + &self, + prev_ciphertext: &[u8], + public_key: &[u8], + vote: u8, + ) -> Result { + match self + .generator + .generate_inputs(prev_ciphertext, public_key, vote) + { + Ok(inputs_json) => { + // Parse the JSON string and return as JsValue. + match js_sys::JSON::parse(&inputs_json) { + Ok(js_value) => Ok(js_value), + Err(_) => Err(JsValue::from_str("Failed to parse inputs JSON")), + } + } + Err(e) => Err(JsValue::from_str(&e.to_string())), + } + } + + /// Generate a public key from JavaScript. + #[wasm_bindgen(js_name = "generatePublicKey")] + pub fn generate_public_key(&self) -> Result, JsValue> { + match self.generator.generate_public_key() { + Ok(public_key_bytes) => Ok(public_key_bytes), + Err(e) => Err(JsValue::from_str(&e.to_string())), + } + } + + /// Encrypt a vote from JavaScript. + #[wasm_bindgen(js_name = "encryptVote")] + pub fn encrypt_vote(&self, public_key: &[u8], vote: u8) -> Result, JsValue> { + match self.generator.encrypt_vote(public_key, vote) { + Ok(ciphertext_bytes) => Ok(ciphertext_bytes), + Err(e) => Err(JsValue::from_str(&e.to_string())), + } + } + + /// Get the version of the library. + #[wasm_bindgen] + pub fn version() -> String { + env!("CARGO_PKG_VERSION").to_string() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use wasm_bindgen_test::*; + + wasm_bindgen_test_configure!(run_in_browser); + + #[wasm_bindgen_test] + fn test_js_inputs_generation_with_defaults() { + // Create generator with default parameters. + let generator = ZKInputsGenerator::with_defaults(); + let public_key = generator.generate_public_key().unwrap(); + let old_ciphertext = generator.encrypt_vote(&public_key, 1).unwrap(); + let result = generator.generate_inputs(&old_ciphertext, &public_key, 1); + + assert!(result.is_ok()); + + let inputs = result.unwrap(); + + // Convert JsValue to string for testing. + let inputs_str = js_sys::JSON::stringify(&inputs) + .unwrap() + .as_string() + .unwrap(); + assert!(inputs_str.contains("params")); + assert!(inputs_str.contains("pk0is")); + } + + #[wasm_bindgen_test] + fn test_js_with_custom_params() { + let degree = 2048; + let plaintext_modulus = 1032193; + let moduli = vec![0x3FFFFFFF000001]; + + // Create generator with custom parameters. + let generator = ZKInputsGenerator::new(degree, plaintext_modulus, moduli).unwrap(); + let public_key = generator.generate_public_key().unwrap(); + let old_ciphertext = generator.encrypt_vote(&public_key, 1).unwrap(); + let result = generator.generate_inputs(&old_ciphertext, &public_key, 1); + + assert!(result.is_ok()); + + let inputs = result.unwrap(); + + // Convert JsValue to string for testing. + let inputs_str = js_sys::JSON::stringify(&inputs) + .unwrap() + .as_string() + .unwrap(); + assert!(inputs_str.contains("params")); + assert!(inputs_str.contains("pk0is")); + } +} diff --git a/examples/CRISP/crates/generator/Cargo.toml b/examples/CRISP/crates/zk-inputs/Cargo.toml similarity index 96% rename from examples/CRISP/crates/generator/Cargo.toml rename to examples/CRISP/crates/zk-inputs/Cargo.toml index c9910a7fd2..b8f6af1ce6 100644 --- a/examples/CRISP/crates/generator/Cargo.toml +++ b/examples/CRISP/crates/zk-inputs/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "crisp-zk-inputs" +name = "zk-inputs" version.workspace = true edition.workspace = true license.workspace = true diff --git a/examples/CRISP/crates/generator/src/ciphertext_addition.rs b/examples/CRISP/crates/zk-inputs/src/ciphertext_addition.rs similarity index 100% rename from examples/CRISP/crates/generator/src/ciphertext_addition.rs rename to examples/CRISP/crates/zk-inputs/src/ciphertext_addition.rs diff --git a/examples/CRISP/crates/generator/src/lib.rs b/examples/CRISP/crates/zk-inputs/src/lib.rs similarity index 77% rename from examples/CRISP/crates/generator/src/lib.rs rename to examples/CRISP/crates/zk-inputs/src/lib.rs index 32eea2f2f6..30451fd691 100644 --- a/examples/CRISP/crates/generator/src/lib.rs +++ b/examples/CRISP/crates/zk-inputs/src/lib.rs @@ -4,9 +4,9 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -//! Core crisp inputs generation library. +//! Core CRISP ZK inputs generation library. //! -//! This crate contains the main logic for generating crisp inputs for zero-knowledge proofs. +//! This crate contains the main logic for generating CRISP inputs for zero-knowledge proofs. use eyre::{Context, Result}; use fhe::bfv::Ciphertext; @@ -26,17 +26,18 @@ use crate::ciphertext_addition::CiphertextAdditionInputs; mod serialization; use serialization::{construct_inputs, serialize_inputs_to_json}; -pub struct CrispZKInputsGenerator { +// Default BFV parameters constants +const DEFAULT_DEGREE: usize = 2048; +const DEFAULT_PLAINTEXT_MODULUS: u64 = 1032193; +const DEFAULT_MODULI: [u64; 1] = [0x3FFFFFFF000001]; + +pub struct ZKInputsGenerator { bfv_params: Arc, } -impl CrispZKInputsGenerator { - pub fn new() -> Self { - Self::with_defaults() - } - - /// Creates a new generator with custom BFV parameters. - pub fn with_params(degree: usize, plaintext_modulus: u64, moduli: &[u64]) -> Self { +impl ZKInputsGenerator { + /// Creates a new generator with the specified BFV parameters. + pub fn new(degree: usize, plaintext_modulus: u64, moduli: &[u64]) -> Self { let bfv_params = BfvParametersBuilder::new() .set_degree(degree) .set_plaintext_modulus(plaintext_modulus) @@ -47,35 +48,28 @@ impl CrispZKInputsGenerator { } /// Creates a generator with default BFV parameters. - fn with_defaults() -> Self { - let degree = 2048; - let plaintext_modulus = 1032193; - let moduli = [0x3FFFFFFF000001]; - - Self::with_params(degree, plaintext_modulus, &moduli) + pub fn with_defaults() -> Self { + Self::new(DEFAULT_DEGREE, DEFAULT_PLAINTEXT_MODULUS, &DEFAULT_MODULI) } - /// Generates crisp ZK inputs for a vote encryption and addition operation. + /// Generates CRISP ZK inputs for a vote encryption and addition operation. /// /// # Arguments - /// * `prev_ciphertext` - Hex-encoded previous ciphertext to add to - /// * `public_key` - Hex-encoded public key for encryption + /// * `prev_ciphertext` - Previous ciphertext bytes to add to + /// * `public_key` - Public key bytes for encryption /// * `vote` - Vote value (0 or 1) /// /// # Returns - /// JSON string containing the crisp ZK inputs + /// JSON string containing the CRISP ZK inputs pub fn generate_inputs( &self, - prev_ciphertext: &str, - public_key: &str, + prev_ciphertext: &[u8], + public_key: &[u8], vote: u8, ) -> Result { // Deserialize the provided public key. - let pk = PublicKey::from_bytes( - &hex::decode(public_key).with_context(|| "Failed to decode public key from hex")?, - &self.bfv_params, - ) - .with_context(|| "Failed to deserialize public key")?; + let pk = PublicKey::from_bytes(public_key, &self.bfv_params) + .with_context(|| "Failed to deserialize public key")?; // Create a sample plaintext consistent with the GRECO sample generator. // All coefficients are 3, and the first coefficient represents the vote. @@ -102,12 +96,8 @@ impl CrispZKInputsGenerator { // Ciphertext Addition Section. // Deserialize the previous ciphertext. - let prev_ct = Ciphertext::from_bytes( - &hex::decode(prev_ciphertext) - .with_context(|| "Failed to decode previous ciphertext from hex")?, - &self.bfv_params, - ) - .with_context(|| "Failed to deserialize previous ciphertext")?; + let prev_ct = Ciphertext::from_bytes(prev_ciphertext, &self.bfv_params) + .with_context(|| "Failed to deserialize previous ciphertext")?; // Compute the ciphertext addition. let sum_ct = &ct + &prev_ct; @@ -131,17 +121,14 @@ impl CrispZKInputsGenerator { /// Encrypts a vote using the provided public key. /// /// # Arguments - /// * `public_key` - Hex-encoded public key for encryption + /// * `public_key` - Public key bytes for encryption /// * `vote` - Vote value (0 or 1) /// /// # Returns - /// Hex-encoded ciphertext - pub fn encrypt_vote(&self, public_key: &str, vote: u8) -> Result { - let pk = PublicKey::from_bytes( - &hex::decode(public_key).with_context(|| "Failed to decode public key from hex")?, - &self.bfv_params, - ) - .with_context(|| "Failed to deserialize public key")?; + /// Ciphertext bytes + pub fn encrypt_vote(&self, public_key: &[u8], vote: u8) -> Result> { + let pk = PublicKey::from_bytes(public_key, &self.bfv_params) + .with_context(|| "Failed to deserialize public key")?; let mut message_data = vec![3u64; self.bfv_params.degree()]; // Set vote value (0 or 1) in the first coefficient. @@ -154,20 +141,20 @@ impl CrispZKInputsGenerator { .try_encrypt_extended(&pt, &mut thread_rng()) .with_context(|| "Failed to encrypt plaintext")?; - Ok(hex::encode(ct.to_bytes())) + Ok(ct.to_bytes()) } /// Generates a new public/secret key pair and returns the public key. /// /// # Returns - /// Hex-encoded public key - pub fn generate_public_key(&self) -> Result { + /// Raw bytes of the public key + pub fn generate_public_key(&self) -> Result> { // Generate keys. let mut rng = thread_rng(); let sk = SecretKey::random(&self.bfv_params, &mut rng); let pk = PublicKey::new(&sk, &mut rng); - Ok(hex::encode(pk.to_bytes())) + Ok(pk.to_bytes()) } /// Returns a clone of the BFV parameters used by this generator. @@ -182,7 +169,7 @@ mod tests { #[test] fn test_inputs_generation_with_defaults() { - let generator = CrispZKInputsGenerator::new(); + let generator = ZKInputsGenerator::with_defaults(); let public_key = generator .generate_public_key() .expect("failed to generate public key"); @@ -201,7 +188,7 @@ mod tests { #[test] fn test_inputs_generation_with_custom_params() { - let generator = CrispZKInputsGenerator::with_params(2048, 1032193, &[0x3FFFFFFF000001]); + let generator = ZKInputsGenerator::new(2048, 1032193, &[0x3FFFFFFF000001]); let public_key = generator .generate_public_key() .expect("failed to generate public key"); @@ -220,7 +207,7 @@ mod tests { #[test] fn test_inputs_generation_with_vote_0() { - let generator = CrispZKInputsGenerator::new(); + let generator = ZKInputsGenerator::with_defaults(); let public_key = generator .generate_public_key() .expect("failed to generate public key"); @@ -239,7 +226,7 @@ mod tests { #[test] fn test_get_bfv_params() { - let generator = CrispZKInputsGenerator::new(); + let generator = ZKInputsGenerator::with_defaults(); let bfv_params = generator.get_bfv_params(); assert!(bfv_params.degree() == 2048); @@ -249,7 +236,7 @@ mod tests { #[test] fn test_secure_rng_usage() { - let generator = CrispZKInputsGenerator::new(); + let generator = ZKInputsGenerator::with_defaults(); // Test that functions use secure randomness (no deterministic seed). let public_key = generator @@ -273,25 +260,25 @@ mod tests { // Error handling tests #[test] fn test_invalid_inputs() { - let generator = CrispZKInputsGenerator::new(); + let generator = ZKInputsGenerator::with_defaults(); - // Test invalid hex inputs. - let result = generator.generate_inputs("invalid_hex", "invalid_hex", 0); + // Test invalid byte inputs. + let result = generator.generate_inputs(&[1, 2, 3], &[4, 5, 6], 0); assert!(result.is_err()); - // Test empty strings. - let result = generator.generate_inputs("", "", 0); + // Test empty slices. + let result = generator.generate_inputs(&[], &[], 0); assert!(result.is_err()); // Test invalid public key for encryption. - let result = generator.encrypt_vote("invalid_hex", 0); + let result = generator.encrypt_vote(&[1, 2, 3], 0); assert!(result.is_err()); } // Core functionality tests #[test] fn test_vote_values() { - let generator = CrispZKInputsGenerator::new(); + let generator = ZKInputsGenerator::with_defaults(); let public_key = generator .generate_public_key() .expect("failed to generate public key"); @@ -310,7 +297,7 @@ mod tests { #[test] fn test_bfv_params_consistency() { - let generator = CrispZKInputsGenerator::new(); + let generator = ZKInputsGenerator::with_defaults(); let bfv_params = generator.get_bfv_params(); // Verify default parameters. @@ -321,7 +308,7 @@ mod tests { #[test] fn test_json_output_structure() { - let generator = CrispZKInputsGenerator::new(); + let generator = ZKInputsGenerator::with_defaults(); let public_key = generator .generate_public_key() .expect("failed to generate public key"); @@ -348,7 +335,7 @@ mod tests { #[test] fn test_cryptographic_properties() { - let generator = CrispZKInputsGenerator::new(); + let generator = ZKInputsGenerator::with_defaults(); let public_key = generator .generate_public_key() .expect("Failed to generate public key"); @@ -369,9 +356,9 @@ mod tests { .expect("Failed to encrypt vote 0 again"); assert_ne!(ct0, ct0_2); - // Test that all ciphertexts are valid hex. - assert!(hex::decode(&ct0).is_ok()); - assert!(hex::decode(&ct1).is_ok()); - assert!(hex::decode(&ct0_2).is_ok()); + // Test that all ciphertexts are non-empty. + assert!(!ct0.is_empty()); + assert!(!ct1.is_empty()); + assert!(!ct0_2.is_empty()); } } diff --git a/examples/CRISP/crates/generator/src/serialization.rs b/examples/CRISP/crates/zk-inputs/src/serialization.rs similarity index 96% rename from examples/CRISP/crates/generator/src/serialization.rs rename to examples/CRISP/crates/zk-inputs/src/serialization.rs index 8fea9f9317..fdc833b565 100644 --- a/examples/CRISP/crates/generator/src/serialization.rs +++ b/examples/CRISP/crates/zk-inputs/src/serialization.rs @@ -4,7 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -//! Serialization module for crisp inputs data. +//! Serialization module for CRISP ZK inputs data. //! //! This module handles the serialization of inputs data to JSON format. @@ -17,7 +17,7 @@ use num_bigint::BigInt; use serde::Serialize; #[derive(Serialize)] -pub struct CrispZKInputs { +pub struct ZKInputs { ct_add: serde_json::Value, params: serde_json::Value, ct0is: Vec, @@ -39,7 +39,7 @@ fn to_string_1d_vec(vec: &[BigInt]) -> Vec { vec.iter().map(|x| x.to_string()).collect() } -/// Constructs a CrispZKInputs from GRECO bounds and vectors. +/// Constructs a ZKInputs from GRECO bounds and vectors. /// /// # Arguments /// * `crypto_params` - Cryptographic parameters from GRECO @@ -48,13 +48,13 @@ fn to_string_1d_vec(vec: &[BigInt]) -> Vec { /// * `ciphertext_addition_inputs_standard` - Standard form ciphertext addition inputs /// /// # Returns -/// A CrispZKInputs struct ready for JSON serialization +/// A ZKInputs struct ready for JSON serialization pub fn construct_inputs( crypto_params: &GrecoCryptographicParameters, bounds: &GrecoBounds, vectors_standard: &GrecoVectors, ciphertext_addition_inputs_standard: &CiphertextAdditionInputs, -) -> CrispZKInputs { +) -> ZKInputs { let mut params_json = serde_json::Map::new(); // Add crypto params. @@ -158,7 +158,7 @@ pub fn construct_inputs( serde_json::json!(ciphertext_addition_inputs_standard.r_bound), ); - CrispZKInputs { + ZKInputs { ct_add: serde_json::Value::Object(ciphertext_addition_params_json), params: serde_json::Value::Object(params_json), ct0is: vectors_standard @@ -248,14 +248,14 @@ pub fn construct_inputs( } } -/// Serializes a CrispZKInputs to JSON string. +/// Serializes a ZKInputs to JSON string. /// /// # Arguments -/// * `inputs` - The CrispZKInputs to serialize +/// * `inputs` - The ZKInputs to serialize /// /// # Returns /// JSON string representation of the inputs -pub fn serialize_inputs_to_json(inputs: &CrispZKInputs) -> Result { +pub fn serialize_inputs_to_json(inputs: &ZKInputs) -> Result { serde_json::to_string(inputs).with_context(|| "Failed to serialize inputs to JSON") } diff --git a/examples/CRISP/docker-compose.yaml b/examples/CRISP/docker-compose.yaml index f7d6d1b7f6..ad2ab3eb58 100644 --- a/examples/CRISP/docker-compose.yaml +++ b/examples/CRISP/docker-compose.yaml @@ -13,7 +13,7 @@ services: - cargo-bin:/home/ubuntu/.cargo/bin - cargo-registry:/home/ubuntu/.cargo/registry - server-target:/app/examples/CRISP/server/target - - generator-target:/app/examples/CRISP/crates/generator/target + - zk-inputs-target:/app/examples/CRISP/crates/zk-inputs/target - evm-node-modules:/app/packages/enclave-contracts/node_modules - root-node-modules:/app/node_modules - client-node-modules:/app/examples/CRISP/client/node_modules @@ -41,7 +41,7 @@ volumes: cargo-bin: cargo-registry: server-target: - generator-target: + zk-inputs-target: evm-node-modules: root-node-modules: client-node-modules: diff --git a/examples/CRISP/package.json b/examples/CRISP/package.json index e13d0df257..da3959bd6a 100644 --- a/examples/CRISP/package.json +++ b/examples/CRISP/package.json @@ -20,7 +20,8 @@ "test:circuits": "cd circuits && nargo test", "test:sdk": "cd sdk && pnpm test", "release:sdk": "cd sdk && pnpm release", - "report": "playwright show-report" + "report": "playwright show-report", + "build:wasm": "wasm-pack build ./crates/zk-inputs-wasm --scope enclave --out-dir ../../packages/crisp-zk-inputs --out-name index" }, "devDependencies": { "@playwright/test": "1.52.0", diff --git a/examples/CRISP/packages/crisp-contracts/deployed_contracts.json b/examples/CRISP/packages/crisp-contracts/deployed_contracts.json index c43bc20f06..49abb9532a 100644 --- a/examples/CRISP/packages/crisp-contracts/deployed_contracts.json +++ b/examples/CRISP/packages/crisp-contracts/deployed_contracts.json @@ -81,5 +81,83 @@ "imageId": "0x23734b77b0f76e85623a88d7a82f24c34c94834f2501964ea123b7a2027013a2" } } + }, + "localhost": { + "PoseidonT3": { + "blockNumber": 21, + "address": "0x3333333C0A88F9BE4fd23ed0536F9B6c427e3B93" + }, + "Enclave": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "registry": "0x0000000000000000000000000000000000000001", + "maxDuration": "2592000", + "params": [ + "0x000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000fc00100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000003fffffff000001" + ] + }, + "blockNumber": 4, + "address": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" + }, + "CiphernodeRegistryOwnable": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "enclaveAddress": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" + }, + "blockNumber": 5, + "address": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9" + }, + "NaiveRegistryFilter": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "ciphernodeRegistryAddress": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9" + }, + "blockNumber": 6, + "address": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" + }, + "MockComputeProvider": { + "blockNumber": 8, + "address": "0x0165878A594ca255338adfa4d48449f69242Eb8F" + }, + "MockDecryptionVerifier": { + "blockNumber": 9, + "address": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" + }, + "MockInputValidator": { + "blockNumber": 10, + "address": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" + }, + "MockE3Program": { + "constructorArgs": { + "mockInputValidator": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" + }, + "blockNumber": 11, + "address": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" + }, + "MockRISC0Verifier": { + "address": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" + }, + "CRISPInputValidator": { + "address": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82" + }, + "CRISPInputValidatorFactory": { + "address": "0x9A676e781A523b5d0C0e43731313A708CB607508", + "constructorArgs": { + "inputValidator": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82" + } + }, + "HonkVerifier": { + "address": "0x0B306BF915C4d645ff596e518fAf3F9669b97016" + }, + "CRISPProgram": { + "address": "0x959922bE3CAee4b8Cd9a407cc3ac1C251C2007B1", + "constructorArgs": { + "enclave": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0", + "verifierAddress": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0", + "inputValidatorAddress": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82", + "honkVerifierAddress": "0x0B306BF915C4d645ff596e518fAf3F9669b97016", + "imageId": "0x23734b77b0f76e85623a88d7a82f24c34c94834f2501964ea123b7a2027013a2" + } + } } -} +} \ No newline at end of file diff --git a/examples/CRISP/packages/crisp-sdk/package.json b/examples/CRISP/packages/crisp-sdk/package.json index e7a5553102..e6ac3e61da 100644 --- a/examples/CRISP/packages/crisp-sdk/package.json +++ b/examples/CRISP/packages/crisp-sdk/package.json @@ -19,7 +19,8 @@ }, "homepage": "https://github.com/gnosisguild/enclave", "scripts": { - "build": "tsc", + "build:wasm": "pnpm -C ../../ build:wasm", + "build": "pnpm build:wasm && tsc", "test": "vitest --run", "prettier:fix": "pnpm prettier --write .", "prettier:check": "pnpm prettier --check .", @@ -34,9 +35,12 @@ "chai": "^6.2.0", "prettier": "^3.2.5", "typescript": "^5.0.0", + "vite": "^5.4.19", + "vite-plugin-wasm": "^3.2.2", "vitest": "^1.6.1" }, "dependencies": { + "@enclave/crisp-zk-inputs": "workspace:*", "@enclave-e3/sdk": "workspace:*", "@zk-kit/lean-imt": "^2.2.4", "poseidon-lite": "^0.3.0", diff --git a/examples/CRISP/packages/crisp-sdk/tests/fixtures/pubkey.bin b/examples/CRISP/packages/crisp-sdk/tests/fixtures/pubkey.bin deleted file mode 100644 index ecc1d2934151137ed56581bc7224a9df26d27513..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27676 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 bco9C3!%!1_PAAwSh10P diff --git a/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts b/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts index e2ffd4ce26..a89cf0c2a4 100644 --- a/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts +++ b/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts @@ -8,6 +8,7 @@ import fs from 'fs/promises' import path from 'path' import { describe, it, expect, beforeAll } from 'vitest' import { BfvProtocolParams, type ProtocolParams } from '@enclave-e3/sdk' +import { ZKInputsGenerator } from '@enclave/crisp-zk-inputs' import { calculateValidIndicesForPlaintext, decodeTally, encodeVote, encryptVoteAndGenerateCRISPInputs, validateVote } from '../src/vote' import { VotingMode } from '../src/types' @@ -118,13 +119,8 @@ describe('Vote', () => { const vote = { yes: 10n, no: 0n } const votingPower = 10n - let publicKey: Uint8Array - - beforeAll(async () => { - const buffer = await fs.readFile(path.resolve(__dirname, './fixtures/pubkey.bin')) - - publicKey = Uint8Array.from(buffer) - }) + let zkInputsGenerator = ZKInputsGenerator.withDefaults() + let publicKey = zkInputsGenerator.generatePublicKey() it('should encrypt a vote and generate the circuit inputs', async () => { const encodedVote = encodeVote(vote, VotingMode.GOVERNANCE, BfvProtocolParams.BFV_NORMAL, votingPower) diff --git a/examples/CRISP/packages/crisp-sdk/vite.config.ts b/examples/CRISP/packages/crisp-sdk/vite.config.ts new file mode 100644 index 0000000000..9dea05e39a --- /dev/null +++ b/examples/CRISP/packages/crisp-sdk/vite.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config' +import wasm from 'vite-plugin-wasm' + +export default defineConfig({ + test: { + environment: 'node', + }, + plugins: [wasm()], +}) diff --git a/examples/CRISP/server/Dockerfile b/examples/CRISP/server/Dockerfile index 5505e97a01..1421dd3e49 100644 --- a/examples/CRISP/server/Dockerfile +++ b/examples/CRISP/server/Dockerfile @@ -31,7 +31,7 @@ COPY examples/CRISP/program/Cargo.toml examples/CRISP/program/ COPY examples/CRISP/program/client/Cargo.toml examples/CRISP/program/client/ COPY examples/CRISP/program/core/Cargo.toml examples/CRISP/program/core/ COPY examples/CRISP/Cargo.toml examples/CRISP/ -COPY examples/CRISP/crates/generator/Cargo.toml examples/CRISP/crates/generator/ +COPY examples/CRISP/crates/zk-inputs/Cargo.toml examples/CRISP/crates/zk-inputs/ COPY crates/sdk/Cargo.toml crates/sdk/ COPY Cargo.toml Cargo.lock ./ @@ -52,7 +52,7 @@ COPY --from=chef /app/examples/CRISP/server/recipe.json \ COPY examples/CRISP/server/Cargo.toml examples/CRISP/server/Cargo.lock examples/CRISP/server/ COPY examples/CRISP/program/core/Cargo.toml examples/CRISP/program/core/ -COPY examples/CRISP/crates/generator/Cargo.toml examples/CRISP/crates/generator/ +COPY examples/CRISP/crates/zk-inputs/Cargo.toml examples/CRISP/crates/zk-inputs/ COPY Cargo.lock ./Cargo.lock COPY Cargo.toml ./Cargo.toml diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 87e2c8f851..1b2c68535c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -342,6 +342,9 @@ importers: '@enclave-e3/sdk': specifier: workspace:* version: link:../../../../packages/enclave-sdk + '@enclave/crisp-zk-inputs': + specifier: workspace:* + version: link:../crisp-zk-inputs '@zk-kit/lean-imt': specifier: ^2.2.4 version: 2.2.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) @@ -367,10 +370,18 @@ importers: typescript: specifier: 5.8.3 version: 5.8.3 + vite: + specifier: ^5.4.19 + version: 5.4.19(@types/node@22.7.5) + vite-plugin-wasm: + specifier: ^3.2.2 + version: 3.5.0(vite@5.4.19(@types/node@22.7.5)) vitest: specifier: ^1.6.1 version: 1.6.1(@types/node@22.7.5) + examples/CRISP/packages/crisp-zk-inputs: {} + packages/enclave-config: dependencies: tsup: @@ -478,7 +489,7 @@ importers: version: 3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) hardhat-gas-reporter: specifier: ^2.2.0 - version: 2.3.0(bufferutil@4.0.9)(hardhat@3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 2.3.0(bufferutil@4.0.9)(hardhat@3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typescript@5.8.3)(utf-8-validate@5.0.10) lodash: specifier: ^4.17.21 version: 4.17.21 @@ -16399,7 +16410,7 @@ snapshots: - debug - utf-8-validate - hardhat-gas-reporter@2.3.0(bufferutil@4.0.9)(hardhat@3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76): + hardhat-gas-reporter@2.3.0(bufferutil@4.0.9)(hardhat@3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typescript@5.8.3)(utf-8-validate@5.0.10): dependencies: '@ethersproject/abi': 5.8.0 '@ethersproject/bytes': 5.8.0 @@ -16416,7 +16427,7 @@ snapshots: lodash: 4.17.21 markdown-table: 2.0.0 sha1: 1.1.1 - viem: 2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - debug diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0f585e5031..7c88d6eced 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -5,6 +5,7 @@ packages: - examples/CRISP/client - examples/CRISP/packages/crisp-sdk - examples/CRISP/packages/crisp-contracts + - examples/CRISP/packages/crisp-zk-inputs - packages/enclave-config - packages/enclave-react - packages/enclave-sdk From 7f6ad2d06a17d43d69247408aafd77a3d65e8899 Mon Sep 17 00:00:00 2001 From: Cedoor Date: Mon, 27 Oct 2025 12:25:27 +0000 Subject: [PATCH 3/4] chore: add missing licenses --- examples/CRISP/crates/zk-inputs-wasm/src/lib.rs | 6 ++++++ examples/CRISP/packages/crisp-sdk/vite.config.ts | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/examples/CRISP/crates/zk-inputs-wasm/src/lib.rs b/examples/CRISP/crates/zk-inputs-wasm/src/lib.rs index 93c7b9e9cb..461972e985 100644 --- a/examples/CRISP/crates/zk-inputs-wasm/src/lib.rs +++ b/examples/CRISP/crates/zk-inputs-wasm/src/lib.rs @@ -1,3 +1,9 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + //! JavaScript library with WASM bindings for CRISP ZK inputs generation. //! //! This crate provides JavaScript bindings for the CRISP ZK inputs generator using WASM. diff --git a/examples/CRISP/packages/crisp-sdk/vite.config.ts b/examples/CRISP/packages/crisp-sdk/vite.config.ts index 9dea05e39a..2a5320c075 100644 --- a/examples/CRISP/packages/crisp-sdk/vite.config.ts +++ b/examples/CRISP/packages/crisp-sdk/vite.config.ts @@ -1,3 +1,9 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + import { defineConfig } from 'vitest/config' import wasm from 'vite-plugin-wasm' From b9be10f39de988770b94d4045dae4a7c9bd29142 Mon Sep 17 00:00:00 2001 From: Cedoor Date: Mon, 27 Oct 2025 12:50:19 +0000 Subject: [PATCH 4/4] refactor: handle errors in ZKInputsGenerator --- .../CRISP/crates/zk-inputs-wasm/src/lib.rs | 12 ++++--- examples/CRISP/crates/zk-inputs/src/lib.rs | 35 ++++++++++--------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/examples/CRISP/crates/zk-inputs-wasm/src/lib.rs b/examples/CRISP/crates/zk-inputs-wasm/src/lib.rs index 461972e985..88e6e0bc6a 100644 --- a/examples/CRISP/crates/zk-inputs-wasm/src/lib.rs +++ b/examples/CRISP/crates/zk-inputs-wasm/src/lib.rs @@ -32,15 +32,17 @@ impl ZKInputsGenerator { plaintext_modulus: u64, moduli: Vec, ) -> Result { - let generator = CoreZKInputsGenerator::new(degree, plaintext_modulus, &moduli); + let generator = CoreZKInputsGenerator::new(degree, plaintext_modulus, &moduli) + .map_err(|e| JsValue::from_str(&e.to_string()))?; Ok(ZKInputsGenerator { generator }) } /// Create a new JavaScript CRISP ZK inputs generator with default BFV parameters. #[wasm_bindgen(js_name = "withDefaults")] - pub fn with_defaults() -> ZKInputsGenerator { - let generator = CoreZKInputsGenerator::with_defaults(); - ZKInputsGenerator { generator } + pub fn with_defaults() -> Result { + let generator = CoreZKInputsGenerator::with_defaults() + .map_err(|e| JsValue::from_str(&e.to_string()))?; + Ok(ZKInputsGenerator { generator }) } /// Generate a CRISP ZK inputs from JavaScript. @@ -101,7 +103,7 @@ mod tests { #[wasm_bindgen_test] fn test_js_inputs_generation_with_defaults() { // Create generator with default parameters. - let generator = ZKInputsGenerator::with_defaults(); + let generator = ZKInputsGenerator::with_defaults().unwrap(); let public_key = generator.generate_public_key().unwrap(); let old_ciphertext = generator.encrypt_vote(&public_key, 1).unwrap(); let result = generator.generate_inputs(&old_ciphertext, &public_key, 1); diff --git a/examples/CRISP/crates/zk-inputs/src/lib.rs b/examples/CRISP/crates/zk-inputs/src/lib.rs index 30451fd691..804b239f5f 100644 --- a/examples/CRISP/crates/zk-inputs/src/lib.rs +++ b/examples/CRISP/crates/zk-inputs/src/lib.rs @@ -27,9 +27,9 @@ mod serialization; use serialization::{construct_inputs, serialize_inputs_to_json}; // Default BFV parameters constants -const DEFAULT_DEGREE: usize = 2048; -const DEFAULT_PLAINTEXT_MODULUS: u64 = 1032193; -const DEFAULT_MODULI: [u64; 1] = [0x3FFFFFFF000001]; +pub const DEFAULT_DEGREE: usize = 2048; +pub const DEFAULT_PLAINTEXT_MODULUS: u64 = 1032193; +pub const DEFAULT_MODULI: [u64; 1] = [0x3FFFFFFF000001]; pub struct ZKInputsGenerator { bfv_params: Arc, @@ -37,18 +37,18 @@ pub struct ZKInputsGenerator { impl ZKInputsGenerator { /// Creates a new generator with the specified BFV parameters. - pub fn new(degree: usize, plaintext_modulus: u64, moduli: &[u64]) -> Self { + pub fn new(degree: usize, plaintext_modulus: u64, moduli: &[u64]) -> Result { let bfv_params = BfvParametersBuilder::new() .set_degree(degree) .set_plaintext_modulus(plaintext_modulus) .set_moduli(moduli) .build_arc() - .unwrap(); - Self { bfv_params } + .map_err(|e| eyre::eyre!("Invalid BFV parameters: {}", e))?; + Ok(Self { bfv_params }) } /// Creates a generator with default BFV parameters. - pub fn with_defaults() -> Self { + pub fn with_defaults() -> Result { Self::new(DEFAULT_DEGREE, DEFAULT_PLAINTEXT_MODULUS, &DEFAULT_MODULI) } @@ -169,7 +169,7 @@ mod tests { #[test] fn test_inputs_generation_with_defaults() { - let generator = ZKInputsGenerator::with_defaults(); + let generator = ZKInputsGenerator::with_defaults().expect("failed to create generator"); let public_key = generator .generate_public_key() .expect("failed to generate public key"); @@ -188,7 +188,8 @@ mod tests { #[test] fn test_inputs_generation_with_custom_params() { - let generator = ZKInputsGenerator::new(2048, 1032193, &[0x3FFFFFFF000001]); + let generator = ZKInputsGenerator::new(2048, 1032193, &[0x3FFFFFFF000001]) + .expect("failed to create generator"); let public_key = generator .generate_public_key() .expect("failed to generate public key"); @@ -207,7 +208,7 @@ mod tests { #[test] fn test_inputs_generation_with_vote_0() { - let generator = ZKInputsGenerator::with_defaults(); + let generator = ZKInputsGenerator::with_defaults().expect("failed to create generator"); let public_key = generator .generate_public_key() .expect("failed to generate public key"); @@ -226,7 +227,7 @@ mod tests { #[test] fn test_get_bfv_params() { - let generator = ZKInputsGenerator::with_defaults(); + let generator = ZKInputsGenerator::with_defaults().expect("failed to create generator"); let bfv_params = generator.get_bfv_params(); assert!(bfv_params.degree() == 2048); @@ -236,7 +237,7 @@ mod tests { #[test] fn test_secure_rng_usage() { - let generator = ZKInputsGenerator::with_defaults(); + let generator = ZKInputsGenerator::with_defaults().expect("failed to create generator"); // Test that functions use secure randomness (no deterministic seed). let public_key = generator @@ -260,7 +261,7 @@ mod tests { // Error handling tests #[test] fn test_invalid_inputs() { - let generator = ZKInputsGenerator::with_defaults(); + let generator = ZKInputsGenerator::with_defaults().expect("failed to create generator"); // Test invalid byte inputs. let result = generator.generate_inputs(&[1, 2, 3], &[4, 5, 6], 0); @@ -278,7 +279,7 @@ mod tests { // Core functionality tests #[test] fn test_vote_values() { - let generator = ZKInputsGenerator::with_defaults(); + let generator = ZKInputsGenerator::with_defaults().expect("failed to create generator"); let public_key = generator .generate_public_key() .expect("failed to generate public key"); @@ -297,7 +298,7 @@ mod tests { #[test] fn test_bfv_params_consistency() { - let generator = ZKInputsGenerator::with_defaults(); + let generator = ZKInputsGenerator::with_defaults().expect("failed to create generator"); let bfv_params = generator.get_bfv_params(); // Verify default parameters. @@ -308,7 +309,7 @@ mod tests { #[test] fn test_json_output_structure() { - let generator = ZKInputsGenerator::with_defaults(); + let generator = ZKInputsGenerator::with_defaults().expect("failed to create generator"); let public_key = generator .generate_public_key() .expect("failed to generate public key"); @@ -335,7 +336,7 @@ mod tests { #[test] fn test_cryptographic_properties() { - let generator = ZKInputsGenerator::with_defaults(); + let generator = ZKInputsGenerator::with_defaults().expect("Failed to create generator"); let public_key = generator .generate_public_key() .expect("Failed to generate public key");