diff --git a/.github/linters/.commitlint.rules.cjs b/.github/linters/.commitlint.rules.cjs deleted file mode 100644 index cc73a801..00000000 --- a/.github/linters/.commitlint.rules.cjs +++ /dev/null @@ -1,37 +0,0 @@ -module.exports = { - rules: { - 'type-case': [2, 'always', 'lower-case'], - 'type-empty': [2, 'never'], - 'type-enum': [ - 2, - 'always', - [ - 'build', - 'chore', - 'ci', - 'docs', - 'feat', - 'fix', - 'perf', - 'refactor', - 'revert', - 'style', - 'test', - ], - ], - 'scope-case': [2, 'always', 'lower-case'], - 'scope-empty': [1, 'never'], - 'subject-case': [ - 2, - 'always', - ['sentence-case'], - ], - 'subject-empty': [1, 'never'], - 'subject-full-stop': [1, 'never', '.'], - 'header-max-length': [2, 'always', 100], - 'body-leading-blank': [1, 'always'], - 'body-max-line-length': [2, 'always', 1000], - 'footer-leading-blank': [1, 'always'], - 'footer-max-line-length': [2, 'always', 100], - }, -}; diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 00000000..5c36adc4 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,9 @@ +{ + "useTabs": true, + "tabWidth": 4, + "printWidth": 120, + "proseWrap": "always", + "semi": true, + "singleQuote": true, + "trailingComma": "es5" +} diff --git a/.releaserc.json b/.releaserc.json index 8ff8e688..fd265153 100644 --- a/.releaserc.json +++ b/.releaserc.json @@ -13,7 +13,6 @@ "plugins": [ [ "@semantic-release/commit-analyzer", { - "parserOpts": "./.github/linters/.commitlint.rules.js", "releaseRules": [ { "breaking": true, "release": "major" }, { "type": "feat", "release": "minor" }, diff --git a/examples/resource/sample-resource.json b/examples/resource/sample-resource.json index f8958cf4..6cce575a 100644 --- a/examples/resource/sample-resource.json +++ b/examples/resource/sample-resource.json @@ -1,30 +1,30 @@ { - "kms": "local", - "network": "testnet", - "payload": { - "id": "cd139d6e-6f21-4dd9-88f9-9408cd0f89ef", - "collectionId": "ae87a2f4-2f9d-4c1d-9c76-fd0408365304", - "name": "sample json resource", - "version": "1.0.0", - "resourceType": "SampleResource", - "alsoKnownAs": [], - "data": "eyJzYW1wbGUiOiJqc29uIn0" - }, - "file": "README.md", - "signInputs": [ - { - "verificationMethodId": "did:cheqd:testnet:ae87a2f4-2f9d-4c1d-9c76-fd0408365304#key-1", - "privateKeyHex": "19ac6832932dfbc38f083a53387c21732ebcc3380259792cf45cfb516c70c5c5d72064f43f8ab7cb189c0d163e2fbba5c7002c1c265764f84e5752cd1d2d4492", - "keyType": "Ed25519" - } - ], - "fee": { - "amount": [ - { - "denom": "ncheq", - "amount": "10000000000" - } - ], - "gas": "800000" - } + "kms": "local", + "network": "testnet", + "payload": { + "id": "cd139d6e-6f21-4dd9-88f9-9408cd0f89ef", + "collectionId": "ae87a2f4-2f9d-4c1d-9c76-fd0408365304", + "name": "sample json resource", + "version": "1.0.0", + "resourceType": "SampleResource", + "alsoKnownAs": [], + "data": "eyJzYW1wbGUiOiJqc29uIn0" + }, + "file": "README.md", + "signInputs": [ + { + "verificationMethodId": "did:cheqd:testnet:ae87a2f4-2f9d-4c1d-9c76-fd0408365304#key-1", + "privateKeyHex": "19ac6832932dfbc38f083a53387c21732ebcc3380259792cf45cfb516c70c5c5d72064f43f8ab7cb189c0d163e2fbba5c7002c1c265764f84e5752cd1d2d4492", + "keyType": "Ed25519" + } + ], + "fee": { + "amount": [ + { + "denom": "ncheq", + "amount": "10000000000" + } + ], + "gas": "800000" + } } diff --git a/examples/status-list-2021/create/create-list-encrypted-revocation.json b/examples/status-list-2021/create/create-list-encrypted-revocation.json index 621d5417..1f5b481f 100644 --- a/examples/status-list-2021/create/create-list-encrypted-revocation.json +++ b/examples/status-list-2021/create/create-list-encrypted-revocation.json @@ -1,20 +1,20 @@ { - "kms": "local", - "issuerDid": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99", - "statusListName": "revocation-list-encrypted-inverse-timelock", - "statusPurpose": "revocation", - "encrypted": true, - "paymentConditions": [ - { - "type": "timelockPayment", - "feePaymentAddress": "cheqd1xl5wccz667lk06ahama26pdqvrkz5aws6m0ztp", - "feePaymentAmount": "147603000000000ncheq", - "intervalInSeconds": 3153600000 - } - ], - "returnSymmetricKey": true, - "dkgOptions": { - "chain": "cheqdMainnet", - "network": "localhost" - } + "kms": "local", + "issuerDid": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99", + "statusListName": "revocation-list-encrypted-inverse-timelock", + "statusPurpose": "revocation", + "encrypted": true, + "paymentConditions": [ + { + "type": "timelockPayment", + "feePaymentAddress": "cheqd1xl5wccz667lk06ahama26pdqvrkz5aws6m0ztp", + "feePaymentAmount": "147603000000000ncheq", + "intervalInSeconds": 3153600000 + } + ], + "returnSymmetricKey": true, + "dkgOptions": { + "chain": "cheqdMainnet", + "network": "localhost" + } } diff --git a/examples/status-list-2021/create/create-list-encrypted-suspension.json b/examples/status-list-2021/create/create-list-encrypted-suspension.json index 5f69eff9..a433621a 100644 --- a/examples/status-list-2021/create/create-list-encrypted-suspension.json +++ b/examples/status-list-2021/create/create-list-encrypted-suspension.json @@ -1,20 +1,20 @@ { - "kms": "local", - "issuerDid": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99", - "statusListName": "suspension-list-encrypted-inverse-timelock", - "statusPurpose": "suspension", - "encrypted": true, - "paymentConditions": [ - { - "type": "timelockPayment", - "feePaymentAddress": "cheqd1xl5wccz667lk06ahama26pdqvrkz5aws6m0ztp", - "feePaymentAmount": "147603000000000ncheq", - "intervalInSeconds": 3153600000 - } - ], - "returnSymmetricKey": true, - "dkgOptions": { - "chain": "cheqdMainnet", - "network": "localhost" - } + "kms": "local", + "issuerDid": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99", + "statusListName": "suspension-list-encrypted-inverse-timelock", + "statusPurpose": "suspension", + "encrypted": true, + "paymentConditions": [ + { + "type": "timelockPayment", + "feePaymentAddress": "cheqd1xl5wccz667lk06ahama26pdqvrkz5aws6m0ztp", + "feePaymentAmount": "147603000000000ncheq", + "intervalInSeconds": 3153600000 + } + ], + "returnSymmetricKey": true, + "dkgOptions": { + "chain": "cheqdMainnet", + "network": "localhost" + } } diff --git a/examples/status-list-2021/create/create-list-unencrypted.json b/examples/status-list-2021/create/create-list-unencrypted.json index fee15fab..39e22167 100644 --- a/examples/status-list-2021/create/create-list-unencrypted.json +++ b/examples/status-list-2021/create/create-list-unencrypted.json @@ -1,7 +1,7 @@ { - "kms": "local", - "issuerDid": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99", - "statusListName": "suspension-list-unencrypted-modifications", - "statusPurpose": "suspension", - "encrypted": false + "kms": "local", + "issuerDid": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99", + "statusListName": "suspension-list-unencrypted-modifications", + "statusPurpose": "suspension", + "encrypted": false } diff --git a/examples/status-list-2021/create/observe-verifier-pays-issuer.json b/examples/status-list-2021/create/observe-verifier-pays-issuer.json index 36d05eb5..fa8b975d 100644 --- a/examples/status-list-2021/create/observe-verifier-pays-issuer.json +++ b/examples/status-list-2021/create/observe-verifier-pays-issuer.json @@ -1,21 +1,21 @@ { - "senderAddress": "cheqd12248whff96tpfyqm2vyvf9k4wda9h2dhdkf2e4", - "recipientAddress": "cheqd1nms3yx4fx2c7e543a5v9cymuce4hh36fuad5t5", - "amount": { - "amount": "1000000000", - "denom": "ncheq" - }, - "memoNonce": "7b3ef487-c567-4e30-9cc6-35dde6870d87", - "network": "mainnet", - "unifiedAccessControlCondition": { - "conditionType": "cosmos", - "path": "/cosmos/tx/v1beta1/txs?events=transfer.recipient='cheqd1nms3yx4fx2c7e543a5v9cymuce4hh36fuad5t5'&events=transfer.sender='cheqd12248whff96tpfyqm2vyvf9k4wda9h2dhdkf2e4'&events=transfer.amount='1000000000ncheq'&order_by=2", - "chain": "cheqdMainnet", - "returnValueTest": { - "key": "$.txs.*.body.memo", - "comparator": "contains", - "value": "7b3ef487-c567-4e30-9cc6-35dde6870d87" - } - }, - "returnTxResponse": true + "senderAddress": "cheqd12248whff96tpfyqm2vyvf9k4wda9h2dhdkf2e4", + "recipientAddress": "cheqd1nms3yx4fx2c7e543a5v9cymuce4hh36fuad5t5", + "amount": { + "amount": "1000000000", + "denom": "ncheq" + }, + "memoNonce": "7b3ef487-c567-4e30-9cc6-35dde6870d87", + "network": "mainnet", + "unifiedAccessControlCondition": { + "conditionType": "cosmos", + "path": "/cosmos/tx/v1beta1/txs?events=transfer.recipient='cheqd1nms3yx4fx2c7e543a5v9cymuce4hh36fuad5t5'&events=transfer.sender='cheqd12248whff96tpfyqm2vyvf9k4wda9h2dhdkf2e4'&events=transfer.amount='1000000000ncheq'&order_by=2", + "chain": "cheqdMainnet", + "returnValueTest": { + "key": "$.txs.*.body.memo", + "comparator": "contains", + "value": "7b3ef487-c567-4e30-9cc6-35dde6870d87" + } + }, + "returnTxResponse": true } diff --git a/examples/status-list-2021/credential/issue/issue-credential-revocable.json b/examples/status-list-2021/credential/issue/issue-credential-revocable.json index 64826ac2..070fc507 100644 --- a/examples/status-list-2021/credential/issue/issue-credential-revocable.json +++ b/examples/status-list-2021/credential/issue/issue-credential-revocable.json @@ -1,24 +1,19 @@ { - "issuanceOptions": { - "credential": { - "credentialSubject": { - "name": "eengineer1", - "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" - }, - "issuer": { - "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" - }, - "type": [ - "VerifiableCredential" - ], - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://veramo.io/contexts/profile/v1" - ] - } - }, - "statusOptions": { - "statusPurpose": "revocation", - "statusListName": "revocation-list-unencrypted-session" - } + "issuanceOptions": { + "credential": { + "credentialSubject": { + "name": "eengineer1", + "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" + }, + "issuer": { + "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" + }, + "type": ["VerifiableCredential"], + "@context": ["https://www.w3.org/2018/credentials/v1", "https://veramo.io/contexts/profile/v1"] + } + }, + "statusOptions": { + "statusPurpose": "revocation", + "statusListName": "revocation-list-unencrypted-session" + } } diff --git a/examples/status-list-2021/credential/issue/issue-credential-suspendable.json b/examples/status-list-2021/credential/issue/issue-credential-suspendable.json index 9c46a316..9bb3611f 100644 --- a/examples/status-list-2021/credential/issue/issue-credential-suspendable.json +++ b/examples/status-list-2021/credential/issue/issue-credential-suspendable.json @@ -1,24 +1,19 @@ { - "issuanceOptions": { - "credential": { - "credentialSubject": { - "name": "eengineer1", - "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" - }, - "issuer": { - "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" - }, - "type": [ - "VerifiableCredential" - ], - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://veramo.io/contexts/profile/v1" - ] - } - }, - "statusOptions": { - "statusPurpose": "suspension", - "statusListName": "suspension-list-unencrypted-sandbox" - } + "issuanceOptions": { + "credential": { + "credentialSubject": { + "name": "eengineer1", + "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" + }, + "issuer": { + "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" + }, + "type": ["VerifiableCredential"], + "@context": ["https://www.w3.org/2018/credentials/v1", "https://veramo.io/contexts/profile/v1"] + } + }, + "statusOptions": { + "statusPurpose": "suspension", + "statusListName": "suspension-list-unencrypted-sandbox" + } } diff --git a/examples/status-list-2021/credential/update-status/revoke-bulk-credentials-unencrypted.json b/examples/status-list-2021/credential/update-status/revoke-bulk-credentials-unencrypted.json index dd8b8653..f51929ce 100644 --- a/examples/status-list-2021/credential/update-status/revoke-bulk-credentials-unencrypted.json +++ b/examples/status-list-2021/credential/update-status/revoke-bulk-credentials-unencrypted.json @@ -1,64 +1,60 @@ { - "credentials": [ - { - "credentialSubject": { - "name": "eengineer1", - "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" - }, - "issuer": { - "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" - }, - "type": [ - "VerifiableCredential" - ], - "credentialStatus": { - "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=revocation-list-unencrypted-modifications&resourceType=StatusList2021Revocation#30519", - "type": "StatusList2021Entry", - "statusPurpose": "revocation", - "statusListIndex": "30519" - }, - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://veramo.io/contexts/profile/v1", - "https://w3id.org/vc-status-list-2021/v1" - ], - "issuanceDate": "2023-06-20T19:30:16.000Z", - "proof": { - "type": "JwtProof2020", - "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9cmV2b2NhdGlvbi1saXN0LXVuZW5jcnlwdGVkLW1vZGlmaWNhdGlvbnMmcmVzb3VyY2VUeXBlPVN0YXR1c0xpc3QyMDIxUmV2b2NhdGlvbiMzMDUxOSIsInR5cGUiOiJTdGF0dXNMaXN0MjAyMUVudHJ5Iiwic3RhdHVzUHVycG9zZSI6InJldm9jYXRpb24iLCJzdGF0dXNMaXN0SW5kZXgiOiIzMDUxOSJ9fSwic3ViIjoiZGlkOmtleTp6Nk1rdkc0ZHBLVnBZd1lxbndjalJkdzhWWjNrbTRTaXNneG0xaWdhUENGenNreGUiLCJuYmYiOjE2ODcyODk0MTYsImlzcyI6ImRpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OSJ9.rHSMrg1AmrVhYhz3Qc3y9OGmT54L8D7J86rEGqN9ntWyDqIaUvigmELkJbuDkhuf3G2kazdo1I_zHJ8g7ckICg" - } - }, - { - "credentialSubject": { - "name": "eengineer1", - "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" - }, - "issuer": { - "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" - }, - "type": [ - "VerifiableCredential" - ], - "credentialStatus": { - "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=revocation-list-unencrypted-modifications&resourceType=StatusList2021Revocation#111881", - "type": "StatusList2021Entry", - "statusPurpose": "revocation", - "statusListIndex": "111881" - }, - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://veramo.io/contexts/profile/v1", - "https://w3id.org/vc-status-list-2021/v1" - ], - "issuanceDate": "2023-06-20T19:28:26.000Z", - "proof": { - "type": "JwtProof2020", - "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9cmV2b2NhdGlvbi1saXN0LXVuZW5jcnlwdGVkLW1vZGlmaWNhdGlvbnMmcmVzb3VyY2VUeXBlPVN0YXR1c0xpc3QyMDIxUmV2b2NhdGlvbiMxMTE4ODEiLCJ0eXBlIjoiU3RhdHVzTGlzdDIwMjFFbnRyeSIsInN0YXR1c1B1cnBvc2UiOiJyZXZvY2F0aW9uIiwic3RhdHVzTGlzdEluZGV4IjoiMTExODgxIn19LCJzdWIiOiJkaWQ6a2V5Ono2TWt2RzRkcEtWcFl3WXFud2NqUmR3OFZaM2ttNFNpc2d4bTFpZ2FQQ0Z6c2t4ZSIsIm5iZiI6MTY4NzI4OTMwNiwiaXNzIjoiZGlkOmNoZXFkOnRlc3RuZXQ6MzIyNzYxZWEtNTg3ZC00NTRhLWE5NTUtNzQ1MjAwMzAxYjk5In0.jSw84dqNlPc3hvwx1tM-icmuYNoSqUARAmGClaMeT9GUd0aWcCxsyCGbtjk8Th6nZYdboF4R3IF9n4wjnDfcDQ" - } - } - ], - "fetchList": true, - "publish": true, - "returnUpdatedStatusList": true, - "returnStatusListMetadata": true + "credentials": [ + { + "credentialSubject": { + "name": "eengineer1", + "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" + }, + "issuer": { + "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" + }, + "type": ["VerifiableCredential"], + "credentialStatus": { + "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=revocation-list-unencrypted-modifications&resourceType=StatusList2021Revocation#30519", + "type": "StatusList2021Entry", + "statusPurpose": "revocation", + "statusListIndex": "30519" + }, + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://veramo.io/contexts/profile/v1", + "https://w3id.org/vc-status-list-2021/v1" + ], + "issuanceDate": "2023-06-20T19:30:16.000Z", + "proof": { + "type": "JwtProof2020", + "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9cmV2b2NhdGlvbi1saXN0LXVuZW5jcnlwdGVkLW1vZGlmaWNhdGlvbnMmcmVzb3VyY2VUeXBlPVN0YXR1c0xpc3QyMDIxUmV2b2NhdGlvbiMzMDUxOSIsInR5cGUiOiJTdGF0dXNMaXN0MjAyMUVudHJ5Iiwic3RhdHVzUHVycG9zZSI6InJldm9jYXRpb24iLCJzdGF0dXNMaXN0SW5kZXgiOiIzMDUxOSJ9fSwic3ViIjoiZGlkOmtleTp6Nk1rdkc0ZHBLVnBZd1lxbndjalJkdzhWWjNrbTRTaXNneG0xaWdhUENGenNreGUiLCJuYmYiOjE2ODcyODk0MTYsImlzcyI6ImRpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OSJ9.rHSMrg1AmrVhYhz3Qc3y9OGmT54L8D7J86rEGqN9ntWyDqIaUvigmELkJbuDkhuf3G2kazdo1I_zHJ8g7ckICg" + } + }, + { + "credentialSubject": { + "name": "eengineer1", + "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" + }, + "issuer": { + "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" + }, + "type": ["VerifiableCredential"], + "credentialStatus": { + "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=revocation-list-unencrypted-modifications&resourceType=StatusList2021Revocation#111881", + "type": "StatusList2021Entry", + "statusPurpose": "revocation", + "statusListIndex": "111881" + }, + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://veramo.io/contexts/profile/v1", + "https://w3id.org/vc-status-list-2021/v1" + ], + "issuanceDate": "2023-06-20T19:28:26.000Z", + "proof": { + "type": "JwtProof2020", + "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9cmV2b2NhdGlvbi1saXN0LXVuZW5jcnlwdGVkLW1vZGlmaWNhdGlvbnMmcmVzb3VyY2VUeXBlPVN0YXR1c0xpc3QyMDIxUmV2b2NhdGlvbiMxMTE4ODEiLCJ0eXBlIjoiU3RhdHVzTGlzdDIwMjFFbnRyeSIsInN0YXR1c1B1cnBvc2UiOiJyZXZvY2F0aW9uIiwic3RhdHVzTGlzdEluZGV4IjoiMTExODgxIn19LCJzdWIiOiJkaWQ6a2V5Ono2TWt2RzRkcEtWcFl3WXFud2NqUmR3OFZaM2ttNFNpc2d4bTFpZ2FQQ0Z6c2t4ZSIsIm5iZiI6MTY4NzI4OTMwNiwiaXNzIjoiZGlkOmNoZXFkOnRlc3RuZXQ6MzIyNzYxZWEtNTg3ZC00NTRhLWE5NTUtNzQ1MjAwMzAxYjk5In0.jSw84dqNlPc3hvwx1tM-icmuYNoSqUARAmGClaMeT9GUd0aWcCxsyCGbtjk8Th6nZYdboF4R3IF9n4wjnDfcDQ" + } + } + ], + "fetchList": true, + "publish": true, + "returnUpdatedStatusList": true, + "returnStatusListMetadata": true } diff --git a/examples/status-list-2021/credential/update-status/revoke-credential-encrypted.json b/examples/status-list-2021/credential/update-status/revoke-credential-encrypted.json index b624c8c3..1829ff41 100644 --- a/examples/status-list-2021/credential/update-status/revoke-credential-encrypted.json +++ b/examples/status-list-2021/credential/update-status/revoke-credential-encrypted.json @@ -1,41 +1,39 @@ { - "credential": { - "credentialSubject": { - "name": "eengineer1", - "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" - }, - "issuer": { - "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" - }, - "type": [ - "VerifiableCredential" - ], - "credentialStatus": { - "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=revocation-list-encrypted-inverse-timelock&resourceType=StatusList2021Revocation#79444", - "type": "StatusList2021Entry", - "statusPurpose": "revocation", - "statusListIndex": "79444" - }, - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://veramo.io/contexts/profile/v1", - "https://w3id.org/vc-status-list-2021/v1" - ], - "issuanceDate": "2023-06-30T10:04:01.000Z", - "proof": { - "type": "JwtProof2020", - "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9cmV2b2NhdGlvbi1saXN0LWVuY3J5cHRlZC1pbnZlcnNlLXRpbWVsb2NrJnJlc291cmNlVHlwZT1TdGF0dXNMaXN0MjAyMVJldm9jYXRpb24jNzk0NDQiLCJ0eXBlIjoiU3RhdHVzTGlzdDIwMjFFbnRyeSIsInN0YXR1c1B1cnBvc2UiOiJyZXZvY2F0aW9uIiwic3RhdHVzTGlzdEluZGV4IjoiNzk0NDQifX0sInN1YiI6ImRpZDprZXk6ejZNa3ZHNGRwS1ZwWXdZcW53Y2pSZHc4Vloza200U2lzZ3htMWlnYVBDRnpza3hlIiwibmJmIjoxNjg4MTE5NDQxLCJpc3MiOiJkaWQ6Y2hlcWQ6dGVzdG5ldDozMjI3NjFlYS01ODdkLTQ1NGEtYTk1NS03NDUyMDAzMDFiOTkifQ.fWCayAbmL4qivVfMz97o6tEFN2W411OFaF3wgZAb5gqoBkJ1uoV2Cp_31LI60ehSPXcDXiIUmDJbFP3S8bnNDg" - } - }, - "symmetricKey": "802648911be3e6cf2580c007805eda9529f358a42a68dfe59f2ed87eea6e1a33", - "fetchList": true, - "publish": true, - "publishEncrypted": true, - "returnUpdatedStatusList": true, - "returnSymmetricKey": true, - "returnStatusListMetadata": true, - "dkgOptions": { - "chain": "cheqdMainnet", - "network": "localhost" - } + "credential": { + "credentialSubject": { + "name": "eengineer1", + "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" + }, + "issuer": { + "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" + }, + "type": ["VerifiableCredential"], + "credentialStatus": { + "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=revocation-list-encrypted-inverse-timelock&resourceType=StatusList2021Revocation#79444", + "type": "StatusList2021Entry", + "statusPurpose": "revocation", + "statusListIndex": "79444" + }, + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://veramo.io/contexts/profile/v1", + "https://w3id.org/vc-status-list-2021/v1" + ], + "issuanceDate": "2023-06-30T10:04:01.000Z", + "proof": { + "type": "JwtProof2020", + "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9cmV2b2NhdGlvbi1saXN0LWVuY3J5cHRlZC1pbnZlcnNlLXRpbWVsb2NrJnJlc291cmNlVHlwZT1TdGF0dXNMaXN0MjAyMVJldm9jYXRpb24jNzk0NDQiLCJ0eXBlIjoiU3RhdHVzTGlzdDIwMjFFbnRyeSIsInN0YXR1c1B1cnBvc2UiOiJyZXZvY2F0aW9uIiwic3RhdHVzTGlzdEluZGV4IjoiNzk0NDQifX0sInN1YiI6ImRpZDprZXk6ejZNa3ZHNGRwS1ZwWXdZcW53Y2pSZHc4Vloza200U2lzZ3htMWlnYVBDRnpza3hlIiwibmJmIjoxNjg4MTE5NDQxLCJpc3MiOiJkaWQ6Y2hlcWQ6dGVzdG5ldDozMjI3NjFlYS01ODdkLTQ1NGEtYTk1NS03NDUyMDAzMDFiOTkifQ.fWCayAbmL4qivVfMz97o6tEFN2W411OFaF3wgZAb5gqoBkJ1uoV2Cp_31LI60ehSPXcDXiIUmDJbFP3S8bnNDg" + } + }, + "symmetricKey": "802648911be3e6cf2580c007805eda9529f358a42a68dfe59f2ed87eea6e1a33", + "fetchList": true, + "publish": true, + "publishEncrypted": true, + "returnUpdatedStatusList": true, + "returnSymmetricKey": true, + "returnStatusListMetadata": true, + "dkgOptions": { + "chain": "cheqdMainnet", + "network": "localhost" + } } diff --git a/examples/status-list-2021/credential/update-status/revoke-credential-unencrypted.json b/examples/status-list-2021/credential/update-status/revoke-credential-unencrypted.json index 6b5ebed6..28096ab7 100644 --- a/examples/status-list-2021/credential/update-status/revoke-credential-unencrypted.json +++ b/examples/status-list-2021/credential/update-status/revoke-credential-unencrypted.json @@ -1,35 +1,33 @@ { - "credential": { - "credentialSubject": { - "name": "eengineer1", - "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" - }, - "issuer": { - "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" - }, - "type": [ - "VerifiableCredential" - ], - "credentialStatus": { - "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=revocation-list-unencrypted-session&resourceType=StatusList2021#40824", - "type": "StatusList2021Entry", - "statusPurpose": "revocation", - "statusListIndex": "40824", - "statusListCredential": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?jwtProof2020=eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9cmV2b2NhdGlvbi1saXN0LXVuZW5jcnlwdGVkLXNlc3Npb24jNDA4MjQiLCJ0eXBlIjoiU3RhdHVzTGlzdDIwMjFFbnRyeSIsInN0YXR1c1B1cnBvc2UiOiJyZXZvY2F0aW9uIiwic3RhdHVzTGlzdEluZGV4IjoiNDA4MjQiLCJzdGF0dXNMaXN0Q3JlZGVudGlhbCI6Imh0dHBzOi8vcmVzb2x2ZXIuY2hlcWQubmV0LzEuMC9pZGVudGlmaWVycy9kaWQ6Y2hlcWQ6dGVzdG5ldDozMjI3NjFlYS01ODdkLTQ1NGEtYTk1NS03NDUyMDAzMDFiOTk_cmVzb3VyY2VOYW1lPXJldm9jYXRpb24tbGlzdC11bmVuY3J5cHRlZC1zZXNzaW9uIn19LCJzdWIiOiJkaWQ6a2V5Ono2TWt2RzRkcEtWcFl3WXFud2NqUmR3OFZaM2ttNFNpc2d4bTFpZ2FQQ0Z6c2t4ZSIsIm5iZiI6MTY4NjY2ODExOCwiaXNzIjoiZGlkOmNoZXFkOnRlc3RuZXQ6MzIyNzYxZWEtNTg3ZC00NTRhLWE5NTUtNzQ1MjAwMzAxYjk5In0.ElzndiaYWUnEDfavjV_CVyTDGnwk3OfOUtrQJYzdIPNrhvBD5B0Zev_m3NUqm37co-WRSQyMz_Jdwomw9sf-Bw" - }, - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://veramo.io/contexts/profile/v1", - "https://w3id.org/vc-status-list-2021/v1" - ], - "issuanceDate": "2023-06-13T14:55:18.000Z", - "proof": { - "type": "JwtProof2020", - "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9cmV2b2NhdGlvbi1saXN0LXVuZW5jcnlwdGVkLXNlc3Npb24jNDA4MjQiLCJ0eXBlIjoiU3RhdHVzTGlzdDIwMjFFbnRyeSIsInN0YXR1c1B1cnBvc2UiOiJyZXZvY2F0aW9uIiwic3RhdHVzTGlzdEluZGV4IjoiNDA4MjQiLCJzdGF0dXNMaXN0Q3JlZGVudGlhbCI6Imh0dHBzOi8vcmVzb2x2ZXIuY2hlcWQubmV0LzEuMC9pZGVudGlmaWVycy9kaWQ6Y2hlcWQ6dGVzdG5ldDozMjI3NjFlYS01ODdkLTQ1NGEtYTk1NS03NDUyMDAzMDFiOTk_cmVzb3VyY2VOYW1lPXJldm9jYXRpb24tbGlzdC11bmVuY3J5cHRlZC1zZXNzaW9uIn19LCJzdWIiOiJkaWQ6a2V5Ono2TWt2RzRkcEtWcFl3WXFud2NqUmR3OFZaM2ttNFNpc2d4bTFpZ2FQQ0Z6c2t4ZSIsIm5iZiI6MTY4NjY2ODExOCwiaXNzIjoiZGlkOmNoZXFkOnRlc3RuZXQ6MzIyNzYxZWEtNTg3ZC00NTRhLWE5NTUtNzQ1MjAwMzAxYjk5In0.ElzndiaYWUnEDfavjV_CVyTDGnwk3OfOUtrQJYzdIPNrhvBD5B0Zev_m3NUqm37co-WRSQyMz_Jdwomw9sf-Bw" - } - }, - "fetchList": true, - "publish": true, - "returnUpdatedStatusList": true, - "returnStatusListMetadata": true + "credential": { + "credentialSubject": { + "name": "eengineer1", + "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" + }, + "issuer": { + "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" + }, + "type": ["VerifiableCredential"], + "credentialStatus": { + "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=revocation-list-unencrypted-session&resourceType=StatusList2021#40824", + "type": "StatusList2021Entry", + "statusPurpose": "revocation", + "statusListIndex": "40824", + "statusListCredential": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?jwtProof2020=eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9cmV2b2NhdGlvbi1saXN0LXVuZW5jcnlwdGVkLXNlc3Npb24jNDA4MjQiLCJ0eXBlIjoiU3RhdHVzTGlzdDIwMjFFbnRyeSIsInN0YXR1c1B1cnBvc2UiOiJyZXZvY2F0aW9uIiwic3RhdHVzTGlzdEluZGV4IjoiNDA4MjQiLCJzdGF0dXNMaXN0Q3JlZGVudGlhbCI6Imh0dHBzOi8vcmVzb2x2ZXIuY2hlcWQubmV0LzEuMC9pZGVudGlmaWVycy9kaWQ6Y2hlcWQ6dGVzdG5ldDozMjI3NjFlYS01ODdkLTQ1NGEtYTk1NS03NDUyMDAzMDFiOTk_cmVzb3VyY2VOYW1lPXJldm9jYXRpb24tbGlzdC11bmVuY3J5cHRlZC1zZXNzaW9uIn19LCJzdWIiOiJkaWQ6a2V5Ono2TWt2RzRkcEtWcFl3WXFud2NqUmR3OFZaM2ttNFNpc2d4bTFpZ2FQQ0Z6c2t4ZSIsIm5iZiI6MTY4NjY2ODExOCwiaXNzIjoiZGlkOmNoZXFkOnRlc3RuZXQ6MzIyNzYxZWEtNTg3ZC00NTRhLWE5NTUtNzQ1MjAwMzAxYjk5In0.ElzndiaYWUnEDfavjV_CVyTDGnwk3OfOUtrQJYzdIPNrhvBD5B0Zev_m3NUqm37co-WRSQyMz_Jdwomw9sf-Bw" + }, + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://veramo.io/contexts/profile/v1", + "https://w3id.org/vc-status-list-2021/v1" + ], + "issuanceDate": "2023-06-13T14:55:18.000Z", + "proof": { + "type": "JwtProof2020", + "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9cmV2b2NhdGlvbi1saXN0LXVuZW5jcnlwdGVkLXNlc3Npb24jNDA4MjQiLCJ0eXBlIjoiU3RhdHVzTGlzdDIwMjFFbnRyeSIsInN0YXR1c1B1cnBvc2UiOiJyZXZvY2F0aW9uIiwic3RhdHVzTGlzdEluZGV4IjoiNDA4MjQiLCJzdGF0dXNMaXN0Q3JlZGVudGlhbCI6Imh0dHBzOi8vcmVzb2x2ZXIuY2hlcWQubmV0LzEuMC9pZGVudGlmaWVycy9kaWQ6Y2hlcWQ6dGVzdG5ldDozMjI3NjFlYS01ODdkLTQ1NGEtYTk1NS03NDUyMDAzMDFiOTk_cmVzb3VyY2VOYW1lPXJldm9jYXRpb24tbGlzdC11bmVuY3J5cHRlZC1zZXNzaW9uIn19LCJzdWIiOiJkaWQ6a2V5Ono2TWt2RzRkcEtWcFl3WXFud2NqUmR3OFZaM2ttNFNpc2d4bTFpZ2FQQ0Z6c2t4ZSIsIm5iZiI6MTY4NjY2ODExOCwiaXNzIjoiZGlkOmNoZXFkOnRlc3RuZXQ6MzIyNzYxZWEtNTg3ZC00NTRhLWE5NTUtNzQ1MjAwMzAxYjk5In0.ElzndiaYWUnEDfavjV_CVyTDGnwk3OfOUtrQJYzdIPNrhvBD5B0Zev_m3NUqm37co-WRSQyMz_Jdwomw9sf-Bw" + } + }, + "fetchList": true, + "publish": true, + "returnUpdatedStatusList": true, + "returnStatusListMetadata": true } diff --git a/examples/status-list-2021/credential/update-status/suspend-bulk-credentials-unencrypted.json b/examples/status-list-2021/credential/update-status/suspend-bulk-credentials-unencrypted.json index 89d8b53f..f29e7080 100644 --- a/examples/status-list-2021/credential/update-status/suspend-bulk-credentials-unencrypted.json +++ b/examples/status-list-2021/credential/update-status/suspend-bulk-credentials-unencrypted.json @@ -1,64 +1,60 @@ { - "credential": [ - { - "credentialSubject": { - "name": "eengineer1", - "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" - }, - "issuer": { - "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" - }, - "type": [ - "VerifiableCredential" - ], - "credentialStatus": { - "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=suspension-list-unencrypted-modifications&resourceType=StatusList2021Suspension#88942", - "type": "StatusList2021Entry", - "statusPurpose": "suspension", - "statusListIndex": "88942" - }, - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://veramo.io/contexts/profile/v1", - "https://w3id.org/vc-status-list-2021/v1" - ], - "issuanceDate": "2023-06-21T13:39:14.000Z", - "proof": { - "type": "JwtProof2020", - "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9c3VzcGVuc2lvbi1saXN0LXVuZW5jcnlwdGVkLW1vZGlmaWNhdGlvbnMmcmVzb3VyY2VUeXBlPVN0YXR1c0xpc3QyMDIxU3VzcGVuc2lvbiM4ODk0MiIsInR5cGUiOiJTdGF0dXNMaXN0MjAyMUVudHJ5Iiwic3RhdHVzUHVycG9zZSI6InN1c3BlbnNpb24iLCJzdGF0dXNMaXN0SW5kZXgiOiI4ODk0MiJ9fSwic3ViIjoiZGlkOmtleTp6Nk1rdkc0ZHBLVnBZd1lxbndjalJkdzhWWjNrbTRTaXNneG0xaWdhUENGenNreGUiLCJuYmYiOjE2ODczNTQ3NTQsImlzcyI6ImRpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OSJ9.UM7oRC0lspkjDx7oTyBmkVBByvkPtcI_35c4Uhomqlq2L8b-YGoE5TSgF4ePR2PvD7H8goo1X_3UeLHlyeoHDA" - } - }, - { - "credentialSubject": { - "name": "eengineer1", - "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" - }, - "issuer": { - "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" - }, - "type": [ - "VerifiableCredential" - ], - "credentialStatus": { - "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=suspension-list-unencrypted-modifications&resourceType=StatusList2021Suspension#73812", - "type": "StatusList2021Entry", - "statusPurpose": "suspension", - "statusListIndex": "73812" - }, - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://veramo.io/contexts/profile/v1", - "https://w3id.org/vc-status-list-2021/v1" - ], - "issuanceDate": "2023-06-21T13:40:39.000Z", - "proof": { - "type": "JwtProof2020", - "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9c3VzcGVuc2lvbi1saXN0LXVuZW5jcnlwdGVkLW1vZGlmaWNhdGlvbnMmcmVzb3VyY2VUeXBlPVN0YXR1c0xpc3QyMDIxU3VzcGVuc2lvbiM3MzgxMiIsInR5cGUiOiJTdGF0dXNMaXN0MjAyMUVudHJ5Iiwic3RhdHVzUHVycG9zZSI6InN1c3BlbnNpb24iLCJzdGF0dXNMaXN0SW5kZXgiOiI3MzgxMiJ9fSwic3ViIjoiZGlkOmtleTp6Nk1rdkc0ZHBLVnBZd1lxbndjalJkdzhWWjNrbTRTaXNneG0xaWdhUENGenNreGUiLCJuYmYiOjE2ODczNTQ4MzksImlzcyI6ImRpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OSJ9.gPH1Sjqj2rCWAmMPU12S6qYxwE12bV2-eUt9gDhM5u9jL7GCp3F_58Fyd8fqVWAS1Exw2k5FERmHRKkBs763Dg" - } - } - ], - "fetchList": true, - "publish": true, - "returnUpdatedStatusList": true, - "returnStatusListMetadata": true + "credential": [ + { + "credentialSubject": { + "name": "eengineer1", + "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" + }, + "issuer": { + "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" + }, + "type": ["VerifiableCredential"], + "credentialStatus": { + "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=suspension-list-unencrypted-modifications&resourceType=StatusList2021Suspension#88942", + "type": "StatusList2021Entry", + "statusPurpose": "suspension", + "statusListIndex": "88942" + }, + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://veramo.io/contexts/profile/v1", + "https://w3id.org/vc-status-list-2021/v1" + ], + "issuanceDate": "2023-06-21T13:39:14.000Z", + "proof": { + "type": "JwtProof2020", + "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9c3VzcGVuc2lvbi1saXN0LXVuZW5jcnlwdGVkLW1vZGlmaWNhdGlvbnMmcmVzb3VyY2VUeXBlPVN0YXR1c0xpc3QyMDIxU3VzcGVuc2lvbiM4ODk0MiIsInR5cGUiOiJTdGF0dXNMaXN0MjAyMUVudHJ5Iiwic3RhdHVzUHVycG9zZSI6InN1c3BlbnNpb24iLCJzdGF0dXNMaXN0SW5kZXgiOiI4ODk0MiJ9fSwic3ViIjoiZGlkOmtleTp6Nk1rdkc0ZHBLVnBZd1lxbndjalJkdzhWWjNrbTRTaXNneG0xaWdhUENGenNreGUiLCJuYmYiOjE2ODczNTQ3NTQsImlzcyI6ImRpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OSJ9.UM7oRC0lspkjDx7oTyBmkVBByvkPtcI_35c4Uhomqlq2L8b-YGoE5TSgF4ePR2PvD7H8goo1X_3UeLHlyeoHDA" + } + }, + { + "credentialSubject": { + "name": "eengineer1", + "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" + }, + "issuer": { + "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" + }, + "type": ["VerifiableCredential"], + "credentialStatus": { + "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=suspension-list-unencrypted-modifications&resourceType=StatusList2021Suspension#73812", + "type": "StatusList2021Entry", + "statusPurpose": "suspension", + "statusListIndex": "73812" + }, + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://veramo.io/contexts/profile/v1", + "https://w3id.org/vc-status-list-2021/v1" + ], + "issuanceDate": "2023-06-21T13:40:39.000Z", + "proof": { + "type": "JwtProof2020", + "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9c3VzcGVuc2lvbi1saXN0LXVuZW5jcnlwdGVkLW1vZGlmaWNhdGlvbnMmcmVzb3VyY2VUeXBlPVN0YXR1c0xpc3QyMDIxU3VzcGVuc2lvbiM3MzgxMiIsInR5cGUiOiJTdGF0dXNMaXN0MjAyMUVudHJ5Iiwic3RhdHVzUHVycG9zZSI6InN1c3BlbnNpb24iLCJzdGF0dXNMaXN0SW5kZXgiOiI3MzgxMiJ9fSwic3ViIjoiZGlkOmtleTp6Nk1rdkc0ZHBLVnBZd1lxbndjalJkdzhWWjNrbTRTaXNneG0xaWdhUENGenNreGUiLCJuYmYiOjE2ODczNTQ4MzksImlzcyI6ImRpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OSJ9.gPH1Sjqj2rCWAmMPU12S6qYxwE12bV2-eUt9gDhM5u9jL7GCp3F_58Fyd8fqVWAS1Exw2k5FERmHRKkBs763Dg" + } + } + ], + "fetchList": true, + "publish": true, + "returnUpdatedStatusList": true, + "returnStatusListMetadata": true } diff --git a/examples/status-list-2021/credential/update-status/suspend-credential-encrypted.json b/examples/status-list-2021/credential/update-status/suspend-credential-encrypted.json index 75d7d8ba..831e94ef 100644 --- a/examples/status-list-2021/credential/update-status/suspend-credential-encrypted.json +++ b/examples/status-list-2021/credential/update-status/suspend-credential-encrypted.json @@ -1,41 +1,39 @@ { - "credential": { - "credentialSubject": { - "name": "eengineer1", - "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" - }, - "issuer": { - "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" - }, - "type": [ - "VerifiableCredential" - ], - "credentialStatus": { - "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=suspension-list-encrypted-inverse-timelock&resourceType=StatusList2021Suspension#111168", - "type": "StatusList2021Entry", - "statusPurpose": "suspension", - "statusListIndex": "111168" - }, - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://veramo.io/contexts/profile/v1", - "https://w3id.org/vc-status-list-2021/v1" - ], - "issuanceDate": "2023-06-30T11:00:14.000Z", - "proof": { - "type": "JwtProof2020", - "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9c3VzcGVuc2lvbi1saXN0LWVuY3J5cHRlZC1pbnZlcnNlLXRpbWVsb2NrJnJlc291cmNlVHlwZT1TdGF0dXNMaXN0MjAyMVN1c3BlbnNpb24jMTExMTY4IiwidHlwZSI6IlN0YXR1c0xpc3QyMDIxRW50cnkiLCJzdGF0dXNQdXJwb3NlIjoic3VzcGVuc2lvbiIsInN0YXR1c0xpc3RJbmRleCI6IjExMTE2OCJ9fSwic3ViIjoiZGlkOmtleTp6Nk1rdkc0ZHBLVnBZd1lxbndjalJkdzhWWjNrbTRTaXNneG0xaWdhUENGenNreGUiLCJuYmYiOjE2ODgxMjI4MTQsImlzcyI6ImRpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OSJ9.x7C9pcS0bkUH5A-bhgunTrpKvARN6ikZVLc0FGh1tz1DGz5BiQEg7Ho8RPiehLfwEy0JNwUk86WOu8iNcrC6Bw" - } - }, - "symmetricKey": "ca530d1478b6fb61ccc2bfda851c6d23d01eb452bda8084b5bf9bff1ec087a74", - "fetchList": true, - "publish": true, - "publishEncrypted": true, - "returnUpdatedStatusList": true, - "returnSymmetricKey": true, - "returnStatusListMetadata": true, - "dkgOptions": { - "chain": "cheqdMainnet", - "network": "localhost" - } + "credential": { + "credentialSubject": { + "name": "eengineer1", + "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" + }, + "issuer": { + "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" + }, + "type": ["VerifiableCredential"], + "credentialStatus": { + "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=suspension-list-encrypted-inverse-timelock&resourceType=StatusList2021Suspension#111168", + "type": "StatusList2021Entry", + "statusPurpose": "suspension", + "statusListIndex": "111168" + }, + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://veramo.io/contexts/profile/v1", + "https://w3id.org/vc-status-list-2021/v1" + ], + "issuanceDate": "2023-06-30T11:00:14.000Z", + "proof": { + "type": "JwtProof2020", + "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9c3VzcGVuc2lvbi1saXN0LWVuY3J5cHRlZC1pbnZlcnNlLXRpbWVsb2NrJnJlc291cmNlVHlwZT1TdGF0dXNMaXN0MjAyMVN1c3BlbnNpb24jMTExMTY4IiwidHlwZSI6IlN0YXR1c0xpc3QyMDIxRW50cnkiLCJzdGF0dXNQdXJwb3NlIjoic3VzcGVuc2lvbiIsInN0YXR1c0xpc3RJbmRleCI6IjExMTE2OCJ9fSwic3ViIjoiZGlkOmtleTp6Nk1rdkc0ZHBLVnBZd1lxbndjalJkdzhWWjNrbTRTaXNneG0xaWdhUENGenNreGUiLCJuYmYiOjE2ODgxMjI4MTQsImlzcyI6ImRpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OSJ9.x7C9pcS0bkUH5A-bhgunTrpKvARN6ikZVLc0FGh1tz1DGz5BiQEg7Ho8RPiehLfwEy0JNwUk86WOu8iNcrC6Bw" + } + }, + "symmetricKey": "ca530d1478b6fb61ccc2bfda851c6d23d01eb452bda8084b5bf9bff1ec087a74", + "fetchList": true, + "publish": true, + "publishEncrypted": true, + "returnUpdatedStatusList": true, + "returnSymmetricKey": true, + "returnStatusListMetadata": true, + "dkgOptions": { + "chain": "cheqdMainnet", + "network": "localhost" + } } diff --git a/examples/status-list-2021/credential/update-status/suspend-credential-unencrypted.json b/examples/status-list-2021/credential/update-status/suspend-credential-unencrypted.json index 52ff1ee2..88b9f318 100644 --- a/examples/status-list-2021/credential/update-status/suspend-credential-unencrypted.json +++ b/examples/status-list-2021/credential/update-status/suspend-credential-unencrypted.json @@ -1,35 +1,33 @@ { - "credential": { - "credentialSubject": { - "name": "eengineer1", - "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" - }, - "issuer": { - "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" - }, - "type": [ - "VerifiableCredential" - ], - "credentialStatus": { - "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=suspension-list-unencrypted-sandbox#95193", - "type": "StatusList2021Entry", - "statusPurpose": "suspension", - "statusListIndex": "95193", - "statusListCredential": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=suspension-list-unencrypted-sandbox" - }, - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://veramo.io/contexts/profile/v1", - "https://w3id.org/vc-status-list-2021/v1" - ], - "issuanceDate": "2023-05-23T18:02:28.000Z", - "proof": { - "type": "JwtProof2020", - "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9c3VzcGVuc2lvbi1saXN0LXVuZW5jcnlwdGVkLXNhbmRib3gjOTUxOTMiLCJ0eXBlIjoiU3RhdHVzTGlzdDIwMjFFbnRyeSIsInN0YXR1c1B1cnBvc2UiOiJzdXNwZW5zaW9uIiwic3RhdHVzTGlzdEluZGV4IjoiOTUxOTMiLCJzdGF0dXNMaXN0Q3JlZGVudGlhbCI6Imh0dHBzOi8vcmVzb2x2ZXIuY2hlcWQubmV0LzEuMC9pZGVudGlmaWVycy9kaWQ6Y2hlcWQ6dGVzdG5ldDozMjI3NjFlYS01ODdkLTQ1NGEtYTk1NS03NDUyMDAzMDFiOTk_cmVzb3VyY2VOYW1lPXN1c3BlbnNpb24tbGlzdC11bmVuY3J5cHRlZC1zYW5kYm94In19LCJzdWIiOiJkaWQ6a2V5Ono2TWt2RzRkcEtWcFl3WXFud2NqUmR3OFZaM2ttNFNpc2d4bTFpZ2FQQ0Z6c2t4ZSIsIm5iZiI6MTY4NDg2NDk0OCwiaXNzIjoiZGlkOmNoZXFkOnRlc3RuZXQ6MzIyNzYxZWEtNTg3ZC00NTRhLWE5NTUtNzQ1MjAwMzAxYjk5In0.FKHWPDQLC0FwfmpioD9CL2jq9Qo8Giq4f3x_2eB-S594Swkm9uwxqAvWrf-ebML_WDnuweYGg4m3ZNk6vg9_BA" - } - }, - "fetchList": true, - "publish": true, - "returnUpdatedStatusList": true, - "returnStatusListMetadata": true + "credential": { + "credentialSubject": { + "name": "eengineer1", + "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" + }, + "issuer": { + "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" + }, + "type": ["VerifiableCredential"], + "credentialStatus": { + "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=suspension-list-unencrypted-sandbox#95193", + "type": "StatusList2021Entry", + "statusPurpose": "suspension", + "statusListIndex": "95193", + "statusListCredential": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=suspension-list-unencrypted-sandbox" + }, + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://veramo.io/contexts/profile/v1", + "https://w3id.org/vc-status-list-2021/v1" + ], + "issuanceDate": "2023-05-23T18:02:28.000Z", + "proof": { + "type": "JwtProof2020", + "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9c3VzcGVuc2lvbi1saXN0LXVuZW5jcnlwdGVkLXNhbmRib3gjOTUxOTMiLCJ0eXBlIjoiU3RhdHVzTGlzdDIwMjFFbnRyeSIsInN0YXR1c1B1cnBvc2UiOiJzdXNwZW5zaW9uIiwic3RhdHVzTGlzdEluZGV4IjoiOTUxOTMiLCJzdGF0dXNMaXN0Q3JlZGVudGlhbCI6Imh0dHBzOi8vcmVzb2x2ZXIuY2hlcWQubmV0LzEuMC9pZGVudGlmaWVycy9kaWQ6Y2hlcWQ6dGVzdG5ldDozMjI3NjFlYS01ODdkLTQ1NGEtYTk1NS03NDUyMDAzMDFiOTk_cmVzb3VyY2VOYW1lPXN1c3BlbnNpb24tbGlzdC11bmVuY3J5cHRlZC1zYW5kYm94In19LCJzdWIiOiJkaWQ6a2V5Ono2TWt2RzRkcEtWcFl3WXFud2NqUmR3OFZaM2ttNFNpc2d4bTFpZ2FQQ0Z6c2t4ZSIsIm5iZiI6MTY4NDg2NDk0OCwiaXNzIjoiZGlkOmNoZXFkOnRlc3RuZXQ6MzIyNzYxZWEtNTg3ZC00NTRhLWE5NTUtNzQ1MjAwMzAxYjk5In0.FKHWPDQLC0FwfmpioD9CL2jq9Qo8Giq4f3x_2eB-S594Swkm9uwxqAvWrf-ebML_WDnuweYGg4m3ZNk6vg9_BA" + } + }, + "fetchList": true, + "publish": true, + "returnUpdatedStatusList": true, + "returnStatusListMetadata": true } diff --git a/examples/status-list-2021/credential/update-status/unsuspend-bulk-credentials-unencrypted.json b/examples/status-list-2021/credential/update-status/unsuspend-bulk-credentials-unencrypted.json index 89d8b53f..f29e7080 100644 --- a/examples/status-list-2021/credential/update-status/unsuspend-bulk-credentials-unencrypted.json +++ b/examples/status-list-2021/credential/update-status/unsuspend-bulk-credentials-unencrypted.json @@ -1,64 +1,60 @@ { - "credential": [ - { - "credentialSubject": { - "name": "eengineer1", - "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" - }, - "issuer": { - "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" - }, - "type": [ - "VerifiableCredential" - ], - "credentialStatus": { - "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=suspension-list-unencrypted-modifications&resourceType=StatusList2021Suspension#88942", - "type": "StatusList2021Entry", - "statusPurpose": "suspension", - "statusListIndex": "88942" - }, - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://veramo.io/contexts/profile/v1", - "https://w3id.org/vc-status-list-2021/v1" - ], - "issuanceDate": "2023-06-21T13:39:14.000Z", - "proof": { - "type": "JwtProof2020", - "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9c3VzcGVuc2lvbi1saXN0LXVuZW5jcnlwdGVkLW1vZGlmaWNhdGlvbnMmcmVzb3VyY2VUeXBlPVN0YXR1c0xpc3QyMDIxU3VzcGVuc2lvbiM4ODk0MiIsInR5cGUiOiJTdGF0dXNMaXN0MjAyMUVudHJ5Iiwic3RhdHVzUHVycG9zZSI6InN1c3BlbnNpb24iLCJzdGF0dXNMaXN0SW5kZXgiOiI4ODk0MiJ9fSwic3ViIjoiZGlkOmtleTp6Nk1rdkc0ZHBLVnBZd1lxbndjalJkdzhWWjNrbTRTaXNneG0xaWdhUENGenNreGUiLCJuYmYiOjE2ODczNTQ3NTQsImlzcyI6ImRpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OSJ9.UM7oRC0lspkjDx7oTyBmkVBByvkPtcI_35c4Uhomqlq2L8b-YGoE5TSgF4ePR2PvD7H8goo1X_3UeLHlyeoHDA" - } - }, - { - "credentialSubject": { - "name": "eengineer1", - "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" - }, - "issuer": { - "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" - }, - "type": [ - "VerifiableCredential" - ], - "credentialStatus": { - "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=suspension-list-unencrypted-modifications&resourceType=StatusList2021Suspension#73812", - "type": "StatusList2021Entry", - "statusPurpose": "suspension", - "statusListIndex": "73812" - }, - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://veramo.io/contexts/profile/v1", - "https://w3id.org/vc-status-list-2021/v1" - ], - "issuanceDate": "2023-06-21T13:40:39.000Z", - "proof": { - "type": "JwtProof2020", - "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9c3VzcGVuc2lvbi1saXN0LXVuZW5jcnlwdGVkLW1vZGlmaWNhdGlvbnMmcmVzb3VyY2VUeXBlPVN0YXR1c0xpc3QyMDIxU3VzcGVuc2lvbiM3MzgxMiIsInR5cGUiOiJTdGF0dXNMaXN0MjAyMUVudHJ5Iiwic3RhdHVzUHVycG9zZSI6InN1c3BlbnNpb24iLCJzdGF0dXNMaXN0SW5kZXgiOiI3MzgxMiJ9fSwic3ViIjoiZGlkOmtleTp6Nk1rdkc0ZHBLVnBZd1lxbndjalJkdzhWWjNrbTRTaXNneG0xaWdhUENGenNreGUiLCJuYmYiOjE2ODczNTQ4MzksImlzcyI6ImRpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OSJ9.gPH1Sjqj2rCWAmMPU12S6qYxwE12bV2-eUt9gDhM5u9jL7GCp3F_58Fyd8fqVWAS1Exw2k5FERmHRKkBs763Dg" - } - } - ], - "fetchList": true, - "publish": true, - "returnUpdatedStatusList": true, - "returnStatusListMetadata": true + "credential": [ + { + "credentialSubject": { + "name": "eengineer1", + "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" + }, + "issuer": { + "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" + }, + "type": ["VerifiableCredential"], + "credentialStatus": { + "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=suspension-list-unencrypted-modifications&resourceType=StatusList2021Suspension#88942", + "type": "StatusList2021Entry", + "statusPurpose": "suspension", + "statusListIndex": "88942" + }, + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://veramo.io/contexts/profile/v1", + "https://w3id.org/vc-status-list-2021/v1" + ], + "issuanceDate": "2023-06-21T13:39:14.000Z", + "proof": { + "type": "JwtProof2020", + "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9c3VzcGVuc2lvbi1saXN0LXVuZW5jcnlwdGVkLW1vZGlmaWNhdGlvbnMmcmVzb3VyY2VUeXBlPVN0YXR1c0xpc3QyMDIxU3VzcGVuc2lvbiM4ODk0MiIsInR5cGUiOiJTdGF0dXNMaXN0MjAyMUVudHJ5Iiwic3RhdHVzUHVycG9zZSI6InN1c3BlbnNpb24iLCJzdGF0dXNMaXN0SW5kZXgiOiI4ODk0MiJ9fSwic3ViIjoiZGlkOmtleTp6Nk1rdkc0ZHBLVnBZd1lxbndjalJkdzhWWjNrbTRTaXNneG0xaWdhUENGenNreGUiLCJuYmYiOjE2ODczNTQ3NTQsImlzcyI6ImRpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OSJ9.UM7oRC0lspkjDx7oTyBmkVBByvkPtcI_35c4Uhomqlq2L8b-YGoE5TSgF4ePR2PvD7H8goo1X_3UeLHlyeoHDA" + } + }, + { + "credentialSubject": { + "name": "eengineer1", + "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" + }, + "issuer": { + "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" + }, + "type": ["VerifiableCredential"], + "credentialStatus": { + "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=suspension-list-unencrypted-modifications&resourceType=StatusList2021Suspension#73812", + "type": "StatusList2021Entry", + "statusPurpose": "suspension", + "statusListIndex": "73812" + }, + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://veramo.io/contexts/profile/v1", + "https://w3id.org/vc-status-list-2021/v1" + ], + "issuanceDate": "2023-06-21T13:40:39.000Z", + "proof": { + "type": "JwtProof2020", + "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9c3VzcGVuc2lvbi1saXN0LXVuZW5jcnlwdGVkLW1vZGlmaWNhdGlvbnMmcmVzb3VyY2VUeXBlPVN0YXR1c0xpc3QyMDIxU3VzcGVuc2lvbiM3MzgxMiIsInR5cGUiOiJTdGF0dXNMaXN0MjAyMUVudHJ5Iiwic3RhdHVzUHVycG9zZSI6InN1c3BlbnNpb24iLCJzdGF0dXNMaXN0SW5kZXgiOiI3MzgxMiJ9fSwic3ViIjoiZGlkOmtleTp6Nk1rdkc0ZHBLVnBZd1lxbndjalJkdzhWWjNrbTRTaXNneG0xaWdhUENGenNreGUiLCJuYmYiOjE2ODczNTQ4MzksImlzcyI6ImRpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OSJ9.gPH1Sjqj2rCWAmMPU12S6qYxwE12bV2-eUt9gDhM5u9jL7GCp3F_58Fyd8fqVWAS1Exw2k5FERmHRKkBs763Dg" + } + } + ], + "fetchList": true, + "publish": true, + "returnUpdatedStatusList": true, + "returnStatusListMetadata": true } diff --git a/examples/status-list-2021/credential/update-status/unsuspend-credential-encrypted.json b/examples/status-list-2021/credential/update-status/unsuspend-credential-encrypted.json index 633a0d1c..d55b6f99 100644 --- a/examples/status-list-2021/credential/update-status/unsuspend-credential-encrypted.json +++ b/examples/status-list-2021/credential/update-status/unsuspend-credential-encrypted.json @@ -1,41 +1,39 @@ { - "credential": { - "credentialSubject": { - "name": "eengineer1", - "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" - }, - "issuer": { - "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" - }, - "type": [ - "VerifiableCredential" - ], - "credentialStatus": { - "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=suspension-list-encrypted-inverse-timelock&resourceType=StatusList2021Suspension#111168", - "type": "StatusList2021Entry", - "statusPurpose": "suspension", - "statusListIndex": "111168" - }, - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://veramo.io/contexts/profile/v1", - "https://w3id.org/vc-status-list-2021/v1" - ], - "issuanceDate": "2023-06-30T11:00:14.000Z", - "proof": { - "type": "JwtProof2020", - "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9c3VzcGVuc2lvbi1saXN0LWVuY3J5cHRlZC1pbnZlcnNlLXRpbWVsb2NrJnJlc291cmNlVHlwZT1TdGF0dXNMaXN0MjAyMVN1c3BlbnNpb24jMTExMTY4IiwidHlwZSI6IlN0YXR1c0xpc3QyMDIxRW50cnkiLCJzdGF0dXNQdXJwb3NlIjoic3VzcGVuc2lvbiIsInN0YXR1c0xpc3RJbmRleCI6IjExMTE2OCJ9fSwic3ViIjoiZGlkOmtleTp6Nk1rdkc0ZHBLVnBZd1lxbndjalJkdzhWWjNrbTRTaXNneG0xaWdhUENGenNreGUiLCJuYmYiOjE2ODgxMjI4MTQsImlzcyI6ImRpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OSJ9.x7C9pcS0bkUH5A-bhgunTrpKvARN6ikZVLc0FGh1tz1DGz5BiQEg7Ho8RPiehLfwEy0JNwUk86WOu8iNcrC6Bw" - } - }, - "symmetricKey": "ace7f0f596c6bc01fbf3ec7751cb7bbf3b992141664657b3e6a0e4e693e70015", - "fetchList": true, - "publish": true, - "publishEncrypted": true, - "returnUpdatedStatusList": true, - "returnSymmetricKey": true, - "returnStatusListMetadata": true, - "dkgOptions": { - "chain": "cheqdMainnet", - "network": "localhost" - } + "credential": { + "credentialSubject": { + "name": "eengineer1", + "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" + }, + "issuer": { + "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" + }, + "type": ["VerifiableCredential"], + "credentialStatus": { + "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=suspension-list-encrypted-inverse-timelock&resourceType=StatusList2021Suspension#111168", + "type": "StatusList2021Entry", + "statusPurpose": "suspension", + "statusListIndex": "111168" + }, + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://veramo.io/contexts/profile/v1", + "https://w3id.org/vc-status-list-2021/v1" + ], + "issuanceDate": "2023-06-30T11:00:14.000Z", + "proof": { + "type": "JwtProof2020", + "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9c3VzcGVuc2lvbi1saXN0LWVuY3J5cHRlZC1pbnZlcnNlLXRpbWVsb2NrJnJlc291cmNlVHlwZT1TdGF0dXNMaXN0MjAyMVN1c3BlbnNpb24jMTExMTY4IiwidHlwZSI6IlN0YXR1c0xpc3QyMDIxRW50cnkiLCJzdGF0dXNQdXJwb3NlIjoic3VzcGVuc2lvbiIsInN0YXR1c0xpc3RJbmRleCI6IjExMTE2OCJ9fSwic3ViIjoiZGlkOmtleTp6Nk1rdkc0ZHBLVnBZd1lxbndjalJkdzhWWjNrbTRTaXNneG0xaWdhUENGenNreGUiLCJuYmYiOjE2ODgxMjI4MTQsImlzcyI6ImRpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OSJ9.x7C9pcS0bkUH5A-bhgunTrpKvARN6ikZVLc0FGh1tz1DGz5BiQEg7Ho8RPiehLfwEy0JNwUk86WOu8iNcrC6Bw" + } + }, + "symmetricKey": "ace7f0f596c6bc01fbf3ec7751cb7bbf3b992141664657b3e6a0e4e693e70015", + "fetchList": true, + "publish": true, + "publishEncrypted": true, + "returnUpdatedStatusList": true, + "returnSymmetricKey": true, + "returnStatusListMetadata": true, + "dkgOptions": { + "chain": "cheqdMainnet", + "network": "localhost" + } } diff --git a/examples/status-list-2021/credential/update-status/unsuspend-credential-unencrypted.json b/examples/status-list-2021/credential/update-status/unsuspend-credential-unencrypted.json index 6e88374a..88b9f318 100644 --- a/examples/status-list-2021/credential/update-status/unsuspend-credential-unencrypted.json +++ b/examples/status-list-2021/credential/update-status/unsuspend-credential-unencrypted.json @@ -1,35 +1,33 @@ { - "credential": { - "credentialSubject": { - "name": "eengineer1", - "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" - }, - "issuer": { - "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" - }, - "type": [ - "VerifiableCredential" - ], - "credentialStatus": { - "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=suspension-list-unencrypted-sandbox#95193", - "type": "StatusList2021Entry", - "statusPurpose": "suspension", - "statusListIndex": "95193", - "statusListCredential": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=suspension-list-unencrypted-sandbox" - }, - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://veramo.io/contexts/profile/v1", - "https://w3id.org/vc-status-list-2021/v1" - ], - "issuanceDate": "2023-05-23T18:02:28.000Z", - "proof": { - "type": "JwtProof2020", - "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9c3VzcGVuc2lvbi1saXN0LXVuZW5jcnlwdGVkLXNhbmRib3gjOTUxOTMiLCJ0eXBlIjoiU3RhdHVzTGlzdDIwMjFFbnRyeSIsInN0YXR1c1B1cnBvc2UiOiJzdXNwZW5zaW9uIiwic3RhdHVzTGlzdEluZGV4IjoiOTUxOTMiLCJzdGF0dXNMaXN0Q3JlZGVudGlhbCI6Imh0dHBzOi8vcmVzb2x2ZXIuY2hlcWQubmV0LzEuMC9pZGVudGlmaWVycy9kaWQ6Y2hlcWQ6dGVzdG5ldDozMjI3NjFlYS01ODdkLTQ1NGEtYTk1NS03NDUyMDAzMDFiOTk_cmVzb3VyY2VOYW1lPXN1c3BlbnNpb24tbGlzdC11bmVuY3J5cHRlZC1zYW5kYm94In19LCJzdWIiOiJkaWQ6a2V5Ono2TWt2RzRkcEtWcFl3WXFud2NqUmR3OFZaM2ttNFNpc2d4bTFpZ2FQQ0Z6c2t4ZSIsIm5iZiI6MTY4NDg2NDk0OCwiaXNzIjoiZGlkOmNoZXFkOnRlc3RuZXQ6MzIyNzYxZWEtNTg3ZC00NTRhLWE5NTUtNzQ1MjAwMzAxYjk5In0.FKHWPDQLC0FwfmpioD9CL2jq9Qo8Giq4f3x_2eB-S594Swkm9uwxqAvWrf-ebML_WDnuweYGg4m3ZNk6vg9_BA" - } - }, - "fetchList": true, - "publish": true, - "returnUpdatedStatusList": true, - "returnStatusListMetadata": true -} \ No newline at end of file + "credential": { + "credentialSubject": { + "name": "eengineer1", + "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" + }, + "issuer": { + "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" + }, + "type": ["VerifiableCredential"], + "credentialStatus": { + "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=suspension-list-unencrypted-sandbox#95193", + "type": "StatusList2021Entry", + "statusPurpose": "suspension", + "statusListIndex": "95193", + "statusListCredential": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=suspension-list-unencrypted-sandbox" + }, + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://veramo.io/contexts/profile/v1", + "https://w3id.org/vc-status-list-2021/v1" + ], + "issuanceDate": "2023-05-23T18:02:28.000Z", + "proof": { + "type": "JwtProof2020", + "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9c3VzcGVuc2lvbi1saXN0LXVuZW5jcnlwdGVkLXNhbmRib3gjOTUxOTMiLCJ0eXBlIjoiU3RhdHVzTGlzdDIwMjFFbnRyeSIsInN0YXR1c1B1cnBvc2UiOiJzdXNwZW5zaW9uIiwic3RhdHVzTGlzdEluZGV4IjoiOTUxOTMiLCJzdGF0dXNMaXN0Q3JlZGVudGlhbCI6Imh0dHBzOi8vcmVzb2x2ZXIuY2hlcWQubmV0LzEuMC9pZGVudGlmaWVycy9kaWQ6Y2hlcWQ6dGVzdG5ldDozMjI3NjFlYS01ODdkLTQ1NGEtYTk1NS03NDUyMDAzMDFiOTk_cmVzb3VyY2VOYW1lPXN1c3BlbnNpb24tbGlzdC11bmVuY3J5cHRlZC1zYW5kYm94In19LCJzdWIiOiJkaWQ6a2V5Ono2TWt2RzRkcEtWcFl3WXFud2NqUmR3OFZaM2ttNFNpc2d4bTFpZ2FQQ0Z6c2t4ZSIsIm5iZiI6MTY4NDg2NDk0OCwiaXNzIjoiZGlkOmNoZXFkOnRlc3RuZXQ6MzIyNzYxZWEtNTg3ZC00NTRhLWE5NTUtNzQ1MjAwMzAxYjk5In0.FKHWPDQLC0FwfmpioD9CL2jq9Qo8Giq4f3x_2eB-S594Swkm9uwxqAvWrf-ebML_WDnuweYGg4m3ZNk6vg9_BA" + } + }, + "fetchList": true, + "publish": true, + "returnUpdatedStatusList": true, + "returnStatusListMetadata": true +} diff --git a/examples/status-list-2021/credential/verify/verify-credential-encrypted-revocation.json b/examples/status-list-2021/credential/verify/verify-credential-encrypted-revocation.json index e9b8b0a1..feabe10c 100644 --- a/examples/status-list-2021/credential/verify/verify-credential-encrypted-revocation.json +++ b/examples/status-list-2021/credential/verify/verify-credential-encrypted-revocation.json @@ -1,35 +1,33 @@ { - "credential": { - "credentialSubject": { - "name": "eengineer1", - "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" - }, - "issuer": { - "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" - }, - "type": [ - "VerifiableCredential" - ], - "credentialStatus": { - "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=revocation-list-encrypted-inverse-timelock&resourceType=StatusList2021Revocation#79444", - "type": "StatusList2021Entry", - "statusPurpose": "revocation", - "statusListIndex": "79444" - }, - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://veramo.io/contexts/profile/v1", - "https://w3id.org/vc-status-list-2021/v1" - ], - "issuanceDate": "2023-06-30T10:04:01.000Z", - "proof": { - "type": "JwtProof2020", - "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9cmV2b2NhdGlvbi1saXN0LWVuY3J5cHRlZC1pbnZlcnNlLXRpbWVsb2NrJnJlc291cmNlVHlwZT1TdGF0dXNMaXN0MjAyMVJldm9jYXRpb24jNzk0NDQiLCJ0eXBlIjoiU3RhdHVzTGlzdDIwMjFFbnRyeSIsInN0YXR1c1B1cnBvc2UiOiJyZXZvY2F0aW9uIiwic3RhdHVzTGlzdEluZGV4IjoiNzk0NDQifX0sInN1YiI6ImRpZDprZXk6ejZNa3ZHNGRwS1ZwWXdZcW53Y2pSZHc4Vloza200U2lzZ3htMWlnYVBDRnpza3hlIiwibmJmIjoxNjg4MTE5NDQxLCJpc3MiOiJkaWQ6Y2hlcWQ6dGVzdG5ldDozMjI3NjFlYS01ODdkLTQ1NGEtYTk1NS03NDUyMDAzMDFiOTkifQ.fWCayAbmL4qivVfMz97o6tEFN2W411OFaF3wgZAb5gqoBkJ1uoV2Cp_31LI60ehSPXcDXiIUmDJbFP3S8bnNDg" - } - }, - "fetchList": true, - "dkgOptions": { - "chain": "cheqdMainnet", - "network": "localhost" - } + "credential": { + "credentialSubject": { + "name": "eengineer1", + "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" + }, + "issuer": { + "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" + }, + "type": ["VerifiableCredential"], + "credentialStatus": { + "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=revocation-list-encrypted-inverse-timelock&resourceType=StatusList2021Revocation#79444", + "type": "StatusList2021Entry", + "statusPurpose": "revocation", + "statusListIndex": "79444" + }, + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://veramo.io/contexts/profile/v1", + "https://w3id.org/vc-status-list-2021/v1" + ], + "issuanceDate": "2023-06-30T10:04:01.000Z", + "proof": { + "type": "JwtProof2020", + "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9cmV2b2NhdGlvbi1saXN0LWVuY3J5cHRlZC1pbnZlcnNlLXRpbWVsb2NrJnJlc291cmNlVHlwZT1TdGF0dXNMaXN0MjAyMVJldm9jYXRpb24jNzk0NDQiLCJ0eXBlIjoiU3RhdHVzTGlzdDIwMjFFbnRyeSIsInN0YXR1c1B1cnBvc2UiOiJyZXZvY2F0aW9uIiwic3RhdHVzTGlzdEluZGV4IjoiNzk0NDQifX0sInN1YiI6ImRpZDprZXk6ejZNa3ZHNGRwS1ZwWXdZcW53Y2pSZHc4Vloza200U2lzZ3htMWlnYVBDRnpza3hlIiwibmJmIjoxNjg4MTE5NDQxLCJpc3MiOiJkaWQ6Y2hlcWQ6dGVzdG5ldDozMjI3NjFlYS01ODdkLTQ1NGEtYTk1NS03NDUyMDAzMDFiOTkifQ.fWCayAbmL4qivVfMz97o6tEFN2W411OFaF3wgZAb5gqoBkJ1uoV2Cp_31LI60ehSPXcDXiIUmDJbFP3S8bnNDg" + } + }, + "fetchList": true, + "dkgOptions": { + "chain": "cheqdMainnet", + "network": "localhost" + } } diff --git a/examples/status-list-2021/credential/verify/verify-credential-encrypted-suspension.json b/examples/status-list-2021/credential/verify/verify-credential-encrypted-suspension.json index dbe511ff..343b533c 100644 --- a/examples/status-list-2021/credential/verify/verify-credential-encrypted-suspension.json +++ b/examples/status-list-2021/credential/verify/verify-credential-encrypted-suspension.json @@ -1,35 +1,33 @@ { - "credential": { - "credentialSubject": { - "name": "eengineer1", - "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" - }, - "issuer": { - "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" - }, - "type": [ - "VerifiableCredential" - ], - "credentialStatus": { - "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=suspension-list-encrypted-inverse-timelock&resourceType=StatusList2021Suspension#111168", - "type": "StatusList2021Entry", - "statusPurpose": "suspension", - "statusListIndex": "111168" - }, - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://veramo.io/contexts/profile/v1", - "https://w3id.org/vc-status-list-2021/v1" - ], - "issuanceDate": "2023-06-30T11:00:14.000Z", - "proof": { - "type": "JwtProof2020", - "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9c3VzcGVuc2lvbi1saXN0LWVuY3J5cHRlZC1pbnZlcnNlLXRpbWVsb2NrJnJlc291cmNlVHlwZT1TdGF0dXNMaXN0MjAyMVN1c3BlbnNpb24jMTExMTY4IiwidHlwZSI6IlN0YXR1c0xpc3QyMDIxRW50cnkiLCJzdGF0dXNQdXJwb3NlIjoic3VzcGVuc2lvbiIsInN0YXR1c0xpc3RJbmRleCI6IjExMTE2OCJ9fSwic3ViIjoiZGlkOmtleTp6Nk1rdkc0ZHBLVnBZd1lxbndjalJkdzhWWjNrbTRTaXNneG0xaWdhUENGenNreGUiLCJuYmYiOjE2ODgxMjI4MTQsImlzcyI6ImRpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OSJ9.x7C9pcS0bkUH5A-bhgunTrpKvARN6ikZVLc0FGh1tz1DGz5BiQEg7Ho8RPiehLfwEy0JNwUk86WOu8iNcrC6Bw" - } - }, - "fetchList": true, - "dkgOptions": { - "chain": "cheqdMainnet", - "network": "localhost" - } + "credential": { + "credentialSubject": { + "name": "eengineer1", + "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" + }, + "issuer": { + "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" + }, + "type": ["VerifiableCredential"], + "credentialStatus": { + "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=suspension-list-encrypted-inverse-timelock&resourceType=StatusList2021Suspension#111168", + "type": "StatusList2021Entry", + "statusPurpose": "suspension", + "statusListIndex": "111168" + }, + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://veramo.io/contexts/profile/v1", + "https://w3id.org/vc-status-list-2021/v1" + ], + "issuanceDate": "2023-06-30T11:00:14.000Z", + "proof": { + "type": "JwtProof2020", + "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9c3VzcGVuc2lvbi1saXN0LWVuY3J5cHRlZC1pbnZlcnNlLXRpbWVsb2NrJnJlc291cmNlVHlwZT1TdGF0dXNMaXN0MjAyMVN1c3BlbnNpb24jMTExMTY4IiwidHlwZSI6IlN0YXR1c0xpc3QyMDIxRW50cnkiLCJzdGF0dXNQdXJwb3NlIjoic3VzcGVuc2lvbiIsInN0YXR1c0xpc3RJbmRleCI6IjExMTE2OCJ9fSwic3ViIjoiZGlkOmtleTp6Nk1rdkc0ZHBLVnBZd1lxbndjalJkdzhWWjNrbTRTaXNneG0xaWdhUENGenNreGUiLCJuYmYiOjE2ODgxMjI4MTQsImlzcyI6ImRpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OSJ9.x7C9pcS0bkUH5A-bhgunTrpKvARN6ikZVLc0FGh1tz1DGz5BiQEg7Ho8RPiehLfwEy0JNwUk86WOu8iNcrC6Bw" + } + }, + "fetchList": true, + "dkgOptions": { + "chain": "cheqdMainnet", + "network": "localhost" + } } diff --git a/examples/status-list-2021/credential/verify/verify-credential-unencrypted-revocation.json b/examples/status-list-2021/credential/verify/verify-credential-unencrypted-revocation.json index 72082935..2bf490a2 100644 --- a/examples/status-list-2021/credential/verify/verify-credential-unencrypted-revocation.json +++ b/examples/status-list-2021/credential/verify/verify-credential-unencrypted-revocation.json @@ -1,31 +1,29 @@ { - "credential": { - "credentialSubject": { - "name": "eengineer1", - "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" - }, - "issuer": { - "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" - }, - "type": [ - "VerifiableCredential" - ], - "credentialStatus": { - "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=revocation-list-unencrypted-session&resourceType=StatusList2021Revocation#111872", - "type": "StatusList2021Entry", - "statusPurpose": "revocation", - "statusListIndex": "111872" - }, - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://veramo.io/contexts/profile/v1", - "https://w3id.org/vc-status-list-2021/v1" - ], - "issuanceDate": "2023-06-30T11:35:33.000Z", - "proof": { - "type": "JwtProof2020", - "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9cmV2b2NhdGlvbi1saXN0LXVuZW5jcnlwdGVkLXNlc3Npb24mcmVzb3VyY2VUeXBlPVN0YXR1c0xpc3QyMDIxUmV2b2NhdGlvbiMxMTE4NzIiLCJ0eXBlIjoiU3RhdHVzTGlzdDIwMjFFbnRyeSIsInN0YXR1c1B1cnBvc2UiOiJyZXZvY2F0aW9uIiwic3RhdHVzTGlzdEluZGV4IjoiMTExODcyIn19LCJzdWIiOiJkaWQ6a2V5Ono2TWt2RzRkcEtWcFl3WXFud2NqUmR3OFZaM2ttNFNpc2d4bTFpZ2FQQ0Z6c2t4ZSIsIm5iZiI6MTY4ODEyNDkzMywiaXNzIjoiZGlkOmNoZXFkOnRlc3RuZXQ6MzIyNzYxZWEtNTg3ZC00NTRhLWE5NTUtNzQ1MjAwMzAxYjk5In0.T3yuilV074onreJU50wbIt5tB1VMJMShFEBkG0kLnwFPcvFzzpzi9vOP9Q92-4ANEtjSZPShpI3z8PnFNFIdAg" - } - }, - "fetchList": true + "credential": { + "credentialSubject": { + "name": "eengineer1", + "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" + }, + "issuer": { + "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" + }, + "type": ["VerifiableCredential"], + "credentialStatus": { + "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=revocation-list-unencrypted-session&resourceType=StatusList2021Revocation#111872", + "type": "StatusList2021Entry", + "statusPurpose": "revocation", + "statusListIndex": "111872" + }, + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://veramo.io/contexts/profile/v1", + "https://w3id.org/vc-status-list-2021/v1" + ], + "issuanceDate": "2023-06-30T11:35:33.000Z", + "proof": { + "type": "JwtProof2020", + "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9cmV2b2NhdGlvbi1saXN0LXVuZW5jcnlwdGVkLXNlc3Npb24mcmVzb3VyY2VUeXBlPVN0YXR1c0xpc3QyMDIxUmV2b2NhdGlvbiMxMTE4NzIiLCJ0eXBlIjoiU3RhdHVzTGlzdDIwMjFFbnRyeSIsInN0YXR1c1B1cnBvc2UiOiJyZXZvY2F0aW9uIiwic3RhdHVzTGlzdEluZGV4IjoiMTExODcyIn19LCJzdWIiOiJkaWQ6a2V5Ono2TWt2RzRkcEtWcFl3WXFud2NqUmR3OFZaM2ttNFNpc2d4bTFpZ2FQQ0Z6c2t4ZSIsIm5iZiI6MTY4ODEyNDkzMywiaXNzIjoiZGlkOmNoZXFkOnRlc3RuZXQ6MzIyNzYxZWEtNTg3ZC00NTRhLWE5NTUtNzQ1MjAwMzAxYjk5In0.T3yuilV074onreJU50wbIt5tB1VMJMShFEBkG0kLnwFPcvFzzpzi9vOP9Q92-4ANEtjSZPShpI3z8PnFNFIdAg" + } + }, + "fetchList": true } diff --git a/examples/status-list-2021/presentation/verify-presentation-unencrypted.json b/examples/status-list-2021/presentation/verify-presentation-unencrypted.json index f3a0a0c4..a2ba9802 100644 --- a/examples/status-list-2021/presentation/verify-presentation-unencrypted.json +++ b/examples/status-list-2021/presentation/verify-presentation-unencrypted.json @@ -1,51 +1,43 @@ { - "presentation": { - "verifiableCredential": [ - { - "credentialSubject": { - "name": "eengineer1", - "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" - }, - "issuer": { - "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" - }, - "type": [ - "VerifiableCredential" - ], - "credentialStatus": { - "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=revocation-list-unencrypted-show-and-tell#118427", - "type": "StatusList2021Entry", - "statusPurpose": "revocation", - "statusListIndex": "118427", - "statusListCredential": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=revocation-list-unencrypted-show-and-tell" - }, - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://veramo.io/contexts/profile/v1", - "https://w3id.org/vc-status-list-2021/v1" - ], - "issuanceDate": "2023-05-30T11:46:22.000Z", - "proof": { - "type": "JwtProof2020", - "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9cmV2b2NhdGlvbi1saXN0LXVuZW5jcnlwdGVkLXNob3ctYW5kLXRlbGwjMTE4NDI3IiwidHlwZSI6IlN0YXR1c0xpc3QyMDIxRW50cnkiLCJzdGF0dXNQdXJwb3NlIjoicmV2b2NhdGlvbiIsInN0YXR1c0xpc3RJbmRleCI6IjExODQyNyIsInN0YXR1c0xpc3RDcmVkZW50aWFsIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9cmV2b2NhdGlvbi1saXN0LXVuZW5jcnlwdGVkLXNob3ctYW5kLXRlbGwifX0sInN1YiI6ImRpZDprZXk6ejZNa3ZHNGRwS1ZwWXdZcW53Y2pSZHc4Vloza200U2lzZ3htMWlnYVBDRnpza3hlIiwibmJmIjoxNjg1NDQ3MTgyLCJpc3MiOiJkaWQ6Y2hlcWQ6dGVzdG5ldDozMjI3NjFlYS01ODdkLTQ1NGEtYTk1NS03NDUyMDAzMDFiOTkifQ.zViydlLYVRV7f8xSsawBzQap1wfsliKTaZ36WNURQSopRhvooHwL2jpD5AJF9rDqqqSsPQcOIkX8uxmseN8XDw" - } - } - ], - "holder": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe", - "verifier": [ - "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" - ], - "type": [ - "VerifiablePresentation" - ], - "@context": [ - "https://www.w3.org/2018/credentials/v1" - ], - "issuanceDate": "2023-05-30T11:48:17.000Z", - "proof": { - "type": "JwtProof2020", - "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2cCI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVQcmVzZW50YXRpb24iXSwidmVyaWZpYWJsZUNyZWRlbnRpYWwiOlsiZXlKaGJHY2lPaUpGWkVSVFFTSXNJblI1Y0NJNklrcFhWQ0o5LmV5SjJZeUk2ZXlKQVkyOXVkR1Y0ZENJNld5Sm9kSFJ3Y3pvdkwzZDNkeTUzTXk1dmNtY3ZNakF4T0M5amNtVmtaVzUwYVdGc2N5OTJNU0lzSW1oMGRIQnpPaTh2ZG1WeVlXMXZMbWx2TDJOdmJuUmxlSFJ6TDNCeWIyWnBiR1V2ZGpFaUxDSm9kSFJ3Y3pvdkwzY3phV1F1YjNKbkwzWmpMWE4wWVhSMWN5MXNhWE4wTFRJd01qRXZkakVpWFN3aWRIbHdaU0k2V3lKV1pYSnBabWxoWW14bFEzSmxaR1Z1ZEdsaGJDSmRMQ0pqY21Wa1pXNTBhV0ZzVTNWaWFtVmpkQ0k2ZXlKdVlXMWxJam9pWldWdVoybHVaV1Z5TVNKOUxDSmpjbVZrWlc1MGFXRnNVM1JoZEhWeklqcDdJbWxrSWpvaWFIUjBjSE02THk5eVpYTnZiSFpsY2k1amFHVnhaQzV1WlhRdk1TNHdMMmxrWlc1MGFXWnBaWEp6TDJScFpEcGphR1Z4WkRwMFpYTjBibVYwT2pNeU1qYzJNV1ZoTFRVNE4yUXRORFUwWVMxaE9UVTFMVGMwTlRJd01ETXdNV0k1T1Q5eVpYTnZkWEpqWlU1aGJXVTljbVYyYjJOaGRHbHZiaTFzYVhOMExYVnVaVzVqY25sd2RHVmtMWE5vYjNjdFlXNWtMWFJsYkd3ak1URTROREkzSWl3aWRIbHdaU0k2SWxOMFlYUjFjMHhwYzNReU1ESXhSVzUwY25raUxDSnpkR0YwZFhOUWRYSndiM05sSWpvaWNtVjJiMk5oZEdsdmJpSXNJbk4wWVhSMWMweHBjM1JKYm1SbGVDSTZJakV4T0RReU55SXNJbk4wWVhSMWMweHBjM1JEY21Wa1pXNTBhV0ZzSWpvaWFIUjBjSE02THk5eVpYTnZiSFpsY2k1amFHVnhaQzV1WlhRdk1TNHdMMmxrWlc1MGFXWnBaWEp6TDJScFpEcGphR1Z4WkRwMFpYTjBibVYwT2pNeU1qYzJNV1ZoTFRVNE4yUXRORFUwWVMxaE9UVTFMVGMwTlRJd01ETXdNV0k1T1Q5eVpYTnZkWEpqWlU1aGJXVTljbVYyYjJOaGRHbHZiaTFzYVhOMExYVnVaVzVqY25sd2RHVmtMWE5vYjNjdFlXNWtMWFJsYkd3aWZYMHNJbk4xWWlJNkltUnBaRHByWlhrNmVqWk5hM1pITkdSd1MxWndXWGRaY1c1M1kycFNaSGM0VmxvemEyMDBVMmx6WjNodE1XbG5ZVkJEUm5wemEzaGxJaXdpYm1KbUlqb3hOamcxTkRRM01UZ3lMQ0pwYzNNaU9pSmthV1E2WTJobGNXUTZkR1Z6ZEc1bGREb3pNakkzTmpGbFlTMDFPRGRrTFRRMU5HRXRZVGsxTlMwM05EVXlNREF6TURGaU9Ua2lmUS56Vml5ZGxMWVZSVjdmOHhTc2F3QnpRYXAxd2ZzbGlLVGFaMzZXTlVSUVNvcFJodm9vSHdMMmpwRDVBSkY5ckRxcXFTc1BRY09Ja1g4dXhtc2VOOFhEdyJdfSwibmJmIjoxNjg1NDQ3Mjk3LCJpc3MiOiJkaWQ6a2V5Ono2TWt2RzRkcEtWcFl3WXFud2NqUmR3OFZaM2ttNFNpc2d4bTFpZ2FQQ0Z6c2t4ZSIsImF1ZCI6WyJkaWQ6Y2hlcWQ6dGVzdG5ldDozMjI3NjFlYS01ODdkLTQ1NGEtYTk1NS03NDUyMDAzMDFiOTkiXX0.ji00YcfbWu6GQQ-omhQxtI9dxQYsv4arNrWEwLzUdYZ1X5BZNQR056HZOnSRNkpFtPpvv73RUY-kVdbf0NcnDA" - } - }, - "fetchList": true -} \ No newline at end of file + "presentation": { + "verifiableCredential": [ + { + "credentialSubject": { + "name": "eengineer1", + "id": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe" + }, + "issuer": { + "id": "did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99" + }, + "type": ["VerifiableCredential"], + "credentialStatus": { + "id": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=revocation-list-unencrypted-show-and-tell#118427", + "type": "StatusList2021Entry", + "statusPurpose": "revocation", + "statusListIndex": "118427", + "statusListCredential": "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99?resourceName=revocation-list-unencrypted-show-and-tell" + }, + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://veramo.io/contexts/profile/v1", + "https://w3id.org/vc-status-list-2021/v1" + ], + "issuanceDate": "2023-05-30T11:46:22.000Z", + "proof": { + "type": "JwtProof2020", + "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vdmVyYW1vLmlvL2NvbnRleHRzL3Byb2ZpbGUvdjEiLCJodHRwczovL3czaWQub3JnL3ZjLXN0YXR1cy1saXN0LTIwMjEvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiZWVuZ2luZWVyMSJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9cmV2b2NhdGlvbi1saXN0LXVuZW5jcnlwdGVkLXNob3ctYW5kLXRlbGwjMTE4NDI3IiwidHlwZSI6IlN0YXR1c0xpc3QyMDIxRW50cnkiLCJzdGF0dXNQdXJwb3NlIjoicmV2b2NhdGlvbiIsInN0YXR1c0xpc3RJbmRleCI6IjExODQyNyIsInN0YXR1c0xpc3RDcmVkZW50aWFsIjoiaHR0cHM6Ly9yZXNvbHZlci5jaGVxZC5uZXQvMS4wL2lkZW50aWZpZXJzL2RpZDpjaGVxZDp0ZXN0bmV0OjMyMjc2MWVhLTU4N2QtNDU0YS1hOTU1LTc0NTIwMDMwMWI5OT9yZXNvdXJjZU5hbWU9cmV2b2NhdGlvbi1saXN0LXVuZW5jcnlwdGVkLXNob3ctYW5kLXRlbGwifX0sInN1YiI6ImRpZDprZXk6ejZNa3ZHNGRwS1ZwWXdZcW53Y2pSZHc4Vloza200U2lzZ3htMWlnYVBDRnpza3hlIiwibmJmIjoxNjg1NDQ3MTgyLCJpc3MiOiJkaWQ6Y2hlcWQ6dGVzdG5ldDozMjI3NjFlYS01ODdkLTQ1NGEtYTk1NS03NDUyMDAzMDFiOTkifQ.zViydlLYVRV7f8xSsawBzQap1wfsliKTaZ36WNURQSopRhvooHwL2jpD5AJF9rDqqqSsPQcOIkX8uxmseN8XDw" + } + } + ], + "holder": "did:key:z6MkvG4dpKVpYwYqnwcjRdw8VZ3km4Sisgxm1igaPCFzskxe", + "verifier": ["did:cheqd:testnet:322761ea-587d-454a-a955-745200301b99"], + "type": ["VerifiablePresentation"], + "@context": ["https://www.w3.org/2018/credentials/v1"], + "issuanceDate": "2023-05-30T11:48:17.000Z", + "proof": { + "type": "JwtProof2020", + "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2cCI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVQcmVzZW50YXRpb24iXSwidmVyaWZpYWJsZUNyZWRlbnRpYWwiOlsiZXlKaGJHY2lPaUpGWkVSVFFTSXNJblI1Y0NJNklrcFhWQ0o5LmV5SjJZeUk2ZXlKQVkyOXVkR1Y0ZENJNld5Sm9kSFJ3Y3pvdkwzZDNkeTUzTXk1dmNtY3ZNakF4T0M5amNtVmtaVzUwYVdGc2N5OTJNU0lzSW1oMGRIQnpPaTh2ZG1WeVlXMXZMbWx2TDJOdmJuUmxlSFJ6TDNCeWIyWnBiR1V2ZGpFaUxDSm9kSFJ3Y3pvdkwzY3phV1F1YjNKbkwzWmpMWE4wWVhSMWN5MXNhWE4wTFRJd01qRXZkakVpWFN3aWRIbHdaU0k2V3lKV1pYSnBabWxoWW14bFEzSmxaR1Z1ZEdsaGJDSmRMQ0pqY21Wa1pXNTBhV0ZzVTNWaWFtVmpkQ0k2ZXlKdVlXMWxJam9pWldWdVoybHVaV1Z5TVNKOUxDSmpjbVZrWlc1MGFXRnNVM1JoZEhWeklqcDdJbWxrSWpvaWFIUjBjSE02THk5eVpYTnZiSFpsY2k1amFHVnhaQzV1WlhRdk1TNHdMMmxrWlc1MGFXWnBaWEp6TDJScFpEcGphR1Z4WkRwMFpYTjBibVYwT2pNeU1qYzJNV1ZoTFRVNE4yUXRORFUwWVMxaE9UVTFMVGMwTlRJd01ETXdNV0k1T1Q5eVpYTnZkWEpqWlU1aGJXVTljbVYyYjJOaGRHbHZiaTFzYVhOMExYVnVaVzVqY25sd2RHVmtMWE5vYjNjdFlXNWtMWFJsYkd3ak1URTROREkzSWl3aWRIbHdaU0k2SWxOMFlYUjFjMHhwYzNReU1ESXhSVzUwY25raUxDSnpkR0YwZFhOUWRYSndiM05sSWpvaWNtVjJiMk5oZEdsdmJpSXNJbk4wWVhSMWMweHBjM1JKYm1SbGVDSTZJakV4T0RReU55SXNJbk4wWVhSMWMweHBjM1JEY21Wa1pXNTBhV0ZzSWpvaWFIUjBjSE02THk5eVpYTnZiSFpsY2k1amFHVnhaQzV1WlhRdk1TNHdMMmxrWlc1MGFXWnBaWEp6TDJScFpEcGphR1Z4WkRwMFpYTjBibVYwT2pNeU1qYzJNV1ZoTFRVNE4yUXRORFUwWVMxaE9UVTFMVGMwTlRJd01ETXdNV0k1T1Q5eVpYTnZkWEpqWlU1aGJXVTljbVYyYjJOaGRHbHZiaTFzYVhOMExYVnVaVzVqY25sd2RHVmtMWE5vYjNjdFlXNWtMWFJsYkd3aWZYMHNJbk4xWWlJNkltUnBaRHByWlhrNmVqWk5hM1pITkdSd1MxWndXWGRaY1c1M1kycFNaSGM0VmxvemEyMDBVMmx6WjNodE1XbG5ZVkJEUm5wemEzaGxJaXdpYm1KbUlqb3hOamcxTkRRM01UZ3lMQ0pwYzNNaU9pSmthV1E2WTJobGNXUTZkR1Z6ZEc1bGREb3pNakkzTmpGbFlTMDFPRGRrTFRRMU5HRXRZVGsxTlMwM05EVXlNREF6TURGaU9Ua2lmUS56Vml5ZGxMWVZSVjdmOHhTc2F3QnpRYXAxd2ZzbGlLVGFaMzZXTlVSUVNvcFJodm9vSHdMMmpwRDVBSkY5ckRxcXFTc1BRY09Ja1g4dXhtc2VOOFhEdyJdfSwibmJmIjoxNjg1NDQ3Mjk3LCJpc3MiOiJkaWQ6a2V5Ono2TWt2RzRkcEtWcFl3WXFud2NqUmR3OFZaM2ttNFNpc2d4bTFpZ2FQQ0Z6c2t4ZSIsImF1ZCI6WyJkaWQ6Y2hlcWQ6dGVzdG5ldDozMjI3NjFlYS01ODdkLTQ1NGEtYTk1NS03NDUyMDAzMDFiOTkiXX0.ji00YcfbWu6GQQ-omhQxtI9dxQYsv4arNrWEwLzUdYZ1X5BZNQR056HZOnSRNkpFtPpvv73RUY-kVdbf0NcnDA" + } + }, + "fetchList": true +} diff --git a/examples/transactions/observe-payment-conditions-from-unified-acc.json b/examples/transactions/observe-payment-conditions-from-unified-acc.json index c1fbf987..6044b278 100644 --- a/examples/transactions/observe-payment-conditions-from-unified-acc.json +++ b/examples/transactions/observe-payment-conditions-from-unified-acc.json @@ -1,15 +1,15 @@ { - "unifiedAccessControlCondition": { - "conditionType": "cosmos", - "path": "/cosmos/tx/v1beta1/txs?events=transfer.recipient='cheqd1xl5wccz667lk06ahama26pdqvrkz5aws6m0ztp'&events=transfer.amount='147603000000000ncheq'&order_by=2&pagination.limit=1", - "chain": "cheqdMainnet", - "method": "timelock", - "parameters": ["latest"], - "returnValueTest": { - "key": "$.tx_responses.*.timestamp", - "comparator": "<=", - "value": "3153600000" - } - }, - "returnTxResponse": true + "unifiedAccessControlCondition": { + "conditionType": "cosmos", + "path": "/cosmos/tx/v1beta1/txs?events=transfer.recipient='cheqd1xl5wccz667lk06ahama26pdqvrkz5aws6m0ztp'&events=transfer.amount='147603000000000ncheq'&order_by=2&pagination.limit=1", + "chain": "cheqdMainnet", + "method": "timelock", + "parameters": ["latest"], + "returnValueTest": { + "key": "$.tx_responses.*.timestamp", + "comparator": "<=", + "value": "3153600000" + } + }, + "returnTxResponse": true } diff --git a/examples/transactions/observe-payment-conditions.json b/examples/transactions/observe-payment-conditions.json index b51dae98..25d088a6 100644 --- a/examples/transactions/observe-payment-conditions.json +++ b/examples/transactions/observe-payment-conditions.json @@ -1,12 +1,12 @@ { - "recipientAddress": "cheqd1xl5wccz667lk06ahama26pdqvrkz5aws6m0ztp", - "amount": { - "denom": "ncheq", - "amount": "147603000000000" - }, - "intervalInSeconds": 3153600000, - "blockHeight": "latest", - "comparator": "<=", - "network": "mainnet", - "returnTxResponse": true + "recipientAddress": "cheqd1xl5wccz667lk06ahama26pdqvrkz5aws6m0ztp", + "amount": { + "denom": "ncheq", + "amount": "147603000000000" + }, + "intervalInSeconds": 3153600000, + "blockHeight": "latest", + "comparator": "<=", + "network": "mainnet", + "returnTxResponse": true } diff --git a/package-lock.json b/package-lock.json index 4b770eaa..faa7079b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,16 @@ { "name": "@cheqd/did-provider-cheqd", - "version": "3.6.9-develop.1", + "version": "3.6.10-develop.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@cheqd/did-provider-cheqd", - "version": "3.6.9-develop.1", + "version": "3.6.10-develop.1", "license": "Apache-2.0", "dependencies": { - "@cheqd/sdk": "^3.7.1", - "@cheqd/ts-proto": "^3.3.2", + "@cheqd/sdk": "^3.7.2", + "@cheqd/ts-proto": "^3.3.5", "@cosmjs/amino": "^0.31.1", "@cosmjs/crypto": "^0.31.1", "@cosmjs/proto-signing": "^0.31.1", @@ -34,7 +34,7 @@ "@semantic-release/changelog": "^6.0.3", "@semantic-release/commit-analyzer": "^11.0.0", "@semantic-release/git": "^10.0.1", - "@semantic-release/github": "^9.0.6", + "@semantic-release/github": "^9.1.0", "@semantic-release/npm": "^11.0.0", "@semantic-release/release-notes-generator": "^12.0.0", "@types/debug": "^4.1.8", @@ -49,7 +49,7 @@ "jest": "^29.7.0", "long": "^4.0.0", "prettier": "^3.0.3", - "semantic-release": "^22.0.0", + "semantic-release": "^22.0.5", "ts-jest": "^29.1.1", "typescript": "^5.2.2" }, @@ -748,11 +748,11 @@ } }, "node_modules/@cheqd/sdk": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/@cheqd/sdk/-/sdk-3.7.1.tgz", - "integrity": "sha512-cVek7qerGnhPrujncybThuNi/DzKwkbHY87pGBsHKswNdQVwJWOWf5gRwQMg0FC3NaKeH2moSZpTrTvGGCK+Uw==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@cheqd/sdk/-/sdk-3.7.2.tgz", + "integrity": "sha512-iA1mxJlx/PY+Dp2PDuwthnLHqfukTRRQCJ/KLcTz/j6pKsYL5ubrmoPQ8eAcmyPVCtLGjzcRpJNS8y3xPNJVzg==", "dependencies": { - "@cheqd/ts-proto": "^3.3.2", + "@cheqd/ts-proto": "^3.3.5", "@cosmjs/amino": "^0.31.1", "@cosmjs/crypto": "^0.31.1", "@cosmjs/encoding": "^0.31.1", @@ -762,7 +762,7 @@ "@cosmjs/tendermint-rpc": "^0.31.1", "@cosmjs/utils": "^0.31.1", "@stablelib/ed25519": "^1.0.3", - "@types/secp256k1": "^4.0.3", + "@types/secp256k1": "^4.0.4", "cosmjs-types": "^0.8.0", "did-jwt": "^7.2.8", "did-resolver": "^4.1.0", @@ -776,9 +776,9 @@ } }, "node_modules/@cheqd/ts-proto": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@cheqd/ts-proto/-/ts-proto-3.3.2.tgz", - "integrity": "sha512-CpA7d4MPquWFPuAS7Ex5WtErLKhdblyYex4j87RGxMAdyCm4RrAK2dJC5gZqpHVX4aMtlG1/qfeoaTNuczZYrg==", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@cheqd/ts-proto/-/ts-proto-3.3.5.tgz", + "integrity": "sha512-fgEG1CPMncvX8p7TWWc2XwLx5dAF1tgJYDweX/H9A2POojkq56YDBYurRzIfUlQQ0dlOQP8LIc71XC/aFpB1vQ==", "dependencies": { "long": "^5.2.3", "protobufjs": "^7.2.5" @@ -3361,12 +3361,12 @@ "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-8.0.0.tgz", - "integrity": "sha512-2xZ+baZWUg+qudVXnnvXz7qfrTmDeYPCzangBVq/1gXxii/OiS//4shJp9dnCCvj1x+JAm9ji1Egwm1BA47lPQ==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.0.0.tgz", + "integrity": "sha512-oIJzCpttmBTlEhBmRvb+b9rlnGpmFgDtZ0bB6nq39qIod6A5DP+7RkVLMOixIgRCYSHDTeayWqmiJ2SZ6xgfdw==", "dev": true, "dependencies": { - "@octokit/types": "^11.0.0" + "@octokit/types": "^12.0.0" }, "engines": { "node": ">= 18" @@ -3375,6 +3375,21 @@ "@octokit/core": ">=5" } }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.0.0.tgz", + "integrity": "sha512-PclQ6JGMTE9iUStpzMkwLCISFn/wDeRjkZFIKALpvJQNBGwDoYYi2fFvuHwssoQ1rXI5mfh6jgTgWuddeUzfWw==", + "dev": true + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.0.0.tgz", + "integrity": "sha512-EzD434aHTFifGudYAygnFlS1Tl6KhbTynEWELQXIbTY8Msvb5nEqTZIm7sbPEt4mQYLZwu3zPKVdeIrw0g7ovg==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^19.0.0" + } + }, "node_modules/@octokit/plugin-retry": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-6.0.0.tgz", @@ -3393,12 +3408,12 @@ } }, "node_modules/@octokit/plugin-throttling": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-7.0.0.tgz", - "integrity": "sha512-KL2k/d0uANc8XqP5S64YcNFCudR3F5AaKO39XWdUtlJIjT9Ni79ekWJ6Kj5xvAw87udkOMEPcVf9xEge2+ahew==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-8.0.0.tgz", + "integrity": "sha512-OkMbHYUidj81q92YRkPzWmwXkEtsI3KOcSkNm763aqUOh9IEplyX05XjKAdZFANAvaYH0Q4JBZwu4h2VnPVXZA==", "dev": true, "dependencies": { - "@octokit/types": "^11.0.0", + "@octokit/types": "^12.0.0", "bottleneck": "^2.15.3" }, "engines": { @@ -3408,6 +3423,21 @@ "@octokit/core": "^5.0.0" } }, + "node_modules/@octokit/plugin-throttling/node_modules/@octokit/openapi-types": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.0.0.tgz", + "integrity": "sha512-PclQ6JGMTE9iUStpzMkwLCISFn/wDeRjkZFIKALpvJQNBGwDoYYi2fFvuHwssoQ1rXI5mfh6jgTgWuddeUzfWw==", + "dev": true + }, + "node_modules/@octokit/plugin-throttling/node_modules/@octokit/types": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.0.0.tgz", + "integrity": "sha512-EzD434aHTFifGudYAygnFlS1Tl6KhbTynEWELQXIbTY8Msvb5nEqTZIm7sbPEt4mQYLZwu3zPKVdeIrw0g7ovg==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^19.0.0" + } + }, "node_modules/@octokit/request": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.1.tgz", @@ -3613,15 +3643,15 @@ } }, "node_modules/@semantic-release/github": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-9.0.6.tgz", - "integrity": "sha512-GBGt9c3c2UdSvso4jcyQQSUpZA9hbfHqGQerZKN9WvVzCIkaBy8xkhOyiFVX08LjRHHT/H221SJNBLtuihX5iw==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-9.1.0.tgz", + "integrity": "sha512-RKhS43OpNnQr9rA9Z72EbG8wM/UmWoCpop2z6G++ulRu7t8MuYS2v6TR2SoOFhcDPa2wY91v5VPouCWtDjz4+Q==", "dev": true, "dependencies": { "@octokit/core": "^5.0.0", - "@octokit/plugin-paginate-rest": "^8.0.0", + "@octokit/plugin-paginate-rest": "^9.0.0", "@octokit/plugin-retry": "^6.0.0", - "@octokit/plugin-throttling": "^7.0.0", + "@octokit/plugin-throttling": "^8.0.0", "@semantic-release/error": "^4.0.0", "aggregate-error": "^5.0.0", "debug": "^4.3.4", @@ -3961,6 +3991,18 @@ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true }, + "node_modules/@sindresorhus/is": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-3.1.2.tgz", + "integrity": "sha512-JiX9vxoKMmu8Y3Zr2RVathBL1Cdu4Nt4MuNWemt1Nc06A0RAin9c5FArkhGsyMBWfCu4zj+9b+GxtjAnE4qqLQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, "node_modules/@sinonjs/commons": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", @@ -4438,9 +4480,9 @@ "dev": true }, "node_modules/@types/secp256k1": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz", - "integrity": "sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.4.tgz", + "integrity": "sha512-oN0PFsYxDZnX/qSJ5S5OwaEDTYfekhvaM5vqui2bu1AA39pKofmgL104Q29KiOXizXS2yLjSzc5YdTyMKdcy4A==", "dependencies": { "@types/node": "*" } @@ -6654,6 +6696,12 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "node_modules/emojilib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", + "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", + "dev": true + }, "node_modules/encode-utf8": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz", @@ -6676,60 +6724,60 @@ } }, "node_modules/env-ci": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-9.1.1.tgz", - "integrity": "sha512-Im2yEWeF4b2RAMAaWvGioXk6m0UNaIjD8hj28j2ij5ldnIFrDQT0+pzDvpbRkcjurhXhf/AsBKv8P2rtmGi9Aw==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-10.0.0.tgz", + "integrity": "sha512-U4xcd/utDYFgMh0yWj07R1H6L5fwhVbmxBCpnL0DbVSDZVnsC82HONw0wxtxNkIAcua3KtbomQvIk5xFZGAQJw==", "dev": true, "dependencies": { - "execa": "^7.0.0", + "execa": "^8.0.0", "java-properties": "^1.0.2" }, "engines": { - "node": "^16.14 || >=18" + "node": "^18.17 || >=20.6.1" } }, "node_modules/env-ci/node_modules/execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, "dependencies": { "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", - "signal-exit": "^3.0.7", + "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" }, "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + "node": ">=16.17" }, "funding": { "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, "node_modules/env-ci/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, "engines": { - "node": ">=10" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/env-ci/node_modules/human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, "engines": { - "node": ">=14.18.0" + "node": ">=16.17.0" } }, "node_modules/env-ci/node_modules/is-stream": { @@ -6798,6 +6846,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/env-ci/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/env-ci/node_modules/strip-final-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", @@ -10105,9 +10165,9 @@ } }, "node_modules/marked": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-5.1.2.tgz", - "integrity": "sha512-ahRPGXJpjMjwSOlBoTMZAK7ATXkli5qCPxZ21TG44rx1KEo44bii4ekgTDQPNRQ4Kh7JMb9Ub1PVk1NxRSsorg==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-9.0.3.tgz", + "integrity": "sha512-pI/k4nzBG1PEq1J3XFEHxVvjicfjl8rgaMaqclouGSMPhk7Q3Ejb2ZRxx/ZQOcQ1909HzVoWCFYq6oLgtL4BpQ==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -10117,23 +10177,23 @@ } }, "node_modules/marked-terminal": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-5.2.0.tgz", - "integrity": "sha512-Piv6yNwAQXGFjZSaiNljyNFw7jKDdGrw70FSbtxEyldLsyeuV5ZHm/1wW++kWbrOF1VPnUgYOhB2oLL0ZpnekA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-6.0.0.tgz", + "integrity": "sha512-6rruICvqRfA4N+Mvdc0UyDbLA0A0nI5omtARIlin3P2F+aNc3EbW91Rd9HTuD0v9qWyHmNIu8Bt40gAnPfldsg==", "dev": true, "dependencies": { "ansi-escapes": "^6.2.0", "cardinal": "^2.1.1", - "chalk": "^5.2.0", + "chalk": "^5.3.0", "cli-table3": "^0.6.3", - "node-emoji": "^1.11.0", - "supports-hyperlinks": "^2.3.0" + "node-emoji": "^2.1.0", + "supports-hyperlinks": "^3.0.0" }, "engines": { - "node": ">=14.13.1 || >=16.0.0" + "node": ">=16.0.0" }, "peerDependencies": { - "marked": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0" + "marked": ">=1 <10" } }, "node_modules/marked-terminal/node_modules/ansi-escapes": { @@ -10443,12 +10503,15 @@ } }, "node_modules/node-emoji": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", - "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.0.tgz", + "integrity": "sha512-tcsBm9C6FmPN5Wo7OjFi9lgMyJjvkAeirmjR/ax8Ttfqy4N8PoFic26uqFTIgayHPNI5FH4ltUvfh9kHzwcK9A==", "dev": true, "dependencies": { - "lodash": "^4.17.21" + "@sindresorhus/is": "^3.1.2", + "char-regex": "^1.0.2", + "emojilib": "^2.4.0", + "skin-tone": "^2.0.0" } }, "node_modules/node-fetch": { @@ -15235,20 +15298,20 @@ } }, "node_modules/semantic-release": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-22.0.0.tgz", - "integrity": "sha512-WTD8zPxh+pyc/DrTALRHJ47p1XbKqi2AJljn5WkXgLFFIMSax4uu15u4ZEZaa7ftBo8cSajh16VeafgUu9DX8g==", + "version": "22.0.5", + "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-22.0.5.tgz", + "integrity": "sha512-ESCEQsZlBj1DWMA84RthaJzQHHnihoGk49s9nUxHfRNUNZelLE9JZrE94bHO2Y00EWb7iwrzr1OYhv5QNVmf8A==", "dev": true, "dependencies": { - "@semantic-release/commit-analyzer": "^11.0.0-beta.3", + "@semantic-release/commit-analyzer": "^11.0.0", "@semantic-release/error": "^4.0.0", "@semantic-release/github": "^9.0.0", "@semantic-release/npm": "^11.0.0", - "@semantic-release/release-notes-generator": "^12.0.0-beta.2", + "@semantic-release/release-notes-generator": "^12.0.0", "aggregate-error": "^5.0.0", "cosmiconfig": "^8.0.0", "debug": "^4.0.0", - "env-ci": "^9.0.0", + "env-ci": "^10.0.0", "execa": "^8.0.0", "figures": "^5.0.0", "find-versions": "^5.1.0", @@ -15257,8 +15320,8 @@ "hook-std": "^3.0.0", "hosted-git-info": "^7.0.0", "lodash-es": "^4.17.21", - "marked": "^5.0.0", - "marked-terminal": "^5.1.1", + "marked": "^9.0.0", + "marked-terminal": "^6.0.0", "micromatch": "^4.0.2", "p-each-series": "^3.0.0", "p-reduce": "^3.0.0", @@ -15768,6 +15831,18 @@ "npm": ">=7.0.0" } }, + "node_modules/skin-tone": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", + "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", + "dev": true, + "dependencies": { + "unicode-emoji-modifier-base": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/slash": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", @@ -16034,16 +16109,16 @@ } }, "node_modules/supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz", + "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==", "dev": true, "dependencies": { "has-flag": "^4.0.0", "supports-color": "^7.0.0" }, "engines": { - "node": ">=8" + "node": ">=14.18" } }, "node_modules/supports-preserve-symlinks-flag": { @@ -16406,6 +16481,15 @@ "node": ">=14.0" } }, + "node_modules/unicode-emoji-modifier-base": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", + "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/unique-string": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", diff --git a/package.json b/package.json index ac7648bb..54a391b9 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "test": "jest --passWithNoTests", "test:watch": "yarn test --watch --verbose", "clean": "rm -rf tmp && rm database.sqlite && rm -rf build && rm tsconfig.tsbuildinfo", + "format": "prettier --write 'src/**/*.{js,ts,cjs,mjs}' 'examples/**/*.{js,json}' 'tests/**/*.{js,ts,cjs,mjs}'", "lint": "eslint --max-warnings=0 src", "semantic": "npx semantic-release" }, @@ -92,8 +93,8 @@ ] }, "dependencies": { - "@cheqd/sdk": "^3.7.1", - "@cheqd/ts-proto": "^3.3.2", + "@cheqd/sdk": "^3.7.2", + "@cheqd/ts-proto": "^3.3.5", "@cosmjs/amino": "^0.31.1", "@cosmjs/crypto": "^0.31.1", "@cosmjs/proto-signing": "^0.31.1", @@ -117,7 +118,7 @@ "@semantic-release/changelog": "^6.0.3", "@semantic-release/commit-analyzer": "^11.0.0", "@semantic-release/git": "^10.0.1", - "@semantic-release/github": "^9.0.6", + "@semantic-release/github": "^9.1.0", "@semantic-release/npm": "^11.0.0", "@semantic-release/release-notes-generator": "^12.0.0", "@types/debug": "^4.1.8", @@ -132,7 +133,7 @@ "jest": "^29.7.0", "long": "^4.0.0", "prettier": "^3.0.3", - "semantic-release": "^22.0.0", + "semantic-release": "^22.0.5", "ts-jest": "^29.1.1", "typescript": "^5.2.2" }, diff --git a/src/agent/ICheqd.ts b/src/agent/ICheqd.ts index 56bf3814..84d27e15 100644 --- a/src/agent/ICheqd.ts +++ b/src/agent/ICheqd.ts @@ -3,4839 +3,6618 @@ // unused vars are kept by convention // non-null assertion is used when we know better than the compiler that the value is not null or undefined import { - CheqdNetwork, - DIDDocument, - DidStdFee, - IKeyPair, - ISignInputs, - MethodSpecificIdAlgo, - ResourceModule, - VerificationMethods, - createDidPayload, - createDidVerificationMethod, - createKeyPairBase64, - createKeyPairHex, - createVerificationKeys -} from '@cheqd/sdk' + CheqdNetwork, + DIDDocument, + DidStdFee, + IKeyPair, + ISignInputs, + MethodSpecificIdAlgo, + ResourceModule, + VerificationMethods, + createDidPayload, + createDidVerificationMethod, + createKeyPairBase64, + createKeyPairHex, + createVerificationKeys, +} from '@cheqd/sdk'; +import { Coin, DeliverTxResponse } from '@cosmjs/stargate'; import { - Coin, - DeliverTxResponse -} from '@cosmjs/stargate' + IAgentContext, + IKeyManager, + IAgentPlugin, + IPluginMethodMap, + IAgentPluginSchema, + IIdentifier, + VerifiableCredential, + IVerifyCredentialArgs, + IVerifyResult, + VerifiablePresentation, + IVerifyPresentationArgs, + IError, + ICreateVerifiableCredentialArgs, + ICredentialIssuer, + IDIDManager, + IDataStore, + IResolver, + W3CVerifiableCredential, + ICredentialVerifier, +} from '@veramo/core'; import { - IAgentContext, - IKeyManager, - IAgentPlugin, - IPluginMethodMap, - IAgentPluginSchema, - IIdentifier, - VerifiableCredential, - IVerifyCredentialArgs, - IVerifyResult, - VerifiablePresentation, - IVerifyPresentationArgs, - IError, - ICreateVerifiableCredentialArgs, - ICredentialIssuer, - IDIDManager, - IDataStore, - IResolver, - W3CVerifiableCredential, - ICredentialVerifier, -} from '@veramo/core' + CheqdDIDProvider, + LinkedResource, + TImportableEd25519Key, + ResourcePayload, + StatusList2021ResourcePayload, + DefaultRESTUrls, + DefaultStatusList2021Encodings, + DefaultStatusList2021ResourceTypes, + DefaultStatusList2021StatusPurposeTypes, + DefaultStatusList2021Encoding, + DefaultStatusList2021ResourceType, + DefaultStatusList2021StatusPurposeType, +} from '../did-manager/cheqd-did-provider.js'; +import { fromString, toString } from 'uint8arrays'; +import { decodeJWT } from 'did-jwt'; +import { StatusList } from '@digitalbazaar/vc-status-list'; +import { v4 } from 'uuid'; +import fs from 'fs'; +import Debug from 'debug'; import { - CheqdDIDProvider, - LinkedResource, - TImportableEd25519Key, - ResourcePayload, - StatusList2021ResourcePayload, - DefaultRESTUrls, - DefaultStatusList2021Encodings, - DefaultStatusList2021ResourceTypes, - DefaultStatusList2021StatusPurposeTypes, - DefaultStatusList2021Encoding, - DefaultStatusList2021ResourceType, - DefaultStatusList2021StatusPurposeType, -} from '../did-manager/cheqd-did-provider.js' -import { - fromString, - toString -} from 'uint8arrays' -import { decodeJWT } from 'did-jwt' -import { StatusList } from '@digitalbazaar/vc-status-list' -import { v4 } from 'uuid' -import fs from 'fs' -import Debug from 'debug' -import { - CosmosAccessControlCondition, - LitCompatibleCosmosChain, - LitCompatibleCosmosChains, - LitNetwork, - LitProtocol, + CosmosAccessControlCondition, + LitCompatibleCosmosChain, + LitCompatibleCosmosChains, + LitNetwork, + LitProtocol, } from '../dkg-threshold/lit-protocol.js'; -import { - blobToHexString, - randomFromRange, - toBlob, -} from '../utils/helpers.js' -import { DefaultResolverUrl } from '../did-manager/cheqd-did-resolver.js' -import { AlternativeUri } from '@cheqd/ts-proto/cheqd/resource/v2/resource.js' - -const debug = Debug('veramo:did-provider-cheqd') - -export type IContext = IAgentContext -export type TExportedDIDDocWithKeys = { didDoc: DIDDocument, keys: TImportableEd25519Key[], versionId?: string } -export type TExportedDIDDocWithLinkedResourceWithKeys = TExportedDIDDocWithKeys & { linkedResource: LinkedResource } -export type LinkedResourceMetadataResolutionResult = { resourceURI: string, resourceCollectionId: string, resourceId: string, resourceName: string, resourceType: string, mediaType: string, resourceVersion?: string, created: string, checksum: string, previousVersionId: string | null, nextVersionId: string | null } -export type DIDMetadataDereferencingResult = { '@context': 'https://w3id.org/did-resolution/v1', dereferencingMetadata: { contentType: string, error?: string, retrieved: string, did: { didString: string, methodSpecificId: string, method: string } }, contentStream: { created: string, versionId: string, linkedResourceMetadata: LinkedResourceMetadataResolutionResult[] }, contentMetadata: Record } -export type ShallowTypedTx = { body: { messages: any[], memo: string, timeout_height: string, extension_options: any[], non_critical_extension_options: any[] }, auth_info: { signer_infos: { public_key: { '@type': string, key: string }, mode_info: { single: { mode: string } }, sequence: string }[], fee: { amount: Coin[], gas_limit: string, payer: string, granter: string }, tip: any | null }, signatures: string[] } -export type ShallowTypedTxTxResponses = { height: string, txhash: string, codespace: string, code: number, data: string, raw_log: string, logs: any[], info: string, gas_wanted: string, gas_used: string, tx: ShallowTypedTx, timestamp: string, events: any[] } -export type ShallowTypedTxsResponse = { txs: ShallowTypedTx[], tx_responses: ShallowTypedTxTxResponses[], pagination: string | null, total: string } | undefined -export type BlockResponse = { block_id: BlockID, block: Block, sdk_block: Block} -export type Block = { header: Header, data: Data, evidence: Evidence, last_commit: LastCommit } -export type Data = { txs: any[] } -export type Evidence = { evidence: any[] } -export type Header = { version: Version, chain_id: string, height: string, time: string, last_block_id: BlockID, last_commit_hash: string, data_hash: string, validators_hash: string, next_validators_hash: string, consensus_hash: string, app_hash: string, last_results_hash: string, evidence_hash: string, proposer_address: string } -export type BlockID = { hash: string, part_set_header: PartSetHeader } -export type PartSetHeader = { total: number, hash: string } -export type Version = { block: string, app: string } -export type LastCommit = { height: string, round: number, block_id: BlockID, signatures: Signature[] } -export type Signature = { block_id_flag: string, validator_address?: string, timestamp: Date, signature?: string } -export type VerificationResult = { verified: boolean, revoked?: boolean, suspended?: boolean, error?: IVerifyResult['error'] } -export type StatusCheckResult = { revoked?: boolean, suspended?: boolean, error?: IError } -export type RevocationResult = { revoked: boolean, error?: IError, statusList?: StatusList2021Revocation, symmetricKey?: string, published?: boolean, resourceMetadata?: LinkedResourceMetadataResolutionResult } -export type BulkRevocationResult = { revoked: boolean[], error?: IError, statusList?: StatusList2021Revocation, symmetricKey?: string, published?: boolean, resourceMetadata?: LinkedResourceMetadataResolutionResult } -export type SuspensionResult = { suspended: boolean, error?: IError, statusList?: StatusList2021Suspension, symmetricKey?: string, published?: boolean, resourceMetadata?: LinkedResourceMetadataResolutionResult } -export type BulkSuspensionResult = { suspended: boolean[], error?: IError, statusList?: StatusList2021Suspension, symmetricKey?: string, published?: boolean, resourceMetadata?: LinkedResourceMetadataResolutionResult } -export type UnsuspensionResult = { unsuspended: boolean, error?: IError, statusList?: StatusList2021Suspension, symmetricKey?: string, published?: boolean, resourceMetadata?: LinkedResourceMetadataResolutionResult } -export type BulkUnsuspensionResult = { unsuspended: boolean[], error?: IError, statusList?: StatusList2021Suspension, symmetricKey?: string, published?: boolean, resourceMetadata?: LinkedResourceMetadataResolutionResult } -export type Bitstring = string -export type StatusList2021Revocation = { StatusList2021: { statusPurpose: typeof DefaultStatusList2021StatusPurposeTypes.revocation, encodedList: string, validFrom: string, validUntil?: string }, metadata: { type: typeof DefaultStatusList2021ResourceTypes.revocation, encrypted: boolean, encoding: DefaultStatusList2021Encoding, encryptedSymmetricKey?: string, paymentConditions?: PaymentCondition[] } } -export type StatusList2021Suspension = { StatusList2021: { statusPurpose: typeof DefaultStatusList2021StatusPurposeTypes.suspension, encodedList: string, validFrom: string, validUntil?: string }, metadata: { type: typeof DefaultStatusList2021ResourceTypes.suspension, encrypted: boolean, encoding: DefaultStatusList2021Encoding, encryptedSymmetricKey?: string, paymentConditions?: PaymentCondition[] } } -export type AccessControlConditionType = typeof AccessControlConditionTypes[keyof typeof AccessControlConditionTypes] -export type AccessControlConditionReturnValueComparator = typeof AccessControlConditionReturnValueComparators[keyof typeof AccessControlConditionReturnValueComparators] -export type PaymentCondition = { feePaymentAddress: string, feePaymentAmount: string, intervalInSeconds: number, blockHeight?: string, type: Extract } -export type DkgOptions = { chain?: Extract, network?: LitNetwork } -export type CreateStatusList2021Result = { created: boolean, error?: Error, resource: StatusList2021Revocation | StatusList2021Suspension, resourceMetadata: LinkedResourceMetadataResolutionResult, encrypted?: boolean, symmetricKey?: string } -export type TransactionResult = { successful: boolean, transactionHash?: string, events?: DeliverTxResponse['events'], rawLog?: string, txResponse?: DeliverTxResponse, error?: IError } -export type ObservationResult = { subscribed: boolean, meetsCondition: boolean, transactionHash?: string, events?: DeliverTxResponse['events'], rawLog?: string, txResponse?: ShallowTypedTxTxResponses, error?: IError } - -export const AccessControlConditionTypes = { timelockPayment: 'timelockPayment', memoNonce: 'memoNonce', balance: 'balance' } as const -export const AccessControlConditionReturnValueComparators = { lessThan: '<', greaterThan: '>', equalTo: '=', lessThanOrEqualTo: '<=', greaterThanOrEqualTo: '>=' } as const - -export const RemoteListPattern = /^(https:\/\/)?[a-z0-9_-]+(\.[a-z0-9_-]+)*\.[a-z]{2,}\/1\.0\/identifiers\/did:cheqd:[a-z]+:[a-zA-Z0-9-]+\?((resourceName=[^&]*)&(resourceType=[^&]*)|((resourceType=[^&]*)&(resourceName=[^&]*)))$/ - -export const CreateIdentifierMethodName = 'cheqdCreateIdentifier' -export const UpdateIdentifierMethodName = 'cheqdUpdateIdentifier' -export const DeactivateIdentifierMethodName = 'cheqdDeactivateIdentifier' -export const CreateResourceMethodName = 'cheqdCreateLinkedResource' -export const CreateStatusList2021MethodName = 'cheqdCreateStatusList2021' -export const BroadcastStatusList2021MethodName = 'cheqdBroadcastStatusList2021' -export const GenerateDidDocMethodName = 'cheqdGenerateDidDoc' -export const GenerateDidDocWithLinkedResourceMethodName = 'cheqdGenerateDidDocWithLinkedResource' -export const GenerateKeyPairMethodName = 'cheqdGenerateIdentityKeys' -export const GenerateVersionIdMethodName = 'cheqdGenerateVersionId' -export const GenerateStatusList2021MethodName = 'cheqdGenerateStatusList2021' -export const IssueRevocableCredentialWithStatusList2021MethodName = 'cheqdIssueRevocableCredentialWithStatusList2021' -export const IssueSuspendableCredentialWithStatusList2021MethodName = 'cheqdIssueSuspendableCredentialWithStatusList2021' -export const VerifyCredentialMethodName = 'cheqdVerifyCredential' -export const VerifyPresentationMethodName = 'cheqdVerifyPresentation' -export const CheckCredentialStatusMethodName = 'cheqdCheckCredentialStatus' -export const RevokeCredentialMethodName = 'cheqdRevokeCredential' -export const RevokeCredentialsMethodName = 'cheqdRevokeCredentials' -export const SuspendCredentialMethodName = 'cheqdSuspendCredential' -export const SuspendCredentialsMethodName = 'cheqdSuspendCredentials' -export const UnsuspendCredentialMethodName = 'cheqdUnsuspendCredential' -export const UnsuspendCredentialsMethodName = 'cheqdUnsuspendCredentials' -export const TransactSendTokensMethodName = 'cheqdTransactSendTokens' -export const ObservePaymentConditionMethodName = 'cheqdObservePaymentCondition' - -export const DidPrefix = 'did' -export const CheqdDidMethod = 'cheqd' +import { blobToHexString, randomFromRange, toBlob } from '../utils/helpers.js'; +import { DefaultResolverUrl } from '../did-manager/cheqd-did-resolver.js'; +import { AlternativeUri } from '@cheqd/ts-proto/cheqd/resource/v2/resource.js'; + +const debug = Debug('veramo:did-provider-cheqd'); + +export type IContext = IAgentContext< + IDIDManager & IKeyManager & IDataStore & IResolver & ICredentialIssuer & ICredentialVerifier & ICheqd +>; +export type TExportedDIDDocWithKeys = { didDoc: DIDDocument; keys: TImportableEd25519Key[]; versionId?: string }; +export type TExportedDIDDocWithLinkedResourceWithKeys = TExportedDIDDocWithKeys & { linkedResource: LinkedResource }; +export type LinkedResourceMetadataResolutionResult = { + resourceURI: string; + resourceCollectionId: string; + resourceId: string; + resourceName: string; + resourceType: string; + mediaType: string; + resourceVersion?: string; + created: string; + checksum: string; + previousVersionId: string | null; + nextVersionId: string | null; +}; +export type DIDMetadataDereferencingResult = { + '@context': 'https://w3id.org/did-resolution/v1'; + dereferencingMetadata: { + contentType: string; + error?: string; + retrieved: string; + did: { didString: string; methodSpecificId: string; method: string }; + }; + contentStream: { + created: string; + versionId: string; + linkedResourceMetadata: LinkedResourceMetadataResolutionResult[]; + }; + contentMetadata: Record; +}; +export type ShallowTypedTx = { + body: { + messages: any[]; + memo: string; + timeout_height: string; + extension_options: any[]; + non_critical_extension_options: any[]; + }; + auth_info: { + signer_infos: { + public_key: { '@type': string; key: string }; + mode_info: { single: { mode: string } }; + sequence: string; + }[]; + fee: { amount: Coin[]; gas_limit: string; payer: string; granter: string }; + tip: any | null; + }; + signatures: string[]; +}; +export type ShallowTypedTxTxResponses = { + height: string; + txhash: string; + codespace: string; + code: number; + data: string; + raw_log: string; + logs: any[]; + info: string; + gas_wanted: string; + gas_used: string; + tx: ShallowTypedTx; + timestamp: string; + events: any[]; +}; +export type ShallowTypedTxsResponse = + | { txs: ShallowTypedTx[]; tx_responses: ShallowTypedTxTxResponses[]; pagination: string | null; total: string } + | undefined; +export type BlockResponse = { block_id: BlockID; block: Block; sdk_block: Block }; +export type Block = { header: Header; data: Data; evidence: Evidence; last_commit: LastCommit }; +export type Data = { txs: any[] }; +export type Evidence = { evidence: any[] }; +export type Header = { + version: Version; + chain_id: string; + height: string; + time: string; + last_block_id: BlockID; + last_commit_hash: string; + data_hash: string; + validators_hash: string; + next_validators_hash: string; + consensus_hash: string; + app_hash: string; + last_results_hash: string; + evidence_hash: string; + proposer_address: string; +}; +export type BlockID = { hash: string; part_set_header: PartSetHeader }; +export type PartSetHeader = { total: number; hash: string }; +export type Version = { block: string; app: string }; +export type LastCommit = { height: string; round: number; block_id: BlockID; signatures: Signature[] }; +export type Signature = { block_id_flag: string; validator_address?: string; timestamp: Date; signature?: string }; +export type VerificationResult = { + verified: boolean; + revoked?: boolean; + suspended?: boolean; + error?: IVerifyResult['error']; +}; +export type StatusCheckResult = { revoked?: boolean; suspended?: boolean; error?: IError }; +export type RevocationResult = { + revoked: boolean; + error?: IError; + statusList?: StatusList2021Revocation; + symmetricKey?: string; + published?: boolean; + resourceMetadata?: LinkedResourceMetadataResolutionResult; +}; +export type BulkRevocationResult = { + revoked: boolean[]; + error?: IError; + statusList?: StatusList2021Revocation; + symmetricKey?: string; + published?: boolean; + resourceMetadata?: LinkedResourceMetadataResolutionResult; +}; +export type SuspensionResult = { + suspended: boolean; + error?: IError; + statusList?: StatusList2021Suspension; + symmetricKey?: string; + published?: boolean; + resourceMetadata?: LinkedResourceMetadataResolutionResult; +}; +export type BulkSuspensionResult = { + suspended: boolean[]; + error?: IError; + statusList?: StatusList2021Suspension; + symmetricKey?: string; + published?: boolean; + resourceMetadata?: LinkedResourceMetadataResolutionResult; +}; +export type UnsuspensionResult = { + unsuspended: boolean; + error?: IError; + statusList?: StatusList2021Suspension; + symmetricKey?: string; + published?: boolean; + resourceMetadata?: LinkedResourceMetadataResolutionResult; +}; +export type BulkUnsuspensionResult = { + unsuspended: boolean[]; + error?: IError; + statusList?: StatusList2021Suspension; + symmetricKey?: string; + published?: boolean; + resourceMetadata?: LinkedResourceMetadataResolutionResult; +}; +export type Bitstring = string; +export type StatusList2021Revocation = { + StatusList2021: { + statusPurpose: typeof DefaultStatusList2021StatusPurposeTypes.revocation; + encodedList: string; + validFrom: string; + validUntil?: string; + }; + metadata: { + type: typeof DefaultStatusList2021ResourceTypes.revocation; + encrypted: boolean; + encoding: DefaultStatusList2021Encoding; + encryptedSymmetricKey?: string; + paymentConditions?: PaymentCondition[]; + }; +}; +export type StatusList2021Suspension = { + StatusList2021: { + statusPurpose: typeof DefaultStatusList2021StatusPurposeTypes.suspension; + encodedList: string; + validFrom: string; + validUntil?: string; + }; + metadata: { + type: typeof DefaultStatusList2021ResourceTypes.suspension; + encrypted: boolean; + encoding: DefaultStatusList2021Encoding; + encryptedSymmetricKey?: string; + paymentConditions?: PaymentCondition[]; + }; +}; +export type AccessControlConditionType = (typeof AccessControlConditionTypes)[keyof typeof AccessControlConditionTypes]; +export type AccessControlConditionReturnValueComparator = + (typeof AccessControlConditionReturnValueComparators)[keyof typeof AccessControlConditionReturnValueComparators]; +export type PaymentCondition = { + feePaymentAddress: string; + feePaymentAmount: string; + intervalInSeconds: number; + blockHeight?: string; + type: Extract; +}; +export type DkgOptions = { + chain?: Extract; + network?: LitNetwork; +}; +export type CreateStatusList2021Result = { + created: boolean; + error?: Error; + resource: StatusList2021Revocation | StatusList2021Suspension; + resourceMetadata: LinkedResourceMetadataResolutionResult; + encrypted?: boolean; + symmetricKey?: string; +}; +export type TransactionResult = { + successful: boolean; + transactionHash?: string; + events?: DeliverTxResponse['events']; + rawLog?: string; + txResponse?: DeliverTxResponse; + error?: IError; +}; +export type ObservationResult = { + subscribed: boolean; + meetsCondition: boolean; + transactionHash?: string; + events?: DeliverTxResponse['events']; + rawLog?: string; + txResponse?: ShallowTypedTxTxResponses; + error?: IError; +}; + +export const AccessControlConditionTypes = { + timelockPayment: 'timelockPayment', + memoNonce: 'memoNonce', + balance: 'balance', +} as const; +export const AccessControlConditionReturnValueComparators = { + lessThan: '<', + greaterThan: '>', + equalTo: '=', + lessThanOrEqualTo: '<=', + greaterThanOrEqualTo: '>=', +} as const; + +export const RemoteListPattern = + /^(https:\/\/)?[a-z0-9_-]+(\.[a-z0-9_-]+)*\.[a-z]{2,}\/1\.0\/identifiers\/did:cheqd:[a-z]+:[a-zA-Z0-9-]+\?((resourceName=[^&]*)&(resourceType=[^&]*)|((resourceType=[^&]*)&(resourceName=[^&]*)))$/; + +export const CreateIdentifierMethodName = 'cheqdCreateIdentifier'; +export const UpdateIdentifierMethodName = 'cheqdUpdateIdentifier'; +export const DeactivateIdentifierMethodName = 'cheqdDeactivateIdentifier'; +export const CreateResourceMethodName = 'cheqdCreateLinkedResource'; +export const CreateStatusList2021MethodName = 'cheqdCreateStatusList2021'; +export const BroadcastStatusList2021MethodName = 'cheqdBroadcastStatusList2021'; +export const GenerateDidDocMethodName = 'cheqdGenerateDidDoc'; +export const GenerateDidDocWithLinkedResourceMethodName = 'cheqdGenerateDidDocWithLinkedResource'; +export const GenerateKeyPairMethodName = 'cheqdGenerateIdentityKeys'; +export const GenerateVersionIdMethodName = 'cheqdGenerateVersionId'; +export const GenerateStatusList2021MethodName = 'cheqdGenerateStatusList2021'; +export const IssueRevocableCredentialWithStatusList2021MethodName = 'cheqdIssueRevocableCredentialWithStatusList2021'; +export const IssueSuspendableCredentialWithStatusList2021MethodName = + 'cheqdIssueSuspendableCredentialWithStatusList2021'; +export const VerifyCredentialMethodName = 'cheqdVerifyCredential'; +export const VerifyPresentationMethodName = 'cheqdVerifyPresentation'; +export const CheckCredentialStatusMethodName = 'cheqdCheckCredentialStatus'; +export const RevokeCredentialMethodName = 'cheqdRevokeCredential'; +export const RevokeCredentialsMethodName = 'cheqdRevokeCredentials'; +export const SuspendCredentialMethodName = 'cheqdSuspendCredential'; +export const SuspendCredentialsMethodName = 'cheqdSuspendCredentials'; +export const UnsuspendCredentialMethodName = 'cheqdUnsuspendCredential'; +export const UnsuspendCredentialsMethodName = 'cheqdUnsuspendCredentials'; +export const TransactSendTokensMethodName = 'cheqdTransactSendTokens'; +export const ObservePaymentConditionMethodName = 'cheqdObservePaymentCondition'; + +export const DidPrefix = 'did'; +export const CheqdDidMethod = 'cheqd'; export interface ICheqdCreateIdentifierArgs { - kms: string - alias: string - document: DIDDocument - keys?: TImportableEd25519Key[] - versionId?: string - fee?: DidStdFee + kms: string; + alias: string; + document: DIDDocument; + keys?: TImportableEd25519Key[]; + versionId?: string; + fee?: DidStdFee; } export interface ICheqdUpdateIdentifierArgs { - kms: string - document: DIDDocument - keys?: TImportableEd25519Key[] - versionId?: string - fee?: DidStdFee + kms: string; + document: DIDDocument; + keys?: TImportableEd25519Key[]; + versionId?: string; + fee?: DidStdFee; } export interface ICheqdDeactivateIdentifierArgs { - kms: string - document: DIDDocument - keys?: TImportableEd25519Key[] - fee?: DidStdFee + kms: string; + document: DIDDocument; + keys?: TImportableEd25519Key[]; + fee?: DidStdFee; } export interface ICheqdCreateLinkedResourceArgs { - kms: string - payload: ResourcePayload - network: CheqdNetwork - file?: string - signInputs?: ISignInputs[] - fee?: DidStdFee + kms: string; + payload: ResourcePayload; + network: CheqdNetwork; + file?: string; + signInputs?: ISignInputs[]; + fee?: DidStdFee; } export interface ICheqdCreateStatusList2021Args { - kms: string - issuerDid: string - statusListName: string - statusPurpose: DefaultStatusList2021StatusPurposeType - encrypted: boolean - paymentConditions?: PaymentCondition[] - dkgOptions?: DkgOptions - resourceVersion?: ResourcePayload['version'] - alsoKnownAs?: ResourcePayload['alsoKnownAs'] - statusListLength?: number - statusListEncoding?: DefaultStatusList2021Encoding - validUntil?: string - returnSymmetricKey?: boolean + kms: string; + issuerDid: string; + statusListName: string; + statusPurpose: DefaultStatusList2021StatusPurposeType; + encrypted: boolean; + paymentConditions?: PaymentCondition[]; + dkgOptions?: DkgOptions; + resourceVersion?: ResourcePayload['version']; + alsoKnownAs?: ResourcePayload['alsoKnownAs']; + statusListLength?: number; + statusListEncoding?: DefaultStatusList2021Encoding; + validUntil?: string; + returnSymmetricKey?: boolean; } export interface ICheqdCreateUnencryptedStatusList2021Args { - kms: string - payload: StatusList2021ResourcePayload - network: CheqdNetwork - file?: string - signInputs?: ISignInputs[] - fee?: DidStdFee + kms: string; + payload: StatusList2021ResourcePayload; + network: CheqdNetwork; + file?: string; + signInputs?: ISignInputs[]; + fee?: DidStdFee; } export interface ICheqdBroadcastStatusList2021Args { - kms: string - payload: StatusList2021ResourcePayload - network: CheqdNetwork - file?: string - signInputs?: ISignInputs[] - fee?: DidStdFee + kms: string; + payload: StatusList2021ResourcePayload; + network: CheqdNetwork; + file?: string; + signInputs?: ISignInputs[]; + fee?: DidStdFee; } export interface ICheqdGenerateDidDocArgs { - verificationMethod: VerificationMethods - methodSpecificIdAlgo: MethodSpecificIdAlgo - network: CheqdNetwork + verificationMethod: VerificationMethods; + methodSpecificIdAlgo: MethodSpecificIdAlgo; + network: CheqdNetwork; } export interface ICheqdGenerateDidDocWithLinkedResourceArgs extends ICheqdGenerateDidDocArgs { - [key: string]: any + [key: string]: any; } export interface ICheqdGenerateKeyPairArgs { - [key: string]: any + [key: string]: any; } export interface ICheqdGenerateVersionIdArgs { - [key: string]: any + [key: string]: any; } export interface ICheqdGenerateStatusList2021Args { - length?: number - buffer?: Uint8Array - bitstringEncoding?: DefaultStatusList2021Encoding + length?: number; + buffer?: Uint8Array; + bitstringEncoding?: DefaultStatusList2021Encoding; } export interface ICheqdIssueRevocableCredentialWithStatusList2021Args { - issuanceOptions: ICreateVerifiableCredentialArgs - statusOptions: { - statusPurpose: 'revocation' - statusListName: string - statusListIndex?: number - statusListVersion?: string - statusListRangeStart?: number - statusListRangeEnd?: number - indexNotIn?: number[] - } + issuanceOptions: ICreateVerifiableCredentialArgs; + statusOptions: { + statusPurpose: 'revocation'; + statusListName: string; + statusListIndex?: number; + statusListVersion?: string; + statusListRangeStart?: number; + statusListRangeEnd?: number; + indexNotIn?: number[]; + }; } export interface ICheqdIssueSuspendableCredentialWithStatusList2021Args { - issuanceOptions: ICreateVerifiableCredentialArgs - statusOptions: { - statusPurpose: 'suspension' - statusListName: string - statusListIndex?: number - statusListVersion?: string - statusListRangeStart?: number - statusListRangeEnd?: number - indexNotIn?: number[] - } + issuanceOptions: ICreateVerifiableCredentialArgs; + statusOptions: { + statusPurpose: 'suspension'; + statusListName: string; + statusListIndex?: number; + statusListVersion?: string; + statusListRangeStart?: number; + statusListRangeEnd?: number; + indexNotIn?: number[]; + }; } export interface ICheqdVerifyCredentialWithStatusList2021Args { - credential: W3CVerifiableCredential - verificationArgs?: IVerifyCredentialArgs - fetchList?: boolean - dkgOptions?: DkgOptions - options?: ICheqdStatusList2021Options + credential: W3CVerifiableCredential; + verificationArgs?: IVerifyCredentialArgs; + fetchList?: boolean; + dkgOptions?: DkgOptions; + options?: ICheqdStatusList2021Options; } export interface ICheqdVerifyPresentationWithStatusList2021Args { - presentation: VerifiablePresentation - verificationArgs?: IVerifyPresentationArgs - fetchList?: boolean - dkgOptions?: DkgOptions - options?: ICheqdStatusList2021Options + presentation: VerifiablePresentation; + verificationArgs?: IVerifyPresentationArgs; + fetchList?: boolean; + dkgOptions?: DkgOptions; + options?: ICheqdStatusList2021Options; } export interface ICheqdCheckCredentialStatusWithStatusList2021Args { - credential?: W3CVerifiableCredential - statusOptions?: ICheqdCheckCredentialWithStatusList2021StatusOptions - fetchList?: boolean - dkgOptions?: DkgOptions - options?: ICheqdStatusList2021Options + credential?: W3CVerifiableCredential; + statusOptions?: ICheqdCheckCredentialWithStatusList2021StatusOptions; + fetchList?: boolean; + dkgOptions?: DkgOptions; + options?: ICheqdStatusList2021Options; } export interface ICheqdRevokeCredentialWithStatusList2021Args { - credential?: W3CVerifiableCredential - revocationOptions?: ICheqdRevokeCredentialWithStatusList2021Options - fetchList?: boolean - publish?: boolean - publishEncrypted?: boolean - symmetricKey?: string - paymentConditions?: PaymentCondition[] - writeToFile?: boolean - returnUpdatedStatusList?: boolean - returnSymmetricKey?: boolean - returnStatusListMetadata?: boolean - dkgOptions?: DkgOptions - options?: ICheqdStatusList2021Options + credential?: W3CVerifiableCredential; + revocationOptions?: ICheqdRevokeCredentialWithStatusList2021Options; + fetchList?: boolean; + publish?: boolean; + publishEncrypted?: boolean; + symmetricKey?: string; + paymentConditions?: PaymentCondition[]; + writeToFile?: boolean; + returnUpdatedStatusList?: boolean; + returnSymmetricKey?: boolean; + returnStatusListMetadata?: boolean; + dkgOptions?: DkgOptions; + options?: ICheqdStatusList2021Options; } export interface ICheqdRevokeBulkCredentialsWithStatusList2021Args { - credentials?: W3CVerifiableCredential[] - revocationOptions?: ICheqdRevokeBulkCredentialsWithStatusList2021Options - fetchList?: boolean - publish?: boolean - publishEncrypted?: boolean - symmetricKey?: string - paymentConditions?: PaymentCondition[] - writeToFile?: boolean - returnUpdatedStatusList?: boolean - returnSymmetricKey?: boolean - returnStatusListMetadata?: boolean - dkgOptions?: DkgOptions - options?: ICheqdStatusList2021Options + credentials?: W3CVerifiableCredential[]; + revocationOptions?: ICheqdRevokeBulkCredentialsWithStatusList2021Options; + fetchList?: boolean; + publish?: boolean; + publishEncrypted?: boolean; + symmetricKey?: string; + paymentConditions?: PaymentCondition[]; + writeToFile?: boolean; + returnUpdatedStatusList?: boolean; + returnSymmetricKey?: boolean; + returnStatusListMetadata?: boolean; + dkgOptions?: DkgOptions; + options?: ICheqdStatusList2021Options; } export interface ICheqdSuspendCredentialWithStatusList2021Args { - credential?: W3CVerifiableCredential - suspensionOptions?: ICheqdSuspendCredentialWithStatusList2021Options - fetchList?: boolean - publish?: boolean - publishEncrypted?: boolean - symmetricKey?: string - paymentConditions?: PaymentCondition[] - writeToFile?: boolean - returnUpdatedStatusList?: boolean - returnSymmetricKey?: boolean - returnStatusListMetadata?: boolean - dkgOptions?: DkgOptions - options?: ICheqdStatusList2021Options + credential?: W3CVerifiableCredential; + suspensionOptions?: ICheqdSuspendCredentialWithStatusList2021Options; + fetchList?: boolean; + publish?: boolean; + publishEncrypted?: boolean; + symmetricKey?: string; + paymentConditions?: PaymentCondition[]; + writeToFile?: boolean; + returnUpdatedStatusList?: boolean; + returnSymmetricKey?: boolean; + returnStatusListMetadata?: boolean; + dkgOptions?: DkgOptions; + options?: ICheqdStatusList2021Options; } export interface ICheqdSuspendBulkCredentialsWithStatusList2021Args { - credentials?: W3CVerifiableCredential[] - suspensionOptions?: ICheqdSuspendBulkCredentialsWithStatusList2021Options - fetchList?: boolean - publish?: boolean - publishEncrypted?: boolean - symmetricKey?: string - paymentConditions?: PaymentCondition[] - writeToFile?: boolean - returnUpdatedStatusList?: boolean - returnSymmetricKey?: boolean - returnStatusListMetadata?: boolean - dkgOptions?: DkgOptions - options?: ICheqdStatusList2021Options + credentials?: W3CVerifiableCredential[]; + suspensionOptions?: ICheqdSuspendBulkCredentialsWithStatusList2021Options; + fetchList?: boolean; + publish?: boolean; + publishEncrypted?: boolean; + symmetricKey?: string; + paymentConditions?: PaymentCondition[]; + writeToFile?: boolean; + returnUpdatedStatusList?: boolean; + returnSymmetricKey?: boolean; + returnStatusListMetadata?: boolean; + dkgOptions?: DkgOptions; + options?: ICheqdStatusList2021Options; } export interface ICheqdUnsuspendCredentialWithStatusList2021Args { - credential?: W3CVerifiableCredential - unsuspensionOptions?: ICheqdUnsuspendCredentialWithStatusList2021Options - fetchList?: boolean - publish?: boolean - publishEncrypted?: boolean - symmetricKey?: string - paymentConditions?: PaymentCondition[] - writeToFile?: boolean - returnUpdatedStatusList?: boolean - returnSymmetricKey?: boolean - returnStatusListMetadata?: boolean - dkgOptions?: DkgOptions - options?: ICheqdStatusList2021Options + credential?: W3CVerifiableCredential; + unsuspensionOptions?: ICheqdUnsuspendCredentialWithStatusList2021Options; + fetchList?: boolean; + publish?: boolean; + publishEncrypted?: boolean; + symmetricKey?: string; + paymentConditions?: PaymentCondition[]; + writeToFile?: boolean; + returnUpdatedStatusList?: boolean; + returnSymmetricKey?: boolean; + returnStatusListMetadata?: boolean; + dkgOptions?: DkgOptions; + options?: ICheqdStatusList2021Options; } export interface ICheqdUnsuspendBulkCredentialsWithStatusList2021Args { - credentials?: W3CVerifiableCredential[] - unsuspensionOptions?: ICheqdUnsuspendBulkCredentialsWithStatusList2021Options - fetchList?: boolean - publish?: boolean - publishEncrypted?: boolean - symmetricKey?: string - paymentConditions?: PaymentCondition[] - writeToFile?: boolean - returnUpdatedStatusList?: boolean - returnSymmetricKey?: boolean - returnStatusListMetadata?: boolean - dkgOptions?: DkgOptions - options?: ICheqdStatusList2021Options + credentials?: W3CVerifiableCredential[]; + unsuspensionOptions?: ICheqdUnsuspendBulkCredentialsWithStatusList2021Options; + fetchList?: boolean; + publish?: boolean; + publishEncrypted?: boolean; + symmetricKey?: string; + paymentConditions?: PaymentCondition[]; + writeToFile?: boolean; + returnUpdatedStatusList?: boolean; + returnSymmetricKey?: boolean; + returnStatusListMetadata?: boolean; + dkgOptions?: DkgOptions; + options?: ICheqdStatusList2021Options; } export interface ICheqdTransactSendTokensArgs { - recipientAddress: string - amount: Coin - network: CheqdNetwork - memo?: string - txBytes?: Uint8Array - returnTxResponse?: boolean + recipientAddress: string; + amount: Coin; + network: CheqdNetwork; + memo?: string; + txBytes?: Uint8Array; + returnTxResponse?: boolean; } export interface ICheqdObservePaymentConditionArgs { - recipientAddress?: string - amount?: Coin - intervalInSeconds?: number - blockHeight?: string - comparator?: Extract> - [UpdateIdentifierMethodName]: (args: ICheqdUpdateIdentifierArgs, context: IContext) => Promise>, - [DeactivateIdentifierMethodName]: (args: ICheqdDeactivateIdentifierArgs, context: IContext) => Promise, - [CreateResourceMethodName]: (args: ICheqdCreateLinkedResourceArgs, context: IContext) => Promise, - [CreateStatusList2021MethodName]: (args: ICheqdCreateStatusList2021Args, context: IContext) => Promise, - [BroadcastStatusList2021MethodName]: (args: ICheqdBroadcastStatusList2021Args, context: IContext) => Promise, - [GenerateDidDocMethodName]: (args: ICheqdGenerateDidDocArgs, context: IContext) => Promise, - [GenerateDidDocWithLinkedResourceMethodName]: (args: ICheqdGenerateDidDocWithLinkedResourceArgs, context: IContext) => Promise, - [GenerateKeyPairMethodName]: (args: ICheqdGenerateKeyPairArgs, context: IContext) => Promise - [GenerateVersionIdMethodName]: (args: ICheqdGenerateVersionIdArgs, context: IContext) => Promise - [GenerateStatusList2021MethodName]: (args: ICheqdGenerateStatusList2021Args, context: IContext) => Promise - [IssueRevocableCredentialWithStatusList2021MethodName]: (args: ICheqdIssueRevocableCredentialWithStatusList2021Args, context: IContext) => Promise - [IssueSuspendableCredentialWithStatusList2021MethodName]: (args: ICheqdIssueSuspendableCredentialWithStatusList2021Args, context: IContext) => Promise - [VerifyCredentialMethodName]: (args: ICheqdVerifyCredentialWithStatusList2021Args, context: IContext) => Promise - [VerifyPresentationMethodName]: (args: ICheqdVerifyPresentationWithStatusList2021Args, context: IContext) => Promise - [CheckCredentialStatusMethodName]: (args: ICheqdCheckCredentialStatusWithStatusList2021Args, context: IContext) => Promise - [RevokeCredentialMethodName]: (args: ICheqdRevokeCredentialWithStatusList2021Args, context: IContext) => Promise - [RevokeCredentialsMethodName]: (args: ICheqdRevokeBulkCredentialsWithStatusList2021Args, context: IContext) => Promise - [SuspendCredentialMethodName]: (args: ICheqdSuspendCredentialWithStatusList2021Args, context: IContext) => Promise - [SuspendCredentialsMethodName]: (args: ICheqdSuspendBulkCredentialsWithStatusList2021Args, context: IContext) => Promise - [UnsuspendCredentialMethodName]: (args: ICheqdUnsuspendCredentialWithStatusList2021Args, context: IContext) => Promise - [UnsuspendCredentialsMethodName]: (args: ICheqdUnsuspendBulkCredentialsWithStatusList2021Args, context: IContext) => Promise - [TransactSendTokensMethodName]: (args: ICheqdTransactSendTokensArgs, context: IContext) => Promise - [ObservePaymentConditionMethodName]: (args: ICheqdObservePaymentConditionArgs, context: IContext) => Promise + [CreateIdentifierMethodName]: ( + args: ICheqdCreateIdentifierArgs, + context: IContext + ) => Promise>; + [UpdateIdentifierMethodName]: ( + args: ICheqdUpdateIdentifierArgs, + context: IContext + ) => Promise>; + [DeactivateIdentifierMethodName]: (args: ICheqdDeactivateIdentifierArgs, context: IContext) => Promise; + [CreateResourceMethodName]: (args: ICheqdCreateLinkedResourceArgs, context: IContext) => Promise; + [CreateStatusList2021MethodName]: ( + args: ICheqdCreateStatusList2021Args, + context: IContext + ) => Promise; + [BroadcastStatusList2021MethodName]: ( + args: ICheqdBroadcastStatusList2021Args, + context: IContext + ) => Promise; + [GenerateDidDocMethodName]: (args: ICheqdGenerateDidDocArgs, context: IContext) => Promise; + [GenerateDidDocWithLinkedResourceMethodName]: ( + args: ICheqdGenerateDidDocWithLinkedResourceArgs, + context: IContext + ) => Promise; + [GenerateKeyPairMethodName]: (args: ICheqdGenerateKeyPairArgs, context: IContext) => Promise; + [GenerateVersionIdMethodName]: (args: ICheqdGenerateVersionIdArgs, context: IContext) => Promise; + [GenerateStatusList2021MethodName]: (args: ICheqdGenerateStatusList2021Args, context: IContext) => Promise; + [IssueRevocableCredentialWithStatusList2021MethodName]: ( + args: ICheqdIssueRevocableCredentialWithStatusList2021Args, + context: IContext + ) => Promise; + [IssueSuspendableCredentialWithStatusList2021MethodName]: ( + args: ICheqdIssueSuspendableCredentialWithStatusList2021Args, + context: IContext + ) => Promise; + [VerifyCredentialMethodName]: ( + args: ICheqdVerifyCredentialWithStatusList2021Args, + context: IContext + ) => Promise; + [VerifyPresentationMethodName]: ( + args: ICheqdVerifyPresentationWithStatusList2021Args, + context: IContext + ) => Promise; + [CheckCredentialStatusMethodName]: ( + args: ICheqdCheckCredentialStatusWithStatusList2021Args, + context: IContext + ) => Promise; + [RevokeCredentialMethodName]: ( + args: ICheqdRevokeCredentialWithStatusList2021Args, + context: IContext + ) => Promise; + [RevokeCredentialsMethodName]: ( + args: ICheqdRevokeBulkCredentialsWithStatusList2021Args, + context: IContext + ) => Promise; + [SuspendCredentialMethodName]: ( + args: ICheqdSuspendCredentialWithStatusList2021Args, + context: IContext + ) => Promise; + [SuspendCredentialsMethodName]: ( + args: ICheqdSuspendBulkCredentialsWithStatusList2021Args, + context: IContext + ) => Promise; + [UnsuspendCredentialMethodName]: ( + args: ICheqdUnsuspendCredentialWithStatusList2021Args, + context: IContext + ) => Promise; + [UnsuspendCredentialsMethodName]: ( + args: ICheqdUnsuspendBulkCredentialsWithStatusList2021Args, + context: IContext + ) => Promise; + [TransactSendTokensMethodName]: ( + args: ICheqdTransactSendTokensArgs, + context: IContext + ) => Promise; + [ObservePaymentConditionMethodName]: ( + args: ICheqdObservePaymentConditionArgs, + context: IContext + ) => Promise; } export class Cheqd implements IAgentPlugin { - readonly methods?: ICheqd - readonly schema?: IAgentPluginSchema = { - "components": { - "schemas": {}, - "methods": { - "cheqdCreateIdentifier": { - "description": "Create a new identifier", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "A cheqdCreateIdentifierArgs object as any for extensibility" - } - }, - "required": [ - "args" - ] - }, - "returnType": { - "type": "object" - } - }, - "cheqdUpdateIdentifier": { - "description": "Update an identifier", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "A cheqdUpdateIdentifierArgs object as any for extensibility" - } - }, - "required": [ - "args" - ] - }, - "returnType": { - "type": "object" - } - }, - "cheqdDeactivateIdentifier": { - "description": "Deactivate an identifier", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "A cheqdDeactivateIdentifierArgs object as any for extensibility" - } - }, - "required": [ - "args" - ] - }, - "returnType": { - "type": "object" - } - }, - "cheqdCreateLinkedResource": { - "description": "Create a new resource", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "A cheqdCreateLinkedResource object as any for extensibility" - } - }, - "required": [ - "args" - ] - }, - "returnType": { - "type": "boolean" - } - }, - "cheqdCreateStatusList2021": { - "description": "Create a new Status List 2021", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "A cheqdCreateStatusList2021Args object as any for extensibility" - } - }, - "required": [ - "args" - ] - }, - "returnType": { - "type": "object" - } - }, - "cheqdBroadcastStatusList2021": { - "description": "Broadcast a Status List 2021 to cheqd ledger", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "A cheqdBroadcastStatusList2021Args object as any for extensibility" - } - }, - "required": [ - "args" - ] - }, - "returnType": { - "type": "object" - } - }, - "cheqdGenerateDidDoc": { - "description": "Generate a new DID document to use with `createIdentifier`", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "A cheqdGenerateDidDocArgs object as any for extensibility" - } - }, - "required": [ - "args" - ] - }, - "returnType": { - "type": "object" - } - }, - "cheqdGenerateDidDocWithLinkedResource": { - "description": "Generate a new DID document to use with `createIdentifier` and / or `createResource`", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "A cheqdGenerateDidDocWithLinkedResourceArgs object as any for extensibility" - } - }, - "required": [ - "args" - ] - }, - "returnType": { - "type": "object" - } - }, - "cheqdGenerateIdentityKeys": { - "description": "Generate a new key pair in hex to use with `createIdentifier`", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "A cheqdGenerateIdentityKeysArgs object as any for extensibility" - } - } - }, - "returnType": { - "type": "object" - } - }, - "cheqdGenerateVersionId": { - "description": "Generate a random uuid", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "A cheqdGenerateVersionIdArgs object as any for extensibility" - } - }, - }, - "returnType": { - "type": "object" - } - }, - "cheqdGenerateStatusList2021": { - "description": "Generate a new Status List 2021", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "A cheqdGenerateStatusList2021Args object as any for extensibility" - } - }, - }, - "returnType": { - "type": "string" - } - }, - "cheqdIssueRevocableCredentialWithStatusList2021": { - "description": "Issue a revocable credential with a Status List 2021 as credential status registry", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "A cheqdIssueCredentialWithStatusList2021Args object as any for extensibility" - } - }, - "required": [ - "args" - ] - }, - "returnType": { - "type": "object" - } - }, - "cheqdIssueSuspendableCredentialWithStatusList2021": { - "description": "Issue a suspendable credential with a Status List 2021 as credential status registry", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "A cheqdIssueCredentialWithStatusList2021Args object as any for extensibility" - } - }, - "required": [ - "args" - ] - }, - "returnType": { - "type": "object" - } - }, - "cheqdVerifyCredential": { - "description": "Verify a credential, enhanced by revocation / suspension check with a Status List 2021 as credential status registry", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "A cheqdVerifyCredentialWithStatusList2021Args object as any for extensibility" - } - }, - "required": [ - "args" - ] - }, - "returnType": { - "type": "object" - } - }, - "cheqdVerifyPresentation": { - "description": "Verify a presentation, enhanced by revocation / suspension check with a Status List 2021 as credential status registry", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "A cheqdVerifyPresentationWithStatusList2021Args object as any for extensibility" - } - }, - "required": [ - "args" - ] - }, - "returnType": { - "type": "object" - } - }, - "cheqdCheckCredentialStatus": { - "description": "Check the revocation / suspension status of a credential with a Status List 2021 as credential status registry", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "A cheqdCheckCredentialStatusWithStatusList2021Args object as any for extensibility" - } - }, - "required": [ - "args" - ] - }, - "returnType": { - "type": "object" - } - }, - "cheqdRevokeCredential": { - "description": "Revoke a credential against a Status List 2021 as credential status registry", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "A cheqdRevokeCredentialWithStatusList2021Args object as any for extensibility" - } - }, - "required": [ - "args" - ] - }, - "returnType": { - "type": "object" - } - }, - "cheqdRevokeCredentials": { - "description": "Revoke multiple credentials against a Status List 2021 as credential status registry", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "A cheqdRevokeBulkCredentialsWithStatusList2021Args object as any for extensibility" - } - }, - "required": [ - "args" - ] - }, - "returnType": { - "type": "array" - } - }, - "cheqdSuspendCredential": { - "description": "Suspend a credential against a Status List 2021 as credential status registry", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "A cheqdSuspendCredentialWithStatusList2021Args object as any for extensibility" - } - }, - "required": [ - "args" - ] - }, - "returnType": { - "type": "object" - } - }, - "cheqdSuspendCredentials": { - "description": "Suspend multiple credentials against a Status List 2021 as credential status registry", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "A cheqdSuspendBulkCredentialsWithStatusList2021Args object as any for extensibility" - } - }, - "required": [ - "args" - ] - }, - "returnType": { - "type": "array" - } - }, - "cheqdUnsuspendCredential": { - "description": "Unsuspend a credential against a Status List 2021 as credential status registry", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "cheqdUnsuspendCredentialWithStatusList2021Args object as any for extensibility" - } - }, - "required": [ - "args" - ] - }, - "returnType": { - "type": "object" - } - }, - "cheqdUnsuspendCredentials": { - "description": "Unsuspend multiple credentials against a Status List 2021 as credential status registry", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "A cheqdUnsuspendBulkCredentialsWithStatusList2021Args object as any for extensibility" - } - }, - "required": [ - "args" - ] - }, - "returnType": { - "type": "array" - } - }, - "cheqdTransactSendTokens": { - "description": "Send tokens from one account to another", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "A cheqdTransactSendTokensArgs object as any for extensibility" - } - }, - "required": [ - "args" - ] - }, - "returnType": { - "type": "object" - } - }, - "cheqdObservePaymentCondition": { - "description": "Observe payment conditions for a given set of payment conditions", - "arguments": { - "type": "object", - "properties": { - "args": { - "type": "object", - "description": "cheqdObservePaymentConditionArgs object as any for extensibility" - } - }, - "required": [ - "args" - ] - }, - "returnType": { - "type": "object" - } - } - } - } - } - private readonly supportedDidProviders: CheqdDIDProvider[] - private didProvider: CheqdDIDProvider; - private providerId: string; - static readonly defaultStatusList2021Length: number = 16 * 1024 * 8 // 16KB in bits or 131072 bits / entries - static readonly defaultContextV1 = 'https://www.w3.org/2018/credentials/v1' - static readonly statusList2021Context = 'https://w3id.org/vc-status-list-2021/v1' - - - constructor(args: { providers: CheqdDIDProvider[] }) { - if (typeof args.providers !== 'object') { - throw new Error('[did-provider-cheqd]: at least one did provider is required') - } - - this.supportedDidProviders = args.providers - this.didProvider = args.providers[0] - this.providerId = Cheqd.generateProviderId(this.didProvider.network) - - this.methods = { - [CreateIdentifierMethodName]: this.CreateIdentifier.bind(this), - [UpdateIdentifierMethodName]: this.UpdateIdentifier.bind(this), - [DeactivateIdentifierMethodName]: this.DeactivateIdentifier.bind(this), - [CreateResourceMethodName]: this.CreateResource.bind(this), - [CreateStatusList2021MethodName]: this.CreateStatusList2021.bind(this), - [BroadcastStatusList2021MethodName]: this.BroadcastStatusList2021.bind(this), - [GenerateDidDocMethodName]: this.GenerateDidDoc.bind(this), - [GenerateDidDocWithLinkedResourceMethodName]: this.GenerateDidDocWithLinkedResource.bind(this), - [GenerateKeyPairMethodName]: this.GenerateIdentityKeys.bind(this), - [GenerateVersionIdMethodName]: this.GenerateVersionId.bind(this), - [GenerateStatusList2021MethodName]: this.GenerateStatusList2021.bind(this), - [IssueRevocableCredentialWithStatusList2021MethodName]: this.IssueRevocableCredentialWithStatusList2021.bind(this), - [IssueSuspendableCredentialWithStatusList2021MethodName]: this.IssueSuspendableCredentialWithStatusList2021.bind(this), - [VerifyCredentialMethodName]: this.VerifyCredentialWithStatusList2021.bind(this), - [VerifyPresentationMethodName]: this.VerifyPresentationWithStatusList2021.bind(this), - [CheckCredentialStatusMethodName]: this.CheckCredentialStatusWithStatusList2021.bind(this), - [RevokeCredentialMethodName]: this.RevokeCredentialWithStatusList2021.bind(this), - [RevokeCredentialsMethodName]: this.RevokeBulkCredentialsWithStatusList2021.bind(this), - [SuspendCredentialMethodName]: this.SuspendCredentialWithStatusList2021.bind(this), - [SuspendCredentialsMethodName]: this.SuspendBulkCredentialsWithStatusList2021.bind(this), - [UnsuspendCredentialMethodName]: this.UnsuspendCredentialWithStatusList2021.bind(this), - [UnsuspendCredentialsMethodName]: this.UnsuspendBulkCredentialsWithStatusList2021.bind(this), - [TransactSendTokensMethodName]: this.TransactSendTokens.bind(this), - [ObservePaymentConditionMethodName]: this.ObservePaymentCondition.bind(this), - } - } - - private async CreateIdentifier(args: ICheqdCreateIdentifierArgs, context: IContext): Promise> { - if (typeof args.kms !== 'string') { - throw new Error('[did-provider-cheqd]: kms is required') - } - - if (typeof args.alias !== 'string') { - throw new Error('[did-provider-cheqd]: alias is required') - } - - if (typeof args.document !== 'object') { - throw new Error('[did-provider-cheqd]: document object is required') - } - - const provider = await Cheqd.loadProvider(args.document.id, this.supportedDidProviders) - - this.didProvider = provider - this.providerId = Cheqd.generateProviderId(this.didProvider.network) - - return await context.agent.didManagerCreate({ - kms: args.kms, - alias: args.alias, - provider: this.providerId, - options: { - document: args.document, - keys: args.keys, - versionId: args?.versionId, - fee: args?.fee - } - }) - } - - private async UpdateIdentifier(args: ICheqdUpdateIdentifierArgs, context: IContext): Promise> { - if (typeof args.kms !== 'string') { - throw new Error('[did-provider-cheqd]: kms is required') - } - - if (typeof args.document !== 'object') { - throw new Error('[did-provider-cheqd]: document object is required') - } - - const provider = await Cheqd.loadProvider(args.document.id, this.supportedDidProviders) - - this.didProvider = provider - this.providerId = Cheqd.generateProviderId(this.didProvider.network) - - return await context.agent.didManagerUpdate({ - did: args.document.id, - document: args.document, - options: { - kms: args.kms, - keys: args.keys, - versionId: args?.versionId, - fee: args?.fee - } - }) - } - - private async DeactivateIdentifier(args: ICheqdDeactivateIdentifierArgs, context: IContext) { - if (typeof args.kms !== 'string') { - throw new Error('[did-provider-cheqd]: kms is required') - } - - if (typeof args.document !== 'object') { - throw new Error('[did-provider-cheqd]: document object is required') - } - - const provider = await Cheqd.loadProvider(args.document.id, this.supportedDidProviders) - - this.didProvider = provider - this.providerId = Cheqd.generateProviderId(this.didProvider.network) - - return await this.didProvider.deactivateIdentifier({ - did: args.document.id, - document: args.document, - options: { - keys: args.keys, - fee: args?.fee - } - }, context) - } - - private async CreateResource(args: ICheqdCreateLinkedResourceArgs, context: IContext) { - if (typeof args.kms !== 'string') { - throw new Error('[did-provider-cheqd]: kms is required') - } - - if (typeof args.payload !== 'object') { - throw new Error('[did-provider-cheqd]: payload object is required') - } - - if (typeof args.network !== 'string') { - throw new Error('[did-provider-cheqd]: network is required') - } - - if (args?.file) { - args.payload.data = await Cheqd.getFile(args.file) - } - - if (typeof args?.payload?.data === 'string') { - args.payload.data = fromString(args.payload.data, 'base64') - } - - this.providerId = Cheqd.generateProviderId(args.network) - this.didProvider = await Cheqd.loadProvider(this.providerId, this.supportedDidProviders) - - return await this.didProvider.createResource({ - options: { - kms: args.kms, - payload: args.payload, - signInputs: args.signInputs, - fee: args?.fee - } - }, context) - } - - private async CreateStatusList2021(args: ICheqdCreateStatusList2021Args, context: IContext) { - if (typeof args.kms !== 'string') { - throw new Error('[did-provider-cheqd]: kms is required') - } - - if (typeof args.issuerDid !== 'string' || !args.issuerDid) { - throw new Error('[did-provider-cheqd]: issuerDid is required') - } - - if (typeof args.statusListName !== 'string' || !args.statusListName) { - throw new Error('[did-provider-cheqd]: statusListName is required') - } - - if (typeof args.statusPurpose !== 'string' || !args.statusPurpose) { - throw new Error('[did-provider-cheqd]: statusPurpose is required') - } - - if (typeof args.encrypted === 'undefined') { - throw new Error('[did-provider-cheqd]: encrypted is required') - } - - // validate statusPurpose - if (!Object.values(DefaultStatusList2021StatusPurposeTypes).includes(args.statusPurpose)) { - throw new Error(`[did-provider-cheqd]: statusPurpose must be one of ${Object.values(DefaultStatusList2021StatusPurposeTypes).join(', ')}`) - } - - // validate statusListLength - if (args?.statusListLength) { - if (typeof args.statusListLength !== 'number') { - throw new Error('[did-provider-cheqd]: statusListLength must be number') - } - - if (args.statusListLength < Cheqd.defaultStatusList2021Length) { - throw new Error(`[did-provider-cheqd]: statusListLength must be greater than or equal to ${Cheqd.defaultStatusList2021Length} number of entries`) - } - } - - // validate statusListEncoding - if (args?.statusListEncoding) { - if (typeof args.statusListEncoding !== 'string') { - throw new Error('[did-provider-cheqd]: statusListEncoding must be string') - } - - if (!Object.values(DefaultStatusList2021Encodings).includes(args.statusListEncoding)) { - throw new Error(`[did-provider-cheqd]: statusListEncoding must be one of ${Object.values(DefaultStatusList2021Encodings).join(', ')}`) - } - } - - // validate validUntil - if (args?.validUntil) { - if (typeof args.validUntil !== 'string') { - throw new Error('[did-provider-cheqd]: validUntil must be string') - } - - if (new Date() <= new Date(args.validUntil)) { - throw new Error('[did-provider-cheqd]: validUntil must be greater than current date') - } - } - - // validate args in pairs - case: encrypted - if (args.encrypted) { - // validate paymentConditions - if (!args?.paymentConditions || !args?.paymentConditions?.length || !Array.isArray(args?.paymentConditions) || args?.paymentConditions.length === 0) { - throw new Error('[did-provider-cheqd]: paymentConditions is required') - } - - if (!args?.paymentConditions?.every((condition) => condition.feePaymentAddress && condition.feePaymentAmount && condition.intervalInSeconds)) { - throw new Error('[did-provider-cheqd]: paymentConditions must contain feePaymentAddress and feeAmount and intervalInSeconds') - } - - if (!args?.paymentConditions?.every((condition) => typeof condition.feePaymentAddress === 'string' && typeof condition.feePaymentAmount === 'string' && typeof condition.intervalInSeconds === 'number')) { - throw new Error('[did-provider-cheqd]: feePaymentAddress and feePaymentAmount must be string and intervalInSeconds must be number') - } - - if (!args?.paymentConditions?.every((condition) => condition.type === AccessControlConditionTypes.timelockPayment)) { - throw new Error('[did-provider-cheqd]: paymentConditions must be of type timelockPayment') - } - } - - // get network - const network = args.issuerDid.split(':')[2] - - // generate bitstring - const bitstring = await context.agent[GenerateStatusList2021MethodName]({ length: args?.statusListLength || Cheqd.defaultStatusList2021Length, bitstringEncoding: args?.statusListEncoding || DefaultStatusList2021Encodings.base64url }) - - // construct data and metadata tuple - const data = args.encrypted - ? (await (async function (that: Cheqd) { - // instantiate dkg-threshold client, in which case lit-protocol is used - const lit = await LitProtocol.create({ - chain: args?.dkgOptions?.chain || that.didProvider.dkgOptions.chain, - litNetwork: args?.dkgOptions?.network || that.didProvider.dkgOptions.network, - }) - - // construct access control conditions - const unifiedAccessControlConditions = await Promise.all(args.paymentConditions!.map(async (condition) => { - switch (condition.type) { - case AccessControlConditionTypes.timelockPayment: - return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({ - key: '$.tx_responses.*.timestamp', - comparator: '<=', - value: `${condition.intervalInSeconds}`, - }, - condition.feePaymentAmount, - condition.feePaymentAddress, - condition?.blockHeight, - args?.dkgOptions?.chain || that.didProvider.dkgOptions.chain - ) - default: - throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`) - } - })) - - // encrypt bitstring - const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt(bitstring, unifiedAccessControlConditions, true) - - // return result tuple - switch (args.statusPurpose) { - case DefaultStatusList2021StatusPurposeTypes.revocation: - return [{ - StatusList2021: { - statusPurpose: args.statusPurpose, - encodedList: await blobToHexString(encryptedString), - validFrom: new Date().toISOString(), - validUntil: args?.validUntil - }, - metadata: { - type: DefaultStatusList2021ResourceTypes.revocation, - encrypted: true, - encoding: args?.statusListEncoding || DefaultStatusList2021Encodings.base64url, - encryptedSymmetricKey, - paymentConditions: args.paymentConditions - } - } satisfies StatusList2021Revocation, - { - symmetricKey: toString(symmetricKey!, 'hex'), - encryptedSymmetricKey, - encryptedString: await blobToHexString(encryptedString), - } - ] satisfies [StatusList2021Revocation, { symmetricKey: string, encryptedSymmetricKey: string, encryptedString: string }] - case DefaultStatusList2021StatusPurposeTypes.suspension: - return [{ - StatusList2021: { - statusPurpose: args.statusPurpose, - encodedList: await blobToHexString(encryptedString), - validFrom: new Date().toISOString(), - validUntil: args?.validUntil - }, - metadata: { - type: DefaultStatusList2021ResourceTypes.suspension, - encrypted: true, - encoding: args?.statusListEncoding || DefaultStatusList2021Encodings.base64url, - encryptedSymmetricKey, - paymentConditions: args.paymentConditions - } - } satisfies StatusList2021Suspension, - { - symmetricKey: toString(symmetricKey!, 'hex'), - encryptedSymmetricKey, - encryptedString: await blobToHexString(encryptedString), - } - ] satisfies [StatusList2021Suspension, { symmetricKey: string, encryptedSymmetricKey: string, encryptedString: string }] - default: - throw new Error(`[did-provider-cheqd]: status purpose is not valid ${args.statusPurpose}`) - } - }(this))) - : (await (async function () { - switch (args.statusPurpose) { - case DefaultStatusList2021StatusPurposeTypes.revocation: - return [{ - StatusList2021: { - statusPurpose: args.statusPurpose, - encodedList: bitstring, - validFrom: new Date().toISOString(), - validUntil: args?.validUntil - }, - metadata: { - type: DefaultStatusList2021ResourceTypes.revocation, - encrypted: false, - encoding: args?.statusListEncoding || DefaultStatusList2021Encodings.base64url, - } - } satisfies StatusList2021Revocation, - undefined - ] satisfies [StatusList2021Revocation, undefined] - case DefaultStatusList2021StatusPurposeTypes.suspension: - return [{ - StatusList2021: { - statusPurpose: args.statusPurpose, - encodedList: bitstring, - validFrom: new Date().toISOString(), - validUntil: args?.validUntil - }, - metadata: { - type: DefaultStatusList2021ResourceTypes.suspension, - encrypted: false, - encoding: args?.statusListEncoding || DefaultStatusList2021Encodings.base64url, - } - } satisfies StatusList2021Suspension, - undefined - ] satisfies [StatusList2021Suspension, undefined] - default: - throw new Error('[did-provider-cheqd]: statusPurpose is not valid') - } - }())) - - // construct payload - const payload = { - id: v4(), - collectionId: args.issuerDid.split(':').reverse()[0], - name: args.statusListName, - resourceType: DefaultStatusList2021ResourceTypes[args.statusPurpose], - version: args?.resourceVersion || new Date().toISOString(), - alsoKnownAs: args?.alsoKnownAs || [], - data: fromString(JSON.stringify(data[0]), 'utf-8'), - } satisfies StatusList2021ResourcePayload - - // return result - return { - created: await context.agent[BroadcastStatusList2021MethodName]({ kms: args.kms, payload, network: network as CheqdNetwork }), - resource: data[0], - resourceMetadata: await Cheqd.fetchStatusList2021Metadata({ credentialStatus: { id: `${DefaultResolverUrl}${args.issuerDid}?resourceName=${args.statusListName}&resourceType=${DefaultStatusList2021ResourceTypes[args.statusPurpose]}`, type: 'StatusList2021Entry' } } as VerifiableCredential), - encrypted: args.encrypted, - symmetricKey: args?.returnSymmetricKey ? data[1]?.symmetricKey : undefined, - } satisfies CreateStatusList2021Result - } - - private async BroadcastStatusList2021(args: ICheqdBroadcastStatusList2021Args, context: IContext) { - if (typeof args.kms !== 'string') { - throw new Error('[did-provider-cheqd]: kms is required') - } - - if (typeof args.payload !== 'object') { - throw new Error('[did-provider-cheqd]: payload object is required') - } - - if (typeof args.network !== 'string') { - throw new Error('[did-provider-cheqd]: network is required') - } - - if (args?.file) { - args.payload.data = await Cheqd.getFile(args.file) - } - - if (typeof args?.payload?.data === 'string') { - args.payload.data = fromString(args.payload.data, 'base64') - } - - // TODO: validate data as per bitstring - - // validate resource type - if (!Object.values(DefaultStatusList2021ResourceTypes).includes(args?.payload?.resourceType)) { - throw new Error(`[did-provider-cheqd]: resourceType must be one of ${Object.values(DefaultStatusList2021ResourceTypes).join(', ')}`) - } - - this.providerId = Cheqd.generateProviderId(args.network) - this.didProvider = await Cheqd.loadProvider(this.providerId, this.supportedDidProviders) - - return await this.didProvider.createResource({ - options: { - kms: args.kms, - payload: args.payload, - signInputs: args.signInputs, - fee: args?.fee || await ResourceModule.generateCreateResourceJsonFees((await this.didProvider.getWalletAccounts())[0].address) - } - }, context) - } - - private async GenerateDidDoc( - args: ICheqdGenerateDidDocArgs, - context: IContext - ): Promise { - if (typeof args.verificationMethod !== 'string') { - throw new Error('[did-provider-cheqd]: verificationMethod is required') - } - - if (typeof args.methodSpecificIdAlgo !== 'string') { - throw new Error('[did-provider-cheqd]: methodSpecificIdAlgo is required') - } - - if (typeof args.network !== 'string') { - throw new Error('[did-provider-cheqd]: network is required') - } - - const keyPair = createKeyPairBase64() - const keyPairHex: IKeyPair = { publicKey: toString(fromString(keyPair.publicKey, 'base64'), 'hex'), privateKey: toString(fromString(keyPair.privateKey, 'base64'), 'hex') } - const verificationKeys = createVerificationKeys(keyPair.publicKey, args.methodSpecificIdAlgo, 'key-1', args.network) - const verificationMethods = createDidVerificationMethod([args.verificationMethod], [verificationKeys]) - - return { - didDoc: createDidPayload(verificationMethods, [verificationKeys]), - versionId: v4(), - keys: [ - { - publicKeyHex: keyPairHex.publicKey, - privateKeyHex: keyPairHex.privateKey, - kid: keyPairHex.publicKey, - type: 'Ed25519' - } - ] - } - } - - private async GenerateDidDocWithLinkedResource(args: any, context: IContext): Promise { - if (typeof args.verificationMethod !== 'string') { - throw new Error('[did-provider-cheqd]: verificationMethod is required') - } - - if (typeof args.methodSpecificIdAlgo !== 'string') { - throw new Error('[did-provider-cheqd]: methodSpecificIdAlgo is required') - } - - if (typeof args.network !== 'string') { - throw new Error('[did-provider-cheqd]: network is required') - } - - const keyPair = createKeyPairBase64() - const keyPairHex: IKeyPair = { publicKey: toString(fromString(keyPair.publicKey, 'base64'), 'hex'), privateKey: toString(fromString(keyPair.privateKey, 'base64'), 'hex') } - const verificationKeys = createVerificationKeys(keyPair.publicKey, args.methodSpecificIdAlgo, 'key-1', args.network) - const verificationMethods = createDidVerificationMethod([args.verificationMethod], [verificationKeys]) - const payload = createDidPayload(verificationMethods, [verificationKeys]) - - return { - didDoc: payload, - versionId: v4(), - keys: [ - { - publicKeyHex: keyPairHex.publicKey, - privateKeyHex: keyPairHex.privateKey, - kid: keyPairHex.publicKey, - type: 'Ed25519' - } - ], - linkedResource: { - id: v4(), - collectionId: payload.id.split(':').reverse()[0], - name: 'sample json resource', - version: '1.0.0', - resourceType: 'SampleResource', - alsoKnownAs: [], - data: toString(new TextEncoder().encode( - JSON.stringify({ sample: 'json' }) - ), 'base64'), - } - } - } - - private async GenerateIdentityKeys(args: any, context: IContext): Promise { - const keyPair = createKeyPairHex() - return { - publicKeyHex: keyPair.publicKey, - privateKeyHex: keyPair.privateKey, - kid: keyPair.publicKey, - type: 'Ed25519' - } - } - - private async GenerateVersionId(args: any, context: IContext): Promise { - return v4() - } - - private async GenerateStatusList2021(args: ICheqdGenerateStatusList2021Args, context: IContext): Promise { - const statusList = args?.buffer - ? new StatusList({ buffer: args.buffer }) - : new StatusList({ length: args?.length || Cheqd.defaultStatusList2021Length }) - - const encoded = await statusList.encode() as Bitstring - - switch (args?.bitstringEncoding) { - case 'base64url': - return encoded - case 'base64': - return toString(fromString(encoded, 'base64url'), 'base64') - case 'hex': - return toString(fromString(encoded, 'base64url'), 'hex') - default: - return encoded - } - } - - private async IssueRevocableCredentialWithStatusList2021(args: ICheqdIssueRevocableCredentialWithStatusList2021Args, context: IContext): Promise { - // generate index - const statusListIndex = args.statusOptions.statusListIndex || await randomFromRange(args.statusOptions.statusListRangeStart || 0, (args.statusOptions.statusListRangeEnd || Cheqd.defaultStatusList2021Length) - 1, args.statusOptions.indexNotIn || []) - - // construct issuer - const issuer = ((args.issuanceOptions.credential.issuer as { id: string }).id) - ? (args.issuanceOptions.credential.issuer as { id: string }).id - : args.issuanceOptions.credential.issuer as string - - // generate status list credential - const statusListCredential = `${DefaultResolverUrl}${issuer}?resourceName=${args.statusOptions.statusListName}&resourceType=StatusList2021Revocation` - - // construct credential status - const credentialStatus = { - id: `${statusListCredential}#${statusListIndex}`, - type: 'StatusList2021Entry', - statusPurpose: 'revocation', - statusListIndex: `${statusListIndex}`, - } - - // add credential status to credential - args.issuanceOptions.credential.credentialStatus = credentialStatus - - // add relevant context - args.issuanceOptions.credential['@context'] = function() { - // if no context is provided, add default context - if (!args.issuanceOptions.credential['@context']) { - return [Cheqd.defaultContextV1, Cheqd.statusList2021Context] - } - - // if context is provided as an array, add default context if it is not already present - if (Array.isArray(args.issuanceOptions.credential['@context'])) { - if (args.issuanceOptions.credential['@context'].length === 0) { - return [Cheqd.defaultContextV1, Cheqd.statusList2021Context] - } - - if (!args.issuanceOptions.credential['@context'].includes(Cheqd.statusList2021Context)) { - return [...args.issuanceOptions.credential['@context'], Cheqd.statusList2021Context] - } - } - - // if context is provided as a string, add default context if it is not already present - if (typeof args.issuanceOptions.credential['@context'] === 'string') return [Cheqd.defaultContextV1, Cheqd.statusList2021Context] - }() - - // create a credential - const credential = await context.agent.createVerifiableCredential(args.issuanceOptions) - - return credential - } - - private async IssueSuspendableCredentialWithStatusList2021(args: ICheqdIssueSuspendableCredentialWithStatusList2021Args, context: IContext): Promise { - // generate index - const statusListIndex = args.statusOptions.statusListIndex || await randomFromRange(args.statusOptions.statusListRangeStart || 0, (args.statusOptions.statusListRangeEnd || Cheqd.defaultStatusList2021Length) - 1, args.statusOptions.indexNotIn || []) - - // construct issuer - const issuer = ((args.issuanceOptions.credential.issuer as { id: string }).id) - ? (args.issuanceOptions.credential.issuer as { id: string }).id - : args.issuanceOptions.credential.issuer as string - - // generate status list credential - const statusListCredential = `${DefaultResolverUrl}${issuer}?resourceName=${args.statusOptions.statusListName}&resourceType=StatusList2021Suspension` - - // construct credential status - const credentialStatus = { - id: `${statusListCredential}#${statusListIndex}`, - type: 'StatusList2021Entry', - statusPurpose: 'suspension', - statusListIndex: `${statusListIndex}`, - } - - // add credential status to credential - args.issuanceOptions.credential.credentialStatus = credentialStatus - - // add relevant context - args.issuanceOptions.credential['@context'] = function() { - // if no context is provided, add default context - if (!args.issuanceOptions.credential['@context']) { - return [Cheqd.defaultContextV1, Cheqd.statusList2021Context] - } - - // if context is provided as an array, add default context if it is not already present - if (Array.isArray(args.issuanceOptions.credential['@context'])) { - if (args.issuanceOptions.credential['@context'].length === 0) { - return [Cheqd.defaultContextV1, Cheqd.statusList2021Context] - } - - if (!args.issuanceOptions.credential['@context'].includes(Cheqd.statusList2021Context)) { - return [...args.issuanceOptions.credential['@context'], Cheqd.statusList2021Context] - } - } - - // if context is provided as a string, add default context if it is not already present - if (typeof args.issuanceOptions.credential['@context'] === 'string') return [Cheqd.defaultContextV1, Cheqd.statusList2021Context] - }() - - // create a credential - const credential = await context.agent.createVerifiableCredential(args.issuanceOptions) - - return credential - } - - private async VerifyCredentialWithStatusList2021(args: ICheqdVerifyCredentialWithStatusList2021Args, context: IContext): Promise { - // verify default policies - const verificationResult = await context.agent.verifyCredential({ - ...args?.verificationArgs, - credential: args.credential, - policies: { - ...args?.verificationArgs?.policies, - credentialStatus: false - }, - } satisfies IVerifyCredentialArgs) - - // early return if verification failed - if (!verificationResult.verified) { - return { verified: false, error: verificationResult.error } - } - - // if jwt credential, decode it - const credential = typeof args.credential === 'string' ? await Cheqd.decodeCredentialJWT(args.credential) : args.credential - - // define issuer - const issuer = typeof credential.issuer === 'string' - ? credential.issuer - : (credential.issuer as { id: string }).id - - // define provider, if applicable - this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders) - - // define provider id, if applicable - this.providerId = Cheqd.generateProviderId(issuer) - - // define dkg options, if provided - args.dkgOptions ||= this.didProvider.dkgOptions - - // verify credential status - switch (credential.credentialStatus?.statusPurpose) { - case 'revocation': - if (await Cheqd.checkRevoked(credential, { ...args.options, topArgs: args })) return { ...verificationResult, revoked: true } - return { ...verificationResult, revoked: false } - case 'suspension': - if (await Cheqd.checkSuspended(credential, { ...args.options, topArgs: args })) return { ...verificationResult, suspended: true } - return { ...verificationResult, suspended: false } - default: - throw new Error(`[did-provider-cheqd]: verify credential: Unsupported status purpose: ${credential.credentialStatus?.statusPurpose}`) - } - } - - private async VerifyPresentationWithStatusList2021(args: ICheqdVerifyPresentationWithStatusList2021Args, context: IContext): Promise { - // verify default policies - const verificationResult = await context.agent.verifyPresentation({ - ...args?.verificationArgs, - presentation: args.presentation, - policies: { - ...args?.verificationArgs?.policies, - credentialStatus: false - }, - } satisfies IVerifyPresentationArgs) - - // early return if verification failed - if (!verificationResult.verified) { - return { verified: false, error: verificationResult.error } - } - - // early return if no verifiable credentials are provided - if (!args.presentation.verifiableCredential) throw new Error('[did-provider-cheqd]: verify presentation: presentation.verifiableCredential is required') - - // verify credential(s) status(es) - for (let credential of args.presentation.verifiableCredential) { - // if jwt credential, decode it - if (typeof credential === 'string') credential = await Cheqd.decodeCredentialJWT(credential) - - // define issuer - const issuer = typeof credential.issuer === 'string' - ? credential.issuer - : (credential.issuer as { id: string }).id - - // define provider, if applicable - this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders) - - // define provider id, if applicable - this.providerId = Cheqd.generateProviderId(issuer) - - // define dkg options, if provided - args.dkgOptions ||= this.didProvider.dkgOptions - - switch (credential.credentialStatus?.statusPurpose) { - case 'revocation': - if (await Cheqd.checkRevoked(credential, { ...args.options, topArgs: args })) return { ...verificationResult, revoked: true } - break - case 'suspension': - if (await Cheqd.checkSuspended(credential, { ...args.options, topArgs: args })) return { ...verificationResult, suspended: true } - break - default: - throw new Error(`[did-provider-cheqd]: verify presentation: Unsupported status purpose: ${credential.credentialStatus?.statusPurpose}`) - } - } - - return { ...verificationResult, verified: true } - } - - private async CheckCredentialStatusWithStatusList2021(args: ICheqdCheckCredentialStatusWithStatusList2021Args, context: IContext): Promise { - // verify credential, if provided and status options are not - if (args?.credential && !args?.statusOptions) { - const verificationResult = await context.agent.verifyCredential({ - credential: args.credential, - policies: { - credentialStatus: false - } - } satisfies IVerifyCredentialArgs) - - // early return if verification failed - if (!verificationResult.verified) { - return { revoked: false, error: verificationResult.error } - } - } - - // if status options are provided, give precedence - if (args?.statusOptions) { - // validate status options - case: statusOptions.issuerDid - if (!args.statusOptions.issuerDid) throw new Error('[did-provider-cheqd]: check status: statusOptions.issuerDid is required') - - // validate status options - case: statusOptions.statusListName - if (!args.statusOptions.statusListName) throw new Error('[did-provider-cheqd]: check status: statusOptions.statusListName is required') - - // validate status options - case: statusOptions.statusListIndex - if (!args.statusOptions.statusPurpose) throw new Error('[did-provider-cheqd]: check status: statusOptions.statusListIndex is required') - - // validate status options - case: statusOptions.statusListIndex - if (!args.statusOptions.statusListIndex) throw new Error('[did-provider-cheqd]: check status: statusOptions.statusListIndex is required') - - // generate resource type - const resourceType = args.statusOptions.statusPurpose === 'revocation' ? 'StatusList2021Revocation' : 'StatusList2021Suspension' - - // construct status list credential - const statusListCredential = `${DefaultResolverUrl}${args.statusOptions.issuerDid}?resourceName=${args.statusOptions.statusListName}&resourceType=${resourceType}` - - // construct credential status - args.credential = { - '@context': [], - issuer: args.statusOptions.issuerDid, - credentialSubject: {}, - credentialStatus: { - id: `${statusListCredential}#${args.statusOptions.statusListIndex}`, - type: 'StatusList2021Entry', - statusPurpose: `${args.statusOptions.statusPurpose}`, - statusListIndex: `${args.statusOptions.statusListIndex}`, - }, - issuanceDate: '', - proof: {} - } - } - - // validate args - case: credential - if (!args.credential) throw new Error('[did-provider-cheqd]: revocation: credential is required') - - // if jwt credential, decode it - const credential = typeof args.credential === 'string' ? await Cheqd.decodeCredentialJWT(args.credential) : args.credential - - // define issuer - const issuer = typeof credential.issuer === 'string' - ? credential.issuer - : (credential.issuer as { id: string }).id - - // define provider, if applicable - this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders) - - // define provider id, if applicable - this.providerId = Cheqd.generateProviderId(issuer) - - // define dkg options, if provided - args.dkgOptions ||= this.didProvider.dkgOptions - - switch (credential.credentialStatus?.statusPurpose) { - case 'revocation': - if (await Cheqd.checkRevoked(credential, { ...args.options, topArgs: args })) return { revoked: true } - return { revoked: false } - case 'suspension': - if (await Cheqd.checkSuspended(credential, { ...args.options, topArgs: args })) return { suspended: true } - return { suspended: false } - default: - throw new Error(`[did-provider-cheqd]: check status: Unsupported status purpose: ${credential.credentialStatus?.statusPurpose}`) - } - } - - private async RevokeCredentialWithStatusList2021(args: ICheqdRevokeCredentialWithStatusList2021Args, context: IContext): Promise { - // verify credential, if provided and revocation options are not - if (args?.credential && !args?.revocationOptions) { - const verificationResult = await context.agent.verifyCredential({ - credential: args.credential, - policies: { - credentialStatus: false - } - } satisfies IVerifyCredentialArgs) - - // early return if verification failed - if (!verificationResult.verified) { - return { revoked: false, error: verificationResult.error } - } - } - - // if revocation options are provided, give precedence - if (args?.revocationOptions) { - // validate revocation options - case: revocationOptions.issuerDid - if (!args.revocationOptions.issuerDid) throw new Error('[did-provider-cheqd]: revocation: revocationOptions.issuerDid is required') - - // validate revocation options - case: revocationOptions.statusListName - if (!args.revocationOptions.statusListName) throw new Error('[did-provider-cheqd]: revocation: revocationOptions.statusListName is required') - - // validate revocation options - case: revocationOptions.statusListIndex - if (!args.revocationOptions.statusListIndex) throw new Error('[did-provider-cheqd]: revocation: revocationOptions.statusListIndex is required') - - // construct status list credential - const statusListCredential = `${DefaultResolverUrl}${args.revocationOptions.issuerDid}?resourceName=${args.revocationOptions.statusListName}&resourceType=StatusList2021Revocation` - - // construct credential status - args.credential = { - '@context': [], - issuer: args.revocationOptions.issuerDid, - credentialSubject: {}, - credentialStatus: { - id: `${statusListCredential}#${args.revocationOptions.statusListIndex}`, - type: 'StatusList2021Entry', - statusPurpose: 'revocation', - statusListIndex: `${args.revocationOptions.statusListIndex}`, - }, - issuanceDate: '', - proof: {} - } - } - - // validate args - case: credential - if (!args.credential) throw new Error('[did-provider-cheqd]: revocation: credential is required') - - // if jwt credential, decode it - const credential = typeof args.credential === 'string' ? await Cheqd.decodeCredentialJWT(args.credential) : args.credential - - // validate status purpose - if (credential.credentialStatus?.statusPurpose !== 'revocation') { - throw new Error(`[did-provider-cheqd]: revocation: Unsupported status purpose: ${credential.credentialStatus?.statusPurpose}`) - } - - // validate args in pairs - case: statusListFile and statusList - if (args.options?.statusListFile && args.options?.statusList) { - throw new Error('[did-provider-cheqd]: revocation: statusListFile and statusList are mutually exclusive') - } - - // validate args in pairs - case: statusListFile and fetchList - if (args.options?.statusListFile && args.options?.fetchList) { - throw new Error('[did-provider-cheqd]: revocation: statusListFile and fetchList are mutually exclusive') - } - - // validate args in pairs - case: statusList and fetchList - if (args.options?.statusList && args.options?.fetchList) { - throw new Error('[did-provider-cheqd]: revocation: statusList and fetchList are mutually exclusive') - } - - // validate args in pairs - case: publish - if (args.options?.publish && !args.fetchList && !(args.options?.statusListFile || args.options?.statusList)) { - throw new Error('[did-provider-cheqd]: revocation: publish requires statusListFile or statusList, if fetchList is disabled') - } - - // define issuer - const issuer = typeof credential.issuer === 'string' - ? credential.issuer - : (credential.issuer as { id: string }).id - - // define provider, if applicable - this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders) - - // define provider id, if applicable - this.providerId = Cheqd.generateProviderId(issuer) - - // define dkg options, if provided - args.dkgOptions ||= this.didProvider.dkgOptions - - // revoke credential - return await Cheqd.revokeCredential(credential, { - ...args.options, - topArgs: args, - publishOptions: { - context, - statusListEncoding: args?.options?.statusListEncoding, - statusListValidUntil: args?.options?.statusListValidUntil, - resourceId: args?.options?.resourceId, - resourceVersion: args?.options?.resourceVersion, - resourceAlsoKnownAs: args?.options?.alsoKnownAs, - signInputs: args?.options?.signInputs, - fee: args?.options?.fee - } - }) - } - - private async RevokeBulkCredentialsWithStatusList2021(args: ICheqdRevokeBulkCredentialsWithStatusList2021Args, context: IContext): Promise { - // verify credential, if provided and revocation options are not - if (args?.credentials && !args?.revocationOptions) { - - const verificationResult = await Promise.all(args.credentials.map(async (credential) => { - return await context.agent.verifyCredential({ - credential, - policies: { - credentialStatus: false - } - } satisfies IVerifyCredentialArgs) - })) - - // early return if verification failed for any credential - if (verificationResult.some(result => !result.verified)) { - // define verified - return { revoked: Array(args.credentials.length).fill(false), error: verificationResult.find(result => !result.verified)!.error || { message: 'verification: could not verify credential' } } - } - } - - // if revocation options are provided, give precedence - if (args?.revocationOptions) { - // validate revocation options - case: revocationOptions.issuerDid - if (!args.revocationOptions.issuerDid) throw new Error('[did-provider-cheqd]: revocation: revocationOptions.issuerDid is required') - - // validate revocation options - case: revocationOptions.statusListName - if (!args.revocationOptions.statusListName) throw new Error('[did-provider-cheqd]: revocation: revocationOptions.statusListName is required') - - // validate revocation options - case: revocationOptions.statusListIndices - if (!args.revocationOptions.statusListIndices || !args.revocationOptions.statusListIndices.length || args.revocationOptions.statusListIndices.length === 0 || !args.revocationOptions.statusListIndices.every(index => !isNaN(+index))) throw new Error('[did-provider-cheqd]: revocation: revocationOptions.statusListIndex is required and must be an array of indices') - - // construct status list credential - const statusListCredential = `${DefaultResolverUrl}${args.revocationOptions.issuerDid}?resourceName=${args.revocationOptions.statusListName}&resourceType=StatusList2021Revocation` - - // construct credential status - args.credentials = args.revocationOptions.statusListIndices.map(index => ({ - '@context': [], - issuer: args.revocationOptions!.issuerDid, - credentialSubject: {}, - credentialStatus: { - id: `${statusListCredential}#${index}`, - type: 'StatusList2021Entry', - statusPurpose: 'revocation', - statusListIndex: `${index}`, - }, - issuanceDate: '', - proof: {} - })) - } - - // validate args - case: credentials - if (!args.credentials || !args.credentials.length || args.credentials.length === 0) throw new Error('[did-provider-cheqd]: revocation: credentials is required and must be an array of credentials') - - // if jwt credentials, decode them - const credentials = await Promise.all(args.credentials.map(async credential => typeof credential === 'string' ? await Cheqd.decodeCredentialJWT(credential) : credential)) - - // validate args in pairs - case: statusListFile and statusList - if (args.options?.statusListFile && args.options?.statusList) { - throw new Error('[did-provider-cheqd]: revocation: statusListFile and statusList are mutually exclusive') - } - - // validate args in pairs - case: statusListFile and fetchList - if (args.options?.statusListFile && args.options?.fetchList) { - throw new Error('[did-provider-cheqd]: revocation: statusListFile and fetchList are mutually exclusive') - } - - // validate args in pairs - case: statusList and fetchList - if (args.options?.statusList && args.options?.fetchList) { - throw new Error('[did-provider-cheqd]: revocation: statusList and fetchList are mutually exclusive') - } - - // validate args in pairs - case: publish - if (args.options?.publish && !args.fetchList && !(args.options?.statusListFile || args.options?.statusList)) { - throw new Error('[did-provider-cheqd]: revocation: publish requires statusListFile or statusList, if fetchList is disabled') - } - - // define issuer - const issuer = typeof credentials[0].issuer === 'string' - ? credentials[0].issuer - : (credentials[0].issuer as { id: string }).id - - // define provider, if applicable - this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders) - - // define provider id, if applicable - this.providerId = Cheqd.generateProviderId(issuer) - - // define dkg options, if provided - args.dkgOptions ||= this.didProvider.dkgOptions - - // revoke credentials - return await Cheqd.revokeCredentials(credentials, { - ...args.options, - topArgs: args, - publishOptions: { - context, - resourceId: args?.options?.resourceId, - resourceVersion: args?.options?.resourceVersion, - resourceAlsoKnownAs: args?.options?.alsoKnownAs, - signInputs: args?.options?.signInputs, - fee: args?.options?.fee - } - }) - } - - private async SuspendCredentialWithStatusList2021(args: ICheqdSuspendCredentialWithStatusList2021Args, context: IContext): Promise { - // verify credential, if provided and suspension options are not - if (args?.credential && !args?.suspensionOptions) { - const verificationResult = await context.agent.verifyCredential({ - credential: args.credential, - policies: { - credentialStatus: false - } - } satisfies IVerifyCredentialArgs) - - // early return if verification failed - if (!verificationResult.verified) { - return { suspended: false, error: verificationResult.error } - } - } - - // if suspension options are provided, give precedence - if (args?.suspensionOptions) { - // validate suspension options - case: suspensionOptions.issuerDid - if (!args.suspensionOptions.issuerDid) throw new Error('[did-provider-cheqd]: suspension: suspensionOptions.issuerDid is required') - - // validate suspension options - case: suspensionOptions.statusListName - if (!args.suspensionOptions.statusListName) throw new Error('[did-provider-cheqd]: suspension: suspensionOptions.statusListName is required') - - // validate suspension options - case: suspensionOptions.statusListIndex - if (!args.suspensionOptions.statusListIndex) throw new Error('[did-provider-cheqd]: suspension: suspensionOptions.statusListIndex is required') - - // construct status list credential - const statusListCredential = `${DefaultResolverUrl}${args.suspensionOptions.issuerDid}?resourceName=${args.suspensionOptions.statusListName}&resourceType=StatusList2021Suspension` - - // construct credential status - args.credential = { - '@context': [], - issuer: args.suspensionOptions.issuerDid, - credentialSubject: {}, - credentialStatus: { - id: `${statusListCredential}#${args.suspensionOptions.statusListIndex}`, - type: 'StatusList2021Entry', - statusPurpose: 'suspension', - statusListIndex: `${args.suspensionOptions.statusListIndex}`, - }, - issuanceDate: '', - proof: {} - } - } - - // validate args - case: credential - if (!args.credential) throw new Error('[did-provider-cheqd]: suspension: credential is required') - - // if jwt credential, decode it - const credential = typeof args.credential === 'string' ? await Cheqd.decodeCredentialJWT(args.credential) : args.credential - - // validate status purpose - if (credential.credentialStatus?.statusPurpose !== 'suspension') { - throw new Error(`[did-provider-cheqd]: suspension: Unsupported status purpose: ${credential.credentialStatus?.statusPurpose}`) - } - - // validate args in pairs - case: statusListFile and statusList - if (args.options?.statusListFile && args.options?.statusList) { - throw new Error('[did-provider-cheqd]: suspension: statusListFile and statusList are mutually exclusive') - } - - // validate args in pairs - case: statusListFile and fetchList - if (args.options?.statusListFile && args.options?.fetchList) { - throw new Error('[did-provider-cheqd]: suspension: statusListFile and fetchList are mutually exclusive') - } - - // validate args in pairs - case: statusList and fetchList - if (args.options?.statusList && args.options?.fetchList) { - throw new Error('[did-provider-cheqd]: suspension: statusList and fetchList are mutually exclusive') - } - - // validate args in pairs - case: publish - if (args.options?.publish && !args.fetchList && !(args.options?.statusListFile || args.options?.statusList)) { - throw new Error('[did-provider-cheqd]: suspension: publish requires statusListFile or statusList, if fetchList is disabled') - } - - // define issuer - const issuer = typeof credential.issuer === 'string' - ? credential.issuer - : (credential.issuer as { id: string }).id - - // define provider, if applicable - this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders) - - // define provider id, if applicable - this.providerId = Cheqd.generateProviderId(issuer) - - // define dkg options, if provided - args.dkgOptions ||= this.didProvider.dkgOptions - - // suspend credential - return await Cheqd.suspendCredential(credential, { - ...args.options, - topArgs: args, - publishOptions: { - context, - statusListEncoding: args?.options?.statusListEncoding, - statusListValidUntil: args?.options?.statusListValidUntil, - resourceId: args?.options?.resourceId, - resourceVersion: args?.options?.resourceVersion, - resourceAlsoKnownAs: args?.options?.alsoKnownAs, - signInputs: args?.options?.signInputs, - fee: args?.options?.fee - } - }) - } - - private async SuspendBulkCredentialsWithStatusList2021(args: ICheqdSuspendBulkCredentialsWithStatusList2021Args, context: IContext): Promise { - // verify credential, if provided and suspension options are not - if (args?.credentials && !args?.suspensionOptions) { - - const verificationResult = await Promise.all(args.credentials.map(async (credential) => { - return await context.agent.verifyCredential({ - credential, - policies: { - credentialStatus: false - } - } satisfies IVerifyCredentialArgs) - })) - - // early return if verification failed for any credential - if (verificationResult.some(result => !result.verified)) { - // define verified - return { suspended: Array(args.credentials.length).fill(false), error: verificationResult.find(result => !result.verified)!.error || { message: 'verification: could not verify credential' } } - } - } - - // if suspension options are provided, give precedence - if (args?.suspensionOptions) { - // validate suspension options - case: suspensionOptions.issuerDid - if (!args.suspensionOptions.issuerDid) throw new Error('[did-provider-cheqd]: suspension: suspensionOptions.issuerDid is required') - - // validate suspension options - case: suspensionOptions.statusListName - if (!args.suspensionOptions.statusListName) throw new Error('[did-provider-cheqd]: suspension: suspensionOptions.statusListName is required') - - // validate suspension options - case: suspensionOptions.statusListIndices - if (!args.suspensionOptions.statusListIndices || !args.suspensionOptions.statusListIndices.length || args.suspensionOptions.statusListIndices.length === 0 || !args.suspensionOptions.statusListIndices.every(index => !isNaN(+index))) throw new Error('[did-provider-cheqd]: suspension: suspensionOptions.statusListIndex is required and must be an array of indices') - - // construct status list credential - const statusListCredential = `${DefaultResolverUrl}${args.suspensionOptions.issuerDid}?resourceName=${args.suspensionOptions.statusListName}&resourceType=StatusList2021Suspension` - - // construct credential status - args.credentials = args.suspensionOptions.statusListIndices.map(index => ({ - '@context': [], - issuer: args.suspensionOptions!.issuerDid, - credentialSubject: {}, - credentialStatus: { - id: `${statusListCredential}#${index}`, - type: 'StatusList2021Entry', - statusPurpose: 'suspension', - statusListIndex: `${index}`, - }, - issuanceDate: '', - proof: {} - })) - } - - // validate args - case: credentials - if (!args.credentials || !args.credentials.length || args.credentials.length === 0) throw new Error('[did-provider-cheqd]: suspension: credentials is required and must be an array of credentials') - - // if jwt credentials, decode them - const credentials = await Promise.all(args.credentials.map(async credential => typeof credential === 'string' ? await Cheqd.decodeCredentialJWT(credential) : credential)) - - // validate args in pairs - case: statusListFile and statusList - if (args.options?.statusListFile && args.options?.statusList) { - throw new Error('[did-provider-cheqd]: suspension: statusListFile and statusList are mutually exclusive') - } - - // validate args in pairs - case: statusListFile and fetchList - if (args.options?.statusListFile && args.options?.fetchList) { - throw new Error('[did-provider-cheqd]: suspension: statusListFile and fetchList are mutually exclusive') - } - - // validate args in pairs - case: statusList and fetchList - if (args.options?.statusList && args.options?.fetchList) { - throw new Error('[did-provider-cheqd]: suspension: statusList and fetchList are mutually exclusive') - } - - // validate args in pairs - case: publish - if (args.options?.publish && !args.fetchList && !(args.options?.statusListFile || args.options?.statusList)) { - throw new Error('[did-provider-cheqd]: suspension: publish requires statusListFile or statusList, if fetchList is disabled') - } - - // define issuer - const issuer = typeof credentials[0].issuer === 'string' - ? credentials[0].issuer - : (credentials[0].issuer as { id: string }).id - - // define provider, if applicable - this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders) - - // define provider id, if applicable - this.providerId = Cheqd.generateProviderId(issuer) - - // define dkg options, if provided - args.dkgOptions ||= this.didProvider.dkgOptions - - // suspend credentials - return await Cheqd.suspendCredentials(credentials, { - ...args.options, - topArgs: args, - publishOptions: { - context, - resourceId: args?.options?.resourceId, - resourceVersion: args?.options?.resourceVersion, - resourceAlsoKnownAs: args?.options?.alsoKnownAs, - signInputs: args?.options?.signInputs, - fee: args?.options?.fee - } - }) - } - - private async UnsuspendCredentialWithStatusList2021(args: ICheqdUnsuspendCredentialWithStatusList2021Args, context: IContext): Promise { - // verify credential, if provided and unsuspension options are not - if (args?.credential && !args?.unsuspensionOptions) { - const verificationResult = await context.agent.verifyCredential({ - credential: args.credential, - policies: { - credentialStatus: false - } - } satisfies IVerifyCredentialArgs) - - // early return if verification failed - if (!verificationResult.verified) { - return { unsuspended: false, error: verificationResult.error } - } - } - - // if unsuspension options are provided, give precedence - if (args?.unsuspensionOptions) { - // validate unsuspension options - case: unsuspensionOptions.issuerDid - if (!args.unsuspensionOptions.issuerDid) throw new Error('[did-provider-cheqd]: unsuspension: unsuspensionOptions.issuerDid is required') - - // validate unsuspension options - case: unsuspensionOptions.statusListName - if (!args.unsuspensionOptions.statusListName) throw new Error('[did-provider-cheqd]: unsuspension: unsuspensionOptions.statusListName is required') - - // validate unsuspension options - case: unsuspensionOptions.statusListIndex - if (!args.unsuspensionOptions.statusListIndex) throw new Error('[did-provider-cheqd]: unsuspension: unsuspensionOptions.statusListIndex is required') - - // construct status list credential - const statusListCredential = `${DefaultResolverUrl}${args.unsuspensionOptions.issuerDid}?resourceName=${args.unsuspensionOptions.statusListName}&resourceType=StatusList2021Suspension` - - // construct credential status - args.credential = { - '@context': [], - issuer: args.unsuspensionOptions.issuerDid, - credentialSubject: {}, - credentialStatus: { - id: `${statusListCredential}#${args.unsuspensionOptions.statusListIndex}`, - type: 'StatusList2021Entry', - statusPurpose: 'suspension', - statusListIndex: `${args.unsuspensionOptions.statusListIndex}`, - }, - issuanceDate: '', - proof: {} - } - } - - // validate args - case: credential - if (!args.credential) throw new Error('[did-provider-cheqd]: unsuspension: credential is required') - - // if jwt credential, decode it - const credential = typeof args.credential === 'string' ? await Cheqd.decodeCredentialJWT(args.credential) : args.credential - - // validate status purpose - if (credential.credentialStatus?.statusPurpose !== 'suspension') { - throw new Error(`[did-provider-cheqd]: suspension: Unsupported status purpose: ${credential.credentialStatus?.statusPurpose}`) - } - - // validate args in pairs - case: statusListFile and statusList - if (args.options?.statusListFile && args.options?.statusList) { - throw new Error('[did-provider-cheqd]: suspension: statusListFile and statusList are mutually exclusive') - } - - // validate args in pairs - case: statusListFile and fetchList - if (args.options?.statusListFile && args.options?.fetchList) { - throw new Error('[did-provider-cheqd]: suspension: statusListFile and fetchList are mutually exclusive') - } - - // validate args in pairs - case: statusList and fetchList - if (args.options?.statusList && args.options?.fetchList) { - throw new Error('[did-provider-cheqd]: suspension: statusList and fetchList are mutually exclusive') - } - - // validate args in pairs - case: publish - if (args.options?.publish && !args.fetchList && !(args.options?.statusListFile || args.options?.statusList)) { - throw new Error('[did-provider-cheqd]: suspension: publish requires statusListFile or statusList, if fetchList is disabled') - } - - // define issuer - const issuer = typeof credential.issuer === 'string' - ? credential.issuer - : (credential.issuer as { id: string }).id - - // define provider, if applicable - this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders) - - // define provider id, if applicable - this.providerId = Cheqd.generateProviderId(issuer) - - // define dkg options, if provided - args.dkgOptions ||= this.didProvider.dkgOptions - - // suspend credential - return await Cheqd.unsuspendCredential(credential, { - ...args.options, - topArgs: args, - publishOptions: { - context, - statusListEncoding: args?.options?.statusListEncoding, - statusListValidUntil: args?.options?.statusListValidUntil, - resourceId: args?.options?.resourceId, - resourceVersion: args?.options?.resourceVersion, - resourceAlsoKnownAs: args?.options?.alsoKnownAs, - signInputs: args?.options?.signInputs, - fee: args?.options?.fee - } - }) - } - - private async UnsuspendBulkCredentialsWithStatusList2021(args: ICheqdUnsuspendBulkCredentialsWithStatusList2021Args, context: IContext): Promise { - // verify credential, if provided and unsuspension options are not - if (args?.credentials && !args?.unsuspensionOptions) { - - const verificationResult = await Promise.all(args.credentials.map(async (credential) => { - return await context.agent.verifyCredential({ - credential, - policies: { - credentialStatus: false - } - } satisfies IVerifyCredentialArgs) - })) - - // early return if verification failed for any credential - if (verificationResult.some(result => !result.verified)) { - // define verified - return { unsuspended: Array(args.credentials.length).fill(false), error: verificationResult.find(result => !result.verified)!.error || { message: 'verification: could not verify credential' } } - } - } - - // if unsuspension options are provided, give precedence - if (args?.unsuspensionOptions) { - // validate unsuspension options - case: unsuspensionOptions.issuerDid - if (!args.unsuspensionOptions.issuerDid) throw new Error('[did-provider-cheqd]: unsuspension: unsuspensionOptions.issuerDid is required') - - // validate unsuspension options - case: unsuspensionOptions.statusListName - if (!args.unsuspensionOptions.statusListName) throw new Error('[did-provider-cheqd]: unsuspension: unsuspensionOptions.statusListName is required') - - // validate unsuspension options - case: unsuspensionOptions.statusListIndices - if (!args.unsuspensionOptions.statusListIndices || !args.unsuspensionOptions.statusListIndices.length || args.unsuspensionOptions.statusListIndices.length === 0 || !args.unsuspensionOptions.statusListIndices.every(index => !isNaN(+index))) throw new Error('[did-provider-cheqd]: unsuspension: unsuspensionOptions.statusListIndex is required and must be an array of indices') - - // construct status list credential - const statusListCredential = `${DefaultResolverUrl}${args.unsuspensionOptions.issuerDid}?resourceName=${args.unsuspensionOptions.statusListName}&resourceType=StatusList2021Suspension` - - // construct credential status - args.credentials = args.unsuspensionOptions.statusListIndices.map(index => ({ - '@context': [], - issuer: args.unsuspensionOptions!.issuerDid, - credentialSubject: {}, - credentialStatus: { - id: `${statusListCredential}#${index}`, - type: 'StatusList2021Entry', - statusPurpose: 'suspension', - statusListIndex: `${index}`, - }, - issuanceDate: '', - proof: {} - })) - } - - // validate args - case: credentials - if (!args.credentials || !args.credentials.length || args.credentials.length === 0) throw new Error('[did-provider-cheqd]: unsuspension: credentials is required and must be an array of credentials') - - // if jwt credentials, decode them - const credentials = await Promise.all(args.credentials.map(async credential => typeof credential === 'string' ? await Cheqd.decodeCredentialJWT(credential) : credential)) - - // validate args in pairs - case: statusListFile and statusList - if (args.options?.statusListFile && args.options?.statusList) { - throw new Error('[did-provider-cheqd]: unsuspension: statusListFile and statusList are mutually exclusive') - } - - // validate args in pairs - case: statusListFile and fetchList - if (args.options?.statusListFile && args.options?.fetchList) { - throw new Error('[did-provider-cheqd]: unsuspension: statusListFile and fetchList are mutually exclusive') - } - - // validate args in pairs - case: statusList and fetchList - if (args.options?.statusList && args.options?.fetchList) { - throw new Error('[did-provider-cheqd]: unsuspension: statusList and fetchList are mutually exclusive') - } - - // validate args in pairs - case: publish - if (args.options?.publish && !args.fetchList && !(args.options?.statusListFile || args.options?.statusList)) { - throw new Error('[did-provider-cheqd]: unsuspension: publish requires statusListFile or statusList, if fetchList is disabled') - } - - // define issuer - const issuer = typeof credentials[0].issuer === 'string' - ? credentials[0].issuer - : (credentials[0].issuer as { id: string }).id - - // define provider, if applicable - this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders) - - // define provider id, if applicable - this.providerId = Cheqd.generateProviderId(issuer) - - // define dkg options, if provided - args.dkgOptions ||= this.didProvider.dkgOptions - - // suspend credentials - return await Cheqd.unsuspendCredentials(credentials, { - ...args.options, - topArgs: args, - publishOptions: { - context, - resourceId: args?.options?.resourceId, - resourceVersion: args?.options?.resourceVersion, - resourceAlsoKnownAs: args?.options?.alsoKnownAs, - signInputs: args?.options?.signInputs, - fee: args?.options?.fee - } - }) - } - - private async TransactSendTokens(args: ICheqdTransactSendTokensArgs, context: IContext): Promise { - // define provider - const provider = function (that) { - // switch on network - return that.supportedDidProviders.find(provider => provider.network === args.network) || (function () { throw new Error(`[did-provider-cheqd]: transact: no relevant providers found`) }()) - }(this) - - try { - // delegate to provider - const transactionResult = await provider.transactSendTokens({ - recipientAddress: args.recipientAddress, - amount: args.amount, - memo: args.memo, - txBytes: args.txBytes, - }) - - // return transaction result - return { - successful: !transactionResult.code, - transactionHash: transactionResult.transactionHash, - events: transactionResult.events, - rawLog: transactionResult.rawLog, - txResponse: args?.returnTxResponse ? transactionResult : undefined - } satisfies TransactionResult - } catch (error) { - // return error - return { - successful: false, - error: error as IError - } satisfies TransactionResult - } - } - - private async ObservePaymentCondition(args: ICheqdObservePaymentConditionArgs, context: IContext): Promise { - // verify with raw unified access control condition, if any - if (args?.unifiedAccessControlCondition) { - // validate args - case: unifiedAccessControlCondition.chain - if (!args.unifiedAccessControlCondition.chain || !Object.values(LitCompatibleCosmosChains).includes(args.unifiedAccessControlCondition.chain as LitCompatibleCosmosChain)) throw new Error('[did-provider-cheqd]: observe: unifiedAccessControlCondition.chain is required and must be a valid Lit-compatible chain') - - // validate args - case: unifiedAccessControlCondition.path - if (!args.unifiedAccessControlCondition.path) throw new Error('[did-provider-cheqd]: observe: unifiedAccessControlCondition.path is required') - - // validate args - case: unifiedAccessControlCondition.conditionType - if (args.unifiedAccessControlCondition.conditionType !== 'cosmos') throw new Error('[did-provider-cheqd]: observe: unifiedAccessControlCondition.conditionType must be cosmos') - - // validate args - case: unifiedAccessControlCondition.method - if (args.unifiedAccessControlCondition.method !== 'timelock') throw new Error('[did-provider-cheqd]: observe: unifiedAccessControlCondition.method must be timelock') - - // validate args - case: unifiedAccessControlCondition.parameters - if (!args.unifiedAccessControlCondition.parameters || !Array.isArray(args.unifiedAccessControlCondition.parameters) || args.unifiedAccessControlCondition.parameters.length === 0 || args.unifiedAccessControlCondition.parameters.length > 1) throw new Error('[did-provider-cheqd]: observe: unifiedAccessControlCondition.parameters is required and must be an array of length 1 of type string content') - - // validate args - case: unifiedAccessControlCondition.returnValueTest - if (!args.unifiedAccessControlCondition.returnValueTest || !args.unifiedAccessControlCondition.returnValueTest.comparator || !args.unifiedAccessControlCondition.returnValueTest.key || !args.unifiedAccessControlCondition.returnValueTest.value) throw new Error('[did-provider-cheqd]: observe: unifiedAccessControlCondition.returnValueTest is required') - - try { - // define network - const network = (function() { - switch (args.unifiedAccessControlCondition.chain) { - case LitCompatibleCosmosChains.cheqdMainnet: - return CheqdNetwork.Mainnet - case LitCompatibleCosmosChains.cheqdTestnet: - return CheqdNetwork.Testnet - default: - throw new Error(`[did-provider-cheqd]: observe: Unsupported chain: ${args.unifiedAccessControlCondition.chain}`) - } - }()) - - // get block height url - const blockHeightUrl = function (){ - switch (args.unifiedAccessControlCondition.parameters[0]) { - case 'latest': - return `${DefaultRESTUrls[network]}/cosmos/base/tendermint/v1beta1/blocks/latest` - default: - return `${DefaultRESTUrls[network]}/cosmos/base/tendermint/v1beta1/blocks/${args.unifiedAccessControlCondition.parameters[0]}` - } - }() - - // fetch block response - const blockHeightResponse = await (await fetch(blockHeightUrl)).json() as BlockResponse - - // get timestamp from block response - const blockTimestamp = Date.parse(blockHeightResponse.block.header.time) - - // construct url - const url = `${DefaultRESTUrls[network]}${args.unifiedAccessControlCondition.path}` - - // fetch relevant txs - const txs = await (await fetch(url)).json() as ShallowTypedTxsResponse - - // skim through txs for relevant events, in which case the transaction timestamp is within the defined interval in seconds, from the block timestamp - const meetsConditionTxIndex = txs?.tx_responses?.findIndex((tx) => { - // get tx timestamp - const txTimestamp = Date.parse(tx.timestamp) - - // calculate diff in seconds - const diffInSeconds = Math.floor((blockTimestamp - txTimestamp) / 1000) - - // return meets condition - switch (args.unifiedAccessControlCondition!.returnValueTest.comparator) { - case '<': - return diffInSeconds < parseInt(args.unifiedAccessControlCondition!.returnValueTest.value) - case '<=': - return diffInSeconds <= parseInt(args.unifiedAccessControlCondition!.returnValueTest.value) - default: - throw new Error(`[did-provider-cheqd]: observe: Unsupported comparator: ${args.unifiedAccessControlCondition!.returnValueTest.comparator}`) - } - }) - - // define meetsCondition - const meetsCondition = (typeof meetsConditionTxIndex !== 'undefined' && meetsConditionTxIndex !== -1) - - // return observation result - return { - subscribed: true, - meetsCondition: meetsCondition, - transactionHash: meetsCondition ? txs!.tx_responses[meetsConditionTxIndex].txhash : undefined, - events: meetsCondition ? txs!.tx_responses[meetsConditionTxIndex].events : undefined, - rawLog: meetsCondition ? txs!.tx_responses[meetsConditionTxIndex].raw_log : undefined, - txResponse: meetsCondition ? (args?.returnTxResponse ? txs!.tx_responses[meetsConditionTxIndex] : undefined) : undefined - } satisfies ObservationResult - } catch (error) { - // return error - return { - subscribed: false, - meetsCondition: false, - error: error as IError - } satisfies ObservationResult - } - } - - // validate access control conditions components - case: recipientAddress - if (!args.recipientAddress) { - throw new Error('[did-provider-cheqd]: observation: recipientAddress is required') - } - - // validate access control conditions components - case: amount - if (!args.amount || !args.amount.amount || !args.amount.denom || args.amount.denom !== 'ncheq') { - 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`') - } - - // validate access control conditions components - case: intervalInSeconds - if (!args.intervalInSeconds) { - throw new Error('[did-provider-cheqd]: observation: intervalInSeconds is required') - } - - // validate access control conditions components - case: comparator - if (!args.comparator || (args.comparator !== '<' && args.comparator !== '<=')) { - throw new Error('[did-provider-cheqd]: observation: comparator is required and must be either `<` or `<=`') - } - - // validate access control conditions components - case: network - if (!args.network) { - throw new Error('[did-provider-cheqd]: observation: network is required') - } - - // define block height, if not provided - args.blockHeight ||= 'latest' - - try { - // get block height url - const blockHeightUrl = function (){ - switch (args.blockHeight) { - case 'latest': - return `${DefaultRESTUrls[args.network]}/cosmos/base/tendermint/v1beta1/blocks/latest` - default: - return `${DefaultRESTUrls[args.network]}/cosmos/base/tendermint/v1beta1/blocks/${args.blockHeight}` - } - }() - - // fetch block response - const blockHeightResponse = await (await fetch(blockHeightUrl)).json() as BlockResponse - - // get timestamp from block response - const blockTimestamp = Date.parse(blockHeightResponse.block.header.time) - - // otherwise, construct url, as per components - 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` - - // fetch relevant txs - const txs = await (await fetch(url)).json() as ShallowTypedTxsResponse - - // skim through txs for relevant events, in which case the transaction timestamp is within the defined interval in seconds, from the block timestamp - const meetsConditionTxIndex = txs?.tx_responses?.findIndex((tx) => { - // get tx timestamp - const txTimestamp = Date.parse(tx.timestamp) - - // calculate diff in seconds - const diffInSeconds = Math.floor((blockTimestamp - txTimestamp) / 1000) - - // return meets condition - switch (args.comparator) { - case '<': - return diffInSeconds < args.intervalInSeconds! - case '<=': - return diffInSeconds <= args.intervalInSeconds! - default: - throw new Error(`[did-provider-cheqd]: observe: Unsupported comparator: ${args.unifiedAccessControlCondition!.returnValueTest.comparator}`) - } - }) - - // define meetsCondition - const meetsCondition = (typeof meetsConditionTxIndex !== 'undefined' && meetsConditionTxIndex !== -1) - - // return observation result - return { - subscribed: true, - meetsCondition: meetsCondition, - transactionHash: meetsCondition ? txs!.tx_responses[meetsConditionTxIndex].txhash : undefined, - events: meetsCondition ? txs!.tx_responses[meetsConditionTxIndex].events : undefined, - rawLog: meetsCondition ? txs!.tx_responses[meetsConditionTxIndex].raw_log : undefined, - txResponse: meetsCondition ? (args?.returnTxResponse ? txs!.tx_responses[meetsConditionTxIndex] : undefined) : undefined - } satisfies ObservationResult - } catch (error) { - // return error - return { - subscribed: false, - meetsCondition: false, - error: error as IError - } satisfies ObservationResult - } - } - - static async revokeCredential(credential: VerifiableCredential, options?: ICheqdStatusList2021Options): Promise { - try { - // validate status purpose - if (credential?.credentialStatus?.statusPurpose !== 'revocation') throw new Error('[did-provider-cheqd]: revocation: Invalid status purpose') - - // fetch status list 2021 - const publishedList = (await Cheqd.fetchStatusList2021(credential)) as StatusList2021Revocation - - // early return, if encrypted and no decryption key provided - if (publishedList.metadata.encrypted && !options?.topArgs?.symmetricKey) throw new Error('[did-provider-cheqd]: revocation: symmetricKey is required, if status list 2021 is encrypted') - - // fetch status list 2021 inscribed in credential - const statusList2021 = options?.topArgs?.fetchList - ? (await async function () { - // if not encrypted, return bitstring - if (!publishedList.metadata.encrypted) - return publishedList.metadata.encoding === 'base64url' - ? publishedList.StatusList2021.encodedList - : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding as DefaultStatusList2021Encoding), 'base64url') - - // otherwise, decrypt and return raw bitstring - const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex')) - - // decrypt - return await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex')) - }()) - : (await async function () { - // transcode to base64url, if needed - const publishedListTranscoded = publishedList.metadata.encoding === 'base64url' - ? publishedList.StatusList2021.encodedList - : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding as DefaultStatusList2021Encoding), 'base64url') - - // if status list 2021 is not fetched, read from file - if (options?.statusListFile) { - // if not encrypted, return bitstring - if (!publishedList.metadata.encrypted) { - // construct encoded status list - const encoded = new StatusList({ buffer: await Cheqd.getFile(options.statusListFile) }).encode() as Bitstring - - // validate against published list - if (encoded !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: revocation: statusListFile does not match published status list 2021') - - // return encoded - return encoded - } - - // otherwise, decrypt and return bitstring - const scopedRawBlob = await toBlob(await Cheqd.getFile(options.statusListFile)) - - // decrypt - const decrypted = await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex')) - - // validate against published list - if (decrypted !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: revocation: statusListFile does not match published status list 2021') - - // return decrypted - return decrypted - } - - if (!options?.statusListInlineBitstring) throw new Error('[did-provider-cheqd]: revocation: statusListInlineBitstring is required, if statusListFile is not provided') - - // validate against published list - if (options?.statusListInlineBitstring !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: revocation: statusListInlineBitstring does not match published status list 2021') - - // otherwise, read from inline bitstring - return options?.statusListInlineBitstring - }()) - - // parse status list 2021 - const statusList = await StatusList.decode({ encodedList: statusList2021 }) - - // early exit, if credential is already revoked - if (statusList.getStatus(Number(credential.credentialStatus.statusListIndex))) return { revoked: false } - - // update revocation status - statusList.setStatus(Number(credential.credentialStatus.statusListIndex), true) - - // set in-memory status list ref - const bitstring = await statusList.encode() as Bitstring - - // cast top-level args - const topArgs = options?.topArgs as ICheqdRevokeCredentialWithStatusList2021Args - - // write status list 2021 to file, if provided - if (topArgs?.writeToFile) { - await Cheqd.writeFile(fromString(bitstring, 'base64url'), options?.statusListFile) - } - - // publish status list 2021, if provided - const published = topArgs?.publish - ? (await async function () { - // fetch status list 2021 metadata - const statusListMetadata = await Cheqd.fetchStatusList2021Metadata(credential) - - // publish status list 2021 as new version - const scoped = topArgs.publishEncrypted - ? (await async function () { - // validate encoding, if provided - if (options?.publishOptions?.statusListEncoding && !Object.values(DefaultStatusList2021Encodings).includes(options?.publishOptions?.statusListEncoding)) { - throw new Error('[did-provider-cheqd]: revocation: Invalid status list encoding') - } - - // validate validUntil, if provided - if (options?.publishOptions?.statusListValidUntil) { - // validate validUntil as string - if (typeof options?.publishOptions?.statusListValidUntil !== 'string') throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be string)') - - // validate validUntil as date - if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be date)') - - // validate validUntil as future date - if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be future date)') - - // validate validUntil towards validFrom - if (new Date(options?.publishOptions?.statusListValidUntil) <= new Date(publishedList.StatusList2021.validFrom)) throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be after validFrom)') - } - - // validate paymentConditions, if provided - if (topArgs?.paymentConditions) { - if (!topArgs?.paymentConditions?.every((condition) => condition.feePaymentAddress && condition.feePaymentAmount && condition.intervalInSeconds)) { - throw new Error('[did-provider-cheqd]: paymentConditions must contain feePaymentAddress and feeAmount and intervalInSeconds') - } - - if (!topArgs?.paymentConditions?.every((condition) => typeof condition.feePaymentAddress === 'string' && typeof condition.feePaymentAmount === 'string' && typeof condition.intervalInSeconds === 'number')) { - throw new Error('[did-provider-cheqd]: feePaymentAddress and feePaymentAmount must be string and intervalInSeconds must be number') - } - - if (!topArgs?.paymentConditions?.every((condition) => condition.type === AccessControlConditionTypes.timelockPayment)) { - throw new Error('[did-provider-cheqd]: paymentConditions must be of type timelockPayment') - } - } - - // validate dkgOptions - if (!topArgs?.dkgOptions || !topArgs?.dkgOptions?.chain || !topArgs?.dkgOptions?.network) { - throw new Error('[did-provider-cheqd]: dkgOptions is required') - } - - // instantiate dkg-threshold client, in which case lit-protocol is used - const lit = await LitProtocol.create({ - chain: topArgs?.dkgOptions?.chain, - litNetwork: topArgs?.dkgOptions?.network - }) - - // construct access control conditions and payment conditions tuple - const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted - ? (await (async function () { - // define payment conditions, give precedence to top-level args - const paymentConditions = topArgs?.paymentConditions || publishedList.metadata.paymentConditions! - - // return access control conditions and payment conditions tuple - return [ - await Promise.all(paymentConditions.map(async (condition) => { - switch (condition.type) { - case AccessControlConditionTypes.timelockPayment: - return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({ - key: '$.tx_responses.*.timestamp', - comparator: '<=', - value: `${condition.intervalInSeconds}`, - }, - condition.feePaymentAmount, - condition.feePaymentAddress, - condition?.blockHeight, - topArgs?.dkgOptions?.chain - ) - default: - throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`) - } - })), - paymentConditions - ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]] - }())) - : (await (async function () { - // validate paymentConditions - if (!topArgs?.paymentConditions) { - throw new Error('[did-provider-cheqd]: paymentConditions is required') - } - - // return access control conditions and payment conditions tuple - return [ - await Promise.all(topArgs.paymentConditions.map(async (condition) => { - switch (condition.type) { - case AccessControlConditionTypes.timelockPayment: - return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({ - key: '$.tx_responses.*.timestamp', - comparator: '<=', - value: `${condition.intervalInSeconds}`, - }, - condition.feePaymentAmount, - condition.feePaymentAddress, - condition?.blockHeight - ) - default: - throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`) - } - })), - topArgs.paymentConditions - ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]] - }())) - - // encrypt bitstring - const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt(bitstring, unifiedAccessControlConditionsTuple[0], true) - - // define status list content - const content = { - StatusList2021: { - statusPurpose: publishedList.StatusList2021.statusPurpose, - encodedList: await blobToHexString(encryptedString), - validFrom: publishedList.StatusList2021.validFrom, - validUntil: options?.publishOptions?.statusListValidUntil || publishedList.StatusList2021.validUntil - }, - metadata: { - type: publishedList.metadata.type, - encrypted: true, - encoding: (options?.publishOptions?.statusListEncoding as DefaultStatusList2021Encoding | undefined) || publishedList.metadata.encoding, - encryptedSymmetricKey, - paymentConditions: unifiedAccessControlConditionsTuple[1] - } - } satisfies StatusList2021Revocation - - // return tuple of publish result and encryption relevant metadata - return [ - await Cheqd.publishStatusList2021(fromString(JSON.stringify(content), 'utf-8'), statusListMetadata, options?.publishOptions), - { encryptedString, encryptedSymmetricKey, symmetricKey: toString(symmetricKey!, 'hex') } - ] - }()) - : (await async function () { - // validate encoding, if provided - if (options?.publishOptions?.statusListEncoding && !Object.values(DefaultStatusList2021Encodings).includes(options?.publishOptions?.statusListEncoding)) { - throw new Error('[did-provider-cheqd]: revocation: Invalid status list encoding') - } - - // validate validUntil, if provided - if (options?.publishOptions?.statusListValidUntil) { - // validate validUntil as string - if (typeof options?.publishOptions?.statusListValidUntil !== 'string') throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be string)') - - // validate validUntil as date - if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be date)') - - // validate validUntil as future date - if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be future date)') - - // validate validUntil towards validFrom - if (new Date(options?.publishOptions?.statusListValidUntil) <= new Date(publishedList.StatusList2021.validFrom)) throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be after validFrom)') - } - - // define status list content - const content = { - StatusList2021: { - statusPurpose: publishedList.StatusList2021.statusPurpose, - encodedList: publishedList.metadata.encoding === 'base64url' ? bitstring : toString(fromString(bitstring, 'base64url'), options!.publishOptions.statusListEncoding as DefaultStatusList2021Encoding), - validFrom: publishedList.StatusList2021.validFrom, - validUntil: options?.publishOptions?.statusListValidUntil || publishedList.StatusList2021.validUntil - }, - metadata: { - type: publishedList.metadata.type, - encoding: (options?.publishOptions?.statusListEncoding as DefaultStatusList2021Encoding | undefined) || publishedList.metadata.encoding, - encrypted: false, - } - } satisfies StatusList2021Revocation - - // return tuple of publish result and encryption relevant metadata - return [ - await Cheqd.publishStatusList2021(fromString(JSON.stringify(content), 'utf-8'), statusListMetadata, options?.publishOptions), - undefined - ] - }()) - - // early exit, if publish failed - if (!scoped[0]) throw new Error('[did-provider-cheqd]: revocation: Failed to publish status list 2021') - - // return publish result - return scoped - }()) - : undefined - - return { - revoked: true, - published: topArgs?.publish ? true : undefined, - statusList: topArgs?.returnUpdatedStatusList ? await Cheqd.fetchStatusList2021(credential) as StatusList2021Revocation : undefined, - symmetricKey: topArgs?.returnSymmetricKey ? (published?.[1] as { symmetricKey: string })?.symmetricKey : undefined, - resourceMetadata: topArgs?.returnStatusListMetadata ? await Cheqd.fetchStatusList2021Metadata(credential) : undefined - } satisfies RevocationResult - } catch (error) { - // silent fail + early exit - console.error(error) - - return { revoked: false, error: error as IError } satisfies RevocationResult - } - } - - static async revokeCredentials(credentials: VerifiableCredential[], options?: ICheqdStatusList2021Options): Promise { - // validate credentials - case: empty - if (!credentials.length || credentials.length === 0) throw new Error('[did-provider-cheqd]: revocation: No credentials provided') - - // validate credentials - case: consistent issuer - if (credentials.map((credential) => { - return ((credential.issuer as { id: string }).id) - ? (credential.issuer as { id: string }).id - : credential.issuer as string - }).filter((value, _, self) => value && value !== self[0]).length > 0) throw new Error('[did-provider-cheqd]: revocation: Credentials must be issued by the same issuer') - - // validate credentials - case: status list index - if (credentials.map((credential) => credential.credentialStatus!.statusListIndex).filter((value, index, self) => self.indexOf(value) !== index).length > 0) throw new Error('[did-provider-cheqd]: revocation: Credentials must have unique status list index') - - // validate credentials - case: status purpose - if (!credentials.every((credential) => credential.credentialStatus?.statusPurpose === 'revocation')) throw new Error('[did-provider-cheqd]: revocation: Invalid status purpose') - - // validate credentials - case: status list id - const remote = credentials[0].credentialStatus?.id - ? (credentials[0].credentialStatus as { id: string }).id.split('#')[0] - : (function(){ - throw new Error('[did-provider-cheqd]: revocation: Invalid status list id') - }()) - - // validate credentials - case: status list id format - if (!RemoteListPattern.test(remote)) throw new Error('[did-provider-cheqd]: revocation: Invalid status list id format: expected: https://../1.0/identifiers/:>?resourceName=&resourceType=') - - if (!credentials.every((credential) => { - return (credential.credentialStatus as { id: string }).id.split('#')[0] === remote - })) throw new Error('[did-provider-cheqd]: revocation: Credentials must belong to the same status list') - - // validate credentials - case: status list type - if (!credentials.every((credential) => credential.credentialStatus?.type === 'StatusList2021Entry')) throw new Error('[did-provider-cheqd]: revocation: Invalid status list type') - - try { - // fetch status list 2021 - const publishedList = (await Cheqd.fetchStatusList2021(credentials[0])) as StatusList2021Revocation - - // early return, if encrypted and no decryption key provided - if (publishedList.metadata.encrypted && !options?.topArgs?.symmetricKey) throw new Error('[did-provider-cheqd]: revocation: symmetricKey is required, if status list 2021 is encrypted') - - // fetch status list 2021 inscribed in credential - const statusList2021 = options?.topArgs?.fetchList - ? (await async function () { - // if not encrypted, return bitstring - if (!publishedList.metadata.encrypted) - return publishedList.metadata.encoding === 'base64url' - ? publishedList.StatusList2021.encodedList - : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding as DefaultStatusList2021Encoding), 'base64url') - - // otherwise, decrypt and return raw bitstring - const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex')) - - // decrypt - return await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex')) - }()) - : (await async function () { - // transcode to base64url, if needed - const publishedListTranscoded = publishedList.metadata.encoding === 'base64url' - ? publishedList.StatusList2021.encodedList - : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding as DefaultStatusList2021Encoding), 'base64url') - - // if status list 2021 is not fetched, read from file - if (options?.statusListFile) { - // if not encrypted, return bitstring - if (!publishedList.metadata.encrypted) { - // construct encoded status list - const encoded = new StatusList({ buffer: await Cheqd.getFile(options.statusListFile) }).encode() as Bitstring - - // validate against published list - if (encoded !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: revocation: statusListFile does not match published status list 2021') - - // return encoded - return encoded - } - - // otherwise, decrypt and return bitstring - const scopedRawBlob = await toBlob(await Cheqd.getFile(options.statusListFile)) - - // decrypt - const decrypted = await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex')) - - // validate against published list - if (decrypted !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: revocation: statusListFile does not match published status list 2021') - - // return decrypted - return decrypted - } - - if (!options?.statusListInlineBitstring) throw new Error('[did-provider-cheqd]: revocation: statusListInlineBitstring is required, if statusListFile is not provided') - - // validate against published list - if (options?.statusListInlineBitstring !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: revocation: statusListInlineBitstring does not match published status list 2021') - - // otherwise, read from inline bitstring - return options?.statusListInlineBitstring - }()) - - // parse status list 2021 - const statusList = await StatusList.decode({ encodedList: statusList2021 }) - - // initiate bulk revocation - const revoked = await Promise.allSettled(credentials.map((credential) => { - return async function () { - // early return, if no credential status - if (!credential.credentialStatus) return { revoked: false } - - // early exit, if credential is already revoked - if (statusList.getStatus(Number(credential.credentialStatus.statusListIndex))) return { revoked: false } - - // update revocation status - statusList.setStatus(Number(credential.credentialStatus.statusListIndex), true) - - // return revocation status - return { revoked: true } - }() - })) satisfies PromiseSettledResult[] - - // revert bulk ops, if some failed - if (revoked.some((result) => result.status === 'fulfilled' && !result.value.revoked )) - throw new Error(`[did-provider-cheqd]: revocation: Bulk revocation failed: already revoked credentials in revocation bundle: raw log: ${JSON.stringify(revoked.map((result) => ({ revoked: result.status === 'fulfilled' ? result.value.revoked : false })))}`) - - // set in-memory status list ref - const bitstring = await statusList.encode() as Bitstring - - // cast top-level args - const topArgs = options?.topArgs as ICheqdRevokeCredentialWithStatusList2021Args - - // write status list 2021 to file, if provided - if (topArgs?.writeToFile) { - await Cheqd.writeFile(fromString(bitstring, 'base64url'), options?.statusListFile) - } - - // publish status list 2021, if provided - const published = topArgs?.publish - ? (await async function () { - // fetch status list 2021 metadata - const statusListMetadata = await Cheqd.fetchStatusList2021Metadata(credentials[0]) - - // publish status list 2021 as new version - const scoped = topArgs.publishEncrypted - ? (await async function () { - // validate encoding, if provided - if (options?.publishOptions?.statusListEncoding && !Object.values(DefaultStatusList2021Encodings).includes(options?.publishOptions?.statusListEncoding)) { - throw new Error('[did-provider-cheqd]: revocation: Invalid status list encoding') - } - - // validate validUntil, if provided - if (options?.publishOptions?.statusListValidUntil) { - // validate validUntil as string - if (typeof options?.publishOptions?.statusListValidUntil !== 'string') throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be string)') - - // validate validUntil as date - if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be date)') - - // validate validUntil as future date - if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be future date)') - - // validate validUntil towards validFrom - if (new Date(options?.publishOptions?.statusListValidUntil) <= new Date(publishedList.StatusList2021.validFrom)) throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be after validFrom)') - } - - // validate paymentConditions, if provided - if (topArgs?.paymentConditions) { - if (!topArgs?.paymentConditions?.every((condition) => condition.feePaymentAddress && condition.feePaymentAmount && condition.intervalInSeconds)) { - throw new Error('[did-provider-cheqd]: paymentConditions must contain feePaymentAddress and feeAmount and intervalInSeconds') - } - - if (!topArgs?.paymentConditions?.every((condition) => typeof condition.feePaymentAddress === 'string' && typeof condition.feePaymentAmount === 'string' && typeof condition.intervalInSeconds === 'number')) { - throw new Error('[did-provider-cheqd]: feePaymentAddress and feePaymentAmount must be string and intervalInSeconds must be number') - } - - if (!topArgs?.paymentConditions?.every((condition) => condition.type === AccessControlConditionTypes.timelockPayment)) { - throw new Error('[did-provider-cheqd]: paymentConditions must be of type timelockPayment') - } - } - - // validate dkgOptions - if (!topArgs?.dkgOptions || !topArgs?.dkgOptions?.chain || !topArgs?.dkgOptions?.network) { - throw new Error('[did-provider-cheqd]: dkgOptions is required') - } - - // instantiate dkg-threshold client, in which case lit-protocol is used - const lit = await LitProtocol.create({ - chain: topArgs?.dkgOptions?.chain, - litNetwork: topArgs?.dkgOptions?.network - }) - - // construct access control conditions and payment conditions tuple - const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted - ? (await (async function () { - // define payment conditions, give precedence to top-level args - const paymentConditions = topArgs?.paymentConditions || publishedList.metadata.paymentConditions! - - // return access control conditions and payment conditions tuple - return [ - await Promise.all(paymentConditions.map(async (condition) => { - switch (condition.type) { - case AccessControlConditionTypes.timelockPayment: - return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({ - key: '$.tx_responses.*.timestamp', - comparator: '<=', - value: `${condition.intervalInSeconds}`, - }, - condition.feePaymentAmount, - condition.feePaymentAddress, - condition?.blockHeight, - topArgs?.dkgOptions?.chain - ) - default: - throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`) - } - })), - paymentConditions - ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]] - }())) - : (await (async function () { - // validate paymentConditions - if (!topArgs?.paymentConditions) { - throw new Error('[did-provider-cheqd]: paymentConditions is required') - } - - // return access control conditions and payment conditions tuple - return [ - await Promise.all(topArgs.paymentConditions.map(async (condition) => { - switch (condition.type) { - case AccessControlConditionTypes.timelockPayment: - return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({ - key: '$.tx_responses.*.timestamp', - comparator: '<=', - value: `${condition.intervalInSeconds}`, - }, - condition.feePaymentAmount, - condition.feePaymentAddress, - condition?.blockHeight - ) - default: - throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`) - } - })), - topArgs.paymentConditions - ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]] - }())) - - // encrypt bitstring - const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt(bitstring, unifiedAccessControlConditionsTuple[0], true) - - // define status list content - const content = { - StatusList2021: { - statusPurpose: publishedList.StatusList2021.statusPurpose, - encodedList: await blobToHexString(encryptedString), - validFrom: publishedList.StatusList2021.validFrom, - validUntil: options?.publishOptions?.statusListValidUntil || publishedList.StatusList2021.validUntil - }, - metadata: { - type: publishedList.metadata.type, - encrypted: true, - encoding: (options?.publishOptions?.statusListEncoding as DefaultStatusList2021Encoding | undefined) || publishedList.metadata.encoding, - encryptedSymmetricKey, - paymentConditions: unifiedAccessControlConditionsTuple[1] - } - } satisfies StatusList2021Revocation - - // return tuple of publish result and encryption relevant metadata - return [ - await Cheqd.publishStatusList2021(fromString(JSON.stringify(content), 'utf-8'), statusListMetadata, options?.publishOptions), - { encryptedString, encryptedSymmetricKey, symmetricKey: toString(symmetricKey!, 'hex') } - ] - }()) - : (await async function () { - // validate encoding, if provided - if (options?.publishOptions?.statusListEncoding && !Object.values(DefaultStatusList2021Encodings).includes(options?.publishOptions?.statusListEncoding)) { - throw new Error('[did-provider-cheqd]: revocation: Invalid status list encoding') - } - - // validate validUntil, if provided - if (options?.publishOptions?.statusListValidUntil) { - // validate validUntil as string - if (typeof options?.publishOptions?.statusListValidUntil !== 'string') throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be string)') - - // validate validUntil as date - if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be date)') - - // validate validUntil as future date - if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be future date)') - - // validate validUntil towards validFrom - if (new Date(options?.publishOptions?.statusListValidUntil) <= new Date(publishedList.StatusList2021.validFrom)) throw new Error('[did-provider-cheqd]: revocation: Invalid status list validUntil (must be after validFrom)') - } - - // define status list content - const content = { - StatusList2021: { - statusPurpose: publishedList.StatusList2021.statusPurpose, - encodedList: publishedList.metadata.encoding === 'base64url' ? bitstring : toString(fromString(bitstring, 'base64url'), options!.publishOptions.statusListEncoding as DefaultStatusList2021Encoding), - validFrom: publishedList.StatusList2021.validFrom, - validUntil: options?.publishOptions?.statusListValidUntil || publishedList.StatusList2021.validUntil - }, - metadata: { - type: publishedList.metadata.type, - encoding: (options?.publishOptions?.statusListEncoding as DefaultStatusList2021Encoding | undefined) || publishedList.metadata.encoding, - encrypted: false, - } - } satisfies StatusList2021Revocation - - // return tuple of publish result and encryption relevant metadata - return [ - await Cheqd.publishStatusList2021(fromString(JSON.stringify(content), 'utf-8'), statusListMetadata, options?.publishOptions), - undefined - ] - }()) - - // early exit, if publish failed - if (!scoped[0]) throw new Error('[did-provider-cheqd]: revocation: Failed to publish status list 2021') - - // return publish result - return scoped - }()) - : undefined - - return { - revoked: revoked.map((result) => result.status === 'fulfilled' ? result.value.revoked : false), - published: topArgs?.publish ? true : undefined, - statusList: topArgs?.returnUpdatedStatusList ? await Cheqd.fetchStatusList2021(credentials[0]) as StatusList2021Revocation : undefined, - symmetricKey: topArgs?.returnSymmetricKey ? (published?.[1] as { symmetricKey: string })?.symmetricKey : undefined, - resourceMetadata: topArgs?.returnStatusListMetadata ? await Cheqd.fetchStatusList2021Metadata(credentials[0]) : undefined - } satisfies BulkRevocationResult - } catch (error) { - // silent fail + early exit - console.error(error) - - return { revoked: [], error: error as IError } satisfies BulkRevocationResult - } - } - - static async suspendCredential(credential: VerifiableCredential, options?: ICheqdStatusList2021Options): Promise { - try { - // validate status purpose - if (credential?.credentialStatus?.statusPurpose !== 'suspension') throw new Error('[did-provider-cheqd]: suspension: Invalid status purpose') - - // fetch status list 2021 - const publishedList = (await Cheqd.fetchStatusList2021(credential)) as StatusList2021Suspension - - // early return, if encrypted and no decryption key provided - if (publishedList.metadata.encrypted && !options?.topArgs?.symmetricKey) throw new Error('[did-provider-cheqd]: suspension: symmetricKey is required, if status list 2021 is encrypted') - - // fetch status list 2021 inscribed in credential - const statusList2021 = options?.topArgs?.fetchList - ? (await async function () { - // if not encrypted, return bitstring - if (!publishedList.metadata.encrypted) - return publishedList.metadata.encoding === 'base64url' - ? publishedList.StatusList2021.encodedList - : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding as DefaultStatusList2021Encoding), 'base64url') - - // otherwise, decrypt and return raw bitstring - const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex')) - - // decrypt - return await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex')) - }()) - : (await async function () { - // transcode to base64url, if needed - const publishedListTranscoded = publishedList.metadata.encoding === 'base64url' - ? publishedList.StatusList2021.encodedList - : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding as DefaultStatusList2021Encoding), 'base64url') - - // if status list 2021 is not fetched, read from file - if (options?.statusListFile) { - // if not encrypted, return bitstring - if (!publishedList.metadata.encrypted) { - // construct encoded status list - const encoded = new StatusList({ buffer: await Cheqd.getFile(options.statusListFile) }).encode() as Bitstring - - // validate against published list - if (encoded !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: suspension: statusListFile does not match published status list 2021') - - // return encoded - return encoded - } - - // otherwise, decrypt and return bitstring - const scopedRawBlob = await toBlob(await Cheqd.getFile(options.statusListFile)) - - // decrypt - const decrypted = await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex')) - - // validate against published list - if (decrypted !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: suspension: statusListFile does not match published status list 2021') - - // return decrypted - return decrypted - } - - if (!options?.statusListInlineBitstring) throw new Error('[did-provider-cheqd]: suspension: statusListInlineBitstring is required, if statusListFile is not provided') - - // validate against published list - if (options?.statusListInlineBitstring !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: suspension: statusListInlineBitstring does not match published status list 2021') - - // otherwise, read from inline bitstring - return options?.statusListInlineBitstring - }()) - - // parse status list 2021 - const statusList = await StatusList.decode({ encodedList: statusList2021 }) - - // early exit, if already suspended - if (statusList.getStatus(Number(credential.credentialStatus.statusListIndex))) return { suspended: true } satisfies SuspensionResult - - // update suspension status - statusList.setStatus(Number(credential.credentialStatus.statusListIndex), true) - - // set in-memory status list ref - const bitstring = await statusList.encode() as Bitstring - - // cast top-level args - const topArgs = options?.topArgs as ICheqdSuspendCredentialWithStatusList2021Args - - // write status list 2021 to file, if provided - if (topArgs?.writeToFile) { - await Cheqd.writeFile(fromString(bitstring, 'base64url'), options?.statusListFile) - } - - // publish status list 2021, if provided - const published = topArgs?.publish - ? (await async function () { - // fetch status list 2021 metadata - const statusListMetadata = await Cheqd.fetchStatusList2021Metadata(credential) - - // publish status list 2021 as new version - const scoped = topArgs.publishEncrypted - ? (await async function () { - // validate encoding, if provided - if (options?.publishOptions?.statusListEncoding && !Object.values(DefaultStatusList2021Encodings).includes(options?.publishOptions?.statusListEncoding)) { - throw new Error('[did-provider-cheqd]: suspension: Invalid status list encoding') - } - - // validate validUntil, if provided - if (options?.publishOptions?.statusListValidUntil) { - // validate validUntil as string - if (typeof options?.publishOptions?.statusListValidUntil !== 'string') throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be string)') - - // validate validUntil as date - if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be date)') - - // validate validUntil as future date - if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be future date)') - - // validate validUntil towards validFrom - if (new Date(options?.publishOptions?.statusListValidUntil) <= new Date(publishedList.StatusList2021.validFrom)) throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be after validFrom)') - } - - // validate paymentConditions, if provided - if (topArgs?.paymentConditions) { - if (!topArgs?.paymentConditions?.every((condition) => condition.feePaymentAddress && condition.feePaymentAmount && condition.intervalInSeconds)) { - throw new Error('[did-provider-cheqd]: paymentConditions must contain feePaymentAddress and feeAmount and intervalInSeconds') - } - - if (!topArgs?.paymentConditions?.every((condition) => typeof condition.feePaymentAddress === 'string' && typeof condition.feePaymentAmount === 'string' && typeof condition.intervalInSeconds === 'number')) { - throw new Error('[did-provider-cheqd]: feePaymentAddress and feePaymentAmount must be string and intervalInSeconds must be number') - } - - if (!topArgs?.paymentConditions?.every((condition) => condition.type === AccessControlConditionTypes.timelockPayment)) { - throw new Error('[did-provider-cheqd]: paymentConditions must be of type timelockPayment') - } - } - - // validate dkgOptions - if (!topArgs?.dkgOptions || !topArgs?.dkgOptions?.chain || !topArgs?.dkgOptions?.network) { - throw new Error('[did-provider-cheqd]: dkgOptions is required') - } - - // instantiate dkg-threshold client, in which case lit-protocol is used - const lit = await LitProtocol.create({ - chain: topArgs?.dkgOptions?.chain, - litNetwork: topArgs?.dkgOptions?.network - }) - - // construct access control conditions and payment conditions tuple - const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted - ? (await (async function () { - // define payment conditions, give precedence to top-level args - const paymentConditions = topArgs?.paymentConditions || publishedList.metadata.paymentConditions! - - // return access control conditions and payment conditions tuple - return [ - await Promise.all(paymentConditions.map(async (condition) => { - switch (condition.type) { - case AccessControlConditionTypes.timelockPayment: - return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({ - key: '$.tx_responses.*.timestamp', - comparator: '<=', - value: `${condition.intervalInSeconds}`, - }, - condition.feePaymentAmount, - condition.feePaymentAddress, - condition?.blockHeight, - topArgs?.dkgOptions?.chain - ) - default: - throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`) - } - })), - paymentConditions - ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]] - }())) - : (await (async function () { - // validate paymentConditions - if (!topArgs?.paymentConditions) { - throw new Error('[did-provider-cheqd]: paymentConditions is required') - } - - // return access control conditions and payment conditions tuple - return [ - await Promise.all(topArgs.paymentConditions.map(async (condition) => { - switch (condition.type) { - case AccessControlConditionTypes.timelockPayment: - return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({ - key: '$.tx_responses.*.timestamp', - comparator: '<=', - value: `${condition.intervalInSeconds}`, - }, - condition.feePaymentAmount, - condition.feePaymentAddress, - condition?.blockHeight - ) - default: - throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`) - } - })), - topArgs.paymentConditions - ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]] - }())) - - // encrypt bitstring - const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt(bitstring, unifiedAccessControlConditionsTuple[0], true) - - // define status list content - const content = { - StatusList2021: { - statusPurpose: publishedList.StatusList2021.statusPurpose, - encodedList: await blobToHexString(encryptedString), - validFrom: publishedList.StatusList2021.validFrom, - validUntil: options?.publishOptions?.statusListValidUntil || publishedList.StatusList2021.validUntil - }, - metadata: { - type: publishedList.metadata.type, - encrypted: true, - encoding: (options?.publishOptions?.statusListEncoding as DefaultStatusList2021Encoding | undefined) || publishedList.metadata.encoding, - encryptedSymmetricKey, - paymentConditions: unifiedAccessControlConditionsTuple[1] - } - } satisfies StatusList2021Suspension - - // return tuple of publish result and encryption relevant metadata - return [ - await Cheqd.publishStatusList2021(fromString(JSON.stringify(content), 'utf-8'), statusListMetadata, options?.publishOptions), - { encryptedString, encryptedSymmetricKey, symmetricKey: toString(symmetricKey!, 'hex') } - ] - }()) - : (await async function () { - // validate encoding, if provided - if (options?.publishOptions?.statusListEncoding && !Object.values(DefaultStatusList2021Encodings).includes(options?.publishOptions?.statusListEncoding)) { - throw new Error('[did-provider-cheqd]: suspension: Invalid status list encoding') - } - - // validate validUntil, if provided - if (options?.publishOptions?.statusListValidUntil) { - // validate validUntil as string - if (typeof options?.publishOptions?.statusListValidUntil !== 'string') throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be string)') - - // validate validUntil as date - if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be date)') - - // validate validUntil as future date - if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be future date)') - - // validate validUntil towards validFrom - if (new Date(options?.publishOptions?.statusListValidUntil) <= new Date(publishedList.StatusList2021.validFrom)) throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be after validFrom)') - } - - // define status list content - const content = { - StatusList2021: { - statusPurpose: publishedList.StatusList2021.statusPurpose, - encodedList: publishedList.metadata.encoding === 'base64url' ? bitstring : toString(fromString(bitstring, 'base64url'), options!.publishOptions.statusListEncoding as DefaultStatusList2021Encoding), - validFrom: publishedList.StatusList2021.validFrom, - validUntil: options?.publishOptions?.statusListValidUntil || publishedList.StatusList2021.validUntil - }, - metadata: { - type: publishedList.metadata.type, - encoding: (options?.publishOptions?.statusListEncoding as DefaultStatusList2021Encoding | undefined) || publishedList.metadata.encoding, - encrypted: false, - } - } satisfies StatusList2021Suspension - - // return tuple of publish result and encryption relevant metadata - return [ - await Cheqd.publishStatusList2021(fromString(JSON.stringify(content), 'utf-8'), statusListMetadata, options?.publishOptions), - undefined - ] - }()) - - // early exit, if publish failed - if (!scoped[0]) throw new Error('[did-provider-cheqd]: suspension: Failed to publish status list 2021') - - // return publish result - return scoped - }()) - : undefined - - return { - suspended: true, - published: topArgs?.publish ? true : undefined, - statusList: topArgs?.returnUpdatedStatusList ? await Cheqd.fetchStatusList2021(credential) as StatusList2021Suspension : undefined, - symmetricKey: topArgs?.returnSymmetricKey ? (published?.[1] as { symmetricKey: string })?.symmetricKey : undefined, - resourceMetadata: topArgs?.returnStatusListMetadata ? await Cheqd.fetchStatusList2021Metadata(credential) : undefined - } satisfies SuspensionResult - } catch (error) { - // silent fail + early exit - console.error(error) - - return { suspended: false, error: error as IError } satisfies SuspensionResult - } - } - - static async suspendCredentials(credentials: VerifiableCredential[], options?: ICheqdStatusList2021Options): Promise { - // validate credentials - case: empty - if (!credentials.length || credentials.length === 0) throw new Error('[did-provider-cheqd]: suspension: No credentials provided') - - // validate credentials - case: consistent issuer - if (credentials.map((credential) => { - return ((credential.issuer as { id: string }).id) - ? (credential.issuer as { id: string }).id - : credential.issuer as string - }).filter((value, _, self) => value && value !== self[0]).length > 0) throw new Error('[did-provider-cheqd]: suspension: Credentials must be issued by the same issuer') - - // validate credentials - case: status list index - if (credentials.map((credential) => credential.credentialStatus!.statusListIndex).filter((value, index, self) => self.indexOf(value) !== index).length > 0) throw new Error('[did-provider-cheqd]: suspension: Credentials must have unique status list index') - - // validate credentials - case: status purpose - if (!credentials.every((credential) => credential.credentialStatus?.statusPurpose === 'suspension')) throw new Error('[did-provider-cheqd]: suspension: Invalid status purpose') - - // validate credentials - case: status list id - const remote = credentials[0].credentialStatus?.id - ? (credentials[0].credentialStatus as { id: string }).id.split('#')[0] - : (function(){ - throw new Error('[did-provider-cheqd]: suspension: Invalid status list id') - }()) - - // validate credentials - case: status list id format - if (!RemoteListPattern.test(remote)) throw new Error('[did-provider-cheqd]: suspension: Invalid status list id format: expected: https://../1.0/identifiers/:>?resourceName=&resourceType=') - - if (!credentials.every((credential) => { - return (credential.credentialStatus as { id: string }).id.split('#')[0] === remote - })) throw new Error('[did-provider-cheqd]: suspension: Credentials must belong to the same status list') - - // validate credentials - case: status list type - if (!credentials.every((credential) => credential.credentialStatus?.type === 'StatusList2021Entry')) throw new Error('[did-provider-cheqd]: suspension: Invalid status list type') - - try { - // fetch status list 2021 - const publishedList = (await Cheqd.fetchStatusList2021(credentials[0])) as StatusList2021Suspension - - // early return, if encrypted and no decryption key provided - if (publishedList.metadata.encrypted && !options?.topArgs?.symmetricKey) throw new Error('[did-provider-cheqd]: suspension: symmetricKey is required, if status list 2021 is encrypted') - - // fetch status list 2021 inscribed in credential - const statusList2021 = options?.topArgs?.fetchList - ? (await async function () { - // if not encrypted, return bitstring - if (!publishedList.metadata.encrypted) - return publishedList.metadata.encoding === 'base64url' - ? publishedList.StatusList2021.encodedList - : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding as DefaultStatusList2021Encoding), 'base64url') - - // otherwise, decrypt and return raw bitstring - const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex')) - - // decrypt - return await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex')) - }()) - : (await async function () { - // transcode to base64url, if needed - const publishedListTranscoded = publishedList.metadata.encoding === 'base64url' - ? publishedList.StatusList2021.encodedList - : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding as DefaultStatusList2021Encoding), 'base64url') - - // if status list 2021 is not fetched, read from file - if (options?.statusListFile) { - // if not encrypted, return bitstring - if (!publishedList.metadata.encrypted) { - // construct encoded status list - const encoded = new StatusList({ buffer: await Cheqd.getFile(options.statusListFile) }).encode() as Bitstring - - // validate against published list - if (encoded !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: suspension: statusListFile does not match published status list 2021') - - // return encoded - return encoded - } - - // otherwise, decrypt and return bitstring - const scopedRawBlob = await toBlob(await Cheqd.getFile(options.statusListFile)) - - // decrypt - const decrypted = await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex')) - - // validate against published list - if (decrypted !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: suspension: statusListFile does not match published status list 2021') - - // return decrypted - return decrypted - } - - if (!options?.statusListInlineBitstring) throw new Error('[did-provider-cheqd]: suspension: statusListInlineBitstring is required, if statusListFile is not provided') - - // validate against published list - if (options?.statusListInlineBitstring !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: suspension: statusListInlineBitstring does not match published status list 2021') - - // otherwise, read from inline bitstring - return options?.statusListInlineBitstring - }()) - - // parse status list 2021 - const statusList = await StatusList.decode({ encodedList: statusList2021 }) - - // initiate bulk suspension - const suspended = await Promise.allSettled(credentials.map((credential) => { - return async function () { - // early return, if no credential status - if (!credential.credentialStatus) return { suspended: false } - - // early exit, if credential is already suspended - if (statusList.getStatus(Number(credential.credentialStatus.statusListIndex))) return { suspended: false } - - // update suspension status - statusList.setStatus(Number(credential.credentialStatus.statusListIndex), true) - - // return suspension status - return { suspended: true } - }() - })) satisfies PromiseSettledResult[] - - // revert bulk ops, if some failed - if (suspended.some((result) => result.status === 'fulfilled' && !result.value.suspended )) - throw new Error(`[did-provider-cheqd]: suspension: Bulk suspension failed: already suspended credentials in suspension bundle: raw log: ${JSON.stringify(suspended.map((result) => ({ suspended: result.status === 'fulfilled' ? result.value.suspended : false })))}`) - - // set in-memory status list ref - const bitstring = await statusList.encode() as Bitstring - - // cast top-level args - const topArgs = options?.topArgs as ICheqdRevokeCredentialWithStatusList2021Args - - // write status list 2021 to file, if provided - if (topArgs?.writeToFile) { - await Cheqd.writeFile(fromString(bitstring, 'base64url'), options?.statusListFile) - } - - // publish status list 2021, if provided - const published = topArgs?.publish - ? (await async function () { - // fetch status list 2021 metadata - const statusListMetadata = await Cheqd.fetchStatusList2021Metadata(credentials[0]) - - // publish status list 2021 as new version - const scoped = topArgs.publishEncrypted - ? (await async function () { - // validate encoding, if provided - if (options?.publishOptions?.statusListEncoding && !Object.values(DefaultStatusList2021Encodings).includes(options?.publishOptions?.statusListEncoding)) { - throw new Error('[did-provider-cheqd]: suspension: Invalid status list encoding') - } - - // validate validUntil, if provided - if (options?.publishOptions?.statusListValidUntil) { - // validate validUntil as string - if (typeof options?.publishOptions?.statusListValidUntil !== 'string') throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be string)') - - // validate validUntil as date - if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be date)') - - // validate validUntil as future date - if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be future date)') - - // validate validUntil towards validFrom - if (new Date(options?.publishOptions?.statusListValidUntil) <= new Date(publishedList.StatusList2021.validFrom)) throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be after validFrom)') - } - - // validate paymentConditions, if provided - if (topArgs?.paymentConditions) { - if (!topArgs?.paymentConditions?.every((condition) => condition.feePaymentAddress && condition.feePaymentAmount && condition.intervalInSeconds)) { - throw new Error('[did-provider-cheqd]: paymentConditions must contain feePaymentAddress and feeAmount and intervalInSeconds') - } - - if (!topArgs?.paymentConditions?.every((condition) => typeof condition.feePaymentAddress === 'string' && typeof condition.feePaymentAmount === 'string' && typeof condition.intervalInSeconds === 'number')) { - throw new Error('[did-provider-cheqd]: feePaymentAddress and feePaymentAmount must be string and intervalInSeconds must be number') - } - - if (!topArgs?.paymentConditions?.every((condition) => condition.type === AccessControlConditionTypes.timelockPayment)) { - throw new Error('[did-provider-cheqd]: paymentConditions must be of type timelockPayment') - } - } - - // validate dkgOptions - if (!topArgs?.dkgOptions || !topArgs?.dkgOptions?.chain || !topArgs?.dkgOptions?.network) { - throw new Error('[did-provider-cheqd]: dkgOptions is required') - } - - // instantiate dkg-threshold client, in which case lit-protocol is used - const lit = await LitProtocol.create({ - chain: topArgs?.dkgOptions?.chain, - litNetwork: topArgs?.dkgOptions?.network - }) - - // construct access control conditions and payment conditions tuple - const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted - ? (await (async function () { - // define payment conditions, give precedence to top-level args - const paymentConditions = topArgs?.paymentConditions || publishedList.metadata.paymentConditions! - - // return access control conditions and payment conditions tuple - return [ - await Promise.all(paymentConditions.map(async (condition) => { - switch (condition.type) { - case AccessControlConditionTypes.timelockPayment: - return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({ - key: '$.tx_responses.*.timestamp', - comparator: '<=', - value: `${condition.intervalInSeconds}`, - }, - condition.feePaymentAmount, - condition.feePaymentAddress, - condition?.blockHeight, - topArgs?.dkgOptions?.chain - ) - default: - throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`) - } - })), - paymentConditions - ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]] - }())) - : (await (async function () { - // validate paymentConditions - if (!topArgs?.paymentConditions) { - throw new Error('[did-provider-cheqd]: paymentConditions is required') - } - - // return access control conditions and payment conditions tuple - return [ - await Promise.all(topArgs.paymentConditions.map(async (condition) => { - switch (condition.type) { - case AccessControlConditionTypes.timelockPayment: - return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({ - key: '$.tx_responses.*.timestamp', - comparator: '<=', - value: `${condition.intervalInSeconds}`, - }, - condition.feePaymentAmount, - condition.feePaymentAddress, - condition?.blockHeight - ) - default: - throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`) - } - })), - topArgs.paymentConditions - ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]] - }())) - - // encrypt bitstring - const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt(bitstring, unifiedAccessControlConditionsTuple[0], true) - - // define status list content - const content = { - StatusList2021: { - statusPurpose: publishedList.StatusList2021.statusPurpose, - encodedList: await blobToHexString(encryptedString), - validFrom: publishedList.StatusList2021.validFrom, - validUntil: options?.publishOptions?.statusListValidUntil || publishedList.StatusList2021.validUntil - }, - metadata: { - type: publishedList.metadata.type, - encrypted: true, - encoding: (options?.publishOptions?.statusListEncoding as DefaultStatusList2021Encoding | undefined) || publishedList.metadata.encoding, - encryptedSymmetricKey, - paymentConditions: unifiedAccessControlConditionsTuple[1] - } - } satisfies StatusList2021Suspension - - // return tuple of publish result and encryption relevant metadata - return [ - await Cheqd.publishStatusList2021(fromString(JSON.stringify(content), 'utf-8'), statusListMetadata, options?.publishOptions), - { encryptedString, encryptedSymmetricKey, symmetricKey: toString(symmetricKey!, 'hex') } - ] - }()) - : (await async function () { - // validate encoding, if provided - if (options?.publishOptions?.statusListEncoding && !Object.values(DefaultStatusList2021Encodings).includes(options?.publishOptions?.statusListEncoding)) { - throw new Error('[did-provider-cheqd]: suspension: Invalid status list encoding') - } - - // validate validUntil, if provided - if (options?.publishOptions?.statusListValidUntil) { - // validate validUntil as string - if (typeof options?.publishOptions?.statusListValidUntil !== 'string') throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be string)') - - // validate validUntil as date - if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be date)') - - // validate validUntil as future date - if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be future date)') - - // validate validUntil towards validFrom - if (new Date(options?.publishOptions?.statusListValidUntil) <= new Date(publishedList.StatusList2021.validFrom)) throw new Error('[did-provider-cheqd]: suspension: Invalid status list validUntil (must be after validFrom)') - } - - // define status list content - const content = { - StatusList2021: { - statusPurpose: publishedList.StatusList2021.statusPurpose, - encodedList: publishedList.metadata.encoding === 'base64url' ? bitstring : toString(fromString(bitstring, 'base64url'), options!.publishOptions.statusListEncoding as DefaultStatusList2021Encoding), - validFrom: publishedList.StatusList2021.validFrom, - validUntil: options?.publishOptions?.statusListValidUntil || publishedList.StatusList2021.validUntil - }, - metadata: { - type: publishedList.metadata.type, - encoding: (options?.publishOptions?.statusListEncoding as DefaultStatusList2021Encoding | undefined) || publishedList.metadata.encoding, - encrypted: false, - } - } satisfies StatusList2021Suspension - - // return tuple of publish result and encryption relevant metadata - return [ - await Cheqd.publishStatusList2021(fromString(JSON.stringify(content), 'utf-8'), statusListMetadata, options?.publishOptions), - undefined - ] - }()) - - // early exit, if publish failed - if (!scoped[0]) throw new Error('[did-provider-cheqd]: suspension: Failed to publish status list 2021') - - // return publish result - return scoped - }()) - : undefined - - return { - suspended: suspended.map((result) => result.status === 'fulfilled' ? result.value.suspended : false), - published: topArgs?.publish ? true : undefined, - statusList: topArgs?.returnUpdatedStatusList ? await Cheqd.fetchStatusList2021(credentials[0]) as StatusList2021Suspension : undefined, - symmetricKey: topArgs?.returnSymmetricKey ? (published?.[1] as { symmetricKey: string })?.symmetricKey : undefined, - resourceMetadata: topArgs?.returnStatusListMetadata ? await Cheqd.fetchStatusList2021Metadata(credentials[0]) : undefined - } satisfies BulkSuspensionResult - } catch (error) { - // silent fail + early exit - console.error(error) - return { suspended: [], error: error as IError } satisfies BulkSuspensionResult - } - } - - static async unsuspendCredential(credential: VerifiableCredential, options?: ICheqdStatusList2021Options): Promise { - try { - // validate status purpose - if (credential?.credentialStatus?.statusPurpose !== 'suspension') throw new Error('[did-provider-cheqd]: unsuspension: Invalid status purpose') - - // fetch status list 2021 - const publishedList = (await Cheqd.fetchStatusList2021(credential)) as StatusList2021Suspension - - // early return, if encrypted and no decryption key provided - if (publishedList.metadata.encrypted && !options?.topArgs?.symmetricKey) throw new Error('[did-provider-cheqd]: unsuspension: symmetricKey is required, if status list 2021 is encrypted') - - // fetch status list 2021 inscribed in credential - const statusList2021 = options?.topArgs?.fetchList - ? (await async function () { - // if not encrypted, return bitstring - if (!publishedList.metadata.encrypted) - return publishedList.metadata.encoding === 'base64url' - ? publishedList.StatusList2021.encodedList - : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding as DefaultStatusList2021Encoding), 'base64url') - - // otherwise, decrypt and return raw bitstring - const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex')) - - // decrypt - return await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex')) - }()) - : (await async function () { - // transcode to base64url, if needed - const publishedListTranscoded = publishedList.metadata.encoding === 'base64url' - ? publishedList.StatusList2021.encodedList - : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding as DefaultStatusList2021Encoding), 'base64url') - - // if status list 2021 is not fetched, read from file - if (options?.statusListFile) { - // if not encrypted, return bitstring - if (!publishedList.metadata.encrypted) { - // construct encoded status list - const encoded = new StatusList({ buffer: await Cheqd.getFile(options.statusListFile) }).encode() as Bitstring - - // validate against published list - if (encoded !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: unsuspension: statusListFile does not match published status list 2021') - - // return encoded - return encoded - } - - // otherwise, decrypt and return bitstring - const scopedRawBlob = await toBlob(await Cheqd.getFile(options.statusListFile)) - - // decrypt - const decrypted = await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex')) - - // validate against published list - if (decrypted !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: unsuspension: statusListFile does not match published status list 2021') - - // return decrypted - return decrypted - } - - if (!options?.statusListInlineBitstring) throw new Error('[did-provider-cheqd]: unsuspension: statusListInlineBitstring is required, if statusListFile is not provided') - - // validate against published list - if (options?.statusListInlineBitstring !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: unsuspension: statusListInlineBitstring does not match published status list 2021') - - // otherwise, read from inline bitstring - return options?.statusListInlineBitstring - }()) - - // parse status list 2021 - const statusList = await StatusList.decode({ encodedList: statusList2021 }) - - // early exit, if already unsuspended - if (!statusList.getStatus(Number(credential.credentialStatus.statusListIndex))) return { unsuspended: true } satisfies UnsuspensionResult - - // update suspension status - statusList.setStatus(Number(credential.credentialStatus.statusListIndex), false) - - // set in-memory status list ref - const bitstring = await statusList.encode() as Bitstring - - // cast top-level args - const topArgs = options?.topArgs as ICheqdSuspendCredentialWithStatusList2021Args - - // write status list 2021 to file, if provided - if (topArgs?.writeToFile) { - await Cheqd.writeFile(fromString(bitstring, 'base64url'), options?.statusListFile) - } - - // publish status list 2021, if provided - const published = topArgs?.publish - ? (await async function () { - // fetch status list 2021 metadata - const statusListMetadata = await Cheqd.fetchStatusList2021Metadata(credential) - - // publish status list 2021 as new version - const scoped = topArgs.publishEncrypted - ? (await async function () { - // validate encoding, if provided - if (options?.publishOptions?.statusListEncoding && !Object.values(DefaultStatusList2021Encodings).includes(options?.publishOptions?.statusListEncoding)) { - throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list encoding') - } - - // validate validUntil, if provided - if (options?.publishOptions?.statusListValidUntil) { - // validate validUntil as string - if (typeof options?.publishOptions?.statusListValidUntil !== 'string') throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be string)') - - // validate validUntil as date - if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be date)') - - // validate validUntil as future date - if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be future date)') - - // validate validUntil towards validFrom - if (new Date(options?.publishOptions?.statusListValidUntil) <= new Date(publishedList.StatusList2021.validFrom)) throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be after validFrom)') - } - - // validate paymentConditions, if provided - if (topArgs?.paymentConditions) { - if (!topArgs?.paymentConditions?.every((condition) => condition.feePaymentAddress && condition.feePaymentAmount && condition.intervalInSeconds)) { - throw new Error('[did-provider-cheqd]: paymentConditions must contain feePaymentAddress and feeAmount and intervalInSeconds') - } - - if (!topArgs?.paymentConditions?.every((condition) => typeof condition.feePaymentAddress === 'string' && typeof condition.feePaymentAmount === 'string' && typeof condition.intervalInSeconds === 'number')) { - throw new Error('[did-provider-cheqd]: feePaymentAddress and feePaymentAmount must be string and intervalInSeconds must be number') - } - - if (!topArgs?.paymentConditions?.every((condition) => condition.type === AccessControlConditionTypes.timelockPayment)) { - throw new Error('[did-provider-cheqd]: paymentConditions must be of type timelockPayment') - } - } - - // validate dkgOptions - if (!topArgs?.dkgOptions || !topArgs?.dkgOptions?.chain || !topArgs?.dkgOptions?.network) { - throw new Error('[did-provider-cheqd]: dkgOptions is required') - } - - // instantiate dkg-threshold client, in which case lit-protocol is used - const lit = await LitProtocol.create({ - chain: topArgs?.dkgOptions?.chain, - litNetwork: topArgs?.dkgOptions?.network - }) - - // construct access control conditions and payment conditions tuple - const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted - ? (await (async function () { - // define payment conditions, give precedence to top-level args - const paymentConditions = topArgs?.paymentConditions || publishedList.metadata.paymentConditions! - - // return access control conditions and payment conditions tuple - return [ - await Promise.all(paymentConditions.map(async (condition) => { - switch (condition.type) { - case AccessControlConditionTypes.timelockPayment: - return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({ - key: '$.tx_responses.*.timestamp', - comparator: '<=', - value: `${condition.intervalInSeconds}`, - }, - condition.feePaymentAmount, - condition.feePaymentAddress, - condition?.blockHeight, - topArgs?.dkgOptions?.chain - ) - default: - throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`) - } - })), - paymentConditions - ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]] - }())) - : (await (async function () { - // validate paymentConditions - if (!topArgs?.paymentConditions) { - throw new Error('[did-provider-cheqd]: paymentConditions is required') - } - - // return access control conditions and payment conditions tuple - return [ - await Promise.all(topArgs.paymentConditions.map(async (condition) => { - switch (condition.type) { - case AccessControlConditionTypes.timelockPayment: - return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({ - key: '$.tx_responses.*.timestamp', - comparator: '<=', - value: `${condition.intervalInSeconds}`, - }, - condition.feePaymentAmount, - condition.feePaymentAddress, - condition?.blockHeight - ) - default: - throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`) - } - })), - topArgs.paymentConditions - ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]] - }())) - - // encrypt bitstring - const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt(bitstring, unifiedAccessControlConditionsTuple[0], true) - - // define status list content - const content = { - StatusList2021: { - statusPurpose: publishedList.StatusList2021.statusPurpose, - encodedList: await blobToHexString(encryptedString), - validFrom: publishedList.StatusList2021.validFrom, - validUntil: options?.publishOptions?.statusListValidUntil || publishedList.StatusList2021.validUntil - }, - metadata: { - type: publishedList.metadata.type, - encrypted: true, - encoding: (options?.publishOptions?.statusListEncoding as DefaultStatusList2021Encoding | undefined) || publishedList.metadata.encoding, - encryptedSymmetricKey, - paymentConditions: unifiedAccessControlConditionsTuple[1] - } - } satisfies StatusList2021Suspension - - // return tuple of publish result and encryption relevant metadata - return [ - await Cheqd.publishStatusList2021(fromString(JSON.stringify(content), 'utf-8'), statusListMetadata, options?.publishOptions), - { encryptedString, encryptedSymmetricKey, symmetricKey: toString(symmetricKey!, 'hex') } - ] - }()) - : (await async function () { - // validate encoding, if provided - if (options?.publishOptions?.statusListEncoding && !Object.values(DefaultStatusList2021Encodings).includes(options?.publishOptions?.statusListEncoding)) { - throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list encoding') - } - - // validate validUntil, if provided - if (options?.publishOptions?.statusListValidUntil) { - // validate validUntil as string - if (typeof options?.publishOptions?.statusListValidUntil !== 'string') throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be string)') - - // validate validUntil as date - if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be date)') - - // validate validUntil as future date - if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be future date)') - - // validate validUntil towards validFrom - if (new Date(options?.publishOptions?.statusListValidUntil) <= new Date(publishedList.StatusList2021.validFrom)) throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be after validFrom)') - } - - // define status list content - const content = { - StatusList2021: { - statusPurpose: publishedList.StatusList2021.statusPurpose, - encodedList: publishedList.metadata.encoding === 'base64url' ? bitstring : toString(fromString(bitstring, 'base64url'), options!.publishOptions.statusListEncoding as DefaultStatusList2021Encoding), - validFrom: publishedList.StatusList2021.validFrom, - validUntil: options?.publishOptions?.statusListValidUntil || publishedList.StatusList2021.validUntil - }, - metadata: { - type: publishedList.metadata.type, - encoding: (options?.publishOptions?.statusListEncoding as DefaultStatusList2021Encoding | undefined) || publishedList.metadata.encoding, - encrypted: false, - } - } satisfies StatusList2021Suspension - - // return tuple of publish result and encryption relevant metadata - return [ - await Cheqd.publishStatusList2021(fromString(JSON.stringify(content), 'utf-8'), statusListMetadata, options?.publishOptions), - undefined - ] - }()) - - // early exit, if publish failed - if (!scoped[0]) throw new Error('[did-provider-cheqd]: unsuspension: Failed to publish status list 2021') - - // return publish result - return scoped - }()) - : undefined - - return { - unsuspended: true, - published: topArgs?.publish ? true : undefined, - statusList: topArgs?.returnUpdatedStatusList ? await Cheqd.fetchStatusList2021(credential) as StatusList2021Suspension : undefined, - symmetricKey: topArgs?.returnSymmetricKey ? (published?.[1] as { symmetricKey: string })?.symmetricKey : undefined, - resourceMetadata: topArgs?.returnStatusListMetadata ? await Cheqd.fetchStatusList2021Metadata(credential) : undefined - } satisfies UnsuspensionResult - } catch (error) { - // silent fail + early exit - console.error(error) - - return { unsuspended: false, error: error as IError } satisfies UnsuspensionResult - } - } - - static async unsuspendCredentials(credentials: VerifiableCredential[], options?: ICheqdStatusList2021Options): Promise { - // validate credentials - case: empty - if (!credentials.length || credentials.length === 0) throw new Error('[did-provider-cheqd]: unsuspension: No credentials provided') - - // validate credentials - case: consistent issuer - if (credentials.map((credential) => { - return ((credential.issuer as { id: string }).id) - ? (credential.issuer as { id: string }).id - : credential.issuer as string - }).filter((value, _, self) => value && value !== self[0]).length > 0) throw new Error('[did-provider-cheqd]: unsuspension: Credentials must be issued by the same issuer') - - // validate credentials - case: status list index - if (credentials.map((credential) => credential.credentialStatus!.statusListIndex).filter((value, index, self) => self.indexOf(value) !== index).length > 0) throw new Error('[did-provider-cheqd]: unsuspension: Credentials must have unique status list index') - - // validate credentials - case: status purpose - if (!credentials.every((credential) => credential.credentialStatus?.statusPurpose === 'suspension')) throw new Error('[did-provider-cheqd]: unsuspension: Invalid status purpose') - - // validate credentials - case: status list id - const remote = credentials[0].credentialStatus?.id - ? (credentials[0].credentialStatus as { id: string }).id.split('#')[0] - : (function(){ - throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list id') - }()) - - // validate credentials - case: status list id format - if (!RemoteListPattern.test(remote)) throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list id format: expected: https://../1.0/identifiers/:>?resourceName=&resourceType=') - - if (!credentials.every((credential) => { - return (credential.credentialStatus as { id: string }).id.split('#')[0] === remote - })) throw new Error('[did-provider-cheqd]: unsuspension: Credentials must belong to the same status list') - - // validate credentials - case: status list type - if (!credentials.every((credential) => credential.credentialStatus?.type === 'StatusList2021Entry')) throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list type') - - try { - // fetch status list 2021 - const publishedList = (await Cheqd.fetchStatusList2021(credentials[0])) as StatusList2021Suspension - - // early return, if encrypted and no decryption key provided - if (publishedList.metadata.encrypted && !options?.topArgs?.symmetricKey) throw new Error('[did-provider-cheqd]: unsuspension: symmetricKey is required, if status list 2021 is encrypted') - - // fetch status list 2021 inscribed in credential - const statusList2021 = options?.topArgs?.fetchList - ? (await async function () { - // if not encrypted, return bitstring - if (!publishedList.metadata.encrypted) - return publishedList.metadata.encoding === 'base64url' - ? publishedList.StatusList2021.encodedList - : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding as DefaultStatusList2021Encoding), 'base64url') - - // otherwise, decrypt and return raw bitstring - const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex')) - - // decrypt - return await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex')) - }()) - : (await async function () { - // transcode to base64url, if needed - const publishedListTranscoded = publishedList.metadata.encoding === 'base64url' - ? publishedList.StatusList2021.encodedList - : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding as DefaultStatusList2021Encoding), 'base64url') - - // if status list 2021 is not fetched, read from file - if (options?.statusListFile) { - // if not encrypted, return bitstring - if (!publishedList.metadata.encrypted) { - // construct encoded status list - const encoded = new StatusList({ buffer: await Cheqd.getFile(options.statusListFile) }).encode() as Bitstring - - // validate against published list - if (encoded !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: unsuspension: statusListFile does not match published status list 2021') - - // return encoded - return encoded - } - - // otherwise, decrypt and return bitstring - const scopedRawBlob = await toBlob(await Cheqd.getFile(options.statusListFile)) - - // decrypt - const decrypted = await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex')) - - // validate against published list - if (decrypted !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: unsuspension: statusListFile does not match published status list 2021') - - // return decrypted - return decrypted - } - - if (!options?.statusListInlineBitstring) throw new Error('[did-provider-cheqd]: unsuspension: statusListInlineBitstring is required, if statusListFile is not provided') - - // validate against published list - if (options?.statusListInlineBitstring !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: unsuspension: statusListInlineBitstring does not match published status list 2021') - - // otherwise, read from inline bitstring - return options?.statusListInlineBitstring - }()) - - // parse status list 2021 - const statusList = await StatusList.decode({ encodedList: statusList2021 }) - - // initiate bulk unsuspension - const unsuspended = await Promise.allSettled(credentials.map((credential) => { - return async function () { - // early return, if no credential status - if (!credential.credentialStatus) return { unsuspended: false } - - // early exit, if credential is already unsuspended - if (!statusList.getStatus(Number(credential.credentialStatus.statusListIndex))) return { unsuspended: true } - - // update unsuspension status - statusList.setStatus(Number(credential.credentialStatus.statusListIndex), false) - - // return unsuspension status - return { unsuspended: true } - }() - })) satisfies PromiseSettledResult[] - - // revert bulk ops, if some failed - if (unsuspended.some((result) => result.status === 'fulfilled' && !result.value.unsuspended )) - throw new Error(`[did-provider-cheqd]: unsuspension: Bulk unsuspension failed: already unsuspended credentials in unsuspension bundle: raw log: ${JSON.stringify(unsuspended.map((result) => ({ unsuspended: result.status === 'fulfilled' ? result.value.unsuspended : false })))}`) - - // set in-memory status list ref - const bitstring = await statusList.encode() as Bitstring - - // cast top-level args - const topArgs = options?.topArgs as ICheqdRevokeCredentialWithStatusList2021Args - - // write status list 2021 to file, if provided - if (topArgs?.writeToFile) { - await Cheqd.writeFile(fromString(bitstring, 'base64url'), options?.statusListFile) - } - - // publish status list 2021, if provided - const published = topArgs?.publish - ? (await async function () { - // fetch status list 2021 metadata - const statusListMetadata = await Cheqd.fetchStatusList2021Metadata(credentials[0]) - - // publish status list 2021 as new version - const scoped = topArgs.publishEncrypted - ? (await async function () { - // validate encoding, if provided - if (options?.publishOptions?.statusListEncoding && !Object.values(DefaultStatusList2021Encodings).includes(options?.publishOptions?.statusListEncoding)) { - throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list encoding') - } - - // validate validUntil, if provided - if (options?.publishOptions?.statusListValidUntil) { - // validate validUntil as string - if (typeof options?.publishOptions?.statusListValidUntil !== 'string') throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be string)') - - // validate validUntil as date - if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be date)') - - // validate validUntil as future date - if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be future date)') - - // validate validUntil towards validFrom - if (new Date(options?.publishOptions?.statusListValidUntil) <= new Date(publishedList.StatusList2021.validFrom)) throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be after validFrom)') - } - - // validate paymentConditions, if provided - if (topArgs?.paymentConditions) { - if (!topArgs?.paymentConditions?.every((condition) => condition.feePaymentAddress && condition.feePaymentAmount && condition.intervalInSeconds)) { - throw new Error('[did-provider-cheqd]: paymentConditions must contain feePaymentAddress and feeAmount and intervalInSeconds') - } - - if (!topArgs?.paymentConditions?.every((condition) => typeof condition.feePaymentAddress === 'string' && typeof condition.feePaymentAmount === 'string' && typeof condition.intervalInSeconds === 'number')) { - throw new Error('[did-provider-cheqd]: feePaymentAddress and feePaymentAmount must be string and intervalInSeconds must be number') - } - - if (!topArgs?.paymentConditions?.every((condition) => condition.type === AccessControlConditionTypes.timelockPayment)) { - throw new Error('[did-provider-cheqd]: paymentConditions must be of type timelockPayment') - } - } - - // validate dkgOptions - if (!topArgs?.dkgOptions || !topArgs?.dkgOptions?.chain || !topArgs?.dkgOptions?.network) { - throw new Error('[did-provider-cheqd]: dkgOptions is required') - } - - // instantiate dkg-threshold client, in which case lit-protocol is used - const lit = await LitProtocol.create({ - chain: topArgs?.dkgOptions?.chain, - litNetwork: topArgs?.dkgOptions?.network - }) - - // construct access control conditions and payment conditions tuple - const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted - ? (await (async function () { - // define payment conditions, give precedence to top-level args - const paymentConditions = topArgs?.paymentConditions || publishedList.metadata.paymentConditions! - - // return access control conditions and payment conditions tuple - return [ - await Promise.all(paymentConditions.map(async (condition) => { - switch (condition.type) { - case AccessControlConditionTypes.timelockPayment: - return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({ - key: '$.tx_responses.*.timestamp', - comparator: '<=', - value: `${condition.intervalInSeconds}`, - }, - condition.feePaymentAmount, - condition.feePaymentAddress, - condition?.blockHeight, - topArgs?.dkgOptions?.chain - ) - default: - throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`) - } - })), - paymentConditions - ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]] - }())) - : (await (async function () { - // validate paymentConditions - if (!topArgs?.paymentConditions) { - throw new Error('[did-provider-cheqd]: paymentConditions is required') - } - - // return access control conditions and payment conditions tuple - return [ - await Promise.all(topArgs.paymentConditions.map(async (condition) => { - switch (condition.type) { - case AccessControlConditionTypes.timelockPayment: - return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({ - key: '$.tx_responses.*.timestamp', - comparator: '<=', - value: `${condition.intervalInSeconds}`, - }, - condition.feePaymentAmount, - condition.feePaymentAddress, - condition?.blockHeight - ) - default: - throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`) - } - })), - topArgs.paymentConditions - ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]] - }())) - - // encrypt bitstring - const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt(bitstring, unifiedAccessControlConditionsTuple[0], true) - - // define status list content - const content = { - StatusList2021: { - statusPurpose: publishedList.StatusList2021.statusPurpose, - encodedList: await blobToHexString(encryptedString), - validFrom: publishedList.StatusList2021.validFrom, - validUntil: options?.publishOptions?.statusListValidUntil || publishedList.StatusList2021.validUntil - }, - metadata: { - type: publishedList.metadata.type, - encrypted: true, - encoding: (options?.publishOptions?.statusListEncoding as DefaultStatusList2021Encoding | undefined) || publishedList.metadata.encoding, - encryptedSymmetricKey, - paymentConditions: unifiedAccessControlConditionsTuple[1] - } - } satisfies StatusList2021Suspension - - // return tuple of publish result and encryption relevant metadata - return [ - await Cheqd.publishStatusList2021(fromString(JSON.stringify(content), 'utf-8'), statusListMetadata, options?.publishOptions), - { encryptedString, encryptedSymmetricKey, symmetricKey: toString(symmetricKey!, 'hex') } - ] - }()) - : (await async function () { - // validate encoding, if provided - if (options?.publishOptions?.statusListEncoding && !Object.values(DefaultStatusList2021Encodings).includes(options?.publishOptions?.statusListEncoding)) { - throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list encoding') - } - - // validate validUntil, if provided - if (options?.publishOptions?.statusListValidUntil) { - // validate validUntil as string - if (typeof options?.publishOptions?.statusListValidUntil !== 'string') throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be string)') - - // validate validUntil as date - if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be date)') - - // validate validUntil as future date - if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be future date)') - - // validate validUntil towards validFrom - if (new Date(options?.publishOptions?.statusListValidUntil) <= new Date(publishedList.StatusList2021.validFrom)) throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be after validFrom)') - } - - // define status list content - const content = { - StatusList2021: { - statusPurpose: publishedList.StatusList2021.statusPurpose, - encodedList: publishedList.metadata.encoding === 'base64url' ? bitstring : toString(fromString(bitstring, 'base64url'), options!.publishOptions.statusListEncoding as DefaultStatusList2021Encoding), - validFrom: publishedList.StatusList2021.validFrom, - validUntil: options?.publishOptions?.statusListValidUntil || publishedList.StatusList2021.validUntil - }, - metadata: { - type: publishedList.metadata.type, - encoding: (options?.publishOptions?.statusListEncoding as DefaultStatusList2021Encoding | undefined) || publishedList.metadata.encoding, - encrypted: false, - } - } satisfies StatusList2021Suspension - - // return tuple of publish result and encryption relevant metadata - return [ - await Cheqd.publishStatusList2021(fromString(JSON.stringify(content), 'utf-8'), statusListMetadata, options?.publishOptions), - undefined - ] - }()) - - // early exit, if publish failed - if (!scoped[0]) throw new Error('[did-provider-cheqd]: unsuspension: Failed to publish status list 2021') - - // return publish result - return scoped - }()) - : undefined - - return { - unsuspended: unsuspended.map((result) => result.status === 'fulfilled' ? result.value.unsuspended : false), - published: topArgs?.publish ? true : undefined, - statusList: topArgs?.returnUpdatedStatusList ? await Cheqd.fetchStatusList2021(credentials[0]) as StatusList2021Suspension : undefined, - symmetricKey: topArgs?.returnSymmetricKey ? (published?.[1] as { symmetricKey: string })?.symmetricKey : undefined, - resourceMetadata: topArgs?.returnStatusListMetadata ? await Cheqd.fetchStatusList2021Metadata(credentials[0]) : undefined - } satisfies BulkUnsuspensionResult - } catch (error) { - // silent fail + early exit - console.error(error) - - return { unsuspended: [], error: error as IError } satisfies BulkUnsuspensionResult - } - } - - static async checkRevoked(credential: VerifiableCredential, options: ICheqdStatusList2021Options = { fetchList: true }): Promise { - // validate status purpose - if (credential.credentialStatus?.statusPurpose !== 'revocation') { - throw new Error(`[did-provider-cheqd]: check: revocation: Unsupported status purpose: ${credential.credentialStatus?.statusPurpose}`) - } - - // fetch status list 2021 - const publishedList = (await Cheqd.fetchStatusList2021(credential)) as StatusList2021Revocation - - // fetch status list 2021 inscribed in credential - const statusList2021 = options?.topArgs?.fetchList - ? (await async function () { - // if not encrypted, return bitstring - if (!publishedList.metadata.encrypted) - return publishedList.metadata.encoding === 'base64url' - ? publishedList.StatusList2021.encodedList - : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding as DefaultStatusList2021Encoding), 'base64url') - - // otherwise, decrypt and return raw bitstring - const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex')) - - // instantiate dkg-threshold client, in which case lit-protocol is used - const lit = await LitProtocol.create({ - chain: options?.topArgs?.dkgOptions?.chain, - litNetwork: options?.topArgs?.dkgOptions?.network - }) - - // construct access control conditions - const unifiedAccessControlConditions = await Promise.all(publishedList.metadata.paymentConditions!.map(async (condition) => { - switch (condition.type) { - case AccessControlConditionTypes.timelockPayment: - return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({ - key: '$.tx_responses.*.timestamp', - comparator: '<=', - value: `${condition.intervalInSeconds}`, - }, - condition.feePaymentAmount, - condition.feePaymentAddress, - condition?.blockHeight, - options?.topArgs?.dkgOptions?.chain - ) - default: - throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`) - } - })) - - // decrypt - return await lit.decrypt(scopedRawBlob, publishedList.metadata.encryptedSymmetricKey!, unifiedAccessControlConditions) - }()) - : (await async function () { - // transcode to base64url, if needed - const publishedListTranscoded = publishedList.metadata.encoding === 'base64url' - ? publishedList.StatusList2021.encodedList - : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding as DefaultStatusList2021Encoding), 'base64url') - - // if status list 2021 is not fetched, read from file - if (options?.statusListFile) { - // if not encrypted, return bitstring - if (!publishedList.metadata.encrypted) { - // construct encoded status list - const encoded = new StatusList({ buffer: await Cheqd.getFile(options.statusListFile) }).encode() as Bitstring - - // validate against published list - if (encoded !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: check: revocation: statusListFile does not match published status list 2021') - - // return encoded - return encoded - } - - // otherwise, decrypt and return bitstring - const scopedRawBlob = await toBlob(await Cheqd.getFile(options.statusListFile)) - - // decrypt - const decrypted = await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex')) - - // validate against published list - if (decrypted !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: check: revocation: statusListFile does not match published status list 2021') - - // return decrypted - return decrypted - } - - if (!options?.statusListInlineBitstring) throw new Error('[did-provider-cheqd]: check: revocation: statusListInlineBitstring is required, if statusListFile is not provided') - - // validate against published list - if (options?.statusListInlineBitstring !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: check: revocation: statusListInlineBitstring does not match published status list 2021') - - // otherwise, read from inline bitstring - return options?.statusListInlineBitstring - }()) - - // transcode, if needed - const transcodedStatusList2021 = publishedList.metadata.encoding === 'base64url' - ? statusList2021 - : toString(fromString(statusList2021, publishedList.metadata.encoding as DefaultStatusList2021Encoding), 'base64url') - - // parse status list 2021 - const statusList = await StatusList.decode({ encodedList: transcodedStatusList2021 }) - - // get status by index - return !!statusList.getStatus(Number(credential.credentialStatus.statusListIndex)) - } - - static async checkSuspended(credential: VerifiableCredential, options: ICheqdStatusList2021Options = { fetchList: true }): Promise { - // validate status purpose - if (credential.credentialStatus?.statusPurpose !== 'suspension') { - throw new Error(`[did-provider-cheqd]: check: suspension: Unsupported status purpose: ${credential.credentialStatus?.statusPurpose}`) - } - - // fetch status list 2021 - const publishedList = (await Cheqd.fetchStatusList2021(credential)) as StatusList2021Suspension - - // fetch status list 2021 inscribed in credential - const statusList2021 = options?.topArgs?.fetchList - ? (await async function () { - // if not encrypted, return bitstring - if (!publishedList.metadata.encrypted) - return publishedList.metadata.encoding === 'base64url' - ? publishedList.StatusList2021.encodedList - : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding as DefaultStatusList2021Encoding), 'base64url') - - // otherwise, decrypt and return bitstring - const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex')) - - // instantiate dkg-threshold client, in which case lit-protocol is used - const lit = await LitProtocol.create({ - chain: options?.topArgs?.dkgOptions?.chain, - litNetwork: options?.topArgs?.dkgOptions?.network - }) - - // construct access control conditions - const unifiedAccessControlConditions = await Promise.all(publishedList.metadata.paymentConditions!.map(async (condition) => { - switch (condition.type) { - case AccessControlConditionTypes.timelockPayment: - return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock({ - key: '$.tx_responses.*.timestamp', - comparator: '<=', - value: `${condition.intervalInSeconds}`, - }, - condition.feePaymentAmount, - condition.feePaymentAddress, - condition?.blockHeight, - options?.topArgs?.dkgOptions?.chain - ) - default: - throw new Error(`[did-provider-cheqd]: unsupported access control condition type ${condition.type}`) - } - })) - - // decrypt - return await lit.decrypt(scopedRawBlob, publishedList.metadata.encryptedSymmetricKey!, unifiedAccessControlConditions) - }()) - : (await async function () { - // transcode to base64url, if needed - const publishedListTranscoded = publishedList.metadata.encoding === 'base64url' - ? publishedList.StatusList2021.encodedList - : toString(fromString(publishedList.StatusList2021.encodedList, publishedList.metadata.encoding as DefaultStatusList2021Encoding), 'base64url') - - // if status list 2021 is not fetched, read from file - if (options?.statusListFile) { - // if not encrypted, return bitstring - if (!publishedList.metadata.encrypted) { - // construct encoded status list - const encoded = new StatusList({ buffer: await Cheqd.getFile(options.statusListFile) }).encode() as Bitstring - - // validate against published list - if (encoded !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: check: suspension: statusListFile does not match published status list 2021') - - // return encoded - return encoded - } - - // otherwise, decrypt and return bitstring - const scopedRawBlob = await toBlob(await Cheqd.getFile(options.statusListFile)) - - // decrypt - const decrypted = await LitProtocol.decryptDirect(scopedRawBlob, fromString(options?.topArgs?.symmetricKey, 'hex')) - - // validate against published list - if (decrypted !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: check: suspension: statusListFile does not match published status list 2021') - - // return decrypted - return decrypted - } - - if (!options?.statusListInlineBitstring) throw new Error('[did-provider-cheqd]: check: suspension: statusListInlineBitstring is required, if statusListFile is not provided') - - // validate against published list - if (options?.statusListInlineBitstring !== publishedListTranscoded) throw new Error('[did-provider-cheqd]: check: suspension: statusListInlineBitstring does not match published status list 2021') - - // otherwise, read from inline bitstring - return options?.statusListInlineBitstring - }()) - - // parse status list 2021 - const statusList = await StatusList.decode({ encodedList: statusList2021 }) - - // get status by index - return !!statusList.getStatus(Number(credential.credentialStatus.statusListIndex)) - } - - static async publishStatusList2021(statusList2021Raw: Uint8Array, statusList2021Metadata: LinkedResourceMetadataResolutionResult, options: { context: IContext, resourceId?: string, resourceVersion?: string, resourceAlsoKnownAs?: AlternativeUri[], signInputs?: ISignInputs[], fee?: DidStdFee }): Promise { - // construct status list 2021 payload from previous version + new version - const payload = { - collectionId: statusList2021Metadata.resourceCollectionId, - id: options?.resourceId || v4(), - name: statusList2021Metadata.resourceName, - version: options?.resourceVersion || new Date().toISOString(), - alsoKnownAs: options?.resourceAlsoKnownAs || [], - resourceType: statusList2021Metadata.resourceType as DefaultStatusList2021ResourceType, - data: statusList2021Raw - } satisfies StatusList2021ResourcePayload - - return await options.context.agent[BroadcastStatusList2021MethodName]({ - kms: (await options.context.agent.keyManagerGetKeyManagementSystems())[0], - payload, - network: statusList2021Metadata.resourceURI.split(':')[2] as CheqdNetwork, - signInputs: options?.signInputs, - fee: options?.fee - }) - } - - static async fetchStatusList2021(credential: VerifiableCredential, returnRaw = false): Promise { - // validate credential status - if (!credential.credentialStatus) throw new Error('[did-provider-cheqd]: fetch status list: Credential status is not present') - - // validate credential status type - if (credential.credentialStatus.type !== 'StatusList2021Entry') throw new Error('[did-provider-cheqd]: fetch status list: Credential status type is not valid') - - // validate credential status list status purpose - if (credential.credentialStatus.statusPurpose !== 'revocation' && credential.credentialStatus.statusPurpose !== 'suspension') throw new Error('[did-provider-cheqd]: fetch status list: Credential status purpose is not valid') - - // fetch status list 2021 - const content = await (await fetch(credential.credentialStatus.id.split('#')[0])).json() as StatusList2021Revocation | StatusList2021Suspension - - if (!(content.StatusList2021 && content.metadata && content.StatusList2021.encodedList && content.StatusList2021.statusPurpose && content.metadata.encoding)) { - throw new Error(`'[did-provider-cheqd]: fetch status list: Status List resource content is not valid'`) - } - - // return raw if requested - if (returnRaw) { - return fromString(content.StatusList2021.encodedList, content.metadata.encoding as DefaultStatusList2021Encoding) - } - - // otherwise, return content - return content - } - - static async fetchStatusList2021Metadata(credential: VerifiableCredential): Promise { - // get base url - const baseUrl = new URL(credential.credentialStatus!.id.split('#')[0]) - - // get resource name - const resourceName = baseUrl.searchParams.get('resourceName') - - // get resource type - const resourceType = baseUrl.searchParams.get('resourceType') - - // unset resource name - baseUrl.searchParams.delete('resourceName') - - // unset resource type - baseUrl.searchParams.delete('resourceType') - - // construct metadata url - const metadataUrl = `${baseUrl.toString()}/metadata` - - // fetch collection metadata - const collectionMetadata = await (await fetch(metadataUrl)).json() as DIDMetadataDereferencingResult - - // early exit if no linked resources - if (!collectionMetadata?.contentStream?.linkedResourceMetadata) throw new Error('[did-provider-cheqd]: fetch status list metadata: No linked resources found') - - // find relevant resources by resource name - const resourceVersioning = collectionMetadata.contentStream.linkedResourceMetadata.filter((resource) => resource.resourceName === resourceName && resource.resourceType === resourceType) - - // early exit if no relevant resources - if (!resourceVersioning.length || resourceVersioning.length === 0) throw new Error(`[did-provider-cheqd]: fetch status list metadata: No relevant resources found by resource name ${resourceName}`) - - // get latest resource version by nextVersionId null pointer, or by latest created date as fallback - return resourceVersioning.find((resource) => !resource.nextVersionId) || resourceVersioning.sort((a, b) => new Date(b.created).getTime() - new Date(a.created).getTime())[0] - } - - static async loadProvider(didUrl: string, providers: CheqdDIDProvider[]): Promise { - const provider = providers.find((provider) => didUrl.includes(`${DidPrefix}:${CheqdDidMethod}:${provider.network}`)) - if (!provider) { - throw new Error(`[did-provider-cheqd]: Provider namespace not found`) - } - return provider - } - - static generateProviderId(namespace: string): string { - return `${DidPrefix}:${CheqdDidMethod}:${namespace}` - } - - static async getFile(filename: string): Promise { - if (typeof filename !== 'string') { - throw new Error('[did-provider-cheqd]: filename is required') - } - - if (!fs.existsSync(filename)) { - debug(`[did-provider-cheqd]: File ${filename} not found`) - throw new Error(`[did-provider-cheqd]: File ${filename} not found`) - } - - return new Promise((resolve, reject) => { - const content = fs.readFileSync(filename) - if (!content) { - reject(new Error(`[did-provider-cheqd]: File ${filename} is empty`)) - } - resolve(new Uint8Array(content)) - }) - } - - static async writeFile(content: Uint8Array, filename?: string): Promise { - if (!filename) { - filename = `statusList2021-${v4()}` - } - - // alert if file exists - if (fs.existsSync(filename)) { - debug(`[did-provider-cheqd]: File ${filename} already exists`) - console.warn(`[did-provider-cheqd]: File ${filename} already exists. Overwriting...`) - } - - return new Promise((resolve, reject) => { - fs.writeFile(filename!, content, (err) => { - if (err) { - reject(new Error(`[did-provider-cheqd]: Error writing file ${filename}: reason: ${err}`)) - } - resolve() - }) - }) - } - - static async decodeCredentialJWT(jwt: string): Promise { - const decodedCredential = decodeJWT(jwt) - - // validate credential payload - if (!decodedCredential.payload) throw new Error('[did-provider-cheqd]: decode jwt: decodedCredential.payload is required') - - // validate credential payload vc property as VerifiableCredential - if (!decodedCredential.payload.vc) throw new Error('[did-provider-cheqd]: decode jwt: decodedCredential.payload.vc is required') - - return { - ...decodedCredential.payload.vc, - issuer: decodedCredential.payload.iss, - } satisfies VerifiableCredential - } + readonly methods?: ICheqd; + readonly schema?: IAgentPluginSchema = { + components: { + schemas: {}, + methods: { + cheqdCreateIdentifier: { + description: 'Create a new identifier', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: 'A cheqdCreateIdentifierArgs object as any for extensibility', + }, + }, + required: ['args'], + }, + returnType: { + type: 'object', + }, + }, + cheqdUpdateIdentifier: { + description: 'Update an identifier', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: 'A cheqdUpdateIdentifierArgs object as any for extensibility', + }, + }, + required: ['args'], + }, + returnType: { + type: 'object', + }, + }, + cheqdDeactivateIdentifier: { + description: 'Deactivate an identifier', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: 'A cheqdDeactivateIdentifierArgs object as any for extensibility', + }, + }, + required: ['args'], + }, + returnType: { + type: 'object', + }, + }, + cheqdCreateLinkedResource: { + description: 'Create a new resource', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: 'A cheqdCreateLinkedResource object as any for extensibility', + }, + }, + required: ['args'], + }, + returnType: { + type: 'boolean', + }, + }, + cheqdCreateStatusList2021: { + description: 'Create a new Status List 2021', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: 'A cheqdCreateStatusList2021Args object as any for extensibility', + }, + }, + required: ['args'], + }, + returnType: { + type: 'object', + }, + }, + cheqdBroadcastStatusList2021: { + description: 'Broadcast a Status List 2021 to cheqd ledger', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: 'A cheqdBroadcastStatusList2021Args object as any for extensibility', + }, + }, + required: ['args'], + }, + returnType: { + type: 'object', + }, + }, + cheqdGenerateDidDoc: { + description: 'Generate a new DID document to use with `createIdentifier`', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: 'A cheqdGenerateDidDocArgs object as any for extensibility', + }, + }, + required: ['args'], + }, + returnType: { + type: 'object', + }, + }, + cheqdGenerateDidDocWithLinkedResource: { + description: 'Generate a new DID document to use with `createIdentifier` and / or `createResource`', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: + 'A cheqdGenerateDidDocWithLinkedResourceArgs object as any for extensibility', + }, + }, + required: ['args'], + }, + returnType: { + type: 'object', + }, + }, + cheqdGenerateIdentityKeys: { + description: 'Generate a new key pair in hex to use with `createIdentifier`', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: 'A cheqdGenerateIdentityKeysArgs object as any for extensibility', + }, + }, + }, + returnType: { + type: 'object', + }, + }, + cheqdGenerateVersionId: { + description: 'Generate a random uuid', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: 'A cheqdGenerateVersionIdArgs object as any for extensibility', + }, + }, + }, + returnType: { + type: 'object', + }, + }, + cheqdGenerateStatusList2021: { + description: 'Generate a new Status List 2021', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: 'A cheqdGenerateStatusList2021Args object as any for extensibility', + }, + }, + }, + returnType: { + type: 'string', + }, + }, + cheqdIssueRevocableCredentialWithStatusList2021: { + description: 'Issue a revocable credential with a Status List 2021 as credential status registry', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: + 'A cheqdIssueCredentialWithStatusList2021Args object as any for extensibility', + }, + }, + required: ['args'], + }, + returnType: { + type: 'object', + }, + }, + cheqdIssueSuspendableCredentialWithStatusList2021: { + description: 'Issue a suspendable credential with a Status List 2021 as credential status registry', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: + 'A cheqdIssueCredentialWithStatusList2021Args object as any for extensibility', + }, + }, + required: ['args'], + }, + returnType: { + type: 'object', + }, + }, + cheqdVerifyCredential: { + description: + 'Verify a credential, enhanced by revocation / suspension check with a Status List 2021 as credential status registry', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: + 'A cheqdVerifyCredentialWithStatusList2021Args object as any for extensibility', + }, + }, + required: ['args'], + }, + returnType: { + type: 'object', + }, + }, + cheqdVerifyPresentation: { + description: + 'Verify a presentation, enhanced by revocation / suspension check with a Status List 2021 as credential status registry', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: + 'A cheqdVerifyPresentationWithStatusList2021Args object as any for extensibility', + }, + }, + required: ['args'], + }, + returnType: { + type: 'object', + }, + }, + cheqdCheckCredentialStatus: { + description: + 'Check the revocation / suspension status of a credential with a Status List 2021 as credential status registry', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: + 'A cheqdCheckCredentialStatusWithStatusList2021Args object as any for extensibility', + }, + }, + required: ['args'], + }, + returnType: { + type: 'object', + }, + }, + cheqdRevokeCredential: { + description: 'Revoke a credential against a Status List 2021 as credential status registry', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: + 'A cheqdRevokeCredentialWithStatusList2021Args object as any for extensibility', + }, + }, + required: ['args'], + }, + returnType: { + type: 'object', + }, + }, + cheqdRevokeCredentials: { + description: 'Revoke multiple credentials against a Status List 2021 as credential status registry', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: + 'A cheqdRevokeBulkCredentialsWithStatusList2021Args object as any for extensibility', + }, + }, + required: ['args'], + }, + returnType: { + type: 'array', + }, + }, + cheqdSuspendCredential: { + description: 'Suspend a credential against a Status List 2021 as credential status registry', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: + 'A cheqdSuspendCredentialWithStatusList2021Args object as any for extensibility', + }, + }, + required: ['args'], + }, + returnType: { + type: 'object', + }, + }, + cheqdSuspendCredentials: { + description: + 'Suspend multiple credentials against a Status List 2021 as credential status registry', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: + 'A cheqdSuspendBulkCredentialsWithStatusList2021Args object as any for extensibility', + }, + }, + required: ['args'], + }, + returnType: { + type: 'array', + }, + }, + cheqdUnsuspendCredential: { + description: 'Unsuspend a credential against a Status List 2021 as credential status registry', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: + 'cheqdUnsuspendCredentialWithStatusList2021Args object as any for extensibility', + }, + }, + required: ['args'], + }, + returnType: { + type: 'object', + }, + }, + cheqdUnsuspendCredentials: { + description: + 'Unsuspend multiple credentials against a Status List 2021 as credential status registry', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: + 'A cheqdUnsuspendBulkCredentialsWithStatusList2021Args object as any for extensibility', + }, + }, + required: ['args'], + }, + returnType: { + type: 'array', + }, + }, + cheqdTransactSendTokens: { + description: 'Send tokens from one account to another', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: 'A cheqdTransactSendTokensArgs object as any for extensibility', + }, + }, + required: ['args'], + }, + returnType: { + type: 'object', + }, + }, + cheqdObservePaymentCondition: { + description: 'Observe payment conditions for a given set of payment conditions', + arguments: { + type: 'object', + properties: { + args: { + type: 'object', + description: 'cheqdObservePaymentConditionArgs object as any for extensibility', + }, + }, + required: ['args'], + }, + returnType: { + type: 'object', + }, + }, + }, + }, + }; + private readonly supportedDidProviders: CheqdDIDProvider[]; + private didProvider: CheqdDIDProvider; + private providerId: string; + static readonly defaultStatusList2021Length: number = 16 * 1024 * 8; // 16KB in bits or 131072 bits / entries + static readonly defaultContextV1 = 'https://www.w3.org/2018/credentials/v1'; + static readonly statusList2021Context = 'https://w3id.org/vc-status-list-2021/v1'; + + constructor(args: { providers: CheqdDIDProvider[] }) { + if (typeof args.providers !== 'object') { + throw new Error('[did-provider-cheqd]: at least one did provider is required'); + } + + this.supportedDidProviders = args.providers; + this.didProvider = args.providers[0]; + this.providerId = Cheqd.generateProviderId(this.didProvider.network); + + this.methods = { + [CreateIdentifierMethodName]: this.CreateIdentifier.bind(this), + [UpdateIdentifierMethodName]: this.UpdateIdentifier.bind(this), + [DeactivateIdentifierMethodName]: this.DeactivateIdentifier.bind(this), + [CreateResourceMethodName]: this.CreateResource.bind(this), + [CreateStatusList2021MethodName]: this.CreateStatusList2021.bind(this), + [BroadcastStatusList2021MethodName]: this.BroadcastStatusList2021.bind(this), + [GenerateDidDocMethodName]: this.GenerateDidDoc.bind(this), + [GenerateDidDocWithLinkedResourceMethodName]: this.GenerateDidDocWithLinkedResource.bind(this), + [GenerateKeyPairMethodName]: this.GenerateIdentityKeys.bind(this), + [GenerateVersionIdMethodName]: this.GenerateVersionId.bind(this), + [GenerateStatusList2021MethodName]: this.GenerateStatusList2021.bind(this), + [IssueRevocableCredentialWithStatusList2021MethodName]: + this.IssueRevocableCredentialWithStatusList2021.bind(this), + [IssueSuspendableCredentialWithStatusList2021MethodName]: + this.IssueSuspendableCredentialWithStatusList2021.bind(this), + [VerifyCredentialMethodName]: this.VerifyCredentialWithStatusList2021.bind(this), + [VerifyPresentationMethodName]: this.VerifyPresentationWithStatusList2021.bind(this), + [CheckCredentialStatusMethodName]: this.CheckCredentialStatusWithStatusList2021.bind(this), + [RevokeCredentialMethodName]: this.RevokeCredentialWithStatusList2021.bind(this), + [RevokeCredentialsMethodName]: this.RevokeBulkCredentialsWithStatusList2021.bind(this), + [SuspendCredentialMethodName]: this.SuspendCredentialWithStatusList2021.bind(this), + [SuspendCredentialsMethodName]: this.SuspendBulkCredentialsWithStatusList2021.bind(this), + [UnsuspendCredentialMethodName]: this.UnsuspendCredentialWithStatusList2021.bind(this), + [UnsuspendCredentialsMethodName]: this.UnsuspendBulkCredentialsWithStatusList2021.bind(this), + [TransactSendTokensMethodName]: this.TransactSendTokens.bind(this), + [ObservePaymentConditionMethodName]: this.ObservePaymentCondition.bind(this), + }; + } + + private async CreateIdentifier( + args: ICheqdCreateIdentifierArgs, + context: IContext + ): Promise> { + if (typeof args.kms !== 'string') { + throw new Error('[did-provider-cheqd]: kms is required'); + } + + if (typeof args.alias !== 'string') { + throw new Error('[did-provider-cheqd]: alias is required'); + } + + if (typeof args.document !== 'object') { + throw new Error('[did-provider-cheqd]: document object is required'); + } + + const provider = await Cheqd.loadProvider(args.document.id, this.supportedDidProviders); + + this.didProvider = provider; + this.providerId = Cheqd.generateProviderId(this.didProvider.network); + + return await context.agent.didManagerCreate({ + kms: args.kms, + alias: args.alias, + provider: this.providerId, + options: { + document: args.document, + keys: args.keys, + versionId: args?.versionId, + fee: args?.fee, + }, + }); + } + + private async UpdateIdentifier( + args: ICheqdUpdateIdentifierArgs, + context: IContext + ): Promise> { + if (typeof args.kms !== 'string') { + throw new Error('[did-provider-cheqd]: kms is required'); + } + + if (typeof args.document !== 'object') { + throw new Error('[did-provider-cheqd]: document object is required'); + } + + const provider = await Cheqd.loadProvider(args.document.id, this.supportedDidProviders); + + this.didProvider = provider; + this.providerId = Cheqd.generateProviderId(this.didProvider.network); + + return await context.agent.didManagerUpdate({ + did: args.document.id, + document: args.document, + options: { + kms: args.kms, + keys: args.keys, + versionId: args?.versionId, + fee: args?.fee, + }, + }); + } + + private async DeactivateIdentifier(args: ICheqdDeactivateIdentifierArgs, context: IContext) { + if (typeof args.kms !== 'string') { + throw new Error('[did-provider-cheqd]: kms is required'); + } + + if (typeof args.document !== 'object') { + throw new Error('[did-provider-cheqd]: document object is required'); + } + + const provider = await Cheqd.loadProvider(args.document.id, this.supportedDidProviders); + + this.didProvider = provider; + this.providerId = Cheqd.generateProviderId(this.didProvider.network); + + return await this.didProvider.deactivateIdentifier( + { + did: args.document.id, + document: args.document, + options: { + keys: args.keys, + fee: args?.fee, + }, + }, + context + ); + } + + private async CreateResource(args: ICheqdCreateLinkedResourceArgs, context: IContext) { + if (typeof args.kms !== 'string') { + throw new Error('[did-provider-cheqd]: kms is required'); + } + + if (typeof args.payload !== 'object') { + throw new Error('[did-provider-cheqd]: payload object is required'); + } + + if (typeof args.network !== 'string') { + throw new Error('[did-provider-cheqd]: network is required'); + } + + if (args?.file) { + args.payload.data = await Cheqd.getFile(args.file); + } + + if (typeof args?.payload?.data === 'string') { + args.payload.data = fromString(args.payload.data, 'base64'); + } + + this.providerId = Cheqd.generateProviderId(args.network); + this.didProvider = await Cheqd.loadProvider(this.providerId, this.supportedDidProviders); + + return await this.didProvider.createResource( + { + options: { + kms: args.kms, + payload: args.payload, + signInputs: args.signInputs, + fee: args?.fee, + }, + }, + context + ); + } + + private async CreateStatusList2021(args: ICheqdCreateStatusList2021Args, context: IContext) { + if (typeof args.kms !== 'string') { + throw new Error('[did-provider-cheqd]: kms is required'); + } + + if (typeof args.issuerDid !== 'string' || !args.issuerDid) { + throw new Error('[did-provider-cheqd]: issuerDid is required'); + } + + if (typeof args.statusListName !== 'string' || !args.statusListName) { + throw new Error('[did-provider-cheqd]: statusListName is required'); + } + + if (typeof args.statusPurpose !== 'string' || !args.statusPurpose) { + throw new Error('[did-provider-cheqd]: statusPurpose is required'); + } + + if (typeof args.encrypted === 'undefined') { + throw new Error('[did-provider-cheqd]: encrypted is required'); + } + + // validate statusPurpose + if (!Object.values(DefaultStatusList2021StatusPurposeTypes).includes(args.statusPurpose)) { + throw new Error( + `[did-provider-cheqd]: statusPurpose must be one of ${Object.values( + DefaultStatusList2021StatusPurposeTypes + ).join(', ')}` + ); + } + + // validate statusListLength + if (args?.statusListLength) { + if (typeof args.statusListLength !== 'number') { + throw new Error('[did-provider-cheqd]: statusListLength must be number'); + } + + if (args.statusListLength < Cheqd.defaultStatusList2021Length) { + throw new Error( + `[did-provider-cheqd]: statusListLength must be greater than or equal to ${Cheqd.defaultStatusList2021Length} number of entries` + ); + } + } + + // validate statusListEncoding + if (args?.statusListEncoding) { + if (typeof args.statusListEncoding !== 'string') { + throw new Error('[did-provider-cheqd]: statusListEncoding must be string'); + } + + if (!Object.values(DefaultStatusList2021Encodings).includes(args.statusListEncoding)) { + throw new Error( + `[did-provider-cheqd]: statusListEncoding must be one of ${Object.values( + DefaultStatusList2021Encodings + ).join(', ')}` + ); + } + } + + // validate validUntil + if (args?.validUntil) { + if (typeof args.validUntil !== 'string') { + throw new Error('[did-provider-cheqd]: validUntil must be string'); + } + + if (new Date() <= new Date(args.validUntil)) { + throw new Error('[did-provider-cheqd]: validUntil must be greater than current date'); + } + } + + // validate args in pairs - case: encrypted + if (args.encrypted) { + // validate paymentConditions + if ( + !args?.paymentConditions || + !args?.paymentConditions?.length || + !Array.isArray(args?.paymentConditions) || + args?.paymentConditions.length === 0 + ) { + throw new Error('[did-provider-cheqd]: paymentConditions is required'); + } + + if ( + !args?.paymentConditions?.every( + (condition) => + condition.feePaymentAddress && condition.feePaymentAmount && condition.intervalInSeconds + ) + ) { + throw new Error( + '[did-provider-cheqd]: paymentConditions must contain feePaymentAddress and feeAmount and intervalInSeconds' + ); + } + + if ( + !args?.paymentConditions?.every( + (condition) => + typeof condition.feePaymentAddress === 'string' && + typeof condition.feePaymentAmount === 'string' && + typeof condition.intervalInSeconds === 'number' + ) + ) { + throw new Error( + '[did-provider-cheqd]: feePaymentAddress and feePaymentAmount must be string and intervalInSeconds must be number' + ); + } + + if ( + !args?.paymentConditions?.every( + (condition) => condition.type === AccessControlConditionTypes.timelockPayment + ) + ) { + throw new Error('[did-provider-cheqd]: paymentConditions must be of type timelockPayment'); + } + } + + // get network + const network = args.issuerDid.split(':')[2]; + + // generate bitstring + const bitstring = await context.agent[GenerateStatusList2021MethodName]({ + length: args?.statusListLength || Cheqd.defaultStatusList2021Length, + bitstringEncoding: args?.statusListEncoding || DefaultStatusList2021Encodings.base64url, + }); + + // construct data and metadata tuple + const data = args.encrypted + ? await (async function (that: Cheqd) { + // instantiate dkg-threshold client, in which case lit-protocol is used + const lit = await LitProtocol.create({ + chain: args?.dkgOptions?.chain || that.didProvider.dkgOptions.chain, + litNetwork: args?.dkgOptions?.network || that.didProvider.dkgOptions.network, + }); + + // construct access control conditions + const unifiedAccessControlConditions = await Promise.all( + args.paymentConditions!.map(async (condition) => { + switch (condition.type) { + case AccessControlConditionTypes.timelockPayment: + return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock( + { + key: '$.tx_responses.*.timestamp', + comparator: '<=', + value: `${condition.intervalInSeconds}`, + }, + condition.feePaymentAmount, + condition.feePaymentAddress, + condition?.blockHeight, + args?.dkgOptions?.chain || that.didProvider.dkgOptions.chain + ); + default: + throw new Error( + `[did-provider-cheqd]: unsupported access control condition type ${condition.type}` + ); + } + }) + ); + + // encrypt bitstring + const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt( + bitstring, + unifiedAccessControlConditions, + true + ); + + // return result tuple + switch (args.statusPurpose) { + case DefaultStatusList2021StatusPurposeTypes.revocation: + return [ + { + StatusList2021: { + statusPurpose: args.statusPurpose, + encodedList: await blobToHexString(encryptedString), + validFrom: new Date().toISOString(), + validUntil: args?.validUntil, + }, + metadata: { + type: DefaultStatusList2021ResourceTypes.revocation, + encrypted: true, + encoding: args?.statusListEncoding || DefaultStatusList2021Encodings.base64url, + encryptedSymmetricKey, + paymentConditions: args.paymentConditions, + }, + } satisfies StatusList2021Revocation, + { + symmetricKey: toString(symmetricKey!, 'hex'), + encryptedSymmetricKey, + encryptedString: await blobToHexString(encryptedString), + }, + ] satisfies [ + StatusList2021Revocation, + { symmetricKey: string; encryptedSymmetricKey: string; encryptedString: string }, + ]; + case DefaultStatusList2021StatusPurposeTypes.suspension: + return [ + { + StatusList2021: { + statusPurpose: args.statusPurpose, + encodedList: await blobToHexString(encryptedString), + validFrom: new Date().toISOString(), + validUntil: args?.validUntil, + }, + metadata: { + type: DefaultStatusList2021ResourceTypes.suspension, + encrypted: true, + encoding: args?.statusListEncoding || DefaultStatusList2021Encodings.base64url, + encryptedSymmetricKey, + paymentConditions: args.paymentConditions, + }, + } satisfies StatusList2021Suspension, + { + symmetricKey: toString(symmetricKey!, 'hex'), + encryptedSymmetricKey, + encryptedString: await blobToHexString(encryptedString), + }, + ] satisfies [ + StatusList2021Suspension, + { symmetricKey: string; encryptedSymmetricKey: string; encryptedString: string }, + ]; + default: + throw new Error(`[did-provider-cheqd]: status purpose is not valid ${args.statusPurpose}`); + } + })(this) + : await (async function () { + switch (args.statusPurpose) { + case DefaultStatusList2021StatusPurposeTypes.revocation: + return [ + { + StatusList2021: { + statusPurpose: args.statusPurpose, + encodedList: bitstring, + validFrom: new Date().toISOString(), + validUntil: args?.validUntil, + }, + metadata: { + type: DefaultStatusList2021ResourceTypes.revocation, + encrypted: false, + encoding: args?.statusListEncoding || DefaultStatusList2021Encodings.base64url, + }, + } satisfies StatusList2021Revocation, + undefined, + ] satisfies [StatusList2021Revocation, undefined]; + case DefaultStatusList2021StatusPurposeTypes.suspension: + return [ + { + StatusList2021: { + statusPurpose: args.statusPurpose, + encodedList: bitstring, + validFrom: new Date().toISOString(), + validUntil: args?.validUntil, + }, + metadata: { + type: DefaultStatusList2021ResourceTypes.suspension, + encrypted: false, + encoding: args?.statusListEncoding || DefaultStatusList2021Encodings.base64url, + }, + } satisfies StatusList2021Suspension, + undefined, + ] satisfies [StatusList2021Suspension, undefined]; + default: + throw new Error('[did-provider-cheqd]: statusPurpose is not valid'); + } + })(); + + // construct payload + const payload = { + id: v4(), + collectionId: args.issuerDid.split(':').reverse()[0], + name: args.statusListName, + resourceType: DefaultStatusList2021ResourceTypes[args.statusPurpose], + version: args?.resourceVersion || new Date().toISOString(), + alsoKnownAs: args?.alsoKnownAs || [], + data: fromString(JSON.stringify(data[0]), 'utf-8'), + } satisfies StatusList2021ResourcePayload; + + // return result + return { + created: await context.agent[BroadcastStatusList2021MethodName]({ + kms: args.kms, + payload, + network: network as CheqdNetwork, + }), + resource: data[0], + resourceMetadata: await Cheqd.fetchStatusList2021Metadata({ + credentialStatus: { + id: `${DefaultResolverUrl}${args.issuerDid}?resourceName=${args.statusListName}&resourceType=${ + DefaultStatusList2021ResourceTypes[args.statusPurpose] + }`, + type: 'StatusList2021Entry', + }, + } as VerifiableCredential), + encrypted: args.encrypted, + symmetricKey: args?.returnSymmetricKey ? data[1]?.symmetricKey : undefined, + } satisfies CreateStatusList2021Result; + } + + private async BroadcastStatusList2021(args: ICheqdBroadcastStatusList2021Args, context: IContext) { + if (typeof args.kms !== 'string') { + throw new Error('[did-provider-cheqd]: kms is required'); + } + + if (typeof args.payload !== 'object') { + throw new Error('[did-provider-cheqd]: payload object is required'); + } + + if (typeof args.network !== 'string') { + throw new Error('[did-provider-cheqd]: network is required'); + } + + if (args?.file) { + args.payload.data = await Cheqd.getFile(args.file); + } + + if (typeof args?.payload?.data === 'string') { + args.payload.data = fromString(args.payload.data, 'base64'); + } + + // TODO: validate data as per bitstring + + // validate resource type + if (!Object.values(DefaultStatusList2021ResourceTypes).includes(args?.payload?.resourceType)) { + throw new Error( + `[did-provider-cheqd]: resourceType must be one of ${Object.values( + DefaultStatusList2021ResourceTypes + ).join(', ')}` + ); + } + + this.providerId = Cheqd.generateProviderId(args.network); + this.didProvider = await Cheqd.loadProvider(this.providerId, this.supportedDidProviders); + + return await this.didProvider.createResource( + { + options: { + kms: args.kms, + payload: args.payload, + signInputs: args.signInputs, + fee: + args?.fee || + (await ResourceModule.generateCreateResourceJsonFees( + (await this.didProvider.getWalletAccounts())[0].address + )), + }, + }, + context + ); + } + + private async GenerateDidDoc(args: ICheqdGenerateDidDocArgs, context: IContext): Promise { + if (typeof args.verificationMethod !== 'string') { + throw new Error('[did-provider-cheqd]: verificationMethod is required'); + } + + if (typeof args.methodSpecificIdAlgo !== 'string') { + throw new Error('[did-provider-cheqd]: methodSpecificIdAlgo is required'); + } + + if (typeof args.network !== 'string') { + throw new Error('[did-provider-cheqd]: network is required'); + } + + const keyPair = createKeyPairBase64(); + const keyPairHex: IKeyPair = { + publicKey: toString(fromString(keyPair.publicKey, 'base64'), 'hex'), + privateKey: toString(fromString(keyPair.privateKey, 'base64'), 'hex'), + }; + const verificationKeys = createVerificationKeys( + keyPair.publicKey, + args.methodSpecificIdAlgo, + 'key-1', + args.network + ); + const verificationMethods = createDidVerificationMethod([args.verificationMethod], [verificationKeys]); + + return { + didDoc: createDidPayload(verificationMethods, [verificationKeys]), + versionId: v4(), + keys: [ + { + publicKeyHex: keyPairHex.publicKey, + privateKeyHex: keyPairHex.privateKey, + kid: keyPairHex.publicKey, + type: 'Ed25519', + }, + ], + }; + } + + private async GenerateDidDocWithLinkedResource( + args: any, + context: IContext + ): Promise { + if (typeof args.verificationMethod !== 'string') { + throw new Error('[did-provider-cheqd]: verificationMethod is required'); + } + + if (typeof args.methodSpecificIdAlgo !== 'string') { + throw new Error('[did-provider-cheqd]: methodSpecificIdAlgo is required'); + } + + if (typeof args.network !== 'string') { + throw new Error('[did-provider-cheqd]: network is required'); + } + + const keyPair = createKeyPairBase64(); + const keyPairHex: IKeyPair = { + publicKey: toString(fromString(keyPair.publicKey, 'base64'), 'hex'), + privateKey: toString(fromString(keyPair.privateKey, 'base64'), 'hex'), + }; + const verificationKeys = createVerificationKeys( + keyPair.publicKey, + args.methodSpecificIdAlgo, + 'key-1', + args.network + ); + const verificationMethods = createDidVerificationMethod([args.verificationMethod], [verificationKeys]); + const payload = createDidPayload(verificationMethods, [verificationKeys]); + + return { + didDoc: payload, + versionId: v4(), + keys: [ + { + publicKeyHex: keyPairHex.publicKey, + privateKeyHex: keyPairHex.privateKey, + kid: keyPairHex.publicKey, + type: 'Ed25519', + }, + ], + linkedResource: { + id: v4(), + collectionId: payload.id.split(':').reverse()[0], + name: 'sample json resource', + version: '1.0.0', + resourceType: 'SampleResource', + alsoKnownAs: [], + data: toString(new TextEncoder().encode(JSON.stringify({ sample: 'json' })), 'base64'), + }, + }; + } + + private async GenerateIdentityKeys(args: any, context: IContext): Promise { + const keyPair = createKeyPairHex(); + return { + publicKeyHex: keyPair.publicKey, + privateKeyHex: keyPair.privateKey, + kid: keyPair.publicKey, + type: 'Ed25519', + }; + } + + private async GenerateVersionId(args: any, context: IContext): Promise { + return v4(); + } + + private async GenerateStatusList2021( + args: ICheqdGenerateStatusList2021Args, + context: IContext + ): Promise { + const statusList = args?.buffer + ? new StatusList({ buffer: args.buffer }) + : new StatusList({ length: args?.length || Cheqd.defaultStatusList2021Length }); + + const encoded = (await statusList.encode()) as Bitstring; + + switch (args?.bitstringEncoding) { + case 'base64url': + return encoded; + case 'base64': + return toString(fromString(encoded, 'base64url'), 'base64'); + case 'hex': + return toString(fromString(encoded, 'base64url'), 'hex'); + default: + return encoded; + } + } + + private async IssueRevocableCredentialWithStatusList2021( + args: ICheqdIssueRevocableCredentialWithStatusList2021Args, + context: IContext + ): Promise { + // generate index + const statusListIndex = + args.statusOptions.statusListIndex || + (await randomFromRange( + args.statusOptions.statusListRangeStart || 0, + (args.statusOptions.statusListRangeEnd || Cheqd.defaultStatusList2021Length) - 1, + args.statusOptions.indexNotIn || [] + )); + + // construct issuer + const issuer = (args.issuanceOptions.credential.issuer as { id: string }).id + ? (args.issuanceOptions.credential.issuer as { id: string }).id + : (args.issuanceOptions.credential.issuer as string); + + // generate status list credential + const statusListCredential = `${DefaultResolverUrl}${issuer}?resourceName=${args.statusOptions.statusListName}&resourceType=StatusList2021Revocation`; + + // construct credential status + const credentialStatus = { + id: `${statusListCredential}#${statusListIndex}`, + type: 'StatusList2021Entry', + statusPurpose: 'revocation', + statusListIndex: `${statusListIndex}`, + }; + + // add credential status to credential + args.issuanceOptions.credential.credentialStatus = credentialStatus; + + // add relevant context + args.issuanceOptions.credential['@context'] = (function () { + // if no context is provided, add default context + if (!args.issuanceOptions.credential['@context']) { + return [Cheqd.defaultContextV1, Cheqd.statusList2021Context]; + } + + // if context is provided as an array, add default context if it is not already present + if (Array.isArray(args.issuanceOptions.credential['@context'])) { + if (args.issuanceOptions.credential['@context'].length === 0) { + return [Cheqd.defaultContextV1, Cheqd.statusList2021Context]; + } + + if (!args.issuanceOptions.credential['@context'].includes(Cheqd.statusList2021Context)) { + return [...args.issuanceOptions.credential['@context'], Cheqd.statusList2021Context]; + } + } + + // if context is provided as a string, add default context if it is not already present + if (typeof args.issuanceOptions.credential['@context'] === 'string') + return [Cheqd.defaultContextV1, Cheqd.statusList2021Context]; + })(); + + // create a credential + const credential = await context.agent.createVerifiableCredential(args.issuanceOptions); + + return credential; + } + + private async IssueSuspendableCredentialWithStatusList2021( + args: ICheqdIssueSuspendableCredentialWithStatusList2021Args, + context: IContext + ): Promise { + // generate index + const statusListIndex = + args.statusOptions.statusListIndex || + (await randomFromRange( + args.statusOptions.statusListRangeStart || 0, + (args.statusOptions.statusListRangeEnd || Cheqd.defaultStatusList2021Length) - 1, + args.statusOptions.indexNotIn || [] + )); + + // construct issuer + const issuer = (args.issuanceOptions.credential.issuer as { id: string }).id + ? (args.issuanceOptions.credential.issuer as { id: string }).id + : (args.issuanceOptions.credential.issuer as string); + + // generate status list credential + const statusListCredential = `${DefaultResolverUrl}${issuer}?resourceName=${args.statusOptions.statusListName}&resourceType=StatusList2021Suspension`; + + // construct credential status + const credentialStatus = { + id: `${statusListCredential}#${statusListIndex}`, + type: 'StatusList2021Entry', + statusPurpose: 'suspension', + statusListIndex: `${statusListIndex}`, + }; + + // add credential status to credential + args.issuanceOptions.credential.credentialStatus = credentialStatus; + + // add relevant context + args.issuanceOptions.credential['@context'] = (function () { + // if no context is provided, add default context + if (!args.issuanceOptions.credential['@context']) { + return [Cheqd.defaultContextV1, Cheqd.statusList2021Context]; + } + + // if context is provided as an array, add default context if it is not already present + if (Array.isArray(args.issuanceOptions.credential['@context'])) { + if (args.issuanceOptions.credential['@context'].length === 0) { + return [Cheqd.defaultContextV1, Cheqd.statusList2021Context]; + } + + if (!args.issuanceOptions.credential['@context'].includes(Cheqd.statusList2021Context)) { + return [...args.issuanceOptions.credential['@context'], Cheqd.statusList2021Context]; + } + } + + // if context is provided as a string, add default context if it is not already present + if (typeof args.issuanceOptions.credential['@context'] === 'string') + return [Cheqd.defaultContextV1, Cheqd.statusList2021Context]; + })(); + + // create a credential + const credential = await context.agent.createVerifiableCredential(args.issuanceOptions); + + return credential; + } + + private async VerifyCredentialWithStatusList2021( + args: ICheqdVerifyCredentialWithStatusList2021Args, + context: IContext + ): Promise { + // verify default policies + const verificationResult = await context.agent.verifyCredential({ + ...args?.verificationArgs, + credential: args.credential, + policies: { + ...args?.verificationArgs?.policies, + credentialStatus: false, + }, + } satisfies IVerifyCredentialArgs); + + // early return if verification failed + if (!verificationResult.verified) { + return { verified: false, error: verificationResult.error }; + } + + // if jwt credential, decode it + const credential = + typeof args.credential === 'string' ? await Cheqd.decodeCredentialJWT(args.credential) : args.credential; + + // define issuer + const issuer = + typeof credential.issuer === 'string' ? credential.issuer : (credential.issuer as { id: string }).id; + + // define provider, if applicable + this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders); + + // define provider id, if applicable + this.providerId = Cheqd.generateProviderId(issuer); + + // define dkg options, if provided + args.dkgOptions ||= this.didProvider.dkgOptions; + + // verify credential status + switch (credential.credentialStatus?.statusPurpose) { + case 'revocation': + if (await Cheqd.checkRevoked(credential, { ...args.options, topArgs: args })) + return { ...verificationResult, revoked: true }; + return { ...verificationResult, revoked: false }; + case 'suspension': + if (await Cheqd.checkSuspended(credential, { ...args.options, topArgs: args })) + return { ...verificationResult, suspended: true }; + return { ...verificationResult, suspended: false }; + default: + throw new Error( + `[did-provider-cheqd]: verify credential: Unsupported status purpose: ${credential.credentialStatus?.statusPurpose}` + ); + } + } + + private async VerifyPresentationWithStatusList2021( + args: ICheqdVerifyPresentationWithStatusList2021Args, + context: IContext + ): Promise { + // verify default policies + const verificationResult = await context.agent.verifyPresentation({ + ...args?.verificationArgs, + presentation: args.presentation, + policies: { + ...args?.verificationArgs?.policies, + credentialStatus: false, + }, + } satisfies IVerifyPresentationArgs); + + // early return if verification failed + if (!verificationResult.verified) { + return { verified: false, error: verificationResult.error }; + } + + // early return if no verifiable credentials are provided + if (!args.presentation.verifiableCredential) + throw new Error('[did-provider-cheqd]: verify presentation: presentation.verifiableCredential is required'); + + // verify credential(s) status(es) + for (let credential of args.presentation.verifiableCredential) { + // if jwt credential, decode it + if (typeof credential === 'string') credential = await Cheqd.decodeCredentialJWT(credential); + + // define issuer + const issuer = + typeof credential.issuer === 'string' ? credential.issuer : (credential.issuer as { id: string }).id; + + // define provider, if applicable + this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders); + + // define provider id, if applicable + this.providerId = Cheqd.generateProviderId(issuer); + + // define dkg options, if provided + args.dkgOptions ||= this.didProvider.dkgOptions; + + switch (credential.credentialStatus?.statusPurpose) { + case 'revocation': + if (await Cheqd.checkRevoked(credential, { ...args.options, topArgs: args })) + return { ...verificationResult, revoked: true }; + break; + case 'suspension': + if (await Cheqd.checkSuspended(credential, { ...args.options, topArgs: args })) + return { ...verificationResult, suspended: true }; + break; + default: + throw new Error( + `[did-provider-cheqd]: verify presentation: Unsupported status purpose: ${credential.credentialStatus?.statusPurpose}` + ); + } + } + + return { ...verificationResult, verified: true }; + } + + private async CheckCredentialStatusWithStatusList2021( + args: ICheqdCheckCredentialStatusWithStatusList2021Args, + context: IContext + ): Promise { + // verify credential, if provided and status options are not + if (args?.credential && !args?.statusOptions) { + const verificationResult = await context.agent.verifyCredential({ + credential: args.credential, + policies: { + credentialStatus: false, + }, + } satisfies IVerifyCredentialArgs); + + // early return if verification failed + if (!verificationResult.verified) { + return { revoked: false, error: verificationResult.error }; + } + } + + // if status options are provided, give precedence + if (args?.statusOptions) { + // validate status options - case: statusOptions.issuerDid + if (!args.statusOptions.issuerDid) + throw new Error('[did-provider-cheqd]: check status: statusOptions.issuerDid is required'); + + // validate status options - case: statusOptions.statusListName + if (!args.statusOptions.statusListName) + throw new Error('[did-provider-cheqd]: check status: statusOptions.statusListName is required'); + + // validate status options - case: statusOptions.statusListIndex + if (!args.statusOptions.statusPurpose) + throw new Error('[did-provider-cheqd]: check status: statusOptions.statusListIndex is required'); + + // validate status options - case: statusOptions.statusListIndex + if (!args.statusOptions.statusListIndex) + throw new Error('[did-provider-cheqd]: check status: statusOptions.statusListIndex is required'); + + // generate resource type + const resourceType = + args.statusOptions.statusPurpose === 'revocation' + ? 'StatusList2021Revocation' + : 'StatusList2021Suspension'; + + // construct status list credential + const statusListCredential = `${DefaultResolverUrl}${args.statusOptions.issuerDid}?resourceName=${args.statusOptions.statusListName}&resourceType=${resourceType}`; + + // construct credential status + args.credential = { + '@context': [], + issuer: args.statusOptions.issuerDid, + credentialSubject: {}, + credentialStatus: { + id: `${statusListCredential}#${args.statusOptions.statusListIndex}`, + type: 'StatusList2021Entry', + statusPurpose: `${args.statusOptions.statusPurpose}`, + statusListIndex: `${args.statusOptions.statusListIndex}`, + }, + issuanceDate: '', + proof: {}, + }; + } + + // validate args - case: credential + if (!args.credential) throw new Error('[did-provider-cheqd]: revocation: credential is required'); + + // if jwt credential, decode it + const credential = + typeof args.credential === 'string' ? await Cheqd.decodeCredentialJWT(args.credential) : args.credential; + + // define issuer + const issuer = + typeof credential.issuer === 'string' ? credential.issuer : (credential.issuer as { id: string }).id; + + // define provider, if applicable + this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders); + + // define provider id, if applicable + this.providerId = Cheqd.generateProviderId(issuer); + + // define dkg options, if provided + args.dkgOptions ||= this.didProvider.dkgOptions; + + switch (credential.credentialStatus?.statusPurpose) { + case 'revocation': + if (await Cheqd.checkRevoked(credential, { ...args.options, topArgs: args })) return { revoked: true }; + return { revoked: false }; + case 'suspension': + if (await Cheqd.checkSuspended(credential, { ...args.options, topArgs: args })) + return { suspended: true }; + return { suspended: false }; + default: + throw new Error( + `[did-provider-cheqd]: check status: Unsupported status purpose: ${credential.credentialStatus?.statusPurpose}` + ); + } + } + + private async RevokeCredentialWithStatusList2021( + args: ICheqdRevokeCredentialWithStatusList2021Args, + context: IContext + ): Promise { + // verify credential, if provided and revocation options are not + if (args?.credential && !args?.revocationOptions) { + const verificationResult = await context.agent.verifyCredential({ + credential: args.credential, + policies: { + credentialStatus: false, + }, + } satisfies IVerifyCredentialArgs); + + // early return if verification failed + if (!verificationResult.verified) { + return { revoked: false, error: verificationResult.error }; + } + } + + // if revocation options are provided, give precedence + if (args?.revocationOptions) { + // validate revocation options - case: revocationOptions.issuerDid + if (!args.revocationOptions.issuerDid) + throw new Error('[did-provider-cheqd]: revocation: revocationOptions.issuerDid is required'); + + // validate revocation options - case: revocationOptions.statusListName + if (!args.revocationOptions.statusListName) + throw new Error('[did-provider-cheqd]: revocation: revocationOptions.statusListName is required'); + + // validate revocation options - case: revocationOptions.statusListIndex + if (!args.revocationOptions.statusListIndex) + throw new Error('[did-provider-cheqd]: revocation: revocationOptions.statusListIndex is required'); + + // construct status list credential + const statusListCredential = `${DefaultResolverUrl}${args.revocationOptions.issuerDid}?resourceName=${args.revocationOptions.statusListName}&resourceType=StatusList2021Revocation`; + + // construct credential status + args.credential = { + '@context': [], + issuer: args.revocationOptions.issuerDid, + credentialSubject: {}, + credentialStatus: { + id: `${statusListCredential}#${args.revocationOptions.statusListIndex}`, + type: 'StatusList2021Entry', + statusPurpose: 'revocation', + statusListIndex: `${args.revocationOptions.statusListIndex}`, + }, + issuanceDate: '', + proof: {}, + }; + } + + // validate args - case: credential + if (!args.credential) throw new Error('[did-provider-cheqd]: revocation: credential is required'); + + // if jwt credential, decode it + const credential = + typeof args.credential === 'string' ? await Cheqd.decodeCredentialJWT(args.credential) : args.credential; + + // validate status purpose + if (credential.credentialStatus?.statusPurpose !== 'revocation') { + throw new Error( + `[did-provider-cheqd]: revocation: Unsupported status purpose: ${credential.credentialStatus?.statusPurpose}` + ); + } + + // validate args in pairs - case: statusListFile and statusList + if (args.options?.statusListFile && args.options?.statusList) { + throw new Error('[did-provider-cheqd]: revocation: statusListFile and statusList are mutually exclusive'); + } + + // validate args in pairs - case: statusListFile and fetchList + if (args.options?.statusListFile && args.options?.fetchList) { + throw new Error('[did-provider-cheqd]: revocation: statusListFile and fetchList are mutually exclusive'); + } + + // validate args in pairs - case: statusList and fetchList + if (args.options?.statusList && args.options?.fetchList) { + throw new Error('[did-provider-cheqd]: revocation: statusList and fetchList are mutually exclusive'); + } + + // validate args in pairs - case: publish + if (args.options?.publish && !args.fetchList && !(args.options?.statusListFile || args.options?.statusList)) { + throw new Error( + '[did-provider-cheqd]: revocation: publish requires statusListFile or statusList, if fetchList is disabled' + ); + } + + // define issuer + const issuer = + typeof credential.issuer === 'string' ? credential.issuer : (credential.issuer as { id: string }).id; + + // define provider, if applicable + this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders); + + // define provider id, if applicable + this.providerId = Cheqd.generateProviderId(issuer); + + // define dkg options, if provided + args.dkgOptions ||= this.didProvider.dkgOptions; + + // revoke credential + return await Cheqd.revokeCredential(credential, { + ...args.options, + topArgs: args, + publishOptions: { + context, + statusListEncoding: args?.options?.statusListEncoding, + statusListValidUntil: args?.options?.statusListValidUntil, + resourceId: args?.options?.resourceId, + resourceVersion: args?.options?.resourceVersion, + resourceAlsoKnownAs: args?.options?.alsoKnownAs, + signInputs: args?.options?.signInputs, + fee: args?.options?.fee, + }, + }); + } + + private async RevokeBulkCredentialsWithStatusList2021( + args: ICheqdRevokeBulkCredentialsWithStatusList2021Args, + context: IContext + ): Promise { + // verify credential, if provided and revocation options are not + if (args?.credentials && !args?.revocationOptions) { + const verificationResult = await Promise.all( + args.credentials.map(async (credential) => { + return await context.agent.verifyCredential({ + credential, + policies: { + credentialStatus: false, + }, + } satisfies IVerifyCredentialArgs); + }) + ); + + // early return if verification failed for any credential + if (verificationResult.some((result) => !result.verified)) { + // define verified + return { + revoked: Array(args.credentials.length).fill(false), + error: verificationResult.find((result) => !result.verified)!.error || { + message: 'verification: could not verify credential', + }, + }; + } + } + + // if revocation options are provided, give precedence + if (args?.revocationOptions) { + // validate revocation options - case: revocationOptions.issuerDid + if (!args.revocationOptions.issuerDid) + throw new Error('[did-provider-cheqd]: revocation: revocationOptions.issuerDid is required'); + + // validate revocation options - case: revocationOptions.statusListName + if (!args.revocationOptions.statusListName) + throw new Error('[did-provider-cheqd]: revocation: revocationOptions.statusListName is required'); + + // validate revocation options - case: revocationOptions.statusListIndices + if ( + !args.revocationOptions.statusListIndices || + !args.revocationOptions.statusListIndices.length || + args.revocationOptions.statusListIndices.length === 0 || + !args.revocationOptions.statusListIndices.every((index) => !isNaN(+index)) + ) + throw new Error( + '[did-provider-cheqd]: revocation: revocationOptions.statusListIndex is required and must be an array of indices' + ); + + // construct status list credential + const statusListCredential = `${DefaultResolverUrl}${args.revocationOptions.issuerDid}?resourceName=${args.revocationOptions.statusListName}&resourceType=StatusList2021Revocation`; + + // construct credential status + args.credentials = args.revocationOptions.statusListIndices.map((index) => ({ + '@context': [], + issuer: args.revocationOptions!.issuerDid, + credentialSubject: {}, + credentialStatus: { + id: `${statusListCredential}#${index}`, + type: 'StatusList2021Entry', + statusPurpose: 'revocation', + statusListIndex: `${index}`, + }, + issuanceDate: '', + proof: {}, + })); + } + + // validate args - case: credentials + if (!args.credentials || !args.credentials.length || args.credentials.length === 0) + throw new Error( + '[did-provider-cheqd]: revocation: credentials is required and must be an array of credentials' + ); + + // if jwt credentials, decode them + const credentials = await Promise.all( + args.credentials.map(async (credential) => + typeof credential === 'string' ? await Cheqd.decodeCredentialJWT(credential) : credential + ) + ); + + // validate args in pairs - case: statusListFile and statusList + if (args.options?.statusListFile && args.options?.statusList) { + throw new Error('[did-provider-cheqd]: revocation: statusListFile and statusList are mutually exclusive'); + } + + // validate args in pairs - case: statusListFile and fetchList + if (args.options?.statusListFile && args.options?.fetchList) { + throw new Error('[did-provider-cheqd]: revocation: statusListFile and fetchList are mutually exclusive'); + } + + // validate args in pairs - case: statusList and fetchList + if (args.options?.statusList && args.options?.fetchList) { + throw new Error('[did-provider-cheqd]: revocation: statusList and fetchList are mutually exclusive'); + } + + // validate args in pairs - case: publish + if (args.options?.publish && !args.fetchList && !(args.options?.statusListFile || args.options?.statusList)) { + throw new Error( + '[did-provider-cheqd]: revocation: publish requires statusListFile or statusList, if fetchList is disabled' + ); + } + + // define issuer + const issuer = + typeof credentials[0].issuer === 'string' + ? credentials[0].issuer + : (credentials[0].issuer as { id: string }).id; + + // define provider, if applicable + this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders); + + // define provider id, if applicable + this.providerId = Cheqd.generateProviderId(issuer); + + // define dkg options, if provided + args.dkgOptions ||= this.didProvider.dkgOptions; + + // revoke credentials + return await Cheqd.revokeCredentials(credentials, { + ...args.options, + topArgs: args, + publishOptions: { + context, + resourceId: args?.options?.resourceId, + resourceVersion: args?.options?.resourceVersion, + resourceAlsoKnownAs: args?.options?.alsoKnownAs, + signInputs: args?.options?.signInputs, + fee: args?.options?.fee, + }, + }); + } + + private async SuspendCredentialWithStatusList2021( + args: ICheqdSuspendCredentialWithStatusList2021Args, + context: IContext + ): Promise { + // verify credential, if provided and suspension options are not + if (args?.credential && !args?.suspensionOptions) { + const verificationResult = await context.agent.verifyCredential({ + credential: args.credential, + policies: { + credentialStatus: false, + }, + } satisfies IVerifyCredentialArgs); + + // early return if verification failed + if (!verificationResult.verified) { + return { suspended: false, error: verificationResult.error }; + } + } + + // if suspension options are provided, give precedence + if (args?.suspensionOptions) { + // validate suspension options - case: suspensionOptions.issuerDid + if (!args.suspensionOptions.issuerDid) + throw new Error('[did-provider-cheqd]: suspension: suspensionOptions.issuerDid is required'); + + // validate suspension options - case: suspensionOptions.statusListName + if (!args.suspensionOptions.statusListName) + throw new Error('[did-provider-cheqd]: suspension: suspensionOptions.statusListName is required'); + + // validate suspension options - case: suspensionOptions.statusListIndex + if (!args.suspensionOptions.statusListIndex) + throw new Error('[did-provider-cheqd]: suspension: suspensionOptions.statusListIndex is required'); + + // construct status list credential + const statusListCredential = `${DefaultResolverUrl}${args.suspensionOptions.issuerDid}?resourceName=${args.suspensionOptions.statusListName}&resourceType=StatusList2021Suspension`; + + // construct credential status + args.credential = { + '@context': [], + issuer: args.suspensionOptions.issuerDid, + credentialSubject: {}, + credentialStatus: { + id: `${statusListCredential}#${args.suspensionOptions.statusListIndex}`, + type: 'StatusList2021Entry', + statusPurpose: 'suspension', + statusListIndex: `${args.suspensionOptions.statusListIndex}`, + }, + issuanceDate: '', + proof: {}, + }; + } + + // validate args - case: credential + if (!args.credential) throw new Error('[did-provider-cheqd]: suspension: credential is required'); + + // if jwt credential, decode it + const credential = + typeof args.credential === 'string' ? await Cheqd.decodeCredentialJWT(args.credential) : args.credential; + + // validate status purpose + if (credential.credentialStatus?.statusPurpose !== 'suspension') { + throw new Error( + `[did-provider-cheqd]: suspension: Unsupported status purpose: ${credential.credentialStatus?.statusPurpose}` + ); + } + + // validate args in pairs - case: statusListFile and statusList + if (args.options?.statusListFile && args.options?.statusList) { + throw new Error('[did-provider-cheqd]: suspension: statusListFile and statusList are mutually exclusive'); + } + + // validate args in pairs - case: statusListFile and fetchList + if (args.options?.statusListFile && args.options?.fetchList) { + throw new Error('[did-provider-cheqd]: suspension: statusListFile and fetchList are mutually exclusive'); + } + + // validate args in pairs - case: statusList and fetchList + if (args.options?.statusList && args.options?.fetchList) { + throw new Error('[did-provider-cheqd]: suspension: statusList and fetchList are mutually exclusive'); + } + + // validate args in pairs - case: publish + if (args.options?.publish && !args.fetchList && !(args.options?.statusListFile || args.options?.statusList)) { + throw new Error( + '[did-provider-cheqd]: suspension: publish requires statusListFile or statusList, if fetchList is disabled' + ); + } + + // define issuer + const issuer = + typeof credential.issuer === 'string' ? credential.issuer : (credential.issuer as { id: string }).id; + + // define provider, if applicable + this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders); + + // define provider id, if applicable + this.providerId = Cheqd.generateProviderId(issuer); + + // define dkg options, if provided + args.dkgOptions ||= this.didProvider.dkgOptions; + + // suspend credential + return await Cheqd.suspendCredential(credential, { + ...args.options, + topArgs: args, + publishOptions: { + context, + statusListEncoding: args?.options?.statusListEncoding, + statusListValidUntil: args?.options?.statusListValidUntil, + resourceId: args?.options?.resourceId, + resourceVersion: args?.options?.resourceVersion, + resourceAlsoKnownAs: args?.options?.alsoKnownAs, + signInputs: args?.options?.signInputs, + fee: args?.options?.fee, + }, + }); + } + + private async SuspendBulkCredentialsWithStatusList2021( + args: ICheqdSuspendBulkCredentialsWithStatusList2021Args, + context: IContext + ): Promise { + // verify credential, if provided and suspension options are not + if (args?.credentials && !args?.suspensionOptions) { + const verificationResult = await Promise.all( + args.credentials.map(async (credential) => { + return await context.agent.verifyCredential({ + credential, + policies: { + credentialStatus: false, + }, + } satisfies IVerifyCredentialArgs); + }) + ); + + // early return if verification failed for any credential + if (verificationResult.some((result) => !result.verified)) { + // define verified + return { + suspended: Array(args.credentials.length).fill(false), + error: verificationResult.find((result) => !result.verified)!.error || { + message: 'verification: could not verify credential', + }, + }; + } + } + + // if suspension options are provided, give precedence + if (args?.suspensionOptions) { + // validate suspension options - case: suspensionOptions.issuerDid + if (!args.suspensionOptions.issuerDid) + throw new Error('[did-provider-cheqd]: suspension: suspensionOptions.issuerDid is required'); + + // validate suspension options - case: suspensionOptions.statusListName + if (!args.suspensionOptions.statusListName) + throw new Error('[did-provider-cheqd]: suspension: suspensionOptions.statusListName is required'); + + // validate suspension options - case: suspensionOptions.statusListIndices + if ( + !args.suspensionOptions.statusListIndices || + !args.suspensionOptions.statusListIndices.length || + args.suspensionOptions.statusListIndices.length === 0 || + !args.suspensionOptions.statusListIndices.every((index) => !isNaN(+index)) + ) + throw new Error( + '[did-provider-cheqd]: suspension: suspensionOptions.statusListIndex is required and must be an array of indices' + ); + + // construct status list credential + const statusListCredential = `${DefaultResolverUrl}${args.suspensionOptions.issuerDid}?resourceName=${args.suspensionOptions.statusListName}&resourceType=StatusList2021Suspension`; + + // construct credential status + args.credentials = args.suspensionOptions.statusListIndices.map((index) => ({ + '@context': [], + issuer: args.suspensionOptions!.issuerDid, + credentialSubject: {}, + credentialStatus: { + id: `${statusListCredential}#${index}`, + type: 'StatusList2021Entry', + statusPurpose: 'suspension', + statusListIndex: `${index}`, + }, + issuanceDate: '', + proof: {}, + })); + } + + // validate args - case: credentials + if (!args.credentials || !args.credentials.length || args.credentials.length === 0) + throw new Error( + '[did-provider-cheqd]: suspension: credentials is required and must be an array of credentials' + ); + + // if jwt credentials, decode them + const credentials = await Promise.all( + args.credentials.map(async (credential) => + typeof credential === 'string' ? await Cheqd.decodeCredentialJWT(credential) : credential + ) + ); + + // validate args in pairs - case: statusListFile and statusList + if (args.options?.statusListFile && args.options?.statusList) { + throw new Error('[did-provider-cheqd]: suspension: statusListFile and statusList are mutually exclusive'); + } + + // validate args in pairs - case: statusListFile and fetchList + if (args.options?.statusListFile && args.options?.fetchList) { + throw new Error('[did-provider-cheqd]: suspension: statusListFile and fetchList are mutually exclusive'); + } + + // validate args in pairs - case: statusList and fetchList + if (args.options?.statusList && args.options?.fetchList) { + throw new Error('[did-provider-cheqd]: suspension: statusList and fetchList are mutually exclusive'); + } + + // validate args in pairs - case: publish + if (args.options?.publish && !args.fetchList && !(args.options?.statusListFile || args.options?.statusList)) { + throw new Error( + '[did-provider-cheqd]: suspension: publish requires statusListFile or statusList, if fetchList is disabled' + ); + } + + // define issuer + const issuer = + typeof credentials[0].issuer === 'string' + ? credentials[0].issuer + : (credentials[0].issuer as { id: string }).id; + + // define provider, if applicable + this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders); + + // define provider id, if applicable + this.providerId = Cheqd.generateProviderId(issuer); + + // define dkg options, if provided + args.dkgOptions ||= this.didProvider.dkgOptions; + + // suspend credentials + return await Cheqd.suspendCredentials(credentials, { + ...args.options, + topArgs: args, + publishOptions: { + context, + resourceId: args?.options?.resourceId, + resourceVersion: args?.options?.resourceVersion, + resourceAlsoKnownAs: args?.options?.alsoKnownAs, + signInputs: args?.options?.signInputs, + fee: args?.options?.fee, + }, + }); + } + + private async UnsuspendCredentialWithStatusList2021( + args: ICheqdUnsuspendCredentialWithStatusList2021Args, + context: IContext + ): Promise { + // verify credential, if provided and unsuspension options are not + if (args?.credential && !args?.unsuspensionOptions) { + const verificationResult = await context.agent.verifyCredential({ + credential: args.credential, + policies: { + credentialStatus: false, + }, + } satisfies IVerifyCredentialArgs); + + // early return if verification failed + if (!verificationResult.verified) { + return { unsuspended: false, error: verificationResult.error }; + } + } + + // if unsuspension options are provided, give precedence + if (args?.unsuspensionOptions) { + // validate unsuspension options - case: unsuspensionOptions.issuerDid + if (!args.unsuspensionOptions.issuerDid) + throw new Error('[did-provider-cheqd]: unsuspension: unsuspensionOptions.issuerDid is required'); + + // validate unsuspension options - case: unsuspensionOptions.statusListName + if (!args.unsuspensionOptions.statusListName) + throw new Error('[did-provider-cheqd]: unsuspension: unsuspensionOptions.statusListName is required'); + + // validate unsuspension options - case: unsuspensionOptions.statusListIndex + if (!args.unsuspensionOptions.statusListIndex) + throw new Error('[did-provider-cheqd]: unsuspension: unsuspensionOptions.statusListIndex is required'); + + // construct status list credential + const statusListCredential = `${DefaultResolverUrl}${args.unsuspensionOptions.issuerDid}?resourceName=${args.unsuspensionOptions.statusListName}&resourceType=StatusList2021Suspension`; + + // construct credential status + args.credential = { + '@context': [], + issuer: args.unsuspensionOptions.issuerDid, + credentialSubject: {}, + credentialStatus: { + id: `${statusListCredential}#${args.unsuspensionOptions.statusListIndex}`, + type: 'StatusList2021Entry', + statusPurpose: 'suspension', + statusListIndex: `${args.unsuspensionOptions.statusListIndex}`, + }, + issuanceDate: '', + proof: {}, + }; + } + + // validate args - case: credential + if (!args.credential) throw new Error('[did-provider-cheqd]: unsuspension: credential is required'); + + // if jwt credential, decode it + const credential = + typeof args.credential === 'string' ? await Cheqd.decodeCredentialJWT(args.credential) : args.credential; + + // validate status purpose + if (credential.credentialStatus?.statusPurpose !== 'suspension') { + throw new Error( + `[did-provider-cheqd]: suspension: Unsupported status purpose: ${credential.credentialStatus?.statusPurpose}` + ); + } + + // validate args in pairs - case: statusListFile and statusList + if (args.options?.statusListFile && args.options?.statusList) { + throw new Error('[did-provider-cheqd]: suspension: statusListFile and statusList are mutually exclusive'); + } + + // validate args in pairs - case: statusListFile and fetchList + if (args.options?.statusListFile && args.options?.fetchList) { + throw new Error('[did-provider-cheqd]: suspension: statusListFile and fetchList are mutually exclusive'); + } + + // validate args in pairs - case: statusList and fetchList + if (args.options?.statusList && args.options?.fetchList) { + throw new Error('[did-provider-cheqd]: suspension: statusList and fetchList are mutually exclusive'); + } + + // validate args in pairs - case: publish + if (args.options?.publish && !args.fetchList && !(args.options?.statusListFile || args.options?.statusList)) { + throw new Error( + '[did-provider-cheqd]: suspension: publish requires statusListFile or statusList, if fetchList is disabled' + ); + } + + // define issuer + const issuer = + typeof credential.issuer === 'string' ? credential.issuer : (credential.issuer as { id: string }).id; + + // define provider, if applicable + this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders); + + // define provider id, if applicable + this.providerId = Cheqd.generateProviderId(issuer); + + // define dkg options, if provided + args.dkgOptions ||= this.didProvider.dkgOptions; + + // suspend credential + return await Cheqd.unsuspendCredential(credential, { + ...args.options, + topArgs: args, + publishOptions: { + context, + statusListEncoding: args?.options?.statusListEncoding, + statusListValidUntil: args?.options?.statusListValidUntil, + resourceId: args?.options?.resourceId, + resourceVersion: args?.options?.resourceVersion, + resourceAlsoKnownAs: args?.options?.alsoKnownAs, + signInputs: args?.options?.signInputs, + fee: args?.options?.fee, + }, + }); + } + + private async UnsuspendBulkCredentialsWithStatusList2021( + args: ICheqdUnsuspendBulkCredentialsWithStatusList2021Args, + context: IContext + ): Promise { + // verify credential, if provided and unsuspension options are not + if (args?.credentials && !args?.unsuspensionOptions) { + const verificationResult = await Promise.all( + args.credentials.map(async (credential) => { + return await context.agent.verifyCredential({ + credential, + policies: { + credentialStatus: false, + }, + } satisfies IVerifyCredentialArgs); + }) + ); + + // early return if verification failed for any credential + if (verificationResult.some((result) => !result.verified)) { + // define verified + return { + unsuspended: Array(args.credentials.length).fill(false), + error: verificationResult.find((result) => !result.verified)!.error || { + message: 'verification: could not verify credential', + }, + }; + } + } + + // if unsuspension options are provided, give precedence + if (args?.unsuspensionOptions) { + // validate unsuspension options - case: unsuspensionOptions.issuerDid + if (!args.unsuspensionOptions.issuerDid) + throw new Error('[did-provider-cheqd]: unsuspension: unsuspensionOptions.issuerDid is required'); + + // validate unsuspension options - case: unsuspensionOptions.statusListName + if (!args.unsuspensionOptions.statusListName) + throw new Error('[did-provider-cheqd]: unsuspension: unsuspensionOptions.statusListName is required'); + + // validate unsuspension options - case: unsuspensionOptions.statusListIndices + if ( + !args.unsuspensionOptions.statusListIndices || + !args.unsuspensionOptions.statusListIndices.length || + args.unsuspensionOptions.statusListIndices.length === 0 || + !args.unsuspensionOptions.statusListIndices.every((index) => !isNaN(+index)) + ) + throw new Error( + '[did-provider-cheqd]: unsuspension: unsuspensionOptions.statusListIndex is required and must be an array of indices' + ); + + // construct status list credential + const statusListCredential = `${DefaultResolverUrl}${args.unsuspensionOptions.issuerDid}?resourceName=${args.unsuspensionOptions.statusListName}&resourceType=StatusList2021Suspension`; + + // construct credential status + args.credentials = args.unsuspensionOptions.statusListIndices.map((index) => ({ + '@context': [], + issuer: args.unsuspensionOptions!.issuerDid, + credentialSubject: {}, + credentialStatus: { + id: `${statusListCredential}#${index}`, + type: 'StatusList2021Entry', + statusPurpose: 'suspension', + statusListIndex: `${index}`, + }, + issuanceDate: '', + proof: {}, + })); + } + + // validate args - case: credentials + if (!args.credentials || !args.credentials.length || args.credentials.length === 0) + throw new Error( + '[did-provider-cheqd]: unsuspension: credentials is required and must be an array of credentials' + ); + + // if jwt credentials, decode them + const credentials = await Promise.all( + args.credentials.map(async (credential) => + typeof credential === 'string' ? await Cheqd.decodeCredentialJWT(credential) : credential + ) + ); + + // validate args in pairs - case: statusListFile and statusList + if (args.options?.statusListFile && args.options?.statusList) { + throw new Error('[did-provider-cheqd]: unsuspension: statusListFile and statusList are mutually exclusive'); + } + + // validate args in pairs - case: statusListFile and fetchList + if (args.options?.statusListFile && args.options?.fetchList) { + throw new Error('[did-provider-cheqd]: unsuspension: statusListFile and fetchList are mutually exclusive'); + } + + // validate args in pairs - case: statusList and fetchList + if (args.options?.statusList && args.options?.fetchList) { + throw new Error('[did-provider-cheqd]: unsuspension: statusList and fetchList are mutually exclusive'); + } + + // validate args in pairs - case: publish + if (args.options?.publish && !args.fetchList && !(args.options?.statusListFile || args.options?.statusList)) { + throw new Error( + '[did-provider-cheqd]: unsuspension: publish requires statusListFile or statusList, if fetchList is disabled' + ); + } + + // define issuer + const issuer = + typeof credentials[0].issuer === 'string' + ? credentials[0].issuer + : (credentials[0].issuer as { id: string }).id; + + // define provider, if applicable + this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders); + + // define provider id, if applicable + this.providerId = Cheqd.generateProviderId(issuer); + + // define dkg options, if provided + args.dkgOptions ||= this.didProvider.dkgOptions; + + // suspend credentials + return await Cheqd.unsuspendCredentials(credentials, { + ...args.options, + topArgs: args, + publishOptions: { + context, + resourceId: args?.options?.resourceId, + resourceVersion: args?.options?.resourceVersion, + resourceAlsoKnownAs: args?.options?.alsoKnownAs, + signInputs: args?.options?.signInputs, + fee: args?.options?.fee, + }, + }); + } + + private async TransactSendTokens( + args: ICheqdTransactSendTokensArgs, + context: IContext + ): Promise { + // define provider + const provider = (function (that) { + // switch on network + return ( + that.supportedDidProviders.find((provider) => provider.network === args.network) || + (function () { + throw new Error(`[did-provider-cheqd]: transact: no relevant providers found`); + })() + ); + })(this); + + try { + // delegate to provider + const transactionResult = await provider.transactSendTokens({ + recipientAddress: args.recipientAddress, + amount: args.amount, + memo: args.memo, + txBytes: args.txBytes, + }); + + // return transaction result + return { + successful: !transactionResult.code, + transactionHash: transactionResult.transactionHash, + events: transactionResult.events, + rawLog: transactionResult.rawLog, + txResponse: args?.returnTxResponse ? transactionResult : undefined, + } satisfies TransactionResult; + } catch (error) { + // return error + return { + successful: false, + error: error as IError, + } satisfies TransactionResult; + } + } + + private async ObservePaymentCondition( + args: ICheqdObservePaymentConditionArgs, + context: IContext + ): Promise { + // verify with raw unified access control condition, if any + if (args?.unifiedAccessControlCondition) { + // validate args - case: unifiedAccessControlCondition.chain + if ( + !args.unifiedAccessControlCondition.chain || + !Object.values(LitCompatibleCosmosChains).includes( + args.unifiedAccessControlCondition.chain as LitCompatibleCosmosChain + ) + ) + throw new Error( + '[did-provider-cheqd]: observe: unifiedAccessControlCondition.chain is required and must be a valid Lit-compatible chain' + ); + + // validate args - case: unifiedAccessControlCondition.path + if (!args.unifiedAccessControlCondition.path) + throw new Error('[did-provider-cheqd]: observe: unifiedAccessControlCondition.path is required'); + + // validate args - case: unifiedAccessControlCondition.conditionType + if (args.unifiedAccessControlCondition.conditionType !== 'cosmos') + throw new Error( + '[did-provider-cheqd]: observe: unifiedAccessControlCondition.conditionType must be cosmos' + ); + + // validate args - case: unifiedAccessControlCondition.method + if (args.unifiedAccessControlCondition.method !== 'timelock') + throw new Error('[did-provider-cheqd]: observe: unifiedAccessControlCondition.method must be timelock'); + + // validate args - case: unifiedAccessControlCondition.parameters + if ( + !args.unifiedAccessControlCondition.parameters || + !Array.isArray(args.unifiedAccessControlCondition.parameters) || + args.unifiedAccessControlCondition.parameters.length === 0 || + args.unifiedAccessControlCondition.parameters.length > 1 + ) + throw new Error( + '[did-provider-cheqd]: observe: unifiedAccessControlCondition.parameters is required and must be an array of length 1 of type string content' + ); + + // validate args - case: unifiedAccessControlCondition.returnValueTest + if ( + !args.unifiedAccessControlCondition.returnValueTest || + !args.unifiedAccessControlCondition.returnValueTest.comparator || + !args.unifiedAccessControlCondition.returnValueTest.key || + !args.unifiedAccessControlCondition.returnValueTest.value + ) + throw new Error( + '[did-provider-cheqd]: observe: unifiedAccessControlCondition.returnValueTest is required' + ); + + try { + // define network + const network = (function () { + switch (args.unifiedAccessControlCondition.chain) { + case LitCompatibleCosmosChains.cheqdMainnet: + return CheqdNetwork.Mainnet; + case LitCompatibleCosmosChains.cheqdTestnet: + return CheqdNetwork.Testnet; + default: + throw new Error( + `[did-provider-cheqd]: observe: Unsupported chain: ${args.unifiedAccessControlCondition.chain}` + ); + } + })(); + + // get block height url + const blockHeightUrl = (function () { + switch (args.unifiedAccessControlCondition.parameters[0]) { + case 'latest': + return `${DefaultRESTUrls[network]}/cosmos/base/tendermint/v1beta1/blocks/latest`; + default: + return `${DefaultRESTUrls[network]}/cosmos/base/tendermint/v1beta1/blocks/${args.unifiedAccessControlCondition.parameters[0]}`; + } + })(); + + // fetch block response + const blockHeightResponse = (await (await fetch(blockHeightUrl)).json()) as BlockResponse; + + // get timestamp from block response + const blockTimestamp = Date.parse(blockHeightResponse.block.header.time); + + // construct url + const url = `${DefaultRESTUrls[network]}${args.unifiedAccessControlCondition.path}`; + + // fetch relevant txs + const txs = (await (await fetch(url)).json()) as ShallowTypedTxsResponse; + + // skim through txs for relevant events, in which case the transaction timestamp is within the defined interval in seconds, from the block timestamp + const meetsConditionTxIndex = txs?.tx_responses?.findIndex((tx) => { + // get tx timestamp + const txTimestamp = Date.parse(tx.timestamp); + + // calculate diff in seconds + const diffInSeconds = Math.floor((blockTimestamp - txTimestamp) / 1000); + + // return meets condition + switch (args.unifiedAccessControlCondition!.returnValueTest.comparator) { + case '<': + return diffInSeconds < parseInt(args.unifiedAccessControlCondition!.returnValueTest.value); + case '<=': + return diffInSeconds <= parseInt(args.unifiedAccessControlCondition!.returnValueTest.value); + default: + throw new Error( + `[did-provider-cheqd]: observe: Unsupported comparator: ${ + args.unifiedAccessControlCondition!.returnValueTest.comparator + }` + ); + } + }); + + // define meetsCondition + const meetsCondition = typeof meetsConditionTxIndex !== 'undefined' && meetsConditionTxIndex !== -1; + + // return observation result + return { + subscribed: true, + meetsCondition: meetsCondition, + transactionHash: meetsCondition ? txs!.tx_responses[meetsConditionTxIndex].txhash : undefined, + events: meetsCondition ? txs!.tx_responses[meetsConditionTxIndex].events : undefined, + rawLog: meetsCondition ? txs!.tx_responses[meetsConditionTxIndex].raw_log : undefined, + txResponse: meetsCondition + ? args?.returnTxResponse + ? txs!.tx_responses[meetsConditionTxIndex] + : undefined + : undefined, + } satisfies ObservationResult; + } catch (error) { + // return error + return { + subscribed: false, + meetsCondition: false, + error: error as IError, + } satisfies ObservationResult; + } + } + + // validate access control conditions components - case: recipientAddress + if (!args.recipientAddress) { + throw new Error('[did-provider-cheqd]: observation: recipientAddress is required'); + } + + // validate access control conditions components - case: amount + if (!args.amount || !args.amount.amount || !args.amount.denom || args.amount.denom !== 'ncheq') { + 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`' + ); + } + + // validate access control conditions components - case: intervalInSeconds + if (!args.intervalInSeconds) { + throw new Error('[did-provider-cheqd]: observation: intervalInSeconds is required'); + } + + // validate access control conditions components - case: comparator + if (!args.comparator || (args.comparator !== '<' && args.comparator !== '<=')) { + throw new Error('[did-provider-cheqd]: observation: comparator is required and must be either `<` or `<=`'); + } + + // validate access control conditions components - case: network + if (!args.network) { + throw new Error('[did-provider-cheqd]: observation: network is required'); + } + + // define block height, if not provided + args.blockHeight ||= 'latest'; + + try { + // get block height url + const blockHeightUrl = (function () { + switch (args.blockHeight) { + case 'latest': + return `${DefaultRESTUrls[args.network]}/cosmos/base/tendermint/v1beta1/blocks/latest`; + default: + return `${DefaultRESTUrls[args.network]}/cosmos/base/tendermint/v1beta1/blocks/${ + args.blockHeight + }`; + } + })(); + + // fetch block response + const blockHeightResponse = (await (await fetch(blockHeightUrl)).json()) as BlockResponse; + + // get timestamp from block response + const blockTimestamp = Date.parse(blockHeightResponse.block.header.time); + + // otherwise, construct url, as per components + 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`; + + // fetch relevant txs + const txs = (await (await fetch(url)).json()) as ShallowTypedTxsResponse; + + // skim through txs for relevant events, in which case the transaction timestamp is within the defined interval in seconds, from the block timestamp + const meetsConditionTxIndex = txs?.tx_responses?.findIndex((tx) => { + // get tx timestamp + const txTimestamp = Date.parse(tx.timestamp); + + // calculate diff in seconds + const diffInSeconds = Math.floor((blockTimestamp - txTimestamp) / 1000); + + // return meets condition + switch (args.comparator) { + case '<': + return diffInSeconds < args.intervalInSeconds!; + case '<=': + return diffInSeconds <= args.intervalInSeconds!; + default: + throw new Error( + `[did-provider-cheqd]: observe: Unsupported comparator: ${ + args.unifiedAccessControlCondition!.returnValueTest.comparator + }` + ); + } + }); + + // define meetsCondition + const meetsCondition = typeof meetsConditionTxIndex !== 'undefined' && meetsConditionTxIndex !== -1; + + // return observation result + return { + subscribed: true, + meetsCondition: meetsCondition, + transactionHash: meetsCondition ? txs!.tx_responses[meetsConditionTxIndex].txhash : undefined, + events: meetsCondition ? txs!.tx_responses[meetsConditionTxIndex].events : undefined, + rawLog: meetsCondition ? txs!.tx_responses[meetsConditionTxIndex].raw_log : undefined, + txResponse: meetsCondition + ? args?.returnTxResponse + ? txs!.tx_responses[meetsConditionTxIndex] + : undefined + : undefined, + } satisfies ObservationResult; + } catch (error) { + // return error + return { + subscribed: false, + meetsCondition: false, + error: error as IError, + } satisfies ObservationResult; + } + } + + static async revokeCredential( + credential: VerifiableCredential, + options?: ICheqdStatusList2021Options + ): Promise { + try { + // validate status purpose + if (credential?.credentialStatus?.statusPurpose !== 'revocation') + throw new Error('[did-provider-cheqd]: revocation: Invalid status purpose'); + + // fetch status list 2021 + const publishedList = (await Cheqd.fetchStatusList2021(credential)) as StatusList2021Revocation; + + // early return, if encrypted and no decryption key provided + if (publishedList.metadata.encrypted && !options?.topArgs?.symmetricKey) + throw new Error( + '[did-provider-cheqd]: revocation: symmetricKey is required, if status list 2021 is encrypted' + ); + + // fetch status list 2021 inscribed in credential + const statusList2021 = options?.topArgs?.fetchList + ? await (async function () { + // if not encrypted, return bitstring + if (!publishedList.metadata.encrypted) + return publishedList.metadata.encoding === 'base64url' + ? publishedList.StatusList2021.encodedList + : toString( + fromString( + publishedList.StatusList2021.encodedList, + publishedList.metadata.encoding as DefaultStatusList2021Encoding + ), + 'base64url' + ); + + // otherwise, decrypt and return raw bitstring + const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex')); + + // decrypt + return await LitProtocol.decryptDirect( + scopedRawBlob, + fromString(options?.topArgs?.symmetricKey, 'hex') + ); + })() + : await (async function () { + // transcode to base64url, if needed + const publishedListTranscoded = + publishedList.metadata.encoding === 'base64url' + ? publishedList.StatusList2021.encodedList + : toString( + fromString( + publishedList.StatusList2021.encodedList, + publishedList.metadata.encoding as DefaultStatusList2021Encoding + ), + 'base64url' + ); + + // if status list 2021 is not fetched, read from file + if (options?.statusListFile) { + // if not encrypted, return bitstring + if (!publishedList.metadata.encrypted) { + // construct encoded status list + const encoded = new StatusList({ + buffer: await Cheqd.getFile(options.statusListFile), + }).encode() as Bitstring; + + // validate against published list + if (encoded !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: revocation: statusListFile does not match published status list 2021' + ); + + // return encoded + return encoded; + } + + // otherwise, decrypt and return bitstring + const scopedRawBlob = await toBlob(await Cheqd.getFile(options.statusListFile)); + + // decrypt + const decrypted = await LitProtocol.decryptDirect( + scopedRawBlob, + fromString(options?.topArgs?.symmetricKey, 'hex') + ); + + // validate against published list + if (decrypted !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: revocation: statusListFile does not match published status list 2021' + ); + + // return decrypted + return decrypted; + } + + if (!options?.statusListInlineBitstring) + throw new Error( + '[did-provider-cheqd]: revocation: statusListInlineBitstring is required, if statusListFile is not provided' + ); + + // validate against published list + if (options?.statusListInlineBitstring !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: revocation: statusListInlineBitstring does not match published status list 2021' + ); + + // otherwise, read from inline bitstring + return options?.statusListInlineBitstring; + })(); + + // parse status list 2021 + const statusList = await StatusList.decode({ encodedList: statusList2021 }); + + // early exit, if credential is already revoked + if (statusList.getStatus(Number(credential.credentialStatus.statusListIndex))) return { revoked: false }; + + // update revocation status + statusList.setStatus(Number(credential.credentialStatus.statusListIndex), true); + + // set in-memory status list ref + const bitstring = (await statusList.encode()) as Bitstring; + + // cast top-level args + const topArgs = options?.topArgs as ICheqdRevokeCredentialWithStatusList2021Args; + + // write status list 2021 to file, if provided + if (topArgs?.writeToFile) { + await Cheqd.writeFile(fromString(bitstring, 'base64url'), options?.statusListFile); + } + + // publish status list 2021, if provided + const published = topArgs?.publish + ? await (async function () { + // fetch status list 2021 metadata + const statusListMetadata = await Cheqd.fetchStatusList2021Metadata(credential); + + // publish status list 2021 as new version + const scoped = topArgs.publishEncrypted + ? await (async function () { + // validate encoding, if provided + if ( + options?.publishOptions?.statusListEncoding && + !Object.values(DefaultStatusList2021Encodings).includes( + options?.publishOptions?.statusListEncoding + ) + ) { + throw new Error( + '[did-provider-cheqd]: revocation: Invalid status list encoding' + ); + } + + // validate validUntil, if provided + if (options?.publishOptions?.statusListValidUntil) { + // validate validUntil as string + if (typeof options?.publishOptions?.statusListValidUntil !== 'string') + throw new Error( + '[did-provider-cheqd]: revocation: Invalid status list validUntil (must be string)' + ); + + // validate validUntil as date + if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) + throw new Error( + '[did-provider-cheqd]: revocation: Invalid status list validUntil (must be date)' + ); + + // validate validUntil as future date + if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) + throw new Error( + '[did-provider-cheqd]: revocation: Invalid status list validUntil (must be future date)' + ); + + // validate validUntil towards validFrom + if ( + new Date(options?.publishOptions?.statusListValidUntil) <= + new Date(publishedList.StatusList2021.validFrom) + ) + throw new Error( + '[did-provider-cheqd]: revocation: Invalid status list validUntil (must be after validFrom)' + ); + } + + // validate paymentConditions, if provided + if (topArgs?.paymentConditions) { + if ( + !topArgs?.paymentConditions?.every( + (condition) => + condition.feePaymentAddress && + condition.feePaymentAmount && + condition.intervalInSeconds + ) + ) { + throw new Error( + '[did-provider-cheqd]: paymentConditions must contain feePaymentAddress and feeAmount and intervalInSeconds' + ); + } + + if ( + !topArgs?.paymentConditions?.every( + (condition) => + typeof condition.feePaymentAddress === 'string' && + typeof condition.feePaymentAmount === 'string' && + typeof condition.intervalInSeconds === 'number' + ) + ) { + throw new Error( + '[did-provider-cheqd]: feePaymentAddress and feePaymentAmount must be string and intervalInSeconds must be number' + ); + } + + if ( + !topArgs?.paymentConditions?.every( + (condition) => + condition.type === AccessControlConditionTypes.timelockPayment + ) + ) { + throw new Error( + '[did-provider-cheqd]: paymentConditions must be of type timelockPayment' + ); + } + } + + // validate dkgOptions + if ( + !topArgs?.dkgOptions || + !topArgs?.dkgOptions?.chain || + !topArgs?.dkgOptions?.network + ) { + throw new Error('[did-provider-cheqd]: dkgOptions is required'); + } + + // instantiate dkg-threshold client, in which case lit-protocol is used + const lit = await LitProtocol.create({ + chain: topArgs?.dkgOptions?.chain, + litNetwork: topArgs?.dkgOptions?.network, + }); + + // construct access control conditions and payment conditions tuple + const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted + ? await (async function () { + // define payment conditions, give precedence to top-level args + const paymentConditions = + topArgs?.paymentConditions || + publishedList.metadata.paymentConditions!; + + // return access control conditions and payment conditions tuple + return [ + await Promise.all( + paymentConditions.map(async (condition) => { + switch (condition.type) { + case AccessControlConditionTypes.timelockPayment: + return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock( + { + key: '$.tx_responses.*.timestamp', + comparator: '<=', + value: `${condition.intervalInSeconds}`, + }, + condition.feePaymentAmount, + condition.feePaymentAddress, + condition?.blockHeight, + topArgs?.dkgOptions?.chain + ); + default: + throw new Error( + `[did-provider-cheqd]: unsupported access control condition type ${condition.type}` + ); + } + }) + ), + paymentConditions, + ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]]; + })() + : await (async function () { + // validate paymentConditions + if (!topArgs?.paymentConditions) { + throw new Error( + '[did-provider-cheqd]: paymentConditions is required' + ); + } + + // return access control conditions and payment conditions tuple + return [ + await Promise.all( + topArgs.paymentConditions.map(async (condition) => { + switch (condition.type) { + case AccessControlConditionTypes.timelockPayment: + return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock( + { + key: '$.tx_responses.*.timestamp', + comparator: '<=', + value: `${condition.intervalInSeconds}`, + }, + condition.feePaymentAmount, + condition.feePaymentAddress, + condition?.blockHeight + ); + default: + throw new Error( + `[did-provider-cheqd]: unsupported access control condition type ${condition.type}` + ); + } + }) + ), + topArgs.paymentConditions, + ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]]; + })(); + + // encrypt bitstring + const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt( + bitstring, + unifiedAccessControlConditionsTuple[0], + true + ); + + // define status list content + const content = { + StatusList2021: { + statusPurpose: publishedList.StatusList2021.statusPurpose, + encodedList: await blobToHexString(encryptedString), + validFrom: publishedList.StatusList2021.validFrom, + validUntil: + options?.publishOptions?.statusListValidUntil || + publishedList.StatusList2021.validUntil, + }, + metadata: { + type: publishedList.metadata.type, + encrypted: true, + encoding: + (options?.publishOptions?.statusListEncoding as + | DefaultStatusList2021Encoding + | undefined) || publishedList.metadata.encoding, + encryptedSymmetricKey, + paymentConditions: unifiedAccessControlConditionsTuple[1], + }, + } satisfies StatusList2021Revocation; + + // return tuple of publish result and encryption relevant metadata + return [ + await Cheqd.publishStatusList2021( + fromString(JSON.stringify(content), 'utf-8'), + statusListMetadata, + options?.publishOptions + ), + { + encryptedString, + encryptedSymmetricKey, + symmetricKey: toString(symmetricKey!, 'hex'), + }, + ]; + })() + : await (async function () { + // validate encoding, if provided + if ( + options?.publishOptions?.statusListEncoding && + !Object.values(DefaultStatusList2021Encodings).includes( + options?.publishOptions?.statusListEncoding + ) + ) { + throw new Error( + '[did-provider-cheqd]: revocation: Invalid status list encoding' + ); + } + + // validate validUntil, if provided + if (options?.publishOptions?.statusListValidUntil) { + // validate validUntil as string + if (typeof options?.publishOptions?.statusListValidUntil !== 'string') + throw new Error( + '[did-provider-cheqd]: revocation: Invalid status list validUntil (must be string)' + ); + + // validate validUntil as date + if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) + throw new Error( + '[did-provider-cheqd]: revocation: Invalid status list validUntil (must be date)' + ); + + // validate validUntil as future date + if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) + throw new Error( + '[did-provider-cheqd]: revocation: Invalid status list validUntil (must be future date)' + ); + + // validate validUntil towards validFrom + if ( + new Date(options?.publishOptions?.statusListValidUntil) <= + new Date(publishedList.StatusList2021.validFrom) + ) + throw new Error( + '[did-provider-cheqd]: revocation: Invalid status list validUntil (must be after validFrom)' + ); + } + + // define status list content + const content = { + StatusList2021: { + statusPurpose: publishedList.StatusList2021.statusPurpose, + encodedList: + publishedList.metadata.encoding === 'base64url' + ? bitstring + : toString( + fromString(bitstring, 'base64url'), + options!.publishOptions + .statusListEncoding as DefaultStatusList2021Encoding + ), + validFrom: publishedList.StatusList2021.validFrom, + validUntil: + options?.publishOptions?.statusListValidUntil || + publishedList.StatusList2021.validUntil, + }, + metadata: { + type: publishedList.metadata.type, + encoding: + (options?.publishOptions?.statusListEncoding as + | DefaultStatusList2021Encoding + | undefined) || publishedList.metadata.encoding, + encrypted: false, + }, + } satisfies StatusList2021Revocation; + + // return tuple of publish result and encryption relevant metadata + return [ + await Cheqd.publishStatusList2021( + fromString(JSON.stringify(content), 'utf-8'), + statusListMetadata, + options?.publishOptions + ), + undefined, + ]; + })(); + + // early exit, if publish failed + if (!scoped[0]) + throw new Error('[did-provider-cheqd]: revocation: Failed to publish status list 2021'); + + // return publish result + return scoped; + })() + : undefined; + + return { + revoked: true, + published: topArgs?.publish ? true : undefined, + statusList: topArgs?.returnUpdatedStatusList + ? ((await Cheqd.fetchStatusList2021(credential)) as StatusList2021Revocation) + : undefined, + symmetricKey: topArgs?.returnSymmetricKey + ? (published?.[1] as { symmetricKey: string })?.symmetricKey + : undefined, + resourceMetadata: topArgs?.returnStatusListMetadata + ? await Cheqd.fetchStatusList2021Metadata(credential) + : undefined, + } satisfies RevocationResult; + } catch (error) { + // silent fail + early exit + console.error(error); + + return { revoked: false, error: error as IError } satisfies RevocationResult; + } + } + + static async revokeCredentials( + credentials: VerifiableCredential[], + options?: ICheqdStatusList2021Options + ): Promise { + // validate credentials - case: empty + if (!credentials.length || credentials.length === 0) + throw new Error('[did-provider-cheqd]: revocation: No credentials provided'); + + // validate credentials - case: consistent issuer + if ( + credentials + .map((credential) => { + return (credential.issuer as { id: string }).id + ? (credential.issuer as { id: string }).id + : (credential.issuer as string); + }) + .filter((value, _, self) => value && value !== self[0]).length > 0 + ) + throw new Error('[did-provider-cheqd]: revocation: Credentials must be issued by the same issuer'); + + // validate credentials - case: status list index + if ( + credentials + .map((credential) => credential.credentialStatus!.statusListIndex) + .filter((value, index, self) => self.indexOf(value) !== index).length > 0 + ) + throw new Error('[did-provider-cheqd]: revocation: Credentials must have unique status list index'); + + // validate credentials - case: status purpose + if (!credentials.every((credential) => credential.credentialStatus?.statusPurpose === 'revocation')) + throw new Error('[did-provider-cheqd]: revocation: Invalid status purpose'); + + // validate credentials - case: status list id + const remote = credentials[0].credentialStatus?.id + ? (credentials[0].credentialStatus as { id: string }).id.split('#')[0] + : (function () { + throw new Error('[did-provider-cheqd]: revocation: Invalid status list id'); + })(); + + // validate credentials - case: status list id format + if (!RemoteListPattern.test(remote)) + throw new Error( + '[did-provider-cheqd]: revocation: Invalid status list id format: expected: https://../1.0/identifiers/:>?resourceName=&resourceType=' + ); + + if ( + !credentials.every((credential) => { + return (credential.credentialStatus as { id: string }).id.split('#')[0] === remote; + }) + ) + throw new Error('[did-provider-cheqd]: revocation: Credentials must belong to the same status list'); + + // validate credentials - case: status list type + if (!credentials.every((credential) => credential.credentialStatus?.type === 'StatusList2021Entry')) + throw new Error('[did-provider-cheqd]: revocation: Invalid status list type'); + + try { + // fetch status list 2021 + const publishedList = (await Cheqd.fetchStatusList2021(credentials[0])) as StatusList2021Revocation; + + // early return, if encrypted and no decryption key provided + if (publishedList.metadata.encrypted && !options?.topArgs?.symmetricKey) + throw new Error( + '[did-provider-cheqd]: revocation: symmetricKey is required, if status list 2021 is encrypted' + ); + + // fetch status list 2021 inscribed in credential + const statusList2021 = options?.topArgs?.fetchList + ? await (async function () { + // if not encrypted, return bitstring + if (!publishedList.metadata.encrypted) + return publishedList.metadata.encoding === 'base64url' + ? publishedList.StatusList2021.encodedList + : toString( + fromString( + publishedList.StatusList2021.encodedList, + publishedList.metadata.encoding as DefaultStatusList2021Encoding + ), + 'base64url' + ); + + // otherwise, decrypt and return raw bitstring + const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex')); + + // decrypt + return await LitProtocol.decryptDirect( + scopedRawBlob, + fromString(options?.topArgs?.symmetricKey, 'hex') + ); + })() + : await (async function () { + // transcode to base64url, if needed + const publishedListTranscoded = + publishedList.metadata.encoding === 'base64url' + ? publishedList.StatusList2021.encodedList + : toString( + fromString( + publishedList.StatusList2021.encodedList, + publishedList.metadata.encoding as DefaultStatusList2021Encoding + ), + 'base64url' + ); + + // if status list 2021 is not fetched, read from file + if (options?.statusListFile) { + // if not encrypted, return bitstring + if (!publishedList.metadata.encrypted) { + // construct encoded status list + const encoded = new StatusList({ + buffer: await Cheqd.getFile(options.statusListFile), + }).encode() as Bitstring; + + // validate against published list + if (encoded !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: revocation: statusListFile does not match published status list 2021' + ); + + // return encoded + return encoded; + } + + // otherwise, decrypt and return bitstring + const scopedRawBlob = await toBlob(await Cheqd.getFile(options.statusListFile)); + + // decrypt + const decrypted = await LitProtocol.decryptDirect( + scopedRawBlob, + fromString(options?.topArgs?.symmetricKey, 'hex') + ); + + // validate against published list + if (decrypted !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: revocation: statusListFile does not match published status list 2021' + ); + + // return decrypted + return decrypted; + } + + if (!options?.statusListInlineBitstring) + throw new Error( + '[did-provider-cheqd]: revocation: statusListInlineBitstring is required, if statusListFile is not provided' + ); + + // validate against published list + if (options?.statusListInlineBitstring !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: revocation: statusListInlineBitstring does not match published status list 2021' + ); + + // otherwise, read from inline bitstring + return options?.statusListInlineBitstring; + })(); + + // parse status list 2021 + const statusList = await StatusList.decode({ encodedList: statusList2021 }); + + // initiate bulk revocation + const revoked = (await Promise.allSettled( + credentials.map((credential) => { + return (async function () { + // early return, if no credential status + if (!credential.credentialStatus) return { revoked: false }; + + // early exit, if credential is already revoked + if (statusList.getStatus(Number(credential.credentialStatus.statusListIndex))) + return { revoked: false }; + + // update revocation status + statusList.setStatus(Number(credential.credentialStatus.statusListIndex), true); + + // return revocation status + return { revoked: true }; + })(); + }) + )) satisfies PromiseSettledResult[]; + + // revert bulk ops, if some failed + if (revoked.some((result) => result.status === 'fulfilled' && !result.value.revoked)) + throw new Error( + `[did-provider-cheqd]: revocation: Bulk revocation failed: already revoked credentials in revocation bundle: raw log: ${JSON.stringify( + revoked.map((result) => ({ + revoked: result.status === 'fulfilled' ? result.value.revoked : false, + })) + )}` + ); + + // set in-memory status list ref + const bitstring = (await statusList.encode()) as Bitstring; + + // cast top-level args + const topArgs = options?.topArgs as ICheqdRevokeCredentialWithStatusList2021Args; + + // write status list 2021 to file, if provided + if (topArgs?.writeToFile) { + await Cheqd.writeFile(fromString(bitstring, 'base64url'), options?.statusListFile); + } + + // publish status list 2021, if provided + const published = topArgs?.publish + ? await (async function () { + // fetch status list 2021 metadata + const statusListMetadata = await Cheqd.fetchStatusList2021Metadata(credentials[0]); + + // publish status list 2021 as new version + const scoped = topArgs.publishEncrypted + ? await (async function () { + // validate encoding, if provided + if ( + options?.publishOptions?.statusListEncoding && + !Object.values(DefaultStatusList2021Encodings).includes( + options?.publishOptions?.statusListEncoding + ) + ) { + throw new Error( + '[did-provider-cheqd]: revocation: Invalid status list encoding' + ); + } + + // validate validUntil, if provided + if (options?.publishOptions?.statusListValidUntil) { + // validate validUntil as string + if (typeof options?.publishOptions?.statusListValidUntil !== 'string') + throw new Error( + '[did-provider-cheqd]: revocation: Invalid status list validUntil (must be string)' + ); + + // validate validUntil as date + if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) + throw new Error( + '[did-provider-cheqd]: revocation: Invalid status list validUntil (must be date)' + ); + + // validate validUntil as future date + if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) + throw new Error( + '[did-provider-cheqd]: revocation: Invalid status list validUntil (must be future date)' + ); + + // validate validUntil towards validFrom + if ( + new Date(options?.publishOptions?.statusListValidUntil) <= + new Date(publishedList.StatusList2021.validFrom) + ) + throw new Error( + '[did-provider-cheqd]: revocation: Invalid status list validUntil (must be after validFrom)' + ); + } + + // validate paymentConditions, if provided + if (topArgs?.paymentConditions) { + if ( + !topArgs?.paymentConditions?.every( + (condition) => + condition.feePaymentAddress && + condition.feePaymentAmount && + condition.intervalInSeconds + ) + ) { + throw new Error( + '[did-provider-cheqd]: paymentConditions must contain feePaymentAddress and feeAmount and intervalInSeconds' + ); + } + + if ( + !topArgs?.paymentConditions?.every( + (condition) => + typeof condition.feePaymentAddress === 'string' && + typeof condition.feePaymentAmount === 'string' && + typeof condition.intervalInSeconds === 'number' + ) + ) { + throw new Error( + '[did-provider-cheqd]: feePaymentAddress and feePaymentAmount must be string and intervalInSeconds must be number' + ); + } + + if ( + !topArgs?.paymentConditions?.every( + (condition) => + condition.type === AccessControlConditionTypes.timelockPayment + ) + ) { + throw new Error( + '[did-provider-cheqd]: paymentConditions must be of type timelockPayment' + ); + } + } + + // validate dkgOptions + if ( + !topArgs?.dkgOptions || + !topArgs?.dkgOptions?.chain || + !topArgs?.dkgOptions?.network + ) { + throw new Error('[did-provider-cheqd]: dkgOptions is required'); + } + + // instantiate dkg-threshold client, in which case lit-protocol is used + const lit = await LitProtocol.create({ + chain: topArgs?.dkgOptions?.chain, + litNetwork: topArgs?.dkgOptions?.network, + }); + + // construct access control conditions and payment conditions tuple + const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted + ? await (async function () { + // define payment conditions, give precedence to top-level args + const paymentConditions = + topArgs?.paymentConditions || + publishedList.metadata.paymentConditions!; + + // return access control conditions and payment conditions tuple + return [ + await Promise.all( + paymentConditions.map(async (condition) => { + switch (condition.type) { + case AccessControlConditionTypes.timelockPayment: + return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock( + { + key: '$.tx_responses.*.timestamp', + comparator: '<=', + value: `${condition.intervalInSeconds}`, + }, + condition.feePaymentAmount, + condition.feePaymentAddress, + condition?.blockHeight, + topArgs?.dkgOptions?.chain + ); + default: + throw new Error( + `[did-provider-cheqd]: unsupported access control condition type ${condition.type}` + ); + } + }) + ), + paymentConditions, + ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]]; + })() + : await (async function () { + // validate paymentConditions + if (!topArgs?.paymentConditions) { + throw new Error( + '[did-provider-cheqd]: paymentConditions is required' + ); + } + + // return access control conditions and payment conditions tuple + return [ + await Promise.all( + topArgs.paymentConditions.map(async (condition) => { + switch (condition.type) { + case AccessControlConditionTypes.timelockPayment: + return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock( + { + key: '$.tx_responses.*.timestamp', + comparator: '<=', + value: `${condition.intervalInSeconds}`, + }, + condition.feePaymentAmount, + condition.feePaymentAddress, + condition?.blockHeight + ); + default: + throw new Error( + `[did-provider-cheqd]: unsupported access control condition type ${condition.type}` + ); + } + }) + ), + topArgs.paymentConditions, + ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]]; + })(); + + // encrypt bitstring + const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt( + bitstring, + unifiedAccessControlConditionsTuple[0], + true + ); + + // define status list content + const content = { + StatusList2021: { + statusPurpose: publishedList.StatusList2021.statusPurpose, + encodedList: await blobToHexString(encryptedString), + validFrom: publishedList.StatusList2021.validFrom, + validUntil: + options?.publishOptions?.statusListValidUntil || + publishedList.StatusList2021.validUntil, + }, + metadata: { + type: publishedList.metadata.type, + encrypted: true, + encoding: + (options?.publishOptions?.statusListEncoding as + | DefaultStatusList2021Encoding + | undefined) || publishedList.metadata.encoding, + encryptedSymmetricKey, + paymentConditions: unifiedAccessControlConditionsTuple[1], + }, + } satisfies StatusList2021Revocation; + + // return tuple of publish result and encryption relevant metadata + return [ + await Cheqd.publishStatusList2021( + fromString(JSON.stringify(content), 'utf-8'), + statusListMetadata, + options?.publishOptions + ), + { + encryptedString, + encryptedSymmetricKey, + symmetricKey: toString(symmetricKey!, 'hex'), + }, + ]; + })() + : await (async function () { + // validate encoding, if provided + if ( + options?.publishOptions?.statusListEncoding && + !Object.values(DefaultStatusList2021Encodings).includes( + options?.publishOptions?.statusListEncoding + ) + ) { + throw new Error( + '[did-provider-cheqd]: revocation: Invalid status list encoding' + ); + } + + // validate validUntil, if provided + if (options?.publishOptions?.statusListValidUntil) { + // validate validUntil as string + if (typeof options?.publishOptions?.statusListValidUntil !== 'string') + throw new Error( + '[did-provider-cheqd]: revocation: Invalid status list validUntil (must be string)' + ); + + // validate validUntil as date + if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) + throw new Error( + '[did-provider-cheqd]: revocation: Invalid status list validUntil (must be date)' + ); + + // validate validUntil as future date + if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) + throw new Error( + '[did-provider-cheqd]: revocation: Invalid status list validUntil (must be future date)' + ); + + // validate validUntil towards validFrom + if ( + new Date(options?.publishOptions?.statusListValidUntil) <= + new Date(publishedList.StatusList2021.validFrom) + ) + throw new Error( + '[did-provider-cheqd]: revocation: Invalid status list validUntil (must be after validFrom)' + ); + } + + // define status list content + const content = { + StatusList2021: { + statusPurpose: publishedList.StatusList2021.statusPurpose, + encodedList: + publishedList.metadata.encoding === 'base64url' + ? bitstring + : toString( + fromString(bitstring, 'base64url'), + options!.publishOptions + .statusListEncoding as DefaultStatusList2021Encoding + ), + validFrom: publishedList.StatusList2021.validFrom, + validUntil: + options?.publishOptions?.statusListValidUntil || + publishedList.StatusList2021.validUntil, + }, + metadata: { + type: publishedList.metadata.type, + encoding: + (options?.publishOptions?.statusListEncoding as + | DefaultStatusList2021Encoding + | undefined) || publishedList.metadata.encoding, + encrypted: false, + }, + } satisfies StatusList2021Revocation; + + // return tuple of publish result and encryption relevant metadata + return [ + await Cheqd.publishStatusList2021( + fromString(JSON.stringify(content), 'utf-8'), + statusListMetadata, + options?.publishOptions + ), + undefined, + ]; + })(); + + // early exit, if publish failed + if (!scoped[0]) + throw new Error('[did-provider-cheqd]: revocation: Failed to publish status list 2021'); + + // return publish result + return scoped; + })() + : undefined; + + return { + revoked: revoked.map((result) => (result.status === 'fulfilled' ? result.value.revoked : false)), + published: topArgs?.publish ? true : undefined, + statusList: topArgs?.returnUpdatedStatusList + ? ((await Cheqd.fetchStatusList2021(credentials[0])) as StatusList2021Revocation) + : undefined, + symmetricKey: topArgs?.returnSymmetricKey + ? (published?.[1] as { symmetricKey: string })?.symmetricKey + : undefined, + resourceMetadata: topArgs?.returnStatusListMetadata + ? await Cheqd.fetchStatusList2021Metadata(credentials[0]) + : undefined, + } satisfies BulkRevocationResult; + } catch (error) { + // silent fail + early exit + console.error(error); + + return { revoked: [], error: error as IError } satisfies BulkRevocationResult; + } + } + + static async suspendCredential( + credential: VerifiableCredential, + options?: ICheqdStatusList2021Options + ): Promise { + try { + // validate status purpose + if (credential?.credentialStatus?.statusPurpose !== 'suspension') + throw new Error('[did-provider-cheqd]: suspension: Invalid status purpose'); + + // fetch status list 2021 + const publishedList = (await Cheqd.fetchStatusList2021(credential)) as StatusList2021Suspension; + + // early return, if encrypted and no decryption key provided + if (publishedList.metadata.encrypted && !options?.topArgs?.symmetricKey) + throw new Error( + '[did-provider-cheqd]: suspension: symmetricKey is required, if status list 2021 is encrypted' + ); + + // fetch status list 2021 inscribed in credential + const statusList2021 = options?.topArgs?.fetchList + ? await (async function () { + // if not encrypted, return bitstring + if (!publishedList.metadata.encrypted) + return publishedList.metadata.encoding === 'base64url' + ? publishedList.StatusList2021.encodedList + : toString( + fromString( + publishedList.StatusList2021.encodedList, + publishedList.metadata.encoding as DefaultStatusList2021Encoding + ), + 'base64url' + ); + + // otherwise, decrypt and return raw bitstring + const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex')); + + // decrypt + return await LitProtocol.decryptDirect( + scopedRawBlob, + fromString(options?.topArgs?.symmetricKey, 'hex') + ); + })() + : await (async function () { + // transcode to base64url, if needed + const publishedListTranscoded = + publishedList.metadata.encoding === 'base64url' + ? publishedList.StatusList2021.encodedList + : toString( + fromString( + publishedList.StatusList2021.encodedList, + publishedList.metadata.encoding as DefaultStatusList2021Encoding + ), + 'base64url' + ); + + // if status list 2021 is not fetched, read from file + if (options?.statusListFile) { + // if not encrypted, return bitstring + if (!publishedList.metadata.encrypted) { + // construct encoded status list + const encoded = new StatusList({ + buffer: await Cheqd.getFile(options.statusListFile), + }).encode() as Bitstring; + + // validate against published list + if (encoded !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: suspension: statusListFile does not match published status list 2021' + ); + + // return encoded + return encoded; + } + + // otherwise, decrypt and return bitstring + const scopedRawBlob = await toBlob(await Cheqd.getFile(options.statusListFile)); + + // decrypt + const decrypted = await LitProtocol.decryptDirect( + scopedRawBlob, + fromString(options?.topArgs?.symmetricKey, 'hex') + ); + + // validate against published list + if (decrypted !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: suspension: statusListFile does not match published status list 2021' + ); + + // return decrypted + return decrypted; + } + + if (!options?.statusListInlineBitstring) + throw new Error( + '[did-provider-cheqd]: suspension: statusListInlineBitstring is required, if statusListFile is not provided' + ); + + // validate against published list + if (options?.statusListInlineBitstring !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: suspension: statusListInlineBitstring does not match published status list 2021' + ); + + // otherwise, read from inline bitstring + return options?.statusListInlineBitstring; + })(); + + // parse status list 2021 + const statusList = await StatusList.decode({ encodedList: statusList2021 }); + + // early exit, if already suspended + if (statusList.getStatus(Number(credential.credentialStatus.statusListIndex))) + return { suspended: true } satisfies SuspensionResult; + + // update suspension status + statusList.setStatus(Number(credential.credentialStatus.statusListIndex), true); + + // set in-memory status list ref + const bitstring = (await statusList.encode()) as Bitstring; + + // cast top-level args + const topArgs = options?.topArgs as ICheqdSuspendCredentialWithStatusList2021Args; + + // write status list 2021 to file, if provided + if (topArgs?.writeToFile) { + await Cheqd.writeFile(fromString(bitstring, 'base64url'), options?.statusListFile); + } + + // publish status list 2021, if provided + const published = topArgs?.publish + ? await (async function () { + // fetch status list 2021 metadata + const statusListMetadata = await Cheqd.fetchStatusList2021Metadata(credential); + + // publish status list 2021 as new version + const scoped = topArgs.publishEncrypted + ? await (async function () { + // validate encoding, if provided + if ( + options?.publishOptions?.statusListEncoding && + !Object.values(DefaultStatusList2021Encodings).includes( + options?.publishOptions?.statusListEncoding + ) + ) { + throw new Error( + '[did-provider-cheqd]: suspension: Invalid status list encoding' + ); + } + + // validate validUntil, if provided + if (options?.publishOptions?.statusListValidUntil) { + // validate validUntil as string + if (typeof options?.publishOptions?.statusListValidUntil !== 'string') + throw new Error( + '[did-provider-cheqd]: suspension: Invalid status list validUntil (must be string)' + ); + + // validate validUntil as date + if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) + throw new Error( + '[did-provider-cheqd]: suspension: Invalid status list validUntil (must be date)' + ); + + // validate validUntil as future date + if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) + throw new Error( + '[did-provider-cheqd]: suspension: Invalid status list validUntil (must be future date)' + ); + + // validate validUntil towards validFrom + if ( + new Date(options?.publishOptions?.statusListValidUntil) <= + new Date(publishedList.StatusList2021.validFrom) + ) + throw new Error( + '[did-provider-cheqd]: suspension: Invalid status list validUntil (must be after validFrom)' + ); + } + + // validate paymentConditions, if provided + if (topArgs?.paymentConditions) { + if ( + !topArgs?.paymentConditions?.every( + (condition) => + condition.feePaymentAddress && + condition.feePaymentAmount && + condition.intervalInSeconds + ) + ) { + throw new Error( + '[did-provider-cheqd]: paymentConditions must contain feePaymentAddress and feeAmount and intervalInSeconds' + ); + } + + if ( + !topArgs?.paymentConditions?.every( + (condition) => + typeof condition.feePaymentAddress === 'string' && + typeof condition.feePaymentAmount === 'string' && + typeof condition.intervalInSeconds === 'number' + ) + ) { + throw new Error( + '[did-provider-cheqd]: feePaymentAddress and feePaymentAmount must be string and intervalInSeconds must be number' + ); + } + + if ( + !topArgs?.paymentConditions?.every( + (condition) => + condition.type === AccessControlConditionTypes.timelockPayment + ) + ) { + throw new Error( + '[did-provider-cheqd]: paymentConditions must be of type timelockPayment' + ); + } + } + + // validate dkgOptions + if ( + !topArgs?.dkgOptions || + !topArgs?.dkgOptions?.chain || + !topArgs?.dkgOptions?.network + ) { + throw new Error('[did-provider-cheqd]: dkgOptions is required'); + } + + // instantiate dkg-threshold client, in which case lit-protocol is used + const lit = await LitProtocol.create({ + chain: topArgs?.dkgOptions?.chain, + litNetwork: topArgs?.dkgOptions?.network, + }); + + // construct access control conditions and payment conditions tuple + const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted + ? await (async function () { + // define payment conditions, give precedence to top-level args + const paymentConditions = + topArgs?.paymentConditions || + publishedList.metadata.paymentConditions!; + + // return access control conditions and payment conditions tuple + return [ + await Promise.all( + paymentConditions.map(async (condition) => { + switch (condition.type) { + case AccessControlConditionTypes.timelockPayment: + return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock( + { + key: '$.tx_responses.*.timestamp', + comparator: '<=', + value: `${condition.intervalInSeconds}`, + }, + condition.feePaymentAmount, + condition.feePaymentAddress, + condition?.blockHeight, + topArgs?.dkgOptions?.chain + ); + default: + throw new Error( + `[did-provider-cheqd]: unsupported access control condition type ${condition.type}` + ); + } + }) + ), + paymentConditions, + ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]]; + })() + : await (async function () { + // validate paymentConditions + if (!topArgs?.paymentConditions) { + throw new Error( + '[did-provider-cheqd]: paymentConditions is required' + ); + } + + // return access control conditions and payment conditions tuple + return [ + await Promise.all( + topArgs.paymentConditions.map(async (condition) => { + switch (condition.type) { + case AccessControlConditionTypes.timelockPayment: + return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock( + { + key: '$.tx_responses.*.timestamp', + comparator: '<=', + value: `${condition.intervalInSeconds}`, + }, + condition.feePaymentAmount, + condition.feePaymentAddress, + condition?.blockHeight + ); + default: + throw new Error( + `[did-provider-cheqd]: unsupported access control condition type ${condition.type}` + ); + } + }) + ), + topArgs.paymentConditions, + ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]]; + })(); + + // encrypt bitstring + const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt( + bitstring, + unifiedAccessControlConditionsTuple[0], + true + ); + + // define status list content + const content = { + StatusList2021: { + statusPurpose: publishedList.StatusList2021.statusPurpose, + encodedList: await blobToHexString(encryptedString), + validFrom: publishedList.StatusList2021.validFrom, + validUntil: + options?.publishOptions?.statusListValidUntil || + publishedList.StatusList2021.validUntil, + }, + metadata: { + type: publishedList.metadata.type, + encrypted: true, + encoding: + (options?.publishOptions?.statusListEncoding as + | DefaultStatusList2021Encoding + | undefined) || publishedList.metadata.encoding, + encryptedSymmetricKey, + paymentConditions: unifiedAccessControlConditionsTuple[1], + }, + } satisfies StatusList2021Suspension; + + // return tuple of publish result and encryption relevant metadata + return [ + await Cheqd.publishStatusList2021( + fromString(JSON.stringify(content), 'utf-8'), + statusListMetadata, + options?.publishOptions + ), + { + encryptedString, + encryptedSymmetricKey, + symmetricKey: toString(symmetricKey!, 'hex'), + }, + ]; + })() + : await (async function () { + // validate encoding, if provided + if ( + options?.publishOptions?.statusListEncoding && + !Object.values(DefaultStatusList2021Encodings).includes( + options?.publishOptions?.statusListEncoding + ) + ) { + throw new Error( + '[did-provider-cheqd]: suspension: Invalid status list encoding' + ); + } + + // validate validUntil, if provided + if (options?.publishOptions?.statusListValidUntil) { + // validate validUntil as string + if (typeof options?.publishOptions?.statusListValidUntil !== 'string') + throw new Error( + '[did-provider-cheqd]: suspension: Invalid status list validUntil (must be string)' + ); + + // validate validUntil as date + if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) + throw new Error( + '[did-provider-cheqd]: suspension: Invalid status list validUntil (must be date)' + ); + + // validate validUntil as future date + if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) + throw new Error( + '[did-provider-cheqd]: suspension: Invalid status list validUntil (must be future date)' + ); + + // validate validUntil towards validFrom + if ( + new Date(options?.publishOptions?.statusListValidUntil) <= + new Date(publishedList.StatusList2021.validFrom) + ) + throw new Error( + '[did-provider-cheqd]: suspension: Invalid status list validUntil (must be after validFrom)' + ); + } + + // define status list content + const content = { + StatusList2021: { + statusPurpose: publishedList.StatusList2021.statusPurpose, + encodedList: + publishedList.metadata.encoding === 'base64url' + ? bitstring + : toString( + fromString(bitstring, 'base64url'), + options!.publishOptions + .statusListEncoding as DefaultStatusList2021Encoding + ), + validFrom: publishedList.StatusList2021.validFrom, + validUntil: + options?.publishOptions?.statusListValidUntil || + publishedList.StatusList2021.validUntil, + }, + metadata: { + type: publishedList.metadata.type, + encoding: + (options?.publishOptions?.statusListEncoding as + | DefaultStatusList2021Encoding + | undefined) || publishedList.metadata.encoding, + encrypted: false, + }, + } satisfies StatusList2021Suspension; + + // return tuple of publish result and encryption relevant metadata + return [ + await Cheqd.publishStatusList2021( + fromString(JSON.stringify(content), 'utf-8'), + statusListMetadata, + options?.publishOptions + ), + undefined, + ]; + })(); + + // early exit, if publish failed + if (!scoped[0]) + throw new Error('[did-provider-cheqd]: suspension: Failed to publish status list 2021'); + + // return publish result + return scoped; + })() + : undefined; + + return { + suspended: true, + published: topArgs?.publish ? true : undefined, + statusList: topArgs?.returnUpdatedStatusList + ? ((await Cheqd.fetchStatusList2021(credential)) as StatusList2021Suspension) + : undefined, + symmetricKey: topArgs?.returnSymmetricKey + ? (published?.[1] as { symmetricKey: string })?.symmetricKey + : undefined, + resourceMetadata: topArgs?.returnStatusListMetadata + ? await Cheqd.fetchStatusList2021Metadata(credential) + : undefined, + } satisfies SuspensionResult; + } catch (error) { + // silent fail + early exit + console.error(error); + + return { suspended: false, error: error as IError } satisfies SuspensionResult; + } + } + + static async suspendCredentials( + credentials: VerifiableCredential[], + options?: ICheqdStatusList2021Options + ): Promise { + // validate credentials - case: empty + if (!credentials.length || credentials.length === 0) + throw new Error('[did-provider-cheqd]: suspension: No credentials provided'); + + // validate credentials - case: consistent issuer + if ( + credentials + .map((credential) => { + return (credential.issuer as { id: string }).id + ? (credential.issuer as { id: string }).id + : (credential.issuer as string); + }) + .filter((value, _, self) => value && value !== self[0]).length > 0 + ) + throw new Error('[did-provider-cheqd]: suspension: Credentials must be issued by the same issuer'); + + // validate credentials - case: status list index + if ( + credentials + .map((credential) => credential.credentialStatus!.statusListIndex) + .filter((value, index, self) => self.indexOf(value) !== index).length > 0 + ) + throw new Error('[did-provider-cheqd]: suspension: Credentials must have unique status list index'); + + // validate credentials - case: status purpose + if (!credentials.every((credential) => credential.credentialStatus?.statusPurpose === 'suspension')) + throw new Error('[did-provider-cheqd]: suspension: Invalid status purpose'); + + // validate credentials - case: status list id + const remote = credentials[0].credentialStatus?.id + ? (credentials[0].credentialStatus as { id: string }).id.split('#')[0] + : (function () { + throw new Error('[did-provider-cheqd]: suspension: Invalid status list id'); + })(); + + // validate credentials - case: status list id format + if (!RemoteListPattern.test(remote)) + throw new Error( + '[did-provider-cheqd]: suspension: Invalid status list id format: expected: https://../1.0/identifiers/:>?resourceName=&resourceType=' + ); + + if ( + !credentials.every((credential) => { + return (credential.credentialStatus as { id: string }).id.split('#')[0] === remote; + }) + ) + throw new Error('[did-provider-cheqd]: suspension: Credentials must belong to the same status list'); + + // validate credentials - case: status list type + if (!credentials.every((credential) => credential.credentialStatus?.type === 'StatusList2021Entry')) + throw new Error('[did-provider-cheqd]: suspension: Invalid status list type'); + + try { + // fetch status list 2021 + const publishedList = (await Cheqd.fetchStatusList2021(credentials[0])) as StatusList2021Suspension; + + // early return, if encrypted and no decryption key provided + if (publishedList.metadata.encrypted && !options?.topArgs?.symmetricKey) + throw new Error( + '[did-provider-cheqd]: suspension: symmetricKey is required, if status list 2021 is encrypted' + ); + + // fetch status list 2021 inscribed in credential + const statusList2021 = options?.topArgs?.fetchList + ? await (async function () { + // if not encrypted, return bitstring + if (!publishedList.metadata.encrypted) + return publishedList.metadata.encoding === 'base64url' + ? publishedList.StatusList2021.encodedList + : toString( + fromString( + publishedList.StatusList2021.encodedList, + publishedList.metadata.encoding as DefaultStatusList2021Encoding + ), + 'base64url' + ); + + // otherwise, decrypt and return raw bitstring + const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex')); + + // decrypt + return await LitProtocol.decryptDirect( + scopedRawBlob, + fromString(options?.topArgs?.symmetricKey, 'hex') + ); + })() + : await (async function () { + // transcode to base64url, if needed + const publishedListTranscoded = + publishedList.metadata.encoding === 'base64url' + ? publishedList.StatusList2021.encodedList + : toString( + fromString( + publishedList.StatusList2021.encodedList, + publishedList.metadata.encoding as DefaultStatusList2021Encoding + ), + 'base64url' + ); + + // if status list 2021 is not fetched, read from file + if (options?.statusListFile) { + // if not encrypted, return bitstring + if (!publishedList.metadata.encrypted) { + // construct encoded status list + const encoded = new StatusList({ + buffer: await Cheqd.getFile(options.statusListFile), + }).encode() as Bitstring; + + // validate against published list + if (encoded !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: suspension: statusListFile does not match published status list 2021' + ); + + // return encoded + return encoded; + } + + // otherwise, decrypt and return bitstring + const scopedRawBlob = await toBlob(await Cheqd.getFile(options.statusListFile)); + + // decrypt + const decrypted = await LitProtocol.decryptDirect( + scopedRawBlob, + fromString(options?.topArgs?.symmetricKey, 'hex') + ); + + // validate against published list + if (decrypted !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: suspension: statusListFile does not match published status list 2021' + ); + + // return decrypted + return decrypted; + } + + if (!options?.statusListInlineBitstring) + throw new Error( + '[did-provider-cheqd]: suspension: statusListInlineBitstring is required, if statusListFile is not provided' + ); + + // validate against published list + if (options?.statusListInlineBitstring !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: suspension: statusListInlineBitstring does not match published status list 2021' + ); + + // otherwise, read from inline bitstring + return options?.statusListInlineBitstring; + })(); + + // parse status list 2021 + const statusList = await StatusList.decode({ encodedList: statusList2021 }); + + // initiate bulk suspension + const suspended = (await Promise.allSettled( + credentials.map((credential) => { + return (async function () { + // early return, if no credential status + if (!credential.credentialStatus) return { suspended: false }; + + // early exit, if credential is already suspended + if (statusList.getStatus(Number(credential.credentialStatus.statusListIndex))) + return { suspended: false }; + + // update suspension status + statusList.setStatus(Number(credential.credentialStatus.statusListIndex), true); + + // return suspension status + return { suspended: true }; + })(); + }) + )) satisfies PromiseSettledResult[]; + + // revert bulk ops, if some failed + if (suspended.some((result) => result.status === 'fulfilled' && !result.value.suspended)) + throw new Error( + `[did-provider-cheqd]: suspension: Bulk suspension failed: already suspended credentials in suspension bundle: raw log: ${JSON.stringify( + suspended.map((result) => ({ + suspended: result.status === 'fulfilled' ? result.value.suspended : false, + })) + )}` + ); + + // set in-memory status list ref + const bitstring = (await statusList.encode()) as Bitstring; + + // cast top-level args + const topArgs = options?.topArgs as ICheqdRevokeCredentialWithStatusList2021Args; + + // write status list 2021 to file, if provided + if (topArgs?.writeToFile) { + await Cheqd.writeFile(fromString(bitstring, 'base64url'), options?.statusListFile); + } + + // publish status list 2021, if provided + const published = topArgs?.publish + ? await (async function () { + // fetch status list 2021 metadata + const statusListMetadata = await Cheqd.fetchStatusList2021Metadata(credentials[0]); + + // publish status list 2021 as new version + const scoped = topArgs.publishEncrypted + ? await (async function () { + // validate encoding, if provided + if ( + options?.publishOptions?.statusListEncoding && + !Object.values(DefaultStatusList2021Encodings).includes( + options?.publishOptions?.statusListEncoding + ) + ) { + throw new Error( + '[did-provider-cheqd]: suspension: Invalid status list encoding' + ); + } + + // validate validUntil, if provided + if (options?.publishOptions?.statusListValidUntil) { + // validate validUntil as string + if (typeof options?.publishOptions?.statusListValidUntil !== 'string') + throw new Error( + '[did-provider-cheqd]: suspension: Invalid status list validUntil (must be string)' + ); + + // validate validUntil as date + if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) + throw new Error( + '[did-provider-cheqd]: suspension: Invalid status list validUntil (must be date)' + ); + + // validate validUntil as future date + if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) + throw new Error( + '[did-provider-cheqd]: suspension: Invalid status list validUntil (must be future date)' + ); + + // validate validUntil towards validFrom + if ( + new Date(options?.publishOptions?.statusListValidUntil) <= + new Date(publishedList.StatusList2021.validFrom) + ) + throw new Error( + '[did-provider-cheqd]: suspension: Invalid status list validUntil (must be after validFrom)' + ); + } + + // validate paymentConditions, if provided + if (topArgs?.paymentConditions) { + if ( + !topArgs?.paymentConditions?.every( + (condition) => + condition.feePaymentAddress && + condition.feePaymentAmount && + condition.intervalInSeconds + ) + ) { + throw new Error( + '[did-provider-cheqd]: paymentConditions must contain feePaymentAddress and feeAmount and intervalInSeconds' + ); + } + + if ( + !topArgs?.paymentConditions?.every( + (condition) => + typeof condition.feePaymentAddress === 'string' && + typeof condition.feePaymentAmount === 'string' && + typeof condition.intervalInSeconds === 'number' + ) + ) { + throw new Error( + '[did-provider-cheqd]: feePaymentAddress and feePaymentAmount must be string and intervalInSeconds must be number' + ); + } + + if ( + !topArgs?.paymentConditions?.every( + (condition) => + condition.type === AccessControlConditionTypes.timelockPayment + ) + ) { + throw new Error( + '[did-provider-cheqd]: paymentConditions must be of type timelockPayment' + ); + } + } + + // validate dkgOptions + if ( + !topArgs?.dkgOptions || + !topArgs?.dkgOptions?.chain || + !topArgs?.dkgOptions?.network + ) { + throw new Error('[did-provider-cheqd]: dkgOptions is required'); + } + + // instantiate dkg-threshold client, in which case lit-protocol is used + const lit = await LitProtocol.create({ + chain: topArgs?.dkgOptions?.chain, + litNetwork: topArgs?.dkgOptions?.network, + }); + + // construct access control conditions and payment conditions tuple + const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted + ? await (async function () { + // define payment conditions, give precedence to top-level args + const paymentConditions = + topArgs?.paymentConditions || + publishedList.metadata.paymentConditions!; + + // return access control conditions and payment conditions tuple + return [ + await Promise.all( + paymentConditions.map(async (condition) => { + switch (condition.type) { + case AccessControlConditionTypes.timelockPayment: + return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock( + { + key: '$.tx_responses.*.timestamp', + comparator: '<=', + value: `${condition.intervalInSeconds}`, + }, + condition.feePaymentAmount, + condition.feePaymentAddress, + condition?.blockHeight, + topArgs?.dkgOptions?.chain + ); + default: + throw new Error( + `[did-provider-cheqd]: unsupported access control condition type ${condition.type}` + ); + } + }) + ), + paymentConditions, + ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]]; + })() + : await (async function () { + // validate paymentConditions + if (!topArgs?.paymentConditions) { + throw new Error( + '[did-provider-cheqd]: paymentConditions is required' + ); + } + + // return access control conditions and payment conditions tuple + return [ + await Promise.all( + topArgs.paymentConditions.map(async (condition) => { + switch (condition.type) { + case AccessControlConditionTypes.timelockPayment: + return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock( + { + key: '$.tx_responses.*.timestamp', + comparator: '<=', + value: `${condition.intervalInSeconds}`, + }, + condition.feePaymentAmount, + condition.feePaymentAddress, + condition?.blockHeight + ); + default: + throw new Error( + `[did-provider-cheqd]: unsupported access control condition type ${condition.type}` + ); + } + }) + ), + topArgs.paymentConditions, + ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]]; + })(); + + // encrypt bitstring + const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt( + bitstring, + unifiedAccessControlConditionsTuple[0], + true + ); + + // define status list content + const content = { + StatusList2021: { + statusPurpose: publishedList.StatusList2021.statusPurpose, + encodedList: await blobToHexString(encryptedString), + validFrom: publishedList.StatusList2021.validFrom, + validUntil: + options?.publishOptions?.statusListValidUntil || + publishedList.StatusList2021.validUntil, + }, + metadata: { + type: publishedList.metadata.type, + encrypted: true, + encoding: + (options?.publishOptions?.statusListEncoding as + | DefaultStatusList2021Encoding + | undefined) || publishedList.metadata.encoding, + encryptedSymmetricKey, + paymentConditions: unifiedAccessControlConditionsTuple[1], + }, + } satisfies StatusList2021Suspension; + + // return tuple of publish result and encryption relevant metadata + return [ + await Cheqd.publishStatusList2021( + fromString(JSON.stringify(content), 'utf-8'), + statusListMetadata, + options?.publishOptions + ), + { + encryptedString, + encryptedSymmetricKey, + symmetricKey: toString(symmetricKey!, 'hex'), + }, + ]; + })() + : await (async function () { + // validate encoding, if provided + if ( + options?.publishOptions?.statusListEncoding && + !Object.values(DefaultStatusList2021Encodings).includes( + options?.publishOptions?.statusListEncoding + ) + ) { + throw new Error( + '[did-provider-cheqd]: suspension: Invalid status list encoding' + ); + } + + // validate validUntil, if provided + if (options?.publishOptions?.statusListValidUntil) { + // validate validUntil as string + if (typeof options?.publishOptions?.statusListValidUntil !== 'string') + throw new Error( + '[did-provider-cheqd]: suspension: Invalid status list validUntil (must be string)' + ); + + // validate validUntil as date + if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) + throw new Error( + '[did-provider-cheqd]: suspension: Invalid status list validUntil (must be date)' + ); + + // validate validUntil as future date + if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) + throw new Error( + '[did-provider-cheqd]: suspension: Invalid status list validUntil (must be future date)' + ); + + // validate validUntil towards validFrom + if ( + new Date(options?.publishOptions?.statusListValidUntil) <= + new Date(publishedList.StatusList2021.validFrom) + ) + throw new Error( + '[did-provider-cheqd]: suspension: Invalid status list validUntil (must be after validFrom)' + ); + } + + // define status list content + const content = { + StatusList2021: { + statusPurpose: publishedList.StatusList2021.statusPurpose, + encodedList: + publishedList.metadata.encoding === 'base64url' + ? bitstring + : toString( + fromString(bitstring, 'base64url'), + options!.publishOptions + .statusListEncoding as DefaultStatusList2021Encoding + ), + validFrom: publishedList.StatusList2021.validFrom, + validUntil: + options?.publishOptions?.statusListValidUntil || + publishedList.StatusList2021.validUntil, + }, + metadata: { + type: publishedList.metadata.type, + encoding: + (options?.publishOptions?.statusListEncoding as + | DefaultStatusList2021Encoding + | undefined) || publishedList.metadata.encoding, + encrypted: false, + }, + } satisfies StatusList2021Suspension; + + // return tuple of publish result and encryption relevant metadata + return [ + await Cheqd.publishStatusList2021( + fromString(JSON.stringify(content), 'utf-8'), + statusListMetadata, + options?.publishOptions + ), + undefined, + ]; + })(); + + // early exit, if publish failed + if (!scoped[0]) + throw new Error('[did-provider-cheqd]: suspension: Failed to publish status list 2021'); + + // return publish result + return scoped; + })() + : undefined; + + return { + suspended: suspended.map((result) => (result.status === 'fulfilled' ? result.value.suspended : false)), + published: topArgs?.publish ? true : undefined, + statusList: topArgs?.returnUpdatedStatusList + ? ((await Cheqd.fetchStatusList2021(credentials[0])) as StatusList2021Suspension) + : undefined, + symmetricKey: topArgs?.returnSymmetricKey + ? (published?.[1] as { symmetricKey: string })?.symmetricKey + : undefined, + resourceMetadata: topArgs?.returnStatusListMetadata + ? await Cheqd.fetchStatusList2021Metadata(credentials[0]) + : undefined, + } satisfies BulkSuspensionResult; + } catch (error) { + // silent fail + early exit + console.error(error); + return { suspended: [], error: error as IError } satisfies BulkSuspensionResult; + } + } + + static async unsuspendCredential( + credential: VerifiableCredential, + options?: ICheqdStatusList2021Options + ): Promise { + try { + // validate status purpose + if (credential?.credentialStatus?.statusPurpose !== 'suspension') + throw new Error('[did-provider-cheqd]: unsuspension: Invalid status purpose'); + + // fetch status list 2021 + const publishedList = (await Cheqd.fetchStatusList2021(credential)) as StatusList2021Suspension; + + // early return, if encrypted and no decryption key provided + if (publishedList.metadata.encrypted && !options?.topArgs?.symmetricKey) + throw new Error( + '[did-provider-cheqd]: unsuspension: symmetricKey is required, if status list 2021 is encrypted' + ); + + // fetch status list 2021 inscribed in credential + const statusList2021 = options?.topArgs?.fetchList + ? await (async function () { + // if not encrypted, return bitstring + if (!publishedList.metadata.encrypted) + return publishedList.metadata.encoding === 'base64url' + ? publishedList.StatusList2021.encodedList + : toString( + fromString( + publishedList.StatusList2021.encodedList, + publishedList.metadata.encoding as DefaultStatusList2021Encoding + ), + 'base64url' + ); + + // otherwise, decrypt and return raw bitstring + const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex')); + + // decrypt + return await LitProtocol.decryptDirect( + scopedRawBlob, + fromString(options?.topArgs?.symmetricKey, 'hex') + ); + })() + : await (async function () { + // transcode to base64url, if needed + const publishedListTranscoded = + publishedList.metadata.encoding === 'base64url' + ? publishedList.StatusList2021.encodedList + : toString( + fromString( + publishedList.StatusList2021.encodedList, + publishedList.metadata.encoding as DefaultStatusList2021Encoding + ), + 'base64url' + ); + + // if status list 2021 is not fetched, read from file + if (options?.statusListFile) { + // if not encrypted, return bitstring + if (!publishedList.metadata.encrypted) { + // construct encoded status list + const encoded = new StatusList({ + buffer: await Cheqd.getFile(options.statusListFile), + }).encode() as Bitstring; + + // validate against published list + if (encoded !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: unsuspension: statusListFile does not match published status list 2021' + ); + + // return encoded + return encoded; + } + + // otherwise, decrypt and return bitstring + const scopedRawBlob = await toBlob(await Cheqd.getFile(options.statusListFile)); + + // decrypt + const decrypted = await LitProtocol.decryptDirect( + scopedRawBlob, + fromString(options?.topArgs?.symmetricKey, 'hex') + ); + + // validate against published list + if (decrypted !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: unsuspension: statusListFile does not match published status list 2021' + ); + + // return decrypted + return decrypted; + } + + if (!options?.statusListInlineBitstring) + throw new Error( + '[did-provider-cheqd]: unsuspension: statusListInlineBitstring is required, if statusListFile is not provided' + ); + + // validate against published list + if (options?.statusListInlineBitstring !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: unsuspension: statusListInlineBitstring does not match published status list 2021' + ); + + // otherwise, read from inline bitstring + return options?.statusListInlineBitstring; + })(); + + // parse status list 2021 + const statusList = await StatusList.decode({ encodedList: statusList2021 }); + + // early exit, if already unsuspended + if (!statusList.getStatus(Number(credential.credentialStatus.statusListIndex))) + return { unsuspended: true } satisfies UnsuspensionResult; + + // update suspension status + statusList.setStatus(Number(credential.credentialStatus.statusListIndex), false); + + // set in-memory status list ref + const bitstring = (await statusList.encode()) as Bitstring; + + // cast top-level args + const topArgs = options?.topArgs as ICheqdSuspendCredentialWithStatusList2021Args; + + // write status list 2021 to file, if provided + if (topArgs?.writeToFile) { + await Cheqd.writeFile(fromString(bitstring, 'base64url'), options?.statusListFile); + } + + // publish status list 2021, if provided + const published = topArgs?.publish + ? await (async function () { + // fetch status list 2021 metadata + const statusListMetadata = await Cheqd.fetchStatusList2021Metadata(credential); + + // publish status list 2021 as new version + const scoped = topArgs.publishEncrypted + ? await (async function () { + // validate encoding, if provided + if ( + options?.publishOptions?.statusListEncoding && + !Object.values(DefaultStatusList2021Encodings).includes( + options?.publishOptions?.statusListEncoding + ) + ) { + throw new Error( + '[did-provider-cheqd]: unsuspension: Invalid status list encoding' + ); + } + + // validate validUntil, if provided + if (options?.publishOptions?.statusListValidUntil) { + // validate validUntil as string + if (typeof options?.publishOptions?.statusListValidUntil !== 'string') + throw new Error( + '[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be string)' + ); + + // validate validUntil as date + if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) + throw new Error( + '[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be date)' + ); + + // validate validUntil as future date + if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) + throw new Error( + '[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be future date)' + ); + + // validate validUntil towards validFrom + if ( + new Date(options?.publishOptions?.statusListValidUntil) <= + new Date(publishedList.StatusList2021.validFrom) + ) + throw new Error( + '[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be after validFrom)' + ); + } + + // validate paymentConditions, if provided + if (topArgs?.paymentConditions) { + if ( + !topArgs?.paymentConditions?.every( + (condition) => + condition.feePaymentAddress && + condition.feePaymentAmount && + condition.intervalInSeconds + ) + ) { + throw new Error( + '[did-provider-cheqd]: paymentConditions must contain feePaymentAddress and feeAmount and intervalInSeconds' + ); + } + + if ( + !topArgs?.paymentConditions?.every( + (condition) => + typeof condition.feePaymentAddress === 'string' && + typeof condition.feePaymentAmount === 'string' && + typeof condition.intervalInSeconds === 'number' + ) + ) { + throw new Error( + '[did-provider-cheqd]: feePaymentAddress and feePaymentAmount must be string and intervalInSeconds must be number' + ); + } + + if ( + !topArgs?.paymentConditions?.every( + (condition) => + condition.type === AccessControlConditionTypes.timelockPayment + ) + ) { + throw new Error( + '[did-provider-cheqd]: paymentConditions must be of type timelockPayment' + ); + } + } + + // validate dkgOptions + if ( + !topArgs?.dkgOptions || + !topArgs?.dkgOptions?.chain || + !topArgs?.dkgOptions?.network + ) { + throw new Error('[did-provider-cheqd]: dkgOptions is required'); + } + + // instantiate dkg-threshold client, in which case lit-protocol is used + const lit = await LitProtocol.create({ + chain: topArgs?.dkgOptions?.chain, + litNetwork: topArgs?.dkgOptions?.network, + }); + + // construct access control conditions and payment conditions tuple + const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted + ? await (async function () { + // define payment conditions, give precedence to top-level args + const paymentConditions = + topArgs?.paymentConditions || + publishedList.metadata.paymentConditions!; + + // return access control conditions and payment conditions tuple + return [ + await Promise.all( + paymentConditions.map(async (condition) => { + switch (condition.type) { + case AccessControlConditionTypes.timelockPayment: + return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock( + { + key: '$.tx_responses.*.timestamp', + comparator: '<=', + value: `${condition.intervalInSeconds}`, + }, + condition.feePaymentAmount, + condition.feePaymentAddress, + condition?.blockHeight, + topArgs?.dkgOptions?.chain + ); + default: + throw new Error( + `[did-provider-cheqd]: unsupported access control condition type ${condition.type}` + ); + } + }) + ), + paymentConditions, + ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]]; + })() + : await (async function () { + // validate paymentConditions + if (!topArgs?.paymentConditions) { + throw new Error( + '[did-provider-cheqd]: paymentConditions is required' + ); + } + + // return access control conditions and payment conditions tuple + return [ + await Promise.all( + topArgs.paymentConditions.map(async (condition) => { + switch (condition.type) { + case AccessControlConditionTypes.timelockPayment: + return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock( + { + key: '$.tx_responses.*.timestamp', + comparator: '<=', + value: `${condition.intervalInSeconds}`, + }, + condition.feePaymentAmount, + condition.feePaymentAddress, + condition?.blockHeight + ); + default: + throw new Error( + `[did-provider-cheqd]: unsupported access control condition type ${condition.type}` + ); + } + }) + ), + topArgs.paymentConditions, + ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]]; + })(); + + // encrypt bitstring + const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt( + bitstring, + unifiedAccessControlConditionsTuple[0], + true + ); + + // define status list content + const content = { + StatusList2021: { + statusPurpose: publishedList.StatusList2021.statusPurpose, + encodedList: await blobToHexString(encryptedString), + validFrom: publishedList.StatusList2021.validFrom, + validUntil: + options?.publishOptions?.statusListValidUntil || + publishedList.StatusList2021.validUntil, + }, + metadata: { + type: publishedList.metadata.type, + encrypted: true, + encoding: + (options?.publishOptions?.statusListEncoding as + | DefaultStatusList2021Encoding + | undefined) || publishedList.metadata.encoding, + encryptedSymmetricKey, + paymentConditions: unifiedAccessControlConditionsTuple[1], + }, + } satisfies StatusList2021Suspension; + + // return tuple of publish result and encryption relevant metadata + return [ + await Cheqd.publishStatusList2021( + fromString(JSON.stringify(content), 'utf-8'), + statusListMetadata, + options?.publishOptions + ), + { + encryptedString, + encryptedSymmetricKey, + symmetricKey: toString(symmetricKey!, 'hex'), + }, + ]; + })() + : await (async function () { + // validate encoding, if provided + if ( + options?.publishOptions?.statusListEncoding && + !Object.values(DefaultStatusList2021Encodings).includes( + options?.publishOptions?.statusListEncoding + ) + ) { + throw new Error( + '[did-provider-cheqd]: unsuspension: Invalid status list encoding' + ); + } + + // validate validUntil, if provided + if (options?.publishOptions?.statusListValidUntil) { + // validate validUntil as string + if (typeof options?.publishOptions?.statusListValidUntil !== 'string') + throw new Error( + '[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be string)' + ); + + // validate validUntil as date + if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) + throw new Error( + '[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be date)' + ); + + // validate validUntil as future date + if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) + throw new Error( + '[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be future date)' + ); + + // validate validUntil towards validFrom + if ( + new Date(options?.publishOptions?.statusListValidUntil) <= + new Date(publishedList.StatusList2021.validFrom) + ) + throw new Error( + '[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be after validFrom)' + ); + } + + // define status list content + const content = { + StatusList2021: { + statusPurpose: publishedList.StatusList2021.statusPurpose, + encodedList: + publishedList.metadata.encoding === 'base64url' + ? bitstring + : toString( + fromString(bitstring, 'base64url'), + options!.publishOptions + .statusListEncoding as DefaultStatusList2021Encoding + ), + validFrom: publishedList.StatusList2021.validFrom, + validUntil: + options?.publishOptions?.statusListValidUntil || + publishedList.StatusList2021.validUntil, + }, + metadata: { + type: publishedList.metadata.type, + encoding: + (options?.publishOptions?.statusListEncoding as + | DefaultStatusList2021Encoding + | undefined) || publishedList.metadata.encoding, + encrypted: false, + }, + } satisfies StatusList2021Suspension; + + // return tuple of publish result and encryption relevant metadata + return [ + await Cheqd.publishStatusList2021( + fromString(JSON.stringify(content), 'utf-8'), + statusListMetadata, + options?.publishOptions + ), + undefined, + ]; + })(); + + // early exit, if publish failed + if (!scoped[0]) + throw new Error('[did-provider-cheqd]: unsuspension: Failed to publish status list 2021'); + + // return publish result + return scoped; + })() + : undefined; + + return { + unsuspended: true, + published: topArgs?.publish ? true : undefined, + statusList: topArgs?.returnUpdatedStatusList + ? ((await Cheqd.fetchStatusList2021(credential)) as StatusList2021Suspension) + : undefined, + symmetricKey: topArgs?.returnSymmetricKey + ? (published?.[1] as { symmetricKey: string })?.symmetricKey + : undefined, + resourceMetadata: topArgs?.returnStatusListMetadata + ? await Cheqd.fetchStatusList2021Metadata(credential) + : undefined, + } satisfies UnsuspensionResult; + } catch (error) { + // silent fail + early exit + console.error(error); + + return { unsuspended: false, error: error as IError } satisfies UnsuspensionResult; + } + } + + static async unsuspendCredentials( + credentials: VerifiableCredential[], + options?: ICheqdStatusList2021Options + ): Promise { + // validate credentials - case: empty + if (!credentials.length || credentials.length === 0) + throw new Error('[did-provider-cheqd]: unsuspension: No credentials provided'); + + // validate credentials - case: consistent issuer + if ( + credentials + .map((credential) => { + return (credential.issuer as { id: string }).id + ? (credential.issuer as { id: string }).id + : (credential.issuer as string); + }) + .filter((value, _, self) => value && value !== self[0]).length > 0 + ) + throw new Error('[did-provider-cheqd]: unsuspension: Credentials must be issued by the same issuer'); + + // validate credentials - case: status list index + if ( + credentials + .map((credential) => credential.credentialStatus!.statusListIndex) + .filter((value, index, self) => self.indexOf(value) !== index).length > 0 + ) + throw new Error('[did-provider-cheqd]: unsuspension: Credentials must have unique status list index'); + + // validate credentials - case: status purpose + if (!credentials.every((credential) => credential.credentialStatus?.statusPurpose === 'suspension')) + throw new Error('[did-provider-cheqd]: unsuspension: Invalid status purpose'); + + // validate credentials - case: status list id + const remote = credentials[0].credentialStatus?.id + ? (credentials[0].credentialStatus as { id: string }).id.split('#')[0] + : (function () { + throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list id'); + })(); + + // validate credentials - case: status list id format + if (!RemoteListPattern.test(remote)) + throw new Error( + '[did-provider-cheqd]: unsuspension: Invalid status list id format: expected: https://../1.0/identifiers/:>?resourceName=&resourceType=' + ); + + if ( + !credentials.every((credential) => { + return (credential.credentialStatus as { id: string }).id.split('#')[0] === remote; + }) + ) + throw new Error('[did-provider-cheqd]: unsuspension: Credentials must belong to the same status list'); + + // validate credentials - case: status list type + if (!credentials.every((credential) => credential.credentialStatus?.type === 'StatusList2021Entry')) + throw new Error('[did-provider-cheqd]: unsuspension: Invalid status list type'); + + try { + // fetch status list 2021 + const publishedList = (await Cheqd.fetchStatusList2021(credentials[0])) as StatusList2021Suspension; + + // early return, if encrypted and no decryption key provided + if (publishedList.metadata.encrypted && !options?.topArgs?.symmetricKey) + throw new Error( + '[did-provider-cheqd]: unsuspension: symmetricKey is required, if status list 2021 is encrypted' + ); + + // fetch status list 2021 inscribed in credential + const statusList2021 = options?.topArgs?.fetchList + ? await (async function () { + // if not encrypted, return bitstring + if (!publishedList.metadata.encrypted) + return publishedList.metadata.encoding === 'base64url' + ? publishedList.StatusList2021.encodedList + : toString( + fromString( + publishedList.StatusList2021.encodedList, + publishedList.metadata.encoding as DefaultStatusList2021Encoding + ), + 'base64url' + ); + + // otherwise, decrypt and return raw bitstring + const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex')); + + // decrypt + return await LitProtocol.decryptDirect( + scopedRawBlob, + fromString(options?.topArgs?.symmetricKey, 'hex') + ); + })() + : await (async function () { + // transcode to base64url, if needed + const publishedListTranscoded = + publishedList.metadata.encoding === 'base64url' + ? publishedList.StatusList2021.encodedList + : toString( + fromString( + publishedList.StatusList2021.encodedList, + publishedList.metadata.encoding as DefaultStatusList2021Encoding + ), + 'base64url' + ); + + // if status list 2021 is not fetched, read from file + if (options?.statusListFile) { + // if not encrypted, return bitstring + if (!publishedList.metadata.encrypted) { + // construct encoded status list + const encoded = new StatusList({ + buffer: await Cheqd.getFile(options.statusListFile), + }).encode() as Bitstring; + + // validate against published list + if (encoded !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: unsuspension: statusListFile does not match published status list 2021' + ); + + // return encoded + return encoded; + } + + // otherwise, decrypt and return bitstring + const scopedRawBlob = await toBlob(await Cheqd.getFile(options.statusListFile)); + + // decrypt + const decrypted = await LitProtocol.decryptDirect( + scopedRawBlob, + fromString(options?.topArgs?.symmetricKey, 'hex') + ); + + // validate against published list + if (decrypted !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: unsuspension: statusListFile does not match published status list 2021' + ); + + // return decrypted + return decrypted; + } + + if (!options?.statusListInlineBitstring) + throw new Error( + '[did-provider-cheqd]: unsuspension: statusListInlineBitstring is required, if statusListFile is not provided' + ); + + // validate against published list + if (options?.statusListInlineBitstring !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: unsuspension: statusListInlineBitstring does not match published status list 2021' + ); + + // otherwise, read from inline bitstring + return options?.statusListInlineBitstring; + })(); + + // parse status list 2021 + const statusList = await StatusList.decode({ encodedList: statusList2021 }); + + // initiate bulk unsuspension + const unsuspended = (await Promise.allSettled( + credentials.map((credential) => { + return (async function () { + // early return, if no credential status + if (!credential.credentialStatus) return { unsuspended: false }; + + // early exit, if credential is already unsuspended + if (!statusList.getStatus(Number(credential.credentialStatus.statusListIndex))) + return { unsuspended: true }; + + // update unsuspension status + statusList.setStatus(Number(credential.credentialStatus.statusListIndex), false); + + // return unsuspension status + return { unsuspended: true }; + })(); + }) + )) satisfies PromiseSettledResult[]; + + // revert bulk ops, if some failed + if (unsuspended.some((result) => result.status === 'fulfilled' && !result.value.unsuspended)) + throw new Error( + `[did-provider-cheqd]: unsuspension: Bulk unsuspension failed: already unsuspended credentials in unsuspension bundle: raw log: ${JSON.stringify( + unsuspended.map((result) => ({ + unsuspended: result.status === 'fulfilled' ? result.value.unsuspended : false, + })) + )}` + ); + + // set in-memory status list ref + const bitstring = (await statusList.encode()) as Bitstring; + + // cast top-level args + const topArgs = options?.topArgs as ICheqdRevokeCredentialWithStatusList2021Args; + + // write status list 2021 to file, if provided + if (topArgs?.writeToFile) { + await Cheqd.writeFile(fromString(bitstring, 'base64url'), options?.statusListFile); + } + + // publish status list 2021, if provided + const published = topArgs?.publish + ? await (async function () { + // fetch status list 2021 metadata + const statusListMetadata = await Cheqd.fetchStatusList2021Metadata(credentials[0]); + + // publish status list 2021 as new version + const scoped = topArgs.publishEncrypted + ? await (async function () { + // validate encoding, if provided + if ( + options?.publishOptions?.statusListEncoding && + !Object.values(DefaultStatusList2021Encodings).includes( + options?.publishOptions?.statusListEncoding + ) + ) { + throw new Error( + '[did-provider-cheqd]: unsuspension: Invalid status list encoding' + ); + } + + // validate validUntil, if provided + if (options?.publishOptions?.statusListValidUntil) { + // validate validUntil as string + if (typeof options?.publishOptions?.statusListValidUntil !== 'string') + throw new Error( + '[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be string)' + ); + + // validate validUntil as date + if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) + throw new Error( + '[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be date)' + ); + + // validate validUntil as future date + if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) + throw new Error( + '[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be future date)' + ); + + // validate validUntil towards validFrom + if ( + new Date(options?.publishOptions?.statusListValidUntil) <= + new Date(publishedList.StatusList2021.validFrom) + ) + throw new Error( + '[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be after validFrom)' + ); + } + + // validate paymentConditions, if provided + if (topArgs?.paymentConditions) { + if ( + !topArgs?.paymentConditions?.every( + (condition) => + condition.feePaymentAddress && + condition.feePaymentAmount && + condition.intervalInSeconds + ) + ) { + throw new Error( + '[did-provider-cheqd]: paymentConditions must contain feePaymentAddress and feeAmount and intervalInSeconds' + ); + } + + if ( + !topArgs?.paymentConditions?.every( + (condition) => + typeof condition.feePaymentAddress === 'string' && + typeof condition.feePaymentAmount === 'string' && + typeof condition.intervalInSeconds === 'number' + ) + ) { + throw new Error( + '[did-provider-cheqd]: feePaymentAddress and feePaymentAmount must be string and intervalInSeconds must be number' + ); + } + + if ( + !topArgs?.paymentConditions?.every( + (condition) => + condition.type === AccessControlConditionTypes.timelockPayment + ) + ) { + throw new Error( + '[did-provider-cheqd]: paymentConditions must be of type timelockPayment' + ); + } + } + + // validate dkgOptions + if ( + !topArgs?.dkgOptions || + !topArgs?.dkgOptions?.chain || + !topArgs?.dkgOptions?.network + ) { + throw new Error('[did-provider-cheqd]: dkgOptions is required'); + } + + // instantiate dkg-threshold client, in which case lit-protocol is used + const lit = await LitProtocol.create({ + chain: topArgs?.dkgOptions?.chain, + litNetwork: topArgs?.dkgOptions?.network, + }); + + // construct access control conditions and payment conditions tuple + const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted + ? await (async function () { + // define payment conditions, give precedence to top-level args + const paymentConditions = + topArgs?.paymentConditions || + publishedList.metadata.paymentConditions!; + + // return access control conditions and payment conditions tuple + return [ + await Promise.all( + paymentConditions.map(async (condition) => { + switch (condition.type) { + case AccessControlConditionTypes.timelockPayment: + return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock( + { + key: '$.tx_responses.*.timestamp', + comparator: '<=', + value: `${condition.intervalInSeconds}`, + }, + condition.feePaymentAmount, + condition.feePaymentAddress, + condition?.blockHeight, + topArgs?.dkgOptions?.chain + ); + default: + throw new Error( + `[did-provider-cheqd]: unsupported access control condition type ${condition.type}` + ); + } + }) + ), + paymentConditions, + ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]]; + })() + : await (async function () { + // validate paymentConditions + if (!topArgs?.paymentConditions) { + throw new Error( + '[did-provider-cheqd]: paymentConditions is required' + ); + } + + // return access control conditions and payment conditions tuple + return [ + await Promise.all( + topArgs.paymentConditions.map(async (condition) => { + switch (condition.type) { + case AccessControlConditionTypes.timelockPayment: + return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock( + { + key: '$.tx_responses.*.timestamp', + comparator: '<=', + value: `${condition.intervalInSeconds}`, + }, + condition.feePaymentAmount, + condition.feePaymentAddress, + condition?.blockHeight + ); + default: + throw new Error( + `[did-provider-cheqd]: unsupported access control condition type ${condition.type}` + ); + } + }) + ), + topArgs.paymentConditions, + ] satisfies [CosmosAccessControlCondition[], PaymentCondition[]]; + })(); + + // encrypt bitstring + const { encryptedString, encryptedSymmetricKey, symmetricKey } = await lit.encrypt( + bitstring, + unifiedAccessControlConditionsTuple[0], + true + ); + + // define status list content + const content = { + StatusList2021: { + statusPurpose: publishedList.StatusList2021.statusPurpose, + encodedList: await blobToHexString(encryptedString), + validFrom: publishedList.StatusList2021.validFrom, + validUntil: + options?.publishOptions?.statusListValidUntil || + publishedList.StatusList2021.validUntil, + }, + metadata: { + type: publishedList.metadata.type, + encrypted: true, + encoding: + (options?.publishOptions?.statusListEncoding as + | DefaultStatusList2021Encoding + | undefined) || publishedList.metadata.encoding, + encryptedSymmetricKey, + paymentConditions: unifiedAccessControlConditionsTuple[1], + }, + } satisfies StatusList2021Suspension; + + // return tuple of publish result and encryption relevant metadata + return [ + await Cheqd.publishStatusList2021( + fromString(JSON.stringify(content), 'utf-8'), + statusListMetadata, + options?.publishOptions + ), + { + encryptedString, + encryptedSymmetricKey, + symmetricKey: toString(symmetricKey!, 'hex'), + }, + ]; + })() + : await (async function () { + // validate encoding, if provided + if ( + options?.publishOptions?.statusListEncoding && + !Object.values(DefaultStatusList2021Encodings).includes( + options?.publishOptions?.statusListEncoding + ) + ) { + throw new Error( + '[did-provider-cheqd]: unsuspension: Invalid status list encoding' + ); + } + + // validate validUntil, if provided + if (options?.publishOptions?.statusListValidUntil) { + // validate validUntil as string + if (typeof options?.publishOptions?.statusListValidUntil !== 'string') + throw new Error( + '[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be string)' + ); + + // validate validUntil as date + if (isNaN(Date.parse(options?.publishOptions?.statusListValidUntil))) + throw new Error( + '[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be date)' + ); + + // validate validUntil as future date + if (new Date(options?.publishOptions?.statusListValidUntil) < new Date()) + throw new Error( + '[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be future date)' + ); + + // validate validUntil towards validFrom + if ( + new Date(options?.publishOptions?.statusListValidUntil) <= + new Date(publishedList.StatusList2021.validFrom) + ) + throw new Error( + '[did-provider-cheqd]: unsuspension: Invalid status list validUntil (must be after validFrom)' + ); + } + + // define status list content + const content = { + StatusList2021: { + statusPurpose: publishedList.StatusList2021.statusPurpose, + encodedList: + publishedList.metadata.encoding === 'base64url' + ? bitstring + : toString( + fromString(bitstring, 'base64url'), + options!.publishOptions + .statusListEncoding as DefaultStatusList2021Encoding + ), + validFrom: publishedList.StatusList2021.validFrom, + validUntil: + options?.publishOptions?.statusListValidUntil || + publishedList.StatusList2021.validUntil, + }, + metadata: { + type: publishedList.metadata.type, + encoding: + (options?.publishOptions?.statusListEncoding as + | DefaultStatusList2021Encoding + | undefined) || publishedList.metadata.encoding, + encrypted: false, + }, + } satisfies StatusList2021Suspension; + + // return tuple of publish result and encryption relevant metadata + return [ + await Cheqd.publishStatusList2021( + fromString(JSON.stringify(content), 'utf-8'), + statusListMetadata, + options?.publishOptions + ), + undefined, + ]; + })(); + + // early exit, if publish failed + if (!scoped[0]) + throw new Error('[did-provider-cheqd]: unsuspension: Failed to publish status list 2021'); + + // return publish result + return scoped; + })() + : undefined; + + return { + unsuspended: unsuspended.map((result) => + result.status === 'fulfilled' ? result.value.unsuspended : false + ), + published: topArgs?.publish ? true : undefined, + statusList: topArgs?.returnUpdatedStatusList + ? ((await Cheqd.fetchStatusList2021(credentials[0])) as StatusList2021Suspension) + : undefined, + symmetricKey: topArgs?.returnSymmetricKey + ? (published?.[1] as { symmetricKey: string })?.symmetricKey + : undefined, + resourceMetadata: topArgs?.returnStatusListMetadata + ? await Cheqd.fetchStatusList2021Metadata(credentials[0]) + : undefined, + } satisfies BulkUnsuspensionResult; + } catch (error) { + // silent fail + early exit + console.error(error); + + return { unsuspended: [], error: error as IError } satisfies BulkUnsuspensionResult; + } + } + + static async checkRevoked( + credential: VerifiableCredential, + options: ICheqdStatusList2021Options = { fetchList: true } + ): Promise { + // validate status purpose + if (credential.credentialStatus?.statusPurpose !== 'revocation') { + throw new Error( + `[did-provider-cheqd]: check: revocation: Unsupported status purpose: ${credential.credentialStatus?.statusPurpose}` + ); + } + + // fetch status list 2021 + const publishedList = (await Cheqd.fetchStatusList2021(credential)) as StatusList2021Revocation; + + // fetch status list 2021 inscribed in credential + const statusList2021 = options?.topArgs?.fetchList + ? await (async function () { + // if not encrypted, return bitstring + if (!publishedList.metadata.encrypted) + return publishedList.metadata.encoding === 'base64url' + ? publishedList.StatusList2021.encodedList + : toString( + fromString( + publishedList.StatusList2021.encodedList, + publishedList.metadata.encoding as DefaultStatusList2021Encoding + ), + 'base64url' + ); + + // otherwise, decrypt and return raw bitstring + const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex')); + + // instantiate dkg-threshold client, in which case lit-protocol is used + const lit = await LitProtocol.create({ + chain: options?.topArgs?.dkgOptions?.chain, + litNetwork: options?.topArgs?.dkgOptions?.network, + }); + + // construct access control conditions + const unifiedAccessControlConditions = await Promise.all( + publishedList.metadata.paymentConditions!.map(async (condition) => { + switch (condition.type) { + case AccessControlConditionTypes.timelockPayment: + return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock( + { + key: '$.tx_responses.*.timestamp', + comparator: '<=', + value: `${condition.intervalInSeconds}`, + }, + condition.feePaymentAmount, + condition.feePaymentAddress, + condition?.blockHeight, + options?.topArgs?.dkgOptions?.chain + ); + default: + throw new Error( + `[did-provider-cheqd]: unsupported access control condition type ${condition.type}` + ); + } + }) + ); + + // decrypt + return await lit.decrypt( + scopedRawBlob, + publishedList.metadata.encryptedSymmetricKey!, + unifiedAccessControlConditions + ); + })() + : await (async function () { + // transcode to base64url, if needed + const publishedListTranscoded = + publishedList.metadata.encoding === 'base64url' + ? publishedList.StatusList2021.encodedList + : toString( + fromString( + publishedList.StatusList2021.encodedList, + publishedList.metadata.encoding as DefaultStatusList2021Encoding + ), + 'base64url' + ); + + // if status list 2021 is not fetched, read from file + if (options?.statusListFile) { + // if not encrypted, return bitstring + if (!publishedList.metadata.encrypted) { + // construct encoded status list + const encoded = new StatusList({ + buffer: await Cheqd.getFile(options.statusListFile), + }).encode() as Bitstring; + + // validate against published list + if (encoded !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: check: revocation: statusListFile does not match published status list 2021' + ); + + // return encoded + return encoded; + } + + // otherwise, decrypt and return bitstring + const scopedRawBlob = await toBlob(await Cheqd.getFile(options.statusListFile)); + + // decrypt + const decrypted = await LitProtocol.decryptDirect( + scopedRawBlob, + fromString(options?.topArgs?.symmetricKey, 'hex') + ); + + // validate against published list + if (decrypted !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: check: revocation: statusListFile does not match published status list 2021' + ); + + // return decrypted + return decrypted; + } + + if (!options?.statusListInlineBitstring) + throw new Error( + '[did-provider-cheqd]: check: revocation: statusListInlineBitstring is required, if statusListFile is not provided' + ); + + // validate against published list + if (options?.statusListInlineBitstring !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: check: revocation: statusListInlineBitstring does not match published status list 2021' + ); + + // otherwise, read from inline bitstring + return options?.statusListInlineBitstring; + })(); + + // transcode, if needed + const transcodedStatusList2021 = + publishedList.metadata.encoding === 'base64url' + ? statusList2021 + : toString( + fromString(statusList2021, publishedList.metadata.encoding as DefaultStatusList2021Encoding), + 'base64url' + ); + + // parse status list 2021 + const statusList = await StatusList.decode({ encodedList: transcodedStatusList2021 }); + + // get status by index + return !!statusList.getStatus(Number(credential.credentialStatus.statusListIndex)); + } + + static async checkSuspended( + credential: VerifiableCredential, + options: ICheqdStatusList2021Options = { fetchList: true } + ): Promise { + // validate status purpose + if (credential.credentialStatus?.statusPurpose !== 'suspension') { + throw new Error( + `[did-provider-cheqd]: check: suspension: Unsupported status purpose: ${credential.credentialStatus?.statusPurpose}` + ); + } + + // fetch status list 2021 + const publishedList = (await Cheqd.fetchStatusList2021(credential)) as StatusList2021Suspension; + + // fetch status list 2021 inscribed in credential + const statusList2021 = options?.topArgs?.fetchList + ? await (async function () { + // if not encrypted, return bitstring + if (!publishedList.metadata.encrypted) + return publishedList.metadata.encoding === 'base64url' + ? publishedList.StatusList2021.encodedList + : toString( + fromString( + publishedList.StatusList2021.encodedList, + publishedList.metadata.encoding as DefaultStatusList2021Encoding + ), + 'base64url' + ); + + // otherwise, decrypt and return bitstring + const scopedRawBlob = await toBlob(fromString(publishedList.StatusList2021.encodedList, 'hex')); + + // instantiate dkg-threshold client, in which case lit-protocol is used + const lit = await LitProtocol.create({ + chain: options?.topArgs?.dkgOptions?.chain, + litNetwork: options?.topArgs?.dkgOptions?.network, + }); + + // construct access control conditions + const unifiedAccessControlConditions = await Promise.all( + publishedList.metadata.paymentConditions!.map(async (condition) => { + switch (condition.type) { + case AccessControlConditionTypes.timelockPayment: + return await LitProtocol.generateCosmosAccessControlConditionInverseTimelock( + { + key: '$.tx_responses.*.timestamp', + comparator: '<=', + value: `${condition.intervalInSeconds}`, + }, + condition.feePaymentAmount, + condition.feePaymentAddress, + condition?.blockHeight, + options?.topArgs?.dkgOptions?.chain + ); + default: + throw new Error( + `[did-provider-cheqd]: unsupported access control condition type ${condition.type}` + ); + } + }) + ); + + // decrypt + return await lit.decrypt( + scopedRawBlob, + publishedList.metadata.encryptedSymmetricKey!, + unifiedAccessControlConditions + ); + })() + : await (async function () { + // transcode to base64url, if needed + const publishedListTranscoded = + publishedList.metadata.encoding === 'base64url' + ? publishedList.StatusList2021.encodedList + : toString( + fromString( + publishedList.StatusList2021.encodedList, + publishedList.metadata.encoding as DefaultStatusList2021Encoding + ), + 'base64url' + ); + + // if status list 2021 is not fetched, read from file + if (options?.statusListFile) { + // if not encrypted, return bitstring + if (!publishedList.metadata.encrypted) { + // construct encoded status list + const encoded = new StatusList({ + buffer: await Cheqd.getFile(options.statusListFile), + }).encode() as Bitstring; + + // validate against published list + if (encoded !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: check: suspension: statusListFile does not match published status list 2021' + ); + + // return encoded + return encoded; + } + + // otherwise, decrypt and return bitstring + const scopedRawBlob = await toBlob(await Cheqd.getFile(options.statusListFile)); + + // decrypt + const decrypted = await LitProtocol.decryptDirect( + scopedRawBlob, + fromString(options?.topArgs?.symmetricKey, 'hex') + ); + + // validate against published list + if (decrypted !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: check: suspension: statusListFile does not match published status list 2021' + ); + + // return decrypted + return decrypted; + } + + if (!options?.statusListInlineBitstring) + throw new Error( + '[did-provider-cheqd]: check: suspension: statusListInlineBitstring is required, if statusListFile is not provided' + ); + + // validate against published list + if (options?.statusListInlineBitstring !== publishedListTranscoded) + throw new Error( + '[did-provider-cheqd]: check: suspension: statusListInlineBitstring does not match published status list 2021' + ); + + // otherwise, read from inline bitstring + return options?.statusListInlineBitstring; + })(); + + // parse status list 2021 + const statusList = await StatusList.decode({ encodedList: statusList2021 }); + + // get status by index + return !!statusList.getStatus(Number(credential.credentialStatus.statusListIndex)); + } + + static async publishStatusList2021( + statusList2021Raw: Uint8Array, + statusList2021Metadata: LinkedResourceMetadataResolutionResult, + options: { + context: IContext; + resourceId?: string; + resourceVersion?: string; + resourceAlsoKnownAs?: AlternativeUri[]; + signInputs?: ISignInputs[]; + fee?: DidStdFee; + } + ): Promise { + // construct status list 2021 payload from previous version + new version + const payload = { + collectionId: statusList2021Metadata.resourceCollectionId, + id: options?.resourceId || v4(), + name: statusList2021Metadata.resourceName, + version: options?.resourceVersion || new Date().toISOString(), + alsoKnownAs: options?.resourceAlsoKnownAs || [], + resourceType: statusList2021Metadata.resourceType as DefaultStatusList2021ResourceType, + data: statusList2021Raw, + } satisfies StatusList2021ResourcePayload; + + return await options.context.agent[BroadcastStatusList2021MethodName]({ + kms: (await options.context.agent.keyManagerGetKeyManagementSystems())[0], + payload, + network: statusList2021Metadata.resourceURI.split(':')[2] as CheqdNetwork, + signInputs: options?.signInputs, + fee: options?.fee, + }); + } + + static async fetchStatusList2021( + credential: VerifiableCredential, + returnRaw = false + ): Promise { + // validate credential status + if (!credential.credentialStatus) + throw new Error('[did-provider-cheqd]: fetch status list: Credential status is not present'); + + // validate credential status type + if (credential.credentialStatus.type !== 'StatusList2021Entry') + throw new Error('[did-provider-cheqd]: fetch status list: Credential status type is not valid'); + + // validate credential status list status purpose + if ( + credential.credentialStatus.statusPurpose !== 'revocation' && + credential.credentialStatus.statusPurpose !== 'suspension' + ) + throw new Error('[did-provider-cheqd]: fetch status list: Credential status purpose is not valid'); + + // fetch status list 2021 + const content = (await (await fetch(credential.credentialStatus.id.split('#')[0])).json()) as + | StatusList2021Revocation + | StatusList2021Suspension; + + if ( + !( + content.StatusList2021 && + content.metadata && + content.StatusList2021.encodedList && + content.StatusList2021.statusPurpose && + content.metadata.encoding + ) + ) { + throw new Error(`'[did-provider-cheqd]: fetch status list: Status List resource content is not valid'`); + } + + // return raw if requested + if (returnRaw) { + return fromString( + content.StatusList2021.encodedList, + content.metadata.encoding as DefaultStatusList2021Encoding + ); + } + + // otherwise, return content + return content; + } + + static async fetchStatusList2021Metadata( + credential: VerifiableCredential + ): Promise { + // get base url + const baseUrl = new URL(credential.credentialStatus!.id.split('#')[0]); + + // get resource name + const resourceName = baseUrl.searchParams.get('resourceName'); + + // get resource type + const resourceType = baseUrl.searchParams.get('resourceType'); + + // unset resource name + baseUrl.searchParams.delete('resourceName'); + + // unset resource type + baseUrl.searchParams.delete('resourceType'); + + // construct metadata url + const metadataUrl = `${baseUrl.toString()}/metadata`; + + // fetch collection metadata + const collectionMetadata = (await (await fetch(metadataUrl)).json()) as DIDMetadataDereferencingResult; + + // early exit if no linked resources + if (!collectionMetadata?.contentStream?.linkedResourceMetadata) + throw new Error('[did-provider-cheqd]: fetch status list metadata: No linked resources found'); + + // find relevant resources by resource name + const resourceVersioning = collectionMetadata.contentStream.linkedResourceMetadata.filter( + (resource) => resource.resourceName === resourceName && resource.resourceType === resourceType + ); + + // early exit if no relevant resources + if (!resourceVersioning.length || resourceVersioning.length === 0) + throw new Error( + `[did-provider-cheqd]: fetch status list metadata: No relevant resources found by resource name ${resourceName}` + ); + + // get latest resource version by nextVersionId null pointer, or by latest created date as fallback + return ( + resourceVersioning.find((resource) => !resource.nextVersionId) || + resourceVersioning.sort((a, b) => new Date(b.created).getTime() - new Date(a.created).getTime())[0] + ); + } + + static async loadProvider(didUrl: string, providers: CheqdDIDProvider[]): Promise { + const provider = providers.find((provider) => + didUrl.includes(`${DidPrefix}:${CheqdDidMethod}:${provider.network}`) + ); + if (!provider) { + throw new Error(`[did-provider-cheqd]: Provider namespace not found`); + } + return provider; + } + + static generateProviderId(namespace: string): string { + return `${DidPrefix}:${CheqdDidMethod}:${namespace}`; + } + + static async getFile(filename: string): Promise { + if (typeof filename !== 'string') { + throw new Error('[did-provider-cheqd]: filename is required'); + } + + if (!fs.existsSync(filename)) { + debug(`[did-provider-cheqd]: File ${filename} not found`); + throw new Error(`[did-provider-cheqd]: File ${filename} not found`); + } + + return new Promise((resolve, reject) => { + const content = fs.readFileSync(filename); + if (!content) { + reject(new Error(`[did-provider-cheqd]: File ${filename} is empty`)); + } + resolve(new Uint8Array(content)); + }); + } + + static async writeFile(content: Uint8Array, filename?: string): Promise { + if (!filename) { + filename = `statusList2021-${v4()}`; + } + + // alert if file exists + if (fs.existsSync(filename)) { + debug(`[did-provider-cheqd]: File ${filename} already exists`); + console.warn(`[did-provider-cheqd]: File ${filename} already exists. Overwriting...`); + } + + return new Promise((resolve, reject) => { + fs.writeFile(filename!, content, (err) => { + if (err) { + reject(new Error(`[did-provider-cheqd]: Error writing file ${filename}: reason: ${err}`)); + } + resolve(); + }); + }); + } + + static async decodeCredentialJWT(jwt: string): Promise { + const decodedCredential = decodeJWT(jwt); + + // validate credential payload + if (!decodedCredential.payload) + throw new Error('[did-provider-cheqd]: decode jwt: decodedCredential.payload is required'); + + // validate credential payload vc property as VerifiableCredential + if (!decodedCredential.payload.vc) + throw new Error('[did-provider-cheqd]: decode jwt: decodedCredential.payload.vc is required'); + + return { + ...decodedCredential.payload.vc, + issuer: decodedCredential.payload.iss, + } satisfies VerifiableCredential; + } } diff --git a/src/agent/index.ts b/src/agent/index.ts index 7a9de06f..4d9bd254 100644 --- a/src/agent/index.ts +++ b/src/agent/index.ts @@ -1 +1 @@ -export * from './ICheqd.js' \ No newline at end of file +export * from './ICheqd.js'; diff --git a/src/did-manager/cheqd-did-provider.ts b/src/did-manager/cheqd-did-provider.ts index b4755964..83e68313 100644 --- a/src/did-manager/cheqd-did-provider.ts +++ b/src/did-manager/cheqd-did-provider.ts @@ -14,18 +14,13 @@ import { DidStdFee, ISignInputs, IContext as ISDKContext, - CheqdNetwork -} from '@cheqd/sdk' -import { MsgCreateResourcePayload } from '@cheqd/ts-proto/cheqd/resource/v2/index.js' -import { - AccountData, - Coin, - DirectSecp256k1HdWallet, - DirectSecp256k1Wallet -} from '@cosmjs/proto-signing' -import { GasPrice, DeliverTxResponse } from '@cosmjs/stargate' -import { assert } from '@cosmjs/utils' -import { DIDDocument } from 'did-resolver' + CheqdNetwork, +} from '@cheqd/sdk'; +import { MsgCreateResourcePayload } from '@cheqd/ts-proto/cheqd/resource/v2/index.js'; +import { AccountData, Coin, DirectSecp256k1HdWallet, DirectSecp256k1Wallet } from '@cosmjs/proto-signing'; +import { GasPrice, DeliverTxResponse } from '@cosmjs/stargate'; +import { assert } from '@cosmjs/utils'; +import { DIDDocument } from 'did-resolver'; import { IIdentifier, IKey, @@ -35,89 +30,85 @@ import { ManagedKeyInfo, MinimalImportableKey, TKeyType, -} from '@veramo/core' -import { AbstractIdentifierProvider } from '@veramo/did-manager' -import { base64ToBytes, extractPublicKeyHex } from '@veramo/utils' -import Debug from 'debug' -import { - EnglishMnemonic as _, - Ed25519 -} from '@cosmjs/crypto' -import { - fromString, - toString -} from 'uint8arrays' -import { - MsgCreateDidDocPayload, - MsgDeactivateDidDocPayload, - SignInfo -} from '@cheqd/ts-proto/cheqd/did/v2/index.js' -import { v4 } from 'uuid' +} from '@veramo/core'; +import { AbstractIdentifierProvider } from '@veramo/did-manager'; +import { base64ToBytes, extractPublicKeyHex } from '@veramo/utils'; +import Debug from 'debug'; +import { EnglishMnemonic as _, Ed25519 } from '@cosmjs/crypto'; +import { fromString, toString } from 'uint8arrays'; +import { MsgCreateDidDocPayload, MsgDeactivateDidDocPayload, SignInfo } from '@cheqd/ts-proto/cheqd/did/v2/index.js'; +import { v4 } from 'uuid'; import { LitCompatibleCosmosChain, LitCompatibleCosmosChains, LitNetwork, - LitNetworks -} from '../dkg-threshold/lit-protocol.js' -import { IContext } from '../agent/ICheqd.js' + LitNetworks, +} from '../dkg-threshold/lit-protocol.js'; +import { IContext } from '../agent/ICheqd.js'; -const debug = Debug('veramo:did-provider-cheqd') +const debug = Debug('veramo:did-provider-cheqd'); export const DefaultRPCUrls = { [CheqdNetwork.Mainnet]: 'https://rpc.cheqd.net', - [CheqdNetwork.Testnet]: 'https://rpc.cheqd.network' -} as const + [CheqdNetwork.Testnet]: 'https://rpc.cheqd.network', +} as const; export const DefaultRESTUrls = { [CheqdNetwork.Mainnet]: 'https://api.cheqd.net', - [CheqdNetwork.Testnet]: 'https://api.cheqd.network' -} as const + [CheqdNetwork.Testnet]: 'https://api.cheqd.network', +} as const; export const DefaultDkgSupportedChains = { [CheqdNetwork.Mainnet]: LitCompatibleCosmosChains.cheqdMainnet, - [CheqdNetwork.Testnet]: LitCompatibleCosmosChains.cheqdTestnet -} as const + [CheqdNetwork.Testnet]: LitCompatibleCosmosChains.cheqdTestnet, +} as const; export const DefaultStatusList2021StatusPurposeTypes = { revocation: 'revocation', - suspension: 'suspension' -} as const + suspension: 'suspension', +} as const; export const DefaultStatusList2021ResourceTypes = { default: 'StatusList2021', revocation: 'StatusList2021Revocation', - suspension: 'StatusList2021Suspension' -} as const + suspension: 'StatusList2021Suspension', +} as const; export const DefaultStatusList2021Encodings = { - 'base64': 'base64', - 'base64url': 'base64url', - 'hex': 'hex' -} as const + base64: 'base64', + base64url: 'base64url', + hex: 'hex', +} as const; -export type DefaultRPCUrl = typeof DefaultRPCUrls[keyof typeof DefaultRPCUrls] +export type DefaultRPCUrl = (typeof DefaultRPCUrls)[keyof typeof DefaultRPCUrls]; -export type DefaultRESTUrl = typeof DefaultRESTUrls[keyof typeof DefaultRESTUrls] +export type DefaultRESTUrl = (typeof DefaultRESTUrls)[keyof typeof DefaultRESTUrls]; -export type DefaultStatusList2021ResourceType = typeof DefaultStatusList2021ResourceTypes[keyof typeof DefaultStatusList2021ResourceTypes] +export type DefaultStatusList2021ResourceType = + (typeof DefaultStatusList2021ResourceTypes)[keyof typeof DefaultStatusList2021ResourceTypes]; -export type DefaultStatusList2021StatusPurposeType = typeof DefaultStatusList2021StatusPurposeTypes[keyof typeof DefaultStatusList2021StatusPurposeTypes] +export type DefaultStatusList2021StatusPurposeType = + (typeof DefaultStatusList2021StatusPurposeTypes)[keyof typeof DefaultStatusList2021StatusPurposeTypes]; -export type DefaultStatusList2021Encoding = typeof DefaultStatusList2021Encodings[keyof typeof DefaultStatusList2021Encodings] +export type DefaultStatusList2021Encoding = + (typeof DefaultStatusList2021Encodings)[keyof typeof DefaultStatusList2021Encodings]; -export type LinkedResource = Omit & { data?: string } +export type LinkedResource = Omit & { data?: string }; -export type ResourcePayload = Partial +export type ResourcePayload = Partial; -export type StatusList2021ResourcePayload = ResourcePayload & { resourceType: DefaultStatusList2021ResourceType } +export type StatusList2021ResourcePayload = ResourcePayload & { resourceType: DefaultStatusList2021ResourceType }; -export type TImportableEd25519Key = Required> & { kid: TImportableEd25519Key['publicKeyHex'], type: 'Ed25519' } +export type TImportableEd25519Key = Required> & { + kid: TImportableEd25519Key['publicKeyHex']; + type: 'Ed25519'; +}; declare const TImportableEd25519Key: { - isTImportableEd25519Key(object: object[]): object is TImportableEd25519Key[]; -} + isTImportableEd25519Key(object: object[]): object is TImportableEd25519Key[]; +}; -export type TSupportedKeyType = 'Ed25519' | 'Secp256k1' +export type TSupportedKeyType = 'Ed25519' | 'Secp256k1'; export class EnglishMnemonic extends _ { static readonly _mnemonicMatcher = /^[a-z]+( [a-z]+)*$/; @@ -126,151 +117,178 @@ export class EnglishMnemonic extends _ { /** * {@link @veramo/did-manager#DIDManager} identifier provider for `did:cheqd` identifiers. * @public -*/ + */ export class CheqdDIDProvider extends AbstractIdentifierProvider { - private defaultKms: string - public readonly network: CheqdNetwork - public readonly rpcUrl: string - private readonly cosmosPayerWallet: Promise - public readonly dkgOptions: { chain: Extract, network: LitNetwork } - private sdk?: CheqdSDK - private fee?: DidStdFee - - static readonly defaultGasPrice = GasPrice.fromString('50ncheq') - - constructor(options: { defaultKms: string, cosmosPayerSeed: string, networkType?: CheqdNetwork, rpcUrl?: string, dkgOptions?: { chain?: Extract, network?: LitNetwork } }) { - super() - this.defaultKms = options.defaultKms - this.network = options.networkType ? options.networkType : CheqdNetwork.Testnet - this.rpcUrl = options.rpcUrl ? options.rpcUrl : DefaultRPCUrls[this.network] + private defaultKms: string; + public readonly network: CheqdNetwork; + public readonly rpcUrl: string; + private readonly cosmosPayerWallet: Promise; + public readonly dkgOptions: { + chain: Extract; + network: LitNetwork; + }; + private sdk?: CheqdSDK; + private fee?: DidStdFee; + + static readonly defaultGasPrice = GasPrice.fromString('50ncheq'); + + constructor(options: { + defaultKms: string; + cosmosPayerSeed: string; + networkType?: CheqdNetwork; + rpcUrl?: string; + dkgOptions?: { + chain?: Extract; + network?: LitNetwork; + }; + }) { + super(); + this.defaultKms = options.defaultKms; + this.network = options.networkType ? options.networkType : CheqdNetwork.Testnet; + this.rpcUrl = options.rpcUrl ? options.rpcUrl : DefaultRPCUrls[this.network]; this.dkgOptions = options.dkgOptions - ? { chain: options.dkgOptions.chain ? options.dkgOptions.chain : DefaultDkgSupportedChains[this.network], network: options.dkgOptions.network ? options.dkgOptions.network : LitNetworks.serrano } - : { chain: DefaultDkgSupportedChains[this.network], network: LitNetworks.serrano } + ? { + chain: options.dkgOptions.chain + ? options.dkgOptions.chain + : DefaultDkgSupportedChains[this.network], + network: options.dkgOptions.network ? options.dkgOptions.network : LitNetworks.serrano, + } + : { chain: DefaultDkgSupportedChains[this.network], network: LitNetworks.serrano }; if (!options?.cosmosPayerSeed || options.cosmosPayerSeed === '') { - this.cosmosPayerWallet = DirectSecp256k1HdWallet.generate() - return + this.cosmosPayerWallet = DirectSecp256k1HdWallet.generate(); + return; } this.cosmosPayerWallet = EnglishMnemonic._mnemonicMatcher.test(options.cosmosPayerSeed) ? DirectSecp256k1HdWallet.fromMnemonic(options.cosmosPayerSeed, { prefix: 'cheqd' }) - : DirectSecp256k1Wallet.fromKey( - fromString( - options.cosmosPayerSeed.replace(/^0x/, ''), - 'hex' - ), - 'cheqd' - ) + : DirectSecp256k1Wallet.fromKey(fromString(options.cosmosPayerSeed.replace(/^0x/, ''), 'hex'), 'cheqd'); } async getWalletAccounts(): Promise { - return await (await this.cosmosPayerWallet).getAccounts() + return await (await this.cosmosPayerWallet).getAccounts(); } private async getCheqdSDK(fee?: DidStdFee, gasPrice?: GasPrice): Promise { if (!this.sdk) { const wallet = await this.cosmosPayerWallet.catch(() => { - throw new Error(`[did-provider-cheqd]: network: ${this.network} valid cosmosPayerSeed is required`) - }) + throw new Error(`[did-provider-cheqd]: network: ${this.network} valid cosmosPayerSeed is required`); + }); const sdkOptions: ICheqdSDKOptions = { - modules: [DIDModule as unknown as AbstractCheqdSDKModule, ResourceModule as unknown as AbstractCheqdSDKModule], + modules: [ + DIDModule as unknown as AbstractCheqdSDKModule, + ResourceModule as unknown as AbstractCheqdSDKModule, + ], rpcUrl: this.rpcUrl, wallet: wallet, - gasPrice - } + gasPrice, + }; - this.sdk = await createCheqdSDK(sdkOptions) - this.fee = fee + this.sdk = await createCheqdSDK(sdkOptions); + this.fee = fee; if (this?.fee && !this?.fee?.payer) { - const feePayer = (await (await this.cosmosPayerWallet).getAccounts())[0].address - this.fee.payer = feePayer + const feePayer = (await (await this.cosmosPayerWallet).getAccounts())[0].address; + this.fee.payer = feePayer; } } - return this.sdk! + return this.sdk!; } async createIdentifier( - { kms, options }: { kms?: string; alias?: string, options: { document: DIDDocument, keys?: TImportableEd25519Key[], versionId?: string, fee?: DidStdFee } }, - context: IContext, + { + kms, + options, + }: { + kms?: string; + alias?: string; + options: { document: DIDDocument; keys?: TImportableEd25519Key[]; versionId?: string; fee?: DidStdFee }; + }, + context: IContext ): Promise> { - const sdk = await this.getCheqdSDK(options?.fee) - const versionId = options.versionId || v4() - const signInputs: ISignInputs[] | SignInfo[] = options.keys - ? function () { - return options.keys.map(key => createSignInputsFromImportableEd25519Key(key, options.document.verificationMethod || [])) - }() + const sdk = await this.getCheqdSDK(options?.fee); + const versionId = options.versionId || v4(); + const signInputs: ISignInputs[] | SignInfo[] = options.keys + ? (function () { + return options.keys.map((key) => + createSignInputsFromImportableEd25519Key(key, options.document.verificationMethod || []) + ); + })() : await (async function (that: CheqdDIDProvider) { - const data = await createMsgCreateDidDocPayloadToSign(options.document, versionId) - return await that.signPayload(context, data, options.document.verificationMethod) - }(this)) + const data = await createMsgCreateDidDocPayloadToSign(options.document, versionId); + return await that.signPayload(context, data, options.document.verificationMethod); + })(this); - const tx = await sdk.createDidDocTx( - signInputs, - options.document, - '', - this?.fee, - undefined, - versionId, - { sdk: sdk } satisfies ISDKContext, - ) + const tx = await sdk.createDidDocTx(signInputs, options.document, '', this?.fee, undefined, versionId, { + sdk: sdk, + } satisfies ISDKContext); - assert(tx.code === 0, `cosmos_transaction: Failed to create DID. Reason: ${tx.rawLog}`) + assert(tx.code === 0, `cosmos_transaction: Failed to create DID. Reason: ${tx.rawLog}`); //* Currently, only one controller key is supported. //* We assume that the first key in the list is the controller key. //* This is subject to change in the near future. - const keys: ManagedKeyInfo[] = options.keys + const keys: ManagedKeyInfo[] = options.keys ? await (async function (that: CheqdDIDProvider) { - const scopedKeys: ManagedKeyInfo[] = [] - for (const key of options.keys!) { - let managedKey: ManagedKeyInfo | undefined - try { - managedKey = await context.agent.keyManagerImport({ - ...key, - kms: kms || that.defaultKms, - } satisfies MinimalImportableKey) - } catch (e) { - debug(`Failed to import key ${key.kid}. Reason: ${e}`) - - // construct key, if it failed to import - managedKey = { ...key, kms: kms || that.defaultKms } - } - if (managedKey) { - scopedKeys.push(managedKey) + const scopedKeys: ManagedKeyInfo[] = []; + for (const key of options.keys!) { + let managedKey: ManagedKeyInfo | undefined; + try { + managedKey = await context.agent.keyManagerImport({ + ...key, + kms: kms || that.defaultKms, + } satisfies MinimalImportableKey); + } catch (e) { + debug(`Failed to import key ${key.kid}. Reason: ${e}`); + + // construct key, if it failed to import + managedKey = { ...key, kms: kms || that.defaultKms }; + } + if (managedKey) { + scopedKeys.push(managedKey); + } } - } - return scopedKeys - }(this)) - : await this.getKeysFromVerificationMethod(context, options.document.verificationMethod) + return scopedKeys; + })(this) + : await this.getKeysFromVerificationMethod(context, options.document.verificationMethod); - const controllerKey: IKey = keys[0] + const controllerKey: IKey = keys[0]; const identifier: IIdentifier = { did: options.document.id, controllerKeyId: controllerKey.kid, keys, services: options.document.service || [], provider: options.document.id.split(':').splice(0, 3).join(':'), - } + }; - debug('Created DID', identifier.did) + debug('Created DID', identifier.did); - return identifier + return identifier; } async updateIdentifier( - { did, document, options}: { did: string, document: DIDDocument, options: { kms: string, keys?: TImportableEd25519Key[], versionId?: string, fee?: DidStdFee } }, - context: IContext, + { + did, + document, + options, + }: { + did: string; + document: DIDDocument; + options: { kms: string; keys?: TImportableEd25519Key[]; versionId?: string; fee?: DidStdFee }; + }, + context: IContext ): Promise { - const sdk = await this.getCheqdSDK(options?.fee) - const versionId = options.versionId || v4() - const signInputs: ISignInputs[] | SignInfo[] = options.keys - ? function (){ - return options.keys.map(key => createSignInputsFromImportableEd25519Key(key, document.verificationMethod || [])) - }() - : await (async function (that: CheqdDIDProvider){ - const data = await createMsgCreateDidDocPayloadToSign(document, versionId) - return await that.signPayload(context, data, document.verificationMethod) - }(this)) + const sdk = await this.getCheqdSDK(options?.fee); + const versionId = options.versionId || v4(); + const signInputs: ISignInputs[] | SignInfo[] = options.keys + ? (function () { + return options.keys.map((key) => + createSignInputsFromImportableEd25519Key(key, document.verificationMethod || []) + ); + })() + : await (async function (that: CheqdDIDProvider) { + const data = await createMsgCreateDidDocPayloadToSign(document, versionId); + return await that.signPayload(context, data, document.verificationMethod); + })(this); const tx = await sdk.updateDidDocTx( signInputs, @@ -279,40 +297,40 @@ export class CheqdDIDProvider extends AbstractIdentifierProvider { this?.fee, undefined, versionId, - { sdk: sdk } satisfies ISDKContext, - ) + { sdk: sdk } satisfies ISDKContext + ); - assert(tx.code === 0, `cosmos_transaction: Failed to update DID. Reason: ${tx.rawLog}`) + assert(tx.code === 0, `cosmos_transaction: Failed to update DID. Reason: ${tx.rawLog}`); //* Currently, only one controller key is supported. //* We assume that the first key in the list is the controller key. //* This is subject to change in the near future. - const keys: ManagedKeyInfo[] = options.keys + const keys: ManagedKeyInfo[] = options.keys ? await (async function (that: CheqdDIDProvider) { - const scopedKeys: ManagedKeyInfo[] = [] - for (const key of options.keys!) { - let managedKey: ManagedKeyInfo | undefined - try { - managedKey = await context.agent.keyManagerImport({ - ...key, - kms: options.kms || that.defaultKms, - } satisfies MinimalImportableKey) - } catch (e) { - debug(`Failed to import key ${key.kid}. Reason: ${e}`) - - // construct key, if it failed to import - managedKey = { ...key, kms: options.kms || that.defaultKms } - } - if (managedKey) { - scopedKeys.push(managedKey) + const scopedKeys: ManagedKeyInfo[] = []; + for (const key of options.keys!) { + let managedKey: ManagedKeyInfo | undefined; + try { + managedKey = await context.agent.keyManagerImport({ + ...key, + kms: options.kms || that.defaultKms, + } satisfies MinimalImportableKey); + } catch (e) { + debug(`Failed to import key ${key.kid}. Reason: ${e}`); + + // construct key, if it failed to import + managedKey = { ...key, kms: options.kms || that.defaultKms }; + } + if (managedKey) { + scopedKeys.push(managedKey); + } } - } - return scopedKeys - }(this)) - : await this.getKeysFromVerificationMethod(context, document.verificationMethod) + return scopedKeys; + })(this) + : await this.getKeysFromVerificationMethod(context, document.verificationMethod); - const controllerKey = keys[0] + const controllerKey = keys[0]; const identifier: IIdentifier = { did: document.id, @@ -320,27 +338,37 @@ export class CheqdDIDProvider extends AbstractIdentifierProvider { keys, services: document.service || [], provider: document.id.split(':').splice(0, 3).join(':'), - } + }; - debug('Updated DID', did) + debug('Updated DID', did); - return identifier + return identifier; } async deactivateIdentifier( - { did, document, options}: { did: string, document: DIDDocument, options: { keys?: TImportableEd25519Key[], fee?: DidStdFee, versionId?: string } }, - context: IContext, + { + did, + document, + options, + }: { + did: string; + document: DIDDocument; + options: { keys?: TImportableEd25519Key[]; fee?: DidStdFee; versionId?: string }; + }, + context: IContext ): Promise { - const sdk = await this.getCheqdSDK(options?.fee) - const versionId = options.versionId || v4() - const signInputs: ISignInputs[] | SignInfo[] = options.keys - ? function (){ - return options.keys.map(key => createSignInputsFromImportableEd25519Key(key, document.verificationMethod || [])) - }() - : await (async function (that: CheqdDIDProvider){ - const data = await createMsgDeactivateDidDocPayloadToSign(document, versionId) - return await that.signPayload(context, data, document.verificationMethod) - }(this)) + const sdk = await this.getCheqdSDK(options?.fee); + const versionId = options.versionId || v4(); + const signInputs: ISignInputs[] | SignInfo[] = options.keys + ? (function () { + return options.keys.map((key) => + createSignInputsFromImportableEd25519Key(key, document.verificationMethod || []) + ); + })() + : await (async function (that: CheqdDIDProvider) { + const data = await createMsgDeactivateDidDocPayloadToSign(document, versionId); + return await that.signPayload(context, data, document.verificationMethod); + })(this); const tx = await sdk.deactivateDidDocTx( signInputs, @@ -349,155 +377,151 @@ export class CheqdDIDProvider extends AbstractIdentifierProvider { this?.fee, undefined, versionId, - { sdk: sdk } satisfies ISDKContext, - ) + { sdk: sdk } satisfies ISDKContext + ); - assert(tx.code === 0, `cosmos_transaction: Failed to update DID. Reason: ${tx.rawLog}`) + assert(tx.code === 0, `cosmos_transaction: Failed to update DID. Reason: ${tx.rawLog}`); - debug('Deactivated DID', did) + debug('Deactivated DID', did); - return true + return true; } async createResource( - { options }: { options: { payload: ResourcePayload, signInputs?: ISignInputs[], kms?: string, fee?: DidStdFee } }, - context: IContext, + { + options, + }: { options: { payload: ResourcePayload; signInputs?: ISignInputs[]; kms?: string; fee?: DidStdFee } }, + context: IContext ): Promise { - const sdk = await this.getCheqdSDK(options?.fee) + const sdk = await this.getCheqdSDK(options?.fee); - const signInputs: ISignInputs[] | SignInfo[] = options.signInputs + const signInputs: ISignInputs[] | SignInfo[] = options.signInputs ? options.signInputs - : await (async function (that: CheqdDIDProvider){ - const did = `did:cheqd:${that.network}:${options.payload.collectionId}` - const { didDocument } = await sdk.queryDidDoc( - did, - { sdk: sdk } - ) - - return await that.signPayload( - context, - MsgCreateResourcePayload.encode(MsgCreateResourcePayload.fromPartial(options.payload)).finish(), - didDocument?.verificationMethod - ) - }(this)) - - const tx = await sdk.createLinkedResourceTx( - signInputs, - options.payload, - '', - this?.fee, - undefined, - { sdk: sdk } - ) + : await (async function (that: CheqdDIDProvider) { + const did = `did:cheqd:${that.network}:${options.payload.collectionId}`; + const { didDocument } = await sdk.queryDidDoc(did, { sdk: sdk }); + + return await that.signPayload( + context, + MsgCreateResourcePayload.encode(MsgCreateResourcePayload.fromPartial(options.payload)).finish(), + didDocument?.verificationMethod + ); + })(this); - assert(tx.code === 0, `cosmos_transaction: Failed to create Resource. Reason: ${tx.rawLog}`) + const tx = await sdk.createLinkedResourceTx(signInputs, options.payload, '', this?.fee, undefined, { + sdk: sdk, + }); - const mapKeyType = (keyType: "Ed25519" | "Secp256k1" | "P256" | undefined): TKeyType | undefined => { + assert(tx.code === 0, `cosmos_transaction: Failed to create Resource. Reason: ${tx.rawLog}`); + + const mapKeyType = (keyType: 'Ed25519' | 'Secp256k1' | 'P256' | undefined): TKeyType | undefined => { switch (keyType) { - case "Ed25519": return "Ed25519" - case "Secp256k1": return "Secp256k1" - default: return undefined + case 'Ed25519': + return 'Ed25519'; + case 'Secp256k1': + return 'Secp256k1'; + default: + return undefined; + } + }; + + if (options.signInputs) { + const signInput = options.signInputs.filter((input) => mapKeyType(input.keyType) !== undefined); + + const keys: ManagedKeyInfo[] = []; + for (const input of signInput) { + let managedKey: ManagedKeyInfo | undefined; + try { + // get public key from private key in hex + const publicKey = toString( + (await Ed25519.makeKeypair(fromString(input.privateKeyHex, 'hex'))).pubkey, + 'hex' + ); + managedKey = await context.agent.keyManagerImport({ + kid: publicKey, + publicKeyHex: publicKey, + privateKeyHex: input.privateKeyHex, + type: mapKeyType(input.keyType) as TSupportedKeyType, + kms: options.kms || this.defaultKms, + } satisfies MinimalImportableKey); + } catch (e) { + debug(`Failed to import key ${input.verificationMethodId}. Reason: ${e}`); + } + if (managedKey) { + keys.push(managedKey); + } } } - if(options.signInputs) { - const signInput = options.signInputs.filter(input => mapKeyType(input.keyType) !== undefined) - - const keys: ManagedKeyInfo[] = [] - for (const input of signInput) { - let managedKey: ManagedKeyInfo | undefined - try { - // get public key from private key in hex - const publicKey = toString((await Ed25519.makeKeypair(fromString(input.privateKeyHex, 'hex'))).pubkey, 'hex') - managedKey = await context.agent.keyManagerImport({ - kid: publicKey, - publicKeyHex: publicKey, - privateKeyHex: input.privateKeyHex, - type: mapKeyType(input.keyType) as TSupportedKeyType, - kms: options.kms || this.defaultKms, - } satisfies MinimalImportableKey) - } catch (e) { - debug(`Failed to import key ${input.verificationMethodId}. Reason: ${e}`) - } - if (managedKey) { - keys.push(managedKey) - } - } - } - - debug('Created Resource', options.payload) - - return true + debug('Created Resource', options.payload); + + return true; } - async deleteIdentifier( - identity: IIdentifier, - context: IContext, - ): Promise { + async deleteIdentifier(identity: IIdentifier, context: IContext): Promise { for (const { kid } of identity.keys) { - await context.agent.keyManagerDelete({ kid }) + await context.agent.keyManagerDelete({ kid }); } - return true + return true; } async addKey( - { - identifier, - key, - options, - }: { identifier: IIdentifier; key: IKey; options?: any }, - context: IContext, + { identifier, key, options }: { identifier: IIdentifier; key: IKey; options?: any }, + context: IContext ): Promise { - throw Error('CheqdDIDProvider addKey is not supported.') + throw Error('CheqdDIDProvider addKey is not supported.'); } async addService( - { - identifier, - service, - options, - }: { identifier: IIdentifier; service: IService; options?: any }, - context: IContext, + { identifier, service, options }: { identifier: IIdentifier; service: IService; options?: any }, + context: IContext ): Promise { - throw Error('CheqdDIDProvider addService is not supported.') + throw Error('CheqdDIDProvider addService is not supported.'); } async removeKey( args: { identifier: IIdentifier; kid: string; - options?: any + options?: any; }, - context: IContext, + context: IContext ): Promise { - throw Error('CheqdDIDProvider removeKey is not supported.') + throw Error('CheqdDIDProvider removeKey is not supported.'); } async removeService( args: { identifier: IIdentifier; id: string; - options?: any + options?: any; }, - context: IContext, + context: IContext ): Promise { - throw Error('CheqdDIDProvider removeService is not supported.') + throw Error('CheqdDIDProvider removeService is not supported.'); } - async transactSendTokens(args: { recipientAddress: string, amount: Coin, memo?: string, txBytes?: Uint8Array, timeoutMs?: number, pollIntervalMs?: number }): Promise { - const sdk = await this.getCheqdSDK(undefined, CheqdDIDProvider.defaultGasPrice) + async transactSendTokens(args: { + recipientAddress: string; + amount: Coin; + memo?: string; + txBytes?: Uint8Array; + timeoutMs?: number; + pollIntervalMs?: number; + }): Promise { + const sdk = await this.getCheqdSDK(undefined, CheqdDIDProvider.defaultGasPrice); if (args?.txBytes) { // broadcast txBytes - const tx = await sdk.signer.broadcastTx(args.txBytes, args?.timeoutMs, args?.pollIntervalMs) + const tx = await sdk.signer.broadcastTx(args.txBytes, args?.timeoutMs, args?.pollIntervalMs); // assert tx code is 0, in other words, tx succeeded - assert(tx.code === 0, `cosmos_transaction: Failed to send tokens. Reason: ${tx.rawLog}`) + assert(tx.code === 0, `cosmos_transaction: Failed to send tokens. Reason: ${tx.rawLog}`); // keep log - debug('Sent tokens', 'txBytes', toString(args.txBytes, 'hex')) + debug('Sent tokens', 'txBytes', toString(args.txBytes, 'hex')); - return tx + return tx; } const tx = await sdk.signer.sendTokens( @@ -505,46 +529,58 @@ export class CheqdDIDProvider extends AbstractIdentifierProvider { args.recipientAddress, [args.amount], 'auto', - args.memo, - ) + args.memo + ); - assert(tx.code === 0, `cosmos_transaction: Failed to send tokens. Reason: ${tx.rawLog}`) + assert(tx.code === 0, `cosmos_transaction: Failed to send tokens. Reason: ${tx.rawLog}`); - debug('Sent tokens', args.amount.amount, args.amount.denom, 'to', args.recipientAddress) + debug('Sent tokens', args.amount.amount, args.amount.denom, 'to', args.recipientAddress); - return tx + return tx; } - private async signPayload(context: IAgentContext, data: Uint8Array, verificationMethod: VerificationMethod[] = []): Promise { - return Promise.all( + private async signPayload( + context: IAgentContext, + data: Uint8Array, + verificationMethod: VerificationMethod[] = [] + ): Promise { + return Promise.all( verificationMethod.map(async (method) => { - const keyRef = extractPublicKeyHex(method) + const keyRef = extractPublicKeyHex(method); return { verificationMethodId: method.id, - signature: base64ToBytes(await context.agent.keyManagerSign({ - keyRef, - data: toString(data, 'hex'), - encoding: 'hex' - })) - } satisfies SignInfo + signature: base64ToBytes( + await context.agent.keyManagerSign({ + keyRef, + data: toString(data, 'hex'), + encoding: 'hex', + }) + ), + } satisfies SignInfo; }) - ) - } - - private async getKeysFromVerificationMethod(context: IAgentContext, verificationMethod: VerificationMethod[] = []): Promise { - return Promise.all( - verificationMethod.map(async (method)=>{ - const kid = extractPublicKeyHex(method) - return await context.agent.keyManagerGet({kid}) + ); + } + + private async getKeysFromVerificationMethod( + context: IAgentContext, + verificationMethod: VerificationMethod[] = [] + ): Promise { + return Promise.all( + verificationMethod.map(async (method) => { + const kid = extractPublicKeyHex(method); + return await context.agent.keyManagerGet({ kid }); }) - ).catch((error)=>{ - throw new Error(`Failed to sign payload: ${error}`) - }) - } + ).catch((error) => { + throw new Error(`Failed to sign payload: ${error}`); + }); + } } -export async function createMsgCreateDidDocPayloadToSign(didPayload: DIDDocument, versionId: string): Promise { - const { protobufVerificationMethod, protobufService } = await DIDModule.validateSpecCompliantPayload(didPayload) +export async function createMsgCreateDidDocPayloadToSign( + didPayload: DIDDocument, + versionId: string +): Promise { + const { protobufVerificationMethod, protobufService } = await DIDModule.validateSpecCompliantPayload(didPayload); return MsgCreateDidDocPayload.encode( MsgCreateDidDocPayload.fromPartial({ context: didPayload?.['@context'], @@ -560,14 +596,17 @@ export async function createMsgCreateDidDocPayloadToSign(didPayload: DIDDocument alsoKnownAs: didPayload.alsoKnownAs, versionId, }) - ).finish() + ).finish(); } -export async function createMsgDeactivateDidDocPayloadToSign(didPayload: DIDDocument, versionId?: string): Promise { +export async function createMsgDeactivateDidDocPayloadToSign( + didPayload: DIDDocument, + versionId?: string +): Promise { return MsgDeactivateDidDocPayload.encode( - MsgDeactivateDidDocPayload.fromPartial({ + MsgDeactivateDidDocPayload.fromPartial({ id: didPayload.id, versionId, }) - ).finish() -} \ No newline at end of file + ).finish(); +} diff --git a/src/did-manager/cheqd-did-resolver.ts b/src/did-manager/cheqd-did-resolver.ts index 5a7b3a54..06177cbe 100644 --- a/src/did-manager/cheqd-did-resolver.ts +++ b/src/did-manager/cheqd-did-resolver.ts @@ -1,29 +1,23 @@ -import { - DIDResolutionOptions, - DIDResolutionResult, - DIDResolver, - ParsedDID, - Resolvable, -} from 'did-resolver' +import { DIDResolutionOptions, DIDResolutionResult, DIDResolver, ParsedDID, Resolvable } from 'did-resolver'; interface Options { - url: string + url: string; } /** * Default resolver url. * @public */ -export const DefaultResolverUrl = 'https://resolver.cheqd.net/1.0/identifiers/' +export const DefaultResolverUrl = 'https://resolver.cheqd.net/1.0/identifiers/'; /** * Creates a CheqdDIDResolver instance that can be used with `did-resolver`. * @public */ export function getResolver(options?: Options): Record { - if (options?.url) return new CheqdDidResolver(options).build() + if (options?.url) return new CheqdDidResolver(options).build(); - return new CheqdDidResolver().build() + return new CheqdDidResolver().build(); } /** @@ -31,33 +25,33 @@ export function getResolver(options?: Options): Record { * @public */ export class CheqdDidResolver { - private resolverUrl = DefaultResolverUrl - - constructor(options?: Options) { - if (options?.url) this.resolverUrl = options.url - } - - async resolve( - did: string, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - parsed: ParsedDID, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - _unused: Resolvable, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - options: DIDResolutionOptions, - ): Promise { - try { - const result = await fetch(this.resolverUrl + did, { - headers: { 'Content-Type': 'application/did+json' }, - }) - const ddo = (await result.json()) as DIDResolutionResult - return ddo - } catch (e) { - return Promise.reject(e) - } - } - - build(): Record { - return { cheqd: this.resolve.bind(this) } - } + private resolverUrl = DefaultResolverUrl; + + constructor(options?: Options) { + if (options?.url) this.resolverUrl = options.url; + } + + async resolve( + did: string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + parsed: ParsedDID, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _unused: Resolvable, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + options: DIDResolutionOptions + ): Promise { + try { + const result = await fetch(this.resolverUrl + did, { + headers: { 'Content-Type': 'application/did+json' }, + }); + const ddo = (await result.json()) as DIDResolutionResult; + return ddo; + } catch (e) { + return Promise.reject(e); + } + } + + build(): Record { + return { cheqd: this.resolve.bind(this) }; + } } diff --git a/src/did-manager/index.ts b/src/did-manager/index.ts index 7c017c71..a4d9d4c6 100644 --- a/src/did-manager/index.ts +++ b/src/did-manager/index.ts @@ -1,3 +1,3 @@ -export * from './cheqd-did-provider.js' -export * from './cheqd-did-resolver.js' -export * from './resolver.js' \ No newline at end of file +export * from './cheqd-did-provider.js'; +export * from './cheqd-did-resolver.js'; +export * from './resolver.js'; diff --git a/src/did-manager/resolver.ts b/src/did-manager/resolver.ts index 8b743812..a9ceea95 100644 --- a/src/did-manager/resolver.ts +++ b/src/did-manager/resolver.ts @@ -1,16 +1,16 @@ -import { DIDResolver } from 'did-resolver' +import { DIDResolver } from 'did-resolver'; interface Options { - url: string + url: string; } /** * @deprecated please use `getUniresolver(url)` or `getUniresolverFor(methods, url)` instead */ export class CheqdUniversalResolver { - constructor(options: Options) { - return getUniversalResolver(options.url) - } + constructor(options: Options) { + return getUniversalResolver(options.url); + } } /** @@ -30,27 +30,25 @@ export class CheqdUniversalResolver { * @returns `DIDResolver` * @public */ -export function getUniversalResolver( - url = 'https://resolver.cheqd.net/1.0/identifiers/', -): DIDResolver { - if (!url) { - throw Error('[did-resolver] Universal: url required') - } +export function getUniversalResolver(url = 'https://resolver.cheqd.net/1.0/identifiers/'): DIDResolver { + if (!url) { + throw Error('[did-resolver] Universal: url required'); + } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const resolve: DIDResolver = async (didUrl: string): Promise => { - try { - const result = await fetch(url + didUrl, { - headers: { 'Content-Type': 'application/did+ld+json' }, - }) - const ddo = await result.json() - return ddo - } catch (e) { - return Promise.reject(e) - } - } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const resolve: DIDResolver = async (didUrl: string): Promise => { + try { + const result = await fetch(url + didUrl, { + headers: { 'Content-Type': 'application/did+ld+json' }, + }); + const ddo = await result.json(); + return ddo; + } catch (e) { + return Promise.reject(e); + } + }; - return resolve + return resolve; } /** @@ -70,13 +68,13 @@ export function getUniversalResolver( * @returns `Record` a mapping of the given methods to an instance of `DIDResolver` */ export function getUniversalResolverFor( - methods: string[], - url = 'https://resolver.cheqd.net/1.0/identifiers/', + methods: string[], + url = 'https://resolver.cheqd.net/1.0/identifiers/' ): Record { - const uniResolver = getUniversalResolver(url) - const mapping: Record = {} - for (const method of methods) { - mapping[method] = uniResolver - } - return mapping + const uniResolver = getUniversalResolver(url); + const mapping: Record = {}; + for (const method of methods) { + mapping[method] = uniResolver; + } + return mapping; } diff --git a/src/dkg-threshold/index.ts b/src/dkg-threshold/index.ts index 4e024b50..2a267cbd 100644 --- a/src/dkg-threshold/index.ts +++ b/src/dkg-threshold/index.ts @@ -1 +1 @@ -export * from './lit-protocol.js' \ No newline at end of file +export * from './lit-protocol.js'; diff --git a/src/dkg-threshold/lit-protocol.ts b/src/dkg-threshold/lit-protocol.ts index ba92584e..84f23443 100644 --- a/src/dkg-threshold/lit-protocol.ts +++ b/src/dkg-threshold/lit-protocol.ts @@ -1,251 +1,283 @@ -import { - OfflineAminoSigner, - Secp256k1HdWallet, - StdSignDoc -} from '@cosmjs/amino' -import { toString } from 'uint8arrays/to-string' -import { sha256 } from '@cosmjs/crypto' -import { - LitNodeClientNodeJs, - LitNodeClient, - decryptString, - encryptString, -} from '@lit-protocol/lit-node-client' -import { JsonSaveEncryptionKeyRequest } from '@lit-protocol/types' -import { randomBytes } from '../utils/helpers.js' -import { isBrowser, isNode } from '../utils/env.js' -import { v4 } from 'uuid' +import { OfflineAminoSigner, Secp256k1HdWallet, StdSignDoc } from '@cosmjs/amino'; +import { toString } from 'uint8arrays/to-string'; +import { sha256 } from '@cosmjs/crypto'; +import { LitNodeClientNodeJs, LitNodeClient, decryptString, encryptString } from '@lit-protocol/lit-node-client'; +import { JsonSaveEncryptionKeyRequest } from '@lit-protocol/types'; +import { randomBytes } from '../utils/helpers.js'; +import { isBrowser, isNode } from '../utils/env.js'; +import { v4 } from 'uuid'; export type EncryptionResult = { - encryptedString: Blob - encryptedSymmetricKey: string - symmetricKey?: Uint8Array -} + encryptedString: Blob; + encryptedSymmetricKey: string; + symmetricKey?: Uint8Array; +}; export type AuthSignature = { - sig: string - derivedVia: 'cosmos.signArbitrary', - signedMessage: string - address: string -} + sig: string; + derivedVia: 'cosmos.signArbitrary'; + signedMessage: string; + address: string; +}; export type CosmosAuthSignature = { - cosmos: AuthSignature -} + cosmos: AuthSignature; +}; export type CosmosReturnValueTest = { - key: string - comparator: string - value: string -} + key: string; + comparator: string; + value: string; +}; export type CosmosAccessControlCondition = { - conditionType: 'cosmos' - path: string - chain: LitCompatibleCosmosChain - method?: string - parameters?: string[] - returnValueTest: CosmosReturnValueTest -} + conditionType: 'cosmos'; + path: string; + chain: LitCompatibleCosmosChain; + method?: string; + parameters?: string[]; + returnValueTest: CosmosReturnValueTest; +}; export type SaveEncryptionKeyArgs = { - unifiedAccessControlConditions: CosmosAccessControlCondition[] - symmetricKey: CryptoKey - authSig: CosmosAuthSignature - chain: string -} + unifiedAccessControlConditions: CosmosAccessControlCondition[]; + symmetricKey: CryptoKey; + authSig: CosmosAuthSignature; + chain: string; +}; export type GetEncryptionKeyArgs = { - unifiedAccessControlConditions: CosmosAccessControlCondition[] - toDecrypt: string - authSig: CosmosAuthSignature - chain: string -} -export type EncryptStringMethodResult = { encryptedString: Blob, symmetricKey: Uint8Array } -export type DecryptStringMethodResult = string -export type EncryptStringMethod = (str: string) => Promise -export type DecryptStringMethod = (encryptedString: Blob, symmetricKey: Uint8Array) => Promise -export type LitNetwork = typeof LitNetworks[keyof typeof LitNetworks] -export type LitCompatibleCosmosChain = typeof LitCompatibleCosmosChains[keyof typeof LitCompatibleCosmosChains] -export type LitProtocolOptions = { cosmosAuthWallet: Secp256k1HdWallet, litNetwork?: LitNetwork, chain?: LitCompatibleCosmosChain } -export type TxNonceFormat = typeof TxNonceFormats[keyof typeof TxNonceFormats] - -export const LitNetworks = { jalapeno: 'jalapeno', serrano: 'serrano', localhost: 'localhost', custom: 'custom' } as const -export const LitCompatibleCosmosChains = { cosmos: 'cosmos', cheqdMainnet: 'cheqdMainnet', cheqdTestnet: 'cheqdTestnet' } as const -export const TxNonceFormats = { entropy: 'entropy', uuid: 'uuid', timestamp: 'timestamp' } as const + unifiedAccessControlConditions: CosmosAccessControlCondition[]; + toDecrypt: string; + authSig: CosmosAuthSignature; + chain: string; +}; +export type EncryptStringMethodResult = { encryptedString: Blob; symmetricKey: Uint8Array }; +export type DecryptStringMethodResult = string; +export type EncryptStringMethod = (str: string) => Promise; +export type DecryptStringMethod = ( + encryptedString: Blob, + symmetricKey: Uint8Array +) => Promise; +export type LitNetwork = (typeof LitNetworks)[keyof typeof LitNetworks]; +export type LitCompatibleCosmosChain = (typeof LitCompatibleCosmosChains)[keyof typeof LitCompatibleCosmosChains]; +export type LitProtocolOptions = { + cosmosAuthWallet: Secp256k1HdWallet; + litNetwork?: LitNetwork; + chain?: LitCompatibleCosmosChain; +}; +export type TxNonceFormat = (typeof TxNonceFormats)[keyof typeof TxNonceFormats]; + +export const LitNetworks = { + jalapeno: 'jalapeno', + serrano: 'serrano', + localhost: 'localhost', + custom: 'custom', +} as const; +export const LitCompatibleCosmosChains = { + cosmos: 'cosmos', + cheqdMainnet: 'cheqdMainnet', + cheqdTestnet: 'cheqdTestnet', +} as const; +export const TxNonceFormats = { entropy: 'entropy', uuid: 'uuid', timestamp: 'timestamp' } as const; export class LitProtocol { - client: LitNodeClientNodeJs | LitNodeClient - litNetwork: LitNetwork = LitNetworks.serrano - chain: LitCompatibleCosmosChain = LitCompatibleCosmosChains.cheqdTestnet - private readonly cosmosAuthWallet: Secp256k1HdWallet - - private constructor(options: LitProtocolOptions) { - // validate options - if (options.litNetwork && !Object.values(LitNetworks).includes(options.litNetwork)) throw new Error(`[did-provider-cheqd]: lit-protocol: Invalid LitNetwork: ${options.litNetwork}`) - if (options.chain && !Object.values(LitCompatibleCosmosChains).includes(options.chain)) throw new Error(`[did-provider-cheqd]: lit-protocol: Invalid LitCompatibleCosmosChain: ${options.chain}`) - - // set options - if (options.litNetwork) this.litNetwork = options.litNetwork - if (options.chain) this.chain = options.chain - this.cosmosAuthWallet = options.cosmosAuthWallet - - // set client as per environment - this.client = function(that: LitProtocol) { - if (isNode) return new LitNodeClientNodeJs({ litNetwork: that.litNetwork }) - if (isBrowser) return new LitNodeClient({ litNetwork: that.litNetwork }) - throw new Error('[did-provider-cheqd]: lit-protocol: Unsupported runtime environment') - }(this) - } - - async connect(): Promise { - return await this.client.connect() - } - - async encrypt(secret: string, unifiedAccessControlConditions: NonNullable, returnSymmetricKey = false): Promise { - const authSig = await LitProtocol.generateAuthSignature(this.cosmosAuthWallet) - const { encryptedString, symmetricKey } = await encryptString(secret as string) as EncryptStringMethodResult - const encryptedSymmetricKey = await this.client.saveEncryptionKey( - { - unifiedAccessControlConditions, - symmetricKey, - authSig: authSig, - chain: this.chain - } - ) - - return { - encryptedString, - encryptedSymmetricKey: toString(encryptedSymmetricKey, 'hex'), - symmetricKey: returnSymmetricKey ? symmetricKey : undefined - } - } - - async decrypt(encryptedString: Blob, encryptedSymmetricKey: string, unifiedAccessControlConditions: NonNullable): Promise { - const authSig = await LitProtocol.generateAuthSignature(this.cosmosAuthWallet) - const symmetricKey = await this.client.getEncryptionKey( - { - unifiedAccessControlConditions, - toDecrypt: encryptedSymmetricKey, - authSig: authSig, - chain: this.chain - } - ) - return await decryptString(encryptedString, symmetricKey) as DecryptStringMethodResult - } - - static async encryptDirect(secret: string): Promise { - const { encryptedString, symmetricKey } = await encryptString(secret as string) as EncryptStringMethodResult - return { - encryptedString, - symmetricKey - } - } - - static async decryptDirect(encryptedString: Blob, symmetricKey: Uint8Array): Promise { - return await decryptString(encryptedString, symmetricKey) as DecryptStringMethodResult - } - - static async create(options: Partial): Promise { - // instantiate underlying cosmos auth wallet - if (!options.cosmosAuthWallet) options.cosmosAuthWallet = await Secp256k1HdWallet.generate(24, { prefix: await LitProtocol.getCosmosWalletPrefix(options?.chain) }) - - // validate top-level options chain - if (!options?.chain) options.chain = LitCompatibleCosmosChains.cheqdTestnet - - // validate top-level options litNetwork - if (!options?.litNetwork) options.litNetwork = LitNetworks.serrano - - const litProtocol = new LitProtocol(options as LitProtocolOptions) - await litProtocol.connect() - return litProtocol - } - - static async getCosmosWalletPrefix(chain?: LitCompatibleCosmosChain): Promise { - switch (chain) { - case LitCompatibleCosmosChains.cosmos: - return 'cosmos' - case LitCompatibleCosmosChains.cheqdMainnet: - return 'cheqd' - case LitCompatibleCosmosChains.cheqdTestnet: - return 'cheqd' - default: - return 'cheqd' - } - } - - static async generateAuthSignature(wallet: OfflineAminoSigner): Promise { - const signerAddress = (await wallet.getAccounts())[0].address - const signData = await LitProtocol.generateSignData() - const signDoc = await LitProtocol.generateSignDoc(signerAddress, signData) - const result = await wallet.signAmino(signerAddress, signDoc) - return { - address: signerAddress, - derivedVia: 'cosmos.signArbitrary', - sig: result.signature.signature, - signedMessage: toString(sha256(new TextEncoder().encode(JSON.stringify(signDoc))), 'hex'), // <-- hex encoded sha256 hash of the json stringified signDoc - } - } - - static async generateSignDoc(address: string, data: Uint8Array): Promise { - return { - account_number: '0', - chain_id: '', - fee: { - amount: [], - gas: '0' - }, - memo: '', - msgs: [ - { - type: 'sign/MsgSignData', - value: { - data: toString(data, 'base64'), - signer: address, - } - } - ], - sequence: '0', - } // <-- should be sorted alphabetically - } - - static async generateSignData(): Promise { - return new TextEncoder().encode(`I am creating an account to use Lit Protocol at 2023-02-21T16:40:15.305Z`) // <-- lit nodes search for this string in the signData - } - - static async generateTxNonce(format?: TxNonceFormat, entropyLength?: number): Promise { - switch (format) { - case TxNonceFormats.entropy: - return toString(await randomBytes(entropyLength || 64), 'hex') - case TxNonceFormats.uuid: - return v4() - case TxNonceFormats.timestamp: - return new Date().toISOString() - default: - return v4() - } - } - - static async generateCosmosAccessControlConditionBalance(returnValueTest: CosmosReturnValueTest, chain: LitCompatibleCosmosChain = LitCompatibleCosmosChains.cheqdTestnet, address = ':userAddress'): Promise { - return { - conditionType: 'cosmos', - path: `/cosmos/bank/v1beta1/balances/${address}`, - chain, - returnValueTest - } - } - - static async generateCosmosAccessControlConditionTransactionMemo(returnValueTest: CosmosReturnValueTest, amount: string, sender: string, recipient = ':userAddress', chain: LitCompatibleCosmosChain = LitCompatibleCosmosChains.cheqdTestnet): Promise { - return { - conditionType: 'cosmos', - path: `/cosmos/tx/v1beta1/txs?events=transfer.recipient='${recipient}'&events=transfer.sender='${sender}'&events=transfer.amount='${amount}'&order_by=2`, - chain, - returnValueTest - } - } - - static async generateCosmosAccessControlConditionInverseTimelock(returnValueTest: CosmosReturnValueTest, amount: string, recipient = ':userAddress', blockHeight = 'latest', chain: LitCompatibleCosmosChain = LitCompatibleCosmosChains.cheqdTestnet): Promise { - return { - conditionType: 'cosmos', - path: `/cosmos/tx/v1beta1/txs?events=transfer.recipient='${recipient}'&events=transfer.amount='${amount}'&order_by=2&pagination.limit=1`, - chain, - method: 'timelock', - parameters: [blockHeight], - returnValueTest - } - } + client: LitNodeClientNodeJs | LitNodeClient; + litNetwork: LitNetwork = LitNetworks.serrano; + chain: LitCompatibleCosmosChain = LitCompatibleCosmosChains.cheqdTestnet; + private readonly cosmosAuthWallet: Secp256k1HdWallet; + + private constructor(options: LitProtocolOptions) { + // validate options + if (options.litNetwork && !Object.values(LitNetworks).includes(options.litNetwork)) + throw new Error(`[did-provider-cheqd]: lit-protocol: Invalid LitNetwork: ${options.litNetwork}`); + if (options.chain && !Object.values(LitCompatibleCosmosChains).includes(options.chain)) + throw new Error(`[did-provider-cheqd]: lit-protocol: Invalid LitCompatibleCosmosChain: ${options.chain}`); + + // set options + if (options.litNetwork) this.litNetwork = options.litNetwork; + if (options.chain) this.chain = options.chain; + this.cosmosAuthWallet = options.cosmosAuthWallet; + + // set client as per environment + this.client = (function (that: LitProtocol) { + if (isNode) return new LitNodeClientNodeJs({ litNetwork: that.litNetwork }); + if (isBrowser) return new LitNodeClient({ litNetwork: that.litNetwork }); + throw new Error('[did-provider-cheqd]: lit-protocol: Unsupported runtime environment'); + })(this); + } + + async connect(): Promise { + return await this.client.connect(); + } + + async encrypt( + secret: string, + unifiedAccessControlConditions: NonNullable, + returnSymmetricKey = false + ): Promise { + const authSig = await LitProtocol.generateAuthSignature(this.cosmosAuthWallet); + const { encryptedString, symmetricKey } = (await encryptString(secret as string)) as EncryptStringMethodResult; + const encryptedSymmetricKey = await this.client.saveEncryptionKey({ + unifiedAccessControlConditions, + symmetricKey, + authSig: authSig, + chain: this.chain, + }); + + return { + encryptedString, + encryptedSymmetricKey: toString(encryptedSymmetricKey, 'hex'), + symmetricKey: returnSymmetricKey ? symmetricKey : undefined, + }; + } + + async decrypt( + encryptedString: Blob, + encryptedSymmetricKey: string, + unifiedAccessControlConditions: NonNullable + ): Promise { + const authSig = await LitProtocol.generateAuthSignature(this.cosmosAuthWallet); + const symmetricKey = await this.client.getEncryptionKey({ + unifiedAccessControlConditions, + toDecrypt: encryptedSymmetricKey, + authSig: authSig, + chain: this.chain, + }); + return (await decryptString(encryptedString, symmetricKey)) as DecryptStringMethodResult; + } + + static async encryptDirect(secret: string): Promise { + const { encryptedString, symmetricKey } = (await encryptString(secret as string)) as EncryptStringMethodResult; + return { + encryptedString, + symmetricKey, + }; + } + + static async decryptDirect(encryptedString: Blob, symmetricKey: Uint8Array): Promise { + return (await decryptString(encryptedString, symmetricKey)) as DecryptStringMethodResult; + } + + static async create(options: Partial): Promise { + // instantiate underlying cosmos auth wallet + if (!options.cosmosAuthWallet) + options.cosmosAuthWallet = await Secp256k1HdWallet.generate(24, { + prefix: await LitProtocol.getCosmosWalletPrefix(options?.chain), + }); + + // validate top-level options chain + if (!options?.chain) options.chain = LitCompatibleCosmosChains.cheqdTestnet; + + // validate top-level options litNetwork + if (!options?.litNetwork) options.litNetwork = LitNetworks.serrano; + + const litProtocol = new LitProtocol(options as LitProtocolOptions); + await litProtocol.connect(); + return litProtocol; + } + + static async getCosmosWalletPrefix(chain?: LitCompatibleCosmosChain): Promise { + switch (chain) { + case LitCompatibleCosmosChains.cosmos: + return 'cosmos'; + case LitCompatibleCosmosChains.cheqdMainnet: + return 'cheqd'; + case LitCompatibleCosmosChains.cheqdTestnet: + return 'cheqd'; + default: + return 'cheqd'; + } + } + + static async generateAuthSignature(wallet: OfflineAminoSigner): Promise { + const signerAddress = (await wallet.getAccounts())[0].address; + const signData = await LitProtocol.generateSignData(); + const signDoc = await LitProtocol.generateSignDoc(signerAddress, signData); + const result = await wallet.signAmino(signerAddress, signDoc); + return { + address: signerAddress, + derivedVia: 'cosmos.signArbitrary', + sig: result.signature.signature, + signedMessage: toString(sha256(new TextEncoder().encode(JSON.stringify(signDoc))), 'hex'), // <-- hex encoded sha256 hash of the json stringified signDoc + }; + } + + static async generateSignDoc(address: string, data: Uint8Array): Promise { + return { + account_number: '0', + chain_id: '', + fee: { + amount: [], + gas: '0', + }, + memo: '', + msgs: [ + { + type: 'sign/MsgSignData', + value: { + data: toString(data, 'base64'), + signer: address, + }, + }, + ], + sequence: '0', + }; // <-- should be sorted alphabetically + } + + static async generateSignData(): Promise { + return new TextEncoder().encode(`I am creating an account to use Lit Protocol at 2023-02-21T16:40:15.305Z`); // <-- lit nodes search for this string in the signData + } + + static async generateTxNonce(format?: TxNonceFormat, entropyLength?: number): Promise { + switch (format) { + case TxNonceFormats.entropy: + return toString(await randomBytes(entropyLength || 64), 'hex'); + case TxNonceFormats.uuid: + return v4(); + case TxNonceFormats.timestamp: + return new Date().toISOString(); + default: + return v4(); + } + } + + static async generateCosmosAccessControlConditionBalance( + returnValueTest: CosmosReturnValueTest, + chain: LitCompatibleCosmosChain = LitCompatibleCosmosChains.cheqdTestnet, + address = ':userAddress' + ): Promise { + return { + conditionType: 'cosmos', + path: `/cosmos/bank/v1beta1/balances/${address}`, + chain, + returnValueTest, + }; + } + + static async generateCosmosAccessControlConditionTransactionMemo( + returnValueTest: CosmosReturnValueTest, + amount: string, + sender: string, + recipient = ':userAddress', + chain: LitCompatibleCosmosChain = LitCompatibleCosmosChains.cheqdTestnet + ): Promise { + return { + conditionType: 'cosmos', + path: `/cosmos/tx/v1beta1/txs?events=transfer.recipient='${recipient}'&events=transfer.sender='${sender}'&events=transfer.amount='${amount}'&order_by=2`, + chain, + returnValueTest, + }; + } + + static async generateCosmosAccessControlConditionInverseTimelock( + returnValueTest: CosmosReturnValueTest, + amount: string, + recipient = ':userAddress', + blockHeight = 'latest', + chain: LitCompatibleCosmosChain = LitCompatibleCosmosChains.cheqdTestnet + ): Promise { + return { + conditionType: 'cosmos', + path: `/cosmos/tx/v1beta1/txs?events=transfer.recipient='${recipient}'&events=transfer.amount='${amount}'&order_by=2&pagination.limit=1`, + chain, + method: 'timelock', + parameters: [blockHeight], + returnValueTest, + }; + } } diff --git a/src/global.d.ts b/src/global.d.ts index 2d540e79..509dc6d0 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -1 +1 @@ -declare module '@digitalbazaar/vc-status-list' \ No newline at end of file +declare module '@digitalbazaar/vc-status-list'; diff --git a/src/index.ts b/src/index.ts index f8b2f27f..04fa0c2a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,8 @@ /** * @public */ -export * from './did-manager/cheqd-did-provider.js' -export * from './did-manager/cheqd-did-resolver.js' -export * from './did-manager/resolver.js' -export * from './dkg-threshold/index.js' -export * from './agent/index.js' +export * from './did-manager/cheqd-did-provider.js'; +export * from './did-manager/cheqd-did-resolver.js'; +export * from './did-manager/resolver.js'; +export * from './dkg-threshold/index.js'; +export * from './agent/index.js'; diff --git a/src/utils/env.ts b/src/utils/env.ts index 5f10d9f9..449f243b 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -1,6 +1,18 @@ -export const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined' -export const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null -export const isWebWorker = typeof self === 'object' && self.constructor && self.constructor.name === 'DedicatedWorkerGlobalScope' -export const isJsDom = (typeof window !== 'undefined' && window.name === 'nodejs') || (typeof navigator !== 'undefined' && (navigator.userAgent.includes('jsdom') || navigator.userAgent.includes('Node.js'))) -export const isReactNative = typeof navigator !== 'undefined' && navigator.product === 'ReactNative' // use wisely: limited as of react-native v0.67 + navigator.product has been deprecated -export const isElectron = (typeof window !== 'undefined' && typeof window.process === 'object' && (window.process as NodeJS.Process & { type: 'browser' | 'renderer' | 'worker' | 'utility' }).type === 'renderer') || (typeof navigator === 'object' && typeof navigator.userAgent === 'string' && navigator.userAgent.indexOf('Electron') >= 0) || (typeof process === 'object' && typeof process.versions === 'object' && !!process.versions.electron) \ No newline at end of file +export const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined'; +export const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null; +export const isWebWorker = + typeof self === 'object' && self.constructor && self.constructor.name === 'DedicatedWorkerGlobalScope'; +export const isJsDom = + (typeof window !== 'undefined' && window.name === 'nodejs') || + (typeof navigator !== 'undefined' && + (navigator.userAgent.includes('jsdom') || navigator.userAgent.includes('Node.js'))); +export const isReactNative = typeof navigator !== 'undefined' && navigator.product === 'ReactNative'; // use wisely: limited as of react-native v0.67 + navigator.product has been deprecated +export const isElectron = + (typeof window !== 'undefined' && + typeof window.process === 'object' && + (window.process as NodeJS.Process & { type: 'browser' | 'renderer' | 'worker' | 'utility' }).type === + 'renderer') || + (typeof navigator === 'object' && + typeof navigator.userAgent === 'string' && + navigator.userAgent.indexOf('Electron') >= 0) || + (typeof process === 'object' && typeof process.versions === 'object' && !!process.versions.electron); diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 701f42eb..6f9dc3bb 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -1,66 +1,61 @@ -import { generate as generateSecret, type GenerateOptions } from 'generate-password' -import { toString } from 'uint8arrays/to-string' +import { generate as generateSecret, type GenerateOptions } from 'generate-password'; +import { toString } from 'uint8arrays/to-string'; export async function randomFromRange(min: number, max: number, notIn: number[]): Promise { - const random = Math.floor(Math.random() * (max - min + 1) + min) - if (notIn.includes(random)) { - return await randomFromRange(min, max, notIn) - } - return random + const random = Math.floor(Math.random() * (max - min + 1) + min); + if (notIn.includes(random)) { + return await randomFromRange(min, max, notIn); + } + return random; } export async function randomUniqueSubsetInRange(min: number, max: number, count: number): Promise> { - const subset: number[] = [] - for (let i = 0; i < count; i++) { - subset.push(await randomFromRange(min, max, subset)) - } - return subset + const subset: number[] = []; + for (let i = 0; i < count; i++) { + subset.push(await randomFromRange(min, max, subset)); + } + return subset; } export async function randomBytes(length: number): Promise { - return Buffer.from( - Array.from( - { length }, - () => Math.floor(Math.random() * 256), - ), - ) + return Buffer.from(Array.from({ length }, () => Math.floor(Math.random() * 256))); } export async function randomUniqueSecret(options?: GenerateOptions): Promise { - return generateSecret({ - length: 64, - numbers: true, - symbols: true, - uppercase: true, - ...options, - }) + return generateSecret({ + length: 64, + numbers: true, + symbols: true, + uppercase: true, + ...options, + }); } export async function initialiseIndexArray(length: number): Promise> { - return Array(length).fill(true) + return Array(length).fill(true); } export async function shuffleArray(array: Array): Promise> { - const shuffled = array.sort(() => Math.random() - 0.5) - return shuffled + const shuffled = array.sort(() => Math.random() - 0.5); + return shuffled; } export async function toBlob(data: Uint8Array): Promise { - return new Blob([data]) + return new Blob([data]); } export async function blobToHexString(blob: Blob): Promise { - // buffer from blob - const buffer = await blob.arrayBuffer() + // buffer from blob + const buffer = await blob.arrayBuffer(); - // convert buffer to uint8Array - const uint8Array = new Uint8Array(buffer) + // convert buffer to uint8Array + const uint8Array = new Uint8Array(buffer); - return toString(uint8Array, 'hex') + return toString(uint8Array, 'hex'); } export function unescapeUnicode(str: string): string { - return str.replace(/\\u([a-fA-F0-9]{4})/g, (m, cc) => { - return String.fromCharCode(parseInt(cc, 16)) - }) -} \ No newline at end of file + return str.replace(/\\u([a-fA-F0-9]{4})/g, (m, cc) => { + return String.fromCharCode(parseInt(cc, 16)); + }); +} diff --git a/src/utils/index.ts b/src/utils/index.ts index 1bd727d6..a59bb03e 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,2 +1,2 @@ -export * from './env.js' -export * from './helpers.js' \ No newline at end of file +export * from './env.js'; +export * from './helpers.js';