@zkpassport/sdk 0.2.8 → 0.2.10

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
@@ -51,6 +51,33 @@ if (typeof globalThis.Buffer === "undefined") {
51
51
  }
52
52
  }
53
53
 
54
+ export type QueryResultError<T> = {
55
+ expected?: T
56
+ received?: T
57
+ message: string
58
+ }
59
+
60
+ export type QueryResultErrors = {
61
+ [key in
62
+ | IDCredential
63
+ | "sig_check_dsc"
64
+ | "sig_check_id_data"
65
+ | "data_check_integrity"
66
+ | "disclose"]: {
67
+ disclose?: QueryResultError<string | number | Date>
68
+ gte?: QueryResultError<number | Date>
69
+ lte?: QueryResultError<number | Date>
70
+ lt?: QueryResultError<number | Date>
71
+ range?: QueryResultError<[number | Date, number | Date]>
72
+ in?: QueryResultError<string[]>
73
+ out?: QueryResultError<string[]>
74
+ eq?: QueryResultError<string | number | Date>
75
+ commitment?: QueryResultError<string>
76
+ date?: QueryResultError<string>
77
+ certificate?: QueryResultError<string>
78
+ }
79
+ }
80
+
54
81
  registerLocale(i18en)
55
82
 
56
83
  function hasRequestedAccessToField(credentialsRequest: Query, field: IDCredential): boolean {
@@ -172,6 +199,7 @@ export type QueryBuilderResult = {
172
199
  uniqueIdentifier: string | undefined
173
200
  verified: boolean
174
201
  result: QueryResult
202
+ queryResultErrors?: QueryResultErrors
175
203
  }) => void,
176
204
  ) => void
177
205
  /**
@@ -235,13 +263,19 @@ export type QueryBuilder = {
235
263
  * @param key The attribute to compare.
236
264
  * @param value The list of values to check inclusion against.
237
265
  */
238
- in: <T extends "nationality">(key: T, value: IDCredentialValue<T>[]) => QueryBuilder
266
+ in: <T extends "nationality" | "issuing_country">(
267
+ key: T,
268
+ value: IDCredentialValue<T>[],
269
+ ) => QueryBuilder
239
270
  /**
240
271
  * Requires this attribute to be excluded from the provided list.
241
272
  * @param key The attribute to compare.
242
273
  * @param value The list of values to check exclusion against.
243
274
  */
244
- out: <T extends "nationality">(key: T, value: IDCredentialValue<T>[]) => QueryBuilder
275
+ out: <T extends "nationality" | "issuing_country">(
276
+ key: T,
277
+ value: IDCredentialValue<T>[],
278
+ ) => QueryBuilder
245
279
  /**
246
280
  * Requires this attribute to be disclosed.
247
281
  * @param key The attribute to disclose.
@@ -276,6 +310,7 @@ export class ZKPassport {
276
310
  > = {}
277
311
  private topicToProofs: Record<string, Array<ProofResult>> = {}
278
312
  private topicToExpectedProofCount: Record<string, number> = {}
313
+ private topicToFailedProofCount: Record<string, number> = {}
279
314
  private topicToResults: Record<string, QueryResult> = {}
280
315
 
281
316
  private onRequestReceivedCallbacks: Record<string, Array<() => void>> = {}
@@ -289,6 +324,7 @@ export class ZKPassport {
289
324
  uniqueIdentifier: string | undefined
290
325
  verified: boolean
291
326
  result: QueryResult
327
+ queryResultErrors?: QueryResultErrors
292
328
  }) => void
293
329
  >
294
330
  > = {}
@@ -315,22 +351,27 @@ export class ZKPassport {
315
351
  // Clear the results straight away to avoid concurrency issues
316
352
  delete this.topicToResults[topic]
317
353
  // Verify the proofs and extract the unique identifier (aka nullifier) and the verification result
318
- const { uniqueIdentifier, verified } = await this.verify(
354
+ const { uniqueIdentifier, verified, queryResultErrors } = await this.verify(
319
355
  topic,
320
356
  this.topicToProofs[topic],
321
357
  result,
322
358
  )
359
+ const hasFailedProofs = this.topicToFailedProofCount[topic] > 0
323
360
  await Promise.all(
324
361
  this.onResultCallbacks[topic].map((callback) =>
325
362
  callback({
326
- uniqueIdentifier,
327
- verified,
363
+ // If there are failed proofs, we don't return the unique identifier
364
+ // and we set the verified result to false
365
+ uniqueIdentifier: hasFailedProofs ? undefined : uniqueIdentifier,
366
+ verified: hasFailedProofs ? false : verified,
328
367
  result,
368
+ queryResultErrors,
329
369
  }),
330
370
  ),
331
371
  )
332
- // Clear the expected proof count
372
+ // Clear the expected proof count and failed proof count
333
373
  delete this.topicToExpectedProofCount[topic]
374
+ delete this.topicToFailedProofCount[topic]
334
375
  }
335
376
 
336
377
  private setExpectedProofCount(topic: string) {
@@ -364,13 +405,29 @@ export class ZKPassport {
364
405
  }
365
406
  break
366
407
  case "in":
367
- if (field === "nationality" && !neededCircuits.includes("inclusion_check_country")) {
368
- neededCircuits.push("inclusion_check_country")
408
+ if (
409
+ field === "nationality" &&
410
+ !neededCircuits.includes("inclusion_check_nationality")
411
+ ) {
412
+ neededCircuits.push("inclusion_check_nationality")
413
+ } else if (
414
+ field === "issuing_country" &&
415
+ !neededCircuits.includes("inclusion_check_issuing_country")
416
+ ) {
417
+ neededCircuits.push("inclusion_check_issuing_country")
369
418
  }
370
419
  break
371
420
  case "out":
372
- if (field === "nationality" && !neededCircuits.includes("exclusion_check_country")) {
373
- neededCircuits.push("exclusion_check_country")
421
+ if (
422
+ field === "nationality" &&
423
+ !neededCircuits.includes("exclusion_check_nationality")
424
+ ) {
425
+ neededCircuits.push("exclusion_check_nationality")
426
+ } else if (
427
+ field === "issuing_country" &&
428
+ !neededCircuits.includes("exclusion_check_issuing_country")
429
+ ) {
430
+ neededCircuits.push("exclusion_check_issuing_country")
374
431
  }
375
432
  break
376
433
  }
@@ -381,6 +438,7 @@ export class ZKPassport {
381
438
  // Each separate needed circuit adds 1 disclosure proof
382
439
  this.topicToExpectedProofCount[topic] =
383
440
  neededCircuits.length === 0 ? 4 : 3 + neededCircuits.length
441
+ this.topicToFailedProofCount[topic] = 0
384
442
  }
385
443
 
386
444
  /**
@@ -441,6 +499,7 @@ export class ZKPassport {
441
499
  // This means the user has an ID that is not supported yet
442
500
  // So we won't receive any proofs and we can handle the result now
443
501
  this.topicToExpectedProofCount[topic] = 0
502
+ this.topicToFailedProofCount[topic] += this.topicToExpectedProofCount[topic]
444
503
  if (this.topicToResults[topic]) {
445
504
  await this.handleResult(topic)
446
505
  }
@@ -448,6 +507,7 @@ export class ZKPassport {
448
507
  // This means one of the disclosure proofs failed to be generated
449
508
  // So we need to remove one from the expected proof count
450
509
  this.topicToExpectedProofCount[topic] -= 1
510
+ this.topicToFailedProofCount[topic] += 1
451
511
  // If the expected proof count is now equal to the number of proofs received
452
512
  // and the results were received, we can handle the result now
453
513
  if (
@@ -494,12 +554,12 @@ export class ZKPassport {
494
554
  rangeCompare(key, [start, end], topic, this.topicToConfig)
495
555
  return this.getZkPassportRequest(topic)
496
556
  },
497
- in: <T extends "nationality">(key: T, value: IDCredentialValue<T>[]) => {
557
+ in: <T extends "nationality" | "issuing_country">(key: T, value: IDCredentialValue<T>[]) => {
498
558
  value = value.map((v) => normalizeCountry(v as CountryName)) as IDCredentialValue<T>[]
499
559
  generalCompare("in", key, value, topic, this.topicToConfig)
500
560
  return this.getZkPassportRequest(topic)
501
561
  },
502
- out: <T extends "nationality">(key: T, value: IDCredentialValue<T>[]) => {
562
+ out: <T extends "nationality" | "issuing_country">(key: T, value: IDCredentialValue<T>[]) => {
503
563
  value = value.map((v) => normalizeCountry(v as CountryName)) as IDCredentialValue<T>[]
504
564
  generalCompare("out", key, value, topic, this.topicToConfig)
505
565
  return this.getZkPassportRequest(topic)
@@ -511,9 +571,6 @@ export class ZKPassport {
511
571
  }
512
572
  return this.getZkPassportRequest(topic)
513
573
  },
514
- /*checkAML: (country?: CountryName | Alpha2Code | Alpha3Code) => {
515
- return this.getZkPassportRequest(topic)
516
- },*/
517
574
  done: () => {
518
575
  const base64Config = Buffer.from(JSON.stringify(this.topicToConfig[topic])).toString(
519
576
  "base64",
@@ -539,6 +596,7 @@ export class ZKPassport {
539
596
  uniqueIdentifier: string | undefined
540
597
  verified: boolean
541
598
  result: QueryResult
599
+ queryResultErrors?: QueryResultErrors
542
600
  }) => void,
543
601
  ) => this.onResultCallbacks[topic].push(callback),
544
602
  onReject: (callback: () => void) => this.onRejectCallbacks[topic].push(callback),
@@ -690,6 +748,23 @@ export class ZKPassport {
690
748
  0,
691
749
  0,
692
750
  )
751
+ const queryResultErrors: QueryResultErrors = {
752
+ sig_check_dsc: {},
753
+ sig_check_id_data: {},
754
+ data_check_integrity: {},
755
+ disclose: {},
756
+ age: {},
757
+ birthdate: {},
758
+ expiry_date: {},
759
+ document_type: {},
760
+ issuing_country: {},
761
+ gender: {},
762
+ nationality: {},
763
+ firstname: {},
764
+ lastname: {},
765
+ fullname: {},
766
+ document_number: {},
767
+ }
693
768
 
694
769
  // Since the order is important for the commitments, we need to sort the proofs
695
770
  // by their expected order: root signature check -> ID signature check -> integrity check -> disclosure
@@ -702,8 +777,10 @@ export class ZKPassport {
702
777
  "compare_age",
703
778
  "compare_birthdate",
704
779
  "compare_expiry",
705
- "exclusion_check_country",
706
- "inclusion_check_country",
780
+ "exclusion_check_nationality",
781
+ "inclusion_check_nationality",
782
+ "exclusion_check_issuing_country",
783
+ "inclusion_check_issuing_country",
707
784
  ]
708
785
  const getIndex = (proof: ProofResult) => {
709
786
  const name = proof.name || ""
@@ -720,7 +797,11 @@ export class ZKPassport {
720
797
  if (merkleRoot !== expectedMerkleRoot) {
721
798
  console.warn("The ID was signed by an unrecognized root certificate")
722
799
  isCorrect = false
723
- break
800
+ queryResultErrors.sig_check_dsc.certificate = {
801
+ expected: `Certificate registry root: ${expectedMerkleRoot.toString()}`,
802
+ received: `Certificate registry root: ${merkleRoot.toString()}`,
803
+ message: "The ID was signed by an unrecognized root certificate",
804
+ }
724
805
  }
725
806
  } else if (proof.name?.startsWith("sig_check_id_data")) {
726
807
  commitmentIn = getCommitmentInFromIDDataProof(proofData)
@@ -729,7 +810,11 @@ export class ZKPassport {
729
810
  "Failed to check the link between the certificate signature and ID signature",
730
811
  )
731
812
  isCorrect = false
732
- break
813
+ queryResultErrors.sig_check_id_data.commitment = {
814
+ expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
815
+ received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
816
+ message: "Failed to check the link between the certificate signature and ID signature",
817
+ }
733
818
  }
734
819
  commitmentOut = getCommitmentOutFromIDDataProof(proofData)
735
820
  } else if (proof.name?.startsWith("data_check_integrity")) {
@@ -737,7 +822,11 @@ export class ZKPassport {
737
822
  if (commitmentIn !== commitmentOut) {
738
823
  console.warn("Failed to check the link between the ID signature and the data signed")
739
824
  isCorrect = false
740
- break
825
+ queryResultErrors.data_check_integrity.commitment = {
826
+ expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
827
+ received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
828
+ message: "Failed to check the link between the ID signature and the data signed",
829
+ }
741
830
  }
742
831
  commitmentOut = getCommitmentOutFromIntegrityProof(proofData)
743
832
  const currentDate = getCurrentDateFromIntegrityProof(proofData)
@@ -750,7 +839,12 @@ export class ZKPassport {
750
839
  `The date used to check the validity of the ID is older than ${this.topicToLocalConfig[topic]?.validity} days. You can ask the user to rescan their ID or ask them to disclose their expiry date`,
751
840
  )
752
841
  isCorrect = false
753
- break
842
+ queryResultErrors.data_check_integrity.date = {
843
+ expected: `Difference: ${this.topicToLocalConfig[topic]?.validity} days`,
844
+ received: `Difference: ${Math.round(todayToCurrentDate / 86400000)} days`,
845
+ message:
846
+ "The date used to check the validity of the ID is older than the validity period",
847
+ }
754
848
  }
755
849
  } else if (proof.name === "disclose_bytes") {
756
850
  commitmentIn = getCommitmentInFromDisclosureProof(proofData)
@@ -759,7 +853,12 @@ export class ZKPassport {
759
853
  "Failed to check the link between the validity of the ID and the data to disclose",
760
854
  )
761
855
  isCorrect = false
762
- break
856
+ queryResultErrors.disclose.commitment = {
857
+ expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
858
+ received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
859
+ message:
860
+ "Failed to check the link between the validity of the ID and the data to disclose",
861
+ }
763
862
  }
764
863
  // We can't be certain that the disclosed data is for a passport or an ID card
765
864
  // so we need to check both (unless the document type is revealed)
@@ -774,12 +873,20 @@ export class ZKPassport {
774
873
  ) {
775
874
  console.warn("Document type does not match the expected document type")
776
875
  isCorrect = false
777
- break
876
+ queryResultErrors.document_type.eq = {
877
+ expected: `${queryResult.document_type.eq.expected}`,
878
+ received: `${disclosedDataPassport.documentType ?? disclosedDataIDCard.documentType}`,
879
+ message: "Document type does not match the expected document type",
880
+ }
778
881
  }
779
882
  if (queryResult.document_type.disclose?.result !== disclosedDataIDCard.documentType) {
780
883
  console.warn("Document type does not match the disclosed document type in query result")
781
884
  isCorrect = false
782
- break
885
+ queryResultErrors.document_type.disclose = {
886
+ expected: `${queryResult.document_type.disclose?.result}`,
887
+ received: `${disclosedDataIDCard.documentType ?? disclosedDataPassport.documentType}`,
888
+ message: "Document type does not match the disclosed document type in query result",
889
+ }
783
890
  }
784
891
  }
785
892
  if (queryResult.birthdate) {
@@ -793,7 +900,11 @@ export class ZKPassport {
793
900
  ) {
794
901
  console.warn("Birthdate does not match the expected birthdate")
795
902
  isCorrect = false
796
- break
903
+ queryResultErrors.birthdate.eq = {
904
+ expected: `${queryResult.birthdate.eq.expected.toISOString()}`,
905
+ received: `${birthdatePassport?.toISOString() ?? birthdateIDCard?.toISOString()}`,
906
+ message: "Birthdate does not match the expected birthdate",
907
+ }
797
908
  }
798
909
  if (
799
910
  queryResult.birthdate.disclose &&
@@ -802,7 +913,11 @@ export class ZKPassport {
802
913
  ) {
803
914
  console.warn("Birthdate does not match the disclosed birthdate in query result")
804
915
  isCorrect = false
805
- break
916
+ queryResultErrors.birthdate.disclose = {
917
+ expected: `${queryResult.birthdate.disclose.result.toISOString()}`,
918
+ received: `${birthdatePassport?.toISOString() ?? birthdateIDCard?.toISOString()}`,
919
+ message: "Birthdate does not match the disclosed birthdate in query result",
920
+ }
806
921
  }
807
922
  }
808
923
  if (queryResult.expiry_date) {
@@ -816,7 +931,11 @@ export class ZKPassport {
816
931
  ) {
817
932
  console.warn("Expiry date does not match the expected expiry date")
818
933
  isCorrect = false
819
- break
934
+ queryResultErrors.expiry_date.eq = {
935
+ expected: `${queryResult.expiry_date.eq.expected.toISOString()}`,
936
+ received: `${expiryDatePassport?.toISOString() ?? expiryDateIDCard?.toISOString()}`,
937
+ message: "Expiry date does not match the expected expiry date",
938
+ }
820
939
  }
821
940
  if (
822
941
  queryResult.expiry_date.disclose &&
@@ -825,7 +944,11 @@ export class ZKPassport {
825
944
  ) {
826
945
  console.warn("Expiry date does not match the disclosed expiry date in query result")
827
946
  isCorrect = false
828
- break
947
+ queryResultErrors.expiry_date.disclose = {
948
+ expected: `${queryResult.expiry_date.disclose.result.toISOString()}`,
949
+ received: `${expiryDatePassport?.toISOString() ?? expiryDateIDCard?.toISOString()}`,
950
+ message: "Expiry date does not match the disclosed expiry date in query result",
951
+ }
829
952
  }
830
953
  }
831
954
  if (queryResult.nationality) {
@@ -839,7 +962,11 @@ export class ZKPassport {
839
962
  ) {
840
963
  console.warn("Nationality does not match the expected nationality")
841
964
  isCorrect = false
842
- break
965
+ queryResultErrors.nationality.eq = {
966
+ expected: `${queryResult.nationality.eq.expected}`,
967
+ received: `${nationalityPassport ?? nationalityIDCard}`,
968
+ message: "Nationality does not match the expected nationality",
969
+ }
843
970
  }
844
971
  if (
845
972
  queryResult.nationality.disclose &&
@@ -848,7 +975,11 @@ export class ZKPassport {
848
975
  ) {
849
976
  console.warn("Nationality does not match the disclosed nationality in query result")
850
977
  isCorrect = false
851
- break
978
+ queryResultErrors.nationality.disclose = {
979
+ expected: `${queryResult.nationality.disclose.result}`,
980
+ received: `${nationalityPassport ?? nationalityIDCard}`,
981
+ message: "Nationality does not match the disclosed nationality in query result",
982
+ }
852
983
  }
853
984
  }
854
985
  if (queryResult.document_number) {
@@ -862,7 +993,11 @@ export class ZKPassport {
862
993
  ) {
863
994
  console.warn("Document number does not match the expected document number")
864
995
  isCorrect = false
865
- break
996
+ queryResultErrors.document_number.eq = {
997
+ expected: `${queryResult.document_number.eq.expected}`,
998
+ received: `${documentNumberPassport ?? documentNumberIDCard}`,
999
+ message: "Document number does not match the expected document number",
1000
+ }
866
1001
  }
867
1002
  if (
868
1003
  queryResult.document_number.disclose &&
@@ -873,7 +1008,12 @@ export class ZKPassport {
873
1008
  "Document number does not match the disclosed document number in query result",
874
1009
  )
875
1010
  isCorrect = false
876
- break
1011
+ queryResultErrors.document_number.disclose = {
1012
+ expected: `${queryResult.document_number.disclose.result}`,
1013
+ received: `${documentNumberPassport ?? documentNumberIDCard}`,
1014
+ message:
1015
+ "Document number does not match the disclosed document number in query result",
1016
+ }
877
1017
  }
878
1018
  }
879
1019
  if (queryResult.gender) {
@@ -887,7 +1027,11 @@ export class ZKPassport {
887
1027
  ) {
888
1028
  console.warn("Gender does not match the expected gender")
889
1029
  isCorrect = false
890
- break
1030
+ queryResultErrors.gender.eq = {
1031
+ expected: `${queryResult.gender.eq.expected}`,
1032
+ received: `${genderPassport ?? genderIDCard}`,
1033
+ message: "Gender does not match the expected gender",
1034
+ }
891
1035
  }
892
1036
  if (
893
1037
  queryResult.gender.disclose &&
@@ -896,7 +1040,11 @@ export class ZKPassport {
896
1040
  ) {
897
1041
  console.warn("Gender does not match the disclosed gender in query result")
898
1042
  isCorrect = false
899
- break
1043
+ queryResultErrors.gender.disclose = {
1044
+ expected: `${queryResult.gender.disclose.result}`,
1045
+ received: `${genderPassport ?? genderIDCard}`,
1046
+ message: "Gender does not match the disclosed gender in query result",
1047
+ }
900
1048
  }
901
1049
  }
902
1050
  if (queryResult.issuing_country) {
@@ -910,7 +1058,11 @@ export class ZKPassport {
910
1058
  ) {
911
1059
  console.warn("Issuing country does not match the expected issuing country")
912
1060
  isCorrect = false
913
- break
1061
+ queryResultErrors.issuing_country.eq = {
1062
+ expected: `${queryResult.issuing_country.eq.expected}`,
1063
+ received: `${issuingCountryPassport ?? issuingCountryIDCard}`,
1064
+ message: "Issuing country does not match the expected issuing country",
1065
+ }
914
1066
  }
915
1067
  if (
916
1068
  queryResult.issuing_country.disclose &&
@@ -921,7 +1073,12 @@ export class ZKPassport {
921
1073
  "Issuing country does not match the disclosed issuing country in query result",
922
1074
  )
923
1075
  isCorrect = false
924
- break
1076
+ queryResultErrors.issuing_country.disclose = {
1077
+ expected: `${queryResult.issuing_country.disclose.result}`,
1078
+ received: `${issuingCountryPassport ?? issuingCountryIDCard}`,
1079
+ message:
1080
+ "Issuing country does not match the disclosed issuing country in query result",
1081
+ }
925
1082
  }
926
1083
  }
927
1084
  if (queryResult.fullname) {
@@ -937,7 +1094,11 @@ export class ZKPassport {
937
1094
  ) {
938
1095
  console.warn("Fullname does not match the expected fullname")
939
1096
  isCorrect = false
940
- break
1097
+ queryResultErrors.fullname.eq = {
1098
+ expected: `${queryResult.fullname.eq.expected}`,
1099
+ received: `${fullnamePassport ?? fullnameIDCard}`,
1100
+ message: "Fullname does not match the expected fullname",
1101
+ }
941
1102
  }
942
1103
  if (
943
1104
  queryResult.fullname.disclose &&
@@ -948,7 +1109,11 @@ export class ZKPassport {
948
1109
  ) {
949
1110
  console.warn("Fullname does not match the disclosed fullname in query result")
950
1111
  isCorrect = false
951
- break
1112
+ queryResultErrors.fullname.disclose = {
1113
+ expected: `${queryResult.fullname.disclose.result}`,
1114
+ received: `${fullnamePassport ?? fullnameIDCard}`,
1115
+ message: "Fullname does not match the disclosed fullname in query result",
1116
+ }
952
1117
  }
953
1118
  }
954
1119
  if (queryResult.firstname) {
@@ -971,7 +1136,11 @@ export class ZKPassport {
971
1136
  ) {
972
1137
  console.warn("Firstname does not match the expected firstname")
973
1138
  isCorrect = false
974
- break
1139
+ queryResultErrors.firstname.eq = {
1140
+ expected: `${queryResult.firstname.eq.expected}`,
1141
+ received: `${firstnamePassport ?? firstnameIDCard}`,
1142
+ message: "Firstname does not match the expected firstname",
1143
+ }
975
1144
  }
976
1145
  if (
977
1146
  queryResult.firstname.disclose &&
@@ -982,7 +1151,11 @@ export class ZKPassport {
982
1151
  ) {
983
1152
  console.warn("Firstname does not match the disclosed firstname in query result")
984
1153
  isCorrect = false
985
- break
1154
+ queryResultErrors.firstname.disclose = {
1155
+ expected: `${queryResult.firstname.disclose.result}`,
1156
+ received: `${firstnamePassport ?? firstnameIDCard}`,
1157
+ message: "Firstname does not match the disclosed firstname in query result",
1158
+ }
986
1159
  }
987
1160
  }
988
1161
  if (queryResult.lastname) {
@@ -1005,7 +1178,11 @@ export class ZKPassport {
1005
1178
  ) {
1006
1179
  console.warn("Lastname does not match the expected lastname")
1007
1180
  isCorrect = false
1008
- break
1181
+ queryResultErrors.lastname.eq = {
1182
+ expected: `${queryResult.lastname.eq.expected}`,
1183
+ received: `${lastnamePassport ?? lastnameIDCard}`,
1184
+ message: "Lastname does not match the expected lastname",
1185
+ }
1009
1186
  }
1010
1187
  if (
1011
1188
  queryResult.lastname.disclose &&
@@ -1016,7 +1193,11 @@ export class ZKPassport {
1016
1193
  ) {
1017
1194
  console.warn("Lastname does not match the disclosed lastname in query result")
1018
1195
  isCorrect = false
1019
- break
1196
+ queryResultErrors.lastname.disclose = {
1197
+ expected: `${queryResult.lastname.disclose.result}`,
1198
+ received: `${lastnamePassport ?? lastnameIDCard}`,
1199
+ message: "Lastname does not match the disclosed lastname in query result",
1200
+ }
1020
1201
  }
1021
1202
  }
1022
1203
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
@@ -1027,7 +1208,12 @@ export class ZKPassport {
1027
1208
  "Failed to check the link between the validity of the ID and the age derived from it",
1028
1209
  )
1029
1210
  isCorrect = false
1030
- break
1211
+ queryResultErrors.age.commitment = {
1212
+ expected: `Commitment: ${commitmentOut}`,
1213
+ received: `Commitment: ${commitmentIn}`,
1214
+ message:
1215
+ "Failed to check the link between the validity of the ID and the age derived from it",
1216
+ }
1031
1217
  }
1032
1218
  const minAge = getMinAgeFromProof(proofData)
1033
1219
  const maxAge = getMaxAgeFromProof(proofData)
@@ -1039,7 +1225,11 @@ export class ZKPassport {
1039
1225
  ) {
1040
1226
  console.warn("Age is not greater than or equal to the expected age")
1041
1227
  isCorrect = false
1042
- break
1228
+ queryResultErrors.age.gte = {
1229
+ expected: queryResult.age.gte.expected,
1230
+ received: minAge,
1231
+ message: "Age is not greater than or equal to the expected age",
1232
+ }
1043
1233
  }
1044
1234
  if (
1045
1235
  queryResult.age.lt &&
@@ -1048,7 +1238,11 @@ export class ZKPassport {
1048
1238
  ) {
1049
1239
  console.warn("Age is not less than the expected age")
1050
1240
  isCorrect = false
1051
- break
1241
+ queryResultErrors.age.lt = {
1242
+ expected: queryResult.age.lt.expected,
1243
+ received: maxAge,
1244
+ message: "Age is not less than the expected age",
1245
+ }
1052
1246
  }
1053
1247
  if (queryResult.age.range) {
1054
1248
  if (
@@ -1058,18 +1252,30 @@ export class ZKPassport {
1058
1252
  ) {
1059
1253
  console.warn("Age is not in the expected range")
1060
1254
  isCorrect = false
1061
- break
1255
+ queryResultErrors.age.range = {
1256
+ expected: queryResult.age.range.expected,
1257
+ received: [minAge, maxAge],
1258
+ message: "Age is not in the expected range",
1259
+ }
1062
1260
  }
1063
1261
  }
1064
1262
  if (!queryResult.age.lt && !queryResult.age.range && maxAge != 0) {
1065
1263
  console.warn("Maximum age should be equal to 0")
1066
1264
  isCorrect = false
1067
- break
1265
+ queryResultErrors.age.disclose = {
1266
+ expected: 0,
1267
+ received: maxAge,
1268
+ message: "Maximum age should be equal to 0",
1269
+ }
1068
1270
  }
1069
1271
  if (!queryResult.age.gte && !queryResult.age.range && minAge != 0) {
1070
1272
  console.warn("Minimum age should be equal to 0")
1071
1273
  isCorrect = false
1072
- break
1274
+ queryResultErrors.age.disclose = {
1275
+ expected: 0,
1276
+ received: minAge,
1277
+ message: "Minimum age should be equal to 0",
1278
+ }
1073
1279
  }
1074
1280
  if (
1075
1281
  queryResult.age.disclose &&
@@ -1078,12 +1284,18 @@ export class ZKPassport {
1078
1284
  ) {
1079
1285
  console.warn("Age does not match the disclosed age in query result")
1080
1286
  isCorrect = false
1081
- break
1287
+ queryResultErrors.age.disclose = {
1288
+ expected: `${minAge}`,
1289
+ received: `${queryResult.age.disclose.result}`,
1290
+ message: "Age does not match the disclosed age in query result",
1291
+ }
1082
1292
  }
1083
1293
  } else {
1084
1294
  console.warn("Age is not set in the query result")
1085
1295
  isCorrect = false
1086
- break
1296
+ queryResultErrors.age.disclose = {
1297
+ message: "Age is not set in the query result",
1298
+ }
1087
1299
  }
1088
1300
  const currentDate = getCurrentDateFromAgeProof(proofData)
1089
1301
  if (
@@ -1092,7 +1304,11 @@ export class ZKPassport {
1092
1304
  ) {
1093
1305
  console.warn("Current date in the proof is too old")
1094
1306
  isCorrect = false
1095
- break
1307
+ queryResultErrors.age.disclose = {
1308
+ expected: `${today.toISOString()}`,
1309
+ received: `${currentDate.toISOString()}`,
1310
+ message: "Current date in the proof is too old",
1311
+ }
1096
1312
  }
1097
1313
  uniqueIdentifier = getCommitmentInFromDisclosureProof(proofData).toString(10)
1098
1314
  } else if (proof.name === "compare_birthdate") {
@@ -1102,7 +1318,12 @@ export class ZKPassport {
1102
1318
  "Failed to check the link between the validity of the ID and the birthdate derived from it",
1103
1319
  )
1104
1320
  isCorrect = false
1105
- break
1321
+ queryResultErrors.birthdate.commitment = {
1322
+ expected: `Commitment: ${commitmentOut}`,
1323
+ received: `Commitment: ${commitmentIn}`,
1324
+ message:
1325
+ "Failed to check the link between the validity of the ID and the birthdate derived from it",
1326
+ }
1106
1327
  }
1107
1328
  const minDate = getMinDateFromProof(proofData)
1108
1329
  const maxDate = getMaxDateFromProof(proofData)
@@ -1114,7 +1335,11 @@ export class ZKPassport {
1114
1335
  ) {
1115
1336
  console.warn("Birthdate is not greater than or equal to the expected birthdate")
1116
1337
  isCorrect = false
1117
- break
1338
+ queryResultErrors.birthdate.gte = {
1339
+ expected: queryResult.birthdate.gte.expected,
1340
+ received: minDate,
1341
+ message: "Birthdate is not greater than or equal to the expected birthdate",
1342
+ }
1118
1343
  }
1119
1344
  if (
1120
1345
  queryResult.birthdate.lte &&
@@ -1123,7 +1348,11 @@ export class ZKPassport {
1123
1348
  ) {
1124
1349
  console.warn("Birthdate is not less than the expected birthdate")
1125
1350
  isCorrect = false
1126
- break
1351
+ queryResultErrors.birthdate.lte = {
1352
+ expected: queryResult.birthdate.lte.expected,
1353
+ received: maxDate,
1354
+ message: "Birthdate is not less than the expected birthdate",
1355
+ }
1127
1356
  }
1128
1357
  if (queryResult.birthdate.range) {
1129
1358
  if (
@@ -1133,7 +1362,11 @@ export class ZKPassport {
1133
1362
  ) {
1134
1363
  console.warn("Birthdate is not in the expected range")
1135
1364
  isCorrect = false
1136
- break
1365
+ queryResultErrors.birthdate.range = {
1366
+ expected: queryResult.birthdate.range.expected,
1367
+ received: [minDate, maxDate],
1368
+ message: "Birthdate is not in the expected range",
1369
+ }
1137
1370
  }
1138
1371
  }
1139
1372
  if (
@@ -1143,7 +1376,11 @@ export class ZKPassport {
1143
1376
  ) {
1144
1377
  console.warn("Maximum birthdate should be equal to default date value")
1145
1378
  isCorrect = false
1146
- break
1379
+ queryResultErrors.birthdate.disclose = {
1380
+ expected: `${defaultDateValue.toISOString()}`,
1381
+ received: `${maxDate.toISOString()}`,
1382
+ message: "Maximum birthdate should be equal to default date value",
1383
+ }
1147
1384
  }
1148
1385
  if (
1149
1386
  !queryResult.birthdate.gte &&
@@ -1152,12 +1389,18 @@ export class ZKPassport {
1152
1389
  ) {
1153
1390
  console.warn("Minimum birthdate should be equal to default date value")
1154
1391
  isCorrect = false
1155
- break
1392
+ queryResultErrors.birthdate.disclose = {
1393
+ expected: `${defaultDateValue.toISOString()}`,
1394
+ received: `${minDate.toISOString()}`,
1395
+ message: "Minimum birthdate should be equal to default date value",
1396
+ }
1156
1397
  }
1157
1398
  } else {
1158
1399
  console.warn("Birthdate is not set in the query result")
1159
1400
  isCorrect = false
1160
- break
1401
+ queryResultErrors.birthdate.disclose = {
1402
+ message: "Birthdate is not set in the query result",
1403
+ }
1161
1404
  }
1162
1405
  uniqueIdentifier = getCommitmentInFromDisclosureProof(proofData).toString(10)
1163
1406
  } else if (proof.name === "compare_expiry") {
@@ -1167,7 +1410,11 @@ export class ZKPassport {
1167
1410
  "Failed to check the link between the validity of the ID and its expiry date",
1168
1411
  )
1169
1412
  isCorrect = false
1170
- break
1413
+ queryResultErrors.expiry_date.commitment = {
1414
+ expected: `Commitment: ${commitmentOut}`,
1415
+ received: `Commitment: ${commitmentIn}`,
1416
+ message: "Failed to check the link between the validity of the ID and its expiry date",
1417
+ }
1171
1418
  }
1172
1419
  const minDate = getMinDateFromProof(proofData)
1173
1420
  const maxDate = getMaxDateFromProof(proofData)
@@ -1179,7 +1426,11 @@ export class ZKPassport {
1179
1426
  ) {
1180
1427
  console.warn("Expiry date is not greater than or equal to the expected expiry date")
1181
1428
  isCorrect = false
1182
- break
1429
+ queryResultErrors.expiry_date.gte = {
1430
+ expected: queryResult.expiry_date.gte.expected,
1431
+ received: minDate,
1432
+ message: "Expiry date is not greater than or equal to the expected expiry date",
1433
+ }
1183
1434
  }
1184
1435
  if (
1185
1436
  queryResult.expiry_date.lte &&
@@ -1188,7 +1439,11 @@ export class ZKPassport {
1188
1439
  ) {
1189
1440
  console.warn("Expiry date is not less than the expected expiry date")
1190
1441
  isCorrect = false
1191
- break
1442
+ queryResultErrors.expiry_date.lte = {
1443
+ expected: queryResult.expiry_date.lte.expected,
1444
+ received: maxDate,
1445
+ message: "Expiry date is not less than the expected expiry date",
1446
+ }
1192
1447
  }
1193
1448
  if (queryResult.expiry_date.range) {
1194
1449
  if (
@@ -1198,7 +1453,11 @@ export class ZKPassport {
1198
1453
  ) {
1199
1454
  console.warn("Expiry date is not in the expected range")
1200
1455
  isCorrect = false
1201
- break
1456
+ queryResultErrors.expiry_date.range = {
1457
+ expected: queryResult.expiry_date.range.expected,
1458
+ received: [minDate, maxDate],
1459
+ message: "Expiry date is not in the expected range",
1460
+ }
1202
1461
  }
1203
1462
  }
1204
1463
  if (
@@ -1208,7 +1467,11 @@ export class ZKPassport {
1208
1467
  ) {
1209
1468
  console.warn("Maximum expiry date should be equal to default date value")
1210
1469
  isCorrect = false
1211
- break
1470
+ queryResultErrors.expiry_date.disclose = {
1471
+ expected: `${defaultDateValue.toISOString()}`,
1472
+ received: `${maxDate.toISOString()}`,
1473
+ message: "Maximum expiry date should be equal to default date value",
1474
+ }
1212
1475
  }
1213
1476
  if (
1214
1477
  !queryResult.expiry_date.gte &&
@@ -1217,22 +1480,33 @@ export class ZKPassport {
1217
1480
  ) {
1218
1481
  console.warn("Minimum expiry date should be equal to default date value")
1219
1482
  isCorrect = false
1220
- break
1483
+ queryResultErrors.expiry_date.disclose = {
1484
+ expected: `${defaultDateValue.toISOString()}`,
1485
+ received: `${minDate.toISOString()}`,
1486
+ message: "Minimum expiry date should be equal to default date value",
1487
+ }
1221
1488
  }
1222
1489
  } else {
1223
1490
  console.warn("Expiry date is not set in the query result")
1224
1491
  isCorrect = false
1225
- break
1492
+ queryResultErrors.expiry_date.disclose = {
1493
+ message: "Expiry date is not set in the query result",
1494
+ }
1226
1495
  }
1227
1496
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
1228
- } else if (proof.name === "exclusion_check_country") {
1497
+ } else if (proof.name === "exclusion_check_nationality") {
1229
1498
  commitmentIn = getCommitmentInFromDisclosureProof(proofData)
1230
1499
  if (commitmentIn !== commitmentOut) {
1231
1500
  console.warn(
1232
- "Failed to check the link between the validity of the ID and the country exclusion check",
1501
+ "Failed to check the link between the validity of the ID and the nationality exclusion check",
1233
1502
  )
1234
1503
  isCorrect = false
1235
- break
1504
+ queryResultErrors.nationality.commitment = {
1505
+ expected: `Commitment: ${commitmentOut}`,
1506
+ received: `Commitment: ${commitmentIn}`,
1507
+ message:
1508
+ "Failed to check the link between the validity of the ID and the nationality exclusion check",
1509
+ }
1236
1510
  }
1237
1511
  const countryList = getCountryListFromExclusionProof(proofData)
1238
1512
  if (
@@ -1243,14 +1517,20 @@ export class ZKPassport {
1243
1517
  if (
1244
1518
  !queryResult.nationality.out.expected?.every((country) => countryList.includes(country))
1245
1519
  ) {
1246
- console.warn("Country exclusion list does not match the one from the query results")
1520
+ console.warn("Nationality exclusion list does not match the one from the query results")
1247
1521
  isCorrect = false
1248
- break
1522
+ queryResultErrors.nationality.out = {
1523
+ expected: queryResult.nationality.out.expected,
1524
+ received: countryList,
1525
+ message: "Nationality exclusion list does not match the one from the query results",
1526
+ }
1249
1527
  }
1250
1528
  } else if (!queryResult.nationality || !queryResult.nationality.out) {
1251
1529
  console.warn("Nationality exclusion is not set in the query result")
1252
1530
  isCorrect = false
1253
- break
1531
+ queryResultErrors.nationality.out = {
1532
+ message: "Nationality exclusion is not set in the query result",
1533
+ }
1254
1534
  }
1255
1535
  // Check the countryList is in ascending order
1256
1536
  // If the prover doesn't use a sorted list then the proof cannot be trusted
@@ -1261,18 +1541,85 @@ export class ZKPassport {
1261
1541
  "The nationality exclusion list has not been sorted, and thus the proof cannot be trusted",
1262
1542
  )
1263
1543
  isCorrect = false
1264
- break
1544
+ queryResultErrors.nationality.out = {
1545
+ message:
1546
+ "The nationality exclusion list has not been sorted, and thus the proof cannot be trusted",
1547
+ }
1265
1548
  }
1266
1549
  }
1267
1550
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
1268
- } else if (proof.name === "inclusion_check_country") {
1551
+ } else if (proof.name === "exclusion_check_issuing_country") {
1269
1552
  commitmentIn = getCommitmentInFromDisclosureProof(proofData)
1270
1553
  if (commitmentIn !== commitmentOut) {
1271
1554
  console.warn(
1272
- "Failed to check the link between the validity of the ID and the country inclusion check",
1555
+ "Failed to check the link between the validity of the ID and the issuing country exclusion check",
1273
1556
  )
1274
1557
  isCorrect = false
1275
- break
1558
+ queryResultErrors.nationality.commitment = {
1559
+ expected: `Commitment: ${commitmentOut}`,
1560
+ received: `Commitment: ${commitmentIn}`,
1561
+ message:
1562
+ "Failed to check the link between the validity of the ID and the issuing country exclusion check",
1563
+ }
1564
+ }
1565
+ const countryList = getCountryListFromExclusionProof(proofData)
1566
+ if (
1567
+ queryResult.issuing_country &&
1568
+ queryResult.issuing_country.out &&
1569
+ queryResult.issuing_country.out.result
1570
+ ) {
1571
+ if (
1572
+ !queryResult.issuing_country.out.expected?.every((country) =>
1573
+ countryList.includes(country),
1574
+ )
1575
+ ) {
1576
+ console.warn(
1577
+ "Issuing country exclusion list does not match the one from the query results",
1578
+ )
1579
+ isCorrect = false
1580
+ queryResultErrors.issuing_country.out = {
1581
+ expected: queryResult.issuing_country.out.expected,
1582
+ received: countryList,
1583
+ message:
1584
+ "Issuing country exclusion list does not match the one from the query results",
1585
+ }
1586
+ }
1587
+ } else if (!queryResult.issuing_country || !queryResult.issuing_country.out) {
1588
+ console.warn("Issuing country exclusion is not set in the query result")
1589
+ isCorrect = false
1590
+ queryResultErrors.issuing_country.out = {
1591
+ message: "Issuing country exclusion is not set in the query result",
1592
+ }
1593
+ }
1594
+ // Check the countryList is in ascending order
1595
+ // If the prover doesn't use a sorted list then the proof cannot be trusted
1596
+ // as it is requirement in the circuit for the exclusion check to work
1597
+ for (let i = 1; i < countryList.length; i++) {
1598
+ if (countryList[i] < countryList[i - 1]) {
1599
+ console.warn(
1600
+ "The issuing country exclusion list has not been sorted, and thus the proof cannot be trusted",
1601
+ )
1602
+ isCorrect = false
1603
+ queryResultErrors.issuing_country.out = {
1604
+ message:
1605
+ "The issuing country exclusion list has not been sorted, and thus the proof cannot be trusted",
1606
+ }
1607
+ }
1608
+ }
1609
+ uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
1610
+ } else if (proof.name === "inclusion_check_nationality") {
1611
+ commitmentIn = getCommitmentInFromDisclosureProof(proofData)
1612
+ if (commitmentIn !== commitmentOut) {
1613
+ console.warn(
1614
+ "Failed to check the link between the validity of the ID and the nationality inclusion check",
1615
+ )
1616
+ isCorrect = false
1617
+ queryResultErrors.nationality.commitment = {
1618
+ expected: `Commitment: ${commitmentOut}`,
1619
+ received: `Commitment: ${commitmentIn}`,
1620
+ message:
1621
+ "Failed to check the link between the validity of the ID and the nationality inclusion check",
1622
+ }
1276
1623
  }
1277
1624
  const countryList = getCountryListFromInclusionProof(proofData)
1278
1625
  if (
@@ -1283,19 +1630,69 @@ export class ZKPassport {
1283
1630
  if (
1284
1631
  !queryResult.nationality.in.expected?.every((country) => countryList.includes(country))
1285
1632
  ) {
1286
- console.warn("Country inclusion list does not match the one from the query results")
1633
+ console.warn("Nationality inclusion list does not match the one from the query results")
1287
1634
  isCorrect = false
1288
- break
1635
+ queryResultErrors.nationality.in = {
1636
+ expected: queryResult.nationality.in.expected,
1637
+ received: countryList,
1638
+ message: "Nationality inclusion list does not match the one from the query results",
1639
+ }
1289
1640
  }
1290
1641
  } else if (!queryResult.nationality || !queryResult.nationality.in) {
1291
1642
  console.warn("Nationality inclusion is not set in the query result")
1292
1643
  isCorrect = false
1293
- break
1644
+ queryResultErrors.nationality.in = {
1645
+ message: "Nationality inclusion is not set in the query result",
1646
+ }
1647
+ }
1648
+ uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
1649
+ } else if (proof.name === "inclusion_check_issuing_country") {
1650
+ commitmentIn = getCommitmentInFromDisclosureProof(proofData)
1651
+ if (commitmentIn !== commitmentOut) {
1652
+ console.warn(
1653
+ "Failed to check the link between the validity of the ID and the issuing country inclusion check",
1654
+ )
1655
+ isCorrect = false
1656
+ queryResultErrors.nationality.commitment = {
1657
+ expected: `Commitment: ${commitmentOut}`,
1658
+ received: `Commitment: ${commitmentIn}`,
1659
+ message:
1660
+ "Failed to check the link between the validity of the ID and the issuing country inclusion check",
1661
+ }
1662
+ }
1663
+ const countryList = getCountryListFromInclusionProof(proofData)
1664
+ if (
1665
+ queryResult.issuing_country &&
1666
+ queryResult.issuing_country.in &&
1667
+ queryResult.issuing_country.in.result
1668
+ ) {
1669
+ if (
1670
+ !queryResult.issuing_country.in.expected?.every((country) =>
1671
+ countryList.includes(country),
1672
+ )
1673
+ ) {
1674
+ console.warn(
1675
+ "Issuing country inclusion list does not match the one from the query results",
1676
+ )
1677
+ isCorrect = false
1678
+ queryResultErrors.issuing_country.in = {
1679
+ expected: queryResult.issuing_country.in.expected,
1680
+ received: countryList,
1681
+ message:
1682
+ "Issuing country inclusion list does not match the one from the query results",
1683
+ }
1684
+ }
1685
+ } else if (!queryResult.issuing_country || !queryResult.issuing_country.in) {
1686
+ console.warn("Issuing country inclusion is not set in the query result")
1687
+ isCorrect = false
1688
+ queryResultErrors.issuing_country.in = {
1689
+ message: "Issuing country inclusion is not set in the query result",
1690
+ }
1294
1691
  }
1295
1692
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
1296
1693
  }
1297
1694
  }
1298
- return { isCorrect, uniqueIdentifier }
1695
+ return { isCorrect, uniqueIdentifier, queryResultErrors }
1299
1696
  }
1300
1697
 
1301
1698
  /**
@@ -1310,17 +1707,15 @@ export class ZKPassport {
1310
1707
  requestId: string,
1311
1708
  proofs?: Array<ProofResult>,
1312
1709
  queryResult?: QueryResult,
1313
- ): Promise<{ uniqueIdentifier: string | undefined; verified: boolean }> {
1710
+ ): Promise<{
1711
+ uniqueIdentifier: string | undefined
1712
+ verified: boolean
1713
+ queryResultErrors?: QueryResultErrors
1714
+ }> {
1314
1715
  let proofsToVerify = proofs
1315
1716
  // There is a minimum of 4 subproofs to make a complete proof
1316
1717
  if (!proofs || proofs.length < 4) {
1317
1718
  proofsToVerify = this.topicToProofs[requestId]
1318
- if (!proofsToVerify || proofsToVerify.length < 4) {
1319
- // It may happen that a request returns a result without proofs
1320
- // Meaning the ID is not supported yet by ZKPassport circuits,
1321
- // so the results has to be trusted and cannot be independently verified
1322
- return { uniqueIdentifier: undefined, verified: false }
1323
- }
1324
1719
  }
1325
1720
  const { BarretenbergVerifier } = await import("@aztec/bb.js")
1326
1721
  const verifier = new BarretenbergVerifier()
@@ -1329,14 +1724,19 @@ export class ZKPassport {
1329
1724
  }*/
1330
1725
  let verified = true
1331
1726
  let uniqueIdentifier: string | undefined
1727
+ let queryResultErrors: QueryResultErrors | undefined
1332
1728
  if (queryResult) {
1333
- const { isCorrect, uniqueIdentifier: uniqueIdentifierFromPublicInputs } =
1334
- await this.checkPublicInputs(proofsToVerify!, queryResult!, requestId)
1729
+ const {
1730
+ isCorrect,
1731
+ uniqueIdentifier: uniqueIdentifierFromPublicInputs,
1732
+ queryResultErrors: queryResultErrorsFromPublicInputs,
1733
+ } = await this.checkPublicInputs(proofsToVerify!, queryResult!, requestId)
1335
1734
  uniqueIdentifier = uniqueIdentifierFromPublicInputs
1336
1735
  verified = isCorrect
1736
+ queryResultErrors = isCorrect ? undefined : queryResultErrorsFromPublicInputs
1337
1737
  }
1338
1738
  // Only proceed with the proof verification if the public inputs are correct
1339
- if (verified) {
1739
+ if (verified && queryResult) {
1340
1740
  for (const proof of proofsToVerify!) {
1341
1741
  const proofData = getProofData(proof.proof as string, true)
1342
1742
  const hostedPackagedCircuit = await getHostedPackagedCircuitByName(
@@ -1358,7 +1758,7 @@ export class ZKPassport {
1358
1758
  }
1359
1759
  }
1360
1760
  this.topicToProofs[requestId] = []
1361
- return { uniqueIdentifier, verified }
1761
+ return { uniqueIdentifier, verified, queryResultErrors }
1362
1762
  }
1363
1763
 
1364
1764
  /**
@@ -1382,14 +1782,17 @@ export class ZKPassport {
1382
1782
  * @param requestId The request ID.
1383
1783
  */
1384
1784
  public cancelRequest(requestId: string) {
1385
- this.topicToWebSocketClient[requestId].close()
1386
- delete this.topicToWebSocketClient[requestId]
1785
+ if (this.topicToWebSocketClient[requestId]) {
1786
+ this.topicToWebSocketClient[requestId].close()
1787
+ delete this.topicToWebSocketClient[requestId]
1788
+ }
1387
1789
  delete this.topicToKeyPair[requestId]
1388
1790
  delete this.topicToConfig[requestId]
1389
1791
  delete this.topicToLocalConfig[requestId]
1390
1792
  delete this.topicToSharedSecret[requestId]
1391
1793
  delete this.topicToProofs[requestId]
1392
1794
  delete this.topicToExpectedProofCount[requestId]
1795
+ delete this.topicToFailedProofCount[requestId]
1393
1796
  delete this.topicToResults[requestId]
1394
1797
  this.onRequestReceivedCallbacks[requestId] = []
1395
1798
  this.onGeneratingProofCallbacks[requestId] = []
@@ -1398,4 +1801,13 @@ export class ZKPassport {
1398
1801
  this.onRejectCallbacks[requestId] = []
1399
1802
  this.onErrorCallbacks[requestId] = []
1400
1803
  }
1804
+
1805
+ /**
1806
+ * @notice Clears all requests.
1807
+ */
1808
+ public clearAllRequests() {
1809
+ for (const requestId in this.topicToWebSocketClient) {
1810
+ this.cancelRequest(requestId)
1811
+ }
1812
+ }
1401
1813
  }