Skip to content

Commit 0d4769f

Browse files
committed
feat(quorum): 🎸 support Cactus Keychain APIs
1. The OpenAPI spec of the quorum connector has been updated to include a new web3 signing credential type pertaining to a case where the caller passes in a reference to a keychain entry and the ID of the keychain where the entry can be located. 2. The quorum connector can now execute transactions with the credential type mentioned above so that passwords/private keys do not have to travel over the wire unless that is the explicit intention of the caller (since it is still a supported option) Signed-off-by: Peter Somogyvari <[email protected]>
1 parent 49ec2d5 commit 0d4769f

File tree

7 files changed

+143
-2
lines changed

7 files changed

+143
-2
lines changed

packages/cactus-plugin-ledger-connector-quorum/src/main/typescript/generated/openapi/typescript-axios/api.ts

+33-1
Original file line numberDiff line numberDiff line change
@@ -319,8 +319,39 @@ export interface SolidityContractJsonArtifact {
319319
* @type Web3SigningCredential
320320
* @export
321321
*/
322-
export type Web3SigningCredential = Web3SigningCredentialGethKeychainPassword | Web3SigningCredentialNone | Web3SigningCredentialPrivateKeyHex;
322+
export type Web3SigningCredential = Web3SigningCredentialCactusKeychainRef | Web3SigningCredentialGethKeychainPassword | Web3SigningCredentialNone | Web3SigningCredentialPrivateKeyHex;
323323

324+
/**
325+
*
326+
* @export
327+
* @interface Web3SigningCredentialCactusKeychainRef
328+
*/
329+
export interface Web3SigningCredentialCactusKeychainRef {
330+
/**
331+
*
332+
* @type {Web3SigningCredentialType}
333+
* @memberof Web3SigningCredentialCactusKeychainRef
334+
*/
335+
type: Web3SigningCredentialType;
336+
/**
337+
* The ethereum account (public key) that the credential belongs to. Basically the username in the traditional terminology of authentication.
338+
* @type {string}
339+
* @memberof Web3SigningCredentialCactusKeychainRef
340+
*/
341+
ethAccount: string;
342+
/**
343+
* The key to use when looking up the the keychain entry holding the secret pointed to by the keychainEntryKey parameter.
344+
* @type {string}
345+
* @memberof Web3SigningCredentialCactusKeychainRef
346+
*/
347+
keychainEntryKey: string;
348+
/**
349+
* The keychain ID to use when looking up the the keychain plugin instance that will be used to retrieve the secret pointed to by the keychainEntryKey parameter.
350+
* @type {string}
351+
* @memberof Web3SigningCredentialCactusKeychainRef
352+
*/
353+
keychainId: string;
354+
}
324355
/**
325356
*
326357
* @export
@@ -390,6 +421,7 @@ export interface Web3SigningCredentialPrivateKeyHex {
390421
* @enum {string}
391422
*/
392423
export enum Web3SigningCredentialType {
424+
CACTUSKEYCHAINREF = 'CACTUS_KEYCHAIN_REF',
393425
GETHKEYCHAINPASSWORD = 'GETH_KEYCHAIN_PASSWORD',
394426
PRIVATEKEYHEX = 'PRIVATE_KEY_HEX',
395427
NONE = 'NONE'

packages/cactus-plugin-ledger-connector-quorum/src/main/typescript/model-type-guards.ts

+16
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
Web3SigningCredentialCactusKeychainRef,
23
Web3SigningCredentialGethKeychainPassword,
34
Web3SigningCredentialNone,
45
Web3SigningCredentialPrivateKeyHex,
@@ -22,3 +23,18 @@ export function isWeb3SigningCredentialGethKeychainPassword(
2223
): x is Web3SigningCredentialGethKeychainPassword {
2324
return x?.type && x?.type === Web3SigningCredentialType.GETHKEYCHAINPASSWORD;
2425
}
26+
27+
export function isWeb3SigningCredentialCactusKeychainRef(
28+
x: any
29+
): x is Web3SigningCredentialCactusKeychainRef {
30+
return (
31+
!!x?.type &&
32+
x?.type === Web3SigningCredentialType.CACTUSKEYCHAINREF &&
33+
!!x?.keychainEntryKey &&
34+
typeof x?.keychainEntryKey === "string" &&
35+
x?.keychainEntryKey.trim().length > 0 &&
36+
!!x?.keychainId &&
37+
typeof x?.keychainId === "string" &&
38+
x?.keychainId.trim().length > 0
39+
);
40+
}

packages/cactus-plugin-ledger-connector-quorum/src/main/typescript/openapi-spec.ts

+46-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ export const CACTUS_OPEN_API_JSON: OpenAPIV3.Document = {
4343
$ref:
4444
"#/components/schemas/Web3SigningCredentialGethKeychainPassword",
4545
},
46+
{
47+
$ref: "#/components/schemas/Web3SigningCredentialCactusKeychainRef",
48+
},
4649
{ $ref: "#/components/schemas/Web3SigningCredentialPrivateKeyHex" },
4750
{ $ref: "#/components/schemas/Web3SigningCredentialNone" },
4851
],
@@ -76,6 +79,43 @@ export const CACTUS_OPEN_API_JSON: OpenAPIV3.Document = {
7679
},
7780
},
7881
},
82+
Web3SigningCredentialCactusKeychainRef: {
83+
type: "object",
84+
required: ["type", "ethAccount", "keychainId", "keychainEntryKey"],
85+
properties: {
86+
type: {
87+
$ref: "#/components/schemas/Web3SigningCredentialType",
88+
},
89+
ethAccount: {
90+
type: "string",
91+
description:
92+
"The ethereum account (public key) that the credential " +
93+
" belongs to. Basically the username in the traditional " +
94+
" terminology of authentication.",
95+
minLength: 64,
96+
maxLength: 64,
97+
nullable: false,
98+
},
99+
keychainEntryKey: {
100+
type: "string",
101+
description:
102+
"The key to use when looking up the" +
103+
" the keychain entry holding the secret pointed to by the " +
104+
" keychainEntryKey parameter.",
105+
minLength: 0,
106+
maxLength: 1024,
107+
},
108+
keychainId: {
109+
type: "string",
110+
description:
111+
"The keychain ID to use when looking up the" +
112+
" the keychain plugin instance that will be used to retrieve" +
113+
" the secret pointed to by the keychainEntryKey parameter.",
114+
minLength: 0,
115+
maxLength: 1024,
116+
},
117+
},
118+
},
79119
Web3SigningCredentialPrivateKeyHex: {
80120
type: "object",
81121
required: ["type", "ethAccount", "secret"],
@@ -114,7 +154,12 @@ export const CACTUS_OPEN_API_JSON: OpenAPIV3.Document = {
114154
},
115155
Web3SigningCredentialType: {
116156
type: "string",
117-
enum: ["GETH_KEYCHAIN_PASSWORD", "PRIVATE_KEY_HEX", "NONE"],
157+
enum: [
158+
"CACTUS_KEYCHAIN_REF",
159+
"GETH_KEYCHAIN_PASSWORD",
160+
"PRIVATE_KEY_HEX",
161+
"NONE",
162+
],
118163
},
119164
EthContractInvocationType: {
120165
type: "string",

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

+43
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import {
1616
PluginAspect,
1717
ICactusPlugin,
1818
ICactusPluginOptions,
19+
PluginRegistry,
20+
IPluginKeychain,
1921
} from "@hyperledger/cactus-core-api";
2022

2123
import {
@@ -37,6 +39,7 @@ import {
3739
RunTransactionResponse,
3840
Web3SigningCredential,
3941
Web3SigningCredentialGethKeychainPassword,
42+
Web3SigningCredentialCactusKeychainRef,
4043
Web3SigningCredentialPrivateKeyHex,
4144
Web3SigningCredentialType,
4245
} from "./generated/openapi/typescript-axios/";
@@ -49,6 +52,7 @@ export interface IPluginLedgerConnectorQuorumOptions
4952
extends ICactusPluginOptions {
5053
rpcApiHttpHost: string;
5154
logLevel?: LogLevelDesc;
55+
pluginRegistry: PluginRegistry;
5256
}
5357

5458
export class PluginLedgerConnectorQuorum
@@ -61,6 +65,7 @@ export class PluginLedgerConnectorQuorum
6165
>,
6266
ICactusPlugin,
6367
IPluginWebService {
68+
private readonly pluginRegistry: PluginRegistry;
6469
private readonly instanceId: string;
6570
private readonly log: Logger;
6671
private readonly web3: Web3;
@@ -77,6 +82,7 @@ export class PluginLedgerConnectorQuorum
7782
Checks.truthy(options, `${fnTag} arg options`);
7883
Checks.truthy(options.rpcApiHttpHost, `${fnTag} options.rpcApiHttpHost`);
7984
Checks.truthy(options.instanceId, `${fnTag} options.instanceId`);
85+
Checks.truthy(options.pluginRegistry, `${fnTag} options.pluginRegistry`);
8086

8187
const level = this.options.logLevel || "INFO";
8288
const label = this.className;
@@ -87,6 +93,7 @@ export class PluginLedgerConnectorQuorum
8793
);
8894
this.web3 = new Web3(web3Provider);
8995
this.instanceId = options.instanceId;
96+
this.pluginRegistry = options.pluginRegistry;
9097
}
9198

9299
public getInstanceId(): string {
@@ -203,6 +210,9 @@ export class PluginLedgerConnectorQuorum
203210
const fnTag = `${this.className}#transact()`;
204211

205212
switch (req.web3SigningCredential.type) {
213+
case Web3SigningCredentialType.CACTUSKEYCHAINREF: {
214+
return this.transactCactusKeychainRef(req);
215+
}
206216
case Web3SigningCredentialType.GETHKEYCHAINPASSWORD: {
207217
return this.transactGethKeychain(req);
208218
}
@@ -290,6 +300,39 @@ export class PluginLedgerConnectorQuorum
290300
}
291301
}
292302

303+
public async transactCactusKeychainRef(
304+
req: RunTransactionRequest
305+
): Promise<RunTransactionResponse> {
306+
const fnTag = `${this.className}#transactCactusKeychainRef()`;
307+
const { transactionConfig, web3SigningCredential } = req;
308+
const {
309+
ethAccount,
310+
keychainEntryKey,
311+
keychainId,
312+
} = web3SigningCredential as Web3SigningCredentialCactusKeychainRef;
313+
314+
// locate the keychain plugin that has access to the keychain backend
315+
// denoted by the keychainID from the request.
316+
const keychainPlugin = this.pluginRegistry
317+
.findManyByAspect<IPluginKeychain>(PluginAspect.KEYCHAIN)
318+
.find((k) => k.getKeychainId() === keychainId);
319+
320+
Checks.truthy(keychainPlugin, `${fnTag} keychain for ID:"${keychainId}"`);
321+
322+
// Now use the found keychain plugin to actually perform the lookup of
323+
// the private key that we need to run the transaction.
324+
const privateKeyHex = await keychainPlugin?.get<string>(keychainEntryKey);
325+
326+
return this.transactPrivateKey({
327+
transactionConfig,
328+
web3SigningCredential: {
329+
ethAccount,
330+
type: Web3SigningCredentialType.PRIVATEKEYHEX,
331+
secret: privateKeyHex,
332+
},
333+
});
334+
}
335+
293336
public async pollForTxReceipt(
294337
txHash: string,
295338
timeoutMs: number = 60000

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

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
IQuorumGenesisOptions,
2222
IAccount,
2323
} from "@hyperledger/cactus-test-tooling";
24+
import { PluginRegistry } from "@hyperledger/cactus-core-api";
2425

2526
const logLevel: LogLevelDesc = "INFO";
2627
const log: Logger = LoggerProvider.getOrCreate({
@@ -58,6 +59,7 @@ test("Quorum Ledger Connector Plugin", async (t: Test) => {
5859
instanceId: uuidV4(),
5960
rpcApiHttpHost,
6061
logLevel,
62+
pluginRegistry: new PluginRegistry(),
6163
}
6264
);
6365
const testEthAccount = web3.eth.accounts.create(uuidV4());

packages/cactus-test-api-client/src/test/typescript/integration/api-client-routing-node-to-node.test.ts

+2
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ test("Routes to correct node based on ledger ID", async (t: Test) => {
134134
instanceId: uuidV4(),
135135
rpcApiHttpHost: rpcApiHttpHost1,
136136
logLevel,
137+
pluginRegistry: new PluginRegistry(),
137138
});
138139

139140
const options: IPluginConsortiumManualOptions = {
@@ -174,6 +175,7 @@ test("Routes to correct node based on ledger ID", async (t: Test) => {
174175
instanceId: uuidV4(),
175176
rpcApiHttpHost: rpcApiHttpHost2,
176177
logLevel,
178+
pluginRegistry: new PluginRegistry(),
177179
});
178180

179181
const options: IPluginConsortiumManualOptions = {

packages/cactus-test-plugin-ledger-connector-quorum/src/test/typescript/integration/plugin-ledger-connector-quorum/deploy-contract/deploy-contract-via-web-service.test.ts

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ test("deploys contract via REST API", async (t: Test) => {
6363
const ledgerConnectorQuorum = new PluginLedgerConnectorQuorum({
6464
instanceId: uuidV4(),
6565
rpcApiHttpHost,
66+
pluginRegistry: new PluginRegistry(),
6667
});
6768
plugins.push(ledgerConnectorQuorum);
6869
const pluginRegistry = new PluginRegistry({ plugins });

0 commit comments

Comments
 (0)