@zkproofport-ai/sdk 0.4.0 → 0.5.0

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/verify.js CHANGED
@@ -1,7 +1,30 @@
1
1
  import { ethers } from 'ethers';
2
- const VERIFIER_ABI = [
3
- 'function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool)',
4
- ];
2
+ import { RPC_ENDPOINTS, verifyProofOnChain } from '@zkproofport-app/core';
3
+ import { isCanonicalCircuitId } from './circuits.js';
4
+ /**
5
+ * Public RPC per chain this SDK verifies on: core's endpoints plus the two
6
+ * Ethereum chains the AI server reports OIDC/Coinbase proofs on (core 0.2.0
7
+ * lists only Base, Base Sepolia and GIWA Sepolia).
8
+ *
9
+ * Never the `rpcUrl` a prove response carries. A response is written by the
10
+ * server whose proof is being checked; an RPC it names can answer `true` to
11
+ * any call as easily as a verifier address it names can. Checked 2026-10-27:
12
+ * both publicnode URLs answer `eth_chainId` with their chain and return code
13
+ * for the pinned verifiers (11155111 0x7fc2…4a8a, 1 0xf3d5…9d0c).
14
+ */
15
+ export const VERIFY_RPC_URLS = Object.freeze({
16
+ ...RPC_ENDPOINTS,
17
+ 1: 'https://ethereum-rpc.publicnode.com',
18
+ 11155111: 'https://ethereum-sepolia-rpc.publicnode.com',
19
+ });
20
+ /** The RPC to verify on `chainId`. Throws naming the chain when there is none. */
21
+ export function verifyRpcUrl(chainId) {
22
+ const url = VERIFY_RPC_URLS[chainId];
23
+ if (!url) {
24
+ throw new Error(`No RPC to verify on chain ${chainId}. Known chains: ${Object.keys(VERIFY_RPC_URLS).join(', ')}.`);
25
+ }
26
+ return url;
27
+ }
5
28
  /**
6
29
  * Split concatenated public inputs hex into array of bytes32 values.
7
30
  */
@@ -14,28 +37,52 @@ function splitPublicInputs(publicInputsHex) {
14
37
  return chunks;
15
38
  }
16
39
  /**
17
- * Verify a ZK proof on-chain using server-provided verification info.
40
+ * Verify a proof on-chain against ZKProofport's pinned verifier.
41
+ *
42
+ * What the response says about its verifier is a claim, never an instruction:
43
+ * - the contract is core's pinned deployment of `circuit` on `claim.chainId`
44
+ * (`PINNED_VERIFIERS`); a chain with no deployment of that circuit is refused;
45
+ * - `claim.verifierAddress` is compared with it, and a different address is
46
+ * refused rather than called;
47
+ * - the RPC is {@link VERIFY_RPC_URLS}, not the response's `rpcUrl`.
48
+ * Core also runs the circuit's issuer check before the contract call: the OIDC
49
+ * provider key against the live Google/Microsoft JWKS, and the signer-list root
50
+ * for Coinbase, Country and GIWA. Any failure is `{ valid: false, error }`.
18
51
  *
19
- * @param verification - Chain info from ProveResponse (chainId, verifierAddress, rpcUrl)
52
+ * @param circuit - Canonical circuit id the proof was generated for
53
+ * @param claim - `verification` from the ProveResponse
20
54
  * @param proof - Raw proof hex
21
55
  * @param publicInputs - Concatenated public inputs hex
22
56
  */
23
- export async function verifyOnChain(verification, proof, publicInputs) {
24
- const provider = new ethers.JsonRpcProvider(verification.rpcUrl);
25
- const verifier = new ethers.Contract(verification.verifierAddress, VERIFIER_ABI, provider);
26
- const publicInputsArray = splitPublicInputs(publicInputs);
57
+ export async function verifyOnChain(circuit, claim, proof, publicInputs) {
58
+ if (!isCanonicalCircuitId(circuit)) {
59
+ return { valid: false, error: `Unknown circuit '${circuit}'; cannot verify it on-chain.` };
60
+ }
61
+ const chainId = claim.chainId;
62
+ if (typeof chainId !== 'number' || !Number.isSafeInteger(chainId) || chainId <= 0) {
63
+ return { valid: false, error: `The response names chain '${String(chainId)}', which is not a chain id.` };
64
+ }
65
+ if (typeof claim.verifierAddress !== 'string') {
66
+ return { valid: false, error: 'The response names no verifier address; refusing to guess one.' };
67
+ }
68
+ let rpcUrl;
27
69
  try {
28
- const result = await verifier.verify(proof, publicInputsArray);
29
- return { valid: result === true };
70
+ rpcUrl = verifyRpcUrl(chainId);
30
71
  }
31
72
  catch (error) {
32
- const message = error instanceof Error ? error.message : String(error);
33
- return { valid: false, error: `On-chain verification failed: ${message}` };
73
+ return { valid: false, error: error instanceof Error ? error.message : String(error) };
74
+ }
75
+ const provider = new ethers.JsonRpcProvider(rpcUrl, chainId, { staticNetwork: true });
76
+ const publicInputsArray = splitPublicInputs(publicInputs);
77
+ const result = await verifyProofOnChain(circuit, { proofHex: proof, publicInputsHex: publicInputsArray, numPublicInputs: publicInputsArray.length }, provider, { chainId }, { verifierAddress: claim.verifierAddress, chainId });
78
+ if (result.error !== undefined) {
79
+ return { valid: false, error: `Verification failed: ${result.error}` };
34
80
  }
81
+ return { valid: result.valid === true };
35
82
  }
36
83
  /**
37
- * Verify a proof using the verification info from a ProveResponse.
38
- * Convenience wrapper that extracts verification data from the prove result.
84
+ * Verify a proof from a ProveResponse against the pinned verifier for its
85
+ * circuit and chain. See {@link verifyOnChain} for what is and is not trusted.
39
86
  *
40
87
  * @param result - The full ProveResponse from generateProof()
41
88
  * @returns VerifyResult
@@ -51,6 +98,6 @@ export async function verifyProof(result) {
51
98
  if (!result.verification) {
52
99
  return { valid: false, error: 'No verification info in response (verifier not deployed on this network)' };
53
100
  }
54
- return verifyOnChain(result.verification, result.proof, result.publicInputs);
101
+ return verifyOnChain(result.circuit, result.verification, result.proof, result.publicInputs);
55
102
  }
56
103
  //# sourceMappingURL=verify.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"verify.js","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhC,MAAM,YAAY,GAAG;IACnB,uGAAuG;CACxG,CAAC;AAEF;;GAEG;AACH,SAAS,iBAAiB,CAAC,eAAuB;IAChD,MAAM,GAAG,GAAG,eAAe,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC;IAC1F,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;QACxC,MAAM,CAAC,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,YAA0E,EAC1E,KAAa,EACb,YAAoB;IAEpB,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,eAAe,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IACjE,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,eAAe,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC;IAC3F,MAAM,iBAAiB,GAAG,iBAAiB,CAAC,YAAY,CAAC,CAAC;IAE1D,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC;QAC/D,OAAO,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,EAAE,CAAC;IACpC,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,iCAAiC,OAAO,EAAE,EAAE,CAAC;IAC7E,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAqB;IACrD,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;QACzB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,0EAA0E,EAAE,CAAC;IAC7G,CAAC;IACD,OAAO,aAAa,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;AAC/E,CAAC"}
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zkproofport-ai/sdk",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "TypeScript SDK for ZKProofport Coinbase KYC, country, OIDC and GIWA attestation proofs. Optional EIP-712 action binding for GIWA; x402 USDC payments.",
5
5
  "keywords": [
6
6
  "sdk",
@@ -37,6 +37,7 @@
37
37
  "dependencies": {
38
38
  "@x402/core": "^2.25.0",
39
39
  "@x402/evm": "^2.25.0",
40
+ "@zkproofport-app/core": "0.3.0",
40
41
  "ethers": "^6.13.0",
41
42
  "viem": "^2.45.2"
42
43
  },
@@ -1,3 +0,0 @@
1
- /** The reason the value is not a usable action, or `null` when it is one. */
2
- export declare function validateTypedAction(value: unknown): string | null;
3
- //# sourceMappingURL=typedAction.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"typedAction.d.ts","sourceRoot":"","sources":["../src/typedAction.ts"],"names":[],"mappings":"AAsBA,6EAA6E;AAC7E,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAkDjE"}
@@ -1,50 +0,0 @@
1
- const ADDRESS = /^0x[0-9a-fA-F]{40}$/;
2
- /** The reason the value is not a usable action, or `null` when it is one. */
3
- export function validateTypedAction(value) {
4
- if (value === undefined || value === null) {
5
- return 'action is required. The circuit proves that a wallet authorised ONE EIP-712 action; there is nothing to prove without it.';
6
- }
7
- if (typeof value !== 'object' || Array.isArray(value)) {
8
- return 'action must be an object.';
9
- }
10
- const a = value;
11
- const d = a.domain;
12
- if (!d || typeof d !== 'object') {
13
- return 'action.domain is required.';
14
- }
15
- for (const k of ['name', 'version', 'verifyingContract']) {
16
- if (typeof d[k] !== 'string' || !d[k]) {
17
- return `action.domain.${k} must be a non-empty string.`;
18
- }
19
- }
20
- if (typeof d.chainId !== 'number' || !Number.isInteger(d.chainId)) {
21
- return 'action.domain.chainId must be an integer.';
22
- }
23
- if (!ADDRESS.test(d.verifyingContract)) {
24
- return 'action.domain.verifyingContract must be a 20-byte hex address.';
25
- }
26
- if (!a.types || typeof a.types !== 'object' || Array.isArray(a.types)) {
27
- return 'action.types is required.';
28
- }
29
- if (typeof a.primaryType !== 'string' || !a.primaryType) {
30
- return 'action.primaryType is required.';
31
- }
32
- if (!(a.primaryType in a.types)) {
33
- return `action.primaryType '${a.primaryType}' is not one of the structs in action.types.`;
34
- }
35
- if (!a.message || typeof a.message !== 'object' || Array.isArray(a.message)) {
36
- return 'action.message is required.';
37
- }
38
- const fields = a.types[a.primaryType];
39
- if (!Array.isArray(fields) || fields.length === 0) {
40
- return `action.types.${a.primaryType} declares no fields.`;
41
- }
42
- const missing = fields
43
- .map(f => f?.name)
44
- .filter(n => typeof n === 'string' && !(n in a.message));
45
- if (missing.length) {
46
- return `action.message is missing: ${missing.join(', ')}`;
47
- }
48
- return null;
49
- }
50
- //# sourceMappingURL=typedAction.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"typedAction.js","sourceRoot":"","sources":["../src/typedAction.ts"],"names":[],"mappings":"AAoBA,MAAM,OAAO,GAAG,qBAAqB,CAAC;AAEtC,6EAA6E;AAC7E,MAAM,UAAU,mBAAmB,CAAC,KAAc;IAChD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1C,OAAO,2HAA2H,CAAC;IACrI,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACtD,OAAO,2BAA2B,CAAC;IACrC,CAAC;IACD,MAAM,CAAC,GAAG,KAA6B,CAAC;IAExC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;IACnB,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAChC,OAAO,4BAA4B,CAAC;IACtC,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,mBAAmB,CAAU,EAAE,CAAC;QAClE,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACtC,OAAO,iBAAiB,CAAC,8BAA8B,CAAC;QAC1D,CAAC;IACH,CAAC;IACD,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QAClE,OAAO,2CAA2C,CAAC;IACrD,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACvC,OAAO,gEAAgE,CAAC;IAC1E,CAAC;IAED,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;QACtE,OAAO,2BAA2B,CAAC;IACrC,CAAC;IACD,IAAI,OAAO,CAAC,CAAC,WAAW,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACxD,OAAO,iCAAiC,CAAC;IAC3C,CAAC;IACD,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;QAChC,OAAO,uBAAuB,CAAC,CAAC,WAAW,8CAA8C,CAAC;IAC5F,CAAC;IACD,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5E,OAAO,6BAA6B,CAAC;IACvC,CAAC;IAED,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IACtC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClD,OAAO,gBAAgB,CAAC,CAAC,WAAW,sBAAsB,CAAC;IAC7D,CAAC;IACD,MAAM,OAAO,GAAG,MAAM;SACnB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC;SACjB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC,IAAK,CAAC,CAAC,OAAkB,CAAC,CAAC,CAAC;IACvE,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,OAAO,8BAA8B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;IAC5D,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}