@zkproofport-app/sdk 0.2.12 → 0.2.14
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 +29 -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 +432 -147
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +433 -146
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +432 -147
- package/dist/index.mjs.map +1 -1
- package/dist/offChainVerifier.d.ts +75 -0
- package/package.json +2 -1
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,399 @@ 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. Proofs here are made with `--oracle_hash keccak` and ZK on, so
|
|
4447
|
+
* the matching check is `acirVerifyUltraKeccakZkHonk(proof, vk)`. Two things
|
|
4448
|
+
* have to line up:
|
|
4449
|
+
*
|
|
4450
|
+
* The version. `@aztec/bb.js@1.0.0-nightly.20250723` is the WASM build of the
|
|
4451
|
+
* same bb the circuits are compiled with. Versions are not interchangeable:
|
|
4452
|
+
* measured on the Korea Mobile ID age circuit, the pinned bb writes a 16256-
|
|
4453
|
+
* byte proof where bb 1.2.0 writes 16224 for the identical circuit and
|
|
4454
|
+
* witness — and the verification key comes out the SAME either way, so the
|
|
4455
|
+
* key cannot warn you. A mismatch shows up only as verification failing.
|
|
4456
|
+
*
|
|
4457
|
+
* The flavour. There are four verify entry points and picking the wrong one
|
|
4458
|
+
* is not a polite false. `acirVerifyUltraZKHonk` (poseidon) answers false for
|
|
4459
|
+
* a perfectly good proof, and `acirVerifyUltraKeccakHonk` — keccak without ZK
|
|
4460
|
+
* — TRAPS the WebAssembly with a bare "unreachable", which looks exactly like
|
|
4461
|
+
* a corrupt proof, a mismatched key, or a missing SRS.
|
|
4462
|
+
*
|
|
4463
|
+
* THE PROOF ARRIVES IN TWO PIECES. The mobile app splits what Barretenberg
|
|
4464
|
+
* produced into `proof` and `publicInputs` before sending it, because the
|
|
4465
|
+
* Solidity verifier takes them separately. Verifying needs them joined back, in
|
|
4466
|
+
* that order, which is what `reconstructHonkProof` does.
|
|
4467
|
+
*/
|
|
4468
|
+
/**
|
|
4469
|
+
* Where verification keys are published.
|
|
4470
|
+
*
|
|
4471
|
+
* Not configurable, deliberately: a caller who could point this somewhere else
|
|
4472
|
+
* could point it at a key of their choosing, and "the proof verified" would
|
|
4473
|
+
* then mean nothing. Same repository the mobile app downloads circuit data
|
|
4474
|
+
* from.
|
|
4475
|
+
*/
|
|
4476
|
+
const CIRCUITS_VK_BASE = 'https://raw.githubusercontent.com/zkproofport/circuits/main';
|
|
4477
|
+
/** How long a fetched verification key is reused. Keys change only on a recompile. */
|
|
4478
|
+
const VK_CACHE_TTL_MS = 60 * 60 * 1000;
|
|
4479
|
+
const vkCache = new Map();
|
|
4480
|
+
/** @internal Hex string (with or without 0x) to bytes. */
|
|
4481
|
+
function hexToBytes(hex) {
|
|
4482
|
+
const clean = hex.startsWith('0x') || hex.startsWith('0X') ? hex.slice(2) : hex;
|
|
4483
|
+
if (clean.length === 0)
|
|
4484
|
+
return new Uint8Array(0);
|
|
4485
|
+
if (clean.length % 2 !== 0) {
|
|
4486
|
+
throw new Error(`Hex string has an odd length (${clean.length}); it cannot be bytes`);
|
|
4487
|
+
}
|
|
4488
|
+
if (!/^[0-9a-fA-F]*$/.test(clean)) {
|
|
4489
|
+
throw new Error('Hex string contains non-hex characters');
|
|
4490
|
+
}
|
|
4491
|
+
const out = new Uint8Array(clean.length / 2);
|
|
4492
|
+
for (let i = 0; i < out.length; i++) {
|
|
4493
|
+
out[i] = parseInt(clean.substr(i * 2, 2), 16);
|
|
4494
|
+
}
|
|
4495
|
+
return out;
|
|
4496
|
+
}
|
|
4497
|
+
/**
|
|
4498
|
+
* @internal Public inputs as the flat byte run Barretenberg expects: each one a
|
|
4499
|
+
* 32-byte big-endian field element, concatenated in order.
|
|
4500
|
+
*
|
|
4501
|
+
* The relay carries them as hex strings that are USUALLY already 32 bytes, but
|
|
4502
|
+
* a value that happens to have leading zeros can arrive short (`0x01`). Left-
|
|
4503
|
+
* padding here rather than trusting the sender is the difference between a
|
|
4504
|
+
* verification that works and one that fails on roughly one proof in a hundred,
|
|
4505
|
+
* for no reason the caller can see.
|
|
4506
|
+
*/
|
|
4507
|
+
function packPublicInputs(publicInputs) {
|
|
4508
|
+
const out = new Uint8Array(publicInputs.length * 32);
|
|
4509
|
+
publicInputs.forEach((input, i) => {
|
|
4510
|
+
const bytes = hexToBytes(input);
|
|
4511
|
+
if (bytes.length > 32) {
|
|
4512
|
+
throw new Error(`Public input ${i} is ${bytes.length} bytes; a field element is at most 32`);
|
|
4513
|
+
}
|
|
4514
|
+
out.set(bytes, i * 32 + (32 - bytes.length));
|
|
4515
|
+
});
|
|
4516
|
+
return out;
|
|
4517
|
+
}
|
|
4518
|
+
/**
|
|
4519
|
+
* Fetch a circuit's verification key, with a small cache.
|
|
4520
|
+
*
|
|
4521
|
+
* @internal
|
|
4522
|
+
*/
|
|
4523
|
+
async function fetchVerificationKey(circuit, timeoutMs) {
|
|
4524
|
+
const cached = vkCache.get(circuit);
|
|
4525
|
+
if (cached && Date.now() - cached.at < VK_CACHE_TTL_MS)
|
|
4526
|
+
return cached.vk;
|
|
4527
|
+
const vkPath = CIRCUIT_VK_PATHS[circuit];
|
|
4528
|
+
if (!vkPath) {
|
|
4529
|
+
throw new Error(`No verification key path for circuit '${circuit}'. ` +
|
|
4530
|
+
`Add it to CIRCUIT_VK_PATHS, or pass verificationKey directly.`);
|
|
4531
|
+
}
|
|
4532
|
+
const url = `${CIRCUITS_VK_BASE}/${vkPath}`;
|
|
4533
|
+
const controller = new AbortController();
|
|
4534
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
4535
|
+
try {
|
|
4536
|
+
const resp = await fetch(url, { signal: controller.signal });
|
|
4537
|
+
if (!resp.ok) {
|
|
4538
|
+
throw new Error(`Could not fetch the verification key (HTTP ${resp.status}) from ${url}`);
|
|
4539
|
+
}
|
|
4540
|
+
const vk = new Uint8Array(await resp.arrayBuffer());
|
|
4541
|
+
// A 404 page or an HTML redirect arrives as a successful response full of
|
|
4542
|
+
// text, and would reach Barretenberg as a "verification key" that fails for
|
|
4543
|
+
// reasons no one could guess from the message.
|
|
4544
|
+
if (vk.length < 512) {
|
|
4545
|
+
throw new Error(`The verification key at ${url} is ${vk.length} bytes, far too small to be one. ` +
|
|
4546
|
+
`The path is probably wrong and something else was served.`);
|
|
4547
|
+
}
|
|
4548
|
+
vkCache.set(circuit, { vk, at: Date.now() });
|
|
4549
|
+
return vk;
|
|
4550
|
+
}
|
|
4551
|
+
finally {
|
|
4552
|
+
clearTimeout(timer);
|
|
4553
|
+
}
|
|
4554
|
+
}
|
|
4555
|
+
/**
|
|
4556
|
+
* Verify a proof off-chain, against its verification key.
|
|
4557
|
+
*
|
|
4558
|
+
* Needs no setup from the caller: the verifier ships with this SDK and is
|
|
4559
|
+
* loaded on first use.
|
|
4560
|
+
*
|
|
4561
|
+
* @param circuit - Canonical circuit identifier
|
|
4562
|
+
* @param proof - Proof bytes as hex, WITHOUT public inputs (as the relay sends it)
|
|
4563
|
+
* @param publicInputs - Public inputs as hex strings, in circuit order
|
|
4564
|
+
* @param options - Optional verification key and timeout
|
|
4565
|
+
* @returns `{ valid }`, or `{ valid: false, error }` when the check could not run
|
|
4566
|
+
*
|
|
4567
|
+
* @example
|
|
4568
|
+
* ```typescript
|
|
4569
|
+
* const result = await verifyProofOffChain(
|
|
4570
|
+
* 'coinbase_attestation',
|
|
4571
|
+
* response.proof,
|
|
4572
|
+
* response.publicInputs
|
|
4573
|
+
* );
|
|
4574
|
+
* if (result.valid) console.log('Proof is valid, no chain involved');
|
|
4575
|
+
* ```
|
|
4576
|
+
*/
|
|
4577
|
+
async function verifyProofOffChain(circuit, proof, publicInputs, options = {}) {
|
|
4578
|
+
const timeoutMs = options.timeoutMs ?? 30000;
|
|
4579
|
+
// A REQUIRED dependency, loaded lazily.
|
|
4580
|
+
//
|
|
4581
|
+
// It was briefly an optional peer dependency, on the reasoning that the WASM
|
|
4582
|
+
// is 16MB and most callers verify on-chain. That was wrong: it would have
|
|
4583
|
+
// meant a customer calling this method got an error telling them to install
|
|
4584
|
+
// something, and "works with zero customer configuration" is the SDK's rule.
|
|
4585
|
+
// A caller should never have to know this is implemented with Barretenberg.
|
|
4586
|
+
//
|
|
4587
|
+
// The dynamic import is what keeps the cost off everyone else — a bundler
|
|
4588
|
+
// splits it out, and a caller who never verifies off-chain never loads it.
|
|
4589
|
+
let bb;
|
|
4590
|
+
try {
|
|
4591
|
+
bb = await import('@aztec/bb.js');
|
|
4592
|
+
}
|
|
4593
|
+
catch (error) {
|
|
4594
|
+
// Reaching here means the install is broken, not that something is missing
|
|
4595
|
+
// by design. Say so, rather than sending the caller off to install a
|
|
4596
|
+
// package they should never have had to hear about.
|
|
4597
|
+
return {
|
|
4598
|
+
valid: false,
|
|
4599
|
+
error: 'Could not load the proof verifier (@aztec/bb.js), which ships with this ' +
|
|
4600
|
+
'SDK. This usually means a broken or partial install — try reinstalling ' +
|
|
4601
|
+
`@zkproofport-app/sdk. Underlying error: ${error instanceof Error ? error.message : String(error)}`,
|
|
4602
|
+
};
|
|
4603
|
+
}
|
|
4604
|
+
let api;
|
|
4605
|
+
try {
|
|
4606
|
+
const proofBytes = hexToBytes(proof);
|
|
4607
|
+
if (proofBytes.length === 0) {
|
|
4608
|
+
return { valid: false, error: 'Proof is empty' };
|
|
4609
|
+
}
|
|
4610
|
+
const publicInputBytes = packPublicInputs(publicInputs);
|
|
4611
|
+
const vk = options.verificationKey ?? (await fetchVerificationKey(circuit, timeoutMs));
|
|
4612
|
+
// Public inputs first, then the proof — the order Barretenberg wrote them in
|
|
4613
|
+
// before the mobile app split them apart for the Solidity verifier.
|
|
4614
|
+
const full = bb.reconstructHonkProof(publicInputBytes, proofBytes);
|
|
4615
|
+
api = (await bb.Barretenberg.new({ threads: 1 }));
|
|
4616
|
+
// THE VERIFY MUST MATCH HOW THE PROOF WAS MADE, AND THERE ARE FOUR OF THEM.
|
|
4617
|
+
//
|
|
4618
|
+
// `scripts/build.sh` in the circuits repo proves with `--oracle_hash keccak`
|
|
4619
|
+
// and ZK on — so the only entry point that answers correctly is the one that
|
|
4620
|
+
// is BOTH. Measured against a real proof of the Korea Mobile ID age circuit,
|
|
4621
|
+
// freshly generated against its own key:
|
|
4622
|
+
//
|
|
4623
|
+
// acirVerifyUltraKeccakZkHonk true ← the right one
|
|
4624
|
+
// acirVerifyUltraKeccakHonk TRAPS with a bare "unreachable"
|
|
4625
|
+
// acirVerifyUltraZKHonk false (poseidon: wrong oracle hash)
|
|
4626
|
+
//
|
|
4627
|
+
// The middle row is why this took a while: picking keccak-without-ZK does
|
|
4628
|
+
// not return false, it kills the WebAssembly instance with a message that
|
|
4629
|
+
// names nothing. It looks identical to a malformed proof, to a mismatched
|
|
4630
|
+
// key, and to a missing SRS.
|
|
4631
|
+
//
|
|
4632
|
+
// Two more things that produce that same trap, both required:
|
|
4633
|
+
// initSRSForCircuitSize must be called before the first verify.
|
|
4634
|
+
// The key must be a RawBuffer — a plain Uint8Array is marshalled as a
|
|
4635
|
+
// length-prefixed vector, so its length is read as key material.
|
|
4636
|
+
// Both are what bb.js's own BarretenbergVerifier does before verifying.
|
|
4637
|
+
await api.initSRSForCircuitSize(0);
|
|
4638
|
+
const valid = await api.acirVerifyUltraKeccakZkHonk(full, new bb.RawBuffer(vk));
|
|
4639
|
+
return { valid };
|
|
4640
|
+
}
|
|
4641
|
+
catch (error) {
|
|
4642
|
+
return { valid: false, error: error instanceof Error ? error.message : String(error) };
|
|
4643
|
+
}
|
|
4644
|
+
finally {
|
|
4645
|
+
// The WASM instance holds worker threads; leaving them running keeps a Node
|
|
4646
|
+
// process alive after the check is done.
|
|
4647
|
+
if (api) {
|
|
4648
|
+
try {
|
|
4649
|
+
await api.destroy();
|
|
4650
|
+
}
|
|
4651
|
+
catch {
|
|
4652
|
+
/* nothing useful to do if teardown fails */
|
|
4653
|
+
}
|
|
4654
|
+
}
|
|
4655
|
+
}
|
|
4656
|
+
}
|
|
4657
|
+
|
|
4257
4658
|
/**
|
|
4258
4659
|
* Proofport SDK - Main class
|
|
4259
4660
|
*/
|
|
@@ -4900,6 +5301,36 @@ class ProofportSDK {
|
|
|
4900
5301
|
};
|
|
4901
5302
|
return verifyProofOnChain(response.circuit, parsedProof, providerOrSigner, customVerifier, responseVerifier);
|
|
4902
5303
|
}
|
|
5304
|
+
/**
|
|
5305
|
+
* Verifies a proof response off-chain, against the circuit's verification key.
|
|
5306
|
+
*
|
|
5307
|
+
* No chain, no RPC, no gas, and no verifier contract — so this works for a
|
|
5308
|
+
* circuit whose verifier has not been deployed anywhere, and it does not
|
|
5309
|
+
* depend on a network being reachable. What it does not give you is the
|
|
5310
|
+
* chain's own opinion: use {@link verifyResponseOnChain} when the answer has
|
|
5311
|
+
* to be one a contract will agree with.
|
|
5312
|
+
*
|
|
5313
|
+
* Needs no setup: the verifier ships with this SDK. It is loaded on first
|
|
5314
|
+
* use, so a caller who only verifies on-chain never pays for it.
|
|
5315
|
+
*
|
|
5316
|
+
* @param response - Proof response from the relay
|
|
5317
|
+
* @param options - Optional verification key (skips the fetch) and timeout
|
|
5318
|
+
* @returns `{ valid }`, or `{ valid: false, error }` if the check could not run
|
|
5319
|
+
*
|
|
5320
|
+
* @example
|
|
5321
|
+
* ```typescript
|
|
5322
|
+
* const result = await sdk.verifyResponseOffChain(response);
|
|
5323
|
+
* if (result.valid) {
|
|
5324
|
+
* console.log('Valid, and nothing was asked of any chain');
|
|
5325
|
+
* }
|
|
5326
|
+
* ```
|
|
5327
|
+
*/
|
|
5328
|
+
async verifyResponseOffChain(response, options) {
|
|
5329
|
+
if (response.status !== 'completed' || !response.proof || !response.publicInputs) {
|
|
5330
|
+
return { valid: false, error: 'Invalid or incomplete response' };
|
|
5331
|
+
}
|
|
5332
|
+
return verifyProofOffChain(response.circuit, response.proof, response.publicInputs, options);
|
|
5333
|
+
}
|
|
4903
5334
|
// ============ Utility Methods ============
|
|
4904
5335
|
/**
|
|
4905
5336
|
* Gets the deployed verifier contract address for a circuit.
|
|
@@ -5573,151 +6004,5 @@ ProofportSDK.WALLET_SIGNATURE_CIRCUITS = [
|
|
|
5573
6004
|
'coinbase_country_attestation',
|
|
5574
6005
|
];
|
|
5575
6006
|
|
|
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 };
|
|
6007
|
+
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
6008
|
//# sourceMappingURL=index.esm.js.map
|