diff --git a/README.md b/README.md index 71095a9..08a7c42 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,8 @@ consumers must preserve unknown fields. | `@verifiable-okf/canon` | ✅ M0 | Canonicalization (§5) + JCS (§6) + signing-payload builder. The shared, version-pinned core. | | `@verifiable-okf/signer` | ✅ M0 | `vokf-sign` — attaches `provenance` without mutating any other byte. | | `@verifiable-okf/verifier` | ✅ M0 | `vokf-verify` — the §10 verification algorithm (status + reason codes), schema validation, did:web/did:key. | -| `@verifiable-okf/visualizer` | ⏳ M1 | Bundle → single offline HTML with per-concept badges. | +| `@verifiable-okf/visualizer` | ✅ M1 | Bundle → single offline HTML with per-concept badges. | +| `@verifiable-okf/attest-zk-groth16` | ✅ M1 | `zk-groth16` attestation verifier (Groth16/BN254 via snarkjs, offline); bundles the `contains_no_pii` demo circuit. | ## Quickstart (< 5 min) @@ -47,4 +48,25 @@ pnpm build pnpm test ``` +## Attestations (zk-groth16) + +Attestations are pluggable, machine-checkable claims carried in `provenance.attestations`. +`@verifiable-okf/attest-zk-groth16` provides an offline Groth16 proof verifier; pass it to the +verifier's policy to turn the §10 attestation path into real ZK checks: + +```ts +import { verifyConcept } from "@verifiable-okf/verifier"; +import { groth16AttestationVerifier } from "@verifiable-okf/attest-zk-groth16"; + +await verifyConcept(concept, { + policy: { requiredClaims: ["contains_no_pii"] }, + attestationVerifiers: { "zk-groth16": groth16AttestationVerifier }, +}); +``` + +The `contains_no_pii` demo circuit (`circuits/contains_no_pii/`, regenerate with `gen.sh`) proves a +private field is absent; verification needs only the bundled verification key — no proving key, no +circom, no network. Proof *issuance* (and metering) is a separate, commercial concern, not part of +this OSS engine. + See [`CANONICALIZATION.md`](./CANONICALIZATION.md) and [`SECURITY.md`](./SECURITY.md). diff --git a/circuits/contains_no_pii/.gitignore b/circuits/contains_no_pii/.gitignore new file mode 100644 index 0000000..e945b99 --- /dev/null +++ b/circuits/contains_no_pii/.gitignore @@ -0,0 +1,4 @@ +build/ +*.ptau +*.zkey +*.wtns diff --git a/circuits/contains_no_pii/contains_no_pii.circom b/circuits/contains_no_pii/contains_no_pii.circom new file mode 100644 index 0000000..f9f0c4f --- /dev/null +++ b/circuits/contains_no_pii/contains_no_pii.circom @@ -0,0 +1,14 @@ +pragma circom 2.1.9; + +// Minimal demo circuit for the `contains_no_pii` claim. +// Proves (in zero knowledge) that a private field equals 0 — i.e. a PII +// counter / flag is absent. A valid proof therefore attests "no PII", +// without revealing anything further. Public output `ok` is always 1. +template ContainsNoPii() { + signal input pii_field; // private witness (e.g. count of PII matches) + signal output ok; // public + pii_field === 0; // constraint: only satisfiable when pii_field == 0 + ok <== 1; +} + +component main = ContainsNoPii(); diff --git a/circuits/contains_no_pii/gen.sh b/circuits/contains_no_pii/gen.sh new file mode 100755 index 0000000..a405e62 --- /dev/null +++ b/circuits/contains_no_pii/gen.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +# Regenerate the contains_no_pii Groth16 artifacts (circom 2 + snarkjs). +# Outputs build/, then copies vkey.json + a sample proof to the circuit root. +set -e +cd "$(dirname "$0")" +mkdir -p build && cd build +circom ../contains_no_pii.circom --r1cs --wasm -o . +npx snarkjs powersoftau new bn128 8 pot_0.ptau +npx snarkjs powersoftau contribute pot_0.ptau pot_1.ptau --name=vokf -e="$(head -c 32 /dev/urandom | xxd -p)" +npx snarkjs powersoftau prepare phase2 pot_1.ptau pot_final.ptau +npx snarkjs groth16 setup contains_no_pii.r1cs pot_final.ptau cnp_0.zkey +npx snarkjs zkey contribute cnp_0.zkey cnp_final.zkey --name=vokf -e="$(head -c 32 /dev/urandom | xxd -p)" +npx snarkjs zkey export verificationkey cnp_final.zkey vkey.json +echo '{"pii_field":"0"}' > input.json +node contains_no_pii_js/generate_witness.js contains_no_pii_js/contains_no_pii.wasm input.json witness.wtns +npx snarkjs groth16 prove cnp_final.zkey witness.wtns proof.json public.json +npx snarkjs groth16 verify vkey.json public.json proof.json +cp vkey.json ../vkey.json && cp proof.json ../proof.sample.json && cp public.json ../public.sample.json diff --git a/circuits/contains_no_pii/proof.sample.json b/circuits/contains_no_pii/proof.sample.json new file mode 100644 index 0000000..4fce68f --- /dev/null +++ b/circuits/contains_no_pii/proof.sample.json @@ -0,0 +1,28 @@ +{ + "pi_a": [ + "17461939730782852438770980938862447298870257132741896786032076201177378191598", + "8607488854563628719732053192769587193635892693849864793195992234487286965736", + "1" + ], + "pi_b": [ + [ + "11728139912199972330023136803603667424732587153851331680813612153155232420464", + "11200038715154571765669600573424442998636501858871986095753385916766423332238" + ], + [ + "15898827097719954490264646923107552463817122452175480198633607290137491470234", + "39309480319576073215644807530307792566864076610431022681114688907195330005" + ], + [ + "1", + "0" + ] + ], + "pi_c": [ + "20569737558178479334100010919083231235115538011684311997543995295726755938354", + "7407113397076647680008114124813864763833032734057969247775727988171334507260", + "1" + ], + "protocol": "groth16", + "curve": "bn128" +} \ No newline at end of file diff --git a/circuits/contains_no_pii/public.sample.json b/circuits/contains_no_pii/public.sample.json new file mode 100644 index 0000000..ab9d6c9 --- /dev/null +++ b/circuits/contains_no_pii/public.sample.json @@ -0,0 +1,3 @@ +[ + "1" +] \ No newline at end of file diff --git a/circuits/contains_no_pii/vkey.json b/circuits/contains_no_pii/vkey.json new file mode 100644 index 0000000..bddc3e6 --- /dev/null +++ b/circuits/contains_no_pii/vkey.json @@ -0,0 +1,94 @@ +{ + "protocol": "groth16", + "curve": "bn128", + "nPublic": 1, + "vk_alpha_1": [ + "1232894381328749349801999194020364750860714090412859425702737227783362329035", + "4436118752635877068544674281701685683067136631392057119874998162712106835950", + "1" + ], + "vk_beta_2": [ + [ + "14716936584645639048355760343770265219429753952539561837148583891266482232582", + "16016704211999055611191571745890472454130292775011844893044274766649904226588" + ], + [ + "9057080589359257783096967548757429701196839693892521700242482106975202632599", + "20533264667873917127608317601690877061735828347178346689977693657285325072488" + ], + [ + "1", + "0" + ] + ], + "vk_gamma_2": [ + [ + "10857046999023057135944570762232829481370756359578518086990519993285655852781", + "11559732032986387107991004021392285783925812861821192530917403151452391805634" + ], + [ + "8495653923123431417604973247489272438418190587263600148770280649306958101930", + "4082367875863433681332203403145435568316851327593401208105741076214120093531" + ], + [ + "1", + "0" + ] + ], + "vk_delta_2": [ + [ + "3474799006316917099767645587129093771951975749530518410992048528464400789842", + "14906175476459237866989995382063729370101320006270423677795159512995448203917" + ], + [ + "5379252983319465967809417952863222386238200287388003645473005624099013594517", + "18986786293210347468478260290195243983141901438725909310713432120103963521504" + ], + [ + "1", + "0" + ] + ], + "vk_alphabeta_12": [ + [ + [ + "403749106030442990619315241768518264979636829364042921852032020812672065137", + "7621931038878796889452159494924950157179135094573670140480507261568496685977" + ], + [ + "16865090028061755323054514475260851841876300676446183862568668824480223320175", + "18327880048007991543735184721865349268992505013333563818619430008549808266562" + ], + [ + "8787872532129923515649140272636460985886459682924130307132452782207431646038", + "13260321749514392872666733411862938534642858847009090275335091916896884689184" + ] + ], + [ + [ + "6387628840775407748790935730268315938700872123575627156397259578938427030883", + "16771398627571756483969946929083216360331205551563536906712676092481376278695" + ], + [ + "15792279552950039777871914493874300116271355423474748217406866705529595597418", + "16476731688131302025407637631205413052485691063167902404388883062051157327695" + ], + [ + "14261736778804873940795974174260233059211405548185751146935318338563960445767", + "6447326858417875907252629836795830459066202724682340898082801712486689440581" + ] + ] + ], + "IC": [ + [ + "18641926387932197056747795654697750247402624656485328984590349834099713095425", + "5797156867949200385824451310324450740426988220911984895467204416449729528236", + "1" + ], + [ + "566468554377789334102443174096780922474059205361865467335928056340967116619", + "19928558876800948858731875360898910993093008468686622797144379353445393047770", + "1" + ] + ] +} \ No newline at end of file diff --git a/fixtures/signed/with-bad-zk-attestation.md b/fixtures/signed/with-bad-zk-attestation.md new file mode 100644 index 0000000..3920e88 --- /dev/null +++ b/fixtures/signed/with-bad-zk-attestation.md @@ -0,0 +1,32 @@ +--- +title: GA4 Purchase Event +resource: https://example.com/okf/ga4/purchase +description: Server-side GA4 purchase event mapping for the storefront. +tags: + - analytics + - ga4 +provenance: + spec_version: verifiable-okf/0.3 + issuer: did:key:z6MkneMkZqwqRiU5mJzSG3kDwzt9P8C59N4NGTfBLfSGE7c7 + content_hash: sha256:94ea40b78f963160edd233a2ad2aa10c5da2d945eb40e4059dd80399d4ccbd00 + valid_from: 2026-01-01T00:00:00Z + valid_until: 2027-01-01T00:00:00Z + attestations: + - claim: contains_no_pii + scheme: zk-groth16 + circuit: contains_no_pii + proof: ewogInBpX2EiOiBbCiAgIjE3NDYxOTM5NzMwNzgyODUyNDM4NzcwOTgwOTM4ODYyNDQ3Mjk4ODcwMjU3MTMyNzQxODk2Nzg2MDMyMDc2MjAxMTc3Mzc4MTkxNTk4IiwKICAiODYwNzQ4ODg1NDU2MzYyODcxOTczMjA1MzE5Mjc2OTU4NzE5MzYzNTg5MjY5Mzg0OTg2NDc5MzE5NTk5MjIzNDQ4NzI4Njk2NTczNiIsCiAgIjEiCiBdLAogInBpX2IiOiBbCiAgWwogICAiMTE3MjgxMzk5MTIxOTk5NzIzMzAwMjMxMzY4MDM2MDM2Njc0MjQ3MzI1ODcxNTM4NTEzMzE2ODA4MTM2MTIxNTMxNTUyMzI0MjA0NjQiLAogICAiMTEyMDAwMzg3MTUxNTQ1NzE3NjU2Njk2MDA1NzM0MjQ0NDI5OTg2MzY1MDE4NTg4NzE5ODYwOTU3NTMzODU5MTY3NjY0MjMzMzIyMzgiCiAgXSwKICBbCiAgICIxNTg5ODgyNzA5NzcxOTk1NDQ5MDI2NDY0NjkyMzEwNzU1MjQ2MzgxNzEyMjQ1MjE3NTQ4MDE5ODYzMzYwNzI5MDEzNzQ5MTQ3MDIzNCIsCiAgICIzOTMwOTQ4MDMxOTU3NjA3MzIxNTY0NDgwNzUzMDMwNzc5MjU2Njg2NDA3NjYxMDQzMTAyMjY4MTExNDY4ODkwNzE5NTMzMDAwNSIKICBdLAogIFsKICAgIjEiLAogICAiMCIKICBdCiBdLAogInBpX2MiOiBbCiAgIjIwNTY5NzM3NTU4MTc4NDc5MzM0MTAwMDEwOTE5MDgzMjMxMjM1MTE1NTM4MDExNjg0MzExOTk3NTQzOTk1Mjk1NzI2NzU1OTM4MzU0IiwKICAiNzQwNzExMzM5NzA3NjY0NzY4MDAwODExNDEyNDgxMzg2NDc2MzgzMzAzMjczNDA1Nzk2OTI0Nzc3NTcyNzk4ODE3MTMzNDUwNzI2MCIsCiAgIjEiCiBdLAogInByb3RvY29sIjogImdyb3RoMTYiLAogImN1cnZlIjogImJuMTI4Igp9 + public_inputs: + - "2" + signature: + scheme: ed25519 + value: Fo0PsKt0yw5QZ4UW7eErcJIgz0FAgOiTdp8+dErnml189GSmyida8gq9DijaIzQnfRNLa/Y6LojEPKcIGx/WAw== +--- +# GA4 Purchase Event + +Send a `purchase` event when an order is confirmed. + +| param | source | +|---|---| +| value | order.total | +| currency | order.currency | diff --git a/fixtures/signed/with-zk-attestation.md b/fixtures/signed/with-zk-attestation.md new file mode 100644 index 0000000..0f28c2a --- /dev/null +++ b/fixtures/signed/with-zk-attestation.md @@ -0,0 +1,32 @@ +--- +title: GA4 Purchase Event +resource: https://example.com/okf/ga4/purchase +description: Server-side GA4 purchase event mapping for the storefront. +tags: + - analytics + - ga4 +provenance: + spec_version: verifiable-okf/0.3 + issuer: did:key:z6MkneMkZqwqRiU5mJzSG3kDwzt9P8C59N4NGTfBLfSGE7c7 + content_hash: sha256:94ea40b78f963160edd233a2ad2aa10c5da2d945eb40e4059dd80399d4ccbd00 + valid_from: 2026-01-01T00:00:00Z + valid_until: 2027-01-01T00:00:00Z + attestations: + - claim: contains_no_pii + scheme: zk-groth16 + circuit: contains_no_pii + proof: ewogInBpX2EiOiBbCiAgIjE3NDYxOTM5NzMwNzgyODUyNDM4NzcwOTgwOTM4ODYyNDQ3Mjk4ODcwMjU3MTMyNzQxODk2Nzg2MDMyMDc2MjAxMTc3Mzc4MTkxNTk4IiwKICAiODYwNzQ4ODg1NDU2MzYyODcxOTczMjA1MzE5Mjc2OTU4NzE5MzYzNTg5MjY5Mzg0OTg2NDc5MzE5NTk5MjIzNDQ4NzI4Njk2NTczNiIsCiAgIjEiCiBdLAogInBpX2IiOiBbCiAgWwogICAiMTE3MjgxMzk5MTIxOTk5NzIzMzAwMjMxMzY4MDM2MDM2Njc0MjQ3MzI1ODcxNTM4NTEzMzE2ODA4MTM2MTIxNTMxNTUyMzI0MjA0NjQiLAogICAiMTEyMDAwMzg3MTUxNTQ1NzE3NjU2Njk2MDA1NzM0MjQ0NDI5OTg2MzY1MDE4NTg4NzE5ODYwOTU3NTMzODU5MTY3NjY0MjMzMzIyMzgiCiAgXSwKICBbCiAgICIxNTg5ODgyNzA5NzcxOTk1NDQ5MDI2NDY0NjkyMzEwNzU1MjQ2MzgxNzEyMjQ1MjE3NTQ4MDE5ODYzMzYwNzI5MDEzNzQ5MTQ3MDIzNCIsCiAgICIzOTMwOTQ4MDMxOTU3NjA3MzIxNTY0NDgwNzUzMDMwNzc5MjU2Njg2NDA3NjYxMDQzMTAyMjY4MTExNDY4ODkwNzE5NTMzMDAwNSIKICBdLAogIFsKICAgIjEiLAogICAiMCIKICBdCiBdLAogInBpX2MiOiBbCiAgIjIwNTY5NzM3NTU4MTc4NDc5MzM0MTAwMDEwOTE5MDgzMjMxMjM1MTE1NTM4MDExNjg0MzExOTk3NTQzOTk1Mjk1NzI2NzU1OTM4MzU0IiwKICAiNzQwNzExMzM5NzA3NjY0NzY4MDAwODExNDEyNDgxMzg2NDc2MzgzMzAzMjczNDA1Nzk2OTI0Nzc3NTcyNzk4ODE3MTMzNDUwNzI2MCIsCiAgIjEiCiBdLAogInByb3RvY29sIjogImdyb3RoMTYiLAogImN1cnZlIjogImJuMTI4Igp9 + public_inputs: + - "1" + signature: + scheme: ed25519 + value: dt9g6B7vZx5hM6j3BZnBJDu5F2d1OjHkoZ7ZkQ7i78Fx1CjzlezpZ5A4YM8ipISCHEF+m0PcM1jo8wPaN+sXAA== +--- +# GA4 Purchase Event + +Send a `purchase` event when an order is confirmed. + +| param | source | +|---|---| +| value | order.total | +| currency | order.currency | diff --git a/packages/attest-zk-groth16/package.json b/packages/attest-zk-groth16/package.json new file mode 100644 index 0000000..2ca98a1 --- /dev/null +++ b/packages/attest-zk-groth16/package.json @@ -0,0 +1,26 @@ +{ + "name": "@verifiable-okf/attest-zk-groth16", + "version": "0.1.0", + "description": "Verifiable OKF attestation verifier for the zk-groth16 scheme (Groth16/BN254 via snarkjs). Offline proof verification; bundles the contains_no_pii demo verification key.", + "license": "Apache-2.0", + "type": "module", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" } }, + "files": ["dist"], + "scripts": { + "build": "tsc -p tsconfig.json && cp -r src/vkeys dist/vkeys", + "test": "vitest run", + "lint": "tsc -p tsconfig.json --noEmit" + }, + "dependencies": { + "snarkjs": "^0.7.6" + }, + "devDependencies": { + "@verifiable-okf/verifier": "workspace:*", + "@verifiable-okf/signer": "workspace:*", + "@types/node": "^20.14.0", + "typescript": "^5.6.0", + "vitest": "^2.1.0" + } +} diff --git a/packages/attest-zk-groth16/src/index.ts b/packages/attest-zk-groth16/src/index.ts new file mode 100644 index 0000000..3add5b3 --- /dev/null +++ b/packages/attest-zk-groth16/src/index.ts @@ -0,0 +1,51 @@ +/** + * @verifiable-okf/attest-zk-groth16 — an AttestationVerifier for the + * `zk-groth16` scheme (Groth16 / BN254 via snarkjs). + * + * Proof verification is fully offline: it needs only the circuit's verification + * key (no proving key, no circom, no network). Pass this to the verifier's + * `attestationVerifiers` map to turn the §10 attestation path into real ZK + * checks. The `contains_no_pii` demo verification key is bundled. + * + * The attestation carries (SPEC §8): `scheme: "zk-groth16"`, `circuit` (id), + * `proof` (base64 of the Groth16 proof JSON), and `public_inputs` (signals). + */ +import { groth16 } from "snarkjs"; +import { createRequire } from "node:module"; + +const require = createRequire(import.meta.url); + +export type Vkey = unknown; + +/** Build a zk-groth16 AttestationVerifier from a map of circuit id → verification key. */ +export function createGroth16Verifier( + vkeys: Readonly>, +): (attestation: Record) => Promise { + return async (attestation) => { + const circuit = String(attestation["circuit"] ?? ""); + const vkey = vkeys[circuit]; + if (!vkey) return false; // unknown circuit → does not verify + const proofB64 = attestation["proof"]; + const publicSignals = attestation["public_inputs"]; + if (typeof proofB64 !== "string" || !Array.isArray(publicSignals)) return false; + let proof: unknown; + try { + proof = JSON.parse(Buffer.from(proofB64, "base64").toString("utf8")); + } catch { + return false; + } + try { + return await groth16.verify(vkey, publicSignals, proof); + } catch { + return false; + } + }; +} + +/** Verification keys bundled with this package (by circuit id). */ +export const BUNDLED_VKEYS: Readonly> = { + contains_no_pii: require("./vkeys/contains_no_pii.vkey.json"), +}; + +/** Default zk-groth16 verifier preloaded with the bundled demo circuit(s). */ +export const groth16AttestationVerifier = createGroth16Verifier(BUNDLED_VKEYS); diff --git a/packages/attest-zk-groth16/src/snarkjs.d.ts b/packages/attest-zk-groth16/src/snarkjs.d.ts new file mode 100644 index 0000000..80e7e83 --- /dev/null +++ b/packages/attest-zk-groth16/src/snarkjs.d.ts @@ -0,0 +1,5 @@ +declare module "snarkjs" { + export const groth16: { + verify(vkey: unknown, publicSignals: unknown, proof: unknown): Promise; + }; +} diff --git a/packages/attest-zk-groth16/src/vkeys/contains_no_pii.vkey.json b/packages/attest-zk-groth16/src/vkeys/contains_no_pii.vkey.json new file mode 100644 index 0000000..bddc3e6 --- /dev/null +++ b/packages/attest-zk-groth16/src/vkeys/contains_no_pii.vkey.json @@ -0,0 +1,94 @@ +{ + "protocol": "groth16", + "curve": "bn128", + "nPublic": 1, + "vk_alpha_1": [ + "1232894381328749349801999194020364750860714090412859425702737227783362329035", + "4436118752635877068544674281701685683067136631392057119874998162712106835950", + "1" + ], + "vk_beta_2": [ + [ + "14716936584645639048355760343770265219429753952539561837148583891266482232582", + "16016704211999055611191571745890472454130292775011844893044274766649904226588" + ], + [ + "9057080589359257783096967548757429701196839693892521700242482106975202632599", + "20533264667873917127608317601690877061735828347178346689977693657285325072488" + ], + [ + "1", + "0" + ] + ], + "vk_gamma_2": [ + [ + "10857046999023057135944570762232829481370756359578518086990519993285655852781", + "11559732032986387107991004021392285783925812861821192530917403151452391805634" + ], + [ + "8495653923123431417604973247489272438418190587263600148770280649306958101930", + "4082367875863433681332203403145435568316851327593401208105741076214120093531" + ], + [ + "1", + "0" + ] + ], + "vk_delta_2": [ + [ + "3474799006316917099767645587129093771951975749530518410992048528464400789842", + "14906175476459237866989995382063729370101320006270423677795159512995448203917" + ], + [ + "5379252983319465967809417952863222386238200287388003645473005624099013594517", + "18986786293210347468478260290195243983141901438725909310713432120103963521504" + ], + [ + "1", + "0" + ] + ], + "vk_alphabeta_12": [ + [ + [ + "403749106030442990619315241768518264979636829364042921852032020812672065137", + "7621931038878796889452159494924950157179135094573670140480507261568496685977" + ], + [ + "16865090028061755323054514475260851841876300676446183862568668824480223320175", + "18327880048007991543735184721865349268992505013333563818619430008549808266562" + ], + [ + "8787872532129923515649140272636460985886459682924130307132452782207431646038", + "13260321749514392872666733411862938534642858847009090275335091916896884689184" + ] + ], + [ + [ + "6387628840775407748790935730268315938700872123575627156397259578938427030883", + "16771398627571756483969946929083216360331205551563536906712676092481376278695" + ], + [ + "15792279552950039777871914493874300116271355423474748217406866705529595597418", + "16476731688131302025407637631205413052485691063167902404388883062051157327695" + ], + [ + "14261736778804873940795974174260233059211405548185751146935318338563960445767", + "6447326858417875907252629836795830459066202724682340898082801712486689440581" + ] + ] + ], + "IC": [ + [ + "18641926387932197056747795654697750247402624656485328984590349834099713095425", + "5797156867949200385824451310324450740426988220911984895467204416449729528236", + "1" + ], + [ + "566468554377789334102443174096780922474059205361865467335928056340967116619", + "19928558876800948858731875360898910993093008468686622797144379353445393047770", + "1" + ] + ] +} \ No newline at end of file diff --git a/packages/attest-zk-groth16/test/groth16.test.ts b/packages/attest-zk-groth16/test/groth16.test.ts new file mode 100644 index 0000000..7eb47dc --- /dev/null +++ b/packages/attest-zk-groth16/test/groth16.test.ts @@ -0,0 +1,55 @@ +import { describe, it, expect } from "vitest"; +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { verifyConcept } from "@verifiable-okf/verifier"; +import { + groth16AttestationVerifier, + createGroth16Verifier, + BUNDLED_VKEYS, +} from "../src/index.js"; + +const root = (p: string) => fileURLToPath(new URL(`../../../${p}`, import.meta.url)); +const NOW = new Date("2026-06-15T00:00:00Z"); +const withZk = readFileSync(root("fixtures/signed/with-zk-attestation.md")); + +const verifiers = { "zk-groth16": groth16AttestationVerifier }; +const policy = { requiredClaims: ["contains_no_pii"] }; + +describe("zk-groth16 attestation (VOKF-7, end-to-end)", () => { + it("verified: a real Groth16 proof for contains_no_pii passes the §10 attestation path", async () => { + const r = await verifyConcept(withZk, { now: NOW, policy, attestationVerifiers: verifiers }); + expect(r.status).toBe("verified"); + expect(r.reasons).toEqual([]); + }); + + it("attestation_failed: a validly-signed concept whose proof does not verify", async () => { + // Signature is valid (signed over the attestation), but the proof is for the + // wrong public signal, so the ZK check fails at the attestation step (not the + // signature step). Tampering the proof byte itself would instead break the + // signature first (signature_invalid) — see verify ordering, §10. + const badZk = readFileSync(root("fixtures/signed/with-bad-zk-attestation.md")); + const r = await verifyConcept(badZk, { now: NOW, policy, attestationVerifiers: verifiers }); + expect(r.status).toBe("failed"); + expect(r.reasons).toEqual(["attestation_failed"]); + }); + + it("verified when the attestation is not required by policy (carrier only)", async () => { + const r = await verifyConcept(withZk, { now: NOW, attestationVerifiers: verifiers }); + expect(r.status).toBe("verified"); + }); + + it("the verifier function: valid proof → true, unknown circuit → false", async () => { + const text = withZk.toString("utf8"); + const proof = text.match(/proof: (\S+)/)![1]!; + const att = { circuit: "contains_no_pii", scheme: "zk-groth16", proof, public_inputs: ["1"] }; + expect(await groth16AttestationVerifier(att)).toBe(true); + expect(await groth16AttestationVerifier({ ...att, circuit: "unknown_circuit" })).toBe(false); + }); + + it("rejects a proof for the wrong public signals", async () => { + const text = withZk.toString("utf8"); + const proof = text.match(/proof: (\S+)/)![1]!; + const v = createGroth16Verifier(BUNDLED_VKEYS); + expect(await v({ circuit: "contains_no_pii", proof, public_inputs: ["2"] })).toBe(false); + }); +}); diff --git a/packages/attest-zk-groth16/tsconfig.json b/packages/attest-zk-groth16/tsconfig.json new file mode 100644 index 0000000..e0d0985 --- /dev/null +++ b/packages/attest-zk-groth16/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { "outDir": "dist", "rootDir": "src", "resolveJsonModule": true }, + "include": ["src"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 81e1041..65fa920 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,6 +18,28 @@ importers: specifier: ^2.1.0 version: 2.1.9(@types/node@20.19.43) + packages/attest-zk-groth16: + dependencies: + snarkjs: + specifier: ^0.7.6 + version: 0.7.6 + devDependencies: + '@types/node': + specifier: ^20.14.0 + version: 20.19.43 + '@verifiable-okf/signer': + specifier: workspace:* + version: link:../signer + '@verifiable-okf/verifier': + specifier: workspace:* + version: link:../verifier + typescript: + specifier: ^5.6.0 + version: 5.9.3 + vitest: + specifier: ^2.1.0 + version: 2.1.9(@types/node@20.19.43) + packages/canon: dependencies: canonicalize: @@ -249,6 +271,12 @@ packages: cpu: [x64] os: [win32] + '@iden3/bigarray@0.0.2': + resolution: {integrity: sha512-Xzdyxqm1bOFF6pdIsiHLLl3HkSLjbhqJHVyqaTxXt3RqXBEnmsUmEW47H7VOi/ak7TdkRpNkxjyK5Zbkm+y52g==} + + '@iden3/binfileutils@0.0.12': + resolution: {integrity: sha512-naAmzuDufRIcoNfQ1d99d7hGHufLA3wZSibtr4dMe6ZeiOPV1KwOZWTJ1YVz4HbaWlpDuzVU72dS4ATQS4PXBQ==} + '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} @@ -468,6 +496,22 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + bfj@7.1.0: + resolution: {integrity: sha512-I6MMLkn+anzNdCUp9hMRyui1HaNEUCco50lxbvNS4+EyXg8lN3nJ48PjPWtbH8UVS9CuMoaKE9U2V3l29DaRQw==} + engines: {node: '>= 8.0.0'} + + bluebird@3.7.2: + resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} + + brace-expansion@2.1.1: + resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -484,6 +528,13 @@ packages: resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} engines: {node: '>= 16'} + check-types@11.2.3: + resolution: {integrity: sha512-+67P1GkJRaxQD6PKK0Et9DhwQB+vGg3PM5+aavopCpZT1lj9jeqfvpgTLAWErNj8qApkkmXlu/Ug74kmhagkXg==} + + circom_runtime@0.1.28: + resolution: {integrity: sha512-ACagpQ7zBRLKDl5xRZ4KpmYIcZDUjOiNRuxvXLqhnnlLSVY1Dbvh73TI853nqoR0oEbihtWmMSjgc5f+pXf/jQ==} + hasBin: true + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -497,6 +548,11 @@ packages: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} + ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} @@ -505,9 +561,32 @@ packages: engines: {node: '>=12'} hasBin: true + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + esprima@1.2.5: + resolution: {integrity: sha512-S9VbPDU0adFErpDai3qDkjq8+G05ONtKzcyNrPKg/ZKa+tf879nX2KexNU95b31UoTJjRLInNBHHHjFPoCd7lQ==} + engines: {node: '>=0.4.0'} + hasBin: true + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + expect-type@1.3.0: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} @@ -518,20 +597,51 @@ packages: fast-uri@3.1.2: resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} + fastfile@0.0.20: + resolution: {integrity: sha512-r5ZDbgImvVWCP0lA/cGNgQcZqR+aYdFx3u+CtJqUE510pBUVGMn4ulL/iRTI4tACTYsNJ736uzFxEBXesPAktA==} + + ffjavascript@0.3.0: + resolution: {integrity: sha512-l7sR5kmU3gRwDy8g0Z2tYBXy5ttmafRPFOqY7S6af5cq51JqJWt5eQ/lSR/rs2wQNbDYaYlQr5O+OSUf/oMLoQ==} + + ffjavascript@0.3.1: + resolution: {integrity: sha512-4PbK1WYodQtuF47D4pRI5KUg3Q392vuP5WjE1THSnceHdXwU3ijaoS0OqxTzLknCtz4Z2TtABzkBdBdMn3B/Aw==} + + filelist@1.0.6: + resolution: {integrity: sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + hoopy@0.1.4: + resolution: {integrity: sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==} + engines: {node: '>= 6.0.0'} + + jake@10.9.4: + resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} + engines: {node: '>=10'} + hasBin: true + json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + jsonpath@1.3.0: + resolution: {integrity: sha512-0kjkYHJBkAy50Z5QzArZ7udmvxrJzkpKYW27fiF//BrMY7TQibYLl+FYIXN2BiYmwMIVzSfD8aDRj6IzgBX2/w==} + + logplease@1.2.15: + resolution: {integrity: sha512-jLlHnlsPSJjpwUfcNyUxXCl33AYg2cHhIf9QhGL2T4iPT0XPB+xP1LRKFPgIg1M/sg9kAJvy94w9CzBNrfnstA==} + loupe@3.2.1: resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + minimatch@5.1.9: + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + engines: {node: '>=10'} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -554,6 +664,9 @@ packages: resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} + r1csfile@0.0.48: + resolution: {integrity: sha512-kHRkKUJNaor31l05f2+RFzvcH5XSa7OfEfd/l4hzjte6NL6fjRkSMfZ4BjySW9wmfdwPOtq3mXurzPvPGEf5Tw==} + require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} @@ -566,13 +679,24 @@ packages: siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + snarkjs@0.7.6: + resolution: {integrity: sha512-4uH1xA5JzVU5jaaWS2fXej3+RC6L5Erhr6INTJtUA27du4Elbh4VXCeeRjB4QiwL6N6y7SNKePw5prTxyEf4Zg==} + hasBin: true + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + static-eval@2.1.1: + resolution: {integrity: sha512-MgWpQ/ZjGieSVB3eOJVs4OA2LT/q1vx98KPCTTQPzq/aLr0YUXTsgryTXr4SLfR0ZfUUCiedM9n/ABeDIyy4mA==} + std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} @@ -594,6 +718,9 @@ packages: resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} engines: {node: '>=14.0.0'} + tryer@1.0.1: + resolution: {integrity: sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==} + turbo@2.9.18: resolution: {integrity: sha512-bwabv6PupzeavybzEoArBAkwq5fnzwf8OFnRtpHwnviFWuwJPFxtyH+aVp36TmIqK3aYYgtTJ3J0m2ysxxSzQg==} hasBin: true @@ -603,6 +730,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + underscore@1.13.6: + resolution: {integrity: sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==} + undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} @@ -667,6 +797,15 @@ packages: jsdom: optional: true + wasmbuilder@0.0.16: + resolution: {integrity: sha512-Qx3lEFqaVvp1cEYW7Bfi+ebRJrOiwz2Ieu7ZG2l7YyeSJIok/reEQCQCuicj/Y32ITIJuGIM9xZQppGx5LrQdA==} + + wasmcurves@0.2.2: + resolution: {integrity: sha512-JRY908NkmKjFl4ytnTu5ED6AwPD+8VJ9oc94kdq7h5bIwbj0L4TDJ69mG+2aLs2SoCmGfqIesMWTEJjtYsoQXQ==} + + web-worker@1.2.0: + resolution: {integrity: sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==} + why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} @@ -748,6 +887,13 @@ snapshots: '@esbuild/win32-x64@0.21.5': optional: true + '@iden3/bigarray@0.0.2': {} + + '@iden3/binfileutils@0.0.12': + dependencies: + fastfile: 0.0.20 + ffjavascript: 0.3.1 + '@jridgewell/sourcemap-codec@1.5.5': {} '@noble/curves@1.9.7': @@ -910,6 +1056,24 @@ snapshots: assertion-error@2.0.1: {} + async@3.2.6: {} + + balanced-match@1.0.2: {} + + bfj@7.1.0: + dependencies: + bluebird: 3.7.2 + check-types: 11.2.3 + hoopy: 0.1.4 + jsonpath: 1.3.0 + tryer: 1.0.1 + + bluebird@3.7.2: {} + + brace-expansion@2.1.1: + dependencies: + balanced-match: 1.0.2 + cac@6.7.14: {} canonicalize@2.1.0: {} @@ -924,12 +1088,22 @@ snapshots: check-error@2.1.3: {} + check-types@11.2.3: {} + + circom_runtime@0.1.28: + dependencies: + ffjavascript: 0.3.1 + debug@4.4.3: dependencies: ms: 2.1.3 deep-eql@5.0.2: {} + ejs@3.1.10: + dependencies: + jake: 10.9.4 + es-module-lexer@1.7.0: {} esbuild@0.21.5: @@ -958,27 +1132,81 @@ snapshots: '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + + esprima@1.2.5: {} + + esprima@4.0.1: {} + + estraverse@5.3.0: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.9 + esutils@2.0.3: {} + expect-type@1.3.0: {} fast-deep-equal@3.1.3: {} fast-uri@3.1.2: {} + fastfile@0.0.20: {} + + ffjavascript@0.3.0: + dependencies: + wasmbuilder: 0.0.16 + wasmcurves: 0.2.2 + web-worker: 1.2.0 + + ffjavascript@0.3.1: + dependencies: + wasmbuilder: 0.0.16 + wasmcurves: 0.2.2 + web-worker: 1.2.0 + + filelist@1.0.6: + dependencies: + minimatch: 5.1.9 + fsevents@2.3.3: optional: true + hoopy@0.1.4: {} + + jake@10.9.4: + dependencies: + async: 3.2.6 + filelist: 1.0.6 + picocolors: 1.1.1 + json-schema-traverse@1.0.0: {} + jsonpath@1.3.0: + dependencies: + esprima: 1.2.5 + static-eval: 2.1.1 + underscore: 1.13.6 + + logplease@1.2.15: {} + loupe@3.2.1: {} magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + minimatch@5.1.9: + dependencies: + brace-expansion: 2.1.1 + ms@2.1.3: {} nanoid@3.3.12: {} @@ -995,6 +1223,13 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + r1csfile@0.0.48: + dependencies: + '@iden3/bigarray': 0.0.2 + '@iden3/binfileutils': 0.0.12 + fastfile: 0.0.20 + ffjavascript: 0.3.0 + require-from-string@2.0.2: {} rollup@4.62.0: @@ -1030,10 +1265,29 @@ snapshots: siginfo@2.0.0: {} + snarkjs@0.7.6: + dependencies: + '@iden3/binfileutils': 0.0.12 + '@noble/hashes': 1.8.0 + bfj: 7.1.0 + circom_runtime: 0.1.28 + ejs: 3.1.10 + fastfile: 0.0.20 + ffjavascript: 0.3.1 + logplease: 1.2.15 + r1csfile: 0.0.48 + source-map-js@1.2.1: {} + source-map@0.6.1: + optional: true + stackback@0.0.2: {} + static-eval@2.1.1: + dependencies: + escodegen: 2.1.0 + std-env@3.10.0: {} tinybench@2.9.0: {} @@ -1046,6 +1300,8 @@ snapshots: tinyspy@3.0.2: {} + tryer@1.0.1: {} + turbo@2.9.18: optionalDependencies: '@turbo/darwin-64': 2.9.18 @@ -1057,6 +1313,8 @@ snapshots: typescript@5.9.3: {} + underscore@1.13.6: {} + undici-types@6.21.0: {} vite-node@2.1.9(@types/node@20.19.43): @@ -1121,6 +1379,14 @@ snapshots: - supports-color - terser + wasmbuilder@0.0.16: {} + + wasmcurves@0.2.2: + dependencies: + wasmbuilder: 0.0.16 + + web-worker@1.2.0: {} + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0