diff --git a/packages/core/package.json b/packages/core/package.json index 2117d99997..517a2a0d6a 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -25,6 +25,9 @@ "prepublishOnly": "pnpm run build" }, "dependencies": { + "@animo-id/mdoc": "0.3.0", + "@animo-id/pex": "4.1.1-alpha.0", + "@astronautlabs/jsonpath": "^1.1.2", "@digitalcredentials/jsonld": "^6.0.0", "@digitalcredentials/jsonld-signatures": "^9.4.0", "@digitalcredentials/vc": "^6.0.1", @@ -35,14 +38,12 @@ "@peculiar/asn1-schema": "^2.3.13", "@peculiar/asn1-x509": "^2.3.13", "@peculiar/x509": "^1.12.1", - "@animo-id/mdoc": "0.3.0", "@sd-jwt/core": "^0.7.2", "@sd-jwt/decode": "^0.7.2", "@sd-jwt/jwt-status-list": "^0.7.2", "@sd-jwt/sd-jwt-vc": "^0.7.2", "@sd-jwt/types": "^0.7.2", "@sd-jwt/utils": "^0.7.2", - "@animo-id/pex": "4.1.1-alpha.0", "@sphereon/pex-models": "^2.3.1", "@sphereon/ssi-types": "0.30.2-next.135", "@stablelib/ed25519": "^1.0.2", @@ -53,7 +54,7 @@ "class-transformer": "0.5.1", "class-validator": "0.14.1", "did-resolver": "^4.1.0", - "@astronautlabs/jsonpath": "^1.1.2", + "didwebvh-ts": "^1.0.3", "lru_map": "^0.4.1", "make-error": "^1.3.6", "object-inspect": "^1.10.3", @@ -70,9 +71,9 @@ "@types/object-inspect": "^1.8.0", "@types/uuid": "^9.0.1", "@types/varint": "^6.0.0", + "nock": "^14.0.0-beta.19", "rimraf": "^4.4.0", "tslog": "^4.8.2", - "typescript": "~5.5.2", - "nock": "^14.0.0-beta.19" + "typescript": "~5.5.2" } } diff --git a/packages/core/src/modules/dids/__tests__/webvh-did.test.ts b/packages/core/src/modules/dids/__tests__/webvh-did.test.ts new file mode 100644 index 0000000000..6ed06a4175 --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/webvh-did.test.ts @@ -0,0 +1,94 @@ +import type { AgentContext } from '../../../agent' +import type { Wallet } from '../../../wallet' + +import { Subject } from 'rxjs' +import { webcrypto } from 'node:crypto' + +import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' +import { InMemoryWallet } from '../../../../../../tests/InMemoryWallet' +import { getAgentConfig, getAgentContext } from '../../../../tests/helpers' +import { EventEmitter } from '../../../agent/EventEmitter' +import { InjectionSymbols } from '../../../constants' +import { CacheModuleConfig } from '../../../modules/cache/CacheModuleConfig' +import { InMemoryLruCache } from '../../cache/InMemoryLruCache' +import { DidsModuleConfig } from '../DidsModuleConfig' +import { DidDocument } from '../domain' +import { DidDocumentRole } from '../domain/DidDocumentRole' +import { WebVHDidResolver } from '../methods' +import { DidRecord, DidRepository } from '../repository' +import { DidResolverService } from '../services' + +// Set up crypto for noble-ed25519 +;(globalThis as any).crypto = webcrypto + +describe('webvh dids', () => { + const config = getAgentConfig('WebVH DIDs Lifecycle') + + let didRepository: DidRepository + let didResolverService: DidResolverService + let wallet: Wallet + let agentContext: AgentContext + let eventEmitter: EventEmitter + + beforeEach(async () => { + wallet = new InMemoryWallet() + const storageService = new InMemoryStorageService() + eventEmitter = new EventEmitter(config.agentDependencies, new Subject()) + didRepository = new DidRepository(storageService, eventEmitter) + + const cacheModuleConfig = new CacheModuleConfig({ + cache: new InMemoryLruCache({ limit: 1 }), + }) + + agentContext = getAgentContext({ + wallet, + registerInstances: [ + [DidRepository, didRepository], + [InjectionSymbols.StorageService, storageService], + [CacheModuleConfig, cacheModuleConfig], + ], + }) + await wallet.createAndOpen(config.walletConfig) + + didResolverService = new DidResolverService( + config.logger, + new DidsModuleConfig({ resolvers: [new WebVHDidResolver()] }), + didRepository + ) + }) + + afterEach(async () => { + await wallet.delete() + }) + + test('resolve a webvh did document', async () => { + const did = + 'did:webvh:QmPEQVM1JPTyrvEgBcDXwjK4TeyLGSX1PxjgyeAisdWM1p:gist.githubusercontent.com:brianorwhatever:9c4633d18eb644f7a47f93a802691626:raw' + + const { didDocument: resolvedDidDocument } = await didResolverService.resolve(agentContext, did) + + expect(resolvedDidDocument).toBeInstanceOf(DidDocument) + }) + + test('receive a webvh did and did document', async () => { + const did = + 'did:webvh:QmPEQVM1JPTyrvEgBcDXwjK4TeyLGSX1PxjgyeAisdWM1p:gist.githubusercontent.com:brianorwhatever:9c4633d18eb644f7a47f93a802691626:raw' + const didDocument = new DidDocument({ + id: did, + service: [], + verificationMethod: [], + }) + + const didDocumentRecord = new DidRecord({ + did: did, + role: DidDocumentRole.Received, + didDocument: didDocument, + }) + + await didRepository.save(agentContext, didDocumentRecord) + + const { didDocument: resolvedDidDocument } = await didResolverService.resolve(agentContext, did) + expect(resolvedDidDocument).toBeInstanceOf(DidDocument) + expect(resolvedDidDocument?.id).toBe(did) + }) +}) \ No newline at end of file diff --git a/packages/core/src/modules/dids/methods/index.ts b/packages/core/src/modules/dids/methods/index.ts index 4faee9c44b..ad2fec0d7d 100644 --- a/packages/core/src/modules/dids/methods/index.ts +++ b/packages/core/src/modules/dids/methods/index.ts @@ -2,3 +2,4 @@ export * from './key' export * from './peer' export * from './web' export * from './jwk' +export * from './webvh' diff --git a/packages/core/src/modules/dids/methods/webvh/WebVHDidResolver.ts b/packages/core/src/modules/dids/methods/webvh/WebVHDidResolver.ts new file mode 100644 index 0000000000..240053eb64 --- /dev/null +++ b/packages/core/src/modules/dids/methods/webvh/WebVHDidResolver.ts @@ -0,0 +1,38 @@ +import type { AgentContext } from '../../../../agent' +import type { DidResolver } from '../../domain/DidResolver' +import type { DidResolutionResult } from '../../types' + +import { resolveDID } from 'didwebvh-ts' + +import { JsonTransformer } from '../../../../utils/JsonTransformer' +import { DidDocument } from '../../domain' + +export class WebVHDidResolver implements DidResolver { + public readonly supportedMethods = ['webvh'] + + public readonly allowsCaching = true + public readonly allowsLocalDidRecord = true + + // FIXME: Would be nice if we don't have to provide a did resolver instance + // private _resolverInstance = new Resolver() + // private resolver = didWeb.getResolver() + + public async resolve(agentContext: AgentContext, did: string): Promise { + const result = await resolveDID(did) + + let didDocument = null + + if (result.doc) { + didDocument = JsonTransformer.fromJSON(result.doc, DidDocument) + } + + return { + didDocument, + didResolutionMetadata: { + servedFromCache: false, + servedFromDidRecord: false, + }, + didDocumentMetadata: result.meta, + } + } +} \ No newline at end of file diff --git a/packages/core/src/modules/dids/methods/webvh/index.ts b/packages/core/src/modules/dids/methods/webvh/index.ts new file mode 100644 index 0000000000..16d9a053cd --- /dev/null +++ b/packages/core/src/modules/dids/methods/webvh/index.ts @@ -0,0 +1 @@ +export * from './WebVHDidResolver' diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9d3528d15b..a824a5a19a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -532,6 +532,9 @@ importers: did-resolver: specifier: ^4.1.0 version: 4.1.0 + didwebvh-ts: + specifier: ^1.0.3 + version: 1.0.3(typescript@5.5.4) lru_map: specifier: ^0.4.1 version: 0.4.1 @@ -2081,6 +2084,7 @@ packages: '@confio/ics23@0.6.8': resolution: {integrity: sha512-wB6uo+3A50m0sW/EWcU64xpV/8wShZ6bMTa7pF8eYsTrSkQA7oLUIJcs/wb8g4y2Oyq701BaGiO6n/ak5WXO1w==} + deprecated: Unmaintained. The codebase for this package was moved to https://github.com/cosmos/ics23 but then the JS implementation was removed in https://github.com/cosmos/ics23/pull/353. Please consult the maintainers of https://github.com/cosmos for further assistance. '@cosmjs/amino@0.30.1': resolution: {integrity: sha512-yNHnzmvAlkETDYIpeCTdVqgvrdt1qgkOXwuRVi8s27UKI5hfqyE9fJ/fuunXE6ZZPnKkjIecDznmuUOMrMvw4w==} @@ -2695,6 +2699,9 @@ packages: resolution: {integrity: sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw==} engines: {node: ^14.21.3 || >=16} + '@noble/ed25519@2.2.3': + resolution: {integrity: sha512-iHV8eI2mRcUmOx159QNrU8vTpQ/Xm70yJ2cTk3Trc86++02usfqFoNl6x0p3JN81ZDS/1gx6xiK0OwrgqCT43g==} + '@noble/hashes@1.4.0': resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} engines: {node: '>= 16'} @@ -3703,6 +3710,9 @@ packages: base-x@3.0.9: resolution: {integrity: sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==} + base-x@5.0.0: + resolution: {integrity: sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ==} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -3807,6 +3817,9 @@ packages: bs58@4.0.1: resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} + bs58@6.0.0: + resolution: {integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==} + bser@2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} @@ -4406,6 +4419,12 @@ packages: did-resolver@4.1.0: resolution: {integrity: sha512-S6fWHvCXkZg2IhS4RcVHxwuyVejPR7c+a4Go0xbQ9ps5kILa8viiYQgrM4gfTyeTjJ0ekgJH9gk/BawTpmkbZA==} + didwebvh-ts@1.0.3: + resolution: {integrity: sha512-PxvRN7sG0UMe+nIUUa+rI6h4gOm5h6H7SFn5zGD1EnPPb1OdyHoCrSYeo2QCyMRlZrkEFYxstTSIykpAs2AN5g==} + hasBin: true + peerDependencies: + typescript: ^5.4.5 + diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -4461,6 +4480,20 @@ packages: elliptic@6.6.1: resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} + elysia@0.8.17: + resolution: {integrity: sha512-hqKHKUxbvlDHnobudtna5nBoGiZ4oa0xdnhynLAJF3+6gTYHAcQ/8/IyxUx5Az5Uy0zTuNiv7m2bIFu7xeMiWg==} + peerDependencies: + '@sinclair/typebox': '>= 0.31.0' + openapi-types: '>= 12.0.0' + typescript: '>= 5.0.0' + peerDependenciesMeta: + '@sinclair/typebox': + optional: true + openapi-types: + optional: true + typescript: + optional: true + emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} @@ -4651,6 +4684,7 @@ packages: eslint@8.57.0: resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true esniff@2.0.1: @@ -4701,6 +4735,9 @@ packages: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} @@ -4787,6 +4824,9 @@ packages: fast-base64-decode@1.0.0: resolution: {integrity: sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q==} + fast-decode-uri-component@1.0.1: + resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -4803,6 +4843,9 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-querystring@1.1.2: + resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} + fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} @@ -5812,6 +5855,9 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-canonicalize@1.0.6: + resolution: {integrity: sha512-kP2iYpOS5SZHYhIaR1t9oG80d4uTY3jPoaBj+nimy3njtJk8+sRsVatN8pyJRDRtk9Su3+6XqA2U8k0dByJBUQ==} + json-parse-better-errors@1.0.2: resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} @@ -6043,9 +6089,11 @@ packages: lodash.get@4.4.2: resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + deprecated: This package is deprecated. Use the optional chaining (?.) operator instead. lodash.isequal@4.5.0: resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. lodash.isplainobject@4.0.6: resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} @@ -6162,6 +6210,9 @@ packages: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} + memoirist@0.1.10: + resolution: {integrity: sha512-k2ARHDrSzqeQAWrgvE2NgNj7p5c1LkeFI1VizklJWDXAOTgq7m8mtl0v6W1mVawFNe3DH0hb4BdA91y3vwbqvw==} + memoize-one@5.2.1: resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} @@ -6392,6 +6443,9 @@ packages: resolution: {integrity: sha512-eajQ/ZH7qXZQR2AgtfpmSMizQzmyYVmCql7pdhldPuYQi4atACekbJaQplk6dWyIi10jCaFnd6pqvcEFXjbaJw==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} + multiformats@13.3.1: + resolution: {integrity: sha512-QxowxTNwJ3r5RMctoGA5p13w5RbRT2QDkoM+yFlqfLiioBp78nhDjnRLvmSBI9+KAqN4VdgOVWM9c0CHd86m3g==} + multiformats@9.7.1: resolution: {integrity: sha512-TaVmGEBt0fhxiNJMGphBfB+oGvUxFs8KgGvgl8d3C+GWtrFcvXdJ2196eg+dYhmSFClmgFfSfJEklo+SZzdNuw==} @@ -7575,6 +7629,7 @@ packages: sudo-prompt@9.2.1: resolution: {integrity: sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. superagent@9.0.2: resolution: {integrity: sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==} @@ -10992,6 +11047,8 @@ snapshots: dependencies: '@noble/hashes': 1.6.0 + '@noble/ed25519@2.2.3': {} + '@noble/hashes@1.4.0': {} '@noble/hashes@1.6.0': {} @@ -12767,6 +12824,8 @@ snapshots: dependencies: safe-buffer: 5.2.1 + base-x@5.0.0: {} + base64-js@1.5.1: {} base64url-universal@1.1.0: @@ -12891,6 +12950,10 @@ snapshots: dependencies: base-x: 3.0.9 + bs58@6.0.0: + dependencies: + base-x: 5.0.0 + bser@2.1.1: dependencies: node-int64: 0.4.0 @@ -13518,6 +13581,20 @@ snapshots: did-resolver@4.1.0: {} + didwebvh-ts@1.0.3(typescript@5.5.4): + dependencies: + '@noble/curves': 1.7.0 + '@noble/ed25519': 2.2.3 + bs58: 6.0.0 + elysia: 0.8.17(typescript@5.5.4) + json-canonicalize: 1.0.6 + multiformats: 13.3.1 + nanoid: 5.0.9 + typescript: 5.5.4 + transitivePeerDependencies: + - '@sinclair/typebox' + - openapi-types + diff-sequences@29.6.3: {} diff@4.0.2: {} @@ -13582,6 +13659,16 @@ snapshots: minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 + elysia@0.8.17(typescript@5.5.4): + dependencies: + cookie: 0.6.0 + eventemitter3: 5.0.1 + fast-decode-uri-component: 1.0.1 + fast-querystring: 1.1.2 + memoirist: 0.1.10 + optionalDependencies: + typescript: 5.5.4 + emittery@0.13.1: {} emoji-regex@8.0.0: {} @@ -13933,6 +14020,8 @@ snapshots: event-target-shim@5.0.1: {} + eventemitter3@5.0.1: {} + events@3.3.0: {} exec-async@2.2.0: {} @@ -14101,6 +14190,8 @@ snapshots: fast-base64-decode@1.0.0: {} + fast-decode-uri-component@1.0.1: {} + fast-deep-equal@3.1.3: {} fast-diff@1.3.0: {} @@ -14117,6 +14208,10 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-querystring@1.1.2: + dependencies: + fast-decode-uri-component: 1.0.1 + fast-safe-stringify@2.1.1: {} fast-text-encoding@1.0.6: {} @@ -15439,6 +15534,8 @@ snapshots: json-buffer@3.0.1: {} + json-canonicalize@1.0.6: {} + json-parse-better-errors@1.0.2: {} json-parse-even-better-errors@2.3.1: {} @@ -15783,6 +15880,8 @@ snapshots: media-typer@0.3.0: {} + memoirist@0.1.10: {} + memoize-one@5.2.1: {} memory-cache@0.2.0: {} @@ -16173,6 +16272,8 @@ snapshots: multiformats@12.1.3: {} + multiformats@13.3.1: {} + multiformats@9.7.1: {} multiformats@9.9.0: {}