From a7f6a9937e1c2352b6bccb8d707dbe0f0b929401 Mon Sep 17 00:00:00 2001 From: Michal Bajer Date: Mon, 18 Sep 2023 12:27:51 +0000 Subject: [PATCH] feat(cactus-plugin-ledger-connector-ethereum): update web3js to 4.X - Update web3js packages from 1.10 to 4.0.3 in `cactus-plugin-ledger-connector-ethereum` and `cactus-test-plugin-ledger-connector-ethereum`. This allows interacting with most recent geth nodes. - Refactor all ethereum tests. Most of the test cases were duplicated multiple times (between different quorum ledger versions test and deployment methods). I've removed all this duplication while maintaining similar level of test coverage. New tests use Geth test ledger instead of Quorum one. - Add web3js type conversions methods to minimize impact of poor dynamic typing in this early release of 4.X. - Update API. In 4.X all numeric responses has been converted to BigNum. To keep up with this some fields has been changed to string instead of number when necessary. Add some missing fields as well. - Add `estimateMaxFeePerGas` method for estimating current transaction cost. - Fix invalid `runTransact` response type. - Add test script for checking integration with Alchemy that must be executed manually (it's excluded from CI at the moment) - `geth-alchemy-integration-manual-check.test`. Instructions on how to run it has been added to package README. Closes: #2580 Depends on #2535 Depends on #2578 Signed-off-by: Michal Bajer --- .github/workflows/ci.yaml | 29 +- .taprc | 11 - jest.config.js | 12 +- .../src/main/typescript/api-server.ts | 22 + .../README.md | 147 +++-- .../package.json | 10 +- .../src/main/json/openapi.json | 223 +++---- .../api-client/ethereum-api-client.ts | 11 +- .../generated/openapi/typescript-axios/api.ts | 216 +++---- .../openapi/typescript-axios/base.ts | 2 +- .../openapi/typescript-axios/common.ts | 2 +- .../openapi/typescript-axios/configuration.ts | 2 +- .../openapi/typescript-axios/index.ts | 2 +- .../plugin-ledger-connector-ethereum.ts | 358 ++++++++---- .../src/main/typescript/public-api.ts | 2 +- .../{ => types}/model-type-guards.ts | 2 +- .../src/main/typescript/types/util-types.ts | 34 ++ ...-solidity-bytecode-endpoint-json-object.ts | 3 +- ...loy-contract-solidity-bytecode-endpoint.ts | 5 +- ...prometheus-exporter-metrics-endpoint-v1.ts | 8 +- .../web-services/invoke-contract-endpoint.ts | 2 +- ...invoke-raw-web3eth-contract-v1-endpoint.ts | 7 +- .../invoke-raw-web3eth-method-v1-endpoint.ts | 7 +- .../web-services/run-transaction-endpoint.ts | 4 +- .../web-services/watch-blocks-v1-endpoint.ts | 103 ++-- ...us.ts => get-ethereum-connector-status.ts} | 0 .../HelloWorldWithArg.json | 68 +++ .../HelloWorldWithArg.sol | 28 + ...oy-and-invoke-using-json-object-v1.test.ts | 416 +++++++++++++ ...eploy-and-invoke-using-keychain-v1.test.ts | 545 ++++++++++++++++++ ...s => geth-invoke-web3-contract-v1.test.ts} | 56 +- ....ts => geth-invoke-web3-method-v1.test.ts} | 36 +- .../openapi-validation-no-keychain.test.ts | 323 ----------- .../openapi/openapi-validation.test.ts | 427 -------------- ...ct-from-json-json-object-endpoints.test.ts | 390 ------------- ...loy-contract-from-json-json-object.test.ts | 381 ------------ .../v2.3.0-deploy-contract-from-json.test.ts | 469 --------------- ...oke-contract-json-object-endpoints.test.ts | 357 ------------ ...v2.3.0-invoke-contract-json-object.test.ts | 310 ---------- .../v2.3.0-invoke-contract.test.ts | 380 ------------ ...ct-from-json-json-object-endpoints.test.ts | 386 ------------- ...loy-contract-from-json-json-object.test.ts | 378 ------------ .../v21.4.1-deploy-contract-from-json.test.ts | 441 -------------- ...oke-contract-json-object-endpoints.test.ts | 352 ----------- ...21.4.1-invoke-contract-json-object.test.ts | 306 ---------- .../v21.4.1-invoke-contract.test.ts | 365 ------------ ...h-alchemy-integration-manual-check.test.ts | 119 ++++ .../typescript/unit/model-type-guards.test.ts | 2 +- .../tsconfig.json | 3 + .../src/main/typescript/geth-test-ledger.ts | 3 +- .../package.json | 6 +- ...ntegration-with-ethereum-connector.test.ts | 127 ++-- .../deploy-contract-via-web-service.test.ts | 270 --------- .../tsconfig.json | 3 + yarn.lock | 52 +- 55 files changed, 2012 insertions(+), 6211 deletions(-) rename packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/{ => types}/model-type-guards.ts (96%) create mode 100644 packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/types/util-types.ts rename packages/cactus-plugin-ledger-connector-ethereum/src/scripts/{get-quorum-connector-status.ts => get-ethereum-connector-status.ts} (100%) create mode 100644 packages/cactus-plugin-ledger-connector-ethereum/src/test/solidity/hello-world-with-arg-contract/HelloWorldWithArg.json create mode 100644 packages/cactus-plugin-ledger-connector-ethereum/src/test/solidity/hello-world-with-arg-contract/HelloWorldWithArg.sol create mode 100644 packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/geth-contract-deploy-and-invoke-using-json-object-v1.test.ts create mode 100644 packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/geth-contract-deploy-and-invoke-using-keychain-v1.test.ts rename packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/{plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-web3-contract-v1.test.ts => geth-invoke-web3-contract-v1.test.ts} (78%) rename packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/{plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-web3-method-v1.test.ts => geth-invoke-web3-method-v1.test.ts} (82%) delete mode 100644 packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/openapi/openapi-validation-no-keychain.test.ts delete mode 100644 packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/openapi/openapi-validation.test.ts delete mode 100644 packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-deploy-contract-from-json-json-object-endpoints.test.ts delete mode 100644 packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-deploy-contract-from-json-json-object.test.ts delete mode 100644 packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-deploy-contract-from-json.test.ts delete mode 100644 packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-invoke-contract-json-object-endpoints.test.ts delete mode 100644 packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-invoke-contract-json-object.test.ts delete mode 100644 packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-invoke-contract.test.ts delete mode 100644 packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-deploy-contract-from-json-json-object-endpoints.test.ts delete mode 100644 packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-deploy-contract-from-json-json-object.test.ts delete mode 100644 packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-deploy-contract-from-json.test.ts delete mode 100644 packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-contract-json-object-endpoints.test.ts delete mode 100644 packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-contract-json-object.test.ts delete mode 100644 packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-contract.test.ts create mode 100644 packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/manual/geth-alchemy-integration-manual-check.test.ts delete mode 100644 packages/cactus-test-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/deploy-contract-via-web-service.test.ts diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 484bb0e6d8c..abc2c8219ce 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -134,11 +134,11 @@ jobs: uses: actions/github-script@v6.4.1 with: script: | - const failMsg = "yarn codegen script produced version control " + - "side-effects: source files have been changed by it that are " + - "otherwise are under version control. " + - "This means (99% of the time) that you need to run the " + - "yarn codegen script locally and then include the changes it " + + const failMsg = "yarn codegen script produced version control " + + "side-effects: source files have been changed by it that are " + + "otherwise are under version control. " + + "This means (99% of the time) that you need to run the " + + "yarn codegen script locally and then include the changes it " + "makes in your own commit when submitting your pull request."; core.setFailed(failMsg) @@ -1325,9 +1325,7 @@ jobs: FULL_BUILD_DISABLED: true JEST_TEST_PATTERN: packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/(unit|integration|benchmark)/.*/*.test.ts JEST_TEST_RUNNER_DISABLED: false - TAPE_TEST_PATTERN: >- - --files={./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/openapi/openapi-validation-no-keychain.test.ts,./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/openapi/openapi-validation.test.ts,./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-deploy-contract-from-json-json-object-endpoints.test.ts,./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-deploy-contract-from-json-json-object.test.ts,./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-invoke-contract-json-object-endpoints.test.ts,./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-invoke-contract-json-object.test.ts,./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-deploy-contract-from-json-json-object-endpoints.test.ts,./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-deploy-contract-from-json-json-object.test.ts,./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-contract-json-object-endpoints.test.ts,./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-contract-json-object.test.ts} - TAPE_TEST_RUNNER_DISABLED: false + TAPE_TEST_RUNNER_DISABLED: true needs: build-dev runs-on: ubuntu-20.04 steps: @@ -1336,17 +1334,14 @@ jobs: with: node-version: v16.14.2 - uses: actions/checkout@v3.5.2 - - id: yarn-cache-dir-path - name: Get yarn cache directory path - run: echo "::set-output name=dir::$(yarn cache dir)" - id: yarn-cache name: Restore Yarn Cache - uses: actions/cache@v3.0.4 + uses: actions/cache@v3.3.1 with: - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('./yarn.lock') }} + path: ./.yarn/ restore-keys: | - ${{ runner.os }}-yarn- + ${{ runner.os }}-yarn-${{ hashFiles('./yarn.lock') }} - run: ./tools/ci.sh cactus-plugin-ledger-connector-quorum: continue-on-error: false @@ -1637,13 +1632,13 @@ jobs: restore-keys: | ${{ runner.os }}-yarn-${{ hashFiles('./yarn.lock') }} - run: ./tools/ci.sh - + - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 - name: Run solidity tests run: cd packages/cactus-plugin-htlc-eth-besu && forge test -vvvvv - + cactus-test-plugin-htlc-eth-besu-erc20: continue-on-error: false env: diff --git a/.taprc b/.taprc index 5b6bdfcb641..0cffbe2a8a2 100644 --- a/.taprc +++ b/.taprc @@ -21,17 +21,6 @@ files: - ./packages/cactus-plugin-ledger-connector-quorum/src/test/typescript/integration/plugin-ledger-connector-quorum/deploy-contract/v2.3.0-deploy-contract-from-json-json-object.test.ts - ./packages/cactus-plugin-ledger-connector-quorum/src/test/typescript/integration/plugin-ledger-connector-quorum/deploy-contract/openapi/openapi-validation.test.ts - ./packages/cactus-plugin-ledger-connector-quorum/src/test/typescript/integration/plugin-ledger-connector-quorum/deploy-contract/v21.4.1-invoke-contract-json-object.test.ts - - ./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-invoke-contract-json-object-endpoints.test.ts - - ./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-contract-json-object-endpoints.test.ts - - ./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-deploy-contract-from-json-json-object-endpoints.test.ts - - ./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-deploy-contract-from-json-json-object-endpoints.test.ts - - ./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/openapi/openapi-validation-no-keychain.test.ts - - ./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-invoke-contract-json-object.test.ts - - ./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-deploy-contract-from-json.test.ts - - ./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-deploy-contract-from-json-json-object.test.ts - - ./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-deploy-contract-from-json-json-object.test.ts - - ./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/openapi/openapi-validation.test.ts - - ./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-contract-json-object.test.ts - ./extensions/cactus-plugin-htlc-coordinator-besu/src/test/typescript/integration/plugin-htlc-coordinator/counterparty-htlc-endpoint.test.ts - ./extensions/cactus-plugin-htlc-coordinator-besu/src/test/typescript/integration/plugin-htlc-coordinator/own-htlc-endpoint.test.ts - ./extensions/cactus-plugin-htlc-coordinator-besu/src/test/typescript/integration/plugin-htlc-coordinator/withdraw-counterparty-endpoint.test.ts diff --git a/jest.config.js b/jest.config.js index 3adc29071a9..fda2795f238 100644 --- a/jest.config.js +++ b/jest.config.js @@ -27,17 +27,7 @@ module.exports = { `./packages/cactus-plugin-ledger-connector-quorum/src/test/typescript/integration/plugin-ledger-connector-quorum/deploy-contract/v2.3.0-deploy-contract-from-json-json-object.test.ts`, `./packages/cactus-plugin-ledger-connector-quorum/src/test/typescript/integration/plugin-ledger-connector-quorum/deploy-contract/openapi/openapi-validation.test.ts`, `./packages/cactus-plugin-ledger-connector-quorum/src/test/typescript/integration/plugin-ledger-connector-quorum/deploy-contract/v21.4.1-invoke-contract-json-object.test.ts`, - `./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-invoke-contract-json-object-endpoints.test.ts`, - `./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-contract-json-object-endpoints.test.ts`, - `./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-deploy-contract-from-json-json-object-endpoints.test.ts`, - `./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-deploy-contract-from-json-json-object-endpoints.test.ts`, - `./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/openapi/openapi-validation-no-keychain.test.ts`, - `./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-invoke-contract-json-object.test.ts`, - `./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-deploy-contract-from-json.test.ts`, - `./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-deploy-contract-from-json-json-object.test.ts`, - `./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-deploy-contract-from-json-json-object.test.ts`, - `./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/openapi/openapi-validation.test.ts`, - `./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-contract-json-object.test.ts`, + `./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/manual/geth-alchemy-integration-manual-check.test.ts`, `./extensions/cactus-plugin-htlc-coordinator-besu/src/test/typescript/integration/plugin-htlc-coordinator/counterparty-htlc-endpoint.test.ts`, `./extensions/cactus-plugin-htlc-coordinator-besu/src/test/typescript/integration/plugin-htlc-coordinator/own-htlc-endpoint.test.ts`, `./extensions/cactus-plugin-htlc-coordinator-besu/src/test/typescript/integration/plugin-htlc-coordinator/withdraw-counterparty-endpoint.test.ts`, diff --git a/packages/cactus-cmd-api-server/src/main/typescript/api-server.ts b/packages/cactus-cmd-api-server/src/main/typescript/api-server.ts index a7fdcc18060..6435e296e0d 100644 --- a/packages/cactus-cmd-api-server/src/main/typescript/api-server.ts +++ b/packages/cactus-cmd-api-server/src/main/typescript/api-server.ts @@ -446,6 +446,12 @@ export class ApiServer { return (pluginInstance as IPluginWebService).shutdown(); }); + if (this.wsApi) { + this.log.info(`Disconnecting SocketIO connections...`); + this.wsApi.disconnectSockets(true); + this.log.info(`SocketIO connections disconnect OK`); + } + this.log.info(`Stopping ${webServicesShutdown.length} WS plugin(s)...`); await Promise.all(webServicesShutdown); this.log.info(`Stopped ${webServicesShutdown.length} WS plugin(s) OK`); @@ -694,6 +700,8 @@ export class ApiServer { const corsMiddleware = this.createCorsMiddleware(allowedDomains); app.use(corsMiddleware); app.use(bodyParser.json({ limit: "50mb" })); + // Add custom replacer to handle bigint responses correctly + app.set("json replacer", this.stringifyBigIntReplacer); const authzFactoryOptions = { apiServerOptions, pluginRegistry, logLevel }; const authzFactory = new AuthorizerFactory(authzFactoryOptions); @@ -807,4 +815,18 @@ export class ApiServer { }; return cors(corsOptionsDelegate); } + + /** + * `JSON.stringify` replacer function to handle BigInt. + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#use_within_json + */ + private stringifyBigIntReplacer( + _key: string, + value: bigint | unknown, + ): string | unknown { + if (typeof value === "bigint") { + return value.toString(); + } + return value; + } } diff --git a/packages/cactus-plugin-ledger-connector-ethereum/README.md b/packages/cactus-plugin-ledger-connector-ethereum/README.md index 597251cf10f..861437994a4 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/README.md +++ b/packages/cactus-plugin-ledger-connector-ethereum/README.md @@ -1,19 +1,20 @@ # `@hyperledger/cactus-plugin-ledger-connector-ethereum` This plugin provides `Cactus` a way to interact with Ethereum networks. Using this we can perform: -* Deploy Smart-contracts through bytecode. -* Build and sign transactions using different keystores. -* Invoke smart-contract functions that we have deployed on the network. + +- Deploy Smart-contracts through bytecode. +- Build and sign transactions using different keystores. +- Invoke smart-contract functions that we have deployed on the network. ## Summary - - [Getting Started](#getting-started) - - [Usage](#usage) - - [Prometheus Exporter](#prometheus-exporter) - - [Runing the tests](#running-the-tests) - - [Contributing](#contributing) - - [License](#license) - - [Acknowledgments](#acknowledgments) +- [Getting Started](#getting-started) +- [Runing the tests](#running-the-tests) +- [Usage](#usage) +- [Prometheus Exporter](#prometheus-exporter) +- [Contributing](#contributing) +- [License](#license) +- [Acknowledgments](#acknowledgments) ## Getting Started @@ -23,6 +24,7 @@ your local machine for development and testing purposes. ### Prerequisites In the root of the project to install the dependencies execute the command: + ```sh npm run configure ``` @@ -30,13 +32,17 @@ npm run configure ## Usage To use this import public-api and create new **PluginLedgerConnectorEthereum**. + ```typescript - const connector: PluginLedgerConnectorEthereum = new PluginLedgerConnectorEthereum({ +const connector: PluginLedgerConnectorEthereum = new PluginLedgerConnectorEthereum( + { instanceId: uuidV4(), rpcApiHttpHost, pluginRegistry: new PluginRegistry(), - }); + }, +); ``` + You can make calls through the connector to the plugin API: ```typescript @@ -53,6 +59,7 @@ async invokeRawWeb3EthContract(req: InvokeRawWeb3EthContractV1Request): Promise< ``` Call example to deploy a contract: + ```typescript const deployOut = await connector.deployContract({ web3SigningCredential: { @@ -64,15 +71,18 @@ const deployOut = await connector.deployContract({ gas: 1000000, }); ``` + The field "type" can have the following values: + ```typescript enum Web3SigningCredentialType { - CACTUSKEYCHAINREF = 'CACTUS_KEYCHAIN_REF', - GETHKEYCHAINPASSWORD = 'GETH_KEYCHAIN_PASSWORD', - PRIVATEKEYHEX = 'PRIVATE_KEY_HEX', - NONE = 'NONE' + CACTUSKEYCHAINREF = "CACTUS_KEYCHAIN_REF", + GETHKEYCHAINPASSWORD = "GETH_KEYCHAIN_PASSWORD", + PRIVATEKEYHEX = "PRIVATE_KEY_HEX", + NONE = "NONE", } ``` + > Extensive documentation and examples in the [readthedocs](https://readthedocs.org/projects/hyperledger-cactus/) (WIP) ## EthereumApiClient @@ -80,7 +90,9 @@ enum Web3SigningCredentialType { All connector API endpoints are defined in [open-api specification](./src/main/json/openapi.json). You can use [EthereumApiClient](./src/main/typescript/api-client) to call remote ethereum connector functions. It also contain additional utility functions to ease integration. ### REST Functions + See [DefaultApi](./src/main/typescript/generated/openapi/typescript-axios/api.ts) for up-to-date listing of supported endpoints. + - deployContractSolBytecodeJsonObjectV1 - deployContractSolBytecodeV1 - getPrometheusMetricsV1 @@ -91,21 +103,27 @@ See [DefaultApi](./src/main/typescript/generated/openapi/typescript-axios/api.ts - runTransactionV1 ### Asynchronous Functions (socket.io) + - watchBlocksV1 ### Send Request Methods + Both methods are deprecated, async version returns immediately while sync respond with Promise of a call results. + - `sendAsyncRequest` - `sendSyncRequest` #### Supported Requests + - `web3Eth`: Calls `invokeRawWeb3EthMethodV1` -- `web3EthContract`: Calls `invokeRawWeb3EthContractV1` +- `web3EthContract`: Calls `invokeRawWeb3EthContractV1` #### Arguments + - The same for both async and sync methods. - Arguments interpretation depends on `method.type` (i.e. request type) -``` typescript + +```typescript // Contract definition for web3EthContract request, ignored otherwise contract: { abi?: AbiItem[], @@ -130,15 +148,11 @@ args: { ## Running the tests -To check that all has been installed correctly and that the pugin has no errors, run both `tap` and `jest` test suites: +To check that all has been installed correctly and that the pugin has no errors run jest test suites. -* Run this command at the project's root: +- Run this command at the project's root: ```sh -# Tap -npx tap --ts --jobs=1 --timeout=60 ./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/openapi/openapi-validation-no-keychain.test.ts ./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/openapi/openapi-validation.test.ts ./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-deploy-contract-from-json-json-object-endpoints.test.ts ./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-deploy-contract-from-json-json-object.test.ts ./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-invoke-contract-json-object-endpoints.test.ts ./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-invoke-contract-json-object.test.ts ./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-deploy-contract-from-json-json-object-endpoints.test.ts ./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-deploy-contract-from-json-json-object.test.ts ./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-contract-json-object-endpoints.test.ts ./packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-contract-json-object.test.ts - -# Jest npx jest cactus-plugin-ledger-connector-ethereum ``` @@ -151,6 +165,7 @@ DOCKER_BUILDKIT=1 docker build -f ./packages/cactus-plugin-ledger-connector-ethe ``` Build with a specific version of the npm package: + ```sh DOCKER_BUILDKIT=1 docker build --build-arg NPM_PKG_VERSION=0.4.1 -f ./packages/cactus-plugin-ledger-connector-ethereum/Dockerfile . -t cplcb ``` @@ -158,6 +173,7 @@ DOCKER_BUILDKIT=1 docker build --build-arg NPM_PKG_VERSION=0.4.1 -f ./packages/c #### Running the container Launch container with plugin configuration as an **environment variable**: + ```sh docker run \ --rm \ @@ -170,6 +186,7 @@ docker run \ ``` Launch container with plugin configuration as a **CLI argument**: + ```sh docker run \ --rm \ @@ -183,6 +200,7 @@ docker run \ ``` Launch container with **configuration file** mounted from host machine: + ```sh echo '{"authorizationProtocol":"NONE","authorizationConfigJson":{},"plugins":[{"packageName":"@hyperledger/cactus-plugin-ledger-connector-ethereum","type":"org.hyperledger.cactus.plugin_import_type.LOCAL","action":"org.hyperledger.cactus.plugin_import_action.INSTALL","options":{"rpcApiHttpHost":"http://localhost:8545","instanceId":"some-unique-ethereum-connector-instance-id"}}]}' > cactus.json @@ -202,11 +220,13 @@ docker run \ Don't have a ethereum network on hand to test with? Test or develop against our ethereum All-In-One container! **Terminal Window 1 (Ledger)** + ```sh docker run -p 0.0.0.0:8545:8545/tcp -p 0.0.0.0:8546:8546/tcp -p 0.0.0.0:8888:8888/tcp -p 0.0.0.0:9001:9001/tcp -p 0.0.0.0:9545:9545/tcp hyperledger/cactus-quorum-all-in-one:latest ``` **Terminal Window 2 (Cactus API Server)** + ```sh docker run \ --network host \ @@ -218,6 +238,7 @@ docker run \ ``` **Terminal Window 3 (curl - replace eth accounts as needed)** + ```sh curl --location --request POST 'http://127.0.0.1:4000/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/run-transaction' \ --header 'Content-Type: application/json' \ @@ -244,23 +265,23 @@ The above should produce a response that looks similar to this: ```json { - "success": true, - "data": { - "transactionReceipt": { - "blockHash": "0x7c97c038a5d3bd84613fe23ed442695276d5d2df97f4e7c4f10ca06765033ffd", - "blockNumber": 1218, - "contractAddress": null, - "cumulativeGasUsed": 21000, - "from": "0x627306090abab3a6e1400e9345bc60c78a8bef57", - "gasUsed": 21000, - "logs": [], - "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "status": true, - "to": "0xf17f52151ebef6c7334fad080c5704d77216b732", - "transactionHash": "0xc7fcb46c735bdc696d500bfc70c72595a2b8c31813929e5c61d9a5aec3376d6f", - "transactionIndex": 0 - } + "success": true, + "data": { + "transactionReceipt": { + "blockHash": "0x7c97c038a5d3bd84613fe23ed442695276d5d2df97f4e7c4f10ca06765033ffd", + "blockNumber": 1218, + "contractAddress": null, + "cumulativeGasUsed": 21000, + "from": "0x627306090abab3a6e1400e9345bc60c78a8bef57", + "gasUsed": 21000, + "logs": [], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "status": true, + "to": "0xf17f52151ebef6c7334fad080c5704d77216b732", + "transactionHash": "0xc7fcb46c735bdc696d500bfc70c72595a2b8c31813929e5c61d9a5aec3376d6f", + "transactionIndex": 0 } + } } ``` @@ -269,12 +290,14 @@ The above should produce a response that looks similar to this: This class creates a prometheus exporter, which scrapes the transactions (total transaction count) for the use cases incorporating the use of Ethereum connector plugin. ### Prometheus Exporter Usage + The prometheus exporter object is initialized in the `PluginLedgerConnectorEthereum` class constructor itself, so instantiating the object of the `PluginLedgerConnectorEthereum` class, gives access to the exporter object. You can also initialize the prometheus exporter object seperately and then pass it to the `IPluginLedgerConnectorEthereumOptions` interface for `PluginLedgerConnectoEthereum` constructor. `getPrometheusMetricsV1` function returns the prometheus exporter metrics, currently displaying the total transaction count, which currently increments everytime the `transact()` method of the `PluginLedgerConnectorEthereum` class is called. ### Prometheus Integration + To use Prometheus with this exporter make sure to install [Prometheus main component](https://prometheus.io/download/). Once Prometheus is setup, the corresponding scrape_config needs to be added to the prometheus.yml @@ -292,25 +315,26 @@ Here the `host:port` is where the prometheus exporter metrics are exposed. The t Once edited, you can start the prometheus service by referencing the above edited prometheus.yml file. On the prometheus graphical interface (defaulted to http://localhost:9090), choose **Graph** from the menu bar, then select the **Console** tab. From the **Insert metric at cursor** drop down, select **cactus_ethereum_total_tx_count** and click **execute** -### Helper code - -###### response.type.ts -This file contains the various responses of the metrics. - -###### data-fetcher.ts -This file contains functions encasing the logic to process the data points - -###### metrics.ts -This file lists all the prometheus metrics and what they are used for. - -## Running the tests - -To check that all has been installed correctly and that the pugin has no errors, there are two options to run the tests: - -* Run this command at the project's root: -```sh -npm run test:plugin-ledger-connector-ethereum -``` +### Manual Alchemy integration test + +There's a simple script for checking integration with [alchemy platform](https://www.alchemy.com/) in `./src/test/typescript/manual/geth-alchemy-integration-manual-check.test.ts`. To run it follow these steps: + +- Sign up on Alchemy platform. +- Prepare your wallet address and private key. +- Use free Sepolia faucet to get some test ether: https://sepoliafaucet.com/ + - note: script assumes Sepolia testnet but it should work with any other testnets from alchemy, you just need to adjust the script accordingly. +- `Create App` on Alchemy dashboard. + - Use any name and description. + - Select `Chain: Ethereum` + - Select `Network: Ethereum Sepolia` +- Click `View Key` (on the dashboard) next to the newly created App. +- Copy HTTPS RPC endpoint to `ALCHEMY_ENDPOINT` variable near top of `geth-invoke-web3-contract-v1.test.ts` file (or just replace **\_\_**API_KEY**\_\_** with your API key). + - note: if you misspell it you'll get authentication errors. +- Copy your account address to `ETH_ADDRESS` variable. +- Copy your private key to `ETH_PRIVATE_KEY` variable. +- **Build the project, or at least this package (`npx tsc`). Remember to run the build after each change in script - it will not happen automatically!** +- Execute inside this package directory: + - `npx jest dist/lib/test/typescript/manual/geth-alchemy-integration-manual-check.test.js` ## Contributing @@ -323,4 +347,7 @@ Please review [CONTIRBUTING.md](../../CONTRIBUTING.md) to get started. This distribution is published under the Apache License Version 2.0 found in the [LICENSE](../../LICENSE) file. ## Acknowledgments -``` \ No newline at end of file + +``` + +``` diff --git a/packages/cactus-plugin-ledger-connector-ethereum/package.json b/packages/cactus-plugin-ledger-connector-ethereum/package.json index bc0bf7c9095..929de67114f 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/package.json +++ b/packages/cactus-plugin-ledger-connector-ethereum/package.json @@ -74,19 +74,21 @@ "sanitize-html": "2.7.0", "socket.io-client": "4.5.4", "typescript-optional": "2.0.1", - "web3": "1.10.0", - "web3-eth-contract": "1.10.0", - "web3-utils": "1.10.0" + "web3": "4.0.3", + "web3-eth-contract": "4.0.3", + "web3-utils": "4.0.3" }, "devDependencies": { "@hyperledger/cactus-plugin-keychain-memory": "2.0.0-alpha.1", + "@hyperledger/cactus-test-geth-ledger": "2.0.0-alpha.1", "@hyperledger/cactus-test-tooling": "2.0.0-alpha.1", "@types/express": "4.17.13", "@types/minimist": "1.2.2", "@types/sanitize-html": "2.6.2", "chalk": "4.1.2", "socket.io": "4.5.4", - "web3-eth": "1.10.0" + "web3-eth": "4.0.3", + "web3-eth-accounts": "4.0.3" }, "engines": { "node": ">=10", diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/main/json/openapi.json b/packages/cactus-plugin-ledger-connector-ethereum/src/main/json/openapi.json index 303f77636cf..2d5f0e80a40 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/main/json/openapi.json +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/main/json/openapi.json @@ -3,7 +3,7 @@ "info": { "title": "Hyperledger Cactus Plugin - Connector Ethereum", "description": "Can perform basic tasks on a Ethereum ledger", - "version": "0.0.1", + "version": "v2.0.0-alpha.1", "license": { "name": "Apache-2.0", "url": "https://www.apache.org/licenses/LICENSE-2.0.html" @@ -217,61 +217,25 @@ "nullable": false }, "from": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "number" - } - ] + "type": "string" }, "to": { - "oneOf": [ - { - "type": "string" - } - ] + "type": "string" }, "value": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "number" - } - ] + "type": "string" }, "gas": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "number" - } - ] + "type": "string" }, "gasPrice": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "number" - } - ] + "type": "string" }, "nonce": { - "type": "number" + "type": "string" }, "data": { - "oneOf": [ - { - "type": "string" - } - ] + "type": "string" } } }, @@ -300,7 +264,7 @@ "pattern": "^0x([A-Fa-f0-9]{64})$" }, "transactionIndex": { - "type": "number", + "type": "string", "nullable": false }, "blockHash": { @@ -310,11 +274,15 @@ "pattern": "^0x([A-Fa-f0-9]{64})$" }, "blockNumber": { - "type": "number", + "type": "string", "nullable": false }, "gasUsed": { - "type": "number", + "type": "string", + "nullable": false + }, + "effectiveGasPrice": { + "type": "string", "nullable": false }, "contractAddress": { @@ -351,8 +319,12 @@ "type": "string", "nullable": false }, - "cumulativeGasUSed": { - "type": "number", + "cumulativeGasUsed": { + "type": "string", + "nullable": false + }, + "type": { + "type": "string", "nullable": false } } @@ -600,37 +572,16 @@ "items": {} }, "value": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "number" - } - ] + "type": "string" }, "gas": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "number" - } - ] + "type": "string" }, "gasPrice": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "number" - } - ] + "type": "string" }, "nonce": { - "type": "number" + "type": "string" }, "timeoutMs": { "type": "number", @@ -687,37 +638,16 @@ "nullable": false }, "value": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "number" - } - ] + "type": "string" }, "gas": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "number" - } - ] + "type": "string" }, "gasPrice": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "number" - } - ] + "type": "string" }, "nonce": { - "type": "number" + "type": "string" }, "timeoutMs": { "type": "number", @@ -793,9 +723,7 @@ "additionalProperties": false, "properties": { "abi": { - "description": "The application binary interface of the solidity contract", - "type": "array", - "items": {} + "description": "The application binary interface of the solidity contract" }, "address": { "description": "Deployed solidity contract address", @@ -872,46 +800,24 @@ "Web3BlockHeader": { "type": "object", "required": [ - "number", - "hash", - "parentHash", - "nonce", "sha3Uncles", - "logsBloom", "transactionRoot", - "stateRoot", - "receiptRoot", - "miner", - "extraData", "gasLimit", - "gasUsed", - "timestamp" + "gasUsed" ], "properties": { "number": { - "type": "number" - }, - "hash": { "type": "string" }, "parentHash": { "type": "string" }, - "nonce": { - "type": "string" - }, "sha3Uncles": { "type": "string" }, - "logsBloom": { - "type": "string" - }, "transactionsRoot": { "type": "string" }, - "stateRoot": { - "type": "string" - }, "receiptsRoot": { "type": "string" }, @@ -924,24 +830,26 @@ "miner": { "type": "string" }, - "extraData": { - "type": "string" - }, "gasLimit": { - "type": "integer" + "type": "string" }, "gasUsed": { - "type": "integer" + "type": "string" + }, + "stateRoot": { + "type": "string" + }, + "logsBloom": { + "type": "string" + }, + "extraData": { + "type": "string" + }, + "nonce": { + "type": "string" }, "timestamp": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "number" - } - ] + "type": "string" } } }, @@ -958,33 +866,31 @@ "value", "gasPrice", "gas", - "input" + "input", + "type", + "chainId" ], "properties": { "hash": { "type": "string" }, "nonce": { - "type": "number" + "type": "string" }, "blockHash": { - "type": "string", - "nullable": true + "type": "string" }, "blockNumber": { - "type": "number", - "nullable": true + "type": "string" }, "transactionIndex": { - "type": "number", - "nullable": true + "type": "string" }, "from": { "type": "string" }, "to": { - "type": "string", - "nullable": true + "type": "string" }, "value": { "type": "string" @@ -993,11 +899,17 @@ "type": "string" }, "gas": { - "type": "number" + "type": "string" }, "input": { "type": "string" }, + "type": { + "type": "string" + }, + "chainId": { + "type": "string" + }, "v": { "type": "string" }, @@ -1013,11 +925,9 @@ "type": "object", "required": [ "number", - "hash", "parentHash", "nonce", "sha3Uncles", - "logsBloom", "transactionRoot", "stateRoot", "receiptRoot", @@ -1033,7 +943,7 @@ ], "properties": { "number": { - "type": "number" + "type": "string" }, "hash": { "type": "string" @@ -1072,10 +982,10 @@ "type": "string" }, "gasLimit": { - "type": "integer" + "type": "string" }, "gasUsed": { - "type": "integer" + "type": "string" }, "timestamp": { "oneOf": [ @@ -1088,7 +998,7 @@ ] }, "size": { - "type": "number" + "type": "string" }, "totalDifficulty": { "type": "string" @@ -1099,6 +1009,9 @@ "type": "string" } }, + "baseFeePerGas": { + "type": "string" + }, "transactions": { "type": "array", "items": { diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/api-client/ethereum-api-client.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/api-client/ethereum-api-client.ts index 18636a46398..6d58572d1f4 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/api-client/ethereum-api-client.ts +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/api-client/ethereum-api-client.ts @@ -13,7 +13,7 @@ import { WatchBlocksV1Progress, } from "../generated/openapi/typescript-axios"; import { Configuration } from "../generated/openapi/typescript-axios/configuration"; -import { AbiItem } from "web3-utils"; +import { ContractAbi } from "web3"; export class EthereumApiClientOptions extends Configuration { readonly logLevel?: LogLevelDesc; @@ -37,7 +37,7 @@ export type EthereumRequestInputWeb3EthContractMethod = { // Common input types for sending requests export type EthereumRequestInputContract = { - abi?: AbiItem[]; + abi?: ContractAbi; address?: string; }; export type EthereumRequestInputMethod = @@ -49,7 +49,8 @@ export type EthereumRequestInputArgs = { export class EthereumApiClient extends DefaultApi - implements ISocketApiClient { + implements ISocketApiClient +{ public static readonly CLASS_NAME = "EthereumApiClient"; private readonly log: Logger; @@ -109,7 +110,7 @@ export class EthereumApiClient finalize(() => { this.log.info("FINALIZE - unsubscribing from the stream..."); socket.emit(WatchBlocksV1.Unsubscribe); - socket.disconnect(); + socket.close(); }), ); } @@ -198,7 +199,7 @@ export class EthereumApiClient // Prepare input const invokeArgs: InvokeRawWeb3EthContractV1Request = { - abi: contract.abi as AbiItem[], + abi: contract.abi, address: contract.address as string, invocationType: method.command, invocationParams: args, diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/api.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/api.ts index bb7a0d6c16e..29203b0e361 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/api.ts +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/api.ts @@ -4,7 +4,7 @@ * Hyperledger Cactus Plugin - Connector Ethereum * Can perform basic tasks on a Ethereum ledger * - * The version of the OpenAPI document: 0.0.1 + * The version of the OpenAPI document: v2.0.0-alpha.1 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -291,59 +291,47 @@ export interface EthereumTransactionConfig { 'rawTransaction'?: string; /** * - * @type {EthereumTransactionConfigFrom} + * @type {string} * @memberof EthereumTransactionConfig */ - 'from'?: EthereumTransactionConfigFrom; + 'from'?: string; /** * - * @type {EthereumTransactionConfigTo} + * @type {string} * @memberof EthereumTransactionConfig */ - 'to'?: EthereumTransactionConfigTo; + 'to'?: string; /** * - * @type {EthereumTransactionConfigFrom} + * @type {string} * @memberof EthereumTransactionConfig */ - 'value'?: EthereumTransactionConfigFrom; + 'value'?: string; /** * - * @type {EthereumTransactionConfigFrom} + * @type {string} * @memberof EthereumTransactionConfig */ - 'gas'?: EthereumTransactionConfigFrom; + 'gas'?: string; /** * - * @type {EthereumTransactionConfigFrom} + * @type {string} * @memberof EthereumTransactionConfig */ - 'gasPrice'?: EthereumTransactionConfigFrom; + 'gasPrice'?: string; /** * - * @type {number} + * @type {string} * @memberof EthereumTransactionConfig */ - 'nonce'?: number; + 'nonce'?: string; /** * - * @type {EthereumTransactionConfigTo} + * @type {string} * @memberof EthereumTransactionConfig */ - 'data'?: EthereumTransactionConfigTo; + 'data'?: string; } -/** - * @type EthereumTransactionConfigFrom - * @export - */ -export type EthereumTransactionConfigFrom = number | string; - -/** - * @type EthereumTransactionConfigTo - * @export - */ -export type EthereumTransactionConfigTo = string; - /** * * @export @@ -382,28 +370,28 @@ export interface InvokeContractJsonObjectV1Request { 'contractAddress': string; /** * - * @type {EthereumTransactionConfigFrom} + * @type {string} * @memberof InvokeContractJsonObjectV1Request */ - 'value'?: EthereumTransactionConfigFrom; + 'value'?: string; /** * - * @type {EthereumTransactionConfigFrom} + * @type {string} * @memberof InvokeContractJsonObjectV1Request */ - 'gas'?: EthereumTransactionConfigFrom; + 'gas'?: string; /** * - * @type {EthereumTransactionConfigFrom} + * @type {string} * @memberof InvokeContractJsonObjectV1Request */ - 'gasPrice'?: EthereumTransactionConfigFrom; + 'gasPrice'?: string; /** * - * @type {number} + * @type {string} * @memberof InvokeContractJsonObjectV1Request */ - 'nonce'?: number; + 'nonce'?: string; /** * The amount of milliseconds to wait for a transaction receipt beforegiving up and crashing. Only has any effect if the invocation type is SEND * @type {number} @@ -457,28 +445,28 @@ export interface InvokeContractV1Request { 'params': Array; /** * - * @type {EthereumTransactionConfigFrom} + * @type {string} * @memberof InvokeContractV1Request */ - 'value'?: EthereumTransactionConfigFrom; + 'value'?: string; /** * - * @type {EthereumTransactionConfigFrom} + * @type {string} * @memberof InvokeContractV1Request */ - 'gas'?: EthereumTransactionConfigFrom; + 'gas'?: string; /** * - * @type {EthereumTransactionConfigFrom} + * @type {string} * @memberof InvokeContractV1Request */ - 'gasPrice'?: EthereumTransactionConfigFrom; + 'gasPrice'?: string; /** * - * @type {number} + * @type {string} * @memberof InvokeContractV1Request */ - 'nonce'?: number; + 'nonce'?: string; /** * The amount of milliseconds to wait for a transaction receipt beforegiving up and crashing. Only has any effect if the invocation type is SEND * @type {number} @@ -527,10 +515,10 @@ export interface InvokeContractV1Response { export interface InvokeRawWeb3EthContractV1Request { /** * The application binary interface of the solidity contract - * @type {Array} + * @type {any} * @memberof InvokeRawWeb3EthContractV1Request */ - 'abi': Array; + 'abi': any; /** * Deployed solidity contract address * @type {string} @@ -828,16 +816,16 @@ export type WatchBlocksV1 = typeof WatchBlocksV1[keyof typeof WatchBlocksV1]; export interface WatchBlocksV1BlockData { /** * - * @type {number} + * @type {string} * @memberof WatchBlocksV1BlockData */ - 'number': number; + 'number': string; /** * * @type {string} * @memberof WatchBlocksV1BlockData */ - 'hash': string; + 'hash'?: string; /** * * @type {string} @@ -861,7 +849,7 @@ export interface WatchBlocksV1BlockData { * @type {string} * @memberof WatchBlocksV1BlockData */ - 'logsBloom': string; + 'logsBloom'?: string; /** * * @type {string} @@ -906,28 +894,28 @@ export interface WatchBlocksV1BlockData { 'extraData': string; /** * - * @type {number} + * @type {string} * @memberof WatchBlocksV1BlockData */ - 'gasLimit': number; + 'gasLimit': string; /** * - * @type {number} + * @type {string} * @memberof WatchBlocksV1BlockData */ - 'gasUsed': number; + 'gasUsed': string; /** * - * @type {EthereumTransactionConfigFrom} + * @type {WatchBlocksV1BlockDataTimestamp} * @memberof WatchBlocksV1BlockData */ - 'timestamp': EthereumTransactionConfigFrom; + 'timestamp': WatchBlocksV1BlockDataTimestamp; /** * - * @type {number} + * @type {string} * @memberof WatchBlocksV1BlockData */ - 'size': number; + 'size': string; /** * * @type {string} @@ -940,6 +928,12 @@ export interface WatchBlocksV1BlockData { * @memberof WatchBlocksV1BlockData */ 'uncles': Array; + /** + * + * @type {string} + * @memberof WatchBlocksV1BlockData + */ + 'baseFeePerGas'?: string; /** * * @type {Array} @@ -947,6 +941,12 @@ export interface WatchBlocksV1BlockData { */ 'transactions': Array; } +/** + * @type WatchBlocksV1BlockDataTimestamp + * @export + */ +export type WatchBlocksV1BlockDataTimestamp = number | string; + /** * * @export @@ -985,102 +985,96 @@ export interface WatchBlocksV1Progress { * @interface Web3BlockHeader */ export interface Web3BlockHeader { - /** - * - * @type {number} - * @memberof Web3BlockHeader - */ - 'number': number; /** * * @type {string} * @memberof Web3BlockHeader */ - 'hash': string; + 'number'?: string; /** * * @type {string} * @memberof Web3BlockHeader */ - 'parentHash': string; + 'parentHash'?: string; /** * * @type {string} * @memberof Web3BlockHeader */ - 'nonce': string; + 'sha3Uncles': string; /** * * @type {string} * @memberof Web3BlockHeader */ - 'sha3Uncles': string; + 'transactionsRoot'?: string; /** * * @type {string} * @memberof Web3BlockHeader */ - 'logsBloom': string; + 'receiptsRoot'?: string; /** * * @type {string} * @memberof Web3BlockHeader */ - 'transactionsRoot'?: string; + 'difficulty'?: string; /** * * @type {string} * @memberof Web3BlockHeader */ - 'stateRoot': string; + 'mixHash'?: string; /** * * @type {string} * @memberof Web3BlockHeader */ - 'receiptsRoot'?: string; + 'miner'?: string; /** * * @type {string} * @memberof Web3BlockHeader */ - 'difficulty'?: string; + 'gasLimit': string; /** * * @type {string} * @memberof Web3BlockHeader */ - 'mixHash'?: string; + 'gasUsed': string; /** * * @type {string} * @memberof Web3BlockHeader */ - 'miner': string; + 'stateRoot'?: string; /** * * @type {string} * @memberof Web3BlockHeader */ - 'extraData': string; + 'logsBloom'?: string; /** * - * @type {number} + * @type {string} * @memberof Web3BlockHeader */ - 'gasLimit': number; + 'extraData'?: string; /** * - * @type {number} + * @type {string} * @memberof Web3BlockHeader */ - 'gasUsed': number; + 'nonce'?: string; /** * - * @type {EthereumTransactionConfigFrom} + * @type {string} * @memberof Web3BlockHeader */ - 'timestamp': EthereumTransactionConfigFrom; + 'timestamp'?: string; } /** * @type Web3SigningCredential @@ -1220,28 +1214,28 @@ export interface Web3Transaction { 'hash': string; /** * - * @type {number} + * @type {string} * @memberof Web3Transaction */ - 'nonce': number; + 'nonce': string; /** * * @type {string} * @memberof Web3Transaction */ - 'blockHash': string | null; + 'blockHash': string; /** * - * @type {number} + * @type {string} * @memberof Web3Transaction */ - 'blockNumber': number | null; + 'blockNumber': string; /** * - * @type {number} + * @type {string} * @memberof Web3Transaction */ - 'transactionIndex': number | null; + 'transactionIndex': string; /** * * @type {string} @@ -1253,7 +1247,7 @@ export interface Web3Transaction { * @type {string} * @memberof Web3Transaction */ - 'to': string | null; + 'to': string; /** * * @type {string} @@ -1268,16 +1262,28 @@ export interface Web3Transaction { 'gasPrice': string; /** * - * @type {number} + * @type {string} * @memberof Web3Transaction */ - 'gas': number; + 'gas': string; /** * * @type {string} * @memberof Web3Transaction */ 'input': string; + /** + * + * @type {string} + * @memberof Web3Transaction + */ + 'type': string; + /** + * + * @type {string} + * @memberof Web3Transaction + */ + 'chainId': string; /** * * @type {string} @@ -1319,10 +1325,10 @@ export interface Web3TransactionReceipt { 'transactionHash': string; /** * - * @type {number} + * @type {string} * @memberof Web3TransactionReceipt */ - 'transactionIndex': number; + 'transactionIndex': string; /** * * @type {string} @@ -1331,16 +1337,22 @@ export interface Web3TransactionReceipt { 'blockHash': string; /** * - * @type {number} + * @type {string} * @memberof Web3TransactionReceipt */ - 'blockNumber': number; + 'blockNumber': string; /** * - * @type {number} + * @type {string} + * @memberof Web3TransactionReceipt + */ + 'gasUsed': string; + /** + * + * @type {string} * @memberof Web3TransactionReceipt */ - 'gasUsed': number; + 'effectiveGasPrice'?: string; /** * * @type {string} @@ -1391,10 +1403,16 @@ export interface Web3TransactionReceipt { 'commitmentHash'?: string; /** * - * @type {number} + * @type {string} + * @memberof Web3TransactionReceipt + */ + 'cumulativeGasUsed'?: string; + /** + * + * @type {string} * @memberof Web3TransactionReceipt */ - 'cumulativeGasUSed'?: number; + 'type'?: string; } /** diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/base.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/base.ts index 542aebb1fb0..90d9122acd6 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/base.ts +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/base.ts @@ -4,7 +4,7 @@ * Hyperledger Cactus Plugin - Connector Ethereum * Can perform basic tasks on a Ethereum ledger * - * The version of the OpenAPI document: 0.0.1 + * The version of the OpenAPI document: v2.0.0-alpha.1 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/common.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/common.ts index ba7055be44d..3d261b26b7c 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/common.ts +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/common.ts @@ -4,7 +4,7 @@ * Hyperledger Cactus Plugin - Connector Ethereum * Can perform basic tasks on a Ethereum ledger * - * The version of the OpenAPI document: 0.0.1 + * The version of the OpenAPI document: v2.0.0-alpha.1 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/configuration.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/configuration.ts index 667f2c7cb6e..76d3db5b830 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/configuration.ts +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/configuration.ts @@ -4,7 +4,7 @@ * Hyperledger Cactus Plugin - Connector Ethereum * Can perform basic tasks on a Ethereum ledger * - * The version of the OpenAPI document: 0.0.1 + * The version of the OpenAPI document: v2.0.0-alpha.1 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/index.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/index.ts index 7b2d607998a..00c54cf92aa 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/index.ts +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/index.ts @@ -4,7 +4,7 @@ * Hyperledger Cactus Plugin - Connector Ethereum * Can perform basic tasks on a Ethereum ledger * - * The version of the OpenAPI document: 0.0.1 + * The version of the OpenAPI document: v2.0.0-alpha.1 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/plugin-ledger-connector-ethereum.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/plugin-ledger-connector-ethereum.ts index 7feac3680ac..bc270cb2986 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/plugin-ledger-connector-ethereum.ts +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/plugin-ledger-connector-ethereum.ts @@ -4,11 +4,14 @@ import type { } from "socket.io"; import { Express } from "express"; -import Web3 from "web3"; -import { AbiItem } from "web3-utils"; -import { Contract } from "web3-eth-contract"; -import { ContractSendMethod } from "web3-eth-contract"; -import { TransactionReceipt } from "web3-eth"; +import Web3, { + ContractAbi, + HttpProvider, + TransactionReceiptBase, + WebSocketProvider, +} from "web3"; +import { NewHeadsSubscription } from "web3-eth"; +import { Contract, PayableMethodObject } from "web3-eth-contract"; import OAS from "../json/openapi.json"; @@ -65,9 +68,17 @@ import { GetPrometheusExporterMetricsEndpointV1 } from "./web-services/get-prome import { InvokeRawWeb3EthMethodEndpoint } from "./web-services/invoke-raw-web3eth-method-v1-endpoint"; import { InvokeRawWeb3EthContractEndpoint } from "./web-services/invoke-raw-web3eth-contract-v1-endpoint"; -import { isWeb3SigningCredentialNone } from "./model-type-guards"; +import { isWeb3SigningCredentialNone } from "./types/model-type-guards"; import { PrometheusExporter } from "./prometheus-exporter/prometheus-exporter"; import { RuntimeError } from "run-time-error"; +import { + Web3StringReturnFormat, + convertWeb3ReceiptStatusToBool, +} from "./types/util-types"; + +// Used when waiting for WS requests to be send correctly before disconnecting +const waitForWsProviderRequestsTimeout = 5 * 1000; // 5s +const waitForWsProviderRequestsStep = 500; // 500ms export interface IPluginLedgerConnectorEthereumOptions extends ICactusPluginOptions { @@ -87,7 +98,8 @@ export class PluginLedgerConnectorEthereum RunTransactionResponse >, ICactusPlugin, - IPluginWebService { + IPluginWebService +{ private readonly pluginRegistry: PluginRegistry; public prometheusExporter: PrometheusExporter; private readonly instanceId: string; @@ -95,6 +107,8 @@ export class PluginLedgerConnectorEthereum private readonly web3: Web3; private endpoints: IWebServiceEndpoint[] | undefined; public static readonly CLASS_NAME = "PluginLedgerConnectorEthereum"; + private watchBlocksSubscriptions: Map = + new Map(); public get className(): string { return PluginLedgerConnectorEthereum.CLASS_NAME; @@ -102,9 +116,9 @@ export class PluginLedgerConnectorEthereum private getWeb3Provider() { if (!this.options.rpcApiWsHost) { - return new Web3.providers.HttpProvider(this.options.rpcApiHttpHost); + return new HttpProvider(this.options.rpcApiHttpHost); } - return new Web3.providers.WebsocketProvider(this.options.rpcApiWsHost); + return new WebSocketProvider(this.options.rpcApiWsHost); } constructor(public readonly options: IPluginLedgerConnectorEthereumOptions) { @@ -150,13 +164,59 @@ export class PluginLedgerConnectorEthereum return this.instanceId; } + private async removeWatchBlocksSubscriptionForSocket(socketId: string) { + try { + const subscription = this.watchBlocksSubscriptions.get(socketId); + if (subscription) { + await subscription.unsubscribe(); + this.watchBlocksSubscriptions.delete(socketId); + this.log.info(`${socketId} ${WatchBlocksV1.Unsubscribe} OK`); + } + } catch (error) { + this.log.debug( + `${socketId} ${WatchBlocksV1.Unsubscribe} Failed (possibly already closed)`, + ); + } + } + public async shutdown(): Promise { this.log.info(`Shutting down ${this.className}...`); - const provider = this.web3.currentProvider; - if (provider && typeof provider == "object") { - if ("disconnect" in provider) { - provider.disconnect(1000, "shutdown"); + + this.log.debug("Remove any remaining web3js subscriptions"); + for (const socketId of this.watchBlocksSubscriptions.keys()) { + this.log.debug(`${WatchBlocksV1.Unsubscribe} shutdown`); + await this.removeWatchBlocksSubscriptionForSocket(socketId); + } + + try { + const wsProvider = this.web3.currentProvider as WebSocketProvider; + if (!wsProvider || !typeof wsProvider.SocketConnection === undefined) { + this.log.debug("Non-WS provider found - finish"); + return; } + + // Wait for WS requests to finish + const looseWsProvider = wsProvider as any; // Used to access protected fields of WS provider + let waitForRequestRemainingSteps = + waitForWsProviderRequestsTimeout / waitForWsProviderRequestsStep; + while ( + waitForRequestRemainingSteps > 0 && + (looseWsProvider._pendingRequestsQueue.size > 0 || + looseWsProvider._sentRequestsQueue.size > 0) + ) { + this.log.debug( + `Waiting for pending and sent requests to finish on web3js WS provider (${waitForWsProviderRequestsStep})...`, + ); + await new Promise((resolve) => + setTimeout(resolve, waitForWsProviderRequestsStep), + ); + waitForRequestRemainingSteps--; + } + + // Disconnect the socket provider + wsProvider.disconnect(); + } catch (error) { + this.log.error("Error when disconnecting web3js WS provider!", error); } } @@ -176,14 +236,29 @@ export class PluginLedgerConnectorEthereum wsApi.on("connection", (socket: SocketIoSocket) => { this.log.debug(`New Socket connected. ID=${socket.id}`); - socket.on(WatchBlocksV1.Subscribe, (options?: WatchBlocksV1Options) => { - new WatchBlocksV1Endpoint({ - web3, - socket, - logLevel, - options, - }).subscribe(); - }); + socket.on( + WatchBlocksV1.Subscribe, + async (options?: WatchBlocksV1Options) => { + const watchBlocksEndpoint = new WatchBlocksV1Endpoint({ + web3, + socket, + logLevel, + options, + }); + this.watchBlocksSubscriptions.set( + socket.id, + await watchBlocksEndpoint.subscribe(), + ); + + socket.on("disconnect", async (reason: string) => { + this.log.debug( + `${WatchBlocksV1.Unsubscribe} disconnect reason=%o`, + reason, + ); + await this.removeWatchBlocksSubscriptionForSocket(socket.id); + }); + }, + ); }); return webServices; @@ -258,14 +333,13 @@ export class PluginLedgerConnectorEthereum return `@hyperledger/cactus-plugin-ledger-connector-ethereum`; } - public async getConsensusAlgorithmFamily(): Promise< - ConsensusAlgorithmFamily - > { + public async getConsensusAlgorithmFamily(): Promise { return ConsensusAlgorithmFamily.Stake; } public async hasTransactionFinality(): Promise { - const currentConsensusAlgorithmFamily = await this.getConsensusAlgorithmFamily(); + const currentConsensusAlgorithmFamily = + await this.getConsensusAlgorithmFamily(); return consensusHasTransactionFinality(currentConsensusAlgorithmFamily); } @@ -351,7 +425,7 @@ export class PluginLedgerConnectorEthereum const contractJSON = JSON.parse(contractStr); // if not exists a contract deployed, we deploy it - const networkId = await this.web3.eth.net.getId(); + const networkId = (await this.web3.eth.net.getId()).toString(); if ( !contractJSON.networks || !contractJSON.networks[networkId] || @@ -392,6 +466,26 @@ export class PluginLedgerConnectorEthereum return this.invokeContract(req); } + /** + * Simple function for estimating `maxFeePerGas` to sent with transaction. + * @warn It's not optimized for either speed or cost, consider using more complex solution on production! + * @param priorityFee what priority tip you plan to include. + * @returns estimated `maxFeePerGas` value. + */ + public async estimateMaxFeePerGas( + priorityFee: number | string = 0, + ): Promise { + const pendingBlock = await this.web3.eth.getBlock("pending"); + const baseFee = pendingBlock.baseFeePerGas; + if (!baseFee) { + throw new Error( + "Can't estimate maxFeePerGas - could not get recent baseFeePerGas", + ); + } + const estimate = BigInt(2) * baseFee + BigInt(priorityFee); + return estimate.toString(); + } + public async invokeContract( req: InvokeContractJsonObjectV1Request, ): Promise { @@ -410,13 +504,10 @@ export class PluginLedgerConnectorEthereum throw new Error(`${fnTag} Contract ABI is necessary`); } - const contractInstance: InstanceType = new this.web3.eth.Contract( - abi, - contractAddress, - ); + const contractInstance = new this.web3.eth.Contract(abi, contractAddress); const isSafeToCall = await this.isSafeToCallContractMethod( - contractInstance, + contractInstance as unknown as Contract, req.methodName, ); if (!isSafeToCall) { @@ -425,13 +516,15 @@ export class PluginLedgerConnectorEthereum ); } - const methodRef = contractInstance.methods[req.methodName]; + const methodRef = contractInstance.methods[req.methodName] as ( + ...args: unknown[] + ) => PayableMethodObject; Checks.truthy(methodRef, `${fnTag} YourContract.${req.methodName}`); - const method: ContractSendMethod = methodRef(...req.params); + const method = methodRef(...req.params); if (req.invocationType === EthContractInvocationType.Call) { contractInstance.methods[req.methodName]; - const callOutput = await (method as any).call(); + const callOutput = await method.call(); const success = true; return { success, callOutput }; } else if (req.invocationType === EthContractInvocationType.Send) { @@ -441,24 +534,29 @@ export class PluginLedgerConnectorEthereum const web3SigningCredential = req.web3SigningCredential as | Web3SigningCredentialPrivateKeyHex | Web3SigningCredentialCactusKeychainRef; - const payload = (method.send as any).request(); - const { params } = payload; - const [transactionConfig] = params; + if (!req.gas) { - req.gas = await this.web3.eth.estimateGas(transactionConfig); + const estimatedGas = await method.estimateGas(); + req.gas = estimatedGas.toString(); } - transactionConfig.from = web3SigningCredential.ethAccount; - transactionConfig.gas = req.gas; - transactionConfig.gasPrice = req.gasPrice; - transactionConfig.value = req.value; - transactionConfig.nonce = req.nonce; - const txReq: RunTransactionRequest = { + const maxFeePerGas = await this.estimateMaxFeePerGas(); + const transactionConfig = { + from: web3SigningCredential.ethAccount, + to: contractAddress, + maxPriorityFeePerGas: req.gasPrice ?? 0, + maxFeePerGas, + gasLimit: req.gas, + value: req.value, + nonce: req.nonce, + data: method.encodeABI(), + }; + + const out = await this.transact({ transactionConfig, web3SigningCredential, timeoutMs: req.timeoutMs || 60000, - }; - const out = await this.transact(txReq); + }); const success = out.transactionReceipt.status; const data = { success, out }; return data; @@ -473,68 +571,83 @@ export class PluginLedgerConnectorEthereum req: RunTransactionRequest, ): Promise { const fnTag = `${this.className}#transact()`; - - switch (req.web3SigningCredential.type) { - case Web3SigningCredentialType.CactusKeychainRef: { - return this.transactCactusKeychainRef(req); - } - case Web3SigningCredentialType.GethKeychainPassword: { - return this.transactGethKeychain(req); - } - case Web3SigningCredentialType.PrivateKeyHex: { - return this.transactPrivateKey(req); - } - case Web3SigningCredentialType.None: { - if (req.transactionConfig.rawTransaction) { - return this.transactSigned(req.transactionConfig.rawTransaction); - } else { + try { + switch (req.web3SigningCredential.type) { + case Web3SigningCredentialType.CactusKeychainRef: { + return await this.transactCactusKeychainRef(req); + } + case Web3SigningCredentialType.GethKeychainPassword: { + return await this.transactGethKeychain(req); + } + case Web3SigningCredentialType.PrivateKeyHex: { + return await this.transactPrivateKey(req); + } + case Web3SigningCredentialType.None: { + if (req.transactionConfig.rawTransaction) { + return await this.transactSigned( + req.transactionConfig.rawTransaction, + ); + } else { + throw new Error( + `${fnTag} Expected pre-signed raw transaction ` + + ` since signing credential is specified as` + + `Web3SigningCredentialType.NONE`, + ); + } + } + default: { throw new Error( - `${fnTag} Expected pre-signed raw transaction ` + - ` since signing credential is specified as` + - `Web3SigningCredentialType.NONE`, + `${fnTag} Unrecognized Web3SigningCredentialType: ` + + `${req.web3SigningCredential.type} Supported ones are: ` + + `${Object.values(Web3SigningCredentialType).join(";")}`, ); } } - default: { - throw new Error( - `${fnTag} Unrecognized Web3SigningCredentialType: ` + - `${req.web3SigningCredential.type} Supported ones are: ` + - `${Object.values(Web3SigningCredentialType).join(";")}`, - ); + } catch (error) { + if ("toJSON" in error) { + this.log.debug("transact() failed with error:", error.toJSON()); } + throw error; } } public async transactSigned( rawTransaction: string, ): Promise { - const fnTag = `${this.className}#transactSigned()`; - - const receipt = await this.web3.eth.sendSignedTransaction(rawTransaction); - - if (receipt instanceof Error) { - this.log.debug(`${fnTag} Web3 sendSignedTransaction failed`, receipt); - throw receipt; - } else { - this.prometheusExporter.addCurrentTransaction(); - return { transactionReceipt: receipt }; - } + const receipt = (await this.web3.eth.sendSignedTransaction( + rawTransaction, + Web3StringReturnFormat, + )) as TransactionReceiptBase; + + this.prometheusExporter.addCurrentTransaction(); + return { + transactionReceipt: { + ...receipt, + status: convertWeb3ReceiptStatusToBool(receipt.status), + }, + }; } public async transactGethKeychain( txIn: RunTransactionRequest, ): Promise { const fnTag = `${this.className}#transactGethKeychain()`; - const { sendTransaction } = this.web3.eth.personal; const { transactionConfig, web3SigningCredential } = txIn; - const { - secret, - } = web3SigningCredential as Web3SigningCredentialGethKeychainPassword; + const { secret } = + web3SigningCredential as Web3SigningCredentialGethKeychainPassword; try { - const txHash = await sendTransaction(transactionConfig, secret); + const txHash = await this.web3.eth.personal.sendTransaction( + transactionConfig, + secret, + ); const transactionReceipt = await this.pollForTxReceipt(txHash); - return { transactionReceipt }; + return { + transactionReceipt: { + ...transactionReceipt, + status: convertWeb3ReceiptStatusToBool(transactionReceipt.status), + }, + }; } catch (ex) { throw new Error( `${fnTag} Failed to invoke web3.eth.personal.sendTransaction(). ` + @@ -548,9 +661,8 @@ export class PluginLedgerConnectorEthereum ): Promise { const fnTag = `${this.className}#transactPrivateKey()`; const { transactionConfig, web3SigningCredential } = req; - const { - secret, - } = web3SigningCredential as Web3SigningCredentialPrivateKeyHex; + const { secret } = + web3SigningCredential as Web3SigningCredentialPrivateKeyHex; const signedTx = await this.web3.eth.accounts.signTransaction( transactionConfig, @@ -572,11 +684,8 @@ export class PluginLedgerConnectorEthereum ): Promise { const fnTag = `${this.className}#transactCactusKeychainRef()`; const { transactionConfig, web3SigningCredential } = req; - const { - ethAccount, - keychainEntryKey, - keychainId, - } = web3SigningCredential as Web3SigningCredentialCactusKeychainRef; + 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. @@ -593,9 +702,8 @@ export class PluginLedgerConnectorEthereum this.log.debug( `${fnTag} Gas not specified in the transaction values. Using the estimate from web3`, ); - transactionConfig.gas = await this.web3.eth.estimateGas( - transactionConfig, - ); + const estimatedGas = await this.web3.eth.estimateGas(transactionConfig); + transactionConfig.gas = estimatedGas.toString(); this.log.debug( `${fnTag} Gas estimated from web3 is: `, transactionConfig.gas, @@ -615,24 +723,31 @@ export class PluginLedgerConnectorEthereum public async pollForTxReceipt( txHash: string, timeoutMs = 60000, - ): Promise { + ): Promise> { const fnTag = `${this.className}#pollForTxReceipt()`; - let txReceipt; let timedOut = false; let tries = 0; const startedAt = new Date(); do { - txReceipt = await this.web3.eth.getTransactionReceipt(txHash); + try { + return (await this.web3.eth.getTransactionReceipt( + txHash, + Web3StringReturnFormat, + )) as TransactionReceiptBase; + } catch (error) { + this.log.debug( + "pollForTxReceipt getTransactionReceipt failed - (retry)", + ); + } + + // Sleep for 1 second + await new Promise((resolve) => setTimeout(resolve, 1000)); tries++; timedOut = Date.now() >= startedAt.getTime() + timeoutMs; - } while (!timedOut && !txReceipt); + } while (!timedOut); - if (!txReceipt) { - throw new Error(`${fnTag} Timed out ${timeoutMs}ms, polls=${tries}`); - } else { - return txReceipt; - } + throw new Error(`${fnTag} Timed out ${timeoutMs}ms, polls=${tries}`); } private async generateBytecode(req: any): Promise { @@ -708,7 +823,7 @@ export class PluginLedgerConnectorEthereum receipt.transactionReceipt.contractAddress && receipt.transactionReceipt.contractAddress != null ) { - const networkId = await this.web3.eth.net.getId(); + const networkId = (await this.web3.eth.net.getId()).toString(); const address = { address: receipt.transactionReceipt.contractAddress }; const network = { [networkId]: address }; contractJSON.networks = network; @@ -747,10 +862,13 @@ export class PluginLedgerConnectorEthereum ); const looseWeb3Eth = this.web3.eth as any; - const isSafeToCall = this.isSafeToCallObjectMethod( - looseWeb3Eth, - args.methodName, - ); + // web3.eth methods in 4.X are stored in parent class + const isSafeToCall = + this.isSafeToCallObjectMethod(looseWeb3Eth, args.methodName) || + this.isSafeToCallObjectMethod( + Object.getPrototypeOf(looseWeb3Eth), + args.methodName, + ); if (!isSafeToCall) { throw new RuntimeError( `Invalid method name provided in request. ${args.methodName} does not exist on the Web3.Eth object.`, @@ -780,13 +898,10 @@ export class PluginLedgerConnectorEthereum ); } - const contract = new this.web3.eth.Contract( - args.abi as AbiItem[], - args.address, - ); + const contract = new this.web3.eth.Contract(args.abi, args.address); const isSafeToCall = await this.isSafeToCallContractMethod( - contract, + contract as unknown as Contract, args.contractMethod, ); if (!isSafeToCall) { @@ -795,8 +910,11 @@ export class PluginLedgerConnectorEthereum ); } - return contract.methods[args.contractMethod](...contractMethodArgs)[ - args.invocationType - ](args.invocationParams); + const methodRef = contract.methods[args.contractMethod] as ( + ...args: unknown[] + ) => any; + return methodRef(...contractMethodArgs)[args.invocationType]( + args.invocationParams, + ); } } diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/public-api.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/public-api.ts index b7bc4f674f3..22a80892364 100755 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/public-api.ts +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/public-api.ts @@ -5,7 +5,7 @@ export { IPluginLedgerConnectorEthereumOptions, } from "./plugin-ledger-connector-ethereum"; -export * from "./model-type-guards"; +export * from "./types/model-type-guards"; export { PluginFactoryLedgerConnector } from "./plugin-factory-ledger-connector"; diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/model-type-guards.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/types/model-type-guards.ts similarity index 96% rename from packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/model-type-guards.ts rename to packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/types/model-type-guards.ts index 312e8851a22..14f74a868ab 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/model-type-guards.ts +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/types/model-type-guards.ts @@ -4,7 +4,7 @@ import { Web3SigningCredentialNone, Web3SigningCredentialPrivateKeyHex, Web3SigningCredentialType, -} from "./generated/openapi/typescript-axios/api"; +} from "../generated/openapi/typescript-axios/api"; export function isWeb3SigningCredentialPrivateKeyHex(x?: { type?: Web3SigningCredentialType; diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/types/util-types.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/types/util-types.ts new file mode 100644 index 00000000000..0c5c8f9df00 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/types/util-types.ts @@ -0,0 +1,34 @@ +import { Bytes, FMT_BYTES, FMT_NUMBER, Numbers } from "web3"; + +const WEB3_RECEIPT_SUCCESS_CODE = "1"; + +/** + * Convert `Numbers` and `Bytes` occurrences to string. + * Use with web3js response types where custom (string) return format was applied but + * didn't affect the response type. + * + * @warn Ensure that web3 method really returns string encoded numbers and bytes before using this! + */ +export type ConvertWeb3ReturnToString = { + [K in keyof T]: T[K] extends Numbers | Bytes | undefined ? string : T[K]; +}; + +/** + * Convert status code to boolean value. + * + * @param status transaction receipt status + * @returns boolean + */ +export function convertWeb3ReceiptStatusToBool(status: Numbers): boolean { + return status.toString() === WEB3_RECEIPT_SUCCESS_CODE; +} + +/** + * Custom web3 return format that will convert numbers and bytes to string. + * + * @warn doesn't work everywhere, sometimes you must copy-paste it directly to satisfy web3 generics. + */ +export const Web3StringReturnFormat = { + number: FMT_NUMBER.STR, + bytes: FMT_BYTES.HEX, +}; diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/deploy-contract-solidity-bytecode-endpoint-json-object.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/deploy-contract-solidity-bytecode-endpoint-json-object.ts index bb9ef90fa16..8c0c20bbc1f 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/deploy-contract-solidity-bytecode-endpoint-json-object.ts +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/deploy-contract-solidity-bytecode-endpoint-json-object.ts @@ -26,7 +26,8 @@ export interface IDeployContractSolidityBytecodeOptionsJsonObject { } export class DeployContractSolidityBytecodeJsonObjectEndpoint - implements IWebServiceEndpoint { + implements IWebServiceEndpoint +{ public static readonly CLASS_NAME = "DeployContractSolidityBytecodeEndpointJsonObject"; diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/deploy-contract-solidity-bytecode-endpoint.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/deploy-contract-solidity-bytecode-endpoint.ts index 3badc666cf0..f06c9a55967 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/deploy-contract-solidity-bytecode-endpoint.ts +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/deploy-contract-solidity-bytecode-endpoint.ts @@ -26,7 +26,8 @@ export interface IDeployContractSolidityBytecodeOptions { } export class DeployContractSolidityBytecodeEndpoint - implements IWebServiceEndpoint { + implements IWebServiceEndpoint +{ public static readonly CLASS_NAME = "DeployContractSolidityBytecodeEndpoint"; private readonly log: Logger; @@ -45,7 +46,7 @@ export class DeployContractSolidityBytecodeEndpoint this.log = LoggerProvider.getOrCreate({ level, label }); } - public get oasPath(): typeof OAS.paths["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/deploy-contract-solidity-bytecode"] { + public get oasPath(): (typeof OAS.paths)["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/deploy-contract-solidity-bytecode"] { return OAS.paths[ "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/deploy-contract-solidity-bytecode" ]; diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/get-prometheus-exporter-metrics-endpoint-v1.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/get-prometheus-exporter-metrics-endpoint-v1.ts index e927fe44379..432a100f131 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/get-prometheus-exporter-metrics-endpoint-v1.ts +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/get-prometheus-exporter-metrics-endpoint-v1.ts @@ -26,7 +26,8 @@ export interface IGetPrometheusExporterMetricsEndpointV1Options { } export class GetPrometheusExporterMetricsEndpointV1 - implements IWebServiceEndpoint { + implements IWebServiceEndpoint +{ private readonly log: Logger; constructor( @@ -46,7 +47,7 @@ export class GetPrometheusExporterMetricsEndpointV1 return this.handleRequest.bind(this); } - public get oasPath(): typeof OAS.paths["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/get-prometheus-exporter-metrics"] { + public get oasPath(): (typeof OAS.paths)["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/get-prometheus-exporter-metrics"] { return OAS.paths[ "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/get-prometheus-exporter-metrics" ]; @@ -87,7 +88,8 @@ export class GetPrometheusExporterMetricsEndpointV1 this.log.debug(`${verbUpper} ${this.getPath()}`); try { - const resBody = await this.options.connector.getPrometheusExporterMetrics(); + const resBody = + await this.options.connector.getPrometheusExporterMetrics(); res.status(200); res.send(resBody); } catch (ex) { diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/invoke-contract-endpoint.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/invoke-contract-endpoint.ts index 559665230a4..f64943b4e0b 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/invoke-contract-endpoint.ts +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/invoke-contract-endpoint.ts @@ -43,7 +43,7 @@ export class InvokeContractEndpoint implements IWebServiceEndpoint { this.log = LoggerProvider.getOrCreate({ level, label }); } - public get oasPath(): typeof OAS.paths["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/invoke-contract"] { + public get oasPath(): (typeof OAS.paths)["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/invoke-contract"] { return OAS.paths[ "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/invoke-contract" ]; diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/invoke-raw-web3eth-contract-v1-endpoint.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/invoke-raw-web3eth-contract-v1-endpoint.ts index 18fa5b26774..d262b0c0a55 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/invoke-raw-web3eth-contract-v1-endpoint.ts +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/invoke-raw-web3eth-contract-v1-endpoint.ts @@ -43,7 +43,7 @@ export class InvokeRawWeb3EthContractEndpoint implements IWebServiceEndpoint { this.log = LoggerProvider.getOrCreate({ level, label }); } - public get oasPath(): typeof OAS.paths["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/invoke-raw-web3eth-contract"] { + public get oasPath(): (typeof OAS.paths)["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/invoke-raw-web3eth-contract"] { return OAS.paths[ "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/invoke-raw-web3eth-contract" ]; @@ -87,9 +87,8 @@ export class InvokeRawWeb3EthContractEndpoint implements IWebServiceEndpoint { this.log.debug(reqTag); try { - const methodResponse = await this.options.connector.invokeRawWeb3EthContract( - req.body, - ); + const methodResponse = + await this.options.connector.invokeRawWeb3EthContract(req.body); const response: InvokeRawWeb3EthContractV1Response = { status: 200, data: methodResponse, diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/invoke-raw-web3eth-method-v1-endpoint.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/invoke-raw-web3eth-method-v1-endpoint.ts index b6a88567b07..32a8a4f62ee 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/invoke-raw-web3eth-method-v1-endpoint.ts +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/invoke-raw-web3eth-method-v1-endpoint.ts @@ -41,7 +41,7 @@ export class InvokeRawWeb3EthMethodEndpoint implements IWebServiceEndpoint { this.log = LoggerProvider.getOrCreate({ level, label }); } - public get oasPath(): typeof OAS.paths["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/invoke-raw-web3eth-method"] { + public get oasPath(): (typeof OAS.paths)["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/invoke-raw-web3eth-method"] { return OAS.paths[ "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/invoke-raw-web3eth-method" ]; @@ -85,9 +85,8 @@ export class InvokeRawWeb3EthMethodEndpoint implements IWebServiceEndpoint { this.log.debug(reqTag); try { - const methodResponse = await this.options.connector.invokeRawWeb3EthMethod( - req.body, - ); + const methodResponse = + await this.options.connector.invokeRawWeb3EthMethod(req.body); const response: InvokeRawWeb3EthMethodV1Response = { status: 200, data: methodResponse, diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/run-transaction-endpoint.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/run-transaction-endpoint.ts index 39bfa68378c..c6b946491ef 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/run-transaction-endpoint.ts +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/run-transaction-endpoint.ts @@ -43,7 +43,7 @@ export class RunTransactionEndpoint implements IWebServiceEndpoint { this.log = LoggerProvider.getOrCreate({ level, label }); } - public get oasPath(): typeof OAS.paths["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/run-transaction"] { + public get oasPath(): (typeof OAS.paths)["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/run-transaction"] { return OAS.paths[ "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/run-transaction" ]; @@ -88,7 +88,7 @@ export class RunTransactionEndpoint implements IWebServiceEndpoint { const reqBody: RunTransactionRequest = req.body; try { const resBody = await this.options.connector.transact(reqBody); - res.json({ success: true, data: resBody }); + res.json(resBody); } catch (ex) { this.log.error(`Crash while serving ${reqTag}`, ex); res.status(500).json({ diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/watch-blocks-v1-endpoint.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/watch-blocks-v1-endpoint.ts index c56f76ebbb5..64cdc882290 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/watch-blocks-v1-endpoint.ts +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/web-services/watch-blocks-v1-endpoint.ts @@ -1,3 +1,7 @@ +import Web3, { BlockHeaderOutput, FMT_BYTES, FMT_NUMBER } from "web3"; +import { NewHeadsSubscription } from "web3-eth"; +import { Socket as SocketIoSocket } from "socket.io"; + import { Logger, LogLevelDesc, @@ -8,10 +12,12 @@ import { WatchBlocksV1Options, WatchBlocksV1Progress, WatchBlocksV1, - WatchBlocksV1BlockData, + Web3Transaction, } from "../generated/openapi/typescript-axios"; -import { Socket as SocketIoSocket } from "socket.io"; -import Web3 from "web3"; +import { + ConvertWeb3ReturnToString, + Web3StringReturnFormat, +} from "../types/util-types"; export interface IWatchBlocksV1EndpointConfiguration { logLevel?: LogLevelDesc; @@ -50,54 +56,69 @@ export class WatchBlocksV1Endpoint { this.log = LoggerProvider.getOrCreate({ level, label }); } - public async subscribe(): Promise { + public async subscribe(): Promise { const { socket, log, web3, isGetBlockData } = this; log.debug(`${WatchBlocksV1.Subscribe} => ${socket.id}`); - const sub = web3.eth.subscribe( + const newBlocksSubscription = await web3.eth.subscribe( "newBlockHeaders", - async (ex, blockHeader) => { - log.debug("newBlockHeaders: Error=%o BlockHeader=%o", ex, blockHeader); - - if (ex) { - socket.emit(WatchBlocksV1.Error, ex.message); - sub.unsubscribe(); - } else if (blockHeader) { - let next: WatchBlocksV1Progress; - - if (isGetBlockData) { - const web3BlockData = await web3.eth.getBlock( - blockHeader.hash, - true, - ); - - next = { - // difficulty and totalDifficulty returned from the ledger are string, forcing typecast - blockData: (web3BlockData as unknown) as WatchBlocksV1BlockData, - }; - } else { - next = { blockHeader }; - } - - socket.emit(WatchBlocksV1.Next, next); - } - }, + undefined, + Web3StringReturnFormat, ); - log.debug("Subscribing to Web3 new block headers event..."); + newBlocksSubscription.on("data", async (blockHeader) => { + log.debug("newBlockHeaders: BlockHeader=%o", blockHeader); + let next: WatchBlocksV1Progress; + + if (isGetBlockData) { + const web3BlockData = await web3.eth.getBlock( + blockHeader.number, + true, + { + number: FMT_NUMBER.STR, + bytes: FMT_BYTES.HEX, + }, + ); + + next = { + blockData: { + ...web3BlockData, + // Return with full tx objects is not detected, must manually force correct type + transactions: + web3BlockData.transactions as unknown as Web3Transaction[], + }, + }; + } else { + // Force fix type of sha3Uncles + let sha3Uncles: string = blockHeader.sha3Uncles as unknown as string; + if (Array.isArray(blockHeader.sha3Uncles)) { + sha3Uncles = blockHeader.sha3Uncles.toString(); + } + + next = { + blockHeader: { + ...(blockHeader as ConvertWeb3ReturnToString), + sha3Uncles, + }, + }; + } + + socket.emit(WatchBlocksV1.Next, next); + }); - socket.on("disconnect", async (reason: string) => { - log.debug("WebSocket:disconnect reason=%o", reason); - sub.unsubscribe((ex: Error, success: boolean) => { - log.debug("Web3 unsubscribe success=%o, ex=%", success, ex); - }); + newBlocksSubscription.on("error", async (error) => { + console.log("Error when subscribing to New block header: ", error); + socket.emit(WatchBlocksV1.Error, error.message); + await newBlocksSubscription.unsubscribe(); }); - socket.on(WatchBlocksV1.Unsubscribe, () => { + socket.on(WatchBlocksV1.Unsubscribe, async () => { log.debug(`${WatchBlocksV1.Unsubscribe}: unsubscribing Web3...`); - sub.unsubscribe((ex: Error, success: boolean) => { - log.debug("Web3 unsubscribe error=%o, success=%", ex, success); - }); + await newBlocksSubscription.unsubscribe(); + log.debug("Web3 unsubscribe done."); }); + + log.debug("Subscribing to Web3 new block headers event..."); + return newBlocksSubscription as unknown as NewHeadsSubscription; } } diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/scripts/get-quorum-connector-status.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/scripts/get-ethereum-connector-status.ts similarity index 100% rename from packages/cactus-plugin-ledger-connector-ethereum/src/scripts/get-quorum-connector-status.ts rename to packages/cactus-plugin-ledger-connector-ethereum/src/scripts/get-ethereum-connector-status.ts diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/test/solidity/hello-world-with-arg-contract/HelloWorldWithArg.json b/packages/cactus-plugin-ledger-connector-ethereum/src/test/solidity/hello-world-with-arg-contract/HelloWorldWithArg.json new file mode 100644 index 00000000000..3eb1600267c --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/test/solidity/hello-world-with-arg-contract/HelloWorldWithArg.json @@ -0,0 +1,68 @@ +{ + "contractName": "HelloWorldWithArg", + "abi": [ + { + "inputs": [ + { + "internalType": "string", + "name": "_name", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "getName", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "sayHello", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "newName", + "type": "string" + } + ], + "name": "setName", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "608060405234801561001057600080fd5b5060405161053b38038061053b8339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b50604052505081516100f6915060009060208401906100fd565b505061019e565b828054600181600116156101000203166002900490600052602060002090601f0160209004810192826101335760008555610179565b82601f1061014c57805160ff1916838001178555610179565b82800160010185558215610179579182015b8281111561017957825182559160200191906001019061015e565b50610185929150610189565b5090565b5b80821115610185576000815560010161018a565b61038e806101ad6000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806317d7de7c14610046578063c47f0027146100c3578063ef5fb05b1461016b575b600080fd5b61004e610173565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610088578181015183820152602001610070565b50505050905090810190601f1680156100b55780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610169600480360360208110156100d957600080fd5b8101906020810181356401000000008111156100f457600080fd5b82018360208201111561010657600080fd5b8035906020019184600183028401116401000000008311171561012857600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610209945050505050565b005b61004e610220565b60008054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156101ff5780601f106101d4576101008083540402835291602001916101ff565b820191906000526020600020905b8154815290600101906020018083116101e257829003601f168201915b5050505050905090565b805161021c9060009060208401906102b7565b5050565b6060600060405160200180806502432b63637960d51b815250600601828054600181600116156101000203166002900480156102935780601f10610271576101008083540402835291820191610293565b820191906000526020600020905b81548152906001019060200180831161027f575b5050602160f81b815260408051601e19818403018152600190920190529250505090565b828054600181600116156101000203166002900490600052602060002090601f0160209004810192826102ed5760008555610333565b82601f1061030657805160ff1916838001178555610333565b82800160010185558215610333579182015b82811115610333578251825591602001919060010190610318565b5061033f929150610343565b5090565b5b8082111561033f576000815560010161034456fea26469706673582212203e4ecb91cb209273de4487a748ef3afbab8f80c825bc13c7da66ac1b5c8dbe5e64736f6c63430007060033", + "gasEstimates": { + "creation": { + "codeDepositCost": "182000", + "executionCost": "infinite", + "totalCost": "infinite" + }, + "external": { + "getName()": "infinite", + "sayHello()": "infinite", + "setName(string)": "infinite" + } + } +} diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/test/solidity/hello-world-with-arg-contract/HelloWorldWithArg.sol b/packages/cactus-plugin-ledger-connector-ethereum/src/test/solidity/hello-world-with-arg-contract/HelloWorldWithArg.sol new file mode 100644 index 00000000000..cb30edb8ac0 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/test/solidity/hello-world-with-arg-contract/HelloWorldWithArg.sol @@ -0,0 +1,28 @@ +// ***************************************************************************** +// IMPORTANT: If you update this code then make sure to recompile +// it and update the .json file as well so that they +// remain in sync for consistent test executions. +// With that said, there shouldn't be any reason to recompile this, like ever... +// ***************************************************************************** + +pragma solidity >=0.7.0; + +contract HelloWorldWithArg { + string private name; + + constructor(string memory _name) { + name = _name; + } + + function sayHello() public view returns (string memory) { + return string(abi.encodePacked("Hello ", name, "!")); + } + + function getName() public view returns (string memory) { + return name; + } + + function setName(string memory newName) public { + name = newName; + } +} diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/geth-contract-deploy-and-invoke-using-json-object-v1.test.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/geth-contract-deploy-and-invoke-using-json-object-v1.test.ts new file mode 100644 index 00000000000..32f827d56be --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/geth-contract-deploy-and-invoke-using-json-object-v1.test.ts @@ -0,0 +1,416 @@ +/** + * Tests for deploying a contract and invoking it's method by directly sending contract JSON. + * + * @note all tests must be run in order, don't use `skip()` or `only()`. @todo - fix that + */ + +////////////////////////////////// +// Constants +////////////////////////////////// + +// Log settings +const testLogLevel: LogLevelDesc = "info"; + +import "jest-extended"; +import express from "express"; +import bodyParser from "body-parser"; +import http from "http"; +import { v4 as uuidV4 } from "uuid"; +import { AddressInfo } from "net"; +import { Server as SocketIoServer } from "socket.io"; +import Web3 from "web3"; +import { Web3Account } from "web3-eth-accounts"; + +import { + LogLevelDesc, + IListenOptions, + Servers, +} from "@hyperledger/cactus-common"; +import { PluginRegistry } from "@hyperledger/cactus-core"; +import { Configuration, Constants } from "@hyperledger/cactus-core-api"; +import { pruneDockerAllIfGithubAction } from "@hyperledger/cactus-test-tooling"; +import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory"; +import { + GethTestLedger, + WHALE_ACCOUNT_ADDRESS, +} from "@hyperledger/cactus-test-geth-ledger"; + +import HelloWorldContractJson from "../../solidity/hello-world-contract/HelloWorld.json"; +import HelloWorldWithArgContractJson from "../../solidity/hello-world-with-arg-contract/HelloWorldWithArg.json"; +import { + EthContractInvocationType, + PluginLedgerConnectorEthereum, + Web3SigningCredentialType, + DefaultApi as EthereumApi, + DeployContractSolidityBytecodeJsonObjectV1Request, + InvokeContractJsonObjectV1Request, +} from "../../../main/typescript/public-api"; + +const containerImageName = "ghcr.io/hyperledger/cacti-geth-all-in-one"; +const containerImageVersion = "2023-07-27-2a8c48ed6"; + +describe("Ethereum contract deploy and invoke using keychain tests", () => { + const keychainEntryKey = uuidV4(); + let testEthAccount: Web3Account, + web3: Web3, + addressInfo, + address: string, + port: number, + contractAddress: string, + apiHost, + apiConfig, + ledger: GethTestLedger, + apiClient: EthereumApi, + connector: PluginLedgerConnectorEthereum, + rpcApiHttpHost: string, + keychainPlugin: PluginKeychainMemory; + const expressApp = express(); + expressApp.use(bodyParser.json({ limit: "250mb" })); + const server = http.createServer(expressApp); + const wsApi = new SocketIoServer(server, { + path: Constants.SocketIoConnectionPathV1, + }); + + ////////////////////////////////// + // Setup + ////////////////////////////////// + + beforeAll(async () => { + const pruning = pruneDockerAllIfGithubAction({ logLevel: testLogLevel }); + await expect(pruning).resolves.toBeTruthy(); + + //ledger = new GethTestLedger({ emitContainerLogs: true, testLogLevel }); + ledger = new GethTestLedger({ + containerImageName, + containerImageVersion, + }); + await ledger.start(); + + const listenOptions: IListenOptions = { + hostname: "0.0.0.0", + port: 0, + server, + }; + addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; + ({ address, port } = addressInfo); + apiHost = `http://${address}:${port}`; + apiConfig = new Configuration({ basePath: apiHost }); + apiClient = new EthereumApi(apiConfig); + rpcApiHttpHost = await ledger.getRpcApiHttpHost(); + web3 = new Web3(rpcApiHttpHost); + testEthAccount = web3.eth.accounts.create(); + + const keychainEntryValue = testEthAccount.privateKey; + 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: testLogLevel, + }); + keychainPlugin.set( + HelloWorldContractJson.contractName, + JSON.stringify(HelloWorldContractJson), + ); + keychainPlugin.set( + HelloWorldWithArgContractJson.contractName, + JSON.stringify(HelloWorldWithArgContractJson), + ); + connector = new PluginLedgerConnectorEthereum({ + instanceId: uuidV4(), + rpcApiHttpHost, + logLevel: testLogLevel, + pluginRegistry: new PluginRegistry({ plugins: [keychainPlugin] }), + }); + }); + + afterAll(async () => { + await ledger.stop(); + await ledger.destroy(); + await Servers.shutdown(server); + + const pruning = pruneDockerAllIfGithubAction({ logLevel: testLogLevel }); + await expect(pruning).resolves.toBeTruthy(); + }); + + 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: { + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, + }, + transactionConfig: { + from: WHALE_ACCOUNT_ADDRESS, + to: testEthAccount.address, + value: initTransferValue, + }, + }); + + const balance = await web3.eth.getBalance(testEthAccount.address); + expect(balance).toBeTruthy(); + expect(balance.toString()).toBe(initTransferValue); + }); + + ////////////////////////////////// + // Deployment Tests + ////////////////////////////////// + + test("deploys contract using json object", async () => { + const deployOut = await apiClient.deployContractSolBytecodeJsonObjectV1({ + web3SigningCredential: { + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, + }, + gas: 1000000, + contractJSON: HelloWorldContractJson, + }); + expect(deployOut).toBeTruthy(); + expect(deployOut.data).toBeTruthy(); + expect(deployOut.data.transactionReceipt).toBeTruthy(); + expect(deployOut.data.transactionReceipt.contractAddress).toBeTruthy(); + + contractAddress = deployOut.data.transactionReceipt + .contractAddress as string; + expect(typeof contractAddress).toBe("string"); + expect(contractAddress).toBeTruthy(); + + const invokeOut = await apiClient.invokeContractV1NoKeychain({ + contractAddress, + invocationType: EthContractInvocationType.Call, + methodName: "sayHello", + params: [], + web3SigningCredential: { + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, + }, + gas: "1000000", + contractJSON: HelloWorldContractJson, + }); + expect(invokeOut).toBeTruthy(); + expect(invokeOut.data).toBeTruthy(); + expect(invokeOut.data.callOutput).toBeTruthy(); + expect(typeof invokeOut.data.callOutput).toBe("string"); + }); + + test("deploys contract using json object with constructorArgs", async () => { + const deployOut = await apiClient.deployContractSolBytecodeJsonObjectV1({ + web3SigningCredential: { + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, + }, + gas: 1000000, + constructorArgs: ["Johnny"], + contractJSON: HelloWorldWithArgContractJson, + }); + expect(deployOut).toBeTruthy(); + expect(deployOut.data).toBeTruthy(); + expect(deployOut.data.transactionReceipt).toBeTruthy(); + expect(deployOut.data.transactionReceipt.contractAddress).toBeTruthy(); + }); + + test("deployContractSolBytecodeJsonObjectV1 without contractJSON should fail", async () => { + try { + await apiClient.deployContractSolBytecodeJsonObjectV1({ + web3SigningCredential: { + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, + }, + gas: 1000000, + } as DeployContractSolidityBytecodeJsonObjectV1Request); + fail( + "Expected deployContractSolBytecodeJsonObjectV1 call to fail but it succeeded.", + ); + } catch (error) { + console.log("deployContractSolBytecodeJsonObjectV1 failed as expected"); + } + }); + + test("deployContractSolBytecodeJsonObjectV1 with additional parameters should fail", async () => { + try { + await apiClient.deployContractSolBytecodeJsonObjectV1({ + web3SigningCredential: { + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, + }, + gas: 1000000, + contractJSON: HelloWorldContractJson, + fake: 4, + } as DeployContractSolidityBytecodeJsonObjectV1Request); + fail( + "Expected deployContractSolBytecodeJsonObjectV1 call to fail but it succeeded.", + ); + } catch (error) { + console.log("deployContractSolBytecodeJsonObjectV1 failed as expected"); + } + }); + + ////////////////////////////////// + // Invoke Tests + ////////////////////////////////// + + test("invoke Web3SigningCredentialType.GETHKEYCHAINPASSWORD", async () => { + const nonce = await web3.eth.getTransactionCount(WHALE_ACCOUNT_ADDRESS); + const newName = `DrCactus${uuidV4()}`; + const setNameOut = await apiClient.invokeContractV1NoKeychain({ + contractJSON: HelloWorldContractJson, + invocationType: EthContractInvocationType.Send, + methodName: "setName", + contractAddress, + params: [newName], + web3SigningCredential: { + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, + }, + nonce: nonce.toString(), + }); + expect(setNameOut).toBeTruthy(); + expect(setNameOut.data).toBeTruthy(); + + try { + await apiClient.invokeContractV1NoKeychain({ + contractJSON: HelloWorldContractJson, + invocationType: EthContractInvocationType.Send, + methodName: "setName", + contractAddress, + params: [newName], + gas: "1000000", + web3SigningCredential: { + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, + }, + nonce: nonce.toString(), + }); + fail("Expected getContractInfoKeychain call to fail but it succeeded."); + } catch (error) { + expect(error).not.toEqual("Nonce too low"); + } + + const getNameOut = await apiClient.invokeContractV1NoKeychain({ + contractJSON: HelloWorldContractJson, + invocationType: EthContractInvocationType.Send, + methodName: "getName", + contractAddress, + params: [], + web3SigningCredential: { + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, + }, + }); + expect(getNameOut).toBeTruthy(); + expect(getNameOut.data).toBeTruthy(); + expect(getNameOut.data.success).toBeTruthy(); + + const invokeGetNameOut = await apiClient.invokeContractV1NoKeychain({ + contractJSON: HelloWorldContractJson, + invocationType: EthContractInvocationType.Call, + methodName: "getName", + contractAddress, + params: [], + web3SigningCredential: { + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, + }, + }); + expect(invokeGetNameOut).toBeTruthy(); + expect(invokeGetNameOut.data).toBeTruthy(); + expect(invokeGetNameOut.data.callOutput).toBeTruthy(); + expect(invokeGetNameOut.data.callOutput).toBe(newName); + }); + + test("invoke Web3SigningCredentialType.PrivateKeyHex", async () => { + const nonce = await web3.eth.getTransactionCount(testEthAccount.address); + const newName = `DrCactus${uuidV4()}`; + const setNameOut = await apiClient.invokeContractV1NoKeychain({ + contractJSON: HelloWorldContractJson, + invocationType: EthContractInvocationType.Send, + methodName: "setName", + contractAddress, + params: [newName], + web3SigningCredential: { + ethAccount: testEthAccount.address, + secret: testEthAccount.privateKey, + type: Web3SigningCredentialType.PrivateKeyHex, + }, + nonce: nonce.toString(), + }); + expect(setNameOut).toBeTruthy(); + expect(setNameOut.data).toBeTruthy(); + + try { + await apiClient.invokeContractV1NoKeychain({ + contractJSON: HelloWorldContractJson, + invocationType: EthContractInvocationType.Send, + methodName: "setName", + contractAddress, + params: [newName], + gas: "1000000", + web3SigningCredential: { + ethAccount: testEthAccount.address, + secret: testEthAccount.privateKey, + type: Web3SigningCredentialType.PrivateKeyHex, + }, + nonce: nonce.toString(), + }); + fail("Expected getContractInfoKeychain call to fail but it succeeded."); + } catch (error) { + expect(error).not.toEqual("Nonce too low"); + } + + const invokeGetNameOut = await apiClient.invokeContractV1NoKeychain({ + contractJSON: HelloWorldContractJson, + invocationType: EthContractInvocationType.Call, + methodName: "getName", + contractAddress, + params: [], + gas: "1000000", + web3SigningCredential: { + ethAccount: testEthAccount.address, + secret: testEthAccount.privateKey, + type: Web3SigningCredentialType.PrivateKeyHex, + }, + }); + expect(invokeGetNameOut).toBeTruthy(); + expect(invokeGetNameOut.data).toBeTruthy(); + expect(invokeGetNameOut.data.callOutput).toBeTruthy(); + expect(invokeGetNameOut.data.callOutput).toBe(newName); + }); + + test("invokeContractV1NoKeychain without methodName should fail", async () => { + try { + await apiClient.invokeContractV1NoKeychain({ + contractJSON: HelloWorldContractJson, + invocationType: EthContractInvocationType.Send, + contractAddress, + params: [`DrCactus${uuidV4()}`], + web3SigningCredential: { + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, + }, + } as InvokeContractJsonObjectV1Request); + fail( + "Expected deployContractSolBytecodeV1 call to fail but it succeeded.", + ); + } catch (error) { + console.log("deployContractSolBytecodeV1 failed as expected"); + } + }); +}); diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/geth-contract-deploy-and-invoke-using-keychain-v1.test.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/geth-contract-deploy-and-invoke-using-keychain-v1.test.ts new file mode 100644 index 00000000000..f50aab20f3a --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/geth-contract-deploy-and-invoke-using-keychain-v1.test.ts @@ -0,0 +1,545 @@ +/** + * Tests for deploying a contract and invoking it's method using contract JSON stored in keystore plugin. + * + * @note all tests must be run in order, don't use `skip()` or `only()`. @todo - fix that + */ + +////////////////////////////////// +// Constants +////////////////////////////////// + +// Log settings +const testLogLevel: LogLevelDesc = "info"; + +import "jest-extended"; +import express from "express"; +import bodyParser from "body-parser"; +import http from "http"; +import { v4 as uuidV4 } from "uuid"; +import { AddressInfo } from "net"; +import { Server as SocketIoServer } from "socket.io"; +import Web3 from "web3"; +import { Web3Account } from "web3-eth-accounts"; + +import { + LogLevelDesc, + IListenOptions, + Servers, +} from "@hyperledger/cactus-common"; +import { PluginRegistry } from "@hyperledger/cactus-core"; +import { Configuration, Constants } from "@hyperledger/cactus-core-api"; +import { pruneDockerAllIfGithubAction } from "@hyperledger/cactus-test-tooling"; +import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory"; +import { + GethTestLedger, + WHALE_ACCOUNT_ADDRESS, +} from "@hyperledger/cactus-test-geth-ledger"; + +import HelloWorldContractJson from "../../solidity/hello-world-contract/HelloWorld.json"; +import HelloWorldWithArgContractJson from "../../solidity/hello-world-with-arg-contract/HelloWorldWithArg.json"; +import { + EthContractInvocationType, + PluginLedgerConnectorEthereum, + Web3SigningCredentialCactusKeychainRef, + Web3SigningCredentialType, + DefaultApi as EthereumApi, + DeployContractSolidityBytecodeV1Request, + RunTransactionRequest, + InvokeContractV1Request, +} from "../../../main/typescript/public-api"; +import { K_CACTUS_ETHEREUM_TOTAL_TX_COUNT } from "../../../main/typescript/prometheus-exporter/metrics"; + +const containerImageName = "ghcr.io/hyperledger/cacti-geth-all-in-one"; +const containerImageVersion = "2023-07-27-2a8c48ed6"; + +describe("Ethereum contract deploy and invoke using keychain tests", () => { + const keychainEntryKey = uuidV4(); + let testEthAccount: Web3Account, + web3: Web3, + addressInfo, + address: string, + port: number, + contractAddress: string, + apiHost, + apiConfig, + ledger: GethTestLedger, + apiClient: EthereumApi, + connector: PluginLedgerConnectorEthereum, + rpcApiHttpHost: string, + keychainPlugin: PluginKeychainMemory; + const expressApp = express(); + expressApp.use(bodyParser.json({ limit: "250mb" })); + const server = http.createServer(expressApp); + const wsApi = new SocketIoServer(server, { + path: Constants.SocketIoConnectionPathV1, + }); + + ////////////////////////////////// + // Setup + ////////////////////////////////// + + beforeAll(async () => { + const pruning = pruneDockerAllIfGithubAction({ logLevel: testLogLevel }); + await expect(pruning).resolves.toBeTruthy(); + + ledger = new GethTestLedger({ + containerImageName, + containerImageVersion, + }); + await ledger.start(); + + const listenOptions: IListenOptions = { + hostname: "0.0.0.0", + port: 0, + server, + }; + addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; + ({ address, port } = addressInfo); + apiHost = `http://${address}:${port}`; + apiConfig = new Configuration({ basePath: apiHost }); + apiClient = new EthereumApi(apiConfig); + rpcApiHttpHost = await ledger.getRpcApiHttpHost(); + web3 = new Web3(rpcApiHttpHost); + testEthAccount = web3.eth.accounts.create(); + + const keychainEntryValue = testEthAccount.privateKey; + 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: testLogLevel, + }); + keychainPlugin.set( + HelloWorldContractJson.contractName, + JSON.stringify(HelloWorldContractJson), + ); + keychainPlugin.set( + HelloWorldWithArgContractJson.contractName, + JSON.stringify(HelloWorldWithArgContractJson), + ); + connector = new PluginLedgerConnectorEthereum({ + instanceId: uuidV4(), + rpcApiHttpHost, + logLevel: testLogLevel, + pluginRegistry: new PluginRegistry({ plugins: [keychainPlugin] }), + }); + }); + + afterAll(async () => { + await ledger.stop(); + await ledger.destroy(); + await Servers.shutdown(server); + + const pruning = pruneDockerAllIfGithubAction({ logLevel: testLogLevel }); + await expect(pruning).resolves.toBeTruthy(); + }); + + 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: { + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, + }, + transactionConfig: { + from: WHALE_ACCOUNT_ADDRESS, + to: testEthAccount.address, + value: initTransferValue, + }, + }); + + const balance = await web3.eth.getBalance(testEthAccount.address); + expect(balance).toBeTruthy(); + expect(balance.toString()).toBe(initTransferValue); + }); + + ////////////////////////////////// + // Deployment Tests + ////////////////////////////////// + + test("deploys contract using keychain", async () => { + const deployOut = await apiClient.deployContractSolBytecodeV1({ + contractName: HelloWorldContractJson.contractName, + keychainId: keychainPlugin.getKeychainId(), + web3SigningCredential: { + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, + }, + gas: 1000000, + }); + expect(deployOut).toBeTruthy(); + expect(deployOut.data).toBeTruthy(); + expect(deployOut.data.transactionReceipt).toBeTruthy(); + expect(deployOut.data.transactionReceipt.contractAddress).toBeTruthy(); + + contractAddress = deployOut.data.transactionReceipt + .contractAddress as string; + expect(typeof contractAddress).toBe("string"); + const invokeOut = await apiClient.invokeContractV1({ + contractName: HelloWorldContractJson.contractName, + invocationType: EthContractInvocationType.Call, + methodName: "sayHello", + keychainId: keychainPlugin.getKeychainId(), + params: [], + web3SigningCredential: { + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, + }, + gas: "1000000", + }); + expect(invokeOut).toBeTruthy(); + expect(invokeOut.data).toBeTruthy(); + expect(invokeOut.data.callOutput).toBeTruthy(); + expect(typeof invokeOut.data.callOutput).toBe("string"); + }); + + test("deploys contract using keychain with constructorArgs", async () => { + const deployOut = await apiClient.deployContractSolBytecodeV1({ + contractName: HelloWorldWithArgContractJson.contractName, + keychainId: keychainPlugin.getKeychainId(), + web3SigningCredential: { + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, + }, + gas: 1000000, + constructorArgs: ["Johnny"], + }); + expect(deployOut).toBeTruthy(); + expect(deployOut.data).toBeTruthy(); + expect(deployOut.data.transactionReceipt).toBeTruthy(); + expect(deployOut.data.transactionReceipt.contractAddress).toBeTruthy(); + }); + + test("deployContractSolBytecodeV1 without contractName should fail", async () => { + try { + await apiClient.deployContractSolBytecodeV1({ + keychainId: keychainPlugin.getKeychainId(), + web3SigningCredential: { + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, + }, + gas: 1000000, + } as DeployContractSolidityBytecodeV1Request); + fail( + "Expected deployContractSolBytecodeV1 call to fail but it succeeded.", + ); + } catch (error) { + console.log("deployContractSolBytecodeV1 failed as expected"); + } + }); + + test("deployContractSolBytecodeV1 with additional parameters should fail", async () => { + try { + await apiClient.deployContractSolBytecodeV1({ + contractName: HelloWorldContractJson.contractName, + keychainId: keychainPlugin.getKeychainId(), + web3SigningCredential: { + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, + }, + gas: 1000000, + fake: 4, + } as DeployContractSolidityBytecodeV1Request); + fail( + "Expected deployContractSolBytecodeV1 call to fail but it succeeded.", + ); + } catch (error) { + console.log("deployContractSolBytecodeV1 failed as expected"); + } + }); + + ////////////////////////////////// + // Invoke Tests + ////////////////////////////////// + + test("invoke Web3SigningCredentialType.GETHKEYCHAINPASSWORD", async () => { + const nonce = await web3.eth.getTransactionCount(WHALE_ACCOUNT_ADDRESS); + const newName = `DrCactus${uuidV4()}`; + const setNameOut = await apiClient.invokeContractV1({ + contractName: HelloWorldContractJson.contractName, + invocationType: EthContractInvocationType.Send, + methodName: "setName", + keychainId: keychainPlugin.getKeychainId(), + params: [newName], + web3SigningCredential: { + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, + }, + nonce: nonce.toString(), + }); + expect(setNameOut).toBeTruthy(); + expect(setNameOut.data).toBeTruthy(); + + try { + await apiClient.invokeContractV1({ + contractName: HelloWorldContractJson.contractName, + invocationType: EthContractInvocationType.Send, + methodName: "setName", + keychainId: keychainPlugin.getKeychainId(), + params: [newName], + gas: "1000000", + web3SigningCredential: { + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, + }, + nonce: nonce.toString(), + }); + fail("Expected invokeContractV1 call to fail but it succeeded."); + } catch (error) { + expect(error).not.toEqual("Nonce too low"); + } + + const getNameOut = await apiClient.invokeContractV1({ + contractName: HelloWorldContractJson.contractName, + invocationType: EthContractInvocationType.Send, + methodName: "getName", + keychainId: keychainPlugin.getKeychainId(), + params: [], + web3SigningCredential: { + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, + }, + }); + expect(getNameOut).toBeTruthy(); + expect(getNameOut.data).toBeTruthy(); + expect(getNameOut.data.success).toBeTruthy(); + + const invokeGetNameOut = await apiClient.invokeContractV1({ + contractName: HelloWorldContractJson.contractName, + invocationType: EthContractInvocationType.Call, + methodName: "getName", + keychainId: keychainPlugin.getKeychainId(), + params: [], + web3SigningCredential: { + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, + }, + }); + expect(invokeGetNameOut).toBeTruthy(); + expect(invokeGetNameOut.data).toBeTruthy(); + expect(invokeGetNameOut.data.callOutput).toBeTruthy(); + expect(invokeGetNameOut.data.callOutput).toBe(newName); + }); + + test("invoke Web3SigningCredentialType.NONE", async () => { + const testEthAccount2 = web3.eth.accounts.create(); + + const value = 10e6; + const { rawTransaction } = await web3.eth.accounts.signTransaction( + { + from: testEthAccount.address, + to: testEthAccount2.address, + value, + maxPriorityFeePerGas: 0, + maxFeePerGas: 0x40000000, + gasLimit: 21000, + }, + testEthAccount.privateKey, + ); + + await apiClient.runTransactionV1({ + web3SigningCredential: { + type: Web3SigningCredentialType.None, + }, + transactionConfig: { + rawTransaction, + }, + }); + + const balance2 = await web3.eth.getBalance(testEthAccount2.address); + expect(balance2).toBeTruthy(); + expect(balance2.toString()).toBe(value.toString()); + }); + + test("runTransactionV1 without transaction config should fail", async () => { + try { + await apiClient.runTransactionV1({ + web3SigningCredential: { + type: Web3SigningCredentialType.None, + }, + } as RunTransactionRequest); + fail( + "Expected deployContractSolBytecodeV1 call to fail but it succeeded.", + ); + } catch (error) { + console.log("deployContractSolBytecodeV1 failed as expected"); + } + }); + + test("invoke Web3SigningCredentialType.PrivateKeyHex", async () => { + const nonce = await web3.eth.getTransactionCount(testEthAccount.address); + const newName = `DrCactus${uuidV4()}`; + const setNameOut = await apiClient.invokeContractV1({ + contractName: HelloWorldContractJson.contractName, + invocationType: EthContractInvocationType.Send, + methodName: "setName", + keychainId: keychainPlugin.getKeychainId(), + params: [newName], + web3SigningCredential: { + ethAccount: testEthAccount.address, + secret: testEthAccount.privateKey, + type: Web3SigningCredentialType.PrivateKeyHex, + }, + nonce: nonce.toString(), + }); + expect(setNameOut).toBeTruthy(); + expect(setNameOut.data).toBeTruthy(); + + try { + await apiClient.invokeContractV1({ + contractName: HelloWorldContractJson.contractName, + invocationType: EthContractInvocationType.Send, + methodName: "setName", + keychainId: keychainPlugin.getKeychainId(), + params: [newName], + gas: "1000000", + web3SigningCredential: { + ethAccount: testEthAccount.address, + secret: testEthAccount.privateKey, + type: Web3SigningCredentialType.PrivateKeyHex, + }, + nonce: nonce.toString(), + }); + fail("Expected invokeContractV1 call to fail but it succeeded."); + } catch (error) { + expect(error).not.toEqual("Nonce too low"); + } + + const invokeGetNameOut = await apiClient.invokeContractV1({ + contractName: HelloWorldContractJson.contractName, + invocationType: EthContractInvocationType.Call, + methodName: "getName", + keychainId: keychainPlugin.getKeychainId(), + params: [], + gas: "1000000", + web3SigningCredential: { + ethAccount: testEthAccount.address, + secret: testEthAccount.privateKey, + type: Web3SigningCredentialType.PrivateKeyHex, + }, + }); + expect(invokeGetNameOut).toBeTruthy(); + expect(invokeGetNameOut.data).toBeTruthy(); + expect(invokeGetNameOut.data.callOutput).toBeTruthy(); + expect(invokeGetNameOut.data.callOutput).toBe(newName); + }); + + test("invoke Web3SigningCredentialType.CactusKeychainRef", async () => { + const newName = `DrCactus${uuidV4()}`; + const nonce = await web3.eth.getTransactionCount(testEthAccount.address); + + const web3SigningCredential: Web3SigningCredentialCactusKeychainRef = { + ethAccount: testEthAccount.address, + keychainEntryKey, + keychainId: keychainPlugin.getKeychainId(), + type: Web3SigningCredentialType.CactusKeychainRef, + }; + + // @todo - using too large nonce freezes the test! Fix that + const setNameOut = await apiClient.invokeContractV1({ + contractName: HelloWorldContractJson.contractName, + invocationType: EthContractInvocationType.Send, + methodName: "setName", + keychainId: keychainPlugin.getKeychainId(), + params: [newName], + gas: "1000000", + web3SigningCredential, + nonce: nonce.toString(), + }); + expect(setNameOut).toBeTruthy(); + expect(setNameOut.data).toBeTruthy(); + + try { + await apiClient.invokeContractV1({ + contractName: HelloWorldContractJson.contractName, + invocationType: EthContractInvocationType.Send, + methodName: "setName", + keychainId: keychainPlugin.getKeychainId(), + params: [newName], + gas: "1000000", + web3SigningCredential: { + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, + }, + nonce: nonce.toString(), + }); + fail("Expected invokeContractV1 call to fail but it succeeded."); + } catch (error) { + expect(error).not.toEqual("Nonce too low"); + } + + const invokeGetNameOut = await apiClient.invokeContractV1({ + contractName: HelloWorldContractJson.contractName, + invocationType: EthContractInvocationType.Call, + methodName: "getName", + keychainId: keychainPlugin.getKeychainId(), + params: [], + gas: "1000000", + web3SigningCredential, + }); + expect(invokeGetNameOut).toBeTruthy(); + expect(invokeGetNameOut.data).toBeTruthy(); + expect(invokeGetNameOut.data.callOutput).toBeTruthy(); + expect(invokeGetNameOut.data.callOutput).toBe(newName); + }); + + test("invokeContractV1 without methodName should fail", async () => { + try { + await apiClient.invokeContractV1({ + contractName: HelloWorldContractJson.contractName, + invocationType: EthContractInvocationType.Send, + keychainId: keychainPlugin.getKeychainId(), + params: [`DrCactus${uuidV4()}`], + web3SigningCredential: { + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, + }, + } as InvokeContractV1Request); + fail( + "Expected deployContractSolBytecodeV1 call to fail but it succeeded.", + ); + } catch (error) { + console.log("deployContractSolBytecodeV1 failed as expected"); + } + }); + + // @todo - move to separate test suite + test("get prometheus exporter metrics", async () => { + const res = await apiClient.getPrometheusMetricsV1(); + const promMetricsOutput = + "# HELP " + + K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + + " Total transactions executed\n" + + "# TYPE " + + K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + + " gauge\n" + + K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + + '{type="' + + K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + + '"} 3'; + expect(res); + expect(res.data); + expect(res.status).toEqual(200); + expect(res.data).toContain(promMetricsOutput); + }); +}); diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-web3-contract-v1.test.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/geth-invoke-web3-contract-v1.test.ts similarity index 78% rename from packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-web3-contract-v1.test.ts rename to packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/geth-invoke-web3-contract-v1.test.ts index a8904ed1d5c..8b0574480b2 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-web3-contract-v1.test.ts +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/geth-invoke-web3-contract-v1.test.ts @@ -10,8 +10,6 @@ const testLogLevel = "info"; const sutLogLevel = "info"; -const containerImageVersion = "2021-05-03-quorum-v21.4.1"; - import "jest-extended"; import { v4 as uuidv4 } from "uuid"; import { PluginRegistry } from "@hyperledger/cactus-core"; @@ -20,31 +18,32 @@ import { InvokeRawWeb3EthContractV1Request, PluginLedgerConnectorEthereum, Web3SigningCredentialType, -} from "../../../../../main/typescript/index"; +} from "../../../main/typescript/index"; +import { pruneDockerAllIfGithubAction } from "@hyperledger/cactus-test-tooling"; import { - QuorumTestLedger, - IQuorumGenesisOptions, - IAccount, - pruneDockerAllIfGithubAction, -} from "@hyperledger/cactus-test-tooling"; + GethTestLedger, + WHALE_ACCOUNT_ADDRESS, +} from "@hyperledger/cactus-test-geth-ledger"; import { Logger, LoggerProvider } from "@hyperledger/cactus-common"; -import { AbiItem } from "web3-utils"; -import HelloWorldContractJson from "../../../../solidity/hello-world-contract/HelloWorld.json"; +import HelloWorldContractJson from "../../solidity/hello-world-contract/HelloWorld.json"; import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory"; +import { ContractAbi } from "web3"; // Unit Test logger setup const log: Logger = LoggerProvider.getOrCreate({ - label: "v21.4.1-invoke-web3-contract-v1.test", + label: "geth-invoke-web3-contract-v1.test", level: testLogLevel, }); log.info("Test started"); +const containerImageName = "ghcr.io/hyperledger/cacti-geth-all-in-one"; +const containerImageVersion = "2023-07-27-2a8c48ed6"; + describe("invokeRawWeb3EthContract Tests", () => { - let ethereumTestLedger: QuorumTestLedger; + let ethereumTestLedger: GethTestLedger; let connector: PluginLedgerConnectorEthereum; - let firstHighNetWorthAccount: string; - let contractAbi: AbiItem[]; + let contractAbi: ContractAbi; let contractAddress: string; ////////////////////////////////// @@ -55,27 +54,14 @@ describe("invokeRawWeb3EthContract Tests", () => { log.info("Prune Docker..."); await pruneDockerAllIfGithubAction({ logLevel: testLogLevel }); - log.info("Start QuorumTestLedger..."); - log.debug("Ethereum version:", containerImageVersion); - ethereumTestLedger = new QuorumTestLedger({ + log.info("Start GethTestLedger..."); + // log.debug("Ethereum version:", containerImageVersion); + ethereumTestLedger = new GethTestLedger({ + containerImageName, containerImageVersion, }); await ethereumTestLedger.start(); - log.info("Get highNetWorthAccounts..."); - const ethereumGenesisOptions: IQuorumGenesisOptions = await ethereumTestLedger.getGenesisJsObject(); - expect(ethereumGenesisOptions).toBeTruthy(); - expect(ethereumGenesisOptions.alloc).toBeTruthy(); - - const highNetWorthAccounts: string[] = Object.keys( - ethereumGenesisOptions.alloc, - ).filter((address: string) => { - const anAccount: IAccount = ethereumGenesisOptions.alloc[address]; - const theBalance = parseInt(anAccount.balance, 10); - return theBalance > 10e7; - }); - [firstHighNetWorthAccount] = highNetWorthAccounts; - const rpcApiHttpHost = await ethereumTestLedger.getRpcApiHttpHost(); log.debug("rpcApiHttpHost:", rpcApiHttpHost); @@ -103,7 +89,7 @@ describe("invokeRawWeb3EthContract Tests", () => { contractName: HelloWorldContractJson.contractName, keychainId: keychainPlugin.getKeychainId(), web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, + ethAccount: WHALE_ACCOUNT_ADDRESS, secret: "", type: Web3SigningCredentialType.GethKeychainPassword, }, @@ -114,7 +100,7 @@ describe("invokeRawWeb3EthContract Tests", () => { expect(deployOut.transactionReceipt.contractAddress).toBeTruthy(); expect(deployOut.transactionReceipt.status).toBeTrue(); - contractAbi = HelloWorldContractJson.abi as AbiItem[]; + contractAbi = HelloWorldContractJson.abi; contractAddress = deployOut.transactionReceipt.contractAddress as string; }); @@ -135,7 +121,7 @@ describe("invokeRawWeb3EthContract Tests", () => { // 1. Set new value (send) const sendInvocationArgs = { - from: firstHighNetWorthAccount, + from: WHALE_ACCOUNT_ADDRESS, }; const sendInvokeArgs: InvokeRawWeb3EthContractV1Request = { @@ -151,7 +137,7 @@ describe("invokeRawWeb3EthContract Tests", () => { sendInvokeArgs, ); expect(resultsSend).toBeTruthy(); - expect(resultsSend.status).toBeTrue(); + expect(resultsSend.status.toString()).toEqual("1"); // // 2. Get new, updated value (call) const callInvokeArgs: InvokeRawWeb3EthContractV1Request = { diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-web3-method-v1.test.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/geth-invoke-web3-method-v1.test.ts similarity index 82% rename from packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-web3-method-v1.test.ts rename to packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/geth-invoke-web3-method-v1.test.ts index 70b75dfd5fb..0702064ff81 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-web3-method-v1.test.ts +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/geth-invoke-web3-method-v1.test.ts @@ -10,28 +10,30 @@ const testLogLevel = "info"; const sutLogLevel = "info"; -const containerImageVersion = "2021-05-03-quorum-v21.4.1"; - import "jest-extended"; import { v4 as uuidv4 } from "uuid"; import { PluginRegistry } from "@hyperledger/cactus-core"; -import { PluginLedgerConnectorEthereum } from "../../../../../main/typescript/index"; -import { - QuorumTestLedger, - pruneDockerAllIfGithubAction, -} from "@hyperledger/cactus-test-tooling"; +import { PluginLedgerConnectorEthereum } from "../../../main/typescript/index"; +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"; // Unit Test logger setup const log: Logger = LoggerProvider.getOrCreate({ - label: "v21.4.1-invoke-web3-method-v1.test", + label: "geth-invoke-web3-method-v1.test", level: testLogLevel, }); log.info("Test started"); +const containerImageName = "ghcr.io/hyperledger/cacti-geth-all-in-one"; +const containerImageVersion = "2023-07-27-2a8c48ed6"; + describe("invokeRawWeb3EthMethod Tests", () => { - let ethereumTestLedger: QuorumTestLedger; + let ethereumTestLedger: GethTestLedger; let connector: PluginLedgerConnectorEthereum; let web3: Web3; @@ -43,9 +45,10 @@ describe("invokeRawWeb3EthMethod Tests", () => { log.info("Prune Docker..."); await pruneDockerAllIfGithubAction({ logLevel: testLogLevel }); - log.info("Start QuorumTestLedger..."); - log.debug("Ethereum version:", containerImageVersion); - ethereumTestLedger = new QuorumTestLedger({ + log.info("Start GethTestLedger..."); + // log.debug("Ethereum version:", containerImageVersion); + ethereumTestLedger = new GethTestLedger({ + containerImageName, containerImageVersion, }); await ethereumTestLedger.start(); @@ -81,7 +84,7 @@ describe("invokeRawWeb3EthMethod Tests", () => { methodName: "getGasPrice", }); expect(connectorResponse).toBeTruthy(); - expect(connectorResponse).toEqual("0"); // gas is free on ethereum + expect(Number(connectorResponse)).toBeGreaterThan(0); }); test("invokeRawWeb3EthMethod with 1-argument method works (getBlock)", async () => { @@ -99,17 +102,16 @@ describe("invokeRawWeb3EthMethod Tests", () => { }); test("invokeRawWeb3EthMethod with 2-argument method works (getStorageAt)", async () => { - const genesisAccount = await ethereumTestLedger.getGenesisAccount(); - log.debug("genesisAccount:", genesisAccount); + log.debug("WHALE_ACCOUNT_ADDRESS:", WHALE_ACCOUNT_ADDRESS); const connectorResponse = await connector.invokeRawWeb3EthMethod({ methodName: "getStorageAt", - params: [genesisAccount, 0], + params: [WHALE_ACCOUNT_ADDRESS, 0], }); expect(connectorResponse).toBeTruthy(); // Compare with direct web3 response - const web3Response = await web3.eth.getStorageAt(genesisAccount, 0); + const web3Response = await web3.eth.getStorageAt(WHALE_ACCOUNT_ADDRESS, 0); expect(web3Response).toBeTruthy(); expect(web3Response).toEqual(connectorResponse); }); diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/openapi/openapi-validation-no-keychain.test.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/openapi/openapi-validation-no-keychain.test.ts deleted file mode 100644 index cab629aa348..00000000000 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/openapi/openapi-validation-no-keychain.test.ts +++ /dev/null @@ -1,323 +0,0 @@ -import test, { Test } from "tape-promise/tape"; -import { v4 as uuidV4 } from "uuid"; - -import { - LogLevelDesc, - IListenOptions, - Servers, -} from "@hyperledger/cactus-common"; - -import HelloWorldContractJson from "../../../../../solidity/hello-world-contract/HelloWorld.json"; - -import { - EthContractInvocationType, - PluginLedgerConnectorEthereum, - Web3SigningCredentialType, - DefaultApi as EthereumApi, - DeployContractSolidityBytecodeJsonObjectV1Request, - InvokeContractJsonObjectV1Request, -} from "../../../../../../main/typescript/public-api"; - -import { - QuorumTestLedger, - IQuorumGenesisOptions, - IAccount, - pruneDockerAllIfGithubAction, -} from "@hyperledger/cactus-test-tooling"; - -const testCase = "Ethereum Ledger Connector Plugin"; -import express from "express"; -import bodyParser from "body-parser"; -import http from "http"; -import { AddressInfo } from "net"; -import { Configuration, Constants } from "@hyperledger/cactus-core-api"; -import { PluginRegistry } from "@hyperledger/cactus-core"; -import { Server as SocketIoServer } from "socket.io"; - -import { installOpenapiValidationMiddleware } from "@hyperledger/cactus-core"; -import OAS from "../../../../../../main/json/openapi.json"; - -const logLevel: LogLevelDesc = "INFO"; - -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 containerImageVersion = "2021-05-03-quorum-v21.4.1"; - const ledgerOptions = { containerImageVersion }; - const ledger = new QuorumTestLedger(ledgerOptions); - test.onFinish(async () => { - await ledger.stop(); - await ledger.destroy(); - await pruneDockerAllIfGithubAction({ logLevel }); - }); - await ledger.start(); - - const rpcApiHttpHost = await ledger.getRpcApiHttpHost(); - const ethereumGenesisOptions: IQuorumGenesisOptions = await ledger.getGenesisJsObject(); - t.ok(ethereumGenesisOptions); - t.ok(ethereumGenesisOptions.alloc); - - const highNetWorthAccounts: string[] = Object.keys( - ethereumGenesisOptions.alloc, - ).filter((address: string) => { - const anAccount: IAccount = ethereumGenesisOptions.alloc[address]; - const theBalance = parseInt(anAccount.balance, 10); - return theBalance > 10e7; - }); - const [firstHighNetWorthAccount] = highNetWorthAccounts; - - const connector: PluginLedgerConnectorEthereum = new PluginLedgerConnectorEthereum( - { - instanceId: uuidV4(), - rpcApiHttpHost, - logLevel, - pluginRegistry: new PluginRegistry(), - }, - ); - - const expressApp = express(); - expressApp.use(bodyParser.json({ limit: "250mb" })); - const server = http.createServer(expressApp); - const listenOptions: IListenOptions = { - hostname: "localhost", - port: 0, - server, - }; - const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; - test.onFinish(async () => await Servers.shutdown(server)); - const { address, port } = addressInfo; - const apiHost = `http://${address}:${port}`; - t.comment( - `Metrics URL: ${apiHost}/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/get-prometheus-exporter-metrics`, - ); - - const apiConfig = new Configuration({ basePath: apiHost }); - const apiClient = new EthereumApi(apiConfig); - - const wsApi = new SocketIoServer(server, { - path: Constants.SocketIoConnectionPathV1, - }); - - await installOpenapiValidationMiddleware({ - logLevel, - app: expressApp, - apiSpec: OAS, - }); - - await connector.getOrCreateWebServices(); - await connector.registerWebServices(expressApp, wsApi); - - const fDeploy = "deployContractSolBytecodeJsonObjectV1"; - const fInvoke = "invokeContractV1NoKeychain"; - const cOk = "without bad request error"; - const cWithoutParams = "not sending all required parameters"; - const cInvalidParams = "sending invalid parameters"; - - let contractAddress: string; - - test(`${testCase} - ${fDeploy} - ${cOk}`, async (t2: Test) => { - const parameters = { - contractAddress, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - // bytecode: HelloWorldContractJson.bytecode, - gas: 1000000, - contractJSON: HelloWorldContractJson, - }; - const res = await apiClient.deployContractSolBytecodeJsonObjectV1( - parameters as DeployContractSolidityBytecodeJsonObjectV1Request, - ); - t2.ok(res, "Contract deployed successfully"); - t2.ok(res.data); - t2.equal( - res.status, - 200, - `Endpoint ${fDeploy}: response.status === 200 OK`, - ); - - contractAddress = res.data.transactionReceipt.contractAddress as string; - - t2.end(); - }); - - test(`${testCase} - ${fInvoke} - ${cOk}`, async (t2: Test) => { - const parameters = { - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [`DrCactus${uuidV4()}`], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - contractJSON: HelloWorldContractJson, - }; - const res = await apiClient.invokeContractV1NoKeychain( - parameters as InvokeContractJsonObjectV1Request, - ); - t2.ok(res, "Contract invoked successfully"); - t2.ok(res.data); - t2.equal( - res.status, - 200, - `Endpoint ${fInvoke}: response.status === 200 OK`, - ); - - t2.end(); - }); - - test(`${testCase} - ${fDeploy} - ${cWithoutParams}`, async (t2: Test) => { - try { - const parameters = { - contractAddress, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - bytecode: HelloWorldContractJson.bytecode, - gas: 1000000, - }; - await apiClient.deployContractSolBytecodeJsonObjectV1( - (parameters as any) as DeployContractSolidityBytecodeJsonObjectV1Request, - ); - } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fDeploy} without required contractJSON and bytecode: response.status === 400 OK`, - ); - const fields = e.response.data.map((param: any) => - param.path.replace(".body.", ""), - ); - t2.ok( - fields.includes("contractJSON"), - "Rejected because contractJSON is required", - ); - t2.notOk(fields.includes("gas"), "gas is not required"); - } - - t2.end(); - }); - - test(`${testCase} - ${fDeploy} - ${cInvalidParams}`, async (t2: Test) => { - try { - const parameters = { - contractAddress, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - bytecode: HelloWorldContractJson.bytecode, - gas: 1000000, - contractJSON: HelloWorldContractJson, - fake: 4, - }; - await apiClient.deployContractSolBytecodeJsonObjectV1( - (parameters as any) as DeployContractSolidityBytecodeJsonObjectV1Request, - ); - } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fDeploy} with fake=4: response.status === 400 OK`, - ); - const fields = e.response.data.map((param: any) => - param.path.replace(".body.", ""), - ); - t2.ok( - fields.includes("fake"), - "Rejected because fake is not a valid parameter", - ); - } - - t2.end(); - }); - - test(`${testCase} - ${fInvoke} - ${cWithoutParams}`, async (t2: Test) => { - try { - const parameters = { - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [`DrCactus${uuidV4()}`], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 2, - }; - await apiClient.invokeContractV1NoKeychain( - (parameters as any) as InvokeContractJsonObjectV1Request, - ); - } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fInvoke} without required contractJSON and methodName: response.status === 400 OK`, - ); - const fields = e.response.data.map((param: any) => - param.path.replace(".body.", ""), - ); - t2.ok( - fields.includes("contractJSON"), - "Rejected because contractJSON is required", - ); - t2.notOk(fields.includes("nonce"), "nonce is not required"); - } - - t2.end(); - }); - - test(`${testCase} - ${fInvoke} - ${cInvalidParams}`, async (t2: Test) => { - try { - const parameters = { - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [`DrCactus${uuidV4()}`], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 2, - contractJSON: HelloWorldContractJson, - fake: 4, - }; - await apiClient.invokeContractV1NoKeychain( - (parameters as any) as InvokeContractJsonObjectV1Request, - ); - } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fInvoke} with fake=4: response.status === 400 OK`, - ); - const fields = e.response.data.map((param: any) => - param.path.replace(".body.", ""), - ); - t2.ok( - fields.includes("fake"), - "Rejected because fake is not a valid parameter", - ); - } - - t2.end(); - }); - - t.end(); -}); diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/openapi/openapi-validation.test.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/openapi/openapi-validation.test.ts deleted file mode 100644 index 41f43ae3220..00000000000 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/openapi/openapi-validation.test.ts +++ /dev/null @@ -1,427 +0,0 @@ -import test, { Test } from "tape-promise/tape"; -import { v4 as uuidV4 } from "uuid"; -import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory"; -import HelloWorldContractJson from "../../../../../solidity/hello-world-contract/HelloWorld.json"; -import { - EthContractInvocationType, - PluginLedgerConnectorEthereum, - Web3SigningCredentialType, - DefaultApi as EthereumApi, - DeployContractSolidityBytecodeV1Request, - InvokeContractV1Request, - RunTransactionRequest, -} from "../../../../../../main/typescript/public-api"; -import { - QuorumTestLedger, - IQuorumGenesisOptions, - IAccount, - pruneDockerAllIfGithubAction, -} from "@hyperledger/cactus-test-tooling"; -import { - LogLevelDesc, - IListenOptions, - Servers, -} from "@hyperledger/cactus-common"; -import { PluginRegistry } from "@hyperledger/cactus-core"; -import { AddressInfo } from "net"; -import express from "express"; -import bodyParser from "body-parser"; -import http from "http"; -import { Configuration, Constants } from "@hyperledger/cactus-core-api"; -import { Server as SocketIoServer } from "socket.io"; - -import { installOpenapiValidationMiddleware } from "@hyperledger/cactus-core"; -import OAS from "../../../../../../main/json/openapi.json"; - -const logLevel: LogLevelDesc = "INFO"; -const testCase = "Ethereum API"; - -test("BEFORE " + testCase, async (t: Test) => { - const pruning = pruneDockerAllIfGithubAction({ logLevel }); - await t.doesNotReject(pruning, "Pruning did not throw OK"); - t.end(); -}); - -test(testCase, async (t: Test) => { - // create the test ethereumTestLedger - const containerImageVersion = "2021-01-08-7a055c3"; // Quorum v2.3.0, Tessera v0.10.0 - - const ledgerOptions = { containerImageVersion }; - const ethereumTestLedger = new QuorumTestLedger(ledgerOptions); - test.onFinish(async () => { - await ethereumTestLedger.stop(); - await ethereumTestLedger.destroy(); - }); - await ethereumTestLedger.start(); - - // retrieve host to which connector will attack - const rpcApiHttpHost = await ethereumTestLedger.getRpcApiHttpHost(); - - // obtain accounts from genesis - const ethereumGenesisOptions: IQuorumGenesisOptions = await ethereumTestLedger.getGenesisJsObject(); - const highNetWorthAccounts: string[] = Object.keys( - ethereumGenesisOptions.alloc, - ).filter((address: string) => { - const anAccount: IAccount = ethereumGenesisOptions.alloc[address]; - const theBalance = parseInt(anAccount.balance, 10); - return theBalance > 10e7; - }); - const [firstHighNetWorthAccount] = highNetWorthAccounts; - - // create a new account - const testEthAccount = await ethereumTestLedger.createEthTestAccount(); - - // create the keychain plugin for recently created account - const keychainEntryKey = uuidV4(); - const keychainId = uuidV4(); - const keychainEntryValue = testEthAccount.privateKey; - const keychainPlugin = new PluginKeychainMemory({ - instanceId: uuidV4(), - keychainId, - backend: new Map([[keychainEntryKey, keychainEntryValue]]), - logLevel, - }); - keychainPlugin.set( - HelloWorldContractJson.contractName, - JSON.stringify(HelloWorldContractJson), - ); - - // create a plugin registry with the recently created keychain plugin - const pluginRegistry = new PluginRegistry({ - plugins: [keychainPlugin], - }); - - // create the connector including test ledger host and plugin registry - const connector: PluginLedgerConnectorEthereum = new PluginLedgerConnectorEthereum( - { - instanceId: uuidV4(), - rpcApiHttpHost, - logLevel, - pluginRegistry, - }, - ); - - const expressApp = express(); - expressApp.use(bodyParser.json({ limit: "250mb" })); - const server = http.createServer(expressApp); - const listenOptions: IListenOptions = { - hostname: "localhost", - port: 0, - server, - }; - const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; - test.onFinish(async () => await Servers.shutdown(server)); - const { address, port } = addressInfo; - const apiHost = `http://${address}:${port}`; - - const apiConfig = new Configuration({ basePath: apiHost }); - const apiClient = new EthereumApi(apiConfig); - - const wsApi = new SocketIoServer(server, { - path: Constants.SocketIoConnectionPathV1, - }); - - await installOpenapiValidationMiddleware({ - logLevel, - app: expressApp, - apiSpec: OAS, - }); - - await connector.getOrCreateWebServices(); - await connector.registerWebServices(expressApp, wsApi); - - const fDeploy = "apiV1EthereumDeployContractSolidityBytecode"; - const fInvoke = "apiV1EthereumInvokeContract"; - const fRun = "apiV1EthereumRunTransaction"; - const cOk = "without bad request error"; - const cWithoutParams = "not sending all required parameters"; - const cInvalidParams = "sending invalid parameters"; - - let contractAddress: string; - - test(`${testCase} - ${fDeploy} - ${cOk}`, async (t2: Test) => { - const parameters = { - contractName: HelloWorldContractJson.contractName, - keychainId: keychainPlugin.getKeychainId(), - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - gas: 1000000, - }; - const res = await apiClient.deployContractSolBytecodeV1( - parameters as DeployContractSolidityBytecodeV1Request, - ); - t2.ok(res, "Contract deployed successfully"); - t2.ok(res.data); - t2.equal( - res.status, - 200, - `Endpoint ${fDeploy}: response.status === 200 OK`, - ); - - contractAddress = res.data.transactionReceipt.contractAddress as string; - - t2.end(); - }); - - test(`${testCase} - ${fDeploy} - ${cWithoutParams}`, async (t2: Test) => { - try { - const parameters = { - keychainId: keychainPlugin.getKeychainId(), - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - }; - await apiClient.deployContractSolBytecodeV1( - parameters as DeployContractSolidityBytecodeV1Request, - ); - } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fDeploy} without required contractName and bytecode: response.status === 400 OK`, - ); - const fields = e.response.data.map((param: any) => - param.path.replace(".body.", ""), - ); - t2.ok( - fields.includes("contractName"), - "Rejected because contractName is required", - ); - t2.notOk(fields.includes("gas"), "gas is not required"); - } - - t2.end(); - }); - - test(`${testCase} - ${fDeploy} - ${cInvalidParams}`, async (t2: Test) => { - try { - const parameters = { - contractName: HelloWorldContractJson.contractName, - keychainId: keychainPlugin.getKeychainId(), - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - gas: 1000000, - fake: 4, - }; - await apiClient.deployContractSolBytecodeV1( - parameters as DeployContractSolidityBytecodeV1Request, - ); - } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fDeploy} with fake=4: response.status === 400 OK`, - ); - const fields = e.response.data.map((param: any) => - param.path.replace(".body.", ""), - ); - t2.ok( - fields.includes("fake"), - "Rejected because fake is not a valid parameter", - ); - } - - t2.end(); - }); - - test(`${testCase} - ${fInvoke} - ${cOk}`, async (t2: Test) => { - const parameters = { - contractName: HelloWorldContractJson.contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [`DrCactus${uuidV4()}`], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 2, - }; - const res = await apiClient.invokeContractV1( - parameters as InvokeContractV1Request, - ); - t2.ok(res, "Contract invoked successfully"); - t2.ok(res.data); - t2.equal( - res.status, - 200, - `Endpoint ${fInvoke}: response.status === 200 OK`, - ); - - t2.end(); - }); - - test(`${testCase} - ${fInvoke} - ${cWithoutParams}`, async (t2: Test) => { - try { - const parameters = { - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Send, - params: [`DrCactus${uuidV4()}`], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - }; - await apiClient.invokeContractV1(parameters as InvokeContractV1Request); - } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fInvoke} without required methodName: response.status === 400 OK`, - ); - const fields = e.response.data.map((param: any) => - param.path.replace(".body.", ""), - ); - t2.ok( - fields.includes("methodName"), - "Rejected because methodName is required", - ); - t2.notOk(fields.includes("nonce"), "nonce is not required"); - } - - t2.end(); - }); - - test(`${testCase} - ${fInvoke} - ${cInvalidParams}`, async (t2: Test) => { - try { - const parameters = { - contractName: HelloWorldContractJson.contractName, - contractAddress, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [`DrCactus${uuidV4()}`], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 2, - fake: 4, - }; - await apiClient.invokeContractV1(parameters as InvokeContractV1Request); - } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fInvoke} with fake=4: response.status === 400 OK`, - ); - const fields = e.response.data.map((param: any) => - param.path.replace(".body.", ""), - ); - t2.ok( - fields.includes("fake"), - "Rejected because fake is not a valid parameter", - ); - } - - t2.end(); - }); - - test(`${testCase} - ${fRun} - ${cOk}`, async (t2: Test) => { - const parameters = { - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - transactionConfig: { - from: firstHighNetWorthAccount, - to: testEthAccount.address, - value: 10e9, - }, - }; - const res = await apiClient.runTransactionV1( - parameters as RunTransactionRequest, - ); - t2.ok(res, "Transaction executed successfully"); - t2.ok(res.data); - t2.equal(res.status, 200, `Endpoint ${fRun}: response.status === 200 OK`); - - t2.end(); - }); - - test(`${testCase} - ${fRun} - ${cWithoutParams}`, async (t2: Test) => { - try { - const parameters = { - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - }; - await apiClient.runTransactionV1(parameters as RunTransactionRequest); - } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fRun} without required transactionConfig: response.status === 400 OK`, - ); - const fields = e.response.data.map((param: { path: string }) => - param.path.replace(".body.", ""), - ); - t2.ok( - fields.includes("transactionConfig"), - "Rejected because transactionConfig is required", - ); - t2.notOk(fields.includes("timeoutMs"), "timeoutMs is not required"); - } - - t2.end(); - }); - - test(`${testCase} - ${fRun} - ${cInvalidParams}`, async (t2: Test) => { - try { - const parameters = { - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - transactionConfig: { - from: firstHighNetWorthAccount, - to: testEthAccount.address, - value: 10e9, - }, - fake: 4, - }; - await apiClient.runTransactionV1(parameters as RunTransactionRequest); - } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fRun} with fake=4: response.status === 400 OK`, - ); - const fields = e.response.data.map((param: { path: string }) => - param.path.replace(".body.", ""), - ); - t2.ok( - fields.includes("fake"), - "Rejected because fake is not a valid parameter", - ); - } - - t2.end(); - }); - - t.end(); -}); - -test("AFTER " + testCase, async (t: Test) => { - const pruning = pruneDockerAllIfGithubAction({ logLevel }); - await t.doesNotReject(pruning, "Pruning did not throw OK"); - t.end(); -}); diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-deploy-contract-from-json-json-object-endpoints.test.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-deploy-contract-from-json-json-object-endpoints.test.ts deleted file mode 100644 index 73ccffc596c..00000000000 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-deploy-contract-from-json-json-object-endpoints.test.ts +++ /dev/null @@ -1,390 +0,0 @@ -import test, { Test } from "tape-promise/tape"; -import Web3 from "web3"; -import { v4 as uuidV4 } from "uuid"; - -import { - LogLevelDesc, - IListenOptions, - Servers, -} from "@hyperledger/cactus-common"; - -import HelloWorldContractJson from "../../../../solidity/hello-world-contract/HelloWorld.json"; - -import { K_CACTUS_ETHEREUM_TOTAL_TX_COUNT } from "../../../../../main/typescript/prometheus-exporter/metrics"; - -import { - EthContractInvocationType, - PluginLedgerConnectorEthereum, - Web3SigningCredentialType, - DefaultApi as EthereumApi, -} from "../../../../../main/typescript/public-api"; - -import { - QuorumTestLedger, - IQuorumGenesisOptions, - IAccount, - pruneDockerAllIfGithubAction, -} from "@hyperledger/cactus-test-tooling"; -import { PluginRegistry } from "@hyperledger/cactus-core"; - -const testCase = "Ethereum Ledger Connector Plugin"; -import express from "express"; -import bodyParser from "body-parser"; -import http from "http"; -import { AddressInfo } from "net"; -import { Configuration, Constants } from "@hyperledger/cactus-core-api"; -import { Server as SocketIoServer } from "socket.io"; - -const logLevel: LogLevelDesc = "INFO"; - -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 containerImageVersion = "2021-01-08-7a055c3"; // Quorum v2.3.0, Tessera v0.10.0 - - const ledgerOptions = { containerImageVersion }; - const ledger = new QuorumTestLedger(ledgerOptions); - test.onFinish(async () => { - await ledger.stop(); - await ledger.destroy(); - await pruneDockerAllIfGithubAction({ logLevel }); - }); - await ledger.start(); - - const rpcApiHttpHost = await ledger.getRpcApiHttpHost(); - const ethereumGenesisOptions: IQuorumGenesisOptions = await ledger.getGenesisJsObject(); - t.ok(ethereumGenesisOptions); - t.ok(ethereumGenesisOptions.alloc); - - const highNetWorthAccounts: string[] = Object.keys( - ethereumGenesisOptions.alloc, - ).filter((address: string) => { - const anAccount: IAccount = ethereumGenesisOptions.alloc[address]; - const theBalance = parseInt(anAccount.balance, 10); - return theBalance > 10e7; - }); - const [firstHighNetWorthAccount] = highNetWorthAccounts; - - const web3 = new Web3(rpcApiHttpHost); - const testEthAccount = web3.eth.accounts.create(uuidV4()); - - // Instantiate connector with the keychain plugin that already has the - // private key we want to use for one of our tests - const connector: PluginLedgerConnectorEthereum = new PluginLedgerConnectorEthereum( - { - instanceId: uuidV4(), - rpcApiHttpHost, - logLevel, - pluginRegistry: new PluginRegistry(), - }, - ); - - const expressApp = express(); - expressApp.use(bodyParser.json({ limit: "250mb" })); - const server = http.createServer(expressApp); - const listenOptions: IListenOptions = { - hostname: "localhost", - port: 0, - server, - }; - const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; - test.onFinish(async () => await Servers.shutdown(server)); - const { address, port } = addressInfo; - const apiHost = `http://${address}:${port}`; - t.comment( - `Metrics URL: ${apiHost}/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/get-prometheus-exporter-metrics`, - ); - - const apiConfig = new Configuration({ basePath: apiHost }); - const apiClient = new EthereumApi(apiConfig); - - const wsApi = new SocketIoServer(server, { - path: Constants.SocketIoConnectionPathV1, - }); - - await connector.getOrCreateWebServices(); - await connector.registerWebServices(expressApp, wsApi); - - await connector.transact({ - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - transactionConfig: { - from: firstHighNetWorthAccount, - to: testEthAccount.address, - value: 10e9, - }, - }); - - const balance = await web3.eth.getBalance(testEthAccount.address); - t.ok(balance, "Retrieved balance of test account OK"); - t.equals(parseInt(balance, 10), 10e9, "Balance of test account is OK"); - - let contractAddress: string; - - test("deploys contract via .json file", async (t2: Test) => { - const deployOut = await apiClient.deployContractSolBytecodeJsonObjectV1({ - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - gas: 1000000, - contractJSON: HelloWorldContractJson, - }); - t2.ok(deployOut, "deployContract() output is truthy OK"); - t2.ok( - deployOut.data.transactionReceipt, - "deployContract() output.transactionReceipt is truthy OK", - ); - t2.ok( - deployOut.data.transactionReceipt.contractAddress, - "deployContract() output.transactionReceipt.contractAddress is truthy OK", - ); - - contractAddress = deployOut.data.transactionReceipt - .contractAddress as string; - t2.ok( - typeof contractAddress === "string", - "contractAddress typeof string OK", - ); - - const invokeOut = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "sayHello", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - gas: 1000000, - contractJSON: HelloWorldContractJson, - }); - t2.ok(invokeOut.data.callOutput, "sayHello() output is truthy"); - t2.true( - typeof invokeOut.data.callOutput === "string", - "sayHello() output is type of string", - ); - }); - - test("invoke Web3SigningCredentialType.GETHKEYCHAINPASSWORD", async (t2: Test) => { - const newName = `DrCactus${uuidV4()}`; - const txCount = await web3.eth.getTransactionCount( - firstHighNetWorthAccount, - ); - const setNameOut = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: txCount, - contractJSON: HelloWorldContractJson, - }); - t2.ok(setNameOut, "setName() invocation #1 output is truthy OK"); - - try { - const setNameOutInvalid = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 2, - contractJSON: HelloWorldContractJson, - }); - t2.ifError(setNameOutInvalid.data.transactionReceipt); - } catch (error) { - t2.notStrictEqual( - error, - "Nonce too low", - "setName() invocation with invalid nonce", - ); - } - - const getNameOut = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "getName", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - contractJSON: HelloWorldContractJson, - }); - t2.ok( - getNameOut.data.success, - `getName() SEND invocation produced receipt OK`, - ); - - const getNameRes = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "getName", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - contractJSON: HelloWorldContractJson, - }); - t2.equal( - getNameRes.data.callOutput, - newName, - "setName() invocation #2 output is truthy OK", - ); - - t2.end(); - }); - - test("invoke Web3SigningCredentialType.NONE", async (t2: Test) => { - const testEthAccount2 = web3.eth.accounts.create(uuidV4()); - - const { rawTransaction } = await web3.eth.accounts.signTransaction( - { - from: testEthAccount.address, - to: testEthAccount2.address, - value: 10e6, - gas: 1000000, - }, - testEthAccount.privateKey, - ); - - await connector.transact({ - web3SigningCredential: { - type: Web3SigningCredentialType.None, - }, - transactionConfig: { - rawTransaction, - }, - }); - - const balance2 = await web3.eth.getBalance(testEthAccount2.address); - t2.ok(balance2, "Retrieved balance of test account 2 OK"); - t2.equals(parseInt(balance2, 10), 10e6, "Balance of test account2 is OK"); - t2.end(); - }); - - test("invoke Web3SigningCredentialType.PrivateKeyHex", async (t2: Test) => { - const newName = `DrCactus${uuidV4()}`; - const setNameOut = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: 1, - contractJSON: HelloWorldContractJson, - }); - t2.ok(setNameOut, "setName() invocation #1 output is truthy OK"); - - try { - const setNameOutInvalid = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: 1, - contractJSON: HelloWorldContractJson, - }); - t2.ifError(setNameOutInvalid.data.transactionReceipt); - } catch (error) { - t2.notStrictEqual( - error, - "Nonce too low", - "setName() invocation with invalid nonce", - ); - } - // const { callOutput: getNameOut } = - const getNameOut = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "getName", - params: [], - // gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - contractJSON: HelloWorldContractJson, - }); - t2.equal( - getNameOut.data.callOutput, - newName, - `getName() output reflects the update OK`, - ); - - const getNameOut2 = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "getName", - params: [], - //gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - contractJSON: HelloWorldContractJson, - }); - t2.ok(getNameOut2, "getName() invocation #2 output is truthy OK"); - - t2.end(); - }); - - test("get prometheus exporter metrics", async (t2: Test) => { - const res = await apiClient.getPrometheusMetricsV1(); - const promMetricsOutput = - "# HELP " + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - " Total transactions executed\n" + - "# TYPE " + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - " gauge\n" + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - '{type="' + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - '"} 3'; - t2.ok(res); - t2.ok(res.data); - t2.equal(res.status, 200); - t2.true( - res.data.includes(promMetricsOutput), - "Total Transaction Count of 3 recorded as expected. RESULT OK.", - ); - t2.end(); - }); - - t.end(); -}); diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-deploy-contract-from-json-json-object.test.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-deploy-contract-from-json-json-object.test.ts deleted file mode 100644 index 0ceaaeb1075..00000000000 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-deploy-contract-from-json-json-object.test.ts +++ /dev/null @@ -1,381 +0,0 @@ -import test, { Test } from "tape-promise/tape"; -import Web3 from "web3"; -import { v4 as uuidV4 } from "uuid"; - -import { - LogLevelDesc, - IListenOptions, - Servers, -} from "@hyperledger/cactus-common"; - -import HelloWorldContractJson from "../../../../solidity/hello-world-contract/HelloWorld.json"; - -import { K_CACTUS_ETHEREUM_TOTAL_TX_COUNT } from "../../../../../main/typescript/prometheus-exporter/metrics"; - -import { - EthContractInvocationType, - PluginLedgerConnectorEthereum, - Web3SigningCredentialType, - DefaultApi as EthereumApi, -} from "../../../../../main/typescript/public-api"; - -import { - QuorumTestLedger, - IQuorumGenesisOptions, - IAccount, - pruneDockerAllIfGithubAction, -} from "@hyperledger/cactus-test-tooling"; -import { PluginRegistry } from "@hyperledger/cactus-core"; - -const testCase = "Ethereum Ledger Connector Plugin"; -import express from "express"; -import bodyParser from "body-parser"; -import http from "http"; -import { AddressInfo } from "net"; -import { Configuration, Constants } from "@hyperledger/cactus-core-api"; -import { Server as SocketIoServer } from "socket.io"; - -const logLevel: LogLevelDesc = "INFO"; - -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 containerImageVersion = "2021-01-08-7a055c3"; // Quorum v2.3.0, Tessera v0.10.0 - - const ledgerOptions = { containerImageVersion }; - const ledger = new QuorumTestLedger(ledgerOptions); - test.onFinish(async () => { - await ledger.stop(); - await ledger.destroy(); - await pruneDockerAllIfGithubAction({ logLevel }); - }); - await ledger.start(); - - const rpcApiHttpHost = await ledger.getRpcApiHttpHost(); - const ethereumGenesisOptions: IQuorumGenesisOptions = await ledger.getGenesisJsObject(); - t.ok(ethereumGenesisOptions); - t.ok(ethereumGenesisOptions.alloc); - - const highNetWorthAccounts: string[] = Object.keys( - ethereumGenesisOptions.alloc, - ).filter((address: string) => { - const anAccount: IAccount = ethereumGenesisOptions.alloc[address]; - const theBalance = parseInt(anAccount.balance, 10); - return theBalance > 10e7; - }); - const [firstHighNetWorthAccount] = highNetWorthAccounts; - - const web3 = new Web3(rpcApiHttpHost); - const testEthAccount = web3.eth.accounts.create(uuidV4()); - - // Instantiate connector with the keychain plugin that already has the - // private key we want to use for one of our tests - const connector: PluginLedgerConnectorEthereum = new PluginLedgerConnectorEthereum( - { - instanceId: uuidV4(), - rpcApiHttpHost, - logLevel, - pluginRegistry: new PluginRegistry(), - }, - ); - - const expressApp = express(); - expressApp.use(bodyParser.json({ limit: "250mb" })); - const server = http.createServer(expressApp); - const listenOptions: IListenOptions = { - hostname: "localhost", - port: 0, - server, - }; - const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; - test.onFinish(async () => await Servers.shutdown(server)); - const { address, port } = addressInfo; - const apiHost = `http://${address}:${port}`; - t.comment( - `Metrics URL: ${apiHost}/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/get-prometheus-exporter-metrics`, - ); - - const apiConfig = new Configuration({ basePath: apiHost }); - const apiClient = new EthereumApi(apiConfig); - - const wsApi = new SocketIoServer(server, { - path: Constants.SocketIoConnectionPathV1, - }); - - await connector.getOrCreateWebServices(); - await connector.registerWebServices(expressApp, wsApi); - - await connector.transact({ - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - transactionConfig: { - from: firstHighNetWorthAccount, - to: testEthAccount.address, - value: 10e9, - }, - }); - - const balance = await web3.eth.getBalance(testEthAccount.address); - t.ok(balance, "Retrieved balance of test account OK"); - t.equals(parseInt(balance, 10), 10e9, "Balance of test account is OK"); - - let contractAddress: string; - - test("deploys contract via .json file", async (t2: Test) => { - const deployOut = await connector.deployContractJsonObject({ - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - gas: 1000000, - contractJSON: HelloWorldContractJson, - }); - t2.ok(deployOut, "deployContract() output is truthy OK"); - t2.ok( - deployOut.transactionReceipt, - "deployContract() output.transactionReceipt is truthy OK", - ); - t2.ok( - deployOut.transactionReceipt.contractAddress, - "deployContract() output.transactionReceipt.contractAddress is truthy OK", - ); - - contractAddress = deployOut.transactionReceipt.contractAddress as string; - t2.ok( - typeof contractAddress === "string", - "contractAddress typeof string OK", - ); - - const { callOutput: helloMsg } = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "sayHello", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - gas: 1000000, - contractJSON: HelloWorldContractJson, - }); - t2.ok(helloMsg, "sayHello() output is truthy"); - t2.true( - typeof helloMsg === "string", - "sayHello() output is type of string", - ); - }); - - test("invoke Web3SigningCredentialType.GETHKEYCHAINPASSWORD", async (t2: Test) => { - const newName = `DrCactus${uuidV4()}`; - const txCount = await web3.eth.getTransactionCount( - firstHighNetWorthAccount, - ); - const setNameOut = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: txCount, - contractJSON: HelloWorldContractJson, - }); - t2.ok(setNameOut, "setName() invocation #1 output is truthy OK"); - - try { - const setNameOutInvalid = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 2, - contractJSON: HelloWorldContractJson, - }); - t2.ifError(setNameOutInvalid.transactionReceipt); - } catch (error) { - t2.notStrictEqual( - error, - "Nonce too low", - "setName() invocation with invalid nonce", - ); - } - - const getNameOut = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "getName", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - contractJSON: HelloWorldContractJson, - }); - t2.ok(getNameOut.success, `getName() SEND invocation produced receipt OK`); - - const { callOutput: getNameOut2 } = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "getName", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - contractJSON: HelloWorldContractJson, - }); - t2.equal( - getNameOut2, - newName, - "setName() invocation #2 output is truthy OK", - ); - - t2.end(); - }); - - test("invoke Web3SigningCredentialType.NONE", async (t2: Test) => { - const testEthAccount2 = web3.eth.accounts.create(uuidV4()); - - const { rawTransaction } = await web3.eth.accounts.signTransaction( - { - from: testEthAccount.address, - to: testEthAccount2.address, - value: 10e6, - gas: 1000000, - }, - testEthAccount.privateKey, - ); - - await connector.transact({ - web3SigningCredential: { - type: Web3SigningCredentialType.None, - }, - transactionConfig: { - rawTransaction, - }, - }); - - const balance2 = await web3.eth.getBalance(testEthAccount2.address); - t2.ok(balance2, "Retrieved balance of test account 2 OK"); - t2.equals(parseInt(balance2, 10), 10e6, "Balance of test account2 is OK"); - t2.end(); - }); - - test("invoke Web3SigningCredentialType.PrivateKeyHex", async (t2: Test) => { - const newName = `DrCactus${uuidV4()}`; - const setNameOut = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: 1, - contractJSON: HelloWorldContractJson, - }); - t2.ok(setNameOut, "setName() invocation #1 output is truthy OK"); - - try { - const setNameOutInvalid = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: 1, - contractJSON: HelloWorldContractJson, - }); - t2.ifError(setNameOutInvalid.transactionReceipt); - } catch (error) { - t2.notStrictEqual( - error, - "Nonce too low", - "setName() invocation with invalid nonce", - ); - } - const { callOutput: getNameOut } = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - contractJSON: HelloWorldContractJson, - }); - t2.equal(getNameOut, newName, `getName() output reflects the update OK`); - - const getNameOut2 = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - contractJSON: HelloWorldContractJson, - }); - t2.ok(getNameOut2, "getName() invocation #2 output is truthy OK"); - - t2.end(); - }); - - test("get prometheus exporter metrics", async (t2: Test) => { - const res = await apiClient.getPrometheusMetricsV1(); - const promMetricsOutput = - "# HELP " + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - " Total transactions executed\n" + - "# TYPE " + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - " gauge\n" + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - '{type="' + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - '"} 3'; - t2.ok(res); - t2.ok(res.data); - t2.equal(res.status, 200); - t2.true( - res.data.includes(promMetricsOutput), - "Total Transaction Count of 3 recorded as expected. RESULT OK.", - ); - t2.end(); - }); - - t.end(); -}); diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-deploy-contract-from-json.test.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-deploy-contract-from-json.test.ts deleted file mode 100644 index 4e9240498e9..00000000000 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-deploy-contract-from-json.test.ts +++ /dev/null @@ -1,469 +0,0 @@ -import Web3 from "web3"; -import { Account } from "web3-core"; -import { v4 as uuidV4 } from "uuid"; -import "jest-extended"; -import { - LogLevelDesc, - IListenOptions, - Servers, -} from "@hyperledger/cactus-common"; - -import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory"; - -import HelloWorldContractJson from "../../../../solidity/hello-world-contract/HelloWorld.json"; - -import { K_CACTUS_ETHEREUM_TOTAL_TX_COUNT } from "../../../../../main/typescript/prometheus-exporter/metrics"; - -import { - EthContractInvocationType, - PluginLedgerConnectorEthereum, - Web3SigningCredentialCactusKeychainRef, - Web3SigningCredentialType, - DefaultApi as EthereumApi, -} from "../../../../../main/typescript/public-api"; - -import { - QuorumTestLedger, - IQuorumGenesisOptions, - IAccount, - pruneDockerAllIfGithubAction, -} from "@hyperledger/cactus-test-tooling"; -import { PluginRegistry } from "@hyperledger/cactus-core"; -import express from "express"; -import bodyParser from "body-parser"; -import http from "http"; -import { AddressInfo } from "net"; -import { Configuration, Constants } from "@hyperledger/cactus-core-api"; -import { Server as SocketIoServer } from "socket.io"; - -const testCase = "Ethereum Ledger Connector Plugin"; - -describe(testCase, () => { - const logLevel: LogLevelDesc = "INFO"; - const contractName = "HelloWorld"; - const keychainEntryKey = uuidV4(); - let firstHighNetWorthAccount: string, - testEthAccount: Account, - web3: Web3, - addressInfo, - address: string, - port: number, - contractAddress: string, - apiHost, - apiConfig, - ledger: QuorumTestLedger, - apiClient: EthereumApi, - connector: PluginLedgerConnectorEthereum, - rpcApiHttpHost: string, - keychainPlugin: PluginKeychainMemory; - const expressApp = express(); - expressApp.use(bodyParser.json({ limit: "250mb" })); - const server = http.createServer(expressApp); - - const wsApi = new SocketIoServer(server, { - path: Constants.SocketIoConnectionPathV1, - }); - - beforeAll(async () => { - const pruning = pruneDockerAllIfGithubAction({ logLevel }); - await expect(pruning).resolves.toBeTruthy(); - }); - - beforeAll(async () => { - const containerImageVersion = "2021-01-08-7a055c3"; // Quorum v2.3.0, Tessera v0.10.0 - const containerImageName = "hyperledger/cactus-quorum-all-in-one"; - const ledgerOptions = { containerImageName, containerImageVersion }; - ledger = new QuorumTestLedger(ledgerOptions); - await ledger.start(); - - const ethereumGenesisOptions: IQuorumGenesisOptions = await ledger.getGenesisJsObject(); - - expect(ethereumGenesisOptions).toBeTruthy(); - expect(ethereumGenesisOptions.alloc).toBeTruthy(); - - const highNetWorthAccounts: string[] = Object.keys( - ethereumGenesisOptions.alloc, - ).filter((address: string) => { - const anAccount: IAccount = ethereumGenesisOptions.alloc[address]; - const theBalance = parseInt(anAccount.balance, 10); - return theBalance > 10e7; - }); - [firstHighNetWorthAccount] = highNetWorthAccounts; - }); - - afterAll(async () => { - await ledger.stop(); - await ledger.destroy(); - }); - - afterAll(async () => await Servers.shutdown(server)); - - afterAll(async () => { - const pruning = pruneDockerAllIfGithubAction({ logLevel }); - await expect(pruning).resolves.toBeTruthy(); - }); - beforeAll(async () => { - await ledger.start(); - - const listenOptions: IListenOptions = { - hostname: "0.0.0.0", - port: 0, - server, - }; - addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; - ({ address, port } = addressInfo); - apiHost = `http://${address}:${port}`; - apiConfig = new Configuration({ basePath: apiHost }); - apiClient = new EthereumApi(apiConfig); - rpcApiHttpHost = await ledger.getRpcApiHttpHost(); - web3 = new Web3(rpcApiHttpHost); - testEthAccount = web3.eth.accounts.create(uuidV4()); - - const keychainEntryValue = testEthAccount.privateKey; - 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, - JSON.stringify(HelloWorldContractJson), - ); - connector = new PluginLedgerConnectorEthereum({ - instanceId: uuidV4(), - rpcApiHttpHost, - logLevel, - pluginRegistry: new PluginRegistry({ plugins: [keychainPlugin] }), - }); - }); - - test(testCase, 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); - - await connector.transact({ - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - transactionConfig: { - from: firstHighNetWorthAccount, - to: testEthAccount.address, - value: 10e9, - }, - }); - - const balance = await web3.eth.getBalance(testEthAccount.address); - expect(balance).toBeTruthy(); - expect(parseInt(balance, 10)).toBe(10e9); - }); - - test("deploys contract via .json file", async () => { - const deployOut = await connector.deployContract({ - contractName: HelloWorldContractJson.contractName, - keychainId: keychainPlugin.getKeychainId(), - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - gas: 1000000, - }); - expect(deployOut).toBeTruthy(); - expect(deployOut.transactionReceipt).toBeTruthy(); - expect(deployOut.transactionReceipt.contractAddress).toBeTruthy(); - - contractAddress = deployOut.transactionReceipt.contractAddress as string; - expect(typeof contractAddress).toBe("string"); - const { callOutput: helloMsg } = await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Call, - methodName: "sayHello", - keychainId: keychainPlugin.getKeychainId(), - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - gas: 1000000, - }); - expect(helloMsg).toBeTruthy(); - expect(typeof helloMsg).toBe("string"); - }); - - test("invoke Web3SigningCredentialType.GETHKEYCHAINPASSWORD", async () => { - const newName = `DrCactus${uuidV4()}`; - const setNameOut = await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - keychainId: keychainPlugin.getKeychainId(), - params: [newName], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 2, - }); - expect(setNameOut).toBeTruthy(); - - try { - await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - keychainId: keychainPlugin.getKeychainId(), - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 2, - }); - fail("Expected getContractInfoKeychain call to fail but it succeeded."); - } catch (error) { - expect(error).not.toEqual("Nonce too low"); - } - - const getNameOut = await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Send, - methodName: "getName", - keychainId: keychainPlugin.getKeychainId(), - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - }); - expect(getNameOut.success).toBeTruthy(); - - const { callOutput: getNameOut2 } = await connector.getContractInfoKeychain( - { - contractName, - invocationType: EthContractInvocationType.Call, - methodName: "getName", - keychainId: keychainPlugin.getKeychainId(), - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - }, - ); - expect(getNameOut2).toBe(newName); - }); - - test("invoke Web3SigningCredentialType.NONE", async () => { - const testEthAccount2 = web3.eth.accounts.create(uuidV4()); - - const { rawTransaction } = await web3.eth.accounts.signTransaction( - { - from: testEthAccount.address, - to: testEthAccount2.address, - value: 10e6, - gas: 1000000, - }, - testEthAccount.privateKey, - ); - - await connector.transact({ - web3SigningCredential: { - type: Web3SigningCredentialType.None, - }, - transactionConfig: { - rawTransaction, - }, - }); - - const balance2 = await web3.eth.getBalance(testEthAccount2.address); - expect(balance2).toBeTruthy(); - expect(parseInt(balance2, 10)).toBe(10e6); - }); - - test("invoke Web3SigningCredentialType.PrivateKeyHex", async () => { - const newName = `DrCactus${uuidV4()}`; - const setNameOut = await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - keychainId: keychainPlugin.getKeychainId(), - params: [newName], - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: 1, - }); - expect(setNameOut).toBeTruthy(); - - try { - await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - keychainId: keychainPlugin.getKeychainId(), - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: 1, - }); - fail("Expected getContractInfoKeychain call to fail but it succeeded."); - } catch (error) { - expect(error).not.toEqual("Nonce too low"); - } - - const { callOutput: getNameOut } = await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Call, - methodName: "getName", - keychainId: keychainPlugin.getKeychainId(), - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - }); - expect(getNameOut).toBe(newName); - - const getNameOut2 = await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Send, - methodName: "getName", - keychainId: keychainPlugin.getKeychainId(), - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - }); - expect(getNameOut2).toBeTruthy(); - }); - - test("invoke Web3SigningCredentialType.CactusKeychainRef", async () => { - const newName = `DrCactus${uuidV4()}`; - - const web3SigningCredential: Web3SigningCredentialCactusKeychainRef = { - ethAccount: testEthAccount.address, - keychainEntryKey, - keychainId: keychainPlugin.getKeychainId(), - type: Web3SigningCredentialType.CactusKeychainRef, - }; - - const setNameOut = await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - keychainId: keychainPlugin.getKeychainId(), - params: [newName], - gas: 1000000, - web3SigningCredential, - nonce: 3, - }); - expect(setNameOut).toBeTruthy(); - - try { - await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - keychainId: keychainPlugin.getKeychainId(), - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 3, - }); - fail("Expected getContractInfoKeychain call to fail but it succeeded."); - } catch (error) { - expect(error).not.toEqual("Nonce too low"); - } - - const { callOutput: getNameOut } = await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Call, - methodName: "getName", - keychainId: keychainPlugin.getKeychainId(), - params: [], - gas: 1000000, - web3SigningCredential, - }); - expect(getNameOut).toContain(newName); - - const getNameOut2 = await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Send, - methodName: "getName", - keychainId: keychainPlugin.getKeychainId(), - params: [], - gas: 1000000, - web3SigningCredential, - }); - expect(getNameOut2).toBeTruthy(); - }); - - test("get prometheus exporter metrics", async () => { - const res = await apiClient.getPrometheusMetricsV1(); - const promMetricsOutput = - "# HELP " + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - " Total transactions executed\n" + - "# TYPE " + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - " gauge\n" + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - '{type="' + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - '"} 5'; - expect(res); - expect(res.data); - expect(res.status).toEqual(200); - expect(res.data).toContain(promMetricsOutput); - }); - - test("deploys contract via .json file with constructorArgs", async () => { - const deployOut = await connector.deployContract({ - contractName: HelloWorldContractJson.contractName, - contractJSON: HelloWorldContractJson, - keychainId: keychainPlugin.getKeychainId(), - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - gas: 1000000, - constructorArgs: ["Test Arg"], - }); - expect(deployOut).toBeTruthy(); - expect(deployOut.transactionReceipt).toBeTruthy(); - expect(deployOut.transactionReceipt.contractAddress).toBeTruthy(); - - contractAddress = deployOut.transactionReceipt.contractAddress as string; - expect(contractAddress).toBeString(); - }); -}); diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-invoke-contract-json-object-endpoints.test.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-invoke-contract-json-object-endpoints.test.ts deleted file mode 100644 index 94a92019242..00000000000 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-invoke-contract-json-object-endpoints.test.ts +++ /dev/null @@ -1,357 +0,0 @@ -import test, { Test } from "tape"; -import Web3 from "web3"; -import { v4 as uuidV4 } from "uuid"; - -import { - LogLevelDesc, - IListenOptions, - Servers, -} from "@hyperledger/cactus-common"; - -import HelloWorldContractJson from "../../../../solidity/hello-world-contract/HelloWorld.json"; - -import { - EthContractInvocationType, - PluginLedgerConnectorEthereum, - Web3SigningCredentialType, - DefaultApi as EthereumApi, -} from "../../../../../main/typescript/public-api"; - -import { - QuorumTestLedger, - IQuorumGenesisOptions, - IAccount, -} from "@hyperledger/cactus-test-tooling"; -import { PluginRegistry } from "@hyperledger/cactus-core"; - -import express from "express"; -import bodyParser from "body-parser"; -import http from "http"; -import { AddressInfo } from "net"; -import { Configuration, Constants } from "@hyperledger/cactus-core-api"; -import { Server as SocketIoServer } from "socket.io"; - -const logLevel: LogLevelDesc = "INFO"; - -test("Ethereum Ledger Connector Plugin", async (t: Test) => { - const containerImageVersion = "2021-01-08-7a055c3"; // Quorum v2.3.0, Tessera v0.10.0 - - const ledgerOptions = { containerImageVersion }; - const ledger = new QuorumTestLedger(ledgerOptions); - test.onFinish(async () => { - await ledger.stop(); - await ledger.destroy(); - }); - await ledger.start(); - - const rpcApiHttpHost = await ledger.getRpcApiHttpHost(); - const ethereumGenesisOptions: IQuorumGenesisOptions = await ledger.getGenesisJsObject(); - t.ok(ethereumGenesisOptions); - t.ok(ethereumGenesisOptions.alloc); - - const highNetWorthAccounts: string[] = Object.keys( - ethereumGenesisOptions.alloc, - ).filter((address: string) => { - const anAccount: IAccount = ethereumGenesisOptions.alloc[address]; - const theBalance = parseInt(anAccount.balance, 10); - return theBalance > 10e7; - }); - const [firstHighNetWorthAccount] = highNetWorthAccounts; - - const web3 = new Web3(rpcApiHttpHost); - const testEthAccount = web3.eth.accounts.create(uuidV4()); - const connector: PluginLedgerConnectorEthereum = new PluginLedgerConnectorEthereum( - { - instanceId: uuidV4(), - rpcApiHttpHost, - logLevel, - pluginRegistry: new PluginRegistry(), - }, - ); - - const expressApp = express(); - expressApp.use(bodyParser.json({ limit: "250mb" })); - const server = http.createServer(expressApp); - const listenOptions: IListenOptions = { - hostname: "localhost", - port: 0, - server, - }; - const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; - test.onFinish(async () => await Servers.shutdown(server)); - const { address, port } = addressInfo; - const apiHost = `http://${address}:${port}`; - t.comment( - `Metrics URL: ${apiHost}/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/get-prometheus-exporter-metrics`, - ); - - const apiConfig = new Configuration({ basePath: apiHost }); - const apiClient = new EthereumApi(apiConfig); - - const wsApi = new SocketIoServer(server, { - path: Constants.SocketIoConnectionPathV1, - }); - - await connector.getOrCreateWebServices(); - await connector.registerWebServices(expressApp, wsApi); - - await connector.transact({ - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - transactionConfig: { - from: firstHighNetWorthAccount, - to: testEthAccount.address, - value: 10e9, - }, - }); - - const balance = await web3.eth.getBalance(testEthAccount.address); - t.ok(balance, "Retrieved balance of test account OK"); - t.equals(parseInt(balance, 10), 10e9, "Balance of test account is OK"); - - let contractAddress: string; - - test("deploys contract via .json file", async (t2: Test) => { - const deployOut = await apiClient.deployContractSolBytecodeJsonObjectV1({ - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - gas: 1000000, - contractJSON: HelloWorldContractJson, - }); - t2.ok(deployOut, "deployContract() output is truthy OK"); - t2.ok( - deployOut.data.transactionReceipt, - "deployContract() output.transactionReceipt is truthy OK", - ); - t2.ok( - deployOut.data.transactionReceipt.contractAddress, - "deployContract() output.transactionReceipt.contractAddress is truthy OK", - ); - - contractAddress = deployOut.data.transactionReceipt - .contractAddress as string; - t2.ok( - typeof contractAddress === "string", - "contractAddress typeof string OK", - ); - - const helloMsgRes = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "sayHello", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - contractJSON: HelloWorldContractJson, - }); - t2.ok(helloMsgRes.data.callOutput, "sayHello() output is truthy"); - t2.true( - typeof helloMsgRes.data.callOutput === "string", - "sayHello() output is type of string", - ); - }); - - test("invoke Web3SigningCredentialType.GETHKEYCHAINPASSWORD", async (t2: Test) => { - const newName = `DrCactus${uuidV4()}`; - const txCount = await web3.eth.getTransactionCount( - firstHighNetWorthAccount, - ); - const setNameOut = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: txCount, - contractJSON: HelloWorldContractJson, - }); - t2.ok(setNameOut, "setName() invocation #1 output is truthy OK"); - - try { - const setNameOutInvalid = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 2, - contractJSON: HelloWorldContractJson, - }); - t2.ifError(setNameOutInvalid.data.transactionReceipt); - } catch (error) { - t2.notStrictEqual( - error, - "Nonce too low", - "setName() invocation with invalid nonce", - ); - } - - const getNameOut = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - contractJSON: HelloWorldContractJson, - }); - t2.ok( - getNameOut.data.success, - `getName() SEND invocation produced receipt OK`, - ); - - const getNameOut2 = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - contractJSON: HelloWorldContractJson, - }); - t2.equal( - getNameOut2.data.callOutput, - newName, - "setName() invocation #2 output is truthy OK", - ); - - t2.end(); - }); - - test("invoke Web3SigningCredentialType.NONE", async (t2: Test) => { - const testEthAccount2 = web3.eth.accounts.create(uuidV4()); - - const { rawTransaction } = await web3.eth.accounts.signTransaction( - { - from: testEthAccount.address, - to: testEthAccount2.address, - value: 10e6, - gas: 1000000, - }, - testEthAccount.privateKey, - ); - - await connector.transact({ - web3SigningCredential: { - type: Web3SigningCredentialType.None, - }, - transactionConfig: { - rawTransaction, - }, - }); - - const balance2 = await web3.eth.getBalance(testEthAccount2.address); - t2.ok(balance2, "Retrieved balance of test account 2 OK"); - t2.equals(parseInt(balance2, 10), 10e6, "Balance of test account2 is OK"); - t2.end(); - }); - - test("invoke Web3SigningCredentialType.PrivateKeyHex", async (t2: Test) => { - const newName = `DrCactus${uuidV4()}`; - const setNameOut = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: 1, - contractJSON: HelloWorldContractJson, - }); - t2.ok(setNameOut, "setName() invocation #1 output is truthy OK"); - - try { - const setNameOutInvalid = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: 1, - contractJSON: HelloWorldContractJson, - }); - t2.ifError(setNameOutInvalid.data.transactionReceipt); - } catch (error) { - t2.notStrictEqual( - error, - "Nonce too low", - "setName() invocation with invalid nonce", - ); - } - - const getNameOut = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - contractJSON: HelloWorldContractJson, - }); - t2.equal( - getNameOut.data.callOutput, - newName, - `getName() output reflects the update OK`, - ); - - const getNameOut2 = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - contractJSON: HelloWorldContractJson, - }); - t2.ok(getNameOut2.data, "getName() invocation #2 output is truthy OK"); - - t2.end(); - }); - - t.end(); -}); diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-invoke-contract-json-object.test.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-invoke-contract-json-object.test.ts deleted file mode 100644 index d1de4ee3d4a..00000000000 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-invoke-contract-json-object.test.ts +++ /dev/null @@ -1,310 +0,0 @@ -import test, { Test } from "tape"; -import Web3 from "web3"; -import { v4 as uuidV4 } from "uuid"; - -import { LogLevelDesc } from "@hyperledger/cactus-common"; - -import HelloWorldContractJson from "../../../../solidity/hello-world-contract/HelloWorld.json"; - -import { - EthContractInvocationType, - PluginLedgerConnectorEthereum, - Web3SigningCredentialType, -} from "../../../../../main/typescript/public-api"; - -import { - QuorumTestLedger, - IQuorumGenesisOptions, - IAccount, -} from "@hyperledger/cactus-test-tooling"; -import { PluginRegistry } from "@hyperledger/cactus-core"; - -const logLevel: LogLevelDesc = "INFO"; - -test("Ethereum Ledger Connector Plugin", async (t: Test) => { - const containerImageVersion = "2021-01-08-7a055c3"; // Quorum v2.3.0, Tessera v0.10.0 - - const ledgerOptions = { containerImageVersion }; - const ledger = new QuorumTestLedger(ledgerOptions); - test.onFinish(async () => { - await ledger.stop(); - await ledger.destroy(); - }); - await ledger.start(); - - const rpcApiHttpHost = await ledger.getRpcApiHttpHost(); - const ethereumGenesisOptions: IQuorumGenesisOptions = await ledger.getGenesisJsObject(); - t.ok(ethereumGenesisOptions); - t.ok(ethereumGenesisOptions.alloc); - - const highNetWorthAccounts: string[] = Object.keys( - ethereumGenesisOptions.alloc, - ).filter((address: string) => { - const anAccount: IAccount = ethereumGenesisOptions.alloc[address]; - const theBalance = parseInt(anAccount.balance, 10); - return theBalance > 10e7; - }); - const [firstHighNetWorthAccount] = highNetWorthAccounts; - - const web3 = new Web3(rpcApiHttpHost); - const testEthAccount = web3.eth.accounts.create(uuidV4()); - const connector: PluginLedgerConnectorEthereum = new PluginLedgerConnectorEthereum( - { - instanceId: uuidV4(), - rpcApiHttpHost, - logLevel, - pluginRegistry: new PluginRegistry(), - }, - ); - - await connector.transact({ - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - transactionConfig: { - from: firstHighNetWorthAccount, - to: testEthAccount.address, - value: 10e9, - }, - }); - - const balance = await web3.eth.getBalance(testEthAccount.address); - t.ok(balance, "Retrieved balance of test account OK"); - t.equals(parseInt(balance, 10), 10e9, "Balance of test account is OK"); - - let contractAddress: string; - - test("deploys contract via .json file", async (t2: Test) => { - const deployOut = await connector.deployContractJsonObject({ - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - gas: 1000000, - contractJSON: HelloWorldContractJson, - }); - t2.ok(deployOut, "deployContract() output is truthy OK"); - t2.ok( - deployOut.transactionReceipt, - "deployContract() output.transactionReceipt is truthy OK", - ); - t2.ok( - deployOut.transactionReceipt.contractAddress, - "deployContract() output.transactionReceipt.contractAddress is truthy OK", - ); - - contractAddress = deployOut.transactionReceipt.contractAddress as string; - t2.ok( - typeof contractAddress === "string", - "contractAddress typeof string OK", - ); - - const { callOutput: helloMsg } = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "sayHello", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - contractJSON: HelloWorldContractJson, - }); - t2.ok(helloMsg, "sayHello() output is truthy"); - t2.true( - typeof helloMsg === "string", - "sayHello() output is type of string", - ); - }); - - test("invoke Web3SigningCredentialType.GETHKEYCHAINPASSWORD", async (t2: Test) => { - const newName = `DrCactus${uuidV4()}`; - const txCount = await web3.eth.getTransactionCount( - firstHighNetWorthAccount, - ); - const setNameOut = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: txCount, - contractJSON: HelloWorldContractJson, - }); - t2.ok(setNameOut, "setName() invocation #1 output is truthy OK"); - - try { - const setNameOutInvalid = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 2, - contractJSON: HelloWorldContractJson, - }); - t2.ifError(setNameOutInvalid.transactionReceipt); - } catch (error) { - t2.notStrictEqual( - error, - "Nonce too low", - "setName() invocation with invalid nonce", - ); - } - - const getNameOut = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - contractJSON: HelloWorldContractJson, - }); - t2.ok(getNameOut.success, `getName() SEND invocation produced receipt OK`); - - const { callOutput: getNameOut2 } = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - contractJSON: HelloWorldContractJson, - }); - t2.equal( - getNameOut2, - newName, - "setName() invocation #2 output is truthy OK", - ); - - t2.end(); - }); - - test("invoke Web3SigningCredentialType.NONE", async (t2: Test) => { - const testEthAccount2 = web3.eth.accounts.create(uuidV4()); - - const { rawTransaction } = await web3.eth.accounts.signTransaction( - { - from: testEthAccount.address, - to: testEthAccount2.address, - value: 10e6, - gas: 1000000, - }, - testEthAccount.privateKey, - ); - - await connector.transact({ - web3SigningCredential: { - type: Web3SigningCredentialType.None, - }, - transactionConfig: { - rawTransaction, - }, - }); - - const balance2 = await web3.eth.getBalance(testEthAccount2.address); - t2.ok(balance2, "Retrieved balance of test account 2 OK"); - t2.equals(parseInt(balance2, 10), 10e6, "Balance of test account2 is OK"); - t2.end(); - }); - - test("invoke Web3SigningCredentialType.PrivateKeyHex", async (t2: Test) => { - const newName = `DrCactus${uuidV4()}`; - const setNameOut = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: 1, - contractJSON: HelloWorldContractJson, - }); - t2.ok(setNameOut, "setName() invocation #1 output is truthy OK"); - - try { - const setNameOutInvalid = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: 1, - contractJSON: HelloWorldContractJson, - }); - t2.ifError(setNameOutInvalid.transactionReceipt); - } catch (error) { - t2.notStrictEqual( - error, - "Nonce too low", - "setName() invocation with invalid nonce", - ); - } - const { callOutput: getNameOut } = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - contractJSON: HelloWorldContractJson, - }); - t2.equal(getNameOut, newName, `getName() output reflects the update OK`); - - const getNameOut2 = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - contractJSON: HelloWorldContractJson, - }); - t2.ok(getNameOut2, "getName() invocation #2 output is truthy OK"); - - t2.end(); - }); - - t.end(); -}); diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-invoke-contract.test.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-invoke-contract.test.ts deleted file mode 100644 index 3f1877786bf..00000000000 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v2.3.0-invoke-contract.test.ts +++ /dev/null @@ -1,380 +0,0 @@ -import Web3 from "web3"; -import "jest-extended"; -import { Account } from "web3-core"; -import { v4 as uuidV4 } from "uuid"; - -import { LogLevelDesc } from "@hyperledger/cactus-common"; - -import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory"; - -import HelloWorldContractJson from "../../../../solidity/hello-world-contract/HelloWorld.json"; - -import { - EthContractInvocationType, - PluginLedgerConnectorEthereum, - Web3SigningCredentialCactusKeychainRef, - Web3SigningCredentialType, -} from "../../../../../main/typescript/public-api"; - -import { - QuorumTestLedger, - IQuorumGenesisOptions, - IAccount, -} from "@hyperledger/cactus-test-tooling"; -import { PluginRegistry } from "@hyperledger/cactus-core"; - -const logLevel: LogLevelDesc = "INFO"; -const contractName = "HelloWorld"; -const testcase = ""; - -describe(testcase, () => { - const containerImageVersion = "2021-01-08-7a055c3"; // Quorum v2.3.0, Tessera v0.10.0 - - const ledgerOptions = { containerImageVersion }; - const ledger = new QuorumTestLedger(ledgerOptions); - const keychainEntryKey = uuidV4(); - - let contractAddress: string, - connector: PluginLedgerConnectorEthereum, - rpcApiHttpHost: string, - web3: Web3, - keychainPlugin: PluginKeychainMemory, - testEthAccount: Account, - ethereumGenesisOptions: IQuorumGenesisOptions, - highNetWorthAccounts: string[], - firstHighNetWorthAccount: string; - - afterAll(async () => { - await ledger.stop(); - await ledger.destroy(); - }); - - beforeAll(async () => { - await ledger.start(); - rpcApiHttpHost = await ledger.getRpcApiHttpHost(); - web3 = new Web3(rpcApiHttpHost); - testEthAccount = web3.eth.accounts.create(uuidV4()); - - const keychainEntryValue = testEthAccount.privateKey; - 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, - }); - ethereumGenesisOptions = await ledger.getGenesisJsObject(); - highNetWorthAccounts = Object.keys(ethereumGenesisOptions.alloc).filter( - (address: string) => { - const anAccount: IAccount = ethereumGenesisOptions.alloc[address]; - const theBalance = parseInt(anAccount.balance, 10); - return theBalance > 10e7; - }, - ); - [firstHighNetWorthAccount] = highNetWorthAccounts; - - connector = new PluginLedgerConnectorEthereum({ - instanceId: uuidV4(), - rpcApiHttpHost, - logLevel, - pluginRegistry: new PluginRegistry({ plugins: [keychainPlugin] }), - }); - }); - - test("Ethereum Ledger Connector Plugin", async () => { - expect(ethereumGenesisOptions).toBeTruthy(); - expect(ethereumGenesisOptions.alloc).toBeTruthy(); - - keychainPlugin.set( - HelloWorldContractJson.contractName, - JSON.stringify(HelloWorldContractJson), - ); - // Instantiate connector with the keychain plugin that already has the - // private key we want to use for one of our tests - - await connector.transact({ - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - transactionConfig: { - from: firstHighNetWorthAccount, - to: testEthAccount.address, - value: 10e9, - }, - }); - - const balance = await web3.eth.getBalance(testEthAccount.address); - expect(balance).toBeTruthy(); - expect(parseInt(balance, 10)).toEqual(10e9); - }); - - test("deploys contract via .json file", async () => { - const deployOut = await connector.deployContract({ - keychainId: keychainPlugin.getKeychainId(), - contractName: HelloWorldContractJson.contractName, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - gas: 1000000, - }); - expect(deployOut).toBeTruthy(); - expect(deployOut.transactionReceipt).toBeTruthy(); - expect(deployOut.transactionReceipt.contractAddress).toBeTruthy(); - - contractAddress = deployOut.transactionReceipt.contractAddress as string; - expect(typeof contractAddress).toBeString(); - - const { callOutput: helloMsg } = await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Call, - methodName: "sayHello", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - }); - expect(helloMsg).toBeTruthy(); - expect(typeof helloMsg).toBeString(); - }); - - test("invoke Web3SigningCredentialType.GETHKEYCHAINPASSWORD", async () => { - const newName = `DrCactus${uuidV4()}`; - const setNameOut = await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 2, - }); - expect(setNameOut).toBeTruthy(); - - try { - await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 2, - }); - fail( - "PluginLedgerConnectorEthereum.invokeContract failed to throw error", - ); - } catch (error) { - expect(error).not.toBe("Nonce too low"); - } - - const getNameOut = await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Send, - methodName: "getName", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - }); - expect(getNameOut.success).toBeTruthy(); - - const { callOutput: getNameOut2 } = await connector.getContractInfoKeychain( - { - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Call, - methodName: "getName", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - }, - ); - expect(getNameOut2).toEqual(newName); - }); - - test("invoke Web3SigningCredentialType.NONE", async () => { - const testEthAccount2 = web3.eth.accounts.create(uuidV4()); - - const { rawTransaction } = await web3.eth.accounts.signTransaction( - { - from: testEthAccount.address, - to: testEthAccount2.address, - value: 10e6, - gas: 1000000, - }, - testEthAccount.privateKey, - ); - - await connector.transact({ - web3SigningCredential: { - type: Web3SigningCredentialType.None, - }, - transactionConfig: { - rawTransaction, - }, - }); - - const balance2 = await web3.eth.getBalance(testEthAccount2.address); - expect(balance2).toBeTruthy(); - expect(parseInt(balance2, 10)).toEqual(10e6); - }); - - test("invoke Web3SigningCredentialType.PrivateKeyHex", async () => { - const newName = `DrCactus${uuidV4()}`; - const setNameOut = await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: 1, - }); - expect(setNameOut).toBeTruthy(); - - try { - await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: 1, - }); - fail( - "PluginLedgerConnectorEthereum.invokeContract failed to throw error", - ); - } catch (error) { - expect(error).not.toBe("Nonce too low"); - } - const { callOutput: getNameOut } = await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Call, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - }); - expect(getNameOut).toEqual(newName); - - const getNameOut2 = await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Send, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - }); - expect(getNameOut2).toBeTruthy(); - }); - - test("invoke Web3SigningCredentialType.CactusKeychainRef", async () => { - const newName = `DrCactus${uuidV4()}`; - - const web3SigningCredential: Web3SigningCredentialCactusKeychainRef = { - ethAccount: testEthAccount.address, - keychainEntryKey, - keychainId: keychainPlugin.getKeychainId(), - type: Web3SigningCredentialType.CactusKeychainRef, - }; - - const setNameOut = await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential, - nonce: 3, - }); - expect(setNameOut).toBeTruthy(); - - try { - await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 3, - }); - fail( - "PluginLedgerConnectorEthereum.invokeContract failed to throw error", - ); - } catch (error) { - expect(error).not.toBe("Nonce too low"); - } - const { callOutput: getNameOut } = await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Call, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential, - }); - expect(getNameOut).toEqual(newName); - - const getNameOut2 = await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Send, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential, - }); - expect(getNameOut2).toBeTruthy(); - }); -}); diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-deploy-contract-from-json-json-object-endpoints.test.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-deploy-contract-from-json-json-object-endpoints.test.ts deleted file mode 100644 index 9d72d756fbb..00000000000 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-deploy-contract-from-json-json-object-endpoints.test.ts +++ /dev/null @@ -1,386 +0,0 @@ -import test, { Test } from "tape-promise/tape"; -import Web3 from "web3"; -import { v4 as uuidV4 } from "uuid"; - -import { - LogLevelDesc, - IListenOptions, - Servers, -} from "@hyperledger/cactus-common"; - -import HelloWorldContractJson from "../../../../solidity/hello-world-contract/HelloWorld.json"; - -import { K_CACTUS_ETHEREUM_TOTAL_TX_COUNT } from "../../../../../main/typescript/prometheus-exporter/metrics"; - -import { - EthContractInvocationType, - PluginLedgerConnectorEthereum, - Web3SigningCredentialType, - DefaultApi as EthereumApi, -} from "../../../../../main/typescript/public-api"; - -import { - QuorumTestLedger, - IQuorumGenesisOptions, - IAccount, - pruneDockerAllIfGithubAction, -} from "@hyperledger/cactus-test-tooling"; - -const testCase = "Ethereum Ledger Connector Plugin"; -import express from "express"; -import bodyParser from "body-parser"; -import http from "http"; -import { AddressInfo } from "net"; -import { Configuration, Constants } from "@hyperledger/cactus-core-api"; -import { PluginRegistry } from "@hyperledger/cactus-core"; -import { Server as SocketIoServer } from "socket.io"; - -const logLevel: LogLevelDesc = "INFO"; - -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 containerImageVersion = "2021-05-03-quorum-v21.4.1"; - const ledgerOptions = { containerImageVersion }; - const ledger = new QuorumTestLedger(ledgerOptions); - test.onFinish(async () => { - await ledger.stop(); - await ledger.destroy(); - await pruneDockerAllIfGithubAction({ logLevel }); - }); - await ledger.start(); - - const rpcApiHttpHost = await ledger.getRpcApiHttpHost(); - const ethereumGenesisOptions: IQuorumGenesisOptions = await ledger.getGenesisJsObject(); - t.ok(ethereumGenesisOptions); - t.ok(ethereumGenesisOptions.alloc); - - const highNetWorthAccounts: string[] = Object.keys( - ethereumGenesisOptions.alloc, - ).filter((address: string) => { - const anAccount: IAccount = ethereumGenesisOptions.alloc[address]; - const theBalance = parseInt(anAccount.balance, 10); - return theBalance > 10e7; - }); - const [firstHighNetWorthAccount] = highNetWorthAccounts; - - const web3 = new Web3(rpcApiHttpHost); - const testEthAccount = web3.eth.accounts.create(uuidV4()); - - const connector: PluginLedgerConnectorEthereum = new PluginLedgerConnectorEthereum( - { - instanceId: uuidV4(), - rpcApiHttpHost, - logLevel, - pluginRegistry: new PluginRegistry(), - }, - ); - - const expressApp = express(); - expressApp.use(bodyParser.json({ limit: "250mb" })); - const server = http.createServer(expressApp); - const listenOptions: IListenOptions = { - hostname: "localhost", - port: 0, - server, - }; - const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; - test.onFinish(async () => await Servers.shutdown(server)); - const { address, port } = addressInfo; - const apiHost = `http://${address}:${port}`; - t.comment( - `Metrics URL: ${apiHost}/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/get-prometheus-exporter-metrics`, - ); - - const apiConfig = new Configuration({ basePath: apiHost }); - const apiClient = new EthereumApi(apiConfig); - - const wsApi = new SocketIoServer(server, { - path: Constants.SocketIoConnectionPathV1, - }); - - await connector.getOrCreateWebServices(); - await connector.registerWebServices(expressApp, wsApi); - - await connector.transact({ - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - transactionConfig: { - from: firstHighNetWorthAccount, - to: testEthAccount.address, - value: 10e9, - }, - }); - - const balance = await web3.eth.getBalance(testEthAccount.address); - t.ok(balance, "Retrieved balance of test account OK"); - t.equals(parseInt(balance, 10), 10e9, "Balance of test account is OK"); - - let contractAddress: string; - - test("deploys contract via .json file", async (t2: Test) => { - const deployOut = await apiClient.deployContractSolBytecodeJsonObjectV1({ - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - gas: 1000000, - contractJSON: HelloWorldContractJson, - }); - t2.ok(deployOut, "deployContract() output is truthy OK"); - t2.ok( - deployOut.data.transactionReceipt, - "deployContract() output.transactionReceipt is truthy OK", - ); - t2.ok( - deployOut.data.transactionReceipt.contractAddress, - "deployContract() output.transactionReceipt.contractAddress is truthy OK", - ); - - contractAddress = deployOut.data.transactionReceipt - .contractAddress as string; - t2.ok( - typeof contractAddress === "string", - "contractAddress typeof string OK", - ); - - const helloMsg = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "sayHello", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - contractJSON: HelloWorldContractJson, - }); - t2.ok(helloMsg.data.callOutput, "sayHello() output is truthy"); - t2.true( - typeof helloMsg.data.callOutput === "string", - "sayHello() output is type of string", - ); - }); - - test("invoke Web3SigningCredentialType.GETHKEYCHAINPASSWORD", async (t2: Test) => { - const newName = `DrCactus${uuidV4()}`; - const txCount = await web3.eth.getTransactionCount( - firstHighNetWorthAccount, - ); - const setNameOut = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: txCount, - contractJSON: HelloWorldContractJson, - }); - t2.ok(setNameOut.data, "setName() invocation #1 output is truthy OK"); - - try { - const setNameOutInvalid = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 2, - contractJSON: HelloWorldContractJson, - }); - t2.ifError(setNameOutInvalid.data.transactionReceipt); - } catch (error) { - t2.notStrictEqual( - error, - "Nonce too low", - "setName() invocation with invalid nonce", - ); - } - - const getNameOut = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "getName", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - contractJSON: HelloWorldContractJson, - }); - t2.ok( - getNameOut.data.success, - `getName() SEND invocation produced receipt OK`, - ); - - const getNameOut2 = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "getName", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - contractJSON: HelloWorldContractJson, - }); - t2.equal( - getNameOut2.data.callOutput, - newName, - "setName() invocation #2 output is truthy OK", - ); - - t2.end(); - }); - - test("invoke Web3SigningCredentialType.NONE", async (t2: Test) => { - const testEthAccount2 = web3.eth.accounts.create(uuidV4()); - - const { rawTransaction } = await web3.eth.accounts.signTransaction( - { - from: testEthAccount.address, - to: testEthAccount2.address, - value: 10e6, - gas: 1000000, - }, - testEthAccount.privateKey, - ); - - await connector.transact({ - web3SigningCredential: { - type: Web3SigningCredentialType.None, - }, - transactionConfig: { - rawTransaction, - }, - }); - - const balance2 = await web3.eth.getBalance(testEthAccount2.address); - t2.ok(balance2, "Retrieved balance of test account 2 OK"); - t2.equals(parseInt(balance2, 10), 10e6, "Balance of test account2 is OK"); - t2.end(); - }); - - test("invoke Web3SigningCredentialType.PrivateKeyHex", async (t2: Test) => { - const newName = `DrCactus${uuidV4()}`; - const txCount = await web3.eth.getTransactionCount(testEthAccount.address); - const setNameOut = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: txCount, - contractJSON: HelloWorldContractJson, - }); - t2.ok(setNameOut.data, "setName() invocation #1 output is truthy OK"); - - try { - const setNameOutInvalid = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - // gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: 1, - contractJSON: HelloWorldContractJson, - }); - t2.ifError(setNameOutInvalid.data.transactionReceipt); - } catch (error) { - t2.notStrictEqual( - error, - "Nonce too low", - "setName() invocation with invalid nonce", - ); - } - const getNameOut = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "getName", - params: [], - // gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - contractJSON: HelloWorldContractJson, - }); - t2.equal( - getNameOut.data.callOutput, - newName, - `getName() output reflects the update OK`, - ); - - const getNameOut2 = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - contractJSON: HelloWorldContractJson, - }); - t2.ok(getNameOut2.data, "getName() invocation #2 output is truthy OK"); - - t2.end(); - }); - - test("get prometheus exporter metrics", async (t2: Test) => { - const res = await apiClient.getPrometheusMetricsV1(); - const promMetricsOutput = - "# HELP " + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - " Total transactions executed\n" + - "# TYPE " + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - " gauge\n" + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - '{type="' + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - '"} 3'; - t2.ok(res); - t2.ok(res.data); - t2.equal(res.status, 200); - t2.true( - res.data.includes(promMetricsOutput), - "Total Transaction Count of 3 recorded as expected. RESULT OK.", - ); - t2.end(); - }); - - t.end(); -}); diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-deploy-contract-from-json-json-object.test.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-deploy-contract-from-json-json-object.test.ts deleted file mode 100644 index 11389ef369d..00000000000 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-deploy-contract-from-json-json-object.test.ts +++ /dev/null @@ -1,378 +0,0 @@ -import test, { Test } from "tape-promise/tape"; -import Web3 from "web3"; -import { v4 as uuidV4 } from "uuid"; - -import { - LogLevelDesc, - IListenOptions, - Servers, -} from "@hyperledger/cactus-common"; - -import HelloWorldContractJson from "../../../../solidity/hello-world-contract/HelloWorld.json"; - -import { K_CACTUS_ETHEREUM_TOTAL_TX_COUNT } from "../../../../../main/typescript/prometheus-exporter/metrics"; - -import { - EthContractInvocationType, - PluginLedgerConnectorEthereum, - Web3SigningCredentialType, - DefaultApi as EthereumApi, -} from "../../../../../main/typescript/public-api"; - -import { - QuorumTestLedger, - IQuorumGenesisOptions, - IAccount, - pruneDockerAllIfGithubAction, -} from "@hyperledger/cactus-test-tooling"; - -const testCase = "Ethereum Ledger Connector Plugin"; -import express from "express"; -import bodyParser from "body-parser"; -import http from "http"; -import { AddressInfo } from "net"; -import { Configuration, Constants } from "@hyperledger/cactus-core-api"; -import { PluginRegistry } from "@hyperledger/cactus-core"; -import { Server as SocketIoServer } from "socket.io"; - -const logLevel: LogLevelDesc = "INFO"; - -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 containerImageVersion = "2021-05-03-quorum-v21.4.1"; - const ledgerOptions = { containerImageVersion }; - const ledger = new QuorumTestLedger(ledgerOptions); - test.onFinish(async () => { - await ledger.stop(); - await ledger.destroy(); - await pruneDockerAllIfGithubAction({ logLevel }); - }); - await ledger.start(); - - const rpcApiHttpHost = await ledger.getRpcApiHttpHost(); - const ethereumGenesisOptions: IQuorumGenesisOptions = await ledger.getGenesisJsObject(); - t.ok(ethereumGenesisOptions); - t.ok(ethereumGenesisOptions.alloc); - - const highNetWorthAccounts: string[] = Object.keys( - ethereumGenesisOptions.alloc, - ).filter((address: string) => { - const anAccount: IAccount = ethereumGenesisOptions.alloc[address]; - const theBalance = parseInt(anAccount.balance, 10); - return theBalance > 10e7; - }); - const [firstHighNetWorthAccount] = highNetWorthAccounts; - - const web3 = new Web3(rpcApiHttpHost); - const testEthAccount = web3.eth.accounts.create(uuidV4()); - - const connector: PluginLedgerConnectorEthereum = new PluginLedgerConnectorEthereum( - { - instanceId: uuidV4(), - rpcApiHttpHost, - logLevel, - pluginRegistry: new PluginRegistry(), - }, - ); - - const expressApp = express(); - expressApp.use(bodyParser.json({ limit: "250mb" })); - const server = http.createServer(expressApp); - const listenOptions: IListenOptions = { - hostname: "localhost", - port: 0, - server, - }; - const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; - test.onFinish(async () => await Servers.shutdown(server)); - const { address, port } = addressInfo; - const apiHost = `http://${address}:${port}`; - t.comment( - `Metrics URL: ${apiHost}/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/get-prometheus-exporter-metrics`, - ); - - const apiConfig = new Configuration({ basePath: apiHost }); - const apiClient = new EthereumApi(apiConfig); - - const wsApi = new SocketIoServer(server, { - path: Constants.SocketIoConnectionPathV1, - }); - - await connector.getOrCreateWebServices(); - await connector.registerWebServices(expressApp, wsApi); - - await connector.transact({ - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - transactionConfig: { - from: firstHighNetWorthAccount, - to: testEthAccount.address, - value: 10e9, - }, - }); - - const balance = await web3.eth.getBalance(testEthAccount.address); - t.ok(balance, "Retrieved balance of test account OK"); - t.equals(parseInt(balance, 10), 10e9, "Balance of test account is OK"); - - let contractAddress: string; - - test("deploys contract via .json file", async (t2: Test) => { - const deployOut = await connector.deployContractJsonObject({ - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - gas: 1000000, - contractJSON: HelloWorldContractJson, - }); - t2.ok(deployOut, "deployContract() output is truthy OK"); - t2.ok( - deployOut.transactionReceipt, - "deployContract() output.transactionReceipt is truthy OK", - ); - t2.ok( - deployOut.transactionReceipt.contractAddress, - "deployContract() output.transactionReceipt.contractAddress is truthy OK", - ); - - contractAddress = deployOut.transactionReceipt.contractAddress as string; - t2.ok( - typeof contractAddress === "string", - "contractAddress typeof string OK", - ); - - const { callOutput: helloMsg } = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "sayHello", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - contractJSON: HelloWorldContractJson, - }); - t2.ok(helloMsg, "sayHello() output is truthy"); - t2.true( - typeof helloMsg === "string", - "sayHello() output is type of string", - ); - }); - - test("invoke Web3SigningCredentialType.GETHKEYCHAINPASSWORD", async (t2: Test) => { - const newName = `DrCactus${uuidV4()}`; - const txCount = await web3.eth.getTransactionCount( - firstHighNetWorthAccount, - ); - const setNameOut = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: txCount, - contractJSON: HelloWorldContractJson, - }); - t2.ok(setNameOut, "setName() invocation #1 output is truthy OK"); - - try { - const setNameOutInvalid = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 2, - contractJSON: HelloWorldContractJson, - }); - t2.ifError(setNameOutInvalid.transactionReceipt); - } catch (error) { - t2.notStrictEqual( - error, - "Nonce too low", - "setName() invocation with invalid nonce", - ); - } - - const getNameOut = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "getName", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - contractJSON: HelloWorldContractJson, - }); - t2.ok(getNameOut.success, `getName() SEND invocation produced receipt OK`); - - const { callOutput: getNameOut2 } = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "getName", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - contractJSON: HelloWorldContractJson, - }); - t2.equal( - getNameOut2, - newName, - "setName() invocation #2 output is truthy OK", - ); - - t2.end(); - }); - - test("invoke Web3SigningCredentialType.NONE", async (t2: Test) => { - const testEthAccount2 = web3.eth.accounts.create(uuidV4()); - - const { rawTransaction } = await web3.eth.accounts.signTransaction( - { - from: testEthAccount.address, - to: testEthAccount2.address, - value: 10e6, - gas: 1000000, - }, - testEthAccount.privateKey, - ); - - await connector.transact({ - web3SigningCredential: { - type: Web3SigningCredentialType.None, - }, - transactionConfig: { - rawTransaction, - }, - }); - - const balance2 = await web3.eth.getBalance(testEthAccount2.address); - t2.ok(balance2, "Retrieved balance of test account 2 OK"); - t2.equals(parseInt(balance2, 10), 10e6, "Balance of test account2 is OK"); - t2.end(); - }); - - test("invoke Web3SigningCredentialType.PrivateKeyHex", async (t2: Test) => { - const newName = `DrCactus${uuidV4()}`; - const txCount = await web3.eth.getTransactionCount(testEthAccount.address); - const setNameOut = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: txCount, - contractJSON: HelloWorldContractJson, - }); - t2.ok(setNameOut, "setName() invocation #1 output is truthy OK"); - - try { - const setNameOutInvalid = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: 1, - contractJSON: HelloWorldContractJson, - }); - t2.ifError(setNameOutInvalid.transactionReceipt); - } catch (error) { - t2.notStrictEqual( - error, - "Nonce too low", - "setName() invocation with invalid nonce", - ); - } - const { callOutput: getNameOut } = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - contractJSON: HelloWorldContractJson, - }); - t2.equal(getNameOut, newName, `getName() output reflects the update OK`); - - const getNameOut2 = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - contractJSON: HelloWorldContractJson, - }); - t2.ok(getNameOut2, "getName() invocation #2 output is truthy OK"); - - t2.end(); - }); - - test("get prometheus exporter metrics", async (t2: Test) => { - const res = await apiClient.getPrometheusMetricsV1(); - const promMetricsOutput = - "# HELP " + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - " Total transactions executed\n" + - "# TYPE " + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - " gauge\n" + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - '{type="' + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - '"} 3'; - t2.ok(res); - t2.ok(res.data); - t2.equal(res.status, 200); - t2.true( - res.data.includes(promMetricsOutput), - "Total Transaction Count of 3 recorded as expected. RESULT OK.", - ); - t2.end(); - }); - - t.end(); -}); diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-deploy-contract-from-json.test.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-deploy-contract-from-json.test.ts deleted file mode 100644 index 3e887f0ee7b..00000000000 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-deploy-contract-from-json.test.ts +++ /dev/null @@ -1,441 +0,0 @@ -import Web3 from "web3"; -import { Account } from "web3-core"; -import { v4 as uuidV4 } from "uuid"; -import "jest-extended"; -import { - LogLevelDesc, - IListenOptions, - Servers, -} from "@hyperledger/cactus-common"; - -import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory"; - -import HelloWorldContractJson from "../../../../solidity/hello-world-contract/HelloWorld.json"; - -import { K_CACTUS_ETHEREUM_TOTAL_TX_COUNT } from "../../../../../main/typescript/prometheus-exporter/metrics"; - -import { - EthContractInvocationType, - PluginLedgerConnectorEthereum, - Web3SigningCredentialCactusKeychainRef, - Web3SigningCredentialType, - DefaultApi as EthereumApi, -} from "../../../../../main/typescript/public-api"; - -import { - QuorumTestLedger, - IQuorumGenesisOptions, - IAccount, - pruneDockerAllIfGithubAction, -} from "@hyperledger/cactus-test-tooling"; -import { PluginRegistry } from "@hyperledger/cactus-core"; -import express from "express"; -import bodyParser from "body-parser"; -import http from "http"; -import { AddressInfo } from "net"; -import { Configuration, Constants } from "@hyperledger/cactus-core-api"; -import { Server as SocketIoServer } from "socket.io"; - -const testCase = "Ethereum Ledger Connector Plugin"; - -describe(testCase, () => { - const logLevel: LogLevelDesc = "INFO"; - const contractName = "HelloWorld"; - - const containerImageVersion = "2021-05-03-quorum-v21.4.1"; - const ledgerOptions = { containerImageVersion }; - const ledger = new QuorumTestLedger(ledgerOptions); - const expressApp = express(); - expressApp.use(bodyParser.json({ limit: "250mb" })); - const server = http.createServer(expressApp); - let addressInfo, - rpcApiHttpHost: string, - ethereumGenesisOptions: IQuorumGenesisOptions, - connector: PluginLedgerConnectorEthereum, - contractAddress: string, - address: string, - port: number, - apiHost, - apiConfig, - apiClient: EthereumApi, - web3: Web3, - testEthAccount: Account, - keychainEntryKey: string, - keychainEntryValue: string, - keychainPlugin: PluginKeychainMemory, - firstHighNetWorthAccount: string; - const wsApi = new SocketIoServer(server, { - path: Constants.SocketIoConnectionPathV1, - }); - - afterAll(async () => await Servers.shutdown(server)); - beforeAll(async () => { - const pruning = pruneDockerAllIfGithubAction({ logLevel }); - await expect(pruning).resolves.toBeTruthy(); - }); - afterAll(async () => { - await ledger.stop(); - await ledger.destroy(); - }); - afterAll(async () => { - const pruning = pruneDockerAllIfGithubAction({ logLevel }); - await expect(pruning).resolves.toBeTruthy(); - }); - - beforeAll(async () => { - await ledger.start(); - rpcApiHttpHost = await ledger.getRpcApiHttpHost(); - expect(rpcApiHttpHost).toBeString(); - - ethereumGenesisOptions = await ledger.getGenesisJsObject(); - expect(ethereumGenesisOptions).toBeTruthy(); - expect(ethereumGenesisOptions.alloc).toBeTruthy(); - - web3 = new Web3(rpcApiHttpHost); - testEthAccount = web3.eth.accounts.create(uuidV4()); - - keychainEntryKey = uuidV4(); - keychainEntryValue = testEthAccount.privateKey; - 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, - }); - connector = new PluginLedgerConnectorEthereum({ - instanceId: uuidV4(), - rpcApiHttpHost, - logLevel, - pluginRegistry: new PluginRegistry({ plugins: [keychainPlugin] }), - }); - const listenOptions: IListenOptions = { - hostname: "localhost", - port: 0, - server, - }; - addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; - ({ address, port } = addressInfo); - apiHost = `http://${address}:${port}`; - apiConfig = new Configuration({ basePath: apiHost }); - apiClient = new EthereumApi(apiConfig); - }); - test("Bootsrap test ETH account with funds from genesis", async () => { - const highNetWorthAccounts: string[] = Object.keys( - ethereumGenesisOptions.alloc, - ).filter((address: string) => { - const anAccount: IAccount = ethereumGenesisOptions.alloc[address]; - const theBalance = parseInt(anAccount.balance, 10); - return theBalance > 10e7; - }); - [firstHighNetWorthAccount] = highNetWorthAccounts; - keychainPlugin.set( - HelloWorldContractJson.contractName, - JSON.stringify(HelloWorldContractJson), - ); - // Instantiate connector with the keychain plugin that already has the - // private key we want to use for one of our tests - await connector.registerWebServices(expressApp, wsApi); - - await connector.transact({ - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - transactionConfig: { - from: firstHighNetWorthAccount, - to: testEthAccount.address, - value: 10e9, - }, - }); - - const balance = await web3.eth.getBalance(testEthAccount.address); - expect(balance).toBeTruthy(); - expect(parseInt(balance, 10)).toBe(10e9); - }); - - test("deploys contract via .json file", async () => { - const deployOut = await connector.deployContract({ - contractName: HelloWorldContractJson.contractName, - keychainId: keychainPlugin.getKeychainId(), - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - gas: 1000000, - }); - expect(deployOut).toBeTruthy(); - expect(deployOut.transactionReceipt).toBeTruthy(); - expect(deployOut.transactionReceipt.contractAddress).toBeTruthy(); - - contractAddress = deployOut.transactionReceipt.contractAddress as string; - expect(typeof contractAddress).toBe("string"); - - const { callOutput: helloMsg } = await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Call, - methodName: "sayHello", - keychainId: keychainPlugin.getKeychainId(), - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - }); - expect(helloMsg).toBeTruthy(); - expect(typeof contractAddress).toBe("string"); - expect(typeof helloMsg).toBe("string"); - }); - - test("invoke Web3SigningCredentialType.GETHKEYCHAINPASSWORD", async () => { - const newName = `DrCactus${uuidV4()}`; - const setNameOut = await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - keychainId: keychainPlugin.getKeychainId(), - params: [newName], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 2, - }); - expect(setNameOut).toBeTruthy(); - - try { - await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - keychainId: keychainPlugin.getKeychainId(), - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 2, - }); - fail( - "PluginLedgerConnectorEthereum.invokeContract failed to throw error", - ); - } catch (error) { - expect(error).not.toBe("Nonce too low"); - } - - const getNameOut = await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Send, - methodName: "getName", - keychainId: keychainPlugin.getKeychainId(), - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - }); - expect(getNameOut.success).toBeTruthy(); - - const { callOutput: getNameOut2 } = await connector.getContractInfoKeychain( - { - contractName, - invocationType: EthContractInvocationType.Call, - methodName: "getName", - keychainId: keychainPlugin.getKeychainId(), - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - }, - ); - expect(getNameOut2).toEqual(newName); - }); - - test("invoke Web3SigningCredentialType.NONE", async () => { - const testEthAccount2 = web3.eth.accounts.create(uuidV4()); - - const { rawTransaction } = await web3.eth.accounts.signTransaction( - { - from: testEthAccount.address, - to: testEthAccount2.address, - value: 10e6, - gas: 1000000, - }, - testEthAccount.privateKey, - ); - - await connector.transact({ - web3SigningCredential: { - type: Web3SigningCredentialType.None, - }, - transactionConfig: { - rawTransaction, - }, - }); - - const balance2 = await web3.eth.getBalance(testEthAccount2.address); - expect(balance2).toBeTruthy(); - expect(parseInt(balance2, 10)).toEqual(10e6); - }); - - test("invoke Web3SigningCredentialType.PrivateKeyHex", async () => { - const newName = `DrCactus${uuidV4()}`; - const setNameOut = await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - keychainId: keychainPlugin.getKeychainId(), - params: [newName], - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: 1, - }); - expect(setNameOut).toBeTruthy(); - - try { - await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - keychainId: keychainPlugin.getKeychainId(), - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: 1, - }); - fail("It should not reach here"); - } catch (error) { - expect(error).not.toBe("Nonce too low"); - } - const { callOutput: getNameOut } = await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Call, - methodName: "getName", - keychainId: keychainPlugin.getKeychainId(), - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - }); - expect(getNameOut).toEqual(newName); - - const getNameOut2 = await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Send, - methodName: "getName", - keychainId: keychainPlugin.getKeychainId(), - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - }); - expect(getNameOut2).toBeTruthy(); - }); - - test("invoke Web3SigningCredentialType.CactusKeychainRef", async () => { - const newName = `DrCactus${uuidV4()}`; - - const web3SigningCredential: Web3SigningCredentialCactusKeychainRef = { - ethAccount: testEthAccount.address, - keychainEntryKey, - keychainId: keychainPlugin.getKeychainId(), - type: Web3SigningCredentialType.CactusKeychainRef, - }; - - const setNameOut = await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - keychainId: keychainPlugin.getKeychainId(), - params: [newName], - gas: 1000000, - web3SigningCredential, - nonce: 3, - }); - expect(setNameOut).toBeTruthy(); - - try { - await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - keychainId: keychainPlugin.getKeychainId(), - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 3, - }); - fail("it should not reach here"); - } catch (error) { - expect(error).not.toBe("Nonce too low"); - } - const { callOutput: getNameOut } = await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Call, - methodName: "getName", - keychainId: keychainPlugin.getKeychainId(), - params: [], - gas: 1000000, - web3SigningCredential, - }); - expect(getNameOut).toEqual(newName); - - const getNameOut2 = await connector.getContractInfoKeychain({ - contractName, - invocationType: EthContractInvocationType.Send, - methodName: "getName", - keychainId: keychainPlugin.getKeychainId(), - params: [], - gas: 1000000, - web3SigningCredential, - }); - expect(getNameOut2).toBeTruthy(); - }); - - test("get prometheus exporter metrics", async () => { - const res = await apiClient.getPrometheusMetricsV1(); - const promMetricsOutput = - "# HELP " + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - " Total transactions executed\n" + - "# TYPE " + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - " gauge\n" + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - '{type="' + - K_CACTUS_ETHEREUM_TOTAL_TX_COUNT + - '"} 5'; - expect(res).toBeTruthy(); - expect(res.data).toBeTruthy(); - expect(res.status).toEqual(200); - expect(res.data).toContain(promMetricsOutput); - }); -}); diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-contract-json-object-endpoints.test.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-contract-json-object-endpoints.test.ts deleted file mode 100644 index a742872688f..00000000000 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-contract-json-object-endpoints.test.ts +++ /dev/null @@ -1,352 +0,0 @@ -import test, { Test } from "tape"; -import Web3 from "web3"; -import { v4 as uuidV4 } from "uuid"; - -import { - LogLevelDesc, - IListenOptions, - Servers, -} from "@hyperledger/cactus-common"; - -import HelloWorldContractJson from "../../../../solidity/hello-world-contract/HelloWorld.json"; - -import { - EthContractInvocationType, - PluginLedgerConnectorEthereum, - Web3SigningCredentialType, - DefaultApi as EthereumApi, -} from "../../../../../main/typescript/public-api"; - -import { - QuorumTestLedger, - IQuorumGenesisOptions, - IAccount, -} from "@hyperledger/cactus-test-tooling"; -import { PluginRegistry } from "@hyperledger/cactus-core"; -import { Configuration, Constants } from "@hyperledger/cactus-core-api"; -import { Server as SocketIoServer } from "socket.io"; - -import express from "express"; -import bodyParser from "body-parser"; -import http from "http"; -import { AddressInfo } from "net"; - -const logLevel: LogLevelDesc = "INFO"; - -test("Ethereum Ledger Connector Plugin", async (t: Test) => { - const containerImageVersion = "2021-05-03-quorum-v21.4.1"; - - const ledgerOptions = { containerImageVersion }; - const ledger = new QuorumTestLedger(ledgerOptions); - test.onFinish(async () => { - await ledger.stop(); - await ledger.destroy(); - }); - await ledger.start(); - - const rpcApiHttpHost = await ledger.getRpcApiHttpHost(); - const ethereumGenesisOptions: IQuorumGenesisOptions = await ledger.getGenesisJsObject(); - t.ok(ethereumGenesisOptions); - t.ok(ethereumGenesisOptions.alloc); - - const highNetWorthAccounts: string[] = Object.keys( - ethereumGenesisOptions.alloc, - ).filter((address: string) => { - const anAccount: IAccount = ethereumGenesisOptions.alloc[address]; - const theBalance = parseInt(anAccount.balance, 10); - return theBalance > 10e7; - }); - const [firstHighNetWorthAccount] = highNetWorthAccounts; - - const web3 = new Web3(rpcApiHttpHost); - const testEthAccount = web3.eth.accounts.create(uuidV4()); - const connector: PluginLedgerConnectorEthereum = new PluginLedgerConnectorEthereum( - { - instanceId: uuidV4(), - rpcApiHttpHost, - logLevel, - pluginRegistry: new PluginRegistry(), - }, - ); - - const expressApp = express(); - expressApp.use(bodyParser.json({ limit: "250mb" })); - const server = http.createServer(expressApp); - const listenOptions: IListenOptions = { - hostname: "localhost", - port: 0, - server, - }; - const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; - test.onFinish(async () => await Servers.shutdown(server)); - const { address, port } = addressInfo; - const apiHost = `http://${address}:${port}`; - t.comment( - `Metrics URL: ${apiHost}/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-ethereum/get-prometheus-exporter-metrics`, - ); - - const apiConfig = new Configuration({ basePath: apiHost }); - const apiClient = new EthereumApi(apiConfig); - - const wsApi = new SocketIoServer(server, { - path: Constants.SocketIoConnectionPathV1, - }); - - await connector.getOrCreateWebServices(); - await connector.registerWebServices(expressApp, wsApi); - - await connector.transact({ - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - transactionConfig: { - from: firstHighNetWorthAccount, - to: testEthAccount.address, - value: 10e9, - }, - }); - - const balance = await web3.eth.getBalance(testEthAccount.address); - t.ok(balance, "Retrieved balance of test account OK"); - t.equals(parseInt(balance, 10), 10e9, "Balance of test account is OK"); - - let contractAddress: string; - - test("deploys contract via .json file", async (t2: Test) => { - const deployOut = await apiClient.deployContractSolBytecodeJsonObjectV1({ - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - gas: 1000000, - contractJSON: HelloWorldContractJson, - }); - t2.ok(deployOut, "deployContract() output is truthy OK"); - t2.ok( - deployOut.data.transactionReceipt, - "deployContract() output.transactionReceipt is truthy OK", - ); - t2.ok( - deployOut.data.transactionReceipt.contractAddress, - "deployContract() output.transactionReceipt.contractAddress is truthy OK", - ); - - contractAddress = deployOut.data.transactionReceipt - .contractAddress as string; - t2.ok( - typeof contractAddress === "string", - "contractAddress typeof string OK", - ); - - const helloMsg = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "sayHello", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - contractJSON: HelloWorldContractJson, - }); - t2.ok(helloMsg.data.callOutput, "sayHello() output is truthy"); - t2.true( - typeof helloMsg.data.callOutput === "string", - "sayHello() output is type of string", - ); - }); - - test("invoke Web3SigningCredentialType.GETHKEYCHAINPASSWORD", async (t2: Test) => { - const newName = `DrCactus${uuidV4()}`; - const txCount = await web3.eth.getTransactionCount( - firstHighNetWorthAccount, - ); - const setNameOut = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: txCount, - contractJSON: HelloWorldContractJson, - }); - t2.ok(setNameOut.data, "setName() invocation #1 output is truthy OK"); - - try { - const setNameOutInvalid = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 2, - contractJSON: HelloWorldContractJson, - }); - t2.ifError(setNameOutInvalid.data.transactionReceipt); - } catch (error) { - t2.notStrictEqual( - error, - "Nonce too low", - "setName() invocation with invalid nonce", - ); - } - - const getNameOut = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "getName", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - contractJSON: HelloWorldContractJson, - }); - t2.ok( - getNameOut.data.success, - `getName() SEND invocation produced receipt OK`, - ); - - const getNameOut2 = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "getName", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - contractJSON: HelloWorldContractJson, - }); - t2.equal( - getNameOut2.data.callOutput, - newName, - "setName() invocation #2 output is truthy OK", - ); - - t2.end(); - }); - - test("invoke Web3SigningCredentialType.NONE", async (t2: Test) => { - const testEthAccount2 = web3.eth.accounts.create(uuidV4()); - - const { rawTransaction } = await web3.eth.accounts.signTransaction( - { - from: testEthAccount.address, - to: testEthAccount2.address, - value: 10e6, - gas: 1000000, - }, - testEthAccount.privateKey, - ); - - await connector.transact({ - web3SigningCredential: { - type: Web3SigningCredentialType.None, - }, - transactionConfig: { - rawTransaction, - }, - }); - - const balance2 = await web3.eth.getBalance(testEthAccount2.address); - t2.ok(balance2, "Retrieved balance of test account 2 OK"); - t2.equals(parseInt(balance2, 10), 10e6, "Balance of test account2 is OK"); - t2.end(); - }); - - test("invoke Web3SigningCredentialType.PrivateKeyHex", async (t2: Test) => { - const newName = `DrCactus${uuidV4()}`; - const txCount = await web3.eth.getTransactionCount(testEthAccount.address); - const setNameOut = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: txCount, - contractJSON: HelloWorldContractJson, - }); - t2.ok(setNameOut.data, "setName() invocation #1 output is truthy OK"); - - try { - const setNameOutInvalid = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: 1, - contractJSON: HelloWorldContractJson, - }); - t2.ifError(setNameOutInvalid.data.transactionReceipt); - } catch (error) { - t2.notStrictEqual( - error, - "Nonce too low", - "setName() invocation with invalid nonce", - ); - } - const getNameOut = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - contractJSON: HelloWorldContractJson, - }); - t2.equal( - getNameOut.data.callOutput, - newName, - `getName() output reflects the update OK`, - ); - - const getNameOut2 = await apiClient.invokeContractV1NoKeychain({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - contractJSON: HelloWorldContractJson, - }); - t2.ok(getNameOut2.data, "getName() invocation #2 output is truthy OK"); - - t2.end(); - }); - - t.end(); -}); diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-contract-json-object.test.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-contract-json-object.test.ts deleted file mode 100644 index 245ced94c24..00000000000 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-contract-json-object.test.ts +++ /dev/null @@ -1,306 +0,0 @@ -import test, { Test } from "tape"; -import Web3 from "web3"; -import { v4 as uuidV4 } from "uuid"; - -import { LogLevelDesc } from "@hyperledger/cactus-common"; - -import HelloWorldContractJson from "../../../../solidity/hello-world-contract/HelloWorld.json"; - -import { - EthContractInvocationType, - PluginLedgerConnectorEthereum, - Web3SigningCredentialType, -} from "../../../../../main/typescript/public-api"; - -import { - QuorumTestLedger, - IQuorumGenesisOptions, - IAccount, -} from "@hyperledger/cactus-test-tooling"; -import { PluginRegistry } from "@hyperledger/cactus-core"; - -const logLevel: LogLevelDesc = "INFO"; - -test("Ethereum Ledger Connector Plugin", async (t: Test) => { - const containerImageVersion = "2021-05-03-quorum-v21.4.1"; - - const ledgerOptions = { containerImageVersion }; - const ledger = new QuorumTestLedger(ledgerOptions); - test.onFinish(async () => { - await ledger.stop(); - await ledger.destroy(); - }); - await ledger.start(); - - const rpcApiHttpHost = await ledger.getRpcApiHttpHost(); - const ethereumGenesisOptions: IQuorumGenesisOptions = await ledger.getGenesisJsObject(); - t.ok(ethereumGenesisOptions); - t.ok(ethereumGenesisOptions.alloc); - - const highNetWorthAccounts: string[] = Object.keys( - ethereumGenesisOptions.alloc, - ).filter((address: string) => { - const anAccount: IAccount = ethereumGenesisOptions.alloc[address]; - const theBalance = parseInt(anAccount.balance, 10); - return theBalance > 10e7; - }); - const [firstHighNetWorthAccount] = highNetWorthAccounts; - - const web3 = new Web3(rpcApiHttpHost); - const testEthAccount = web3.eth.accounts.create(uuidV4()); - const connector: PluginLedgerConnectorEthereum = new PluginLedgerConnectorEthereum( - { - instanceId: uuidV4(), - rpcApiHttpHost, - logLevel, - pluginRegistry: new PluginRegistry(), - }, - ); - - await connector.transact({ - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - transactionConfig: { - from: firstHighNetWorthAccount, - to: testEthAccount.address, - value: 10e9, - }, - }); - - const balance = await web3.eth.getBalance(testEthAccount.address); - t.ok(balance, "Retrieved balance of test account OK"); - t.equals(parseInt(balance, 10), 10e9, "Balance of test account is OK"); - - let contractAddress: string; - - test("deploys contract via .json file", async (t2: Test) => { - const deployOut = await connector.deployContractJsonObject({ - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - gas: 1000000, - contractJSON: HelloWorldContractJson, - }); - t2.ok(deployOut, "deployContract() output is truthy OK"); - t2.ok( - deployOut.transactionReceipt, - "deployContract() output.transactionReceipt is truthy OK", - ); - t2.ok( - deployOut.transactionReceipt.contractAddress, - "deployContract() output.transactionReceipt.contractAddress is truthy OK", - ); - - contractAddress = deployOut.transactionReceipt.contractAddress as string; - t2.ok( - typeof contractAddress === "string", - "contractAddress typeof string OK", - ); - - const { callOutput: helloMsg } = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "sayHello", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - contractJSON: HelloWorldContractJson, - }); - t2.ok(helloMsg, "sayHello() output is truthy"); - t2.true( - typeof helloMsg === "string", - "sayHello() output is type of string", - ); - }); - - test("invoke Web3SigningCredentialType.GETHKEYCHAINPASSWORD", async (t2: Test) => { - const newName = `DrCactus${uuidV4()}`; - const txCount = await web3.eth.getTransactionCount( - firstHighNetWorthAccount, - ); - const setNameOut = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: txCount, - contractJSON: HelloWorldContractJson, - }); - t2.ok(setNameOut, "setName() invocation #1 output is truthy OK"); - - try { - const setNameOutInvalid = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 2, - contractJSON: HelloWorldContractJson, - }); - t2.ifError(setNameOutInvalid.transactionReceipt); - } catch (error) { - t2.notStrictEqual( - error, - "Nonce too low", - "setName() invocation with invalid nonce", - ); - } - - const getNameOut = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "getName", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - contractJSON: HelloWorldContractJson, - }); - t2.ok(getNameOut.success, `getName() SEND invocation produced receipt OK`); - - const { callOutput: getNameOut2 } = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "getName", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - contractJSON: HelloWorldContractJson, - }); - t2.equal( - getNameOut2, - newName, - "setName() invocation #2 output is truthy OK", - ); - - t2.end(); - }); - - test("invoke Web3SigningCredentialType.NONE", async (t2: Test) => { - const testEthAccount2 = web3.eth.accounts.create(uuidV4()); - - const { rawTransaction } = await web3.eth.accounts.signTransaction( - { - from: testEthAccount.address, - to: testEthAccount2.address, - value: 10e6, - gas: 1000000, - }, - testEthAccount.privateKey, - ); - - await connector.transact({ - web3SigningCredential: { - type: Web3SigningCredentialType.None, - }, - transactionConfig: { - rawTransaction, - }, - }); - - const balance2 = await web3.eth.getBalance(testEthAccount2.address); - t2.ok(balance2, "Retrieved balance of test account 2 OK"); - t2.equals(parseInt(balance2, 10), 10e6, "Balance of test account2 is OK"); - t2.end(); - }); - - test("invoke Web3SigningCredentialType.PrivateKeyHex", async (t2: Test) => { - const newName = `DrCactus${uuidV4()}`; - const txCount = await web3.eth.getTransactionCount(testEthAccount.address); - const setNameOut = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: txCount, - contractJSON: HelloWorldContractJson, - }); - t2.ok(setNameOut, "setName() invocation #1 output is truthy OK"); - - try { - const setNameOutInvalid = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: 1, - contractJSON: HelloWorldContractJson, - }); - t2.ifError(setNameOutInvalid.transactionReceipt); - } catch (error) { - t2.notStrictEqual( - error, - "Nonce too low", - "setName() invocation with invalid nonce", - ); - } - const { callOutput: getNameOut } = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Call, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - contractJSON: HelloWorldContractJson, - }); - t2.equal(getNameOut, newName, `getName() output reflects the update OK`); - - const getNameOut2 = await connector.getContractInfo({ - contractAddress, - invocationType: EthContractInvocationType.Send, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - contractJSON: HelloWorldContractJson, - }); - t2.ok(getNameOut2, "getName() invocation #2 output is truthy OK"); - - t2.end(); - }); - - t.end(); -}); diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-contract.test.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-contract.test.ts deleted file mode 100644 index 17a5c76c35a..00000000000 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/v21.4.1-invoke-contract.test.ts +++ /dev/null @@ -1,365 +0,0 @@ -import "jest-extended"; -import Web3 from "web3"; -import { v4 as uuidV4 } from "uuid"; - -import { LogLevelDesc } from "@hyperledger/cactus-common"; - -import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory"; - -import HelloWorldContractJson from "../../../../solidity/hello-world-contract/HelloWorld.json"; - -import { - EthContractInvocationType, - PluginLedgerConnectorEthereum, - Web3SigningCredentialCactusKeychainRef, - Web3SigningCredentialType, -} from "../../../../../main/typescript/public-api"; - -import { - QuorumTestLedger, - IQuorumGenesisOptions, - IAccount, -} from "@hyperledger/cactus-test-tooling"; -import { PluginRegistry } from "@hyperledger/cactus-core"; - -const logLevel: LogLevelDesc = "INFO"; -const contractName = "HelloWorld"; -const testCase = "Ethereum Ledger Connector Plugin"; - -describe(testCase, () => { - afterAll(async () => { - await ledger.stop(); - await ledger.destroy(); - }); - - const containerImageVersion = "2021-05-03-quorum-v21.4.1"; - const ledgerOptions = { containerImageVersion }; - const ledger = new QuorumTestLedger(ledgerOptions); - - test(testCase, async () => { - await ledger.start(); - const rpcApiHttpHost = await ledger.getRpcApiHttpHost(); - const ethereumGenesisOptions: IQuorumGenesisOptions = await ledger.getGenesisJsObject(); - expect(ethereumGenesisOptions).toBeTruthy(); - expect(ethereumGenesisOptions.alloc).toBeTruthy(); - - const highNetWorthAccounts: string[] = Object.keys( - ethereumGenesisOptions.alloc, - ).filter((address: string) => { - const anAccount: IAccount = ethereumGenesisOptions.alloc[address]; - const theBalance = parseInt(anAccount.balance, 10); - return theBalance > 10e7; - }); - const [firstHighNetWorthAccount] = highNetWorthAccounts; - - 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, - JSON.stringify(HelloWorldContractJson), - ); - // Instantiate connector with the keychain plugin that already has the - // private key we want to use for one of our tests - const connector: PluginLedgerConnectorEthereum = new PluginLedgerConnectorEthereum( - { - instanceId: uuidV4(), - rpcApiHttpHost, - logLevel, - pluginRegistry: new PluginRegistry({ plugins: [keychainPlugin] }), - }, - ); - - await connector.transact({ - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - transactionConfig: { - from: firstHighNetWorthAccount, - to: testEthAccount.address, - value: 10e9, - }, - }); - - const balance = await web3.eth.getBalance(testEthAccount.address); - expect(balance).toBeTruthy(); - expect(parseInt(balance, 10)).toEqual(10e9); - let contractAddress: string; - - { - const deployOut = await connector.deployContract({ - keychainId: keychainPlugin.getKeychainId(), - contractName: HelloWorldContractJson.contractName, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - gas: 1000000, - }); - expect(deployOut).toBeTruthy(); - expect(deployOut.transactionReceipt).toBeTruthy(); - expect(deployOut.transactionReceipt.contractAddress).toBeTruthy(); - - contractAddress = deployOut.transactionReceipt.contractAddress as string; - expect(typeof contractAddress === "string").toBeTrue(); - - const { callOutput: helloMsg } = await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Call, - methodName: "sayHello", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - }); - expect(helloMsg).toBeTruthy(); - expect(typeof helloMsg === "string").toBeTrue(); - } - - { - const newName = `DrCactus${uuidV4()}`; - const setNameOut = await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 2, - }); - expect(setNameOut).toBeTruthy(); - - try { - const setNameOutInvalid = await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 2, - }); - expect(setNameOutInvalid.transactionReceipt).toBeFalsy(); - } catch (error) { - expect(error.message).toMatch(/nonce too low/); - } - - const getNameOut = await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Send, - methodName: "getName", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - }); - expect(getNameOut.success).toBeTruthy(); - - const { - callOutput: getNameOut2, - } = await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Call, - methodName: "getName", - params: [], - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - }); - expect(getNameOut2).toEqual(newName); - } - - { - const testEthAccount2 = web3.eth.accounts.create(uuidV4()); - const { rawTransaction } = await web3.eth.accounts.signTransaction( - { - from: testEthAccount.address, - to: testEthAccount2.address, - value: 10e6, - gas: 1000000, - }, - testEthAccount.privateKey, - ); - - await connector.transact({ - web3SigningCredential: { - type: Web3SigningCredentialType.None, - }, - transactionConfig: { - rawTransaction, - }, - }); - - const balance2 = await web3.eth.getBalance(testEthAccount2.address); - expect(balance2).toBeTruthy(); - expect(parseInt(balance2, 10)).toEqual(10e6); - } - - { - const newName = `DrCactus${uuidV4()}`; - const setNameOut = await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: 1, - }); - expect(setNameOut).toBeTruthy(); - - try { - const setNameOutInvalid = await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - nonce: 1, - }); - expect(setNameOutInvalid.transactionReceipt).toBeFalsy(); - } catch (error) { - expect(error.message).toMatch(/nonce too low/); - } - const { - callOutput: getNameOut, - } = await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Call, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - }); - expect(getNameOut).toEqual(newName); - - const getNameOut2 = await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Send, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - }); - expect(getNameOut2).toBeTruthy(); - } - - { - const newName = `DrCactus${uuidV4()}`; - - const web3SigningCredential: Web3SigningCredentialCactusKeychainRef = { - ethAccount: testEthAccount.address, - keychainEntryKey, - keychainId: keychainPlugin.getKeychainId(), - type: Web3SigningCredentialType.CactusKeychainRef, - }; - - const setNameOut = await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential, - nonce: 3, - }); - expect(setNameOut).toBeTruthy(); - - try { - const setNameOutInvalid = await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - nonce: 3, - }); - expect(setNameOutInvalid.transactionReceipt).toBeFalsy(); - } catch (error) { - expect(error.message).toMatch(/nonce too low/); - } - const { - callOutput: getNameOut, - } = await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Call, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential, - }); - expect(getNameOut).toEqual(newName); - - const getNameOut2 = await connector.getContractInfoKeychain({ - contractName, - keychainId: keychainPlugin.getKeychainId(), - invocationType: EthContractInvocationType.Send, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential, - }); - expect(getNameOut2).toBeTruthy(); - } - }); -}); diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/manual/geth-alchemy-integration-manual-check.test.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/manual/geth-alchemy-integration-manual-check.test.ts new file mode 100644 index 00000000000..601e3d0df3b --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/manual/geth-alchemy-integration-manual-check.test.ts @@ -0,0 +1,119 @@ +/* + * Copyright 2020-2023 Hyperledger Cactus Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * For instructions on how to run it see cactus-plugin-ledger-connector-ethereum README + */ + +////////////////////////////////// +// Constants +////////////////////////////////// + +const ALCHEMY_ENDPOINT = + "https://eth-sepolia.g.alchemy.com/v2/______API_KEY______"; +const ETH_ADDRESS = "______ADDRESS______"; +const ETH_PRIVATE_KEY = "______PRIVATE_KEY______"; + +const testLogLevel = "info"; +const sutLogLevel = "info"; + +import "jest-extended"; +import { v4 as uuidv4 } from "uuid"; +import { Logger, LoggerProvider } from "@hyperledger/cactus-common"; +import { PluginRegistry } from "@hyperledger/cactus-core"; +import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory"; +import { + EthContractInvocationType, + PluginLedgerConnectorEthereum, + Web3SigningCredentialType, +} from "../../../main/typescript/index"; +import HelloWorldContractJson from "../../solidity/hello-world-contract/HelloWorld.json"; + +jest.setTimeout(60 * 1000); // 1 minute timeout + +// Unit Test logger setup +const log: Logger = LoggerProvider.getOrCreate({ + label: "geth-alchemy-integration-manual-check.test", + level: testLogLevel, +}); +log.info("Test started"); + +describe("Alchemy integration manual tests", () => { + let connector: PluginLedgerConnectorEthereum; + let keychainPlugin: PluginKeychainMemory; + + ////////////////////////////////// + // Environment Setup + ////////////////////////////////// + + beforeAll(async () => { + log.debug("ALCHEMY_ENDPOINT:", ALCHEMY_ENDPOINT); + + log.info("Create PluginKeychainMemory..."); + keychainPlugin = new PluginKeychainMemory({ + instanceId: uuidv4(), + keychainId: uuidv4(), + logLevel: sutLogLevel, + }); + keychainPlugin.set( + HelloWorldContractJson.contractName, + JSON.stringify(HelloWorldContractJson), + ); + + log.info("Create PluginLedgerConnectorEthereum..."); + connector = new PluginLedgerConnectorEthereum({ + rpcApiHttpHost: ALCHEMY_ENDPOINT, + logLevel: sutLogLevel, + instanceId: uuidv4(), + pluginRegistry: new PluginRegistry({ plugins: [keychainPlugin] }), + }); + }); + + afterAll(async () => { + log.info("Shutdown connector"); + await connector.shutdown(); + }); + + test("deploy sample contract to testnet", async () => { + const deployOut = await connector.deployContract({ + contractName: HelloWorldContractJson.contractName, + keychainId: keychainPlugin.getKeychainId(), + web3SigningCredential: { + ethAccount: ETH_ADDRESS, + secret: ETH_PRIVATE_KEY, + type: Web3SigningCredentialType.PrivateKeyHex, + }, + gas: 300000, + gasPrice: 400000, + }); + expect(deployOut).toBeTruthy(); + expect(deployOut.transactionReceipt).toBeTruthy(); + log.debug("Deployment receipt:", deployOut.transactionReceipt); + const { contractAddress, transactionHash } = deployOut.transactionReceipt; + expect(contractAddress).toBeTruthy(); + expect(transactionHash).toBeTruthy(); + log.info(`Transaction: https://sepolia.etherscan.io/tx/${transactionHash}`); + log.info( + `New Contract: https://sepolia.etherscan.io/address/${contractAddress}`, + ); + + expect(typeof contractAddress).toBe("string"); + const invokeOut = await connector.getContractInfoKeychain({ + contractName: HelloWorldContractJson.contractName, + invocationType: EthContractInvocationType.Call, + methodName: "sayHello", + keychainId: keychainPlugin.getKeychainId(), + params: [], + web3SigningCredential: { + ethAccount: ETH_ADDRESS, + secret: ETH_PRIVATE_KEY, + type: Web3SigningCredentialType.PrivateKeyHex, + }, + gas: "300000", + }); + expect(invokeOut).toBeTruthy(); + expect(invokeOut.callOutput).toBeTruthy(); + expect(typeof invokeOut.callOutput).toBe("string"); + log.info("Method query OK!"); + }); +}); diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/unit/model-type-guards.test.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/unit/model-type-guards.test.ts index fdd4daac699..ee0886dda9c 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/unit/model-type-guards.test.ts +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/unit/model-type-guards.test.ts @@ -3,7 +3,7 @@ import { isWeb3SigningCredentialGethKeychainPassword, isWeb3SigningCredentialNone, isWeb3SigningCredentialPrivateKeyHex, -} from "../../../main/typescript/model-type-guards"; +} from "../../../main/typescript/types/model-type-guards"; import { Web3SigningCredentialGethKeychainPassword, Web3SigningCredentialType, diff --git a/packages/cactus-plugin-ledger-connector-ethereum/tsconfig.json b/packages/cactus-plugin-ledger-connector-ethereum/tsconfig.json index 2c24a0daea8..85fe5ef9105 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/tsconfig.json +++ b/packages/cactus-plugin-ledger-connector-ethereum/tsconfig.json @@ -27,6 +27,9 @@ }, { "path": "../cactus-test-tooling/tsconfig.json" + }, + { + "path": "../cactus-test-geth-ledger/tsconfig.json" } ] } \ No newline at end of file diff --git a/packages/cactus-test-geth-ledger/src/main/typescript/geth-test-ledger.ts b/packages/cactus-test-geth-ledger/src/main/typescript/geth-test-ledger.ts index 2fa9bc53e6e..de3c2777705 100644 --- a/packages/cactus-test-geth-ledger/src/main/typescript/geth-test-ledger.ts +++ b/packages/cactus-test-geth-ledger/src/main/typescript/geth-test-ledger.ts @@ -37,7 +37,8 @@ export const GETH_TEST_LEDGER_DEFAULT_OPTIONS = Object.freeze({ export const WHALE_ACCOUNT_PRIVATE_KEY = "86bbf98cf5e5b1c43d2c8701764897357e0fa24982c0137efabf6dc3a6e7b69e"; -export const WHALE_ACCOUNT_ADDRESS = "6a2ec8c50ba1a9ce47c52d1cb5b7136ee9d0ccc0"; +export const WHALE_ACCOUNT_ADDRESS = + "0x6a2ec8c50ba1a9ce47c52d1cb5b7136ee9d0ccc0"; export class GethTestLedger { public static readonly CLASS_NAME = "GethTestLedger"; diff --git a/packages/cactus-test-plugin-ledger-connector-ethereum/package.json b/packages/cactus-test-plugin-ledger-connector-ethereum/package.json index cfb219798d0..cdbd2d88e36 100644 --- a/packages/cactus-test-plugin-ledger-connector-ethereum/package.json +++ b/packages/cactus-test-plugin-ledger-connector-ethereum/package.json @@ -57,12 +57,12 @@ "@hyperledger/cactus-plugin-keychain-memory": "2.0.0-alpha.1", "@hyperledger/cactus-plugin-ledger-connector-ethereum": "2.0.0-alpha.1", "@hyperledger/cactus-verifier-client": "2.0.0-alpha.1", - "web3": "1.10.0", - "web3-utils": "1.10.0" + "web3": "4.0.3", + "web3-utils": "4.0.3" }, "devDependencies": { + "@hyperledger/cactus-test-geth-ledger": "2.0.0-alpha.1", "@hyperledger/cactus-test-tooling": "2.0.0-alpha.1", - "@types/convict": "6.1.1", "@types/lodash": "4.14.195", "lodash": "4.17.21" }, diff --git a/packages/cactus-test-plugin-ledger-connector-ethereum/src/test/typescript/integration/api-client/verifier-integration-with-ethereum-connector.test.ts b/packages/cactus-test-plugin-ledger-connector-ethereum/src/test/typescript/integration/api-client/verifier-integration-with-ethereum-connector.test.ts index 013b9b12f2d..d1766a62a03 100644 --- a/packages/cactus-test-plugin-ledger-connector-ethereum/src/test/typescript/integration/api-client/verifier-integration-with-ethereum-connector.test.ts +++ b/packages/cactus-test-plugin-ledger-connector-ethereum/src/test/typescript/integration/api-client/verifier-integration-with-ethereum-connector.test.ts @@ -1,6 +1,9 @@ /* * Copyright 2022 Hyperledger Cactus Contributors * SPDX-License-Identifier: Apache-2.0 + * + * @todo - there's an ugly warning in the end caused by some issue with clearing web3js ws connection to geth. + * Investigate and report if needed. */ ////////////////////////////////// @@ -10,15 +13,10 @@ const testLogLevel = "info"; const sutLogLevel = "info"; -const containerImageName = - "ghcr.io/hyperledger/cactus-quorum-multi-party-all-in-one"; -const containerImageVersion = "2022-04-06-fd10e27"; - import "jest-extended"; import lodash from "lodash"; import { v4 as uuidv4 } from "uuid"; -import Web3 from "web3"; -import { AbiItem } from "web3-utils"; +import Web3, { ContractAbi } from "web3"; import { PluginRegistry } from "@hyperledger/cactus-core"; import { PluginLedgerConnectorEthereum, @@ -40,12 +38,13 @@ import { AuthorizationProtocol, ConfigService, } from "@hyperledger/cactus-cmd-api-server"; - import { Verifier, VerifierFactory } from "@hyperledger/cactus-verifier-client"; +import { pruneDockerAllIfGithubAction } from "@hyperledger/cactus-test-tooling"; import { - pruneDockerAllIfGithubAction, - QuorumMultiPartyTestLedger, -} from "@hyperledger/cactus-test-tooling"; + GethTestLedger, + WHALE_ACCOUNT_ADDRESS, +} from "@hyperledger/cactus-test-geth-ledger"; +import { PayableMethodObject } from "web3-eth-contract"; import HelloWorldContractJson from "../../../solidity/hello-world-contract/HelloWorld.json"; @@ -54,20 +53,17 @@ const log: Logger = LoggerProvider.getOrCreate({ level: testLogLevel, }); +const containerImageName = "ghcr.io/hyperledger/cacti-geth-all-in-one"; +const containerImageVersion = "2023-07-27-2a8c48ed6"; + log.info("Test started"); describe("Verifier integration with ethereum connector tests", () => { - let ethereumTestLedger: QuorumMultiPartyTestLedger; + let ethereumTestLedger: GethTestLedger; let apiServer: ApiServer; let connector: PluginLedgerConnectorEthereum; let web3: Web3; let keychainPlugin: PluginKeychainMemory; - let connectionProfile: ReturnType< - typeof QuorumMultiPartyTestLedger.prototype.getKeys - > extends Promise - ? T - : never; - const ethereumValidatorId = "testEthereumId"; let globalVerifierFactory: VerifierFactory; @@ -80,21 +76,15 @@ describe("Verifier integration with ethereum connector tests", () => { await pruneDockerAllIfGithubAction({ logLevel: testLogLevel }); // Start Ledger - log.info("Start QuorumMultiPartyTestLedger..."); - log.debug("QuorumMultiParty image:", containerImageName); - log.debug("QuorumMultiParty version:", containerImageVersion); - ethereumTestLedger = new QuorumMultiPartyTestLedger({ + log.info("Start GethTestLedger..."); + // log.debug("GethTestLedger image:", containerImageName); + ethereumTestLedger = new GethTestLedger({ containerImageName, containerImageVersion, logLevel: sutLogLevel, - emitContainerLogs: false, - //useRunningLedger: true, }); await ethereumTestLedger.start(); - connectionProfile = await ethereumTestLedger.getKeys(); - log.debug("connectionProfile:", connectionProfile); - // Setup ApiServer plugins const plugins: ICactusPlugin[] = []; const pluginRegistry = new PluginRegistry({ plugins }); @@ -112,9 +102,11 @@ describe("Verifier integration with ethereum connector tests", () => { plugins.push(keychainPlugin); log.info("Create PluginLedgerConnectorEthereum..."); + const rpcApiHttpHost = await ethereumTestLedger.getRpcApiHttpHost(); + const rpcApiWsHost = await ethereumTestLedger.getRpcApiWebSocketHost(); connector = new PluginLedgerConnectorEthereum({ - rpcApiHttpHost: connectionProfile.quorum.member1.url, - rpcApiWsHost: connectionProfile.quorum.member1.wsUrl, + rpcApiHttpHost, + rpcApiWsHost, logLevel: sutLogLevel, instanceId: uuidv4(), pluginRegistry: new PluginRegistry({ plugins: [keychainPlugin] }), @@ -122,7 +114,7 @@ describe("Verifier integration with ethereum connector tests", () => { plugins.push(connector); // Create web3 provider for test - web3 = new Web3(connectionProfile.quorum.member1.url); + web3 = new Web3(rpcApiHttpHost); // Create Api Server log.info("Create ApiServer..."); @@ -157,7 +149,7 @@ describe("Verifier integration with ethereum connector tests", () => { [ { validatorID: ethereumValidatorId, - validatorType: "QUORUM_2X", + validatorType: "ETH_1X", basePath: apiHost, logLevel: sutLogLevel, }, @@ -237,7 +229,7 @@ describe("Verifier integration with ethereum connector tests", () => { describe("web3EthContract tests", () => { let verifier: Verifier; let contractCommon: { - abi: AbiItem[]; + abi: ContractAbi; address: string; }; @@ -253,9 +245,9 @@ describe("Verifier integration with ethereum connector tests", () => { contractName: HelloWorldContractJson.contractName, keychainId: keychainPlugin.getKeychainId(), web3SigningCredential: { - ethAccount: connectionProfile.quorum.member2.accountAddress, - secret: connectionProfile.quorum.member2.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, + ethAccount: WHALE_ACCOUNT_ADDRESS, + secret: "", + type: Web3SigningCredentialType.GethKeychainPassword, }, gas: 1000000, }); @@ -265,16 +257,15 @@ describe("Verifier integration with ethereum connector tests", () => { expect(deployOut.transactionReceipt.status).toBeTrue(); contractCommon = { - abi: HelloWorldContractJson.abi as AbiItem[], + abi: HelloWorldContractJson.abi, address: deployOut.transactionReceipt.contractAddress as string, }; }); test("Invalid web3EthContract calls are rejected by EthereumApiClient", async () => { // Define correct input parameters - const correctContract: Record = lodash.clone( - contractCommon, - ); + const correctContract: Record = + lodash.clone(contractCommon); const correctMethod: Record = { type: "web3EthContract", command: "call", @@ -367,7 +358,7 @@ describe("Verifier integration with ethereum connector tests", () => { }; const argsSend = { args: { - from: connectionProfile.quorum.member1.accountAddress, + from: WHALE_ACCOUNT_ADDRESS, }, }; @@ -377,7 +368,7 @@ describe("Verifier integration with ethereum connector tests", () => { argsSend, ); expect(resultsSend.status).toEqual(200); - expect(resultsSend.data.status).toBeTrue(); + expect(resultsSend.data.status).toEqual("1"); // 2. Get new, updated value (call) const methodCall = { @@ -407,7 +398,7 @@ describe("Verifier integration with ethereum connector tests", () => { }; const argsEncode = { args: { - from: connectionProfile.quorum.member1.accountAddress, + from: WHALE_ACCOUNT_ADDRESS, }, }; @@ -424,9 +415,10 @@ describe("Verifier integration with ethereum connector tests", () => { contractCommon.abi, contractCommon.address, ); - const web3Encode = await web3Contract.methods - .setName(...methodEncode.params) - .encodeABI(argsEncode); + const methodRef = web3Contract.methods.setName as ( + ...args: unknown[] + ) => PayableMethodObject; + const web3Encode = methodRef(...methodEncode.params).encodeABI(); expect(resultsEncode.data).toEqual(web3Encode); }); @@ -446,17 +438,20 @@ describe("Verifier integration with ethereum connector tests", () => { argsEstimateGas, ); expect(resultsEstimateGas.status).toEqual(200); - expect(resultsEstimateGas.data).toBeGreaterThan(0); + expect(Number(resultsEstimateGas.data)).toBeGreaterThan(0); // Compare gas estimate with direct web3 call const web3Contract = new web3.eth.Contract( contractCommon.abi, contractCommon.address, ); - const web3Encode = await web3Contract.methods - .setName(...methodEstimateGas.params) - .estimateGas(argsEstimateGas); - expect(resultsEstimateGas.data).toEqual(web3Encode); + const methodRef = web3Contract.methods.setName as ( + ...args: unknown[] + ) => PayableMethodObject; + const web3Encode = await methodRef( + ...methodEstimateGas.params, + ).estimateGas(argsEstimateGas); + expect(resultsEstimateGas.data).toEqual(web3Encode.toString()); }); test("Sending transaction with sendAsyncRequest works", async () => { @@ -472,7 +467,7 @@ describe("Verifier integration with ethereum connector tests", () => { }; const argsSendAsync = { args: { - from: connectionProfile.quorum.member1.accountAddress, + from: WHALE_ACCOUNT_ADDRESS, }, }; @@ -509,7 +504,7 @@ describe("Verifier integration with ethereum connector tests", () => { // web3Eth.getBalance const contract = {}; const method = { type: "web3Eth", command: "getBalance" }; - const args = { args: [connectionProfile.quorum.member2.accountAddress] }; + const args = { args: [WHALE_ACCOUNT_ADDRESS] }; const results = await globalVerifierFactory .getVerifier(ethereumValidatorId) @@ -526,7 +521,7 @@ describe("Verifier integration with ethereum connector tests", () => { command: "getBalance", }; const correctArgs: any = { - args: [connectionProfile.quorum.member2.accountAddress], + args: [WHALE_ACCOUNT_ADDRESS], }; const verifier = globalVerifierFactory.getVerifier(ethereumValidatorId); @@ -576,20 +571,19 @@ describe("Verifier integration with ethereum connector tests", () => { expect(results.errorDetail).toBeTruthy(); }); - function assertBlockHeader(header: Web3BlockHeader) { + function assertBlockHeader(header?: Web3BlockHeader) { + if (!header) { + throw new Error("Header is missing!"); + } + // Check if defined and with expected type // Ignore nullable / undefine-able fields expect(typeof header.parentHash).toEqual("string"); expect(typeof header.sha3Uncles).toEqual("string"); expect(typeof header.miner).toEqual("string"); - expect(typeof header.stateRoot).toEqual("string"); - expect(typeof header.logsBloom).toEqual("string"); - expect(typeof header.number).toEqual("number"); - expect(typeof header.gasLimit).toEqual("number"); - expect(typeof header.gasUsed).toEqual("number"); - expect(typeof header.extraData).toEqual("string"); - expect(typeof header.nonce).toEqual("string"); - expect(typeof header.hash).toEqual("string"); + expect(typeof header.number).toEqual("string"); + expect(typeof header.gasLimit).toEqual("string"); + expect(typeof header.gasUsed).toEqual("string"); expect(typeof header.difficulty).toEqual("string"); } @@ -605,11 +599,13 @@ describe("Verifier integration with ethereum connector tests", () => { expect(ledgerEvent.data?.blockHeader).toBeTruthy(); // check some fields - assertBlockHeader(ledgerEvent.data?.blockHeader as Web3BlockHeader); + assertBlockHeader(ledgerEvent.data?.blockHeader); }); test("Monitor new blocks data on Ethereum", async () => { const ledgerEvent = await monitorAndGetBlock({ getBlockData: true }); + //const ledgerEvent = await monitorAndGetBlock(); + log.info("ledgerEvent", ledgerEvent); // assert well-formed output expect(ledgerEvent.id).toEqual(""); expect(ledgerEvent.verifierId).toEqual(ethereumValidatorId); @@ -620,12 +616,13 @@ describe("Verifier integration with ethereum connector tests", () => { expect(ledgerEvent.data?.blockData).toBeTruthy(); // check some fields - assertBlockHeader(ledgerEvent.data?.blockData as Web3BlockHeader); - expect(typeof ledgerEvent.data?.blockData?.size).toEqual("number"); + assertBlockHeader( + ledgerEvent.data?.blockData as unknown as Web3BlockHeader, + ); // remove as unknown + expect(typeof ledgerEvent.data?.blockData?.size).toEqual("string"); expect(typeof ledgerEvent.data?.blockData?.totalDifficulty).toEqual( "string", ); expect(typeof ledgerEvent.data?.blockData?.uncles).toEqual("object"); - expect(typeof ledgerEvent.data?.blockData?.transactions).toEqual("object"); }); }); diff --git a/packages/cactus-test-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/deploy-contract-via-web-service.test.ts b/packages/cactus-test-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/deploy-contract-via-web-service.test.ts deleted file mode 100644 index ae576303dea..00000000000 --- a/packages/cactus-test-plugin-ledger-connector-ethereum/src/test/typescript/integration/plugin-ledger-connector-ethereum/deploy-contract/deploy-contract-via-web-service.test.ts +++ /dev/null @@ -1,270 +0,0 @@ -import Web3 from "web3"; -import { v4 as uuidV4 } from "uuid"; -import convict from "convict"; -import "jest-extended"; -import { - QuorumTestLedger, - IQuorumGenesisOptions, - IAccount, - pruneDockerAllIfGithubAction, -} from "@hyperledger/cactus-test-tooling"; -import HelloWorldContractJson from "../../../../solidity/hello-world-contract/HelloWorld.json"; -import { - Logger, - LoggerProvider, - LogLevelDesc, -} from "@hyperledger/cactus-common"; -import { - PluginLedgerConnectorEthereum, - DefaultApi, - Web3SigningCredentialType, - DeployContractSolidityBytecodeV1Request, - EthContractInvocationType, - Configuration, -} from "@hyperledger/cactus-plugin-ledger-connector-ethereum"; - -import { - ApiServer, - AuthorizationProtocol, - ConfigService, - ICactusApiServerOptions, -} from "@hyperledger/cactus-cmd-api-server"; - -import { PluginRegistry } from "@hyperledger/cactus-core"; -import { ICactusPlugin, IPluginKeychain } from "@hyperledger/cactus-core-api"; -import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory"; -import { AddressInfo } from "net"; -const testCase = "deploys contract via REST API"; -describe(testCase, () => { - const logLevel: LogLevelDesc = "TRACE"; - const log: Logger = LoggerProvider.getOrCreate({ - label: "test-deploy-contract-via-web-service", - level: logLevel, - }); - const ledger = new QuorumTestLedger(); - - const plugins: ICactusPlugin[] = []; - const pluginRegistry = new PluginRegistry({ plugins }); - const contractName = "HelloWorld"; - let addressInfo: AddressInfo, - configService: ConfigService, - cactusApiServerOptions: ICactusApiServerOptions, - config: convict.Config, - apiServer: ApiServer, - protocol, - basePath: string, - configuration, - rpcApiHttpHost: string, - kvStoragePlugin: IPluginKeychain, - client: DefaultApi, - ethereumGenesisOptions: IQuorumGenesisOptions, - firstHighNetWorthAccount: string, - apiServerStartOut: { - addressInfoCockpit?: AddressInfo; - addressInfoApi: AddressInfo; - addressInfoGrpc: AddressInfo; - }; - - // Instantiate a ledger object - // Gather parameteres needed to run an embedded ApiServer which can connect to/interact with said ledger - - afterAll(async () => await apiServer.shutdown()); - beforeAll(async () => { - const pruning = pruneDockerAllIfGithubAction({ logLevel }); - await expect(pruning).resolves.toBeTruthy(); - }); - afterAll(async () => { - await ledger.stop(); - await ledger.destroy(); - }); - afterAll(async () => { - const pruning = pruneDockerAllIfGithubAction({ logLevel }); - await expect(pruning).resolves.toBeTruthy(); - }); - - beforeAll(async () => { - configService = new ConfigService(); - cactusApiServerOptions = await configService.newExampleConfig(); - cactusApiServerOptions.authorizationProtocol = AuthorizationProtocol.NONE; - cactusApiServerOptions.configFile = ""; - cactusApiServerOptions.apiCorsDomainCsv = "*"; - cactusApiServerOptions.apiTlsEnabled = false; - cactusApiServerOptions.apiPort = 0; - config = await configService.newExampleConfigConvict( - cactusApiServerOptions, - ); - await ledger.start(); - rpcApiHttpHost = await ledger.getRpcApiHttpHost(); - - kvStoragePlugin = new PluginKeychainMemory({ - backend: new Map(), - instanceId: uuidV4(), - keychainId: uuidV4(), - }); - kvStoragePlugin.set( - HelloWorldContractJson.contractName, - JSON.stringify(HelloWorldContractJson), - ); - plugins.push(kvStoragePlugin); - - const ledgerConnectorEthereum = new PluginLedgerConnectorEthereum({ - instanceId: uuidV4(), - rpcApiHttpHost, - pluginRegistry: new PluginRegistry({ plugins: [kvStoragePlugin] }), - }); - plugins.push(ledgerConnectorEthereum); - - apiServer = new ApiServer({ - config: config.getProperties(), - pluginRegistry, - }); - - // Start the API server which now is connected to the ethereum ledger - apiServerStartOut = await apiServer.start(); - log.debug(`ApiServer.started OK:`, apiServerStartOut); - const httpServer = apiServer.getHttpServerApi(); - addressInfo = httpServer?.address() as AddressInfo; - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - protocol = config.get("apiTlsEnabled") ? "https:" : "http:"; - basePath = `${protocol}//${addressInfo.address}:${addressInfo.port}`; - configuration = new Configuration({ basePath }); - client = new DefaultApi(configuration); - // Find a high net worth account in the genesis object of the ethereum ledger - ethereumGenesisOptions = await ledger.getGenesisJsObject(); - const highNetWorthAccounts: string[] = Object.keys( - ethereumGenesisOptions.alloc, - ).filter((address: string) => { - const anAccount: IAccount = ethereumGenesisOptions.alloc[address]; - const balance: number = parseInt(anAccount.balance, 10); - return balance > 10e7; - }); - [firstHighNetWorthAccount] = highNetWorthAccounts; - }); - - test(testCase, async () => { - expect(ethereumGenesisOptions); - expect(ethereumGenesisOptions.alloc); - - // 6. Instantiate the SDK dynamically with whatever port the API server ended up bound to (port 0) - log.debug(`AddressInfo: `, addressInfo); - log.debug(`SDK base path: %s`, basePath); - - // 7. Assemble request to invoke the deploy contract method of the ethereum ledger connector plugin via the REST API - const req: DeployContractSolidityBytecodeV1Request = { - contractName: HelloWorldContractJson.contractName, - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - keychainId: kvStoragePlugin.getKeychainId(), - gas: 1000000, - }; - - // 8. Deploy smart contract by issuing REST API call - const res = await client.deployContractSolBytecodeV1(req); - - expect(res).toBeTruthy(); - expect(res.status).toBeWithin(199, 300); - }); - - test("Invoke contract via SDK ApiClient object", async () => { - const web3 = new Web3(rpcApiHttpHost); - const testEthAccount = web3.eth.accounts.create(uuidV4()); - - const res1 = await client.runTransactionV1({ - web3SigningCredential: { - ethAccount: firstHighNetWorthAccount, - secret: "", - type: Web3SigningCredentialType.GethKeychainPassword, - }, - transactionConfig: { - from: firstHighNetWorthAccount, - to: testEthAccount.address, - value: 10e9, - }, - }); - expect(res1).toBeTruthy(); - expect(res1.status).toBeWithin(199, 300); - - const balance = await web3.eth.getBalance(testEthAccount.address); - expect(balance).toBeTruthy(); - expect(parseInt(balance, 10)).toEqual(10e9); - - const sayHelloRes = await client.invokeContractV1({ - contractName, - invocationType: EthContractInvocationType.Call, - methodName: "sayHello", - params: [], - web3SigningCredential: { - type: Web3SigningCredentialType.None, - }, - keychainId: kvStoragePlugin.getKeychainId(), - }); - expect(sayHelloRes).toBeTruthy(); - expect(sayHelloRes.status).toBeWithin(199, 300); - expect(sayHelloRes.data).toBeTruthy(); - expect(sayHelloRes.data.callOutput).toBeTruthy(); - expect(typeof sayHelloRes.data.callOutput).toBeString(); - expect(sayHelloRes.data.callOutput).toBe("Hello World!"); - - const newName = `DrCactus${uuidV4()}`; - const setName1Res = await client.invokeContractV1({ - contractName, - invocationType: EthContractInvocationType.Send, - methodName: "setName", - params: [newName], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - keychainId: kvStoragePlugin.getKeychainId(), - }); - expect(setName1Res).toBeTruthy(); - expect(setName1Res).toBeTruthy(); - expect(setName1Res.status).toBeWithin(199, 300); - expect(setName1Res.data).toBeTruthy(); - - const getName1Res = await client.invokeContractV1({ - contractName, - invocationType: EthContractInvocationType.Call, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - keychainId: kvStoragePlugin.getKeychainId(), - }); - expect(getName1Res).toBeTruthy(); - expect(getName1Res.status).toBeWithin(199, 300); - expect(getName1Res.data).toBeTruthy(); - expect(getName1Res.data.callOutput).toBeTruthy(); - expect(getName1Res.data.callOutput).toBeString(); - expect(getName1Res.data.callOutput).toEqual(newName); - - const getName2Res = await client.invokeContractV1({ - contractName, - invocationType: EthContractInvocationType.Send, - methodName: "getName", - params: [], - gas: 1000000, - web3SigningCredential: { - ethAccount: testEthAccount.address, - secret: testEthAccount.privateKey, - type: Web3SigningCredentialType.PrivateKeyHex, - }, - keychainId: kvStoragePlugin.getKeychainId(), - }); - - expect(getName2Res).toBeTruthy(); - expect(getName2Res.status).toBeWithin(199, 300); - expect(getName2Res.data).toBeTruthy(); - expect(getName2Res.data.callOutput).not.toBeTruthy(); - }); -}); diff --git a/packages/cactus-test-plugin-ledger-connector-ethereum/tsconfig.json b/packages/cactus-test-plugin-ledger-connector-ethereum/tsconfig.json index 635675a1c8e..15f75ed343a 100644 --- a/packages/cactus-test-plugin-ledger-connector-ethereum/tsconfig.json +++ b/packages/cactus-test-plugin-ledger-connector-ethereum/tsconfig.json @@ -33,6 +33,9 @@ }, { "path": "../cactus-test-tooling/tsconfig.json" + }, + { + "path": "../cactus-test-geth-ledger/tsconfig.json" } ] } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 3f5ffc95dca..c0443371f9b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7044,6 +7044,7 @@ __metadata: "@hyperledger/cactus-core": 2.0.0-alpha.1 "@hyperledger/cactus-core-api": 2.0.0-alpha.1 "@hyperledger/cactus-plugin-keychain-memory": 2.0.0-alpha.1 + "@hyperledger/cactus-test-geth-ledger": 2.0.0-alpha.1 "@hyperledger/cactus-test-tooling": 2.0.0-alpha.1 "@types/express": 4.17.13 "@types/minimist": 1.2.2 @@ -7059,10 +7060,11 @@ __metadata: socket.io: 4.5.4 socket.io-client: 4.5.4 typescript-optional: 2.0.1 - web3: 1.10.0 - web3-eth: 1.10.0 - web3-eth-contract: 1.10.0 - web3-utils: 1.10.0 + web3: 4.0.3 + web3-eth: 4.0.3 + web3-eth-accounts: 4.0.3 + web3-eth-contract: 4.0.3 + web3-utils: 4.0.3 bin: cacti-ethereum-connector-status: dist/lib/scripts/get-ethereum-connector-status.js languageName: unknown @@ -7517,7 +7519,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperledger/cactus-test-geth-ledger@workspace:packages/cactus-test-geth-ledger": +"@hyperledger/cactus-test-geth-ledger@2.0.0-alpha.1, @hyperledger/cactus-test-geth-ledger@workspace:packages/cactus-test-geth-ledger": version: 0.0.0-use.local resolution: "@hyperledger/cactus-test-geth-ledger@workspace:packages/cactus-test-geth-ledger" dependencies: @@ -7617,13 +7619,13 @@ __metadata: "@hyperledger/cactus-core-api": 2.0.0-alpha.1 "@hyperledger/cactus-plugin-keychain-memory": 2.0.0-alpha.1 "@hyperledger/cactus-plugin-ledger-connector-ethereum": 2.0.0-alpha.1 + "@hyperledger/cactus-test-geth-ledger": 2.0.0-alpha.1 "@hyperledger/cactus-test-tooling": 2.0.0-alpha.1 "@hyperledger/cactus-verifier-client": 2.0.0-alpha.1 - "@types/convict": 6.1.1 "@types/lodash": 4.14.195 lodash: 4.17.21 - web3: 1.10.0 - web3-utils: 1.10.0 + web3: 4.0.3 + web3-utils: 4.0.3 languageName: unknown linkType: soft @@ -46038,6 +46040,21 @@ __metadata: languageName: node linkType: hard +"web3-eth-contract@npm:4.0.3": + version: 4.0.3 + resolution: "web3-eth-contract@npm:4.0.3" + dependencies: + web3-core: ^4.0.3 + web3-errors: ^1.0.2 + web3-eth: ^4.0.3 + web3-eth-abi: ^4.0.3 + web3-types: ^1.0.2 + web3-utils: ^4.0.3 + web3-validator: ^1.0.2 + checksum: 24f5cc9645af7c2c6ffe6ff2dd819c576904b959e4d2388bb9565014bfa256c75903450b9de48a6e734bbca4a5542c441246497a858ed392cf71340bb1e961ca + languageName: node + linkType: hard + "web3-eth-contract@npm:^4.0.3, web3-eth-contract@npm:^4.0.5": version: 4.0.5 resolution: "web3-eth-contract@npm:4.0.5" @@ -46516,6 +46533,25 @@ __metadata: languageName: node linkType: hard +"web3-eth@npm:4.0.3": + version: 4.0.3 + resolution: "web3-eth@npm:4.0.3" + dependencies: + setimmediate: ^1.0.5 + web3-core: ^4.0.3 + web3-errors: ^1.0.2 + web3-eth-abi: ^4.0.3 + web3-eth-accounts: ^4.0.3 + web3-net: ^4.0.3 + web3-providers-ws: ^4.0.3 + web3-rpc-methods: ^1.0.2 + web3-types: ^1.0.2 + web3-utils: ^4.0.3 + web3-validator: ^1.0.2 + checksum: ce758d952cd1bd414a9449defc68d6a3c8fc1d183dfb152a2130e774018bef9ff7af70844884cabe5b4e5238823a061eba2c7622e49ea7afe8f3cec112930f2e + languageName: node + linkType: hard + "web3-eth@npm:4.1.1, web3-eth@npm:^4.0.3, web3-eth@npm:^4.1.1": version: 4.1.1 resolution: "web3-eth@npm:4.1.1"