Skip to content

Commit e50d9ce

Browse files
committed
feat(core-api): add instanceId getter to ICactusPlugin
This will enable us to differentiate between instances of the same plugin class at runtime which is a pre- requisite for having intra-node routing among plugins and also for better supporting fine grained dependency injection at runtime where a certain plugin wants to import another one and it's not enough to have the plugin aspect available or even the exact class that was instantiated. An example of the above situation would be where you have multiple different ledger connectors who all need their own keychain plugins that may be connecting to different underlying key management solutions because of some exotic deployment topology where let's say you keep your keys for your Fabric legder in a Vault instance but the Corda or Besu related private keys are in the built-in KMS of your cloud provider of choice. In this situation you might need two separate instances of the keychain plugin and the ledger connectors need to have the ability to import exactly the instance of them that they need otherwise they'll be talking to the wrong KMS backend and that is not just a security hole but but also just breaks everything in the Cactus node. Lots of files had to be changed for this one simple change in the interface because it's a new mandatory method prescribed for every single class that claims to implement a Cactus plugin. So because of that the diff is huge but you should be looking at this one in particular: ./packages/cactus-core-api/src/main/typescript/plugin/i-cactus-plugin.ts Signed-off-by: Peter Somogyvari <[email protected]>
1 parent 31be060 commit e50d9ce

File tree

21 files changed

+175
-18
lines changed

21 files changed

+175
-18
lines changed

packages/cactus-core-api/src/main/typescript/plugin/i-cactus-plugin.ts

+49
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,58 @@
11
import { PluginAspect } from "./plugin-aspect";
22

3+
/**
4+
* The common interface definiton that plugin classes can use to inherit from
5+
* when defining their own options interface for their constructors.
6+
*
7+
* Not all plugins need to have a unique plugin ID, so if the plugin you are
8+
* working does not, it is okay to provide a dummy implementation, but it is
9+
* not really recommended. Instead the constructor of your plugin should
10+
* generate one automatically at runtime if you definitely do not want the
11+
* caller of your plugin class to have to provide an instanceID.
12+
*
13+
* For example if you have a ledger called `FooBar` then this could be seen as
14+
* a recommended implementation:
15+
*
16+
* ```typescript
17+
*
18+
* interface IPluginLedgerConnectorFooBarOptions extends ICactusPluginOptions {
19+
* logLevel?: LogLevelDesc;
20+
* // your other FooBar specific parameters here
21+
* }
22+
*
23+
* class PluginLedgerConnectorFooBar implements ICactusPlugin {
24+
* constructor(public readonly options: IPluginLedgerConnectorFooBarOptions) {
25+
* // your constructor logic here
26+
* }
27+
*
28+
* public getInstanceId(): string {
29+
* // this works because your {IPluginLedgerConnectorFooBarOptions}
30+
* // inherits from the ICactusPluginOptions interface.
31+
* return this.options.instanceId;
32+
* }
33+
* ```
34+
*
35+
*/
36+
export interface ICactusPluginOptions {
37+
instanceId: string;
38+
}
39+
340
/**
441
* This is the common base for all other plugin interface definitions to have as a parent.
542
*/
643
export interface ICactusPlugin {
44+
/**
45+
* Returns a string that uniquely identifies the specific instance of a plugin
46+
* from other instances that may have been created from the exact same class.
47+
* We need this to cover scenarios where identical plugin implementations
48+
* need to be used because for example you have two different deployments of
49+
* the same kind of ledger, leading to you needing two separate instances of
50+
* the same kind of plugin in the plugin registry as well.
51+
*
52+
* @see {ICactusPluginOptions} For further details relevant to the instanceId
53+
*/
54+
getInstanceId(): string;
55+
756
/**
857
* Returns the NodeJS/npm package name of the plugin which is used to identify
958
* plugin instances at runtime and differentiate them from other types of plugins.

packages/cactus-core-api/src/main/typescript/public-api.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ export {
99
} from "./plugin/web-service/i-plugin-web-service";
1010
export { IWebServiceEndpoint } from "./plugin/web-service/i-web-service-endpoint";
1111
export { PluginFactory } from "./plugin/plugin-factory";
12-
export { ICactusPlugin, isICactusPlugin } from "./plugin/i-cactus-plugin";
12+
export {
13+
ICactusPlugin,
14+
ICactusPluginOptions,
15+
isICactusPlugin,
16+
} from "./plugin/i-cactus-plugin";
1317
export { PluginAspect } from "./plugin/plugin-aspect";
1418
export { PluginRegistry } from "./plugin/plugin-registry";

packages/cactus-plugin-consortium-manual/src/main/typescript/plugin-consortium-manual.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,24 @@ import {
1212
PluginRegistry,
1313
IWebServiceEndpoint,
1414
ICactusPlugin,
15+
ICactusPluginOptions,
1516
} from "@hyperledger/cactus-core-api";
1617
import {
18+
Checks,
1719
Logger,
1820
LoggerProvider,
1921
LogLevelDesc,
2022
} from "@hyperledger/cactus-common";
2123
import { GetConsortiumEndpointV1 } from "./consortium/get-consortium-jws-endpoint-v1";
2224
import { GetNodeJwsEndpoint } from "./consortium/get-node-jws-endpoint-v1";
25+
import uuid from "uuid";
2326

2427
export interface IWebAppOptions {
2528
port: number;
2629
hostname: string;
2730
}
2831

29-
export interface IPluginConsortiumManualOptions {
32+
export interface IPluginConsortiumManualOptions extends ICactusPluginOptions {
3033
keyPairPem: string;
3134
consortium: Consortium;
3235
pluginRegistry?: PluginRegistry;
@@ -37,15 +40,23 @@ export interface IPluginConsortiumManualOptions {
3740
export class PluginConsortiumManual
3841
implements ICactusPlugin, IPluginWebService {
3942
private readonly log: Logger;
43+
private readonly instanceId: string;
4044
private httpServer: Server | SecureServer | null = null;
4145

4246
constructor(public readonly options: IPluginConsortiumManualOptions) {
47+
const fnTag = `PluginConsortiumManual#constructor()`;
4348
if (!options) {
44-
throw new Error(`PluginConsortiumManual#ctor options falsy.`);
49+
throw new Error(`${fnTag} options falsy.`);
4550
}
51+
Checks.truthy(options.instanceId, `${fnTag} options.instanceId`);
4652
this.log = LoggerProvider.getOrCreate({
4753
label: "plugin-consortium-manual",
4854
});
55+
this.instanceId = this.options.instanceId;
56+
}
57+
58+
public getInstanceId(): string {
59+
return this.instanceId;
4960
}
5061

5162
public async shutdown(): Promise<void> {

packages/cactus-plugin-keychain-memory/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,12 @@
6262
},
6363
"homepage": "https://github.com/hyperledger/cactus#readme",
6464
"dependencies": {
65+
"@hyperledger/cactus-common": "^0.2.0",
6566
"@hyperledger/cactus-core-api": "^0.2.0",
6667
"joi": "14.3.1",
6768
"typescript-optional": "2.0.1"
6869
},
6970
"devDependencies": {
7071
"@types/joi": "14.3.4"
7172
}
72-
}
73+
}

packages/cactus-plugin-keychain-memory/src/main/typescript/plugin-factory-keychain.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { v4 as uuidv4 } from "uuid";
2+
13
import { PluginFactory } from "@hyperledger/cactus-core-api";
24
import {
35
IPluginKeychainOptions,
@@ -9,7 +11,10 @@ export class PluginFactoryKeychain extends PluginFactory<
911
IPluginKeychainOptions
1012
> {
1113
async create(
12-
options: IPluginKeychainOptions = { backend: new Map() }
14+
options: IPluginKeychainOptions = {
15+
backend: new Map(),
16+
instanceId: uuidv4(),
17+
}
1318
): Promise<PluginKeychainMemory> {
1419
return new PluginKeychainMemory(options);
1520
}

packages/cactus-plugin-keychain-memory/src/main/typescript/plugin-keychain-memory.ts

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,33 @@
11
import {
22
ICactusPlugin,
3+
ICactusPluginOptions,
34
IPluginKeychain,
45
PluginAspect,
56
} from "@hyperledger/cactus-core-api";
67

7-
export interface IPluginKeychainOptions {
8+
import { Checks } from "@hyperledger/cactus-common";
9+
10+
export interface IPluginKeychainOptions extends ICactusPluginOptions {
811
backend: Map<string, any>;
912
}
1013

1114
export class PluginKeychainMemory implements ICactusPlugin, IPluginKeychain {
15+
private readonly instanceId: string;
16+
1217
constructor(public readonly options: IPluginKeychainOptions) {
18+
const fnTag = `PluginKeychainMemory#constructor()`;
1319
if (!options) {
14-
throw new Error(`PluginKeychainMemory#ctor options falsy.`);
20+
throw new Error(`${fnTag} options falsy.`);
1521
}
22+
Checks.truthy(options.instanceId, `${fnTag} options.instanceId`);
1623
if (!options.backend) {
1724
options.backend = new Map();
1825
}
26+
this.instanceId = this.options.instanceId;
27+
}
28+
29+
public getInstanceId(): string {
30+
return this.instanceId;
1931
}
2032

2133
public getPackageName(): string {

packages/cactus-plugin-kv-storage-memory/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,12 @@
5959
},
6060
"homepage": "https://github.com/hyperledger/cactus#readme",
6161
"dependencies": {
62+
"@hyperledger/cactus-common": "^0.2.0",
6263
"@hyperledger/cactus-core-api": "^0.2.0",
6364
"joi": "14.3.1",
6465
"typescript-optional": "2.0.1"
6566
},
6667
"devDependencies": {
6768
"@types/joi": "14.3.4"
6869
}
69-
}
70+
}

packages/cactus-plugin-kv-storage-memory/src/main/typescript/plugin-factory-kv-storage.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { v4 as uuidv4 } from "uuid";
2+
13
import { PluginFactory } from "@hyperledger/cactus-core-api";
24
import {
35
IPluginKVStorageOptions,
@@ -9,7 +11,10 @@ export class PluginFactoryKVStorage extends PluginFactory<
911
IPluginKVStorageOptions
1012
> {
1113
async create(
12-
options: IPluginKVStorageOptions = { backend: new Map() }
14+
options: IPluginKVStorageOptions = {
15+
backend: new Map(),
16+
instanceId: uuidv4(),
17+
}
1318
): Promise<PluginKVStorageMemory> {
1419
return new PluginKVStorageMemory(options);
1520
}

packages/cactus-plugin-kv-storage-memory/src/main/typescript/plugin-kv-storage-memory.ts

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,33 @@
11
import {
22
ICactusPlugin,
3+
ICactusPluginOptions,
34
IPluginKVStorage,
45
PluginAspect,
56
} from "@hyperledger/cactus-core-api";
67

7-
export interface IPluginKVStorageOptions {
8+
import { Checks } from "@hyperledger/cactus-common";
9+
10+
export interface IPluginKVStorageOptions extends ICactusPluginOptions {
811
backend: Map<string, any>;
912
}
1013

1114
export class PluginKVStorageMemory implements ICactusPlugin, IPluginKVStorage {
15+
private readonly instanceId: string;
16+
1217
constructor(public readonly options: IPluginKVStorageOptions) {
18+
const fnTag = `PluginKVStorageMemory#constructor()`;
1319
if (!options) {
14-
throw new Error(`PluginKVStorageMemory#ctor options falsy.`);
20+
throw new Error(`${fnTag} options falsy.`);
1521
}
22+
Checks.truthy(options.instanceId, `${fnTag} options.instanceId`);
1623
if (!options.backend) {
1724
options.backend = new Map();
1825
}
26+
this.instanceId = this.options.instanceId;
27+
}
28+
29+
public getInstanceId(): string {
30+
return this.instanceId;
1931
}
2032

2133
public getPackageName(): string {

packages/cactus-plugin-ledger-connector-besu/src/main/typescript/plugin-ledger-connector-besu.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
import {
22
ICactusPlugin,
3+
ICactusPluginOptions,
34
IPluginLedgerConnector,
45
PluginAspect,
56
} from "@hyperledger/cactus-core-api";
67
import Web3 from "web3";
78
import EEAClient, { IWeb3InstanceExtended } from "web3-eea";
89

910
import {
11+
Checks,
1012
Logger,
1113
LoggerProvider,
1214
LogLevelDesc,
1315
} from "@hyperledger/cactus-common";
1416

15-
export interface IPluginLedgerConnectorBesuOptions {
17+
export interface IPluginLedgerConnectorBesuOptions
18+
extends ICactusPluginOptions {
1619
rpcApiHttpHost: string;
1720
logLevel?: LogLevelDesc;
1821
}
@@ -72,14 +75,17 @@ export class PluginLedgerConnectorBesu
7275
IBesuTransactionIn,
7376
IBesuTransactionOut
7477
> {
78+
private readonly instanceId: string;
7579
private readonly log: Logger;
7680
private readonly web3: Web3;
7781
private readonly web3Eea: IWeb3InstanceExtended;
7882

7983
constructor(public readonly options: IPluginLedgerConnectorBesuOptions) {
84+
const fnTag = `PluginLedgerConnectorBesu#constructor()`;
8085
if (!options) {
81-
throw new Error(`PluginLedgerConnectorBesu#ctor options falsy.`);
86+
throw new Error(`${fnTag} options falsy.`);
8287
}
88+
Checks.truthy(options.instanceId, `${fnTag} options.instanceId`);
8389
const web3Provider = new Web3.providers.HttpProvider(
8490
this.options.rpcApiHttpHost
8591
);
@@ -89,6 +95,11 @@ export class PluginLedgerConnectorBesu
8995
const level = options.logLevel || "INFO";
9096
const label = "plugin-ledger-connector-besu";
9197
this.log = LoggerProvider.getOrCreate({ level, label });
98+
this.instanceId = this.options.instanceId;
99+
}
100+
101+
public getInstanceId(): string {
102+
return this.instanceId;
92103
}
93104

94105
public getPackageName(): string {

packages/cactus-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-ledger-connector-besu/deploy-contract/deploy-contract-from-json.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// tslint:disable-next-line: no-var-requires
22
const tap = require("tap");
3+
import { v4 as uuidv4 } from "uuid";
34
import {
45
PluginLedgerConnectorBesu,
56
PluginFactoryLedgerConnector,
@@ -26,6 +27,7 @@ tap.test("deploys contract via .json file", async (assert: any) => {
2627
const factory = new PluginFactoryLedgerConnector();
2728
const connector: PluginLedgerConnectorBesu = await factory.create({
2829
rpcApiHttpHost,
30+
instanceId: uuidv4(),
2931
});
3032

3133
const options = {

packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/plugin-ledger-connector-fabric.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
IPluginWebService,
1414
IWebServiceEndpoint,
1515
ICactusPlugin,
16+
ICactusPluginOptions,
1617
} from "@hyperledger/cactus-core-api";
1718

1819
import {
@@ -27,7 +28,8 @@ import {
2728
} from "./deploy-contract-go-bin-endpoint-v1";
2829
import { ISigningIdentity } from "./i-fabric-signing-identity";
2930

30-
export interface IPluginLedgerConnectorFabricOptions {
31+
export interface IPluginLedgerConnectorFabricOptions
32+
extends ICactusPluginOptions {
3133
opsApiHttpHost: string;
3234
logLevel?: LogLevelDesc;
3335
webAppOptions?: any;
@@ -45,6 +47,7 @@ export class PluginLedgerConnectorFabric
4547
IPluginLedgerConnector<any, any, any, any>,
4648
ICactusPlugin,
4749
IPluginWebService {
50+
private readonly instanceId: string;
4851
private readonly log: Logger;
4952

5053
private httpServer: Server | SecureServer | undefined;
@@ -53,6 +56,9 @@ export class PluginLedgerConnectorFabric
5356
const fnTag = "PluginLedgerConnectorFabric#constructor()";
5457

5558
Checks.truthy(options, `${fnTag} arg options`);
59+
Checks.truthy(options.instanceId, `${fnTag} options.instanceId`);
60+
61+
this.instanceId = options.instanceId;
5662

5763
const level = this.options.logLevel || "INFO";
5864
const label = "plugin-ledger-connector-fabric";
@@ -66,6 +72,10 @@ export class PluginLedgerConnectorFabric
6672
throw new Error("Method not implemented.");
6773
}
6874

75+
public getInstanceId(): string {
76+
return this.instanceId;
77+
}
78+
6979
public getPackageName(): string {
7080
return `@hyperledger/cactus-plugin-ledger-connectur-fabric`;
7181
}

packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/deploy-contract-go-bin-endpoint-v1/deploy-contract/deploy-cc-from-golang-source.test.ts

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { AddressInfo } from "net";
33
import path from "path";
44

55
import test, { Test } from "tape";
6+
import { v4 as uuidv4 } from "uuid";
67

78
import axios, { AxiosRequestConfig } from "axios";
89
import FormData from "form-data";
@@ -39,6 +40,7 @@ test("deploys contract from go source", async (t: Test) => {
3940
const adminSigningIdentity = await ledger.getAdminSigningIdentity();
4041

4142
const pluginOpts: IPluginLedgerConnectorFabricOptions = {
43+
instanceId: uuidv4(),
4244
opsApiHttpHost,
4345
connectionProfile,
4446
adminSigningIdentity,

0 commit comments

Comments
 (0)