Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cactus-plugin-ledger-connector-ethereum): add json-rpc proxy #2753

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions packages/cactus-plugin-ledger-connector-ethereum/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,19 @@ args: {
},
```

## JSON-RPC Proxy
- Connector can be used with web3js to send any JSON-RPC request to the ethereum node.

### Example
``` typescript
const proxyUrl = new URL(
"/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/json-rpc",
apiHost,
);
const web3ProxyClient = new Web3(proxyUrl.toString());
const gasPrice = await web3ProxyClient.eth.getGasPrice();
```

## Running the tests

To check that all has been installed correctly and that the pugin has no errors run jest test suites.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"@hyperledger/cactus-core-api": "2.0.0-alpha.2",
"axios": "0.21.4",
"express": "4.17.3",
"http-proxy-middleware": "2.0.6",
"minimist": "1.2.8",
"prom-client": "13.2.0",
"run-time-error": "1.4.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1159,6 +1159,9 @@
}
}
}
},
"/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/json-rpc": {
"summary": "Proxy endpoint to pass JSON-RPC requests to remote ethereum node."
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ import {
} from "./types/model-type-guards";
import { PrometheusExporter } from "./prometheus-exporter/prometheus-exporter";
import { RuntimeError } from "run-time-error";
import { createProxyMiddleware, fixRequestBody } from "http-proxy-middleware";

import {
Web3StringReturnFormat,
convertWeb3ReceiptStatusToBool,
Expand Down Expand Up @@ -339,6 +341,26 @@ export class PluginLedgerConnectorEthereum
});
}

// Register JSON-RPC proxy to pass requests directly to ethereum node
if (this.options.rpcApiHttpHost) {
const proxyUrl =
"/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/json-rpc";
const targetUrl = this.options.rpcApiHttpHost;
app.use(
proxyUrl,
createProxyMiddleware({
target: targetUrl,
changeOrigin: true,
pathRewrite: {
[".*"]: "",
},
onProxyReq: fixRequestBody,
logLevel: "error",
}),
);
this.log.info(`Registered proxy from ${proxyUrl} to ${targetUrl}`);
}

return webServices;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe("Ethereum contract deploy and invoke using keychain tests", () => {
address: string,
port: number,
contractAddress: string,
apiHost,
apiHost: string,
apiConfig,
ledger: GethTestLedger,
apiClient: EthereumApi,
Expand Down Expand Up @@ -129,6 +129,9 @@ describe("Ethereum contract deploy and invoke using keychain tests", () => {
logLevel: testLogLevel,
pluginRegistry: new PluginRegistry({ plugins: [keychainPlugin] }),
});

await connector.getOrCreateWebServices();
await connector.registerWebServices(expressApp, wsApi);
});

afterAll(async () => {
Expand All @@ -143,9 +146,6 @@ describe("Ethereum contract deploy and invoke using keychain tests", () => {
test("setup ethereum connector", async () => {
// Instantiate connector with the keychain plugin that already has the
// private key we want to use for one of our tests
await connector.getOrCreateWebServices();
await connector.registerWebServices(expressApp, wsApi);

const initTransferValue = web3.utils.toWei(5000, "ether");
await apiClient.runTransactionV1({
web3SigningCredential: {
Expand Down Expand Up @@ -360,6 +360,7 @@ describe("Ethereum contract deploy and invoke using keychain tests", () => {
maxPriorityFeePerGas: 0,
maxFeePerGas: 0x40000000,
gasLimit: 21000,
type: 2,
},
testEthAccount.privateKey,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,29 @@ const testLogLevel = "info";
const sutLogLevel = "info";

import "jest-extended";
import express from "express";
import bodyParser from "body-parser";
import http from "http";
import Web3 from "web3";
import { v4 as uuidv4 } from "uuid";
import { Server as SocketIoServer } from "socket.io";
import { AddressInfo } from "net";

import { PluginRegistry } from "@hyperledger/cactus-core";
import { PluginLedgerConnectorEthereum } from "../../../main/typescript/index";
import { Constants } from "@hyperledger/cactus-core-api";
import {
IListenOptions,
Logger,
LoggerProvider,
Servers,
} from "@hyperledger/cactus-common";
import { pruneDockerAllIfGithubAction } from "@hyperledger/cactus-test-tooling";
import { Logger, LoggerProvider } from "@hyperledger/cactus-common";
import {
GethTestLedger,
WHALE_ACCOUNT_ADDRESS,
} from "@hyperledger/cactus-test-geth-ledger";
import Web3 from "web3";

import { PluginLedgerConnectorEthereum } from "../../../main/typescript/index";

// Unit Test logger setup
const log: Logger = LoggerProvider.getOrCreate({
Expand All @@ -36,6 +49,13 @@ describe("invokeRawWeb3EthMethod Tests", () => {
let ethereumTestLedger: GethTestLedger;
let connector: PluginLedgerConnectorEthereum;
let web3: Web3;
let apiHost: string;
const expressApp = express();
expressApp.use(bodyParser.json({ limit: "250mb" }));
const server = http.createServer(expressApp);
const wsApi = new SocketIoServer(server, {
path: Constants.SocketIoConnectionPathV1,
});

//////////////////////////////////
// Environment Setup
Expand Down Expand Up @@ -64,21 +84,51 @@ describe("invokeRawWeb3EthMethod Tests", () => {
pluginRegistry: new PluginRegistry(),
});

const listenOptions: IListenOptions = {
hostname: "0.0.0.0",
port: 0,
server,
};
const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo;
const { address, port } = addressInfo;
apiHost = `http://${address}:${port}`;

await connector.getOrCreateWebServices();
await connector.registerWebServices(expressApp, wsApi);

web3 = new Web3(rpcApiHttpHost);
});

afterAll(async () => {
log.info("Shutdown connector");
await connector.shutdown();
log.info("Shutdown server");
await Servers.shutdown(server);

log.info("Stop and destroy the test ledger...");
await ethereumTestLedger.stop();
await ethereumTestLedger.destroy();
if (connector) {
log.info("Shutdown connector");
await connector.shutdown();
}

if (ethereumTestLedger) {
log.info("Stop and destroy the test ledger...");
await ethereumTestLedger.stop();
await ethereumTestLedger.destroy();
}

log.info("Prune docker...");
await pruneDockerAllIfGithubAction({ logLevel: testLogLevel });
});

test("invoke method using json-rpc proxy", async () => {
const proxyUrl = new URL(
"/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/json-rpc",
apiHost,
);
const web3ProxyClient = new Web3(proxyUrl.toString());
const gasPrice = await web3ProxyClient.eth.getGasPrice();
expect(gasPrice).toBeTruthy();
expect(Number(gasPrice)).toBeGreaterThan(0);
});

test("invokeRawWeb3EthMethod with 0-argument method works (getGasPrice)", async () => {
const connectorResponse = await connector.invokeRawWeb3EthMethod({
methodName: "getGasPrice",
Expand Down
3 changes: 2 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6488,6 +6488,7 @@ __metadata:
axios: 0.21.4
chalk: 4.1.2
express: 4.17.3
http-proxy-middleware: 2.0.6
js-yaml: 4.1.0
minimist: 1.2.8
prom-client: 13.2.0
Expand Down Expand Up @@ -24695,7 +24696,7 @@ __metadata:
languageName: node
linkType: hard

"http-proxy-middleware@npm:^2.0.3":
"http-proxy-middleware@npm:2.0.6, http-proxy-middleware@npm:^2.0.3":
version: 2.0.6
resolution: "http-proxy-middleware@npm:2.0.6"
dependencies:
Expand Down
Loading