UNPKG

@dwn-protocol/id-sdk

Version:

SDK for accessing the features and capabilities

299 lines (254 loc) 10.1 kB
import type { KeyValueStore } from '../common/index.js'; import type { DidResolutionResult, DidResolverCache, PortableDid } from '../dids/index.js'; import { Level } from 'level'; import { Jose } from '../crypto/index.js'; import { Dwn, MessageStoreLevel, DataStoreLevel, EventLogLevel } from '@dwn-protocol/id'; import { LevelStore, MemoryStore } from '../common/index.js'; import { DidIonMethod, DidKeyMethod, DidResolver, DidResolverCacheLevel } from '../dids/index.js'; import type { IDManagedAgent } from './types/agent.js'; import { LocalKms } from './kms-local.js'; import { DidManager } from './did-manager.js'; import { DwnManager } from './dwn-manager.js'; import { KeyManager } from './key-manager.js'; import { IDRpcClient } from './rpc-client.js'; import { AppDataVault } from './app-data-store.js'; import { SyncManagerLevel } from './sync-manager.js'; import { cryptoToPortableKeyPair } from './utils.js'; import { DidStoreDwn, DidStoreMemory } from './store-managed-did.js'; import { IdentityManager, ManagedIdentity } from './identity-manager.js'; import { IdentityStoreDwn, IdentityStoreMemory } from './store-managed-identity.js'; import { KeyStoreDwn, KeyStoreMemory, PrivateKeyStoreDwn, PrivateKeyStoreMemory } from './store-managed-key.js'; type CreateMethodOptions = { agentClass: new (options: any) => IDManagedAgent agentStores?: 'dwn' | 'memory'; testDataLocation?: string; } type TestManagedAgentOptions = { agent: IDManagedAgent agentStores: 'dwn' | 'memory'; appDataStore: KeyValueStore<string, any>; didResolverCache: DidResolverCache; dwn: Dwn; dwnDataStore: DataStoreLevel; dwnEventLog: EventLogLevel; dwnMessageStore: MessageStoreLevel; syncStore: Level; } export class TestManagedAgent { agent: IDManagedAgent; agentStores: 'dwn' | 'memory'; appDataStore: KeyValueStore<string, any>; didResolverCache: DidResolverCache; dwn: Dwn; dwnDataStore: DataStoreLevel; dwnEventLog: EventLogLevel; dwnMessageStore: MessageStoreLevel; syncStore: Level; constructor(options: TestManagedAgentOptions) { this.agent = options.agent; this.agentStores = options.agentStores; this.appDataStore = options.appDataStore; this.didResolverCache = options.didResolverCache; this.dwn = options.dwn; this.dwnDataStore = options.dwnDataStore; this.dwnEventLog = options.dwnEventLog; this.dwnMessageStore = options.dwnMessageStore; this.syncStore = options.syncStore; } async clearStorage(): Promise<void> { this.agent.agentDid = undefined; await this.appDataStore.clear(); await this.didResolverCache.clear(); await this.dwnDataStore.clear(); await this.dwnEventLog.clear(); await this.dwnMessageStore.clear(); await this.syncStore.clear(); /** Easiest way to start with fresh in-memory stores is to * re-instantiate all of the managed agent components */ if (this.agentStores === 'memory') { const { didManager, identityManager, keyManager } = TestManagedAgent.useMemoryStorage({ agent: this.agent }); this.agent.didManager = didManager; this.agent.identityManager = identityManager; this.agent.keyManager = keyManager; } } async closeStorage(): Promise<void> { await this.appDataStore.close(); await this.didResolverCache.close(); await this.dwnDataStore.close(); await this.dwnEventLog.close(); await this.dwnMessageStore.close(); await this.syncStore.close(); } static async create(options: CreateMethodOptions): Promise<TestManagedAgent> { let { agentClass, agentStores, testDataLocation } = options; agentStores ??= 'memory'; testDataLocation ??= '__TESTDATA__'; const testDataPath = (path: string) => `${testDataLocation}/${path}`; const { appData, appDataStore, didManager, didResolverCache, identityManager, keyManager } = (agentStores === 'memory') ? TestManagedAgent.useMemoryStorage() : TestManagedAgent.useDiskStorage({ testDataLocation }); // Instantiate DID resolver. const didMethodApis = [DidIonMethod, DidKeyMethod]; const didResolver = new DidResolver({ cache : didResolverCache, didResolvers : didMethodApis }); // Instantiate custom stores to use with DWN instance. const dwnDataStore = new DataStoreLevel({ blockstoreLocation: testDataPath('DWN_DATASTORE') }); const dwnEventLog = new EventLogLevel({ location: testDataPath('DWN_EVENTLOG') }); const dwnMessageStore = new MessageStoreLevel({ blockstoreLocation : testDataPath('DWN_MESSAGESTORE'), indexLocation : testDataPath('DWN_MESSAGEINDEX') }); // Instantiate custom DWN instance. const dwn = await Dwn.create({ eventLog : dwnEventLog, dataStore : dwnDataStore, // @ts-expect-error because the DidResolver implementation doesn't have the dump() method. didResolver : didResolver, messageStore : dwnMessageStore }); // Instantiate a DwnManager using the custom DWN instance. const dwnManager = new DwnManager({ dwn }); // Instantiate an RPC Client. const rpcClient = new IDRpcClient(); // Instantiate a custom SyncManager and LevelDB-backed store. const syncStore = new Level(testDataPath('SYNC_STORE')); const syncManager = new SyncManagerLevel({ db: syncStore }); const agent = new agentClass({ agentDid: '', appData, didManager, didResolver, dwnManager, identityManager, keyManager, rpcClient, syncManager }); return new TestManagedAgent({ agent, agentStores, appDataStore, didResolverCache, dwn, dwnDataStore, dwnEventLog, dwnMessageStore, syncStore, }); } async createAgentDid(): Promise<void> { // Create an a DID and key set for the Agent. const agentDid = await DidKeyMethod.create({ keyAlgorithm: 'Ed25519' }); const privateCryptoKey = await Jose.jwkToCryptoKey({ key: agentDid.keySet.verificationMethodKeys![0].privateKeyJwk! }); const publicCryptoKey = await Jose.jwkToCryptoKey({ key: agentDid.keySet.verificationMethodKeys![0].publicKeyJwk! }); const agentSigningKey = { privateKey: privateCryptoKey, publicKey: publicCryptoKey }; // Set the DID as the default signing key. const alias = await this.agent.didManager.getDefaultSigningKey({ did: agentDid.did }); const defaultSigningKey = cryptoToPortableKeyPair({ cryptoKeyPair: agentSigningKey, keyData: { alias, kms: 'memory' } }); await this.agent.keyManager.setDefaultSigningKey({ key: defaultSigningKey }); // Set the DID as the Agent's DID. this.agent.agentDid = agentDid.did; } public async createIdentity(options: { keyAlgorithm?: 'Ed25519' | 'secp256k1'; testDwnUrls: string[] }): Promise<{ did: PortableDid, identity: ManagedIdentity }> { // Default to generating Ed25519 keys. const { keyAlgorithm, testDwnUrls } = options; const didOptions = await DidIonMethod.generateDwnOptions({ signingKeyAlgorithm : keyAlgorithm, serviceEndpointNodes : testDwnUrls }); // Create a PortableDid. const did = await DidIonMethod.create({ anchor: false, ...didOptions }); // Create a ManagedIdentity. const identity: ManagedIdentity = { did : did.did, name : 'Test' }; return { did, identity }; } private static useDiskStorage(options: { agent?: IDManagedAgent, testDataLocation: string }) { const { agent, testDataLocation } = options; const testDataPath = (path: string) => `${testDataLocation}/${path}`; const appDataStore = new LevelStore(testDataPath('APPDATA')); const appData = new AppDataVault({ keyDerivationWorkFactor : 1, store : appDataStore }); const didManager = new DidManager({ agent, didMethods : [DidIonMethod, DidKeyMethod], store : new DidStoreDwn() }); const didResolverCache = new DidResolverCacheLevel({ location: testDataPath('DID_RESOLVERCACHE') }); const identityManager = new IdentityManager({ agent, store: new IdentityStoreDwn() }); const localKmsDwn = new LocalKms({ agent, kmsName : 'local', keyStore : new KeyStoreDwn({ schema: 'https://identity.foundation/schemas/dwn/kms-key' }), privateKeyStore : new PrivateKeyStoreDwn() }); const localKmsMemory = new LocalKms({ agent, kmsName: 'memory' }); const keyManager = new KeyManager({ agent, kms: { local : localKmsDwn, memory : localKmsMemory }, store: new KeyStoreDwn({ schema: 'https://identity.foundation/schemas/dwn/managed-key' }) }); return { appData, appDataStore, didManager, didResolverCache, identityManager, keyManager }; } private static useMemoryStorage(options?: { agent: IDManagedAgent }) { const { agent } = options ?? {}; const appDataStore = new MemoryStore<string, any>(); const appData = new AppDataVault({ keyDerivationWorkFactor : 1, store : appDataStore }); const didManager = new DidManager({ agent, didMethods : [DidIonMethod, DidKeyMethod], store : new DidStoreMemory() }); const didResolverCache = new MemoryStore<string, DidResolutionResult | void>(); const identityManager = new IdentityManager({ agent, store: new IdentityStoreMemory() }); const localKmsDwn = new LocalKms({ agent, kmsName : 'local', keyStore : new KeyStoreMemory(), privateKeyStore : new PrivateKeyStoreMemory() }); const localKmsMemory = new LocalKms({ agent, kmsName: 'memory' }); const keyManager = new KeyManager({ agent, kms: { local : localKmsDwn, memory : localKmsMemory }, store: new KeyStoreMemory() }); return { appData, appDataStore, didManager, didResolverCache, identityManager, keyManager }; } }