@zkproofport-app/sdk 0.5.0-beta.2 → 0.5.0-beta.4
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/README.md +34 -122
- package/dist/ProofportSDK.d.ts +33 -20
- package/dist/constants.d.ts +2 -11
- package/dist/deeplink.d.ts +17 -18
- package/dist/index.d.ts +8 -6
- package/dist/index.esm.js +139 -204
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +206 -202
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +139 -204
- package/dist/index.mjs.map +1 -1
- package/dist/offChainVerifier.d.ts +20 -4
- package/dist/relayStatus.d.ts +11 -0
- package/dist/types.d.ts +11 -63
- package/package.json +2 -2
package/dist/index.esm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { checkProofIssuer, parseProofForOnChain, verifyProofOnChain, getPinnedVerifier, extractScopeFromPublicInputs, extractNullifierFromPublicInputs, extractDomainFromPublicInputs } from '@zkproofport-app/core';
|
|
2
|
-
export { COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT,
|
|
3
|
-
import { isCircuitId, ALL_CIRCUIT_IDS, circuitActionBinding,
|
|
4
|
-
export { ALL_CIRCUIT_IDS, CIRCUIT_ACTION_BINDING, CIRCUIT_IDS, CIRCUIT_SUPPORT_STATUS, CIRCUIT_VK_PATHS, EXPERIMENTAL_CIRCUIT_IDS, PLANNED_CIRCUIT_IDS, SUPPORTED_CIRCUIT_IDS, circuitActionBinding, getCircuitSupportStatus, isCircuitId, isSupportedCircuitId } from '@zkproofport-app/core/circuits';
|
|
2
|
+
export { COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT, OIDC_ALLOWED_AUDIENCES, OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT, OIDC_NONCE_DOMAIN, OIDC_NOW_MAX_AGE_SECONDS, OIDC_NOW_MAX_FUTURE_SECONDS, OIDC_V1_PUBLIC_INPUT_COUNT, OIDC_V2_PUBLIC_INPUT_COUNT, PINNED_VARIANT_VERIFIERS, PINNED_VERIFIERS, checkOidcPolicy, deriveOidcNonceBinding, extractDomainFromPublicInputs, extractNullifierFromPublicInputs, extractScopeFromPublicInputs, getPinnedVariantVerifier, getPinnedVerifier, oidcAudienceHash } from '@zkproofport-app/core';
|
|
3
|
+
import { isCircuitId, ALL_CIRCUIT_IDS, circuitActionBinding, resolveSignerVariant, getCircuitPackage, CIRCUIT_NEEDS_WALLET_SIGNATURE } from '@zkproofport-app/core/circuits';
|
|
4
|
+
export { ALL_CIRCUIT_IDS, CIRCUIT_ACTION_BINDING, CIRCUIT_IDS, CIRCUIT_PACKAGES, CIRCUIT_SUPPORT_STATUS, CIRCUIT_VK_PATHS, EXPERIMENTAL_CIRCUIT_IDS, PLANNED_CIRCUIT_IDS, SIGNER_VARIANTS, SUPPORTED_CIRCUIT_IDS, circuitActionBinding, evmProofByteLength, getCircuitPackage, getCircuitSupportStatus, isCircuitId, isSignerVariant, isSupportedCircuitId, resolveSignerVariant, signerVariantsOf } from '@zkproofport-app/core/circuits';
|
|
5
5
|
import { validateTypedAction } from '@zkproofport-app/core/typedAction';
|
|
6
6
|
export { parseTypedAction, validateTypedAction } from '@zkproofport-app/core/typedAction';
|
|
7
7
|
|
|
@@ -30,7 +30,7 @@ const RELAY_URLS = {
|
|
|
30
30
|
* Mirrors the mobile app's `CIRCUIT_NETWORKS` (proofport-app
|
|
31
31
|
* src/config/contracts.ts), because a proof is checked where the app said its
|
|
32
32
|
* verifier is: Base for the Coinbase and OIDC circuits, GIWA Sepolia for GIWA
|
|
33
|
-
* and Dojang (testnet-only)
|
|
33
|
+
* and Dojang (testnet-only).
|
|
34
34
|
* The ADDRESS on that chain always comes from core's pinned table, never from
|
|
35
35
|
* a proof response.
|
|
36
36
|
*/
|
|
@@ -41,9 +41,6 @@ const VERIFICATION_CHAINS = {
|
|
|
41
41
|
oidc_domain_attestation: 8453,
|
|
42
42
|
giwa_attestation: 91342,
|
|
43
43
|
dojang_playground: 91342,
|
|
44
|
-
mdl_kr_ownership: 84532,
|
|
45
|
-
mdl_kr_age: 84532,
|
|
46
|
-
mdl_kr_region: 84532,
|
|
47
44
|
},
|
|
48
45
|
staging: {
|
|
49
46
|
coinbase_attestation: 84532,
|
|
@@ -51,9 +48,6 @@ const VERIFICATION_CHAINS = {
|
|
|
51
48
|
oidc_domain_attestation: 84532,
|
|
52
49
|
giwa_attestation: 91342,
|
|
53
50
|
dojang_playground: 91342,
|
|
54
|
-
mdl_kr_ownership: 84532,
|
|
55
|
-
mdl_kr_age: 84532,
|
|
56
|
-
mdl_kr_region: 84532,
|
|
57
51
|
},
|
|
58
52
|
local: {
|
|
59
53
|
coinbase_attestation: 84532,
|
|
@@ -61,9 +55,6 @@ const VERIFICATION_CHAINS = {
|
|
|
61
55
|
oidc_domain_attestation: 84532,
|
|
62
56
|
giwa_attestation: 91342,
|
|
63
57
|
dojang_playground: 91342,
|
|
64
|
-
mdl_kr_ownership: 84532,
|
|
65
|
-
mdl_kr_age: 84532,
|
|
66
|
-
mdl_kr_region: 84532,
|
|
67
58
|
},
|
|
68
59
|
};
|
|
69
60
|
/**
|
|
@@ -124,8 +115,10 @@ const CIRCUIT_METADATA = {
|
|
|
124
115
|
oidc_domain_attestation: {
|
|
125
116
|
name: 'OIDC Domain',
|
|
126
117
|
description: 'Prove email domain affiliation via OIDC JWT',
|
|
127
|
-
|
|
128
|
-
|
|
118
|
+
// v2: 148 v1 fields, then aud_hash (32), nonce_mode (1), nonce_binding
|
|
119
|
+
// (32), now (1). oidc-domain-attestation/src/main.nr.
|
|
120
|
+
publicInputsCount: 214,
|
|
121
|
+
publicInputNames: ['pubkey_modulus_limbs', 'domain', 'scope', 'nullifier', 'provider', 'aud_hash', 'nonce_mode', 'nonce_binding', 'now'],
|
|
129
122
|
},
|
|
130
123
|
// Planned circuit — the identifier is reserved and the circuit exists, but it
|
|
131
124
|
// is not officially supported yet. See CIRCUIT_SUPPORT_STATUS in ./circuits.
|
|
@@ -158,27 +151,6 @@ const CIRCUIT_METADATA = {
|
|
|
158
151
|
publicInputsCount: 160,
|
|
159
152
|
publicInputNames: ['signal_hash', 'domain_separator', 'action_hash', 'scope', 'nullifier'],
|
|
160
153
|
},
|
|
161
|
-
// Korea Mobile ID (mDL) circuits. Planned — not officially supported yet.
|
|
162
|
-
// Counts are byte-flattened field totals from the compiled circuit ABIs:
|
|
163
|
-
// [u8; 32] arrays contribute 32 inputs each.
|
|
164
|
-
mdl_kr_ownership: {
|
|
165
|
-
name: 'Korea Mobile ID — Ownership',
|
|
166
|
-
description: "Prove valid Korean mobile driver's license ownership with selective disclosure",
|
|
167
|
-
publicInputsCount: 97,
|
|
168
|
-
publicInputNames: ['scope', 'nullifier_value', 'disclose_flags', 'owner_commit'],
|
|
169
|
-
},
|
|
170
|
-
mdl_kr_age: {
|
|
171
|
-
name: 'Korea Mobile ID — Age',
|
|
172
|
-
description: "Prove minimum age from Korean mobile driver's license without revealing birth date",
|
|
173
|
-
publicInputsCount: 66,
|
|
174
|
-
publicInputNames: ['scope', 'nullifier_value', 'age_threshold', 'current_year'],
|
|
175
|
-
},
|
|
176
|
-
mdl_kr_region: {
|
|
177
|
-
name: 'Korea Mobile ID — Region',
|
|
178
|
-
description: "Prove si/do residency from Korean mobile driver's license without revealing address",
|
|
179
|
-
publicInputsCount: 96,
|
|
180
|
-
publicInputNames: ['scope', 'nullifier_value', 'region_code'],
|
|
181
|
-
},
|
|
182
154
|
};
|
|
183
155
|
/**
|
|
184
156
|
* Default proof request expiration time in milliseconds.
|
|
@@ -422,6 +394,12 @@ function parseProofResponseUrl(url) {
|
|
|
422
394
|
if (timestamp) {
|
|
423
395
|
response.timestamp = parseInt(timestamp, 10);
|
|
424
396
|
}
|
|
397
|
+
// Kept as sent, even when unknown: verification refuses it by name,
|
|
398
|
+
// which says more than a callback that fails to parse.
|
|
399
|
+
const signerVariant = urlObj.searchParams.get('signerVariant');
|
|
400
|
+
if (signerVariant !== null) {
|
|
401
|
+
response.signerVariant = signerVariant;
|
|
402
|
+
}
|
|
425
403
|
}
|
|
426
404
|
else if (status === 'error') {
|
|
427
405
|
response.error = urlObj.searchParams.get('error') || undefined;
|
|
@@ -453,94 +431,6 @@ function parseProofResponseUrl(url) {
|
|
|
453
431
|
function isProofportDeepLink(url, scheme = DEFAULT_SCHEME) {
|
|
454
432
|
return url.toLowerCase().startsWith(`${scheme.toLowerCase()}://`);
|
|
455
433
|
}
|
|
456
|
-
/**
|
|
457
|
-
* @internal
|
|
458
|
-
* Validates a proof request for completeness and correctness.
|
|
459
|
-
*
|
|
460
|
-
* Performs comprehensive validation including:
|
|
461
|
-
* - Required fields (requestId, circuit, callbackUrl)
|
|
462
|
-
* - Circuit type validity (must be a supported circuit)
|
|
463
|
-
* - Circuit-specific input validation (e.g., userAddress format, countryList structure)
|
|
464
|
-
* - Expiration check (if expiresAt is provided)
|
|
465
|
-
*
|
|
466
|
-
* Note: userAddress is optional for both circuits - the mobile app will prompt
|
|
467
|
-
* for wallet connection if not provided.
|
|
468
|
-
*
|
|
469
|
-
* @param request - Proof request to validate
|
|
470
|
-
* @returns Validation result with `valid` flag and optional `error` message
|
|
471
|
-
*/
|
|
472
|
-
/**
|
|
473
|
-
* Validates a free-text mDL input (scope / targetRegion).
|
|
474
|
-
* Rejects empty or whitespace-only values, control characters (which break
|
|
475
|
-
* deep-link query encoding and downstream logging), and values over 256 chars
|
|
476
|
-
* (server-side length cap mirror). UTF-8 text such as Korean region names is
|
|
477
|
-
* allowed.
|
|
478
|
-
*
|
|
479
|
-
* @returns Error message string, or null when valid.
|
|
480
|
-
*/
|
|
481
|
-
function validateMdlTextInput(value, fieldName) {
|
|
482
|
-
if (typeof value !== 'string' || value.trim() === '') {
|
|
483
|
-
return `${fieldName} is required and must be a non-empty string`;
|
|
484
|
-
}
|
|
485
|
-
if (value.length > 256) {
|
|
486
|
-
return `${fieldName} must be 256 characters or fewer`;
|
|
487
|
-
}
|
|
488
|
-
// eslint-disable-next-line no-control-regex
|
|
489
|
-
if (/[\u0000-\u001F\u007F]/.test(value)) {
|
|
490
|
-
return `${fieldName} must not contain control characters`;
|
|
491
|
-
}
|
|
492
|
-
return null;
|
|
493
|
-
}
|
|
494
|
-
/**
|
|
495
|
-
* Validates dapp-supplied inputs for the Korea Mobile ID (mDL) circuits.
|
|
496
|
-
* Shared by deep-link request validation and relay request creation so the
|
|
497
|
-
* same guards apply on every request path.
|
|
498
|
-
*
|
|
499
|
-
* Rules:
|
|
500
|
-
* - `scope` — required non-empty string, ≤256 chars, no control characters
|
|
501
|
-
* - `mdl_kr_ownership.discloseFlags` — optional integer 0–15 (0x0F bitmask)
|
|
502
|
-
* - `mdl_kr_age.ageThreshold` — required integer 1–150
|
|
503
|
-
* - `mdl_kr_region.targetRegion` — required non-empty string (Korean si/do),
|
|
504
|
-
* ≤256 chars, no control characters
|
|
505
|
-
*
|
|
506
|
-
* @param circuit - One of the three mDL circuit identifiers
|
|
507
|
-
* @param inputs - Dapp-supplied circuit inputs
|
|
508
|
-
* @returns Error message string, or null when the inputs are valid
|
|
509
|
-
*/
|
|
510
|
-
function validateMdlInputs(circuit, inputs) {
|
|
511
|
-
const scopeError = validateMdlTextInput(inputs.scope, 'scope');
|
|
512
|
-
if (scopeError) {
|
|
513
|
-
return scopeError;
|
|
514
|
-
}
|
|
515
|
-
if (circuit === 'mdl_kr_ownership') {
|
|
516
|
-
const { discloseFlags } = inputs;
|
|
517
|
-
if (discloseFlags !== undefined) {
|
|
518
|
-
if (typeof discloseFlags !== 'number' ||
|
|
519
|
-
!Number.isInteger(discloseFlags) ||
|
|
520
|
-
discloseFlags < 0 ||
|
|
521
|
-
discloseFlags > 0x0f) {
|
|
522
|
-
return 'discloseFlags must be an integer between 0 and 15';
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
else if (circuit === 'mdl_kr_age') {
|
|
527
|
-
const { ageThreshold } = inputs;
|
|
528
|
-
if (typeof ageThreshold !== 'number' ||
|
|
529
|
-
!Number.isInteger(ageThreshold) ||
|
|
530
|
-
ageThreshold < 1 ||
|
|
531
|
-
ageThreshold > 150) {
|
|
532
|
-
return 'ageThreshold is required and must be an integer between 1 and 150';
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
else {
|
|
536
|
-
const { targetRegion } = inputs;
|
|
537
|
-
const regionError = validateMdlTextInput(targetRegion, 'targetRegion');
|
|
538
|
-
if (regionError) {
|
|
539
|
-
return regionError;
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
return null;
|
|
543
|
-
}
|
|
544
434
|
/**
|
|
545
435
|
* Longest accepted `returnScheme` value.
|
|
546
436
|
* Kept in sync with proofport-relay `src/returnScheme.ts`.
|
|
@@ -700,6 +590,22 @@ function detectReturnScheme(userAgent) {
|
|
|
700
590
|
}
|
|
701
591
|
return undefined;
|
|
702
592
|
}
|
|
593
|
+
/**
|
|
594
|
+
* @internal
|
|
595
|
+
* Validates a proof request for completeness and correctness.
|
|
596
|
+
*
|
|
597
|
+
* Performs comprehensive validation including:
|
|
598
|
+
* - Required fields (requestId, circuit, callbackUrl)
|
|
599
|
+
* - Circuit type validity (must be a supported circuit)
|
|
600
|
+
* - Circuit-specific input validation (e.g., userAddress format, countryList structure)
|
|
601
|
+
* - Expiration check (if expiresAt is provided)
|
|
602
|
+
*
|
|
603
|
+
* Note: userAddress is optional for both circuits - the mobile app will prompt
|
|
604
|
+
* for wallet connection if not provided.
|
|
605
|
+
*
|
|
606
|
+
* @param request - Proof request to validate
|
|
607
|
+
* @returns Validation result with `valid` flag and optional `error` message
|
|
608
|
+
*/
|
|
703
609
|
function validateProofRequest(request) {
|
|
704
610
|
if (!request.requestId) {
|
|
705
611
|
return { valid: false, error: 'Missing requestId' };
|
|
@@ -755,14 +661,6 @@ function validateProofRequest(request) {
|
|
|
755
661
|
return { valid: false, error: 'provider must be a non-empty string when specified' };
|
|
756
662
|
}
|
|
757
663
|
}
|
|
758
|
-
else if (request.circuit === 'mdl_kr_ownership' ||
|
|
759
|
-
request.circuit === 'mdl_kr_age' ||
|
|
760
|
-
request.circuit === 'mdl_kr_region') {
|
|
761
|
-
const mdlError = validateMdlInputs(request.circuit, request.inputs);
|
|
762
|
-
if (mdlError) {
|
|
763
|
-
return { valid: false, error: mdlError };
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
664
|
// The action, decided by the circuit's own entry rather than by naming a
|
|
767
665
|
// circuit here, so every action-capable circuit is checked the same way.
|
|
768
666
|
{
|
|
@@ -3866,7 +3764,7 @@ function estimateQRDataSize(requestOrUrl, scheme = DEFAULT_SCHEME) {
|
|
|
3866
3764
|
* The version. `@aztec/bb.js` must be the WASM build of the same bb the
|
|
3867
3765
|
* circuits are compiled with -- `BB_VERSION` in circuits/toolchain.env,
|
|
3868
3766
|
* 4.2.0-aztecnr-rc.2 since the beta.19 toolchain. Versions are not
|
|
3869
|
-
* interchangeable: on
|
|
3767
|
+
* interchangeable: on one of our circuits the 2025 nightly wrote
|
|
3870
3768
|
* a 16256-byte proof where bb 1.2.0 wrote 16224 for the identical circuit and
|
|
3871
3769
|
* witness, with the SAME verification key, so the key cannot warn you. A
|
|
3872
3770
|
* mismatch shows up only as verification failing.
|
|
@@ -3968,15 +3866,12 @@ function normalisePublicInputs(publicInputs) {
|
|
|
3968
3866
|
*
|
|
3969
3867
|
* @internal
|
|
3970
3868
|
*/
|
|
3971
|
-
async function fetchVerificationKey(circuit, timeoutMs) {
|
|
3972
|
-
const
|
|
3869
|
+
async function fetchVerificationKey(circuit, variant, timeoutMs) {
|
|
3870
|
+
const cacheKey = `${circuit}/${variant}`;
|
|
3871
|
+
const cached = vkCache.get(cacheKey);
|
|
3973
3872
|
if (cached && Date.now() - cached.at < VK_CACHE_TTL_MS)
|
|
3974
3873
|
return cached.vk;
|
|
3975
|
-
const vkPath =
|
|
3976
|
-
if (!vkPath) {
|
|
3977
|
-
throw new Error(`No verification key path for circuit '${circuit}'. ` +
|
|
3978
|
-
`Add it to CIRCUIT_VK_PATHS, or pass verificationKey directly.`);
|
|
3979
|
-
}
|
|
3874
|
+
const vkPath = getCircuitPackage(circuit, variant).vkPath;
|
|
3980
3875
|
const url = `${CIRCUITS_VK_BASE}/${vkPath}`;
|
|
3981
3876
|
const controller = new AbortController();
|
|
3982
3877
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -3993,7 +3888,7 @@ async function fetchVerificationKey(circuit, timeoutMs) {
|
|
|
3993
3888
|
throw new Error(`The verification key at ${url} is ${vk.length} bytes, far too small to be one. ` +
|
|
3994
3889
|
`The path is probably wrong and something else was served.`);
|
|
3995
3890
|
}
|
|
3996
|
-
vkCache.set(
|
|
3891
|
+
vkCache.set(cacheKey, { vk, at: Date.now() });
|
|
3997
3892
|
return vk;
|
|
3998
3893
|
}
|
|
3999
3894
|
finally {
|
|
@@ -4006,9 +3901,11 @@ async function fetchVerificationKey(circuit, timeoutMs) {
|
|
|
4006
3901
|
* Needs no setup from the caller: the verifier ships with this SDK and is
|
|
4007
3902
|
* loaded on first use.
|
|
4008
3903
|
*
|
|
4009
|
-
* For `oidc_domain_attestation` it first checks the
|
|
4010
|
-
*
|
|
4011
|
-
*
|
|
3904
|
+
* For `oidc_domain_attestation` it first checks the token's audience, nonce and
|
|
3905
|
+
* time (core's `checkOidcPolicy`, against `options.request` when given) and the
|
|
3906
|
+
* proof's RSA key against the provider's published JWKS (core's
|
|
3907
|
+
* `checkOidcProviderKey`); any refusal, or a JWKS that cannot be fetched, is
|
|
3908
|
+
* `{ valid: false }`.
|
|
4012
3909
|
*
|
|
4013
3910
|
* @param circuit - Canonical circuit identifier
|
|
4014
3911
|
* @param proof - Proof bytes as hex, WITHOUT public inputs (as the relay sends it)
|
|
@@ -4031,12 +3928,16 @@ async function verifyProofOffChain(circuit, proof, publicInputs, options = {}) {
|
|
|
4031
3928
|
if (!isCircuitId(circuit)) {
|
|
4032
3929
|
return { valid: false, error: `Unknown circuit '${circuit}'. Known: ${ALL_CIRCUIT_IDS.join(', ')}.` };
|
|
4033
3930
|
}
|
|
3931
|
+
const signer = resolveSignerVariant(circuit, options.signerVariant, proof);
|
|
3932
|
+
if ('error' in signer) {
|
|
3933
|
+
return { valid: false, error: signer.error };
|
|
3934
|
+
}
|
|
4034
3935
|
// Before Barretenberg, and for the same reason as on-chain: a proof can be
|
|
4035
3936
|
// mathematically valid and still mean nothing, because some circuits take
|
|
4036
3937
|
// their trust anchor as a public input the prover chooses. For OIDC that is
|
|
4037
3938
|
// the RSA key -- see core's oidcProviderKeys. Checked here so a dApp that
|
|
4038
3939
|
// verifies off-chain gets the same answer as one that verifies on-chain.
|
|
4039
|
-
const issuer = await checkProofIssuer(circuit, publicInputs);
|
|
3940
|
+
const issuer = await checkProofIssuer(circuit, publicInputs, { request: options.request });
|
|
4040
3941
|
if (!issuer.ok) {
|
|
4041
3942
|
return { valid: false, error: issuer.error };
|
|
4042
3943
|
}
|
|
@@ -4077,7 +3978,7 @@ async function verifyProofOffChain(circuit, proof, publicInputs, options = {}) {
|
|
|
4077
3978
|
// relay can deliver a short value (`0x01`), and the check below must see
|
|
4078
3979
|
// exactly what the circuit committed to.
|
|
4079
3980
|
const fields = normalisePublicInputs(publicInputs);
|
|
4080
|
-
const vk = options.verificationKey ?? (await fetchVerificationKey(circuit, timeoutMs));
|
|
3981
|
+
const vk = options.verificationKey ?? (await fetchVerificationKey(circuit, signer.variant, timeoutMs));
|
|
4081
3982
|
const barretenberg = await bb.Barretenberg.new({ threads: 1 });
|
|
4082
3983
|
api = barretenberg;
|
|
4083
3984
|
const verifier = new bb.UltraHonkVerifierBackend(barretenberg);
|
|
@@ -4111,6 +4012,17 @@ const RELAY_TERMINAL_STATUSES = ['completed', 'error', 'failed', 'cancelled', 'e
|
|
|
4111
4012
|
function isTerminalRelayStatus(status) {
|
|
4112
4013
|
return RELAY_TERMINAL_STATUSES.includes(status);
|
|
4113
4014
|
}
|
|
4015
|
+
/**
|
|
4016
|
+
* How long `waitForProof()` / `waitForResult()` wait when the caller passes no
|
|
4017
|
+
* `timeoutMs`: 600 000 ms (10 min).
|
|
4018
|
+
*
|
|
4019
|
+
* The relay answers `expired` itself at 540 s, so with this default a dApp
|
|
4020
|
+
* always gets a terminal status to branch on rather than its own timeout. A
|
|
4021
|
+
* heavy proof on an older phone takes a few minutes after the user confirms;
|
|
4022
|
+
* the earlier 300 000 ms default could fire while such a proof was still being
|
|
4023
|
+
* made.
|
|
4024
|
+
*/
|
|
4025
|
+
const DEFAULT_RELAY_WAIT_MS = 600000;
|
|
4114
4026
|
|
|
4115
4027
|
/**
|
|
4116
4028
|
* Proofport SDK - Main class
|
|
@@ -4178,6 +4090,13 @@ class ProofportSDK {
|
|
|
4178
4090
|
*/
|
|
4179
4091
|
constructor(config = {}) {
|
|
4180
4092
|
this.pendingRequests = new Map();
|
|
4093
|
+
/**
|
|
4094
|
+
* requestId -> scope of the relay requests this instance created, so that
|
|
4095
|
+
* verifyResponse* can check an OIDC proof's request-bound nonce without the
|
|
4096
|
+
* dApp passing the request back in. Oldest entries go first past
|
|
4097
|
+
* {@link ProofportSDK.MAX_REMEMBERED_RELAY_REQUESTS}.
|
|
4098
|
+
*/
|
|
4099
|
+
this.relayRequests = new Map();
|
|
4181
4100
|
this.signer = null;
|
|
4182
4101
|
this.socket = null;
|
|
4183
4102
|
/**
|
|
@@ -4254,7 +4173,7 @@ class ProofportSDK {
|
|
|
4254
4173
|
const request = {
|
|
4255
4174
|
requestId: generateRequestId(),
|
|
4256
4175
|
// The circuit asked for, always. This used to be the literal
|
|
4257
|
-
// 'coinbase_attestation', and every circuit that was not country
|
|
4176
|
+
// 'coinbase_attestation', and every circuit that was not country
|
|
4258
4177
|
// was routed here -- a GIWA, Dojang or OIDC request silently became a
|
|
4259
4178
|
// Coinbase KYC request.
|
|
4260
4179
|
circuit,
|
|
@@ -4361,45 +4280,10 @@ class ProofportSDK {
|
|
|
4361
4280
|
switch (builder) {
|
|
4362
4281
|
case 'country':
|
|
4363
4282
|
return this.createCoinbaseCountryRequest(inputs, options);
|
|
4364
|
-
case 'mdl':
|
|
4365
|
-
return this.createMdlKrRequest(circuit, inputs, options);
|
|
4366
4283
|
case 'scoped':
|
|
4367
4284
|
return this.createScopedRequest(circuit, inputs, options);
|
|
4368
4285
|
}
|
|
4369
4286
|
}
|
|
4370
|
-
/**
|
|
4371
|
-
* @internal
|
|
4372
|
-
* Creates a Korea Mobile ID (mDL) proof request for any of the three
|
|
4373
|
-
* mDL predicate circuits (ownership / age / region).
|
|
4374
|
-
*
|
|
4375
|
-
* The mobile app collects the license data on-device; the dapp only
|
|
4376
|
-
* supplies the predicate parameters validated here. No wallet signature
|
|
4377
|
-
* is required for mDL circuits.
|
|
4378
|
-
*
|
|
4379
|
-
* @param circuit - 'mdl_kr_ownership' | 'mdl_kr_age' | 'mdl_kr_region'
|
|
4380
|
-
* @param inputs - Predicate inputs (see MdlKr*Inputs types)
|
|
4381
|
-
* @param options - Request configuration options
|
|
4382
|
-
*
|
|
4383
|
-
* @throws Error if the predicate inputs fail validation
|
|
4384
|
-
*/
|
|
4385
|
-
createMdlKrRequest(circuit, inputs, options = {}) {
|
|
4386
|
-
const mdlError = validateMdlInputs(circuit, inputs);
|
|
4387
|
-
if (mdlError) {
|
|
4388
|
-
throw new Error(`${mdlError} (circuit: ${circuit})`);
|
|
4389
|
-
}
|
|
4390
|
-
const request = {
|
|
4391
|
-
requestId: generateRequestId(),
|
|
4392
|
-
circuit,
|
|
4393
|
-
inputs,
|
|
4394
|
-
message: options.message,
|
|
4395
|
-
dappName: options.dappName,
|
|
4396
|
-
dappIcon: options.dappIcon,
|
|
4397
|
-
createdAt: Date.now(),
|
|
4398
|
-
expiresAt: Date.now() + (options.expiresInMs || DEFAULT_REQUEST_EXPIRY_MS),
|
|
4399
|
-
};
|
|
4400
|
-
this.pendingRequests.set(request.requestId, request);
|
|
4401
|
-
return request;
|
|
4402
|
-
}
|
|
4403
4287
|
// ============ Deep Link Generation ============
|
|
4404
4288
|
/**
|
|
4405
4289
|
* @internal
|
|
@@ -4739,9 +4623,50 @@ class ProofportSDK {
|
|
|
4739
4623
|
* }
|
|
4740
4624
|
* ```
|
|
4741
4625
|
*/
|
|
4742
|
-
async verifyOnChain(circuit, proof, publicInputs, providerOrSigner) {
|
|
4626
|
+
async verifyOnChain(circuit, proof, publicInputs, providerOrSigner, options = {}) {
|
|
4743
4627
|
const parsedProof = parseProofForOnChain(proof, publicInputs, publicInputs.length);
|
|
4744
|
-
return verifyProofOnChain(circuit, parsedProof, providerOrSigner, this.verifierTarget(circuit)
|
|
4628
|
+
return verifyProofOnChain(circuit, parsedProof, providerOrSigner, this.verifierTarget(circuit), undefined, {
|
|
4629
|
+
request: options.request,
|
|
4630
|
+
signerVariant: options.signerVariant,
|
|
4631
|
+
});
|
|
4632
|
+
}
|
|
4633
|
+
/**
|
|
4634
|
+
* The request an `oidc_domain_attestation` response answers: the one the
|
|
4635
|
+
* caller passed, else the relay request this instance created under the
|
|
4636
|
+
* response's requestId. `{ error }` when there is none, or when the two
|
|
4637
|
+
* disagree -- an OIDC proof made with a request-bound nonce is only
|
|
4638
|
+
* meaningful against the request it was bound to, so a response this SDK
|
|
4639
|
+
* cannot place is refused rather than checked without it.
|
|
4640
|
+
*/
|
|
4641
|
+
oidcRequestFor(response, explicit) {
|
|
4642
|
+
if (explicit !== undefined) {
|
|
4643
|
+
if (explicit === null || typeof explicit !== 'object' || explicit.requestId !== response.requestId) {
|
|
4644
|
+
return {
|
|
4645
|
+
error: `The request passed in (${JSON.stringify(explicit?.requestId)}) is not the one this response answers (${JSON.stringify(response.requestId)}).`,
|
|
4646
|
+
};
|
|
4647
|
+
}
|
|
4648
|
+
return { request: explicit };
|
|
4649
|
+
}
|
|
4650
|
+
const remembered = this.relayRequests.get(response.requestId);
|
|
4651
|
+
if (!remembered) {
|
|
4652
|
+
return {
|
|
4653
|
+
error: `No request ${JSON.stringify(response.requestId)} was created by this SDK instance, so the OIDC proof's ` +
|
|
4654
|
+
'nonce cannot be checked against it. Pass { request: { scope, requestId } } -- the scope and relay ' +
|
|
4655
|
+
'requestId of the request you created.',
|
|
4656
|
+
};
|
|
4657
|
+
}
|
|
4658
|
+
return { request: remembered };
|
|
4659
|
+
}
|
|
4660
|
+
/** @internal Remember a relay request's scope for verifyResponse*. */
|
|
4661
|
+
rememberRelayRequest(requestId, scope) {
|
|
4662
|
+
if (typeof requestId !== 'string' || typeof scope !== 'string')
|
|
4663
|
+
return;
|
|
4664
|
+
this.relayRequests.delete(requestId);
|
|
4665
|
+
this.relayRequests.set(requestId, { scope, requestId });
|
|
4666
|
+
while (this.relayRequests.size > ProofportSDK.MAX_REMEMBERED_RELAY_REQUESTS) {
|
|
4667
|
+
const oldest = this.relayRequests.keys().next().value;
|
|
4668
|
+
this.relayRequests.delete(oldest);
|
|
4669
|
+
}
|
|
4745
4670
|
}
|
|
4746
4671
|
/**
|
|
4747
4672
|
* The verifier for a circuit: the one the dApp configured, otherwise the
|
|
@@ -4768,6 +4693,9 @@ class ProofportSDK {
|
|
|
4768
4693
|
*
|
|
4769
4694
|
* @param response - ProofResponse object from parseResponse()
|
|
4770
4695
|
* @param providerOrSigner - ethers v6 Provider or Signer (defaults to base mainnet public RPC)
|
|
4696
|
+
* @param options.request - For `oidc_domain_attestation`: the `{ scope, requestId }`
|
|
4697
|
+
* the response answers. Not needed for a relay request this SDK instance
|
|
4698
|
+
* created; an OIDC response this instance cannot place is refused without it.
|
|
4771
4699
|
*
|
|
4772
4700
|
* @returns Promise resolving to verification result with valid boolean and optional error message
|
|
4773
4701
|
*
|
|
@@ -4794,10 +4722,17 @@ class ProofportSDK {
|
|
|
4794
4722
|
* });
|
|
4795
4723
|
* ```
|
|
4796
4724
|
*/
|
|
4797
|
-
async verifyResponseOnChain(response, providerOrSigner) {
|
|
4725
|
+
async verifyResponseOnChain(response, providerOrSigner, options = {}) {
|
|
4798
4726
|
if (response.status !== 'completed' || !response.proof || !response.publicInputs) {
|
|
4799
4727
|
return { valid: false, error: 'Invalid or incomplete response' };
|
|
4800
4728
|
}
|
|
4729
|
+
let request;
|
|
4730
|
+
if (response.circuit === 'oidc_domain_attestation') {
|
|
4731
|
+
const found = this.oidcRequestFor(response, options.request);
|
|
4732
|
+
if ('error' in found)
|
|
4733
|
+
return { valid: false, error: found.error };
|
|
4734
|
+
request = found.request;
|
|
4735
|
+
}
|
|
4801
4736
|
const parsedProof = parseProofForOnChain(response.proof, response.publicInputs, response.publicInputs.length);
|
|
4802
4737
|
// The response's verifierAddress and chainId are a claim to check, never
|
|
4803
4738
|
// the contract to call: whoever wrote the response would otherwise pick
|
|
@@ -4806,7 +4741,7 @@ class ProofportSDK {
|
|
|
4806
4741
|
return verifyProofOnChain(response.circuit, parsedProof, providerOrSigner, this.verifierTarget(response.circuit), {
|
|
4807
4742
|
verifierAddress: response.verifierAddress,
|
|
4808
4743
|
chainId: response.chainId,
|
|
4809
|
-
});
|
|
4744
|
+
}, { request, signerVariant: response.signerVariant });
|
|
4810
4745
|
}
|
|
4811
4746
|
/**
|
|
4812
4747
|
* Verifies a proof response off-chain, against the circuit's verification key.
|
|
@@ -4836,7 +4771,14 @@ class ProofportSDK {
|
|
|
4836
4771
|
if (response.status !== 'completed' || !response.proof || !response.publicInputs) {
|
|
4837
4772
|
return { valid: false, error: 'Invalid or incomplete response' };
|
|
4838
4773
|
}
|
|
4839
|
-
|
|
4774
|
+
let request;
|
|
4775
|
+
if (response.circuit === 'oidc_domain_attestation') {
|
|
4776
|
+
const found = this.oidcRequestFor(response, options?.request);
|
|
4777
|
+
if ('error' in found)
|
|
4778
|
+
return { valid: false, error: found.error };
|
|
4779
|
+
request = found.request;
|
|
4780
|
+
}
|
|
4781
|
+
return verifyProofOffChain(response.circuit, response.proof, response.publicInputs, { ...options, request, signerVariant: response.signerVariant });
|
|
4840
4782
|
}
|
|
4841
4783
|
// ============ Utility Methods ============
|
|
4842
4784
|
/**
|
|
@@ -5194,14 +5136,6 @@ class ProofportSDK {
|
|
|
5194
5136
|
if (needsSignature && !this.signer) {
|
|
5195
5137
|
throw new Error('Signer not set. Call setSigner() first. Wallet signature is required for this circuit.');
|
|
5196
5138
|
}
|
|
5197
|
-
// mDL circuits: validate predicate inputs client-side before hitting the
|
|
5198
|
-
// relay, so a bad ageThreshold / targetRegion fails fast with a clear error.
|
|
5199
|
-
if (circuit === 'mdl_kr_ownership' || circuit === 'mdl_kr_age' || circuit === 'mdl_kr_region') {
|
|
5200
|
-
const mdlError = validateMdlInputs(circuit, inputs);
|
|
5201
|
-
if (mdlError) {
|
|
5202
|
-
throw new Error(`${mdlError} (circuit: ${circuit})`);
|
|
5203
|
-
}
|
|
5204
|
-
}
|
|
5205
5139
|
// The action, per the circuit's entry in CIRCUIT_ACTION_BINDING. Checked
|
|
5206
5140
|
// here, before a challenge is burned, because the alternative is a refusal
|
|
5207
5141
|
// from inside the mobile app -- a screen the dapp developer never sees,
|
|
@@ -5275,7 +5209,9 @@ class ProofportSDK {
|
|
|
5275
5209
|
const error = await response.json().catch(() => ({ error: `HTTP ${response.status}` }));
|
|
5276
5210
|
throw new Error(error.error || `Relay request failed: HTTP ${response.status}`);
|
|
5277
5211
|
}
|
|
5278
|
-
|
|
5212
|
+
const created = await response.json();
|
|
5213
|
+
this.rememberRelayRequest(created.requestId, scope);
|
|
5214
|
+
return created;
|
|
5279
5215
|
}
|
|
5280
5216
|
/**
|
|
5281
5217
|
* Polls the relay for proof result status.
|
|
@@ -5315,14 +5251,14 @@ class ProofportSDK {
|
|
|
5315
5251
|
* @param requestId - The relay-issued request ID
|
|
5316
5252
|
* @param options - Polling options
|
|
5317
5253
|
* @param options.intervalMs - Polling interval in milliseconds (default: 2000)
|
|
5318
|
-
* @param options.timeoutMs - Maximum polling time in milliseconds (default:
|
|
5254
|
+
* @param options.timeoutMs - Maximum polling time in milliseconds (default: 600000 = 10 min, `DEFAULT_RELAY_WAIT_MS`)
|
|
5319
5255
|
* @param options.onStatusChange - Callback when status changes
|
|
5320
5256
|
* @returns Promise resolving to final RelayProofResult
|
|
5321
5257
|
* @throws Error if timeout or relay error
|
|
5322
5258
|
*/
|
|
5323
5259
|
async waitForResult(requestId, options = {}) {
|
|
5324
5260
|
const interval = options.intervalMs || 2000;
|
|
5325
|
-
const timeout = options.timeoutMs ||
|
|
5261
|
+
const timeout = options.timeoutMs || DEFAULT_RELAY_WAIT_MS;
|
|
5326
5262
|
const startTime = Date.now();
|
|
5327
5263
|
let lastStatus = '';
|
|
5328
5264
|
while (Date.now() - startTime < timeout) {
|
|
@@ -5434,12 +5370,12 @@ class ProofportSDK {
|
|
|
5434
5370
|
*
|
|
5435
5371
|
* @param requestId - The relay-issued request ID
|
|
5436
5372
|
* @param options - Configuration options
|
|
5437
|
-
* @param options.timeoutMs - Maximum wait time in ms (default:
|
|
5373
|
+
* @param options.timeoutMs - Maximum wait time in ms (default: 600000 = 10 min, `DEFAULT_RELAY_WAIT_MS`)
|
|
5438
5374
|
* @param options.onStatusChange - Callback for status updates
|
|
5439
5375
|
* @returns Promise resolving to final RelayProofResult
|
|
5440
5376
|
*/
|
|
5441
5377
|
async waitForProof(requestId, options = {}) {
|
|
5442
|
-
const timeout = options.timeoutMs ||
|
|
5378
|
+
const timeout = options.timeoutMs || DEFAULT_RELAY_WAIT_MS;
|
|
5443
5379
|
// Try Socket.IO first
|
|
5444
5380
|
if (this.relayUrl) {
|
|
5445
5381
|
try {
|
|
@@ -5558,6 +5494,8 @@ class ProofportSDK {
|
|
|
5558
5494
|
return extractDomainFromPublicInputs(publicInputs, circuit);
|
|
5559
5495
|
}
|
|
5560
5496
|
}
|
|
5497
|
+
/** Bound on {@link relayRequests}; a dApp that verifies later can pass the request explicitly. */
|
|
5498
|
+
ProofportSDK.MAX_REMEMBERED_RELAY_REQUESTS = 1000;
|
|
5561
5499
|
/**
|
|
5562
5500
|
* How each circuit's request is built and checked. Keyed by circuit id and
|
|
5563
5501
|
* exhaustive over it, so a new circuit is a compile error here instead of
|
|
@@ -5569,10 +5507,7 @@ ProofportSDK.REQUEST_BUILDERS = {
|
|
|
5569
5507
|
oidc_domain_attestation: 'scoped',
|
|
5570
5508
|
giwa_attestation: 'scoped',
|
|
5571
5509
|
dojang_playground: 'scoped',
|
|
5572
|
-
mdl_kr_ownership: 'mdl',
|
|
5573
|
-
mdl_kr_age: 'mdl',
|
|
5574
|
-
mdl_kr_region: 'mdl',
|
|
5575
5510
|
};
|
|
5576
5511
|
|
|
5577
|
-
export { ProofportSDK, RELAY_TERMINAL_STATUSES, ProofportSDK as default, isTerminalRelayStatus, verifyProofOffChain };
|
|
5512
|
+
export { DEFAULT_RELAY_WAIT_MS, ProofportSDK, RELAY_TERMINAL_STATUSES, ProofportSDK as default, isTerminalRelayStatus, verifyProofOffChain };
|
|
5578
5513
|
//# sourceMappingURL=index.esm.js.map
|