From 9ac74ffe519700b059a7eb30ac72a11a6c3132bc Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Tue, 7 Feb 2023 11:31:51 +0100 Subject: [PATCH 1/5] Initial setup --- packages/asset-did/src/AssetDid.ts | 49 +------- packages/asset-did/src/Resolver.ts | 108 ++++++++++++++++++ packages/asset-did/src/index.ts | 1 + packages/types/src/AssetDid.ts | 29 +++++ packages/types/src/Attestation.ts | 2 +- packages/types/src/Claim.ts | 2 +- packages/types/src/Credential.ts | 2 +- packages/types/src/CryptoCallbacks.ts | 2 +- packages/types/src/Delegation.ts | 2 +- .../{DidDocument.ts => KiltDidDocument.ts} | 0 ...Exporter.ts => KiltDidDocumentExporter.ts} | 4 +- .../{DidResolver.ts => KiltDidResolver.ts} | 4 +- packages/types/src/Message.ts | 2 +- packages/types/src/PublicCredential.ts | 2 +- packages/types/src/Quote.ts | 2 +- packages/types/src/index.ts | 6 +- 16 files changed, 156 insertions(+), 61 deletions(-) create mode 100644 packages/asset-did/src/Resolver.ts rename packages/types/src/{DidDocument.ts => KiltDidDocument.ts} (100%) rename packages/types/src/{DidDocumentExporter.ts => KiltDidDocumentExporter.ts} (97%) rename packages/types/src/{DidResolver.ts => KiltDidResolver.ts} (97%) diff --git a/packages/asset-did/src/AssetDid.ts b/packages/asset-did/src/AssetDid.ts index 395c2067b..6117d5a07 100644 --- a/packages/asset-did/src/AssetDid.ts +++ b/packages/asset-did/src/AssetDid.ts @@ -5,52 +5,9 @@ * found in the LICENSE file in the root directory of this source tree. */ -import type { - AssetDidUri, - Caip19AssetId, - Caip19AssetInstance, - Caip19AssetNamespace, - Caip19AssetReference, - Caip2ChainId, - Caip2ChainNamespace, - Caip2ChainReference, -} from '@kiltprotocol/types' -import { SDKErrors } from '@kiltprotocol/utils' +import type { AssetDidUri } from '@kiltprotocol/types' -// Matches AssetDIDs as per the [AssetDID specification](https://github.com/KILTprotocol/spec-asset-did). -const ASSET_DID_REGEX = - /^did:asset:(?(?[-a-z0-9]{3,8}):(?[-a-zA-Z0-9]{1,32}))\.(?(?[-a-z0-9]{3,8}):(?[-a-zA-Z0-9]{1,64})(:(?[-a-zA-Z0-9]{1,78}))?)$/ - -type IAssetDidParsingResult = { - uri: AssetDidUri - chainId: Caip2ChainId - chainNamespace: Caip2ChainNamespace - chainReference: Caip2ChainReference - assetId: Caip19AssetId - assetNamespace: Caip19AssetNamespace - assetReference: Caip19AssetReference - assetInstance?: Caip19AssetInstance -} - -/** - * Parses an AssetDID uri and returns the information contained within in a structured form. - - * @param assetDidUri An AssetDID uri as a string. -* @returns Object containing information extracted from the AssetDID uri. - */ -export function parse(assetDidUri: AssetDidUri): IAssetDidParsingResult { - const matches = ASSET_DID_REGEX.exec(assetDidUri)?.groups - if (!matches) { - throw new SDKErrors.InvalidDidFormatError(assetDidUri) - } - - const { chainId, assetId } = matches as Omit - - return { - ...(matches as Omit), - uri: `did:asset:${chainId}.${assetId}`, - } -} +import { resolve } from './Resolver.js' /** * Checks that a string (or other input) is a valid AssetDID uri. @@ -63,5 +20,5 @@ export function validateUri(input: unknown): void { throw new TypeError(`Asset DID string expected, got ${typeof input}`) } - parse(input as AssetDidUri) + resolve(input as AssetDidUri) } diff --git a/packages/asset-did/src/Resolver.ts b/packages/asset-did/src/Resolver.ts new file mode 100644 index 000000000..3c8fe93eb --- /dev/null +++ b/packages/asset-did/src/Resolver.ts @@ -0,0 +1,108 @@ +/** + * Copyright (c) 2018-2023, BOTLabs GmbH. + * + * This source code is licensed under the BSD 4-Clause "Original" license + * found in the LICENSE file in the root directory of this source tree. + */ + +import type { + AssetDidDocument, + AssetDidResolutionDocumentMetadata, + AssetDidResolutionMetadata, + AssetDidUri, + Caip19AssetId, + Caip19AssetInstance, + Caip19AssetNamespace, + Caip19AssetReference, + Caip2ChainId, + Caip2ChainNamespace, + Caip2ChainReference, + ConformingAssetDidResolutionResult, +} from '@kiltprotocol/types' +import { SDKErrors } from '@kiltprotocol/utils' + +export type IAssetDidParsingResult = { + uri: AssetDidUri + chainId: Caip2ChainId + chainNamespace: Caip2ChainNamespace + chainReference: Caip2ChainReference + assetId: Caip19AssetId + assetNamespace: Caip19AssetNamespace + assetReference: Caip19AssetReference + assetInstance?: Caip19AssetInstance +} + +// Matches AssetDIDs as per the [AssetDID specification](https://github.com/KILTprotocol/spec-asset-did). +const ASSET_DID_REGEX = + /^did:asset:(?(?[-a-z0-9]{3,8}):(?[-a-zA-Z0-9]{1,32}))\.(?(?[-a-z0-9]{3,8}):(?[-a-zA-Z0-9]{1,64})(:(?[-a-zA-Z0-9]{1,78}))?)$/ + +/** + * Parses an AssetDID URI and returns the information contained within in a structured form. + + * @param assetDidUri An AssetDID uri as a string. +* @returns Object containing information extracted from the AssetDID uri. + */ +export function resolve(assetDidUri: AssetDidUri): IAssetDidParsingResult { + const matches = ASSET_DID_REGEX.exec(assetDidUri)?.groups + if (!matches) { + throw new SDKErrors.InvalidDidFormatError(assetDidUri) + } + + const { chainId, assetId } = matches as Omit + + return { + ...(matches as Omit), + uri: `did:asset:${chainId}.${assetId}`, + } +} + +/** + * Implementation of `resolve` compliant with AssetDID specification (https://github.com/KILTprotocol/spec-asset-did). + * As opposed to `resolve`, which takes a more pragmatic approach, the returned object is fully compliant with the AssetDID specification. + * + * @param assetDidUri The DID to resolve. + * @returns An object with the properties `didDocument` (a spec-conforming DID document or `undefined`), `didDocumentMetadata` (equivalent to `metadata` returned by [[resolve]]), as well as `didResolutionMetadata` (indicating an `error` if any). + */ +export function resolveCompliant( + assetDidUri: AssetDidUri +): ConformingAssetDidResolutionResult { + // No canonicalId support as of now + const didDocumentMetadata: AssetDidResolutionDocumentMetadata = {} + const didResolutionMetadata: AssetDidResolutionMetadata = {} + let didDocument: AssetDidDocument | undefined + + try { + const { + uri, + chainNamespace, + chainReference, + assetNamespace, + assetReference, + assetInstance, + } = resolve(assetDidUri) + + didDocument = { + id: uri, + chain: { + namespace: chainNamespace, + reference: chainReference, + }, + asset: { + namespace: assetNamespace, + reference: assetReference, + identifier: assetInstance, + }, + }, + } catch (e) { + didResolutionMetadata.error = 'invalidDid' + if (e instanceof Error) { + didResolutionMetadata.errorMessage = e.message + } + } + + return { + didDocumentMetadata, + didResolutionMetadata, + didDocument + } +} diff --git a/packages/asset-did/src/index.ts b/packages/asset-did/src/index.ts index 9b1404adc..6d15400c0 100644 --- a/packages/asset-did/src/index.ts +++ b/packages/asset-did/src/index.ts @@ -6,3 +6,4 @@ */ export * from './AssetDid.js' +export * from './Resolver.js' diff --git a/packages/types/src/AssetDid.ts b/packages/types/src/AssetDid.ts index ecb9a52ef..18b5fa213 100644 --- a/packages/types/src/AssetDid.ts +++ b/packages/types/src/AssetDid.ts @@ -41,3 +41,32 @@ export type Caip19AssetId = * A string containing an AssetDID as per the [AssetDID specification](https://github.com/KILTprotocol/spec-asset-did). */ export type AssetDidUri = `did:asset:${Caip2ChainId}.${Caip19AssetId}` + +export interface AssetDidDocument { + id: AssetDidUri + + chain: { + namespace: Caip2ChainNamespace + reference: Caip2ChainReference + } + asset: { + namespace: Caip19AssetNamespace + reference: Caip19AssetReference + identifier?: Caip19AssetInstance + } +} + +export interface AssetDidResolutionMetadata { + error?: 'invalidDid' + errorMessage?: string +} + +export interface AssetDidResolutionDocumentMetadata { + canonicalId?: AssetDidUri +} + +export interface ConformingAssetDidResolutionResult { + didDocumentMetadata: AssetDidResolutionDocumentMetadata + didResolutionMetadata: AssetDidResolutionMetadata + didDocument?: AssetDidDocument +} diff --git a/packages/types/src/Attestation.ts b/packages/types/src/Attestation.ts index 0f42f8974..15be11a3e 100644 --- a/packages/types/src/Attestation.ts +++ b/packages/types/src/Attestation.ts @@ -5,7 +5,7 @@ * found in the LICENSE file in the root directory of this source tree. */ -import type { DidUri } from './DidDocument' +import type { DidUri } from './KiltDidDocument' import type { IDelegationNode } from './Delegation' import type { ICredential } from './Credential' import type { CTypeHash } from './CType' diff --git a/packages/types/src/Claim.ts b/packages/types/src/Claim.ts index 42f448fdb..a2b0c3f6f 100644 --- a/packages/types/src/Claim.ts +++ b/packages/types/src/Claim.ts @@ -6,7 +6,7 @@ */ import type { CTypeHash } from './CType' -import type { DidUri } from './DidDocument' +import type { DidUri } from './KiltDidDocument' export type IClaimContents = Record< string, diff --git a/packages/types/src/Credential.ts b/packages/types/src/Credential.ts index e0f7e657c..5d26e4b52 100644 --- a/packages/types/src/Credential.ts +++ b/packages/types/src/Credential.ts @@ -6,7 +6,7 @@ */ import type { HexString } from '@polkadot/util/types' -import type { DidSignature } from './DidDocument' +import type { DidSignature } from './KiltDidDocument' import type { IClaim } from './Claim' import type { IDelegationNode } from './Delegation' diff --git a/packages/types/src/CryptoCallbacks.ts b/packages/types/src/CryptoCallbacks.ts index 3bb8225b1..b437cc54e 100644 --- a/packages/types/src/CryptoCallbacks.ts +++ b/packages/types/src/CryptoCallbacks.ts @@ -10,7 +10,7 @@ import type { DidUri, DidVerificationKey, VerificationKeyRelationship, -} from './DidDocument.js' +} from './KiltDidDocument.js' /** * Base interface for all signing requests. diff --git a/packages/types/src/Delegation.ts b/packages/types/src/Delegation.ts index 7049e3361..949576c6f 100644 --- a/packages/types/src/Delegation.ts +++ b/packages/types/src/Delegation.ts @@ -6,7 +6,7 @@ */ import type { CTypeHash } from './CType' -import type { DidUri } from './DidDocument' +import type { DidUri } from './KiltDidDocument' /* eslint-disable no-bitwise */ export const Permission = { diff --git a/packages/types/src/DidDocument.ts b/packages/types/src/KiltDidDocument.ts similarity index 100% rename from packages/types/src/DidDocument.ts rename to packages/types/src/KiltDidDocument.ts diff --git a/packages/types/src/DidDocumentExporter.ts b/packages/types/src/KiltDidDocumentExporter.ts similarity index 97% rename from packages/types/src/DidDocumentExporter.ts rename to packages/types/src/KiltDidDocumentExporter.ts index 95efb1d43..ece9a376b 100644 --- a/packages/types/src/DidDocumentExporter.ts +++ b/packages/types/src/KiltDidDocumentExporter.ts @@ -13,8 +13,8 @@ import { DidVerificationKey, EncryptionKeyType, VerificationKeyType, -} from './DidDocument.js' -import { DidResolutionDocumentMetadata } from './DidResolver.js' +} from './KiltDidDocument.js' +import { DidResolutionDocumentMetadata } from './KiltDidResolver.js' export type ConformingDidDocumentKeyType = | 'Ed25519VerificationKey2018' diff --git a/packages/types/src/DidResolver.ts b/packages/types/src/KiltDidResolver.ts similarity index 97% rename from packages/types/src/DidResolver.ts rename to packages/types/src/KiltDidResolver.ts index fd74dc237..da96917ce 100644 --- a/packages/types/src/DidResolver.ts +++ b/packages/types/src/KiltDidResolver.ts @@ -8,14 +8,14 @@ import { ConformingDidKey, ConformingDidServiceEndpoint, -} from './DidDocumentExporter.js' +} from './KiltDidDocumentExporter.js' import type { DidDocument, DidKey, DidResourceUri, DidUri, KeyRelationship, -} from './DidDocument.js' +} from './KiltDidDocument.js' /** * DID resolution metadata that includes a subset of the properties defined in the [W3C proposed standard](https://www.w3.org/TR/did-core/#did-resolution). diff --git a/packages/types/src/Message.ts b/packages/types/src/Message.ts index ba2445d1f..a5fbc7c20 100644 --- a/packages/types/src/Message.ts +++ b/packages/types/src/Message.ts @@ -7,7 +7,7 @@ import type { AnyJson } from '@polkadot/types/types/codec' -import type { DidResourceUri, DidSignature, DidUri } from './DidDocument.js' +import type { DidResourceUri, DidSignature, DidUri } from './KiltDidDocument.js' import type { IAttestation } from './Attestation.js' import type { PartialClaim } from './Claim.js' import type { IDelegationNode } from './Delegation.js' diff --git a/packages/types/src/PublicCredential.ts b/packages/types/src/PublicCredential.ts index 25451038b..08136d086 100644 --- a/packages/types/src/PublicCredential.ts +++ b/packages/types/src/PublicCredential.ts @@ -11,7 +11,7 @@ import type { BN } from '@polkadot/util' import type { CTypeHash } from './CType' import type { IDelegationNode } from './Delegation' import type { IClaimContents } from './Claim' -import type { DidUri } from './DidDocument' +import type { DidUri } from './KiltDidDocument' import type { AssetDidUri } from './AssetDid' /* diff --git a/packages/types/src/Quote.ts b/packages/types/src/Quote.ts index 2a3f0ed6d..ea6000713 100644 --- a/packages/types/src/Quote.ts +++ b/packages/types/src/Quote.ts @@ -5,7 +5,7 @@ * found in the LICENSE file in the root directory of this source tree. */ -import type { DidSignature, DidUri } from './DidDocument' +import type { DidSignature, DidUri } from './KiltDidDocument' import type { ICredential } from './Credential' import type { CTypeHash } from './CType' diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 81efc6583..85d274f67 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -28,8 +28,8 @@ export * from './Message.js' export * from './Quote.js' export * from './Credential.js' export * from './Terms.js' -export * from './DidDocument.js' +export * from './KiltDidDocument.js' export * from './CryptoCallbacks.js' -export * from './DidResolver.js' -export * from './DidDocumentExporter.js' +export * from './KiltDidResolver.js' +export * from './KiltDidDocumentExporter.js' export * from './PublicCredential.js' From 482ba97f4f064ab794b17bef8165685f7665a226 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Tue, 7 Feb 2023 16:32:46 +0100 Subject: [PATCH 2/5] First round of features complete. Tests missing --- packages/asset-did/package.json | 1 + packages/asset-did/src/DidDocumentExporter.ts | 38 +++++++++++++++++++ packages/asset-did/src/Resolver.ts | 12 +++--- packages/asset-did/src/index.ts | 1 + .../src/DidDocumentExporter/DidContexts.ts | 8 ++++ yarn.lock | 1 + 6 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 packages/asset-did/src/DidDocumentExporter.ts diff --git a/packages/asset-did/package.json b/packages/asset-did/package.json index 7bb054b12..153bdc460 100644 --- a/packages/asset-did/package.json +++ b/packages/asset-did/package.json @@ -30,6 +30,7 @@ "bugs": "https://github.com/KILTprotocol/sdk-js/issues", "homepage": "https://github.com/KILTprotocol/sdk-js#readme", "dependencies": { + "@kiltprotocol/did": "workspace:*", "@kiltprotocol/types": "workspace:*", "@kiltprotocol/utils": "workspace:*" } diff --git a/packages/asset-did/src/DidDocumentExporter.ts b/packages/asset-did/src/DidDocumentExporter.ts new file mode 100644 index 000000000..3733bc308 --- /dev/null +++ b/packages/asset-did/src/DidDocumentExporter.ts @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2018-2023, BOTLabs GmbH. + * + * This source code is licensed under the BSD 4-Clause "Original" license + * found in the LICENSE file in the root directory of this source tree. + */ + +import type { AssetDidDocument } from '@kiltprotocol/types' + +import { ASSET_DID_CONTEXT_URL, W3C_DID_CONTEXT_URL } from '@kiltprotocol/did' +import { SDKErrors } from '@kiltprotocol/utils' + +import { resolveCompliant, ResolvedAssetDid } from './Resolver.js' + +/** + * Export a [[ResolvedAssetDid]] to a W3C-spec conforming DID Document in the format provided. + * + * @param did The [[ResolvedAssetDid]]. + * @param mimeType The format for the output DID Document. Accepted values are `application/json` and `application/ld+json`. + * @returns The DID Document formatted according to the mime type provided, or an error if the format specified is not supported. + */ +export function exportToDidDocument( + did: ResolvedAssetDid, + mimeType: 'application/json' | 'application/ld+json' +): AssetDidDocument { + const { didDocument, didResolutionMetadata } = resolveCompliant(did.uri) + // Error cases should never happen if the `ResolvedAssetDid` is created via the `resolve` function. + if (didResolutionMetadata.errorMessage) { + throw new Error(didResolutionMetadata.errorMessage) + } else if (!didDocument) { + throw new SDKErrors.InvalidDidFormatError(did.uri) + } + if (mimeType === 'application/ld+json') { + didDocument['@context'] = [W3C_DID_CONTEXT_URL, ASSET_DID_CONTEXT_URL] + } + + return didDocument +} diff --git a/packages/asset-did/src/Resolver.ts b/packages/asset-did/src/Resolver.ts index 3c8fe93eb..0d37b2aea 100644 --- a/packages/asset-did/src/Resolver.ts +++ b/packages/asset-did/src/Resolver.ts @@ -21,7 +21,7 @@ import type { } from '@kiltprotocol/types' import { SDKErrors } from '@kiltprotocol/utils' -export type IAssetDidParsingResult = { +export type ResolvedAssetDid = { uri: AssetDidUri chainId: Caip2ChainId chainNamespace: Caip2ChainNamespace @@ -42,16 +42,16 @@ const ASSET_DID_REGEX = * @param assetDidUri An AssetDID uri as a string. * @returns Object containing information extracted from the AssetDID uri. */ -export function resolve(assetDidUri: AssetDidUri): IAssetDidParsingResult { +export function resolve(assetDidUri: AssetDidUri): ResolvedAssetDid { const matches = ASSET_DID_REGEX.exec(assetDidUri)?.groups if (!matches) { throw new SDKErrors.InvalidDidFormatError(assetDidUri) } - const { chainId, assetId } = matches as Omit + const { chainId, assetId } = matches as Omit return { - ...(matches as Omit), + ...(matches as Omit), uri: `did:asset:${chainId}.${assetId}`, } } @@ -92,7 +92,7 @@ export function resolveCompliant( reference: assetReference, identifier: assetInstance, }, - }, + } } catch (e) { didResolutionMetadata.error = 'invalidDid' if (e instanceof Error) { @@ -103,6 +103,6 @@ export function resolveCompliant( return { didDocumentMetadata, didResolutionMetadata, - didDocument + didDocument, } } diff --git a/packages/asset-did/src/index.ts b/packages/asset-did/src/index.ts index 6d15400c0..052990309 100644 --- a/packages/asset-did/src/index.ts +++ b/packages/asset-did/src/index.ts @@ -6,4 +6,5 @@ */ export * from './AssetDid.js' +export * from './DidDocumentExporter.js' export * from './Resolver.js' diff --git a/packages/did/src/DidDocumentExporter/DidContexts.ts b/packages/did/src/DidDocumentExporter/DidContexts.ts index dd3d86ce1..ce95b533f 100644 --- a/packages/did/src/DidDocumentExporter/DidContexts.ts +++ b/packages/did/src/DidDocumentExporter/DidContexts.ts @@ -11,6 +11,14 @@ */ export const KILT_DID_CONTEXT_URL = 'ipfs://QmU7QkuTCPz7NmD5bD7Z7mQVz2UsSPaEK58B5sYnjnPRNW' + +/** + * IPFS URL identifying a JSON-LD context file describing terms used in DID documents of the AssetDID method that are not defined in the W3C DID core context. + * Should be the second entry in the ordered set of contexts after [[W3C_DID_CONTEXT_URL]] in the JSON-LD representation of an AssetDID document. + */ +export const ASSET_DID_CONTEXT_URL = + 'ipfs://QmUAcsTVNfjGoZ3dcuHKikFJZpRiUkXCpbWcfxb1j5qnv4' + /** * URL identifying the JSON-LD context file that is part of the W3C DID core specifications describing the terms defined by the core data model. * Must be the first entry in the ordered set of contexts in a JSON-LD representation of a DID document. diff --git a/yarn.lock b/yarn.lock index ec200b426..d23b1cb9e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1837,6 +1837,7 @@ __metadata: version: 0.0.0-use.local resolution: "@kiltprotocol/asset-did@workspace:packages/asset-did" dependencies: + "@kiltprotocol/did": "workspace:*" "@kiltprotocol/types": "workspace:*" "@kiltprotocol/utils": "workspace:*" languageName: unknown From c9452c8f9a61cec3b900d878ee278f9e527f5f6c Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Fri, 10 Feb 2023 10:32:58 +0100 Subject: [PATCH 3/5] Add unit tests --- .../asset-did/src/DidDocumentExporter.spec.ts | 60 +++++++ packages/asset-did/src/DidDocumentExporter.ts | 28 +++- packages/asset-did/src/Resolver.spec.ts | 146 ++++++++++++++++++ packages/asset-did/src/Resolver.ts | 2 +- packages/types/src/AssetDid.ts | 1 + 5 files changed, 228 insertions(+), 9 deletions(-) create mode 100644 packages/asset-did/src/DidDocumentExporter.spec.ts create mode 100644 packages/asset-did/src/Resolver.spec.ts diff --git a/packages/asset-did/src/DidDocumentExporter.spec.ts b/packages/asset-did/src/DidDocumentExporter.spec.ts new file mode 100644 index 000000000..246bd6d96 --- /dev/null +++ b/packages/asset-did/src/DidDocumentExporter.spec.ts @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2018-2023, BOTLabs GmbH. + * + * This source code is licensed under the BSD 4-Clause "Original" license + * found in the LICENSE file in the root directory of this source tree. + */ + +import type { AssetDidDocument } from '@kiltprotocol/types' + +import { exportToDidDocument } from './DidDocumentExporter' +import { resolve } from './Resolver' + +/** + * @group unit/did + */ + +const assetDid = + 'did:asset:eip155:1.erc20:0x71C7656EC7ab88b098defB751B7401B5f6d8976F:123' + +describe('DidDocumentExporter.exportToDidDocument', () => { + it('should correctly export a JSON DID document', async () => { + const resolution = resolve(assetDid) + expect( + exportToDidDocument(resolution, 'application/json') + ).toMatchObject({ + id: assetDid, + chain: { + namespace: 'eip155', + reference: '1', + }, + asset: { + namespace: 'erc20', + reference: '0x71C7656EC7ab88b098defB751B7401B5f6d8976F', + identifier: '123', + }, + }) + }) + + it('should correctly export a JSON-LD DID document', async () => { + const resolution = resolve(assetDid) + expect( + exportToDidDocument(resolution, 'application/ld+json') + ).toMatchObject({ + '@context': [ + 'https://www.w3.org/ns/did/v1', + 'ipfs://QmUAcsTVNfjGoZ3dcuHKikFJZpRiUkXCpbWcfxb1j5qnv4', + ], + id: assetDid, + chain: { + namespace: 'eip155', + reference: '1', + }, + asset: { + namespace: 'erc20', + reference: '0x71C7656EC7ab88b098defB751B7401B5f6d8976F', + identifier: '123', + }, + }) + }) +}) diff --git a/packages/asset-did/src/DidDocumentExporter.ts b/packages/asset-did/src/DidDocumentExporter.ts index 3733bc308..746288532 100644 --- a/packages/asset-did/src/DidDocumentExporter.ts +++ b/packages/asset-did/src/DidDocumentExporter.ts @@ -8,9 +8,8 @@ import type { AssetDidDocument } from '@kiltprotocol/types' import { ASSET_DID_CONTEXT_URL, W3C_DID_CONTEXT_URL } from '@kiltprotocol/did' -import { SDKErrors } from '@kiltprotocol/utils' -import { resolveCompliant, ResolvedAssetDid } from './Resolver.js' +import { ResolvedAssetDid } from './Resolver.js' /** * Export a [[ResolvedAssetDid]] to a W3C-spec conforming DID Document in the format provided. @@ -23,12 +22,25 @@ export function exportToDidDocument( did: ResolvedAssetDid, mimeType: 'application/json' | 'application/ld+json' ): AssetDidDocument { - const { didDocument, didResolutionMetadata } = resolveCompliant(did.uri) - // Error cases should never happen if the `ResolvedAssetDid` is created via the `resolve` function. - if (didResolutionMetadata.errorMessage) { - throw new Error(didResolutionMetadata.errorMessage) - } else if (!didDocument) { - throw new SDKErrors.InvalidDidFormatError(did.uri) + const { + uri, + chainNamespace, + chainReference, + assetNamespace, + assetReference, + assetInstance, + } = did + const didDocument: AssetDidDocument = { + id: uri, + chain: { + namespace: chainNamespace, + reference: chainReference, + }, + asset: { + namespace: assetNamespace, + reference: assetReference, + identifier: assetInstance, + }, } if (mimeType === 'application/ld+json') { didDocument['@context'] = [W3C_DID_CONTEXT_URL, ASSET_DID_CONTEXT_URL] diff --git a/packages/asset-did/src/Resolver.spec.ts b/packages/asset-did/src/Resolver.spec.ts new file mode 100644 index 000000000..3b3f62ac6 --- /dev/null +++ b/packages/asset-did/src/Resolver.spec.ts @@ -0,0 +1,146 @@ +/** + * Copyright (c) 2018-2023, BOTLabs GmbH. + * + * This source code is licensed under the BSD 4-Clause "Original" license + * found in the LICENSE file in the root directory of this source tree. + */ + +/** + * @group unit/did + */ + +import type { + AssetDidUri, + ConformingAssetDidResolutionResult, +} from '@kiltprotocol/types' +import { SDKErrors } from '@kiltprotocol/utils' + +import type { ResolvedAssetDid } from './Resolver' +import { resolve, resolveCompliant } from './Resolver' + +describe('Resolver.resolve', () => { + it('should correctly resolve a valid AssetDID without an asset identifier', async () => { + const assetDid = + 'did:asset:eip155:1.erc20:0x71C7656EC7ab88b098defB751B7401B5f6d8976F' + expect(resolve(assetDid)).toMatchObject({ + uri: 'did:asset:eip155:1.erc20:0x71C7656EC7ab88b098defB751B7401B5f6d8976F', + chainId: 'eip155:1', + chainNamespace: 'eip155', + chainReference: '1', + assetId: 'erc20:0x71C7656EC7ab88b098defB751B7401B5f6d8976F', + assetNamespace: 'erc20', + assetReference: '0x71C7656EC7ab88b098defB751B7401B5f6d8976F', + assetInstance: undefined, + }) + }) + it('should correctly resolve a valid AssetDID with an asset identifier', async () => { + const assetDid = + 'did:asset:eip155:1.erc20:0x71C7656EC7ab88b098defB751B7401B5f6d8976F:123' + expect(resolve(assetDid)).toMatchObject({ + uri: 'did:asset:eip155:1.erc20:0x71C7656EC7ab88b098defB751B7401B5f6d8976F:123', + chainId: 'eip155:1', + chainNamespace: 'eip155', + chainReference: '1', + assetId: 'erc20:0x71C7656EC7ab88b098defB751B7401B5f6d8976F:123', + assetNamespace: 'erc20', + assetReference: '0x71C7656EC7ab88b098defB751B7401B5f6d8976F', + assetInstance: '123', + }) + }) + it('should fail to resolve invalid AssetDIDs', async () => { + const assetDids: string[] = [ + 'did', + 'did:', + 'did:asset', + 'did:asset:', + 'did:asset:eip155', + 'did:asset:eip155:', + 'did:asset:eip155:1', + 'did:asset:eip155:1.', + 'did:asset:eip155:1.erc20', + 'did:asset:eip155:1.erc20:', + 'did:asset:eip155:1.erc20:0x71C7656EC7ab88b098defB751B7401B5f6d8976F:', + 'did:asset:eip155:1.erc20:0x71C7656EC7ab88b098defB751B7401B5f6d8976F:123:', + ] + assetDids.forEach((assetDid) => + expect(() => resolve(assetDid as AssetDidUri)).toThrowError( + new SDKErrors.InvalidDidFormatError(assetDid) + ) + ) + }) +}) + +describe('Resolver.resolveCompliant', () => { + it('should correctly resolve a valid AssetDID without an asset identifier', async () => { + const assetDid = + 'did:asset:eip155:1.erc20:0x71C7656EC7ab88b098defB751B7401B5f6d8976F' + expect( + resolveCompliant(assetDid) + ).toMatchObject({ + didDocument: { + id: 'did:asset:eip155:1.erc20:0x71C7656EC7ab88b098defB751B7401B5f6d8976F', + chain: { + namespace: 'eip155', + reference: '1', + }, + asset: { + namespace: 'erc20', + reference: '0x71C7656EC7ab88b098defB751B7401B5f6d8976F', + identifier: undefined, + }, + }, + didDocumentMetadata: {}, + didResolutionMetadata: {}, + }) + }) + it('should correctly resolve a valid AssetDID with an asset identifier', async () => { + const assetDid = + 'did:asset:eip155:1.erc20:0x71C7656EC7ab88b098defB751B7401B5f6d8976F:123' + expect( + resolveCompliant(assetDid) + ).toMatchObject({ + didDocument: { + id: 'did:asset:eip155:1.erc20:0x71C7656EC7ab88b098defB751B7401B5f6d8976F:123', + chain: { + namespace: 'eip155', + reference: '1', + }, + asset: { + namespace: 'erc20', + reference: '0x71C7656EC7ab88b098defB751B7401B5f6d8976F', + identifier: '123', + }, + }, + didDocumentMetadata: {}, + didResolutionMetadata: {}, + }) + }) + it('should fail to resolve invalid AssetDIDs', async () => { + const assetDids: string[] = [ + 'did', + 'did:', + 'did:asset', + 'did:asset:', + 'did:asset:eip155', + 'did:asset:eip155:', + 'did:asset:eip155:1', + 'did:asset:eip155:1.', + 'did:asset:eip155:1.erc20', + 'did:asset:eip155:1.erc20:', + 'did:asset:eip155:1.erc20:0x71C7656EC7ab88b098defB751B7401B5f6d8976F:', + 'did:asset:eip155:1.erc20:0x71C7656EC7ab88b098defB751B7401B5f6d8976F:123:', + ] + assetDids.forEach((assetDid) => + expect( + resolveCompliant(assetDid as AssetDidUri) + ).toMatchObject({ + didDocument: undefined, + didDocumentMetadata: {}, + didResolutionMetadata: { + error: 'invalidDid', + errorMessage: new SDKErrors.InvalidDidFormatError(assetDid).message, + }, + }) + ) + }) +}) diff --git a/packages/asset-did/src/Resolver.ts b/packages/asset-did/src/Resolver.ts index 0d37b2aea..2a0cd4436 100644 --- a/packages/asset-did/src/Resolver.ts +++ b/packages/asset-did/src/Resolver.ts @@ -40,7 +40,7 @@ const ASSET_DID_REGEX = * Parses an AssetDID URI and returns the information contained within in a structured form. * @param assetDidUri An AssetDID uri as a string. -* @returns Object containing information extracted from the AssetDID uri. + * @returns Object containing information extracted from the AssetDID uri. */ export function resolve(assetDidUri: AssetDidUri): ResolvedAssetDid { const matches = ASSET_DID_REGEX.exec(assetDidUri)?.groups diff --git a/packages/types/src/AssetDid.ts b/packages/types/src/AssetDid.ts index 18b5fa213..6a206ca4e 100644 --- a/packages/types/src/AssetDid.ts +++ b/packages/types/src/AssetDid.ts @@ -43,6 +43,7 @@ export type Caip19AssetId = export type AssetDidUri = `did:asset:${Caip2ChainId}.${Caip19AssetId}` export interface AssetDidDocument { + '@context'?: string[] id: AssetDidUri chain: { From fb887b46bb2233f8e6e6527840d0d86e9783588f Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Fri, 10 Feb 2023 10:45:32 +0100 Subject: [PATCH 4/5] Fix unit test modules for AssetDIDs --- packages/asset-did/src/DidDocumentExporter.spec.ts | 2 +- packages/asset-did/src/Resolver.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/asset-did/src/DidDocumentExporter.spec.ts b/packages/asset-did/src/DidDocumentExporter.spec.ts index 246bd6d96..a1bd16afd 100644 --- a/packages/asset-did/src/DidDocumentExporter.spec.ts +++ b/packages/asset-did/src/DidDocumentExporter.spec.ts @@ -11,7 +11,7 @@ import { exportToDidDocument } from './DidDocumentExporter' import { resolve } from './Resolver' /** - * @group unit/did + * @group unit/assetdid */ const assetDid = diff --git a/packages/asset-did/src/Resolver.spec.ts b/packages/asset-did/src/Resolver.spec.ts index 3b3f62ac6..be6f3810c 100644 --- a/packages/asset-did/src/Resolver.spec.ts +++ b/packages/asset-did/src/Resolver.spec.ts @@ -6,7 +6,7 @@ */ /** - * @group unit/did + * @group unit/assetdid */ import type { From a3537e16b9f57679c961aa1b356d81c46ca3dbba Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Mon, 13 Feb 2023 14:37:35 +0100 Subject: [PATCH 5/5] Add entire context as static file --- .../did/src/DidDocumentExporter/DidContexts.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/did/src/DidDocumentExporter/DidContexts.ts b/packages/did/src/DidDocumentExporter/DidContexts.ts index 4316c49fb..f5b3272af 100644 --- a/packages/did/src/DidDocumentExporter/DidContexts.ts +++ b/packages/did/src/DidDocumentExporter/DidContexts.ts @@ -48,6 +48,22 @@ export const DID_CONTEXTS = { }, ], }, + [ASSET_DID_CONTEXT_URL]: { + '@context': { + '@protected': true, + id: '@id', + chain: + 'https://github.com/KILTprotocol/spec-asset-did/blob/43457f9d1119bff1c3152e368ddd4e3bdb9558cb/README.md', + namespace: + 'https://github.com/KILTprotocol/spec-asset-did/blob/43457f9d1119bff1c3152e368ddd4e3bdb9558cb/README.md', + reference: + 'https://github.com/KILTprotocol/spec-asset-did/blob/43457f9d1119bff1c3152e368ddd4e3bdb9558cb/README.md', + asset: + 'https://github.com/KILTprotocol/spec-asset-did/blob/43457f9d1119bff1c3152e368ddd4e3bdb9558cb/README.md', + identifier: + 'https://github.com/KILTprotocol/spec-asset-did/blob/43457f9d1119bff1c3152e368ddd4e3bdb9558cb/README.md', + }, + }, [W3C_DID_CONTEXT_URL]: { '@context': { '@protected': true,