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(connector-go-ethereum): add getBlock and getTransactionReceipt methods to connector #2256

Merged
merged 1 commit into from
Mar 29, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -252,19 +252,20 @@ export async function startGoEthereumSocketIOConnector() {
* startMonitor: starting block generation event monitoring
**/
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";
logger.info("event data callbacked.");
} else {
emitType = "monitor_error";
}
client.emit(emitType, event);
});
Smonitor.startMonitor(
client.id,
monitorOptions?.allBlocks ?? false,
(event) => {
let emitType = "";
if (event.status == 200) {
emitType = "eventReceived";
logger.info("event data callbacked.");
} else {
emitType = "monitor_error";
}
client.emit(emitType, event);
},
);
});

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ import * as SplugUtil from "./PluginUtil";
// Load libraries, SDKs, etc. according to specifications of endchains as needed
import Web3 from "web3";
import { AbiItem } from "web3-utils";
import { BlockNumber } from "web3-core";
import { safeStringifyException } from "@hyperledger/cactus-common";

var WEB3_HTTP_PROVIDER_OPTIONS = {
const WEB3_HTTP_PROVIDER_OPTIONS = {
keepAlive: true,
};

Expand Down Expand Up @@ -60,7 +61,12 @@ function getWeb3Provider(host: string) {
}
}

const web3 = new Web3(getWeb3Provider(configRead("ledgerUrl")));
const web3Provider = getWeb3Provider(configRead("ledgerUrl"));
const web3 = new Web3(web3Provider);

export function shutdown() {
web3Provider.disconnect();
}

/*
* ServerPlugin
Expand Down Expand Up @@ -93,6 +99,135 @@ export class ServerPlugin {
}
}

/*
* getBlock
*
* @param {String|Number} block hash, block number or string:"earliest", "latest" or "pending"
*
* @return {Object} JSON object
*/

async getBlock(args: any) {
logger.debug("getBlock start");

const blockID: BlockNumber = args.args.args[0];
const returnTransactionObjects = args.args.args[1] ?? false;
const reqID = args["reqID"];

if (!blockID) {
const emsg = "JSON parse error!";
logger.warn(emsg);
throw {
resObj: {
status: 504,
errorDetail: emsg,
},
id: reqID,
};
}

try {
const blockData = await web3.eth.getBlock(
blockID,
returnTransactionObjects,
);
const result = {
blockData: blockData,
};
logger.debug(`getBlock(): result: ${result}`);

const signedResults = signMessageJwt({ result });
logger.debug(`getBlock(): signedResults: ${signedResults}`);

return {
resObj: {
status: 200,
data: signedResults,
},
id: reqID,
};
} catch (e) {
const retObj = {
resObj: {
status: 504,
errorDetail: safeStringifyException(e),
},
id: reqID,
};

logger.error(`##getBlock: retObj: ${JSON.stringify(retObj)}`);

throw retObj;
}
}

/*
* getTransactionReceipt
*
* @param {String} transaction hash
*
* @return {Object} JSON object
*/

async getTransactionReceipt(args: any) {
logger.debug("getTransactionReceipt start");

const txHash: string = args.args.args[0];
const reqID = args["reqID"];

if (txHash === undefined) {
const emsg = "JSON parse error!";
logger.warn(emsg);
throw {
resObj: {
status: 504,
errorDetail: emsg,
},
id: reqID,
};
}

try {
const txReceipt = await web3.eth.getTransactionReceipt(txHash);
logger.info(`getTransactionReceipt(): txReceipt: ${txReceipt}`);

const result = {
txReceipt: txReceipt,
};
logger.debug(`getTransactionReceipt(): result: ${result}`);

const signedResults = signMessageJwt({ result: result });
logger.debug(`getTransactionReceipt(): signedResults: ${signedResults}`);

const retObj = {
resObj: {
status: 200,
data: signedResults,
},
id: reqID,
};

logger.debug(
`##getTransactionReceipt: retObj: ${JSON.stringify(retObj)}`,
);
return retObj;
} catch (e) {
const retObj = {
resObj: {
status: 504,
errorDetail: safeStringifyException(e),
},
id: reqID,
};

logger.error(
`##getTransactionReceipt: retObj: ${JSON.stringify(retObj)}`,
);

throw retObj;
}
}

// Define an arbitrary function and implement it according to specifications of end-chains
/**
* getNumericBalance
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { startGoEthereumSocketIOConnector } from "./common/core/bin/www"
export { startGoEthereumSocketIOConnector } from "./common/core/bin/www";
export { shutdown } from "./connector/ServerPlugin";
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,17 @@ describe("Go-Ethereum-SocketIO connector tests", () => {
let connectorServer: HttpsServer;
let apiClient: SocketIOApiClient;
let constTestAcc: Account;
let connectorModule: any;
const constTestAccBalance = 5 * 1000000;

//////////////////////////////////
// Environment Setup
//////////////////////////////////

async function deploySmartContract(): Promise<string> {
async function deploySmartContract(): Promise<{
contractAddress: string;
blockNumber: number;
}> {
const txReceipt = await ledger.deployContract(
HelloWorldContractJson.abi as any,
"0x" + HelloWorldContractJson.bytecode,
Expand All @@ -74,7 +78,11 @@ describe("Go-Ethereum-SocketIO connector tests", () => {
"Deployed test smart contract, TX on block number",
txReceipt.blockNumber,
);
return txReceipt.contractAddress ?? "";

return {
contractAddress: txReceipt.contractAddress ?? "",
blockNumber: txReceipt.blockNumber,
};
}

beforeAll(async () => {
Expand All @@ -99,7 +107,8 @@ describe("Go-Ethereum-SocketIO connector tests", () => {
web3 = new Web3(ledgerRpcUrl);

// Deploy test smart contract
contractAddress = await deploySmartContract();
const deployOutput = await deploySmartContract();
contractAddress = deployOutput.contractAddress;

// Generate connector private key and certificate
const pkiGenerator = new SelfSignedPkiGenerator();
Expand All @@ -125,7 +134,7 @@ describe("Go-Ethereum-SocketIO connector tests", () => {
process.env["NODE_CONFIG"] = configJson;

// Load connector module
const connectorModule = await import("../../../main/typescript/index");
connectorModule = await import("../../../main/typescript/index");

// Run the connector
connectorServer = await connectorModule.startGoEthereumSocketIOConnector();
Expand Down Expand Up @@ -160,6 +169,10 @@ describe("Go-Ethereum-SocketIO connector tests", () => {
afterAll(async () => {
log.info("FINISHING THE TESTS");

if (connectorModule) {
connectorModule.shutdown();
}

if (apiClient) {
log.info("Close ApiClient connection...");
apiClient.close();
Expand Down Expand Up @@ -200,6 +213,97 @@ describe("Go-Ethereum-SocketIO connector tests", () => {
expect(balance).toEqual(constTestAccBalance.toString());
});

test("getBlock returns valid block data for different methods", async () => {
const getBlock = async (param: string | number) => {
const method = { type: "function", command: "getBlock" };
const args = { args: [param] };
const response = await apiClient.sendSyncRequest({}, method, args);
expect(response).toBeTruthy();
return response;
};

const latestBlock = await getBlock("latest");
expect(latestBlock.status).toEqual(200);
const { data: blockDataByHash } = await getBlock(
latestBlock.data.blockData.hash,
);
expect(blockDataByHash.blockData.hash).toEqual(
latestBlock.data.blockData.hash,
);
const { data: blockDataByNumber } = await getBlock(
latestBlock.data.blockData.number,
);
expect(blockDataByNumber.blockData.hash).toEqual(
blockDataByHash.blockData.hash,
);

// Assert transaction data is not returned
const { blockNumber } = await deploySmartContract();
const blockWithTx = await getBlock(blockNumber);
const firstTx = blockWithTx.data.blockData.transactions[0];
expect(firstTx).toBeTruthy();
// Only string hashes are returned when flag is false
expect(typeof firstTx).toEqual("string");
});

test("getBlock returns transaction data when requested", async () => {
const method = { type: "function", command: "getBlock" };
const { blockNumber } = await deploySmartContract();
const args = { args: [blockNumber, true] };

const response = await apiClient.sendSyncRequest({}, method, args);

// Assert correct response
expect(response).toBeTruthy();
expect(response.status).toEqual(200);

// Assert valid block data
const block = response.data.blockData;
expect(block).toBeTruthy();
expect(block.hash).toBeTruthy();

// Assert transaction data was returned as requested
expect(block.transactions.length).toBeGreaterThan(0);
const firstTx = block.transactions[0];
expect(firstTx).toBeTruthy();
expect(firstTx.hash).toBeTruthy();
});

test("Function getTransactionReceipt returns transaction receipt of given transaction", async () => {
async function getTransactionHash() {
const fromAccInitBalance = 1500;
const toAccInitBalance = 1500;
const transferAmount = 500;
//creating two accounts to perform transaction on them
const fromAddress = await ledger.createEthTestAccount(fromAccInitBalance);
const toAcc = await ledger.createEthTestAccount(toAccInitBalance);
// adding account using a private key to the wallet
web3.eth.accounts.wallet.add(fromAddress.privateKey);

const signedTx = await fromAddress.signTransaction({
from: fromAddress.address,
to: toAcc.address,
value: transferAmount,
gas: 1000000,
});
const method = { type: "function", command: "sendRawTransaction" };
const args = { args: [{ serializedTx: signedTx.rawTransaction }] };
// transfering funds to trigger transaction
const response = await apiClient.sendSyncRequest({}, method, args);
// returning only transaction hash
return response.data.txid;
}

const transactionHash = await getTransactionHash();
const method = { type: "function", command: "getTransactionReceipt" };
const args = { args: [transactionHash] };

const response = await apiClient.sendSyncRequest({}, method, args);
expect(response).toBeTruthy();
expect(response.status).toEqual(200);
expect(response.data.txReceipt.transactionHash).toEqual(transactionHash);
});

/**
* Test ServerPlugin getNumericBalance function.
*/
Expand Down Expand Up @@ -294,7 +398,6 @@ describe("Go-Ethereum-SocketIO connector tests", () => {
gas: 1000000,
});
expect(signedTx).toBeTruthy();
log.warn(signedTx);

const method = { type: "function", command: "sendRawTransaction" };
const args = { args: [{ serializedTx: signedTx.rawTransaction }] };
Expand Down Expand Up @@ -377,7 +480,7 @@ describe("Go-Ethereum-SocketIO connector tests", () => {
/**
* Test ServerMonitorPlugin startMonitor/stopMonitor functions.
*/
test.only(
test(
"Monitoring returns new block",
async () => {
// Create monitoring promise and subscription
Expand Down