@zkproofport-app/sdk 0.5.0-beta.4 → 0.5.0-beta.6
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 +59 -28
- package/dist/ProofportSDK.d.ts +24 -1
- package/dist/constants.d.ts +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js +109 -23
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +111 -21
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +109 -23
- package/dist/index.mjs.map +1 -1
- package/dist/offChainVerifier.d.ts +8 -1
- package/dist/types.d.ts +7 -0
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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, 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';
|
|
1
|
+
import { countryListError, checkProofIssuer, parseProofForOnChain, verifyProofOnChain, getPinnedVerifier, extractScopeFromPublicInputs, extractNullifierFromPublicInputs, extractDomainFromPublicInputs } from '@zkproofport-app/core';
|
|
2
|
+
export { COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_COUNT, 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
3
|
import { isCircuitId, ALL_CIRCUIT_IDS, circuitActionBinding, resolveSignerVariant, getCircuitPackage, CIRCUIT_NEEDS_WALLET_SIGNATURE } from '@zkproofport-app/core/circuits';
|
|
4
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';
|
|
@@ -90,27 +90,32 @@ const DEEP_LINK_HOSTS = {
|
|
|
90
90
|
* ```typescript
|
|
91
91
|
* const metadata = CIRCUIT_METADATA['coinbase_attestation'];
|
|
92
92
|
* console.log(metadata.name); // "Coinbase KYC"
|
|
93
|
-
* console.log(metadata.publicInputsCount); //
|
|
93
|
+
* console.log(metadata.publicInputsCount); // 193
|
|
94
94
|
* ```
|
|
95
95
|
*/
|
|
96
96
|
const CIRCUIT_METADATA = {
|
|
97
97
|
coinbase_attestation: {
|
|
98
98
|
name: 'Coinbase KYC',
|
|
99
|
-
description: 'Prove Coinbase identity verification',
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
99
|
+
description: 'Prove Coinbase identity verification, optionally authorizing one EIP-712 action',
|
|
100
|
+
// Coinbase KYC v2 (coinbase-attestation-v2/src/main.nr): GIWA's six
|
|
101
|
+
// `pub [u8; 32]` in its order, then `attested_at: pub u64` = 193 fields.
|
|
102
|
+
// signal_hash 0-31, domain_separator 32-63, action_hash 64-95,
|
|
103
|
+
// signer_list_merkle_root 96-127, scope 128-159, nullifier 160-191,
|
|
104
|
+
// attested_at 192. v1's 128 fields are refused on every verify path.
|
|
105
|
+
publicInputsCount: 193,
|
|
106
|
+
publicInputNames: ['signal_hash', 'domain_separator', 'action_hash', 'signer_list_merkle_root', 'scope', 'nullifier', 'attested_at'],
|
|
106
107
|
},
|
|
107
108
|
coinbase_country_attestation: {
|
|
108
109
|
name: 'Coinbase Country',
|
|
109
|
-
description: 'Prove Coinbase country verification',
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
description: 'Prove Coinbase country verification, optionally authorizing one EIP-712 action',
|
|
111
|
+
// Coinbase Country v2 (coinbase-country-attestation-v2/src/main.nr):
|
|
112
|
+
// signal_hash 0-31, domain_separator 32-63, action_hash 64-95,
|
|
113
|
+
// signer_list_merkle_root 96-127, country_list 128-147 (10 x 2 bytes),
|
|
114
|
+
// country_list_length 148, is_included 149, scope 150-181,
|
|
115
|
+
// nullifier 182-213, attested_at 214 = 215 fields. v1's 150 fields are
|
|
116
|
+
// refused on every verify path.
|
|
117
|
+
publicInputsCount: 215,
|
|
118
|
+
publicInputNames: ['signal_hash', 'domain_separator', 'action_hash', 'signer_list_merkle_root', 'country_list', 'country_list_length', 'is_included', 'scope', 'nullifier', 'attested_at'],
|
|
114
119
|
},
|
|
115
120
|
oidc_domain_attestation: {
|
|
116
121
|
name: 'OIDC Domain',
|
|
@@ -639,11 +644,9 @@ function validateProofRequest(request) {
|
|
|
639
644
|
if (inputs.userAddress && !/^0x[a-fA-F0-9]{40}$/.test(inputs.userAddress)) {
|
|
640
645
|
return { valid: false, error: 'Invalid userAddress format' };
|
|
641
646
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
if (!inputs.countryList.every(c => typeof c === 'string')) {
|
|
646
|
-
return { valid: false, error: 'countryList must contain only strings' };
|
|
647
|
+
const listError = countryListError(inputs.countryList);
|
|
648
|
+
if (listError) {
|
|
649
|
+
return { valid: false, error: listError };
|
|
647
650
|
}
|
|
648
651
|
if (typeof inputs.isIncluded !== 'boolean') {
|
|
649
652
|
return { valid: false, error: 'isIncluded is required and must be a boolean' };
|
|
@@ -3937,7 +3940,7 @@ async function verifyProofOffChain(circuit, proof, publicInputs, options = {}) {
|
|
|
3937
3940
|
// their trust anchor as a public input the prover chooses. For OIDC that is
|
|
3938
3941
|
// the RSA key -- see core's oidcProviderKeys. Checked here so a dApp that
|
|
3939
3942
|
// verifies off-chain gets the same answer as one that verifies on-chain.
|
|
3940
|
-
const issuer = await checkProofIssuer(circuit, publicInputs, { request: options.request });
|
|
3943
|
+
const issuer = await checkProofIssuer(circuit, publicInputs, { request: options.request, countryCondition: options.countryCondition });
|
|
3941
3944
|
if (!issuer.ok) {
|
|
3942
3945
|
return { valid: false, error: issuer.error };
|
|
3943
3946
|
}
|
|
@@ -4097,6 +4100,12 @@ class ProofportSDK {
|
|
|
4097
4100
|
* {@link ProofportSDK.MAX_REMEMBERED_RELAY_REQUESTS}.
|
|
4098
4101
|
*/
|
|
4099
4102
|
this.relayRequests = new Map();
|
|
4103
|
+
/**
|
|
4104
|
+
* requestId -> the country list and direction of the Coinbase Country relay
|
|
4105
|
+
* requests this instance created, so verifyResponse* can hold the proof to
|
|
4106
|
+
* them. Bounded like {@link relayRequests}.
|
|
4107
|
+
*/
|
|
4108
|
+
this.countryRequests = new Map();
|
|
4100
4109
|
this.signer = null;
|
|
4101
4110
|
this.socket = null;
|
|
4102
4111
|
/**
|
|
@@ -4228,6 +4237,15 @@ class ProofportSDK {
|
|
|
4228
4237
|
if (typeof inputs.isIncluded !== 'boolean') {
|
|
4229
4238
|
throw new Error('isIncluded is required for coinbase_country_attestation circuit');
|
|
4230
4239
|
}
|
|
4240
|
+
const listError = countryListError(inputs.countryList);
|
|
4241
|
+
if (listError)
|
|
4242
|
+
throw new Error(`${listError} (circuit: coinbase_country_attestation)`);
|
|
4243
|
+
// Coinbase Country v2 binds an action or none, like Coinbase KYC v2.
|
|
4244
|
+
if (inputs.action !== undefined) {
|
|
4245
|
+
const actionError = validateTypedAction(inputs.action);
|
|
4246
|
+
if (actionError)
|
|
4247
|
+
throw new Error(`${actionError} (circuit: coinbase_country_attestation)`);
|
|
4248
|
+
}
|
|
4231
4249
|
const request = {
|
|
4232
4250
|
requestId: generateRequestId(),
|
|
4233
4251
|
circuit: 'coinbase_country_attestation',
|
|
@@ -4628,6 +4646,7 @@ class ProofportSDK {
|
|
|
4628
4646
|
return verifyProofOnChain(circuit, parsedProof, providerOrSigner, this.verifierTarget(circuit), undefined, {
|
|
4629
4647
|
request: options.request,
|
|
4630
4648
|
signerVariant: options.signerVariant,
|
|
4649
|
+
countryCondition: options.countryCondition,
|
|
4631
4650
|
});
|
|
4632
4651
|
}
|
|
4633
4652
|
/**
|
|
@@ -4668,6 +4687,43 @@ class ProofportSDK {
|
|
|
4668
4687
|
this.relayRequests.delete(oldest);
|
|
4669
4688
|
}
|
|
4670
4689
|
}
|
|
4690
|
+
/**
|
|
4691
|
+
* The country list and direction a `coinbase_country_attestation` response
|
|
4692
|
+
* must have been proved against: the one the caller passed, else the relay
|
|
4693
|
+
* request this instance created under the response's requestId, else the
|
|
4694
|
+
* deep-link request still pending under it. `{ error }` when there is none:
|
|
4695
|
+
* the proof's list is the prover's choice, so a response this SDK cannot
|
|
4696
|
+
* place is refused rather than accepted for whatever list it carries.
|
|
4697
|
+
*/
|
|
4698
|
+
countryConditionFor(response, explicit) {
|
|
4699
|
+
if (explicit !== undefined)
|
|
4700
|
+
return { condition: explicit };
|
|
4701
|
+
const remembered = this.countryRequests.get(response.requestId);
|
|
4702
|
+
if (remembered)
|
|
4703
|
+
return { condition: remembered };
|
|
4704
|
+
const pending = this.pendingRequests.get(response.requestId);
|
|
4705
|
+
const inputs = pending?.circuit === 'coinbase_country_attestation' ? pending.inputs : undefined;
|
|
4706
|
+
if (inputs && Array.isArray(inputs.countryList) && typeof inputs.isIncluded === 'boolean') {
|
|
4707
|
+
return { condition: { countryList: [...inputs.countryList], isIncluded: inputs.isIncluded } };
|
|
4708
|
+
}
|
|
4709
|
+
return {
|
|
4710
|
+
error: `No Coinbase Country request ${JSON.stringify(response.requestId)} was created by this SDK instance, so the ` +
|
|
4711
|
+
"proof's country list cannot be checked against what was asked. Pass { countryCondition: { countryList, " +
|
|
4712
|
+
'isIncluded } } -- the list and direction of the request you created.',
|
|
4713
|
+
};
|
|
4714
|
+
}
|
|
4715
|
+
/** @internal Remember a Coinbase Country relay request's list and direction for verifyResponse*. */
|
|
4716
|
+
rememberCountryRequest(requestId, inputs) {
|
|
4717
|
+
const i = inputs;
|
|
4718
|
+
if (typeof requestId !== 'string' || !i || !Array.isArray(i.countryList) || typeof i.isIncluded !== 'boolean')
|
|
4719
|
+
return;
|
|
4720
|
+
this.countryRequests.delete(requestId);
|
|
4721
|
+
this.countryRequests.set(requestId, { countryList: [...i.countryList], isIncluded: i.isIncluded });
|
|
4722
|
+
while (this.countryRequests.size > ProofportSDK.MAX_REMEMBERED_RELAY_REQUESTS) {
|
|
4723
|
+
const oldest = this.countryRequests.keys().next().value;
|
|
4724
|
+
this.countryRequests.delete(oldest);
|
|
4725
|
+
}
|
|
4726
|
+
}
|
|
4671
4727
|
/**
|
|
4672
4728
|
* The verifier for a circuit: the one the dApp configured, otherwise the
|
|
4673
4729
|
* pinned deployment on the chain this SDK's environment verifies that
|
|
@@ -4696,6 +4752,10 @@ class ProofportSDK {
|
|
|
4696
4752
|
* @param options.request - For `oidc_domain_attestation`: the `{ scope, requestId }`
|
|
4697
4753
|
* the response answers. Not needed for a relay request this SDK instance
|
|
4698
4754
|
* created; an OIDC response this instance cannot place is refused without it.
|
|
4755
|
+
* @param options.countryCondition - For `coinbase_country_attestation`: the
|
|
4756
|
+
* `{ countryList, isIncluded }` the request asked for. Not needed for a
|
|
4757
|
+
* request this SDK instance created (relay or deep link); a Country response
|
|
4758
|
+
* this instance cannot place is refused without it.
|
|
4699
4759
|
*
|
|
4700
4760
|
* @returns Promise resolving to verification result with valid boolean and optional error message
|
|
4701
4761
|
*
|
|
@@ -4733,6 +4793,13 @@ class ProofportSDK {
|
|
|
4733
4793
|
return { valid: false, error: found.error };
|
|
4734
4794
|
request = found.request;
|
|
4735
4795
|
}
|
|
4796
|
+
let countryCondition;
|
|
4797
|
+
if (response.circuit === 'coinbase_country_attestation') {
|
|
4798
|
+
const found = this.countryConditionFor(response, options.countryCondition);
|
|
4799
|
+
if ('error' in found)
|
|
4800
|
+
return { valid: false, error: found.error };
|
|
4801
|
+
countryCondition = found.condition;
|
|
4802
|
+
}
|
|
4736
4803
|
const parsedProof = parseProofForOnChain(response.proof, response.publicInputs, response.publicInputs.length);
|
|
4737
4804
|
// The response's verifierAddress and chainId are a claim to check, never
|
|
4738
4805
|
// the contract to call: whoever wrote the response would otherwise pick
|
|
@@ -4741,7 +4808,7 @@ class ProofportSDK {
|
|
|
4741
4808
|
return verifyProofOnChain(response.circuit, parsedProof, providerOrSigner, this.verifierTarget(response.circuit), {
|
|
4742
4809
|
verifierAddress: response.verifierAddress,
|
|
4743
4810
|
chainId: response.chainId,
|
|
4744
|
-
}, { request, signerVariant: response.signerVariant });
|
|
4811
|
+
}, { request, signerVariant: response.signerVariant, countryCondition });
|
|
4745
4812
|
}
|
|
4746
4813
|
/**
|
|
4747
4814
|
* Verifies a proof response off-chain, against the circuit's verification key.
|
|
@@ -4778,7 +4845,14 @@ class ProofportSDK {
|
|
|
4778
4845
|
return { valid: false, error: found.error };
|
|
4779
4846
|
request = found.request;
|
|
4780
4847
|
}
|
|
4781
|
-
|
|
4848
|
+
let countryCondition;
|
|
4849
|
+
if (response.circuit === 'coinbase_country_attestation') {
|
|
4850
|
+
const found = this.countryConditionFor(response, options?.countryCondition);
|
|
4851
|
+
if ('error' in found)
|
|
4852
|
+
return { valid: false, error: found.error };
|
|
4853
|
+
countryCondition = found.condition;
|
|
4854
|
+
}
|
|
4855
|
+
return verifyProofOffChain(response.circuit, response.proof, response.publicInputs, { ...options, request, countryCondition, signerVariant: response.signerVariant });
|
|
4782
4856
|
}
|
|
4783
4857
|
// ============ Utility Methods ============
|
|
4784
4858
|
/**
|
|
@@ -5156,6 +5230,16 @@ class ProofportSDK {
|
|
|
5156
5230
|
}
|
|
5157
5231
|
}
|
|
5158
5232
|
}
|
|
5233
|
+
// A Coinbase Country list the circuit would refuse is refused here, before a
|
|
5234
|
+
// challenge is burned, as createProofRequest does.
|
|
5235
|
+
if (circuit === 'coinbase_country_attestation') {
|
|
5236
|
+
const c = inputs;
|
|
5237
|
+
const listError = countryListError(c.countryList);
|
|
5238
|
+
if (listError)
|
|
5239
|
+
throw new Error(`${listError} (circuit: ${circuit})`);
|
|
5240
|
+
if (typeof c.isIncluded !== 'boolean')
|
|
5241
|
+
throw new Error('isIncluded is required for coinbase_country_attestation circuit');
|
|
5242
|
+
}
|
|
5159
5243
|
// Return target: validated before we burn a challenge, so a typo surfaces
|
|
5160
5244
|
// as a local error instead of a relay 400 on the second round trip.
|
|
5161
5245
|
if (options.returnScheme !== undefined) {
|
|
@@ -5211,6 +5295,8 @@ class ProofportSDK {
|
|
|
5211
5295
|
}
|
|
5212
5296
|
const created = await response.json();
|
|
5213
5297
|
this.rememberRelayRequest(created.requestId, scope);
|
|
5298
|
+
if (circuit === 'coinbase_country_attestation')
|
|
5299
|
+
this.rememberCountryRequest(created.requestId, inputs);
|
|
5214
5300
|
return created;
|
|
5215
5301
|
}
|
|
5216
5302
|
/**
|