diff --git a/examples/bbs-dock.js b/examples/bbs-dock.js index 9b197aba4..419799137 100644 --- a/examples/bbs-dock.js +++ b/examples/bbs-dock.js @@ -1,5 +1,5 @@ import { initializeWasm } from '@docknetwork/credential-sdk/crypto'; -import { DIDResolver } from '@docknetwork/credential-sdk/resolver'; +import { Resolver } from '@docknetwork/credential-sdk/resolver'; import Bls12381G2KeyPairDock2022 from '@docknetwork/credential-sdk/vc/crypto/Bls12381G2KeyPairDock2022'; import { issueCredential, @@ -15,8 +15,10 @@ const keypairOpts = { 'przwNdX6Bn5TzwmX56fYKQr6vk5U2DsfJJHZJQzr1Sxd9oJUg1rEEoUP7Bz33WNpykvkkqoTByMwnceCx9yvTW8CG1V5XpSwHPSN222cwMe9xr4mViyLWkKtoraybEPeLHT', }; -class ExampleDIDResolver extends DIDResolver { - static METHOD = 'example'; +class ExampleDIDResolver extends Resolver { + prefix = 'did'; + + method = 'example'; constructor() { super(void 0); diff --git a/examples/resolver.js b/examples/resolver.js index 98c15ed10..fc0533ba4 100644 --- a/examples/resolver.js +++ b/examples/resolver.js @@ -6,13 +6,15 @@ import { DidKey, DIDDocument, } from '@docknetwork/credential-sdk/types'; +import { parseDIDUrl } from '@docknetwork/credential-sdk/utils'; import { NoDIDError } from '@docknetwork/credential-sdk/modules/abstract/did'; import { - DIDResolver, DIDKeyResolver, UniversalResolver, WILDCARD, - DockDIDResolver, + DIDResolver, + ResolverRouter, + Resolver, } from '@docknetwork/credential-sdk/resolver'; import { DockDIDModule } from '@docknetwork/dock-blockchain-modules'; @@ -39,8 +41,10 @@ const dock = new DockAPI(); const didModule = new DockDIDModule(dock); // Custom ethereum resolver class -class EtherResolver extends DIDResolver { - static METHOD = 'ethr'; +class EtherResolver extends Resolver { + prefix = 'did'; + + method = 'ethr'; constructor(config) { super(); @@ -48,7 +52,7 @@ class EtherResolver extends DIDResolver { } async resolve(did) { - const parsed = this.parseDid(did); + const parsed = parseDIDUrl(did); try { return this.ethres(did, parsed); } catch (e) { @@ -86,12 +90,12 @@ async function main() { const resolvers = [ new DIDKeyResolver(), // did:key resolver - new DockDIDResolver(didModule), // Prebuilt resolver + new DIDResolver(didModule), // Prebuilt resolver new EtherResolver(ethereumProviderConfig), // Custom resolver ]; - class AnyDIDResolver extends DIDResolver { - static METHOD = WILDCARD; + class AnyDIDResolver extends ResolverRouter { + method = WILDCARD; } const resolver = new AnyDIDResolver([ diff --git a/examples/schema.js b/examples/schema.js index cc540ab39..a0ecb2ee0 100644 --- a/examples/schema.js +++ b/examples/schema.js @@ -21,8 +21,8 @@ import { import { UniversalResolver, - DockResolver, - WildcardMultiResolver, + CoreResolver, + WildcardResolverRouter, } from '@docknetwork/credential-sdk/resolver'; const dock = new DockAPI(); @@ -103,8 +103,8 @@ async function main() { console.log('Result from chain:', result); const universalResolverUrl = 'https://uniresolver.io'; - const resolver = new WildcardMultiResolver([ - new DockResolver(modules), + const resolver = new WildcardResolverRouter([ + new CoreResolver(modules), new UniversalResolver(universalResolverUrl), ]); diff --git a/packages/cheqd-blockchain-api/src/index.js b/packages/cheqd-blockchain-api/src/index.js index cae2feefb..5c716b588 100644 --- a/packages/cheqd-blockchain-api/src/index.js +++ b/packages/cheqd-blockchain-api/src/index.js @@ -6,7 +6,10 @@ import { } from '@docknetwork/credential-sdk/utils'; import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing'; import { - DIDModule, ResourceModule, createCheqdSDK, CheqdNetwork, + DIDModule, + ResourceModule, + createCheqdSDK, + CheqdNetwork, } from '@cheqd/sdk'; import { MsgCreateDidDocPayload, @@ -167,6 +170,10 @@ export class CheqdAPI extends ApiProvider { return res; } + methods() { + return ['cheqd']; + } + supportsIdentifier(id) { this.ensureInitialized(); @@ -182,6 +189,8 @@ export class CheqdAPI extends ApiProvider { return this.supportsIdentifier(id[0]); } else if (id instanceof TypedEnum) { return this.supportsIdentifier(this.value); + } else if (id.Qualifier?.contains('cheqd:')) { + return true; } return false; diff --git a/packages/cheqd-blockchain-modules/src/common/cheqd-api-provider.js b/packages/cheqd-blockchain-modules/src/common/cheqd-api-provider.js index c2cc30e46..517d4b5c5 100644 --- a/packages/cheqd-blockchain-modules/src/common/cheqd-api-provider.js +++ b/packages/cheqd-blockchain-modules/src/common/cheqd-api-provider.js @@ -16,6 +16,10 @@ class CheqdApiProvider extends ApiProvider { return this.cheqd.fees; } + methods() { + return this.cheqd.methods(); + } + isInitialized() { return this.cheqd.isInitialized(); } diff --git a/packages/cheqd-blockchain-modules/src/common/inject-cheqd.js b/packages/cheqd-blockchain-modules/src/common/inject-cheqd.js index 14bc20192..d9319b432 100644 --- a/packages/cheqd-blockchain-modules/src/common/inject-cheqd.js +++ b/packages/cheqd-blockchain-modules/src/common/inject-cheqd.js @@ -1,7 +1,7 @@ import { withExtendedStaticProperties } from '@docknetwork/credential-sdk/utils/inheritance'; import CheqdAPIProvider from './cheqd-api-provider'; -export default function injectDock(klass) { +export default function injectCheqd(klass) { const name = `withCheqd(${klass.name})`; const obj = { @@ -22,6 +22,10 @@ export default function injectDock(klass) { supportsIdentifier(id) { return this.apiProvider.supportsIdentifier(id); } + + methods() { + return this.apiProvider.methods(); + } }, }; diff --git a/packages/credential-sdk/src/modules/abstract/common/api-provider.js b/packages/credential-sdk/src/modules/abstract/common/api-provider.js index db42813a4..0c352fa89 100644 --- a/packages/credential-sdk/src/modules/abstract/common/api-provider.js +++ b/packages/credential-sdk/src/modules/abstract/common/api-provider.js @@ -2,6 +2,15 @@ import { withExtendedPrototypeProperties } from '../../../utils'; class ApiProvider { /** + * Returns array of the methods supported by the given API. + * @returns {Array} + */ + methods() { + throw new Error('Unimplemented'); + } + + /** + * Returns `true` is the API was initialized. * @returns {boolean} */ isInitialized() { @@ -39,6 +48,6 @@ class ApiProvider { * Base class that must be extended by all API providers. */ export default withExtendedPrototypeProperties( - ['isInitialized', 'stateChangeBytes', 'signAndSend'], + ['methods', 'isInitialized', 'stateChangeBytes', 'signAndSend'], ApiProvider, ); diff --git a/packages/credential-sdk/src/modules/abstract/common/base-module.js b/packages/credential-sdk/src/modules/abstract/common/base-module.js index ee74e1eac..219b53dd7 100644 --- a/packages/credential-sdk/src/modules/abstract/common/base-module.js +++ b/packages/credential-sdk/src/modules/abstract/common/base-module.js @@ -1,7 +1,6 @@ -/** - * Base module class that must be extended by all modules. - */ -export default class AbstractBaseModule { +import { withExtendedPrototypeProperties } from '../../../utils'; + +class AbstractBaseModule { /** * Signs and sends provided extrinsic. * @@ -19,3 +18,8 @@ export default class AbstractBaseModule { return await this.apiProvider.signAndSend(extrinsic, params); } } + +/** + * Base module class that must be extended by all modules. + */ +export default withExtendedPrototypeProperties(['methods'], AbstractBaseModule); diff --git a/packages/credential-sdk/src/modules/abstract/index.js b/packages/credential-sdk/src/modules/abstract/index.js index 11df80904..467398757 100644 --- a/packages/credential-sdk/src/modules/abstract/index.js +++ b/packages/credential-sdk/src/modules/abstract/index.js @@ -54,7 +54,10 @@ export const AbstractCoreModules = withExtendedStaticProperties( AttestModule: { key: 'attest', optional: false }, BlobModule: { key: 'blob', optional: false }, DIDModule: { key: 'did', optional: false }, - OffchainSignaturesModule: { key: 'offchainSignatures', optional: false }, + OffchainSignaturesModule: { + key: 'offchainSignatures', + optional: false, + }, BBSModule: { key: 'bbs', optional: false }, BBSPlusModule: { key: 'bbsPlus', optional: false }, PSModule: { key: 'ps', optional: false }, diff --git a/packages/credential-sdk/src/modules/multi-api/common.js b/packages/credential-sdk/src/modules/multi-api/common.js index 36e2a2c34..2909a6797 100644 --- a/packages/credential-sdk/src/modules/multi-api/common.js +++ b/packages/credential-sdk/src/modules/multi-api/common.js @@ -26,6 +26,10 @@ export function injectModuleRouter(klass) { dispatchById(id, fn) { return fn(this.moduleById(id)); } + + methods() { + return this.modules.flatMap((module) => module.methods()); + } }, }; diff --git a/packages/credential-sdk/src/modules/tests/blob-module.js b/packages/credential-sdk/src/modules/tests/blob-module.js index 26843204e..e7e3d9561 100644 --- a/packages/credential-sdk/src/modules/tests/blob-module.js +++ b/packages/credential-sdk/src/modules/tests/blob-module.js @@ -2,6 +2,8 @@ import { DidKeypair, Ed25519Keypair } from '../../keypairs'; import { NoBlobError } from '../abstract/blob/errors'; import { DIDDocument } from '../../types'; import { itIf } from './common'; +import { BlobResolver } from '../../resolver/blob'; +import { stringToU8a } from '../../utils'; // eslint-disable-next-line jest/no-export export default function generateBlobModuleTests( @@ -56,5 +58,28 @@ export default function generateBlobModuleTests( new NoBlobError(id), ); }); + + test('`BlobResolver`', async () => { + const resolver = new BlobResolver(blobModule); + const did = DID.random(); + + const keyPair = Ed25519Keypair.random(); + const didKeypair = new DidKeypair([did, 1], keyPair); + + const document = DIDDocument.create(did, [didKeypair.didKey()]); + + await didModule.createDocument(document, didKeypair); + + const blob1 = { + id: BlobId.random(did), + blob: 'abcdef', + }; + + await blobModule.new(blob1, didKeypair); + expect(await resolver.resolve(String(blob1.id))).toEqual([ + String(did), + stringToU8a(blob1.blob), + ]); + }); }); } diff --git a/packages/credential-sdk/src/modules/tests/did-module.js b/packages/credential-sdk/src/modules/tests/did-module.js index d11250d94..f6b6020dc 100644 --- a/packages/credential-sdk/src/modules/tests/did-module.js +++ b/packages/credential-sdk/src/modules/tests/did-module.js @@ -1,4 +1,5 @@ import { DidKeypair, Ed25519Keypair } from '../../keypairs'; +import { DIDResolver } from '../../resolver'; import { DIDDocument, BBSPublicKeyValue, @@ -229,5 +230,19 @@ export default function generateDIDModuleTests( new NoDIDError(did), ); }); + + test('`DIDResolver`', async () => { + const resolver = new DIDResolver(module); + const did = DID.random(); + + const keyPair = Ed25519Keypair.random(); + const didKeypair = new DidKeypair([did, 1], keyPair); + + const document = DIDDocument.create(did, [didKeypair.didKey()]); + + await module.createDocument(document, didKeypair); + + expect(await resolver.resolve(String(did))).toEqual(document.toJSON()); + }); }); } diff --git a/packages/credential-sdk/src/resolver/blob/blob-resolver.js b/packages/credential-sdk/src/resolver/blob/blob-resolver.js index ca9428879..4b557d879 100644 --- a/packages/credential-sdk/src/resolver/blob/blob-resolver.js +++ b/packages/credential-sdk/src/resolver/blob/blob-resolver.js @@ -1,9 +1,36 @@ -import { MultiResolver } from '../generic'; +import { ensureInstanceOf } from '../../utils'; +import { AbstractBlobModule } from '../../modules/abstract/blob'; +import { Resolver } from '../generic'; + +class DockBlobResolver extends Resolver { + prefix = 'blob'; + + get method() { + return this.blobModule.methods(); + } + + /** + * @param {AbstractBlobModule} + * @constructor + */ + constructor(blobModule) { + super(); + + /** + * @type {AbstractBlobModule} + */ + this.blobModule = ensureInstanceOf(blobModule, AbstractBlobModule); + } + + async resolve(blobUri) { + const [author, blob] = await this.blobModule.get(blobUri); + + return [String(author), blob.toObjectOrBytes()]; + } +} /** - * Resolves `Blob` with the identifier `blob:*`. - * @abstract + * Resolves `Blob`s with identifier `blob:dock:*`. + * @type {DockBlobResolver} */ -export default class BlobResolver extends MultiResolver { - static PREFIX = 'blob'; -} +export default DockBlobResolver; diff --git a/packages/credential-sdk/src/resolver/blob/dock-blob-resolver.js b/packages/credential-sdk/src/resolver/blob/dock-blob-resolver.js deleted file mode 100644 index 20d26b0e1..000000000 --- a/packages/credential-sdk/src/resolver/blob/dock-blob-resolver.js +++ /dev/null @@ -1,32 +0,0 @@ -import { ensureInstanceOf } from '../../utils'; -import BlobResolver from './blob-resolver'; -import { AbstractBlobModule } from '../../modules/abstract/blob'; - -class DockBlobResolver extends BlobResolver { - static METHOD = 'dock'; - - /** - * @param {DockAPI} dock - An initialized connection to a dock full-node. - * @constructor - */ - constructor(blobModule) { - super(); - - /** - * @type {DockAPI} - */ - this.blobModule = ensureInstanceOf(blobModule, AbstractBlobModule); - } - - async resolve(blobUri) { - const [author, blob] = await this.blobModule.get(blobUri); - - return [String(author), blob.toObjectOrBytes()]; - } -} - -/** - * Resolves `Blob`s with identifier `blob:dock:*`. - * @type {DockBlobResolver} - */ -export default DockBlobResolver; diff --git a/packages/credential-sdk/src/resolver/blob/index.js b/packages/credential-sdk/src/resolver/blob/index.js index d560061f4..9f1a4729c 100644 --- a/packages/credential-sdk/src/resolver/blob/index.js +++ b/packages/credential-sdk/src/resolver/blob/index.js @@ -1,2 +1 @@ export { default as BlobResolver } from './blob-resolver'; -export { default as DockBlobResolver } from './dock-blob-resolver'; diff --git a/packages/credential-sdk/src/resolver/core-resolver.js b/packages/credential-sdk/src/resolver/core-resolver.js new file mode 100644 index 000000000..63c27ed3a --- /dev/null +++ b/packages/credential-sdk/src/resolver/core-resolver.js @@ -0,0 +1,21 @@ +import { DIDResolver } from './did'; +import { StatusList2021Resolver } from './status-list2021'; +import { ResolverRouter } from './generic'; +import { BlobResolver } from './blob'; +import { ensureInstanceOf } from '../utils'; +import { AbstractCoreModules } from '../modules'; + +/** + * Resolves dock-hosted entities such us `did:dock:*` and `status-list2021:dock:*`, `rev-reg:dock:*`, `blob:dock:*`. + */ +export default class CoreResolver extends ResolverRouter { + constructor(modules) { + ensureInstanceOf(modules, AbstractCoreModules); + + super([ + new DIDResolver(modules.did), + new StatusList2021Resolver(modules.statusListCredential), + new BlobResolver(modules.blob), + ]); + } +} diff --git a/packages/credential-sdk/src/resolver/did/did-key-resolver.js b/packages/credential-sdk/src/resolver/did/did-key-resolver.js index 52e5eed29..80c1d4366 100644 --- a/packages/credential-sdk/src/resolver/did/did-key-resolver.js +++ b/packages/credential-sdk/src/resolver/did/did-key-resolver.js @@ -1,20 +1,23 @@ import { getResolver } from 'key-did-resolver'; -import DIDResolver from './did-resolver'; +import { Resolver } from '../generic'; +import { parseDIDUrl } from '../../utils'; const resolveMethod = getResolver().key; /** * Resolves `DID` keys with identifier `did:key:*`. */ -export default class DIDKeyResolver extends DIDResolver { - static METHOD = 'key'; +export default class DIDKeyResolver extends Resolver { + prefix = 'did'; + + method = 'key'; constructor() { super(void 0); } async resolve(did) { - const parsed = this.parseDid(did); + const parsed = parseDIDUrl(did); const { didDocument } = await resolveMethod(did, parsed, null, {}); return { diff --git a/packages/credential-sdk/src/resolver/did/did-resolver.js b/packages/credential-sdk/src/resolver/did/did-resolver.js index 9b3581cc9..abd0277e1 100644 --- a/packages/credential-sdk/src/resolver/did/did-resolver.js +++ b/packages/credential-sdk/src/resolver/did/did-resolver.js @@ -1,75 +1,37 @@ -/* -eslint prefer-destructuring: "off" -*/ -// Parse method taken from: https://github.com/decentralized-identity/did-resolver/blob/1.1.0/src/resolver.ts -// Copyright 2018 ConsenSys AG +import { AbstractDIDModule } from '../../modules/abstract/did'; +import { ensureInstanceOf, parseDIDUrl } from '../../utils'; +import { Resolver } from '../generic'; -// Licensed under the Apache License, Version 2.0(the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +class DIDResolver extends Resolver { + prefix = 'did'; -// http://www.apache.org/licenses/LICENSE-2.0 + get method() { + return this.didModule.methods(); + } -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. + /** + * @param {AbstractDIDModule} + * @constructor + */ + constructor(didModule) { + super(); -import { MultiResolver, METHOD_REG_EXP_PATTERN } from '../generic'; + /** + * @type {AbstractDIDModule} + */ + this.didModule = ensureInstanceOf(didModule, AbstractDIDModule); + } -const ID_CHAR = '[a-zA-Z0-9_.-]'; -const METHOD_ID = `(${ID_CHAR}+(:${ID_CHAR}+)*)`; -const PARAM_CHAR = '[a-zA-Z0-9_.:%-]'; -const PARAM = `;${PARAM_CHAR}+=${PARAM_CHAR}*`; -const PARAMS = `((${PARAM})*)`; -// eslint-disable-next-line no-useless-escape -const PATH = '(/[^#?]*)?'; -const QUERY = '([?][^#]*)?'; -// eslint-disable-next-line no-useless-escape -const FRAGMENT = '(#.*)?'; -const DID_MATCHER = new RegExp( - `^did:${METHOD_REG_EXP_PATTERN}:${METHOD_ID}${PARAMS}${PATH}${QUERY}${FRAGMENT}$`, -); + async resolve(didURL) { + const { did } = parseDIDUrl(didURL); + const document = await this.didModule.getDocument(did); -export function parseDIDUrl(didUrl) { - if (didUrl === '' || !didUrl) throw new Error('Missing DID'); - const sections = didUrl.match(DID_MATCHER); - if (sections) { - const parts = { - did: `did:${sections[1]}:${sections[2]}`, - method: sections[1], - id: sections[2], - didUrl, - }; - if (sections[4]) { - const params = sections[4].slice(1).split(';'); - parts.params = {}; - for (const p of params) { - const kv = p.split('='); - parts.params[kv[0]] = kv[1]; - } - } - if (sections[6]) parts.path = sections[6]; - if (sections[7]) parts.query = sections[7].slice(1); - if (sections[8]) parts.fragment = sections[8].slice(1); - return parts; + return document.toJSON(); } - throw new Error(`Invalid DID: \`${didUrl}\``); } /** - * Resolves `DID` with the identifier `did:*`. - * @abstract + * Resolves `DID`s with identifier `did:dock:*`. + * @type {DIDResolver} */ -export default class DIDResolver extends MultiResolver { - static PREFIX = 'did'; - - /** - * - * @param {string} did - */ - parseDid(did) { - return parseDIDUrl(did); - } -} +export default DIDResolver; diff --git a/packages/credential-sdk/src/resolver/did/dock-did-resolver.js b/packages/credential-sdk/src/resolver/did/dock-did-resolver.js deleted file mode 100644 index c8776c93b..000000000 --- a/packages/credential-sdk/src/resolver/did/dock-did-resolver.js +++ /dev/null @@ -1,34 +0,0 @@ -import { AbstractDIDModule } from '../../modules/abstract/did'; -import DIDResolver from './did-resolver'; -import { ensureInstanceOf } from '../../utils'; - -class DockDIDResolver extends DIDResolver { - static METHOD = 'dock'; - - /** - * @param {DockAPI} dock - An initialized connection to a dock full-node. - * @constructor - */ - constructor(didModule) { - super(); - - /** - * @type {AbstractDIDModule} - */ - this.didModule = ensureInstanceOf(didModule, AbstractDIDModule); - } - - async resolve(qualifiedDid) { - const { did } = this.parseDid(qualifiedDid); - - const document = await this.didModule.getDocument(did); - - return document.toJSON(); - } -} - -/** - * Resolves `DID`s with identifier `did:dock:*`. - * @type {DockDIDResolver} - */ -export default DockDIDResolver; diff --git a/packages/credential-sdk/src/resolver/did/index.js b/packages/credential-sdk/src/resolver/did/index.js index d384719a2..7cfcb90e5 100644 --- a/packages/credential-sdk/src/resolver/did/index.js +++ b/packages/credential-sdk/src/resolver/did/index.js @@ -1,4 +1,3 @@ -export { default as DIDResolver } from './did-resolver'; export { default as DIDKeyResolver } from './did-key-resolver'; -export { default as DockDIDResolver } from './dock-did-resolver'; +export { default as DIDResolver } from './did-resolver'; export { default as UniversalResolver } from './universal-resolver'; diff --git a/packages/credential-sdk/src/resolver/did/universal-resolver.js b/packages/credential-sdk/src/resolver/did/universal-resolver.js index 326673261..ce627dbb7 100644 --- a/packages/credential-sdk/src/resolver/did/universal-resolver.js +++ b/packages/credential-sdk/src/resolver/did/universal-resolver.js @@ -6,9 +6,9 @@ import jsonFetch from '../../utils/json-fetch'; * Resolves `DID`s with wildcard method: `did:*:`. */ export default class UniversalResolver extends Resolver { - static PREFIX = 'did'; + prefix = 'did'; - static METHOD = WILDCARD; + method = WILDCARD; /** * Create an adapter to a diff --git a/packages/credential-sdk/src/resolver/dock-resolver.js b/packages/credential-sdk/src/resolver/dock-resolver.js deleted file mode 100644 index 49d934d18..000000000 --- a/packages/credential-sdk/src/resolver/dock-resolver.js +++ /dev/null @@ -1,29 +0,0 @@ -import { DockDIDResolver } from './did'; -import { DockStatusList2021Resolver } from './status-list2021'; -import { MultiResolver } from './generic'; -import { DockBlobResolver } from './blob'; -import { ensureInstanceOf } from '../utils'; -import { AbstractCoreModules } from '../modules'; - -/** - * Resolves dock-hosted entities such us `did:dock:*` and `status-list2021:dock:*`, `rev-reg:dock:*`, `blob:dock:*`. - */ -export default class DockResolver extends MultiResolver { - static PREFIX = [ - DockDIDResolver.PREFIX, - DockStatusList2021Resolver.PREFIX, - DockBlobResolver.PREFIX, - ]; - - static METHOD = 'dock'; - - constructor(modules) { - ensureInstanceOf(modules, AbstractCoreModules); - - super([ - new DockDIDResolver(modules.did), - new DockStatusList2021Resolver(modules.statusListCredential), - new DockBlobResolver(modules.blob), - ]); - } -} diff --git a/packages/credential-sdk/src/resolver/generic/const.js b/packages/credential-sdk/src/resolver/generic/const.js index 62cd715cf..2cb2bb5b7 100644 --- a/packages/credential-sdk/src/resolver/generic/const.js +++ b/packages/credential-sdk/src/resolver/generic/const.js @@ -9,7 +9,7 @@ export const METHOD_REG_EXP_PATTERN = '([a-zA-Z0-9_]+)'; export const HEX_ID_REG_EXP_PATTERN = '(0x[0-9a-fA-F]+)'; /** - * Use to specify `PREFIX`/`METHOD` that will be dispatched in case no direct matches are found. + * Use to specify `prefix`/`method` that will be dispatched in case no direct matches are found. */ export const WILDCARD = Symbol.for( '@docknetwork/credential-sdk/resolver/wildcard-resolver', diff --git a/packages/credential-sdk/src/resolver/generic/helpers.js b/packages/credential-sdk/src/resolver/generic/helpers.js index 7b64c2c68..ec96797a2 100644 --- a/packages/credential-sdk/src/resolver/generic/helpers.js +++ b/packages/credential-sdk/src/resolver/generic/helpers.js @@ -1,4 +1,3 @@ -import MultiResolver from './multi-resolver'; import Resolver from './resolver'; import { WILDCARD } from './const'; import { ensureItemsAllowed } from '../utils'; @@ -11,44 +10,39 @@ import { ensureItemsAllowed } from '../utils'; * @param {object} [config={}] * @param {Array | string | symbol} [config.prefix=WILDCARD] * @param {Array | string | symbol} [config.method=WILDCARD] - * @returns {Resolver | MultiResolver} + * @returns {Resolver | ResolverRouter} */ export const createResolver = ( resolverOrFn, { prefix = WILDCARD, method = WILDCARD } = {}, -) => { - const isMulti = Array.isArray(prefix) || Array.isArray(method); - const baseClass = isMulti ? MultiResolver : Resolver; +) => new (class ResolverCreatedUsingCreateResolver extends Resolver { + prefix = prefix; - return new (class ResolverCreatedUsingCreateResolver extends baseClass { - static PREFIX = prefix; + method = method; - static METHOD = method; + constructor() { + super(); - constructor() { - super(); - - const isFn = typeof resolverOrFn === 'function'; - if (!isFn && resolverOrFn instanceof Resolver) { - ensureItemsAllowed( - [].concat(resolverOrFn.constructor.PREFIX), - [].concat(this.constructor.PREFIX), - WILDCARD, - ); - ensureItemsAllowed( - [].concat(resolverOrFn.constructor.METHOD), - [].concat(this.constructor.METHOD), - WILDCARD, - ); - } - - this.resolve = isFn - ? resolverOrFn - : resolverOrFn.resolve.bind(resolverOrFn); + const isFn = typeof resolverOrFn === 'function'; + if (!isFn && resolverOrFn instanceof Resolver) { + ensureItemsAllowed( + [].concat(resolverOrFn.prefix), + [].concat(this.prefix), + WILDCARD, + ); + ensureItemsAllowed( + [].concat(resolverOrFn.method), + [].concat(this.method), + WILDCARD, + ); } - async resolve(_id) { - throw new Error('Unimplemented'); - } - })(); -}; + this.resolve = isFn + ? resolverOrFn + : resolverOrFn.resolve.bind(resolverOrFn); + } + + async resolve(_id) { + throw new Error('Unimplemented'); + } +})(); diff --git a/packages/credential-sdk/src/resolver/generic/index.js b/packages/credential-sdk/src/resolver/generic/index.js index 309afc43e..2eba03d63 100644 --- a/packages/credential-sdk/src/resolver/generic/index.js +++ b/packages/credential-sdk/src/resolver/generic/index.js @@ -1,5 +1,5 @@ -export { default as MultiResolver } from './multi-resolver'; +export { default as ResolverRouter } from './resolver-router'; export { default as Resolver } from './resolver'; -export { default as WildcardMultiResolver } from './wildcard-multi-resolver'; +export { default as WildcardResolverRouter } from './wildcard-resolver-router'; export * from './helpers'; export * from './const'; diff --git a/packages/credential-sdk/src/resolver/generic/multi-resolver.js b/packages/credential-sdk/src/resolver/generic/resolver-router.js similarity index 66% rename from packages/credential-sdk/src/resolver/generic/multi-resolver.js rename to packages/credential-sdk/src/resolver/generic/resolver-router.js index 0d4e0e076..b9496db4e 100644 --- a/packages/credential-sdk/src/resolver/generic/multi-resolver.js +++ b/packages/credential-sdk/src/resolver/generic/resolver-router.js @@ -1,59 +1,45 @@ import Resolver from './resolver'; -import { ensureItemsAllowed, cacheLast } from '../utils'; +import { cacheLast, itemsOrWildcard } from '../utils'; import { WILDCARD } from './const'; import { ensureString } from '../../utils/type-helpers'; /** - * Successor can either - * - implement its own `resolve` and have multiple `PREFIX`/`METHOD`(s) - * - act as a router and resolve an entity using a provided list of resolvers + * Acts as a router and resolves an entity using a provided list of resolvers. * - * In the first case, it has to implement a `resolve` function and avoid providing any resolvers to the `super.constructor`. + * Each resolver must have properties `prefix` and `method` set. * - * In the second case, it doesn't have to override `resolve` but instead provide a list of resolvers into the `super.constructor`. - * Each resolver must have static properties `PREFIX` and `METHOD`, and unlike `Resolver`, `MultiResolver` can have `PREFIX` - * and `METHOD` specified as Arrays of strings. - * - * If the resolver must be used for any `PREFIX`/`METHOD` as default, use the `WILDCARD` symbol. + * If the resolver must be used for any `prefix`/`method` as default, use the `WILDCARD` symbol. * In case no matching resolver is found, will be used first to match either the `WILDCARD` method, `WILDCARD` prefix, or both. * * @class * @abstract * @template T */ -export default class MultiResolver extends Resolver { +export default class ResolverRouter extends Resolver { /** * Matching string prefix, an array of string prefixes, or wildcard pattern. * @type {Array | string | symbol} - * @abstract - * @static + * @prop prefix */ - static PREFIX; /** * Matching string method, an array of string methods, or wildcard pattern. * @type {Array | string | symbol} - * @abstract - * @static + * @prop method */ - static METHOD; /** * - * @param {Array | MultiResolver>} [resolvers=[]] + * @param {Array | ResolverRouter>} [resolvers=[]] */ constructor(resolvers = []) { super(); let resolverList; - if (this.resolve !== MultiResolver.prototype.resolve) { - if (!resolvers.length) { - resolverList = [this]; - } else { - throw new Error( - `\`${this.constructor.name}\` has a custom \`resolve\` implementation, so it can't have any nested resolvers`, - ); - } + if (this.resolve !== ResolverRouter.prototype.resolve) { + throw new Error( + `\`${this.constructor.name}\` has a custom \`resolve\` implementation, so it can't have any nested resolvers`, + ); } else { resolverList = resolvers; @@ -64,6 +50,14 @@ export default class MultiResolver extends Resolver { } } + this.prefix = itemsOrWildcard( + resolverList.flatMap((resolver) => [].concat(resolver.prefix)), + WILDCARD, + ); + this.method = itemsOrWildcard( + resolverList.flatMap((resolver) => [].concat(resolver.method)), + WILDCARD, + ); this.resolvers = this.buildResolversMap(resolverList); this.matchingResolver = cacheLast(this.matchingResolver); } @@ -80,12 +74,12 @@ export default class MultiResolver extends Resolver { /** * Resolves an entity with the provided identifier. * @param {string} id - fully qualified identifier. - * @returns {Resolver | MultiResolver | null} + * @returns {Resolver | ResolverRouter | null} */ matchingResolver(id) { ensureString(id); - const prefix = this.prefix(id); - const method = this.method(id); + const prefix = this.extractPrefix(id); + const method = this.extractMethod(id); for (const [currentPrefix, currentMethod] of [ [prefix, method], @@ -118,26 +112,19 @@ export default class MultiResolver extends Resolver { } if ( this === resolver - && resolver.resolve === MultiResolver.prototype.resolve + && resolver.resolve === ResolverRouter.prototype.resolve ) { throw new Error( `Resolver \`${resolver.constructor.name}\` must implement its own \`resolve\``, ); } - const { PREFIX, METHOD } = resolver.constructor; - const prefixArr = [].concat(PREFIX); - const methodArr = [].concat(METHOD); - - ensureItemsAllowed( - prefixArr, - [].concat(this.constructor.PREFIX), - WILDCARD, - ); - ensureItemsAllowed( - methodArr, - [].concat(this.constructor.METHOD), - WILDCARD, - ); + if (resolver.prefix == null) { + throw new Error(`No prefix in \`${resolver.constructor.name}\``); + } else if (resolver.method == null) { + throw new Error(`No method in \`${resolver.constructor.name}\``); + } + const prefixArr = [].concat(resolver.prefix); + const methodArr = [].concat(resolver.method); for (const prefix of prefixArr) { for (const method of methodArr) { diff --git a/packages/credential-sdk/src/resolver/generic/resolver.js b/packages/credential-sdk/src/resolver/generic/resolver.js index 61762e1ed..964c7c4ef 100644 --- a/packages/credential-sdk/src/resolver/generic/resolver.js +++ b/packages/credential-sdk/src/resolver/generic/resolver.js @@ -1,5 +1,4 @@ import { ensureString } from '../../utils/type-helpers'; -import { withExtendedStaticProperties } from '../../utils/inheritance'; import { WILDCARD } from './const'; /** @@ -9,20 +8,16 @@ import { WILDCARD } from './const'; */ class Resolver { /** - * Matching prefix - either a string or wildcard pattern. - * @type {string | symbol} - * @abstract - * @static + * Matching prefix - either a string, array of strings or wildcard pattern. + * @type {Array | string | symbol} + * @prop prefix */ - static PREFIX; /** - * Matching method - either a string or wildcard pattern. - * @type {string | symbol} - * @abstract - * @static - */ - static METHOD; + * Matching method - either a string, array of strings or wildcard pattern. + * @type {Array | string | symbol} + * @prop method + * /** * Returns `true` if an entity with the provided identifier can be resolved using this resolver. @@ -31,12 +26,13 @@ class Resolver { */ supports(id) { ensureString(id); + const methods = [].concat(this.method); + const prefixes = [].concat(this.prefix); return ( - (this.constructor.METHOD === WILDCARD - || this.method(id) === this.constructor.METHOD) - && (this.constructor.PREFIX === WILDCARD - || this.prefix(id) === this.constructor.PREFIX) + (methods.includes(WILDCARD) + || methods.includes(this.extractMethod(id))) + && (prefixes.includes(WILDCARD) || prefixes.includes(this.extractPrefix(id))) ); } @@ -58,7 +54,7 @@ class Resolver { * @param {string} id * @returns {string} */ - prefix(id) { + extractPrefix(id) { ensureString(id); const end = id.indexOf(':'); @@ -72,7 +68,7 @@ class Resolver { * @param {string} id * @returns {string} */ - method(id) { + extractMethod(id) { ensureString(id); const start = id.indexOf(':'); const end = id.indexOf(':', start + 1); @@ -84,8 +80,8 @@ class Resolver { /** * Resolves an entity if its identifier is supported. - * Each resolver must have static `string` or `WILDCARD` symbol properties `PREFIX` and `METHOD`, + * Each resolver must have static `string` or `WILDCARD` symbol properties `prefix` and `METHOD`, * then the `supports` method will return `true` if they match the identifier. - * In case the resolver must be used for any `PREFIX`/`METHOD` as default, use the `WILDCARD` symbol. + * In case the resolver must be used for any `prefix`/`METHOD` as default, use the `WILDCARD` symbol. */ -export default withExtendedStaticProperties(['PREFIX', 'METHOD'], Resolver); +export default Resolver; diff --git a/packages/credential-sdk/src/resolver/generic/wildcard-multi-resolver.js b/packages/credential-sdk/src/resolver/generic/wildcard-multi-resolver.js deleted file mode 100644 index 967939873..000000000 --- a/packages/credential-sdk/src/resolver/generic/wildcard-multi-resolver.js +++ /dev/null @@ -1,11 +0,0 @@ -import MultiResolver from './multi-resolver'; -import { WILDCARD } from './const'; - -/** - * Wildcard `PREFIX`/`METHOD` resolver. Used primarily as a router to combine different resolvers together. - */ -export default class WildcardMultiResolver extends MultiResolver { - static PREFIX = WILDCARD; - - static METHOD = WILDCARD; -} diff --git a/packages/credential-sdk/src/resolver/generic/wildcard-resolver-router.js b/packages/credential-sdk/src/resolver/generic/wildcard-resolver-router.js new file mode 100644 index 000000000..c4198a4eb --- /dev/null +++ b/packages/credential-sdk/src/resolver/generic/wildcard-resolver-router.js @@ -0,0 +1,11 @@ +import ResolverRouter from './resolver-router'; +import { WILDCARD } from './const'; + +/** + * Wildcard `prefix`/`method` resolver. Used primarily as a router to combine different resolvers together. + */ +export default class WildcardResolverRouter extends ResolverRouter { + prefix = WILDCARD; + + method = WILDCARD; +} diff --git a/packages/credential-sdk/src/resolver/index.js b/packages/credential-sdk/src/resolver/index.js index c017ddf8d..4006b1260 100644 --- a/packages/credential-sdk/src/resolver/index.js +++ b/packages/credential-sdk/src/resolver/index.js @@ -1,4 +1,4 @@ export * from './did'; export * from './generic'; export * from './status-list2021'; -export { default as DockResolver } from './dock-resolver'; +export { default as CoreResolver } from './core-resolver'; diff --git a/packages/credential-sdk/src/resolver/status-list2021/dock-status-list2021-resolver.js b/packages/credential-sdk/src/resolver/status-list2021/dock-status-list2021-resolver.js deleted file mode 100644 index 78e891a06..000000000 --- a/packages/credential-sdk/src/resolver/status-list2021/dock-status-list2021-resolver.js +++ /dev/null @@ -1,39 +0,0 @@ -import { ensureInstanceOf } from '../../utils'; -import AbstractStatusListCredentialModule from '../../modules/abstract/status-list-credential/module'; -import StatusList2021Resolver from './status-list2021-resolver'; - -class DockStatusListResolver extends StatusList2021Resolver { - static METHOD = 'dock'; - - /** - * @param {AbstractStatusListCredentialModule} statusListCredentialModule - * @constructor - */ - constructor(statusListCredentialModule) { - super(); - - /** - * @type {AbstractStatusListCredentialModule} - */ - this.statusListCredentialModule = ensureInstanceOf( - statusListCredentialModule, - AbstractStatusListCredentialModule, - ); - } - - async resolve(fullyQualifiedStatusListId) { - const { id: dockStatusListId } = this.parse(fullyQualifiedStatusListId); - - const cred = await this.statusListCredentialModule.getStatusListCredential( - dockStatusListId, - ); - - return cred?.value.list.toJSON(); - } -} - -/** - * Resolves `StatusList2021Credential`s with identifier `status-list2021:dock:*`. - * @type {DockStatusListResolver} - */ -export default DockStatusListResolver; diff --git a/packages/credential-sdk/src/resolver/status-list2021/index.js b/packages/credential-sdk/src/resolver/status-list2021/index.js index aeb2f8bf7..a1a09be6a 100644 --- a/packages/credential-sdk/src/resolver/status-list2021/index.js +++ b/packages/credential-sdk/src/resolver/status-list2021/index.js @@ -1,2 +1 @@ export { default as StatusList2021Resolver } from './status-list2021-resolver'; -export { default as DockStatusList2021Resolver } from './dock-status-list2021-resolver'; diff --git a/packages/credential-sdk/src/resolver/status-list2021/status-list2021-resolver.js b/packages/credential-sdk/src/resolver/status-list2021/status-list2021-resolver.js index da949b1c1..a7a1d82e4 100644 --- a/packages/credential-sdk/src/resolver/status-list2021/status-list2021-resolver.js +++ b/packages/credential-sdk/src/resolver/status-list2021/status-list2021-resolver.js @@ -1,34 +1,57 @@ +import { ensureInstanceOf } from '../../utils'; +import AbstractStatusListCredentialModule from '../../modules/abstract/status-list-credential/module'; import { - MultiResolver, - METHOD_REG_EXP_PATTERN, HEX_ID_REG_EXP_PATTERN, + METHOD_REG_EXP_PATTERN, + Resolver, } from '../generic'; const STATUS_LIST_ID_MATCHER = new RegExp( `^status-list2021:${METHOD_REG_EXP_PATTERN}:${HEX_ID_REG_EXP_PATTERN}$`, ); -/** - * A `StatusList2021Credential` resolver. - * Accepts identifiers in format `status-list2021:`. - * - * @abstract - */ -export default class StatusList2021Resolver extends MultiResolver { - static PREFIX = 'status-list2021'; +class StatusListResolver extends Resolver { + prefix = 'status-list2021'; + + get method() { + return this.statusListCredentialModule.methods(); + } - parse(statusListId) { - if (!statusListId) throw new Error('Missing `statusListId`'); + /** + * @param {AbstractStatusListCredentialModule} statusListCredentialModule + * @constructor + */ + constructor(statusListCredentialModule) { + super(); - const sections = statusListId.match(STATUS_LIST_ID_MATCHER); - if (sections) { - const [, method, id] = sections; + /** + * @type {AbstractStatusListCredentialModule} + */ + this.statusListCredentialModule = ensureInstanceOf( + statusListCredentialModule, + AbstractStatusListCredentialModule, + ); + } - return { method, id }; + async resolve(statusListId) { + const match = statusListId.match(STATUS_LIST_ID_MATCHER); + if (!match) { + throw new Error( + `Invalid \`StatusList2021Credential\` id: \`${statusListId}\``, + ); } + const [, _, id] = statusListId.match(STATUS_LIST_ID_MATCHER); - throw new Error( - `Invalid \`StatusList2021Credential\` id: \`${statusListId}\``, + const cred = await this.statusListCredentialModule.getStatusListCredential( + id, ); + + return cred?.value.list.toJSON(); } } + +/** + * Resolves `StatusList2021Credential`s with identifier `status-list2021:dock:*`. + * @type {StatusListResolver} + */ +export default StatusListResolver; diff --git a/packages/credential-sdk/src/resolver/utils.js b/packages/credential-sdk/src/resolver/utils.js index f9cff4f72..5949a23da 100644 --- a/packages/credential-sdk/src/resolver/utils.js +++ b/packages/credential-sdk/src/resolver/utils.js @@ -65,3 +65,13 @@ export const ensureItemsAllowed = (items, allowed, wildcard) => { } } }; + +export const itemsOrWildcard = (items, wildcard) => { + for (const item of items) { + if (item === wildcard) { + return wildcard; + } + } + + return [...new Set(items)]; +}; diff --git a/packages/credential-sdk/src/types/accumulator/accumulator-id.js b/packages/credential-sdk/src/types/accumulator/accumulator-id.js new file mode 100644 index 000000000..98620035d --- /dev/null +++ b/packages/credential-sdk/src/types/accumulator/accumulator-id.js @@ -0,0 +1,99 @@ +import { CheqdMainnetDid, CheqdTestnetDid, DIDRef } from '../did'; +import { + TypedBytes, + TypedEnum, + TypedUUID, + sized, + withFrom, + withQualifier, +} from '../generic'; + +export class AccumulatorId extends withFrom( + withQualifier(TypedEnum, true), + (value, from) => { + try { + // eslint-disable-next-line no-use-before-define + return DockAccumulatorId.from(value); + } catch { + return from(value); + } + }, +) { + static Qualifier = 'accumulator:'; + + toJSON() { + return String(this); + } +} + +export class CheqdAccumulatorIdValue extends withQualifier(DIDRef) { + static Qualifier = 'accumulator:cheqd:'; + + static Ident = TypedUUID; + + static fromUnqualifiedString(str) { + const lastColon = str.lastIndexOf(':'); + const did = `did:cheqd:${str.slice(0, lastColon)}`; + const id = str.slice(lastColon + 1); + + return new this(did, id); + } + + toEncodedString() { + const { did, value } = this; + let prefix = ''; + if (did.value instanceof CheqdTestnetDid) { + prefix = 'testnet'; + } else if (did.value instanceof CheqdMainnetDid) { + prefix = 'mainnet'; + } + + return `${prefix}:${did.toEncodedString()}:${value}`; + } +} + +export class DockAccumulatorIdValue extends sized(TypedBytes) { + static Size = 32; +} + +export class DockAccumulatorIdIdent extends withQualifier( + withFrom( + sized(TypedBytes), + // eslint-disable-next-line no-use-before-define + (value, from) => (value instanceof DockAccumulatorId ? value[1] : from(value)), + ), +) { + static Qualifier = 'accumulator:dock:'; + + static Size = 32; + + static fromUnqualifiedString(bytes) { + return new this(bytes); + } + + toEncodedString() { + return this.value; + } +} + +export class CheqdAccumulatorId extends AccumulatorId { + static Class = CheqdAccumulatorIdValue; + + static Type = 'cheqd'; + + static random(did) { + return new this(this.Class.random(did)); + } +} + +export class DockAccumulatorId extends AccumulatorId { + static Class = DockAccumulatorIdIdent; + + static Type = 'dock'; + + static random() { + return new this(this.Class.random()); + } +} + +AccumulatorId.bindVariants(CheqdAccumulatorId, DockAccumulatorId); diff --git a/packages/credential-sdk/src/types/accumulator/index.js b/packages/credential-sdk/src/types/accumulator/index.js index 957d140cf..628f8868c 100644 --- a/packages/credential-sdk/src/types/accumulator/index.js +++ b/packages/credential-sdk/src/types/accumulator/index.js @@ -1,81 +1,6 @@ -import { DIDRef } from '../did'; -import { - TypedBytes, - TypedEnum, - TypedUUID, - sized, - withFrom, - withQualifier, -} from '../generic'; - -export class AccumulatorId extends withFrom( - withQualifier(TypedEnum, true), - (value, from) => { - try { - // eslint-disable-next-line no-use-before-define - return DockAccumulatorId.from(value); - } catch { - return from(value); - } - }, -) { - static Qualifier = 'accumulator:'; - - toJSON() { - return String(this); - } -} - -export class CheqdAccumulatorIdValue extends withQualifier(DIDRef) { - static Qualifier = 'accumulator:cheqd:'; - - static Ident = TypedUUID; -} - -export class DockAccumulatorIdValue extends sized(TypedBytes) { - static Size = 32; -} - -export class DockAccumulatorIdIdent extends withQualifier( - withFrom( - sized(TypedBytes), - // eslint-disable-next-line no-use-before-define - (value, from) => (value instanceof DockAccumulatorId ? value[1] : from(value)), - ), -) { - static Qualifier = 'accumulator:dock:'; - - static Size = 32; - - toEncodedString() { - return this.value; - } -} - -export class CheqdAccumulatorId extends AccumulatorId { - static Class = CheqdAccumulatorIdValue; - - static Type = 'cheqd'; - - static random(did) { - return new this(this.Class.random(did)); - } -} - -export class DockAccumulatorId extends AccumulatorId { - static Class = DockAccumulatorIdIdent; - - static Type = 'dock'; - - static random() { - return new this(this.Class.random()); - } -} - -AccumulatorId.bindVariants(CheqdAccumulatorId, DockAccumulatorId); - export * from './keys'; export * from './params'; export * from './public-key'; export * from './accumulator'; export * from './counters'; +export * from './accumulator-id'; diff --git a/packages/credential-sdk/src/types/blob/blob-id.js b/packages/credential-sdk/src/types/blob/blob-id.js index 999e38b39..67e331990 100644 --- a/packages/credential-sdk/src/types/blob/blob-id.js +++ b/packages/credential-sdk/src/types/blob/blob-id.js @@ -11,14 +11,17 @@ import { import { CheqdBlobQualifier, DockBlobQualifier } from './const'; import { CheqdMainnetDid, CheqdTestnetDid, DIDRef } from '../did'; -export class BlobId extends withFrom(withQualifier(TypedEnum, true), (value, from) => { - try { - // eslint-disable-next-line no-use-before-define - return DockBlobId.from(value); - } catch { - return from(value); - } -}) { +export class BlobId extends withFrom( + withQualifier(TypedEnum, true), + (value, from) => { + try { + // eslint-disable-next-line no-use-before-define + return DockBlobId.from(value); + } catch { + return from(value); + } + }, +) { static Qualifier = 'blob:'; toJSON() { @@ -31,15 +34,24 @@ export class CheqdBlobIdValue extends withQualifier(DIDRef) { static Ident = TypedUUID; + static fromUnqualifiedString(str) { + const lastColon = str.lastIndexOf(':'); + const did = `did:cheqd:${str.slice(0, lastColon)}`; + const id = str.slice(lastColon + 1); + + return new this(did, id); + } + toEncodedString() { + const { did, value } = this; let prefix = ''; - if (this[0].value instanceof CheqdTestnetDid) { + if (did.value instanceof CheqdTestnetDid) { prefix = 'testnet'; - } else if (this[0].value instanceof CheqdMainnetDid) { + } else if (did.value instanceof CheqdMainnetDid) { prefix = 'mainnet'; } - return `${prefix}:${this.value}`; + return `${prefix}:${did.toEncodedString()}:${value}`; } } diff --git a/packages/credential-sdk/src/types/did/onchain/typed-did/cheqd-did.js b/packages/credential-sdk/src/types/did/onchain/typed-did/cheqd-did.js index 8df748078..3af8aa51d 100644 --- a/packages/credential-sdk/src/types/did/onchain/typed-did/cheqd-did.js +++ b/packages/credential-sdk/src/types/did/onchain/typed-did/cheqd-did.js @@ -30,6 +30,10 @@ export class CheqdMainnetDidValue extends CheqdDidValue { } export class CheqdDid extends withQualifier(TypedEnum, true) { + get Qualifier() { + return this.Class?.Qualifier; + } + static random() { return new this(this.Class.random()); } diff --git a/packages/credential-sdk/src/types/generic/with-qualifier.js b/packages/credential-sdk/src/types/generic/with-qualifier.js index b928a7db2..6775f2c0f 100644 --- a/packages/credential-sdk/src/types/generic/with-qualifier.js +++ b/packages/credential-sdk/src/types/generic/with-qualifier.js @@ -1,4 +1,3 @@ -import { ID_STR } from '@docknetwork/crypto-wasm-ts'; import TypedBytes from './typed-bytes'; import { withExtendedStaticProperties, @@ -76,9 +75,13 @@ export default function withQualifier(klass, wrapper = false) { } static fromUnqualifiedString(str) { - throw new Error( - `Can't build an instance of \`${this.name}\` from unqualified string: ${ID_STR}` - ); + if (this.Class == null) { + throw new Error( + `Can't build an instance of \`${this.name}\` from unqualified string: ${str}` + ); + } else { + return new this(this.Class.fromUnqualifiedString(str)); + } } /** diff --git a/packages/credential-sdk/src/utils/misc.js b/packages/credential-sdk/src/utils/misc.js index 2a372b2aa..2f30dbecf 100644 --- a/packages/credential-sdk/src/utils/misc.js +++ b/packages/credential-sdk/src/utils/misc.js @@ -3,6 +3,7 @@ import elliptic from 'elliptic'; import { sha256 } from 'js-sha256'; import { PatternMatcher } from './generic'; +import { METHOD_REG_EXP_PATTERN } from '../resolver/generic/const'; const EC = elliptic.ec; const secp256k1Curve = new EC('secp256k1'); @@ -81,6 +82,54 @@ export function getUniqueElementsFromArray(a, filterCallback) { }); } +const ID_CHAR = '[a-zA-Z0-9_.-]'; +const METHOD_ID = `(${ID_CHAR}+(:${ID_CHAR}+)*)`; +const PARAM_CHAR = '[a-zA-Z0-9_.:%-]'; +const PARAM = `;${PARAM_CHAR}+=${PARAM_CHAR}*`; +const PARAMS = `((${PARAM})*)`; +// eslint-disable-next-line no-useless-escape +const PATH = '(/[^#?]*)?'; +const QUERY = '([?][^#]*)?'; +// eslint-disable-next-line no-useless-escape +const FRAGMENT = '(#.*)?'; +const DID_MATCHER = new RegExp( + `^did:${METHOD_REG_EXP_PATTERN}:${METHOD_ID}${PARAMS}${PATH}${QUERY}${FRAGMENT}$`, +); + +/** + * Parses supplied DID URL. + * @param {string} didUrl + * @returns {object} + */ +export function parseDIDUrl(didUrl) { + if (didUrl === '' || !didUrl) throw new Error('Missing DID'); + const sections = didUrl.match(DID_MATCHER); + if (sections) { + const parts = { + did: `did:${sections[1]}:${sections[2]}`, + method: sections[1], + id: sections[2], + didUrl, + }; + if (sections[4]) { + const params = sections[4].slice(1).split(';'); + parts.params = {}; + for (const p of params) { + const kv = p.split('='); + + // eslint-disable-next-line prefer-destructuring + parts.params[kv[0]] = kv[1]; + } + } + // eslint-disable-next-line prefer-destructuring + if (sections[6]) parts.path = sections[6]; + if (sections[7]) parts.query = sections[7].slice(1); + if (sections[8]) parts.fragment = sections[8].slice(1); + return parts; + } + throw new Error(`Invalid DID: \`${didUrl}\``); +} + /** * Ensures that provided value matches supplied pattern(s), throws an error otherwise. * diff --git a/packages/credential-sdk/src/vc/presentations.js b/packages/credential-sdk/src/vc/presentations.js index 6b69eaff3..38d149b6a 100644 --- a/packages/credential-sdk/src/vc/presentations.js +++ b/packages/credential-sdk/src/vc/presentations.js @@ -9,7 +9,7 @@ import { } from '@docknetwork/crypto-wasm-ts'; import b58 from 'bs58'; import { getPrivateStatus, verifyCredential } from './credentials'; -import DIDResolver from "../resolver/did/did-resolver"; // eslint-disable-line +/// import DIDResolver from "../resolver/did/did-resolver"; // eslint-disable-line import { isCredVerGte060 } from './crypto/common/DockCryptoSignature'; import defaultDocumentLoader from './document-loader'; diff --git a/packages/credential-sdk/src/vc/verifiable-presentation.js b/packages/credential-sdk/src/vc/verifiable-presentation.js index b6db87d85..c847f437f 100644 --- a/packages/credential-sdk/src/vc/verifiable-presentation.js +++ b/packages/credential-sdk/src/vc/verifiable-presentation.js @@ -9,7 +9,7 @@ import { import { getUniqueElementsFromArray } from '../utils/misc'; import VerifiableCredential from './verifiable-credential'; -import DIDResolver from "../resolver/did/did-resolver"; // eslint-disable-line +// import DIDResolver from "../resolver/did/did-resolver"; // eslint-disable-line const DEFAULT_CONTEXT = 'https://www.w3.org/2018/credentials/v1'; const DEFAULT_TYPE = 'VerifiablePresentation'; diff --git a/packages/credential-sdk/tests/resolvers.test.js b/packages/credential-sdk/tests/resolvers.test.js index 6dfbf5b87..1cb9a1723 100644 --- a/packages/credential-sdk/tests/resolvers.test.js +++ b/packages/credential-sdk/tests/resolvers.test.js @@ -1,130 +1,130 @@ -import { MultiResolver, WILDCARD, createResolver } from "../src/resolver"; - -class APrefixBMethodFull extends MultiResolver { - static PREFIX = "a"; - static METHOD = "b"; +import { + Resolver, + ResolverRouter, + WILDCARD, + WildcardResolverRouter, + createResolver, +} from "../src/resolver"; + +class APrefixBMethodResolver extends Resolver { + prefix = "a"; + method = "b"; async resolve(id) { return this.supports(id) ? `ab-full-${id}` : null; } } -class CPrefixDMethodFull extends MultiResolver { - static PREFIX = "c"; - static METHOD = "d"; +class CPrefixDMethodResolver extends Resolver { + prefix = "c"; + method = "d"; async resolve(id) { return this.supports(id) ? `cd-full-${id}` : null; } } -class BMethod extends MultiResolver { - static METHOD = "b"; +class BMethod extends ResolverRouter { + method = "b"; } -class APrefix extends MultiResolver { - static PREFIX = "a"; +class APrefix extends ResolverRouter { + prefix = "a"; } -class APrefixBMethod extends APrefix { - static METHOD = "b"; +class APrefixBMethod extends ResolverRouter { + method = "b"; } -class APrefixBMethodFullExtended extends APrefixBMethod { +class APrefixBMethodResolverExtended extends Resolver { + prefix = "a"; + method = "b"; + async resolve(id) { return this.supports(id) ? `ab-extended-${id}` : null; } } -class APrefixCDMethod extends MultiResolver { - static PREFIX = "a"; - static METHOD = ["c", "d"]; +class APrefixCDMethod extends Resolver { + prefix = "a"; + method = ["c", "d"]; async resolve(id) { return this.supports(id) ? `a-cd-${id}` : null; } } -class APrefixWildcardMethodFull extends MultiResolver { - static PREFIX = "a"; - static METHOD = WILDCARD; +class APrefixWildcardMethodResolver extends Resolver { + prefix = "a"; + method = WILDCARD; async resolve(id) { return this.supports(id) ? `a-wildcard-${id}` : null; } } -class WildcardPrefixBMethodFull extends MultiResolver { - static PREFIX = WILDCARD; - static METHOD = "b"; +class WildcardPrefixBMethodResolver extends Resolver { + prefix = WILDCARD; + method = "b"; async resolve(id) { return this.supports(id) ? `wildcard-b-${id}` : null; } } -class WildcardPrefixAndMethod extends MultiResolver { - static PREFIX = WILDCARD; - static METHOD = WILDCARD; -} +class WildcardPrefixAndMethodResolver extends Resolver { + prefix = WILDCARD; + method = WILDCARD; -class WildcardPrefixAndMethodFull extends WildcardPrefixAndMethod { async resolve(id) { return this.supports(id) ? `wildcard-wildcard-${id}` : null; } } describe("Resolvers", () => { - it("checks `MultiResolver`", async () => { - expect(() => new MultiResolver()).toThrowError( - "Static property `PREFIX` of `MultiResolver` isn't extended properly" + it("checks `ResolverRouter`", async () => { + expect(() => new ResolverRouter()).toThrowError( + "No resolvers were provided. You need to either implement `resolve` or provide a list of resolvers" ); expect(() => new BMethod()).toThrowError( - "Static property `PREFIX` of `BMethod` isn't extended properly" + "No resolvers were provided. You need to either implement `resolve` or provide a list of resolvers" ); expect(() => new APrefixBMethod()).toThrowError( "No resolvers were provided. You need to either implement `resolve` or provide a list of resolvers" ); expect(() => new APrefix()).toThrowError( - "Static property `METHOD` of `APrefix` isn't extended properly" - ); - expect(() => new MultiResolver([new APrefixBMethodFull()])).toThrowError( - "Static property `PREFIX` of `MultiResolver` isn't extended properly" - ); - expect( - () => - new APrefixBMethod([new APrefixBMethodFull(), new APrefixBMethodFull()]) - ).toThrowError( - "Two resolvers for the same prefix and method - `a:b:`: `APrefixBMethodFull` and `APrefixBMethodFull`" + "No resolvers were provided. You need to either implement `resolve` or provide a list of resolvers" ); - expect(await new APrefixBMethodFull().resolve("a:b:123")).toBe( + expect(await new APrefixBMethodResolver().resolve("a:b:123")).toBe( "ab-full-a:b:123" ); - expect(new APrefixBMethodFull().supports("a:b:123")).toBe(true); - expect(new APrefixBMethodFull().supports("a:c:123")).toBe(false); - expect(new APrefixBMethodFull().supports("c:b:123")).toBe(false); - expect(new APrefixBMethodFull().supports("a:b:123")).toBe(true); - expect(new APrefixBMethodFull().supports("a:c:123")).toBe(false); - expect(new APrefixBMethodFull().supports("c:b:123")).toBe(false); - - expect(await new APrefixBMethodFullExtended().resolve("a:b:456")).toBe( + expect(new APrefixBMethodResolver().supports("a:b:123")).toBe(true); + expect(new APrefixBMethodResolver().supports("a:c:123")).toBe(false); + expect(new APrefixBMethodResolver().supports("c:b:123")).toBe(false); + expect(new APrefixBMethodResolver().supports("a:b:123")).toBe(true); + expect(new APrefixBMethodResolver().supports("a:c:123")).toBe(false); + expect(new APrefixBMethodResolver().supports("c:b:123")).toBe(false); + + expect(await new APrefixBMethodResolverExtended().resolve("a:b:456")).toBe( "ab-extended-a:b:456" ); expect( - await new APrefixBMethod([new APrefixBMethodFull()]).resolve("a:b:456") + await new APrefixBMethod([new APrefixBMethodResolver()]).resolve( + "a:b:456" + ) ).toBe("ab-full-a:b:456"); expect( - await new APrefixBMethod([new APrefixBMethodFullExtended()]).resolve( + await new APrefixBMethod([new APrefixBMethodResolverExtended()]).resolve( "a:b:456" ) ).toBe("ab-extended-a:b:456"); - const wildcard = new WildcardPrefixAndMethod([ - new APrefixBMethodFull(), - new CPrefixDMethodFull(), - new APrefixWildcardMethodFull(), - new WildcardPrefixBMethodFull(), - new WildcardPrefixAndMethodFull(), + const wildcard = new WildcardResolverRouter([ + new APrefixBMethodResolver(), + new CPrefixDMethodResolver(), + new APrefixWildcardMethodResolver(), + new WildcardPrefixBMethodResolver(), + new WildcardPrefixAndMethodResolver(), ]); expect(await wildcard.resolve("a:b:")).toBe("ab-full-a:b:"); @@ -139,9 +139,9 @@ describe("Resolvers", () => { "wildcard-wildcard-asdasdasd:asdasdasd:" ); - const nestedWildcard = new WildcardPrefixAndMethod([ - new APrefixBMethodFullExtended(), - new WildcardPrefixAndMethod([wildcard]), + const nestedWildcard = new WildcardResolverRouter([ + new APrefixBMethodResolverExtended(), + new WildcardResolverRouter([wildcard]), ]); expect(await nestedWildcard.resolve("a:b:")).toBe("ab-extended-a:b:"); expect(await nestedWildcard.resolve("c:d:")).toBe("cd-full-c:d:"); @@ -152,15 +152,15 @@ describe("Resolvers", () => { "wildcard-wildcard-asdasdasd:asdasdasd:" ); - const abTop = new APrefixBMethodFullExtended(); - const ab = new APrefixBMethodFull(); - const cd = new CPrefixDMethodFull(); + const abTop = new APrefixBMethodResolverExtended(); + const ab = new APrefixBMethodResolver(); + const cd = new CPrefixDMethodResolver(); const acdTop = new APrefixCDMethod(); - const awildcard = new APrefixWildcardMethodFull(); - const widlcardb = new WildcardPrefixBMethodFull(); + const awildcard = new APrefixWildcardMethodResolver(); + const widlcardb = new WildcardPrefixBMethodResolver(); - const discreteWrappedInWildcard = new WildcardPrefixAndMethod([ - new WildcardPrefixAndMethod([ab, cd, awildcard, widlcardb]), + const discreteWrappedInWildcard = new WildcardResolverRouter([ + new WildcardResolverRouter([ab, cd, awildcard, widlcardb]), acdTop, abTop, ]); @@ -191,11 +191,11 @@ describe("Resolvers", () => { const resolve = async () => 1; expect(() => - createResolver(new APrefixBMethodFull(), { prefix: "c" }) + createResolver(new APrefixBMethodResolver(), { prefix: "c" }) ).toThrowError("Item not found in [c]: `a`"); expect(() => - createResolver(new APrefixBMethodFull(), { method: "c" }) + createResolver(new APrefixBMethodResolver(), { method: "c" }) ).toThrowError("Item not found in [c]: `b`"); const singleResolver = createResolver(resolve, { diff --git a/packages/dock-blockchain-api/scripts/loader.mjs b/packages/dock-blockchain-api/scripts/loader.mjs new file mode 100644 index 000000000..5ff5be1fd --- /dev/null +++ b/packages/dock-blockchain-api/scripts/loader.mjs @@ -0,0 +1,26 @@ +const solutions = [ + (s) => s, + (s) => `${s}/index.js`, + (s) => `${s}.js`, + (s) => `${s}/lib/index.js`, +]; + +export async function resolve(specifier, context, defaultResolve) { + for (const solve of solutions) { + const resolvedPath = solve(specifier); + + try { + return await defaultResolve(resolvedPath, context, defaultResolve); + } catch (err) { + if ( + err.code !== "ERR_MODULE_NOT_FOUND" && + err.code !== "ERR_PACKAGE_PATH_NOT_EXPORTED" && + err.code !== "ERR_UNSUPPORTED_DIR_IMPORT" + ) { + console.error(`Error resolving path: ${resolvedPath}`, err); + } + } + } + + throw new Error(`Module not found: ${specifier}`); +} diff --git a/packages/dock-blockchain-api/scripts/slack.js b/packages/dock-blockchain-api/scripts/slack.js index d247b62a6..548cca842 100644 --- a/packages/dock-blockchain-api/scripts/slack.js +++ b/packages/dock-blockchain-api/scripts/slack.js @@ -1,10 +1,5 @@ import { curry } from "ramda"; import fetch from "node-fetch"; -import { envObj, notNilAnd } from "./helpers"; - -const { SlackNotificationWebhookUrl } = envObj({ - SlackNotificationWebhookUrl: notNilAnd(String), -}); export const TYPES = { WARNING: "warning", @@ -15,7 +10,7 @@ export const TYPES = { /** * Posts a message to Slack. */ -export const postMessage = curry(async (type, header, fields) => { +export const postMessage = curry(async (url, type, header, fields) => { const body = Buffer.from( JSON.stringify({ text: header, @@ -33,7 +28,7 @@ export const postMessage = curry(async (type, header, fields) => { "Content-Length": body.byteLength, }; - return await fetch(SlackNotificationWebhookUrl, { + return await fetch(url, { headers, method: "POST", body, diff --git a/packages/dock-blockchain-api/scripts/watch-essential-events.js b/packages/dock-blockchain-api/scripts/watch-essential-events.js index f8e9f996d..cf9162afa 100644 --- a/packages/dock-blockchain-api/scripts/watch-essential-events.js +++ b/packages/dock-blockchain-api/scripts/watch-essential-events.js @@ -79,8 +79,9 @@ import { finiteNumber, timestampLogger, blockByNumber, + accountIdentity, } from "./helpers"; -import dock from "../src"; +import { DockAPI } from "../src"; import { sendAlarmEmailText } from "./email_utils"; import { TYPES, postMessage } from "./slack"; @@ -96,6 +97,8 @@ const { BlockProcessingTimeOut, RetryDelay, RetryAttempts, + SlackNotificationsWebhookMainUrl, + SlackNotificationsWebhookMigrationUrl, } = envObj({ // Email to send alarm emails to. TxWatcherAlarmEmailTo: notNilAnd(split(",")), @@ -124,6 +127,8 @@ const { RetryDelay: o(finiteNumber, defaultTo(3e4)), // Amount of retry attempts before exiting with 1 code. RetryAttempts: o(finiteNumber, defaultTo(3)), + SlackNotificationsWebhookMainUrl: notNilAnd(String), + SlackNotificationsWebhookMigrationUrl: notNilAnd(String), }); const main = async (dock, startBlock) => { @@ -159,6 +164,7 @@ const main = async (dock, startBlock) => { (error) => void timestampLogger.error(error) || sendMessage( + SlackNotificationsWebhookMainUrl, "Mainnet watcher was restarted", `Due to either connection or node API problems, the dock blockchain essential notifications watcher was restarted with the last unprocessed block ${minUnprocessed}.` ) @@ -267,8 +273,33 @@ const processBlocks = (dock, startBlock) => { events$.pipe(applyFilters(eventFilters)) ).pipe( batchNotifications(BatchNoficationTimeout), - tap((email) => timestampLogger.log("Sending email: ", email)), - mergeMap(o(from, sendMessage("Mainnet alarm notification"))) + tap((email) => timestampLogger.log("Sending message: ", email)), + mergeMap((message) => { + if (!message.includes("migrated")) { + return from( + sendMessage( + SlackNotificationsWebhookMainUrl, + "Mainnet alarm notification", + message + ) + ); + } else { + return from( + postMessage( + SlackNotificationsWebhookMigrationUrl, + TYPES.SUCCESS, + "Token migration", + [ + { + title: "Summary", + value: message, + short: false, + }, + ] + ) + ); + } + }) ); const number = block.block.header.number.toNumber(); @@ -404,10 +435,10 @@ const syncPrevousBlocks = curry((dock, startBlock, currentBlocks$) => { * @param {string} message * @returns {Observable<*>} */ -const sendMessage = curry((header, message) => +const sendMessage = curry((url, header, message) => merge( from( - postMessage(TYPES.DANGER, header, [ + postMessage(url, TYPES.DANGER, header, [ { title: "Summary", value: message, @@ -574,6 +605,16 @@ const technicalCommitteeMembershipEvent = eventByMethodFilter( */ const electionsEvent = eventByMethodFilter("elections"); +/** + * Filter events produced by `staking` pallet. + */ +const stakingEvent = eventByMethodFilter("staking"); + +/** + * Filter events produced by `cheqdMigration` pallet. + */ +const cheqdMigrationEvent = eventByMethodFilter("cheqdMigration"); + /** * Filter extrinsics produced by `technicalCommitteeMembership` pallet. */ @@ -763,6 +804,62 @@ const eventFilters = [ }) => of(`Council member renounced: ${member.toString()}`) ), + checkMap( + stakingEvent("Unbonded"), + ( + { + event: { + data: [account, balance], + }, + }, + dock + ) => + of({ account, balance }).pipe( + filterRx(({ balance }) => balance.toNumber() > 1e12), + concatMap(({ account, balance }) => + from(accountIdentity(dock.api, account)).pipe( + mapRx((acc) => `Account ${acc} unbonded ${formatDock(balance)}`) + ) + ) + ) + ), + + checkMap( + stakingEvent("Chilled"), + ( + { + event: { + data: [account], + }, + }, + dock + ) => + from(accountIdentity(dock.api, account)).pipe( + mapRx((account) => `Account ${account} chilled`) + ) + ), + + checkMap( + cheqdMigrationEvent("Migrated"), + ( + { + event: { + data: [dockAccount, cheqdAccount, tokensAmount], + }, + }, + dock + ) => + from(accountIdentity(dock.api, dockAccount)).pipe( + mergeMap((account) => + of( + `${account} migrated \`${formatDock( + tokensAmount + )}\` to \`${cheqdAccount}\`` + ) + ) + ) + ), + // Council members changed checkMap( electionsEvent("NewTerm"), @@ -903,7 +1000,7 @@ const createTxWithEventsCombinator = (dock) => { * @param {Array<*>} events * @param {*} tx * @param {{config: number, rootTx: *}} param2 - * @returns Promise<*> + * @returns */ const pickEventsForExtrinsic = (events, tx, { config, rootTx }) => { let batchIdx = -1; @@ -1020,7 +1117,7 @@ const createTxWithEventsCombinator = (dock) => { return mergeEventsWithExtrs; }; -main(dock, StartBlock) +main(new DockAPI(), StartBlock) .then(() => process.exit(0)) .catch((err) => { timestampLogger.error(err); diff --git a/packages/dock-blockchain-api/src/api/index.js b/packages/dock-blockchain-api/src/api/index.js index 4d60884c7..f1bb898c2 100644 --- a/packages/dock-blockchain-api/src/api/index.js +++ b/packages/dock-blockchain-api/src/api/index.js @@ -337,6 +337,10 @@ export default class DockAPI extends ApiProvider { return Boolean(this.api) && this.api.isConnected; } + methods() { + return ['dock']; + } + supportsIdentifier(id) { this.ensureInitialized(); diff --git a/packages/dock-blockchain-modules/package.json b/packages/dock-blockchain-modules/package.json index 302c4afdf..0afecf386 100644 --- a/packages/dock-blockchain-modules/package.json +++ b/packages/dock-blockchain-modules/package.json @@ -70,7 +70,7 @@ "docs": "rm -rf out && mkdir out && touch out/.nojekyll && jsdoc src -r -c ../../.jsdoc -d out/reference", "prepublishOnly": "yarn build", "dev-node": "../../scripts/run_dock_node_in_docker --dev --rpc-external --ws-external --rpc-cors=all", - "test": "LOG_STATE_CHANGE=1 NODE_ENV=production jest --verbose --runInBand --forceExit ./tests/integration/*", + "test": "LOG_STATE_CHANGE=1 NODE_ENV=production jest --verbose --runInBand --forceExit ./tests/*", "test-with-node": "../../scripts/with_dock_docker_test_node yarn test", "test-with-all-nodes": "../../scripts/with_all_dock_docker_test_nodes yarn test-integration" } diff --git a/packages/dock-blockchain-modules/src/common/dock-api-provider.js b/packages/dock-blockchain-modules/src/common/dock-api-provider.js index 4bf298c59..a9487220d 100644 --- a/packages/dock-blockchain-modules/src/common/dock-api-provider.js +++ b/packages/dock-blockchain-modules/src/common/dock-api-provider.js @@ -21,6 +21,10 @@ class DockApiProvider extends ApiProvider { return api; } + methods() { + return this.dock.methods(); + } + isInitialized() { return this.dock.isInitialized(); } diff --git a/packages/dock-blockchain-modules/src/common/inject-dock.js b/packages/dock-blockchain-modules/src/common/inject-dock.js index dd55221b6..c2f36de1e 100644 --- a/packages/dock-blockchain-modules/src/common/inject-dock.js +++ b/packages/dock-blockchain-modules/src/common/inject-dock.js @@ -18,6 +18,10 @@ export default function injectDock(klass) { this.apiProvider = new DockApiProvider(dock); this.dockOnly = new this.constructor.DockOnly(this.apiProvider); } + + methods() { + return this.apiProvider.methods(); + } }, }; diff --git a/packages/dock-blockchain-modules/tests/integration/anoncreds/bbs-wallet.test.js b/packages/dock-blockchain-modules/tests/integration/anoncreds/bbs-wallet.test.js index 912831cfc..b222a7e56 100644 --- a/packages/dock-blockchain-modules/tests/integration/anoncreds/bbs-wallet.test.js +++ b/packages/dock-blockchain-modules/tests/integration/anoncreds/bbs-wallet.test.js @@ -4,8 +4,8 @@ import BbsPlusPresentation from "@docknetwork/credential-sdk/vc/presentation"; import VerifiableCredential from "@docknetwork/credential-sdk/vc/verifiable-credential"; import { DIDKeyResolver, - DockResolver, - WildcardMultiResolver, + CoreResolver, + WildcardResolverRouter, } from "@docknetwork/credential-sdk/resolver"; import VerifiablePresentation from "@docknetwork/credential-sdk/vc/verifiable-presentation"; import { keyDocToKeypair } from "./wallet-util"; @@ -305,8 +305,8 @@ async function verifyDerivedCredential(dock, credential, resolver) { describe("Presentation from older credentials", () => { const dock = new DockAPI(); const modules = new DockCoreModules(dock); - const resolver = new WildcardMultiResolver([ - new DockResolver(modules), + const resolver = new WildcardResolverRouter([ + new CoreResolver(modules), new DIDKeyResolver(), ]); diff --git a/packages/dock-blockchain-modules/tests/integration/anoncreds/derived-credentials.test.js b/packages/dock-blockchain-modules/tests/integration/anoncreds/derived-credentials.test.js index 2977de94f..a7a19a9db 100644 --- a/packages/dock-blockchain-modules/tests/integration/anoncreds/derived-credentials.test.js +++ b/packages/dock-blockchain-modules/tests/integration/anoncreds/derived-credentials.test.js @@ -39,7 +39,7 @@ import { verifyPresentation, verifyCredential, } from "@docknetwork/credential-sdk/vc"; -import { DockResolver } from "@docknetwork/credential-sdk/resolver"; +import { CoreResolver } from "@docknetwork/credential-sdk/resolver"; import { createPresentation } from "../../create-presentation"; import { AccumulatorType } from "@docknetwork/credential-sdk/modules/abstract/accumulator/module"; import { @@ -113,7 +113,7 @@ describe.each(Schemes)( }) => { const dock = new DockAPI(); const modules = new MultiApiCoreModules([new DockCoreModules(dock)]); - const resolver = new DockResolver(modules); + const resolver = new CoreResolver(modules); const did1 = DockDid.random(); if (Name === "PS") { return; diff --git a/packages/dock-blockchain-modules/tests/integration/anoncreds/issuing.test.js b/packages/dock-blockchain-modules/tests/integration/anoncreds/issuing.test.js index 9fbbbf0e5..59bc9c481 100644 --- a/packages/dock-blockchain-modules/tests/integration/anoncreds/issuing.test.js +++ b/packages/dock-blockchain-modules/tests/integration/anoncreds/issuing.test.js @@ -9,7 +9,7 @@ import { import stringify from "json-stringify-deterministic"; import { DockAPI } from "@docknetwork/dock-blockchain-api"; import { DockDid } from "@docknetwork/credential-sdk/types"; -import { DockResolver } from "@docknetwork/credential-sdk/resolver"; +import { CoreResolver } from "@docknetwork/credential-sdk/resolver"; import { registerNewDIDUsingPair, getCredMatcherDoc, @@ -45,7 +45,7 @@ describe.each(Schemes)( ({ Name, Module, Context, CryptoKeyPair, getModule, VerKey, SigType }) => { const dock = new DockAPI(); const modules = new MultiApiCoreModules([new DockCoreModules(dock)]); - const resolver = new DockResolver(modules); + const resolver = new CoreResolver(modules); const did1 = DockDid.random(); const pair1 = new DidKeypair([did1, 1], Ed25519Keypair.random()); diff --git a/packages/dock-blockchain-modules/tests/integration/anoncreds/presentation.test.js b/packages/dock-blockchain-modules/tests/integration/anoncreds/presentation.test.js index 1c5b32b48..ab5d9db2e 100644 --- a/packages/dock-blockchain-modules/tests/integration/anoncreds/presentation.test.js +++ b/packages/dock-blockchain-modules/tests/integration/anoncreds/presentation.test.js @@ -21,7 +21,7 @@ import { issueCredential, verifyPresentation, } from "@docknetwork/credential-sdk/vc"; -import { DockResolver } from "@docknetwork/credential-sdk/resolver"; +import { CoreResolver } from "@docknetwork/credential-sdk/resolver"; import { getResidentCardCredentialAndSchema, setupExternalSchema, @@ -35,9 +35,7 @@ import { Ed25519Keypair, DidKeypair, } from "@docknetwork/credential-sdk/keypairs"; -import { - MultiApiCoreModules -} from "@docknetwork/credential-sdk/modules"; +import { MultiApiCoreModules } from "@docknetwork/credential-sdk/modules"; import { DockCoreModules } from "../../../src"; describe.each(Schemes)( @@ -53,7 +51,7 @@ describe.each(Schemes)( }) => { const dock = new DockAPI(); const modules = new MultiApiCoreModules([new DockCoreModules(dock)]); - const resolver = new DockResolver(modules); + const resolver = new CoreResolver(modules); let account; let did1; let pair1; diff --git a/packages/dock-blockchain-modules/tests/integration/issuing-and-presentation-with-2-subjects.test.js b/packages/dock-blockchain-modules/tests/integration/issuing-and-presentation-with-2-subjects.test.js index e902b4643..04cb62f7c 100644 --- a/packages/dock-blockchain-modules/tests/integration/issuing-and-presentation-with-2-subjects.test.js +++ b/packages/dock-blockchain-modules/tests/integration/issuing-and-presentation-with-2-subjects.test.js @@ -6,7 +6,7 @@ import mockFetch from "../mocks/fetch"; import { DockDid } from "@docknetwork/credential-sdk/types"; import { DockAPI } from "@docknetwork/dock-blockchain-api"; -import { DockResolver } from "@docknetwork/credential-sdk/resolver"; +import { CoreResolver } from "@docknetwork/credential-sdk/resolver"; import { FullNodeEndpoint, @@ -77,7 +77,7 @@ const unsignedCred = { describe("Verifiable Credential issuance and presentation where the credential has 2 subjects and of the subject acts as the holder of the presentation", () => { const dock = new DockAPI(); const modules = new DockCoreModules(dock); - const resolver = new DockResolver(modules); + const resolver = new CoreResolver(modules); beforeAll(async () => { await dock.init({ diff --git a/packages/dock-blockchain-modules/tests/integration/schema.test.js b/packages/dock-blockchain-modules/tests/integration/schema.test.js index 5c64dfde4..21210c0d8 100644 --- a/packages/dock-blockchain-modules/tests/integration/schema.test.js +++ b/packages/dock-blockchain-modules/tests/integration/schema.test.js @@ -17,7 +17,7 @@ import VerifiableCredential from "@docknetwork/credential-sdk/vc/verifiable-cred import exampleSchema from "../example-schema"; import VerifiablePresentation from "@docknetwork/credential-sdk/vc/verifiable-presentation"; import { getKeyDoc } from "@docknetwork/credential-sdk/vc/helpers"; -import { DockResolver } from "@docknetwork/credential-sdk/resolver"; +import { CoreResolver } from "@docknetwork/credential-sdk/resolver"; import { DockCoreModules } from "../../src"; import { registerNewDIDUsingPair } from "./helpers"; import { @@ -88,7 +88,7 @@ describe("Schema Blob Module Integration", () => { keyDoc = getKeyDoc(dockDID, new Ed25519Keypair(firstKeySeed)); // Create a resolver for dock DIDs - dockResolver = new DockResolver(modules); + dockResolver = new CoreResolver(modules); // Create a valid credential with a schema validCredential = new VerifiableCredential( diff --git a/packages/dock-blockchain-modules/tests/integration/status-list-credential.test.js b/packages/dock-blockchain-modules/tests/integration/status-list-credential.test.js index 16f10bb75..56ffc2cf6 100644 --- a/packages/dock-blockchain-modules/tests/integration/status-list-credential.test.js +++ b/packages/dock-blockchain-modules/tests/integration/status-list-credential.test.js @@ -14,7 +14,7 @@ import { verifyPresentation, } from "@docknetwork/credential-sdk/vc"; -import { DockResolver } from "@docknetwork/credential-sdk/resolver"; +import { CoreResolver } from "@docknetwork/credential-sdk/resolver"; import { createPresentation } from "../create-presentation"; import { DockCoreModules } from "../../src"; @@ -33,7 +33,7 @@ const credId = "A large credential id with size > 32 bytes"; describe("StatusList2021Credential", () => { const dockAPI = new DockAPI(); const modules = new DockCoreModules(dockAPI); - const resolver = new DockResolver(modules); + const resolver = new CoreResolver(modules); // Create a random status list id const statusListCredentialId = randomAsHex(32); diff --git a/scripts/with_cheqd_docker_test_node b/scripts/with_cheqd_docker_test_node index 4c770d356..2e4cbef5d 100755 --- a/scripts/with_cheqd_docker_test_node +++ b/scripts/with_cheqd_docker_test_node @@ -13,7 +13,7 @@ docker pull --platform linux/amd64 ghcr.io/cheqd/cheqd-node alice_container_id=$(docker run --platform linux/amd64 -d --rm --name cheqd-dev -p 26656:26656 -p 26657:26657 -p 1317:1317 -p 9090:9090 -e CHEQD_MNEMONIC="$CHEQD_MNEMONIC" -v $entrypoint:/usr/local/bin/entrypoint.sh -v $config:/tmp/cheqd_config.toml --entrypoint /usr/local/bin/entrypoint.sh ghcr.io/cheqd/cheqd-node start) cleanup() { - docker kill $alice_container_id& + docker kill $alice_container_id 2> /dev/null } try_with_node() { @@ -22,7 +22,7 @@ try_with_node() { $@ } -trap cleanup EXIT +trap cleanup EXIT SIGINT SIGTERM if try_with_node $@; then exit_code=$? @@ -30,7 +30,4 @@ else exit_code=$? fi -# Kill nodes -cleanup - exit $exit_code diff --git a/scripts/with_dock_docker_test_node b/scripts/with_dock_docker_test_node index f64f64b5f..cec212f8f 100755 --- a/scripts/with_dock_docker_test_node +++ b/scripts/with_dock_docker_test_node @@ -18,7 +18,7 @@ alice_container_id=$( ) cleanup() { - docker kill $alice_container_id& + docker kill $alice_container_id 2> /dev/null } try_with_node() { @@ -28,7 +28,7 @@ try_with_node() { $@ } -trap cleanup EXIT +trap cleanup EXIT SIGINT SIGTERM if try_with_node $@; then exit_code=$? @@ -36,7 +36,4 @@ else exit_code=$? fi -# Kill nodes -cleanup - exit $exit_code diff --git a/tutorials/src/tutorial_blobs_schemas.md b/tutorials/src/tutorial_blobs_schemas.md index 6855a29cd..c8e5e7fdd 100644 --- a/tutorials/src/tutorial_blobs_schemas.md +++ b/tutorials/src/tutorial_blobs_schemas.md @@ -160,7 +160,7 @@ used when present. Basically, once you've created and stored your Schema on chai ```javascript > const dockApi = new DockAPI(); -> const dockResolver = new DockResolver(dockApi); +> const CoreResolver = new CoreResolver(dockApi); > let validCredential = new VerifiableCredential('https://example.com/credentials/123'); > validCredential.addContext('https://www.w3.org/2018/credentials/examples/v1'); > const ctx1 = { @@ -178,7 +178,7 @@ used when present. Basically, once you've created and stored your Schema on chai > validCredential.setSchema(blobHexIdToQualified(blobId), 'JsonSchemaValidator2018'); > await validCredential.sign(keyDoc); > await validCredential.verify({ - resolver: dockResolver, + resolver: CoreResolver, compactProof: true, }); ``` diff --git a/tutorials/src/tutorial_resolver.md b/tutorials/src/tutorial_resolver.md index 28fe2c7a4..b27383c1b 100644 --- a/tutorials/src/tutorial_resolver.md +++ b/tutorials/src/tutorial_resolver.md @@ -12,22 +12,22 @@ The SDK supports resolving Dock DIDs natively. For other DIDs, resolving the DID Each resolver should extend the class `DIDResolver` and implement the `resolve` method that accepts a DID and returns the DID document. -There is another class called `MultiResolver` that can accept several types of resolvers (objects of subclasses -of `DIDResolver`) and once the `MultiResolver` is initialized with the resolvers of different DID methods, it can resolve +There is another class called `ResolverRouter` that can accept several types of resolvers (objects of subclasses +of `DIDResolver`) and once the `ResolverRouter` is initialized with the resolvers of different DID methods, it can resolve DIDs of those methods. ## Dock resolver -The resolver for Dock DIDs `DockResolver` connects to the Dock blockchain to get the DID details. +The resolver for Dock DIDs `CoreResolver` connects to the Dock blockchain to get the DID details. The resolver is constructed by passing it a Dock API object so that it can connect to a Dock node. This is how you resolve a Dock DID: ```js -import { DockResolver } from "@docknetwork/credential-sdk/resolver"; +import { CoreResolver } from "@docknetwork/credential-sdk/resolver"; // Assuming the presence of modules created using `CheqdCoreModules` or `DockCoreModules` from the API object. -const dockResolver = new DockResolver(modules); +const dockResolver = new CoreResolver(modules); // Say you had a DID `did:dock:5D.....` const didDocument = await dockResolver.resolve("did:dock:5D....."); ``` @@ -42,7 +42,8 @@ Following is an example to build a custom Ethereum resolver. It uses the library as configuration. The example below uses Infura to get access to an Ethereum node and read the DID off Ethereum. ```js -import { DIDResolver } from "@docknetwork/credential-sdk/resolver"; +import { Resolver } from "@docknetwork/credential-sdk/resolver"; +import { parseDIDUrl } from "@docknetwork/credential-sdk/utils"; import ethr from "ethr-did-resolver"; // Infura's Ethereum provider for the main net. @@ -56,8 +57,10 @@ const ethereumProviderConfig = { }; // Custom ethereum resolver class -class EtherResolver extends DIDResolver { - static METHOD = "ethr"; +class EtherResolver extends Resolver { + prefix = "did"; + + method = "ethr"; constructor(config) { super(); @@ -65,7 +68,7 @@ class EtherResolver extends DIDResolver { } async resolve(did) { - const parsed = this.parseDid(did); + const parsed = parseDIDUrl(did); try { return this.ethres(did, parsed); } catch (e) { @@ -111,17 +114,18 @@ For resolving DID of any other method, `UniversalResolver` object will be used. ```js import { - DockDIDResolver, - DIDResolver, + Resolver, WILDCARD, } from "@docknetwork/credential-sdk/resolver"; -class MultiDIDResolver extends DIDResolver { - static METHOD = WILDCARD; +class MultiDIDResolver extends Resolver { + prefix = "did"; + + method = WILDCARD; constructor(modules) { super([ - new DockDIDResolver(modules.did), + new DIDResolver(modules.did), new EtherResolver(ethereumProviderConfig), new UniversalResolver(universalResolverUrl), ]); @@ -130,7 +134,7 @@ class MultiDIDResolver extends DIDResolver { const multiResolver = new MultiDIDResolver(resolvers); -// Say you had a DID `did:dock:5D....`, then the `DockResolver` will be used as there a resolver for Dock DID. +// Say you had a DID `did:dock:5D....`, then the `CoreResolver` will be used as there a resolver for Dock DID. const didDocumentDock = await multiResolver.resolve("did:dock:5D...."); // Say you had a DID `did:btcr:xk....`, then the `UniversalResolver` will be used as there is no resolver for BTC DID.