diff --git a/CHANGELOG.md b/CHANGELOG.md index 504b97d..c3ad4a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,47 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.6.0] - 2026-05-12 + +### Changed + +- **`@orbinum/circuits`** bumped `0.7.0` → `0.8.0` — the `disclosure.circom` circuit now performs **ECDH encryption on-circuit** over Baby Jubjub instead of revealing note fields as plaintext public signals. +- **`src/disclosure/index.ts`** — `generateDisclosureProof()` updated to match the new circuit interface: + - Three new required parameters: `auditorPkX: bigint`, `auditorPkY: bigint`, `r: bigint` (ephemeral scalar — must be random, `< Baby Jubjub suborder`). + - `buildCircuitInputs` is now synchronous — the off-circuit `buildPoseidon()` call (circomlibjs) is removed because the owner hash is now computed inside the circuit. + - Circuit inputs updated: `revealed_value`, `revealed_asset_id`, `revealed_owner_hash` replaced by `auditor_pk_x`, `auditor_pk_y`, `r`. + - Output `revealedData` replaced by `encryptedData` (see types). +- **`src/disclosure/types.ts`** — `DisclosureProofOutput.revealedData` replaced by `encryptedData`: + ``` + encryptedData: { + epkX: string; // ephemeral public key x (Baby Jubjub) + epkY: string; // ephemeral public key y (Baby Jubjub) + encValue: string; // encrypted note value (0 if not disclosed) + encAssetId: string; // encrypted asset ID (0 if not disclosed) + encOwnerHash: string;// encrypted Poseidon(owner_pubkey) (0 if not disclosed) + commitment: string; // note commitment (always present, not encrypted) + } + ``` + Public signal order updated: `[epk_x, epk_y, enc_value, enc_asset_id, enc_owner_hash, commitment, auditor_pk_x, auditor_pk_y]`. +- **`src/circuits/config.ts`** — `expectedPublicSignals` for `Disclosure` updated: 4 → 8. +- **`tests/circuits/config.test.ts`** — expectation updated to match new signal count. +- **`tests/disclosure/index.test.ts`** — tests rewritten for new ECDH-based signature, auditor key inputs, and `encryptedData` output shape. +- **`tests/generate/index.test.ts`** — disclosure test cases updated. + +### Removed + +- **`circomlibjs`** removed as a runtime import from `src/disclosure/index.ts` — off-circuit `Poseidon(owner_pubkey)` hashing is no longer required, as the circuit handles all encryption internally. + +### Breaking Changes + +- `generateDisclosureProof(value, ownerPubkey, blinding, assetId, commitment, mask, options?)` → `generateDisclosureProof(value, ownerPubkey, blinding, assetId, commitment, auditorPkX, auditorPkY, r, mask, options?)`. +- `DisclosureProofOutput.revealedData` is removed. Use `encryptedData` instead. The auditor decrypts offline: + ``` + shared = sk_A · epk (Baby Jubjub scalar multiplication) + plaintext_i = enc_i - Poseidon(shared.x, shared.y, i) mod BN254_P + ``` +- Public signal count for `Disclosure` circuit: 4 → 8. + ## [3.5.4] - 2026-05-08 ### Fixed diff --git a/package.json b/package.json index 98d60ec..2af0d52 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@orbinum/proof-generator", - "version": "3.5.4", + "version": "3.6.0", "description": "ZK-SNARK proof generator for Orbinum. Combines snarkjs (witness) with arkworks WASM (proof generation) to produce 128-byte Groth16 proofs.", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -39,7 +39,7 @@ "orbinum" ], "dependencies": { - "@orbinum/circuits": "0.7.0", + "@orbinum/circuits": "0.8.0", "@orbinum/groth16-proofs": "3.0.0", "circomlibjs": "0.1.7", "snarkjs": "0.7.6" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 606974c..2033b86 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@orbinum/circuits': - specifier: 0.7.0 - version: 0.7.0 + specifier: 0.8.0 + version: 0.8.0 '@orbinum/groth16-proofs': specifier: 3.0.0 version: 3.0.0 @@ -41,7 +41,7 @@ importers: version: 6.0.2 vitest: specifier: 4.1.3 - version: 4.1.3(@types/node@25.5.2)(@vitest/coverage-v8@4.1.3)(vite@8.0.10(@types/node@25.5.2)) + version: 4.1.3(@types/node@25.5.2)(@vitest/coverage-v8@4.1.3)(vite@8.0.12(@types/node@25.5.2)) packages: @@ -191,114 +191,114 @@ packages: resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} engines: {node: ^14.21.3 || >=16} - '@orbinum/circuits@0.7.0': - resolution: {integrity: sha512-Iwd+Gv9PM5++qgwgRkzKUvlA6CiG+h+zLi1XvJoXYgfJizyHN0WgrPPEu79BwQxuKczmXFWf9P9kQxTWCeLi/w==} + '@orbinum/circuits@0.8.0': + resolution: {integrity: sha512-8BslXDY3iSjumyj5VNC1vjm8nbuxenla4Wb0xPLd5OOK2LPsRWF88eC+ieZdNT81+WprTkAKqxCx+Kb3ZOkm9w==} engines: {node: '>=18.0.0'} '@orbinum/groth16-proofs@3.0.0': resolution: {integrity: sha512-1I8/wMkCKn5zpS4qVoaXnUfNiuHoo7Ym8hlG6avaijGtITLY6BzLEcwjBjVHZ2vpI+CjL557ORfyjuGrFDzJSw==} engines: {node: '>=18.0.0'} - '@oxc-project/types@0.127.0': - resolution: {integrity: sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==} + '@oxc-project/types@0.129.0': + resolution: {integrity: sha512-3oz8m3FGdr2nDXVqmFUw7jolKliC4MoyXYIG2c7gpjBnzUWQpUGIYcXYKxTdTi+N2jusvt610ckTMkxdwHkYEg==} - '@rolldown/binding-android-arm64@1.0.0-rc.17': - resolution: {integrity: sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==} + '@rolldown/binding-android-arm64@1.0.0': + resolution: {integrity: sha512-TWMZnRLMe63C2Lhyicviu7ZHaU4kxa6PS3rofvc9GmcvptzNN11BcfQ4Sl7MwTOsisQoa2keB/EBdNCAnUo8vA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.0-rc.17': - resolution: {integrity: sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==} + '@rolldown/binding-darwin-arm64@1.0.0': + resolution: {integrity: sha512-6XcD+8k0gPVItNagEw78/qqcBDwKcwDYS8V2hRmVsfUSIrd8cWe/CBvRDI5toqFyPfj+FJr6t8U6Xj2P2prEew==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.0-rc.17': - resolution: {integrity: sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==} + '@rolldown/binding-darwin-x64@1.0.0': + resolution: {integrity: sha512-iN/tWVXRQDWvmZlKdceP1Dwug9GDpEymhb9p4xnEe6zvCg5lFmzVljl+1qR1NVx3yfGpr2Na+CuLmv5IU8uzfQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.0-rc.17': - resolution: {integrity: sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==} + '@rolldown/binding-freebsd-x64@1.0.0': + resolution: {integrity: sha512-jjQMDvvwSOuhOwMszD/klSOjyWMM3zI64hWTj9KT5x4MxRbZAf+7vLQ6qouRhtsLVFHr3f0ILaJAfgENPiQdAQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17': - resolution: {integrity: sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.0': + resolution: {integrity: sha512-d//Dtg2x6/m3mbV64yUGNnDGNZaDGRpDLLNGerHQUVObuNaIQaaDp25yUiqGXtHEXX+NP2d0wAlmKgpYgIAJ2A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17': - resolution: {integrity: sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==} + '@rolldown/binding-linux-arm64-gnu@1.0.0': + resolution: {integrity: sha512-n7Ofp0mx+aB2cC+Sdy5YtMnXtY9lchnHbY+3Yt0uq9JsWQExf4f5Whu0tK0R8Jdc9S6RchTHjIFY7uc92puOVQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.17': - resolution: {integrity: sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==} + '@rolldown/binding-linux-arm64-musl@1.0.0': + resolution: {integrity: sha512-EIVjy2cgd7uuMMo94FVkBp7F6DhcZAUwNURkSG3RwUmvAXR6s0ISxM81U+IydcZByPG0pZIHsf1b6kTxoFDgJA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17': - resolution: {integrity: sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==} + '@rolldown/binding-linux-ppc64-gnu@1.0.0': + resolution: {integrity: sha512-JEwwOPcwTLAcpDQlqSmjEmfs63xJnSiUNIGvLcDLUHCWK4XowpS/7c7tUsUH6uT/ct6bMUTdXKfI8967FYj6mg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17': - resolution: {integrity: sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==} + '@rolldown/binding-linux-s390x-gnu@1.0.0': + resolution: {integrity: sha512-0wjCFhLrihtAubnT9iA0N++0pSV0z5Hg7tNGdNJ4RFaINceHadoF+kiFGyY1qSSNVIAZtLotG8Ju1bgDPkjnFA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.17': - resolution: {integrity: sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==} + '@rolldown/binding-linux-x64-gnu@1.0.0': + resolution: {integrity: sha512-Dfn7iak9BcMMePxcoJfpSbWqnEyrp/dRF63/8qW/eHBdOZov6x5aShLLEYGYdIeSJ6vMLK/XCVB+lGIxm41bQA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.0.0-rc.17': - resolution: {integrity: sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==} + '@rolldown/binding-linux-x64-musl@1.0.0': + resolution: {integrity: sha512-5/utzzDmD/pD/bmuaUcbTf/sZYy0aztwIVlfpoW1fTjCZ0BaPOMVWGZL1zvgxyi7ZIVYWlxKONHmSbHuiOh8Jw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@rolldown/binding-openharmony-arm64@1.0.0-rc.17': - resolution: {integrity: sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==} + '@rolldown/binding-openharmony-arm64@1.0.0': + resolution: {integrity: sha512-ouJs8VcUomfLfpbUECqFMRqdV4x6aeAK3MA4m6vTrJJjKyWTV5KnxZx7Jd9G+GlDaQQxubcba00x16OyJ1meig==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.0-rc.17': - resolution: {integrity: sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==} + '@rolldown/binding-wasm32-wasi@1.0.0': + resolution: {integrity: sha512-E+oHKGiDA+lsKMmFtffDDw91EryDT7uJocrIuCHqhm6bCTM6xFK+3gaCkYOHfPwQr0cCNarSM2xaELoQDz9jJg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17': - resolution: {integrity: sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==} + '@rolldown/binding-win32-arm64-msvc@1.0.0': + resolution: {integrity: sha512-yYK02n8Rngo+gbm1y6G0+7jk1sJ/2Wt7K0me0Y7k/ErBpyf+LJ2gFpqWVTcRV1rUepBlQRmpgWkTQCiiwrK0Ow==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.17': - resolution: {integrity: sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==} + '@rolldown/binding-win32-x64-msvc@1.0.0': + resolution: {integrity: sha512-14bpChMahXRRXiTwahSl+zzHPW6qQTXtkMuJBFlbo+pqSAews2d4BdCSHfrJ/MBsCZtpmTafsY+1QhBzitcmdg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@rolldown/pluginutils@1.0.0-rc.17': - resolution: {integrity: sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==} + '@rolldown/pluginutils@1.0.0': + resolution: {integrity: sha512-aKs/3GSWyV0mrhNmt/96/Z3yczC3yvrzYATCiCXQebBsGyYzjNdUphRVLeJQ67ySKVXRfMxt2lm12pmXvbPFQQ==} '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -315,8 +315,8 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} '@types/node@25.5.2': resolution: {integrity: sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==} @@ -699,8 +699,8 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} - rolldown@1.0.0-rc.17: - resolution: {integrity: sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==} + rolldown@1.0.0: + resolution: {integrity: sha512-yD986aXDESFGS95spT1LAv0jssywP4npMEjmMHyN2/5+eE8qQJUype2AaKkRiLgBgyD0LFlubwAht7VmY8rGoA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -710,8 +710,8 @@ packages: scrypt-js@3.0.1: resolution: {integrity: sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==} - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + semver@7.8.0: + resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} engines: {node: '>=10'} hasBin: true @@ -781,13 +781,13 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - vite@8.0.10: - resolution: {integrity: sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==} + vite@8.0.12: + resolution: {integrity: sha512-w2dDofOWv2QB09ZITZBsvKTVAlYvPR4IAmrY/v0ir9KvLs0xybR7i48wxhM1/oyBWO34wPns+bPGw5ZrZqDpZg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: '@types/node': ^20.19.0 || >=22.12.0 - '@vitejs/devtools': ^0.1.0 + '@vitejs/devtools': ^0.1.18 esbuild: ^0.27.0 || ^0.28.0 jiti: '>=1.21.0' less: ^4.0.0 @@ -1204,62 +1204,62 @@ snapshots: '@noble/hashes@1.8.0': {} - '@orbinum/circuits@0.7.0': {} + '@orbinum/circuits@0.8.0': {} '@orbinum/groth16-proofs@3.0.0': {} - '@oxc-project/types@0.127.0': {} + '@oxc-project/types@0.129.0': {} - '@rolldown/binding-android-arm64@1.0.0-rc.17': + '@rolldown/binding-android-arm64@1.0.0': optional: true - '@rolldown/binding-darwin-arm64@1.0.0-rc.17': + '@rolldown/binding-darwin-arm64@1.0.0': optional: true - '@rolldown/binding-darwin-x64@1.0.0-rc.17': + '@rolldown/binding-darwin-x64@1.0.0': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-rc.17': + '@rolldown/binding-freebsd-x64@1.0.0': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17': + '@rolldown/binding-linux-arm-gnueabihf@1.0.0': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17': + '@rolldown/binding-linux-arm64-gnu@1.0.0': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.17': + '@rolldown/binding-linux-arm64-musl@1.0.0': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17': + '@rolldown/binding-linux-ppc64-gnu@1.0.0': optional: true - '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17': + '@rolldown/binding-linux-s390x-gnu@1.0.0': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.17': + '@rolldown/binding-linux-x64-gnu@1.0.0': optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-rc.17': + '@rolldown/binding-linux-x64-musl@1.0.0': optional: true - '@rolldown/binding-openharmony-arm64@1.0.0-rc.17': + '@rolldown/binding-openharmony-arm64@1.0.0': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-rc.17': + '@rolldown/binding-wasm32-wasi@1.0.0': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17': + '@rolldown/binding-win32-arm64-msvc@1.0.0': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.17': + '@rolldown/binding-win32-x64-msvc@1.0.0': optional: true - '@rolldown/pluginutils@1.0.0-rc.17': {} + '@rolldown/pluginutils@1.0.0': {} '@standard-schema/spec@1.1.0': {} @@ -1277,7 +1277,7 @@ snapshots: '@types/deep-eql@4.0.2': {} - '@types/estree@1.0.8': {} + '@types/estree@1.0.9': {} '@types/node@25.5.2': dependencies: @@ -1297,7 +1297,7 @@ snapshots: obug: 2.1.1 std-env: 4.1.0 tinyrainbow: 3.1.0 - vitest: 4.1.3(@types/node@25.5.2)(@vitest/coverage-v8@4.1.3)(vite@8.0.10(@types/node@25.5.2)) + vitest: 4.1.3(@types/node@25.5.2)(@vitest/coverage-v8@4.1.3)(vite@8.0.12(@types/node@25.5.2)) '@vitest/expect@4.1.3': dependencies: @@ -1308,13 +1308,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.3(vite@8.0.10(@types/node@25.5.2))': + '@vitest/mocker@4.1.3(vite@8.0.12(@types/node@25.5.2))': dependencies: '@vitest/spy': 4.1.3 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.0.10(@types/node@25.5.2) + vite: 8.0.12(@types/node@25.5.2) '@vitest/pretty-format@4.1.3': dependencies: @@ -1453,7 +1453,7 @@ snapshots: estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 esutils@2.0.3: {} @@ -1637,7 +1637,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.7.4 + semver: 7.8.0 minimalistic-assert@1.0.1: {} @@ -1684,32 +1684,32 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 - rolldown@1.0.0-rc.17: + rolldown@1.0.0: dependencies: - '@oxc-project/types': 0.127.0 - '@rolldown/pluginutils': 1.0.0-rc.17 + '@oxc-project/types': 0.129.0 + '@rolldown/pluginutils': 1.0.0 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-rc.17 - '@rolldown/binding-darwin-arm64': 1.0.0-rc.17 - '@rolldown/binding-darwin-x64': 1.0.0-rc.17 - '@rolldown/binding-freebsd-x64': 1.0.0-rc.17 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.17 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.17 - '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.17 - '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.17 - '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.17 - '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.17 - '@rolldown/binding-linux-x64-musl': 1.0.0-rc.17 - '@rolldown/binding-openharmony-arm64': 1.0.0-rc.17 - '@rolldown/binding-wasm32-wasi': 1.0.0-rc.17 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.17 - '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.17 + '@rolldown/binding-android-arm64': 1.0.0 + '@rolldown/binding-darwin-arm64': 1.0.0 + '@rolldown/binding-darwin-x64': 1.0.0 + '@rolldown/binding-freebsd-x64': 1.0.0 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0 + '@rolldown/binding-linux-arm64-gnu': 1.0.0 + '@rolldown/binding-linux-arm64-musl': 1.0.0 + '@rolldown/binding-linux-ppc64-gnu': 1.0.0 + '@rolldown/binding-linux-s390x-gnu': 1.0.0 + '@rolldown/binding-linux-x64-gnu': 1.0.0 + '@rolldown/binding-linux-x64-musl': 1.0.0 + '@rolldown/binding-openharmony-arm64': 1.0.0 + '@rolldown/binding-wasm32-wasi': 1.0.0 + '@rolldown/binding-win32-arm64-msvc': 1.0.0 + '@rolldown/binding-win32-x64-msvc': 1.0.0 safe-buffer@5.2.1: {} scrypt-js@3.0.1: {} - semver@7.7.4: {} + semver@7.8.0: {} siginfo@2.0.0: {} @@ -1770,21 +1770,21 @@ snapshots: util-deprecate@1.0.2: {} - vite@8.0.10(@types/node@25.5.2): + vite@8.0.12(@types/node@25.5.2): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 postcss: 8.5.14 - rolldown: 1.0.0-rc.17 + rolldown: 1.0.0 tinyglobby: 0.2.16 optionalDependencies: '@types/node': 25.5.2 fsevents: 2.3.3 - vitest@4.1.3(@types/node@25.5.2)(@vitest/coverage-v8@4.1.3)(vite@8.0.10(@types/node@25.5.2)): + vitest@4.1.3(@types/node@25.5.2)(@vitest/coverage-v8@4.1.3)(vite@8.0.12(@types/node@25.5.2)): dependencies: '@vitest/expect': 4.1.3 - '@vitest/mocker': 4.1.3(vite@8.0.10(@types/node@25.5.2)) + '@vitest/mocker': 4.1.3(vite@8.0.12(@types/node@25.5.2)) '@vitest/pretty-format': 4.1.3 '@vitest/runner': 4.1.3 '@vitest/snapshot': 4.1.3 @@ -1801,7 +1801,7 @@ snapshots: tinyexec: 1.1.2 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vite: 8.0.10(@types/node@25.5.2) + vite: 8.0.12(@types/node@25.5.2) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 25.5.2 diff --git a/src/circuits/config.ts b/src/circuits/config.ts index 6302395..0a884e4 100644 --- a/src/circuits/config.ts +++ b/src/circuits/config.ts @@ -24,7 +24,8 @@ function getExpectedPublicSignals(circuitType: CircuitType): number { // [merkle_root, nullifiers[2], commitments[2], asset_id, fee] return 7; case CircuitType.Disclosure: - return 4; + // outputs: [epk_x, epk_y, enc_value, enc_asset_id, enc_owner_hash] + inputs: [commitment, auditor_pk_x, auditor_pk_y] + return 8; case CircuitType.PrivateLink: return 2; default: diff --git a/src/disclosure/index.ts b/src/disclosure/index.ts index f5fff9d..cb4fdfd 100644 --- a/src/disclosure/index.ts +++ b/src/disclosure/index.ts @@ -3,20 +3,22 @@ * * Generates Groth16 proofs for the `disclosure.circom` circuit. * - * ## Circuit Public Inputs (in order) - * 0. commitment – Note commitment (always revealed) - * 1. revealed_value – Note value, or 0 if not disclosed - * 2. revealed_asset_id – Asset ID, or 0 if not disclosed - * 3. revealed_owner_hash – Poseidon(owner_pubkey), or 0 if not disclosed + * ## Circuit Public Inputs (in order, from snarkjs publicSignals) + * 0. epk_x – Ephemeral public key x (Baby Jubjub) + * 1. epk_y – Ephemeral public key y (Baby Jubjub) + * 2. enc_value – Encrypted note value (0 if not disclosed) + * 3. enc_asset_id – Encrypted asset ID (0 if not disclosed) + * 4. enc_owner_hash – Encrypted Poseidon(owner_pubkey) (0 if not disclosed) + * 5. commitment – Note commitment (always present) + * 6. auditor_pk_x – Auditor Baby Jubjub public key x + * 7. auditor_pk_y – Auditor Baby Jubjub public key y * * @module @orbinum/proof-generator/disclosure */ -// @ts-ignore - circomlibjs has no type declarations -import { buildPoseidon } from 'circomlibjs'; import { generateProof } from '../generate'; import { CircuitType, ProofResult } from '../circuits/types'; -import { bigIntToHex, hexSignalToBigInt, u64ToFieldStr } from '../utils'; +import { u64ToFieldStr } from '../utils'; import { ArtifactProvider } from '../providers/interface'; import { DisclosureMask, DisclosureProofOutput } from './types'; @@ -32,26 +34,22 @@ export type { DisclosureMask, DisclosureProofOutput } from './types'; * All values are decimal BigInt strings — the native format that snarkjs * expects for scalar field elements. */ -async function buildCircuitInputs( +function buildCircuitInputs( value: bigint, ownerPubkey: bigint, blinding: bigint, assetId: bigint, commitment: bigint, + auditorPkX: bigint, + auditorPkY: bigint, + r: bigint, mask: DisclosureMask -): Promise> { - const poseidon = await buildPoseidon(); - const F = poseidon.F; - - // viewing_key = Poseidon(owner_pubkey) — matches the circom constraint - const viewingKey: bigint = F.toObject(poseidon([ownerPubkey])); - +): Record { return { // Public inputs commitment: commitment.toString(), - revealed_value: (mask.discloseValue ? value : 0n).toString(), - revealed_asset_id: (mask.discloseAssetId ? assetId : 0n).toString(), - revealed_owner_hash: (mask.discloseOwner ? viewingKey : 0n).toString(), + auditor_pk_x: auditorPkX.toString(), + auditor_pk_y: auditorPkY.toString(), // Private inputs value: u64ToFieldStr(value), asset_id: u64ToFieldStr(assetId), @@ -60,6 +58,7 @@ async function buildCircuitInputs( disclose_value: mask.discloseValue ? '1' : '0', disclose_asset_id: mask.discloseAssetId ? '1' : '0', disclose_owner: mask.discloseOwner ? '1' : '0', + r: r.toString(), }; } @@ -70,11 +69,19 @@ async function buildCircuitInputs( /** * Generate a selective disclosure Groth16 proof. * + * The circuit encrypts the selected fields on-circuit using ECDH over Baby + * Jubjub. The auditor decrypts offline with their spending key: + * shared = sk_A · epk + * plaintext_i = enc_i - Poseidon(shared.x, shared.y, i) (mod BN254_P) + * * @param value – Note value as BigInt (u64 field element) * @param ownerPubkey – Owner public key as BigInt (BN254 scalar) * @param blinding – Blinding factor as BigInt * @param assetId – Asset ID as BigInt (u32) * @param commitment – Note commitment as BigInt + * @param auditorPkX – Auditor Baby Jubjub public key x-coordinate + * @param auditorPkY – Auditor Baby Jubjub public key y-coordinate + * @param r – Ephemeral scalar (must be random, < Baby Jubjub order) * @param mask – Which fields to disclose to the auditor * @param options – Optional artifact provider override */ @@ -84,6 +91,9 @@ export async function generateDisclosureProof( blinding: bigint, assetId: bigint, commitment: bigint, + auditorPkX: bigint, + auditorPkY: bigint, + r: bigint, mask: DisclosureMask, options: { provider?: ArtifactProvider; verbose?: boolean } = {} ): Promise { @@ -93,21 +103,39 @@ export async function generateDisclosureProof( ); } - const inputs = await buildCircuitInputs(value, ownerPubkey, blinding, assetId, commitment, mask); + const inputs = buildCircuitInputs( + value, + ownerPubkey, + blinding, + assetId, + commitment, + auditorPkX, + auditorPkY, + r, + mask + ); - // Public signal order from disclosure.circom: - // [0] commitment [1] revealed_value [2] revealed_asset_id [3] revealed_owner_hash + // Public signal order from disclosure.circom (outputs first, then public inputs): + // [0] epk_x [1] epk_y [2] enc_value [3] enc_asset_id [4] enc_owner_hash + // [5] commitment [6] auditor_pk_x [7] auditor_pk_y const result: ProofResult = await generateProof(CircuitType.Disclosure, inputs, { provider: options.provider, verbose: options.verbose, }); - const [sigCommitment, sigValue, sigAssetId, sigOwnerHash] = result.publicSignals; + const [sigEpkX, sigEpkY, sigEncValue, sigEncAssetId, sigEncOwnerHash, sigCommitment] = + result.publicSignals; - const revealedData: DisclosureProofOutput['revealedData'] = { commitment: sigCommitment }; - if (mask.discloseValue) revealedData.value = hexSignalToBigInt(sigValue).toString(10); - if (mask.discloseAssetId) revealedData.assetId = Number(hexSignalToBigInt(sigAssetId)); - if (mask.discloseOwner) revealedData.ownerHash = bigIntToHex(hexSignalToBigInt(sigOwnerHash)); - - return { proof: result.proof, publicSignals: result.publicSignals, revealedData }; + return { + proof: result.proof, + publicSignals: result.publicSignals, + encryptedData: { + epkX: sigEpkX, + epkY: sigEpkY, + encValue: sigEncValue, + encAssetId: sigEncAssetId, + encOwnerHash: sigEncOwnerHash, + commitment: sigCommitment, + }, + }; } diff --git a/src/disclosure/types.ts b/src/disclosure/types.ts index 45cb537..9c71a6d 100644 --- a/src/disclosure/types.ts +++ b/src/disclosure/types.ts @@ -19,18 +19,27 @@ export interface DisclosureProofOutput { proof: string; /** * Raw public signals in hex (0x-prefixed, 32 bytes each). - * Order: [commitment, revealed_value, revealed_asset_id, revealed_owner_hash] + * Order: [epk_x, epk_y, enc_value, enc_asset_id, enc_owner_hash, commitment, auditor_pk_x, auditor_pk_y] */ publicSignals: string[]; - /** Human-readable revealed data */ - revealedData: { - /** Revealed note value as decimal string, or undefined if not disclosed */ - value?: string; - /** Revealed asset ID as number, or undefined if not disclosed */ - assetId?: number; - /** Revealed owner hash as 0x-prefixed hex, or undefined if not disclosed */ - ownerHash?: string; - /** Note commitment (always present) */ + /** + * ECDH-encrypted disclosure data. + * The auditor decrypts offline using their Baby Jubjub spending key: + * shared = sk_A · epk + * plaintext_i = enc_i - Poseidon(shared.x, shared.y, i) (mod BN254_P) + */ + encryptedData: { + /** Ephemeral public key x-coordinate (Baby Jubjub) */ + epkX: string; + /** Ephemeral public key y-coordinate (Baby Jubjub) */ + epkY: string; + /** Encrypted note value (0 if not disclosed) */ + encValue: string; + /** Encrypted asset ID (0 if not disclosed) */ + encAssetId: string; + /** Encrypted owner hash Poseidon(owner_pubkey) (0 if not disclosed) */ + encOwnerHash: string; + /** Note commitment (always present, not encrypted) */ commitment: string; }; } diff --git a/tests/circuits/config.test.ts b/tests/circuits/config.test.ts index e5078ac..78dd8f2 100644 --- a/tests/circuits/config.test.ts +++ b/tests/circuits/config.test.ts @@ -29,7 +29,8 @@ describe('getCircuitConfig', () => { expect(config.wasmPath).toBe('disclosure.wasm'); expect(config.zkeyPath).toBe('disclosure_pk.zkey'); expect(config.provingKeyPath).toBe('disclosure_pk.ark'); - expect(config.expectedPublicSignals).toBe(4); + // [epk_x, epk_y, enc_value, enc_asset_id, enc_owner_hash, commitment, auditor_pk_x, auditor_pk_y] + expect(config.expectedPublicSignals).toBe(8); }); it('returns correct config for PrivateLink', () => { diff --git a/tests/disclosure/index.test.ts b/tests/disclosure/index.test.ts index 35e9cd4..7a1e65b 100644 --- a/tests/disclosure/index.test.ts +++ b/tests/disclosure/index.test.ts @@ -1,7 +1,7 @@ /** * Tests: disclosure/index.ts — generateDisclosureProof() * - * generateProof and circomlibjs are mocked so no real circuit artifacts + * generateProof is mocked so no real circuit artifacts * or cryptographic operations are required. */ @@ -10,28 +10,21 @@ import { generateDisclosureProof } from '../../src/disclosure'; import type { DisclosureMask } from '../../src/disclosure'; import type { ArtifactProvider } from '../../src/providers/interface'; -// ─── Mock circomlibjs ───────────────────────────────────────────────────────── - -vi.mock('circomlibjs', () => { - const poseidon = Object.assign(vi.fn().mockReturnValue('stub'), { - F: { toObject: vi.fn().mockReturnValue(999n) }, - }); - return { - buildPoseidon: vi.fn().mockResolvedValue(poseidon), - }; -}); - // ─── Mock generateProof ─────────────────────────────────────────────────────── vi.mock('../../src/generate', () => ({ generateProof: vi.fn().mockResolvedValue({ proof: '0x' + 'aa'.repeat(128), publicSignals: [ - // commitment, revealed_value, revealed_asset_id, revealed_owner_hash - '0x' + '01'.repeat(32), - '0x' + '02'.repeat(32), - '0x' + '03'.repeat(32), - '0x' + '04'.repeat(32), + // epk_x, epk_y, enc_value, enc_asset_id, enc_owner_hash, commitment, auditor_pk_x, auditor_pk_y + '0x' + '01'.repeat(32), // epk_x + '0x' + '02'.repeat(32), // epk_y + '0x' + '03'.repeat(32), // enc_value + '0x' + '04'.repeat(32), // enc_asset_id + '0x' + '05'.repeat(32), // enc_owner_hash + '0x' + '06'.repeat(32), // commitment + '0x' + '07'.repeat(32), // auditor_pk_x + '0x' + '08'.repeat(32), // auditor_pk_y ], circuitType: 'disclosure', }), @@ -44,6 +37,9 @@ const OWNER_PUBKEY = 12345n; const BLINDING = 999n; const ASSET_ID = 1n; const COMMITMENT = 42n; +const AUDITOR_PK_X = 111n; +const AUDITOR_PK_Y = 222n; +const R = 333n; const MASK_ALL: DisclosureMask = { discloseValue: true, @@ -63,137 +59,156 @@ describe('generateDisclosureProof', () => { vi.clearAllMocks(); }); - it('returns proof, publicSignals and revealedData', async () => { + it('returns proof, publicSignals and encryptedData', async () => { const result = await generateDisclosureProof( VALUE, OWNER_PUBKEY, BLINDING, ASSET_ID, COMMITMENT, + AUDITOR_PK_X, + AUDITOR_PK_Y, + R, MASK_ALL, { provider: MOCK_PROVIDER } ); expect(result.proof).toBe('0x' + 'aa'.repeat(128)); - expect(result.publicSignals).toHaveLength(4); - expect(result.revealedData.commitment).toBeDefined(); - }); - - it('populates revealedData.value when discloseValue is true', async () => { - const result = await generateDisclosureProof( - VALUE, - OWNER_PUBKEY, - BLINDING, - ASSET_ID, - COMMITMENT, - MASK_ALL, - { provider: MOCK_PROVIDER } - ); - expect(result.revealedData.value).toBeDefined(); - expect(typeof result.revealedData.value).toBe('string'); + expect(result.publicSignals).toHaveLength(8); + expect(result.encryptedData.commitment).toBeDefined(); }); - it('populates revealedData.assetId when discloseAssetId is true', async () => { + it('encryptedData contains epk and encrypted fields', async () => { const result = await generateDisclosureProof( VALUE, OWNER_PUBKEY, BLINDING, ASSET_ID, COMMITMENT, + AUDITOR_PK_X, + AUDITOR_PK_Y, + R, MASK_ALL, { provider: MOCK_PROVIDER } ); - expect(result.revealedData.assetId).toBeDefined(); - expect(typeof result.revealedData.assetId).toBe('number'); + expect(result.encryptedData.epkX).toBeDefined(); + expect(result.encryptedData.epkY).toBeDefined(); + expect(result.encryptedData.encValue).toBeDefined(); + expect(result.encryptedData.encAssetId).toBeDefined(); + expect(result.encryptedData.encOwnerHash).toBeDefined(); }); - it('populates revealedData.ownerHash when discloseOwner is true', async () => { + it('encryptedData maps correctly to publicSignals positions', async () => { const result = await generateDisclosureProof( VALUE, OWNER_PUBKEY, BLINDING, ASSET_ID, COMMITMENT, + AUDITOR_PK_X, + AUDITOR_PK_Y, + R, MASK_ALL, { provider: MOCK_PROVIDER } ); - expect(result.revealedData.ownerHash).toBeDefined(); - expect(result.revealedData.ownerHash).toMatch(/^0x/); + const s = result.publicSignals; + expect(result.encryptedData.epkX).toBe(s[0]); + expect(result.encryptedData.epkY).toBe(s[1]); + expect(result.encryptedData.encValue).toBe(s[2]); + expect(result.encryptedData.encAssetId).toBe(s[3]); + expect(result.encryptedData.encOwnerHash).toBe(s[4]); + expect(result.encryptedData.commitment).toBe(s[5]); }); - it('omits revealedData.value when discloseValue is false', async () => { - const mask: DisclosureMask = { + it('throws when all mask fields are false', async () => { + const emptyMask: DisclosureMask = { discloseValue: false, - discloseAssetId: true, + discloseAssetId: false, discloseOwner: false, }; - const result = await generateDisclosureProof( + await expect( + generateDisclosureProof( + VALUE, + OWNER_PUBKEY, + BLINDING, + ASSET_ID, + COMMITMENT, + AUDITOR_PK_X, + AUDITOR_PK_Y, + R, + emptyMask + ) + ).rejects.toThrow('DisclosureMask'); + }); + + it('passes the provider option through to generateProof', async () => { + const { generateProof } = await import('../../src/generate'); + await generateDisclosureProof( VALUE, OWNER_PUBKEY, BLINDING, ASSET_ID, COMMITMENT, - mask, + AUDITOR_PK_X, + AUDITOR_PK_Y, + R, + MASK_ALL, { provider: MOCK_PROVIDER } ); - expect(result.revealedData.value).toBeUndefined(); + expect(generateProof).toHaveBeenCalledWith( + 'disclosure', + expect.any(Object), + expect.objectContaining({ provider: MOCK_PROVIDER }) + ); }); - it('omits revealedData.assetId when discloseAssetId is false', async () => { - const mask: DisclosureMask = { - discloseValue: true, - discloseAssetId: false, - discloseOwner: false, - }; - const result = await generateDisclosureProof( + it('passes auditor pk and r as circuit inputs', async () => { + const { generateProof } = await import('../../src/generate'); + await generateDisclosureProof( VALUE, OWNER_PUBKEY, BLINDING, ASSET_ID, COMMITMENT, - mask, + AUDITOR_PK_X, + AUDITOR_PK_Y, + R, + MASK_ALL, { provider: MOCK_PROVIDER } ); - expect(result.revealedData.assetId).toBeUndefined(); + expect(generateProof).toHaveBeenCalledWith( + 'disclosure', + expect.objectContaining({ + auditor_pk_x: AUDITOR_PK_X.toString(), + auditor_pk_y: AUDITOR_PK_Y.toString(), + r: R.toString(), + }), + expect.any(Object) + ); }); - it('omits revealedData.ownerHash when discloseOwner is false', async () => { + it('sets disclose_* inputs correctly from mask', async () => { + const { generateProof } = await import('../../src/generate'); const mask: DisclosureMask = { discloseValue: true, discloseAssetId: false, discloseOwner: false, }; - const result = await generateDisclosureProof( + await generateDisclosureProof( VALUE, OWNER_PUBKEY, BLINDING, ASSET_ID, COMMITMENT, + AUDITOR_PK_X, + AUDITOR_PK_Y, + R, mask, { provider: MOCK_PROVIDER } ); - expect(result.revealedData.ownerHash).toBeUndefined(); - }); - - it('throws when all mask fields are false', async () => { - const emptyMask: DisclosureMask = { - discloseValue: false, - discloseAssetId: false, - discloseOwner: false, - }; - await expect( - generateDisclosureProof(VALUE, OWNER_PUBKEY, BLINDING, ASSET_ID, COMMITMENT, emptyMask) - ).rejects.toThrow('DisclosureMask'); - }); - - it('passes the provider option through to generateProof', async () => { - const { generateProof } = await import('../../src/generate'); - await generateDisclosureProof(VALUE, OWNER_PUBKEY, BLINDING, ASSET_ID, COMMITMENT, MASK_ALL, { - provider: MOCK_PROVIDER, - }); expect(generateProof).toHaveBeenCalledWith( 'disclosure', - expect.any(Object), - expect.objectContaining({ provider: MOCK_PROVIDER }) + expect.objectContaining({ disclose_value: '1', disclose_asset_id: '0', disclose_owner: '0' }), + expect.any(Object) ); }); }); diff --git a/tests/generate/index.test.ts b/tests/generate/index.test.ts index 115c768..0e2f6f2 100644 --- a/tests/generate/index.test.ts +++ b/tests/generate/index.test.ts @@ -25,6 +25,10 @@ vi.mock('@orbinum/groth16-proofs', () => ({ '0x' + '02'.repeat(32), '0x' + '03'.repeat(32), '0x' + '04'.repeat(32), + '0x' + '05'.repeat(32), + '0x' + '06'.repeat(32), + '0x' + '07'.repeat(32), + '0x' + '08'.repeat(32), ], }) ), @@ -45,12 +49,12 @@ vi.mock('snarkjs', () => ({ ], pi_c: ['7', '8', '1'], }, - publicSignals: ['10', '20', '30', '40'], + publicSignals: ['10', '20', '30', '40', '50', '60', '70', '80'], }), }, wtns: { calculate: vi.fn().mockResolvedValue(undefined), - exportJson: vi.fn().mockResolvedValue([1n, 10n, 20n, 30n, 40n]), + exportJson: vi.fn().mockResolvedValue([1n, 10n, 20n, 30n, 40n, 50n, 60n, 70n, 80n]), }, })); @@ -90,7 +94,7 @@ describe('generateProof — snarkjs backend (default)', () => { it('returns ProofResult with proof and publicSignals', async () => { const result = await generateProof(CircuitType.Disclosure, VALID_INPUTS, { provider }); expect(result.proof).toMatch(/^0x[0-9a-f]+$/i); - expect(result.publicSignals).toHaveLength(4); + expect(result.publicSignals).toHaveLength(8); expect(result.circuitType).toBe(CircuitType.Disclosure); }); @@ -153,7 +157,7 @@ describe('generateProof — arkworks backend', () => { backend: 'arkworks', }); expect(result.proof).toMatch(/^0x[0-9a-f]+$/i); - expect(result.publicSignals).toHaveLength(4); + expect(result.publicSignals).toHaveLength(8); expect(result.circuitType).toBe(CircuitType.Disclosure); }); @@ -178,7 +182,7 @@ describe('generateProof — arkworks backend', () => { const [numSigs, witnessJson, pkBytes] = ( wasm.generate_proof_from_decimal_wasm as ReturnType ).mock.calls[0]; - expect(numSigs).toBe(4); + expect(numSigs).toBe(8); const parsed = JSON.parse(witnessJson); expect(Array.isArray(parsed)).toBe(true); expect(parsed[0]).toBe('1');