diff --git a/packages/cactus-plugin-ledger-connector-besu/package-lock.json b/packages/cactus-plugin-ledger-connector-besu/package-lock.json index 62c182d6956..d105b19f8bb 100644 --- a/packages/cactus-plugin-ledger-connector-besu/package-lock.json +++ b/packages/cactus-plugin-ledger-connector-besu/package-lock.json @@ -23,6 +23,7 @@ "devDependencies": { "@types/express": "4.17.8", "socket.io": "4.0.1", + "web3-core": "1.2.7", "web3-eth": "1.2.7" }, "engines": { @@ -440,6 +441,17 @@ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" }, + "node_modules/bufferutil": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.3.tgz", + "integrity": "sha512-yEYTwGndELGvfXsImMBLop58eaGW+YdONi1fNjTINSY98tmMmFijBG6WXgdkfuLNt4imzQNtIE+eBp1PVpMCSw==", + "hasInstallScript": true, + "optional": true, + "peer": true, + "dependencies": { + "node-gyp-build": "^4.2.0" + } + }, "node_modules/bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -1862,9 +1874,9 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "node_modules/nan": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", - "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==" + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" }, "node_modules/nano-json-stream-parser": { "version": "0.1.2", @@ -1884,6 +1896,18 @@ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" }, + "node_modules/node-gyp-build": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz", + "integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==", + "optional": true, + "peer": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/normalize-url": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", @@ -2858,6 +2882,17 @@ "node": ">= 4" } }, + "node_modules/utf-8-validate": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.5.tgz", + "integrity": "sha512-+pnxRYsS/axEpkrrEpzYfNZGXp0IjC/9RIxwM5gntY4Koi8SHmUGSfxfWqxZdRxrtaoVstuOzUp/rbs3JSPELQ==", + "hasInstallScript": true, + "optional": true, + "peer": true, + "dependencies": { + "node-gyp-build": "^4.2.0" + } + }, "node_modules/utf8": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", @@ -3740,6 +3775,16 @@ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" }, + "bufferutil": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.3.tgz", + "integrity": "sha512-yEYTwGndELGvfXsImMBLop58eaGW+YdONi1fNjTINSY98tmMmFijBG6WXgdkfuLNt4imzQNtIE+eBp1PVpMCSw==", + "optional": true, + "peer": true, + "requires": { + "node-gyp-build": "^4.2.0" + } + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -4945,9 +4990,9 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "nan": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", - "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==" + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" }, "nano-json-stream-parser": { "version": "0.1.2", @@ -4964,6 +5009,13 @@ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" }, + "node-gyp-build": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz", + "integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==", + "optional": true, + "peer": true + }, "normalize-url": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", @@ -5767,6 +5819,16 @@ "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=" }, + "utf-8-validate": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.5.tgz", + "integrity": "sha512-+pnxRYsS/axEpkrrEpzYfNZGXp0IjC/9RIxwM5gntY4Koi8SHmUGSfxfWqxZdRxrtaoVstuOzUp/rbs3JSPELQ==", + "optional": true, + "peer": true, + "requires": { + "node-gyp-build": "^4.2.0" + } + }, "utf8": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", diff --git a/packages/cactus-plugin-ledger-connector-besu/package.json b/packages/cactus-plugin-ledger-connector-besu/package.json index cb30aa4b9e4..df9def04344 100644 --- a/packages/cactus-plugin-ledger-connector-besu/package.json +++ b/packages/cactus-plugin-ledger-connector-besu/package.json @@ -102,6 +102,7 @@ "@hyperledger/cactus-test-tooling": "0.5.0", "@types/express": "4.17.8", "socket.io": "4.0.1", + "web3-core": "1.2.7", "web3-eth": "1.2.7" } } diff --git a/packages/cactus-plugin-ledger-connector-besu/src/main/json/openapi.json b/packages/cactus-plugin-ledger-connector-besu/src/main/json/openapi.json index f988ee60534..37d43120171 100644 --- a/packages/cactus-plugin-ledger-connector-besu/src/main/json/openapi.json +++ b/packages/cactus-plugin-ledger-connector-besu/src/main/json/openapi.json @@ -55,7 +55,26 @@ } } }, - + "BlockTransactionString":{ + "type": "object", + "properties": { + "transactions":{ + "type": "array", + "items": {} + } + } + }, + "GetBlockV1Response": { + "type": "object", + "required": [ + "blockTransactionString" + ], + "properties": { + "blockTransactionString": { + "$ref":"#/components/schemas/BlockTransactionString" + } + } + }, "EvmTransaction":{ "type":"object", "properties":{ @@ -98,9 +117,8 @@ "properties": { "transaction": { "$ref":"#/components/schemas/EvmTransaction" - } } - + } }, "GetTransactionV1Request": { "type": "object", @@ -110,7 +128,14 @@ "type": "string" } } - + } + }, + "GetBlockV1Request": { + "required": ["blockHashOrBlockNumber"], + "type": "object", + "properties": { + "blockHashOrBlockNumber": { + } }, "WatchBlocksV1": { "type": "string", @@ -846,6 +871,40 @@ } } }, + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/get-block": { + "post": { + "x-hyperledger-cactus": { + "http": { + "verbLowerCase": "post", + "path": "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/get-block" + } + }, + "operationId": "getBlockV1", + "summary": "Returns a block matching the block", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetBlockV1Request" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetBlockV1Response" + } + } + } + } + } + } + }, "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/run-transaction": { "post": { "x-hyperledger-cactus": { diff --git a/packages/cactus-plugin-ledger-connector-besu/src/main/typescript/generated/openapi/typescript-axios/api.ts b/packages/cactus-plugin-ledger-connector-besu/src/main/typescript/generated/openapi/typescript-axios/api.ts index 23dacedf609..618f9ec2164 100644 --- a/packages/cactus-plugin-ledger-connector-besu/src/main/typescript/generated/openapi/typescript-axios/api.ts +++ b/packages/cactus-plugin-ledger-connector-besu/src/main/typescript/generated/openapi/typescript-axios/api.ts @@ -199,66 +199,122 @@ export interface EvmTransaction { * @memberof EvmTransaction */ hash?: string; +} + +/** + * + * @export + * @interface EvmBlock + */ +export interface EvmBlock { /** * * @type {number} - * @memberof EvmTransaction + * @memberof EvmBlock */ - nonce?: number; + number?: number; /** * - * @type {any} - * @memberof EvmTransaction + * @type {string} + * @memberof EvmBlock */ - blockHash?: any | null; + hash?: string; /** * - * @type {any} - * @memberof EvmTransaction + * @type {number} + * @memberof EvmBlock */ - blockNumber?: any | null; + nonce?: number; /** * - * @type {any} - * @memberof EvmTransaction + * @type {string} + * @memberof EvmBlock */ - transactionIndex?: any | null; + parentHash?: string; /** * * @type {string} - * @memberof EvmTransaction + * @memberof EvmBlock */ - from?: string; + sha3Uncles?: string; /** * - * @type {any} - * @memberof EvmTransaction + * @type {string} + * @memberof EvmBlock */ - to?: any | null; + logsBloom?: string; /** * * @type {string} - * @memberof EvmTransaction + * @memberof EvmBlock */ - value?: string; + transactionsRoot?: string; /** * * @type {string} - * @memberof EvmTransaction + * @memberof EvmBlock */ - gasPrice?: string; + stateRoot?: string; + /** + * + * @type {string} + * @memberof EvmBlock + */ + miner?: string; /** * * @type {number} - * @memberof EvmTransaction + * @memberof EvmBlock */ - gas?: number; + difficulty?: number; + /** + * + * @type {number} + * @memberof EvmBlock + */ + totalDifficulty?: number; /** * * @type {string} - * @memberof EvmTransaction + * @memberof EvmBlock */ - input?: string; + extraData?: string; + /** + * + * @type {number} + * @memberof EvmBlock + */ + size?: number; + /** + * + * @type {number} + * @memberof EvmBlock + */ + gasLimit?: number; + /** + * + * @type {number} + * @memberof EvmBlock + */ + gasUsed?: number; + /** + * + * @type {any} + * @memberof EvmBlock + */ + timestamp?: any | null; + /** + * + * @type {Array} + * @memberof EvmBlock + */ + transactions?: Array; + /** + * + * @type {Array} + * @memberof EvmBlock + */ + uncles?: Array; } /** * @@ -305,6 +361,21 @@ export interface GetTransactionV1Request { */ transactionHash: string; } + +/** + * + * @export + * @interface GetBlockV1Request + */ +export interface GetBlockV1Request { + /** + * + * @type {any} + * @memberof GetBlockV1Request + */ + blockHashOrBlockNumber: any | null; +} + /** * * @export @@ -318,6 +389,20 @@ export interface GetTransactionV1Response { */ transaction: EvmTransaction; } + +/** + * + * @export + * @interface GetBlockV1Response + */ +export interface GetBlockV1Response { + /** + * + * @type {EvmBlock} + * @memberof GetBlockV1Response + */ + block: EvmBlock; +} /** * * @export @@ -957,6 +1042,40 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati options: localVarRequestOptions, }; }, + /** + * + * @summary Returns a block matching the block + * @param {GetBlockV1Request} [getBlockV1Request] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getBlockV1: async (getBlockV1Request?: GetBlockV1Request, options: any = {}): Promise => { + const localVarPath = `/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/get-block`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter, options.query); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(getBlockV1Request, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, /** * * @summary Get the Prometheus Metrics @@ -1066,6 +1185,17 @@ export const DefaultApiFp = function(configuration?: Configuration) { const localVarAxiosArgs = await localVarAxiosParamCreator.apiV1BesuRunTransaction(runTransactionRequest, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, + /** + * + * @summary Returns a block matching the block + * @param {GetBlockV1Request} [getBlockV1Request] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getBlockV1(getBlockV1Request?: GetBlockV1Request, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getBlockV1(getBlockV1Request, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, /** * * @summary Get the Prometheus Metrics @@ -1127,6 +1257,16 @@ export const DefaultApiFactory = function (configuration?: Configuration, basePa apiV1BesuRunTransaction(runTransactionRequest?: RunTransactionRequest, options?: any): AxiosPromise { return localVarFp.apiV1BesuRunTransaction(runTransactionRequest, options).then((request) => request(axios, basePath)); }, + /** + * + * @summary Returns a block matching the block + * @param {GetBlockV1Request} [getBlockV1Request] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getBlockV1(getBlockV1Request?: GetBlockV1Request, options?: any): AxiosPromise { + return localVarFp.getBlockV1(getBlockV1Request, options).then((request) => request(axios, basePath)); + }, /** * * @summary Get the Prometheus Metrics @@ -1192,6 +1332,18 @@ export class DefaultApi extends BaseAPI { return DefaultApiFp(this.configuration).apiV1BesuRunTransaction(runTransactionRequest, options).then((request) => request(this.axios, this.basePath)); } + /** + * + * @summary Returns a block matching the block + * @param {GetBlockV1Request} [getBlockV1Request] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public getBlockV1(getBlockV1Request?: GetBlockV1Request, options?: any) { + return DefaultApiFp(this.configuration).getBlockV1(getBlockV1Request, options).then((request) => request(this.axios, this.basePath)); + } + /** * * @summary Get the Prometheus Metrics diff --git a/packages/cactus-plugin-ledger-connector-besu/src/main/typescript/plugin-ledger-connector-besu.ts b/packages/cactus-plugin-ledger-connector-besu/src/main/typescript/plugin-ledger-connector-besu.ts index 9f42fd20566..f6d237ca1ac 100644 --- a/packages/cactus-plugin-ledger-connector-besu/src/main/typescript/plugin-ledger-connector-besu.ts +++ b/packages/cactus-plugin-ledger-connector-besu/src/main/typescript/plugin-ledger-connector-besu.ts @@ -62,9 +62,10 @@ import { Web3SigningCredentialType, GetTransactionV1Request, GetTransactionV1Response, -} from "./generated/openapi/typescript-axios/"; + GetBlockV1Request, + GetBlockV1Response, +} from "./generated/openapi/typescript-axios"; -import { RunTransactionEndpoint } from "./web-services/run-transaction-endpoint"; import { InvokeContractEndpoint } from "./web-services/invoke-contract-endpoint"; import { isWeb3SigningCredentialNone } from "./model-type-guards"; import { BesuSignTransactionEndpointV1 } from "./web-services/sign-transaction-endpoint-v1"; @@ -74,6 +75,8 @@ import { IGetPrometheusExporterMetricsEndpointV1Options, } from "./web-services/get-prometheus-exporter-metrics-endpoint-v1"; import { WatchBlocksV1Endpoint } from "./web-services/watch-blocks-v1-endpoint"; +import { RunTransactionEndpoint } from "./web-services/run-transaction-endpoint"; +import { GetBlockEndpoint } from "./web-services/get-block-v1-endpoint-"; export const E_KEYCHAIN_NOT_FOUND = "cactus.connector.besu.keychain_not_found"; @@ -212,6 +215,13 @@ export class PluginLedgerConnectorBesu }); endpoints.push(endpoint); } + { + const endpoint = new GetBlockEndpoint({ + connector: this, + logLevel: this.options.logLevel, + }); + endpoints.push(endpoint); + } { const endpoint = new InvokeContractEndpoint({ connector: this, @@ -722,4 +732,11 @@ export class PluginLedgerConnectorBesu ); return { transaction }; } + + public async getBlock( + request: GetBlockV1Request, + ): Promise { + const block = await this.web3.eth.getBlock(request.blockHashOrBlockNumber); + return { block }; + } } diff --git a/packages/cactus-plugin-ledger-connector-besu/src/main/typescript/web-services/get-block-v1-endpoint-.ts b/packages/cactus-plugin-ledger-connector-besu/src/main/typescript/web-services/get-block-v1-endpoint-.ts new file mode 100644 index 00000000000..7c8cee0e6b9 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-besu/src/main/typescript/web-services/get-block-v1-endpoint-.ts @@ -0,0 +1,101 @@ +import { Express, Request, Response } from "express"; + +import { + Logger, + Checks, + LogLevelDesc, + LoggerProvider, + IAsyncProvider, +} from "@hyperledger/cactus-common"; +import { + IEndpointAuthzOptions, + IExpressRequestHandler, + IWebServiceEndpoint, +} from "@hyperledger/cactus-core-api"; +import { registerWebServiceEndpoint } from "@hyperledger/cactus-core"; + +import { PluginLedgerConnectorBesu } from "../plugin-ledger-connector-besu"; + +import OAS from "../../json/openapi.json"; + +export interface IGetBlockEndpointOptions { + logLevel?: LogLevelDesc; + connector: PluginLedgerConnectorBesu; +} + +export class GetBlockEndpoint implements IWebServiceEndpoint { + public static readonly CLASS_NAME = "GetBlockEndpoint"; + + private readonly log: Logger; + + public get className(): string { + return GetBlockEndpoint.CLASS_NAME; + } + + constructor(public readonly options: IGetBlockEndpointOptions) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(options, `${fnTag} arg options`); + Checks.truthy(options.connector, `${fnTag} arg options.connector`); + + const level = this.options.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + } + + public getOasPath() { + return OAS.paths[ + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/get-block" + ]; + } + + public getPath(): string { + const apiPath = this.getOasPath(); + return apiPath.post["x-hyperledger-cactus"].http.path; + } + + public getVerbLowerCase(): string { + const apiPath = this.getOasPath(); + return apiPath.post["x-hyperledger-cactus"].http.verbLowerCase; + } + + public getOperationId(): string { + return this.getOasPath().post.operationId; + } + + getAuthorizationOptionsProvider(): IAsyncProvider { + // TODO: make this an injectable dependency in the constructor + return { + get: async () => ({ + isProtected: true, + requiredRoles: [], + }), + }; + } + + public async registerExpress( + expressApp: Express, + ): Promise { + await registerWebServiceEndpoint(expressApp, this); + return this; + } + + public getExpressRequestHandler(): IExpressRequestHandler { + return this.handleRequest.bind(this); + } + + public async handleRequest(req: Request, res: Response): Promise { + const reqTag = `${this.getVerbLowerCase()} - ${this.getPath()}`; + this.log.debug(reqTag); + const reqBody = req.body; + try { + const resBody = await this.options.connector.getBlock(reqBody); + res.json(resBody); + } catch (ex) { + this.log.error(`Crash while serving ${reqTag}`, ex); + res.status(500).json({ + message: "Internal Server Error", + error: ex?.stack || ex?.message, + }); + } + } +} diff --git a/packages/cactus-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-ledger-connector-besu/deploy-contract/get-block.test.ts b/packages/cactus-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-ledger-connector-besu/deploy-contract/get-block.test.ts new file mode 100644 index 00000000000..91c7221e721 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-ledger-connector-besu/deploy-contract/get-block.test.ts @@ -0,0 +1,63 @@ +import test, { Test } from "tape"; +import { v4 as uuidv4 } from "uuid"; +import { PluginRegistry } from "@hyperledger/cactus-core"; +import { + PluginLedgerConnectorBesu, + PluginFactoryLedgerConnector, + GetBlockV1Request, +} from "../../../../../main/typescript/public-api"; +import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory"; +import { BesuTestLedger } from "@hyperledger/cactus-test-tooling"; +import { LogLevelDesc } from "@hyperledger/cactus-common"; +import HelloWorldContractJson from "../../../../solidity/hello-world-contract/HelloWorld.json"; +import Web3 from "web3"; +import { PluginImportType } from "@hyperledger/cactus-core-api"; + +test("can get block from blockchain", async (t: Test) => { + const logLevel: LogLevelDesc = "TRACE"; + const besuTestLedger = new BesuTestLedger(); + await besuTestLedger.start(); + + test.onFinish(async () => { + await besuTestLedger.stop(); + await besuTestLedger.destroy(); + }); + + const rpcApiHttpHost = await besuTestLedger.getRpcApiHttpHost(); + const rpcApiWsHost = await besuTestLedger.getRpcApiWsHost(); + const web3 = new Web3(rpcApiHttpHost); + const testEthAccount = web3.eth.accounts.create(uuidv4()); + + const keychainEntryKey = uuidv4(); + const keychainEntryValue = testEthAccount.privateKey; + const keychainPlugin = new PluginKeychainMemory({ + instanceId: uuidv4(), + keychainId: uuidv4(), + // pre-provision keychain with mock backend holding the private key of the + // test account that we'll reference while sending requests with the + // signing credential pointing to this keychain entry. + backend: new Map([[keychainEntryKey, keychainEntryValue]]), + logLevel, + }); + keychainPlugin.set( + HelloWorldContractJson.contractName, + HelloWorldContractJson, + ); + const factory = new PluginFactoryLedgerConnector({ + pluginImportType: PluginImportType.Local, + }); + const connector: PluginLedgerConnectorBesu = await factory.create({ + rpcApiHttpHost, + rpcApiWsHost, + instanceId: uuidv4(), + pluginRegistry: new PluginRegistry({ plugins: [keychainPlugin] }), + }); + + const request: GetBlockV1Request = { blockHashOrBlockNumber: 0 }; + const currentBlock = await connector.getBlock(request.blockHashOrBlockNumber); + t.comment(JSON.stringify(currentBlock)); + //makes the information in to string + t.ok(currentBlock, " Block response is OK :-)"); + t.equal(typeof currentBlock, "object", "Block response type is OK :-)"); + t.end(); +}); diff --git a/packages/cactus-test-plugin-ledger-connector-besu/package-lock.json b/packages/cactus-test-plugin-ledger-connector-besu/package-lock.json index e5cfeaf700d..300dd284255 100644 --- a/packages/cactus-test-plugin-ledger-connector-besu/package-lock.json +++ b/packages/cactus-test-plugin-ledger-connector-besu/package-lock.json @@ -505,6 +505,26 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" }, + "node_modules/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dependencies": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", @@ -1422,6 +1442,46 @@ "safe-buffer": "^5.1.1" } }, + "node_modules/express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dependencies": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/ext": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", @@ -2712,66 +2772,6 @@ "node": ">=6" } }, - "node_modules/servify/node_modules/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "dependencies": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/servify/node_modules/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "dependencies": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -4706,6 +4706,23 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", @@ -5539,6 +5556,43 @@ "safe-buffer": "^5.1.1" } }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, "ext": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", @@ -6612,62 +6666,6 @@ "express": "^4.14.0", "request": "^2.79.0", "xhr": "^2.3.3" - }, - "dependencies": { - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - } - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - } - } } }, "setimmediate": { diff --git a/packages/cactus-test-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-validator-besu/get-block-endpoint.test.ts b/packages/cactus-test-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-validator-besu/get-block-endpoint.test.ts new file mode 100644 index 00000000000..7ba80ad2a0c --- /dev/null +++ b/packages/cactus-test-plugin-ledger-connector-besu/src/test/typescript/integration/plugin-validator-besu/get-block-endpoint.test.ts @@ -0,0 +1,147 @@ +import test, { Test } from "tape-promise/tape"; + +import { v4 as uuidv4 } from "uuid"; +import { createServer } from "http"; +import KeyEncoder from "key-encoder"; +import { AddressInfo } from "net"; + +import { + ApiServer, + AuthorizationProtocol, + ConfigService, +} from "@hyperledger/cactus-cmd-api-server"; +import { + Secp256k1Keys, + KeyFormat, + LogLevelDesc, +} from "@hyperledger/cactus-common"; + +import { + BesuTestLedger, + pruneDockerAllIfGithubAction, +} from "@hyperledger/cactus-test-tooling"; + +import { + BesuApiClientOptions, + BesuApiClient, + IPluginLedgerConnectorBesuOptions, + PluginLedgerConnectorBesu, + GetBlockV1Request, +} from "@hyperledger/cactus-plugin-ledger-connector-besu"; + +import { PluginRegistry } from "@hyperledger/cactus-core"; + +import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory"; + +const testCase = "Test sign transaction endpoint"; +const logLevel: LogLevelDesc = "TRACE"; + +test("BEFORE " + testCase, async (t: Test) => { + const pruning = pruneDockerAllIfGithubAction({ logLevel }); + await t.doesNotReject(pruning, "Pruning didn't throw OK"); + t.end(); +}); + +test(testCase, async (t: Test) => { + const keyEncoder: KeyEncoder = new KeyEncoder("secp256k1"); + const keychainId = uuidv4(); + const keychainRef = uuidv4(); + + const { privateKey } = Secp256k1Keys.generateKeyPairsBuffer(); + const keyHex = privateKey.toString("hex"); + const pem = keyEncoder.encodePrivate(keyHex, KeyFormat.Raw, KeyFormat.PEM); + + const keychain = new PluginKeychainMemory({ + backend: new Map([[keychainRef, pem]]), + keychainId, + logLevel, + instanceId: uuidv4(), + }); + + const httpServer1 = createServer(); + await new Promise((resolve, reject) => { + httpServer1.once("error", reject); + httpServer1.once("listening", resolve); + httpServer1.listen(0, "127.0.0.1"); + }); + const addressInfo1 = httpServer1.address() as AddressInfo; + t.comment(`HttpServer1 AddressInfo: ${JSON.stringify(addressInfo1)}`); + const node1Host = `http://${addressInfo1.address}:${addressInfo1.port}`; + t.comment(`Cactus Node 1 Host: ${node1Host}`); + + const besuTestLedger = new BesuTestLedger(); + await besuTestLedger.start(); + + const tearDown = async () => { + await besuTestLedger.stop(); + await besuTestLedger.destroy(); + }; + + test.onFinish(tearDown); + + const rpcApiHttpHost = await besuTestLedger.getRpcApiHttpHost(); + const rpcApiWsHost = await besuTestLedger.getRpcApiWsHost(); + + // 2. Instantiate plugin registry which will provide the web service plugin with the key value storage plugin + const pluginRegistry = new PluginRegistry({ plugins: [keychain] }); + + // 3. Instantiate the web service consortium plugin + const options: IPluginLedgerConnectorBesuOptions = { + instanceId: uuidv4(), + rpcApiHttpHost, + rpcApiWsHost, + pluginRegistry, + logLevel, + }; + const pluginValidatorBesu = new PluginLedgerConnectorBesu(options); + + // 4. Create the API Server object that we embed in this test + const configService = new ConfigService(); + const apiServerOptions = configService.newExampleConfig(); + apiServerOptions.authorizationProtocol = AuthorizationProtocol.NONE; + apiServerOptions.configFile = ""; + apiServerOptions.apiCorsDomainCsv = "*"; + apiServerOptions.apiPort = addressInfo1.port; + apiServerOptions.cockpitPort = 0; + apiServerOptions.apiTlsEnabled = false; + const config = configService.newExampleConfigConvict(apiServerOptions); + + pluginRegistry.add(pluginValidatorBesu); + + const apiServer = new ApiServer({ + httpServerApi: httpServer1, + config: config.getProperties(), + pluginRegistry, + }); + + // 5. make sure the API server is shut down when the testing if finished. + test.onFinish(() => apiServer.shutdown()); + + // 6. Start the API server which is now listening on port A and it's healthcheck works through the main SDK + await apiServer.start(); + + // 7. Instantiate the main SDK dynamically with whatever port the API server ended up bound to (port 0) + t.comment(`AddressInfo: ${JSON.stringify(addressInfo1)}`); + + const request: GetBlockV1Request = { + blockHashOrBlockNumber: 0, + }; + + const configuration = new BesuApiClientOptions({ basePath: node1Host }); + const api = new BesuApiClient(configuration); + + // Test for 200 valid response test case + const res = await api.getBlockV1(request); + + const { status, data } = res; + t.true(status >= 200, "status GTE 200 OK"); + t.true(status < 300, "status LT 300 OK"); + t.ok(data, "GetBlockResponse Truthy OK"); + t.true(typeof data.block === "object", "Response data is OK"); +}); + +test("AFTER " + testCase, async (t: Test) => { + const pruning = pruneDockerAllIfGithubAction({ logLevel }); + await t.doesNotReject(pruning, "Pruning didn't throw OK"); + t.end(); +});