From a78b18df511402fd79bbbe7b511b68984373dd2d Mon Sep 17 00:00:00 2001 From: Peter Somogyvari Date: Wed, 16 Dec 2020 13:47:59 -0800 Subject: [PATCH] refactor(core-api): normalize consortium data model Resolves #351 Signed-off-by: Peter Somogyvari --- .../src/main/typescript/supply-chain-app.ts | 47 ++-- packages/cactus-api-client/package.json | 1 + .../src/main/typescript/api-client.ts | 46 ++-- .../typescript/default-consortium-provider.ts | 21 +- .../main/typescript/config/config-service.ts | 50 +++-- .../src/main/json/generated/openapi-spec.json | 167 +++++++++----- .../generated/openapi/typescript-axios/api.ts | 125 +++++++---- .../src/main/typescript/openapi-spec.ts | 207 +++++++++++++----- .../main/typescript/consortium-repository.ts | 64 ++++++ .../src/main/typescript/index.web.ts | 5 +- .../src/main/typescript/public-api.ts | 4 + .../get-consortium-jws-endpoint-v1.ts | 12 +- .../consortium/get-node-jws-endpoint-v1.ts | 17 +- .../typescript/plugin-consortium-manual.ts | 24 +- .../get-node-jws-endpoint-v1.test.ts | 24 +- .../api-client-routing-node-to-node.test.ts | 88 +++++--- .../get-consortium-jws-endpoint.test.ts | 117 +++++----- ...security-isolation-via-api-server-ports.ts | 19 +- 18 files changed, 695 insertions(+), 343 deletions(-) create mode 100644 packages/cactus-core/src/main/typescript/consortium-repository.ts diff --git a/examples/cactus-example-supply-chain-backend/src/main/typescript/supply-chain-app.ts b/examples/cactus-example-supply-chain-backend/src/main/typescript/supply-chain-app.ts index 61e85705a6..ebf69fba07 100644 --- a/examples/cactus-example-supply-chain-backend/src/main/typescript/supply-chain-app.ts +++ b/examples/cactus-example-supply-chain-backend/src/main/typescript/supply-chain-app.ts @@ -8,7 +8,9 @@ import exitHook, { IAsyncExitHookDoneCallback } from "async-exit-hook"; import { CactusNode, Consortium, + ConsortiumDatabase, ConsortiumMember, + Ledger, LedgerType, } from "@hyperledger/cactus-core-api"; @@ -110,13 +112,13 @@ export class SupplyChainApp { const keyPairB = await JWK.generate("EC", "secp256k1"); const keyPairPemB = keyPairB.toPEM(true); - const consortium = this.createConsortium( + const consortiumDatabase = this.createConsortium( httpApiA, httpApiB, keyPairA, keyPairB ); - const consortiumPrettyJson = JSON.stringify(consortium, null, 4); + const consortiumPrettyJson = JSON.stringify(consortiumDatabase, null, 4); this.log.info(`Created Consortium definition: %o`, consortiumPrettyJson); this.log.info(`Configuring Cactus Node for Ledger A...`); @@ -127,7 +129,7 @@ export class SupplyChainApp { plugins: [ new PluginConsortiumManual({ instanceId: "PluginConsortiumManual_A", - consortium, + consortiumDatabase, keyPairPem: keyPairPemA, logLevel: this.options.logLevel, }), @@ -168,7 +170,7 @@ export class SupplyChainApp { plugins: [ new PluginConsortiumManual({ instanceId: "PluginConsortiumManual_B", - consortium, + consortiumDatabase, keyPairPem: keyPairPemB, logLevel: this.options.logLevel, }), @@ -219,7 +221,7 @@ export class SupplyChainApp { serverB: Server, keyPairA: JWK.ECKey, keyPairB: JWK.ECKey - ): Consortium { + ): ConsortiumDatabase { const consortiumName = "Example Supply Chain Consortium"; const consortiumId = uuidv4(); @@ -234,20 +236,21 @@ export class SupplyChainApp { publicKeyPem: keyPairA.toPEM(false), consortiumId, id: nodeIdA, - plugins: [], - ledgers: [], + pluginInstanceIds: [], + ledgerIds: [], }; const memberA: ConsortiumMember = { id: memberIdA, - nodes: [cactusNodeA], + nodeIds: [cactusNodeA.id], name: "Example Manufacturer Corp", }; - cactusNodeA.ledgers.push({ + const ledger1 = { id: "BesuDemoLedger", ledgerType: LedgerType.BESU1X, - }); + }; + cactusNodeA.ledgerIds.push(ledger1.id); const memberIdB = uuidv4(); const nodeIdB = uuidv4(); @@ -260,29 +263,39 @@ export class SupplyChainApp { publicKeyPem: keyPairB.toPEM(false), consortiumId, id: nodeIdB, - plugins: [], - ledgers: [], + pluginInstanceIds: [], + ledgerIds: [], }; const memberB: ConsortiumMember = { id: memberIdB, - nodes: [cactusNodeB], + nodeIds: [cactusNodeB.id], name: "Example Retailer Corp", }; - cactusNodeB.ledgers.push({ + const ledger2: Ledger = { id: "QuorumDemoLedger", ledgerType: LedgerType.QUORUM2X, - }); + }; + + cactusNodeB.ledgerIds.push(ledger2.id); const consortium: Consortium = { id: consortiumId, name: consortiumName, mainApiHost: nodeApiHostA, - members: [memberA, memberB], + memberIds: [memberA.id, memberB.id], + }; + + const consortiumDatabase: ConsortiumDatabase = { + cactusNode: [cactusNodeA, cactusNodeB], + consortium: [consortium], + consortiumMember: [memberA, memberB], + ledger: [ledger1, ledger2], + pluginInstance: [], }; - return consortium; + return consortiumDatabase; } public async startNode( diff --git a/packages/cactus-api-client/package.json b/packages/cactus-api-client/package.json index 70c983a44a..74a6e6c199 100644 --- a/packages/cactus-api-client/package.json +++ b/packages/cactus-api-client/package.json @@ -63,6 +63,7 @@ "homepage": "https://github.com/hyperledger/cactus#readme", "dependencies": { "@hyperledger/cactus-common": "^0.2.0", + "@hyperledger/cactus-core": "^0.2.0", "@hyperledger/cactus-core-api": "^0.2.0", "@hyperledger/cactus-plugin-consortium-manual": "^0.2.0", "axios": "0.19.2", diff --git a/packages/cactus-api-client/src/main/typescript/api-client.ts b/packages/cactus-api-client/src/main/typescript/api-client.ts index 3633e0f102..c829b0ec6f 100644 --- a/packages/cactus-api-client/src/main/typescript/api-client.ts +++ b/packages/cactus-api-client/src/main/typescript/api-client.ts @@ -1,10 +1,8 @@ import { Checks, IAsyncProvider, Objects } from "@hyperledger/cactus-common"; -import { - Consortium, - ConsortiumMember, - CactusNode, - Ledger, -} from "@hyperledger/cactus-core-api"; + +import { ConsortiumDatabase, Ledger } from "@hyperledger/cactus-core-api"; + +import { ConsortiumRepository } from "@hyperledger/cactus-core"; import { DefaultApi as ApiConsortium } from "@hyperledger/cactus-plugin-consortium-manual"; import { DefaultConsortiumProvider } from "./default-consortium-provider"; @@ -78,7 +76,7 @@ export class ApiClient extends DefaultApi { * * @see {DefaultConsortiumProvider} */ - public get defaultConsortiumProvider(): IAsyncProvider { + public get defaultConsortiumDbProvider(): IAsyncProvider { Checks.truthy(this.configuration, "ApiClient#configuration"); const apiClient = new ApiConsortium(this.configuration); return new DefaultConsortiumProvider({ apiClient }); @@ -101,20 +99,21 @@ export class ApiClient extends DefaultApi { * * @param ledgerOrId The ID of the ledger to obtain an API client object for * or the `Ledger` object which will be used to get the ledgerId from. - * @param consortiumProvider The provider that can be used to retrieve the + * @param consortiumDbProvider The provider that can be used to retrieve the * consortium metadata at runtime for the purposes of looking up ledgers by * the provided `ledgerId` parameter. */ public async ofLedger( ledgerOrId: string | Ledger, ctor: new (configuration?: Configuration) => T, - consortiumProvider: IAsyncProvider = this - .defaultConsortiumProvider + consortiumDbProvider?: IAsyncProvider ): Promise { const fnTags = "ApiClient#forLedgerId()"; + const provider = consortiumDbProvider || this.defaultConsortiumDbProvider; + Checks.truthy(ledgerOrId, `${fnTags}:ledgerOrId`); - Checks.truthy(consortiumProvider, `${fnTags}:consortiumProvider`); + Checks.truthy(provider, `${fnTags}:consortiumDbProvider`); let ledgerId: string; if (typeof ledgerOrId === "string") { @@ -123,22 +122,16 @@ export class ApiClient extends DefaultApi { ledgerId = ledgerOrId.id; } - const consortium: Consortium = await consortiumProvider.get(); - Checks.truthy(consortiumProvider, `${fnTags}:consortium`); + const db: ConsortiumDatabase = await provider.get(); + const repo = new ConsortiumRepository({ db }); - // Find a list of nodes in the consortium that have a connector plugin - // running that's associated with the ledger based on ledger ID. - const nodes = consortium.members - .map((member: ConsortiumMember) => - member.nodes.filter((node: CactusNode) => - node.ledgers.some((ledger: Ledger) => ledger.id === ledgerId) - ) - ) - .flat(); + const nodes = repo.nodesWithLedger(ledgerId); // pick a random element from the array of nodes that have a connection to // the target ledger (based on the ledger ID) - const randomNode = nodes[Math.floor(Math.random() * nodes.length)]; + const randomIdx = Math.floor(Math.random() * nodes.length); + + const randomNode = nodes[randomIdx]; const configuration = new Configuration({ basePath: randomNode.nodeApiHost, @@ -147,10 +140,3 @@ export class ApiClient extends DefaultApi { return new ApiClient(configuration).extendWith(ctor); } } - -// type UnionToIntersection = -// (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never - -// function extendWith(...args: A): UnionToIntersection { return null! } - -// extendWith(new DefaultApi(), new ApiConsortium()); diff --git a/packages/cactus-api-client/src/main/typescript/default-consortium-provider.ts b/packages/cactus-api-client/src/main/typescript/default-consortium-provider.ts index 56fa47a4da..3698c84ca0 100644 --- a/packages/cactus-api-client/src/main/typescript/default-consortium-provider.ts +++ b/packages/cactus-api-client/src/main/typescript/default-consortium-provider.ts @@ -4,7 +4,7 @@ import { LoggerProvider, } from "@hyperledger/cactus-common"; import { Checks, IAsyncProvider } from "@hyperledger/cactus-common"; -import { Consortium } from "@hyperledger/cactus-core-api"; +import { Consortium, ConsortiumDatabase } from "@hyperledger/cactus-core-api"; import { DefaultApi, GetConsortiumJwsResponse, @@ -15,7 +15,8 @@ export interface IDefaultConsortiumProviderOptions { apiClient: DefaultApi; } -export class DefaultConsortiumProvider implements IAsyncProvider { +export class DefaultConsortiumProvider + implements IAsyncProvider { public static readonly CLASS_NAME = "DefaultConsortiumProvider"; private readonly log: Logger; @@ -33,7 +34,7 @@ export class DefaultConsortiumProvider implements IAsyncProvider { this.log = LoggerProvider.getOrCreate({ level, label }); } - parseConsortiumJws(response: GetConsortiumJwsResponse): Consortium { + parseConsortiumJws(response: GetConsortiumJwsResponse): ConsortiumDatabase { const fnTag = `DefaultConsortiumProvider#parseConsortiumJws()`; Checks.truthy(response, `${fnTag}::response`); @@ -41,24 +42,28 @@ export class DefaultConsortiumProvider implements IAsyncProvider { Checks.truthy(response.jws.payload, `${fnTag}::response.jws.payload`); const json = Buffer.from(response.jws.payload, "base64").toString(); - const consortium = JSON.parse(json)?.consortium as Consortium; + const body = JSON.parse(json); + const { + consortiumDatabase, + }: { consortiumDatabase: ConsortiumDatabase } = body; - Checks.truthy(consortium, `${fnTag}::consortium`); + Checks.truthy(consortiumDatabase, `${fnTag}::consortiumDatabase`); // FIXME Ideally there would be an option here to validate the JWS based on // all the signatures and the corresponding public keys (which the caller // would have to be able to supply). // We do not yet have this crypto functions available in a cross platform // manner so it is omitted for now but much needed prior to any GA release. - return consortium; + return consortiumDatabase; } - public async get(): Promise { + public async get(): Promise { try { const res = await this.options.apiClient.apiV1PluginsHyperledgerCactusPluginConsortiumManualConsortiumJwsGet(); return this.parseConsortiumJws(res.data); } catch (ex) { - this.log.error(`Request for Consortium JWS failed: `, ex?.toJSON()); + const innerException = (ex.toJSON && ex.toJSON()) || ex; + this.log.error(`Request for Consortium JWS failed: `, innerException); throw ex; } } diff --git a/packages/cactus-cmd-api-server/src/main/typescript/config/config-service.ts b/packages/cactus-cmd-api-server/src/main/typescript/config/config-service.ts index e135cd154e..124d31e4b5 100644 --- a/packages/cactus-cmd-api-server/src/main/typescript/config/config-service.ts +++ b/packages/cactus-cmd-api-server/src/main/typescript/config/config-service.ts @@ -11,7 +11,7 @@ import { } from "@hyperledger/cactus-common"; import { FORMAT_PLUGIN_ARRAY } from "./convict-plugin-array-format"; import { SelfSignedPkiGenerator, IPki } from "./self-signed-pki-generator"; -import { Consortium } from "@hyperledger/cactus-core-api"; +import { Consortium, ConsortiumDatabase } from "@hyperledger/cactus-core-api"; convict.addFormat(FORMAT_PLUGIN_ARRAY); convict.addFormat(ipaddress); @@ -411,40 +411,44 @@ export class ConfigService { const keyPair = JWK.generateSync("EC", "secp256k1", { use: "sig" }, true); const keyPairPem = keyPair.toPEM(true); const memberId1 = "Cactus_Example_Consortium_Member_1"; - const consortium: Consortium = { - name: "Example Cactus Consortium", - id: uuidV4(), - mainApiHost: apiBaseUrl, - members: [ + const nodeId1 = "Cactus_Example_Consortium_Node_1"; + const consortium: ConsortiumDatabase = { + cactusNode: [ + { + consortiumId: "Cactus_Example_Consortium", + id: nodeId1, + ledgerIds: [], + memberId: memberId1, + pluginInstanceIds: [], + nodeApiHost: apiBaseUrl, + publicKeyPem: keyPair.toPEM(false), + }, + ], + consortiumMember: [ { id: memberId1, name: "Example Cactus Consortium Member 1", - nodes: [ - { - consortiumId: "Cactus_Example_Consortium", - id: "Cactus_Example_Consortium_Node_1", - ledgers: [], - memberId: memberId1, - plugins: [], - nodeApiHost: apiBaseUrl, - publicKeyPem: keyPair.toPEM(false), - }, - ], + nodeIds: [nodeId1], + }, + ], + ledger: [], + pluginInstance: [], + consortium: [ + { + name: "Example Cactus Consortium", + id: uuidV4(), + mainApiHost: apiBaseUrl, + memberIds: [memberId1], }, ], }; - const cockpitTlsEnabled: boolean = (schema.cockpitTlsEnabled as SchemaObj) - .default; + const cockpitTlsEnabled = (schema.cockpitTlsEnabled as SchemaObj).default; const cockpitHost = (schema.cockpitHost as SchemaObj).default; const cockpitPort = (schema.cockpitPort as SchemaObj).default; - // const cockpitProtocol = cockpitTlsEnabled ? "https:" : "http"; - // const cockpitBaseUrl = `${cockpitProtocol}//${cockpitHost}:${cockpitPort}`; - const pkiGenerator = new SelfSignedPkiGenerator(); const pkiServer: IPki = pkiGenerator.create("localhost"); - // const pkiClient: IPki = pkiGenerator.create("localhost", pkiServer); const plugins = [ { diff --git a/packages/cactus-core-api/src/main/json/generated/openapi-spec.json b/packages/cactus-core-api/src/main/json/generated/openapi-spec.json index d02e09597a..e7c2b65aaf 100644 --- a/packages/cactus-core-api/src/main/json/generated/openapi-spec.json +++ b/packages/cactus-core-api/src/main/json/generated/openapi-spec.json @@ -27,6 +27,92 @@ ], "components": { "schemas": { + "PrimaryKey": { + "type": "string", + "minLength": 1, + "maxLength": 128, + "nullable": false + }, + "ConsortiumMemberId": { + "$ref": "#/components/schemas/PrimaryKey", + "description": "ID of Consortium member who operates the ledger (if any). Defined as an optional property in case the ledger is a permissionless and/or public one such as the Bitcoin or Ethereum mainnets." + }, + "CactusNodeId": { + "$ref": "#/components/schemas/PrimaryKey", + "description": "ID of a Cactus node that must uniquely distinguish it from all other Cactus nodes within a Consortium. Note that API server instances do not have their own identity the way a node does." + }, + "ConsortiumId": { + "$ref": "#/components/schemas/PrimaryKey" + }, + "LedgerId": { + "description": "String that uniquely identifies a ledger within a Cactus consortium so that transactions can be routed to the correct ledger.", + "$ref": "#/components/schemas/PrimaryKey" + }, + "PluginInstanceId": { + "description": "String that uniquely identifies a plugin instance within a Cactus consortium so that requests can be addressed/routed directly to individual plugins when necessary.", + "$ref": "#/components/schemas/PrimaryKey" + }, + "ConsortiumDatabase": { + "required": [ + "consortium", + "ledger", + "consortiumMember", + "cactusNode", + "pluginInstance" + ], + "properties": { + "consortium": { + "description": "A collection of Consortium entities. In practice this should only ever contain a single consortium, but we defined it as an array to keep the convention up with the rest of the collections defined in the Consortium data in general. Also, if we ever decide to somehow have some sort of consortium to consortium integration (which does not make much sense in the current frame of mind of the author in the year 2020) then having this as an array will have proven itself to be an excellent long term compatibility/extensibility decision indeed.", + "type": "array", + "items": { + "$ref": "#/components/schemas/Consortium" + }, + "default": [], + "minItems": 0, + "maxItems": 2048 + }, + "ledger": { + "description": "The complete collection of all ledger entities inexistence within the consortium.", + "type": "array", + "items": { + "$ref": "#/components/schemas/Ledger" + }, + "default": [], + "minItems": 0, + "maxItems": 2048 + }, + "consortiumMember": { + "description": "The complete collection of all consortium member entities in existence within the consortium.", + "type": "array", + "items": { + "$ref": "#/components/schemas/ConsortiumMember" + }, + "default": [], + "minItems": 0, + "maxItems": 2048 + }, + "cactusNode": { + "description": "The complete collection of all cactus nodes entities in existence within the consortium.", + "type": "array", + "items": { + "$ref": "#/components/schemas/CactusNode" + }, + "default": [], + "minItems": 0, + "maxItems": 2048 + }, + "pluginInstance": { + "description": "The complete collection of all plugin instance entities in existence within the consortium.", + "type": "array", + "items": { + "$ref": "#/components/schemas/PluginInstance" + }, + "default": [], + "minItems": 0, + "maxItems": 2048 + } + } + }, "Ledger": { "type": "object", "required": [ @@ -35,19 +121,14 @@ ], "properties": { "id": { - "description": "String that uniquely identifies a ledger within a Cactus consortium so that transactions can be routed to the correct ledger.", - "type": "string", - "nullable": false, - "minLength": 1, - "maxLength": 128 + "$ref": "#/components/schemas/LedgerId" }, "ledgerType": { "$ref": "#/components/schemas/LedgerType", "nullable": false }, - "operator": { - "description": "The consortium member who is operating the ledger. Defined as an optional property in case the ledger is a permissionless one such as the Bitcoin or Ethereum mainnets.", - "$ref": "#/components/schemas/ConsortiumMember" + "consortiumMemberId": { + "$ref": "#/components/schemas/ConsortiumMemberId" } } }, @@ -71,11 +152,11 @@ "id", "name", "mainApiHost", - "members" + "memberIds" ], "properties": { "id": { - "type": "string" + "$ref": "#/components/schemas/ConsortiumId" }, "name": { "type": "string" @@ -83,11 +164,13 @@ "mainApiHost": { "type": "string" }, - "members": { + "memberIds": { + "description": "The collection (array) of primary keys of consortium member entities that belong to this Consortium.", "type": "array", "items": { - "$ref": "#/components/schemas/ConsortiumMember" + "$ref": "#/components/schemas/ConsortiumMemberId" }, + "default": [], "minItems": 1, "maxItems": 2048, "nullable": false @@ -99,28 +182,27 @@ "required": [ "id", "name", - "nodes" + "nodeIds" ], "properties": { "id": { - "type": "string", - "minLength": 1, - "maxLength": 2048, - "nullable": false + "$ref": "#/components/schemas/ConsortiumMemberId" }, "name": { "type": "string", + "description": "The human readable name a Consortium member can be referred to while making it easy for humans to distinguish this particular consortium member entity from any other ones.", "minLength": 1, "maxLength": 2048, "nullable": false }, - "nodes": { + "nodeIds": { "type": "array", + "default": [], "nullable": false, "minItems": 1, "maxItems": 2048, "items": { - "$ref": "#/components/schemas/CactusNode" + "$ref": "#/components/schemas/CactusNodeId" } } } @@ -164,70 +246,57 @@ "nodeApiHost", "memberId", "publicKeyPem", - "plugins", - "ledgers" + "pluginInstanceIds", + "ledgerIds" ], "properties": { "id": { - "type": "string", - "description": "The unique identifier of a Cactus node. Recommended to assign a value to this that is guaranteed to be unique in the whole consortium or better yet, globally anywhere.", - "example": "809a76ba-cfb8-4045-a5c6-ed70a7314c25", - "minLength": 1, - "maxLength": 1024, - "nullable": false + "$ref": "#/components/schemas/CactusNodeId", + "example": "809a76ba-cfb8-4045-a5c6-ed70a7314c25" }, "consortiumId": { - "type": "string", + "$ref": "#/components/schemas/ConsortiumId", "description": "ID of the Cactus Consortium this node is in.", - "example": "3e2670d9-2d14-45bd-96f5-33e2c4b4e3fb", - "minLength": 1, - "maxLength": 1024, - "nullable": false + "example": "3e2670d9-2d14-45bd-96f5-33e2c4b4e3fb" }, "memberId": { - "type": "string", - "description": "ID of the Cactus Consortium member this node is operated by.", - "example": "b3674a28-e442-4feb-b1f3-8cbe46c20e5e", - "minLength": 1, - "maxLength": 1024, - "nullable": false + "$ref": "#/components/schemas/ConsortiumMemberId", + "example": "b3674a28-e442-4feb-b1f3-8cbe46c20e5e" }, - "ledgers": { - "description": "Stores an array of Ledger entities that are reachable (routable) via this Cactus Node. This information is used by the client side SDK API client to figure out at runtime where to send API requests that are specific to a certain ledger such as requests to execute transactions.", + "ledgerIds": { + "description": "Stores an array of Ledger entity IDs that are reachable (routable) via this Cactus Node. This information is used by the client side SDK API client to figure out at runtime where to send API requests that are specific to a certain ledger such as requests to execute transactions.", "type": "array", "nullable": false, "minItems": 0, "maxItems": 2048, "default": [], "items": { - "$ref": "#/components/schemas/Ledger" + "$ref": "#/components/schemas/LedgerId" } }, - "plugins": { + "pluginInstanceIds": { "type": "array", "nullable": false, "minItems": 0, "maxItems": 2048, "default": [], "items": { - "$ref": "#/components/schemas/CactusPlugin" + "$ref": "#/components/schemas/PluginInstanceId" } } } } ] }, - "CactusPlugin": { + "PluginInstance": { "type": "object", "required": [ - "id" + "id", + "packageName" ], "properties": { "id": { - "type": "string", - "minLength": 1, - "maxLength": 1024, - "nullable": false + "$ref": "PluginInstanceId" }, "packageName": { "type": "string", diff --git a/packages/cactus-core-api/src/main/typescript/generated/openapi/typescript-axios/api.ts b/packages/cactus-core-api/src/main/typescript/generated/openapi/typescript-axios/api.ts index e9f225f8b6..a7381e6850 100644 --- a/packages/cactus-core-api/src/main/typescript/generated/openapi/typescript-axios/api.ts +++ b/packages/cactus-core-api/src/main/typescript/generated/openapi/typescript-axios/api.ts @@ -38,35 +38,35 @@ export interface CactusNode { */ publicKeyPem: string; /** - * The unique identifier of a Cactus node. Recommended to assign a value to this that is guaranteed to be unique in the whole consortium or better yet, globally anywhere. + * * @type {string} * @memberof CactusNode */ id: string; /** - * ID of the Cactus Consortium this node is in. + * * @type {string} * @memberof CactusNode */ consortiumId: string; /** - * ID of the Cactus Consortium member this node is operated by. + * * @type {string} * @memberof CactusNode */ memberId: string; /** - * Stores an array of Ledger entities that are reachable (routable) via this Cactus Node. This information is used by the client side SDK API client to figure out at runtime where to send API requests that are specific to a certain ledger such as requests to execute transactions. - * @type {Array} + * Stores an array of Ledger entity IDs that are reachable (routable) via this Cactus Node. This information is used by the client side SDK API client to figure out at runtime where to send API requests that are specific to a certain ledger such as requests to execute transactions. + * @type {Array} * @memberof CactusNode */ - ledgers: Array; + ledgerIds: Array; /** * - * @type {Array} + * @type {Array} * @memberof CactusNode */ - plugins: Array; + pluginInstanceIds: Array; } /** * @@ -75,35 +75,35 @@ export interface CactusNode { */ export interface CactusNodeAllOf { /** - * The unique identifier of a Cactus node. Recommended to assign a value to this that is guaranteed to be unique in the whole consortium or better yet, globally anywhere. + * * @type {string} * @memberof CactusNodeAllOf */ id: string; /** - * ID of the Cactus Consortium this node is in. + * * @type {string} * @memberof CactusNodeAllOf */ consortiumId: string; /** - * ID of the Cactus Consortium member this node is operated by. + * * @type {string} * @memberof CactusNodeAllOf */ memberId: string; /** - * Stores an array of Ledger entities that are reachable (routable) via this Cactus Node. This information is used by the client side SDK API client to figure out at runtime where to send API requests that are specific to a certain ledger such as requests to execute transactions. - * @type {Array} + * Stores an array of Ledger entity IDs that are reachable (routable) via this Cactus Node. This information is used by the client side SDK API client to figure out at runtime where to send API requests that are specific to a certain ledger such as requests to execute transactions. + * @type {Array} * @memberof CactusNodeAllOf */ - ledgers: Array; + ledgerIds: Array; /** * - * @type {Array} + * @type {Array} * @memberof CactusNodeAllOf */ - plugins: Array; + pluginInstanceIds: Array; } /** * A Cactus node meta information @@ -124,25 +124,6 @@ export interface CactusNodeMeta { */ publicKeyPem: string; } -/** - * - * @export - * @interface CactusPlugin - */ -export interface CactusPlugin { - /** - * - * @type {string} - * @memberof CactusPlugin - */ - id: string; - /** - * - * @type {string} - * @memberof CactusPlugin - */ - packageName?: string; -} /** * * @export @@ -168,11 +149,48 @@ export interface Consortium { */ mainApiHost: string; /** - * - * @type {Array} + * The collection (array) of primary keys of consortium member entities that belong to this Consortium. + * @type {Array} * @memberof Consortium */ - members: Array; + memberIds: Array; +} +/** + * + * @export + * @interface ConsortiumDatabase + */ +export interface ConsortiumDatabase { + /** + * A collection of Consortium entities. In practice this should only ever contain a single consortium, but we defined it as an array to keep the convention up with the rest of the collections defined in the Consortium data in general. Also, if we ever decide to somehow have some sort of consortium to consortium integration (which does not make much sense in the current frame of mind of the author in the year 2020) then having this as an array will have proven itself to be an excellent long term compatibility/extensibility decision indeed. + * @type {Array} + * @memberof ConsortiumDatabase + */ + consortium: Array; + /** + * The complete collection of all ledger entities inexistence within the consortium. + * @type {Array} + * @memberof ConsortiumDatabase + */ + ledger: Array; + /** + * The complete collection of all consortium member entities in existence within the consortium. + * @type {Array} + * @memberof ConsortiumDatabase + */ + consortiumMember: Array; + /** + * The complete collection of all cactus nodes entities in existence within the consortium. + * @type {Array} + * @memberof ConsortiumDatabase + */ + cactusNode: Array; + /** + * The complete collection of all plugin instance entities in existence within the consortium. + * @type {Array} + * @memberof ConsortiumDatabase + */ + pluginInstance: Array; } /** * @@ -187,17 +205,17 @@ export interface ConsortiumMember { */ id: string; /** - * + * The human readable name a Consortium member can be referred to while making it easy for humans to distinguish this particular consortium member entity from any other ones. * @type {string} * @memberof ConsortiumMember */ name: string; /** * - * @type {Array} + * @type {Array} * @memberof ConsortiumMember */ - nodes: Array; + nodeIds: Array; } /** * @@ -250,7 +268,7 @@ export interface JWSRecipient { */ export interface Ledger { /** - * String that uniquely identifies a ledger within a Cactus consortium so that transactions can be routed to the correct ledger. + * * @type {string} * @memberof Ledger */ @@ -263,10 +281,10 @@ export interface Ledger { ledgerType: LedgerType; /** * - * @type {ConsortiumMember} + * @type {string} * @memberof Ledger */ - operator?: ConsortiumMember; + consortiumMemberId?: string; } /** * Enumerates the different ledger vendors and their major versions encoded within the name of the LedgerType. For example \"BESU_1X\" involves all of the [1.0.0;2.0.0) where 1.0.0 is included and anything up until, but not 2.0.0. See: https://stackoverflow.com/a/4396303/698470 for further explanation. @@ -284,4 +302,23 @@ export enum LedgerType { SAWTOOTH1X = 'SAWTOOTH_1X' } +/** + * + * @export + * @interface PluginInstance + */ +export interface PluginInstance { + /** + * + * @type {string} + * @memberof PluginInstance + */ + id: string; + /** + * + * @type {string} + * @memberof PluginInstance + */ + packageName: string; +} diff --git a/packages/cactus-core-api/src/main/typescript/openapi-spec.ts b/packages/cactus-core-api/src/main/typescript/openapi-spec.ts index f6d9612ac6..7ce0603348 100644 --- a/packages/cactus-core-api/src/main/typescript/openapi-spec.ts +++ b/packages/cactus-core-api/src/main/typescript/openapi-spec.ts @@ -35,30 +35,136 @@ export const CACTUS_OPEN_API_JSON: OpenAPIV3.Document = { ], components: { schemas: { + PrimaryKey: { + type: "string", + minLength: 1, + maxLength: 128, + nullable: false, + }, + ConsortiumMemberId: { + $ref: "#/components/schemas/PrimaryKey", + description: + "ID of Consortium member who operates the ledger (if any). " + + "Defined as an optional property in case the ledger is a " + + "permissionless and/or public one such as the Bitcoin or " + + "Ethereum mainnets.", + }, + CactusNodeId: { + $ref: "#/components/schemas/PrimaryKey", + description: + "ID of a Cactus node that must uniquely distinguish it from all " + + "other Cactus nodes within a Consortium. Note that API server " + + "instances do not have their own identity the way a node does.", + }, + ConsortiumId: { + $ref: "#/components/schemas/PrimaryKey", + }, + LedgerId: { + description: + "String that uniquely identifies a ledger within a" + + " Cactus consortium so that transactions can be routed to the" + + " correct ledger.", + $ref: "#/components/schemas/PrimaryKey", + }, + PluginInstanceId: { + description: + "String that uniquely identifies a plugin instance within a" + + " Cactus consortium so that requests can be addressed/routed " + + " directly to individual plugins when necessary.", + $ref: "#/components/schemas/PrimaryKey", + }, + ConsortiumDatabase: { + required: [ + "consortium", + "ledger", + "consortiumMember", + "cactusNode", + "pluginInstance", + ], + properties: { + consortium: { + description: + "A collection of Consortium entities. In practice " + + "this should only ever contain a single consortium, but we " + + "defined it as an array to keep the convention up with the" + + " rest of the collections defined in the Consortium data in " + + "general. Also, if we ever decide to somehow have some sort " + + "of consortium to consortium integration (which does not make " + + "much sense in the current frame of mind of the author in the " + + "year 2020) then having this as an array will have proven " + + "itself to be an excellent long term compatibility/" + + "extensibility decision indeed.", + type: "array", + items: { + $ref: "#/components/schemas/Consortium", + }, + default: [], + minItems: 0, + maxItems: 2048, + }, + ledger: { + description: + "The complete collection of all ledger entities in" + + "existence within the consortium.", + type: "array", + items: { + $ref: "#/components/schemas/Ledger", + }, + default: [], + minItems: 0, + maxItems: 2048, + }, + consortiumMember: { + description: + "The complete collection of all consortium member" + + " entities in existence within the consortium.", + type: "array", + items: { + $ref: "#/components/schemas/ConsortiumMember", + }, + default: [], + minItems: 0, + maxItems: 2048, + }, + cactusNode: { + description: + "The complete collection of all cactus nodes" + + " entities in existence within the consortium.", + type: "array", + items: { + $ref: "#/components/schemas/CactusNode", + }, + default: [], + minItems: 0, + maxItems: 2048, + }, + pluginInstance: { + description: + "The complete collection of all plugin instance" + + " entities in existence within the consortium.", + type: "array", + items: { + $ref: "#/components/schemas/PluginInstance", + }, + default: [], + minItems: 0, + maxItems: 2048, + }, + }, + }, Ledger: { type: "object", required: ["id", "ledgerType"], properties: { id: { - description: - "String that uniquely identifies a ledger within a" + - " Cactus consortium so that transactions can be routed to the" + - " correct ledger.", - type: "string", - nullable: false, - minLength: 1, - maxLength: 128, + $ref: "#/components/schemas/LedgerId", }, ledgerType: { $ref: "#/components/schemas/LedgerType", nullable: false, }, - operator: { - description: - "The consortium member who is operating the ledger. " + - "Defined as an optional property in case the ledger is a " + - "permissionless one such as the Bitcoin or Ethereum mainnets.", - $ref: "#/components/schemas/ConsortiumMember", + consortiumMemberId: { + $ref: "#/components/schemas/ConsortiumMemberId", }, }, }, @@ -83,10 +189,10 @@ export const CACTUS_OPEN_API_JSON: OpenAPIV3.Document = { }, Consortium: { type: "object", - required: ["id", "name", "mainApiHost", "members"], + required: ["id", "name", "mainApiHost", "memberIds"], properties: { id: { - type: "string", + $ref: "#/components/schemas/ConsortiumId", }, name: { type: "string", @@ -94,11 +200,15 @@ export const CACTUS_OPEN_API_JSON: OpenAPIV3.Document = { mainApiHost: { type: "string", }, - members: { + memberIds: { + description: + "The collection (array) of primary keys of" + + " consortium member entities that belong to this Consortium.", type: "array", items: { - $ref: "#/components/schemas/ConsortiumMember", + $ref: "#/components/schemas/ConsortiumMemberId", }, + default: [], minItems: 1, maxItems: 2048, nullable: false, @@ -107,27 +217,29 @@ export const CACTUS_OPEN_API_JSON: OpenAPIV3.Document = { }, ConsortiumMember: { type: "object", - required: ["id", "name", "nodes"], + required: ["id", "name", "nodeIds"], properties: { id: { - type: "string", - minLength: 1, - maxLength: 2048, - nullable: false, + $ref: "#/components/schemas/ConsortiumMemberId", }, name: { type: "string", + description: + "The human readable name a Consortium member can be " + + "referred to while making it easy for humans to distinguish " + + "this particular consortium member entity from any other ones.", minLength: 1, maxLength: 2048, nullable: false, }, - nodes: { + nodeIds: { type: "array", + default: [], nullable: false, minItems: 1, maxItems: 2048, items: { - $ref: "#/components/schemas/CactusNode", + $ref: "#/components/schemas/CactusNodeId", }, }, }, @@ -175,42 +287,26 @@ export const CACTUS_OPEN_API_JSON: OpenAPIV3.Document = { "nodeApiHost", "memberId", "publicKeyPem", - "plugins", - "ledgers", + "pluginInstanceIds", + "ledgerIds", ], properties: { id: { - type: "string", - description: - "The unique identifier of a Cactus node. Recommended" + - " to assign a value to this that is guaranteed to be unique" + - " in the whole consortium or better yet, globally anywhere.", + $ref: "#/components/schemas/CactusNodeId", example: "809a76ba-cfb8-4045-a5c6-ed70a7314c25", - minLength: 1, - maxLength: 1024, - nullable: false, }, consortiumId: { - type: "string", + $ref: "#/components/schemas/ConsortiumId", description: "ID of the Cactus Consortium this node is in.", example: "3e2670d9-2d14-45bd-96f5-33e2c4b4e3fb", - minLength: 1, - maxLength: 1024, - nullable: false, }, memberId: { - type: "string", - description: - "ID of the Cactus Consortium member this " + - "node is operated by.", + $ref: "#/components/schemas/ConsortiumMemberId", example: "b3674a28-e442-4feb-b1f3-8cbe46c20e5e", - minLength: 1, - maxLength: 1024, - nullable: false, }, - ledgers: { + ledgerIds: { description: - "Stores an array of Ledger entities that are " + + "Stores an array of Ledger entity IDs that are " + "reachable (routable) via this Cactus Node. This " + "information is used by the client side SDK API client to " + "figure out at runtime where to send API requests that are " + @@ -222,32 +318,29 @@ export const CACTUS_OPEN_API_JSON: OpenAPIV3.Document = { maxItems: 2048, default: [], items: { - $ref: "#/components/schemas/Ledger", + $ref: "#/components/schemas/LedgerId", }, }, - plugins: { + pluginInstanceIds: { type: "array", nullable: false, minItems: 0, maxItems: 2048, default: [], items: { - $ref: "#/components/schemas/CactusPlugin", + $ref: "#/components/schemas/PluginInstanceId", }, }, }, }, ], }, - CactusPlugin: { + PluginInstance: { type: "object", - required: ["id"], + required: ["id", "packageName"], properties: { id: { - type: "string", - minLength: 1, - maxLength: 1024, - nullable: false, + $ref: "PluginInstanceId", }, packageName: { type: "string", diff --git a/packages/cactus-core/src/main/typescript/consortium-repository.ts b/packages/cactus-core/src/main/typescript/consortium-repository.ts new file mode 100644 index 0000000000..826ebeb40d --- /dev/null +++ b/packages/cactus-core/src/main/typescript/consortium-repository.ts @@ -0,0 +1,64 @@ +import { + Logger, + Checks, + LogLevelDesc, + LoggerProvider, +} from "@hyperledger/cactus-common"; + +import { CactusNode, ConsortiumDatabase } from "@hyperledger/cactus-core-api"; + +export interface IConsortiumRepositoryOptions { + logLevel?: LogLevelDesc; + db: ConsortiumDatabase; +} + +/** + * Class responsible for making it convenient for developers to query the + * `ConsortiumDatabase` model type which is a flat data structure storing + * all the different types of entities for Consortium representation such as + * `CactusNode`, `ConsortumMember`, `Ledger` etc.. + */ +export class ConsortiumRepository { + public static readonly CLASS_NAME = "ConsortiumRepository"; + + private readonly log: Logger; + private readonly db: ConsortiumDatabase; + + public get className() { + return ConsortiumRepository.CLASS_NAME; + } + + constructor(public readonly options: IConsortiumRepositoryOptions) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(options, `${fnTag} arg options`); + Checks.truthy(options.db, `${fnTag} arg options.db`); + Checks.truthy(options.db.cactusNode, `${fnTag} arg options.db.cactusNode`); + this.db = options.db; + + const level = this.options.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + } + + public get consortiumDatabase(): ConsortiumDatabase { + return this.options.db; + } + + public get allNodes(): CactusNode[] { + return this.options.db.cactusNode; + } + + /** + * Queries the complete list of nodes within the consortium to obtain a sub- + * set of `CactusNode`s which are connected to a `Ledger` with the given + * `ledgerId`. + * @param ledgerId The ID of the ledger to filter nodes based on. + * @throws {Error} If `ledgerId` is falsy or blank. + */ + public nodesWithLedger(ledgerId: string): CactusNode[] { + const fnTag = `${this.className}#nodesWithLedger()`; + Checks.nonBlankString(ledgerId, `${fnTag}:ledgerId`); + + return this.allNodes.filter((cn) => cn.ledgerIds.includes(ledgerId)); + } +} diff --git a/packages/cactus-core/src/main/typescript/index.web.ts b/packages/cactus-core/src/main/typescript/index.web.ts index cb0ff5c3b5..9a8dc78e58 100755 --- a/packages/cactus-core/src/main/typescript/index.web.ts +++ b/packages/cactus-core/src/main/typescript/index.web.ts @@ -1 +1,4 @@ -export {}; +export { + ConsortiumRepository, + IConsortiumRepositoryOptions, +} from "./consortium-repository"; diff --git a/packages/cactus-core/src/main/typescript/public-api.ts b/packages/cactus-core/src/main/typescript/public-api.ts index 111a92d7ab..e4f65b8754 100755 --- a/packages/cactus-core/src/main/typescript/public-api.ts +++ b/packages/cactus-core/src/main/typescript/public-api.ts @@ -1,2 +1,6 @@ export { registerWebServiceEndpoint } from "./web-services/register-web-service-endpoint"; export { IPluginRegistryOptions, PluginRegistry } from "./plugin-registry"; +export { + ConsortiumRepository, + IConsortiumRepositoryOptions, +} from "./consortium-repository"; diff --git a/packages/cactus-plugin-consortium-manual/src/main/typescript/consortium/get-consortium-jws-endpoint-v1.ts b/packages/cactus-plugin-consortium-manual/src/main/typescript/consortium/get-consortium-jws-endpoint-v1.ts index 70a78c3bd0..a66ef87871 100644 --- a/packages/cactus-plugin-consortium-manual/src/main/typescript/consortium/get-consortium-jws-endpoint-v1.ts +++ b/packages/cactus-plugin-consortium-manual/src/main/typescript/consortium/get-consortium-jws-endpoint-v1.ts @@ -22,13 +22,16 @@ import { LoggerProvider, } from "@hyperledger/cactus-common"; -import { registerWebServiceEndpoint } from "@hyperledger/cactus-core"; +import { + registerWebServiceEndpoint, + ConsortiumRepository, +} from "@hyperledger/cactus-core"; import { GetConsortiumEndpointV1 as Constants } from "./get-consortium-jws-endpoint-constants"; export interface IGetConsortiumJwsEndpointOptions { keyPairPem: string; - consortium: Consortium; + consortiumRepo: ConsortiumRepository; path: string; logLevel?: LogLevelDesc; } @@ -44,7 +47,7 @@ export class GetConsortiumEndpointV1 implements IWebServiceEndpoint { if (!options.keyPairPem) { throw new Error(`${fnTag} options.keyPairPem falsy.`); } - if (!options.consortium) { + if (!options.consortiumRepo) { throw new Error(`${fnTag} options.consortium falsy.`); } if (!options.path) { @@ -82,8 +85,7 @@ export class GetConsortiumEndpointV1 implements IWebServiceEndpoint { this.log.debug(`GET ${this.getPath()}`); try { - const nodes2d = this.options.consortium.members?.map((m) => m.nodes); - const nodes = flatten(nodes2d); + const nodes = this.options.consortiumRepo.allNodes; const requests: Promise>[] = nodes .map((cnm) => cnm.nodeApiHost) diff --git a/packages/cactus-plugin-consortium-manual/src/main/typescript/consortium/get-node-jws-endpoint-v1.ts b/packages/cactus-plugin-consortium-manual/src/main/typescript/consortium/get-node-jws-endpoint-v1.ts index c293051332..818e1a63b0 100644 --- a/packages/cactus-plugin-consortium-manual/src/main/typescript/consortium/get-node-jws-endpoint-v1.ts +++ b/packages/cactus-plugin-consortium-manual/src/main/typescript/consortium/get-node-jws-endpoint-v1.ts @@ -16,15 +16,19 @@ import { Logger, LogLevelDesc, LoggerProvider, + Checks, } from "@hyperledger/cactus-common"; -import { registerWebServiceEndpoint } from "@hyperledger/cactus-core"; +import { + registerWebServiceEndpoint, + ConsortiumRepository, +} from "@hyperledger/cactus-core"; import { GetNodeJwsEndpoint as Constants } from "./get-node-jws-endpoint-constants"; export interface IGetNodeJwsEndpointOptions { keyPairPem: string; - consortium: Consortium; + consortiumRepo: ConsortiumRepository; path: string; logLevel?: LogLevelDesc; } @@ -43,6 +47,8 @@ export class GetNodeJwsEndpoint implements IWebServiceEndpoint { if (!options.path) { throw new Error(`${fnTag} options.path falsy.`); } + Checks.truthy(options.consortiumRepo, `${fnTag} options.consortiumRepo`); + const level = options.logLevel || "INFO"; const label = "get-node-jws-endpoint-v1"; this.log = LoggerProvider.getOrCreate({ level, label }); @@ -85,17 +91,18 @@ export class GetNodeJwsEndpoint implements IWebServiceEndpoint { public async createJws(): Promise { const fnTag = "GetNodeJwsEndpoint#createJws()"; - const { keyPairPem, consortium } = this.options; + const { keyPairPem, consortiumRepo: repo } = this.options; try { const keyPair = JWK.asKey(keyPairPem); - const payload = jsonStableStringify({ consortium }); + const payloadObject = { consortiumDatabase: repo.consortiumDatabase }; + const payloadJson = jsonStableStringify(payloadObject); const _protected = { iat: Date.now(), jti: uuid.v4(), iss: "Hyperledger Cactus", }; // TODO: double check if this casting is safe (it is supposed to be) - return JWS.sign.general(payload, keyPair, _protected) as JWSGeneral; + return JWS.sign.general(payloadJson, keyPair, _protected) as JWSGeneral; } catch (ex) { throw new Error(`${fnTag} ${ex.stack}`); } diff --git a/packages/cactus-plugin-consortium-manual/src/main/typescript/plugin-consortium-manual.ts b/packages/cactus-plugin-consortium-manual/src/main/typescript/plugin-consortium-manual.ts index eec78da12e..3803f24c7d 100644 --- a/packages/cactus-plugin-consortium-manual/src/main/typescript/plugin-consortium-manual.ts +++ b/packages/cactus-plugin-consortium-manual/src/main/typescript/plugin-consortium-manual.ts @@ -6,7 +6,7 @@ import express, { Express } from "express"; import bodyParser from "body-parser"; import { - Consortium, + ConsortiumDatabase, IPluginWebService, PluginAspect, IWebServiceEndpoint, @@ -14,7 +14,11 @@ import { ICactusPluginOptions, } from "@hyperledger/cactus-core-api"; -import { PluginRegistry } from "@hyperledger/cactus-core"; +import { + PluginRegistry, + IConsortiumRepositoryOptions, + ConsortiumRepository, +} from "@hyperledger/cactus-core"; import { Checks, @@ -24,7 +28,6 @@ import { } from "@hyperledger/cactus-common"; import { GetConsortiumEndpointV1 } from "./consortium/get-consortium-jws-endpoint-v1"; import { GetNodeJwsEndpoint } from "./consortium/get-node-jws-endpoint-v1"; -import uuid from "uuid"; export interface IWebAppOptions { port: number; @@ -33,7 +36,7 @@ export interface IWebAppOptions { export interface IPluginConsortiumManualOptions extends ICactusPluginOptions { keyPairPem: string; - consortium: Consortium; + consortiumDatabase: ConsortiumDatabase; pluginRegistry?: PluginRegistry; logLevel?: LogLevelDesc; webAppOptions?: IWebAppOptions; @@ -51,6 +54,10 @@ export class PluginConsortiumManual throw new Error(`${fnTag} options falsy.`); } Checks.truthy(options.instanceId, `${fnTag} options.instanceId`); + Checks.truthy( + options.consortiumDatabase, + `${fnTag} options.consortiumDatabase` + ); this.log = LoggerProvider.getOrCreate({ label: "plugin-consortium-manual", }); @@ -105,13 +112,16 @@ export class PluginConsortiumManual this.log.info(`Creation of HTTP server OK`, { address }); } - const { consortium, keyPairPem } = this.options; + const { consortiumDatabase, keyPairPem } = this.options; const packageName = this.getPackageName(); + const consortiumRepo = new ConsortiumRepository({ + db: consortiumDatabase, + }); const endpoints: IWebServiceEndpoint[] = []; { const path = `/api/v1/plugins/${packageName}/consortium/jws`; - const options = { path, keyPairPem, consortium }; + const options = { path, keyPairPem, consortiumRepo }; const endpoint = new GetConsortiumEndpointV1(options); webApp.get(endpoint.getPath(), endpoint.getExpressRequestHandler()); endpoints.push(endpoint); @@ -119,7 +129,7 @@ export class PluginConsortiumManual } { const path = `/api/v1/plugins/${packageName}/node/jws`; - const options = { path, keyPairPem, consortium }; + const options = { path, keyPairPem, consortiumRepo }; const endpoint = new GetNodeJwsEndpoint(options); webApp.get(endpoint.getPath(), endpoint.getExpressRequestHandler()); endpoints.push(endpoint); diff --git a/packages/cactus-plugin-consortium-manual/src/test/typescript/unit/consortium/get-node-jws-endpoint-v1.test.ts b/packages/cactus-plugin-consortium-manual/src/test/typescript/unit/consortium/get-node-jws-endpoint-v1.test.ts index 1a4097eb4e..c6dc449b39 100644 --- a/packages/cactus-plugin-consortium-manual/src/test/typescript/unit/consortium/get-node-jws-endpoint-v1.test.ts +++ b/packages/cactus-plugin-consortium-manual/src/test/typescript/unit/consortium/get-node-jws-endpoint-v1.test.ts @@ -5,9 +5,12 @@ import { v4 as uuidV4 } from "uuid"; import { CactusNode, Consortium, + ConsortiumDatabase, ConsortiumMember, } from "@hyperledger/cactus-core-api"; +import { ConsortiumRepository } from "@hyperledger/cactus-core"; + import { GetNodeJwsEndpoint, IGetNodeJwsEndpointOptions, @@ -30,13 +33,13 @@ test("Can provide JWS", async (t: Test) => { publicKeyPem: keyPair.toPEM(false), consortiumId, id: nodeId, - plugins: [], - ledgers: [], + pluginInstanceIds: [], + ledgerIds: [], }; const member: ConsortiumMember = { id: memberId, - nodes: [cactusNode], + nodeIds: [cactusNode.id], name: "Example Corp", }; @@ -44,11 +47,20 @@ test("Can provide JWS", async (t: Test) => { id: consortiumId, name: consortiumName, mainApiHost: "http://127.0.0.1:80", - members: [member], + memberIds: [member.id], + }; + + const db: ConsortiumDatabase = { + cactusNode: [], + consortium: [], + consortiumMember: [], + ledger: [], + pluginInstance: [], }; + const consortiumRepo = new ConsortiumRepository({ db }); const epOpts: IGetNodeJwsEndpointOptions = { - consortium, + consortiumRepo, keyPairPem, path: "/some-fake-path-for-http-requests", }; @@ -68,7 +80,7 @@ test("Can provide JWS", async (t: Test) => { if (typeof payload === "string") { t.fail(`JWS Verification result: ${payload}`); } else { - t.ok(payload.consortium, "JWS payload.consortium truthy"); + t.ok(payload.consortiumDatabase, "JWS payload.consortiumDatabase truthy"); } t.end(); diff --git a/packages/cactus-test-api-client/src/test/typescript/integration/api-client-routing-node-to-node.test.ts b/packages/cactus-test-api-client/src/test/typescript/integration/api-client-routing-node-to-node.test.ts index a6442cc79a..dd6ab487a6 100644 --- a/packages/cactus-test-api-client/src/test/typescript/integration/api-client-routing-node-to-node.test.ts +++ b/packages/cactus-test-api-client/src/test/typescript/integration/api-client-routing-node-to-node.test.ts @@ -7,7 +7,14 @@ import Web3 from "web3"; import { ApiClient } from "@hyperledger/cactus-api-client"; import { ApiServer, ConfigService } from "@hyperledger/cactus-cmd-api-server"; -import { Consortium, Ledger, LedgerType } from "@hyperledger/cactus-core-api"; +import { + CactusNode, + Consortium, + ConsortiumDatabase, + ConsortiumMember, + Ledger, + LedgerType, +} from "@hyperledger/cactus-core-api"; import { PluginRegistry } from "@hyperledger/cactus-core"; import { DefaultApi as QuorumApi, @@ -58,43 +65,54 @@ test("Routes to correct node based on ledger ID", async (t: Test) => { const consortiumName = "Example Corp. & Friends Crypto Consortium"; const memberId1 = uuidV4(); const memberId2 = uuidV4(); + + const node1: CactusNode = { + nodeApiHost: node1Host, + publicKeyPem: pubKeyPem1, + consortiumId, + id: uuidV4(), + ledgerIds: [ledger1.id], + memberId: memberId1, + pluginInstanceIds: [], + }; + + const member1: ConsortiumMember = { + id: memberId1, + name: "Example Corp 1", + nodeIds: [node1.id], + }; + + const node2: CactusNode = { + nodeApiHost: node2Host, + publicKeyPem: pubKeyPem2, + consortiumId, + id: uuidV4(), + ledgerIds: [ledger2.id], + memberId: memberId2, + pluginInstanceIds: [], + }; + + const member2: ConsortiumMember = { + id: memberId2, + name: "Example Corp 2", + nodeIds: [node2.id], + }; + const consortium: Consortium = { id: consortiumId, mainApiHost: node1Host, name: consortiumName, - members: [ - { - id: memberId1, - name: "Example Corp 1", - nodes: [ - { - nodeApiHost: node1Host, - publicKeyPem: pubKeyPem1, - consortiumId, - id: uuidV4(), - ledgers: [ledger1], - memberId: memberId1, - plugins: [], - }, - ], - }, - { - id: memberId2, - name: "Example Corp 2", - nodes: [ - { - nodeApiHost: node2Host, - publicKeyPem: pubKeyPem2, - consortiumId, - id: uuidV4(), - ledgers: [ledger2], - memberId: memberId2, - plugins: [], - }, - ], - }, - ], + memberIds: [member1.id, member2.id], }; + + const consortiumDatabase: ConsortiumDatabase = { + cactusNode: [node1, node2], + consortium: [consortium], + consortiumMember: [member1, member2], + ledger: [ledger1, ledger2], + pluginInstance: [], + }; + const mainApiClient = new ApiClient({ basePath: consortium.mainApiHost }); test("Set Up Test ledgers, Consortium, Cactus Nodes", async (t2: Test) => { @@ -137,7 +155,7 @@ test("Routes to correct node based on ledger ID", async (t: Test) => { instanceId: uuidV4(), pluginRegistry, keyPairPem: keyPair1.toPEM(true), - consortium, + consortiumDatabase, logLevel, }; const pluginConsortiumManual = new PluginConsortiumManual(options); @@ -178,7 +196,7 @@ test("Routes to correct node based on ledger ID", async (t: Test) => { instanceId: uuidV4(), pluginRegistry, keyPairPem: keyPair2.toPEM(true), - consortium, + consortiumDatabase, logLevel, }; const pluginConsortiumManual = new PluginConsortiumManual(options); diff --git a/packages/cactus-test-plugin-consortium-manual/src/test/typescript/integration/plugin-consortium-manual/get-consortium-jws-endpoint.test.ts b/packages/cactus-test-plugin-consortium-manual/src/test/typescript/integration/plugin-consortium-manual/get-consortium-jws-endpoint.test.ts index eeaf153732..b46f2f75c1 100644 --- a/packages/cactus-test-plugin-consortium-manual/src/test/typescript/integration/plugin-consortium-manual/get-consortium-jws-endpoint.test.ts +++ b/packages/cactus-test-plugin-consortium-manual/src/test/typescript/integration/plugin-consortium-manual/get-consortium-jws-endpoint.test.ts @@ -12,7 +12,12 @@ import { DefaultApi, Configuration, } from "@hyperledger/cactus-plugin-consortium-manual"; -import { Consortium } from "@hyperledger/cactus-core-api"; +import { + CactusNode, + Consortium, + ConsortiumDatabase, + ConsortiumMember, +} from "@hyperledger/cactus-core-api"; import { PluginRegistry } from "@hyperledger/cactus-core"; test("member node public keys and hosts are pre-shared", async (t: Test) => { @@ -66,57 +71,67 @@ test("member node public keys and hosts are pre-shared", async (t: Test) => { const pubKeyPem3 = keyPair3.toPEM(false); t.comment(`Cactus Node 3 Public Key PEM: ${pubKeyPem3}`); + const node1: CactusNode = { + consortiumId, + memberId: memberId1, + id: "Example_Cactus_Node_1", + nodeApiHost: node1Host, + publicKeyPem: pubKeyPem1, + ledgerIds: [], + pluginInstanceIds: [], + }; + + const member1: ConsortiumMember = { + id: memberId1, + name: "Example Corp 1", + nodeIds: [node1.id], + }; + + const node2: CactusNode = { + consortiumId, + memberId: memberId2, + id: "Example_Cactus_Node_2", + nodeApiHost: node2Host, + publicKeyPem: pubKeyPem2, + ledgerIds: [], + pluginInstanceIds: [], + }; + + const member2: ConsortiumMember = { + id: memberId2, + name: "Example Corp 2", + nodeIds: [node2.id], + }; + + const node3: CactusNode = { + consortiumId, + memberId: memberId3, + id: "Example_Cactus_Node_3", + nodeApiHost: node3Host, + publicKeyPem: pubKeyPem3, + ledgerIds: [], + pluginInstanceIds: [], + }; + + const member3: ConsortiumMember = { + id: memberId3, + name: "Example Corp 3", + nodeIds: [node3.id], + }; + const consortium: Consortium = { id: consortiumId, mainApiHost: node1Host, name: consortiumName, - members: [ - { - id: memberId1, - name: "Example Corp 1", - nodes: [ - { - consortiumId, - memberId: memberId1, - id: "Example_Cactus_Node_1", - nodeApiHost: node1Host, - publicKeyPem: pubKeyPem1, - ledgers: [], - plugins: [], - }, - ], - }, - { - id: memberId2, - name: "Example Corp 2", - nodes: [ - { - consortiumId, - memberId: memberId2, - id: "Example_Cactus_Node_2", - nodeApiHost: node2Host, - publicKeyPem: pubKeyPem2, - ledgers: [], - plugins: [], - }, - ], - }, - { - id: memberId3, - name: "Example Corp 3", - nodes: [ - { - consortiumId, - memberId: memberId3, - id: "Example_Cactus_Node_3", - nodeApiHost: node3Host, - publicKeyPem: pubKeyPem3, - ledgers: [], - plugins: [], - }, - ], - }, - ], + memberIds: [memberId1, memberId2, memberId3], + }; + + const consortiumDatabase: ConsortiumDatabase = { + cactusNode: [node1, node2, node3], + consortium: [consortium], + consortiumMember: [member1, member2, member3], + ledger: [], + pluginInstance: [], }; t.comment(`Setting up first node...`); @@ -129,7 +144,7 @@ test("member node public keys and hosts are pre-shared", async (t: Test) => { instanceId: uuidV4(), pluginRegistry, keyPairPem: keyPair1.toPEM(true), - consortium, + consortiumDatabase, logLevel: "trace", }; const pluginConsortiumManual = new PluginConsortiumManual(options); @@ -182,7 +197,7 @@ test("member node public keys and hosts are pre-shared", async (t: Test) => { instanceId: uuidV4(), pluginRegistry, keyPairPem: keyPair2.toPEM(true), - consortium, + consortiumDatabase, logLevel: "trace", }; const pluginConsortiumManual = new PluginConsortiumManual(options); @@ -236,7 +251,7 @@ test("member node public keys and hosts are pre-shared", async (t: Test) => { instanceId: uuidV4(), pluginRegistry, keyPairPem: keyPair3.toPEM(true), - consortium, + consortiumDatabase, logLevel: "trace", }; const pluginConsortiumManual = new PluginConsortiumManual(options); diff --git a/packages/cactus-test-plugin-consortium-manual/src/test/typescript/integration/plugin-consortium-manual/security-isolation-via-api-server-ports.ts b/packages/cactus-test-plugin-consortium-manual/src/test/typescript/integration/plugin-consortium-manual/security-isolation-via-api-server-ports.ts index 8e7203ae68..123aa84088 100644 --- a/packages/cactus-test-plugin-consortium-manual/src/test/typescript/integration/plugin-consortium-manual/security-isolation-via-api-server-ports.ts +++ b/packages/cactus-test-plugin-consortium-manual/src/test/typescript/integration/plugin-consortium-manual/security-isolation-via-api-server-ports.ts @@ -10,6 +10,7 @@ import { CactusNode, ConsortiumMember, Consortium, + ConsortiumDatabase, } from "@hyperledger/cactus-core-api"; import { PluginRegistry } from "@hyperledger/cactus-core"; @@ -50,13 +51,13 @@ tap.test( publicKeyPem: keyPair.toPEM(false), consortiumId, id: nodeId, - plugins: [], - ledgers: [], + pluginInstanceIds: [], + ledgerIds: [], }; const member: ConsortiumMember = { id: memberId, - nodes: [cactusNode], + nodeIds: [cactusNode.id], name: "Example Corp", }; @@ -64,7 +65,15 @@ tap.test( id: consortiumId, name: consortiumName, mainApiHost: "http://127.0.0.1:80", - members: [member], + memberIds: [member.id], + }; + + const consortiumDatabase: ConsortiumDatabase = { + cactusNode: [cactusNode], + consortiumMember: [member], + ledger: [], + pluginInstance: [], + consortium: [consortium], }; // 3. Instantiate the web service consortium plugin which will host itself on a new TCP port for isolation/security @@ -74,7 +83,7 @@ tap.test( instanceId: uuidV4(), pluginRegistry, keyPairPem, - consortium, + consortiumDatabase, logLevel: "trace", webAppOptions: { hostname: "127.0.0.1",