Skip to content

Commit

Permalink
feat(quorum): 🎸 support Cactus Keychain APIs
Browse files Browse the repository at this point in the history
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 <peter.somogyvari@accenture.com>
  • Loading branch information
petermetz committed Dec 1, 2020
1 parent 49ec2d5 commit 0d4769f
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -319,8 +319,39 @@ export interface SolidityContractJsonArtifact {
* @type Web3SigningCredential
* @export
*/
export type Web3SigningCredential = Web3SigningCredentialGethKeychainPassword | Web3SigningCredentialNone | Web3SigningCredentialPrivateKeyHex;
export type Web3SigningCredential = Web3SigningCredentialCactusKeychainRef | Web3SigningCredentialGethKeychainPassword | Web3SigningCredentialNone | Web3SigningCredentialPrivateKeyHex;

/**
*
* @export
* @interface Web3SigningCredentialCactusKeychainRef
*/
export interface Web3SigningCredentialCactusKeychainRef {
/**
*
* @type {Web3SigningCredentialType}
* @memberof Web3SigningCredentialCactusKeychainRef
*/
type: Web3SigningCredentialType;
/**
* The ethereum account (public key) that the credential belongs to. Basically the username in the traditional terminology of authentication.
* @type {string}
* @memberof Web3SigningCredentialCactusKeychainRef
*/
ethAccount: string;
/**
* The key to use when looking up the the keychain entry holding the secret pointed to by the keychainEntryKey parameter.
* @type {string}
* @memberof Web3SigningCredentialCactusKeychainRef
*/
keychainEntryKey: string;
/**
* 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.
* @type {string}
* @memberof Web3SigningCredentialCactusKeychainRef
*/
keychainId: string;
}
/**
*
* @export
Expand Down Expand Up @@ -390,6 +421,7 @@ export interface Web3SigningCredentialPrivateKeyHex {
* @enum {string}
*/
export enum Web3SigningCredentialType {
CACTUSKEYCHAINREF = 'CACTUS_KEYCHAIN_REF',
GETHKEYCHAINPASSWORD = 'GETH_KEYCHAIN_PASSWORD',
PRIVATEKEYHEX = 'PRIVATE_KEY_HEX',
NONE = 'NONE'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
Web3SigningCredentialCactusKeychainRef,
Web3SigningCredentialGethKeychainPassword,
Web3SigningCredentialNone,
Web3SigningCredentialPrivateKeyHex,
Expand All @@ -22,3 +23,18 @@ export function isWeb3SigningCredentialGethKeychainPassword(
): x is Web3SigningCredentialGethKeychainPassword {
return x?.type && x?.type === Web3SigningCredentialType.GETHKEYCHAINPASSWORD;
}

export function isWeb3SigningCredentialCactusKeychainRef(
x: any
): x is Web3SigningCredentialCactusKeychainRef {
return (
!!x?.type &&
x?.type === Web3SigningCredentialType.CACTUSKEYCHAINREF &&
!!x?.keychainEntryKey &&
typeof x?.keychainEntryKey === "string" &&
x?.keychainEntryKey.trim().length > 0 &&
!!x?.keychainId &&
typeof x?.keychainId === "string" &&
x?.keychainId.trim().length > 0
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ export const CACTUS_OPEN_API_JSON: OpenAPIV3.Document = {
$ref:
"#/components/schemas/Web3SigningCredentialGethKeychainPassword",
},
{
$ref: "#/components/schemas/Web3SigningCredentialCactusKeychainRef",
},
{ $ref: "#/components/schemas/Web3SigningCredentialPrivateKeyHex" },
{ $ref: "#/components/schemas/Web3SigningCredentialNone" },
],
Expand Down Expand Up @@ -76,6 +79,43 @@ export const CACTUS_OPEN_API_JSON: OpenAPIV3.Document = {
},
},
},
Web3SigningCredentialCactusKeychainRef: {
type: "object",
required: ["type", "ethAccount", "keychainId", "keychainEntryKey"],
properties: {
type: {
$ref: "#/components/schemas/Web3SigningCredentialType",
},
ethAccount: {
type: "string",
description:
"The ethereum account (public key) that the credential " +
" belongs to. Basically the username in the traditional " +
" terminology of authentication.",
minLength: 64,
maxLength: 64,
nullable: false,
},
keychainEntryKey: {
type: "string",
description:
"The key to use when looking up the" +
" the keychain entry holding the secret pointed to by the " +
" keychainEntryKey parameter.",
minLength: 0,
maxLength: 1024,
},
keychainId: {
type: "string",
description:
"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.",
minLength: 0,
maxLength: 1024,
},
},
},
Web3SigningCredentialPrivateKeyHex: {
type: "object",
required: ["type", "ethAccount", "secret"],
Expand Down Expand Up @@ -114,7 +154,12 @@ export const CACTUS_OPEN_API_JSON: OpenAPIV3.Document = {
},
Web3SigningCredentialType: {
type: "string",
enum: ["GETH_KEYCHAIN_PASSWORD", "PRIVATE_KEY_HEX", "NONE"],
enum: [
"CACTUS_KEYCHAIN_REF",
"GETH_KEYCHAIN_PASSWORD",
"PRIVATE_KEY_HEX",
"NONE",
],
},
EthContractInvocationType: {
type: "string",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
PluginAspect,
ICactusPlugin,
ICactusPluginOptions,
PluginRegistry,
IPluginKeychain,
} from "@hyperledger/cactus-core-api";

import {
Expand All @@ -37,6 +39,7 @@ import {
RunTransactionResponse,
Web3SigningCredential,
Web3SigningCredentialGethKeychainPassword,
Web3SigningCredentialCactusKeychainRef,
Web3SigningCredentialPrivateKeyHex,
Web3SigningCredentialType,
} from "./generated/openapi/typescript-axios/";
Expand All @@ -49,6 +52,7 @@ export interface IPluginLedgerConnectorQuorumOptions
extends ICactusPluginOptions {
rpcApiHttpHost: string;
logLevel?: LogLevelDesc;
pluginRegistry: PluginRegistry;
}

export class PluginLedgerConnectorQuorum
Expand All @@ -61,6 +65,7 @@ export class PluginLedgerConnectorQuorum
>,
ICactusPlugin,
IPluginWebService {
private readonly pluginRegistry: PluginRegistry;
private readonly instanceId: string;
private readonly log: Logger;
private readonly web3: Web3;
Expand All @@ -77,6 +82,7 @@ export class PluginLedgerConnectorQuorum
Checks.truthy(options, `${fnTag} arg options`);
Checks.truthy(options.rpcApiHttpHost, `${fnTag} options.rpcApiHttpHost`);
Checks.truthy(options.instanceId, `${fnTag} options.instanceId`);
Checks.truthy(options.pluginRegistry, `${fnTag} options.pluginRegistry`);

const level = this.options.logLevel || "INFO";
const label = this.className;
Expand All @@ -87,6 +93,7 @@ export class PluginLedgerConnectorQuorum
);
this.web3 = new Web3(web3Provider);
this.instanceId = options.instanceId;
this.pluginRegistry = options.pluginRegistry;
}

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

switch (req.web3SigningCredential.type) {
case Web3SigningCredentialType.CACTUSKEYCHAINREF: {
return this.transactCactusKeychainRef(req);
}
case Web3SigningCredentialType.GETHKEYCHAINPASSWORD: {
return this.transactGethKeychain(req);
}
Expand Down Expand Up @@ -290,6 +300,39 @@ export class PluginLedgerConnectorQuorum
}
}

public async transactCactusKeychainRef(
req: RunTransactionRequest
): Promise<RunTransactionResponse> {
const fnTag = `${this.className}#transactCactusKeychainRef()`;
const { transactionConfig, web3SigningCredential } = req;
const {
ethAccount,
keychainEntryKey,
keychainId,
} = web3SigningCredential as Web3SigningCredentialCactusKeychainRef;

// locate the keychain plugin that has access to the keychain backend
// denoted by the keychainID from the request.
const keychainPlugin = this.pluginRegistry
.findManyByAspect<IPluginKeychain>(PluginAspect.KEYCHAIN)
.find((k) => k.getKeychainId() === keychainId);

Checks.truthy(keychainPlugin, `${fnTag} keychain for ID:"${keychainId}"`);

// Now use the found keychain plugin to actually perform the lookup of
// the private key that we need to run the transaction.
const privateKeyHex = await keychainPlugin?.get<string>(keychainEntryKey);

return this.transactPrivateKey({
transactionConfig,
web3SigningCredential: {
ethAccount,
type: Web3SigningCredentialType.PRIVATEKEYHEX,
secret: privateKeyHex,
},
});
}

public async pollForTxReceipt(
txHash: string,
timeoutMs: number = 60000
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
IQuorumGenesisOptions,
IAccount,
} from "@hyperledger/cactus-test-tooling";
import { PluginRegistry } from "@hyperledger/cactus-core-api";

const logLevel: LogLevelDesc = "INFO";
const log: Logger = LoggerProvider.getOrCreate({
Expand Down Expand Up @@ -58,6 +59,7 @@ test("Quorum Ledger Connector Plugin", async (t: Test) => {
instanceId: uuidV4(),
rpcApiHttpHost,
logLevel,
pluginRegistry: new PluginRegistry(),
}
);
const testEthAccount = web3.eth.accounts.create(uuidV4());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ test("Routes to correct node based on ledger ID", async (t: Test) => {
instanceId: uuidV4(),
rpcApiHttpHost: rpcApiHttpHost1,
logLevel,
pluginRegistry: new PluginRegistry(),
});

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

const options: IPluginConsortiumManualOptions = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ test("deploys contract via REST API", async (t: Test) => {
const ledgerConnectorQuorum = new PluginLedgerConnectorQuorum({
instanceId: uuidV4(),
rpcApiHttpHost,
pluginRegistry: new PluginRegistry(),
});
plugins.push(ledgerConnectorQuorum);
const pluginRegistry = new PluginRegistry({ plugins });
Expand Down

0 comments on commit 0d4769f

Please sign in to comment.