From 6dbe6b54eaee156cd236ebbde4337b8599b05c08 Mon Sep 17 00:00:00 2001 From: tomasz awramski Date: Thu, 22 Dec 2022 11:02:19 +0100 Subject: [PATCH] feat: connector-go-ethereum now can report empty blocks - added parameter in monitorOptions what triggers monitor to report empty blocks Closes: 2236 Signed-off-by: tomasz awramski --- .../main/typescript/common/core/bin/www.ts | 71 +++++--- .../connector/ServerMonitorPlugin.ts | 37 ++-- .../go-ethereum-socketio-connector.test.ts | 170 ++++++++---------- 3 files changed, 148 insertions(+), 130 deletions(-) diff --git a/packages/cactus-plugin-ledger-connector-go-ethereum-socketio/src/main/typescript/common/core/bin/www.ts b/packages/cactus-plugin-ledger-connector-go-ethereum-socketio/src/main/typescript/common/core/bin/www.ts index 8644e6e9cc..bc44111af9 100644 --- a/packages/cactus-plugin-ledger-connector-go-ethereum-socketio/src/main/typescript/common/core/bin/www.ts +++ b/packages/cactus-plugin-ledger-connector-go-ethereum-socketio/src/main/typescript/common/core/bin/www.ts @@ -24,12 +24,12 @@ if (!process.env["NODE_CONFIG_DIR"]) { import { configRead } from "@hyperledger/cactus-cmd-socketio-server"; import fs = require("fs"); -import { Server } from "socket.io" +import { Server } from "socket.io"; // Log settings import { getLogger } from "log4js"; const logger = getLogger("connector_main[" + process.pid + "]"); -logger.level = configRead('logLevel', 'info'); +logger.level = configRead("logLevel", "info"); // implementation class of a part dependent of end-chains (server plugin) import { ServerPlugin } from "../../../connector/ServerPlugin"; @@ -59,25 +59,30 @@ export async function startGoEthereumSocketIOConnector() { const Smonitor = new ServerMonitorPlugin(); // Get port from environment and store in Express. - const sslport = normalizePort(process.env.PORT || configRead('sslParam.port')); + const sslport = normalizePort( + process.env.PORT || configRead("sslParam.port"), + ); app.set("port", sslport); // Specify private key and certificate let keyString: string; let certString: string; try { - keyString = configRead('sslParam.keyValue'); - certString = configRead('sslParam.certValue'); + keyString = configRead("sslParam.keyValue"); + certString = configRead("sslParam.certValue"); } catch { - keyString = fs.readFileSync(configRead('sslParam.key'), "ascii"); - certString = fs.readFileSync(configRead('sslParam.cert'), "ascii"); + keyString = fs.readFileSync(configRead("sslParam.key"), "ascii"); + certString = fs.readFileSync(configRead("sslParam.cert"), "ascii"); } // Create HTTPS server. - const server = https.createServer({ - key: keyString, - cert: certString, - }, app); // Start as an https server. + const server = https.createServer( + { + key: keyString, + cert: certString, + }, + app, + ); // Start as an https server. const io = new Server(server); // Event listener for HTTPS server "error" event. @@ -126,7 +131,8 @@ export async function startGoEthereumSocketIOConnector() { // Check for the existence of the specified function and call it if it exists. if (Splug.isExistFunction(func)) { // Can be called with Server plugin function name. - (Splug as any)[func](args) + (Splug as any) + [func](args) .then((respObj: unknown) => { logger.info("*** RESPONSE ***"); logger.info("Client ID :" + client.id); @@ -206,7 +212,8 @@ export async function startGoEthereumSocketIOConnector() { // Check for the existence of the specified function and call it if it exists. if (Splug.isExistFunction(func)) { // Can be called with Server plugin function name. - (Splug as any)[func](args) + (Splug as any) + [func](args) .then((respObj: unknown) => { logger.info("*** RESPONSE ***"); logger.info("Client ID :" + client.id); @@ -244,8 +251,11 @@ export async function startGoEthereumSocketIOConnector() { /** * startMonitor: starting block generation event monitoring **/ - client.on("startMonitor", function () { - Smonitor.startMonitor(client.id, (event) => { + client.on("startMonitor", function (monitorOptions) { + + monitorOptions = monitorOptions ?? {allBlocks: false}; + logger.debug("monitorOptions", monitorOptions); + Smonitor.startMonitor(client.id, monitorOptions.allBlocks, (event) => { let emitType = ""; if (event.status == 200) { emitType = "eventReceived"; @@ -264,7 +274,7 @@ export async function startGoEthereumSocketIOConnector() { client.on("stopMonitor", function (reason) { Smonitor.stopMonitor(client.id).then(() => { logger.info("stopMonitor completed."); - }) + }); }); client.on("disconnect", function (reason) { @@ -277,22 +287,27 @@ export async function startGoEthereumSocketIOConnector() { }); // Listen on provided port, on all network interfaces. - return new Promise((resolve) => server.listen(sslport, () => resolve(server))); + return new Promise((resolve) => + server.listen(sslport, () => resolve(server)), + ); } if (require.main === module) { // When this file executed as a script, not loaded as module - run the connector - startGoEthereumSocketIOConnector().then((server) => { - const addr = server.address(); + startGoEthereumSocketIOConnector() + .then((server) => { + const addr = server.address(); - if (!addr) { - logger.error("Could not get running server address - exit."); - process.exit(1); - } + if (!addr) { + logger.error("Could not get running server address - exit."); + process.exit(1); + } - const bind = typeof addr === "string" ? "pipe " + addr : "port " + addr.port; - logger.debug("Listening on " + bind); - }).catch((err) => { - logger.error("Could not start go-ethereum-socketio connector:", err); - }); + const bind = + typeof addr === "string" ? "pipe " + addr : "port " + addr.port; + logger.debug("Listening on " + bind); + }) + .catch((err) => { + logger.error("Could not start go-ethereum-socketio connector:", err); + }); } diff --git a/packages/cactus-plugin-ledger-connector-go-ethereum-socketio/src/main/typescript/connector/ServerMonitorPlugin.ts b/packages/cactus-plugin-ledger-connector-go-ethereum-socketio/src/main/typescript/connector/ServerMonitorPlugin.ts index f6de01b3db..56be15efe8 100644 --- a/packages/cactus-plugin-ledger-connector-go-ethereum-socketio/src/main/typescript/connector/ServerMonitorPlugin.ts +++ b/packages/cactus-plugin-ledger-connector-go-ethereum-socketio/src/main/typescript/connector/ServerMonitorPlugin.ts @@ -48,7 +48,19 @@ export class ServerMonitorPlugin { * @param {string} clientId: Client ID from which monitoring start request was made * @param {function} cb: A callback function that receives monitoring results at any time. */ - startMonitor(clientId: string, cb: MonitorCallback) { + + blockSigner = (blockData: any) => { + const signedBlockData = signMessageJwt({ + blockData: blockData, + }); + logger.debug(`signedBlockData = ${signedBlockData}`); + return { + status: 200, + blockData: signedBlockData, + }; + }; + + startMonitor(clientId: string, allBlocks = false, cb: MonitorCallback) { logger.info("*** START MONITOR ***"); logger.info("Client ID :" + clientId); // Implement handling to receive events from an end-chain and return them in a callback function @@ -74,18 +86,21 @@ export class ServerMonitorPlugin { console.log("##[HL-BC] Validate transactions(D3)"); console.log("##[HL-BC] digital sign on valid transaction(D4)"); - if (trLength > 0) { + if (allBlocks == false) { + if (trLength > 0) { + logger.info("*** SEND BLOCK DATA ***"); + logger.debug( + "monitor is set to report blocks with at least one transaction", + ); + logger.debug(`blockData = ${JSON.stringify(blockData)}`); + const retObj = this.blockSigner(blockData); + cb(retObj); + } + } else { logger.info("*** SEND BLOCK DATA ***"); + logger.debug("monitor is set to report empty blocks"); logger.debug(`blockData = ${JSON.stringify(blockData)}`); - const signedBlockData = signMessageJwt({ - blockData: blockData, - }); - logger.debug(`signedBlockData = ${signedBlockData}`); - // Notify only if transaction exists - const retObj = { - status: 200, - blockData: signedBlockData, - }; + const retObj = this.blockSigner(blockData); cb(retObj); } } else { diff --git a/packages/cactus-plugin-ledger-connector-go-ethereum-socketio/src/test/typescript/integration/go-ethereum-socketio-connector.test.ts b/packages/cactus-plugin-ledger-connector-go-ethereum-socketio/src/test/typescript/integration/go-ethereum-socketio-connector.test.ts index f6fd9a0fa6..f3ea1c1c3a 100644 --- a/packages/cactus-plugin-ledger-connector-go-ethereum-socketio/src/test/typescript/integration/go-ethereum-socketio-connector.test.ts +++ b/packages/cactus-plugin-ledger-connector-go-ethereum-socketio/src/test/typescript/integration/go-ethereum-socketio-connector.test.ts @@ -62,12 +62,18 @@ describe("Go-Ethereum-SocketIO connector tests", () => { ////////////////////////////////// async function deploySmartContract(): Promise { - const txReceipt = await ledger.deployContract(HelloWorldContractJson.abi as any, "0x" + HelloWorldContractJson.bytecode); + const txReceipt = await ledger.deployContract( + HelloWorldContractJson.abi as any, + "0x" + HelloWorldContractJson.bytecode, + ); expect(txReceipt.contractAddress).toBeTruthy(); expect(txReceipt.status).toBeTrue(); expect(txReceipt.blockHash).toBeTruthy(); expect(txReceipt.blockNumber).toBeGreaterThan(1); - log.debug("Deployed test smart contract, TX on block number", txReceipt.blockNumber); + log.debug( + "Deployed test smart contract, TX on block number", + txReceipt.blockNumber, + ); return txReceipt.contractAddress ?? ""; } @@ -80,7 +86,7 @@ describe("Go-Ethereum-SocketIO connector tests", () => { imageName, imageVersion, emitContainerLogs: true, - logLevel: sutLogLevel + logLevel: sutLogLevel, }); await ledger.start(); const ledgerRpcUrl = await ledger.getRpcApiWebSocketHost(); @@ -174,7 +180,7 @@ describe("Go-Ethereum-SocketIO connector tests", () => { // SocketIOApiClient has timeout running for each request which is not cancellable at the moment. // Wait timeout amount of seconds to make sure all handles are closed. - await new Promise((resolve) => setTimeout(resolve, syncReqTimeout)) + await new Promise((resolve) => setTimeout(resolve, syncReqTimeout)); log.info("Prune Docker..."); await pruneDockerAllIfGithubAction({ logLevel: testLogLevel }); @@ -200,14 +206,10 @@ describe("Go-Ethereum-SocketIO connector tests", () => { test("Function getNumericBalance returns const account balance", async () => { const method = { type: "function", command: "getNumericBalance" }; const argsParam = { - args: [constTestAcc.address] + args: [constTestAcc.address], }; - const response = await apiClient.sendSyncRequest( - {}, - method, - argsParam, - ); + const response = await apiClient.sendSyncRequest({}, method, argsParam); expect(response).toBeTruthy(); expect(response.status).toEqual(200); @@ -228,18 +230,16 @@ describe("Go-Ethereum-SocketIO connector tests", () => { const method = { type: "function", command: "transferNumericAsset" }; const argsParam = { - args: [{ - fromAddress: fromAcc.address, - toAddress: toAcc.address, - amount: transferAmount, - }] + args: [ + { + fromAddress: fromAcc.address, + toAddress: toAcc.address, + amount: transferAmount, + }, + ], }; - const response = await apiClient.sendSyncRequest( - {}, - method, - argsParam, - ); + const response = await apiClient.sendSyncRequest({}, method, argsParam); expect(response).toBeTruthy(); }); @@ -251,11 +251,7 @@ describe("Go-Ethereum-SocketIO connector tests", () => { const method = { type: "function", command: "getNonce" }; const args = { args: { args: [constTestAcc.address] } }; - const response = await apiClient.sendSyncRequest( - {}, - method, - args, - ); + const response = await apiClient.sendSyncRequest({}, method, args); expect(response).toBeTruthy(); expect(response.status).toEqual(200); @@ -272,11 +268,7 @@ describe("Go-Ethereum-SocketIO connector tests", () => { const method = { type: "function", command: "toHex" }; const args = { args: { args: [value] } }; - const response = await apiClient.sendSyncRequest( - {}, - method, - args, - ); + const response = await apiClient.sendSyncRequest({}, method, args); expect(response).toBeTruthy(); expect(response.status).toEqual(200); @@ -307,11 +299,7 @@ describe("Go-Ethereum-SocketIO connector tests", () => { const method = { type: "function", command: "sendRawTransaction" }; const args = { args: [{ serializedTx: signedTx.rawTransaction }] }; - const response = await apiClient.sendSyncRequest( - {}, - method, - args, - ); + const response = await apiClient.sendSyncRequest({}, method, args); expect(response).toBeTruthy(); expect(response.status).toEqual(200); @@ -327,11 +315,7 @@ describe("Go-Ethereum-SocketIO connector tests", () => { const method = { type: "web3Eth", command: "getBalance" }; const args = { args: [constTestAcc.address] }; - const response = await apiClient.sendSyncRequest( - {}, - method, - args, - ); + const response = await apiClient.sendSyncRequest({}, method, args); expect(response).toBeTruthy(); expect(response.status).toEqual(200); @@ -346,11 +330,7 @@ describe("Go-Ethereum-SocketIO connector tests", () => { const method = { type: "web3Eth", command: "foo" }; const args = {}; - const response = await apiClient.sendSyncRequest( - {}, - method, - args, - ); + const response = await apiClient.sendSyncRequest({}, method, args); expect(response).toBeTruthy(); expect(response.status).toEqual(504); @@ -361,15 +341,14 @@ describe("Go-Ethereum-SocketIO connector tests", () => { * Test ServerPlugin contract function. */ test("Calling pure smart contract method works", async () => { - const contract = { abi: HelloWorldContractJson.abi, address: contractAddress }; + const contract = { + abi: HelloWorldContractJson.abi, + address: contractAddress, + }; const method = { type: "contract", command: "sayHello", function: "call" }; const args = { args: [] }; - const response = await apiClient.sendSyncRequest( - contract, - method, - args, - ); + const response = await apiClient.sendSyncRequest(contract, method, args); expect(response).toBeTruthy(); expect(response.status).toEqual(200); @@ -380,16 +359,15 @@ describe("Go-Ethereum-SocketIO connector tests", () => { /** * Test ServerPlugin contract method checking. */ - test("Calling contract returns error for non existant contract method", async () => { - const contract = { abi: HelloWorldContractJson.abi, address: contractAddress }; + test("Calling contract returns error for non existant contract method", async () => { + const contract = { + abi: HelloWorldContractJson.abi, + address: contractAddress, + }; const method = { type: "contract", command: "foo", function: "call" }; const args = { args: [] }; - const response = await apiClient.sendSyncRequest( - contract, - method, - args, - ); + const response = await apiClient.sendSyncRequest(contract, method, args); expect(response).toBeTruthy(); expect(response.status).toEqual(504); @@ -399,41 +377,51 @@ describe("Go-Ethereum-SocketIO connector tests", () => { /** * Test ServerMonitorPlugin startMonitor/stopMonitor functions. */ - test("Monitoring returns new block", async () => { - // Create monitoring promise and subscription - let monitorSub: any; - const newBlockPromise = new Promise((resolve, reject) => { - monitorSub = apiClient.watchBlocksV1().subscribe({ - next: block => resolve(block), - error: err => reject(err), - complete: () => reject("Unexpected watchBlocksV1 completion - reject."), + test.only( + "Monitoring returns new block", + async () => { + // Create monitoring promise and subscription + let monitorSub: any; + const newBlockPromise = new Promise((resolve, reject) => { + monitorSub = apiClient.watchBlocksV1().subscribe({ + next: (block) => resolve(block), + error: (err) => reject(err), + complete: () => + reject("Unexpected watchBlocksV1 completion - reject."), + }); }); - }); - try { - // Repeat deploySmartContract until block was received - while (true) { - const deployPromise = deploySmartContract(); - const resolvedValue = await Promise.race([newBlockPromise, deployPromise]); - log.debug("Monitor: resolvedValue", resolvedValue); - if (resolvedValue && resolvedValue.blockData) { - log.info("Resolved watchBlock promise"); - expect(resolvedValue.status).toEqual(200); - expect(resolvedValue.blockData.number).toBeGreaterThan(1); - expect(resolvedValue.blockData.transactions.length).toBeGreaterThan(0); - break; + try { + // Repeat deploySmartContract until block was received + while (true) { + const deployPromise = deploySmartContract(); + const resolvedValue = await Promise.race([ + newBlockPromise, + deployPromise, + ]); + log.debug("Monitor: resolvedValue", resolvedValue); + if (resolvedValue && resolvedValue.blockData) { + log.info("Resolved watchBlock promise"); + expect(resolvedValue.status).toEqual(200); + expect(resolvedValue.blockData.number).toBeGreaterThan(1); + expect(resolvedValue.blockData.transactions.length).toBeGreaterThan( + 0, + ); + break; + } + // Sleep 1 second and try again + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + } catch (error) { + throw error; + } finally { + if (monitorSub) { + monitorSub.unsubscribe(); + } else { + log.warn("monitorSub was not valid, could not unsubscribe"); } - // Sleep 1 second and try again - await new Promise((resolve) => setTimeout(resolve, 1000)) - } - } catch (error) { - throw error; - } finally { - if (monitorSub) { - monitorSub.unsubscribe(); - } else { - log.warn("monitorSub was not valid, could not unsubscribe"); } - } - }, testTimeout); + }, + testTimeout, + ); });