@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/dist/esm/index.js CHANGED
@@ -70,6 +70,7 @@ export class ZKPassport {
70
70
  this.topicToService = {};
71
71
  this.topicToProofs = {};
72
72
  this.topicToExpectedProofCount = {};
73
+ this.topicToFailedProofCount = {};
73
74
  this.topicToResults = {};
74
75
  this.onRequestReceivedCallbacks = {};
75
76
  this.onGeneratingProofCallbacks = {};
@@ -94,14 +95,19 @@ export class ZKPassport {
94
95
  // Clear the results straight away to avoid concurrency issues
95
96
  delete this.topicToResults[topic];
96
97
  // Verify the proofs and extract the unique identifier (aka nullifier) and the verification result
97
- const { uniqueIdentifier, verified } = await this.verify(topic, this.topicToProofs[topic], result);
98
+ const { uniqueIdentifier, verified, queryResultErrors } = await this.verify(topic, this.topicToProofs[topic], result);
99
+ const hasFailedProofs = this.topicToFailedProofCount[topic] > 0;
98
100
  await Promise.all(this.onResultCallbacks[topic].map((callback) => callback({
99
- uniqueIdentifier,
100
- verified,
101
+ // If there are failed proofs, we don't return the unique identifier
102
+ // and we set the verified result to false
103
+ uniqueIdentifier: hasFailedProofs ? undefined : uniqueIdentifier,
104
+ verified: hasFailedProofs ? false : verified,
101
105
  result,
106
+ queryResultErrors,
102
107
  })));
103
- // Clear the expected proof count
108
+ // Clear the expected proof count and failed proof count
104
109
  delete this.topicToExpectedProofCount[topic];
110
+ delete this.topicToFailedProofCount[topic];
105
111
  }
106
112
  setExpectedProofCount(topic) {
107
113
  const fields = Object.keys(this.topicToConfig[topic]).filter((key) => hasRequestedAccessToField(this.topicToConfig[topic], key));
@@ -152,6 +158,7 @@ export class ZKPassport {
152
158
  // Each separate needed circuit adds 1 disclosure proof
153
159
  this.topicToExpectedProofCount[topic] =
154
160
  neededCircuits.length === 0 ? 4 : 3 + neededCircuits.length;
161
+ this.topicToFailedProofCount[topic] = 0;
155
162
  }
156
163
  /**
157
164
  * @notice Handle an encrypted message.
@@ -207,6 +214,7 @@ export class ZKPassport {
207
214
  // This means the user has an ID that is not supported yet
208
215
  // So we won't receive any proofs and we can handle the result now
209
216
  this.topicToExpectedProofCount[topic] = 0;
217
+ this.topicToFailedProofCount[topic] += this.topicToExpectedProofCount[topic];
210
218
  if (this.topicToResults[topic]) {
211
219
  await this.handleResult(topic);
212
220
  }
@@ -215,6 +223,7 @@ export class ZKPassport {
215
223
  // This means one of the disclosure proofs failed to be generated
216
224
  // So we need to remove one from the expected proof count
217
225
  this.topicToExpectedProofCount[topic] -= 1;
226
+ this.topicToFailedProofCount[topic] += 1;
218
227
  // If the expected proof count is now equal to the number of proofs received
219
228
  // and the results were received, we can handle the result now
220
229
  if (this.topicToResults[topic] &&
@@ -271,9 +280,6 @@ export class ZKPassport {
271
280
  };
272
281
  return this.getZkPassportRequest(topic);
273
282
  },
274
- /*checkAML: (country?: CountryName | Alpha2Code | Alpha3Code) => {
275
- return this.getZkPassportRequest(topic)
276
- },*/
277
283
  done: () => {
278
284
  const base64Config = Buffer.from(JSON.stringify(this.topicToConfig[topic])).toString("base64");
279
285
  const base64Service = Buffer.from(JSON.stringify(this.topicToService[topic])).toString("base64");
@@ -384,6 +390,23 @@ export class ZKPassport {
384
390
  const defaultDateValue = new Date(1111, 10, 11);
385
391
  const currentTime = new Date();
386
392
  const today = new Date(currentTime.getFullYear(), currentTime.getMonth(), currentTime.getDate(), 0, 0, 0, 0);
393
+ const queryResultErrors = {
394
+ sig_check_dsc: {},
395
+ sig_check_id_data: {},
396
+ data_check_integrity: {},
397
+ disclose: {},
398
+ age: {},
399
+ birthdate: {},
400
+ expiry_date: {},
401
+ document_type: {},
402
+ issuing_country: {},
403
+ gender: {},
404
+ nationality: {},
405
+ firstname: {},
406
+ lastname: {},
407
+ fullname: {},
408
+ document_number: {},
409
+ };
387
410
  // Since the order is important for the commitments, we need to sort the proofs
388
411
  // by their expected order: root signature check -> ID signature check -> integrity check -> disclosure
389
412
  const sortedProofs = proofs.sort((a, b) => {
@@ -412,7 +435,11 @@ export class ZKPassport {
412
435
  if (merkleRoot !== expectedMerkleRoot) {
413
436
  console.warn("The ID was signed by an unrecognized root certificate");
414
437
  isCorrect = false;
415
- break;
438
+ queryResultErrors.sig_check_dsc.certificate = {
439
+ expected: `Certificate registry root: ${expectedMerkleRoot.toString()}`,
440
+ received: `Certificate registry root: ${merkleRoot.toString()}`,
441
+ message: "The ID was signed by an unrecognized root certificate",
442
+ };
416
443
  }
417
444
  }
418
445
  else if (proof.name?.startsWith("sig_check_id_data")) {
@@ -420,7 +447,11 @@ export class ZKPassport {
420
447
  if (commitmentIn !== commitmentOut) {
421
448
  console.warn("Failed to check the link between the certificate signature and ID signature");
422
449
  isCorrect = false;
423
- break;
450
+ queryResultErrors.sig_check_id_data.commitment = {
451
+ expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
452
+ received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
453
+ message: "Failed to check the link between the certificate signature and ID signature",
454
+ };
424
455
  }
425
456
  commitmentOut = getCommitmentOutFromIDDataProof(proofData);
426
457
  }
@@ -429,7 +460,11 @@ export class ZKPassport {
429
460
  if (commitmentIn !== commitmentOut) {
430
461
  console.warn("Failed to check the link between the ID signature and the data signed");
431
462
  isCorrect = false;
432
- break;
463
+ queryResultErrors.data_check_integrity.commitment = {
464
+ expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
465
+ received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
466
+ message: "Failed to check the link between the ID signature and the data signed",
467
+ };
433
468
  }
434
469
  commitmentOut = getCommitmentOutFromIntegrityProof(proofData);
435
470
  const currentDate = getCurrentDateFromIntegrityProof(proofData);
@@ -440,7 +475,11 @@ export class ZKPassport {
440
475
  if (todayToCurrentDate >= actualDifference) {
441
476
  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`);
442
477
  isCorrect = false;
443
- break;
478
+ queryResultErrors.data_check_integrity.date = {
479
+ expected: `Difference: ${this.topicToLocalConfig[topic]?.validity} days`,
480
+ received: `Difference: ${Math.round(todayToCurrentDate / 86400000)} days`,
481
+ message: "The date used to check the validity of the ID is older than the validity period",
482
+ };
444
483
  }
445
484
  }
446
485
  else if (proof.name === "disclose_bytes") {
@@ -448,7 +487,11 @@ export class ZKPassport {
448
487
  if (commitmentIn !== commitmentOut) {
449
488
  console.warn("Failed to check the link between the validity of the ID and the data to disclose");
450
489
  isCorrect = false;
451
- break;
490
+ queryResultErrors.disclose.commitment = {
491
+ expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
492
+ received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
493
+ message: "Failed to check the link between the validity of the ID and the data to disclose",
494
+ };
452
495
  }
453
496
  // We can't be certain that the disclosed data is for a passport or an ID card
454
497
  // so we need to check both (unless the document type is revealed)
@@ -461,12 +504,20 @@ export class ZKPassport {
461
504
  queryResult.document_type.eq.expected !== disclosedDataPassport.documentType) {
462
505
  console.warn("Document type does not match the expected document type");
463
506
  isCorrect = false;
464
- break;
507
+ queryResultErrors.document_type.eq = {
508
+ expected: `${queryResult.document_type.eq.expected}`,
509
+ received: `${disclosedDataPassport.documentType}`,
510
+ message: "Document type does not match the expected document type",
511
+ };
465
512
  }
466
513
  if (queryResult.document_type.disclose?.result !== disclosedDataIDCard.documentType) {
467
514
  console.warn("Document type does not match the disclosed document type in query result");
468
515
  isCorrect = false;
469
- break;
516
+ queryResultErrors.document_type.disclose = {
517
+ expected: `${queryResult.document_type.disclose?.result}`,
518
+ received: `${disclosedDataIDCard.documentType}`,
519
+ message: "Document type does not match the disclosed document type in query result",
520
+ };
470
521
  }
471
522
  }
472
523
  if (queryResult.birthdate) {
@@ -478,14 +529,22 @@ export class ZKPassport {
478
529
  queryResult.birthdate.eq.expected.getTime() !== birthdateIDCard.getTime()) {
479
530
  console.warn("Birthdate does not match the expected birthdate");
480
531
  isCorrect = false;
481
- break;
532
+ queryResultErrors.birthdate.eq = {
533
+ expected: `${queryResult.birthdate.eq.expected.toISOString()}`,
534
+ received: `${birthdatePassport.toISOString()}`,
535
+ message: "Birthdate does not match the expected birthdate",
536
+ };
482
537
  }
483
538
  if (queryResult.birthdate.disclose &&
484
539
  queryResult.birthdate.disclose.result.getTime() !== birthdatePassport.getTime() &&
485
540
  queryResult.birthdate.disclose.result.getTime() !== birthdateIDCard.getTime()) {
486
541
  console.warn("Birthdate does not match the disclosed birthdate in query result");
487
542
  isCorrect = false;
488
- break;
543
+ queryResultErrors.birthdate.disclose = {
544
+ expected: `${queryResult.birthdate.disclose.result.toISOString()}`,
545
+ received: `${birthdatePassport.toISOString()}`,
546
+ message: "Birthdate does not match the disclosed birthdate in query result",
547
+ };
489
548
  }
490
549
  }
491
550
  if (queryResult.expiry_date) {
@@ -497,14 +556,22 @@ export class ZKPassport {
497
556
  queryResult.expiry_date.eq.expected.getTime() !== expiryDateIDCard.getTime()) {
498
557
  console.warn("Expiry date does not match the expected expiry date");
499
558
  isCorrect = false;
500
- break;
559
+ queryResultErrors.expiry_date.eq = {
560
+ expected: `${queryResult.expiry_date.eq.expected.toISOString()}`,
561
+ received: `${expiryDatePassport.toISOString()}`,
562
+ message: "Expiry date does not match the expected expiry date",
563
+ };
501
564
  }
502
565
  if (queryResult.expiry_date.disclose &&
503
566
  queryResult.expiry_date.disclose.result.getTime() !== expiryDatePassport.getTime() &&
504
567
  queryResult.expiry_date.disclose.result.getTime() !== expiryDateIDCard.getTime()) {
505
568
  console.warn("Expiry date does not match the disclosed expiry date in query result");
506
569
  isCorrect = false;
507
- break;
570
+ queryResultErrors.expiry_date.disclose = {
571
+ expected: `${queryResult.expiry_date.disclose.result.toISOString()}`,
572
+ received: `${expiryDatePassport.toISOString()}`,
573
+ message: "Expiry date does not match the disclosed expiry date in query result",
574
+ };
508
575
  }
509
576
  }
510
577
  if (queryResult.nationality) {
@@ -516,14 +583,22 @@ export class ZKPassport {
516
583
  queryResult.nationality.eq.expected !== nationalityIDCard) {
517
584
  console.warn("Nationality does not match the expected nationality");
518
585
  isCorrect = false;
519
- break;
586
+ queryResultErrors.nationality.eq = {
587
+ expected: `${queryResult.nationality.eq.expected}`,
588
+ received: `${nationalityPassport}`,
589
+ message: "Nationality does not match the expected nationality",
590
+ };
520
591
  }
521
592
  if (queryResult.nationality.disclose &&
522
593
  queryResult.nationality.disclose.result !== nationalityPassport &&
523
594
  queryResult.nationality.disclose.result !== nationalityIDCard) {
524
595
  console.warn("Nationality does not match the disclosed nationality in query result");
525
596
  isCorrect = false;
526
- break;
597
+ queryResultErrors.nationality.disclose = {
598
+ expected: `${queryResult.nationality.disclose.result}`,
599
+ received: `${nationalityPassport}`,
600
+ message: "Nationality does not match the disclosed nationality in query result",
601
+ };
527
602
  }
528
603
  }
529
604
  if (queryResult.document_number) {
@@ -535,14 +610,22 @@ export class ZKPassport {
535
610
  queryResult.document_number.eq.expected !== documentNumberIDCard) {
536
611
  console.warn("Document number does not match the expected document number");
537
612
  isCorrect = false;
538
- break;
613
+ queryResultErrors.document_number.eq = {
614
+ expected: `${queryResult.document_number.eq.expected}`,
615
+ received: `${documentNumberPassport}`,
616
+ message: "Document number does not match the expected document number",
617
+ };
539
618
  }
540
619
  if (queryResult.document_number.disclose &&
541
620
  queryResult.document_number.disclose.result !== documentNumberPassport &&
542
621
  queryResult.document_number.disclose.result !== documentNumberIDCard) {
543
622
  console.warn("Document number does not match the disclosed document number in query result");
544
623
  isCorrect = false;
545
- break;
624
+ queryResultErrors.document_number.disclose = {
625
+ expected: `${queryResult.document_number.disclose.result}`,
626
+ received: `${documentNumberPassport}`,
627
+ message: "Document number does not match the disclosed document number in query result",
628
+ };
546
629
  }
547
630
  }
548
631
  if (queryResult.gender) {
@@ -554,14 +637,22 @@ export class ZKPassport {
554
637
  queryResult.gender.eq.expected !== genderIDCard) {
555
638
  console.warn("Gender does not match the expected gender");
556
639
  isCorrect = false;
557
- break;
640
+ queryResultErrors.gender.eq = {
641
+ expected: `${queryResult.gender.eq.expected}`,
642
+ received: `${genderPassport}`,
643
+ message: "Gender does not match the expected gender",
644
+ };
558
645
  }
559
646
  if (queryResult.gender.disclose &&
560
647
  queryResult.gender.disclose.result !== genderPassport &&
561
648
  queryResult.gender.disclose.result !== genderIDCard) {
562
649
  console.warn("Gender does not match the disclosed gender in query result");
563
650
  isCorrect = false;
564
- break;
651
+ queryResultErrors.gender.disclose = {
652
+ expected: `${queryResult.gender.disclose.result}`,
653
+ received: `${genderPassport}`,
654
+ message: "Gender does not match the disclosed gender in query result",
655
+ };
565
656
  }
566
657
  }
567
658
  if (queryResult.issuing_country) {
@@ -573,14 +664,22 @@ export class ZKPassport {
573
664
  queryResult.issuing_country.eq.expected !== issuingCountryIDCard) {
574
665
  console.warn("Issuing country does not match the expected issuing country");
575
666
  isCorrect = false;
576
- break;
667
+ queryResultErrors.issuing_country.eq = {
668
+ expected: `${queryResult.issuing_country.eq.expected}`,
669
+ received: `${issuingCountryPassport}`,
670
+ message: "Issuing country does not match the expected issuing country",
671
+ };
577
672
  }
578
673
  if (queryResult.issuing_country.disclose &&
579
674
  queryResult.issuing_country.disclose.result !== issuingCountryPassport &&
580
675
  queryResult.issuing_country.disclose.result !== issuingCountryIDCard) {
581
676
  console.warn("Issuing country does not match the disclosed issuing country in query result");
582
677
  isCorrect = false;
583
- break;
678
+ queryResultErrors.issuing_country.disclose = {
679
+ expected: `${queryResult.issuing_country.disclose.result}`,
680
+ received: `${issuingCountryPassport}`,
681
+ message: "Issuing country does not match the disclosed issuing country in query result",
682
+ };
584
683
  }
585
684
  }
586
685
  if (queryResult.fullname) {
@@ -594,7 +693,11 @@ export class ZKPassport {
594
693
  fullnameIDCard.toLowerCase()) {
595
694
  console.warn("Fullname does not match the expected fullname");
596
695
  isCorrect = false;
597
- break;
696
+ queryResultErrors.fullname.eq = {
697
+ expected: `${queryResult.fullname.eq.expected}`,
698
+ received: `${fullnamePassport}`,
699
+ message: "Fullname does not match the expected fullname",
700
+ };
598
701
  }
599
702
  if (queryResult.fullname.disclose &&
600
703
  formatName(queryResult.fullname.disclose.result).toLowerCase() !==
@@ -603,7 +706,11 @@ export class ZKPassport {
603
706
  fullnameIDCard.toLowerCase()) {
604
707
  console.warn("Fullname does not match the disclosed fullname in query result");
605
708
  isCorrect = false;
606
- break;
709
+ queryResultErrors.fullname.disclose = {
710
+ expected: `${queryResult.fullname.disclose.result}`,
711
+ received: `${fullnamePassport}`,
712
+ message: "Fullname does not match the disclosed fullname in query result",
713
+ };
607
714
  }
608
715
  }
609
716
  if (queryResult.firstname) {
@@ -622,7 +729,11 @@ export class ZKPassport {
622
729
  firstnameIDCard.toLowerCase()) {
623
730
  console.warn("Firstname does not match the expected firstname");
624
731
  isCorrect = false;
625
- break;
732
+ queryResultErrors.firstname.eq = {
733
+ expected: `${queryResult.firstname.eq.expected}`,
734
+ received: `${firstnamePassport}`,
735
+ message: "Firstname does not match the expected firstname",
736
+ };
626
737
  }
627
738
  if (queryResult.firstname.disclose &&
628
739
  formatName(queryResult.firstname.disclose.result).toLowerCase() !==
@@ -631,7 +742,11 @@ export class ZKPassport {
631
742
  firstnameIDCard.toLowerCase()) {
632
743
  console.warn("Firstname does not match the disclosed firstname in query result");
633
744
  isCorrect = false;
634
- break;
745
+ queryResultErrors.firstname.disclose = {
746
+ expected: `${queryResult.firstname.disclose.result}`,
747
+ received: `${firstnamePassport}`,
748
+ message: "Firstname does not match the disclosed firstname in query result",
749
+ };
635
750
  }
636
751
  }
637
752
  if (queryResult.lastname) {
@@ -650,7 +765,11 @@ export class ZKPassport {
650
765
  lastnameIDCard.toLowerCase()) {
651
766
  console.warn("Lastname does not match the expected lastname");
652
767
  isCorrect = false;
653
- break;
768
+ queryResultErrors.lastname.eq = {
769
+ expected: `${queryResult.lastname.eq.expected}`,
770
+ received: `${lastnamePassport}`,
771
+ message: "Lastname does not match the expected lastname",
772
+ };
654
773
  }
655
774
  if (queryResult.lastname.disclose &&
656
775
  formatName(queryResult.lastname.disclose.result).toLowerCase() !==
@@ -659,7 +778,11 @@ export class ZKPassport {
659
778
  lastnameIDCard.toLowerCase()) {
660
779
  console.warn("Lastname does not match the disclosed lastname in query result");
661
780
  isCorrect = false;
662
- break;
781
+ queryResultErrors.lastname.disclose = {
782
+ expected: `${queryResult.lastname.disclose.result}`,
783
+ received: `${lastnamePassport}`,
784
+ message: "Lastname does not match the disclosed lastname in query result",
785
+ };
663
786
  }
664
787
  }
665
788
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
@@ -669,7 +792,11 @@ export class ZKPassport {
669
792
  if (commitmentIn !== commitmentOut) {
670
793
  console.warn("Failed to check the link between the validity of the ID and the age derived from it");
671
794
  isCorrect = false;
672
- break;
795
+ queryResultErrors.age.commitment = {
796
+ expected: `Commitment: ${commitmentOut}`,
797
+ received: `Commitment: ${commitmentIn}`,
798
+ message: "Failed to check the link between the validity of the ID and the age derived from it",
799
+ };
673
800
  }
674
801
  const minAge = getMinAgeFromProof(proofData);
675
802
  const maxAge = getMaxAgeFromProof(proofData);
@@ -679,14 +806,22 @@ export class ZKPassport {
679
806
  minAge < queryResult.age.gte.expected) {
680
807
  console.warn("Age is not greater than or equal to the expected age");
681
808
  isCorrect = false;
682
- break;
809
+ queryResultErrors.age.gte = {
810
+ expected: queryResult.age.gte.expected,
811
+ received: minAge,
812
+ message: "Age is not greater than or equal to the expected age",
813
+ };
683
814
  }
684
815
  if (queryResult.age.lt &&
685
816
  queryResult.age.lt.result &&
686
817
  maxAge >= queryResult.age.lt.expected) {
687
818
  console.warn("Age is not less than the expected age");
688
819
  isCorrect = false;
689
- break;
820
+ queryResultErrors.age.lt = {
821
+ expected: queryResult.age.lt.expected,
822
+ received: maxAge,
823
+ message: "Age is not less than the expected age",
824
+ };
690
825
  }
691
826
  if (queryResult.age.range) {
692
827
  if (queryResult.age.range.result &&
@@ -694,38 +829,60 @@ export class ZKPassport {
694
829
  maxAge >= queryResult.age.range.expected[1])) {
695
830
  console.warn("Age is not in the expected range");
696
831
  isCorrect = false;
697
- break;
832
+ queryResultErrors.age.range = {
833
+ expected: queryResult.age.range.expected,
834
+ received: [minAge, maxAge],
835
+ message: "Age is not in the expected range",
836
+ };
698
837
  }
699
838
  }
700
839
  if (!queryResult.age.lt && !queryResult.age.range && maxAge != 0) {
701
840
  console.warn("Maximum age should be equal to 0");
702
841
  isCorrect = false;
703
- break;
842
+ queryResultErrors.age.disclose = {
843
+ expected: 0,
844
+ received: maxAge,
845
+ message: "Maximum age should be equal to 0",
846
+ };
704
847
  }
705
848
  if (!queryResult.age.gte && !queryResult.age.range && minAge != 0) {
706
849
  console.warn("Minimum age should be equal to 0");
707
850
  isCorrect = false;
708
- break;
851
+ queryResultErrors.age.disclose = {
852
+ expected: 0,
853
+ received: minAge,
854
+ message: "Minimum age should be equal to 0",
855
+ };
709
856
  }
710
857
  if (queryResult.age.disclose &&
711
858
  (queryResult.age.disclose.result !== minAge ||
712
859
  queryResult.age.disclose.result !== maxAge)) {
713
860
  console.warn("Age does not match the disclosed age in query result");
714
861
  isCorrect = false;
715
- break;
862
+ queryResultErrors.age.disclose = {
863
+ expected: `${minAge}`,
864
+ received: `${queryResult.age.disclose.result}`,
865
+ message: "Age does not match the disclosed age in query result",
866
+ };
716
867
  }
717
868
  }
718
869
  else {
719
870
  console.warn("Age is not set in the query result");
720
871
  isCorrect = false;
721
- break;
872
+ queryResultErrors.age.disclose = {
873
+ message: "Age is not set in the query result",
874
+ };
722
875
  }
723
876
  const currentDate = getCurrentDateFromAgeProof(proofData);
724
877
  if (currentDate.getTime() !== today.getTime() &&
725
878
  currentDate.getTime() !== today.getTime() - 86400000) {
726
879
  console.warn("Current date in the proof is too old");
727
880
  isCorrect = false;
728
- break;
881
+ queryResultErrors.age.disclose = {
882
+ expected: `${today.toISOString()}`,
883
+ received: `${currentDate.toISOString()}`,
884
+ message: "Current date in the proof is too old",
885
+ };
729
886
  }
730
887
  uniqueIdentifier = getCommitmentInFromDisclosureProof(proofData).toString(10);
731
888
  }
@@ -734,7 +891,11 @@ export class ZKPassport {
734
891
  if (commitmentIn !== commitmentOut) {
735
892
  console.warn("Failed to check the link between the validity of the ID and the birthdate derived from it");
736
893
  isCorrect = false;
737
- break;
894
+ queryResultErrors.birthdate.commitment = {
895
+ expected: `Commitment: ${commitmentOut}`,
896
+ received: `Commitment: ${commitmentIn}`,
897
+ message: "Failed to check the link between the validity of the ID and the birthdate derived from it",
898
+ };
738
899
  }
739
900
  const minDate = getMinDateFromProof(proofData);
740
901
  const maxDate = getMaxDateFromProof(proofData);
@@ -744,14 +905,22 @@ export class ZKPassport {
744
905
  minDate < queryResult.birthdate.gte.expected) {
745
906
  console.warn("Birthdate is not greater than or equal to the expected birthdate");
746
907
  isCorrect = false;
747
- break;
908
+ queryResultErrors.birthdate.gte = {
909
+ expected: queryResult.birthdate.gte.expected,
910
+ received: minDate,
911
+ message: "Birthdate is not greater than or equal to the expected birthdate",
912
+ };
748
913
  }
749
914
  if (queryResult.birthdate.lte &&
750
915
  queryResult.birthdate.lte.result &&
751
916
  maxDate > queryResult.birthdate.lte.expected) {
752
917
  console.warn("Birthdate is not less than the expected birthdate");
753
918
  isCorrect = false;
754
- break;
919
+ queryResultErrors.birthdate.lte = {
920
+ expected: queryResult.birthdate.lte.expected,
921
+ received: maxDate,
922
+ message: "Birthdate is not less than the expected birthdate",
923
+ };
755
924
  }
756
925
  if (queryResult.birthdate.range) {
757
926
  if (queryResult.birthdate.range.result &&
@@ -759,7 +928,11 @@ export class ZKPassport {
759
928
  maxDate > queryResult.birthdate.range.expected[1])) {
760
929
  console.warn("Birthdate is not in the expected range");
761
930
  isCorrect = false;
762
- break;
931
+ queryResultErrors.birthdate.range = {
932
+ expected: queryResult.birthdate.range.expected,
933
+ received: [minDate, maxDate],
934
+ message: "Birthdate is not in the expected range",
935
+ };
763
936
  }
764
937
  }
765
938
  if (!queryResult.birthdate.lte &&
@@ -767,20 +940,30 @@ export class ZKPassport {
767
940
  maxDate.getTime() != defaultDateValue.getTime()) {
768
941
  console.warn("Maximum birthdate should be equal to default date value");
769
942
  isCorrect = false;
770
- break;
943
+ queryResultErrors.birthdate.disclose = {
944
+ expected: `${defaultDateValue.toISOString()}`,
945
+ received: `${maxDate.toISOString()}`,
946
+ message: "Maximum birthdate should be equal to default date value",
947
+ };
771
948
  }
772
949
  if (!queryResult.birthdate.gte &&
773
950
  !queryResult.birthdate.range &&
774
951
  minDate.getTime() != defaultDateValue.getTime()) {
775
952
  console.warn("Minimum birthdate should be equal to default date value");
776
953
  isCorrect = false;
777
- break;
954
+ queryResultErrors.birthdate.disclose = {
955
+ expected: `${defaultDateValue.toISOString()}`,
956
+ received: `${minDate.toISOString()}`,
957
+ message: "Minimum birthdate should be equal to default date value",
958
+ };
778
959
  }
779
960
  }
780
961
  else {
781
962
  console.warn("Birthdate is not set in the query result");
782
963
  isCorrect = false;
783
- break;
964
+ queryResultErrors.birthdate.disclose = {
965
+ message: "Birthdate is not set in the query result",
966
+ };
784
967
  }
785
968
  uniqueIdentifier = getCommitmentInFromDisclosureProof(proofData).toString(10);
786
969
  }
@@ -789,7 +972,11 @@ export class ZKPassport {
789
972
  if (commitmentIn !== commitmentOut) {
790
973
  console.warn("Failed to check the link between the validity of the ID and its expiry date");
791
974
  isCorrect = false;
792
- break;
975
+ queryResultErrors.expiry_date.commitment = {
976
+ expected: `Commitment: ${commitmentOut}`,
977
+ received: `Commitment: ${commitmentIn}`,
978
+ message: "Failed to check the link between the validity of the ID and its expiry date",
979
+ };
793
980
  }
794
981
  const minDate = getMinDateFromProof(proofData);
795
982
  const maxDate = getMaxDateFromProof(proofData);
@@ -799,14 +986,22 @@ export class ZKPassport {
799
986
  minDate < queryResult.expiry_date.gte.expected) {
800
987
  console.warn("Expiry date is not greater than or equal to the expected expiry date");
801
988
  isCorrect = false;
802
- break;
989
+ queryResultErrors.expiry_date.gte = {
990
+ expected: queryResult.expiry_date.gte.expected,
991
+ received: minDate,
992
+ message: "Expiry date is not greater than or equal to the expected expiry date",
993
+ };
803
994
  }
804
995
  if (queryResult.expiry_date.lte &&
805
996
  queryResult.expiry_date.lte.result &&
806
997
  maxDate > queryResult.expiry_date.lte.expected) {
807
998
  console.warn("Expiry date is not less than the expected expiry date");
808
999
  isCorrect = false;
809
- break;
1000
+ queryResultErrors.expiry_date.lte = {
1001
+ expected: queryResult.expiry_date.lte.expected,
1002
+ received: maxDate,
1003
+ message: "Expiry date is not less than the expected expiry date",
1004
+ };
810
1005
  }
811
1006
  if (queryResult.expiry_date.range) {
812
1007
  if (queryResult.expiry_date.range.result &&
@@ -814,7 +1009,11 @@ export class ZKPassport {
814
1009
  maxDate > queryResult.expiry_date.range.expected[1])) {
815
1010
  console.warn("Expiry date is not in the expected range");
816
1011
  isCorrect = false;
817
- break;
1012
+ queryResultErrors.expiry_date.range = {
1013
+ expected: queryResult.expiry_date.range.expected,
1014
+ received: [minDate, maxDate],
1015
+ message: "Expiry date is not in the expected range",
1016
+ };
818
1017
  }
819
1018
  }
820
1019
  if (!queryResult.expiry_date.lte &&
@@ -822,20 +1021,30 @@ export class ZKPassport {
822
1021
  maxDate.getTime() != defaultDateValue.getTime()) {
823
1022
  console.warn("Maximum expiry date should be equal to default date value");
824
1023
  isCorrect = false;
825
- break;
1024
+ queryResultErrors.expiry_date.disclose = {
1025
+ expected: `${defaultDateValue.toISOString()}`,
1026
+ received: `${maxDate.toISOString()}`,
1027
+ message: "Maximum expiry date should be equal to default date value",
1028
+ };
826
1029
  }
827
1030
  if (!queryResult.expiry_date.gte &&
828
1031
  !queryResult.expiry_date.range &&
829
1032
  minDate.getTime() != defaultDateValue.getTime()) {
830
1033
  console.warn("Minimum expiry date should be equal to default date value");
831
1034
  isCorrect = false;
832
- break;
1035
+ queryResultErrors.expiry_date.disclose = {
1036
+ expected: `${defaultDateValue.toISOString()}`,
1037
+ received: `${minDate.toISOString()}`,
1038
+ message: "Minimum expiry date should be equal to default date value",
1039
+ };
833
1040
  }
834
1041
  }
835
1042
  else {
836
1043
  console.warn("Expiry date is not set in the query result");
837
1044
  isCorrect = false;
838
- break;
1045
+ queryResultErrors.expiry_date.disclose = {
1046
+ message: "Expiry date is not set in the query result",
1047
+ };
839
1048
  }
840
1049
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
841
1050
  }
@@ -844,7 +1053,11 @@ export class ZKPassport {
844
1053
  if (commitmentIn !== commitmentOut) {
845
1054
  console.warn("Failed to check the link between the validity of the ID and the country exclusion check");
846
1055
  isCorrect = false;
847
- break;
1056
+ queryResultErrors.nationality.commitment = {
1057
+ expected: `Commitment: ${commitmentOut}`,
1058
+ received: `Commitment: ${commitmentIn}`,
1059
+ message: "Failed to check the link between the validity of the ID and the country exclusion check",
1060
+ };
848
1061
  }
849
1062
  const countryList = getCountryListFromExclusionProof(proofData);
850
1063
  if (queryResult.nationality &&
@@ -853,13 +1066,19 @@ export class ZKPassport {
853
1066
  if (!queryResult.nationality.out.expected?.every((country) => countryList.includes(country))) {
854
1067
  console.warn("Country exclusion list does not match the one from the query results");
855
1068
  isCorrect = false;
856
- break;
1069
+ queryResultErrors.nationality.out = {
1070
+ expected: queryResult.nationality.out.expected,
1071
+ received: countryList,
1072
+ message: "Country exclusion list does not match the one from the query results",
1073
+ };
857
1074
  }
858
1075
  }
859
1076
  else if (!queryResult.nationality || !queryResult.nationality.out) {
860
1077
  console.warn("Nationality exclusion is not set in the query result");
861
1078
  isCorrect = false;
862
- break;
1079
+ queryResultErrors.nationality.out = {
1080
+ message: "Nationality exclusion is not set in the query result",
1081
+ };
863
1082
  }
864
1083
  // Check the countryList is in ascending order
865
1084
  // If the prover doesn't use a sorted list then the proof cannot be trusted
@@ -868,7 +1087,9 @@ export class ZKPassport {
868
1087
  if (countryList[i] < countryList[i - 1]) {
869
1088
  console.warn("The nationality exclusion list has not been sorted, and thus the proof cannot be trusted");
870
1089
  isCorrect = false;
871
- break;
1090
+ queryResultErrors.nationality.out = {
1091
+ message: "The nationality exclusion list has not been sorted, and thus the proof cannot be trusted",
1092
+ };
872
1093
  }
873
1094
  }
874
1095
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
@@ -878,7 +1099,11 @@ export class ZKPassport {
878
1099
  if (commitmentIn !== commitmentOut) {
879
1100
  console.warn("Failed to check the link between the validity of the ID and the country inclusion check");
880
1101
  isCorrect = false;
881
- break;
1102
+ queryResultErrors.nationality.commitment = {
1103
+ expected: `Commitment: ${commitmentOut}`,
1104
+ received: `Commitment: ${commitmentIn}`,
1105
+ message: "Failed to check the link between the validity of the ID and the country inclusion check",
1106
+ };
882
1107
  }
883
1108
  const countryList = getCountryListFromInclusionProof(proofData);
884
1109
  if (queryResult.nationality &&
@@ -887,18 +1112,24 @@ export class ZKPassport {
887
1112
  if (!queryResult.nationality.in.expected?.every((country) => countryList.includes(country))) {
888
1113
  console.warn("Country inclusion list does not match the one from the query results");
889
1114
  isCorrect = false;
890
- break;
1115
+ queryResultErrors.nationality.in = {
1116
+ expected: queryResult.nationality.in.expected,
1117
+ received: countryList,
1118
+ message: "Country inclusion list does not match the one from the query results",
1119
+ };
891
1120
  }
892
1121
  }
893
1122
  else if (!queryResult.nationality || !queryResult.nationality.in) {
894
1123
  console.warn("Nationality inclusion is not set in the query result");
895
1124
  isCorrect = false;
896
- break;
1125
+ queryResultErrors.nationality.in = {
1126
+ message: "Nationality inclusion is not set in the query result",
1127
+ };
897
1128
  }
898
1129
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
899
1130
  }
900
1131
  }
901
- return { isCorrect, uniqueIdentifier };
1132
+ return { isCorrect, uniqueIdentifier, queryResultErrors };
902
1133
  }
903
1134
  /**
904
1135
  * @notice Verify the proofs received from the mobile app.
@@ -913,12 +1144,6 @@ export class ZKPassport {
913
1144
  // There is a minimum of 4 subproofs to make a complete proof
914
1145
  if (!proofs || proofs.length < 4) {
915
1146
  proofsToVerify = this.topicToProofs[requestId];
916
- if (!proofsToVerify || proofsToVerify.length < 4) {
917
- // It may happen that a request returns a result without proofs
918
- // Meaning the ID is not supported yet by ZKPassport circuits,
919
- // so the results has to be trusted and cannot be independently verified
920
- return { uniqueIdentifier: undefined, verified: false };
921
- }
922
1147
  }
923
1148
  const { BarretenbergVerifier } = await import("@aztec/bb.js");
924
1149
  const verifier = new BarretenbergVerifier();
@@ -927,13 +1152,15 @@ export class ZKPassport {
927
1152
  }*/
928
1153
  let verified = true;
929
1154
  let uniqueIdentifier;
1155
+ let queryResultErrors;
930
1156
  if (queryResult) {
931
- const { isCorrect, uniqueIdentifier: uniqueIdentifierFromPublicInputs } = await this.checkPublicInputs(proofsToVerify, queryResult, requestId);
1157
+ const { isCorrect, uniqueIdentifier: uniqueIdentifierFromPublicInputs, queryResultErrors: queryResultErrorsFromPublicInputs, } = await this.checkPublicInputs(proofsToVerify, queryResult, requestId);
932
1158
  uniqueIdentifier = uniqueIdentifierFromPublicInputs;
933
1159
  verified = isCorrect;
1160
+ queryResultErrors = isCorrect ? undefined : queryResultErrorsFromPublicInputs;
934
1161
  }
935
1162
  // Only proceed with the proof verification if the public inputs are correct
936
- if (verified) {
1163
+ if (verified && queryResult) {
937
1164
  for (const proof of proofsToVerify) {
938
1165
  const proofData = getProofData(proof.proof, true);
939
1166
  const hostedPackagedCircuit = await getHostedPackagedCircuitByName(proof.version, proof.name);
@@ -953,7 +1180,7 @@ export class ZKPassport {
953
1180
  }
954
1181
  }
955
1182
  this.topicToProofs[requestId] = [];
956
- return { uniqueIdentifier, verified };
1183
+ return { uniqueIdentifier, verified, queryResultErrors };
957
1184
  }
958
1185
  /**
959
1186
  * @notice Returns the URL of the request.
@@ -971,14 +1198,17 @@ export class ZKPassport {
971
1198
  * @param requestId The request ID.
972
1199
  */
973
1200
  cancelRequest(requestId) {
974
- this.topicToWebSocketClient[requestId].close();
975
- delete this.topicToWebSocketClient[requestId];
1201
+ if (this.topicToWebSocketClient[requestId]) {
1202
+ this.topicToWebSocketClient[requestId].close();
1203
+ delete this.topicToWebSocketClient[requestId];
1204
+ }
976
1205
  delete this.topicToKeyPair[requestId];
977
1206
  delete this.topicToConfig[requestId];
978
1207
  delete this.topicToLocalConfig[requestId];
979
1208
  delete this.topicToSharedSecret[requestId];
980
1209
  delete this.topicToProofs[requestId];
981
1210
  delete this.topicToExpectedProofCount[requestId];
1211
+ delete this.topicToFailedProofCount[requestId];
982
1212
  delete this.topicToResults[requestId];
983
1213
  this.onRequestReceivedCallbacks[requestId] = [];
984
1214
  this.onGeneratingProofCallbacks[requestId] = [];
@@ -987,4 +1217,12 @@ export class ZKPassport {
987
1217
  this.onRejectCallbacks[requestId] = [];
988
1218
  this.onErrorCallbacks[requestId] = [];
989
1219
  }
1220
+ /**
1221
+ * @notice Clears all requests.
1222
+ */
1223
+ clearAllRequests() {
1224
+ for (const requestId in this.topicToWebSocketClient) {
1225
+ this.cancelRequest(requestId);
1226
+ }
1227
+ }
990
1228
  }