@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/dist/cjs/index.js CHANGED
@@ -80,6 +80,7 @@ class ZKPassport {
80
80
  this.topicToService = {};
81
81
  this.topicToProofs = {};
82
82
  this.topicToExpectedProofCount = {};
83
+ this.topicToFailedProofCount = {};
83
84
  this.topicToResults = {};
84
85
  this.onRequestReceivedCallbacks = {};
85
86
  this.onGeneratingProofCallbacks = {};
@@ -104,14 +105,19 @@ class ZKPassport {
104
105
  // Clear the results straight away to avoid concurrency issues
105
106
  delete this.topicToResults[topic];
106
107
  // Verify the proofs and extract the unique identifier (aka nullifier) and the verification result
107
- const { uniqueIdentifier, verified } = await this.verify(topic, this.topicToProofs[topic], result);
108
+ const { uniqueIdentifier, verified, queryResultErrors } = await this.verify(topic, this.topicToProofs[topic], result);
109
+ const hasFailedProofs = this.topicToFailedProofCount[topic] > 0;
108
110
  await Promise.all(this.onResultCallbacks[topic].map((callback) => callback({
109
- uniqueIdentifier,
110
- verified,
111
+ // If there are failed proofs, we don't return the unique identifier
112
+ // and we set the verified result to false
113
+ uniqueIdentifier: hasFailedProofs ? undefined : uniqueIdentifier,
114
+ verified: hasFailedProofs ? false : verified,
111
115
  result,
116
+ queryResultErrors,
112
117
  })));
113
- // Clear the expected proof count
118
+ // Clear the expected proof count and failed proof count
114
119
  delete this.topicToExpectedProofCount[topic];
120
+ delete this.topicToFailedProofCount[topic];
115
121
  }
116
122
  setExpectedProofCount(topic) {
117
123
  const fields = Object.keys(this.topicToConfig[topic]).filter((key) => hasRequestedAccessToField(this.topicToConfig[topic], key));
@@ -145,13 +151,23 @@ class ZKPassport {
145
151
  }
146
152
  break;
147
153
  case "in":
148
- if (field === "nationality" && !neededCircuits.includes("inclusion_check_country")) {
149
- neededCircuits.push("inclusion_check_country");
154
+ if (field === "nationality" &&
155
+ !neededCircuits.includes("inclusion_check_nationality")) {
156
+ neededCircuits.push("inclusion_check_nationality");
157
+ }
158
+ else if (field === "issuing_country" &&
159
+ !neededCircuits.includes("inclusion_check_issuing_country")) {
160
+ neededCircuits.push("inclusion_check_issuing_country");
150
161
  }
151
162
  break;
152
163
  case "out":
153
- if (field === "nationality" && !neededCircuits.includes("exclusion_check_country")) {
154
- neededCircuits.push("exclusion_check_country");
164
+ if (field === "nationality" &&
165
+ !neededCircuits.includes("exclusion_check_nationality")) {
166
+ neededCircuits.push("exclusion_check_nationality");
167
+ }
168
+ else if (field === "issuing_country" &&
169
+ !neededCircuits.includes("exclusion_check_issuing_country")) {
170
+ neededCircuits.push("exclusion_check_issuing_country");
155
171
  }
156
172
  break;
157
173
  }
@@ -162,6 +178,7 @@ class ZKPassport {
162
178
  // Each separate needed circuit adds 1 disclosure proof
163
179
  this.topicToExpectedProofCount[topic] =
164
180
  neededCircuits.length === 0 ? 4 : 3 + neededCircuits.length;
181
+ this.topicToFailedProofCount[topic] = 0;
165
182
  }
166
183
  /**
167
184
  * @notice Handle an encrypted message.
@@ -217,6 +234,7 @@ class ZKPassport {
217
234
  // This means the user has an ID that is not supported yet
218
235
  // So we won't receive any proofs and we can handle the result now
219
236
  this.topicToExpectedProofCount[topic] = 0;
237
+ this.topicToFailedProofCount[topic] += this.topicToExpectedProofCount[topic];
220
238
  if (this.topicToResults[topic]) {
221
239
  await this.handleResult(topic);
222
240
  }
@@ -225,6 +243,7 @@ class ZKPassport {
225
243
  // This means one of the disclosure proofs failed to be generated
226
244
  // So we need to remove one from the expected proof count
227
245
  this.topicToExpectedProofCount[topic] -= 1;
246
+ this.topicToFailedProofCount[topic] += 1;
228
247
  // If the expected proof count is now equal to the number of proofs received
229
248
  // and the results were received, we can handle the result now
230
249
  if (this.topicToResults[topic] &&
@@ -281,9 +300,6 @@ class ZKPassport {
281
300
  };
282
301
  return this.getZkPassportRequest(topic);
283
302
  },
284
- /*checkAML: (country?: CountryName | Alpha2Code | Alpha3Code) => {
285
- return this.getZkPassportRequest(topic)
286
- },*/
287
303
  done: () => {
288
304
  const base64Config = buffer_1.Buffer.from(JSON.stringify(this.topicToConfig[topic])).toString("base64");
289
305
  const base64Service = buffer_1.Buffer.from(JSON.stringify(this.topicToService[topic])).toString("base64");
@@ -394,6 +410,23 @@ class ZKPassport {
394
410
  const defaultDateValue = new Date(1111, 10, 11);
395
411
  const currentTime = new Date();
396
412
  const today = new Date(currentTime.getFullYear(), currentTime.getMonth(), currentTime.getDate(), 0, 0, 0, 0);
413
+ const queryResultErrors = {
414
+ sig_check_dsc: {},
415
+ sig_check_id_data: {},
416
+ data_check_integrity: {},
417
+ disclose: {},
418
+ age: {},
419
+ birthdate: {},
420
+ expiry_date: {},
421
+ document_type: {},
422
+ issuing_country: {},
423
+ gender: {},
424
+ nationality: {},
425
+ firstname: {},
426
+ lastname: {},
427
+ fullname: {},
428
+ document_number: {},
429
+ };
397
430
  // Since the order is important for the commitments, we need to sort the proofs
398
431
  // by their expected order: root signature check -> ID signature check -> integrity check -> disclosure
399
432
  const sortedProofs = proofs.sort((a, b) => {
@@ -405,8 +438,10 @@ class ZKPassport {
405
438
  "compare_age",
406
439
  "compare_birthdate",
407
440
  "compare_expiry",
408
- "exclusion_check_country",
409
- "inclusion_check_country",
441
+ "exclusion_check_nationality",
442
+ "inclusion_check_nationality",
443
+ "exclusion_check_issuing_country",
444
+ "inclusion_check_issuing_country",
410
445
  ];
411
446
  const getIndex = (proof) => {
412
447
  const name = proof.name || "";
@@ -422,7 +457,11 @@ class ZKPassport {
422
457
  if (merkleRoot !== expectedMerkleRoot) {
423
458
  console.warn("The ID was signed by an unrecognized root certificate");
424
459
  isCorrect = false;
425
- break;
460
+ queryResultErrors.sig_check_dsc.certificate = {
461
+ expected: `Certificate registry root: ${expectedMerkleRoot.toString()}`,
462
+ received: `Certificate registry root: ${merkleRoot.toString()}`,
463
+ message: "The ID was signed by an unrecognized root certificate",
464
+ };
426
465
  }
427
466
  }
428
467
  else if (proof.name?.startsWith("sig_check_id_data")) {
@@ -430,7 +469,11 @@ class ZKPassport {
430
469
  if (commitmentIn !== commitmentOut) {
431
470
  console.warn("Failed to check the link between the certificate signature and ID signature");
432
471
  isCorrect = false;
433
- break;
472
+ queryResultErrors.sig_check_id_data.commitment = {
473
+ expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
474
+ received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
475
+ message: "Failed to check the link between the certificate signature and ID signature",
476
+ };
434
477
  }
435
478
  commitmentOut = (0, utils_1.getCommitmentOutFromIDDataProof)(proofData);
436
479
  }
@@ -439,7 +482,11 @@ class ZKPassport {
439
482
  if (commitmentIn !== commitmentOut) {
440
483
  console.warn("Failed to check the link between the ID signature and the data signed");
441
484
  isCorrect = false;
442
- break;
485
+ queryResultErrors.data_check_integrity.commitment = {
486
+ expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
487
+ received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
488
+ message: "Failed to check the link between the ID signature and the data signed",
489
+ };
443
490
  }
444
491
  commitmentOut = (0, utils_1.getCommitmentOutFromIntegrityProof)(proofData);
445
492
  const currentDate = (0, utils_1.getCurrentDateFromIntegrityProof)(proofData);
@@ -450,7 +497,11 @@ class ZKPassport {
450
497
  if (todayToCurrentDate >= actualDifference) {
451
498
  console.warn(`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`);
452
499
  isCorrect = false;
453
- break;
500
+ queryResultErrors.data_check_integrity.date = {
501
+ expected: `Difference: ${this.topicToLocalConfig[topic]?.validity} days`,
502
+ received: `Difference: ${Math.round(todayToCurrentDate / 86400000)} days`,
503
+ message: "The date used to check the validity of the ID is older than the validity period",
504
+ };
454
505
  }
455
506
  }
456
507
  else if (proof.name === "disclose_bytes") {
@@ -458,7 +509,11 @@ class ZKPassport {
458
509
  if (commitmentIn !== commitmentOut) {
459
510
  console.warn("Failed to check the link between the validity of the ID and the data to disclose");
460
511
  isCorrect = false;
461
- break;
512
+ queryResultErrors.disclose.commitment = {
513
+ expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
514
+ received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
515
+ message: "Failed to check the link between the validity of the ID and the data to disclose",
516
+ };
462
517
  }
463
518
  // We can't be certain that the disclosed data is for a passport or an ID card
464
519
  // so we need to check both (unless the document type is revealed)
@@ -471,12 +526,20 @@ class ZKPassport {
471
526
  queryResult.document_type.eq.expected !== disclosedDataPassport.documentType) {
472
527
  console.warn("Document type does not match the expected document type");
473
528
  isCorrect = false;
474
- break;
529
+ queryResultErrors.document_type.eq = {
530
+ expected: `${queryResult.document_type.eq.expected}`,
531
+ received: `${disclosedDataPassport.documentType ?? disclosedDataIDCard.documentType}`,
532
+ message: "Document type does not match the expected document type",
533
+ };
475
534
  }
476
535
  if (queryResult.document_type.disclose?.result !== disclosedDataIDCard.documentType) {
477
536
  console.warn("Document type does not match the disclosed document type in query result");
478
537
  isCorrect = false;
479
- break;
538
+ queryResultErrors.document_type.disclose = {
539
+ expected: `${queryResult.document_type.disclose?.result}`,
540
+ received: `${disclosedDataIDCard.documentType ?? disclosedDataPassport.documentType}`,
541
+ message: "Document type does not match the disclosed document type in query result",
542
+ };
480
543
  }
481
544
  }
482
545
  if (queryResult.birthdate) {
@@ -488,14 +551,22 @@ class ZKPassport {
488
551
  queryResult.birthdate.eq.expected.getTime() !== birthdateIDCard.getTime()) {
489
552
  console.warn("Birthdate does not match the expected birthdate");
490
553
  isCorrect = false;
491
- break;
554
+ queryResultErrors.birthdate.eq = {
555
+ expected: `${queryResult.birthdate.eq.expected.toISOString()}`,
556
+ received: `${birthdatePassport?.toISOString() ?? birthdateIDCard?.toISOString()}`,
557
+ message: "Birthdate does not match the expected birthdate",
558
+ };
492
559
  }
493
560
  if (queryResult.birthdate.disclose &&
494
561
  queryResult.birthdate.disclose.result.getTime() !== birthdatePassport.getTime() &&
495
562
  queryResult.birthdate.disclose.result.getTime() !== birthdateIDCard.getTime()) {
496
563
  console.warn("Birthdate does not match the disclosed birthdate in query result");
497
564
  isCorrect = false;
498
- break;
565
+ queryResultErrors.birthdate.disclose = {
566
+ expected: `${queryResult.birthdate.disclose.result.toISOString()}`,
567
+ received: `${birthdatePassport?.toISOString() ?? birthdateIDCard?.toISOString()}`,
568
+ message: "Birthdate does not match the disclosed birthdate in query result",
569
+ };
499
570
  }
500
571
  }
501
572
  if (queryResult.expiry_date) {
@@ -507,14 +578,22 @@ class ZKPassport {
507
578
  queryResult.expiry_date.eq.expected.getTime() !== expiryDateIDCard.getTime()) {
508
579
  console.warn("Expiry date does not match the expected expiry date");
509
580
  isCorrect = false;
510
- break;
581
+ queryResultErrors.expiry_date.eq = {
582
+ expected: `${queryResult.expiry_date.eq.expected.toISOString()}`,
583
+ received: `${expiryDatePassport?.toISOString() ?? expiryDateIDCard?.toISOString()}`,
584
+ message: "Expiry date does not match the expected expiry date",
585
+ };
511
586
  }
512
587
  if (queryResult.expiry_date.disclose &&
513
588
  queryResult.expiry_date.disclose.result.getTime() !== expiryDatePassport.getTime() &&
514
589
  queryResult.expiry_date.disclose.result.getTime() !== expiryDateIDCard.getTime()) {
515
590
  console.warn("Expiry date does not match the disclosed expiry date in query result");
516
591
  isCorrect = false;
517
- break;
592
+ queryResultErrors.expiry_date.disclose = {
593
+ expected: `${queryResult.expiry_date.disclose.result.toISOString()}`,
594
+ received: `${expiryDatePassport?.toISOString() ?? expiryDateIDCard?.toISOString()}`,
595
+ message: "Expiry date does not match the disclosed expiry date in query result",
596
+ };
518
597
  }
519
598
  }
520
599
  if (queryResult.nationality) {
@@ -526,14 +605,22 @@ class ZKPassport {
526
605
  queryResult.nationality.eq.expected !== nationalityIDCard) {
527
606
  console.warn("Nationality does not match the expected nationality");
528
607
  isCorrect = false;
529
- break;
608
+ queryResultErrors.nationality.eq = {
609
+ expected: `${queryResult.nationality.eq.expected}`,
610
+ received: `${nationalityPassport ?? nationalityIDCard}`,
611
+ message: "Nationality does not match the expected nationality",
612
+ };
530
613
  }
531
614
  if (queryResult.nationality.disclose &&
532
615
  queryResult.nationality.disclose.result !== nationalityPassport &&
533
616
  queryResult.nationality.disclose.result !== nationalityIDCard) {
534
617
  console.warn("Nationality does not match the disclosed nationality in query result");
535
618
  isCorrect = false;
536
- break;
619
+ queryResultErrors.nationality.disclose = {
620
+ expected: `${queryResult.nationality.disclose.result}`,
621
+ received: `${nationalityPassport ?? nationalityIDCard}`,
622
+ message: "Nationality does not match the disclosed nationality in query result",
623
+ };
537
624
  }
538
625
  }
539
626
  if (queryResult.document_number) {
@@ -545,14 +632,22 @@ class ZKPassport {
545
632
  queryResult.document_number.eq.expected !== documentNumberIDCard) {
546
633
  console.warn("Document number does not match the expected document number");
547
634
  isCorrect = false;
548
- break;
635
+ queryResultErrors.document_number.eq = {
636
+ expected: `${queryResult.document_number.eq.expected}`,
637
+ received: `${documentNumberPassport ?? documentNumberIDCard}`,
638
+ message: "Document number does not match the expected document number",
639
+ };
549
640
  }
550
641
  if (queryResult.document_number.disclose &&
551
642
  queryResult.document_number.disclose.result !== documentNumberPassport &&
552
643
  queryResult.document_number.disclose.result !== documentNumberIDCard) {
553
644
  console.warn("Document number does not match the disclosed document number in query result");
554
645
  isCorrect = false;
555
- break;
646
+ queryResultErrors.document_number.disclose = {
647
+ expected: `${queryResult.document_number.disclose.result}`,
648
+ received: `${documentNumberPassport ?? documentNumberIDCard}`,
649
+ message: "Document number does not match the disclosed document number in query result",
650
+ };
556
651
  }
557
652
  }
558
653
  if (queryResult.gender) {
@@ -564,14 +659,22 @@ class ZKPassport {
564
659
  queryResult.gender.eq.expected !== genderIDCard) {
565
660
  console.warn("Gender does not match the expected gender");
566
661
  isCorrect = false;
567
- break;
662
+ queryResultErrors.gender.eq = {
663
+ expected: `${queryResult.gender.eq.expected}`,
664
+ received: `${genderPassport ?? genderIDCard}`,
665
+ message: "Gender does not match the expected gender",
666
+ };
568
667
  }
569
668
  if (queryResult.gender.disclose &&
570
669
  queryResult.gender.disclose.result !== genderPassport &&
571
670
  queryResult.gender.disclose.result !== genderIDCard) {
572
671
  console.warn("Gender does not match the disclosed gender in query result");
573
672
  isCorrect = false;
574
- break;
673
+ queryResultErrors.gender.disclose = {
674
+ expected: `${queryResult.gender.disclose.result}`,
675
+ received: `${genderPassport ?? genderIDCard}`,
676
+ message: "Gender does not match the disclosed gender in query result",
677
+ };
575
678
  }
576
679
  }
577
680
  if (queryResult.issuing_country) {
@@ -583,14 +686,22 @@ class ZKPassport {
583
686
  queryResult.issuing_country.eq.expected !== issuingCountryIDCard) {
584
687
  console.warn("Issuing country does not match the expected issuing country");
585
688
  isCorrect = false;
586
- break;
689
+ queryResultErrors.issuing_country.eq = {
690
+ expected: `${queryResult.issuing_country.eq.expected}`,
691
+ received: `${issuingCountryPassport ?? issuingCountryIDCard}`,
692
+ message: "Issuing country does not match the expected issuing country",
693
+ };
587
694
  }
588
695
  if (queryResult.issuing_country.disclose &&
589
696
  queryResult.issuing_country.disclose.result !== issuingCountryPassport &&
590
697
  queryResult.issuing_country.disclose.result !== issuingCountryIDCard) {
591
698
  console.warn("Issuing country does not match the disclosed issuing country in query result");
592
699
  isCorrect = false;
593
- break;
700
+ queryResultErrors.issuing_country.disclose = {
701
+ expected: `${queryResult.issuing_country.disclose.result}`,
702
+ received: `${issuingCountryPassport ?? issuingCountryIDCard}`,
703
+ message: "Issuing country does not match the disclosed issuing country in query result",
704
+ };
594
705
  }
595
706
  }
596
707
  if (queryResult.fullname) {
@@ -604,7 +715,11 @@ class ZKPassport {
604
715
  fullnameIDCard.toLowerCase()) {
605
716
  console.warn("Fullname does not match the expected fullname");
606
717
  isCorrect = false;
607
- break;
718
+ queryResultErrors.fullname.eq = {
719
+ expected: `${queryResult.fullname.eq.expected}`,
720
+ received: `${fullnamePassport ?? fullnameIDCard}`,
721
+ message: "Fullname does not match the expected fullname",
722
+ };
608
723
  }
609
724
  if (queryResult.fullname.disclose &&
610
725
  (0, utils_1.formatName)(queryResult.fullname.disclose.result).toLowerCase() !==
@@ -613,7 +728,11 @@ class ZKPassport {
613
728
  fullnameIDCard.toLowerCase()) {
614
729
  console.warn("Fullname does not match the disclosed fullname in query result");
615
730
  isCorrect = false;
616
- break;
731
+ queryResultErrors.fullname.disclose = {
732
+ expected: `${queryResult.fullname.disclose.result}`,
733
+ received: `${fullnamePassport ?? fullnameIDCard}`,
734
+ message: "Fullname does not match the disclosed fullname in query result",
735
+ };
617
736
  }
618
737
  }
619
738
  if (queryResult.firstname) {
@@ -632,7 +751,11 @@ class ZKPassport {
632
751
  firstnameIDCard.toLowerCase()) {
633
752
  console.warn("Firstname does not match the expected firstname");
634
753
  isCorrect = false;
635
- break;
754
+ queryResultErrors.firstname.eq = {
755
+ expected: `${queryResult.firstname.eq.expected}`,
756
+ received: `${firstnamePassport ?? firstnameIDCard}`,
757
+ message: "Firstname does not match the expected firstname",
758
+ };
636
759
  }
637
760
  if (queryResult.firstname.disclose &&
638
761
  (0, utils_1.formatName)(queryResult.firstname.disclose.result).toLowerCase() !==
@@ -641,7 +764,11 @@ class ZKPassport {
641
764
  firstnameIDCard.toLowerCase()) {
642
765
  console.warn("Firstname does not match the disclosed firstname in query result");
643
766
  isCorrect = false;
644
- break;
767
+ queryResultErrors.firstname.disclose = {
768
+ expected: `${queryResult.firstname.disclose.result}`,
769
+ received: `${firstnamePassport ?? firstnameIDCard}`,
770
+ message: "Firstname does not match the disclosed firstname in query result",
771
+ };
645
772
  }
646
773
  }
647
774
  if (queryResult.lastname) {
@@ -660,7 +787,11 @@ class ZKPassport {
660
787
  lastnameIDCard.toLowerCase()) {
661
788
  console.warn("Lastname does not match the expected lastname");
662
789
  isCorrect = false;
663
- break;
790
+ queryResultErrors.lastname.eq = {
791
+ expected: `${queryResult.lastname.eq.expected}`,
792
+ received: `${lastnamePassport ?? lastnameIDCard}`,
793
+ message: "Lastname does not match the expected lastname",
794
+ };
664
795
  }
665
796
  if (queryResult.lastname.disclose &&
666
797
  (0, utils_1.formatName)(queryResult.lastname.disclose.result).toLowerCase() !==
@@ -669,7 +800,11 @@ class ZKPassport {
669
800
  lastnameIDCard.toLowerCase()) {
670
801
  console.warn("Lastname does not match the disclosed lastname in query result");
671
802
  isCorrect = false;
672
- break;
803
+ queryResultErrors.lastname.disclose = {
804
+ expected: `${queryResult.lastname.disclose.result}`,
805
+ received: `${lastnamePassport ?? lastnameIDCard}`,
806
+ message: "Lastname does not match the disclosed lastname in query result",
807
+ };
673
808
  }
674
809
  }
675
810
  uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
@@ -679,7 +814,11 @@ class ZKPassport {
679
814
  if (commitmentIn !== commitmentOut) {
680
815
  console.warn("Failed to check the link between the validity of the ID and the age derived from it");
681
816
  isCorrect = false;
682
- break;
817
+ queryResultErrors.age.commitment = {
818
+ expected: `Commitment: ${commitmentOut}`,
819
+ received: `Commitment: ${commitmentIn}`,
820
+ message: "Failed to check the link between the validity of the ID and the age derived from it",
821
+ };
683
822
  }
684
823
  const minAge = (0, utils_1.getMinAgeFromProof)(proofData);
685
824
  const maxAge = (0, utils_1.getMaxAgeFromProof)(proofData);
@@ -689,14 +828,22 @@ class ZKPassport {
689
828
  minAge < queryResult.age.gte.expected) {
690
829
  console.warn("Age is not greater than or equal to the expected age");
691
830
  isCorrect = false;
692
- break;
831
+ queryResultErrors.age.gte = {
832
+ expected: queryResult.age.gte.expected,
833
+ received: minAge,
834
+ message: "Age is not greater than or equal to the expected age",
835
+ };
693
836
  }
694
837
  if (queryResult.age.lt &&
695
838
  queryResult.age.lt.result &&
696
839
  maxAge >= queryResult.age.lt.expected) {
697
840
  console.warn("Age is not less than the expected age");
698
841
  isCorrect = false;
699
- break;
842
+ queryResultErrors.age.lt = {
843
+ expected: queryResult.age.lt.expected,
844
+ received: maxAge,
845
+ message: "Age is not less than the expected age",
846
+ };
700
847
  }
701
848
  if (queryResult.age.range) {
702
849
  if (queryResult.age.range.result &&
@@ -704,38 +851,60 @@ class ZKPassport {
704
851
  maxAge >= queryResult.age.range.expected[1])) {
705
852
  console.warn("Age is not in the expected range");
706
853
  isCorrect = false;
707
- break;
854
+ queryResultErrors.age.range = {
855
+ expected: queryResult.age.range.expected,
856
+ received: [minAge, maxAge],
857
+ message: "Age is not in the expected range",
858
+ };
708
859
  }
709
860
  }
710
861
  if (!queryResult.age.lt && !queryResult.age.range && maxAge != 0) {
711
862
  console.warn("Maximum age should be equal to 0");
712
863
  isCorrect = false;
713
- break;
864
+ queryResultErrors.age.disclose = {
865
+ expected: 0,
866
+ received: maxAge,
867
+ message: "Maximum age should be equal to 0",
868
+ };
714
869
  }
715
870
  if (!queryResult.age.gte && !queryResult.age.range && minAge != 0) {
716
871
  console.warn("Minimum age should be equal to 0");
717
872
  isCorrect = false;
718
- break;
873
+ queryResultErrors.age.disclose = {
874
+ expected: 0,
875
+ received: minAge,
876
+ message: "Minimum age should be equal to 0",
877
+ };
719
878
  }
720
879
  if (queryResult.age.disclose &&
721
880
  (queryResult.age.disclose.result !== minAge ||
722
881
  queryResult.age.disclose.result !== maxAge)) {
723
882
  console.warn("Age does not match the disclosed age in query result");
724
883
  isCorrect = false;
725
- break;
884
+ queryResultErrors.age.disclose = {
885
+ expected: `${minAge}`,
886
+ received: `${queryResult.age.disclose.result}`,
887
+ message: "Age does not match the disclosed age in query result",
888
+ };
726
889
  }
727
890
  }
728
891
  else {
729
892
  console.warn("Age is not set in the query result");
730
893
  isCorrect = false;
731
- break;
894
+ queryResultErrors.age.disclose = {
895
+ message: "Age is not set in the query result",
896
+ };
732
897
  }
733
898
  const currentDate = (0, utils_1.getCurrentDateFromAgeProof)(proofData);
734
899
  if (currentDate.getTime() !== today.getTime() &&
735
900
  currentDate.getTime() !== today.getTime() - 86400000) {
736
901
  console.warn("Current date in the proof is too old");
737
902
  isCorrect = false;
738
- break;
903
+ queryResultErrors.age.disclose = {
904
+ expected: `${today.toISOString()}`,
905
+ received: `${currentDate.toISOString()}`,
906
+ message: "Current date in the proof is too old",
907
+ };
739
908
  }
740
909
  uniqueIdentifier = (0, utils_1.getCommitmentInFromDisclosureProof)(proofData).toString(10);
741
910
  }
@@ -744,7 +913,11 @@ class ZKPassport {
744
913
  if (commitmentIn !== commitmentOut) {
745
914
  console.warn("Failed to check the link between the validity of the ID and the birthdate derived from it");
746
915
  isCorrect = false;
747
- break;
916
+ queryResultErrors.birthdate.commitment = {
917
+ expected: `Commitment: ${commitmentOut}`,
918
+ received: `Commitment: ${commitmentIn}`,
919
+ message: "Failed to check the link between the validity of the ID and the birthdate derived from it",
920
+ };
748
921
  }
749
922
  const minDate = (0, utils_1.getMinDateFromProof)(proofData);
750
923
  const maxDate = (0, utils_1.getMaxDateFromProof)(proofData);
@@ -754,14 +927,22 @@ class ZKPassport {
754
927
  minDate < queryResult.birthdate.gte.expected) {
755
928
  console.warn("Birthdate is not greater than or equal to the expected birthdate");
756
929
  isCorrect = false;
757
- break;
930
+ queryResultErrors.birthdate.gte = {
931
+ expected: queryResult.birthdate.gte.expected,
932
+ received: minDate,
933
+ message: "Birthdate is not greater than or equal to the expected birthdate",
934
+ };
758
935
  }
759
936
  if (queryResult.birthdate.lte &&
760
937
  queryResult.birthdate.lte.result &&
761
938
  maxDate > queryResult.birthdate.lte.expected) {
762
939
  console.warn("Birthdate is not less than the expected birthdate");
763
940
  isCorrect = false;
764
- break;
941
+ queryResultErrors.birthdate.lte = {
942
+ expected: queryResult.birthdate.lte.expected,
943
+ received: maxDate,
944
+ message: "Birthdate is not less than the expected birthdate",
945
+ };
765
946
  }
766
947
  if (queryResult.birthdate.range) {
767
948
  if (queryResult.birthdate.range.result &&
@@ -769,7 +950,11 @@ class ZKPassport {
769
950
  maxDate > queryResult.birthdate.range.expected[1])) {
770
951
  console.warn("Birthdate is not in the expected range");
771
952
  isCorrect = false;
772
- break;
953
+ queryResultErrors.birthdate.range = {
954
+ expected: queryResult.birthdate.range.expected,
955
+ received: [minDate, maxDate],
956
+ message: "Birthdate is not in the expected range",
957
+ };
773
958
  }
774
959
  }
775
960
  if (!queryResult.birthdate.lte &&
@@ -777,20 +962,30 @@ class ZKPassport {
777
962
  maxDate.getTime() != defaultDateValue.getTime()) {
778
963
  console.warn("Maximum birthdate should be equal to default date value");
779
964
  isCorrect = false;
780
- break;
965
+ queryResultErrors.birthdate.disclose = {
966
+ expected: `${defaultDateValue.toISOString()}`,
967
+ received: `${maxDate.toISOString()}`,
968
+ message: "Maximum birthdate should be equal to default date value",
969
+ };
781
970
  }
782
971
  if (!queryResult.birthdate.gte &&
783
972
  !queryResult.birthdate.range &&
784
973
  minDate.getTime() != defaultDateValue.getTime()) {
785
974
  console.warn("Minimum birthdate should be equal to default date value");
786
975
  isCorrect = false;
787
- break;
976
+ queryResultErrors.birthdate.disclose = {
977
+ expected: `${defaultDateValue.toISOString()}`,
978
+ received: `${minDate.toISOString()}`,
979
+ message: "Minimum birthdate should be equal to default date value",
980
+ };
788
981
  }
789
982
  }
790
983
  else {
791
984
  console.warn("Birthdate is not set in the query result");
792
985
  isCorrect = false;
793
- break;
986
+ queryResultErrors.birthdate.disclose = {
987
+ message: "Birthdate is not set in the query result",
988
+ };
794
989
  }
795
990
  uniqueIdentifier = (0, utils_1.getCommitmentInFromDisclosureProof)(proofData).toString(10);
796
991
  }
@@ -799,7 +994,11 @@ class ZKPassport {
799
994
  if (commitmentIn !== commitmentOut) {
800
995
  console.warn("Failed to check the link between the validity of the ID and its expiry date");
801
996
  isCorrect = false;
802
- break;
997
+ queryResultErrors.expiry_date.commitment = {
998
+ expected: `Commitment: ${commitmentOut}`,
999
+ received: `Commitment: ${commitmentIn}`,
1000
+ message: "Failed to check the link between the validity of the ID and its expiry date",
1001
+ };
803
1002
  }
804
1003
  const minDate = (0, utils_1.getMinDateFromProof)(proofData);
805
1004
  const maxDate = (0, utils_1.getMaxDateFromProof)(proofData);
@@ -809,14 +1008,22 @@ class ZKPassport {
809
1008
  minDate < queryResult.expiry_date.gte.expected) {
810
1009
  console.warn("Expiry date is not greater than or equal to the expected expiry date");
811
1010
  isCorrect = false;
812
- break;
1011
+ queryResultErrors.expiry_date.gte = {
1012
+ expected: queryResult.expiry_date.gte.expected,
1013
+ received: minDate,
1014
+ message: "Expiry date is not greater than or equal to the expected expiry date",
1015
+ };
813
1016
  }
814
1017
  if (queryResult.expiry_date.lte &&
815
1018
  queryResult.expiry_date.lte.result &&
816
1019
  maxDate > queryResult.expiry_date.lte.expected) {
817
1020
  console.warn("Expiry date is not less than the expected expiry date");
818
1021
  isCorrect = false;
819
- break;
1022
+ queryResultErrors.expiry_date.lte = {
1023
+ expected: queryResult.expiry_date.lte.expected,
1024
+ received: maxDate,
1025
+ message: "Expiry date is not less than the expected expiry date",
1026
+ };
820
1027
  }
821
1028
  if (queryResult.expiry_date.range) {
822
1029
  if (queryResult.expiry_date.range.result &&
@@ -824,7 +1031,11 @@ class ZKPassport {
824
1031
  maxDate > queryResult.expiry_date.range.expected[1])) {
825
1032
  console.warn("Expiry date is not in the expected range");
826
1033
  isCorrect = false;
827
- break;
1034
+ queryResultErrors.expiry_date.range = {
1035
+ expected: queryResult.expiry_date.range.expected,
1036
+ received: [minDate, maxDate],
1037
+ message: "Expiry date is not in the expected range",
1038
+ };
828
1039
  }
829
1040
  }
830
1041
  if (!queryResult.expiry_date.lte &&
@@ -832,44 +1043,64 @@ class ZKPassport {
832
1043
  maxDate.getTime() != defaultDateValue.getTime()) {
833
1044
  console.warn("Maximum expiry date should be equal to default date value");
834
1045
  isCorrect = false;
835
- break;
1046
+ queryResultErrors.expiry_date.disclose = {
1047
+ expected: `${defaultDateValue.toISOString()}`,
1048
+ received: `${maxDate.toISOString()}`,
1049
+ message: "Maximum expiry date should be equal to default date value",
1050
+ };
836
1051
  }
837
1052
  if (!queryResult.expiry_date.gte &&
838
1053
  !queryResult.expiry_date.range &&
839
1054
  minDate.getTime() != defaultDateValue.getTime()) {
840
1055
  console.warn("Minimum expiry date should be equal to default date value");
841
1056
  isCorrect = false;
842
- break;
1057
+ queryResultErrors.expiry_date.disclose = {
1058
+ expected: `${defaultDateValue.toISOString()}`,
1059
+ received: `${minDate.toISOString()}`,
1060
+ message: "Minimum expiry date should be equal to default date value",
1061
+ };
843
1062
  }
844
1063
  }
845
1064
  else {
846
1065
  console.warn("Expiry date is not set in the query result");
847
1066
  isCorrect = false;
848
- break;
1067
+ queryResultErrors.expiry_date.disclose = {
1068
+ message: "Expiry date is not set in the query result",
1069
+ };
849
1070
  }
850
1071
  uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
851
1072
  }
852
- else if (proof.name === "exclusion_check_country") {
1073
+ else if (proof.name === "exclusion_check_nationality") {
853
1074
  commitmentIn = (0, utils_1.getCommitmentInFromDisclosureProof)(proofData);
854
1075
  if (commitmentIn !== commitmentOut) {
855
- console.warn("Failed to check the link between the validity of the ID and the country exclusion check");
1076
+ console.warn("Failed to check the link between the validity of the ID and the nationality exclusion check");
856
1077
  isCorrect = false;
857
- break;
1078
+ queryResultErrors.nationality.commitment = {
1079
+ expected: `Commitment: ${commitmentOut}`,
1080
+ received: `Commitment: ${commitmentIn}`,
1081
+ message: "Failed to check the link between the validity of the ID and the nationality exclusion check",
1082
+ };
858
1083
  }
859
1084
  const countryList = (0, utils_1.getCountryListFromExclusionProof)(proofData);
860
1085
  if (queryResult.nationality &&
861
1086
  queryResult.nationality.out &&
862
1087
  queryResult.nationality.out.result) {
863
1088
  if (!queryResult.nationality.out.expected?.every((country) => countryList.includes(country))) {
864
- console.warn("Country exclusion list does not match the one from the query results");
1089
+ console.warn("Nationality exclusion list does not match the one from the query results");
865
1090
  isCorrect = false;
866
- break;
1091
+ queryResultErrors.nationality.out = {
1092
+ expected: queryResult.nationality.out.expected,
1093
+ received: countryList,
1094
+ message: "Nationality exclusion list does not match the one from the query results",
1095
+ };
867
1096
  }
868
1097
  }
869
1098
  else if (!queryResult.nationality || !queryResult.nationality.out) {
870
1099
  console.warn("Nationality exclusion is not set in the query result");
871
1100
  isCorrect = false;
872
- break;
1101
+ queryResultErrors.nationality.out = {
1102
+ message: "Nationality exclusion is not set in the query result",
1103
+ };
873
1104
  }
874
1105
  // Check the countryList is in ascending order
875
1106
  // If the prover doesn't use a sorted list then the proof cannot be trusted
@@ -878,37 +1109,129 @@ class ZKPassport {
878
1109
  if (countryList[i] < countryList[i - 1]) {
879
1110
  console.warn("The nationality exclusion list has not been sorted, and thus the proof cannot be trusted");
880
1111
  isCorrect = false;
881
- break;
1112
+ queryResultErrors.nationality.out = {
1113
+ message: "The nationality exclusion list has not been sorted, and thus the proof cannot be trusted",
1114
+ };
882
1115
  }
883
1116
  }
884
1117
  uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
885
1118
  }
886
- else if (proof.name === "inclusion_check_country") {
1119
+ else if (proof.name === "exclusion_check_issuing_country") {
887
1120
  commitmentIn = (0, utils_1.getCommitmentInFromDisclosureProof)(proofData);
888
1121
  if (commitmentIn !== commitmentOut) {
889
- console.warn("Failed to check the link between the validity of the ID and the country inclusion check");
1122
+ console.warn("Failed to check the link between the validity of the ID and the issuing country exclusion check");
890
1123
  isCorrect = false;
891
- break;
1124
+ queryResultErrors.nationality.commitment = {
1125
+ expected: `Commitment: ${commitmentOut}`,
1126
+ received: `Commitment: ${commitmentIn}`,
1127
+ message: "Failed to check the link between the validity of the ID and the issuing country exclusion check",
1128
+ };
1129
+ }
1130
+ const countryList = (0, utils_1.getCountryListFromExclusionProof)(proofData);
1131
+ if (queryResult.issuing_country &&
1132
+ queryResult.issuing_country.out &&
1133
+ queryResult.issuing_country.out.result) {
1134
+ if (!queryResult.issuing_country.out.expected?.every((country) => countryList.includes(country))) {
1135
+ console.warn("Issuing country exclusion list does not match the one from the query results");
1136
+ isCorrect = false;
1137
+ queryResultErrors.issuing_country.out = {
1138
+ expected: queryResult.issuing_country.out.expected,
1139
+ received: countryList,
1140
+ message: "Issuing country exclusion list does not match the one from the query results",
1141
+ };
1142
+ }
1143
+ }
1144
+ else if (!queryResult.issuing_country || !queryResult.issuing_country.out) {
1145
+ console.warn("Issuing country exclusion is not set in the query result");
1146
+ isCorrect = false;
1147
+ queryResultErrors.issuing_country.out = {
1148
+ message: "Issuing country exclusion is not set in the query result",
1149
+ };
1150
+ }
1151
+ // Check the countryList is in ascending order
1152
+ // If the prover doesn't use a sorted list then the proof cannot be trusted
1153
+ // as it is requirement in the circuit for the exclusion check to work
1154
+ for (let i = 1; i < countryList.length; i++) {
1155
+ if (countryList[i] < countryList[i - 1]) {
1156
+ console.warn("The issuing country exclusion list has not been sorted, and thus the proof cannot be trusted");
1157
+ isCorrect = false;
1158
+ queryResultErrors.issuing_country.out = {
1159
+ message: "The issuing country exclusion list has not been sorted, and thus the proof cannot be trusted",
1160
+ };
1161
+ }
1162
+ }
1163
+ uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
1164
+ }
1165
+ else if (proof.name === "inclusion_check_nationality") {
1166
+ commitmentIn = (0, utils_1.getCommitmentInFromDisclosureProof)(proofData);
1167
+ if (commitmentIn !== commitmentOut) {
1168
+ console.warn("Failed to check the link between the validity of the ID and the nationality inclusion check");
1169
+ isCorrect = false;
1170
+ queryResultErrors.nationality.commitment = {
1171
+ expected: `Commitment: ${commitmentOut}`,
1172
+ received: `Commitment: ${commitmentIn}`,
1173
+ message: "Failed to check the link between the validity of the ID and the nationality inclusion check",
1174
+ };
892
1175
  }
893
1176
  const countryList = (0, utils_1.getCountryListFromInclusionProof)(proofData);
894
1177
  if (queryResult.nationality &&
895
1178
  queryResult.nationality.in &&
896
1179
  queryResult.nationality.in.result) {
897
1180
  if (!queryResult.nationality.in.expected?.every((country) => countryList.includes(country))) {
898
- console.warn("Country inclusion list does not match the one from the query results");
1181
+ console.warn("Nationality inclusion list does not match the one from the query results");
899
1182
  isCorrect = false;
900
- break;
1183
+ queryResultErrors.nationality.in = {
1184
+ expected: queryResult.nationality.in.expected,
1185
+ received: countryList,
1186
+ message: "Nationality inclusion list does not match the one from the query results",
1187
+ };
901
1188
  }
902
1189
  }
903
1190
  else if (!queryResult.nationality || !queryResult.nationality.in) {
904
1191
  console.warn("Nationality inclusion is not set in the query result");
905
1192
  isCorrect = false;
906
- break;
1193
+ queryResultErrors.nationality.in = {
1194
+ message: "Nationality inclusion is not set in the query result",
1195
+ };
1196
+ }
1197
+ uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
1198
+ }
1199
+ else if (proof.name === "inclusion_check_issuing_country") {
1200
+ commitmentIn = (0, utils_1.getCommitmentInFromDisclosureProof)(proofData);
1201
+ if (commitmentIn !== commitmentOut) {
1202
+ console.warn("Failed to check the link between the validity of the ID and the issuing country inclusion check");
1203
+ isCorrect = false;
1204
+ queryResultErrors.nationality.commitment = {
1205
+ expected: `Commitment: ${commitmentOut}`,
1206
+ received: `Commitment: ${commitmentIn}`,
1207
+ message: "Failed to check the link between the validity of the ID and the issuing country inclusion check",
1208
+ };
1209
+ }
1210
+ const countryList = (0, utils_1.getCountryListFromInclusionProof)(proofData);
1211
+ if (queryResult.issuing_country &&
1212
+ queryResult.issuing_country.in &&
1213
+ queryResult.issuing_country.in.result) {
1214
+ if (!queryResult.issuing_country.in.expected?.every((country) => countryList.includes(country))) {
1215
+ console.warn("Issuing country inclusion list does not match the one from the query results");
1216
+ isCorrect = false;
1217
+ queryResultErrors.issuing_country.in = {
1218
+ expected: queryResult.issuing_country.in.expected,
1219
+ received: countryList,
1220
+ message: "Issuing country inclusion list does not match the one from the query results",
1221
+ };
1222
+ }
1223
+ }
1224
+ else if (!queryResult.issuing_country || !queryResult.issuing_country.in) {
1225
+ console.warn("Issuing country inclusion is not set in the query result");
1226
+ isCorrect = false;
1227
+ queryResultErrors.issuing_country.in = {
1228
+ message: "Issuing country inclusion is not set in the query result",
1229
+ };
907
1230
  }
908
1231
  uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
909
1232
  }
910
1233
  }
911
- return { isCorrect, uniqueIdentifier };
1234
+ return { isCorrect, uniqueIdentifier, queryResultErrors };
912
1235
  }
913
1236
  /**
914
1237
  * @notice Verify the proofs received from the mobile app.
@@ -923,12 +1246,6 @@ class ZKPassport {
923
1246
  // There is a minimum of 4 subproofs to make a complete proof
924
1247
  if (!proofs || proofs.length < 4) {
925
1248
  proofsToVerify = this.topicToProofs[requestId];
926
- if (!proofsToVerify || proofsToVerify.length < 4) {
927
- // It may happen that a request returns a result without proofs
928
- // Meaning the ID is not supported yet by ZKPassport circuits,
929
- // so the results has to be trusted and cannot be independently verified
930
- return { uniqueIdentifier: undefined, verified: false };
931
- }
932
1249
  }
933
1250
  const { BarretenbergVerifier } = await Promise.resolve().then(() => tslib_1.__importStar(require("@aztec/bb.js")));
934
1251
  const verifier = new BarretenbergVerifier();
@@ -937,13 +1254,15 @@ class ZKPassport {
937
1254
  }*/
938
1255
  let verified = true;
939
1256
  let uniqueIdentifier;
1257
+ let queryResultErrors;
940
1258
  if (queryResult) {
941
- const { isCorrect, uniqueIdentifier: uniqueIdentifierFromPublicInputs } = await this.checkPublicInputs(proofsToVerify, queryResult, requestId);
1259
+ const { isCorrect, uniqueIdentifier: uniqueIdentifierFromPublicInputs, queryResultErrors: queryResultErrorsFromPublicInputs, } = await this.checkPublicInputs(proofsToVerify, queryResult, requestId);
942
1260
  uniqueIdentifier = uniqueIdentifierFromPublicInputs;
943
1261
  verified = isCorrect;
1262
+ queryResultErrors = isCorrect ? undefined : queryResultErrorsFromPublicInputs;
944
1263
  }
945
1264
  // Only proceed with the proof verification if the public inputs are correct
946
- if (verified) {
1265
+ if (verified && queryResult) {
947
1266
  for (const proof of proofsToVerify) {
948
1267
  const proofData = (0, utils_1.getProofData)(proof.proof, true);
949
1268
  const hostedPackagedCircuit = await (0, utils_1.getHostedPackagedCircuitByName)(proof.version, proof.name);
@@ -963,7 +1282,7 @@ class ZKPassport {
963
1282
  }
964
1283
  }
965
1284
  this.topicToProofs[requestId] = [];
966
- return { uniqueIdentifier, verified };
1285
+ return { uniqueIdentifier, verified, queryResultErrors };
967
1286
  }
968
1287
  /**
969
1288
  * @notice Returns the URL of the request.
@@ -981,14 +1300,17 @@ class ZKPassport {
981
1300
  * @param requestId The request ID.
982
1301
  */
983
1302
  cancelRequest(requestId) {
984
- this.topicToWebSocketClient[requestId].close();
985
- delete this.topicToWebSocketClient[requestId];
1303
+ if (this.topicToWebSocketClient[requestId]) {
1304
+ this.topicToWebSocketClient[requestId].close();
1305
+ delete this.topicToWebSocketClient[requestId];
1306
+ }
986
1307
  delete this.topicToKeyPair[requestId];
987
1308
  delete this.topicToConfig[requestId];
988
1309
  delete this.topicToLocalConfig[requestId];
989
1310
  delete this.topicToSharedSecret[requestId];
990
1311
  delete this.topicToProofs[requestId];
991
1312
  delete this.topicToExpectedProofCount[requestId];
1313
+ delete this.topicToFailedProofCount[requestId];
992
1314
  delete this.topicToResults[requestId];
993
1315
  this.onRequestReceivedCallbacks[requestId] = [];
994
1316
  this.onGeneratingProofCallbacks[requestId] = [];
@@ -997,5 +1319,13 @@ class ZKPassport {
997
1319
  this.onRejectCallbacks[requestId] = [];
998
1320
  this.onErrorCallbacks[requestId] = [];
999
1321
  }
1322
+ /**
1323
+ * @notice Clears all requests.
1324
+ */
1325
+ clearAllRequests() {
1326
+ for (const requestId in this.topicToWebSocketClient) {
1327
+ this.cancelRequest(requestId);
1328
+ }
1329
+ }
1000
1330
  }
1001
1331
  exports.ZKPassport = ZKPassport;