@zkpassport/sdk 0.2.8 → 0.2.9

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
  /**
@@ -276,6 +304,7 @@ export class ZKPassport {
276
304
  > = {}
277
305
  private topicToProofs: Record<string, Array<ProofResult>> = {}
278
306
  private topicToExpectedProofCount: Record<string, number> = {}
307
+ private topicToFailedProofCount: Record<string, number> = {}
279
308
  private topicToResults: Record<string, QueryResult> = {}
280
309
 
281
310
  private onRequestReceivedCallbacks: Record<string, Array<() => void>> = {}
@@ -289,6 +318,7 @@ export class ZKPassport {
289
318
  uniqueIdentifier: string | undefined
290
319
  verified: boolean
291
320
  result: QueryResult
321
+ queryResultErrors?: QueryResultErrors
292
322
  }) => void
293
323
  >
294
324
  > = {}
@@ -315,22 +345,27 @@ export class ZKPassport {
315
345
  // Clear the results straight away to avoid concurrency issues
316
346
  delete this.topicToResults[topic]
317
347
  // Verify the proofs and extract the unique identifier (aka nullifier) and the verification result
318
- const { uniqueIdentifier, verified } = await this.verify(
348
+ const { uniqueIdentifier, verified, queryResultErrors } = await this.verify(
319
349
  topic,
320
350
  this.topicToProofs[topic],
321
351
  result,
322
352
  )
353
+ const hasFailedProofs = this.topicToFailedProofCount[topic] > 0
323
354
  await Promise.all(
324
355
  this.onResultCallbacks[topic].map((callback) =>
325
356
  callback({
326
- uniqueIdentifier,
327
- verified,
357
+ // If there are failed proofs, we don't return the unique identifier
358
+ // and we set the verified result to false
359
+ uniqueIdentifier: hasFailedProofs ? undefined : uniqueIdentifier,
360
+ verified: hasFailedProofs ? false : verified,
328
361
  result,
362
+ queryResultErrors,
329
363
  }),
330
364
  ),
331
365
  )
332
- // Clear the expected proof count
366
+ // Clear the expected proof count and failed proof count
333
367
  delete this.topicToExpectedProofCount[topic]
368
+ delete this.topicToFailedProofCount[topic]
334
369
  }
335
370
 
336
371
  private setExpectedProofCount(topic: string) {
@@ -381,6 +416,7 @@ export class ZKPassport {
381
416
  // Each separate needed circuit adds 1 disclosure proof
382
417
  this.topicToExpectedProofCount[topic] =
383
418
  neededCircuits.length === 0 ? 4 : 3 + neededCircuits.length
419
+ this.topicToFailedProofCount[topic] = 0
384
420
  }
385
421
 
386
422
  /**
@@ -441,6 +477,7 @@ export class ZKPassport {
441
477
  // This means the user has an ID that is not supported yet
442
478
  // So we won't receive any proofs and we can handle the result now
443
479
  this.topicToExpectedProofCount[topic] = 0
480
+ this.topicToFailedProofCount[topic] += this.topicToExpectedProofCount[topic]
444
481
  if (this.topicToResults[topic]) {
445
482
  await this.handleResult(topic)
446
483
  }
@@ -448,6 +485,7 @@ export class ZKPassport {
448
485
  // This means one of the disclosure proofs failed to be generated
449
486
  // So we need to remove one from the expected proof count
450
487
  this.topicToExpectedProofCount[topic] -= 1
488
+ this.topicToFailedProofCount[topic] += 1
451
489
  // If the expected proof count is now equal to the number of proofs received
452
490
  // and the results were received, we can handle the result now
453
491
  if (
@@ -511,9 +549,6 @@ export class ZKPassport {
511
549
  }
512
550
  return this.getZkPassportRequest(topic)
513
551
  },
514
- /*checkAML: (country?: CountryName | Alpha2Code | Alpha3Code) => {
515
- return this.getZkPassportRequest(topic)
516
- },*/
517
552
  done: () => {
518
553
  const base64Config = Buffer.from(JSON.stringify(this.topicToConfig[topic])).toString(
519
554
  "base64",
@@ -539,6 +574,7 @@ export class ZKPassport {
539
574
  uniqueIdentifier: string | undefined
540
575
  verified: boolean
541
576
  result: QueryResult
577
+ queryResultErrors?: QueryResultErrors
542
578
  }) => void,
543
579
  ) => this.onResultCallbacks[topic].push(callback),
544
580
  onReject: (callback: () => void) => this.onRejectCallbacks[topic].push(callback),
@@ -690,6 +726,23 @@ export class ZKPassport {
690
726
  0,
691
727
  0,
692
728
  )
729
+ const queryResultErrors: QueryResultErrors = {
730
+ sig_check_dsc: {},
731
+ sig_check_id_data: {},
732
+ data_check_integrity: {},
733
+ disclose: {},
734
+ age: {},
735
+ birthdate: {},
736
+ expiry_date: {},
737
+ document_type: {},
738
+ issuing_country: {},
739
+ gender: {},
740
+ nationality: {},
741
+ firstname: {},
742
+ lastname: {},
743
+ fullname: {},
744
+ document_number: {},
745
+ }
693
746
 
694
747
  // Since the order is important for the commitments, we need to sort the proofs
695
748
  // by their expected order: root signature check -> ID signature check -> integrity check -> disclosure
@@ -720,7 +773,11 @@ export class ZKPassport {
720
773
  if (merkleRoot !== expectedMerkleRoot) {
721
774
  console.warn("The ID was signed by an unrecognized root certificate")
722
775
  isCorrect = false
723
- break
776
+ queryResultErrors.sig_check_dsc.certificate = {
777
+ expected: `Certificate registry root: ${expectedMerkleRoot.toString()}`,
778
+ received: `Certificate registry root: ${merkleRoot.toString()}`,
779
+ message: "The ID was signed by an unrecognized root certificate",
780
+ }
724
781
  }
725
782
  } else if (proof.name?.startsWith("sig_check_id_data")) {
726
783
  commitmentIn = getCommitmentInFromIDDataProof(proofData)
@@ -729,7 +786,11 @@ export class ZKPassport {
729
786
  "Failed to check the link between the certificate signature and ID signature",
730
787
  )
731
788
  isCorrect = false
732
- break
789
+ queryResultErrors.sig_check_id_data.commitment = {
790
+ expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
791
+ received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
792
+ message: "Failed to check the link between the certificate signature and ID signature",
793
+ }
733
794
  }
734
795
  commitmentOut = getCommitmentOutFromIDDataProof(proofData)
735
796
  } else if (proof.name?.startsWith("data_check_integrity")) {
@@ -737,7 +798,11 @@ export class ZKPassport {
737
798
  if (commitmentIn !== commitmentOut) {
738
799
  console.warn("Failed to check the link between the ID signature and the data signed")
739
800
  isCorrect = false
740
- break
801
+ queryResultErrors.data_check_integrity.commitment = {
802
+ expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
803
+ received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
804
+ message: "Failed to check the link between the ID signature and the data signed",
805
+ }
741
806
  }
742
807
  commitmentOut = getCommitmentOutFromIntegrityProof(proofData)
743
808
  const currentDate = getCurrentDateFromIntegrityProof(proofData)
@@ -750,7 +815,12 @@ export class ZKPassport {
750
815
  `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
816
  )
752
817
  isCorrect = false
753
- break
818
+ queryResultErrors.data_check_integrity.date = {
819
+ expected: `Difference: ${this.topicToLocalConfig[topic]?.validity} days`,
820
+ received: `Difference: ${Math.round(todayToCurrentDate / 86400000)} days`,
821
+ message:
822
+ "The date used to check the validity of the ID is older than the validity period",
823
+ }
754
824
  }
755
825
  } else if (proof.name === "disclose_bytes") {
756
826
  commitmentIn = getCommitmentInFromDisclosureProof(proofData)
@@ -759,7 +829,12 @@ export class ZKPassport {
759
829
  "Failed to check the link between the validity of the ID and the data to disclose",
760
830
  )
761
831
  isCorrect = false
762
- break
832
+ queryResultErrors.disclose.commitment = {
833
+ expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
834
+ received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
835
+ message:
836
+ "Failed to check the link between the validity of the ID and the data to disclose",
837
+ }
763
838
  }
764
839
  // We can't be certain that the disclosed data is for a passport or an ID card
765
840
  // so we need to check both (unless the document type is revealed)
@@ -774,12 +849,20 @@ export class ZKPassport {
774
849
  ) {
775
850
  console.warn("Document type does not match the expected document type")
776
851
  isCorrect = false
777
- break
852
+ queryResultErrors.document_type.eq = {
853
+ expected: `${queryResult.document_type.eq.expected}`,
854
+ received: `${disclosedDataPassport.documentType}`,
855
+ message: "Document type does not match the expected document type",
856
+ }
778
857
  }
779
858
  if (queryResult.document_type.disclose?.result !== disclosedDataIDCard.documentType) {
780
859
  console.warn("Document type does not match the disclosed document type in query result")
781
860
  isCorrect = false
782
- break
861
+ queryResultErrors.document_type.disclose = {
862
+ expected: `${queryResult.document_type.disclose?.result}`,
863
+ received: `${disclosedDataIDCard.documentType}`,
864
+ message: "Document type does not match the disclosed document type in query result",
865
+ }
783
866
  }
784
867
  }
785
868
  if (queryResult.birthdate) {
@@ -793,7 +876,11 @@ export class ZKPassport {
793
876
  ) {
794
877
  console.warn("Birthdate does not match the expected birthdate")
795
878
  isCorrect = false
796
- break
879
+ queryResultErrors.birthdate.eq = {
880
+ expected: `${queryResult.birthdate.eq.expected.toISOString()}`,
881
+ received: `${birthdatePassport.toISOString()}`,
882
+ message: "Birthdate does not match the expected birthdate",
883
+ }
797
884
  }
798
885
  if (
799
886
  queryResult.birthdate.disclose &&
@@ -802,7 +889,11 @@ export class ZKPassport {
802
889
  ) {
803
890
  console.warn("Birthdate does not match the disclosed birthdate in query result")
804
891
  isCorrect = false
805
- break
892
+ queryResultErrors.birthdate.disclose = {
893
+ expected: `${queryResult.birthdate.disclose.result.toISOString()}`,
894
+ received: `${birthdatePassport.toISOString()}`,
895
+ message: "Birthdate does not match the disclosed birthdate in query result",
896
+ }
806
897
  }
807
898
  }
808
899
  if (queryResult.expiry_date) {
@@ -816,7 +907,11 @@ export class ZKPassport {
816
907
  ) {
817
908
  console.warn("Expiry date does not match the expected expiry date")
818
909
  isCorrect = false
819
- break
910
+ queryResultErrors.expiry_date.eq = {
911
+ expected: `${queryResult.expiry_date.eq.expected.toISOString()}`,
912
+ received: `${expiryDatePassport.toISOString()}`,
913
+ message: "Expiry date does not match the expected expiry date",
914
+ }
820
915
  }
821
916
  if (
822
917
  queryResult.expiry_date.disclose &&
@@ -825,7 +920,11 @@ export class ZKPassport {
825
920
  ) {
826
921
  console.warn("Expiry date does not match the disclosed expiry date in query result")
827
922
  isCorrect = false
828
- break
923
+ queryResultErrors.expiry_date.disclose = {
924
+ expected: `${queryResult.expiry_date.disclose.result.toISOString()}`,
925
+ received: `${expiryDatePassport.toISOString()}`,
926
+ message: "Expiry date does not match the disclosed expiry date in query result",
927
+ }
829
928
  }
830
929
  }
831
930
  if (queryResult.nationality) {
@@ -839,7 +938,11 @@ export class ZKPassport {
839
938
  ) {
840
939
  console.warn("Nationality does not match the expected nationality")
841
940
  isCorrect = false
842
- break
941
+ queryResultErrors.nationality.eq = {
942
+ expected: `${queryResult.nationality.eq.expected}`,
943
+ received: `${nationalityPassport}`,
944
+ message: "Nationality does not match the expected nationality",
945
+ }
843
946
  }
844
947
  if (
845
948
  queryResult.nationality.disclose &&
@@ -848,7 +951,11 @@ export class ZKPassport {
848
951
  ) {
849
952
  console.warn("Nationality does not match the disclosed nationality in query result")
850
953
  isCorrect = false
851
- break
954
+ queryResultErrors.nationality.disclose = {
955
+ expected: `${queryResult.nationality.disclose.result}`,
956
+ received: `${nationalityPassport}`,
957
+ message: "Nationality does not match the disclosed nationality in query result",
958
+ }
852
959
  }
853
960
  }
854
961
  if (queryResult.document_number) {
@@ -862,7 +969,11 @@ export class ZKPassport {
862
969
  ) {
863
970
  console.warn("Document number does not match the expected document number")
864
971
  isCorrect = false
865
- break
972
+ queryResultErrors.document_number.eq = {
973
+ expected: `${queryResult.document_number.eq.expected}`,
974
+ received: `${documentNumberPassport}`,
975
+ message: "Document number does not match the expected document number",
976
+ }
866
977
  }
867
978
  if (
868
979
  queryResult.document_number.disclose &&
@@ -873,7 +984,12 @@ export class ZKPassport {
873
984
  "Document number does not match the disclosed document number in query result",
874
985
  )
875
986
  isCorrect = false
876
- break
987
+ queryResultErrors.document_number.disclose = {
988
+ expected: `${queryResult.document_number.disclose.result}`,
989
+ received: `${documentNumberPassport}`,
990
+ message:
991
+ "Document number does not match the disclosed document number in query result",
992
+ }
877
993
  }
878
994
  }
879
995
  if (queryResult.gender) {
@@ -887,7 +1003,11 @@ export class ZKPassport {
887
1003
  ) {
888
1004
  console.warn("Gender does not match the expected gender")
889
1005
  isCorrect = false
890
- break
1006
+ queryResultErrors.gender.eq = {
1007
+ expected: `${queryResult.gender.eq.expected}`,
1008
+ received: `${genderPassport}`,
1009
+ message: "Gender does not match the expected gender",
1010
+ }
891
1011
  }
892
1012
  if (
893
1013
  queryResult.gender.disclose &&
@@ -896,7 +1016,11 @@ export class ZKPassport {
896
1016
  ) {
897
1017
  console.warn("Gender does not match the disclosed gender in query result")
898
1018
  isCorrect = false
899
- break
1019
+ queryResultErrors.gender.disclose = {
1020
+ expected: `${queryResult.gender.disclose.result}`,
1021
+ received: `${genderPassport}`,
1022
+ message: "Gender does not match the disclosed gender in query result",
1023
+ }
900
1024
  }
901
1025
  }
902
1026
  if (queryResult.issuing_country) {
@@ -910,7 +1034,11 @@ export class ZKPassport {
910
1034
  ) {
911
1035
  console.warn("Issuing country does not match the expected issuing country")
912
1036
  isCorrect = false
913
- break
1037
+ queryResultErrors.issuing_country.eq = {
1038
+ expected: `${queryResult.issuing_country.eq.expected}`,
1039
+ received: `${issuingCountryPassport}`,
1040
+ message: "Issuing country does not match the expected issuing country",
1041
+ }
914
1042
  }
915
1043
  if (
916
1044
  queryResult.issuing_country.disclose &&
@@ -921,7 +1049,12 @@ export class ZKPassport {
921
1049
  "Issuing country does not match the disclosed issuing country in query result",
922
1050
  )
923
1051
  isCorrect = false
924
- break
1052
+ queryResultErrors.issuing_country.disclose = {
1053
+ expected: `${queryResult.issuing_country.disclose.result}`,
1054
+ received: `${issuingCountryPassport}`,
1055
+ message:
1056
+ "Issuing country does not match the disclosed issuing country in query result",
1057
+ }
925
1058
  }
926
1059
  }
927
1060
  if (queryResult.fullname) {
@@ -937,7 +1070,11 @@ export class ZKPassport {
937
1070
  ) {
938
1071
  console.warn("Fullname does not match the expected fullname")
939
1072
  isCorrect = false
940
- break
1073
+ queryResultErrors.fullname.eq = {
1074
+ expected: `${queryResult.fullname.eq.expected}`,
1075
+ received: `${fullnamePassport}`,
1076
+ message: "Fullname does not match the expected fullname",
1077
+ }
941
1078
  }
942
1079
  if (
943
1080
  queryResult.fullname.disclose &&
@@ -948,7 +1085,11 @@ export class ZKPassport {
948
1085
  ) {
949
1086
  console.warn("Fullname does not match the disclosed fullname in query result")
950
1087
  isCorrect = false
951
- break
1088
+ queryResultErrors.fullname.disclose = {
1089
+ expected: `${queryResult.fullname.disclose.result}`,
1090
+ received: `${fullnamePassport}`,
1091
+ message: "Fullname does not match the disclosed fullname in query result",
1092
+ }
952
1093
  }
953
1094
  }
954
1095
  if (queryResult.firstname) {
@@ -971,7 +1112,11 @@ export class ZKPassport {
971
1112
  ) {
972
1113
  console.warn("Firstname does not match the expected firstname")
973
1114
  isCorrect = false
974
- break
1115
+ queryResultErrors.firstname.eq = {
1116
+ expected: `${queryResult.firstname.eq.expected}`,
1117
+ received: `${firstnamePassport}`,
1118
+ message: "Firstname does not match the expected firstname",
1119
+ }
975
1120
  }
976
1121
  if (
977
1122
  queryResult.firstname.disclose &&
@@ -982,7 +1127,11 @@ export class ZKPassport {
982
1127
  ) {
983
1128
  console.warn("Firstname does not match the disclosed firstname in query result")
984
1129
  isCorrect = false
985
- break
1130
+ queryResultErrors.firstname.disclose = {
1131
+ expected: `${queryResult.firstname.disclose.result}`,
1132
+ received: `${firstnamePassport}`,
1133
+ message: "Firstname does not match the disclosed firstname in query result",
1134
+ }
986
1135
  }
987
1136
  }
988
1137
  if (queryResult.lastname) {
@@ -1005,7 +1154,11 @@ export class ZKPassport {
1005
1154
  ) {
1006
1155
  console.warn("Lastname does not match the expected lastname")
1007
1156
  isCorrect = false
1008
- break
1157
+ queryResultErrors.lastname.eq = {
1158
+ expected: `${queryResult.lastname.eq.expected}`,
1159
+ received: `${lastnamePassport}`,
1160
+ message: "Lastname does not match the expected lastname",
1161
+ }
1009
1162
  }
1010
1163
  if (
1011
1164
  queryResult.lastname.disclose &&
@@ -1016,7 +1169,11 @@ export class ZKPassport {
1016
1169
  ) {
1017
1170
  console.warn("Lastname does not match the disclosed lastname in query result")
1018
1171
  isCorrect = false
1019
- break
1172
+ queryResultErrors.lastname.disclose = {
1173
+ expected: `${queryResult.lastname.disclose.result}`,
1174
+ received: `${lastnamePassport}`,
1175
+ message: "Lastname does not match the disclosed lastname in query result",
1176
+ }
1020
1177
  }
1021
1178
  }
1022
1179
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
@@ -1027,7 +1184,12 @@ export class ZKPassport {
1027
1184
  "Failed to check the link between the validity of the ID and the age derived from it",
1028
1185
  )
1029
1186
  isCorrect = false
1030
- break
1187
+ queryResultErrors.age.commitment = {
1188
+ expected: `Commitment: ${commitmentOut}`,
1189
+ received: `Commitment: ${commitmentIn}`,
1190
+ message:
1191
+ "Failed to check the link between the validity of the ID and the age derived from it",
1192
+ }
1031
1193
  }
1032
1194
  const minAge = getMinAgeFromProof(proofData)
1033
1195
  const maxAge = getMaxAgeFromProof(proofData)
@@ -1039,7 +1201,11 @@ export class ZKPassport {
1039
1201
  ) {
1040
1202
  console.warn("Age is not greater than or equal to the expected age")
1041
1203
  isCorrect = false
1042
- break
1204
+ queryResultErrors.age.gte = {
1205
+ expected: queryResult.age.gte.expected,
1206
+ received: minAge,
1207
+ message: "Age is not greater than or equal to the expected age",
1208
+ }
1043
1209
  }
1044
1210
  if (
1045
1211
  queryResult.age.lt &&
@@ -1048,7 +1214,11 @@ export class ZKPassport {
1048
1214
  ) {
1049
1215
  console.warn("Age is not less than the expected age")
1050
1216
  isCorrect = false
1051
- break
1217
+ queryResultErrors.age.lt = {
1218
+ expected: queryResult.age.lt.expected,
1219
+ received: maxAge,
1220
+ message: "Age is not less than the expected age",
1221
+ }
1052
1222
  }
1053
1223
  if (queryResult.age.range) {
1054
1224
  if (
@@ -1058,18 +1228,30 @@ export class ZKPassport {
1058
1228
  ) {
1059
1229
  console.warn("Age is not in the expected range")
1060
1230
  isCorrect = false
1061
- break
1231
+ queryResultErrors.age.range = {
1232
+ expected: queryResult.age.range.expected,
1233
+ received: [minAge, maxAge],
1234
+ message: "Age is not in the expected range",
1235
+ }
1062
1236
  }
1063
1237
  }
1064
1238
  if (!queryResult.age.lt && !queryResult.age.range && maxAge != 0) {
1065
1239
  console.warn("Maximum age should be equal to 0")
1066
1240
  isCorrect = false
1067
- break
1241
+ queryResultErrors.age.disclose = {
1242
+ expected: 0,
1243
+ received: maxAge,
1244
+ message: "Maximum age should be equal to 0",
1245
+ }
1068
1246
  }
1069
1247
  if (!queryResult.age.gte && !queryResult.age.range && minAge != 0) {
1070
1248
  console.warn("Minimum age should be equal to 0")
1071
1249
  isCorrect = false
1072
- break
1250
+ queryResultErrors.age.disclose = {
1251
+ expected: 0,
1252
+ received: minAge,
1253
+ message: "Minimum age should be equal to 0",
1254
+ }
1073
1255
  }
1074
1256
  if (
1075
1257
  queryResult.age.disclose &&
@@ -1078,12 +1260,18 @@ export class ZKPassport {
1078
1260
  ) {
1079
1261
  console.warn("Age does not match the disclosed age in query result")
1080
1262
  isCorrect = false
1081
- break
1263
+ queryResultErrors.age.disclose = {
1264
+ expected: `${minAge}`,
1265
+ received: `${queryResult.age.disclose.result}`,
1266
+ message: "Age does not match the disclosed age in query result",
1267
+ }
1082
1268
  }
1083
1269
  } else {
1084
1270
  console.warn("Age is not set in the query result")
1085
1271
  isCorrect = false
1086
- break
1272
+ queryResultErrors.age.disclose = {
1273
+ message: "Age is not set in the query result",
1274
+ }
1087
1275
  }
1088
1276
  const currentDate = getCurrentDateFromAgeProof(proofData)
1089
1277
  if (
@@ -1092,7 +1280,11 @@ export class ZKPassport {
1092
1280
  ) {
1093
1281
  console.warn("Current date in the proof is too old")
1094
1282
  isCorrect = false
1095
- break
1283
+ queryResultErrors.age.disclose = {
1284
+ expected: `${today.toISOString()}`,
1285
+ received: `${currentDate.toISOString()}`,
1286
+ message: "Current date in the proof is too old",
1287
+ }
1096
1288
  }
1097
1289
  uniqueIdentifier = getCommitmentInFromDisclosureProof(proofData).toString(10)
1098
1290
  } else if (proof.name === "compare_birthdate") {
@@ -1102,7 +1294,12 @@ export class ZKPassport {
1102
1294
  "Failed to check the link between the validity of the ID and the birthdate derived from it",
1103
1295
  )
1104
1296
  isCorrect = false
1105
- break
1297
+ queryResultErrors.birthdate.commitment = {
1298
+ expected: `Commitment: ${commitmentOut}`,
1299
+ received: `Commitment: ${commitmentIn}`,
1300
+ message:
1301
+ "Failed to check the link between the validity of the ID and the birthdate derived from it",
1302
+ }
1106
1303
  }
1107
1304
  const minDate = getMinDateFromProof(proofData)
1108
1305
  const maxDate = getMaxDateFromProof(proofData)
@@ -1114,7 +1311,11 @@ export class ZKPassport {
1114
1311
  ) {
1115
1312
  console.warn("Birthdate is not greater than or equal to the expected birthdate")
1116
1313
  isCorrect = false
1117
- break
1314
+ queryResultErrors.birthdate.gte = {
1315
+ expected: queryResult.birthdate.gte.expected,
1316
+ received: minDate,
1317
+ message: "Birthdate is not greater than or equal to the expected birthdate",
1318
+ }
1118
1319
  }
1119
1320
  if (
1120
1321
  queryResult.birthdate.lte &&
@@ -1123,7 +1324,11 @@ export class ZKPassport {
1123
1324
  ) {
1124
1325
  console.warn("Birthdate is not less than the expected birthdate")
1125
1326
  isCorrect = false
1126
- break
1327
+ queryResultErrors.birthdate.lte = {
1328
+ expected: queryResult.birthdate.lte.expected,
1329
+ received: maxDate,
1330
+ message: "Birthdate is not less than the expected birthdate",
1331
+ }
1127
1332
  }
1128
1333
  if (queryResult.birthdate.range) {
1129
1334
  if (
@@ -1133,7 +1338,11 @@ export class ZKPassport {
1133
1338
  ) {
1134
1339
  console.warn("Birthdate is not in the expected range")
1135
1340
  isCorrect = false
1136
- break
1341
+ queryResultErrors.birthdate.range = {
1342
+ expected: queryResult.birthdate.range.expected,
1343
+ received: [minDate, maxDate],
1344
+ message: "Birthdate is not in the expected range",
1345
+ }
1137
1346
  }
1138
1347
  }
1139
1348
  if (
@@ -1143,7 +1352,11 @@ export class ZKPassport {
1143
1352
  ) {
1144
1353
  console.warn("Maximum birthdate should be equal to default date value")
1145
1354
  isCorrect = false
1146
- break
1355
+ queryResultErrors.birthdate.disclose = {
1356
+ expected: `${defaultDateValue.toISOString()}`,
1357
+ received: `${maxDate.toISOString()}`,
1358
+ message: "Maximum birthdate should be equal to default date value",
1359
+ }
1147
1360
  }
1148
1361
  if (
1149
1362
  !queryResult.birthdate.gte &&
@@ -1152,12 +1365,18 @@ export class ZKPassport {
1152
1365
  ) {
1153
1366
  console.warn("Minimum birthdate should be equal to default date value")
1154
1367
  isCorrect = false
1155
- break
1368
+ queryResultErrors.birthdate.disclose = {
1369
+ expected: `${defaultDateValue.toISOString()}`,
1370
+ received: `${minDate.toISOString()}`,
1371
+ message: "Minimum birthdate should be equal to default date value",
1372
+ }
1156
1373
  }
1157
1374
  } else {
1158
1375
  console.warn("Birthdate is not set in the query result")
1159
1376
  isCorrect = false
1160
- break
1377
+ queryResultErrors.birthdate.disclose = {
1378
+ message: "Birthdate is not set in the query result",
1379
+ }
1161
1380
  }
1162
1381
  uniqueIdentifier = getCommitmentInFromDisclosureProof(proofData).toString(10)
1163
1382
  } else if (proof.name === "compare_expiry") {
@@ -1167,7 +1386,11 @@ export class ZKPassport {
1167
1386
  "Failed to check the link between the validity of the ID and its expiry date",
1168
1387
  )
1169
1388
  isCorrect = false
1170
- break
1389
+ queryResultErrors.expiry_date.commitment = {
1390
+ expected: `Commitment: ${commitmentOut}`,
1391
+ received: `Commitment: ${commitmentIn}`,
1392
+ message: "Failed to check the link between the validity of the ID and its expiry date",
1393
+ }
1171
1394
  }
1172
1395
  const minDate = getMinDateFromProof(proofData)
1173
1396
  const maxDate = getMaxDateFromProof(proofData)
@@ -1179,7 +1402,11 @@ export class ZKPassport {
1179
1402
  ) {
1180
1403
  console.warn("Expiry date is not greater than or equal to the expected expiry date")
1181
1404
  isCorrect = false
1182
- break
1405
+ queryResultErrors.expiry_date.gte = {
1406
+ expected: queryResult.expiry_date.gte.expected,
1407
+ received: minDate,
1408
+ message: "Expiry date is not greater than or equal to the expected expiry date",
1409
+ }
1183
1410
  }
1184
1411
  if (
1185
1412
  queryResult.expiry_date.lte &&
@@ -1188,7 +1415,11 @@ export class ZKPassport {
1188
1415
  ) {
1189
1416
  console.warn("Expiry date is not less than the expected expiry date")
1190
1417
  isCorrect = false
1191
- break
1418
+ queryResultErrors.expiry_date.lte = {
1419
+ expected: queryResult.expiry_date.lte.expected,
1420
+ received: maxDate,
1421
+ message: "Expiry date is not less than the expected expiry date",
1422
+ }
1192
1423
  }
1193
1424
  if (queryResult.expiry_date.range) {
1194
1425
  if (
@@ -1198,7 +1429,11 @@ export class ZKPassport {
1198
1429
  ) {
1199
1430
  console.warn("Expiry date is not in the expected range")
1200
1431
  isCorrect = false
1201
- break
1432
+ queryResultErrors.expiry_date.range = {
1433
+ expected: queryResult.expiry_date.range.expected,
1434
+ received: [minDate, maxDate],
1435
+ message: "Expiry date is not in the expected range",
1436
+ }
1202
1437
  }
1203
1438
  }
1204
1439
  if (
@@ -1208,7 +1443,11 @@ export class ZKPassport {
1208
1443
  ) {
1209
1444
  console.warn("Maximum expiry date should be equal to default date value")
1210
1445
  isCorrect = false
1211
- break
1446
+ queryResultErrors.expiry_date.disclose = {
1447
+ expected: `${defaultDateValue.toISOString()}`,
1448
+ received: `${maxDate.toISOString()}`,
1449
+ message: "Maximum expiry date should be equal to default date value",
1450
+ }
1212
1451
  }
1213
1452
  if (
1214
1453
  !queryResult.expiry_date.gte &&
@@ -1217,12 +1456,18 @@ export class ZKPassport {
1217
1456
  ) {
1218
1457
  console.warn("Minimum expiry date should be equal to default date value")
1219
1458
  isCorrect = false
1220
- break
1459
+ queryResultErrors.expiry_date.disclose = {
1460
+ expected: `${defaultDateValue.toISOString()}`,
1461
+ received: `${minDate.toISOString()}`,
1462
+ message: "Minimum expiry date should be equal to default date value",
1463
+ }
1221
1464
  }
1222
1465
  } else {
1223
1466
  console.warn("Expiry date is not set in the query result")
1224
1467
  isCorrect = false
1225
- break
1468
+ queryResultErrors.expiry_date.disclose = {
1469
+ message: "Expiry date is not set in the query result",
1470
+ }
1226
1471
  }
1227
1472
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
1228
1473
  } else if (proof.name === "exclusion_check_country") {
@@ -1232,7 +1477,12 @@ export class ZKPassport {
1232
1477
  "Failed to check the link between the validity of the ID and the country exclusion check",
1233
1478
  )
1234
1479
  isCorrect = false
1235
- break
1480
+ queryResultErrors.nationality.commitment = {
1481
+ expected: `Commitment: ${commitmentOut}`,
1482
+ received: `Commitment: ${commitmentIn}`,
1483
+ message:
1484
+ "Failed to check the link between the validity of the ID and the country exclusion check",
1485
+ }
1236
1486
  }
1237
1487
  const countryList = getCountryListFromExclusionProof(proofData)
1238
1488
  if (
@@ -1245,12 +1495,18 @@ export class ZKPassport {
1245
1495
  ) {
1246
1496
  console.warn("Country exclusion list does not match the one from the query results")
1247
1497
  isCorrect = false
1248
- break
1498
+ queryResultErrors.nationality.out = {
1499
+ expected: queryResult.nationality.out.expected,
1500
+ received: countryList,
1501
+ message: "Country exclusion list does not match the one from the query results",
1502
+ }
1249
1503
  }
1250
1504
  } else if (!queryResult.nationality || !queryResult.nationality.out) {
1251
1505
  console.warn("Nationality exclusion is not set in the query result")
1252
1506
  isCorrect = false
1253
- break
1507
+ queryResultErrors.nationality.out = {
1508
+ message: "Nationality exclusion is not set in the query result",
1509
+ }
1254
1510
  }
1255
1511
  // Check the countryList is in ascending order
1256
1512
  // If the prover doesn't use a sorted list then the proof cannot be trusted
@@ -1261,7 +1517,10 @@ export class ZKPassport {
1261
1517
  "The nationality exclusion list has not been sorted, and thus the proof cannot be trusted",
1262
1518
  )
1263
1519
  isCorrect = false
1264
- break
1520
+ queryResultErrors.nationality.out = {
1521
+ message:
1522
+ "The nationality exclusion list has not been sorted, and thus the proof cannot be trusted",
1523
+ }
1265
1524
  }
1266
1525
  }
1267
1526
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
@@ -1272,7 +1531,12 @@ export class ZKPassport {
1272
1531
  "Failed to check the link between the validity of the ID and the country inclusion check",
1273
1532
  )
1274
1533
  isCorrect = false
1275
- break
1534
+ queryResultErrors.nationality.commitment = {
1535
+ expected: `Commitment: ${commitmentOut}`,
1536
+ received: `Commitment: ${commitmentIn}`,
1537
+ message:
1538
+ "Failed to check the link between the validity of the ID and the country inclusion check",
1539
+ }
1276
1540
  }
1277
1541
  const countryList = getCountryListFromInclusionProof(proofData)
1278
1542
  if (
@@ -1285,17 +1549,23 @@ export class ZKPassport {
1285
1549
  ) {
1286
1550
  console.warn("Country inclusion list does not match the one from the query results")
1287
1551
  isCorrect = false
1288
- break
1552
+ queryResultErrors.nationality.in = {
1553
+ expected: queryResult.nationality.in.expected,
1554
+ received: countryList,
1555
+ message: "Country inclusion list does not match the one from the query results",
1556
+ }
1289
1557
  }
1290
1558
  } else if (!queryResult.nationality || !queryResult.nationality.in) {
1291
1559
  console.warn("Nationality inclusion is not set in the query result")
1292
1560
  isCorrect = false
1293
- break
1561
+ queryResultErrors.nationality.in = {
1562
+ message: "Nationality inclusion is not set in the query result",
1563
+ }
1294
1564
  }
1295
1565
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
1296
1566
  }
1297
1567
  }
1298
- return { isCorrect, uniqueIdentifier }
1568
+ return { isCorrect, uniqueIdentifier, queryResultErrors }
1299
1569
  }
1300
1570
 
1301
1571
  /**
@@ -1310,17 +1580,15 @@ export class ZKPassport {
1310
1580
  requestId: string,
1311
1581
  proofs?: Array<ProofResult>,
1312
1582
  queryResult?: QueryResult,
1313
- ): Promise<{ uniqueIdentifier: string | undefined; verified: boolean }> {
1583
+ ): Promise<{
1584
+ uniqueIdentifier: string | undefined
1585
+ verified: boolean
1586
+ queryResultErrors?: QueryResultErrors
1587
+ }> {
1314
1588
  let proofsToVerify = proofs
1315
1589
  // There is a minimum of 4 subproofs to make a complete proof
1316
1590
  if (!proofs || proofs.length < 4) {
1317
1591
  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
1592
  }
1325
1593
  const { BarretenbergVerifier } = await import("@aztec/bb.js")
1326
1594
  const verifier = new BarretenbergVerifier()
@@ -1329,14 +1597,19 @@ export class ZKPassport {
1329
1597
  }*/
1330
1598
  let verified = true
1331
1599
  let uniqueIdentifier: string | undefined
1600
+ let queryResultErrors: QueryResultErrors | undefined
1332
1601
  if (queryResult) {
1333
- const { isCorrect, uniqueIdentifier: uniqueIdentifierFromPublicInputs } =
1334
- await this.checkPublicInputs(proofsToVerify!, queryResult!, requestId)
1602
+ const {
1603
+ isCorrect,
1604
+ uniqueIdentifier: uniqueIdentifierFromPublicInputs,
1605
+ queryResultErrors: queryResultErrorsFromPublicInputs,
1606
+ } = await this.checkPublicInputs(proofsToVerify!, queryResult!, requestId)
1335
1607
  uniqueIdentifier = uniqueIdentifierFromPublicInputs
1336
1608
  verified = isCorrect
1609
+ queryResultErrors = isCorrect ? undefined : queryResultErrorsFromPublicInputs
1337
1610
  }
1338
1611
  // Only proceed with the proof verification if the public inputs are correct
1339
- if (verified) {
1612
+ if (verified && queryResult) {
1340
1613
  for (const proof of proofsToVerify!) {
1341
1614
  const proofData = getProofData(proof.proof as string, true)
1342
1615
  const hostedPackagedCircuit = await getHostedPackagedCircuitByName(
@@ -1358,7 +1631,7 @@ export class ZKPassport {
1358
1631
  }
1359
1632
  }
1360
1633
  this.topicToProofs[requestId] = []
1361
- return { uniqueIdentifier, verified }
1634
+ return { uniqueIdentifier, verified, queryResultErrors }
1362
1635
  }
1363
1636
 
1364
1637
  /**
@@ -1382,14 +1655,17 @@ export class ZKPassport {
1382
1655
  * @param requestId The request ID.
1383
1656
  */
1384
1657
  public cancelRequest(requestId: string) {
1385
- this.topicToWebSocketClient[requestId].close()
1386
- delete this.topicToWebSocketClient[requestId]
1658
+ if (this.topicToWebSocketClient[requestId]) {
1659
+ this.topicToWebSocketClient[requestId].close()
1660
+ delete this.topicToWebSocketClient[requestId]
1661
+ }
1387
1662
  delete this.topicToKeyPair[requestId]
1388
1663
  delete this.topicToConfig[requestId]
1389
1664
  delete this.topicToLocalConfig[requestId]
1390
1665
  delete this.topicToSharedSecret[requestId]
1391
1666
  delete this.topicToProofs[requestId]
1392
1667
  delete this.topicToExpectedProofCount[requestId]
1668
+ delete this.topicToFailedProofCount[requestId]
1393
1669
  delete this.topicToResults[requestId]
1394
1670
  this.onRequestReceivedCallbacks[requestId] = []
1395
1671
  this.onGeneratingProofCallbacks[requestId] = []
@@ -1398,4 +1674,13 @@ export class ZKPassport {
1398
1674
  this.onRejectCallbacks[requestId] = []
1399
1675
  this.onErrorCallbacks[requestId] = []
1400
1676
  }
1677
+
1678
+ /**
1679
+ * @notice Clears all requests.
1680
+ */
1681
+ public clearAllRequests() {
1682
+ for (const requestId in this.topicToWebSocketClient) {
1683
+ this.cancelRequest(requestId)
1684
+ }
1685
+ }
1401
1686
  }