@zkproofport-app/sdk 0.2.12 → 0.2.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ProofportSDK.d.ts +31 -0
- package/dist/circuits.d.ts +14 -0
- package/dist/circuits.esm.js +23 -1
- package/dist/circuits.esm.js.map +1 -1
- package/dist/circuits.js +23 -0
- package/dist/circuits.js.map +1 -1
- package/dist/circuits.mjs +23 -1
- package/dist/circuits.mjs.map +1 -1
- package/dist/constants.d.ts +7 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.esm.js +418 -147
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +419 -146
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +418 -147
- package/dist/index.mjs.map +1 -1
- package/dist/offChainVerifier.d.ts +73 -0
- package/package.json +8 -3
package/dist/index.esm.js
CHANGED
|
@@ -138,6 +138,13 @@ const VERIFIER_ABI = [
|
|
|
138
138
|
* Supported networks:
|
|
139
139
|
* - 84532: Base Sepolia (testnet)
|
|
140
140
|
* - 8453: Base Mainnet (production)
|
|
141
|
+
* - 91342: GIWA Sepolia (testnet) — where the GIWA account circuit verifies
|
|
142
|
+
*
|
|
143
|
+
* A chain missing from this map is not a quiet degradation: `getDefaultProvider`
|
|
144
|
+
* throws "No RPC endpoint configured for chain N". That is what a caller saw
|
|
145
|
+
* when verifying a GIWA proof on chain 91342 — the verifier contract had been
|
|
146
|
+
* deployed and the proof was valid, but the SDK had no way to reach the chain
|
|
147
|
+
* to ask. Deploying a verifier to a new network means adding it here too.
|
|
141
148
|
*
|
|
142
149
|
* @example
|
|
143
150
|
* ```typescript
|
|
@@ -149,6 +156,7 @@ const VERIFIER_ABI = [
|
|
|
149
156
|
const RPC_ENDPOINTS = {
|
|
150
157
|
84532: 'https://sepolia.base.org', // Base Sepolia
|
|
151
158
|
8453: 'https://mainnet.base.org', // Base Mainnet
|
|
159
|
+
91342: 'https://sepolia-rpc.giwa.io', // GIWA Sepolia
|
|
152
160
|
};
|
|
153
161
|
/**
|
|
154
162
|
* Default proof request expiration time in milliseconds.
|
|
@@ -4254,6 +4262,383 @@ function reconstructBytes32FromFields(fields) {
|
|
|
4254
4262
|
return '0x' + bytes;
|
|
4255
4263
|
}
|
|
4256
4264
|
|
|
4265
|
+
/**
|
|
4266
|
+
* Canonical ZKProofport circuit identifiers.
|
|
4267
|
+
*
|
|
4268
|
+
* This module is the single source of truth for circuit IDs. Every other layer
|
|
4269
|
+
* — the mobile app, the demo, the relay, server-side provers — reads its list
|
|
4270
|
+
* from here instead of keeping a private copy that silently drifts.
|
|
4271
|
+
*
|
|
4272
|
+
* It has **no imports and no dependencies**, so it can be pulled in on its own:
|
|
4273
|
+
*
|
|
4274
|
+
* ```typescript
|
|
4275
|
+
* import { CIRCUIT_IDS, isCircuitId } from '@zkproofport-app/sdk/circuits';
|
|
4276
|
+
* ```
|
|
4277
|
+
*
|
|
4278
|
+
* The same names are also re-exported from the package root
|
|
4279
|
+
* (`@zkproofport-app/sdk`) for callers that already import the SDK.
|
|
4280
|
+
*
|
|
4281
|
+
* ## Identifiers are canonical and case-sensitive
|
|
4282
|
+
*
|
|
4283
|
+
* Each ID is the circuit's `name` in its `Nargo.toml`, verbatim. The format is
|
|
4284
|
+
* lowercase with underscores. Hyphenated spellings (`coinbase-kyc`,
|
|
4285
|
+
* `mdl-kr-age`) are directory or UI route names in other repositories — they
|
|
4286
|
+
* are **not** circuit IDs, and passing one produces a nullifier mismatch or a
|
|
4287
|
+
* failed on-chain lookup rather than a clear error.
|
|
4288
|
+
*/
|
|
4289
|
+
/**
|
|
4290
|
+
* Canonical circuit identifiers, keyed by a stable constant name.
|
|
4291
|
+
*
|
|
4292
|
+
* Prefer these constants over string literals so a rename is a compile error
|
|
4293
|
+
* rather than a runtime surprise.
|
|
4294
|
+
*
|
|
4295
|
+
* @example
|
|
4296
|
+
* ```typescript
|
|
4297
|
+
* import { CIRCUIT_IDS } from '@zkproofport-app/sdk/circuits';
|
|
4298
|
+
*
|
|
4299
|
+
* const relay = await sdk.createRelayRequest(CIRCUIT_IDS.COINBASE_ATTESTATION, {
|
|
4300
|
+
* scope: 'myapp.com',
|
|
4301
|
+
* });
|
|
4302
|
+
* ```
|
|
4303
|
+
*/
|
|
4304
|
+
const CIRCUIT_IDS = Object.freeze({
|
|
4305
|
+
/** Coinbase KYC attestation. Officially supported. */
|
|
4306
|
+
COINBASE_ATTESTATION: 'coinbase_attestation',
|
|
4307
|
+
/** Coinbase country attestation (inclusion / exclusion). Officially supported. */
|
|
4308
|
+
COINBASE_COUNTRY_ATTESTATION: 'coinbase_country_attestation',
|
|
4309
|
+
/** OIDC email-domain attestation. Officially supported. */
|
|
4310
|
+
OIDC_DOMAIN_ATTESTATION: 'oidc_domain_attestation',
|
|
4311
|
+
/** GIWA attestation. Planned — not officially supported yet. */
|
|
4312
|
+
GIWA_ATTESTATION: 'giwa_attestation',
|
|
4313
|
+
/** Korea Mobile ID ownership. Planned — not officially supported yet. */
|
|
4314
|
+
MDL_KR_OWNERSHIP: 'mdl_kr_ownership',
|
|
4315
|
+
/** Korea Mobile ID age threshold. Planned — not officially supported yet. */
|
|
4316
|
+
MDL_KR_AGE: 'mdl_kr_age',
|
|
4317
|
+
/** Korea Mobile ID si/do region. Planned — not officially supported yet. */
|
|
4318
|
+
MDL_KR_REGION: 'mdl_kr_region',
|
|
4319
|
+
});
|
|
4320
|
+
/**
|
|
4321
|
+
* Support status for every circuit.
|
|
4322
|
+
*
|
|
4323
|
+
* The record is exhaustive over {@link CircuitId}: adding an identifier without
|
|
4324
|
+
* giving it a status is a compile error, so this list cannot drift from
|
|
4325
|
+
* {@link CIRCUIT_IDS}.
|
|
4326
|
+
*
|
|
4327
|
+
* @example
|
|
4328
|
+
* ```typescript
|
|
4329
|
+
* import { CIRCUIT_SUPPORT_STATUS } from '@zkproofport-app/sdk/circuits';
|
|
4330
|
+
*
|
|
4331
|
+
* CIRCUIT_SUPPORT_STATUS.coinbase_attestation; // 'supported'
|
|
4332
|
+
* CIRCUIT_SUPPORT_STATUS.mdl_kr_age; // 'planned'
|
|
4333
|
+
* ```
|
|
4334
|
+
*/
|
|
4335
|
+
const CIRCUIT_SUPPORT_STATUS = Object.freeze({
|
|
4336
|
+
coinbase_attestation: 'supported',
|
|
4337
|
+
coinbase_country_attestation: 'supported',
|
|
4338
|
+
oidc_domain_attestation: 'supported',
|
|
4339
|
+
giwa_attestation: 'planned',
|
|
4340
|
+
mdl_kr_ownership: 'planned',
|
|
4341
|
+
mdl_kr_age: 'planned',
|
|
4342
|
+
mdl_kr_region: 'planned',
|
|
4343
|
+
});
|
|
4344
|
+
/**
|
|
4345
|
+
* Where each circuit's verification key lives inside the `zkproofport/circuits`
|
|
4346
|
+
* repository, relative to its root.
|
|
4347
|
+
*
|
|
4348
|
+
* Off-chain verification needs the verification key and nothing else — no
|
|
4349
|
+
* circuit bytecode, no witness. The key is public by nature: it is what anyone
|
|
4350
|
+
* checking a proof must already have, and the Solidity verifiers deployed
|
|
4351
|
+
* on-chain embed the same key.
|
|
4352
|
+
*
|
|
4353
|
+
* Exhaustive over {@link CircuitId} on purpose. A new circuit whose key path is
|
|
4354
|
+
* not listed here fails to compile rather than failing at verification time,
|
|
4355
|
+
* where the only symptom would be a proof that cannot be checked.
|
|
4356
|
+
*/
|
|
4357
|
+
const CIRCUIT_VK_PATHS = Object.freeze({
|
|
4358
|
+
coinbase_attestation: 'coinbase-attestation/target/vk',
|
|
4359
|
+
coinbase_country_attestation: 'coinbase-country-attestation/target/vk',
|
|
4360
|
+
oidc_domain_attestation: 'oidc-domain-attestation/target/vk',
|
|
4361
|
+
giwa_attestation: 'giwa-attestation/target/vk',
|
|
4362
|
+
mdl_kr_ownership: 'mdl/kr-ownership/target/vk',
|
|
4363
|
+
mdl_kr_age: 'mdl/kr-age/target/vk',
|
|
4364
|
+
mdl_kr_region: 'mdl/kr-region/target/vk',
|
|
4365
|
+
});
|
|
4366
|
+
/**
|
|
4367
|
+
* Every canonical circuit identifier, in declaration order.
|
|
4368
|
+
*
|
|
4369
|
+
* Includes circuits that are still `planned`. Use {@link SUPPORTED_CIRCUIT_IDS}
|
|
4370
|
+
* when you only want the ones that are officially supported today.
|
|
4371
|
+
*/
|
|
4372
|
+
const ALL_CIRCUIT_IDS = Object.freeze(Object.values(CIRCUIT_IDS));
|
|
4373
|
+
/**
|
|
4374
|
+
* Circuits that are officially supported today: the two Coinbase circuits and
|
|
4375
|
+
* OIDC domain attestation.
|
|
4376
|
+
*
|
|
4377
|
+
* Derived from {@link CIRCUIT_SUPPORT_STATUS}, so it cannot fall out of sync.
|
|
4378
|
+
*/
|
|
4379
|
+
const SUPPORTED_CIRCUIT_IDS = Object.freeze(ALL_CIRCUIT_IDS.filter((id) => CIRCUIT_SUPPORT_STATUS[id] === 'supported'));
|
|
4380
|
+
/**
|
|
4381
|
+
* Circuits whose identifiers are reserved but which are not officially
|
|
4382
|
+
* supported yet: GIWA attestation and the three Korea Mobile ID circuits.
|
|
4383
|
+
*
|
|
4384
|
+
* Derived from {@link CIRCUIT_SUPPORT_STATUS}, so it cannot fall out of sync.
|
|
4385
|
+
*/
|
|
4386
|
+
const PLANNED_CIRCUIT_IDS = Object.freeze(ALL_CIRCUIT_IDS.filter((id) => CIRCUIT_SUPPORT_STATUS[id] === 'planned'));
|
|
4387
|
+
/**
|
|
4388
|
+
* Narrows an unknown value to a canonical circuit identifier.
|
|
4389
|
+
*
|
|
4390
|
+
* Returns `false` for hyphenated route names such as `'coinbase-kyc'`, which
|
|
4391
|
+
* are not circuit IDs.
|
|
4392
|
+
*
|
|
4393
|
+
* @example
|
|
4394
|
+
* ```typescript
|
|
4395
|
+
* isCircuitId('coinbase_attestation'); // true
|
|
4396
|
+
* isCircuitId('coinbase-kyc'); // false
|
|
4397
|
+
* ```
|
|
4398
|
+
*/
|
|
4399
|
+
function isCircuitId(value) {
|
|
4400
|
+
return typeof value === 'string' && ALL_CIRCUIT_IDS.includes(value);
|
|
4401
|
+
}
|
|
4402
|
+
/**
|
|
4403
|
+
* Narrows an unknown value to a circuit identifier that is officially supported
|
|
4404
|
+
* today. Planned circuits return `false`.
|
|
4405
|
+
*
|
|
4406
|
+
* @example
|
|
4407
|
+
* ```typescript
|
|
4408
|
+
* isSupportedCircuitId('oidc_domain_attestation'); // true
|
|
4409
|
+
* isSupportedCircuitId('mdl_kr_age'); // false — planned
|
|
4410
|
+
* ```
|
|
4411
|
+
*/
|
|
4412
|
+
function isSupportedCircuitId(value) {
|
|
4413
|
+
return isCircuitId(value) && CIRCUIT_SUPPORT_STATUS[value] === 'supported';
|
|
4414
|
+
}
|
|
4415
|
+
/**
|
|
4416
|
+
* Returns the support status of a circuit.
|
|
4417
|
+
*
|
|
4418
|
+
* @param circuit - A canonical circuit identifier.
|
|
4419
|
+
* @throws Error when the value is not a canonical circuit identifier.
|
|
4420
|
+
*
|
|
4421
|
+
* @example
|
|
4422
|
+
* ```typescript
|
|
4423
|
+
* getCircuitSupportStatus('giwa_attestation'); // 'planned'
|
|
4424
|
+
* ```
|
|
4425
|
+
*/
|
|
4426
|
+
function getCircuitSupportStatus(circuit) {
|
|
4427
|
+
if (!isCircuitId(circuit)) {
|
|
4428
|
+
throw new Error(`Unknown circuit '${String(circuit)}'. Expected one of: ${ALL_CIRCUIT_IDS.join(', ')}`);
|
|
4429
|
+
}
|
|
4430
|
+
return CIRCUIT_SUPPORT_STATUS[circuit];
|
|
4431
|
+
}
|
|
4432
|
+
|
|
4433
|
+
/**
|
|
4434
|
+
* Off-chain proof verification — checking a proof against its verification key,
|
|
4435
|
+
* with no chain, no RPC, and no gas.
|
|
4436
|
+
*
|
|
4437
|
+
* WHY THIS EXISTS. The SDK had only `verifyProofOnChain`, and every caller that
|
|
4438
|
+
* wanted an off-chain check called that instead and relabelled the result. The
|
|
4439
|
+
* demo page did exactly this from its first commit: pressing "Off-Chain Verify"
|
|
4440
|
+
* ran an on-chain verification and printed "Off-Chain Verification Passed!".
|
|
4441
|
+
* The answer was true and the label was a lie, which is the worst combination —
|
|
4442
|
+
* nothing looked broken. The mobile app has always verified off-chain properly
|
|
4443
|
+
* (through mopro's native Barretenberg); it was only the JavaScript side that
|
|
4444
|
+
* had no such thing.
|
|
4445
|
+
*
|
|
4446
|
+
* HOW IT WORKS. `bb` proofs are checked by `acirVerifyUltraKeccakHonk(proof,
|
|
4447
|
+
* vk)` — the keccak variant, matching how these proofs are produced. Two things
|
|
4448
|
+
* have to line up:
|
|
4449
|
+
*
|
|
4450
|
+
* The version. The verification key format is not stable across bb releases:
|
|
4451
|
+
* the pinned build emits a 1816-byte key where bb 0.87.0 and 1.2.0 emit 1760.
|
|
4452
|
+
* `@aztec/bb.js@1.0.0-nightly.20250723` is the WASM build of the same bb the
|
|
4453
|
+
* circuits were compiled with. A different one will reject valid proofs.
|
|
4454
|
+
*
|
|
4455
|
+
* The oracle hash. Proofs are generated with `--oracle_hash keccak` so the
|
|
4456
|
+
* Solidity verifiers can check them, so the poseidon-flavoured verify — which
|
|
4457
|
+
* is what `BarretenbergVerifier.verifyUltraHonkProof` uses — says false for a
|
|
4458
|
+
* perfectly good proof.
|
|
4459
|
+
*
|
|
4460
|
+
* THE PROOF ARRIVES IN TWO PIECES. The mobile app splits what Barretenberg
|
|
4461
|
+
* produced into `proof` and `publicInputs` before sending it, because the
|
|
4462
|
+
* Solidity verifier takes them separately. Verifying needs them joined back, in
|
|
4463
|
+
* that order, which is what `reconstructHonkProof` does.
|
|
4464
|
+
*/
|
|
4465
|
+
/**
|
|
4466
|
+
* Where verification keys are published.
|
|
4467
|
+
*
|
|
4468
|
+
* Not configurable, deliberately: a caller who could point this somewhere else
|
|
4469
|
+
* could point it at a key of their choosing, and "the proof verified" would
|
|
4470
|
+
* then mean nothing. Same repository the mobile app downloads circuit data
|
|
4471
|
+
* from.
|
|
4472
|
+
*/
|
|
4473
|
+
const CIRCUITS_VK_BASE = 'https://raw.githubusercontent.com/zkproofport/circuits/main';
|
|
4474
|
+
/** How long a fetched verification key is reused. Keys change only on a recompile. */
|
|
4475
|
+
const VK_CACHE_TTL_MS = 60 * 60 * 1000;
|
|
4476
|
+
const vkCache = new Map();
|
|
4477
|
+
/** @internal Hex string (with or without 0x) to bytes. */
|
|
4478
|
+
function hexToBytes(hex) {
|
|
4479
|
+
const clean = hex.startsWith('0x') || hex.startsWith('0X') ? hex.slice(2) : hex;
|
|
4480
|
+
if (clean.length === 0)
|
|
4481
|
+
return new Uint8Array(0);
|
|
4482
|
+
if (clean.length % 2 !== 0) {
|
|
4483
|
+
throw new Error(`Hex string has an odd length (${clean.length}); it cannot be bytes`);
|
|
4484
|
+
}
|
|
4485
|
+
if (!/^[0-9a-fA-F]*$/.test(clean)) {
|
|
4486
|
+
throw new Error('Hex string contains non-hex characters');
|
|
4487
|
+
}
|
|
4488
|
+
const out = new Uint8Array(clean.length / 2);
|
|
4489
|
+
for (let i = 0; i < out.length; i++) {
|
|
4490
|
+
out[i] = parseInt(clean.substr(i * 2, 2), 16);
|
|
4491
|
+
}
|
|
4492
|
+
return out;
|
|
4493
|
+
}
|
|
4494
|
+
/**
|
|
4495
|
+
* @internal Public inputs as the flat byte run Barretenberg expects: each one a
|
|
4496
|
+
* 32-byte big-endian field element, concatenated in order.
|
|
4497
|
+
*
|
|
4498
|
+
* The relay carries them as hex strings that are USUALLY already 32 bytes, but
|
|
4499
|
+
* a value that happens to have leading zeros can arrive short (`0x01`). Left-
|
|
4500
|
+
* padding here rather than trusting the sender is the difference between a
|
|
4501
|
+
* verification that works and one that fails on roughly one proof in a hundred,
|
|
4502
|
+
* for no reason the caller can see.
|
|
4503
|
+
*/
|
|
4504
|
+
function packPublicInputs(publicInputs) {
|
|
4505
|
+
const out = new Uint8Array(publicInputs.length * 32);
|
|
4506
|
+
publicInputs.forEach((input, i) => {
|
|
4507
|
+
const bytes = hexToBytes(input);
|
|
4508
|
+
if (bytes.length > 32) {
|
|
4509
|
+
throw new Error(`Public input ${i} is ${bytes.length} bytes; a field element is at most 32`);
|
|
4510
|
+
}
|
|
4511
|
+
out.set(bytes, i * 32 + (32 - bytes.length));
|
|
4512
|
+
});
|
|
4513
|
+
return out;
|
|
4514
|
+
}
|
|
4515
|
+
/**
|
|
4516
|
+
* Fetch a circuit's verification key, with a small cache.
|
|
4517
|
+
*
|
|
4518
|
+
* @internal
|
|
4519
|
+
*/
|
|
4520
|
+
async function fetchVerificationKey(circuit, timeoutMs) {
|
|
4521
|
+
const cached = vkCache.get(circuit);
|
|
4522
|
+
if (cached && Date.now() - cached.at < VK_CACHE_TTL_MS)
|
|
4523
|
+
return cached.vk;
|
|
4524
|
+
const vkPath = CIRCUIT_VK_PATHS[circuit];
|
|
4525
|
+
if (!vkPath) {
|
|
4526
|
+
throw new Error(`No verification key path for circuit '${circuit}'. ` +
|
|
4527
|
+
`Add it to CIRCUIT_VK_PATHS, or pass verificationKey directly.`);
|
|
4528
|
+
}
|
|
4529
|
+
const url = `${CIRCUITS_VK_BASE}/${vkPath}`;
|
|
4530
|
+
const controller = new AbortController();
|
|
4531
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
4532
|
+
try {
|
|
4533
|
+
const resp = await fetch(url, { signal: controller.signal });
|
|
4534
|
+
if (!resp.ok) {
|
|
4535
|
+
throw new Error(`Could not fetch the verification key (HTTP ${resp.status}) from ${url}`);
|
|
4536
|
+
}
|
|
4537
|
+
const vk = new Uint8Array(await resp.arrayBuffer());
|
|
4538
|
+
// A 404 page or an HTML redirect arrives as a successful response full of
|
|
4539
|
+
// text, and would reach Barretenberg as a "verification key" that fails for
|
|
4540
|
+
// reasons no one could guess from the message.
|
|
4541
|
+
if (vk.length < 512) {
|
|
4542
|
+
throw new Error(`The verification key at ${url} is ${vk.length} bytes, far too small to be one. ` +
|
|
4543
|
+
`The path is probably wrong and something else was served.`);
|
|
4544
|
+
}
|
|
4545
|
+
vkCache.set(circuit, { vk, at: Date.now() });
|
|
4546
|
+
return vk;
|
|
4547
|
+
}
|
|
4548
|
+
finally {
|
|
4549
|
+
clearTimeout(timer);
|
|
4550
|
+
}
|
|
4551
|
+
}
|
|
4552
|
+
/**
|
|
4553
|
+
* Verify a proof off-chain, against its verification key.
|
|
4554
|
+
*
|
|
4555
|
+
* Needs `@aztec/bb.js` at the pinned version. It is an optional peer dependency
|
|
4556
|
+
* — the WASM is large and most callers verify on-chain — so the failure to have
|
|
4557
|
+
* it installed says so plainly rather than surfacing as a module-not-found.
|
|
4558
|
+
*
|
|
4559
|
+
* @param circuit - Canonical circuit identifier
|
|
4560
|
+
* @param proof - Proof bytes as hex, WITHOUT public inputs (as the relay sends it)
|
|
4561
|
+
* @param publicInputs - Public inputs as hex strings, in circuit order
|
|
4562
|
+
* @param options - Optional verification key and timeout
|
|
4563
|
+
* @returns `{ valid }`, or `{ valid: false, error }` when the check could not run
|
|
4564
|
+
*
|
|
4565
|
+
* @example
|
|
4566
|
+
* ```typescript
|
|
4567
|
+
* const result = await verifyProofOffChain(
|
|
4568
|
+
* 'coinbase_attestation',
|
|
4569
|
+
* response.proof,
|
|
4570
|
+
* response.publicInputs
|
|
4571
|
+
* );
|
|
4572
|
+
* if (result.valid) console.log('Proof is valid, no chain involved');
|
|
4573
|
+
* ```
|
|
4574
|
+
*/
|
|
4575
|
+
async function verifyProofOffChain(circuit, proof, publicInputs, options = {}) {
|
|
4576
|
+
const timeoutMs = options.timeoutMs ?? 30000;
|
|
4577
|
+
let bb;
|
|
4578
|
+
try {
|
|
4579
|
+
bb = await import('@aztec/bb.js');
|
|
4580
|
+
}
|
|
4581
|
+
catch {
|
|
4582
|
+
return {
|
|
4583
|
+
valid: false,
|
|
4584
|
+
error: 'Off-chain verification needs @aztec/bb.js. Install the pinned version: ' +
|
|
4585
|
+
'npm install @aztec/bb.js@1.0.0-nightly.20250723',
|
|
4586
|
+
};
|
|
4587
|
+
}
|
|
4588
|
+
let api;
|
|
4589
|
+
try {
|
|
4590
|
+
const proofBytes = hexToBytes(proof);
|
|
4591
|
+
if (proofBytes.length === 0) {
|
|
4592
|
+
return { valid: false, error: 'Proof is empty' };
|
|
4593
|
+
}
|
|
4594
|
+
const publicInputBytes = packPublicInputs(publicInputs);
|
|
4595
|
+
const vk = options.verificationKey ?? (await fetchVerificationKey(circuit, timeoutMs));
|
|
4596
|
+
// Public inputs first, then the proof — the order Barretenberg wrote them in
|
|
4597
|
+
// before the mobile app split them apart for the Solidity verifier.
|
|
4598
|
+
const full = bb.reconstructHonkProof(publicInputBytes, proofBytes);
|
|
4599
|
+
api = (await bb.Barretenberg.new({ threads: 1 }));
|
|
4600
|
+
// THE VERIFY MUST MATCH HOW THE PROOF WAS MADE, AND THERE ARE FOUR OF THEM.
|
|
4601
|
+
//
|
|
4602
|
+
// `scripts/build.sh` in the circuits repo proves with `--oracle_hash keccak`
|
|
4603
|
+
// and ZK on — so the only entry point that answers correctly is the one that
|
|
4604
|
+
// is BOTH. Measured against a real proof of the Korea Mobile ID age circuit,
|
|
4605
|
+
// freshly generated against its own key:
|
|
4606
|
+
//
|
|
4607
|
+
// acirVerifyUltraKeccakZkHonk true ← the right one
|
|
4608
|
+
// acirVerifyUltraKeccakHonk TRAPS with a bare "unreachable"
|
|
4609
|
+
// acirVerifyUltraZKHonk false (poseidon: wrong oracle hash)
|
|
4610
|
+
//
|
|
4611
|
+
// The middle row is why this took a while: picking keccak-without-ZK does
|
|
4612
|
+
// not return false, it kills the WebAssembly instance with a message that
|
|
4613
|
+
// names nothing. It looks identical to a malformed proof, to a mismatched
|
|
4614
|
+
// key, and to a missing SRS.
|
|
4615
|
+
//
|
|
4616
|
+
// Two more things that produce that same trap, both required:
|
|
4617
|
+
// initSRSForCircuitSize must be called before the first verify.
|
|
4618
|
+
// The key must be a RawBuffer — a plain Uint8Array is marshalled as a
|
|
4619
|
+
// length-prefixed vector, so its length is read as key material.
|
|
4620
|
+
// Both are what bb.js's own BarretenbergVerifier does before verifying.
|
|
4621
|
+
await api.initSRSForCircuitSize(0);
|
|
4622
|
+
const valid = await api.acirVerifyUltraKeccakZkHonk(full, new bb.RawBuffer(vk));
|
|
4623
|
+
return { valid };
|
|
4624
|
+
}
|
|
4625
|
+
catch (error) {
|
|
4626
|
+
return { valid: false, error: error instanceof Error ? error.message : String(error) };
|
|
4627
|
+
}
|
|
4628
|
+
finally {
|
|
4629
|
+
// The WASM instance holds worker threads; leaving them running keeps a Node
|
|
4630
|
+
// process alive after the check is done.
|
|
4631
|
+
if (api) {
|
|
4632
|
+
try {
|
|
4633
|
+
await api.destroy();
|
|
4634
|
+
}
|
|
4635
|
+
catch {
|
|
4636
|
+
/* nothing useful to do if teardown fails */
|
|
4637
|
+
}
|
|
4638
|
+
}
|
|
4639
|
+
}
|
|
4640
|
+
}
|
|
4641
|
+
|
|
4257
4642
|
/**
|
|
4258
4643
|
* Proofport SDK - Main class
|
|
4259
4644
|
*/
|
|
@@ -4900,6 +5285,38 @@ class ProofportSDK {
|
|
|
4900
5285
|
};
|
|
4901
5286
|
return verifyProofOnChain(response.circuit, parsedProof, providerOrSigner, customVerifier, responseVerifier);
|
|
4902
5287
|
}
|
|
5288
|
+
/**
|
|
5289
|
+
* Verifies a proof response off-chain, against the circuit's verification key.
|
|
5290
|
+
*
|
|
5291
|
+
* No chain, no RPC, no gas, and no verifier contract — so this works for a
|
|
5292
|
+
* circuit whose verifier has not been deployed anywhere, and it does not
|
|
5293
|
+
* depend on a network being reachable. What it does not give you is the
|
|
5294
|
+
* chain's own opinion: use {@link verifyResponseOnChain} when the answer has
|
|
5295
|
+
* to be one a contract will agree with.
|
|
5296
|
+
*
|
|
5297
|
+
* Requires the optional peer dependency:
|
|
5298
|
+
* `npm install @aztec/bb.js@1.0.0-nightly.20250723`. The version is exact —
|
|
5299
|
+
* verification keys are not portable across bb releases, so a different build
|
|
5300
|
+
* rejects valid proofs.
|
|
5301
|
+
*
|
|
5302
|
+
* @param response - Proof response from the relay
|
|
5303
|
+
* @param options - Optional verification key (skips the fetch) and timeout
|
|
5304
|
+
* @returns `{ valid }`, or `{ valid: false, error }` if the check could not run
|
|
5305
|
+
*
|
|
5306
|
+
* @example
|
|
5307
|
+
* ```typescript
|
|
5308
|
+
* const result = await sdk.verifyResponseOffChain(response);
|
|
5309
|
+
* if (result.valid) {
|
|
5310
|
+
* console.log('Valid, and nothing was asked of any chain');
|
|
5311
|
+
* }
|
|
5312
|
+
* ```
|
|
5313
|
+
*/
|
|
5314
|
+
async verifyResponseOffChain(response, options) {
|
|
5315
|
+
if (response.status !== 'completed' || !response.proof || !response.publicInputs) {
|
|
5316
|
+
return { valid: false, error: 'Invalid or incomplete response' };
|
|
5317
|
+
}
|
|
5318
|
+
return verifyProofOffChain(response.circuit, response.proof, response.publicInputs, options);
|
|
5319
|
+
}
|
|
4903
5320
|
// ============ Utility Methods ============
|
|
4904
5321
|
/**
|
|
4905
5322
|
* Gets the deployed verifier contract address for a circuit.
|
|
@@ -5573,151 +5990,5 @@ ProofportSDK.WALLET_SIGNATURE_CIRCUITS = [
|
|
|
5573
5990
|
'coinbase_country_attestation',
|
|
5574
5991
|
];
|
|
5575
5992
|
|
|
5576
|
-
|
|
5577
|
-
* Canonical ZKProofport circuit identifiers.
|
|
5578
|
-
*
|
|
5579
|
-
* This module is the single source of truth for circuit IDs. Every other layer
|
|
5580
|
-
* — the mobile app, the demo, the relay, server-side provers — reads its list
|
|
5581
|
-
* from here instead of keeping a private copy that silently drifts.
|
|
5582
|
-
*
|
|
5583
|
-
* It has **no imports and no dependencies**, so it can be pulled in on its own:
|
|
5584
|
-
*
|
|
5585
|
-
* ```typescript
|
|
5586
|
-
* import { CIRCUIT_IDS, isCircuitId } from '@zkproofport-app/sdk/circuits';
|
|
5587
|
-
* ```
|
|
5588
|
-
*
|
|
5589
|
-
* The same names are also re-exported from the package root
|
|
5590
|
-
* (`@zkproofport-app/sdk`) for callers that already import the SDK.
|
|
5591
|
-
*
|
|
5592
|
-
* ## Identifiers are canonical and case-sensitive
|
|
5593
|
-
*
|
|
5594
|
-
* Each ID is the circuit's `name` in its `Nargo.toml`, verbatim. The format is
|
|
5595
|
-
* lowercase with underscores. Hyphenated spellings (`coinbase-kyc`,
|
|
5596
|
-
* `mdl-kr-age`) are directory or UI route names in other repositories — they
|
|
5597
|
-
* are **not** circuit IDs, and passing one produces a nullifier mismatch or a
|
|
5598
|
-
* failed on-chain lookup rather than a clear error.
|
|
5599
|
-
*/
|
|
5600
|
-
/**
|
|
5601
|
-
* Canonical circuit identifiers, keyed by a stable constant name.
|
|
5602
|
-
*
|
|
5603
|
-
* Prefer these constants over string literals so a rename is a compile error
|
|
5604
|
-
* rather than a runtime surprise.
|
|
5605
|
-
*
|
|
5606
|
-
* @example
|
|
5607
|
-
* ```typescript
|
|
5608
|
-
* import { CIRCUIT_IDS } from '@zkproofport-app/sdk/circuits';
|
|
5609
|
-
*
|
|
5610
|
-
* const relay = await sdk.createRelayRequest(CIRCUIT_IDS.COINBASE_ATTESTATION, {
|
|
5611
|
-
* scope: 'myapp.com',
|
|
5612
|
-
* });
|
|
5613
|
-
* ```
|
|
5614
|
-
*/
|
|
5615
|
-
const CIRCUIT_IDS = Object.freeze({
|
|
5616
|
-
/** Coinbase KYC attestation. Officially supported. */
|
|
5617
|
-
COINBASE_ATTESTATION: 'coinbase_attestation',
|
|
5618
|
-
/** Coinbase country attestation (inclusion / exclusion). Officially supported. */
|
|
5619
|
-
COINBASE_COUNTRY_ATTESTATION: 'coinbase_country_attestation',
|
|
5620
|
-
/** OIDC email-domain attestation. Officially supported. */
|
|
5621
|
-
OIDC_DOMAIN_ATTESTATION: 'oidc_domain_attestation',
|
|
5622
|
-
/** GIWA attestation. Planned — not officially supported yet. */
|
|
5623
|
-
GIWA_ATTESTATION: 'giwa_attestation',
|
|
5624
|
-
/** Korea Mobile ID ownership. Planned — not officially supported yet. */
|
|
5625
|
-
MDL_KR_OWNERSHIP: 'mdl_kr_ownership',
|
|
5626
|
-
/** Korea Mobile ID age threshold. Planned — not officially supported yet. */
|
|
5627
|
-
MDL_KR_AGE: 'mdl_kr_age',
|
|
5628
|
-
/** Korea Mobile ID si/do region. Planned — not officially supported yet. */
|
|
5629
|
-
MDL_KR_REGION: 'mdl_kr_region',
|
|
5630
|
-
});
|
|
5631
|
-
/**
|
|
5632
|
-
* Support status for every circuit.
|
|
5633
|
-
*
|
|
5634
|
-
* The record is exhaustive over {@link CircuitId}: adding an identifier without
|
|
5635
|
-
* giving it a status is a compile error, so this list cannot drift from
|
|
5636
|
-
* {@link CIRCUIT_IDS}.
|
|
5637
|
-
*
|
|
5638
|
-
* @example
|
|
5639
|
-
* ```typescript
|
|
5640
|
-
* import { CIRCUIT_SUPPORT_STATUS } from '@zkproofport-app/sdk/circuits';
|
|
5641
|
-
*
|
|
5642
|
-
* CIRCUIT_SUPPORT_STATUS.coinbase_attestation; // 'supported'
|
|
5643
|
-
* CIRCUIT_SUPPORT_STATUS.mdl_kr_age; // 'planned'
|
|
5644
|
-
* ```
|
|
5645
|
-
*/
|
|
5646
|
-
const CIRCUIT_SUPPORT_STATUS = Object.freeze({
|
|
5647
|
-
coinbase_attestation: 'supported',
|
|
5648
|
-
coinbase_country_attestation: 'supported',
|
|
5649
|
-
oidc_domain_attestation: 'supported',
|
|
5650
|
-
giwa_attestation: 'planned',
|
|
5651
|
-
mdl_kr_ownership: 'planned',
|
|
5652
|
-
mdl_kr_age: 'planned',
|
|
5653
|
-
mdl_kr_region: 'planned',
|
|
5654
|
-
});
|
|
5655
|
-
/**
|
|
5656
|
-
* Every canonical circuit identifier, in declaration order.
|
|
5657
|
-
*
|
|
5658
|
-
* Includes circuits that are still `planned`. Use {@link SUPPORTED_CIRCUIT_IDS}
|
|
5659
|
-
* when you only want the ones that are officially supported today.
|
|
5660
|
-
*/
|
|
5661
|
-
const ALL_CIRCUIT_IDS = Object.freeze(Object.values(CIRCUIT_IDS));
|
|
5662
|
-
/**
|
|
5663
|
-
* Circuits that are officially supported today: the two Coinbase circuits and
|
|
5664
|
-
* OIDC domain attestation.
|
|
5665
|
-
*
|
|
5666
|
-
* Derived from {@link CIRCUIT_SUPPORT_STATUS}, so it cannot fall out of sync.
|
|
5667
|
-
*/
|
|
5668
|
-
const SUPPORTED_CIRCUIT_IDS = Object.freeze(ALL_CIRCUIT_IDS.filter((id) => CIRCUIT_SUPPORT_STATUS[id] === 'supported'));
|
|
5669
|
-
/**
|
|
5670
|
-
* Circuits whose identifiers are reserved but which are not officially
|
|
5671
|
-
* supported yet: GIWA attestation and the three Korea Mobile ID circuits.
|
|
5672
|
-
*
|
|
5673
|
-
* Derived from {@link CIRCUIT_SUPPORT_STATUS}, so it cannot fall out of sync.
|
|
5674
|
-
*/
|
|
5675
|
-
const PLANNED_CIRCUIT_IDS = Object.freeze(ALL_CIRCUIT_IDS.filter((id) => CIRCUIT_SUPPORT_STATUS[id] === 'planned'));
|
|
5676
|
-
/**
|
|
5677
|
-
* Narrows an unknown value to a canonical circuit identifier.
|
|
5678
|
-
*
|
|
5679
|
-
* Returns `false` for hyphenated route names such as `'coinbase-kyc'`, which
|
|
5680
|
-
* are not circuit IDs.
|
|
5681
|
-
*
|
|
5682
|
-
* @example
|
|
5683
|
-
* ```typescript
|
|
5684
|
-
* isCircuitId('coinbase_attestation'); // true
|
|
5685
|
-
* isCircuitId('coinbase-kyc'); // false
|
|
5686
|
-
* ```
|
|
5687
|
-
*/
|
|
5688
|
-
function isCircuitId(value) {
|
|
5689
|
-
return typeof value === 'string' && ALL_CIRCUIT_IDS.includes(value);
|
|
5690
|
-
}
|
|
5691
|
-
/**
|
|
5692
|
-
* Narrows an unknown value to a circuit identifier that is officially supported
|
|
5693
|
-
* today. Planned circuits return `false`.
|
|
5694
|
-
*
|
|
5695
|
-
* @example
|
|
5696
|
-
* ```typescript
|
|
5697
|
-
* isSupportedCircuitId('oidc_domain_attestation'); // true
|
|
5698
|
-
* isSupportedCircuitId('mdl_kr_age'); // false — planned
|
|
5699
|
-
* ```
|
|
5700
|
-
*/
|
|
5701
|
-
function isSupportedCircuitId(value) {
|
|
5702
|
-
return isCircuitId(value) && CIRCUIT_SUPPORT_STATUS[value] === 'supported';
|
|
5703
|
-
}
|
|
5704
|
-
/**
|
|
5705
|
-
* Returns the support status of a circuit.
|
|
5706
|
-
*
|
|
5707
|
-
* @param circuit - A canonical circuit identifier.
|
|
5708
|
-
* @throws Error when the value is not a canonical circuit identifier.
|
|
5709
|
-
*
|
|
5710
|
-
* @example
|
|
5711
|
-
* ```typescript
|
|
5712
|
-
* getCircuitSupportStatus('giwa_attestation'); // 'planned'
|
|
5713
|
-
* ```
|
|
5714
|
-
*/
|
|
5715
|
-
function getCircuitSupportStatus(circuit) {
|
|
5716
|
-
if (!isCircuitId(circuit)) {
|
|
5717
|
-
throw new Error(`Unknown circuit '${String(circuit)}'. Expected one of: ${ALL_CIRCUIT_IDS.join(', ')}`);
|
|
5718
|
-
}
|
|
5719
|
-
return CIRCUIT_SUPPORT_STATUS[circuit];
|
|
5720
|
-
}
|
|
5721
|
-
|
|
5722
|
-
export { ALL_CIRCUIT_IDS, CIRCUIT_IDS, CIRCUIT_SUPPORT_STATUS, COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT, MDL_KR_PUBLIC_INPUT_LAYOUT, OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT, PLANNED_CIRCUIT_IDS, ProofportSDK, SUPPORTED_CIRCUIT_IDS, ProofportSDK as default, extractDomainFromPublicInputs, extractNullifierFromPublicInputs, extractScopeFromPublicInputs, getCircuitSupportStatus, isCircuitId, isSupportedCircuitId };
|
|
5993
|
+
export { ALL_CIRCUIT_IDS, CIRCUIT_IDS, CIRCUIT_SUPPORT_STATUS, CIRCUIT_VK_PATHS, COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT, MDL_KR_PUBLIC_INPUT_LAYOUT, OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT, PLANNED_CIRCUIT_IDS, ProofportSDK, SUPPORTED_CIRCUIT_IDS, ProofportSDK as default, extractDomainFromPublicInputs, extractNullifierFromPublicInputs, extractScopeFromPublicInputs, getCircuitSupportStatus, isCircuitId, isSupportedCircuitId, verifyProofOffChain };
|
|
5723
5994
|
//# sourceMappingURL=index.esm.js.map
|