@zkpassport/sdk 0.3.0 → 0.3.1

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/src/index.ts CHANGED
@@ -53,6 +53,10 @@ import {
53
53
  getCommittedInputCount,
54
54
  ProofMode,
55
55
  ProofType,
56
+ getScopeHash,
57
+ ProofData,
58
+ getScopeFromOuterProof,
59
+ getSubscopeFromOuterProof,
56
60
  } from "@zkpassport/utils"
57
61
  import { bytesToHex } from "@noble/ciphers/utils"
58
62
  import { getWebSocketClient, WebSocketClient } from "./websocket"
@@ -101,6 +105,7 @@ export type QueryResultErrors = {
101
105
  commitment?: QueryResultError<string>
102
106
  date?: QueryResultError<string>
103
107
  certificate?: QueryResultError<string>
108
+ scope?: QueryResultError<string>
104
109
  }
105
110
  }
106
111
 
@@ -111,6 +116,8 @@ export type SolidityVerifierParameters = {
111
116
  committedInputs: string
112
117
  committedInputCounts: number[]
113
118
  validityPeriodInDays: number
119
+ scope: string
120
+ subscope: string
114
121
  }
115
122
 
116
123
  export type EVMChain = "ethereum_sepolia" | "local_anvil"
@@ -386,6 +393,7 @@ export class ZKPassport {
386
393
  proofs: this.topicToProofs[topic],
387
394
  queryResult: result,
388
395
  validity: this.topicToLocalConfig[topic]?.validity,
396
+ scope: this.topicToService[topic]?.scope,
389
397
  })
390
398
  delete this.topicToProofs[topic]
391
399
  const hasFailedProofs = this.topicToFailedProofCount[topic] > 0
@@ -1756,10 +1764,39 @@ export class ZKPassport {
1756
1764
  return { isCorrect, queryResultErrors }
1757
1765
  }
1758
1766
 
1767
+ private checkScopeFromDisclosureProof(
1768
+ proofData: ProofData,
1769
+ queryResultErrors: QueryResultErrors,
1770
+ key: string,
1771
+ scope?: string,
1772
+ ) {
1773
+ let isCorrect = true
1774
+ if (this.domain && getScopeHash(this.domain) !== BigInt(proofData.publicInputs[1])) {
1775
+ console.warn("The proof comes from a different domain than the one expected")
1776
+ isCorrect = false
1777
+ queryResultErrors[key as keyof QueryResultErrors].scope = {
1778
+ expected: `Scope: ${getScopeHash(this.domain).toString()}`,
1779
+ received: `Scope: ${BigInt(proofData.publicInputs[1]).toString()}`,
1780
+ message: "The proof comes from a different domain than the one expected",
1781
+ }
1782
+ }
1783
+ if (scope && getScopeHash(scope) !== BigInt(proofData.publicInputs[2])) {
1784
+ console.warn("The proof uses a different scope than the one expected")
1785
+ isCorrect = false
1786
+ queryResultErrors[key as keyof QueryResultErrors].scope = {
1787
+ expected: `Scope: ${getScopeHash(scope).toString()}`,
1788
+ received: `Scope: ${BigInt(proofData.publicInputs[2]).toString()}`,
1789
+ message: "The proof uses a different scope than the one expected",
1790
+ }
1791
+ }
1792
+ return { isCorrect, queryResultErrors }
1793
+ }
1794
+
1759
1795
  private async checkPublicInputs(
1760
1796
  proofs: Array<ProofResult>,
1761
1797
  queryResult: QueryResult,
1762
1798
  validity?: number,
1799
+ scope?: string,
1763
1800
  ) {
1764
1801
  let commitmentIn: bigint | undefined
1765
1802
  let commitmentOut: bigint | undefined
@@ -1866,6 +1903,24 @@ export class ZKPassport {
1866
1903
  message: "The proof does not verify all the requested conditions and information",
1867
1904
  }
1868
1905
  }
1906
+ if (this.domain && getScopeHash(this.domain) !== getScopeFromOuterProof(proofData)) {
1907
+ console.warn("The proof comes from a different domain than the one expected")
1908
+ isCorrect = false
1909
+ queryResultErrors.outer.scope = {
1910
+ expected: `Scope: ${getScopeHash(this.domain).toString()}`,
1911
+ received: `Scope: ${getScopeFromOuterProof(proofData).toString()}`,
1912
+ message: "The proof comes from a different domain than the one expected",
1913
+ }
1914
+ }
1915
+ if (scope && getScopeHash(scope) !== getSubscopeFromOuterProof(proofData)) {
1916
+ console.warn("The proof uses a different scope than the one expected")
1917
+ isCorrect = false
1918
+ queryResultErrors.outer.scope = {
1919
+ expected: `Scope: ${getScopeHash(scope).toString()}`,
1920
+ received: `Scope: ${getSubscopeFromOuterProof(proofData).toString()}`,
1921
+ message: "The proof uses a different scope than the one expected",
1922
+ }
1923
+ }
1869
1924
  if (!!committedInputs?.compare_age) {
1870
1925
  const ageCommittedInputs = committedInputs?.compare_age as AgeCommittedInputs
1871
1926
  const ageParameterCommitment = isForEVM
@@ -2194,12 +2249,20 @@ export class ZKPassport {
2194
2249
  message: "The disclosed data does not match the data committed by the proof",
2195
2250
  }
2196
2251
  }
2252
+ const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
2253
+ this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "disclose", scope)
2254
+ isCorrect = isCorrect && isCorrectScope
2255
+ queryResultErrors = {
2256
+ ...queryResultErrors,
2257
+ ...queryResultErrorsScope,
2258
+ }
2197
2259
  const { isCorrect: isCorrectDisclose, queryResultErrors: queryResultErrorsDisclose } =
2198
2260
  this.checkDiscloseBytesPublicInputs(proof, queryResult)
2199
- isCorrect = isCorrect && isCorrectDisclose
2261
+ isCorrect = isCorrect && isCorrectDisclose && isCorrectScope
2200
2262
  queryResultErrors = {
2201
2263
  ...queryResultErrors,
2202
2264
  ...queryResultErrorsDisclose,
2265
+ ...queryResultErrorsScope,
2203
2266
  }
2204
2267
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
2205
2268
  } else if (proof.name === "compare_age") {
@@ -2235,12 +2298,15 @@ export class ZKPassport {
2235
2298
  "The conditions for the age check do not match the conditions checked by the proof",
2236
2299
  }
2237
2300
  }
2301
+ const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
2302
+ this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "age", scope)
2238
2303
  const { isCorrect: isCorrectAge, queryResultErrors: queryResultErrorsAge } =
2239
2304
  this.checkAgePublicInputs(proof, queryResult)
2240
- isCorrect = isCorrect && isCorrectAge
2305
+ isCorrect = isCorrect && isCorrectAge && isCorrectScope
2241
2306
  queryResultErrors = {
2242
2307
  ...queryResultErrors,
2243
2308
  ...queryResultErrorsAge,
2309
+ ...queryResultErrorsScope,
2244
2310
  }
2245
2311
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
2246
2312
  } else if (proof.name === "compare_birthdate") {
@@ -2277,12 +2343,15 @@ export class ZKPassport {
2277
2343
  "The conditions for the birthdate check do not match the conditions checked by the proof",
2278
2344
  }
2279
2345
  }
2346
+ const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
2347
+ this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "birthdate", scope)
2280
2348
  const { isCorrect: isCorrectBirthdate, queryResultErrors: queryResultErrorsBirthdate } =
2281
2349
  this.checkBirthdatePublicInputs(proof, queryResult)
2282
- isCorrect = isCorrect && isCorrectBirthdate
2350
+ isCorrect = isCorrect && isCorrectBirthdate && isCorrectScope
2283
2351
  queryResultErrors = {
2284
2352
  ...queryResultErrors,
2285
2353
  ...queryResultErrorsBirthdate,
2354
+ ...queryResultErrorsScope,
2286
2355
  }
2287
2356
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
2288
2357
  } else if (proof.name === "compare_expiry") {
@@ -2318,12 +2387,15 @@ export class ZKPassport {
2318
2387
  "The conditions for the expiry date check do not match the conditions checked by the proof",
2319
2388
  }
2320
2389
  }
2390
+ const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
2391
+ this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "expiry_date", scope)
2321
2392
  const { isCorrect: isCorrectExpiryDate, queryResultErrors: queryResultErrorsExpiryDate } =
2322
2393
  this.checkExpiryDatePublicInputs(proof, queryResult)
2323
- isCorrect = isCorrect && isCorrectExpiryDate
2394
+ isCorrect = isCorrect && isCorrectExpiryDate && isCorrectScope
2324
2395
  queryResultErrors = {
2325
2396
  ...queryResultErrors,
2326
2397
  ...queryResultErrorsExpiryDate,
2398
+ ...queryResultErrorsScope,
2327
2399
  }
2328
2400
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
2329
2401
  } else if (proof.name === "exclusion_check_nationality") {
@@ -2361,15 +2433,17 @@ export class ZKPassport {
2361
2433
  "The committed country list for the exclusion check does not match the one from the proof",
2362
2434
  }
2363
2435
  }
2364
-
2436
+ const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
2437
+ this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "nationality", scope)
2365
2438
  const {
2366
2439
  isCorrect: isCorrectNationalityExclusion,
2367
2440
  queryResultErrors: queryResultErrorsNationalityExclusion,
2368
2441
  } = this.checkNationalityExclusionPublicInputs(queryResult, countryList)
2369
- isCorrect = isCorrect && isCorrectNationalityExclusion
2442
+ isCorrect = isCorrect && isCorrectNationalityExclusion && isCorrectScope
2370
2443
  queryResultErrors = {
2371
2444
  ...queryResultErrors,
2372
2445
  ...queryResultErrorsNationalityExclusion,
2446
+ ...queryResultErrorsScope,
2373
2447
  }
2374
2448
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
2375
2449
  } else if (proof.name === "exclusion_check_issuing_country") {
@@ -2407,14 +2481,17 @@ export class ZKPassport {
2407
2481
  "The committed country list for the issuing country exclusion check does not match the one from the proof",
2408
2482
  }
2409
2483
  }
2484
+ const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
2485
+ this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "nationality", scope)
2410
2486
  const {
2411
2487
  isCorrect: isCorrectIssuingCountryExclusion,
2412
2488
  queryResultErrors: queryResultErrorsIssuingCountryExclusion,
2413
2489
  } = this.checkIssuingCountryExclusionPublicInputs(queryResult, countryList)
2414
- isCorrect = isCorrect && isCorrectIssuingCountryExclusion
2490
+ isCorrect = isCorrect && isCorrectIssuingCountryExclusion && isCorrectScope
2415
2491
  queryResultErrors = {
2416
2492
  ...queryResultErrors,
2417
2493
  ...queryResultErrorsIssuingCountryExclusion,
2494
+ ...queryResultErrorsScope,
2418
2495
  }
2419
2496
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
2420
2497
  } else if (proof.name === "inclusion_check_nationality") {
@@ -2452,14 +2529,17 @@ export class ZKPassport {
2452
2529
  "The committed country list for the nationality inclusion check does not match the one from the proof",
2453
2530
  }
2454
2531
  }
2532
+ const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
2533
+ this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "nationality", scope)
2455
2534
  const {
2456
2535
  isCorrect: isCorrectNationalityInclusion,
2457
2536
  queryResultErrors: queryResultErrorsNationalityInclusion,
2458
2537
  } = this.checkNationalityInclusionPublicInputs(queryResult, countryList)
2459
- isCorrect = isCorrect && isCorrectNationalityInclusion
2538
+ isCorrect = isCorrect && isCorrectNationalityInclusion && isCorrectScope
2460
2539
  queryResultErrors = {
2461
2540
  ...queryResultErrors,
2462
2541
  ...queryResultErrorsNationalityInclusion,
2542
+ ...queryResultErrorsScope,
2463
2543
  }
2464
2544
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
2465
2545
  } else if (proof.name === "inclusion_check_issuing_country") {
@@ -2497,14 +2577,17 @@ export class ZKPassport {
2497
2577
  "The committed country list for the issuing country inclusion check does not match the one from the proof",
2498
2578
  }
2499
2579
  }
2580
+ const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
2581
+ this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "nationality", scope)
2500
2582
  const {
2501
2583
  isCorrect: isCorrectIssuingCountryInclusion,
2502
2584
  queryResultErrors: queryResultErrorsIssuingCountryInclusion,
2503
2585
  } = this.checkIssuingCountryInclusionPublicInputs(queryResult, countryList)
2504
- isCorrect = isCorrect && isCorrectIssuingCountryInclusion
2586
+ isCorrect = isCorrect && isCorrectIssuingCountryInclusion && isCorrectScope
2505
2587
  queryResultErrors = {
2506
2588
  ...queryResultErrors,
2507
2589
  ...queryResultErrorsIssuingCountryInclusion,
2590
+ ...queryResultErrorsScope,
2508
2591
  }
2509
2592
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
2510
2593
  }
@@ -2524,10 +2607,12 @@ export class ZKPassport {
2524
2607
  proofs,
2525
2608
  queryResult,
2526
2609
  validity,
2610
+ scope,
2527
2611
  }: {
2528
2612
  proofs: Array<ProofResult>
2529
2613
  queryResult: QueryResult
2530
2614
  validity?: number
2615
+ scope?: string
2531
2616
  }): Promise<{
2532
2617
  uniqueIdentifier: string | undefined
2533
2618
  verified: boolean
@@ -2555,7 +2640,7 @@ export class ZKPassport {
2555
2640
  isCorrect,
2556
2641
  uniqueIdentifier: uniqueIdentifierFromPublicInputs,
2557
2642
  queryResultErrors: queryResultErrorsFromPublicInputs,
2558
- } = await this.checkPublicInputs(proofs, formattedResult, validity)
2643
+ } = await this.checkPublicInputs(proofs, formattedResult, validity, scope)
2559
2644
  uniqueIdentifier = uniqueIdentifierFromPublicInputs
2560
2645
  verified = isCorrect
2561
2646
  queryResultErrors = isCorrect ? undefined : queryResultErrorsFromPublicInputs
@@ -2571,24 +2656,23 @@ export class ZKPassport {
2571
2656
  try {
2572
2657
  const { createPublicClient, http } = await import("viem")
2573
2658
  const { sepolia } = await import("viem/chains")
2574
- const verifierDetails = this.getSolidityVerifierDetails("ethereum_sepolia")
2659
+ const { address, abi, functionName } =
2660
+ this.getSolidityVerifierDetails("ethereum_sepolia")
2575
2661
  const client = createPublicClient({
2576
2662
  chain: sepolia,
2577
2663
  transport: http("https://ethereum-sepolia-rpc.publicnode.com"),
2578
2664
  })
2579
- const params = this.getSolidityVerifierParameters(proof)
2665
+ const params = this.getSolidityVerifierParameters({
2666
+ proof,
2667
+ validityPeriodInDays: validity,
2668
+ domain: this.domain,
2669
+ scope,
2670
+ })
2580
2671
  const result = await client.readContract({
2581
- address: verifierDetails.address as `0x${string}`,
2582
- abi: verifierDetails.abi,
2583
- functionName: "verifyProof",
2584
- args: [
2585
- params.vkeyHash,
2586
- params.proof,
2587
- params.publicInputs,
2588
- params.committedInputs,
2589
- params.committedInputCounts,
2590
- params.validityPeriodInDays,
2591
- ],
2672
+ address,
2673
+ abi,
2674
+ functionName,
2675
+ args: [params],
2592
2676
  })
2593
2677
  const isVerified = Array.isArray(result) ? Boolean(result[0]) : false
2594
2678
  verified = isVerified
@@ -2624,7 +2708,8 @@ export class ZKPassport {
2624
2708
  }
2625
2709
 
2626
2710
  public getSolidityVerifierDetails(network: EVMChain): {
2627
- address: string
2711
+ address: `0x${string}`
2712
+ functionName: string
2628
2713
  abi: {
2629
2714
  type: "function" | "event" | "constructor"
2630
2715
  name: string
@@ -2632,21 +2717,35 @@ export class ZKPassport {
2632
2717
  outputs: { name: string; type: string; internalType: string }[]
2633
2718
  }[]
2634
2719
  } {
2720
+ const baseConfig = {
2721
+ functionName: "verifyProof",
2722
+ abi: ZKPassportVerifierAbi.abi as any,
2723
+ }
2635
2724
  if (network === "ethereum_sepolia") {
2636
2725
  return {
2637
- address: "0xca644D3424c2ee577FaaF2b56C0f9D1937E8e87C",
2638
- abi: ZKPassportVerifierAbi.abi as any,
2726
+ ...baseConfig,
2727
+ address: "0x21E12Fa30a1F98699F242ac062Db4a8e7b344B5d",
2639
2728
  }
2640
2729
  } else if (network === "local_anvil") {
2641
2730
  return {
2642
- address: "0x0",
2643
- abi: ZKPassportVerifierAbi.abi as any,
2731
+ ...baseConfig,
2732
+ address: "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6",
2644
2733
  }
2645
2734
  }
2646
2735
  throw new Error(`Unsupported network: ${network}`)
2647
2736
  }
2648
2737
 
2649
- public getSolidityVerifierParameters(proof: ProofResult, validityPeriodInDays: number = 7) {
2738
+ public getSolidityVerifierParameters({
2739
+ proof,
2740
+ validityPeriodInDays = 7,
2741
+ domain,
2742
+ scope,
2743
+ }: {
2744
+ proof: ProofResult
2745
+ validityPeriodInDays?: number
2746
+ domain?: string
2747
+ scope?: string
2748
+ }) {
2650
2749
  if (!proof.name?.startsWith("outer_evm")) {
2651
2750
  throw new Error(
2652
2751
  "This proof cannot be verified on an EVM chain. Please make sure to use the `compressed-evm` mode.",
@@ -2779,6 +2878,8 @@ export class ZKPassport {
2779
2878
  committedInputs: `0x${compressedCommittedInputs}`,
2780
2879
  committedInputCounts: committedInputCountsArray,
2781
2880
  validityPeriodInDays,
2881
+ scope: domain ?? this.domain,
2882
+ subscope: scope ?? "",
2782
2883
  }
2783
2884
  return params
2784
2885
  }