diff --git a/agent.yml b/agent.yml index 20946baf..482ce165 100644 --- a/agent.yml +++ b/agent.yml @@ -212,7 +212,7 @@ didCheqdMainnetProvider: $require: '@cheqd/did-provider-cheqd#CheqdDIDProvider' $args: - defaultKms: local - cosmosPayerMnemonic: 'your cosmos payer mnemonic' + cosmosPayerMnemonic: 'your cosmos payer mnemonic or private key in hex format' networkType: mainnet rpcUrl: 'https://rpc.cheqd.net' @@ -220,7 +220,7 @@ didCheqdTestnetProvider: $require: '@cheqd/did-provider-cheqd#CheqdDIDProvider' $args: - defaultKms: local - cosmosPayerMnemonic: 'your cosmos payer mnemonic' + cosmosPayerMnemonic: 'your cosmos payer mnemonic or private key in hex format' networkType: testnet rpcUrl: 'https://rpc.cheqd.network' @@ -286,5 +286,6 @@ agent: - $ref: /dbConnection - $require: '@cheqd/did-provider-cheqd#Cheqd' $args: - - provider: - $ref: /didCheqdTestnetProvider + - providers: + - $ref: /didCheqdTestnetProvider + - $ref: /didCheqdMainnetProvider diff --git a/package-lock.json b/package-lock.json index ebbf6687..4b3322a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,17 @@ { "name": "@cheqd/did-provider-cheqd", - "version": "1.7.20", + "version": "1.8.0-develop.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@cheqd/did-provider-cheqd", - "version": "1.7.20", + "version": "1.8.0-develop.1", "license": "Apache-2.0", "dependencies": { - "@cheqd/sdk": "^1.5.0-develop.1", - "@cheqd/ts-proto": "^1.0.16-develop.2", + "@cheqd/sdk": "^2.0.0-develop.1", + "@cheqd/ts-proto": "^2.0.0-develop.1", + "@cosmjs/crypto": "^0.29.5", "@cosmjs/proto-signing": "^0.29.4", "@cosmjs/utils": "^0.29.4", "@veramo/core": "^4.1.1", @@ -18,6 +19,7 @@ "@veramo/did-provider-key": "^4.1.1", "@veramo/key-manager": "^4.1.1", "debug": "^4.3.4", + "did-resolver": "^4.0.1", "multibase": "^4.0.6", "multicodec": "^3.2.1", "uint8arrays": "^3.1.1" @@ -2343,11 +2345,11 @@ } }, "node_modules/@cheqd/sdk": { - "version": "1.5.0-develop.1", - "resolved": "https://registry.npmjs.org/@cheqd/sdk/-/sdk-1.5.0-develop.1.tgz", - "integrity": "sha512-zrj9oQkUSfZ6u8Hsl2+vpwgm6w6R3wOnHUOoKe/6TwwHMp9mYp9zifmAe/n7WJH9JcOzhCKWlnMo8iXrnKuMrQ==", + "version": "2.0.0-develop.1", + "resolved": "https://registry.npmjs.org/@cheqd/sdk/-/sdk-2.0.0-develop.1.tgz", + "integrity": "sha512-cDuiSKzwJEODjcGgyiWJNvZoS0fwKIQrBOQ9EJgFF/VP96VcfOYozxvma9QO5XAzh+8+G0aSKoi6nXAv3jFbOw==", "dependencies": { - "@cheqd/ts-proto": "^1.0.16-develop.2", + "@cheqd/ts-proto": "^2.0.0-develop.1", "@cosmjs/amino": "^0.29.4", "@cosmjs/encoding": "^0.29.4", "@cosmjs/math": "^0.29.4", @@ -2358,14 +2360,15 @@ "@stablelib/ed25519": "^1.0.3", "cosmjs-types": "^0.5.2", "did-jwt": "^6.9.0", + "did-resolver": "^4.0.1", "multiformats": "^9.9.0", "uuid": "^9.0.0" } }, "node_modules/@cheqd/ts-proto": { - "version": "1.0.16-develop.2", - "resolved": "https://registry.npmjs.org/@cheqd/ts-proto/-/ts-proto-1.0.16-develop.2.tgz", - "integrity": "sha512-bx44Fe0Q/tfrGGfkt46Lx0Pjoo9gM1mfgAB54bPt1MEqM6lsgczP+SYINEwOIW8clwd6P6Ks6SVKetU2piIAuw==", + "version": "2.0.0-develop.1", + "resolved": "https://registry.npmjs.org/@cheqd/ts-proto/-/ts-proto-2.0.0-develop.1.tgz", + "integrity": "sha512-+dsHyZABC1spbkTjO1dGJOF20sHUumnDIoKGZtrWiu15iIa9vg2IAgKKVyLc7aBs04dxVs/888KN03zAWJvyJQ==", "dependencies": { "long": "^5.2.1", "protobufjs": "~7.1.1" @@ -2437,13 +2440,13 @@ } }, "node_modules/@cosmjs/crypto": { - "version": "0.29.4", - "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.29.4.tgz", - "integrity": "sha512-PmSxoFl/Won7kHZv3PQUUgdmEiAMqdY7XnEnVh9PbU7Hht6uo7PQ+M0eIGW3NIXYKmn6oVExER+xOfLfq4YNGw==", + "version": "0.29.5", + "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.29.5.tgz", + "integrity": "sha512-2bKkaLGictaNL0UipQCL6C1afaisv6k8Wr/GCLx9FqiyFkh9ZgRHDyetD64ZsjnWV/N/D44s/esI+k6oPREaiQ==", "dependencies": { - "@cosmjs/encoding": "^0.29.4", - "@cosmjs/math": "^0.29.4", - "@cosmjs/utils": "^0.29.4", + "@cosmjs/encoding": "^0.29.5", + "@cosmjs/math": "^0.29.5", + "@cosmjs/utils": "^0.29.5", "@noble/hashes": "^1", "bn.js": "^5.2.0", "elliptic": "^6.5.4", @@ -2451,9 +2454,9 @@ } }, "node_modules/@cosmjs/encoding": { - "version": "0.29.4", - "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.29.4.tgz", - "integrity": "sha512-nlwCh4j+kIqEcwNu8AFSmqXGj0bvF4nLC3J1X0eJyJenlgJBiiAGjYp3nxMf/ZjKkZP65Fq7MXVtAYs3K8xvvQ==", + "version": "0.29.5", + "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.29.5.tgz", + "integrity": "sha512-G4rGl/Jg4dMCw5u6PEZHZcoHnUBlukZODHbm/wcL4Uu91fkn5jVo5cXXZcvs4VCkArVGrEj/52eUgTZCmOBGWQ==", "dependencies": { "base64-js": "^1.3.0", "bech32": "^1.1.4", @@ -2470,9 +2473,9 @@ } }, "node_modules/@cosmjs/math": { - "version": "0.29.4", - "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.29.4.tgz", - "integrity": "sha512-IvT1Cj3qOMGqz7v5FxdDCBEIDL2k9m5rufrkuD4oL9kS79ebnhA0lquX6ApPubUohTXl+5PnLo02W8HEH6Stkg==", + "version": "0.29.5", + "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.29.5.tgz", + "integrity": "sha512-2GjKcv+A9f86MAWYLUkjhw1/WpRl2R1BTb3m9qPG7lzMA7ioYff9jY5SPCfafKdxM4TIQGxXQlYGewQL16O68Q==", "dependencies": { "bn.js": "^5.2.0" } @@ -2587,9 +2590,9 @@ } }, "node_modules/@cosmjs/utils": { - "version": "0.29.4", - "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.29.4.tgz", - "integrity": "sha512-X1pZWRHDbTPLa6cYW0NHvtig+lSxOdLAX7K/xp67ywBy2knnDOyzz1utGTOowmiM98XuV9quK/BWePKkJOaHpQ==" + "version": "0.29.5", + "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.29.5.tgz", + "integrity": "sha512-m7h+RXDUxOzEOGt4P+3OVPX7PuakZT3GBmaM/Y2u+abN3xZkziykD/NvedYFvvCCdQo714XcGl33bwifS9FZPQ==" }, "node_modules/@did-core/data-model": { "version": "0.1.1-unstable.15", @@ -28948,11 +28951,11 @@ "integrity": "sha512-R524tD5VwOt3QRHr7N518nqTVR/HKgfWL4LypekcGuNQN8R4PWScvuRcRzrY39A28kLztMv+TJdiKuMNbkU1ug==" }, "@cheqd/sdk": { - "version": "1.5.0-develop.1", - "resolved": "https://registry.npmjs.org/@cheqd/sdk/-/sdk-1.5.0-develop.1.tgz", - "integrity": "sha512-zrj9oQkUSfZ6u8Hsl2+vpwgm6w6R3wOnHUOoKe/6TwwHMp9mYp9zifmAe/n7WJH9JcOzhCKWlnMo8iXrnKuMrQ==", + "version": "2.0.0-develop.1", + "resolved": "https://registry.npmjs.org/@cheqd/sdk/-/sdk-2.0.0-develop.1.tgz", + "integrity": "sha512-cDuiSKzwJEODjcGgyiWJNvZoS0fwKIQrBOQ9EJgFF/VP96VcfOYozxvma9QO5XAzh+8+G0aSKoi6nXAv3jFbOw==", "requires": { - "@cheqd/ts-proto": "^1.0.16-develop.2", + "@cheqd/ts-proto": "^2.0.0-develop.1", "@cosmjs/amino": "^0.29.4", "@cosmjs/encoding": "^0.29.4", "@cosmjs/math": "^0.29.4", @@ -28963,14 +28966,15 @@ "@stablelib/ed25519": "^1.0.3", "cosmjs-types": "^0.5.2", "did-jwt": "^6.9.0", + "did-resolver": "^4.0.1", "multiformats": "^9.9.0", "uuid": "^9.0.0" } }, "@cheqd/ts-proto": { - "version": "1.0.16-develop.2", - "resolved": "https://registry.npmjs.org/@cheqd/ts-proto/-/ts-proto-1.0.16-develop.2.tgz", - "integrity": "sha512-bx44Fe0Q/tfrGGfkt46Lx0Pjoo9gM1mfgAB54bPt1MEqM6lsgczP+SYINEwOIW8clwd6P6Ks6SVKetU2piIAuw==", + "version": "2.0.0-develop.1", + "resolved": "https://registry.npmjs.org/@cheqd/ts-proto/-/ts-proto-2.0.0-develop.1.tgz", + "integrity": "sha512-+dsHyZABC1spbkTjO1dGJOF20sHUumnDIoKGZtrWiu15iIa9vg2IAgKKVyLc7aBs04dxVs/888KN03zAWJvyJQ==", "requires": { "long": "^5.2.1", "protobufjs": "~7.1.1" @@ -29036,13 +29040,13 @@ } }, "@cosmjs/crypto": { - "version": "0.29.4", - "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.29.4.tgz", - "integrity": "sha512-PmSxoFl/Won7kHZv3PQUUgdmEiAMqdY7XnEnVh9PbU7Hht6uo7PQ+M0eIGW3NIXYKmn6oVExER+xOfLfq4YNGw==", + "version": "0.29.5", + "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.29.5.tgz", + "integrity": "sha512-2bKkaLGictaNL0UipQCL6C1afaisv6k8Wr/GCLx9FqiyFkh9ZgRHDyetD64ZsjnWV/N/D44s/esI+k6oPREaiQ==", "requires": { - "@cosmjs/encoding": "^0.29.4", - "@cosmjs/math": "^0.29.4", - "@cosmjs/utils": "^0.29.4", + "@cosmjs/encoding": "^0.29.5", + "@cosmjs/math": "^0.29.5", + "@cosmjs/utils": "^0.29.5", "@noble/hashes": "^1", "bn.js": "^5.2.0", "elliptic": "^6.5.4", @@ -29050,9 +29054,9 @@ } }, "@cosmjs/encoding": { - "version": "0.29.4", - "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.29.4.tgz", - "integrity": "sha512-nlwCh4j+kIqEcwNu8AFSmqXGj0bvF4nLC3J1X0eJyJenlgJBiiAGjYp3nxMf/ZjKkZP65Fq7MXVtAYs3K8xvvQ==", + "version": "0.29.5", + "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.29.5.tgz", + "integrity": "sha512-G4rGl/Jg4dMCw5u6PEZHZcoHnUBlukZODHbm/wcL4Uu91fkn5jVo5cXXZcvs4VCkArVGrEj/52eUgTZCmOBGWQ==", "requires": { "base64-js": "^1.3.0", "bech32": "^1.1.4", @@ -29069,9 +29073,9 @@ } }, "@cosmjs/math": { - "version": "0.29.4", - "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.29.4.tgz", - "integrity": "sha512-IvT1Cj3qOMGqz7v5FxdDCBEIDL2k9m5rufrkuD4oL9kS79ebnhA0lquX6ApPubUohTXl+5PnLo02W8HEH6Stkg==", + "version": "0.29.5", + "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.29.5.tgz", + "integrity": "sha512-2GjKcv+A9f86MAWYLUkjhw1/WpRl2R1BTb3m9qPG7lzMA7ioYff9jY5SPCfafKdxM4TIQGxXQlYGewQL16O68Q==", "requires": { "bn.js": "^5.2.0" } @@ -29185,9 +29189,9 @@ } }, "@cosmjs/utils": { - "version": "0.29.4", - "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.29.4.tgz", - "integrity": "sha512-X1pZWRHDbTPLa6cYW0NHvtig+lSxOdLAX7K/xp67ywBy2knnDOyzz1utGTOowmiM98XuV9quK/BWePKkJOaHpQ==" + "version": "0.29.5", + "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.29.5.tgz", + "integrity": "sha512-m7h+RXDUxOzEOGt4P+3OVPX7PuakZT3GBmaM/Y2u+abN3xZkziykD/NvedYFvvCCdQo714XcGl33bwifS9FZPQ==" }, "@did-core/data-model": { "version": "0.1.1-unstable.15", diff --git a/package.json b/package.json index 8f076bb2..7955c87f 100644 --- a/package.json +++ b/package.json @@ -45,8 +45,9 @@ ] }, "dependencies": { - "@cheqd/sdk": "^1.5.0-develop.1", - "@cheqd/ts-proto": "^1.0.16-develop.2", + "@cheqd/sdk": "^2.0.0-develop.1", + "@cheqd/ts-proto": "^2.0.0-develop.1", + "@cosmjs/crypto": "^0.29.5", "@cosmjs/proto-signing": "^0.29.4", "@cosmjs/utils": "^0.29.4", "@veramo/core": "^4.1.1", @@ -54,6 +55,7 @@ "@veramo/did-provider-key": "^4.1.1", "@veramo/key-manager": "^4.1.1", "debug": "^4.3.4", + "did-resolver": "^4.0.1", "multibase": "^4.0.6", "multicodec": "^3.2.1", "uint8arrays": "^3.1.1" diff --git a/src/agent/ICheqd.ts b/src/agent/ICheqd.ts index e0a6d379..98c1ed04 100644 --- a/src/agent/ICheqd.ts +++ b/src/agent/ICheqd.ts @@ -1,5 +1,21 @@ -import { CheqdNetwork, IKeyPair, MethodSpecificIdAlgo, VerificationMethods } from '@cheqd/sdk/build/types' -import { createDidPayload, createDidVerificationMethod, createKeyPairBase64, createKeyPairHex, createVerificationKeys } from '@cheqd/sdk' +/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars, @typescript-eslint/no-non-null-assertion */ +// any is used for extensibility +// 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, + IKeyPair, + MethodSpecificIdAlgo, + VerificationMethods +} from '@cheqd/sdk/build/types' +import { + createDidPayload, + createDidVerificationMethod, + createKeyPairBase64, + createKeyPairHex, + createVerificationKeys +} from '@cheqd/sdk' import { IAgentContext, IKeyManager, @@ -8,14 +24,15 @@ import { IAgentPluginSchema, IIdentifier } from '@veramo/core' -import { CheqdDIDProvider, IdentifierPayload, TImportableEd25519Key } from '../did-manager/cheqd-did-provider'; +import { CheqdDIDProvider, TImportableEd25519Key } from '../did-manager/cheqd-did-provider'; import { fromString, toString } from 'uint8arrays' type IContext = IAgentContext -type TExportedDIDDocWithKeys = { didDoc: IdentifierPayload, keys: TImportableEd25519Key } +type TExportedDIDDocWithKeys = { didDoc: DIDDocument, keys: TImportableEd25519Key[] } const CreateIdentifierMethodName = 'cheqdCreateIdentifier' const UpdateIdentifierMethodName = 'cheqdUpdateIdentifier' +const DeactivateIdentifierMethodName = 'cheqdDeactivateIdentifier' const CreateResourceMethodName = 'cheqdCreateResource' const GenerateDidDocMethodName = 'cheqdGenerateDidDoc' const GenerateKeyPairMethodName = 'cheqdGenerateIdentityKeys' @@ -26,6 +43,7 @@ const CheqdDidMethod = 'cheqd' export interface ICheqd extends IPluginMethodMap { [CreateIdentifierMethodName]: (args: any, context: IContext) => Promise> [UpdateIdentifierMethodName]: (args: any, context: IContext) => Promise, + [DeactivateIdentifierMethodName]: (args: any, context: IContext) => Promise, [CreateResourceMethodName]: (args: any, context: IContext) => Promise, [GenerateDidDocMethodName]: (args: any, context: IContext) => Promise, [GenerateKeyPairMethodName]: (args: any, context: IContext) => Promise @@ -73,6 +91,24 @@ export class Cheqd implements IAgentPlugin { "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" + } + }, "cheqdCreateResource": { "description": "Create a new resource", "arguments": { @@ -127,20 +163,23 @@ export class Cheqd implements IAgentPlugin { } } } - readonly didProvider: CheqdDIDProvider; - readonly providerId: string; + private readonly supportedDidProviders: CheqdDIDProvider[] + private didProvider: CheqdDIDProvider; + private providerId: string; - constructor(args: { provider: CheqdDIDProvider }) { - if (typeof args.provider !== 'object') { - throw new Error('[cheqd-plugin]: provider is required') + constructor(args: { providers: CheqdDIDProvider[] }) { + if (typeof args.providers !== 'object') { + throw new Error('[did-provider-cheqd]: at least one did provider is required') } - this.didProvider = args.provider - this.providerId = `${DidPrefix}:${CheqdDidMethod}:${this.didProvider.network}` + 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), [GenerateDidDocMethodName]: this.GenerateDidDoc.bind(this), [GenerateKeyPairMethodName]: this.GenerateIdentityKeys.bind(this) @@ -149,71 +188,116 @@ export class Cheqd implements IAgentPlugin { private async CreateIdentifier(args: any, context: IContext): Promise> { if (typeof args.kms !== 'string') { - throw new Error('[cheqd-plugin]: kms is required') + throw new Error('[did-provider-cheqd]: kms is required') } if (typeof args.alias !== 'string') { - throw new Error('[cheqd-plugin]: alias is required') + throw new Error('[did-provider-cheqd]: alias is required') } if (typeof args.document !== 'object') { - throw new Error('[cheqd-plugin]: document object is required') + throw new Error('[did-provider-cheqd]: document object is required') } if (typeof args.keys !== 'object') { - throw new Error('[cheqd-plugin]: keys array is required') + throw new Error('[did-provider-cheqd]: keys array is required') } + const provider = await Cheqd.loadProvider(document as unknown as DIDDocument, 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 + keys: args.keys, + fee: args?.fee } }) } private async UpdateIdentifier(args: any, context: IContext) { if (typeof args.kms !== 'string') { - throw new Error('[cheqd-plugin]: kms is required') + throw new Error('[did-provider-cheqd]: kms is required') } if (typeof args.did !== 'string') { - throw new Error('[cheqd-plugin]: did is required') + throw new Error('[did-provider-cheqd]: did is required') } if (typeof args.document !== 'object') { - throw new Error('[cheqd-plugin]: document object is required') + throw new Error('[did-provider-cheqd]: document object is required') } if (typeof args.keys !== 'object') { - throw new Error('[cheqd-plugin]: keys array is required') + throw new Error('[did-provider-cheqd]: keys array is required') } + const provider = await Cheqd.loadProvider(document as unknown as DIDDocument, this.supportedDidProviders) + + this.didProvider = provider + this.providerId = Cheqd.generateProviderId(this.didProvider.network) + return await context.agent.didManagerUpdate({ did: args.did, document: args.document, provider: this.providerId, options: { kms: args.kms, - keys: args.keys + keys: args.keys, + fee: args?.fee } }) } + private async DeactivateIdentifier(args: any, context: IContext) { + if (typeof args.kms !== 'string') { + throw new Error('[did-provider-cheqd]: kms is required') + } + + if (typeof args.did !== 'string') { + throw new Error('[did-provider-cheqd]: did is required') + } + + if (typeof args.document !== 'object') { + throw new Error('[did-provider-cheqd]: document object is required') + } + + if (typeof args.keys !== 'object') { + throw new Error('[did-provider-cheqd]: keys array is required') + } + + const provider = await Cheqd.loadProvider(document as unknown as DIDDocument, this.supportedDidProviders) + + this.didProvider = provider + this.providerId = Cheqd.generateProviderId(this.didProvider.network) + + return await this.didProvider.deactivateIdentifier({ + did: args.did, + document: args.document, + options: { + kms: args.kms, + keys: args.keys, + fee: args?.fee + } + }, context) + } + private async CreateResource(args: any, context: IContext) { if (typeof args.kms !== 'string') { - throw new Error('[cheqd-plugin]: kms is required') + throw new Error('[did-provider-cheqd]: kms is required') } if (typeof args.payload !== 'object') { - throw new Error('[cheqd-plugin]: payload object is required') + throw new Error('[did-provider-cheqd]: payload object is required') } if (typeof args.payload !== 'object') { - throw new Error('[cheqd-plugin]: payload object is required') + throw new Error('[did-provider-cheqd]: payload object is required') } return await this.didProvider.createResource({ @@ -226,43 +310,40 @@ export class Cheqd implements IAgentPlugin { } private async GenerateDidDoc( - args: { verificationMethod: VerificationMethods, methodSpecificIdAlgo: MethodSpecificIdAlgo, methodSpecificIdLength: 16 | 32, network: CheqdNetwork }, - // eslint-disable-next-line @typescript-eslint/no-unused-vars + args: { verificationMethod: VerificationMethods, methodSpecificIdAlgo: MethodSpecificIdAlgo, network: CheqdNetwork }, context: IContext ): Promise { if (typeof args.verificationMethod !== 'string') { - throw new Error('[cheqd-plugin]: verificationMethod is required') + throw new Error('[did-provider-cheqd]: verificationMethod is required') } if (typeof args.methodSpecificIdAlgo !== 'string') { - throw new Error('[cheqd-plugin]: methodSpecificIdAlgo is required') - } - - if (typeof args.methodSpecificIdLength !== 'number') { - throw new Error('[cheqd-plugin]: methodSpecificIdLength is required') + throw new Error('[did-provider-cheqd]: methodSpecificIdAlgo is required') } if (typeof args.network !== 'string') { - throw new Error('[cheqd-plugin]: network is required') + 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, args.methodSpecificIdAlgo, 'key-1', args.methodSpecificIdLength, args.network) + const verificationKeys = createVerificationKeys(keyPair.publicKey, args.methodSpecificIdAlgo, 'key-1', args.network) const verificationMethods = createDidVerificationMethod([args.verificationMethod], [verificationKeys]) return { didDoc: createDidPayload(verificationMethods, [verificationKeys]), - keys: { - publicKeyHex: keyPairHex.publicKey, - privateKeyHex: keyPairHex.privateKey, - kid: keyPairHex.publicKey, - type: 'Ed25519' - } + keys: [ + { + publicKeyHex: keyPairHex.publicKey, + privateKeyHex: keyPairHex.privateKey, + kid: keyPairHex.publicKey, + type: 'Ed25519' + } + ] } } - // eslint-disable-next-line @typescript-eslint/no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any private async GenerateIdentityKeys(args: any, context: IContext): Promise { const keyPair = createKeyPairHex() return { @@ -272,4 +353,16 @@ export class Cheqd implements IAgentPlugin { type: 'Ed25519' } } + + static async loadProvider(document: DIDDocument, providers: CheqdDIDProvider[]): Promise { + const provider = providers.find((provider) => document.id.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}` + } } diff --git a/src/did-manager/cheqd-did-provider.ts b/src/did-manager/cheqd-did-provider.ts index d3bc3818..ac2a775c 100644 --- a/src/did-manager/cheqd-did-provider.ts +++ b/src/did-manager/cheqd-did-provider.ts @@ -1,7 +1,10 @@ +/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars, @typescript-eslint/no-non-null-assertion */ +// any is used for extensibility +// 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 { CheqdSDK, createCheqdSDK, createSignInputsFromImportableEd25519Key, DIDModule, ICheqdSDKOptions, ResourceModule } from '@cheqd/sdk' import { AbstractCheqdSDKModule } from '@cheqd/sdk/build/modules/_' -import { DidStdFee, ISignInputs } from '@cheqd/sdk/build/types' -import { MsgCreateDidDocPayload, MsgUpdateDidDocPayload, Service, VerificationMethod } from '@cheqd/ts-proto/cheqd/did/v2' +import { VerificationMethod, DidStdFee, ISignInputs, IContext as ISDKContext } from '@cheqd/sdk/build/types' import { MsgCreateResourcePayload } from '@cheqd/ts-proto/cheqd/resource/v2' import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing' import { assert } from '@cosmjs/utils' @@ -18,6 +21,8 @@ import { } from '@veramo/core' import { AbstractIdentifierProvider } from '@veramo/did-manager' import Debug from 'debug' +import { Bip39, EnglishMnemonic as _ } from '@cosmjs/crypto' +import { fromString } from 'uint8arrays/from-string' const debug = Debug('veramo:did-provider-cheqd') @@ -33,14 +38,16 @@ export enum NetworkType { Testnet = "testnet" } -export type IdentifierPayload = Partial | Partial - export type ResourcePayload = Partial export type TImportableEd25519Key = Required> & { kid: TImportableEd25519Key['publicKeyHex'], type: 'Ed25519' } export type TSupportedKeyType = 'Ed25519' | 'Secp256k1' +export class EnglishMnemonic extends _ { + static readonly _mnemonicMatcher = /^[a-z]+( [a-z]+)*$/; +} + /** * {@link @veramo/did-manager#DIDManager} identifier provider for `did:cheqd` identifiers. * @public @@ -53,65 +60,68 @@ export class CheqdDIDProvider extends AbstractIdentifierProvider { private sdk?: CheqdSDK private fee?: DidStdFee - constructor(options: { defaultKms: string, cosmosPayerMnemonic: string, networkType?: NetworkType, rpcUrl?: string }) { + constructor(options: { defaultKms: string, cosmosPayerSeed: string, networkType?: NetworkType, rpcUrl?: string }) { super() this.defaultKms = options.defaultKms - this.cosmosPayerWallet = DirectSecp256k1HdWallet.fromMnemonic(options.cosmosPayerMnemonic, { prefix: 'cheqd' }) this.network = options.networkType ? options.networkType : NetworkType.Testnet this.rpcUrl = options.rpcUrl ? options.rpcUrl : (this.network === NetworkType.Testnet ? DefaultRPCUrl.Testnet : DefaultRPCUrl.Mainnet) - } - /** - * 1. Check if SDK - * 2. If not, instantiate and pass around - * 3. Try creating the DID from the raw payload - * 4. Throw if it fails - * 5. If it succeeds, print the DID - * 6. Store the keys in the key manager - * 7. Return the DID implementing IIdentifier - */ + if (!options?.cosmosPayerSeed || options.cosmosPayerSeed === '') { + this.cosmosPayerWallet = Promise.reject('cosmosPayerSeed is required') + return + } + this.cosmosPayerWallet = EnglishMnemonic._mnemonicMatcher.test(options.cosmosPayerSeed) + ? DirectSecp256k1HdWallet.fromMnemonic(options.cosmosPayerSeed, { prefix: 'cheqd' }) + : DirectSecp256k1HdWallet.fromMnemonic( + Bip39.encode( + fromString( + options.cosmosPayerSeed.replace(/^0x/, ''), + 'hex' + ) + ).toString(), + { prefix: 'cheqd' } + ) + } private async getCheqdSDK(fee?: DidStdFee): Promise { if (!this.sdk) { + const wallet = await this.cosmosPayerWallet.catch(() => { + throw new Error(`[did-provider-cheqd]: network: ${this.network} valid cosmosPayerSeed is required`) + }) const sdkOptions: ICheqdSDKOptions = { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - No actual type insufficiency here. Learn more about this in the docs. modules: [DIDModule as unknown as AbstractCheqdSDKModule, ResourceModule as unknown as AbstractCheqdSDKModule], rpcUrl: this.rpcUrl, - wallet: await this.cosmosPayerWallet, + wallet: wallet, } this.sdk = await createCheqdSDK(sdkOptions) - this.fee = fee || { - amount: [ - { - denom: 'ncheq', - amount: '5000000000' - } - ], - gas: '200000', - payer: (await sdkOptions.wallet.getAccounts())[0].address, - } + this.fee = fee } - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return this.sdk! } async createIdentifier( - { kms, options }: { kms?: string; alias?: string, options: { document: IdentifierPayload, keys: TImportableEd25519Key[] } }, + { kms, options }: { kms?: string; alias?: string, options: { document: DIDDocument, keys: TImportableEd25519Key[], versionId?: string, fee?: DidStdFee } }, context: IContext, ): Promise> { - const sdk = await this.getCheqdSDK() + const sdk = await this.getCheqdSDK(options?.fee) const signInputs = options.keys.map(key => createSignInputsFromImportableEd25519Key(key, options.document.verificationMethod ?? [])) + if (!this?.fee) { + const feePayer = (await (await this.cosmosPayerWallet).getAccounts())[0].address + this.fee = await DIDModule.generateCreateDidDocFees(feePayer) + } + const tx = await sdk.createDidTx( signInputs, options.document, '', - this.fee || 'auto', + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.fee!, undefined, - { sdk: sdk } + options?.versionId, + { sdk: sdk } as ISDKContext, ) assert(tx.code === 0, `cosmos_transaction: Failed to create DID. Reason: ${tx.rawLog}`) @@ -139,27 +149,31 @@ export class CheqdDIDProvider extends AbstractIdentifierProvider { return identifier } - // TODO: Add client side diff calculation using the resolver & SDK helper functions. - //* This will allow for better accuracy and predictability of `updateIdentifier` race conditions. async updateIdentifier( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - { did, document, options}: { did: string, document: Partial, options: { kms: string, keys: TImportableEd25519Key[] } }, + { did, document, options}: { did: string, document: DIDDocument, options: { kms: string, keys: TImportableEd25519Key[], versionId?: string, fee?: DidStdFee } }, context: IContext, ): Promise { - const sdk = await this.getCheqdSDK() + const sdk = await this.getCheqdSDK(options?.fee) - const signInputs = options.keys.map(key => createSignInputsFromImportableEd25519Key(key, document.verificationMethod as unknown as VerificationMethod[] ?? [])) + const signInputs = options.keys.map(key => createSignInputsFromImportableEd25519Key(key, document.verificationMethod ?? [])) + + if (!this?.fee) { + const feePayer = (await (await this.cosmosPayerWallet).getAccounts())[0].address + this.fee = await DIDModule.generateCreateDidDocFees(feePayer) + } const tx = await sdk.updateDidTx( signInputs, - document as Partial, + document as DIDDocument, '', - this.fee || 'auto', + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.fee!, undefined, - { sdk: sdk } + options?.versionId, + { sdk: sdk } as ISDKContext, ) - assert(tx.code === 0, `cosmos_transaction: Failed to create DID. Reason: ${tx.rawLog}`) + assert(tx.code === 0, `cosmos_transaction: Failed to update DID. Reason: ${tx.rawLog}`) //* Currently, only one controller key is supported. This is subject to change in the near future. @@ -168,14 +182,18 @@ export class CheqdDIDProvider extends AbstractIdentifierProvider { kms: options.kms || this.defaultKms, } as MinimalImportableKey) - const _keys = await Promise.all(options.keys.slice(1).map(async key => await context.agent.keyManagerImport({ ...key, kms: options.kms || this.defaultKms }))) + const _keys = await Promise.all(options.keys.slice(1).map( + async key => await context.agent.keyManagerImport({ ...key, kms: options.kms || this.defaultKms }) + .catch(e => { + if (e.message.includes('key_already_exists')) debug('Key already exists'); else throw e + }) + )) ?? [] const identifier: IIdentifier = { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - did: document.id!, + did: document.id, controllerKeyId: controllerKey.kid, keys: [controllerKey, ..._keys], - services: document.service as unknown as Service[] || [], + services: document.service || [], provider: 'cheqd', } @@ -184,16 +202,55 @@ export class CheqdDIDProvider extends AbstractIdentifierProvider { return identifier } + async deactivateIdentifier( + { did, document, options}: { did: string, document: DIDDocument, options: { kms: string, keys: TImportableEd25519Key[], fee?: DidStdFee } }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + context: IContext, + ): Promise { + const sdk = await this.getCheqdSDK(options?.fee) + + const signInputs = options.keys.map(key => createSignInputsFromImportableEd25519Key(key, document.verificationMethod as unknown as VerificationMethod[] ?? [])) + + if (!this?.fee) { + const feePayer = (await (await this.cosmosPayerWallet).getAccounts())[0].address + this.fee = await DIDModule.generateCreateDidDocFees(feePayer) + } + + const tx = await sdk.deactivateDidTx( + signInputs, + document as DIDDocument, + '', + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.fee!, + undefined, + undefined, + { sdk: sdk } as ISDKContext, + ) + + assert(tx.code === 0, `cosmos_transaction: Failed to update DID. Reason: ${tx.rawLog}`) + + debug('Deactivated DID', did) + + return true + } + async createResource( - { options }: { options: { payload: ResourcePayload, signInputs: ISignInputs[], kms: string } }, + { options }: { options: { payload: ResourcePayload, signInputs: ISignInputs[], kms: string, fee?: DidStdFee } }, context: IContext, ): Promise { - const sdk = await this.getCheqdSDK() + const sdk = await this.getCheqdSDK(options?.fee) + + if (!this?.fee) { + const feePayer = (await (await this.cosmosPayerWallet).getAccounts())[0].address + this.fee = await DIDModule.generateCreateDidDocFees(feePayer) + } + const tx = await sdk.createResourceTx( options.signInputs, options.payload, '', - this.fee || 'auto', + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.fee!, undefined, { sdk: sdk } ) @@ -207,7 +264,7 @@ export class CheqdDIDProvider extends AbstractIdentifierProvider { default: return undefined } } - + await Promise.all(options.signInputs.filter(input => mapKeyType(input.keyType) !== undefined) .map(async signInput => await context.agent.keyManagerImport({ privateKeyHex: signInput.privateKeyHex, @@ -233,65 +290,45 @@ export class CheqdDIDProvider extends AbstractIdentifierProvider { async addKey( { - // eslint-disable-next-line @typescript-eslint/no-unused-vars identifier, - // eslint-disable-next-line @typescript-eslint/no-unused-vars key, - // eslint-disable-next-line @typescript-eslint/no-unused-vars options, - // eslint-disable-next-line @typescript-eslint/no-explicit-any }: { identifier: IIdentifier; key: IKey; options?: any }, - // eslint-disable-next-line @typescript-eslint/no-unused-vars context: IContext, - // eslint-disable-next-line @typescript-eslint/no-explicit-any ): Promise { - throw Error('CheqdDIDProvider addKey not supported yet.') + throw Error('CheqdDIDProvider addKey is not supported.') } async addService( { - // eslint-disable-next-line @typescript-eslint/no-unused-vars identifier, - // eslint-disable-next-line @typescript-eslint/no-unused-vars service, - // eslint-disable-next-line @typescript-eslint/no-unused-vars options, - // eslint-disable-next-line @typescript-eslint/no-explicit-any }: { identifier: IIdentifier; service: IService; options?: any }, - // eslint-disable-next-line @typescript-eslint/no-unused-vars context: IContext, - // eslint-disable-next-line @typescript-eslint/no-explicit-any ): Promise { - throw Error('CheqdDIDProvider addService not supported yet.') + throw Error('CheqdDIDProvider addService is not supported.') } async removeKey( - // eslint-disable-next-line @typescript-eslint/no-unused-vars args: { identifier: IIdentifier; kid: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any options?: any }, - // eslint-disable-next-line @typescript-eslint/no-unused-vars context: IContext, - // eslint-disable-next-line @typescript-eslint/no-explicit-any ): Promise { - throw Error('CheqdDIDProvider removeKey not supported yet.') + throw Error('CheqdDIDProvider removeKey is not supported.') } async removeService( - // eslint-disable-next-line @typescript-eslint/no-unused-vars args: { identifier: IIdentifier; id: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any options?: any }, - // eslint-disable-next-line @typescript-eslint/no-unused-vars context: IContext, - // eslint-disable-next-line @typescript-eslint/no-explicit-any ): Promise { - throw Error('CheqdDIDProvider removeService not supported yet.') + throw Error('CheqdDIDProvider removeService is not supported.') } }