From 94e07ca466aaef3b248ec740cb7ecf0ba7c06721 Mon Sep 17 00:00:00 2001 From: Cedoor Date: Wed, 12 Nov 2025 17:45:25 +0000 Subject: [PATCH 1/5] chore: set target web/nodejs for crisp zk-inputs --- examples/CRISP/client/vite.config.ts | 10 ++- examples/CRISP/package.json | 1 - .../CRISP/packages/crisp-sdk/package.json | 7 +-- examples/CRISP/packages/crisp-sdk/src/vote.ts | 6 ++ .../CRISP/packages/crisp-zk-inputs/README.md | 26 ++++++++ .../CRISP/packages/crisp-zk-inputs/init.d.ts | 9 +++ .../packages/crisp-zk-inputs/init_node.cjs | 9 +++ .../packages/crisp-zk-inputs/init_node.js | 9 +++ .../packages/crisp-zk-inputs/init_web.js | 29 +++++++++ .../packages/crisp-zk-inputs/package.json | 61 ++++++++++++++++--- .../packages/crisp-zk-inputs/scripts/build.js | 56 +++++++++++++++++ pnpm-lock.yaml | 9 ++- 12 files changed, 215 insertions(+), 17 deletions(-) create mode 100644 examples/CRISP/packages/crisp-zk-inputs/README.md create mode 100644 examples/CRISP/packages/crisp-zk-inputs/init.d.ts create mode 100644 examples/CRISP/packages/crisp-zk-inputs/init_node.cjs create mode 100644 examples/CRISP/packages/crisp-zk-inputs/init_node.js create mode 100644 examples/CRISP/packages/crisp-zk-inputs/init_web.js create mode 100644 examples/CRISP/packages/crisp-zk-inputs/scripts/build.js diff --git a/examples/CRISP/client/vite.config.ts b/examples/CRISP/client/vite.config.ts index 9d811d384c..aa7d4a5bb3 100644 --- a/examples/CRISP/client/vite.config.ts +++ b/examples/CRISP/client/vite.config.ts @@ -23,7 +23,15 @@ export default defineConfig({ }, optimizeDeps: { esbuildOptions: { target: 'esnext' }, - exclude: ['@rollup/browser', '@crisp-e3/zk-inputs', '@noir-lang/noirc_abi', '@noir-lang/acvm_js', '@noir-lang/noir_js', '@aztec/bb.js'], + exclude: [ + '@rollup/browser', + '@crisp-e3/zk-inputs', + '@crisp-e3/zk-inputs/init', + '@noir-lang/noirc_abi', + '@noir-lang/acvm_js', + '@noir-lang/noir_js', + '@aztec/bb.js', + ], }, resolve: { alias: { diff --git a/examples/CRISP/package.json b/examples/CRISP/package.json index 2e33479df0..b289ea149f 100644 --- a/examples/CRISP/package.json +++ b/examples/CRISP/package.json @@ -23,7 +23,6 @@ "test:sdk": "cd sdk && pnpm test", "release:sdk": "cd sdk && pnpm release", "report": "playwright show-report", - "build:wasm": "wasm-pack build ./crates/zk-inputs-wasm --no-pack --out-dir ../../packages/crisp-zk-inputs/pkg --out-name index", "publish:packages": "tsx scripts/publish.ts" }, "devDependencies": { diff --git a/examples/CRISP/packages/crisp-sdk/package.json b/examples/CRISP/packages/crisp-sdk/package.json index 8cc9bbeab6..3374021d57 100644 --- a/examples/CRISP/packages/crisp-sdk/package.json +++ b/examples/CRISP/packages/crisp-sdk/package.json @@ -20,10 +20,9 @@ }, "homepage": "https://github.com/gnosisguild/enclave", "scripts": { - "build:wasm": "pnpm -C ../../ build:wasm", - "compile:circuit": "pnpm -C ../../ compile:circuit", - "build": "pnpm compile:circuit && pnpm build:wasm && tsup", - "test": "pnpm compile:circuit && vitest --run", + "build:wasm": "pnpm -C ../crisp-zk-inputs build", + "build": "pnpm build:wasm && tsup", + "test": "pnpm build:wasm && vitest --run", "prettier:fix": "pnpm prettier --write .", "prettier:check": "pnpm prettier --check .", "release": "pnpm build && pnpm publish --access public" diff --git a/examples/CRISP/packages/crisp-sdk/src/vote.ts b/examples/CRISP/packages/crisp-sdk/src/vote.ts index 6c2e2895c8..74eb901224 100644 --- a/examples/CRISP/packages/crisp-sdk/src/vote.ts +++ b/examples/CRISP/packages/crisp-sdk/src/vote.ts @@ -5,6 +5,7 @@ // or FITNESS FOR A PARTICULAR PURPOSE. import { ZKInputsGenerator } from '@crisp-e3/zk-inputs' +import initializeWasm from '@crisp-e3/zk-inputs/init' import { BFVParams, type CRISPCircuitInputs, type EncryptVoteAndGenerateCRISPInputsParams, type IVote, VotingMode } from './types' import { toBinary } from './utils' import { MAXIMUM_VOTE_VALUE, DEFAULT_BFV_PARAMS, HALF_LARGEST_MINIMUM_DEGREE, MESSAGE } from './constants' @@ -153,6 +154,7 @@ export const validateVote = (votingMode: VotingMode, vote: IVote, votingPower: b } export const encryptVote = async (encodedVote: string[], publicKey: Uint8Array): Promise => { + await initializeWasm() const zkInputsGenerator: ZKInputsGenerator = new ZKInputsGenerator( DEFAULT_BFV_PARAMS.degree, DEFAULT_BFV_PARAMS.plaintextModulus, @@ -191,6 +193,8 @@ export const encryptVoteAndGenerateCRISPInputs = async ({ slotAddress, isFirstVote, }: EncryptVoteAndGenerateCRISPInputsParams): Promise => { + await initializeWasm() + if (encodedVote.length !== bfvParams.degree) { throw new RangeError(`encodedVote length ${encodedVote.length} does not match BFV degree ${bfvParams.degree}`) } @@ -238,6 +242,8 @@ export const generateMaskVote = async ( slotAddress: string, isFirstVote: boolean, ): Promise => { + await initializeWasm() + const plaintextVote: IVote = { yes: 0n, no: 0n, diff --git a/examples/CRISP/packages/crisp-zk-inputs/README.md b/examples/CRISP/packages/crisp-zk-inputs/README.md new file mode 100644 index 0000000000..8cdce981b0 --- /dev/null +++ b/examples/CRISP/packages/crisp-zk-inputs/README.md @@ -0,0 +1,26 @@ +# Wasm bundle for crisp-zk-inputs + +Here we export wasm funcionality for consumption in typescript to enable use to share code between Rust and Typescript. + +## Usage + +This package exposes an `init` subpackage default function which should be used to universally load the wasm module instead of exporting the default loader. + +This is because in modern node there is no need for preloading however in the browser we still need to load the wasm bundle. + +##### ❌ DONT USE THE DEFAULT INIT + +```ts +// Bad! Because this uses the raw loader which doesn't exist in node contexts +import init, { bfvEncryptNumber } from "@crisp-e3/zk-inputs"; +``` + +##### ✅ DO USE THE EXPORTED SUBMODULE + +```ts +// Good! Use the universal loader +import init from "@crisp-e3/zk-inputs/init"; + +await init(); +// other package imports here +``` diff --git a/examples/CRISP/packages/crisp-zk-inputs/init.d.ts b/examples/CRISP/packages/crisp-zk-inputs/init.d.ts new file mode 100644 index 0000000000..51b87772e2 --- /dev/null +++ b/examples/CRISP/packages/crisp-zk-inputs/init.d.ts @@ -0,0 +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. + +type Params = { module_or_path?: any }; +declare function init(param?: Params): Promise; +export default init; diff --git a/examples/CRISP/packages/crisp-zk-inputs/init_node.cjs b/examples/CRISP/packages/crisp-zk-inputs/init_node.cjs new file mode 100644 index 0000000000..42be6ce9b1 --- /dev/null +++ b/examples/CRISP/packages/crisp-zk-inputs/init_node.cjs @@ -0,0 +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. + +module.exports = async function initializeWasm() { + // Node does not need to be loaded async +} \ No newline at end of file diff --git a/examples/CRISP/packages/crisp-zk-inputs/init_node.js b/examples/CRISP/packages/crisp-zk-inputs/init_node.js new file mode 100644 index 0000000000..8386f0fd0b --- /dev/null +++ b/examples/CRISP/packages/crisp-zk-inputs/init_node.js @@ -0,0 +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. + +export default async function initializeWasm() { + // Node does not need to be loaded async +} diff --git a/examples/CRISP/packages/crisp-zk-inputs/init_web.js b/examples/CRISP/packages/crisp-zk-inputs/init_web.js new file mode 100644 index 0000000000..050570857f --- /dev/null +++ b/examples/CRISP/packages/crisp-zk-inputs/init_web.js @@ -0,0 +1,29 @@ +// 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 * as bindgen from "./dist/web/index.js"; + +let promise; + +export default async function initializeWasm(initParams) { + promise ??= (async () => { + const { default: base64 } = await import("./dist/web/index_base64.js"); + + const binaryString = atob(base64); + const len = binaryString.length; + const bytes = new Uint8Array(len); + + for (let i = 0; i < len; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + + bindgen.initSync(bytes); + + return bindgen; + })(); + + return promise; +} diff --git a/examples/CRISP/packages/crisp-zk-inputs/package.json b/examples/CRISP/packages/crisp-zk-inputs/package.json index 93d82ccc4c..c75bfb47bc 100644 --- a/examples/CRISP/packages/crisp-zk-inputs/package.json +++ b/examples/CRISP/packages/crisp-zk-inputs/package.json @@ -8,19 +8,60 @@ "type": "git", "url": "https://github.com/gnosisguild/enclave" }, + "main": "dist/node/index.js", + "module": "dist/web/index.js", + "types": "dist/web/index.d.ts", "files": [ - "pkg/index_bg.wasm", - "pkg/index.js", - "pkg/index_bg.js", - "pkg/index.d.ts" + "dist/", + "dist/node/**", + "dist/web/**", + "init_node.js", + "init_node.cjs", + "init_web.js", + "init.d.ts" ], + "exports": { + ".": { + "node": { + "types": "./dist/node/index.d.ts", + "default": "./dist/node/index.js" + }, + "browser": { + "types": "./dist/web/index.d.ts", + "default": "./dist/web/index.js" + }, + "default": { + "types": "./dist/web/index.d.ts", + "default": "./dist/web/index.js" + } + }, + "./init": { + "node": { + "types": "./init.d.ts", + "import": "./init_node.js", + "require": "./init_node.cjs" + }, + "browser": { + "types": "./init.d.ts", + "default": "./init_web.js" + }, + "default": { + "types": "./init.d.ts", + "import": "./init_node.js", + "require": "./init_node.cjs", + "default": "./init_web.js" + } + } + }, "publishConfig": { "access": "public" }, - "main": "pkg/index.js", - "types": "pkg/index.d.ts", - "sideEffects": [ - "./pkg/index.js", - "./pkg/snippets/*" - ] + "scripts": { + "compile:circuit": "pnpm -C ../../ compile:circuit", + "build": "rm -rf dist && pnpm compile:circuit && node scripts/build.js" + }, + "devDependencies": { + "execa": "^8.0.1", + "replace-in-file": "^7.2.0" + } } diff --git a/examples/CRISP/packages/crisp-zk-inputs/scripts/build.js b/examples/CRISP/packages/crisp-zk-inputs/scripts/build.js new file mode 100644 index 0000000000..05d149f061 --- /dev/null +++ b/examples/CRISP/packages/crisp-zk-inputs/scripts/build.js @@ -0,0 +1,56 @@ +// 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 { execa } from "execa"; +import { readFile, writeFile, rm } from "fs/promises"; +import { resolve } from "path"; +import replaceInFile from "replace-in-file"; + +try { + // Build WASM with web and node target - generates index.js and index_bg.wasm. + const distWeb = resolve(process.cwd(), "dist/web"); + const distNode = resolve(process.cwd(), "dist/node"); + + await execa("wasm-pack", [ + "build", + "../../crates/zk-inputs-wasm", + "--target=web", + `--out-dir=${distWeb}`, + "--no-pack", + "--out-name=index", + ]); + await execa("wasm-pack", [ + "build", + "../../crates/zk-inputs-wasm", + "--target=nodejs", + `--out-dir=${distNode}`, + "--no-pack", + "--out-name=index", + ]); + + // Convert WASM binary to base64 for bundler compatibility. + const wasmBinary = await readFile("./dist/web/index_bg.wasm"); + const base64Src = `export default '${wasmBinary.toString("base64")}';\n`; + + // Parallel cleanup and JS modification to prevent Next.js and other bundlers static analysis issues. + await Promise.all([ + await Promise.all([ + rm("./dist/web/index_bg.wasm", { force: true }), + rm("./dist/web/index_bg.wasm.d.ts", { force: true }), + rm("./dist/web/.gitignore", { force: true }), + rm("./dist/node/.gitignore", { force: true }), + ]), + replaceInFile({ + files: "./dist/web/index.js", + from: /module_or_path\s*=\s*new URL\(['"]index_bg\.wasm['"],\s*import\.meta\.url\);\s*/g, + to: "/* wasm URL disabled: load via @crisp-e3/zk-inputs/init */\n", + }), + writeFile("./dist/web/index_base64.js", base64Src), + ]); +} catch (error) { + console.error(error); + process.exit(1); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2f4f19f131..dd7303d7f2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -383,7 +383,14 @@ importers: specifier: ^1.6.1 version: 1.6.1(@types/node@22.7.5) - examples/CRISP/packages/crisp-zk-inputs: {} + examples/CRISP/packages/crisp-zk-inputs: + devDependencies: + execa: + specifier: ^8.0.1 + version: 8.0.1 + replace-in-file: + specifier: ^7.2.0 + version: 7.2.0 packages/enclave-config: dependencies: From d7d8566add097cb4690b193ed560a7f3bd30f946 Mon Sep 17 00:00:00 2001 From: Cedoor Date: Wed, 12 Nov 2025 17:52:48 +0000 Subject: [PATCH 2/5] docs: update crisp-zk-inputs README.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- examples/CRISP/packages/crisp-zk-inputs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/CRISP/packages/crisp-zk-inputs/README.md b/examples/CRISP/packages/crisp-zk-inputs/README.md index 8cdce981b0..5c87fe5c61 100644 --- a/examples/CRISP/packages/crisp-zk-inputs/README.md +++ b/examples/CRISP/packages/crisp-zk-inputs/README.md @@ -1,6 +1,6 @@ # Wasm bundle for crisp-zk-inputs -Here we export wasm funcionality for consumption in typescript to enable use to share code between Rust and Typescript. +Here we export wasm functionality for consumption in TypeScript to enable us to share code between Rust and TypeScript. ## Usage From 2925170e4cfaf65d6f123590ecca839e6466aa88 Mon Sep 17 00:00:00 2001 From: Cedoor Date: Wed, 12 Nov 2025 17:56:00 +0000 Subject: [PATCH 3/5] refactor: rename initializeWasm to init --- examples/CRISP/packages/crisp-zk-inputs/init.d.ts | 3 +-- examples/CRISP/packages/crisp-zk-inputs/init_node.cjs | 4 ++-- examples/CRISP/packages/crisp-zk-inputs/init_node.js | 2 +- examples/CRISP/packages/crisp-zk-inputs/init_web.js | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/examples/CRISP/packages/crisp-zk-inputs/init.d.ts b/examples/CRISP/packages/crisp-zk-inputs/init.d.ts index 51b87772e2..fa29ba16f4 100644 --- a/examples/CRISP/packages/crisp-zk-inputs/init.d.ts +++ b/examples/CRISP/packages/crisp-zk-inputs/init.d.ts @@ -4,6 +4,5 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -type Params = { module_or_path?: any }; -declare function init(param?: Params): Promise; +declare function init(): Promise; export default init; diff --git a/examples/CRISP/packages/crisp-zk-inputs/init_node.cjs b/examples/CRISP/packages/crisp-zk-inputs/init_node.cjs index 42be6ce9b1..2efc91fecf 100644 --- a/examples/CRISP/packages/crisp-zk-inputs/init_node.cjs +++ b/examples/CRISP/packages/crisp-zk-inputs/init_node.cjs @@ -4,6 +4,6 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -module.exports = async function initializeWasm() { +module.exports = async function init() { // Node does not need to be loaded async -} \ No newline at end of file +}; diff --git a/examples/CRISP/packages/crisp-zk-inputs/init_node.js b/examples/CRISP/packages/crisp-zk-inputs/init_node.js index 8386f0fd0b..d0f22af2d0 100644 --- a/examples/CRISP/packages/crisp-zk-inputs/init_node.js +++ b/examples/CRISP/packages/crisp-zk-inputs/init_node.js @@ -4,6 +4,6 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -export default async function initializeWasm() { +export default async function init() { // Node does not need to be loaded async } diff --git a/examples/CRISP/packages/crisp-zk-inputs/init_web.js b/examples/CRISP/packages/crisp-zk-inputs/init_web.js index 050570857f..35ac69108d 100644 --- a/examples/CRISP/packages/crisp-zk-inputs/init_web.js +++ b/examples/CRISP/packages/crisp-zk-inputs/init_web.js @@ -8,7 +8,7 @@ import * as bindgen from "./dist/web/index.js"; let promise; -export default async function initializeWasm(initParams) { +export default async function init() { promise ??= (async () => { const { default: base64 } = await import("./dist/web/index_base64.js"); From 0f9329413936ee8e650e4a25a61847d0e0ec81e9 Mon Sep 17 00:00:00 2001 From: Cedoor Date: Wed, 12 Nov 2025 17:58:02 +0000 Subject: [PATCH 4/5] refactor: parallel promises in build script --- .../CRISP/packages/crisp-zk-inputs/scripts/build.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/examples/CRISP/packages/crisp-zk-inputs/scripts/build.js b/examples/CRISP/packages/crisp-zk-inputs/scripts/build.js index 05d149f061..3fe35987a1 100644 --- a/examples/CRISP/packages/crisp-zk-inputs/scripts/build.js +++ b/examples/CRISP/packages/crisp-zk-inputs/scripts/build.js @@ -37,12 +37,10 @@ try { // Parallel cleanup and JS modification to prevent Next.js and other bundlers static analysis issues. await Promise.all([ - await Promise.all([ - rm("./dist/web/index_bg.wasm", { force: true }), - rm("./dist/web/index_bg.wasm.d.ts", { force: true }), - rm("./dist/web/.gitignore", { force: true }), - rm("./dist/node/.gitignore", { force: true }), - ]), + rm("./dist/web/index_bg.wasm", { force: true }), + rm("./dist/web/index_bg.wasm.d.ts", { force: true }), + rm("./dist/web/.gitignore", { force: true }), + rm("./dist/node/.gitignore", { force: true }), replaceInFile({ files: "./dist/web/index.js", from: /module_or_path\s*=\s*new URL\(['"]index_bg\.wasm['"],\s*import\.meta\.url\);\s*/g, From 1bce676736041554643202648a723f568ecaf7e3 Mon Sep 17 00:00:00 2001 From: Cedoor Date: Wed, 12 Nov 2025 18:00:39 +0000 Subject: [PATCH 5/5] docs: add README headings --- examples/CRISP/packages/crisp-zk-inputs/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/CRISP/packages/crisp-zk-inputs/README.md b/examples/CRISP/packages/crisp-zk-inputs/README.md index 5c87fe5c61..d88c1a8faf 100644 --- a/examples/CRISP/packages/crisp-zk-inputs/README.md +++ b/examples/CRISP/packages/crisp-zk-inputs/README.md @@ -8,14 +8,14 @@ This package exposes an `init` subpackage default function which should be used This is because in modern node there is no need for preloading however in the browser we still need to load the wasm bundle. -##### ❌ DONT USE THE DEFAULT INIT +### ❌ DONT USE THE DEFAULT INIT ```ts // Bad! Because this uses the raw loader which doesn't exist in node contexts import init, { bfvEncryptNumber } from "@crisp-e3/zk-inputs"; ``` -##### ✅ DO USE THE EXPORTED SUBMODULE +### ✅ DO USE THE EXPORTED SUBMODULE ```ts // Good! Use the universal loader