@cheqd/did-provider-cheqd 3.5.0 → 3.6.0-develop.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/build/cjs/agent/ICheqd.d.ts +98 -129
  2. package/build/cjs/agent/ICheqd.d.ts.map +1 -1
  3. package/build/cjs/agent/ICheqd.js +916 -246
  4. package/build/cjs/agent/ICheqd.js.map +1 -1
  5. package/build/cjs/did-manager/cheqd-did-provider.d.ts +14 -1
  6. package/build/cjs/did-manager/cheqd-did-provider.d.ts.map +1 -1
  7. package/build/cjs/did-manager/cheqd-did-provider.js +17 -4
  8. package/build/cjs/did-manager/cheqd-did-provider.js.map +1 -1
  9. package/build/cjs/dkg-threshold/lit-protocol.d.ts +4 -1
  10. package/build/cjs/dkg-threshold/lit-protocol.d.ts.map +1 -1
  11. package/build/cjs/dkg-threshold/lit-protocol.js +10 -0
  12. package/build/cjs/dkg-threshold/lit-protocol.js.map +1 -1
  13. package/build/esm/agent/ICheqd.d.ts +98 -129
  14. package/build/esm/agent/ICheqd.d.ts.map +1 -1
  15. package/build/esm/agent/ICheqd.js +918 -248
  16. package/build/esm/agent/ICheqd.js.map +1 -1
  17. package/build/esm/did-manager/cheqd-did-provider.d.ts +14 -1
  18. package/build/esm/did-manager/cheqd-did-provider.d.ts.map +1 -1
  19. package/build/esm/did-manager/cheqd-did-provider.js +16 -3
  20. package/build/esm/did-manager/cheqd-did-provider.js.map +1 -1
  21. package/build/esm/dkg-threshold/lit-protocol.d.ts +4 -1
  22. package/build/esm/dkg-threshold/lit-protocol.d.ts.map +1 -1
  23. package/build/esm/dkg-threshold/lit-protocol.js +10 -0
  24. package/build/esm/dkg-threshold/lit-protocol.js.map +1 -1
  25. package/build/tsconfig.cjs.tsbuildinfo +1 -1
  26. package/build/tsconfig.esm.tsbuildinfo +1 -1
  27. package/build/tsconfig.types.tsbuildinfo +1 -1
  28. package/build/types/agent/ICheqd.d.ts +98 -129
  29. package/build/types/agent/ICheqd.d.ts.map +1 -1
  30. package/build/types/did-manager/cheqd-did-provider.d.ts +14 -1
  31. package/build/types/did-manager/cheqd-did-provider.d.ts.map +1 -1
  32. package/build/types/dkg-threshold/lit-protocol.d.ts +4 -1
  33. package/build/types/dkg-threshold/lit-protocol.d.ts.map +1 -1
  34. package/package.json +3 -2
  35. package/src/agent/ICheqd.ts +1101 -351
  36. package/src/did-manager/cheqd-did-provider.ts +32 -6
  37. package/src/dkg-threshold/lit-protocol.ts +15 -2
@@ -10,8 +10,8 @@ import { StatusList } from '@digitalbazaar/vc-status-list';
10
10
  import { v4 } from 'uuid';
11
11
  import fs from 'fs';
12
12
  import Debug from 'debug';
13
- import { LitCompatibleCosmosChains, LitProtocol } from '../dkg-threshold/lit-protocol.js';
14
- import { blobToHexString, randomFromRange, toBlob, unescapeUnicode } from '../utils/helpers.js';
13
+ import { LitCompatibleCosmosChains, LitProtocol, } from '../dkg-threshold/lit-protocol.js';
14
+ import { blobToHexString, randomFromRange, toBlob, } from '../utils/helpers.js';
15
15
  import { resolverUrl } from '../did-manager/cheqd-did-resolver.js';
16
16
  const debug = Debug('veramo:did-provider-cheqd');
17
17
  export const AccessControlConditionTypes = { timelockPayment: 'timelockPayment', memoNonce: 'memoNonce', balance: 'balance' };
@@ -28,7 +28,6 @@ const GenerateDidDocWithLinkedResourceMethodName = 'cheqdGenerateDidDocWithLinke
28
28
  const GenerateKeyPairMethodName = 'cheqdGenerateIdentityKeys';
29
29
  const GenerateVersionIdMethodName = 'cheqdGenerateVersionId';
30
30
  const GenerateStatusList2021MethodName = 'cheqdGenerateStatusList2021';
31
- const GenerateEncryptedStatusList2021MethodName = 'cheqdGenerateEncryptedStatusList2021';
32
31
  const IssueRevocableCredentialWithStatusList2021MethodName = 'cheqdIssueRevocableCredentialWithStatusList2021';
33
32
  const IssueSuspendableCredentialWithStatusList2021MethodName = 'cheqdIssueSuspendableCredentialWithStatusList2021';
34
33
  const VerifyCredentialMethodName = 'cheqdVerifyCredential';
@@ -40,8 +39,8 @@ const SuspendCredentialMethodName = 'cheqdSuspendCredential';
40
39
  const SuspendCredentialsMethodName = 'cheqdSuspendCredentials';
41
40
  const UnsuspendCredentialMethodName = 'cheqdUnsuspendCredential';
42
41
  const UnsuspendCredentialsMethodName = 'cheqdUnsuspendCredentials';
43
- const TransactVerifierPaysIssuerMethodName = 'cheqdTransactVerifierPaysIssuer';
44
- const ObserveVerifierPaysIssuerMethodName = 'cheqdObserveVerifierPaysIssuer';
42
+ const TransactSendTokensMethodName = 'cheqdTransactSendTokens';
43
+ const ObservePaymentConditionMethodName = 'cheqdObservePaymentCondition';
45
44
  const DidPrefix = 'did';
46
45
  const CheqdDidMethod = 'cheqd';
47
46
  export class Cheqd {
@@ -239,24 +238,6 @@ export class Cheqd {
239
238
  "type": "string"
240
239
  }
241
240
  },
242
- "cheqdGenerateEncryptedStatusList2021": {
243
- "description": "Generate a new encrypted Status List 2021",
244
- "arguments": {
245
- "type": "object",
246
- "properties": {
247
- "args": {
248
- "type": "object",
249
- "description": "A cheqdGenerateEncryptedStatusList2021Args object as any for extensibility"
250
- }
251
- },
252
- "required": [
253
- "args"
254
- ]
255
- },
256
- "returnType": {
257
- "type": "string"
258
- }
259
- },
260
241
  "cheqdIssueRevocableCredentialWithStatusList2021": {
261
242
  "description": "Issue a revocable credential with a Status List 2021 as credential status registry",
262
243
  "arguments": {
@@ -455,14 +436,14 @@ export class Cheqd {
455
436
  "type": "array"
456
437
  }
457
438
  },
458
- "cheqdTransactVerifierPaysIssuer": {
459
- "description": "Initiate a transaction where the verifier pays the issuer for a credential status check",
439
+ "cheqdTransactSendTokens": {
440
+ "description": "Send tokens from one account to another",
460
441
  "arguments": {
461
442
  "type": "object",
462
443
  "properties": {
463
444
  "args": {
464
445
  "type": "object",
465
- "description": "A cheqdTransactVerifierPaysIssuerArgs object as any for extensibility"
446
+ "description": "A cheqdTransactSendTokensArgs object as any for extensibility"
466
447
  }
467
448
  },
468
449
  "required": [
@@ -473,14 +454,14 @@ export class Cheqd {
473
454
  "type": "object"
474
455
  }
475
456
  },
476
- "cheqdObserveVerifierPaysIssuer": {
477
- "description": "Observe a transaction where the verifier pays the issuer for a credential status check",
457
+ "cheqdObservePaymentCondition": {
458
+ "description": "Observe payment conditions for a given set of payment conditions",
478
459
  "arguments": {
479
460
  "type": "object",
480
461
  "properties": {
481
462
  "args": {
482
463
  "type": "object",
483
- "description": "cheqdObserveVerifierPaysIssuerArgs object as any for extensibility"
464
+ "description": "cheqdObservePaymentConditionArgs object as any for extensibility"
484
465
  }
485
466
  },
486
467
  "required": [
@@ -519,7 +500,6 @@ export class Cheqd {
519
500
  [GenerateKeyPairMethodName]: this.GenerateIdentityKeys.bind(this),
520
501
  [GenerateVersionIdMethodName]: this.GenerateVersionId.bind(this),
521
502
  [GenerateStatusList2021MethodName]: this.GenerateStatusList2021.bind(this),
522
- [GenerateEncryptedStatusList2021MethodName]: this.GenerateEncryptedStatusList2021.bind(this),
523
503
  [IssueRevocableCredentialWithStatusList2021MethodName]: this.IssueRevocableCredentialWithStatusList2021.bind(this),
524
504
  [IssueSuspendableCredentialWithStatusList2021MethodName]: this.IssueSuspendableCredentialWithStatusList2021.bind(this),
525
505
  [VerifyCredentialMethodName]: this.VerifyCredentialWithStatusList2021.bind(this),
@@ -531,8 +511,8 @@ export class Cheqd {
531
511
  [SuspendCredentialsMethodName]: this.SuspendBulkCredentialsWithStatusList2021.bind(this),
532
512
  [UnsuspendCredentialMethodName]: this.UnsuspendCredentialWithStatusList2021.bind(this),
533
513
  [UnsuspendCredentialsMethodName]: this.UnsuspendBulkCredentialsWithStatusList2021.bind(this),
534
- [TransactVerifierPaysIssuerMethodName]: this.TransactVerifierPaysIssuer.bind(this),
535
- [ObserveVerifierPaysIssuerMethodName]: this.ObserveVerifierPaysIssuer.bind(this),
514
+ [TransactSendTokensMethodName]: this.TransactSendTokens.bind(this),
515
+ [ObservePaymentConditionMethodName]: this.ObservePaymentCondition.bind(this),
536
516
  };
537
517
  }
538
518
  async CreateIdentifier(args, context) {
@@ -680,11 +660,11 @@ export class Cheqd {
680
660
  if (!args?.paymentConditions || !args?.paymentConditions?.length || !Array.isArray(args?.paymentConditions) || args?.paymentConditions.length === 0) {
681
661
  throw new Error('[did-provider-cheqd]: paymentConditions is required');
682
662
  }
683
- if (!args?.paymentConditions?.every((condition) => condition.feePaymentAddress && condition.feePaymentAmount)) {
684
- throw new Error('[did-provider-cheqd]: paymentConditions must contain feePaymentAddress and feeAmount');
663
+ if (!args?.paymentConditions?.every((condition) => condition.feePaymentAddress && condition.feePaymentAmount && condition.intervalInSeconds)) {
664
+ throw new Error('[did-provider-cheqd]: paymentConditions must contain feePaymentAddress and feeAmount and intervalInSeconds');
685
665
  }
686
- if (!args?.paymentConditions?.every((condition) => typeof condition.feePaymentAddress === 'string' && typeof condition.feePaymentAmount === 'string')) {
687
- throw new Error('[did-provider-cheqd]: feePaymentAddress and feePaymentAmount must be string');
666
+ if (!args?.paymentConditions?.every((condition) => typeof condition.feePaymentAddress === 'string' && typeof condition.feePaymentAmount === 'string' && typeof condition.intervalInSeconds === 'number')) {
667
+ throw new Error('[did-provider-cheqd]: feePaymentAddress and feePaymentAmount must be string and intervalInSeconds must be number');
688
668
  }
689
669
  if (!args?.paymentConditions?.every((condition) => condition.type === AccessControlConditionTypes.timelockPayment)) {
690
670
  throw new Error('[did-provider-cheqd]: paymentConditions must be of type timelockPayment');
@@ -694,42 +674,111 @@ export class Cheqd {
694
674
  const network = args.issuerDid.split(':')[2];
695
675
  // generate bitstring
696
676
  const bitstring = await context.agent[GenerateStatusList2021MethodName]({ length: args?.statusListLength || Cheqd.defaultStatusList2021Length, bitstringEncoding: args?.statusListEncoding || DefaultStatusList2021Encodings.base64url });
697
- // construct data
677
+ // construct data and metadata tuple
698
678
  const data = args.encrypted
699
- ? (await (async function () {
700
- // TODO: implement
701
- throw new Error('[did-provider-cheqd]: encrypted status list is not implemented yet');
702
- }()))
703
- : (await (async function () {
679
+ ? (await (async function (that) {
680
+ // instantiate dkg-threshold client, in which case lit-protocol is used
681
+ const lit = await LitProtocol.create({
682
+ chain: args?.dkgOptions?.chain || that.didProvider.dkgOptions.chain,
683
+ litNetwork: args?.dkgOptions?.network || that.didProvider.dkgOptions.network,
684
+ });
685
+ // construct access control conditions
686
+ const unifiedAccessControlConditions = await Promise.all(args.paymentConditions.map(async (condition) => {
687
+ switch (condition.type) {
688
+ case AccessControlConditionTypes.timelockPayment:
689
+ return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({
690
+ key: '$.tx_responses.*.timestamp',
691
+ comparator: '<=',
692
+ value: `${condition.intervalInSeconds}`,
693
+ }, condition.feePaymentAmount, condition.feePaymentAddress, condition?.blockHeight, args?.dkgOptions?.chain || that.didProvider.dkgOptions.chain);
694
+ default:
695
+ throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`);
696
+ }
697
+ }));
698
+ // encrypt bitstring
699
+ const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt(bitstring, unifiedAccessControlConditions, true);
700
+ // return result tuple
704
701
  switch (args.statusPurpose) {
705
702
  case DefaultStatusList2021StatusPurposeTypes.revocation:
706
- return {
707
- StatusList2021: {
708
- statusPurpose: args.statusPurpose,
709
- encodedList: bitstring,
710
- validFrom: new Date().toISOString(),
711
- validUntil: args?.validUntil
703
+ return [{
704
+ StatusList2021: {
705
+ statusPurpose: args.statusPurpose,
706
+ encodedList: await blobToHexString(encryptedString),
707
+ validFrom: new Date().toISOString(),
708
+ validUntil: args?.validUntil
709
+ },
710
+ metadata: {
711
+ type: DefaultStatusList2021ResourceTypes.revocation,
712
+ encrypted: true,
713
+ encoding: args?.statusListEncoding || DefaultStatusList2021Encodings.base64url,
714
+ encryptedSymmetricKey,
715
+ paymentConditions: args.paymentConditions
716
+ }
712
717
  },
713
- metadata: {
714
- type: DefaultStatusList2021ResourceTypes.revocation,
715
- encrypted: false,
716
- encoding: args?.statusListEncoding || DefaultStatusList2021Encodings.base64url,
718
+ {
719
+ symmetricKey: toString(symmetricKey, 'hex'),
720
+ encryptedSymmetricKey,
721
+ encryptedString: await blobToHexString(encryptedString),
717
722
  }
718
- };
723
+ ];
719
724
  case DefaultStatusList2021StatusPurposeTypes.suspension:
720
- return {
721
- StatusList2021: {
722
- statusPurpose: args.statusPurpose,
723
- encodedList: bitstring,
724
- validFrom: new Date().toISOString(),
725
- validUntil: args?.validUntil
725
+ return [{
726
+ StatusList2021: {
727
+ statusPurpose: args.statusPurpose,
728
+ encodedList: await blobToHexString(encryptedString),
729
+ validFrom: new Date().toISOString(),
730
+ validUntil: args?.validUntil
731
+ },
732
+ metadata: {
733
+ type: DefaultStatusList2021ResourceTypes.suspension,
734
+ encrypted: true,
735
+ encoding: args?.statusListEncoding || DefaultStatusList2021Encodings.base64url,
736
+ encryptedSymmetricKey,
737
+ paymentConditions: args.paymentConditions
738
+ }
726
739
  },
727
- metadata: {
728
- type: DefaultStatusList2021ResourceTypes.suspension,
729
- encrypted: false,
730
- encoding: args?.statusListEncoding || DefaultStatusList2021Encodings.base64url,
740
+ {
741
+ symmetricKey: toString(symmetricKey, 'hex'),
742
+ encryptedSymmetricKey,
743
+ encryptedString: await blobToHexString(encryptedString),
731
744
  }
732
- };
745
+ ];
746
+ default:
747
+ throw new Error(`[did-provider-cheqd]: status purpose is not valid ${args.statusPurpose}`);
748
+ }
749
+ }(this)))
750
+ : (await (async function () {
751
+ switch (args.statusPurpose) {
752
+ case DefaultStatusList2021StatusPurposeTypes.revocation:
753
+ return [{
754
+ StatusList2021: {
755
+ statusPurpose: args.statusPurpose,
756
+ encodedList: bitstring,
757
+ validFrom: new Date().toISOString(),
758
+ validUntil: args?.validUntil
759
+ },
760
+ metadata: {
761
+ type: DefaultStatusList2021ResourceTypes.revocation,
762
+ encrypted: false,
763
+ encoding: args?.statusListEncoding || DefaultStatusList2021Encodings.base64url,
764
+ }
765
+ },
766
+ undefined];
767
+ case DefaultStatusList2021StatusPurposeTypes.suspension:
768
+ return [{
769
+ StatusList2021: {
770
+ statusPurpose: args.statusPurpose,
771
+ encodedList: bitstring,
772
+ validFrom: new Date().toISOString(),
773
+ validUntil: args?.validUntil
774
+ },
775
+ metadata: {
776
+ type: DefaultStatusList2021ResourceTypes.suspension,
777
+ encrypted: false,
778
+ encoding: args?.statusListEncoding || DefaultStatusList2021Encodings.base64url,
779
+ }
780
+ },
781
+ undefined];
733
782
  default:
734
783
  throw new Error('[did-provider-cheqd]: statusPurpose is not valid');
735
784
  }
@@ -742,13 +791,15 @@ export class Cheqd {
742
791
  resourceType: DefaultStatusList2021ResourceTypes[args.statusPurpose],
743
792
  version: args?.resourceVersion || new Date().toISOString(),
744
793
  alsoKnownAs: args?.alsoKnownAs || [],
745
- data: fromString(JSON.stringify(data), 'utf-8'),
794
+ data: fromString(JSON.stringify(data[0]), 'utf-8'),
746
795
  };
747
796
  // return result
748
797
  return {
749
798
  created: await context.agent[BroadcastStatusList2021MethodName]({ kms: args.kms, payload, network: network }),
750
- statusList2021: data,
751
- resourceMetadata: await Cheqd.fetchStatusList2021Metadata({ credentialStatus: { id: `${resolverUrl}${args.issuerDid}?resourceName=${args.statusListName}&resourceType=${DefaultStatusList2021ResourceTypes[args.statusPurpose]}`, type: 'StatusList2021Entry' } })
799
+ resource: data[0],
800
+ resourceMetadata: await Cheqd.fetchStatusList2021Metadata({ credentialStatus: { id: `${resolverUrl}${args.issuerDid}?resourceName=${args.statusListName}&resourceType=${DefaultStatusList2021ResourceTypes[args.statusPurpose]}`, type: 'StatusList2021Entry' } }),
801
+ encrypted: args.encrypted,
802
+ symmetricKey: args?.returnSymmetricKey ? data[1]?.symmetricKey : undefined,
752
803
  };
753
804
  }
754
805
  async BroadcastStatusList2021(args, context) {
@@ -875,54 +926,6 @@ export class Cheqd {
875
926
  return encoded;
876
927
  }
877
928
  }
878
- async GenerateEncryptedStatusList2021(args, context) {
879
- // validate encryptionOptions
880
- if (!args.encryptionOptions) {
881
- throw new Error('[did-provider-cheqd]: encryptionOptions is required');
882
- }
883
- // validate encryptionOptions.accessControlConditions
884
- if (!args.encryptionOptions.accessControlConditions) {
885
- throw new Error('[did-provider-cheqd]: encryptionOptions.accessControlConditions is required');
886
- }
887
- // generate status list
888
- const statusList = args?.buffer
889
- ? new StatusList({ buffer: args.buffer })
890
- : new StatusList({ length: args?.length || Cheqd.defaultStatusList2021Length });
891
- // encode status list
892
- const encoded = await statusList.encode();
893
- // instantiate dkg-threshold client, in which case lit-protocol is used
894
- const lit = await LitProtocol.create({
895
- chain: args.bootstrapOptions.chain,
896
- litNetwork: args.bootstrapOptions.litNetwork,
897
- });
898
- // construct access control conditions
899
- const unifiedAccessControlConditions = await Promise.all(args.encryptionOptions.accessControlConditions.map(async (condition) => {
900
- switch (condition.type) {
901
- case AccessControlConditionTypes.memoNonce:
902
- return await LitProtocol.generateCosmosAccessControlConditionTransactionMemo({
903
- key: '$.txs.*.body.memo',
904
- comparator: 'contains',
905
- value: condition?.specificNonce || await LitProtocol.generateTxNonce(condition?.nonceFormat)
906
- }, condition.amountObserved, condition.senderAddressObserved, condition.recipientAddressObserved, args.bootstrapOptions.chain);
907
- case AccessControlConditionTypes.balance:
908
- return await LitProtocol.generateCosmosAccessControlConditionBalance({
909
- key: '$.balances[0].amount',
910
- comparator: condition.comparator,
911
- value: condition.amountObserved
912
- }, args.bootstrapOptions.chain, condition.addressObserved);
913
- default:
914
- throw new Error(`[did-provider-cheqd]: accessControlCondition type is not supported`);
915
- }
916
- }));
917
- // encrypt data
918
- const { encryptedString, encryptedSymmetricKey } = await lit.encrypt(encoded, unifiedAccessControlConditions);
919
- // return result
920
- return {
921
- encryptedStatusList2021: await blobToHexString(encryptedString),
922
- encryptedSymmetricKey,
923
- unifiedAccessControlConditions
924
- };
925
- }
926
929
  async IssueRevocableCredentialWithStatusList2021(args, context) {
927
930
  // generate index
928
931
  const statusListIndex = args.statusOptions.statusListIndex || await randomFromRange(args.statusOptions.statusListRangeStart || 0, (args.statusOptions.statusListRangeEnd || Cheqd.defaultStatusList2021Length) - 1, args.statusOptions.indexNotIn || []);
@@ -1021,6 +1024,8 @@ export class Cheqd {
1021
1024
  }
1022
1025
  // if jwt credential, decode it
1023
1026
  const credential = typeof args.credential === 'string' ? await Cheqd.decodeCredentialJWT(args.credential) : args.credential;
1027
+ // define dkg options, if provided
1028
+ args.dkgOptions ||= this.didProvider.dkgOptions;
1024
1029
  // verify credential status
1025
1030
  switch (credential.credentialStatus?.statusPurpose) {
1026
1031
  case 'revocation':
@@ -1049,8 +1054,11 @@ export class Cheqd {
1049
1054
  if (!verificationResult.verified) {
1050
1055
  return { verified: false, error: verificationResult.error };
1051
1056
  }
1057
+ // early return if no verifiable credentials are provided
1052
1058
  if (!args.presentation.verifiableCredential)
1053
1059
  throw new Error('[did-provider-cheqd]: verify presentation: presentation.verifiableCredential is required');
1060
+ // define dkg options, if provided
1061
+ args.dkgOptions ||= this.didProvider.dkgOptions;
1054
1062
  // verify credential(s) status(es)
1055
1063
  for (let credential of args.presentation.verifiableCredential) {
1056
1064
  // if jwt credential, decode it
@@ -1123,6 +1131,8 @@ export class Cheqd {
1123
1131
  throw new Error('[did-provider-cheqd]: revocation: credential is required');
1124
1132
  // if jwt credential, decode it
1125
1133
  const credential = typeof args.credential === 'string' ? await Cheqd.decodeCredentialJWT(args.credential) : args.credential;
1134
+ // define dkg options, if provided
1135
+ args.dkgOptions ||= this.didProvider.dkgOptions;
1126
1136
  switch (credential.credentialStatus?.statusPurpose) {
1127
1137
  case 'revocation':
1128
1138
  if (await Cheqd.checkRevoked(credential, { ...args.options, topArgs: args }))
@@ -1203,6 +1213,8 @@ export class Cheqd {
1203
1213
  if (args.options?.publish && !args.fetchList && !(args.options?.statusListFile || args.options?.statusList)) {
1204
1214
  throw new Error('[did-provider-cheqd]: revocation: publish requires statusListFile or statusList, if fetchList is disabled');
1205
1215
  }
1216
+ // define dkg options, if provided
1217
+ args.dkgOptions ||= this.didProvider.dkgOptions;
1206
1218
  // revoke credential
1207
1219
  return await Cheqd.revokeCredential(credential, {
1208
1220
  ...args.options,
@@ -1285,6 +1297,8 @@ export class Cheqd {
1285
1297
  if (args.options?.publish && !args.fetchList && !(args.options?.statusListFile || args.options?.statusList)) {
1286
1298
  throw new Error('[did-provider-cheqd]: revocation: publish requires statusListFile or statusList, if fetchList is disabled');
1287
1299
  }
1300
+ // define dkg options, if provided
1301
+ args.dkgOptions ||= this.didProvider.dkgOptions;
1288
1302
  // revoke credentials
1289
1303
  return await Cheqd.revokeCredentials(credentials, {
1290
1304
  ...args.options,
@@ -1625,13 +1639,13 @@ export class Cheqd {
1625
1639
  }
1626
1640
  });
1627
1641
  }
1628
- async TransactVerifierPaysIssuer(args, context) {
1642
+ async TransactSendTokens(args, context) {
1629
1643
  try {
1630
1644
  // delegate to provider
1631
1645
  const transactionResult = await this.didProvider.transactSendTokens({
1632
1646
  recipientAddress: args.recipientAddress,
1633
1647
  amount: args.amount,
1634
- memoNonce: args.memoNonce,
1648
+ memo: args.memo,
1635
1649
  txBytes: args.txBytes,
1636
1650
  });
1637
1651
  // return transaction result
@@ -1651,9 +1665,27 @@ export class Cheqd {
1651
1665
  };
1652
1666
  }
1653
1667
  }
1654
- async ObserveVerifierPaysIssuer(args, context) {
1655
- // verify with raw unified access control conditions, if any
1668
+ async ObservePaymentCondition(args, context) {
1669
+ // verify with raw unified access control condition, if any
1656
1670
  if (args?.unifiedAccessControlCondition) {
1671
+ // validate args - case: unifiedAccessControlCondition.chain
1672
+ if (!args.unifiedAccessControlCondition.chain || !Object.values(LitCompatibleCosmosChains).includes(args.unifiedAccessControlCondition.chain))
1673
+ throw new Error('[did-provider-cheqd]: observe: unifiedAccessControlCondition.chain is required and must be a valid Lit-compatible chain');
1674
+ // validate args - case: unifiedAccessControlCondition.path
1675
+ if (!args.unifiedAccessControlCondition.path)
1676
+ throw new Error('[did-provider-cheqd]: observe: unifiedAccessControlCondition.path is required');
1677
+ // validate args - case: unifiedAccessControlCondition.conditionType
1678
+ if (args.unifiedAccessControlCondition.conditionType !== 'cosmos')
1679
+ throw new Error('[did-provider-cheqd]: observe: unifiedAccessControlCondition.conditionType must be cosmos');
1680
+ // validate args - case: unifiedAccessControlCondition.method
1681
+ if (args.unifiedAccessControlCondition.method !== 'timelock')
1682
+ throw new Error('[did-provider-cheqd]: observe: unifiedAccessControlCondition.method must be timelock');
1683
+ // validate args - case: unifiedAccessControlCondition.parameters
1684
+ if (!args.unifiedAccessControlCondition.parameters || !Array.isArray(args.unifiedAccessControlCondition.parameters) || args.unifiedAccessControlCondition.parameters.length === 0 || args.unifiedAccessControlCondition.parameters.length > 1)
1685
+ throw new Error('[did-provider-cheqd]: observe: unifiedAccessControlCondition.parameters is required and must be an array of length 1 of type string content');
1686
+ // validate args - case: unifiedAccessControlCondition.returnValueTest
1687
+ if (!args.unifiedAccessControlCondition.returnValueTest || !args.unifiedAccessControlCondition.returnValueTest.comparator || !args.unifiedAccessControlCondition.returnValueTest.key || !args.unifiedAccessControlCondition.returnValueTest.value)
1688
+ throw new Error('[did-provider-cheqd]: observe: unifiedAccessControlCondition.returnValueTest is required');
1657
1689
  try {
1658
1690
  // define network
1659
1691
  const network = (function () {
@@ -1666,12 +1698,39 @@ export class Cheqd {
1666
1698
  throw new Error(`[did-provider-cheqd]: observe: Unsupported chain: ${args.unifiedAccessControlCondition.chain}`);
1667
1699
  }
1668
1700
  }());
1701
+ // get block height url
1702
+ const blockHeightUrl = function () {
1703
+ switch (args.unifiedAccessControlCondition.parameters[0]) {
1704
+ case 'latest':
1705
+ return `${DefaultRESTUrls[network]}/cosmos/base/tendermint/v1beta1/blocks/latest`;
1706
+ default:
1707
+ return `${DefaultRESTUrls[network]}/cosmos/base/tendermint/v1beta1/blocks/${args.unifiedAccessControlCondition.parameters[0]}`;
1708
+ }
1709
+ }();
1710
+ // fetch block response
1711
+ const blockHeightResponse = await (await fetch(blockHeightUrl)).json();
1712
+ // get timestamp from block response
1713
+ const blockTimestamp = Date.parse(blockHeightResponse.block.header.time);
1669
1714
  // construct url
1670
1715
  const url = `${DefaultRESTUrls[network]}${args.unifiedAccessControlCondition.path}`;
1671
1716
  // fetch relevant txs
1672
1717
  const txs = await (await fetch(url)).json();
1673
- // skim through txs for relevant events, in which case memoNonce is present and strict equals to the one provided
1674
- const meetsConditionTxIndex = txs?.txs?.findIndex(tx => unescapeUnicode(tx.body.memo) === unescapeUnicode(args.unifiedAccessControlCondition.returnValueTest.value));
1718
+ // skim through txs for relevant events, in which case the transaction timestamp is within the defined interval in seconds, from the block timestamp
1719
+ const meetsConditionTxIndex = txs?.tx_responses?.findIndex((tx) => {
1720
+ // get tx timestamp
1721
+ const txTimestamp = Date.parse(tx.timestamp);
1722
+ // calculate diff in seconds
1723
+ const diffInSeconds = Math.floor((blockTimestamp - txTimestamp) / 1000);
1724
+ // return meets condition
1725
+ switch (args.unifiedAccessControlCondition.returnValueTest.comparator) {
1726
+ case '<':
1727
+ return diffInSeconds < parseInt(args.unifiedAccessControlCondition.returnValueTest.value);
1728
+ case '<=':
1729
+ return diffInSeconds <= parseInt(args.unifiedAccessControlCondition.returnValueTest.value);
1730
+ default:
1731
+ throw new Error(`[did-provider-cheqd]: observe: Unsupported comparator: ${args.unifiedAccessControlCondition.returnValueTest.comparator}`);
1732
+ }
1733
+ });
1675
1734
  // define meetsCondition
1676
1735
  const meetsCondition = (typeof meetsConditionTxIndex !== 'undefined' && meetsConditionTxIndex !== -1);
1677
1736
  // return observation result
@@ -1693,10 +1752,6 @@ export class Cheqd {
1693
1752
  };
1694
1753
  }
1695
1754
  }
1696
- // validate access control conditions components - case: senderAddress
1697
- if (!args.senderAddress) {
1698
- throw new Error('[did-provider-cheqd]: observation: senderAddress is required');
1699
- }
1700
1755
  // validate access control conditions components - case: recipientAddress
1701
1756
  if (!args.recipientAddress) {
1702
1757
  throw new Error('[did-provider-cheqd]: observation: recipientAddress is required');
@@ -1705,21 +1760,54 @@ export class Cheqd {
1705
1760
  if (!args.amount || !args.amount.amount || !args.amount.denom || args.amount.denom !== 'ncheq') {
1706
1761
  throw new Error('[did-provider-cheqd]: observation: amount is required, and must be an object with amount and denom valid string properties, amongst which denom must be `ncheq`');
1707
1762
  }
1708
- // validate access control conditions components - case: memoNonce
1709
- if (!args.memoNonce) {
1710
- throw new Error('[did-provider-cheqd]: observation: memoNonce is required');
1763
+ // validate access control conditions components - case: intervalInSeconds
1764
+ if (!args.intervalInSeconds) {
1765
+ throw new Error('[did-provider-cheqd]: observation: intervalInSeconds is required');
1766
+ }
1767
+ // validate access control conditions components - case: comparator
1768
+ if (!args.comparator || (args.comparator !== '<' && args.comparator !== '<=')) {
1769
+ throw new Error('[did-provider-cheqd]: observation: comparator is required and must be either `<` or `<=`');
1711
1770
  }
1712
1771
  // validate access control conditions components - case: network
1713
1772
  if (!args.network) {
1714
1773
  throw new Error('[did-provider-cheqd]: observation: network is required');
1715
1774
  }
1775
+ // define block height, if not provided
1776
+ args.blockHeight ||= 'latest';
1716
1777
  try {
1778
+ // get block height url
1779
+ const blockHeightUrl = function () {
1780
+ switch (args.blockHeight) {
1781
+ case 'latest':
1782
+ return `${DefaultRESTUrls[args.network]}/cosmos/base/tendermint/v1beta1/blocks/latest`;
1783
+ default:
1784
+ return `${DefaultRESTUrls[args.network]}/cosmos/base/tendermint/v1beta1/blocks/${args.blockHeight}`;
1785
+ }
1786
+ }();
1787
+ // fetch block response
1788
+ const blockHeightResponse = await (await fetch(blockHeightUrl)).json();
1789
+ // get timestamp from block response
1790
+ const blockTimestamp = Date.parse(blockHeightResponse.block.header.time);
1717
1791
  // otherwise, construct url, as per components
1718
- const url = `${DefaultRESTUrls[args.network]}/cosmos/tx/v1beta1/txs?events=transfer.recipient='${args.recipientAddress}'&events=transfer.sender='${args.senderAddress}'&events=transfer.amount='${args.amount.amount}${args.amount.denom}'`;
1792
+ const url = `${DefaultRESTUrls[args.network]}/cosmos/tx/v1beta1/txs?events=transfer.recipient='${args.recipientAddress}'&events=transfer.amount='${args.amount.amount}${args.amount.denom}'&order_by=2&pagination.limit=1`;
1719
1793
  // fetch relevant txs
1720
1794
  const txs = await (await fetch(url)).json();
1721
- // skim through txs for relevant events, in which case memoNonce is present and strict equals to the one provided
1722
- const meetsConditionTxIndex = txs?.txs?.findIndex(tx => unescapeUnicode(tx.body.memo) === unescapeUnicode(args.memoNonce));
1795
+ // skim through txs for relevant events, in which case the transaction timestamp is within the defined interval in seconds, from the block timestamp
1796
+ const meetsConditionTxIndex = txs?.tx_responses?.findIndex((tx) => {
1797
+ // get tx timestamp
1798
+ const txTimestamp = Date.parse(tx.timestamp);
1799
+ // calculate diff in seconds
1800
+ const diffInSeconds = Math.floor((blockTimestamp - txTimestamp) / 1000);
1801
+ // return meets condition
1802
+ switch (args.comparator) {
1803
+ case '<':
1804
+ return diffInSeconds < args.intervalInSeconds;
1805
+ case '<=':
1806
+ return diffInSeconds <= args.intervalInSeconds;
1807
+ default:
1808
+ throw new Error(`[did-provider-cheqd]: observe: Unsupported comparator: ${args.unifiedAccessControlCondition.returnValueTest.comparator}`);
1809
+ }
1810
+ });
1723
1811
  // define meetsCondition
1724
1812
  const meetsCondition = (typeof meetsConditionTxIndex !== 'undefined' && meetsConditionTxIndex !== -1);
1725
1813
  // return observation result
@@ -1759,8 +1847,8 @@ export class Cheqd {
1759
1847
  return publishedList.metadata.encoding === 'base64url'
1760
1848
  ? publishedList.StatusList2021.encodedList
1761
1849
  : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding), 'base64url');
1762
- // otherwise, decrypt and return bitstring
1763
- const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding));
1850
+ // otherwise, decrypt and return raw bitstring
1851
+ const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex'));
1764
1852
  // decrypt
1765
1853
  return await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex'));
1766
1854
  }())
@@ -1822,16 +1910,111 @@ export class Cheqd {
1822
1910
  // publish status list 2021 as new version
1823
1911
  const scoped = topArgs.publishEncrypted
1824
1912
  ? (await async function () {
1913
+ // validate encoding, if provided
1914
+ if (options?.publishOptions?.statusListEncoding && !Object.values(DefaultStatusList2021Encodings).includes(options?.publishOptions?.statusListEncoding)) {
1915
+ throw new Error('[did-provider-cheqd]: revocation: Invalid status list encoding');
1916
+ }
1917
+ // validate validUntil, if provided
1918
+ if (options?.publishOptions?.statusListValidUntil) {
1919
+ // validate validUntil as string
1920
+ if (typeof options?.publishOptions?.statusListValidUntil !== 'string')
1921
+ throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be string)');
1922
+ // validate validUntil as date
1923
+ if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil)))
1924
+ throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be date)');
1925
+ // validate validUntil as future date
1926
+ if (new Date(options?.publishOptions?.statusListValidUntil) < new Date())
1927
+ throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be future date)');
1928
+ // validate validUntil towards validFrom
1929
+ if (new Date(options?.publishOptions?.statusListValidUntil) <= new Date(publishedList.StatusList2021.validFrom))
1930
+ throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be after validFrom)');
1931
+ }
1932
+ // validate paymentConditions, if provided
1933
+ if (topArgs?.paymentConditions) {
1934
+ if (!topArgs?.paymentConditions?.every((condition) => condition.feePaymentAddress && condition.feePaymentAmount && condition.intervalInSeconds)) {
1935
+ throw new Error('[did-provider-cheqd]: paymentConditions must contain feePaymentAddress and feeAmount and intervalInSeconds');
1936
+ }
1937
+ if (!topArgs?.paymentConditions?.every((condition) => typeof condition.feePaymentAddress === 'string' && typeof condition.feePaymentAmount === 'string' && typeof condition.intervalInSeconds === 'number')) {
1938
+ throw new Error('[did-provider-cheqd]: feePaymentAddress and feePaymentAmount must be string and intervalInSeconds must be number');
1939
+ }
1940
+ if (!topArgs?.paymentConditions?.every((condition) => condition.type === AccessControlConditionTypes.timelockPayment)) {
1941
+ throw new Error('[did-provider-cheqd]: paymentConditions must be of type timelockPayment');
1942
+ }
1943
+ }
1944
+ // validate dkgOptions
1945
+ if (!topArgs?.dkgOptions || !topArgs?.dkgOptions?.chain || !topArgs?.dkgOptions?.network) {
1946
+ throw new Error('[did-provider-cheqd]: dkgOptions is required');
1947
+ }
1825
1948
  // instantiate dkg-threshold client, in which case lit-protocol is used
1826
1949
  const lit = await LitProtocol.create({
1827
- chain: options?.topArgs?.bootstrapOptions?.chain,
1828
- litNetwork: options?.topArgs?.bootstrapOptions?.litNetwork
1950
+ chain: topArgs?.dkgOptions?.chain,
1951
+ litNetwork: topArgs?.dkgOptions?.network
1829
1952
  });
1830
- // encrypt
1831
- const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt(bitstring, options?.topArgs?.encryptionOptions?.unifiedAccessControlConditions, true);
1953
+ // construct access control conditions and payment conditions tuple
1954
+ const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted
1955
+ ? (await (async function () {
1956
+ // define payment conditions, give precedence to top-level args
1957
+ const paymentConditions = topArgs?.paymentConditions || publishedList.metadata.paymentConditions;
1958
+ // return access control conditions and payment conditions tuple
1959
+ return [
1960
+ await Promise.all(paymentConditions.map(async (condition) => {
1961
+ switch (condition.type) {
1962
+ case AccessControlConditionTypes.timelockPayment:
1963
+ return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({
1964
+ key: '$.tx_responses.*.timestamp',
1965
+ comparator: '<=',
1966
+ value: `${condition.intervalInSeconds}`,
1967
+ }, condition.feePaymentAmount, condition.feePaymentAddress, condition?.blockHeight, topArgs?.dkgOptions?.chain);
1968
+ default:
1969
+ throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`);
1970
+ }
1971
+ })),
1972
+ paymentConditions
1973
+ ];
1974
+ }()))
1975
+ : (await (async function () {
1976
+ // validate paymentConditions
1977
+ if (!topArgs?.paymentConditions) {
1978
+ throw new Error('[did-provider-cheqd]: paymentConditions is required');
1979
+ }
1980
+ // return access control conditions and payment conditions tuple
1981
+ return [
1982
+ await Promise.all(topArgs.paymentConditions.map(async (condition) => {
1983
+ switch (condition.type) {
1984
+ case AccessControlConditionTypes.timelockPayment:
1985
+ return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({
1986
+ key: '$.tx_responses.*.timestamp',
1987
+ comparator: '<=',
1988
+ value: `${condition.intervalInSeconds}`,
1989
+ }, condition.feePaymentAmount, condition.feePaymentAddress, condition?.blockHeight);
1990
+ default:
1991
+ throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`);
1992
+ }
1993
+ })),
1994
+ topArgs.paymentConditions
1995
+ ];
1996
+ }()));
1997
+ // encrypt bitstring
1998
+ const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt(bitstring, unifiedAccessControlConditionsTuple[0], true);
1999
+ // define status list content
2000
+ const content = {
2001
+ StatusList2021: {
2002
+ statusPurpose: publishedList.StatusList2021.statusPurpose,
2003
+ encodedList: await blobToHexString(encryptedString),
2004
+ validFrom: publishedList.StatusList2021.validFrom,
2005
+ validUntil: options?.publishOptions?.statusListValidUntil || publishedList.StatusList2021.validUntil
2006
+ },
2007
+ metadata: {
2008
+ type: publishedList.metadata.type,
2009
+ encrypted: true,
2010
+ encoding: options?.publishOptions?.statusListEncoding || publishedList.metadata.encoding,
2011
+ encryptedSymmetricKey,
2012
+ paymentConditions: unifiedAccessControlConditionsTuple[1]
2013
+ }
2014
+ };
1832
2015
  // return tuple of publish result and encryption relevant metadata
1833
2016
  return [
1834
- await Cheqd.publishStatusList2021(new Uint8Array(await encryptedString.arrayBuffer()), statusListMetadata, options?.publishOptions),
2017
+ await Cheqd.publishStatusList2021(fromString(JSON.stringify(content), 'utf-8'), statusListMetadata, options?.publishOptions),
1835
2018
  { encryptedString, encryptedSymmetricKey, symmetricKey: toString(symmetricKey, 'hex') }
1836
2019
  ];
1837
2020
  }())
@@ -1886,8 +2069,6 @@ export class Cheqd {
1886
2069
  revoked: true,
1887
2070
  published: topArgs?.publish ? true : undefined,
1888
2071
  statusList: topArgs?.returnUpdatedStatusList ? await Cheqd.fetchStatusList2021(credential) : undefined,
1889
- encryptedStatusList: topArgs?.returnUpdatedEncryptedStatusList ? await blobToHexString(published?.[1]?.encryptedString) : undefined,
1890
- encryptedSymmetricKey: topArgs?.returnEncryptedSymmetricKey ? published?.[1]?.encryptedSymmetricKey : undefined,
1891
2072
  symmetricKey: topArgs?.returnSymmetricKey ? published?.[1]?.symmetricKey : undefined,
1892
2073
  resourceMetadata: topArgs?.returnStatusListMetadata ? await Cheqd.fetchStatusList2021Metadata(credential) : undefined
1893
2074
  };
@@ -1945,8 +2126,8 @@ export class Cheqd {
1945
2126
  return publishedList.metadata.encoding === 'base64url'
1946
2127
  ? publishedList.StatusList2021.encodedList
1947
2128
  : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding), 'base64url');
1948
- // otherwise, decrypt and return bitstring
1949
- const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding));
2129
+ // otherwise, decrypt and return raw bitstring
2130
+ const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex'));
1950
2131
  // decrypt
1951
2132
  return await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex'));
1952
2133
  }())
@@ -2021,16 +2202,111 @@ export class Cheqd {
2021
2202
  // publish status list 2021 as new version
2022
2203
  const scoped = topArgs.publishEncrypted
2023
2204
  ? (await async function () {
2205
+ // validate encoding, if provided
2206
+ if (options?.publishOptions?.statusListEncoding && !Object.values(DefaultStatusList2021Encodings).includes(options?.publishOptions?.statusListEncoding)) {
2207
+ throw new Error('[did-provider-cheqd]: revocation: Invalid status list encoding');
2208
+ }
2209
+ // validate validUntil, if provided
2210
+ if (options?.publishOptions?.statusListValidUntil) {
2211
+ // validate validUntil as string
2212
+ if (typeof options?.publishOptions?.statusListValidUntil !== 'string')
2213
+ throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be string)');
2214
+ // validate validUntil as date
2215
+ if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil)))
2216
+ throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be date)');
2217
+ // validate validUntil as future date
2218
+ if (new Date(options?.publishOptions?.statusListValidUntil) < new Date())
2219
+ throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be future date)');
2220
+ // validate validUntil towards validFrom
2221
+ if (new Date(options?.publishOptions?.statusListValidUntil) <= new Date(publishedList.StatusList2021.validFrom))
2222
+ throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be after validFrom)');
2223
+ }
2224
+ // validate paymentConditions, if provided
2225
+ if (topArgs?.paymentConditions) {
2226
+ if (!topArgs?.paymentConditions?.every((condition) => condition.feePaymentAddress && condition.feePaymentAmount && condition.intervalInSeconds)) {
2227
+ throw new Error('[did-provider-cheqd]: paymentConditions must contain feePaymentAddress and feeAmount and intervalInSeconds');
2228
+ }
2229
+ if (!topArgs?.paymentConditions?.every((condition) => typeof condition.feePaymentAddress === 'string' && typeof condition.feePaymentAmount === 'string' && typeof condition.intervalInSeconds === 'number')) {
2230
+ throw new Error('[did-provider-cheqd]: feePaymentAddress and feePaymentAmount must be string and intervalInSeconds must be number');
2231
+ }
2232
+ if (!topArgs?.paymentConditions?.every((condition) => condition.type === AccessControlConditionTypes.timelockPayment)) {
2233
+ throw new Error('[did-provider-cheqd]: paymentConditions must be of type timelockPayment');
2234
+ }
2235
+ }
2236
+ // validate dkgOptions
2237
+ if (!topArgs?.dkgOptions || !topArgs?.dkgOptions?.chain || !topArgs?.dkgOptions?.network) {
2238
+ throw new Error('[did-provider-cheqd]: dkgOptions is required');
2239
+ }
2024
2240
  // instantiate dkg-threshold client, in which case lit-protocol is used
2025
2241
  const lit = await LitProtocol.create({
2026
- chain: options?.topArgs?.bootstrapOptions?.chain,
2027
- litNetwork: options?.topArgs?.bootstrapOptions?.litNetwork
2242
+ chain: topArgs?.dkgOptions?.chain,
2243
+ litNetwork: topArgs?.dkgOptions?.network
2028
2244
  });
2029
- // encrypt
2030
- const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt(bitstring, options?.topArgs?.encryptionOptions?.unifiedAccessControlConditions, true);
2245
+ // construct access control conditions and payment conditions tuple
2246
+ const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted
2247
+ ? (await (async function () {
2248
+ // define payment conditions, give precedence to top-level args
2249
+ const paymentConditions = topArgs?.paymentConditions || publishedList.metadata.paymentConditions;
2250
+ // return access control conditions and payment conditions tuple
2251
+ return [
2252
+ await Promise.all(paymentConditions.map(async (condition) => {
2253
+ switch (condition.type) {
2254
+ case AccessControlConditionTypes.timelockPayment:
2255
+ return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({
2256
+ key: '$.tx_responses.*.timestamp',
2257
+ comparator: '<=',
2258
+ value: `${condition.intervalInSeconds}`,
2259
+ }, condition.feePaymentAmount, condition.feePaymentAddress, condition?.blockHeight, topArgs?.dkgOptions?.chain);
2260
+ default:
2261
+ throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`);
2262
+ }
2263
+ })),
2264
+ paymentConditions
2265
+ ];
2266
+ }()))
2267
+ : (await (async function () {
2268
+ // validate paymentConditions
2269
+ if (!topArgs?.paymentConditions) {
2270
+ throw new Error('[did-provider-cheqd]: paymentConditions is required');
2271
+ }
2272
+ // return access control conditions and payment conditions tuple
2273
+ return [
2274
+ await Promise.all(topArgs.paymentConditions.map(async (condition) => {
2275
+ switch (condition.type) {
2276
+ case AccessControlConditionTypes.timelockPayment:
2277
+ return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({
2278
+ key: '$.tx_responses.*.timestamp',
2279
+ comparator: '<=',
2280
+ value: `${condition.intervalInSeconds}`,
2281
+ }, condition.feePaymentAmount, condition.feePaymentAddress, condition?.blockHeight);
2282
+ default:
2283
+ throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`);
2284
+ }
2285
+ })),
2286
+ topArgs.paymentConditions
2287
+ ];
2288
+ }()));
2289
+ // encrypt bitstring
2290
+ const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt(bitstring, unifiedAccessControlConditionsTuple[0], true);
2291
+ // define status list content
2292
+ const content = {
2293
+ StatusList2021: {
2294
+ statusPurpose: publishedList.StatusList2021.statusPurpose,
2295
+ encodedList: await blobToHexString(encryptedString),
2296
+ validFrom: publishedList.StatusList2021.validFrom,
2297
+ validUntil: options?.publishOptions?.statusListValidUntil || publishedList.StatusList2021.validUntil
2298
+ },
2299
+ metadata: {
2300
+ type: publishedList.metadata.type,
2301
+ encrypted: true,
2302
+ encoding: options?.publishOptions?.statusListEncoding || publishedList.metadata.encoding,
2303
+ encryptedSymmetricKey,
2304
+ paymentConditions: unifiedAccessControlConditionsTuple[1]
2305
+ }
2306
+ };
2031
2307
  // return tuple of publish result and encryption relevant metadata
2032
2308
  return [
2033
- await Cheqd.publishStatusList2021(new Uint8Array(await encryptedString.arrayBuffer()), statusListMetadata, options?.publishOptions),
2309
+ await Cheqd.publishStatusList2021(fromString(JSON.stringify(content), 'utf-8'), statusListMetadata, options?.publishOptions),
2034
2310
  { encryptedString, encryptedSymmetricKey, symmetricKey: toString(symmetricKey, 'hex') }
2035
2311
  ];
2036
2312
  }())
@@ -2085,8 +2361,6 @@ export class Cheqd {
2085
2361
  revoked: revoked.map((result) => result.status === 'fulfilled' ? result.value.revoked : false),
2086
2362
  published: topArgs?.publish ? true : undefined,
2087
2363
  statusList: topArgs?.returnUpdatedStatusList ? await Cheqd.fetchStatusList2021(credentials[0]) : undefined,
2088
- encryptedStatusList: topArgs?.returnUpdatedEncryptedStatusList ? await blobToHexString(published?.[1]?.encryptedString) : undefined,
2089
- encryptedSymmetricKey: topArgs?.returnEncryptedSymmetricKey ? published?.[1]?.encryptedSymmetricKey : undefined,
2090
2364
  symmetricKey: topArgs?.returnSymmetricKey ? published?.[1]?.symmetricKey : undefined,
2091
2365
  resourceMetadata: topArgs?.returnStatusListMetadata ? await Cheqd.fetchStatusList2021Metadata(credentials[0]) : undefined
2092
2366
  };
@@ -2115,8 +2389,8 @@ export class Cheqd {
2115
2389
  return publishedList.metadata.encoding === 'base64url'
2116
2390
  ? publishedList.StatusList2021.encodedList
2117
2391
  : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding), 'base64url');
2118
- // otherwise, decrypt and return bitstring
2119
- const scopedRawBlob = await toBlob(await Cheqd.fetchStatusList2021(credential, true));
2392
+ // otherwise, decrypt and return raw bitstring
2393
+ const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex'));
2120
2394
  // decrypt
2121
2395
  return await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex'));
2122
2396
  }())
@@ -2133,7 +2407,7 @@ export class Cheqd {
2133
2407
  const encoded = new StatusList({ buffer: await Cheqd.getFile(options.statusListFile) }).encode();
2134
2408
  // validate against published list
2135
2409
  if (encoded !== publishedListTranscoded)
2136
- throw new Error('[did-provider-cheqd]: revocation: statusListFile does not match published status list 2021');
2410
+ throw new Error('[did-provider-cheqd]: suspension: statusListFile does not match published status list 2021');
2137
2411
  // return encoded
2138
2412
  return encoded;
2139
2413
  }
@@ -2143,15 +2417,15 @@ export class Cheqd {
2143
2417
  const decrypted = await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex'));
2144
2418
  // validate against published list
2145
2419
  if (decrypted !== publishedListTranscoded)
2146
- throw new Error('[did-provider-cheqd]: revocation: statusListFile does not match published status list 2021');
2420
+ throw new Error('[did-provider-cheqd]: suspension: statusListFile does not match published status list 2021');
2147
2421
  // return decrypted
2148
2422
  return decrypted;
2149
2423
  }
2150
2424
  if (!options?.statusListInlineBitstring)
2151
- throw new Error('[did-provider-cheqd]: revocation: statusListInlineBitstring is required, if statusListFile is not provided');
2425
+ throw new Error('[did-provider-cheqd]: suspension: statusListInlineBitstring is required, if statusListFile is not provided');
2152
2426
  // validate against published list
2153
2427
  if (options?.statusListInlineBitstring !== publishedListTranscoded)
2154
- throw new Error('[did-provider-cheqd]: revocation: statusListInlineBitstring does not match published status list 2021');
2428
+ throw new Error('[did-provider-cheqd]: suspension: statusListInlineBitstring does not match published status list 2021');
2155
2429
  // otherwise, read from inline bitstring
2156
2430
  return options?.statusListInlineBitstring;
2157
2431
  }());
@@ -2178,38 +2452,133 @@ export class Cheqd {
2178
2452
  // publish status list 2021 as new version
2179
2453
  const scoped = topArgs.publishEncrypted
2180
2454
  ? (await async function () {
2455
+ // validate encoding, if provided
2456
+ if (options?.publishOptions?.statusListEncoding && !Object.values(DefaultStatusList2021Encodings).includes(options?.publishOptions?.statusListEncoding)) {
2457
+ throw new Error('[did-provider-cheqd]: suspension: Invalid status list encoding');
2458
+ }
2459
+ // validate validUntil, if provided
2460
+ if (options?.publishOptions?.statusListValidUntil) {
2461
+ // validate validUntil as string
2462
+ if (typeof options?.publishOptions?.statusListValidUntil !== 'string')
2463
+ throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be string)');
2464
+ // validate validUntil as date
2465
+ if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil)))
2466
+ throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be date)');
2467
+ // validate validUntil as future date
2468
+ if (new Date(options?.publishOptions?.statusListValidUntil) < new Date())
2469
+ throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be future date)');
2470
+ // validate validUntil towards validFrom
2471
+ if (new Date(options?.publishOptions?.statusListValidUntil) <= new Date(publishedList.StatusList2021.validFrom))
2472
+ throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be after validFrom)');
2473
+ }
2474
+ // validate paymentConditions, if provided
2475
+ if (topArgs?.paymentConditions) {
2476
+ if (!topArgs?.paymentConditions?.every((condition) => condition.feePaymentAddress && condition.feePaymentAmount && condition.intervalInSeconds)) {
2477
+ throw new Error('[did-provider-cheqd]: paymentConditions must contain feePaymentAddress and feeAmount and intervalInSeconds');
2478
+ }
2479
+ if (!topArgs?.paymentConditions?.every((condition) => typeof condition.feePaymentAddress === 'string' && typeof condition.feePaymentAmount === 'string' && typeof condition.intervalInSeconds === 'number')) {
2480
+ throw new Error('[did-provider-cheqd]: feePaymentAddress and feePaymentAmount must be string and intervalInSeconds must be number');
2481
+ }
2482
+ if (!topArgs?.paymentConditions?.every((condition) => condition.type === AccessControlConditionTypes.timelockPayment)) {
2483
+ throw new Error('[did-provider-cheqd]: paymentConditions must be of type timelockPayment');
2484
+ }
2485
+ }
2486
+ // validate dkgOptions
2487
+ if (!topArgs?.dkgOptions || !topArgs?.dkgOptions?.chain || !topArgs?.dkgOptions?.network) {
2488
+ throw new Error('[did-provider-cheqd]: dkgOptions is required');
2489
+ }
2181
2490
  // instantiate dkg-threshold client, in which case lit-protocol is used
2182
2491
  const lit = await LitProtocol.create({
2183
- chain: options?.topArgs?.bootstrapOptions?.chain,
2184
- litNetwork: options?.topArgs?.bootstrapOptions?.litNetwork
2492
+ chain: topArgs?.dkgOptions?.chain,
2493
+ litNetwork: topArgs?.dkgOptions?.network
2185
2494
  });
2186
- // encrypt
2187
- const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt(bitstring, options?.topArgs?.encryptionOptions?.unifiedAccessControlConditions, true);
2495
+ // construct access control conditions and payment conditions tuple
2496
+ const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted
2497
+ ? (await (async function () {
2498
+ // define payment conditions, give precedence to top-level args
2499
+ const paymentConditions = topArgs?.paymentConditions || publishedList.metadata.paymentConditions;
2500
+ // return access control conditions and payment conditions tuple
2501
+ return [
2502
+ await Promise.all(paymentConditions.map(async (condition) => {
2503
+ switch (condition.type) {
2504
+ case AccessControlConditionTypes.timelockPayment:
2505
+ return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({
2506
+ key: '$.tx_responses.*.timestamp',
2507
+ comparator: '<=',
2508
+ value: `${condition.intervalInSeconds}`,
2509
+ }, condition.feePaymentAmount, condition.feePaymentAddress, condition?.blockHeight, topArgs?.dkgOptions?.chain);
2510
+ default:
2511
+ throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`);
2512
+ }
2513
+ })),
2514
+ paymentConditions
2515
+ ];
2516
+ }()))
2517
+ : (await (async function () {
2518
+ // validate paymentConditions
2519
+ if (!topArgs?.paymentConditions) {
2520
+ throw new Error('[did-provider-cheqd]: paymentConditions is required');
2521
+ }
2522
+ // return access control conditions and payment conditions tuple
2523
+ return [
2524
+ await Promise.all(topArgs.paymentConditions.map(async (condition) => {
2525
+ switch (condition.type) {
2526
+ case AccessControlConditionTypes.timelockPayment:
2527
+ return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({
2528
+ key: '$.tx_responses.*.timestamp',
2529
+ comparator: '<=',
2530
+ value: `${condition.intervalInSeconds}`,
2531
+ }, condition.feePaymentAmount, condition.feePaymentAddress, condition?.blockHeight);
2532
+ default:
2533
+ throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`);
2534
+ }
2535
+ })),
2536
+ topArgs.paymentConditions
2537
+ ];
2538
+ }()));
2539
+ // encrypt bitstring
2540
+ const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt(bitstring, unifiedAccessControlConditionsTuple[0], true);
2541
+ // define status list content
2542
+ const content = {
2543
+ StatusList2021: {
2544
+ statusPurpose: publishedList.StatusList2021.statusPurpose,
2545
+ encodedList: await blobToHexString(encryptedString),
2546
+ validFrom: publishedList.StatusList2021.validFrom,
2547
+ validUntil: options?.publishOptions?.statusListValidUntil || publishedList.StatusList2021.validUntil
2548
+ },
2549
+ metadata: {
2550
+ type: publishedList.metadata.type,
2551
+ encrypted: true,
2552
+ encoding: options?.publishOptions?.statusListEncoding || publishedList.metadata.encoding,
2553
+ encryptedSymmetricKey,
2554
+ paymentConditions: unifiedAccessControlConditionsTuple[1]
2555
+ }
2556
+ };
2188
2557
  // return tuple of publish result and encryption relevant metadata
2189
2558
  return [
2190
- await Cheqd.publishStatusList2021(new Uint8Array(await encryptedString.arrayBuffer()), statusListMetadata, options?.publishOptions),
2559
+ await Cheqd.publishStatusList2021(fromString(JSON.stringify(content), 'utf-8'), statusListMetadata, options?.publishOptions),
2191
2560
  { encryptedString, encryptedSymmetricKey, symmetricKey: toString(symmetricKey, 'hex') }
2192
2561
  ];
2193
2562
  }())
2194
2563
  : (await async function () {
2195
2564
  // validate encoding, if provided
2196
2565
  if (options?.publishOptions?.statusListEncoding && !Object.values(DefaultStatusList2021Encodings).includes(options?.publishOptions?.statusListEncoding)) {
2197
- throw new Error('[did-provider-cheqd]: revocation: Invalid status list encoding');
2566
+ throw new Error('[did-provider-cheqd]: suspension: Invalid status list encoding');
2198
2567
  }
2199
2568
  // validate validUntil, if provided
2200
2569
  if (options?.publishOptions?.statusListValidUntil) {
2201
2570
  // validate validUntil as string
2202
2571
  if (typeof options?.publishOptions?.statusListValidUntil !== 'string')
2203
- throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be string)');
2572
+ throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be string)');
2204
2573
  // validate validUntil as date
2205
2574
  if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil)))
2206
- throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be date)');
2575
+ throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be date)');
2207
2576
  // validate validUntil as future date
2208
2577
  if (new Date(options?.publishOptions?.statusListValidUntil) < new Date())
2209
- throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be future date)');
2578
+ throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be future date)');
2210
2579
  // validate validUntil towards validFrom
2211
2580
  if (new Date(options?.publishOptions?.statusListValidUntil) <= new Date(publishedList.StatusList2021.validFrom))
2212
- throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be after validFrom)');
2581
+ throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be after validFrom)');
2213
2582
  }
2214
2583
  // define status list content
2215
2584
  const content = {
@@ -2242,8 +2611,6 @@ export class Cheqd {
2242
2611
  suspended: true,
2243
2612
  published: topArgs?.publish ? true : undefined,
2244
2613
  statusList: topArgs?.returnUpdatedStatusList ? await Cheqd.fetchStatusList2021(credential) : undefined,
2245
- encryptedStatusList: topArgs?.returnUpdatedEncryptedStatusList ? await blobToHexString(published?.[1]?.encryptedString) : undefined,
2246
- encryptedSymmetricKey: topArgs?.returnEncryptedSymmetricKey ? published?.[1]?.encryptedSymmetricKey : undefined,
2247
2614
  symmetricKey: topArgs?.returnSymmetricKey ? published?.[1]?.symmetricKey : undefined,
2248
2615
  resourceMetadata: topArgs?.returnStatusListMetadata ? await Cheqd.fetchStatusList2021Metadata(credential) : undefined
2249
2616
  };
@@ -2301,8 +2668,8 @@ export class Cheqd {
2301
2668
  return publishedList.metadata.encoding === 'base64url'
2302
2669
  ? publishedList.StatusList2021.encodedList
2303
2670
  : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding), 'base64url');
2304
- // otherwise, decrypt and return bitstring
2305
- const scopedRawBlob = await toBlob(await Cheqd.fetchStatusList2021(credentials[0], true));
2671
+ // otherwise, decrypt and return raw bitstring
2672
+ const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex'));
2306
2673
  // decrypt
2307
2674
  return await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex'));
2308
2675
  }())
@@ -2319,7 +2686,7 @@ export class Cheqd {
2319
2686
  const encoded = new StatusList({ buffer: await Cheqd.getFile(options.statusListFile) }).encode();
2320
2687
  // validate against published list
2321
2688
  if (encoded !== publishedListTranscoded)
2322
- throw new Error('[did-provider-cheqd]: revocation: statusListFile does not match published status list 2021');
2689
+ throw new Error('[did-provider-cheqd]: suspension: statusListFile does not match published status list 2021');
2323
2690
  // return encoded
2324
2691
  return encoded;
2325
2692
  }
@@ -2329,15 +2696,15 @@ export class Cheqd {
2329
2696
  const decrypted = await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex'));
2330
2697
  // validate against published list
2331
2698
  if (decrypted !== publishedListTranscoded)
2332
- throw new Error('[did-provider-cheqd]: revocation: statusListFile does not match published status list 2021');
2699
+ throw new Error('[did-provider-cheqd]: suspension: statusListFile does not match published status list 2021');
2333
2700
  // return decrypted
2334
2701
  return decrypted;
2335
2702
  }
2336
2703
  if (!options?.statusListInlineBitstring)
2337
- throw new Error('[did-provider-cheqd]: revocation: statusListInlineBitstring is required, if statusListFile is not provided');
2704
+ throw new Error('[did-provider-cheqd]: suspension: statusListInlineBitstring is required, if statusListFile is not provided');
2338
2705
  // validate against published list
2339
2706
  if (options?.statusListInlineBitstring !== publishedListTranscoded)
2340
- throw new Error('[did-provider-cheqd]: revocation: statusListInlineBitstring does not match published status list 2021');
2707
+ throw new Error('[did-provider-cheqd]: suspension: statusListInlineBitstring does not match published status list 2021');
2341
2708
  // otherwise, read from inline bitstring
2342
2709
  return options?.statusListInlineBitstring;
2343
2710
  }());
@@ -2377,38 +2744,133 @@ export class Cheqd {
2377
2744
  // publish status list 2021 as new version
2378
2745
  const scoped = topArgs.publishEncrypted
2379
2746
  ? (await async function () {
2747
+ // validate encoding, if provided
2748
+ if (options?.publishOptions?.statusListEncoding && !Object.values(DefaultStatusList2021Encodings).includes(options?.publishOptions?.statusListEncoding)) {
2749
+ throw new Error('[did-provider-cheqd]: suspension: Invalid status list encoding');
2750
+ }
2751
+ // validate validUntil, if provided
2752
+ if (options?.publishOptions?.statusListValidUntil) {
2753
+ // validate validUntil as string
2754
+ if (typeof options?.publishOptions?.statusListValidUntil !== 'string')
2755
+ throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be string)');
2756
+ // validate validUntil as date
2757
+ if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil)))
2758
+ throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be date)');
2759
+ // validate validUntil as future date
2760
+ if (new Date(options?.publishOptions?.statusListValidUntil) < new Date())
2761
+ throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be future date)');
2762
+ // validate validUntil towards validFrom
2763
+ if (new Date(options?.publishOptions?.statusListValidUntil) <= new Date(publishedList.StatusList2021.validFrom))
2764
+ throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be after validFrom)');
2765
+ }
2766
+ // validate paymentConditions, if provided
2767
+ if (topArgs?.paymentConditions) {
2768
+ if (!topArgs?.paymentConditions?.every((condition) => condition.feePaymentAddress && condition.feePaymentAmount && condition.intervalInSeconds)) {
2769
+ throw new Error('[did-provider-cheqd]: paymentConditions must contain feePaymentAddress and feeAmount and intervalInSeconds');
2770
+ }
2771
+ if (!topArgs?.paymentConditions?.every((condition) => typeof condition.feePaymentAddress === 'string' && typeof condition.feePaymentAmount === 'string' && typeof condition.intervalInSeconds === 'number')) {
2772
+ throw new Error('[did-provider-cheqd]: feePaymentAddress and feePaymentAmount must be string and intervalInSeconds must be number');
2773
+ }
2774
+ if (!topArgs?.paymentConditions?.every((condition) => condition.type === AccessControlConditionTypes.timelockPayment)) {
2775
+ throw new Error('[did-provider-cheqd]: paymentConditions must be of type timelockPayment');
2776
+ }
2777
+ }
2778
+ // validate dkgOptions
2779
+ if (!topArgs?.dkgOptions || !topArgs?.dkgOptions?.chain || !topArgs?.dkgOptions?.network) {
2780
+ throw new Error('[did-provider-cheqd]: dkgOptions is required');
2781
+ }
2380
2782
  // instantiate dkg-threshold client, in which case lit-protocol is used
2381
2783
  const lit = await LitProtocol.create({
2382
- chain: options?.topArgs?.bootstrapOptions?.chain,
2383
- litNetwork: options?.topArgs?.bootstrapOptions?.litNetwork
2784
+ chain: topArgs?.dkgOptions?.chain,
2785
+ litNetwork: topArgs?.dkgOptions?.network
2384
2786
  });
2385
- // encrypt
2386
- const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt(bitstring, options?.topArgs?.encryptionOptions?.unifiedAccessControlConditions, true);
2787
+ // construct access control conditions and payment conditions tuple
2788
+ const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted
2789
+ ? (await (async function () {
2790
+ // define payment conditions, give precedence to top-level args
2791
+ const paymentConditions = topArgs?.paymentConditions || publishedList.metadata.paymentConditions;
2792
+ // return access control conditions and payment conditions tuple
2793
+ return [
2794
+ await Promise.all(paymentConditions.map(async (condition) => {
2795
+ switch (condition.type) {
2796
+ case AccessControlConditionTypes.timelockPayment:
2797
+ return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({
2798
+ key: '$.tx_responses.*.timestamp',
2799
+ comparator: '<=',
2800
+ value: `${condition.intervalInSeconds}`,
2801
+ }, condition.feePaymentAmount, condition.feePaymentAddress, condition?.blockHeight, topArgs?.dkgOptions?.chain);
2802
+ default:
2803
+ throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`);
2804
+ }
2805
+ })),
2806
+ paymentConditions
2807
+ ];
2808
+ }()))
2809
+ : (await (async function () {
2810
+ // validate paymentConditions
2811
+ if (!topArgs?.paymentConditions) {
2812
+ throw new Error('[did-provider-cheqd]: paymentConditions is required');
2813
+ }
2814
+ // return access control conditions and payment conditions tuple
2815
+ return [
2816
+ await Promise.all(topArgs.paymentConditions.map(async (condition) => {
2817
+ switch (condition.type) {
2818
+ case AccessControlConditionTypes.timelockPayment:
2819
+ return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({
2820
+ key: '$.tx_responses.*.timestamp',
2821
+ comparator: '<=',
2822
+ value: `${condition.intervalInSeconds}`,
2823
+ }, condition.feePaymentAmount, condition.feePaymentAddress, condition?.blockHeight);
2824
+ default:
2825
+ throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`);
2826
+ }
2827
+ })),
2828
+ topArgs.paymentConditions
2829
+ ];
2830
+ }()));
2831
+ // encrypt bitstring
2832
+ const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt(bitstring, unifiedAccessControlConditionsTuple[0], true);
2833
+ // define status list content
2834
+ const content = {
2835
+ StatusList2021: {
2836
+ statusPurpose: publishedList.StatusList2021.statusPurpose,
2837
+ encodedList: await blobToHexString(encryptedString),
2838
+ validFrom: publishedList.StatusList2021.validFrom,
2839
+ validUntil: options?.publishOptions?.statusListValidUntil || publishedList.StatusList2021.validUntil
2840
+ },
2841
+ metadata: {
2842
+ type: publishedList.metadata.type,
2843
+ encrypted: true,
2844
+ encoding: options?.publishOptions?.statusListEncoding || publishedList.metadata.encoding,
2845
+ encryptedSymmetricKey,
2846
+ paymentConditions: unifiedAccessControlConditionsTuple[1]
2847
+ }
2848
+ };
2387
2849
  // return tuple of publish result and encryption relevant metadata
2388
2850
  return [
2389
- await Cheqd.publishStatusList2021(new Uint8Array(await encryptedString.arrayBuffer()), statusListMetadata, options?.publishOptions),
2851
+ await Cheqd.publishStatusList2021(fromString(JSON.stringify(content), 'utf-8'), statusListMetadata, options?.publishOptions),
2390
2852
  { encryptedString, encryptedSymmetricKey, symmetricKey: toString(symmetricKey, 'hex') }
2391
2853
  ];
2392
2854
  }())
2393
2855
  : (await async function () {
2394
2856
  // validate encoding, if provided
2395
2857
  if (options?.publishOptions?.statusListEncoding && !Object.values(DefaultStatusList2021Encodings).includes(options?.publishOptions?.statusListEncoding)) {
2396
- throw new Error('[did-provider-cheqd]: revocation: Invalid status list encoding');
2858
+ throw new Error('[did-provider-cheqd]: suspension: Invalid status list encoding');
2397
2859
  }
2398
2860
  // validate validUntil, if provided
2399
2861
  if (options?.publishOptions?.statusListValidUntil) {
2400
2862
  // validate validUntil as string
2401
2863
  if (typeof options?.publishOptions?.statusListValidUntil !== 'string')
2402
- throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be string)');
2864
+ throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be string)');
2403
2865
  // validate validUntil as date
2404
2866
  if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil)))
2405
- throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be date)');
2867
+ throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be date)');
2406
2868
  // validate validUntil as future date
2407
2869
  if (new Date(options?.publishOptions?.statusListValidUntil) < new Date())
2408
- throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be future date)');
2870
+ throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be future date)');
2409
2871
  // validate validUntil towards validFrom
2410
2872
  if (new Date(options?.publishOptions?.statusListValidUntil) <= new Date(publishedList.StatusList2021.validFrom))
2411
- throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be after validFrom)');
2873
+ throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be after validFrom)');
2412
2874
  }
2413
2875
  // define status list content
2414
2876
  const content = {
@@ -2441,8 +2903,6 @@ export class Cheqd {
2441
2903
  suspended: suspended.map((result) => result.status === 'fulfilled' ? result.value.suspended : false),
2442
2904
  published: topArgs?.publish ? true : undefined,
2443
2905
  statusList: topArgs?.returnUpdatedStatusList ? await Cheqd.fetchStatusList2021(credentials[0]) : undefined,
2444
- encryptedStatusList: topArgs?.returnUpdatedEncryptedStatusList ? await blobToHexString(published?.[1]?.encryptedString) : undefined,
2445
- encryptedSymmetricKey: topArgs?.returnEncryptedSymmetricKey ? published?.[1]?.encryptedSymmetricKey : undefined,
2446
2906
  symmetricKey: topArgs?.returnSymmetricKey ? published?.[1]?.symmetricKey : undefined,
2447
2907
  resourceMetadata: topArgs?.returnStatusListMetadata ? await Cheqd.fetchStatusList2021Metadata(credentials[0]) : undefined
2448
2908
  };
@@ -2462,7 +2922,7 @@ export class Cheqd {
2462
2922
  const publishedList = (await Cheqd.fetchStatusList2021(credential));
2463
2923
  // early return, if encrypted and no decryption key provided
2464
2924
  if (publishedList.metadata.encrypted && !options?.topArgs?.symmetricKey)
2465
- throw new Error('[did-provider-cheqd]: suspension: symmetricKey is required, if status list 2021 is encrypted');
2925
+ throw new Error('[did-provider-cheqd]: unsuspension: symmetricKey is required, if status list 2021 is encrypted');
2466
2926
  // fetch status list 2021 inscribed in credential
2467
2927
  const statusList2021 = options?.topArgs?.fetchList
2468
2928
  ? (await async function () {
@@ -2471,8 +2931,8 @@ export class Cheqd {
2471
2931
  return publishedList.metadata.encoding === 'base64url'
2472
2932
  ? publishedList.StatusList2021.encodedList
2473
2933
  : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding), 'base64url');
2474
- // otherwise, decrypt and return bitstring
2475
- const scopedRawBlob = await toBlob(await Cheqd.fetchStatusList2021(credential, true));
2934
+ // otherwise, decrypt and return raw bitstring
2935
+ const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex'));
2476
2936
  // decrypt
2477
2937
  return await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex'));
2478
2938
  }())
@@ -2489,7 +2949,7 @@ export class Cheqd {
2489
2949
  const encoded = new StatusList({ buffer: await Cheqd.getFile(options.statusListFile) }).encode();
2490
2950
  // validate against published list
2491
2951
  if (encoded !== publishedListTranscoded)
2492
- throw new Error('[did-provider-cheqd]: revocation: statusListFile does not match published status list 2021');
2952
+ throw new Error('[did-provider-cheqd]: unsuspension: statusListFile does not match published status list 2021');
2493
2953
  // return encoded
2494
2954
  return encoded;
2495
2955
  }
@@ -2499,15 +2959,15 @@ export class Cheqd {
2499
2959
  const decrypted = await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex'));
2500
2960
  // validate against published list
2501
2961
  if (decrypted !== publishedListTranscoded)
2502
- throw new Error('[did-provider-cheqd]: revocation: statusListFile does not match published status list 2021');
2962
+ throw new Error('[did-provider-cheqd]: unsuspension: statusListFile does not match published status list 2021');
2503
2963
  // return decrypted
2504
2964
  return decrypted;
2505
2965
  }
2506
2966
  if (!options?.statusListInlineBitstring)
2507
- throw new Error('[did-provider-cheqd]: revocation: statusListInlineBitstring is required, if statusListFile is not provided');
2967
+ throw new Error('[did-provider-cheqd]: unsuspension: statusListInlineBitstring is required, if statusListFile is not provided');
2508
2968
  // validate against published list
2509
2969
  if (options?.statusListInlineBitstring !== publishedListTranscoded)
2510
- throw new Error('[did-provider-cheqd]: revocation: statusListInlineBitstring does not match published status list 2021');
2970
+ throw new Error('[did-provider-cheqd]: unsuspension: statusListInlineBitstring does not match published status list 2021');
2511
2971
  // otherwise, read from inline bitstring
2512
2972
  return options?.statusListInlineBitstring;
2513
2973
  }());
@@ -2534,38 +2994,133 @@ export class Cheqd {
2534
2994
  // publish status list 2021 as new version
2535
2995
  const scoped = topArgs.publishEncrypted
2536
2996
  ? (await async function () {
2997
+ // validate encoding, if provided
2998
+ if (options?.publishOptions?.statusListEncoding && !Object.values(DefaultStatusList2021Encodings).includes(options?.publishOptions?.statusListEncoding)) {
2999
+ throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list encoding');
3000
+ }
3001
+ // validate validUntil, if provided
3002
+ if (options?.publishOptions?.statusListValidUntil) {
3003
+ // validate validUntil as string
3004
+ if (typeof options?.publishOptions?.statusListValidUntil !== 'string')
3005
+ throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be string)');
3006
+ // validate validUntil as date
3007
+ if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil)))
3008
+ throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be date)');
3009
+ // validate validUntil as future date
3010
+ if (new Date(options?.publishOptions?.statusListValidUntil) < new Date())
3011
+ throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be future date)');
3012
+ // validate validUntil towards validFrom
3013
+ if (new Date(options?.publishOptions?.statusListValidUntil) <= new Date(publishedList.StatusList2021.validFrom))
3014
+ throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be after validFrom)');
3015
+ }
3016
+ // validate paymentConditions, if provided
3017
+ if (topArgs?.paymentConditions) {
3018
+ if (!topArgs?.paymentConditions?.every((condition) => condition.feePaymentAddress && condition.feePaymentAmount && condition.intervalInSeconds)) {
3019
+ throw new Error('[did-provider-cheqd]: paymentConditions must contain feePaymentAddress and feeAmount and intervalInSeconds');
3020
+ }
3021
+ if (!topArgs?.paymentConditions?.every((condition) => typeof condition.feePaymentAddress === 'string' && typeof condition.feePaymentAmount === 'string' && typeof condition.intervalInSeconds === 'number')) {
3022
+ throw new Error('[did-provider-cheqd]: feePaymentAddress and feePaymentAmount must be string and intervalInSeconds must be number');
3023
+ }
3024
+ if (!topArgs?.paymentConditions?.every((condition) => condition.type === AccessControlConditionTypes.timelockPayment)) {
3025
+ throw new Error('[did-provider-cheqd]: paymentConditions must be of type timelockPayment');
3026
+ }
3027
+ }
3028
+ // validate dkgOptions
3029
+ if (!topArgs?.dkgOptions || !topArgs?.dkgOptions?.chain || !topArgs?.dkgOptions?.network) {
3030
+ throw new Error('[did-provider-cheqd]: dkgOptions is required');
3031
+ }
2537
3032
  // instantiate dkg-threshold client, in which case lit-protocol is used
2538
3033
  const lit = await LitProtocol.create({
2539
- chain: options?.topArgs?.bootstrapOptions?.chain,
2540
- litNetwork: options?.topArgs?.bootstrapOptions?.litNetwork
3034
+ chain: topArgs?.dkgOptions?.chain,
3035
+ litNetwork: topArgs?.dkgOptions?.network
2541
3036
  });
2542
- // encrypt
2543
- const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt(bitstring, options?.topArgs?.encryptionOptions?.unifiedAccessControlConditions, true);
3037
+ // construct access control conditions and payment conditions tuple
3038
+ const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted
3039
+ ? (await (async function () {
3040
+ // define payment conditions, give precedence to top-level args
3041
+ const paymentConditions = topArgs?.paymentConditions || publishedList.metadata.paymentConditions;
3042
+ // return access control conditions and payment conditions tuple
3043
+ return [
3044
+ await Promise.all(paymentConditions.map(async (condition) => {
3045
+ switch (condition.type) {
3046
+ case AccessControlConditionTypes.timelockPayment:
3047
+ return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({
3048
+ key: '$.tx_responses.*.timestamp',
3049
+ comparator: '<=',
3050
+ value: `${condition.intervalInSeconds}`,
3051
+ }, condition.feePaymentAmount, condition.feePaymentAddress, condition?.blockHeight, topArgs?.dkgOptions?.chain);
3052
+ default:
3053
+ throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`);
3054
+ }
3055
+ })),
3056
+ paymentConditions
3057
+ ];
3058
+ }()))
3059
+ : (await (async function () {
3060
+ // validate paymentConditions
3061
+ if (!topArgs?.paymentConditions) {
3062
+ throw new Error('[did-provider-cheqd]: paymentConditions is required');
3063
+ }
3064
+ // return access control conditions and payment conditions tuple
3065
+ return [
3066
+ await Promise.all(topArgs.paymentConditions.map(async (condition) => {
3067
+ switch (condition.type) {
3068
+ case AccessControlConditionTypes.timelockPayment:
3069
+ return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({
3070
+ key: '$.tx_responses.*.timestamp',
3071
+ comparator: '<=',
3072
+ value: `${condition.intervalInSeconds}`,
3073
+ }, condition.feePaymentAmount, condition.feePaymentAddress, condition?.blockHeight);
3074
+ default:
3075
+ throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`);
3076
+ }
3077
+ })),
3078
+ topArgs.paymentConditions
3079
+ ];
3080
+ }()));
3081
+ // encrypt bitstring
3082
+ const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt(bitstring, unifiedAccessControlConditionsTuple[0], true);
3083
+ // define status list content
3084
+ const content = {
3085
+ StatusList2021: {
3086
+ statusPurpose: publishedList.StatusList2021.statusPurpose,
3087
+ encodedList: await blobToHexString(encryptedString),
3088
+ validFrom: publishedList.StatusList2021.validFrom,
3089
+ validUntil: options?.publishOptions?.statusListValidUntil || publishedList.StatusList2021.validUntil
3090
+ },
3091
+ metadata: {
3092
+ type: publishedList.metadata.type,
3093
+ encrypted: true,
3094
+ encoding: options?.publishOptions?.statusListEncoding || publishedList.metadata.encoding,
3095
+ encryptedSymmetricKey,
3096
+ paymentConditions: unifiedAccessControlConditionsTuple[1]
3097
+ }
3098
+ };
2544
3099
  // return tuple of publish result and encryption relevant metadata
2545
3100
  return [
2546
- await Cheqd.publishStatusList2021(new Uint8Array(await encryptedString.arrayBuffer()), statusListMetadata, options?.publishOptions),
3101
+ await Cheqd.publishStatusList2021(fromString(JSON.stringify(content), 'utf-8'), statusListMetadata, options?.publishOptions),
2547
3102
  { encryptedString, encryptedSymmetricKey, symmetricKey: toString(symmetricKey, 'hex') }
2548
3103
  ];
2549
3104
  }())
2550
3105
  : (await async function () {
2551
3106
  // validate encoding, if provided
2552
3107
  if (options?.publishOptions?.statusListEncoding && !Object.values(DefaultStatusList2021Encodings).includes(options?.publishOptions?.statusListEncoding)) {
2553
- throw new Error('[did-provider-cheqd]: revocation: Invalid status list encoding');
3108
+ throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list encoding');
2554
3109
  }
2555
3110
  // validate validUntil, if provided
2556
3111
  if (options?.publishOptions?.statusListValidUntil) {
2557
3112
  // validate validUntil as string
2558
3113
  if (typeof options?.publishOptions?.statusListValidUntil !== 'string')
2559
- throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be string)');
3114
+ throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be string)');
2560
3115
  // validate validUntil as date
2561
3116
  if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil)))
2562
- throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be date)');
3117
+ throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be date)');
2563
3118
  // validate validUntil as future date
2564
3119
  if (new Date(options?.publishOptions?.statusListValidUntil) < new Date())
2565
- throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be future date)');
3120
+ throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be future date)');
2566
3121
  // validate validUntil towards validFrom
2567
3122
  if (new Date(options?.publishOptions?.statusListValidUntil) <= new Date(publishedList.StatusList2021.validFrom))
2568
- throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be after validFrom)');
3123
+ throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be after validFrom)');
2569
3124
  }
2570
3125
  // define status list content
2571
3126
  const content = {
@@ -2598,8 +3153,6 @@ export class Cheqd {
2598
3153
  unsuspended: true,
2599
3154
  published: topArgs?.publish ? true : undefined,
2600
3155
  statusList: topArgs?.returnUpdatedStatusList ? await Cheqd.fetchStatusList2021(credential) : undefined,
2601
- encryptedStatusList: topArgs?.returnUpdatedEncryptedStatusList ? await blobToHexString(published?.[1]?.encryptedString) : undefined,
2602
- encryptedSymmetricKey: topArgs?.returnEncryptedSymmetricKey ? published?.[1]?.encryptedSymmetricKey : undefined,
2603
3156
  symmetricKey: topArgs?.returnSymmetricKey ? published?.[1]?.symmetricKey : undefined,
2604
3157
  resourceMetadata: topArgs?.returnStatusListMetadata ? await Cheqd.fetchStatusList2021Metadata(credential) : undefined
2605
3158
  };
@@ -2657,8 +3210,8 @@ export class Cheqd {
2657
3210
  return publishedList.metadata.encoding === 'base64url'
2658
3211
  ? publishedList.StatusList2021.encodedList
2659
3212
  : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding), 'base64url');
2660
- // otherwise, decrypt and return bitstring
2661
- const scopedRawBlob = await toBlob(await Cheqd.fetchStatusList2021(credentials[0], true));
3213
+ // otherwise, decrypt and return raw bitstring
3214
+ const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex'));
2662
3215
  // decrypt
2663
3216
  return await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex'));
2664
3217
  }())
@@ -2675,7 +3228,7 @@ export class Cheqd {
2675
3228
  const encoded = new StatusList({ buffer: await Cheqd.getFile(options.statusListFile) }).encode();
2676
3229
  // validate against published list
2677
3230
  if (encoded !== publishedListTranscoded)
2678
- throw new Error('[did-provider-cheqd]: revocation: statusListFile does not match published status list 2021');
3231
+ throw new Error('[did-provider-cheqd]: unsuspension: statusListFile does not match published status list 2021');
2679
3232
  // return encoded
2680
3233
  return encoded;
2681
3234
  }
@@ -2685,15 +3238,15 @@ export class Cheqd {
2685
3238
  const decrypted = await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex'));
2686
3239
  // validate against published list
2687
3240
  if (decrypted !== publishedListTranscoded)
2688
- throw new Error('[did-provider-cheqd]: revocation: statusListFile does not match published status list 2021');
3241
+ throw new Error('[did-provider-cheqd]: unsuspension: statusListFile does not match published status list 2021');
2689
3242
  // return decrypted
2690
3243
  return decrypted;
2691
3244
  }
2692
3245
  if (!options?.statusListInlineBitstring)
2693
- throw new Error('[did-provider-cheqd]: revocation: statusListInlineBitstring is required, if statusListFile is not provided');
3246
+ throw new Error('[did-provider-cheqd]: unsuspension: statusListInlineBitstring is required, if statusListFile is not provided');
2694
3247
  // validate against published list
2695
3248
  if (options?.statusListInlineBitstring !== publishedListTranscoded)
2696
- throw new Error('[did-provider-cheqd]: revocation: statusListInlineBitstring does not match published status list 2021');
3249
+ throw new Error('[did-provider-cheqd]: unsuspension: statusListInlineBitstring does not match published status list 2021');
2697
3250
  // otherwise, read from inline bitstring
2698
3251
  return options?.statusListInlineBitstring;
2699
3252
  }());
@@ -2733,38 +3286,133 @@ export class Cheqd {
2733
3286
  // publish status list 2021 as new version
2734
3287
  const scoped = topArgs.publishEncrypted
2735
3288
  ? (await async function () {
3289
+ // validate encoding, if provided
3290
+ if (options?.publishOptions?.statusListEncoding && !Object.values(DefaultStatusList2021Encodings).includes(options?.publishOptions?.statusListEncoding)) {
3291
+ throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list encoding');
3292
+ }
3293
+ // validate validUntil, if provided
3294
+ if (options?.publishOptions?.statusListValidUntil) {
3295
+ // validate validUntil as string
3296
+ if (typeof options?.publishOptions?.statusListValidUntil !== 'string')
3297
+ throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be string)');
3298
+ // validate validUntil as date
3299
+ if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil)))
3300
+ throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be date)');
3301
+ // validate validUntil as future date
3302
+ if (new Date(options?.publishOptions?.statusListValidUntil) < new Date())
3303
+ throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be future date)');
3304
+ // validate validUntil towards validFrom
3305
+ if (new Date(options?.publishOptions?.statusListValidUntil) <= new Date(publishedList.StatusList2021.validFrom))
3306
+ throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be after validFrom)');
3307
+ }
3308
+ // validate paymentConditions, if provided
3309
+ if (topArgs?.paymentConditions) {
3310
+ if (!topArgs?.paymentConditions?.every((condition) => condition.feePaymentAddress && condition.feePaymentAmount && condition.intervalInSeconds)) {
3311
+ throw new Error('[did-provider-cheqd]: paymentConditions must contain feePaymentAddress and feeAmount and intervalInSeconds');
3312
+ }
3313
+ if (!topArgs?.paymentConditions?.every((condition) => typeof condition.feePaymentAddress === 'string' && typeof condition.feePaymentAmount === 'string' && typeof condition.intervalInSeconds === 'number')) {
3314
+ throw new Error('[did-provider-cheqd]: feePaymentAddress and feePaymentAmount must be string and intervalInSeconds must be number');
3315
+ }
3316
+ if (!topArgs?.paymentConditions?.every((condition) => condition.type === AccessControlConditionTypes.timelockPayment)) {
3317
+ throw new Error('[did-provider-cheqd]: paymentConditions must be of type timelockPayment');
3318
+ }
3319
+ }
3320
+ // validate dkgOptions
3321
+ if (!topArgs?.dkgOptions || !topArgs?.dkgOptions?.chain || !topArgs?.dkgOptions?.network) {
3322
+ throw new Error('[did-provider-cheqd]: dkgOptions is required');
3323
+ }
2736
3324
  // instantiate dkg-threshold client, in which case lit-protocol is used
2737
3325
  const lit = await LitProtocol.create({
2738
- chain: options?.topArgs?.bootstrapOptions?.chain,
2739
- litNetwork: options?.topArgs?.bootstrapOptions?.litNetwork
3326
+ chain: topArgs?.dkgOptions?.chain,
3327
+ litNetwork: topArgs?.dkgOptions?.network
2740
3328
  });
2741
- // encrypt
2742
- const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt(bitstring, options?.topArgs?.encryptionOptions?.unifiedAccessControlConditions, true);
3329
+ // construct access control conditions and payment conditions tuple
3330
+ const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted
3331
+ ? (await (async function () {
3332
+ // define payment conditions, give precedence to top-level args
3333
+ const paymentConditions = topArgs?.paymentConditions || publishedList.metadata.paymentConditions;
3334
+ // return access control conditions and payment conditions tuple
3335
+ return [
3336
+ await Promise.all(paymentConditions.map(async (condition) => {
3337
+ switch (condition.type) {
3338
+ case AccessControlConditionTypes.timelockPayment:
3339
+ return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({
3340
+ key: '$.tx_responses.*.timestamp',
3341
+ comparator: '<=',
3342
+ value: `${condition.intervalInSeconds}`,
3343
+ }, condition.feePaymentAmount, condition.feePaymentAddress, condition?.blockHeight, topArgs?.dkgOptions?.chain);
3344
+ default:
3345
+ throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`);
3346
+ }
3347
+ })),
3348
+ paymentConditions
3349
+ ];
3350
+ }()))
3351
+ : (await (async function () {
3352
+ // validate paymentConditions
3353
+ if (!topArgs?.paymentConditions) {
3354
+ throw new Error('[did-provider-cheqd]: paymentConditions is required');
3355
+ }
3356
+ // return access control conditions and payment conditions tuple
3357
+ return [
3358
+ await Promise.all(topArgs.paymentConditions.map(async (condition) => {
3359
+ switch (condition.type) {
3360
+ case AccessControlConditionTypes.timelockPayment:
3361
+ return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({
3362
+ key: '$.tx_responses.*.timestamp',
3363
+ comparator: '<=',
3364
+ value: `${condition.intervalInSeconds}`,
3365
+ }, condition.feePaymentAmount, condition.feePaymentAddress, condition?.blockHeight);
3366
+ default:
3367
+ throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`);
3368
+ }
3369
+ })),
3370
+ topArgs.paymentConditions
3371
+ ];
3372
+ }()));
3373
+ // encrypt bitstring
3374
+ const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt(bitstring, unifiedAccessControlConditionsTuple[0], true);
3375
+ // define status list content
3376
+ const content = {
3377
+ StatusList2021: {
3378
+ statusPurpose: publishedList.StatusList2021.statusPurpose,
3379
+ encodedList: await blobToHexString(encryptedString),
3380
+ validFrom: publishedList.StatusList2021.validFrom,
3381
+ validUntil: options?.publishOptions?.statusListValidUntil || publishedList.StatusList2021.validUntil
3382
+ },
3383
+ metadata: {
3384
+ type: publishedList.metadata.type,
3385
+ encrypted: true,
3386
+ encoding: options?.publishOptions?.statusListEncoding || publishedList.metadata.encoding,
3387
+ encryptedSymmetricKey,
3388
+ paymentConditions: unifiedAccessControlConditionsTuple[1]
3389
+ }
3390
+ };
2743
3391
  // return tuple of publish result and encryption relevant metadata
2744
3392
  return [
2745
- await Cheqd.publishStatusList2021(new Uint8Array(await encryptedString.arrayBuffer()), statusListMetadata, options?.publishOptions),
3393
+ await Cheqd.publishStatusList2021(fromString(JSON.stringify(content), 'utf-8'), statusListMetadata, options?.publishOptions),
2746
3394
  { encryptedString, encryptedSymmetricKey, symmetricKey: toString(symmetricKey, 'hex') }
2747
3395
  ];
2748
3396
  }())
2749
3397
  : (await async function () {
2750
3398
  // validate encoding, if provided
2751
3399
  if (options?.publishOptions?.statusListEncoding && !Object.values(DefaultStatusList2021Encodings).includes(options?.publishOptions?.statusListEncoding)) {
2752
- throw new Error('[did-provider-cheqd]: revocation: Invalid status list encoding');
3400
+ throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list encoding');
2753
3401
  }
2754
3402
  // validate validUntil, if provided
2755
3403
  if (options?.publishOptions?.statusListValidUntil) {
2756
3404
  // validate validUntil as string
2757
3405
  if (typeof options?.publishOptions?.statusListValidUntil !== 'string')
2758
- throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be string)');
3406
+ throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be string)');
2759
3407
  // validate validUntil as date
2760
3408
  if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil)))
2761
- throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be date)');
3409
+ throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be date)');
2762
3410
  // validate validUntil as future date
2763
3411
  if (new Date(options?.publishOptions?.statusListValidUntil) < new Date())
2764
- throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be future date)');
3412
+ throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be future date)');
2765
3413
  // validate validUntil towards validFrom
2766
3414
  if (new Date(options?.publishOptions?.statusListValidUntil) <= new Date(publishedList.StatusList2021.validFrom))
2767
- throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be after validFrom)');
3415
+ throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be after validFrom)');
2768
3416
  }
2769
3417
  // define status list content
2770
3418
  const content = {
@@ -2797,8 +3445,6 @@ export class Cheqd {
2797
3445
  unsuspended: unsuspended.map((result) => result.status === 'fulfilled' ? result.value.unsuspended : false),
2798
3446
  published: topArgs?.publish ? true : undefined,
2799
3447
  statusList: topArgs?.returnUpdatedStatusList ? await Cheqd.fetchStatusList2021(credentials[0]) : undefined,
2800
- encryptedStatusList: topArgs?.returnUpdatedEncryptedStatusList ? await blobToHexString(published?.[1]?.encryptedString) : undefined,
2801
- encryptedSymmetricKey: topArgs?.returnEncryptedSymmetricKey ? published?.[1]?.encryptedSymmetricKey : undefined,
2802
3448
  symmetricKey: topArgs?.returnSymmetricKey ? published?.[1]?.symmetricKey : undefined,
2803
3449
  resourceMetadata: topArgs?.returnStatusListMetadata ? await Cheqd.fetchStatusList2021Metadata(credentials[0]) : undefined
2804
3450
  };
@@ -2812,13 +3458,10 @@ export class Cheqd {
2812
3458
  static async checkRevoked(credential, options = { fetchList: true }) {
2813
3459
  // validate status purpose
2814
3460
  if (credential.credentialStatus?.statusPurpose !== 'revocation') {
2815
- throw new Error(`[did-provider-cheqd]: revocation: Unsupported status purpose: ${credential.credentialStatus?.statusPurpose}`);
3461
+ throw new Error(`[did-provider-cheqd]: check: revocation: Unsupported status purpose: ${credential.credentialStatus?.statusPurpose}`);
2816
3462
  }
2817
3463
  // fetch status list 2021
2818
3464
  const publishedList = (await Cheqd.fetchStatusList2021(credential));
2819
- // early return, if encrypted and decryption key is not provided
2820
- if (publishedList.metadata.encrypted && !options?.topArgs?.encryptedSymmetricKey)
2821
- throw new Error('[did-provider-cheqd]: revocation: encryptedSymmetricKey is required, if status list 2021 is encrypted');
2822
3465
  // fetch status list 2021 inscribed in credential
2823
3466
  const statusList2021 = options?.topArgs?.fetchList
2824
3467
  ? (await async function () {
@@ -2827,15 +3470,28 @@ export class Cheqd {
2827
3470
  return publishedList.metadata.encoding === 'base64url'
2828
3471
  ? publishedList.StatusList2021.encodedList
2829
3472
  : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding), 'base64url');
2830
- // otherwise, decrypt and return bitstring
2831
- const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding));
3473
+ // otherwise, decrypt and return raw bitstring
3474
+ const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex'));
2832
3475
  // instantiate dkg-threshold client, in which case lit-protocol is used
2833
3476
  const lit = await LitProtocol.create({
2834
- chain: options?.topArgs?.bootstrapOptions?.chain,
2835
- litNetwork: options?.topArgs?.bootstrapOptions?.litNetwork
3477
+ chain: options?.topArgs?.dkgOptions?.chain,
3478
+ litNetwork: options?.topArgs?.dkgOptions?.network
2836
3479
  });
3480
+ // construct access control conditions
3481
+ const unifiedAccessControlConditions = await Promise.all(publishedList.metadata.paymentConditions.map(async (condition) => {
3482
+ switch (condition.type) {
3483
+ case AccessControlConditionTypes.timelockPayment:
3484
+ return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({
3485
+ key: '$.tx_responses.*.timestamp',
3486
+ comparator: '<=',
3487
+ value: `${condition.intervalInSeconds}`,
3488
+ }, condition.feePaymentAmount, condition.feePaymentAddress, condition?.blockHeight, options?.topArgs?.dkgOptions?.chain);
3489
+ default:
3490
+ throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`);
3491
+ }
3492
+ }));
2837
3493
  // decrypt
2838
- return await lit.decrypt(scopedRawBlob, options?.topArgs?.encryptedSymmetricKey, options?.topArgs?.decryptionOptions?.unifiedAccessControlConditions);
3494
+ return await lit.decrypt(scopedRawBlob, publishedList.metadata.encryptedSymmetricKey, unifiedAccessControlConditions);
2839
3495
  }())
2840
3496
  : (await async function () {
2841
3497
  // transcode to base64url, if needed
@@ -2850,7 +3506,7 @@ export class Cheqd {
2850
3506
  const encoded = new StatusList({ buffer: await Cheqd.getFile(options.statusListFile) }).encode();
2851
3507
  // validate against published list
2852
3508
  if (encoded !== publishedListTranscoded)
2853
- throw new Error('[did-provider-cheqd]: revocation: statusListFile does not match published status list 2021');
3509
+ throw new Error('[did-provider-cheqd]: check: revocation: statusListFile does not match published status list 2021');
2854
3510
  // return encoded
2855
3511
  return encoded;
2856
3512
  }
@@ -2860,33 +3516,34 @@ export class Cheqd {
2860
3516
  const decrypted = await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex'));
2861
3517
  // validate against published list
2862
3518
  if (decrypted !== publishedListTranscoded)
2863
- throw new Error('[did-provider-cheqd]: revocation: statusListFile does not match published status list 2021');
3519
+ throw new Error('[did-provider-cheqd]: check: revocation: statusListFile does not match published status list 2021');
2864
3520
  // return decrypted
2865
3521
  return decrypted;
2866
3522
  }
2867
3523
  if (!options?.statusListInlineBitstring)
2868
- throw new Error('[did-provider-cheqd]: revocation: statusListInlineBitstring is required, if statusListFile is not provided');
3524
+ throw new Error('[did-provider-cheqd]: check: revocation: statusListInlineBitstring is required, if statusListFile is not provided');
2869
3525
  // validate against published list
2870
3526
  if (options?.statusListInlineBitstring !== publishedListTranscoded)
2871
- throw new Error('[did-provider-cheqd]: revocation: statusListInlineBitstring does not match published status list 2021');
3527
+ throw new Error('[did-provider-cheqd]: check: revocation: statusListInlineBitstring does not match published status list 2021');
2872
3528
  // otherwise, read from inline bitstring
2873
3529
  return options?.statusListInlineBitstring;
2874
3530
  }());
3531
+ // transcode, if needed
3532
+ const transcodedStatusList2021 = publishedList.metadata.encoding === 'base64url'
3533
+ ? statusList2021
3534
+ : toString(fromString(statusList2021, publishedList.metadata.encoding), 'base64url');
2875
3535
  // parse status list 2021
2876
- const statusList = await StatusList.decode({ encodedList: statusList2021 });
3536
+ const statusList = await StatusList.decode({ encodedList: transcodedStatusList2021 });
2877
3537
  // get status by index
2878
3538
  return !!statusList.getStatus(Number(credential.credentialStatus.statusListIndex));
2879
3539
  }
2880
3540
  static async checkSuspended(credential, options = { fetchList: true }) {
2881
3541
  // validate status purpose
2882
3542
  if (credential.credentialStatus?.statusPurpose !== 'suspension') {
2883
- throw new Error(`[did-provider-cheqd]: suspension: Unsupported status purpose: ${credential.credentialStatus?.statusPurpose}`);
3543
+ throw new Error(`[did-provider-cheqd]: check: suspension: Unsupported status purpose: ${credential.credentialStatus?.statusPurpose}`);
2884
3544
  }
2885
3545
  // fetch status list 2021
2886
3546
  const publishedList = (await Cheqd.fetchStatusList2021(credential));
2887
- // early return, if encrypted and decryption key is not provided
2888
- if (publishedList.metadata.encrypted && !options?.topArgs?.encryptedSymmetricKey)
2889
- throw new Error('[did-provider-cheqd]: revocation: encryptedSymmetricKey is required, if status list 2021 is encrypted');
2890
3547
  // fetch status list 2021 inscribed in credential
2891
3548
  const statusList2021 = options?.topArgs?.fetchList
2892
3549
  ? (await async function () {
@@ -2896,14 +3553,27 @@ export class Cheqd {
2896
3553
  ? publishedList.StatusList2021.encodedList
2897
3554
  : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding), 'base64url');
2898
3555
  // otherwise, decrypt and return bitstring
2899
- const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding));
3556
+ const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex'));
2900
3557
  // instantiate dkg-threshold client, in which case lit-protocol is used
2901
3558
  const lit = await LitProtocol.create({
2902
- chain: options?.topArgs?.bootstrapOptions?.chain,
2903
- litNetwork: options?.topArgs?.bootstrapOptions?.litNetwork
3559
+ chain: options?.topArgs?.dkgOptions?.chain,
3560
+ litNetwork: options?.topArgs?.dkgOptions?.network
2904
3561
  });
3562
+ // construct access control conditions
3563
+ const unifiedAccessControlConditions = await Promise.all(publishedList.metadata.paymentConditions.map(async (condition) => {
3564
+ switch (condition.type) {
3565
+ case AccessControlConditionTypes.timelockPayment:
3566
+ return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({
3567
+ key: '$.tx_responses.*.timestamp',
3568
+ comparator: '<=',
3569
+ value: `${condition.intervalInSeconds}`,
3570
+ }, condition.feePaymentAmount, condition.feePaymentAddress, condition?.blockHeight, options?.topArgs?.dkgOptions?.chain);
3571
+ default:
3572
+ throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`);
3573
+ }
3574
+ }));
2905
3575
  // decrypt
2906
- return await lit.decrypt(scopedRawBlob, options?.topArgs?.encryptedSymmetricKey, options?.topArgs?.decryptionOptions?.unifiedAccessControlConditions);
3576
+ return await lit.decrypt(scopedRawBlob, publishedList.metadata.encryptedSymmetricKey, unifiedAccessControlConditions);
2907
3577
  }())
2908
3578
  : (await async function () {
2909
3579
  // transcode to base64url, if needed
@@ -2918,7 +3588,7 @@ export class Cheqd {
2918
3588
  const encoded = new StatusList({ buffer: await Cheqd.getFile(options.statusListFile) }).encode();
2919
3589
  // validate against published list
2920
3590
  if (encoded !== publishedListTranscoded)
2921
- throw new Error('[did-provider-cheqd]: revocation: statusListFile does not match published status list 2021');
3591
+ throw new Error('[did-provider-cheqd]: check: suspension: statusListFile does not match published status list 2021');
2922
3592
  // return encoded
2923
3593
  return encoded;
2924
3594
  }
@@ -2928,15 +3598,15 @@ export class Cheqd {
2928
3598
  const decrypted = await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex'));
2929
3599
  // validate against published list
2930
3600
  if (decrypted !== publishedListTranscoded)
2931
- throw new Error('[did-provider-cheqd]: revocation: statusListFile does not match published status list 2021');
3601
+ throw new Error('[did-provider-cheqd]: check: suspension: statusListFile does not match published status list 2021');
2932
3602
  // return decrypted
2933
3603
  return decrypted;
2934
3604
  }
2935
3605
  if (!options?.statusListInlineBitstring)
2936
- throw new Error('[did-provider-cheqd]: revocation: statusListInlineBitstring is required, if statusListFile is not provided');
3606
+ throw new Error('[did-provider-cheqd]: check: suspension: statusListInlineBitstring is required, if statusListFile is not provided');
2937
3607
  // validate against published list
2938
3608
  if (options?.statusListInlineBitstring !== publishedListTranscoded)
2939
- throw new Error('[did-provider-cheqd]: revocation: statusListInlineBitstring does not match published status list 2021');
3609
+ throw new Error('[did-provider-cheqd]: check: suspension: statusListInlineBitstring does not match published status list 2021');
2940
3610
  // otherwise, read from inline bitstring
2941
3611
  return options?.statusListInlineBitstring;
2942
3612
  }());