diff --git a/examples/CHANGELOG.md b/examples/CHANGELOG.md index 9a813319b..ba66dc420 100644 --- a/examples/CHANGELOG.md +++ b/examples/CHANGELOG.md @@ -1,5 +1,21 @@ # @docknetwork/sdk-examples +## 0.2.0 + +### Minor Changes + +- - Updated `DIDDocument`: modified the rules for storing keys in the verificationMethod and added the required validations. + - Developed generic tests for modules to ensure they cover the same use cases, regardless of the specific implementation. + - Implemented the `CheqdBlobModule`. + - Misc tweaks. + +### Patch Changes + +- Updated dependencies + - @docknetwork/dock-blockchain-modules@0.3.0 + - @docknetwork/dock-blockchain-api@0.2.0 + - @docknetwork/credential-sdk@0.3.0 + ## 0.1.2 ### Patch Changes diff --git a/examples/package.json b/examples/package.json index ae3ad5875..3e2bf0532 100644 --- a/examples/package.json +++ b/examples/package.json @@ -2,7 +2,7 @@ "name": "@docknetwork/sdk-examples", "private": true, "type": "module", - "version": "0.1.2", + "version": "0.2.0", "scripts": { "bbs-dock-example": "babel-node --loader=./loader.mjs ./bbs-dock.js", "claim-deduction-example": "babel-node --loader=./loader.mjs ./claim-deduction.js", @@ -19,9 +19,9 @@ "lint": "eslint \"*.js\"" }, "dependencies": { - "@docknetwork/credential-sdk": "0.2.0", - "@docknetwork/dock-blockchain-api": "0.1.2", - "@docknetwork/dock-blockchain-modules": "0.2.0" + "@docknetwork/credential-sdk": "0.3.0", + "@docknetwork/dock-blockchain-api": "0.2.0", + "@docknetwork/dock-blockchain-modules": "0.3.0" }, "devDependencies": { "babel-eslint": "^10.1.0", diff --git a/examples/schema.js b/examples/schema.js index d46f2f990..cc540ab39 100644 --- a/examples/schema.js +++ b/examples/schema.js @@ -6,6 +6,7 @@ import { DIDDocument, DidKey, VerificationRelationship, + DockBlobId, } from '@docknetwork/credential-sdk/types'; import { Schema } from '@docknetwork/credential-sdk/modules'; import { DockCoreModules } from '@docknetwork/dock-blockchain-modules'; @@ -70,7 +71,7 @@ async function main() { await createDID(subjectDID, subjectPair); console.log('Creating a new schema...'); - const schema = new Schema(); + const schema = new Schema(DockBlobId.random()); await schema.setJSONSchema({ $schema: 'http://json-schema.org/draft-07/schema#', description: 'Dock Schema Example', diff --git a/packages/cheqd-blockchain-api/CHANGELOG.md b/packages/cheqd-blockchain-api/CHANGELOG.md index c9c7cf4ef..f6b2d0f81 100644 --- a/packages/cheqd-blockchain-api/CHANGELOG.md +++ b/packages/cheqd-blockchain-api/CHANGELOG.md @@ -1,5 +1,14 @@ # @docknetwork/cheqd-blockchain-api +## 0.2.0 + +### Minor Changes + +- - Updated `DIDDocument`: modified the rules for storing keys in the verificationMethod and added the required validations. + - Developed generic tests for modules to ensure they cover the same use cases, regardless of the specific implementation. + - Implemented the `CheqdBlobModule`. + - Misc tweaks. + ## 0.1.2 ### Patch Changes diff --git a/packages/cheqd-blockchain-api/package.json b/packages/cheqd-blockchain-api/package.json index a5daf9d7e..ba6a10d20 100644 --- a/packages/cheqd-blockchain-api/package.json +++ b/packages/cheqd-blockchain-api/package.json @@ -1,6 +1,6 @@ { "name": "@docknetwork/cheqd-blockchain-api", - "version": "0.1.2", + "version": "0.2.0", "license": "MIT", "main": "./dist/esm/index.js", "type": "module", diff --git a/packages/cheqd-blockchain-api/src/index.js b/packages/cheqd-blockchain-api/src/index.js index 0ff3bebc5..7bc6a744c 100644 --- a/packages/cheqd-blockchain-api/src/index.js +++ b/packages/cheqd-blockchain-api/src/index.js @@ -145,7 +145,9 @@ export class CheqdAPI extends ApiProvider { if (res.code) { console.error(res); - throw new Error(`Error: ${JSON.stringify(res)}`); + throw new Error( + JSON.stringify(res, (_, value) => (typeof value === 'bigint' ? `${value.toString()}n` : value)), + ); } return res; diff --git a/packages/cheqd-blockchain-modules/CHANGELOG.md b/packages/cheqd-blockchain-modules/CHANGELOG.md index 354e05df7..1b5a60d2d 100644 --- a/packages/cheqd-blockchain-modules/CHANGELOG.md +++ b/packages/cheqd-blockchain-modules/CHANGELOG.md @@ -1,5 +1,19 @@ # @docknetwork/cheqd-blockchain-modules +## 0.3.0 + +### Minor Changes + +- - Updated `DIDDocument`: modified the rules for storing keys in the verificationMethod and added the required validations. + - Developed generic tests for modules to ensure they cover the same use cases, regardless of the specific implementation. + - Implemented the `CheqdBlobModule`. + - Misc tweaks. + +### Patch Changes + +- Updated dependencies + - @docknetwork/credential-sdk@0.3.0 + ## 0.2.0 ### Minor Changes diff --git a/packages/cheqd-blockchain-modules/package.json b/packages/cheqd-blockchain-modules/package.json index 5e3ceb6ea..7e9d65c1e 100644 --- a/packages/cheqd-blockchain-modules/package.json +++ b/packages/cheqd-blockchain-modules/package.json @@ -1,6 +1,6 @@ { "name": "@docknetwork/cheqd-blockchain-modules", - "version": "0.2.0", + "version": "0.3.0", "type": "module", "license": "MIT", "main": "./dist/esm/index.js", @@ -28,7 +28,7 @@ "node": ">=18.0.0" }, "dependencies": { - "@docknetwork/credential-sdk": "0.2.0" + "@docknetwork/credential-sdk": "0.3.0" }, "devDependencies": { "@babel/cli": "^7.24.1", @@ -37,7 +37,7 @@ "@babel/plugin-syntax-import-attributes": "^7.25.6", "@babel/plugin-transform-modules-commonjs": "^7.24.1", "@babel/preset-env": "^7.24.3", - "@docknetwork/cheqd-blockchain-api": "0.1.2", + "@docknetwork/cheqd-blockchain-api": "0.2.0", "@rollup/plugin-alias": "^4.0.2", "@rollup/plugin-babel": "^6.0.4", "@rollup/plugin-commonjs": "^24.0.0", diff --git a/packages/cheqd-blockchain-modules/src/attest/internal.js b/packages/cheqd-blockchain-modules/src/attest/internal.js index 59a63fa2e..eb7e93603 100644 --- a/packages/cheqd-blockchain-modules/src/attest/internal.js +++ b/packages/cheqd-blockchain-modules/src/attest/internal.js @@ -4,7 +4,7 @@ import { CheqdCreateResource, createInternalCheqdModule } from '../common'; const methods = { setClaim: (iri, targetDid) => new CheqdCreateResource( - targetDid.value, + CheqdDid.from(targetDid).value, TypedUUID.random(), '1.0', [], @@ -24,29 +24,15 @@ export default class CheqdInternalAttestModule extends createInternalCheqdModule }; async attest(did, attestId) { - const stringDid = CheqdDid.from(did).toEncodedString(); - const stringAttestId = String(TypedUUID.from(attestId)); - const item = await this.query.resource(stringDid, stringAttestId); - - return option(Iri).from(item?.resource?.data); + return option(Iri).from( + (await this.resource(did, attestId))?.resource?.data, + ); } async attestId(did) { - let resources = []; - let paginationKey; - - do { - // eslint-disable-next-line no-await-in-loop - const res = await this.query.collectionResources( - CheqdDid.from(did).toEncodedString(), - paginationKey, - ); - resources = resources.concat( - res.resources.filter((resource) => resource.resourceType === 'attest'), - ); - ({ paginationKey } = res); - } while (!resources.length && paginationKey != null); - - return resources.find((resource) => !resource.nextVersionId)?.id; + return await this.latestResourceIdBy( + did, + (resource) => resource.resourceType === 'attest', + ); } } diff --git a/packages/cheqd-blockchain-modules/src/blob/internal.js b/packages/cheqd-blockchain-modules/src/blob/internal.js new file mode 100644 index 000000000..9c5ce9a68 --- /dev/null +++ b/packages/cheqd-blockchain-modules/src/blob/internal.js @@ -0,0 +1,41 @@ +import { + Blob, + CheqdBlobId, + CheqdBlobWithId, + CheqdDid, +} from '@docknetwork/credential-sdk/types'; +import { option } from '@docknetwork/credential-sdk/types/generic'; +import { CheqdCreateResource, createInternalCheqdModule } from '../common'; + +const methods = { + new: (blobWithId) => { + const { blob, id } = CheqdBlobWithId.from(blobWithId); + const [did, uuid] = id; + + return new CheqdCreateResource( + CheqdDid.from(did).value, + uuid, + '1.0', + [], + 'Blob', + 'blob', + blob, + ); + }, +}; + +export default class CheqdInternalBlobModule extends createInternalCheqdModule( + methods, +) { + static Prop = 'resource'; + + static MsgNames = { + new: 'MsgCreateResource', + }; + + async blob(blobId) { + return option(Blob).from( + (await this.resource(...CheqdBlobId.from(blobId)))?.resource?.data, + ); + } +} diff --git a/packages/cheqd-blockchain-modules/src/blob/module.js b/packages/cheqd-blockchain-modules/src/blob/module.js new file mode 100644 index 000000000..83d51c90e --- /dev/null +++ b/packages/cheqd-blockchain-modules/src/blob/module.js @@ -0,0 +1,25 @@ +import AbstractBlobModule from '@docknetwork/credential-sdk/modules/blob/module'; +import { NoBlobError } from '@docknetwork/credential-sdk/modules/blob/errors'; +import { CheqdBlobId } from '@docknetwork/credential-sdk/types'; +import { injectCheqd } from '../common'; +import CheqdInternalBlobModule from './internal'; +import { OwnerWithBlob } from './types'; + +export default class CheqdBlobModule extends injectCheqd(AbstractBlobModule) { + static CheqdOnly = CheqdInternalBlobModule; + + async newTx(blobWithId, targetDid, didKeypair) { + return await this.cheqdOnly.tx.new(blobWithId, targetDid, didKeypair); + } + + async get(blobId) { + const id = CheqdBlobId.from(blobId); + const blob = await this.cheqdOnly.blob(id); + + if (blob == null) { + throw new NoBlobError(id); + } + + return new OwnerWithBlob(id[0], blob); + } +} diff --git a/packages/cheqd-blockchain-modules/src/blob/types.js b/packages/cheqd-blockchain-modules/src/blob/types.js new file mode 100644 index 000000000..0f88d2492 --- /dev/null +++ b/packages/cheqd-blockchain-modules/src/blob/types.js @@ -0,0 +1,6 @@ +import { CheqdDid, Blob } from '@docknetwork/credential-sdk/types'; +import { TypedTuple } from '@docknetwork/credential-sdk/types/generic'; + +export class OwnerWithBlob extends TypedTuple { + static Classes = [CheqdDid, Blob]; +} diff --git a/packages/cheqd-blockchain-modules/src/common/builders.js b/packages/cheqd-blockchain-modules/src/common/builders.js index d5e3c187f..614d2bd65 100644 --- a/packages/cheqd-blockchain-modules/src/common/builders.js +++ b/packages/cheqd-blockchain-modules/src/common/builders.js @@ -1,4 +1,8 @@ -import { VerificationMethodSignature } from '@docknetwork/credential-sdk/types'; +import { + VerificationMethodSignature, + CheqdDid, +} from '@docknetwork/credential-sdk/types'; +import { TypedUUID } from '@docknetwork/credential-sdk/types/generic'; import { CheqdPayloadWithTypeUrl } from './payload'; /** @@ -75,6 +79,7 @@ class Root { } } +/* eslint-disable sonarjs/cognitive-complexity */ export function createInternalCheqdModule( methods = Object.create(null), baseClass = class CheqdModuleBaseClass {}, @@ -100,6 +105,56 @@ export function createInternalCheqdModule( this.apiProvider = apiProvider; } + async resource(did, id) { + const strDid = CheqdDid.from(did).toEncodedString(); + const strID = String(TypedUUID.from(id)); + + try { + return await this.apiProvider.sdk.querier.resource.resource( + strDid, + strID, + ); + } catch (err) { + if (!String(err).includes('DID Doc not found')) { + throw err; + } + } + + return null; + } + + async latestResourceIdBy(did, cond) { + let resources; + let paginationKey; + const encodedDid = CheqdDid.from(did).toEncodedString(); + + do { + try { + // eslint-disable-next-line operator-linebreak + ({ resources, paginationKey } = + // eslint-disable-next-line no-await-in-loop + await this.apiProvider.sdk.querier.resource.collectionResources( + encodedDid, + paginationKey, + )); + } catch (err) { + if (!String(err).includes('DID Doc not found')) { + throw err; + } else { + break; + } + } + + for (const resource of resources) { + if (cond(resource) && !resource.nextVersionId) { + return resource.id; + } + } + } while (paginationKey != null); + + return null; + } + get query() { return this.apiProvider.sdk.querier[this.constructor.Prop]; } diff --git a/packages/cheqd-blockchain-modules/src/did/module.js b/packages/cheqd-blockchain-modules/src/did/module.js index ffd8f34d2..7f2dd8f1b 100644 --- a/packages/cheqd-blockchain-modules/src/did/module.js +++ b/packages/cheqd-blockchain-modules/src/did/module.js @@ -34,9 +34,13 @@ export default class CheqdDIDModule extends injectCheqd(AbstractDIDModule) { async updateDocumentTx(document, didSigners) { const didDocument = DIDDocument.from(document); if (didDocument.attests != null) { - throw new Error( - `Non-null \`attest\` was provided for DID document \`${didDocument.id}\`. Use \`attest\` module to set attestations.`, - ); + const currentDocument = await this.getDocument(didDocument.id); + + if (!didDocument.attests.eq(currentDocument.attests)) { + throw new Error( + '`attests` modifications are not supported in the `updateDocument` transaction.', + ); + } } return await this.cheqdOnly.tx.updateDidDocument(didDocument, didSigners); @@ -71,9 +75,8 @@ export default class CheqdDIDModule extends injectCheqd(AbstractDIDModule) { throw new NoDIDError(cheqdDid); } - const document = CheqdDIDDocument.from(doc).toDIDDocument(); - const attests = await this.attest.getAttests(did); - - return document.setAttests(attests); + return CheqdDIDDocument.from(doc) + .toDIDDocument() + .setAttests(await this.attest.getAttests(cheqdDid)); } } diff --git a/packages/cheqd-blockchain-modules/src/index.js b/packages/cheqd-blockchain-modules/src/index.js index 1032f97b1..7287f463a 100644 --- a/packages/cheqd-blockchain-modules/src/index.js +++ b/packages/cheqd-blockchain-modules/src/index.js @@ -1,9 +1,9 @@ import { AbstractCoreModules } from '@docknetwork/credential-sdk/modules'; import CheqdAttestModule from './attest/module'; +import CheqdBlobModule from './blob/module'; import CheqdDIDModule from './did/module'; // import CheqdAccumulatorModule from './accumulator/module'; // import CheqdAnchorModule from './anchor/module'; -// import CheqdBlobModule from './blob/module'; // import CheqdOffchainSignaturesModule from './offchain-signatures/module'; // import CheqdBBSModule from './offchain-signatures/bbs'; // import CheqdBBSPlusModule from './offchain-signatures/bbs-plus'; @@ -18,7 +18,7 @@ export class CheqdCoreModules extends AbstractCoreModules { static AttestModule = CheqdAttestModule; - // static BlobModule = CheqdBlobModule; + static BlobModule = CheqdBlobModule; static DIDModule = CheqdDIDModule; @@ -38,9 +38,10 @@ export class CheqdCoreModules extends AbstractCoreModules { export { CheqdAttestModule, CheqdDIDModule, + CheqdBlobModule, // CheqdAccumulatorModule, // CheqdAnchorModule, - // CheqdBlobModule, + // , // CheqdOffchainSignaturesModule, // CheqdBBSModule, // CheqdBBSPlusModule, diff --git a/packages/cheqd-blockchain-modules/tests/attest-module.test.js b/packages/cheqd-blockchain-modules/tests/attest-module.test.js new file mode 100644 index 000000000..0bb7096da --- /dev/null +++ b/packages/cheqd-blockchain-modules/tests/attest-module.test.js @@ -0,0 +1,29 @@ +import { CheqdAPI } from "@docknetwork/cheqd-blockchain-api"; +import { CheqdTestnetDid } from "@docknetwork/credential-sdk/types"; +import generateAttestModuleTests from "@docknetwork/credential-sdk/generate-tests/attest-module"; +import CheqdDIDModule from "../src/did/module"; +import { faucet } from "./constants"; +import CheqdAttestModule from "../src/attest/module"; + +describe("AttestModule", () => { + const cheqd = new CheqdAPI(); + + beforeAll(async () => { + await cheqd.init({ + url: process.env.ENDPOINT_URL || "http://localhost:26657", + mnemonic: faucet.mnemonic, + }); + }); + + afterAll(async () => { + await cheqd.disconnect(); + }); + + generateAttestModuleTests( + { + did: new CheqdDIDModule(cheqd), + attest: new CheqdAttestModule(cheqd), + }, + { DID: CheqdTestnetDid } + ); +}); diff --git a/packages/cheqd-blockchain-modules/tests/attest.test.js b/packages/cheqd-blockchain-modules/tests/attest.test.js deleted file mode 100644 index a2537edff..000000000 --- a/packages/cheqd-blockchain-modules/tests/attest.test.js +++ /dev/null @@ -1,63 +0,0 @@ -import { - DidKeypair, - Ed25519Keypair, -} from "@docknetwork/credential-sdk/keypairs"; -import { CheqdAPI } from "@docknetwork/cheqd-blockchain-api"; -import { timeout } from "@docknetwork/credential-sdk/utils"; -import { - DIDDocument, - CheqdTestnetDid, -} from "@docknetwork/credential-sdk/types"; -import CheqdDIDModule from "../src/did/module"; -import { faucet } from "./constants"; -import CheqdAttestModule from "../src/attest/module"; - -describe("DIDModule", () => { - const cheqd = new CheqdAPI(); - const didModule = new CheqdDIDModule(cheqd); - const attestModule = new CheqdAttestModule(cheqd); - - beforeAll(async () => { - await cheqd.init({ - url: process.env.ENDPOINT_URL || "http://localhost:26657", - mnemonic: faucet.mnemonic, - }); - }); - - afterAll(async () => { - await cheqd.disconnect(); - }); - - it("Generates a `DIDDocument` and appends an attestation to it", async () => { - const did = CheqdTestnetDid.random(); - - const keyPair = Ed25519Keypair.random(); - const didKeypair = new DidKeypair([did, 1], keyPair); - - const document = DIDDocument.create(did, [didKeypair.didKey()]); - - await didModule.createDocument(document, didKeypair); - - expect((await didModule.getDocument(did)).toJSON()).toEqual( - document.toJSON() - ); - - const iri = "some iri"; - await attestModule.setClaim(iri, did, didKeypair); - - expect((await attestModule.getAttests(did)).toString()).toBe(iri); - document.setAttests(iri); - expect((await didModule.getDocument(did)).toJSON()).toEqual( - document.toJSON() - ); - - const iri2 = "other iri"; - await attestModule.setClaim(iri2, did, didKeypair); - await timeout(500); - expect((await attestModule.getAttests(did)).toString()).toBe(iri2); - document.setAttests(iri2); - expect((await didModule.getDocument(did)).toJSON()).toEqual( - document.toJSON() - ); - }); -}); diff --git a/packages/cheqd-blockchain-modules/tests/blob-module.test.js b/packages/cheqd-blockchain-modules/tests/blob-module.test.js new file mode 100644 index 000000000..9f9580199 --- /dev/null +++ b/packages/cheqd-blockchain-modules/tests/blob-module.test.js @@ -0,0 +1,35 @@ +import { CheqdAPI } from "@docknetwork/cheqd-blockchain-api"; +import { + CheqdTestnetDid, + CheqdBlobId, +} from "@docknetwork/credential-sdk/types"; +import generateBlobModuleTests from "@docknetwork/credential-sdk/generate-tests/blob-module"; +import CheqdDIDModule from "../src/did/module"; +import { faucet } from "./constants"; +import CheqdBlobModule from "../src/blob/module"; + +describe("BlobModule", () => { + const cheqd = new CheqdAPI(); + + beforeAll(async () => { + await cheqd.init({ + url: process.env.ENDPOINT_URL || "http://localhost:26657", + mnemonic: faucet.mnemonic, + }); + }); + + afterAll(async () => { + await cheqd.disconnect(); + }); + + generateBlobModuleTests( + { + did: new CheqdDIDModule(cheqd), + blob: new CheqdBlobModule(cheqd), + }, + { + DID: CheqdTestnetDid, + BlobId: CheqdBlobId, + } + ); +}); diff --git a/packages/cheqd-blockchain-modules/tests/did-module.test.js b/packages/cheqd-blockchain-modules/tests/did-module.test.js index 14ba87f01..4d21854c2 100644 --- a/packages/cheqd-blockchain-modules/tests/did-module.test.js +++ b/packages/cheqd-blockchain-modules/tests/did-module.test.js @@ -1,26 +1,11 @@ -import { - DidKeypair, - Ed25519Keypair, -} from "@docknetwork/credential-sdk/keypairs"; import { CheqdAPI } from "@docknetwork/cheqd-blockchain-api"; +import { CheqdTestnetDid } from "@docknetwork/credential-sdk/types"; +import didModuleTests from "@docknetwork/credential-sdk/generate-tests/did-module"; import CheqdDIDModule from "../src/did/module"; import { faucet } from "./constants"; -import { - DIDDocument, - BBSPublicKeyValue, - DidKey, - BBSPlusPublicKeyValue, - PSPublicKeyValue, - VerificationMethodRef, - CheqdTestnetDid, -} from "@docknetwork/credential-sdk/types"; -import { TypedBytes } from "@docknetwork/credential-sdk/types/generic"; -import { ServiceEndpoint } from "@docknetwork/credential-sdk/types/did/offchain"; -import { NoDIDError } from "@docknetwork/credential-sdk/modules/did"; describe("DIDModule", () => { const cheqd = new CheqdAPI(); - const module = new CheqdDIDModule(cheqd); beforeAll(async () => { await cheqd.init({ @@ -33,136 +18,5 @@ describe("DIDModule", () => { await cheqd.disconnect(); }); - it("Creates basic `DIDDocument` with keys", async () => { - const did = CheqdTestnetDid.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 module.getDocument(did)).toJSON()).toEqual(document.toJSON()); - }); - - it("Operates with `DIDDocument` containing BBS/BBSPlus/PS keys", async () => { - const did = CheqdTestnetDid.random(); - - const keyPair = Ed25519Keypair.random(); - const didKeypair = new DidKeypair([did, 1], keyPair); - - const bbsKey = new BBSPublicKeyValue(TypedBytes.random(100)); - const bbsPlusKey = new BBSPlusPublicKeyValue(TypedBytes.random(100)); - const psKey = new PSPublicKeyValue(TypedBytes.random(1000)); - - const document = DIDDocument.create(did, [ - didKeypair.didKey(), - new DidKey(bbsKey), - new DidKey(bbsPlusKey), - new DidKey(psKey), - ]); - - await module.createDocument(document, didKeypair); - - expect((await module.getDocument(did)).toJSON()).toEqual(document.toJSON()); - - document.removeKey([did, 3]); - - await module.updateDocument(document, didKeypair); - - expect((await module.getDocument(did)).toJSON()).toEqual(document.toJSON()); - - expect(document.didKeys().toJSON()).toEqual([ - [didKeypair.verificationMethodId.toJSON(), didKeypair.didKey().toJSON()], - [new VerificationMethodRef(did, 2).toJSON(), new DidKey(bbsKey).toJSON()], - [new VerificationMethodRef(did, 4).toJSON(), new DidKey(psKey).toJSON()], - ]); - }); - - it("Operates with `DIDDocument` containing services", async () => { - const did = CheqdTestnetDid.random(); - - const keyPair = Ed25519Keypair.random(); - const didKeypair = new DidKeypair([did, 1], keyPair); - - const service1 = new ServiceEndpoint("LinkedDomains", [ - "ServiceEndpoint#1", - ]); - const service2 = new ServiceEndpoint("LinkedDomains", [ - "ServiceEndpoint#2", - ]); - - const document = DIDDocument.create(did, [didKeypair.didKey()], [did], { - service1, - service2, - }); - - await module.createDocument(document, didKeypair); - - expect((await module.getDocument(did)).toJSON()).toEqual(document.toJSON()); - - document.removeServiceEndpoint([did, "service2"]); - - await module.updateDocument(document, didKeypair); - - expect((await module.getDocument(did)).toJSON()).toEqual(document.toJSON()); - - expect(document.service.length).toEqual(1); - }); - - it("Updates `DIDDocument`'s keys", async () => { - const did = CheqdTestnetDid.random(); - - const keyPair1 = Ed25519Keypair.random(); - const keyPair2 = Ed25519Keypair.random(); - const keyPair3 = Ed25519Keypair.random(); - - const didKeypair1 = new DidKeypair([did, 1], keyPair1); - const didKeypair2 = new DidKeypair([did, 2], keyPair2); - const didKeypair3 = new DidKeypair([did, 3], keyPair3); - - const document = DIDDocument.create(did, [ - didKeypair1.didKey(), - didKeypair2.didKey(), - ]); - - await module.createDocument(document, didKeypair2); - expect((await module.getDocument(did)).toJSON()).toEqual(document.toJSON()); - - document.removeKey([did, 2]); - await module.updateDocument(document, didKeypair1); - expect((await module.getDocument(did)).toJSON()).toEqual(document.toJSON()); - - await expect(() => - module.updateDocument(document, didKeypair2) - ).rejects.toThrow(); - - document.addKey([did, 3], didKeypair3.didKey()); - await module.updateDocument(document, didKeypair1); - expect((await module.getDocument(did)).toJSON()).toEqual(document.toJSON()); - }); - - it("Deactivates `DIDDocument`", async () => { - const did = CheqdTestnetDid.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 module.cheqdOnly.getDidDocumentWithMetadata(did)).metadata - .deactivated - ).toBe(false); - expect((await module.getDocument(did)).toJSON()).toEqual(document.toJSON()); - - await module.removeDocument(did, didKeypair); - expect( - (await module.cheqdOnly.getDidDocumentWithMetadata(did)).metadata - .deactivated - ).toBe(true); - expect(() => module.getDocument(did)).rejects.toThrow(new NoDIDError(did)); - }); + didModuleTests({ did: new CheqdDIDModule(cheqd) }, { DID: CheqdTestnetDid }); }); diff --git a/packages/credential-sdk/CHANGELOG.md b/packages/credential-sdk/CHANGELOG.md index c90e717d0..abeb424a5 100644 --- a/packages/credential-sdk/CHANGELOG.md +++ b/packages/credential-sdk/CHANGELOG.md @@ -1,5 +1,14 @@ # @docknetwork/credential-sdk +## 0.3.0 + +### Minor Changes + +- - Updated `DIDDocument`: modified the rules for storing keys in the verificationMethod and added the required validations. + - Developed generic tests for modules to ensure they cover the same use cases, regardless of the specific implementation. + - Implemented the `CheqdBlobModule`. + - Misc tweaks. + ## 0.2.0 ### Minor Changes diff --git a/packages/credential-sdk/package.json b/packages/credential-sdk/package.json index b4f0aaa53..c67c61b45 100644 --- a/packages/credential-sdk/package.json +++ b/packages/credential-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@docknetwork/credential-sdk", - "version": "0.2.0", + "version": "0.3.0", "license": "MIT", "type": "module", "files": [ @@ -93,7 +93,6 @@ "test-ipfs": "NODE_OPTIONS=--experimental-vm-modules NODE_ENV=production jest --verbose --runInBand --forceExit ./tests/ipfs", "test-ipfs-with-node": "../scripts/with_dock_docker_test_node yarn test-ipfs", "test": "yarn jest --verbose ./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", "lint": "eslint \"src/**/*.js\"", "lint-scripts": "eslint \"scripts/**/*.js\"", diff --git a/packages/credential-sdk/src/generate-tests/attest-module.js b/packages/credential-sdk/src/generate-tests/attest-module.js new file mode 100644 index 000000000..b9080469b --- /dev/null +++ b/packages/credential-sdk/src/generate-tests/attest-module.js @@ -0,0 +1,49 @@ +import { DidKeypair, Ed25519Keypair } from '../keypairs'; +import { DIDDocument } from '../types'; +import { itIf } from './common'; + +// eslint-disable-next-line jest/no-export +export default function generateAttestModuleTests( + { did: didModule, attest: attestModule }, + { DID }, + filter = () => true, +) { + const test = itIf(filter); + + test('Generates a `DIDDocument` and appends an `Attest` to it', async () => { + 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); + + expect((await didModule.getDocument(did)).toJSON()).toEqual( + document.toJSON(), + ); + + const iri = 'some iri'; + await attestModule.setClaim(iri, did, didKeypair); + + expect((await attestModule.getAttests(did)).toString()).toBe(iri); + document.setAttests(iri); + expect((await didModule.getDocument(did)).toJSON()).toEqual( + document.toJSON(), + ); + + const iri2 = 'other iri'; + await attestModule.setClaim(iri2, did, didKeypair); + + expect((await attestModule.getAttests(did)).toString()).toBe(iri2); + document.setAttests(iri2); + expect((await didModule.getDocument(did)).toJSON()).toEqual( + document.toJSON(), + ); + }); + + test('Returns `null` on missing attest', async () => { + expect(await attestModule.getAttests(DID.random())).toBe(null); + }); +} diff --git a/packages/credential-sdk/src/generate-tests/blob-module.js b/packages/credential-sdk/src/generate-tests/blob-module.js new file mode 100644 index 000000000..5b2e72fb4 --- /dev/null +++ b/packages/credential-sdk/src/generate-tests/blob-module.js @@ -0,0 +1,51 @@ +import { DidKeypair, Ed25519Keypair } from '../keypairs'; +import { NoBlobError } from '../modules/blob/errors'; +import { DIDDocument } from '../types'; + +// eslint-disable-next-line jest/no-export +export default function generateBlobModuleTests( + { did: didModule, blob: blobModule }, + { DID, BlobId }, +) { + it('Generates a `DIDDocument` and creates a `Blob` owned by this DID', async () => { + 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, did, didKeypair); + + const written1 = await blobModule.get(blob1.id); + + expect(written1[0].eq(did)).toBe(true); + expect(written1[1].toUTF8String()).toEqual(blob1.blob); + + const blob2 = { + id: BlobId.random(did), + blob: { + example: 'content', + }, + }; + await blobModule.new(blob2, did, didKeypair); + + const written2 = await blobModule.get(blob2.id); + expect(written2[0].eq(did)).toBe(true); + expect(written2[1].toObject()).toEqual(blob2.blob); + + await expect(() => blobModule.new(blob2, did, didKeypair)).rejects.toThrow(); + }); + + it('Throws an error on missing blob', async () => { + const id = BlobId.random(DID.random()); + + await expect(() => blobModule.get(id)).rejects.toThrow(new NoBlobError(id)); + }); +} diff --git a/packages/credential-sdk/src/generate-tests/common.js b/packages/credential-sdk/src/generate-tests/common.js new file mode 100644 index 000000000..26cddd3d9 --- /dev/null +++ b/packages/credential-sdk/src/generate-tests/common.js @@ -0,0 +1,6 @@ +/* eslint-disable */ +export const itIf = + (filter) => + (...args) => + filter(...args) ? it(...args) : it.skip(...args); +/* eslint-enable */ diff --git a/packages/credential-sdk/src/generate-tests/did-module.js b/packages/credential-sdk/src/generate-tests/did-module.js new file mode 100644 index 000000000..754737c14 --- /dev/null +++ b/packages/credential-sdk/src/generate-tests/did-module.js @@ -0,0 +1,203 @@ +import { DidKeypair, Ed25519Keypair } from '../keypairs'; +import { + DIDDocument, + BBSPublicKeyValue, + DidKey, + BBSPlusPublicKeyValue, + PSPublicKeyValue, + VerificationMethodRef, +} from '../types'; +import { TypedBytes } from '../types/generic'; +import { ServiceEndpoint } from '../types/did/offchain'; +import { NoDIDError } from '../modules/did'; +import { itIf } from './common'; + +export default function generateDIDModuleTests( + { did: module }, + { DID }, + filter = () => true, +) { + const test = itIf(filter); + + test('Creates basic `DIDDocument` with keys', async () => { + 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 module.getDocument(did)).toJSON()).toEqual(document.toJSON()); + }); + + test('Creates `DIDDocument` containing BBS/BBSPlus/PS keys', async () => { + const did = DID.random(); + + const keyPair = Ed25519Keypair.random(); + const didKeypair = new DidKeypair([did, 1], keyPair); + + const bbsKey = new BBSPublicKeyValue(TypedBytes.random(100)); + const bbsPlusKey = new BBSPlusPublicKeyValue(TypedBytes.random(100)); + const psKey = new PSPublicKeyValue(TypedBytes.random(1000)); + + const document = DIDDocument.create(did, [ + didKeypair.didKey(), + new DidKey(bbsKey), + new DidKey(bbsPlusKey), + new DidKey(psKey), + ]); + + await module.createDocument(document, didKeypair); + + expect((await module.getDocument(did)).toJSON()).toEqual(document.toJSON()); + }); + + test('Updates `DIDDocument` containing BBS/BBSPlus/PS keys', async () => { + const did = DID.random(); + + const keyPair = Ed25519Keypair.random(); + const didKeypair = new DidKeypair([did, 1], keyPair); + + const bbsKey = new DidKey(new BBSPublicKeyValue(TypedBytes.random(100))); + const bbsPlusKey = new DidKey( + new BBSPlusPublicKeyValue(TypedBytes.random(100)), + ); + const psKey = new DidKey(new PSPublicKeyValue(TypedBytes.random(1000))); + + const document = DIDDocument.create(did, [didKeypair.didKey()]); + + await module.createDocument(document, didKeypair); + + expect((await module.getDocument(did)).toJSON()).toEqual(document.toJSON()); + + document.addKey([did, document.nextKeyIndex()], bbsKey); + document.addKey([did, document.nextKeyIndex()], bbsPlusKey); + document.addKey([did, document.nextKeyIndex()], psKey); + + await module.updateDocument(document, didKeypair); + + expect((await module.getDocument(did)).toJSON()).toEqual(document.toJSON()); + + expect(document.didKeys().toJSON()).toEqual([ + [didKeypair.verificationMethodId.toJSON(), didKeypair.didKey().toJSON()], + [new VerificationMethodRef(did, 2).toJSON(), bbsKey.toJSON()], + [new VerificationMethodRef(did, 3).toJSON(), bbsPlusKey.toJSON()], + [new VerificationMethodRef(did, 4).toJSON(), psKey.toJSON()], + ]); + }); + + test('Creates `DIDDocument` containing services', async () => { + const did = DID.random(); + + const keyPair = Ed25519Keypair.random(); + const didKeypair = new DidKeypair([did, 1], keyPair); + + const service1 = new ServiceEndpoint('LinkedDomains', [ + 'ServiceEndpoint#1', + ]); + const service2 = new ServiceEndpoint('LinkedDomains', [ + 'ServiceEndpoint#2', + ]); + + const document = DIDDocument.create(did, [didKeypair.didKey()], [], { + service1, + service2, + }); + + await module.createDocument(document, didKeypair); + + expect((await module.getDocument(did)).toJSON()).toEqual(document.toJSON()); + }); + + test('Updates `DIDDocument` containing services', async () => { + const did = DID.random(); + + const keyPair = Ed25519Keypair.random(); + const didKeypair = new DidKeypair([did, 1], keyPair); + + const service1 = new ServiceEndpoint('LinkedDomains', [ + 'ServiceEndpoint#1', + ]); + const service2 = new ServiceEndpoint('LinkedDomains', [ + 'ServiceEndpoint#2', + ]); + + const document = DIDDocument.create(did, [didKeypair.didKey()]); + + await module.createDocument(document, didKeypair); + + expect((await module.getDocument(did)).toJSON()).toEqual(document.toJSON()); + + document.addServiceEndpoint([did, 'service1'], service1); + document.addServiceEndpoint([did, 'service2'], service2); + + await module.updateDocument(document, didKeypair); + + expect((await module.getDocument(did)).toJSON()).toEqual(document.toJSON()); + + document.removeServiceEndpoint([did, 'service2']); + + await module.updateDocument(document, didKeypair); + + expect((await module.getDocument(did)).toJSON()).toEqual(document.toJSON()); + + expect(document.service.length).toEqual(1); + }); + + test("Updates `DIDDocument`'s keys", async () => { + const did = DID.random(); + + const keyPair1 = Ed25519Keypair.random(); + const keyPair2 = Ed25519Keypair.random(); + const keyPair3 = Ed25519Keypair.random(); + + const didKeypair1 = new DidKeypair([did, 1], keyPair1); + const didKeypair2 = new DidKeypair([did, 2], keyPair2); + const didKeypair3 = new DidKeypair([did, 3], keyPair3); + + const document = DIDDocument.create(did, [ + didKeypair1.didKey(), + didKeypair2.didKey(), + ]); + + await module.createDocument(document, didKeypair2); + expect((await module.getDocument(did)).toJSON()).toEqual(document.toJSON()); + + document.removeKey([did, 2]); + await module.updateDocument(document, didKeypair1); + expect((await module.getDocument(did)).toJSON()).toEqual(document.toJSON()); + + document.addKey([did, 3], didKeypair3.didKey()); + await expect(() => module.updateDocument(document, didKeypair2)).rejects.toThrow(); + + await module.updateDocument(document, didKeypair1); + expect((await module.getDocument(did)).toJSON()).toEqual(document.toJSON()); + }); + + test('Removes (deactivates) `DIDDocument`', async () => { + 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 module.getDocument(did)).toJSON()).toEqual(document.toJSON()); + + await module.removeDocument(did, didKeypair); + await expect(() => module.getDocument(did)).rejects.toThrow( + new NoDIDError(did), + ); + }); + + test("Throws an error if `DIDDocument` doesn't exist", async () => { + const did = DID.random(); + + await expect(() => module.getDocument(did)).rejects.toThrow( + new NoDIDError(did), + ); + }); +} diff --git a/packages/credential-sdk/src/modules/schema/module.js b/packages/credential-sdk/src/modules/schema/module.js index 5f30b1046..940c74ca8 100644 --- a/packages/credential-sdk/src/modules/schema/module.js +++ b/packages/credential-sdk/src/modules/schema/module.js @@ -4,16 +4,16 @@ import { validate } from 'jsonschema'; // Supported schemas import JSONSchema07 from '../../vc/schemas/schema-draft-07'; import jsonFetch from '../../utils/json-fetch'; -import { BlobId, Blob, BlobWithId } from '../../types'; +import { Blob, BlobWithId } from '../../types'; export default class Schema { /** * Creates a new `Schema` object * @constructor - * @param {string} [id] - optional schema ID, if not given, generate a random id + * @param {string} [id] - schema ID */ - constructor(id = BlobId.random(32)) { - this.id = BlobId.from(id); + constructor(id) { + this.id = id; } static fromJSON(json) { diff --git a/packages/credential-sdk/src/resolver/did/dock-did-resolver.js b/packages/credential-sdk/src/resolver/did/dock-did-resolver.js index 56ba9fd83..34fc62ea5 100644 --- a/packages/credential-sdk/src/resolver/did/dock-did-resolver.js +++ b/packages/credential-sdk/src/resolver/did/dock-did-resolver.js @@ -1,5 +1,4 @@ import { AbstractDIDModule } from '../../modules/did'; -import { validateDockDIDSS58Identifier } from '../../types/did'; import DIDResolver from './did-resolver'; import { ensureInstanceOf } from '../../utils'; @@ -20,8 +19,7 @@ class DockDIDResolver extends DIDResolver { } async resolve(qualifiedDid) { - const { id, did } = this.parseDid(qualifiedDid); - validateDockDIDSS58Identifier(id); + const { did } = this.parseDid(qualifiedDid); const document = await this.didModule.getDocument(did); diff --git a/packages/credential-sdk/src/types/accumulator/index.js b/packages/credential-sdk/src/types/accumulator/index.js index ae00852c2..ee0f2393c 100644 --- a/packages/credential-sdk/src/types/accumulator/index.js +++ b/packages/credential-sdk/src/types/accumulator/index.js @@ -1,9 +1,9 @@ -import { IdentRef } from '../did'; +import { DIDRef } from '../did'; import { TypedBytes, TypedUUID, sized, withFrom, } from '../generic'; -export class AccumulatorId extends IdentRef {} +export class AccumulatorId extends DIDRef {} export class CheqdAccumulatorIdIdent extends TypedUUID {} diff --git a/packages/credential-sdk/src/types/blob/blob-id.js b/packages/credential-sdk/src/types/blob/blob-id.js index 60e51714b..721cfd5d1 100644 --- a/packages/credential-sdk/src/types/blob/blob-id.js +++ b/packages/credential-sdk/src/types/blob/blob-id.js @@ -1,26 +1,31 @@ import { encodeAsSS58, decodeFromSS58 } from '../../utils/ss58'; import { isHex } from '../../utils/bytes'; -import { TypedBytes, sized, withQualifier } from '../generic'; -import { DockBlobQualifier } from './const'; +import { + TypedBytes, TypedUUID, sized, withQualifier, +} from '../generic'; +import { CheqdBlobQualifier, DockBlobQualifier } from './const'; +import { CheqdMainnetDid, CheqdTestnetDid, DIDRef } from '../did'; -export class BlobId extends withQualifier(TypedBytes) { - static Qualifier = DockBlobQualifier; +export class CheqdBlobId extends DIDRef { + static Qualifier = CheqdBlobQualifier; - static fromUnqualifiedString(bytes) { - return new this(isHex(bytes) ? bytes : decodeFromSS58(bytes)); - } + static Ident = TypedUUID; toEncodedString() { - return encodeAsSS58(this.value); + let prefix = ''; + if (this[0].value instanceof CheqdTestnetDid) { + prefix = 'testnet'; + } else if (this[0].value instanceof CheqdMainnetDid) { + prefix = 'mainnet'; + } + + return `${prefix}:${this.value}`; } } -export class DockBlobId extends sized(BlobId) { +export class DockBlobId extends sized(withQualifier(TypedBytes)) { static Size = 32; -} -/* -export class BlobIdIdent extends withQualifier(TypedBytes) { static Qualifier = DockBlobQualifier; static fromUnqualifiedString(bytes) { @@ -31,34 +36,3 @@ export class BlobIdIdent extends withQualifier(TypedBytes) { return encodeAsSS58(this.value); } } - -export class BlobId extends IdentRef {} - -export class CheqdBlobIdIdent extends withFrom(sized(BlobIdIdent), (value, from) => value instanceof CheqdBlobId ? value[1]: from(value)) { - static Qualifier = DockBlobQualifier; - - static fromUnqualifiedString(bytes) { - return new this(bytes); - } - - toEncodedString() { - return this.value; - } -} - -export class CheqdBlobId extends BlobId { - static Ident = CheqdBlobIdIdent; -} - -export class DockBlobIdIdent extends withFrom(sized(BlobIdIdent), (value, from) => value instanceof DockBlobId ? value[1]: from(value)) { - static Size = 32; -} - -export class DockBlobId extends BlobId { - static Ident = DockBlobIdIdent; -} - -export class DockBlobId extends sized(BlobId) { - static Size = 32; -} -*/ diff --git a/packages/credential-sdk/src/types/blob/blob.js b/packages/credential-sdk/src/types/blob/blob.js index d62e0cd3e..a777a8dc3 100644 --- a/packages/credential-sdk/src/types/blob/blob.js +++ b/packages/credential-sdk/src/types/blob/blob.js @@ -37,6 +37,10 @@ export default class Blob extends TypedBytes { return new this(value); } + toUTF8String() { + return u8aToString(this); + } + toObject() { try { return JSON.parse(u8aToString(this.bytes)); diff --git a/packages/credential-sdk/src/types/blob/const.js b/packages/credential-sdk/src/types/blob/const.js index 1a559584e..b2595e0b9 100644 --- a/packages/credential-sdk/src/types/blob/const.js +++ b/packages/credential-sdk/src/types/blob/const.js @@ -1,2 +1,4 @@ export const DockBlobQualifier = 'blob:dock:'; +export const CheqdBlobQualifier = 'blob:cheqd:'; export const DockBlobIdByteSize = 32; +export const CheqdBlobIdByteSize = 16; diff --git a/packages/credential-sdk/src/types/blob/index.js b/packages/credential-sdk/src/types/blob/index.js index 1db3c6ceb..0b697ea7e 100644 --- a/packages/credential-sdk/src/types/blob/index.js +++ b/packages/credential-sdk/src/types/blob/index.js @@ -1,5 +1,5 @@ -import { TypedStruct } from '../generic'; -import { BlobId } from './blob-id'; +import { Any, TypedStruct, withProp } from '../generic'; +import { CheqdBlobId, DockBlobId } from './blob-id'; import Blob from './blob'; export * from './blob-id'; @@ -7,7 +7,10 @@ export { default as Blob } from './blob'; export class BlobWithId extends TypedStruct { static Classes = { - id: BlobId, + id: Any, blob: Blob, }; } + +export class CheqdBlobWithId extends withProp(BlobWithId, 'id', CheqdBlobId) {} +export class DockBlobWithId extends withProp(BlobWithId, 'id', DockBlobId) {} diff --git a/packages/credential-sdk/src/types/did/document.js b/packages/credential-sdk/src/types/did/document.js index bf2a24406..046946c57 100644 --- a/packages/credential-sdk/src/types/did/document.js +++ b/packages/credential-sdk/src/types/did/document.js @@ -36,8 +36,19 @@ import { isBytes, stringToU8a, u8aToString, + valueBytes, withExtendedStaticProperties, } from '../../utils'; +import { + BBDT16PublicKey, + BBDT16PublicKeyValue, + BBSPlusPublicKey, + BBSPlusPublicKeyValue, + BBSPublicKey, + BBSPublicKeyValue, + PSPublicKey, + PSPublicKeyValue, +} from '../offchain-signatures'; export const ATTESTS_IRI = 'https://rdf.dock.io/alpha/2021#attestsDocumentContents'; @@ -232,6 +243,16 @@ export class DidKeyValueWithRef extends withFrom( key: DidKeyValue, }; + toVerificationMethod() { + // eslint-disable-next-line no-use-before-define + return new VerificationMethod( + this.ref, + this.key.constructor.VerKeyType, + this.ref.did, + this.key.value.bytes, + ); + } + toJSON() { const { ref, key } = this; @@ -346,6 +367,13 @@ export class VerificationMethod extends withFrom(TypedStruct, (value, from) => ( publicKeyHex: option(TypedBytes), }; + isOffchain() { + return !( + this.type instanceof Ed25519Verification2018Method + || this.type instanceof Ed25519Verification2020Method + ); + } + publicKey() { const bytes = ( this.publicKeyBase58 @@ -353,6 +381,7 @@ export class VerificationMethod extends withFrom(TypedStruct, (value, from) => ( || this.publicKeyJwk || this.publicKeyHex )?.bytes; + if (bytes == null) { throw new Error( `Expected either of ${fmtIter([ @@ -364,25 +393,26 @@ export class VerificationMethod extends withFrom(TypedStruct, (value, from) => ( ); } - let PublicKey; switch (this.type.type) { case Ed25519VerKeyName: - PublicKey = PublicKeyEd25519; - break; + return new PublicKeyEd25519(bytes); case Sr25519VerKeyName: - PublicKey = PublicKeySr25519; - break; + return new PublicKeySr25519(bytes); case Ed255192020VerKeyName: - PublicKey = PublicKeyEd25519; - break; + return new PublicKeyEd25519(bytes); case EcdsaSecp256k1VerKeyName: - PublicKey = PublicKeySecp256k1; - break; + return new PublicKeySecp256k1(bytes); + case Bls12381G2VerificationKeyDock2022.Type: + return new BBSPlusPublicKey(new BBSPlusPublicKeyValue(bytes)); + case Bls12381BBSVerificationKeyDock2023.Type: + return new BBSPublicKey(new BBSPublicKeyValue(bytes)); + case Bls12381PSVerificationKeyDock2023.Type: + return new PSPublicKey(new PSPublicKeyValue(bytes)); + case Bls12381BBDT16VerificationKeyDock2024.Type: + return new BBDT16PublicKey(new BBDT16PublicKeyValue(bytes)); default: throw new Error(`Unknown key type ${this.type.type}`); } - - return new PublicKey(bytes); } static fromDidKey(keyRef, didKey) { @@ -392,7 +422,7 @@ export class VerificationMethod extends withFrom(TypedStruct, (value, from) => ( ref, didKey.publicKey.constructor.VerKeyType, ref[0], - didKey.publicKey.value, + valueBytes(didKey.publicKey), ); } @@ -402,9 +432,13 @@ export class VerificationMethod extends withFrom(TypedStruct, (value, from) => ( this.id, this.controller, this.type, - this.publicKey().value.bytes, + valueBytes(this.publicKey()), ); } + + toDidKeyValueWithRef() { + return new DidKeyValueWithRef(this.id, this.publicKey()); + } } export class CheqdVerificationMethod extends withFrom( @@ -420,6 +454,13 @@ export class CheqdVerificationMethod extends withFrom( verificationMaterial: PublicKeyBase58, }; + isOffchain() { + return !( + this.verificationMethodType instanceof Ed25519Verification2018Method + || this.verificationMethodType instanceof Ed25519Verification2020Method + ); + } + toVerificationMethod() { return new VerificationMethod( this.id, @@ -497,7 +538,7 @@ export class DIDDocument extends TypedStruct { verificationMethod: VerificationMethods, service: option(Services), authentication: option(VerificationMethodReferences), - assertionMethod: option(AssertionMethod), + assertionMethod: option(VerificationMethodReferences), keyAgreement: option(VerificationMethodReferences), capabilityInvocation: option(VerificationMethodReferences), capabilityDelegation: option(VerificationMethodReferences), @@ -506,7 +547,7 @@ export class DIDDocument extends TypedStruct { static create( did, - keys, + keys = [], controllers = [], serviceEndpoints = {}, { @@ -516,7 +557,7 @@ export class DIDDocument extends TypedStruct { [ATTESTS_IRI]: attests = null, } = {}, ) { - const obj = new this( + const doc = new this( context, did, alsoKnownAs, @@ -532,44 +573,49 @@ export class DIDDocument extends TypedStruct { ); let idx = 0; + let selfControlled = false; for (const key of keys) { - obj.addKey([did, ++idx], key); + const didKey = DidKey.from(key); + doc.addKey([did, ++idx], didKey); + + selfControlled ||= didKey.verRels.isAuthentication(); } for (const [id, serviceEndpoint] of Object.entries(serviceEndpoints)) { - obj.addServiceEndpoint([did, id], serviceEndpoint); + doc.addServiceEndpoint([did, id], serviceEndpoint); + } + if (selfControlled && !doc.controller.find((ctrl) => ctrl.eq(did))) { + doc.addController(did); } - return obj; + return doc; } addKey(keyRef, didKey) { const ref = VerificationMethodRef.from(keyRef); const key = DidKey.from(didKey); - const isVerificationMethod = key.publicKey.constructor.VerificationMethod; - const refOrKey = isVerificationMethod - ? ref - : new DidKeyValueWithRef(keyRef, key.publicKey); + + if (this.verificationMethod.find((existing) => existing.eq(ref))) { + throw new Error(`Verification method \`${ref}\` already exists`); + } if (key.verRels.isAuthentication()) { this.authentication ||= []; - this.authentication.push(refOrKey); + this.authentication.push(ref); } if (key.verRels.isAssertion()) { this.assertionMethod ||= []; - this.assertionMethod.push(refOrKey); + this.assertionMethod.push(ref); } if (key.verRels.isKeyAgreement()) { this.keyAgreement ||= []; - this.keyAgreement.push(refOrKey); + this.keyAgreement.push(ref); } if (key.verRels.isCapabilityInvocation()) { this.capabilityInvocation ||= []; - this.capabilityInvocation.push(refOrKey); + this.capabilityInvocation.push(ref); } - if (isVerificationMethod) { - this.verificationMethod.push(VerificationMethod.fromDidKey(keyRef, key)); - } + this.verificationMethod.push(VerificationMethod.fromDidKey(ref, key)); return this; } @@ -605,6 +651,8 @@ export class DIDDocument extends TypedStruct { // eslint-disable-next-line no-bitwise if (~keyIndex) { this.verificationMethod.splice(keyIndex, 1); + } else { + throw new Error(`Verification method with id \`${ref}\` doesn't exist`); } const isNotRef = (value) => !(value.ref ?? value).eq(ref); @@ -618,6 +666,15 @@ export class DIDDocument extends TypedStruct { return this; } + nextKeyIndex() { + return ( + [...this.verificationMethod].reduce( + (max, { id: { index } }) => Math.max(max, index ?? 0), + 0, + ) + 1 + ); + } + get attests() { return this[ATTESTS_IRI]; } @@ -626,8 +683,12 @@ export class DIDDocument extends TypedStruct { return this.verificationMethod; } - setAttests(iri) { + set attests(iri) { this[ATTESTS_IRI] = iri; + } + + setAttests(iri) { + this.attests = iri; return this; } @@ -668,11 +729,6 @@ export class DIDDocument extends TypedStruct { return [method.id, new DidKey(method.publicKey(), verRels)]; }) - .concat( - [...assertionMethod] - .filter((v) => v instanceof DidKeyValueWithRef) - .map((v) => [v.ref, new DidKey(v.key)]), - ) .filter(Boolean); return new DidKeys(keys); @@ -731,6 +787,22 @@ export class CheqdDIDDocument extends TypedStruct { versionId: option(VersionId), }; + constructor(...args) { + super(...args); + + const offchainVerMethod = [ + ...this.verificationMethod.filter((verMethod) => verMethod.isOffchain()), + ].map((verMethod) => verMethod.toVerificationMethod()); + this.verificationMethod = this.verificationMethod.filter( + (verMethod) => !verMethod.isOffchain(), + ); + + this.assertionMethod = [ + ...this.assertionMethod, + ...[...offchainVerMethod].map((verMethod) => verMethod.toDidKeyValueWithRef()), + ]; + } + toDIDDocument() { const { context, @@ -746,15 +818,24 @@ export class CheqdDIDDocument extends TypedStruct { service, } = this; + const offchainVerMethod = [ + ...assertionMethod.filter( + (keyRefOrKey) => keyRefOrKey instanceof DidKeyValueWithRef, + ), + ].map((verMethod) => verMethod.toVerificationMethod()); + const assertionMethodWithoutOffchainKeys = assertionMethod.filter( + (keyRefOrKey) => !(keyRefOrKey instanceof DidKeyValueWithRef), + ); + return new DIDDocument( context, id, alsoKnownAs, controller, - verificationMethod, + [...verificationMethod, ...offchainVerMethod], service, authentication, - assertionMethod, + assertionMethodWithoutOffchainKeys, keyAgreement, capabilityInvocation, capabilityDelegation, diff --git a/packages/credential-sdk/src/types/did/onchain/did-key.js b/packages/credential-sdk/src/types/did/onchain/did-key.js index 6d99abbee..348c01853 100644 --- a/packages/credential-sdk/src/types/did/onchain/did-key.js +++ b/packages/credential-sdk/src/types/did/onchain/did-key.js @@ -43,8 +43,6 @@ export class DidKeyEd25519PublicKey extends DidKeyValue { static Type = 'ed25519'; static Class = PublicKeyEd25519Value; - - static VerificationMethod = true; } /** * Class representing Secp256k1 PublicKey @@ -55,8 +53,6 @@ export class DidKeySecp256k1PublicKey extends DidKeyValue { static Type = 'secp256k1'; static Class = PublicKeySecp256k1Value; - - static VerificationMethod = true; } /** * Class representing Sr25519 PublicKey @@ -67,8 +63,6 @@ export class DidKeySr25519PublicKey extends DidKeyValue { static Type = 'sr25519'; static Class = PublicKeySr25519Value; - - static VerificationMethod = true; } /** * Class representing X25519 PublicKey @@ -79,8 +73,6 @@ export class DidKeyX25519PublicKey extends DidKeyValue { static Type = 'x25519'; static Class = PublicKeyX25519Value; - - static VerificationMethod = true; } export class DidKeyBBSPublicKey extends DidKeyValue { static Class = BBSPublicKeyValue; @@ -88,8 +80,6 @@ export class DidKeyBBSPublicKey extends DidKeyValue { static Type = 'bbs'; static VerKeyType = Bls12381BBS23DockVerKeyName; - - static VerificationMethod = false; } export class DidKeyBBSPlusPublicKey extends DidKeyValue { static Class = BBSPlusPublicKeyValue; @@ -97,8 +87,6 @@ export class DidKeyBBSPlusPublicKey extends DidKeyValue { static Type = 'bbsPlus'; static VerKeyType = Bls12381BBSDockVerKeyName; - - static VerificationMethod = false; } export class DidKeyPSPublicKey extends DidKeyValue { static Class = PSPublicKeyValue; @@ -106,8 +94,6 @@ export class DidKeyPSPublicKey extends DidKeyValue { static Type = 'ps'; static VerKeyType = Bls12381PSDockVerKeyName; - - static VerificationMethod = false; } DidKeyValue.bindVariants( @@ -170,6 +156,10 @@ export class DidKey extends TypedStruct { static fromKeypair(keyPair, verRels) { return new this(keyPair.publicKey(), verRels); } + + isOffchain() { + return Assertion.some((klass) => this.publicKey instanceof klass); + } } export class DidKeysList extends TypedArray { diff --git a/packages/credential-sdk/src/types/did/onchain/index.js b/packages/credential-sdk/src/types/did/onchain/index.js index 411dbcafa..17480ce25 100644 --- a/packages/credential-sdk/src/types/did/onchain/index.js +++ b/packages/credential-sdk/src/types/did/onchain/index.js @@ -1,6 +1,5 @@ import { Null, TypedNumber, TypedStruct } from '../../generic'; -export * from './utils'; export * from './constants'; export * from './typed-did'; export * from './controllers'; 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 4fcc76a78..8df748078 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 @@ -49,10 +49,5 @@ export class CheqdMainnetDid extends CheqdDid { static Type = 'mainnet'; } -export class CheqdGenericDid extends CheqdDid { - static Class = CheqdDidValue; - static Type = 'generic'; -} - -CheqdDid.bindVariants(CheqdTestnetDid, CheqdMainnetDid, CheqdGenericDid); +CheqdDid.bindVariants(CheqdTestnetDid, CheqdMainnetDid); diff --git a/packages/credential-sdk/src/types/did/onchain/typed-did/did-method-key/did-method-key-public-key.js b/packages/credential-sdk/src/types/did/onchain/typed-did/did-method-key/did-method-key-public-key.js index d48356ee0..e7a4557f3 100644 --- a/packages/credential-sdk/src/types/did/onchain/typed-did/did-method-key/did-method-key-public-key.js +++ b/packages/credential-sdk/src/types/did/onchain/typed-did/did-method-key/did-method-key-public-key.js @@ -1,7 +1,7 @@ import bs58 from 'bs58'; import { base58btc } from 'multiformats/bases/base58'; import varint from 'varint'; -import { TypedEnum, withQualifier } from '../../../../generic'; +import { TypedEnum, TypedStruct, withQualifier } from '../../../../generic'; import { PublicKeyEd25519Value, PublicKeySecp256k1Value, @@ -13,6 +13,8 @@ import { Ed25519PublicKeyPrefix, Secp256k1PublicKeyPrefix, } from '../../constants'; +import DidOrDidMethodKeySignature from '../signature'; +import { DidMethodKeySignatureValue } from './did-method-key-signature'; export class DidMethodKeyPublicKey extends withQualifier(TypedEnum) { static Qualifier = DidMethodKeyQualifier; @@ -74,15 +76,27 @@ export class DidMethodKeyPublicKey extends withQualifier(TypedEnum) { throw new Error('Expected keypair that has the same key as in `this`'); } - return { - didMethodKeySignature: { - didMethodKey: this, - sig: keyPair.sign(bytes), - }, - }; + // eslint-disable-next-line no-use-before-define + return new DidMethodKeySignature( + // eslint-disable-next-line no-use-before-define + new DidMethodKeySignatureValueObject(this, keyPair.sign(bytes)), + ); } } +export class DidMethodKeySignatureValueObject extends TypedStruct { + static Classes = { + didMethodKey: DidMethodKeyPublicKey, + sig: DidMethodKeySignatureValue, + }; +} + +export class DidMethodKeySignature extends DidOrDidMethodKeySignature { + static Type = 'didMethodKeySignature'; + + static Class = DidMethodKeySignatureValueObject; +} + export class DidMethodKeyPublicKeyEd25519 extends DidMethodKeyPublicKey { static Class = PublicKeyEd25519Value; diff --git a/packages/credential-sdk/src/types/did/onchain/typed-did/did-method-key/did-method-key-signature.js b/packages/credential-sdk/src/types/did/onchain/typed-did/did-method-key/did-method-key-signature.js index 22684ffc3..8d8d85fbb 100644 --- a/packages/credential-sdk/src/types/did/onchain/typed-did/did-method-key/did-method-key-signature.js +++ b/packages/credential-sdk/src/types/did/onchain/typed-did/did-method-key/did-method-key-signature.js @@ -4,15 +4,15 @@ import { SignatureSecp256k1Value, } from '../../../../signatures'; -export class DidMethodKeySignature extends TypedEnum {} -export class DidMethodKeySignatureEd25519 extends DidMethodKeySignature { +export class DidMethodKeySignatureValue extends TypedEnum {} +export class DidMethodKeySignatureValueEd25519 extends DidMethodKeySignatureValue { static Class = SignatureEd25519Value; } -export class DidMethodKeySignatureSecp256k1 extends DidMethodKeySignature { +export class DidMethodKeySignatureValueSecp256k1 extends DidMethodKeySignatureValue { static Class = SignatureSecp256k1Value; } -DidMethodKeySignature.bindVariants( - DidMethodKeySignatureEd25519, - DidMethodKeySignatureSecp256k1, +DidMethodKeySignatureValue.bindVariants( + DidMethodKeySignatureValueEd25519, + DidMethodKeySignatureValueSecp256k1, ); diff --git a/packages/credential-sdk/src/types/did/onchain/typed-did/dock-did-value.js b/packages/credential-sdk/src/types/did/onchain/typed-did/dock-did-value.js index 5362dee57..e04d4fbc7 100644 --- a/packages/credential-sdk/src/types/did/onchain/typed-did/dock-did-value.js +++ b/packages/credential-sdk/src/types/did/onchain/typed-did/dock-did-value.js @@ -1,6 +1,14 @@ import { DockDIDQualifier } from '../constants'; import { decodeFromSS58, encodeAsSS58, isHex } from '../../../../utils'; -import { withQualifier, TypedBytes, sized } from '../../../generic'; +import { + withQualifier, + TypedBytes, + sized, + TypedNumber, + TypedStruct, +} from '../../../generic'; +import DidOrDidMethodKeySignature from './signature'; +import { Signature } from '../../../signatures'; /** * `did:dock:*` @@ -24,12 +32,24 @@ export default class DockDidValue extends sized(withQualifier(TypedBytes)) { } signWith(keyPair, bytes) { - return { - didSignature: { - did: this, - keyId: keyPair.keyId, - sig: keyPair.sign(bytes), - }, - }; + // eslint-disable-next-line no-use-before-define + return new DockDidSignature( + // eslint-disable-next-line no-use-before-define + new DockDidSignatureValue(this, keyPair.keyId, keyPair.sign(bytes)), + ); } } + +export class DockDidSignatureValue extends TypedStruct { + static Classes = { + did: DockDidValue, + keyId: class KeyId extends TypedNumber {}, + sig: Signature, + }; +} + +export class DockDidSignature extends DidOrDidMethodKeySignature { + static Type = 'didSignature'; + + static Class = DockDidSignatureValue; +} diff --git a/packages/credential-sdk/src/types/did/onchain/typed-did/index.js b/packages/credential-sdk/src/types/did/onchain/typed-did/index.js index a76be7329..0d6204fd2 100644 --- a/packages/credential-sdk/src/types/did/onchain/typed-did/index.js +++ b/packages/credential-sdk/src/types/did/onchain/typed-did/index.js @@ -1,7 +1,9 @@ -import { DidMethodKeyPublicKey } from './did-method-key'; -import DockDidValue from './dock-did-value'; -import { TypedEnum, withQualifier } from '../../../generic'; +import { DidMethodKeyPublicKey, DidMethodKeySignature } from './did-method-key'; +import DockDidValue, { DockDidSignature } from './dock-did-value'; +import { TypedEnum, TypedTuple, withQualifier } from '../../../generic'; import { CheqdDid } from './cheqd-did'; +import { withExtendedStaticProperties } from '../../../../utils'; +import DidOrDidMethodKeySignature from './signature'; export const DockDidOrDidMethodKey = withQualifier( class DockDidOrDidMethodKey extends TypedEnum { @@ -19,20 +21,16 @@ export const DockDidOrDidMethodKey = withQualifier( /** * Creates signature for the state change with supplied arguments. * - * @param api + * @param apiProvider * @param name * @param payload * @param keyRef * @returns {object} */ async signStateChange(apiProvider, name, payload, keyRef) { - // eslint-disable-next-line no-param-reassign - payload.nonce ??= 1 + (await apiProvider.nonce(this)); - if ( - Number(process.env.LOG_STATE_CHANGE) - || Boolean(process.env.LOG_STATE_CHANGE) - ) { - console.log('State change:', name, '=>', payload); + const { LOG_STATE_CHANGE } = process.env; + if (Number(LOG_STATE_CHANGE) || Boolean(LOG_STATE_CHANGE)) { + console.dir(payload, { depth: null }); } const bytes = await apiProvider.stateChangeBytes(name, payload); @@ -49,8 +47,10 @@ export const DockDidOrDidMethodKey = withQualifier( * @param keyRef */ async changeState(api, method, name, payload, keyRef) { - const signature = await this.signStateChange(api, name, payload, keyRef); - return await method(payload, signature); + return await method( + payload, + await this.signStateChange(api, name, payload, keyRef), + ); } toString() { @@ -153,16 +153,22 @@ export const NamespaceDid = withQualifier( ); class DockNamespaceDid extends NamespaceDid { + static Qualifier = 'did:dock:'; + static Type = 'dock'; static Class = DockDidValueToString; } class DidNamespaceKey extends NamespaceDid { + static Qualifier = 'did:key:'; + static Type = 'didMethodKey'; static Class = DidMethodKeyPublicKeyToString; } class CheqdNamespaceDid extends NamespaceDid { + static Qualifier = 'did:cheqd:'; + static Type = 'cheqd'; static Class = CheqdDid; @@ -170,5 +176,55 @@ class CheqdNamespaceDid extends NamespaceDid { NamespaceDid.bindVariants(DockNamespaceDid, DidNamespaceKey, CheqdNamespaceDid); +export class DIDRef extends withExtendedStaticProperties( + ['Ident'], + withQualifier(TypedTuple), +) { + static Qualifier = ''; + + static get Classes() { + return [NamespaceDid, this.Ident]; + } + + static random(did) { + return new this(did, this.Ident.random()); + } + + get did() { + return this[0]; + } + + get value() { + return this[1]; + } + + static fromUnqualifiedString(str) { + const regex = new RegExp(`^${this.Qualifier}([^#]+):(.+)$`); + const match = str.match(regex); + + if (!match) { + throw new Error(`Invalid format for DID reference: \`${str}\``); + } + + const [, did, value] = match; + return new this(did, value); + } + + toEncodedString() { + const { did, value } = this; + + return `${did}:${value}`; + } + + toJSON() { + return this.toString(); + } +} + +DidOrDidMethodKeySignature.bindVariants( + DockDidSignature, + DidMethodKeySignature, +); + export { DockDidValue, DidMethodKeyPublicKey }; export * from './cheqd-did'; diff --git a/packages/credential-sdk/src/types/did/onchain/typed-did/signature.js b/packages/credential-sdk/src/types/did/onchain/typed-did/signature.js new file mode 100644 index 000000000..bb6818d40 --- /dev/null +++ b/packages/credential-sdk/src/types/did/onchain/typed-did/signature.js @@ -0,0 +1,3 @@ +import { TypedEnum } from '../../../generic'; + +export default class DidOrDidMethodKeySignature extends TypedEnum {} diff --git a/packages/credential-sdk/src/types/did/onchain/utils.js b/packages/credential-sdk/src/types/did/onchain/utils.js deleted file mode 100644 index 36448e5a8..000000000 --- a/packages/credential-sdk/src/types/did/onchain/utils.js +++ /dev/null @@ -1,83 +0,0 @@ -/* eslint-disable max-classes-per-file */ - -import { isHexWithGivenByteSize } from '../../../utils/bytes'; -import { - PublicKey, // eslint-disable-line -} from '../../public-keys'; - -import { Signature } from "../../signatures"; // eslint-disable-line - -import { DockDIDByteSize } from './constants'; - -/** - * Check if the given identifier is the hex representation of a Dock DID. - * @param {string} identifier - The identifier to check. - * @return {void} Throws exception if invalid identifier - */ -export function validateDockDIDHexIdentifier(identifier) { - // Byte size of the Dock DID identifier, i.e. the `DockDIDQualifier` is not counted. - if (!isHexWithGivenByteSize(identifier, DockDIDByteSize)) { - throw new Error(`DID identifier must be ${DockDIDByteSize} bytes`); - } -} - -/** - * Check if the given identifier is 32 byte valid SS58 string - * @param {string} identifier - The identifier to check. - * @return {void} Throws exception if invalid identifier - */ -export function validateDockDIDSS58Identifier(identifier) { - // base58-check regex - const regex = new RegExp(/^[5KL][1-9A-HJ-NP-Za-km-z]{47}$/); - const matches = regex.exec(identifier); - if (!matches) { - throw new Error('The identifier must be 32 bytes and valid SS58 string'); - } -} - -/** - * Returns a `DidKey` as expected by the Substrate node - * @param {PublicKey} publicKey - The public key for the DID. The Keyring is intentionally avoided here as it may not be - * accessible always, like in case of hardware wallet - * @param {VerificationRelationship} verRel - * @returns {object} - The object has structure and keys with same names as expected by the Substrate node - */ -export function createDidKey(publicKey, verRel) { - return { - publicKey: publicKey.toJSON(), - verRels: verRel.value, - }; -} - -/** - * - * @param {string|DockDidOrDidMethodKey} did - DID as string or an object - * @param {number} keyId - - * @param rawSig - * @param {Signature} sig - * @returns {object} - */ -export function createDidSig(did, { keyId }, rawSig) { - const sig = rawSig.toJSON(); - - if (did.isDid) { - return { - DidSignature: { - did: did.asDid, - keyId, - sig, - }, - }; - } else if (did.isDidMethodKey) { - return { - DidMethodKeySignature: { - didMethodKey: did.asDidMethodKey, - sig, - }, - }; - } else { - throw new Error( - `Incorrect DID passed: \`${did}\`, expected instance of either \`DockDid\` or \`DidMethodKey\``, - ); - } -} diff --git a/packages/credential-sdk/src/types/generic/typed-enum.js b/packages/credential-sdk/src/types/generic/typed-enum.js index 05bd0c508..742993e1c 100644 --- a/packages/credential-sdk/src/types/generic/typed-enum.js +++ b/packages/credential-sdk/src/types/generic/typed-enum.js @@ -87,6 +87,22 @@ class TypedEnum extends withBase(class EnumBase {}) { }); const that = this; + + // TODO: revisit this + if (Class.Variants) { + const klassFrom = Class.from; + + for (const Variant of Class.Variants) { + Variant.from = function from(value) { + if (value instanceof that) { + return klassFrom.call(this, value[asIdentifier]); + } else { + return klassFrom.call(this, value); + } + }; + } + } + const klassFrom = Class.from; Class.from = function from(value) { if (value instanceof that) { diff --git a/packages/credential-sdk/src/types/generic/typed-number.js b/packages/credential-sdk/src/types/generic/typed-number.js index dd58358e2..aac792b1c 100644 --- a/packages/credential-sdk/src/types/generic/typed-number.js +++ b/packages/credential-sdk/src/types/generic/typed-number.js @@ -17,6 +17,18 @@ class TypedNumber extends withBase(class NumberBase {}) { this.value = num; } + inc() { + return new this.constructor(++this.value); + } + + dec() { + return new this.constructor(--this.value); + } + + toJSON() { + return this.value; + } + static from(value) { if (value instanceof this) { return value; @@ -27,10 +39,6 @@ class TypedNumber extends withBase(class NumberBase {}) { } } - toJSON() { - return this.value; - } - static fromJSON(value) { return new this(value); } diff --git a/packages/credential-sdk/src/types/generic/typed-uuid.js b/packages/credential-sdk/src/types/generic/typed-uuid.js index 18530d786..19531e03f 100644 --- a/packages/credential-sdk/src/types/generic/typed-uuid.js +++ b/packages/credential-sdk/src/types/generic/typed-uuid.js @@ -1,9 +1,10 @@ import { parse, validate, stringify, v4, } from 'uuid'; -import TypedString from './typed-string'; +import TypedBytes from './typed-bytes'; +import { normalizeOrConvertStringToU8a } from '../../utils'; -export default class TypedUUID extends TypedString { +export default class TypedUUID extends TypedBytes { constructor(id) { super(validate(id) ? parse(id) : id); @@ -20,6 +21,26 @@ export default class TypedUUID extends TypedString { return this.value; } + static fromBytesAdapt(bytesOrString) { + let bytes = normalizeOrConvertStringToU8a(bytesOrString); + if (bytes.length < 16) { + const newBytes = new Uint8Array(16); + newBytes.set(bytes); + newBytes.fill(0, bytes.length); + + bytes = newBytes; + } else if (bytes.length > 16) { + bytes = bytes.slice(0, 16); + } + + // eslint-disable-next-line no-bitwise + bytes[6] = (bytes[6] & 0x0f) | 0x40; + // eslint-disable-next-line no-bitwise + bytes[8] = (bytes[8] & 0x3f) | 0x80; + + return new this(bytes); + } + static random() { return new this(v4()); } diff --git a/packages/credential-sdk/src/types/generic/with-eq.js b/packages/credential-sdk/src/types/generic/with-eq.js index a9e1414e6..ca9db2137 100644 --- a/packages/credential-sdk/src/types/generic/with-eq.js +++ b/packages/credential-sdk/src/types/generic/with-eq.js @@ -17,7 +17,9 @@ export default function withEq(klass) { return false; } else if (Object.is(this, other)) { return true; - } else if (!isEqualToOrPrototypeOf(klass, other.constructor)) { + } else if ( + !isEqualToOrPrototypeOf(this.constructor, other.constructor) + ) { let compareWith; try { compareWith = this.constructor.from(other); diff --git a/packages/credential-sdk/src/types/generic/with-qualifier.js b/packages/credential-sdk/src/types/generic/with-qualifier.js index 4c81b37fd..b928a7db2 100644 --- a/packages/credential-sdk/src/types/generic/with-qualifier.js +++ b/packages/credential-sdk/src/types/generic/with-qualifier.js @@ -105,6 +105,19 @@ export default function withQualifier(klass, wrapper = false) { } else { return this.fromQualifiedString(String(value)); } + } else if ( + value?.constructor?.Qualifier != null && + !this.Qualifiers.find((qualifier) => + value.constructor.Qualifier.startsWith(qualifier) + ) + ) { + throw new Error( + `Value has a different qualifier: \`${ + value.constructor.Qualifier + }\` while expected one of \`${fmtIter(this.Qualifiers)}\` by \`${ + this.name + }\`` + ); } else { return from(value); } @@ -183,6 +196,13 @@ export default function withQualifier(klass, wrapper = false) { } else { return this.fromUnqualifiedString(String(value)); } + } else if ( + value?.constructor?.Qualifier != null && + !value.constructor.Qualifier.startsWith(this.Qualifier) + ) { + throw new Error( + `Value has a different qualifier: \`${value.constructor.Qualifier}\` while expected \`${this.Qualifier}\` by \`${this.name}\`` + ); } else { return from(value); } diff --git a/packages/credential-sdk/src/types/offchain-signatures/params/index.js b/packages/credential-sdk/src/types/offchain-signatures/params/index.js index 858657de8..72547aa07 100644 --- a/packages/credential-sdk/src/types/offchain-signatures/params/index.js +++ b/packages/credential-sdk/src/types/offchain-signatures/params/index.js @@ -29,6 +29,11 @@ export class PSParams extends OffchainSignatureParams { static Class = PSParamsValue; } +export class BBDT16Params extends OffchainSignatureParams { + static Type = 'bbdt16'; + + static Class = BBDT16Params; +} OffchainSignatureParams.bindVariants(BBSParams, BBSPlusParams, PSParams); diff --git a/packages/credential-sdk/src/types/offchain-signatures/params/value.js b/packages/credential-sdk/src/types/offchain-signatures/params/value.js index 6e2d24f53..c6fa6e073 100644 --- a/packages/credential-sdk/src/types/offchain-signatures/params/value.js +++ b/packages/credential-sdk/src/types/offchain-signatures/params/value.js @@ -21,3 +21,6 @@ export class BBSPlusParamsValue extends OffchainSignatureParamsValue { export class PSParamsValue extends OffchainSignatureParamsValue { static Type = 'ps'; } +export class BBDT16ParamsValue extends OffchainSignatureParamsValue { + static Type = 'bbdt16'; +} diff --git a/packages/credential-sdk/src/types/offchain-signatures/public-keys/index.js b/packages/credential-sdk/src/types/offchain-signatures/public-keys/index.js index 668ea7079..1028a7cc5 100644 --- a/packages/credential-sdk/src/types/offchain-signatures/public-keys/index.js +++ b/packages/credential-sdk/src/types/offchain-signatures/public-keys/index.js @@ -1,4 +1,4 @@ -import { TypedEnum, withProp } from '../../generic'; +import { TypedEnum, option, withProp } from '../../generic'; import { Bls12381BBS23DockVerKeyName, Bls12381BBSDockVerKeyName, @@ -8,11 +8,13 @@ import { BBSPublicKeyValue, BBSPlusPublicKeyValue, PSPublicKeyValue, + BBDT16PublicKeyValue, } from './value'; import { CheqdOffchainSignatureParamsRef, DockOffchainSignatureParamsRef, } from './ref'; +import { Bls12381BBDT16DockVerKeyName } from '../../../vc/crypto/constants'; export class OffchainSignaturePublicKey extends TypedEnum { get bytes() { @@ -52,6 +54,13 @@ export class PSPublicKey extends OffchainSignaturePublicKey { static VerKeyType = Bls12381PSDockVerKeyName; } +export class BBDT16PublicKey extends OffchainSignaturePublicKey { + static Class = BBDT16PublicKeyValue; + + static type = 'bbdt16'; + + static VerKeyType = Bls12381BBDT16DockVerKeyName; +} OffchainSignaturePublicKey.bindVariants( BBSPublicKey, @@ -62,13 +71,13 @@ OffchainSignaturePublicKey.bindVariants( export class DockOffchainSignaturePublicKey extends withProp( OffchainSignaturePublicKey, 'paramsRef', - DockOffchainSignatureParamsRef, + option(DockOffchainSignatureParamsRef), ) {} export class CheqdOffchainSignaturePublicKey extends withProp( OffchainSignaturePublicKey, 'paramsRef', - CheqdOffchainSignatureParamsRef, + option(CheqdOffchainSignatureParamsRef), ) {} export * from './value'; diff --git a/packages/credential-sdk/src/types/offchain-signatures/public-keys/value.js b/packages/credential-sdk/src/types/offchain-signatures/public-keys/value.js index 4e0010d33..d6743769f 100644 --- a/packages/credential-sdk/src/types/offchain-signatures/public-keys/value.js +++ b/packages/credential-sdk/src/types/offchain-signatures/public-keys/value.js @@ -6,7 +6,9 @@ import { Any, } from '../../generic'; import { CurveType, CurveTypeBls12381 } from '../curve-type'; -import { BBSParams, BBSPlusParams, PSParams } from '../params'; +import { + BBDT16Params, BBSParams, BBSPlusParams, PSParams, +} from '../params'; export class OffchainSignaturePublicKeyValue extends TypedStruct { static Classes = { @@ -36,3 +38,6 @@ export class BBSPlusPublicKeyValue extends OffchainSignaturePublicKeyValue { export class PSPublicKeyValue extends OffchainSignaturePublicKeyValue { static Params = PSParams; } +export class BBDT16PublicKeyValue extends OffchainSignaturePublicKeyValue { + static Params = BBDT16Params; +} diff --git a/packages/credential-sdk/src/utils/bytes.js b/packages/credential-sdk/src/utils/bytes.js index 2198fc5dc..b5d0dd261 100644 --- a/packages/credential-sdk/src/utils/bytes.js +++ b/packages/credential-sdk/src/utils/bytes.js @@ -1,5 +1,5 @@ import { applyToValue } from './interfaces'; -import { ensureBytes, ensureString, ensureUint8Array } from './type-helpers'; +import { ensureBytes, ensureString } from './type-helpers'; /** * Check if the given input is hexadecimal or not. Optionally checks for the byte size of the hex. Case-insensitive on hex chars @@ -153,7 +153,7 @@ export const normalizeOrConvertStringToU8a = (bytesOrString) => (typeof bytesOrS */ export const valueBytes = (value) => applyToValue( (inner) => Array.isArray(inner) || inner instanceof Uint8Array || 'bytes' in inner, - (inner) => ensureUint8Array(inner.bytes ?? u8aToU8a(inner)), + (inner) => normalizeToU8a(inner.bytes ?? inner), value, ); diff --git a/packages/credential-sdk/src/utils/misc.js b/packages/credential-sdk/src/utils/misc.js index 6f719b7e1..2a372b2aa 100644 --- a/packages/credential-sdk/src/utils/misc.js +++ b/packages/credential-sdk/src/utils/misc.js @@ -18,10 +18,6 @@ export function getBytesForStateChange(api, stateChange) { return api.createType('StateChange', stateChange).toU8a(); } -export async function getDidNonce() { - throw new Error('Unimplemented'); -} - export function getStateChange(api, name, value) { const stateChange = {}; stateChange[name] = value; diff --git a/packages/credential-sdk/tests/__snapshots__/document.test.js.snap b/packages/credential-sdk/tests/__snapshots__/document.test.js.snap index 935618de3..e3e8252fc 100644 --- a/packages/credential-sdk/tests/__snapshots__/document.test.js.snap +++ b/packages/credential-sdk/tests/__snapshots__/document.test.js.snap @@ -20,6 +20,43 @@ exports[`\`DIDDocument\` \`DIDDocument.create\` works 1`] = ` `; exports[`\`DIDDocument\` \`DIDDocument.create\` works 2`] = ` +{ + "@context": [ + "https://www.w3.org/ns/did/v1", + ], + "alsoKnownAs": [], + "assertionMethod": [ + "did:cheqd:testnet:f1749383-d9dd-479f-82aa-e52fe8f59c54#keys-1", + ], + "authentication": [ + "did:cheqd:testnet:f1749383-d9dd-479f-82aa-e52fe8f59c54#keys-1", + ], + "capabilityDelegation": [], + "capabilityInvocation": [ + "did:cheqd:testnet:f1749383-d9dd-479f-82aa-e52fe8f59c54#keys-1", + ], + "controller": [ + "did:cheqd:testnet:f1749383-d9dd-479f-82aa-e52fe8f59c54", + ], + "https://rdf.dock.io/alpha/2021#attestsDocumentContents": null, + "id": "did:cheqd:testnet:f1749383-d9dd-479f-82aa-e52fe8f59c54", + "keyAgreement": [], + "service": [], + "verificationMethod": [ + { + "controller": "did:cheqd:testnet:f1749383-d9dd-479f-82aa-e52fe8f59c54", + "id": "did:cheqd:testnet:f1749383-d9dd-479f-82aa-e52fe8f59c54#keys-1", + "publicKeyBase58": "Bt5Mafvpcqou1pBF8SnW1BJMvpkSzZnPEgWunTUFwHZm", + "publicKeyBase64": null, + "publicKeyHex": null, + "publicKeyJwk": null, + "type": "Ed25519VerificationKey2018", + }, + ], +} +`; + +exports[`\`DIDDocument\` \`DIDDocument.create\` works 3`] = ` { "@context": [ "https://www.w3.org/ns/did/v1", @@ -46,6 +83,7 @@ exports[`\`DIDDocument\` \`DIDDocument.create\` works 2`] = ` ], "controller": [ "did:key:z6MktvqCyLxTsXUH1tUZncNdVeEZ7hNh7npPRbUU27GTrYb8", + "did:dock:5DEHasvC9G3eVF3qCsN2VQvEbHYdQtsv74ozZ1ngQQj39Luk", ], "https://rdf.dock.io/alpha/2021#attestsDocumentContents": null, "id": "did:dock:5DEHasvC9G3eVF3qCsN2VQvEbHYdQtsv74ozZ1ngQQj39Luk", @@ -130,7 +168,7 @@ exports[`\`DIDDocument\` \`DIDDocument.create\` works 2`] = ` } `; -exports[`\`DIDDocument\` \`DIDDocument.create\` works 3`] = ` +exports[`\`DIDDocument\` \`DIDDocument.create\` works 4`] = ` { "@context": [ "overriden", @@ -149,7 +187,7 @@ exports[`\`DIDDocument\` \`DIDDocument.create\` works 3`] = ` } `; -exports[`\`DIDDocument\` DIDDocument.from works 1`] = ` +exports[`\`DIDDocument\` \`DIDDocument.from\` works 1`] = ` { "@context": [ "test", diff --git a/packages/credential-sdk/tests/document.test.js b/packages/credential-sdk/tests/document.test.js index 5989fe094..0b25b05af 100644 --- a/packages/credential-sdk/tests/document.test.js +++ b/packages/credential-sdk/tests/document.test.js @@ -1,4 +1,8 @@ -import { VerificationRelationship, DidKey } from "../src/types/did"; +import { + VerificationRelationship, + DidKey, + CheqdTestnetDid, +} from "../src/types/did"; import { DIDDocument } from "../src/types/did/document"; import { PublicKeyEd25519 } from "../src/types"; @@ -22,7 +26,7 @@ describe("`DIDDocument`", () => { const CAP_INV = new VerificationRelationship().setCapabilityInvocation(); const KEY_AGR = new VerificationRelationship().setKeyAgreement(); - test(`DIDDocument.from works`, () => { + test(`\`DIDDocument.from\` works`, () => { const doc = { "@context": ["test"], id: "did:dock:5DEHasvC9G3eVF3qCsN2VQvEbHYdQtsv74ozZ1ngQQj39Luk", @@ -51,6 +55,14 @@ describe("`DIDDocument`", () => { ) ).toMatchSnapshot(); + const cheqdDid = CheqdTestnetDid.from( + "did:cheqd:testnet:f1749383-d9dd-479f-82aa-e52fe8f59c54" + ); + + expect( + DIDDocument.create(cheqdDid, [new DidKey(RANDOM_PKS[0])], [cheqdDid], []) + ).toMatchSnapshot(); + expect( DIDDocument.create( "did:dock:5DEHasvC9G3eVF3qCsN2VQvEbHYdQtsv74ozZ1ngQQj39Luk", @@ -64,7 +76,10 @@ describe("`DIDDocument`", () => { new DidKey(RANDOM_PKS[6], AUTH | ASSERT | CAP_INV), new DidKey(RANDOM_PKS[7], KEY_AGR), ], - ["did:key:z6MktvqCyLxTsXUH1tUZncNdVeEZ7hNh7npPRbUU27GTrYb8"], + [ + "did:key:z6MktvqCyLxTsXUH1tUZncNdVeEZ7hNh7npPRbUU27GTrYb8", + "did:dock:5DEHasvC9G3eVF3qCsN2VQvEbHYdQtsv74ozZ1ngQQj39Luk", + ], [] ) ).toMatchSnapshot(); diff --git a/packages/credential-sdk/tests/schema.test.js b/packages/credential-sdk/tests/schema.test.js index d33f04b90..e0b83314d 100644 --- a/packages/credential-sdk/tests/schema.test.js +++ b/packages/credential-sdk/tests/schema.test.js @@ -2,7 +2,7 @@ import VerifiableCredential from "../src/vc/verifiable-credential"; import { Schema } from "../src/modules/schema"; -import { BlobId } from "../src/types/blob"; +import { DockBlobId } from "../src/types/blob"; import { expandJSONLD } from "../src/vc/helpers"; @@ -39,18 +39,11 @@ describe("VerifiableCredential Tests", () => { describe("Basic Schema Tests", () => { let schema; + beforeAll(async () => { - schema = new Schema(); + schema = new Schema(DockBlobId.random()); }, 10000); - test("accepts the id optionally and generates id of correct size when id is not given", () => { - const schemaNoID = new Schema(); - const encodedIDByteSize = 48; - expect(String(schemaNoID.id).length).toBe( - encodedIDByteSize + BlobId.Qualifier.length - ); - }); - test("setJSONSchema will only accept valid JSON schema and set the schema key of the object.", async () => { await expect( schema.setJSONSchema({ @@ -88,7 +81,7 @@ describe("Basic Schema Tests", () => { }); describe("Validate Credential Schema utility", () => { - const schema = new Schema(); + const schema = new Schema(DockBlobId.random()); schema.setJSONSchema(exampleSchema); let expandedCredential; diff --git a/packages/credential-sdk/tests/serialize.test.js b/packages/credential-sdk/tests/serialize.test.js index 119ebe45c..8896db987 100644 --- a/packages/credential-sdk/tests/serialize.test.js +++ b/packages/credential-sdk/tests/serialize.test.js @@ -3,6 +3,7 @@ import VerifiablePresentation from "../src/vc/verifiable-presentation"; import { Schema } from "../src/modules/schema"; import exampleCredential from "./utils/example-credential"; +import { DockBlobId } from "../src/types"; describe("Serialization", () => { test("VerifiableCredential fromJSON should fail if no type is provided", () => { @@ -62,7 +63,7 @@ describe("Serialization", () => { }); test("Schema from/to JSON serialization", async () => { - const schema = new Schema(); + const schema = new Schema(DockBlobId.random()); await schema.setJSONSchema({ $schema: "http://json-schema.org/draft-07/schema#", description: "Dock Schema Example", diff --git a/packages/dock-blockchain-api/CHANGELOG.md b/packages/dock-blockchain-api/CHANGELOG.md index 9874d03dc..b91d5747f 100644 --- a/packages/dock-blockchain-api/CHANGELOG.md +++ b/packages/dock-blockchain-api/CHANGELOG.md @@ -1,5 +1,19 @@ # @docknetwork/dock-blockchain-api +## 0.2.0 + +### Minor Changes + +- - Updated `DIDDocument`: modified the rules for storing keys in the verificationMethod and added the required validations. + - Developed generic tests for modules to ensure they cover the same use cases, regardless of the specific implementation. + - Implemented the `CheqdBlobModule`. + - Misc tweaks. + +### Patch Changes + +- Updated dependencies + - @docknetwork/credential-sdk@0.3.0 + ## 0.1.2 ### Patch Changes diff --git a/packages/dock-blockchain-api/package.json b/packages/dock-blockchain-api/package.json index 3f32cccf6..f71ebcb7f 100644 --- a/packages/dock-blockchain-api/package.json +++ b/packages/dock-blockchain-api/package.json @@ -1,6 +1,6 @@ { "name": "@docknetwork/dock-blockchain-api", - "version": "0.1.2", + "version": "0.2.0", "license": "MIT", "main": "./dist/esm/index.js", "type": "module", @@ -84,7 +84,7 @@ "@polkadot/api": "10.12.4" }, "dependencies": { - "@docknetwork/credential-sdk": "0.2.0", + "@docknetwork/credential-sdk": "0.3.0", "@docknetwork/node-types": "^0.17.0", "@juanelas/base64": "^1.0.5", "@polkadot/api": "10.12.4", diff --git a/packages/dock-blockchain-api/src/api/index.js b/packages/dock-blockchain-api/src/api/index.js index c73ae083f..81745bc01 100644 --- a/packages/dock-blockchain-api/src/api/index.js +++ b/packages/dock-blockchain-api/src/api/index.js @@ -156,7 +156,7 @@ export default class DockAPI extends ApiProvider { } isInitialized() { - return !!this.api; + return this.api != null; } /** TODO: Should probably use set/get and rename account to _account @@ -183,9 +183,9 @@ export default class DockAPI extends ApiProvider { */ async signExtrinsic(extrinsic, params = {}) { if (this.customSignTx) { - return this.customSignTx(extrinsic, params, this); + return await this.customSignTx(extrinsic, params, this); } - return extrinsic.signAsync(this.getAccount(), params); + return await extrinsic.signAsync(this.getAccount(), params); } /** @@ -233,10 +233,25 @@ export default class DockAPI extends ApiProvider { ); } + /** + * Converts supplied payload to bytes representing state change with the provided name. + * @param {string} name + * @param {object} payload + * @returns {Uint8Array} + */ async stateChangeBytes(name, payload) { return this.api.createType('StateChange', { [name]: payload }).toU8a(); } + /** + * Batches supplied transactions into a single call. + * @param {Array} + * @returns {SubmittableExtrinsics} + */ + async batchAll(transactions) { + return this.api.tx.utility.batchAll(transactions); + } + /** * Helper function to send without retrying a transaction that has already been signed. * @param {DockAPI} dock diff --git a/packages/dock-blockchain-modules/CHANGELOG.md b/packages/dock-blockchain-modules/CHANGELOG.md index e23ee1e96..7c8995f3b 100644 --- a/packages/dock-blockchain-modules/CHANGELOG.md +++ b/packages/dock-blockchain-modules/CHANGELOG.md @@ -1,5 +1,19 @@ # @docknetwork/dock-blockchain-modules +## 0.3.0 + +### Minor Changes + +- - Updated `DIDDocument`: modified the rules for storing keys in the verificationMethod and added the required validations. + - Developed generic tests for modules to ensure they cover the same use cases, regardless of the specific implementation. + - Implemented the `CheqdBlobModule`. + - Misc tweaks. + +### Patch Changes + +- Updated dependencies + - @docknetwork/credential-sdk@0.3.0 + ## 0.2.0 ### Minor Changes diff --git a/packages/dock-blockchain-modules/package.json b/packages/dock-blockchain-modules/package.json index fbb4f9dd2..33bdf12ff 100644 --- a/packages/dock-blockchain-modules/package.json +++ b/packages/dock-blockchain-modules/package.json @@ -1,6 +1,6 @@ { "name": "@docknetwork/dock-blockchain-modules", - "version": "0.2.0", + "version": "0.3.0", "license": "MIT", "type": "module", "main": "./dist/esm/index.js", @@ -28,7 +28,7 @@ "node": ">=18.0.0" }, "dependencies": { - "@docknetwork/credential-sdk": "0.2.0" + "@docknetwork/credential-sdk": "0.3.0" }, "devDependencies": { "@babel/cli": "^7.24.1", @@ -37,7 +37,7 @@ "@babel/plugin-syntax-import-attributes": "^7.25.6", "@babel/plugin-transform-modules-commonjs": "^7.24.1", "@babel/preset-env": "^7.24.3", - "@docknetwork/dock-blockchain-api": "0.1.2", + "@docknetwork/dock-blockchain-api": "0.2.0", "@rollup/plugin-alias": "^4.0.2", "@rollup/plugin-babel": "^6.0.4", "@rollup/plugin-commonjs": "^24.0.0", diff --git a/packages/dock-blockchain-modules/src/attest/module.js b/packages/dock-blockchain-modules/src/attest/module.js index 6c5f257cb..3578d8b77 100644 --- a/packages/dock-blockchain-modules/src/attest/module.js +++ b/packages/dock-blockchain-modules/src/attest/module.js @@ -7,20 +7,18 @@ export default class DockAttestModule extends injectDock(AbstractAttestModule) { /** * Fetches the DIDs attestations IRI from the chain - * @param {string} hexId - DID in hex format - * @return {Promise} The DID's attestation, if any + * @param {*} did + * @return {Promise} The DID's attestation, if any */ async getAttests(did) { - return (await this.dockOnly.attest(did)).iri?.value; + return (await this.dockOnly.attest(did)).iri; } /** * Creates an attestation claim on chain for a specific DID - * @param priority * @param iri * @param did * @param signingKeyRef - * @param params */ async setClaimTx(iri, targetDid, didKeypair) { ensureTargetKeypair(targetDid, didKeypair); diff --git a/packages/dock-blockchain-modules/src/blob/actions.js b/packages/dock-blockchain-modules/src/blob/actions.js index 117170b52..b467445eb 100644 --- a/packages/dock-blockchain-modules/src/blob/actions.js +++ b/packages/dock-blockchain-modules/src/blob/actions.js @@ -2,11 +2,11 @@ import { TypedStruct, TypedNumber, } from '@docknetwork/credential-sdk/types/generic'; -import { BlobWithDockId } from './types'; +import { DockBlobWithId } from '@docknetwork/credential-sdk/types'; export class AddBlob extends TypedStruct { static Classes = { - blob: BlobWithDockId, + blob: DockBlobWithId, nonce: class Nonce extends TypedNumber {}, }; } diff --git a/packages/dock-blockchain-modules/src/blob/types.js b/packages/dock-blockchain-modules/src/blob/types.js index 35aca36e4..3602312f2 100644 --- a/packages/dock-blockchain-modules/src/blob/types.js +++ b/packages/dock-blockchain-modules/src/blob/types.js @@ -1,16 +1,6 @@ -import { - DockDidOrDidMethodKey, - Blob, - BlobWithId, -} from '@docknetwork/credential-sdk/types'; -import { DockBlobId } from '@docknetwork/credential-sdk/types/blob/blob-id'; -import { - TypedTuple, - withProp, -} from '@docknetwork/credential-sdk/types/generic'; +import { DockDidOrDidMethodKey, Blob } from '@docknetwork/credential-sdk/types'; +import { TypedTuple } from '@docknetwork/credential-sdk/types/generic'; export class OwnerWithBlob extends TypedTuple { static Classes = [DockDidOrDidMethodKey, Blob]; } - -export class BlobWithDockId extends withProp(BlobWithId, 'id', DockBlobId) {} diff --git a/packages/dock-blockchain-modules/src/common/builders.js b/packages/dock-blockchain-modules/src/common/builders.js index 639c515f0..2f62e8ba6 100644 --- a/packages/dock-blockchain-modules/src/common/builders.js +++ b/packages/dock-blockchain-modules/src/common/builders.js @@ -29,10 +29,17 @@ export const createDIDMethodWithPolicyTx = (fnName) => { const [didKeypair] = args.slice(root.payload[fnName].length - 2); const { did: signer } = ensureInstanceOf(didKeypair, DidKeypair); - // eslint-disable-next-line no-param-reassign - args[root.payload[fnName].length - 1] - ??= 1 + (await root.apiProvider.didNonce(didKeypair.did)); - const { data, nonce } = root.payload[fnName].apply(this.root, args); + + const handlePayload = (maybeDidNonce) => { + // eslint-disable-next-line no-param-reassign + args[root.payload[fnName].length - 1] ??= maybeDidNonce.inc(); + + return root.payload[fnName].apply(this.root, args); + }; + + const { data, nonce } = args[root.payload[fnName].length - 1] == null + ? await root.apiProvider.withDidNonce(didKeypair.did, handlePayload) + : handlePayload(); const sig = await DockDidOrDidMethodKey.from(signer).signStateChange( root.apiProvider, @@ -59,16 +66,25 @@ export const createDIDMethodTx = (fnName) => { const [didKeypair] = args.slice(root.payload[fnName].length - 2); ensureInstanceOf(didKeypair, DidKeypair); - // eslint-disable-next-line no-param-reassign - args[root.payload[fnName].length - 1] - ??= 1 + (await root.apiProvider.didNonce(didKeypair.did)); + + // eslint-disable-next-line + const handlePayload = (maybeDidNonce) => { + // eslint-disable-next-line no-param-reassign + args[root.payload[fnName].length - 1] ??= maybeDidNonce.inc(); + + return root.payload[fnName].apply(this.root, args); + }; + + const payload = args[root.payload[fnName].length - 1] == null + ? await root.apiProvider.withDidNonce(didKeypair.did, handlePayload) + : handlePayload(); return await DockDidOrDidMethodKey.from(didKeypair.did).changeState( root.apiProvider, root.rawTx[fnName], root.constructor.MethodNameOverrides?.[fnName] ?? fnNameToMethodName(fnName), - root.payload[fnName].apply(this.root, args), + payload, didKeypair, ); }, 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 f72090d23..af769b1c9 100644 --- a/packages/dock-blockchain-modules/src/common/dock-api-provider.js +++ b/packages/dock-blockchain-modules/src/common/dock-api-provider.js @@ -36,8 +36,16 @@ class DockApiProvider extends ApiProvider { ); } - async didNonce(did) { - return await new DockDIDModuleInternal(this).nonce(did); + async withDidNonce(did, fn) { + if (typeof this.dock.withDidNonce === 'function') { + return await this.dock.withDidNonce(did, fn); + } else { + return fn(await new DockDIDModuleInternal(this).nonce(did)); + } + } + + async batchAll(transactions) { + return await this.dock.batchAll(transactions); } } diff --git a/packages/dock-blockchain-modules/src/common/with-params-and-public-keys.js b/packages/dock-blockchain-modules/src/common/with-params-and-public-keys.js index 839237ce6..0a7fbd4f0 100644 --- a/packages/dock-blockchain-modules/src/common/with-params-and-public-keys.js +++ b/packages/dock-blockchain-modules/src/common/with-params-and-public-keys.js @@ -104,11 +104,10 @@ export default function withParamsAndPublicKeys(klass) { * Add a public key * @param publicKey - public key to add. * @param targetDid - The DID to which key is being added - * @param signerDid - The DID that is adding the key by signing the payload because it controls `targetDid` - * @param signingKeyRef - Signer's signingKeyRef + * @param didKeypair - Signer's didKeypair * @returns {Promise<*>} */ - async addPublicKeyTx(id, publicKey, targetDid, didKeypair) { + async addPublicKeyTx(_id, publicKey, targetDid, didKeypair) { return await this.dockOnly.tx.addPublicKey( publicKey, targetDid, @@ -120,8 +119,7 @@ export default function withParamsAndPublicKeys(klass) { * Remove public key * @param removeKeyId - Identifier of the public key to be removed. * @param targetDid - The DID from which key is being removed - * @param signerDid - The DID that is removing the key by signing the payload because it controls `targetDid` - * @param signingKeyRef - Signer's signing key reference + * @param didKeypair - Signer's signing key reference * @returns {Promise<*>} */ async removePublicKeyTx(id, targetDid, didKeypair) { diff --git a/packages/dock-blockchain-modules/src/did/module.js b/packages/dock-blockchain-modules/src/did/module.js index 1c6465cdf..2f5711383 100644 --- a/packages/dock-blockchain-modules/src/did/module.js +++ b/packages/dock-blockchain-modules/src/did/module.js @@ -70,6 +70,14 @@ export default class DockDIDModule extends injectDock(AbstractDIDModule) { } = nextDocument.didKeys().diff(currentDocument.didKeys()); const { added: newControllers, removed: removedControllers } = nextDocument.controller.diff(currentDocument.controller); const { added: newServices, removed: removedServices } = nextDocument.service.diff(currentDocument.service); + const newOnChainKeys = [...newMethods].filter( + ([_, key]) => !key.isOffchain(), + ); + const newOffchainKeys = [...newMethods].filter(([_, key]) => key.isOffchain()); + const removedOnChainKeys = [...removedMethods].filter( + ([_, key]) => !key.isOffchain(), + ); + const removedOffchainKeys = [...removedMethods].filter(([_, key]) => key.isOffchain()); if (modified.size) { throw new Error("Can't have modified verificationMethods"); @@ -88,12 +96,11 @@ export default class DockDIDModule extends injectDock(AbstractDIDModule) { (method) => !method.did.eq(didDocument.id), ) ) { - throw new Error('Can change controller keys'); + throw new Error("Can't change controller keys"); } - let nonce = +(await this.dockOnly.nonce(signerDid)); - - const { id: did, '@context': context } = nextDocument; + const { id: docId, '@context': context } = nextDocument; + const did = DockDidOrDidMethodKey.from(docId); if (context.length !== 1 || context[0].value !== CONTEXT_URI) { throw new Error( @@ -103,49 +110,65 @@ export default class DockDIDModule extends injectDock(AbstractDIDModule) { ); } - const txs = await Promise.all( - [ - newMethods.size - && this.dockOnly.tx.addKeys( - [...newMethods.values()], - did, - didKeypair, - ++nonce, - ), + const txs = ( + await this.dockOnly.apiProvider.withDidNonce(signerDid, (nonce) => Promise.all([ + newOnChainKeys.length + && this.dockOnly.tx.addKeys( + [...newOnChainKeys].map(([_, key]) => key), + did, + didKeypair, + nonce.inc(), + ), newControllers.length - && this.dockOnly.tx.addControllers( - newControllers, - did, - didKeypair, - ++nonce, - ), + && this.dockOnly.tx.addControllers( + newControllers, + did, + didKeypair, + nonce.inc(), + ), + ...newOffchainKeys.map(([_, key]) => this.offchainSignatures.dockOnly.tx.addPublicKey( + key.publicKey, + did, + didKeypair, + nonce.inc(), + )), ...[...newServices].map(({ id, type, serviceEndpoint }) => this.dockOnly.tx.addServiceEndpoint( id, type, serviceEndpoint, did, didKeypair, - ++nonce, + nonce.inc(), + )), + ...[...removedServices].map(({ id }) => this.dockOnly.tx.removeServiceEndpoint(id, didKeypair, nonce.inc())), + ...removedOffchainKeys.map(([{ index }]) => this.offchainSignatures.dockOnly.tx.removePublicKey( + index, + did, + didKeypair, + nonce.inc(), )), - ...[...removedServices].map(({ id }) => this.dockOnly.tx.removeServiceEndpoint(id, did, didKeypair, ++nonce)), removedControllers.length - && this.dockOnly.tx.removeControllers( - removedControllers, - did, - didKeypair, - ++nonce, - ), - removedMethods.size - && this.dockOnly.tx.removeKeys( - [...removedMethods.keys()].map((method) => method.index), - did, - didKeypair, - ++nonce, - ), - ].filter(Boolean), - ); + && this.dockOnly.tx.removeControllers( + removedControllers, + did, + didKeypair, + nonce.inc(), + ), + removedOnChainKeys.length + && this.dockOnly.tx.removeKeys( + [...removedOnChainKeys].map(([{ index }]) => index), + did, + didKeypair, + nonce.inc(), + ), + ])) + ).filter(Boolean); + + if (!txs.length) { + throw new Error(`No changes for ${did}`); + } - return await this.dockOnly.apiProvider.api.tx.utility.batchAll(txs); + return await this.dockOnly.apiProvider.batchAll(txs); } async removeDocumentTx(did, didKeypair) { diff --git a/packages/dock-blockchain-modules/src/trust-registry/internal.js b/packages/dock-blockchain-modules/src/trust-registry/internal.js index 368def392..20116c0d6 100644 --- a/packages/dock-blockchain-modules/src/trust-registry/internal.js +++ b/packages/dock-blockchain-modules/src/trust-registry/internal.js @@ -634,9 +634,11 @@ export default class DockInternalTrustRegistryModule extends createInternalDockM * @returns {Promise} */ async getActorDidAndNonce(actorDid, nonce) { - const hexDID = DockDidOrDidMethodKey.from(actorDid); - const lastNonce = nonce ?? 1 + (await this.apiProvider.didNonce(hexDID)); - return [hexDID, lastNonce]; + const actor = DockDidOrDidMethodKey.from(actorDid); + const lastNonce = nonce + ?? (await this.apiProvider.withDidNonce(actor, (didNonce) => didNonce.inc())); + + return [actor, lastNonce]; } /** diff --git a/packages/dock-blockchain-modules/src/trust-registry/module.js b/packages/dock-blockchain-modules/src/trust-registry/module.js index 27aba269a..70aba58df 100644 --- a/packages/dock-blockchain-modules/src/trust-registry/module.js +++ b/packages/dock-blockchain-modules/src/trust-registry/module.js @@ -43,28 +43,30 @@ export default class DockTrustRegistryModule extends injectDock( } async createRegistryTx(id, info, schemas, didKeypair) { - const nonce = await this.dockOnly.apiProvider.didNonce(didKeypair.did); - const init = await this.dockOnly.initOrUpdateTx( - info.convener, - id, - info.name, - info.govFramework, - didKeypair, - 1 + nonce, - ); - - const setSchemas = await this.dockOnly.setSchemasMetadataTx( - didKeypair.did, - id, - { Set: schemas }, - didKeypair, - 2 + nonce, + return await this.dockOnly.apiProvider.batchAll( + await Promise.all( + await this.dockOnly.apiProvider.withDidNonce( + didKeypair.did, + (nonce) => [ + this.dockOnly.initOrUpdateTx( + info.convener, + id, + info.name, + info.govFramework, + didKeypair, + nonce.inc(), + ), + this.dockOnly.setSchemasMetadataTx( + didKeypair.did, + id, + { Set: schemas }, + didKeypair, + nonce.inc(), + ), + ], + ), + ), ); - - return await this.dockOnly.apiProvider.api.tx.utility.batchAll([ - init, - setSchemas, - ]); } async updateRegistryTx(id, info, schemas, didKeypair) { diff --git a/packages/dock-blockchain-modules/tests/integration/did/did-basic.test.js b/packages/dock-blockchain-modules/tests/integration/did/did-basic.test.js index b77872cd6..4c8621d6b 100644 --- a/packages/dock-blockchain-modules/tests/integration/did/did-basic.test.js +++ b/packages/dock-blockchain-modules/tests/integration/did/did-basic.test.js @@ -59,18 +59,15 @@ describe("Basic DID tests", () => { const did = DockDid.random(); const pair = new Ed25519Keypair(seed); - const publicKey = pair.publicKey(); - - const verRels = new VerificationRelationship(); - const didKey = new DidKey(publicKey, verRels); + const didPair = new DidKeypair([did, 1], pair); - const doc = DIDDocument.create(did, [didKey], [did]); + const doc = DIDDocument.create(did, [didPair.didKey()], [did]); await modules.did.createDocument(doc); expect((await modules.did.getDocument(did)).toJSON()).toEqual(doc.toJSON()); const pair2 = Ed25519Keypair.random(); - const didKey2 = new DidKey(pair2.publicKey(), verRels); + const didPair2 = new DidKeypair([did, 2], pair2); const service1 = new ServiceEndpoint("LinkedDomains", [ "ServiceEndpoint#1", @@ -78,10 +75,10 @@ describe("Basic DID tests", () => { doc .addServiceEndpoint([did, "service1"], service1) - .addKey([did, 2], didKey2) + .addKey([did, 2], didPair2.didKey()) .removeKey([did, 1]); - await modules.did.updateDocument(doc, new DidKeypair([did, 1], pair)); + await modules.did.updateDocument(doc, didPair); expect((await modules.did.getDocument(did)).toJSON()).toEqual(doc.toJSON()); @@ -92,7 +89,7 @@ describe("Basic DID tests", () => { doc .removeServiceEndpoint("service1") .addServiceEndpoint([did, "service2"], service2); - await modules.did.updateDocument(doc, new DidKeypair([did, 2], pair2)); + await modules.did.updateDocument(doc, didPair2); expect((await modules.did.getDocument(did)).toJSON()).toEqual(doc.toJSON()); }); @@ -170,7 +167,7 @@ describe("Basic DID tests", () => { await modules.attest.setClaim(iri, dockDid, pair); const att = await modules.attest.getAttests(dockDid); - expect(att).toEqual(iri); + expect(att.value).toEqual(iri); // Get document to verify the claim is there const didDocument = await modules.did.getDocument(dockDid); diff --git a/packages/dock-blockchain-modules/tests/integration/did/did-key.test.js b/packages/dock-blockchain-modules/tests/integration/did/did-key.test.js index 0929f8d88..bf258c34e 100644 --- a/packages/dock-blockchain-modules/tests/integration/did/did-key.test.js +++ b/packages/dock-blockchain-modules/tests/integration/did/did-key.test.js @@ -72,7 +72,7 @@ describe("Basic DID tests", () => { ); const att1 = await modules.attest.getAttests(testDidMethodKey1); - expect(att1).toEqual(iri); + expect(att1.value).toEqual(iri); await modules.attest.setClaim( iri, @@ -81,7 +81,7 @@ describe("Basic DID tests", () => { ); const att2 = await modules.attest.getAttests(testDidMethodKey2); - expect(att2).toEqual(iri); + expect(att2.value).toEqual(iri); }, 30000); test("Conversion works properly (including SS58 format)", () => { diff --git a/packages/dock-blockchain-modules/tests/integration/modules/attest-module.test.js b/packages/dock-blockchain-modules/tests/integration/modules/attest-module.test.js new file mode 100644 index 000000000..85b908695 --- /dev/null +++ b/packages/dock-blockchain-modules/tests/integration/modules/attest-module.test.js @@ -0,0 +1,32 @@ +import { DockAPI } from "@docknetwork/dock-blockchain-api"; +import { DockDid } from "@docknetwork/credential-sdk/types"; +import generateAttestModuleTests from "@docknetwork/credential-sdk/generate-tests/attest-module"; +import DockAttestModule from "../../../src/attest/module"; +import { DockDIDModule } from "../../../src"; +import { + FullNodeEndpoint, + TestAccountURI, + TestKeyringOpts, +} from "../../test-constants"; + +describe("AttestModule", () => { + const dock = new DockAPI(); + + beforeAll(async () => { + await dock.init({ + keyring: TestKeyringOpts, + address: FullNodeEndpoint, + }); + const account = dock.keyring.addFromUri(TestAccountURI); + dock.setAccount(account); + }); + + afterAll(async () => { + await dock.disconnect(); + }); + + generateAttestModuleTests( + { did: new DockDIDModule(dock), attest: new DockAttestModule(dock) }, + { DID: DockDid } + ); +}); diff --git a/packages/dock-blockchain-modules/tests/integration/modules/blob-module.test.js b/packages/dock-blockchain-modules/tests/integration/modules/blob-module.test.js new file mode 100644 index 000000000..db83c93fe --- /dev/null +++ b/packages/dock-blockchain-modules/tests/integration/modules/blob-module.test.js @@ -0,0 +1,33 @@ +import { DockAPI } from "@docknetwork/dock-blockchain-api"; +import { DockDid } from "@docknetwork/credential-sdk/types"; +import generateBlobModuleTests from "@docknetwork/credential-sdk/generate-tests/blob-module"; +import DockBlobModule from "../../../src/blob/module"; +import { DockDIDModule } from "../../../src"; +import { + FullNodeEndpoint, + TestAccountURI, + TestKeyringOpts, +} from "../../test-constants"; +import { DockBlobId } from "../../../../credential-sdk/src/types"; + +describe("BlobModule", () => { + const dock = new DockAPI(); + + beforeAll(async () => { + await dock.init({ + keyring: TestKeyringOpts, + address: FullNodeEndpoint, + }); + const account = dock.keyring.addFromUri(TestAccountURI); + dock.setAccount(account); + }); + + afterAll(async () => { + await dock.disconnect(); + }); + + generateBlobModuleTests( + { did: new DockDIDModule(dock), blob: new DockBlobModule(dock) }, + { DID: DockDid, BlobId: DockBlobId } + ); +}); diff --git a/packages/dock-blockchain-modules/tests/integration/modules/did-module.test.js b/packages/dock-blockchain-modules/tests/integration/modules/did-module.test.js new file mode 100644 index 000000000..4eb35f615 --- /dev/null +++ b/packages/dock-blockchain-modules/tests/integration/modules/did-module.test.js @@ -0,0 +1,34 @@ +import didModuleTests from "@docknetwork/credential-sdk/generate-tests/did-module"; +import { DockAPI } from "@docknetwork/dock-blockchain-api"; +import DockDIDModule from "../../../src/did/module"; +import { + FullNodeEndpoint, + TestAccountURI, + TestKeyringOpts, +} from "../../test-constants"; +import { DockDid } from "@docknetwork/credential-sdk/types"; + +describe("DIDModule", () => { + const dock = new DockAPI(); + + beforeAll(async () => { + await dock.init({ + keyring: TestKeyringOpts, + address: FullNodeEndpoint, + }); + const account = dock.keyring.addFromUri(TestAccountURI); + dock.setAccount(account); + }); + + afterAll(async () => { + await dock.disconnect(); + }); + + didModuleTests( + { did: new DockDIDModule(dock) }, + { DID: DockDid }, + (name) => + name !== "Creates `DIDDocument` containing BBS/BBSPlus/PS keys" && + name !== "Creates `DIDDocument` containing services" + ); +}); diff --git a/packages/dock-blockchain-modules/tests/integration/schema.test.js b/packages/dock-blockchain-modules/tests/integration/schema.test.js index fbac5ebf3..ff7e18568 100644 --- a/packages/dock-blockchain-modules/tests/integration/schema.test.js +++ b/packages/dock-blockchain-modules/tests/integration/schema.test.js @@ -136,7 +136,7 @@ describe("Schema Blob Module Integration", () => { }, 30000); test("Set and get schema", async () => { - const schema = new Schema(); + const schema = new Schema(DockBlobId.random()); await schema.setJSONSchema(exampleSchema); await modules.blob.new(schema.toBlob(), dockDID, pair); const schemaObj = await Schema.get(blobId, modules.blob); diff --git a/scripts/with_cheqd_docker_test_node b/scripts/with_cheqd_docker_test_node index bad8fa9bb..d94ed89f5 100755 --- a/scripts/with_cheqd_docker_test_node +++ b/scripts/with_cheqd_docker_test_node @@ -10,7 +10,11 @@ config="$(realpath $(dirname $0)/cheqd_config.toml)" docker pull --platform linux/amd64 ghcr.io/cheqd/cheqd-node # start a pos alice node -alice_container_id=$(docker run --platform linux/amd64 -d --rm -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) +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 +} try_with_node() { sleep 10; @@ -18,6 +22,8 @@ try_with_node() { $@ } +trap cleanup SIGINT + if try_with_node $@; then exit_code=$? else @@ -25,6 +31,6 @@ else fi # Kill nodes -docker kill $alice_container_id +cleanup exit $exit_code diff --git a/scripts/with_dock_docker_test_node b/scripts/with_dock_docker_test_node index de14ffba9..a8a938ddf 100755 --- a/scripts/with_dock_docker_test_node +++ b/scripts/with_dock_docker_test_node @@ -11,12 +11,16 @@ docker pull $CONFIG_DOCK_NODE_IMAGE # start a pos alice node alice_container_id=$( - docker run --rm -d --name alice \ + docker run --rm -d --name dock-dev \ -p 9944:9944 -p 9933:9933 -p 30333:30333 \ $CONFIG_DOCK_NODE_IMAGE \ --alice --rpc-external --ws-external ) +cleanup() { + docker kill $alice_container_id +} + try_with_node() { # Wait for nodes to start listening for RPC "$root_dir"/scripts/wait_for_node_rpc_http @@ -24,6 +28,8 @@ try_with_node() { $@ } +trap cleanup SIGINT + if try_with_node $@; then exit_code=$? else @@ -31,6 +37,6 @@ else fi # Kill nodes -docker kill $alice_container_id +cleanup exit $exit_code diff --git a/tutorials/src/tutorial_blobs_schemas.md b/tutorials/src/tutorial_blobs_schemas.md index 486a8b414..20c53d381 100644 --- a/tutorials/src/tutorial_blobs_schemas.md +++ b/tutorials/src/tutorial_blobs_schemas.md @@ -68,7 +68,7 @@ The first step to creating a Schema is to initialize it, we can do that using th accepts an (optional) `id` string as sole argument: ```javascript -const myNewSchema = new Schema(); +const myNewSchema = new Schema(DockBlobId.random()); ``` When an `id` isn't passed, a random `blobId` will be assigned as the schema's id. diff --git a/tutorials/src/tutorial_resolver.md b/tutorials/src/tutorial_resolver.md index d99fc0557..28fe2c7a4 100644 --- a/tutorials/src/tutorial_resolver.md +++ b/tutorials/src/tutorial_resolver.md @@ -26,10 +26,10 @@ This is how you resolve a Dock DID: ```js import { DockResolver } from "@docknetwork/credential-sdk/resolver"; -// Assuming the presence of Dock API object `dock` -const dockResolver = new DockResolver(dock); +// Assuming the presence of modules created using `CheqdCoreModules` or `DockCoreModules` from the API object. +const dockResolver = new DockResolver(modules); // Say you had a DID `did:dock:5D.....` -const didDocument = dockResolver.resolve("did:dock:5D....."); +const didDocument = await dockResolver.resolve("did:dock:5D....."); ``` ## Creating a resolver class for a different method @@ -78,7 +78,7 @@ class EtherResolver extends DIDResolver { const ethResolver = new EtherResolver(ethereumProviderConfig); // Say you had a DID `did:ethr:0x6f....` -const didDocument = ethResolver.resolve("did:ethr:0x6f...."); +const didDocument = await ethResolver.resolve("did:ethr:0x6f...."); ``` ## Universal resolver @@ -119,9 +119,9 @@ import { class MultiDIDResolver extends DIDResolver { static METHOD = WILDCARD; - constructor(dock) { + constructor(modules) { super([ - new DockDIDResolver(dock), + new DockDIDResolver(modules.did), new EtherResolver(ethereumProviderConfig), new UniversalResolver(universalResolverUrl), ]); @@ -131,8 +131,8 @@ 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. -const didDocumentDock = multiResolver.resolve("did:dock:5D...."); +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. -const didDocumentBtc = multiResolver.resolve("did:btcr:xk...."); +const didDocumentBtc = await multiResolver.resolve("did:btcr:xk...."); ``` diff --git a/tutorials/src/tutorial_revocation.md b/tutorials/src/tutorial_revocation.md index 6bd722778..5732b9367 100644 --- a/tutorials/src/tutorial_revocation.md +++ b/tutorials/src/tutorial_revocation.md @@ -1,6 +1,5 @@ # Revocation - This guide provides instructions for managing credential revocation using `StatusList2021Credential`. ## Prerequisites @@ -17,7 +16,7 @@ This guide provides instructions for managing credential revocation using `Statu Create a unique identifier for tracking revocation status. ```javascript - import { DockStatusListCredentialId } from '@docknetwork/credential-sdk/types'; + import { DockStatusListCredentialId } from "@docknetwork/credential-sdk/types"; const statusListCredentialId = DockStatusListCredentialId.random(); ``` @@ -64,13 +63,13 @@ This guide provides instructions for managing credential revocation using `Statu Sign and issue the credential with the added status list entry. ```javascript - import { addStatusList21EntryToCredential } from '@docknetwork/credential-sdk/vc'; + import { addStatusList21EntryToCredential } from "@docknetwork/credential-sdk/vc"; const credential = await issueCredential( issuerKey, unsignedCred, void 0, - defaultDocumentLoader(resolver), + defaultDocumentLoader(resolver) ); ``` @@ -80,7 +79,10 @@ This guide provides instructions for managing credential revocation using `Statu Retrieve the existing status list credential and update it to revoke the issued credential by its index. ```javascript - const fetchedCred = await modules.statusListCredential.getStatusListCredential(statusListCredentialId); + const fetchedCred = + await modules.statusListCredential.getStatusListCredential( + statusListCredentialId + ); await fetchedCred.update(issuerKey, { revokeIndices: [statusListCredentialIndex], // Index of the credential to revoke }); @@ -89,7 +91,7 @@ This guide provides instructions for managing credential revocation using `Statu statusListCredentialId, fetchedCred, issuerDID, - issuerKeyPair, + issuerKeyPair ); ```