diff --git a/packages/contact-manager-rest-api/__tests__/RestAPI.ts b/packages/contact-manager-rest-api/__tests__/RestAPI.ts index 79e6fbb42..6cafd5ac7 100644 --- a/packages/contact-manager-rest-api/__tests__/RestAPI.ts +++ b/packages/contact-manager-rest-api/__tests__/RestAPI.ts @@ -1,4 +1,4 @@ -import {ExpressCorsConfigurer} from "../../ssi-express-support/src"; +import { ExpressCorsConfigurer } from '../../ssi-express-support/src' import { ExpressBuilder } from '../../ssi-express-support/src' import { ContactManagerApiServer } from '../src' import agent from './agent' @@ -9,7 +9,7 @@ const builder = ExpressBuilder.fromServerOpts({ }) .withMorganLogging({ format: 'dev' }) .withPassportAuth(false) - .withCorsConfigurer(new ExpressCorsConfigurer().allowOrigin('*')) + .withCorsConfigurer(new ExpressCorsConfigurer().allowOrigin('*')) const expressSupport = builder.build({ startListening: true }) diff --git a/packages/contact-manager-rest-api/__tests__/agent.ts b/packages/contact-manager-rest-api/__tests__/agent.ts index 8dda2689a..295ce6bdf 100644 --- a/packages/contact-manager-rest-api/__tests__/agent.ts +++ b/packages/contact-manager-rest-api/__tests__/agent.ts @@ -1,62 +1,178 @@ -import { createAgent } from '@veramo/core' -import { DataStore, DataStoreORM } from '@veramo/data-store' -import { IRequiredPlugins } from '../src' -import { DB_CONNECTION_NAME, sqliteConfig } from './database' -import { ContactManager } from '@sphereon/ssi-sdk.contact-manager' -import { ContactStore, PartyTypeEnum } from '@sphereon/ssi-sdk.data-store' -import { DataSources } from '@sphereon/ssi-sdk.agent-config' -import { v4 } from 'uuid' +import {createAgent, IIdentifier} from '@veramo/core' +import {DataStore, DataStoreORM, DIDStore} from '@veramo/data-store' +import {IRequiredPlugins} from '../src' +import {DB_CONNECTION_NAME, sqliteConfig} from './database' +import {AddContactArgs, ContactManager} from '@sphereon/ssi-sdk.contact-manager' +import { + ContactStore, + CorrelationIdentifierEnum, + IdentityRoleEnum, + NonPersistedIdentity, + PartyTypeEnum +} from '@sphereon/ssi-sdk.data-store' +import {DataSources} from '@sphereon/ssi-sdk.agent-config' +import {v4} from 'uuid' +import {DIDManager} from "@veramo/did-manager"; +import {KeyManager, MemoryKeyStore, MemoryPrivateKeyStore} from "@veramo/key-manager"; +import {KeyManagementSystem} from "@veramo/kms-local"; +import {IonDIDProvider} from "@veramo/did-provider-ion"; +import {IonPublicKeyPurpose} from "@decentralized-identity/ion-sdk"; + +export const DID_PREFIX = 'did' + +const PRIVATE_RECOVERY_KEY_HEX = 'd39e66e720c00b244923eb861122ed25116555ae771ee9a57b749640173d7cf8' +const PRIVATE_UPDATE_KEY_HEX = '0121009becfa9caf6221dce6f4f7b55dd3376e79c4ca83ce92bd43861c2393ec' +const PRIVATE_DID1_KEY_HEX = 'e0453c226bd4458740c45f0d0590e696da2fe9c5c66f81908aedd43a7b7da252' +const PRIVATE_DID2_KEY_HEX = '74213f5204ea414deb4dc2c470d1700b8cc2076ddd8d3ddb06dae08902dddd0c' +const PRIVATE_DID3_KEY_HEX = '90868704b3bb2bdd27e2e831654c4adb2ea7e4f0e090d03aa3ae38020346aa12' +const PRIVATE_DID4_KEY_HEX = 'f367873323bf0dd701ec972d8a17aee7a9dcad13bd6deb64e8653da113094261' + +export enum KeyManagementSystemEnum { + LOCAL = 'local', +} + +export enum SupportedDidMethodEnum { + DID_ION = 'ion', +} + +export const didProviders = { + [`${DID_PREFIX}:${SupportedDidMethodEnum.DID_ION}`]: new IonDIDProvider({ + defaultKms: KeyManagementSystemEnum.LOCAL, + }), +} const dbConnection = DataSources.singleInstance() - .addConfig(DB_CONNECTION_NAME, sqliteConfig) - .getDbConnection(DB_CONNECTION_NAME) + .addConfig(DB_CONNECTION_NAME, sqliteConfig) + .getDbConnection(DB_CONNECTION_NAME) + const agent = createAgent({ - plugins: [new DataStore(dbConnection), new DataStoreORM(dbConnection), new ContactManager({ store: new ContactStore(dbConnection) })], + plugins: [ + new DataStore(dbConnection), + new DataStoreORM(dbConnection), + new ContactManager({store: new ContactStore(dbConnection)}), + new KeyManager({ + store: new MemoryKeyStore(), + kms: { + local: new KeyManagementSystem(new MemoryPrivateKeyStore()), + }, + }), + new DIDManager({ + store: new DIDStore(dbConnection), + defaultProvider: `${DID_PREFIX}:${SupportedDidMethodEnum.DID_ION}`, + providers: didProviders, + }), + ], }) +const generateIdentity = (contact: Record, didKeyIdentifier: IIdentifier): NonPersistedIdentity => { + return { + alias: didKeyIdentifier.alias, + roles: [IdentityRoleEnum.ISSUER], + identifier: { + type: CorrelationIdentifierEnum.DID, + correlationId: didKeyIdentifier.did, + }, + } as NonPersistedIdentity +}; + +async function addContacts() { + try { + const ctPeople = await agent.cmAddContactType({ + name: "people", + type: PartyTypeEnum.NATURAL_PERSON, + tenantId: v4() + }); + + const ctOrganizations = await agent.cmAddContactType({ + name: "organizations", + type: PartyTypeEnum.ORGANIZATION, + tenantId: v4() + }); + + + const persona1 = { + firstName: "Viola", + middleName: "D.", + lastName: "Kemp", + displayName: "Viola Kemp", + contactType: ctPeople, + uri: "example.com" + } as AddContactArgs; + + let didKeyIdentifier = await agent.didManagerCreate(existingDidConfig(false, 'bram', PRIVATE_DID1_KEY_HEX)); + persona1.identities = [generateIdentity(persona1, didKeyIdentifier)]; + await agent.cmAddContact(persona1); + + const persona2 = { + firstName: "Kevin", + middleName: "T.", + lastName: "Bloomer", + displayName: "Kevin Bloomer", + contactType: ctPeople, + uri: "example.com" + } as AddContactArgs; + + didKeyIdentifier = await agent.didManagerCreate(existingDidConfig(false, 'kraak', PRIVATE_DID2_KEY_HEX)); + persona2.identities = [generateIdentity(persona2, didKeyIdentifier)]; + await agent.cmAddContact(persona2); + + + const organization1 = { + legalName: "Sphereon International", + displayName: "Sphereon B.V.", + contactType: ctOrganizations, + uri: "sphereon.com" + } as AddContactArgs; + + didKeyIdentifier = await agent.didManagerCreate(existingDidConfig(false, 'sphereon', PRIVATE_DID3_KEY_HEX)); + organization1.identities = [generateIdentity(organization1, didKeyIdentifier)]; + await agent.cmAddContact(organization1); + + const organization2 = { + legalName: "Kamer van verkoophandel", + displayName: "Kamer van koophandel", + contactType: ctOrganizations, + uri: "kvk.nl" + } as AddContactArgs; + + didKeyIdentifier = await agent.didManagerCreate(existingDidConfig(false, 'kvk', PRIVATE_DID4_KEY_HEX)); + organization2.identities = [generateIdentity(organization2, didKeyIdentifier)]; + await agent.cmAddContact(organization2); + } catch (e) { + console.log(e); + } +} + + +function existingDidConfig(anchor: boolean = false, kid: string, privateDIDKeyHex: String) { + return { + options: { + anchor, + recoveryKey: { + kid: 'recovery-test2', + key: { + privateKeyHex: PRIVATE_RECOVERY_KEY_HEX, + }, + }, + updateKey: { + kid: 'update-test2', + key: { + privateKeyHex: PRIVATE_UPDATE_KEY_HEX, + }, + }, + verificationMethods: [ + { + kid, + purposes: [IonPublicKeyPurpose.Authentication, IonPublicKeyPurpose.AssertionMethod], + key: { + privateKeyHex: privateDIDKeyHex, + }, + }, + ], + }, + } +} + +addContacts() -agent.cmAddContactType({ - name: "people", - type: PartyTypeEnum.NATURAL_PERSON, - tenantId: v4() -}).then(ct=> { - agent.cmAddContact({ - firstName: "Abraham", - middleName: "Gerrit Jan", - lastName: "ten Cate", - displayName: "Bram ten Cate", - contactType: ct, - uri: "example.com" - }) - agent.cmAddContact({ - firstName: "Kraak", - middleName: "en", - lastName: "Smaak", - displayName: "Kraak en Smaak", - contactType: ct, - uri: "example.com" - }) -}).catch(e => { - console.log(e) -}) -agent.cmAddContactType({ - name: "orgzanizations", - type: PartyTypeEnum.ORGANIZATION, - tenantId: v4() -}).then(ct=> { - agent.cmAddContact({ - legalName: "Sphereon International", - displayName: "Sphereon B.V.", - contactType: ct, - uri: "sphereon.com" - }) - agent.cmAddContact({ - legalName: "Kamer van verkoophandel", - displayName: "Kamer van koophandel", - contactType: ct, - uri: "kvk.nl" - }) -}).catch(e=> { - console.log(e) -}) export default agent diff --git a/packages/contact-manager-rest-api/__tests__/database/config.ts b/packages/contact-manager-rest-api/__tests__/database/config.ts index 44aab5a1f..8234f1f32 100644 --- a/packages/contact-manager-rest-api/__tests__/database/config.ts +++ b/packages/contact-manager-rest-api/__tests__/database/config.ts @@ -1,6 +1,8 @@ -import { DataStoreContactEntities } from '@sphereon/ssi-sdk.data-store' -import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions' -import { DataStoreContactMigrations } from '@sphereon/ssi-sdk.data-store/dist/migrations/generic' +import {DataStoreContactEntities} from '@sphereon/ssi-sdk.data-store' +import {SqliteConnectionOptions} from 'typeorm/driver/sqlite/SqliteConnectionOptions' +import {DataStoreContactMigrations} from '@sphereon/ssi-sdk.data-store/dist/migrations/generic' +import {Entities as VeramoDataStoreEntities} from "@veramo/data-store"; +import {migrations as VeramoDataStoreMigrations} from "@veramo/data-store/build/migrations"; const DB_CONNECTION_NAME = 'default' const DB_ENCRYPTION_KEY = '29739248cad1bd1a0fc4d9b75cd4d2990de535baf5caadfdf8d8f86664aa830c' @@ -8,8 +10,8 @@ const DB_ENCRYPTION_KEY = '29739248cad1bd1a0fc4d9b75cd4d2990de535baf5caadfdf8d8f const sqliteConfig: SqliteConnectionOptions = { type: 'sqlite', database: '__tests__/database/test.sqlite', - entities: [...DataStoreContactEntities], - migrations: [...DataStoreContactMigrations], + entities: [...DataStoreContactEntities, ...VeramoDataStoreEntities], + migrations: [...DataStoreContactMigrations, ...VeramoDataStoreMigrations], migrationsRun: false, // We run migrations from code to ensure proper ordering with Redux synchronize: false, // We do not enable synchronize, as we use migrations from code migrationsTransactionMode: 'each', // protect every migration with a separate transaction diff --git a/packages/contact-manager-rest-api/package.json b/packages/contact-manager-rest-api/package.json index 4519e26b3..a32f04584 100644 --- a/packages/contact-manager-rest-api/package.json +++ b/packages/contact-manager-rest-api/package.json @@ -49,6 +49,9 @@ "@veramo/key-manager": "4.2.0", "@veramo/kms-local": "4.2.0", "@veramo/utils": "4.2.0", + "@veramo/did-manager": "4.2.0", + "@veramo/did-provider-ion": "4.2.0", + "@decentralized-identity/ion-sdk": "^0.6.0", "morgan": "^1.10.0", "nock": "^13.2.1", "passport": "^0.6.0", diff --git a/packages/contact-manager-rest-api/src/contact-manager-api-server.ts b/packages/contact-manager-rest-api/src/contact-manager-api-server.ts index 0b92cdc69..e69d2e6d3 100644 --- a/packages/contact-manager-rest-api/src/contact-manager-api-server.ts +++ b/packages/contact-manager-rest-api/src/contact-manager-api-server.ts @@ -11,7 +11,7 @@ import { partiesTypeReadEndpoint, partyWriteEndpoint, partyTypeReadEndpoint, - identitiesReadEndpoint + identitiesReadEndpoint, } from './api-functions' import { copyGlobalAuthToEndpoints, ExpressSupport } from '@sphereon/ssi-express-support' @@ -32,12 +32,7 @@ export class ContactManagerApiServer { this._express = args.expressSupport.express this._router = express.Router() const context = agentContext(agent) - const features = opts?.enableFeatures ?? [ - 'party_read', - 'party_write', - 'party_type_read', - 'identity_read' - ] + const features = opts?.enableFeatures ?? ['party_read', 'party_write', 'party_type_read', 'identity_read'] console.log(`Contact Manager API enabled, with features: ${JSON.stringify(features)}}`) // endpoints diff --git a/packages/contact-manager-rest-api/src/types.ts b/packages/contact-manager-rest-api/src/types.ts index 42fd15da4..ed3aab83c 100644 --- a/packages/contact-manager-rest-api/src/types.ts +++ b/packages/contact-manager-rest-api/src/types.ts @@ -1,12 +1,8 @@ import { GenericAuthArgs, ISingleEndpointOpts } from '@sphereon/ssi-express-support' import { IContactManager } from '@sphereon/ssi-sdk.contact-manager' -import { IAgentContext, IDataStore, IKeyManager } from '@veramo/core' +import {IAgentContext, IDataStore, IDIDManager, IKeyManager} from '@veramo/core' -export type ContactManagerMRestApiFeatures = - 'party_read' | - 'party_write' | - 'party_type_read' | - 'identity_read' +export type ContactManagerMRestApiFeatures = 'party_read' | 'party_write' | 'party_type_read' | 'identity_read' export interface IContactManagerAPIEndpointOpts { endpointOpts?: { @@ -20,5 +16,5 @@ export interface IContactManagerAPIEndpointOpts { enableFeatures?: ContactManagerMRestApiFeatures[] } -export type IRequiredPlugins = IContactManager & IDataStore & IKeyManager +export type IRequiredPlugins = IContactManager & IDataStore & IKeyManager & IDIDManager export type IRequiredContext = IAgentContext diff --git a/packages/contact-manager/src/agent/ContactManager.ts b/packages/contact-manager/src/agent/ContactManager.ts index 561e7d43a..90aa95f6b 100644 --- a/packages/contact-manager/src/agent/ContactManager.ts +++ b/packages/contact-manager/src/agent/ContactManager.ts @@ -184,5 +184,4 @@ export class ContactManager implements IAgentPlugin { throw new Error('Contact not supported') } - } diff --git a/packages/data-store/src/__tests__/contact.entities.test.ts b/packages/data-store/src/__tests__/contact.entities.test.ts index e79eb1910..98c02f3c8 100644 --- a/packages/data-store/src/__tests__/contact.entities.test.ts +++ b/packages/data-store/src/__tests__/contact.entities.test.ts @@ -41,6 +41,8 @@ import { partyTypeEntityFrom, } from '../utils/contact/MappingUtils' +// TODO write test adding two contacts reusing the same contactType + describe('Database entities tests', (): void => { let dbConnection: DataSource diff --git a/packages/data-store/src/entities/contact/BaseContactEntity.ts b/packages/data-store/src/entities/contact/BaseContactEntity.ts index d0f548eb0..c353664de 100644 --- a/packages/data-store/src/entities/contact/BaseContactEntity.ts +++ b/packages/data-store/src/entities/contact/BaseContactEntity.ts @@ -1,6 +1,15 @@ -import { BaseEntity, +import { + BaseEntity, BeforeInsert, - BeforeUpdate, CreateDateColumn, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn, TableInheritance, UpdateDateColumn } from 'typeorm' + BeforeUpdate, + CreateDateColumn, + Entity, + JoinColumn, + OneToOne, + PrimaryGeneratedColumn, + TableInheritance, + UpdateDateColumn, +} from 'typeorm' import { PartyEntity } from './PartyEntity' @Entity('BaseContact') diff --git a/packages/data-store/src/entities/contact/PartyEntity.ts b/packages/data-store/src/entities/contact/PartyEntity.ts index 43bb09279..9b50a363a 100644 --- a/packages/data-store/src/entities/contact/PartyEntity.ts +++ b/packages/data-store/src/entities/contact/PartyEntity.ts @@ -85,24 +85,6 @@ export class PartyEntity extends BaseEntity { this.lastUpdatedAt = new Date() } - @BeforeInsert() - @BeforeUpdate() - async checkUniqueTenantId(): Promise { - const result: Array = await PartyEntity.find({ - where: { - partyType: { - tenantId: this.partyType.tenantId, - }, - }, - }) - - if (result?.length > 0) { - return Promise.reject(Error('Tenant id already in use')) - } - - return - } - @BeforeInsert() @BeforeUpdate() async validate(): Promise { diff --git a/packages/oid4vci-issuer-rest-client/src/agent/OID4VCIRestClient.ts b/packages/oid4vci-issuer-rest-client/src/agent/OID4VCIRestClient.ts index 10c6b1f51..5659094b7 100644 --- a/packages/oid4vci-issuer-rest-client/src/agent/OID4VCIRestClient.ts +++ b/packages/oid4vci-issuer-rest-client/src/agent/OID4VCIRestClient.ts @@ -4,7 +4,8 @@ import { IOID4VCIClientCreateOfferUriRequest, IOID4VCIClientCreateOfferUriRequestArgs, IOID4VCIClientCreateOfferUriResponse, - IOID4VCIClientGetIssueStatusArgs, IRestClientAuthenticationOpts, + IOID4VCIClientGetIssueStatusArgs, + IRestClientAuthenticationOpts, } from '../types/IOID4VCIRestClient' import { IssueStatusResponse } from '@sphereon/oid4vci-common' import Debug from 'debug' @@ -24,7 +25,7 @@ export class OID4VCIRestClient implements IAgentPlugin { private readonly agentBaseUrl?: string private readonly authOpts?: IRestClientAuthenticationOpts - constructor(args?: { baseUrl?: string, authentication?: IRestClientAuthenticationOpts }) { + constructor(args?: { baseUrl?: string; authentication?: IRestClientAuthenticationOpts }) { if (args?.baseUrl) { this.agentBaseUrl = args.baseUrl } diff --git a/packages/vc-status-list-issuer-drivers/src/drivers.ts b/packages/vc-status-list-issuer-drivers/src/drivers.ts index e06f44708..c8c043e99 100644 --- a/packages/vc-status-list-issuer-drivers/src/drivers.ts +++ b/packages/vc-status-list-issuer-drivers/src/drivers.ts @@ -10,7 +10,7 @@ import { import { StatusList2021EntryCredentialStatus, statusListCredentialToDetails, StatusListDetails } from '@sphereon/ssi-sdk.vc-status-list' import { OriginalVerifiableCredential, StatusListCredentialIdMode, StatusListDriverType } from '@sphereon/ssi-types' import { DataSource } from 'typeorm' -import {Driver} from "./types"; +import { Driver } from './types' export interface StatusListManagementOptions { id?: string @@ -42,7 +42,6 @@ export async function getDriver(args: { id?: string; correlationId?: string; dbN return await AgentTypeORMDriver.init(getOptions(args), { dataSources: DataSources.singleInstance() }) } - export class AgentTypeORMDriver implements Driver { private _statusListLength: number | undefined diff --git a/packages/vc-status-list-issuer-drivers/src/types.ts b/packages/vc-status-list-issuer-drivers/src/types.ts index 9aafa231c..ebf576b77 100644 --- a/packages/vc-status-list-issuer-drivers/src/types.ts +++ b/packages/vc-status-list-issuer-drivers/src/types.ts @@ -1,11 +1,12 @@ import { IAddStatusListEntryArgs, - IGetStatusListEntryByCredentialIdArgs, IGetStatusListEntryByIndexArgs, + IGetStatusListEntryByCredentialIdArgs, + IGetStatusListEntryByIndexArgs, IStatusListEntryEntity, - StatusListStore -} from "@sphereon/ssi-sdk.data-store"; -import {StatusList2021EntryCredentialStatus, StatusListDetails} from "@sphereon/ssi-sdk.vc-status-list"; -import {OriginalVerifiableCredential, StatusListDriverType} from "@sphereon/ssi-types"; + StatusListStore, +} from '@sphereon/ssi-sdk.data-store' +import { StatusList2021EntryCredentialStatus, StatusListDetails } from '@sphereon/ssi-sdk.vc-status-list' +import { OriginalVerifiableCredential, StatusListDriverType } from '@sphereon/ssi-types' import { IAgentContext, ICredentialIssuer, @@ -17,7 +18,7 @@ import { IKeyManager, IResolver, } from '@veramo/core' -import {DriverOptions} from "./drivers"; +import { DriverOptions } from './drivers' export type IRequiredPlugins = IDataStore & IDataStoreORM & diff --git a/packages/web3-provider-headless/__tests__/agent.ts b/packages/web3-provider-headless/__tests__/agent.ts index f8774cacb..f1ce06187 100644 --- a/packages/web3-provider-headless/__tests__/agent.ts +++ b/packages/web3-provider-headless/__tests__/agent.ts @@ -1,103 +1,110 @@ -import {ExpressBuilder, ExpressCorsConfigurer} from '@sphereon/ssi-express-support' -import {SphereonKeyManager} from '@sphereon/ssi-sdk-ext.key-manager' -import {SphereonKeyManagementSystem} from '@sphereon/ssi-sdk-ext.kms-local' -import {createAgent, IDIDManager, IKeyManager, IResolver, ManagedKeyInfo, TAgent} from '@veramo/core' -import {DIDManager, MemoryDIDStore} from '@veramo/did-manager' -import {getDidKeyResolver, KeyDIDProvider} from '@veramo/did-provider-key' -import {DIDResolverPlugin} from '@veramo/did-resolver' -import {MemoryKeyStore, MemoryPrivateKeyStore} from '@veramo/key-manager' -import {Resolver} from 'did-resolver' -import {Signer} from 'ethers' -import {IRequiredContext, IWeb3Provider} from '../src' -import {EthersHeadlessProvider} from '../src' -import {EthersKMSSignerBuilder} from '../src' -import {createRpcServer} from '../src' -import {injectWeb3Provider} from './web3-helper' +import { ExpressBuilder, ExpressCorsConfigurer } from '@sphereon/ssi-express-support' +import { SphereonKeyManager } from '@sphereon/ssi-sdk-ext.key-manager' +import { SphereonKeyManagementSystem } from '@sphereon/ssi-sdk-ext.kms-local' +import { createAgent, IDIDManager, IKeyManager, IResolver, ManagedKeyInfo, TAgent } from '@veramo/core' +import { DIDManager, MemoryDIDStore } from '@veramo/did-manager' +import { getDidKeyResolver, KeyDIDProvider } from '@veramo/did-provider-key' +import { DIDResolverPlugin } from '@veramo/did-resolver' +import { MemoryKeyStore, MemoryPrivateKeyStore } from '@veramo/key-manager' +import { Resolver } from 'did-resolver' +import { Signer } from 'ethers' +import { IRequiredContext, IWeb3Provider } from '../src' +import { EthersHeadlessProvider } from '../src' +import { EthersKMSSignerBuilder } from '../src' +import { createRpcServer } from '../src' +import { injectWeb3Provider } from './web3-helper' import configJSON from './config.json' const agent: TAgent = createAgent({ - plugins: [ - new SphereonKeyManager({ - store: new MemoryKeyStore(), - kms: { - local: new SphereonKeyManagementSystem(new MemoryPrivateKeyStore()), - }, - }), - new DIDManager({ - providers: { - 'did:key': new KeyDIDProvider({defaultKms: 'local'}), - }, - store: new MemoryDIDStore(), - defaultProvider: 'did:key', - }), - new DIDResolverPlugin({ - resolver: new Resolver({ - ...getDidKeyResolver(), - }), - }), - ], + plugins: [ + new SphereonKeyManager({ + store: new MemoryKeyStore(), + kms: { + local: new SphereonKeyManagementSystem(new MemoryPrivateKeyStore()), + }, + }), + new DIDManager({ + providers: { + 'did:key': new KeyDIDProvider({ defaultKms: 'local' }), + }, + store: new MemoryDIDStore(), + defaultProvider: 'did:key', + }), + new DIDResolverPlugin({ + resolver: new Resolver({ + ...getDidKeyResolver(), + }), + }), + ], }) -const context: IRequiredContext = {agent} +const context: IRequiredContext = { agent } if (configJSON && Array.isArray(configJSON.configs)) { - const configs = configJSON as Configs - for (const config of configs.configs) { - const expressSupport = ExpressBuilder.fromServerOpts({ - hostname: config.hostname ?? '127.0.0.1', - port: config.port ?? 2999, - basePath: config.basePath ?? '/web3/rpc', - }) - .withCorsConfigurer(new ExpressCorsConfigurer().allowOrigin('*')) - .build() - const wallets = config.wallets - if (!wallets || wallets.length === 0) { - throw Error('Cannot have an RPC provider without wallets. Adjust the config') - } - const result = wallets.map(wallet => { - const privateKeys = wallet.privateKeys - if (!privateKeys || privateKeys.length === 0) { - throw Error('A wallet without private keys when setting up the headless RPC web3 wallet. Adjust the config') - } - const importedKeys = privateKeys.map(privateKeyHex => - agent - .keyManagerImport({ - // privateKeyHex: '8f2695a99c416ab9241fc75ae53f90b083aecff9e4463e046a1527f456b502c6', - privateKeyHex, - kms: 'local', - type: 'Secp256k1', - }).then(key => { - console.log(`Imported key ${JSON.stringify(key)}`) - return key - }).catch(e => console.log(e)) - ) - - - return Promise.all(importedKeys).then( - walletsKeys => { - console.log('=============KEY===========') - console.log(JSON.stringify(walletsKeys)) - console.log('=============KEY===========') + const configs = configJSON as Configs + for (const config of configs.configs) { + const expressSupport = ExpressBuilder.fromServerOpts({ + hostname: config.hostname ?? '127.0.0.1', + port: config.port ?? 2999, + basePath: config.basePath ?? '/web3/rpc', + }) + .withCorsConfigurer(new ExpressCorsConfigurer().allowOrigin('*')) + .build() + const wallets = config.wallets + if (!wallets || wallets.length === 0) { + throw Error('Cannot have an RPC provider without wallets. Adjust the config') + } + const result = wallets.map((wallet) => { + const privateKeys = wallet.privateKeys + if (!privateKeys || privateKeys.length === 0) { + throw Error('A wallet without private keys when setting up the headless RPC web3 wallet. Adjust the config') + } + const importedKeys = privateKeys.map((privateKeyHex) => + agent + .keyManagerImport({ + // privateKeyHex: '8f2695a99c416ab9241fc75ae53f90b083aecff9e4463e046a1527f456b502c6', + privateKeyHex, + kms: 'local', + type: 'Secp256k1', + }) + .then((key) => { + console.log(`Imported key ${JSON.stringify(key)}`) + return key + }) + .catch((e) => console.log(e)) + ) - const kmsSigners = walletsKeys.filter(key => key !== undefined).map(key => new EthersKMSSignerBuilder().withContext(context).withKeyRef(key as ManagedKeyInfo).build()) - let signers: Signer[] - let web3Provider: IWeb3Provider + return Promise.all(importedKeys) + .then((walletsKeys) => { + console.log('=============KEY===========') + console.log(JSON.stringify(walletsKeys)) + console.log('=============KEY===========') - // Inject window.ethereum instance - [signers, web3Provider] = injectWeb3Provider({signers: kmsSigners}) - const headlessProvider = web3Provider as EthersHeadlessProvider - console.log(`NO Wallets: ${JSON.stringify(signers.length)}`) + const kmsSigners = walletsKeys + .filter((key) => key !== undefined) + .map((key) => + new EthersKMSSignerBuilder() + .withContext(context) + .withKeyRef(key as ManagedKeyInfo) + .build() + ) + let signers: Signer[] + let web3Provider: IWeb3Provider - createRpcServer(headlessProvider, expressSupport, {path: wallet.path ?? '', basePath: config.basePath}) - } - ).then(value => wallet) - }) + // Inject window.ethereum instance + ;[signers, web3Provider] = injectWeb3Provider({ signers: kmsSigners }) + const headlessProvider = web3Provider as EthersHeadlessProvider + console.log(`NO Wallets: ${JSON.stringify(signers.length)}`) - Promise.all(result).then(walletConfig => { - expressSupport.start() - console.log('Done setting up ' + config.basePath) + createRpcServer(headlessProvider, expressSupport, { path: wallet.path ?? '', basePath: config.basePath }) }) - } + .then((value) => wallet) + }) + Promise.all(result).then((walletConfig) => { + expressSupport.start() + console.log('Done setting up ' + config.basePath) + }) + } } /*agent @@ -131,19 +138,18 @@ if (configJSON && Array.isArray(configJSON.configs)) { }) console.log('DONE')*/ - export interface Configs { - configs: Config[] + configs: Config[] } export interface Config { - basePath?: string - hostname?: string - port?: number - wallets: WalletConfig[] + basePath?: string + hostname?: string + port?: number + wallets: WalletConfig[] } export interface WalletConfig { - privateKeys: string[], - path?: string + privateKeys: string[] + path?: string } diff --git a/packages/web3-provider-headless/src/rpc-server.ts b/packages/web3-provider-headless/src/rpc-server.ts index 16d5a8f30..17e0a2235 100644 --- a/packages/web3-provider-headless/src/rpc-server.ts +++ b/packages/web3-provider-headless/src/rpc-server.ts @@ -1,89 +1,93 @@ -import {ExpressSupport, ISingleEndpointOpts, sendErrorResponse} from '@sphereon/ssi-express-support' -import {Router} from "express"; -import {EthersHeadlessProvider} from './ethers-headless-provider' -import {Web3Method} from './types' +import { ExpressSupport, ISingleEndpointOpts, sendErrorResponse } from '@sphereon/ssi-express-support' +import { Router } from 'express' +import { EthersHeadlessProvider } from './ethers-headless-provider' +import { Web3Method } from './types' -export function createRpcServer(provider: EthersHeadlessProvider, expressSupport: ExpressSupport, opts?: ISingleEndpointOpts & { basePath?: string}) { - const express = expressSupport.express - const router = Router() - // const app = expressSupport.express - // app.post(opts?.basePath ?? "/web3/rpc", (req, res, next) => {console.log(`${JSON.stringify(req.body, null,2)}`); next()} , rpcHandler(createService(provider))); - const path = opts?.path ?? '/web3/rpc' - console.log(`RPC server will use basePath ${opts?.basePath ?? '/'} and path ${path}`) - router.post( - path, - (req, res, next) => { - console.log(`REQ ${req.body?.method}:\r\n${JSON.stringify(req.body, null, 2)}\r\n===`) - next() - }, - async (req, res, next) => { - try { - const method = req.body.method - const params = req.body.params - const id = req.body.id +export function createRpcServer( + provider: EthersHeadlessProvider, + expressSupport: ExpressSupport, + opts?: ISingleEndpointOpts & { basePath?: string } +) { + const express = expressSupport.express + const router = Router() + // const app = expressSupport.express + // app.post(opts?.basePath ?? "/web3/rpc", (req, res, next) => {console.log(`${JSON.stringify(req.body, null,2)}`); next()} , rpcHandler(createService(provider))); + const path = opts?.path ?? '/web3/rpc' + console.log(`RPC server will use basePath ${opts?.basePath ?? '/'} and path ${path}`) + router.post( + path, + (req, res, next) => { + console.log(`REQ ${req.body?.method}:\r\n${JSON.stringify(req.body, null, 2)}\r\n===`) + next() + }, + async (req, res, next) => { + try { + const method = req.body.method + const params = req.body.params + const id = req.body.id - // todo: A Notification is a Request object without an "id" member. - // A Request object that is a Notification signifies the Client's lack of interest in the corresponding Response object, - // and as such no Response object needs to be returned to the client. The Server MUST NOT reply to a Notification, including those that are within a batch request. - if (req.body.jsonrpc !== '2.0') { - console.log('No valid JSON RPC call received', JSON.stringify(req.body)) - return sendErrorResponse(res, 200, { - id: req.body.id, - jsonrpc: '2.0', - error: 'No valid JSON RPC call received. No jsonrp version supplied', - code: -32600, - }) - } else if (!id || !method) { - console.log('No valid JSON RPC call received', JSON.stringify(req.body)) - return sendErrorResponse(res, 200, { - id: req.body.id, - jsonrpc: '2.0', - error: 'No valid JSON RPC call received', - code: -32600, - }) - } - const result = provider.request({method, params}) - provider.authorizeAll() - const respBody = {id, jsonrpc: '2.0', result: await result} - res.json(respBody) - console.log(`RESPONSE for ${method}:\r\n${JSON.stringify(respBody, null, 2)}`) - } catch (error) { - console.log(error.message) - let msg = error.message - if (`body` in error) { - msg = error.body - return sendErrorResponse(res, 200, msg) - // res.json(error.body) - } else { - return sendErrorResponse(res, 200, { - id: req.body.id, - jsonrpc: '2.0', - error: msg, - code: error.code ?? -32000, - }) - } - } - return next() + // todo: A Notification is a Request object without an "id" member. + // A Request object that is a Notification signifies the Client's lack of interest in the corresponding Response object, + // and as such no Response object needs to be returned to the client. The Server MUST NOT reply to a Notification, including those that are within a batch request. + if (req.body.jsonrpc !== '2.0') { + console.log('No valid JSON RPC call received', JSON.stringify(req.body)) + return sendErrorResponse(res, 200, { + id: req.body.id, + jsonrpc: '2.0', + error: 'No valid JSON RPC call received. No jsonrp version supplied', + code: -32600, + }) + } else if (!id || !method) { + console.log('No valid JSON RPC call received', JSON.stringify(req.body)) + return sendErrorResponse(res, 200, { + id: req.body.id, + jsonrpc: '2.0', + error: 'No valid JSON RPC call received', + code: -32600, + }) } - ) - express.use(opts.basePath ?? '', router) + const result = provider.request({ method, params }) + provider.authorizeAll() + const respBody = { id, jsonrpc: '2.0', result: await result } + res.json(respBody) + console.log(`RESPONSE for ${method}:\r\n${JSON.stringify(respBody, null, 2)}`) + } catch (error) { + console.log(error.message) + let msg = error.message + if (`body` in error) { + msg = error.body + return sendErrorResponse(res, 200, msg) + // res.json(error.body) + } else { + return sendErrorResponse(res, 200, { + id: req.body.id, + jsonrpc: '2.0', + error: msg, + code: error.code ?? -32000, + }) + } + } + return next() + } + ) + express.use(opts.basePath ?? '', router) } export function createServiceMethod(method: string, service: Record, provider: EthersHeadlessProvider) { - service[method] = async (params: any) => { - // @ts-ignore - const result = provider.request({method, params}) + service[method] = async (params: any) => { + // @ts-ignore + const result = provider.request({ method, params }) - provider.authorizeAll() - return await result - } + provider.authorizeAll() + return await result + } } export function createService(provider: EthersHeadlessProvider) { - const service = {} - for (const method of Object.values(Web3Method)) { - createServiceMethod(method, service, provider) - } + const service = {} + for (const method of Object.values(Web3Method)) { + createServiceMethod(method, service, provider) + } - return service + return service } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e283468ac..872c25ba8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -280,6 +280,9 @@ importers: specifier: ^8.3.2 version: 8.3.2 devDependencies: + '@decentralized-identity/ion-sdk': + specifier: ^0.6.0 + version: 0.6.0 '@types/body-parser': specifier: ^1.19.2 version: 1.19.2 @@ -319,6 +322,12 @@ importers: '@veramo/data-store': specifier: 4.2.0 version: 4.2.0(patch_hash=feb5u2ygzsdf67qbxr2lxgqjyy)(sqlite3@5.1.6)(ts-node@10.9.1) + '@veramo/did-manager': + specifier: 4.2.0 + version: 4.2.0 + '@veramo/did-provider-ion': + specifier: 4.2.0 + version: 4.2.0(@sphereon/react-native-argon2@2.0.9)(react-native@0.72.4) '@veramo/key-manager': specifier: 4.2.0 version: 4.2.0