diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 11e0cf92f2..32120498ea 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 6ab6638358..063604d5b8 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 361ce76a6a..ff19a579f6 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 64e5800af9..9b2a38f9d7 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 @@ -438,6 +438,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`); @@ -688,6 +694,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); @@ -804,4 +812,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 597251cf10..52533de45d 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/README.md +++ b/packages/cactus-plugin-ledger-connector-ethereum/README.md @@ -1,19 +1,21 @@ # `@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) +- [Usage](#usage) +- [EthereumApiClient](#ethereumapiclient) +- [Runing the tests](#running-the-tests) +- [Prometheus Exporter](#prometheus-exporter) +- [Contributing](#contributing) +- [License](#license) +- [Acknowledgments](#acknowledgments) ## Getting Started @@ -23,6 +25,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 +33,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 +60,7 @@ async invokeRawWeb3EthContract(req: InvokeRawWeb3EthContractV1Request): Promise< ``` Call example to deploy a contract: + ```typescript const deployOut = await connector.deployContract({ web3SigningCredential: { @@ -64,15 +72,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 +91,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 +104,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 +149,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 +166,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 +174,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 +187,7 @@ docker run \ ``` Launch container with plugin configuration as a **CLI argument**: + ```sh docker run \ --rm \ @@ -183,6 +201,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 +221,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 +239,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 +266,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 +291,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 +316,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 +348,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 0b58ad331d..12a1f09d75 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.2", + "@hyperledger/cactus-test-geth-ledger": "2.0.0-alpha.2", "@hyperledger/cactus-test-tooling": "2.0.0-alpha.2", "@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 12eedaab27..56bf44de28 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 @@ -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 18636a4639..6d58572d1f 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 a232aeafe3..29203b0e36 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 @@ -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/plugin-ledger-connector-ethereum.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/plugin-ledger-connector-ethereum.ts index 7feac3680a..bc270cb298 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 b7bc4f674f..22a8089236 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 312e8851a2..14f74a868a 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 0000000000..0c5c8f9df0 --- /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 bb9ef90fa1..8c0c20bbc1 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 3badc666cf..f06c9a5596 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 e927fe4437..432a100f13 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 559665230a..f64943b4e0 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 18fa5b2677..d262b0c0a5 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 b6a88567b0..32a8a4f62e 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 39bfa68378..c6b946491e 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 c56f76ebbb..64cdc88229 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 0000000000..3eb1600267 --- /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 0000000000..cb30edb8ac --- /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 0000000000..32f827d56b --- /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 0000000000..f50aab20f3 --- /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 a8904ed1d5..8b0574480b 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 70b75dfd5f..0702064ff8 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 cab629aa34..0000000000 --- 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 41f43ae322..0000000000 --- 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 73ccffc596..0000000000 --- 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 0ceaaeb107..0000000000 --- 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 4e9240498e..0000000000 --- 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 94a9201924..0000000000 --- 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 d1de4ee3d4..0000000000 --- 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 3f1877786b..0000000000 --- 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 9d72d756fb..0000000000 --- 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 11389ef369..0000000000 --- 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 3e887f0ee7..0000000000 --- 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 a742872688..0000000000 --- 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 245ced94c2..0000000000 --- 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 17a5c76c35..0000000000 --- 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 0000000000..601e3d0df3 --- /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 fdd4daac69..ee0886dda9 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 2c24a0daea..85fe5ef910 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 2fa9bc53e6..de3c277770 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 864aea8e9f..bfe6efeebb 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.2", "@hyperledger/cactus-plugin-ledger-connector-ethereum": "2.0.0-alpha.2", "@hyperledger/cactus-verifier-client": "2.0.0-alpha.2", - "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.2", "@hyperledger/cactus-test-tooling": "2.0.0-alpha.2", - "@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 013b9b12f2..d1766a62a0 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 ae576303de..0000000000 --- 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 635675a1c8..15f75ed343 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 a778dfa991..62ba4dd935 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6472,6 +6472,7 @@ __metadata: "@hyperledger/cactus-core": 2.0.0-alpha.2 "@hyperledger/cactus-core-api": 2.0.0-alpha.2 "@hyperledger/cactus-plugin-keychain-memory": 2.0.0-alpha.2 + "@hyperledger/cactus-test-geth-ledger": 2.0.0-alpha.2 "@hyperledger/cactus-test-tooling": 2.0.0-alpha.2 "@types/express": 4.17.13 "@types/minimist": 1.2.2 @@ -6487,10 +6488,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 @@ -6946,7 +6948,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.2, @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: @@ -7046,13 +7048,13 @@ __metadata: "@hyperledger/cactus-core-api": 2.0.0-alpha.2 "@hyperledger/cactus-plugin-keychain-memory": 2.0.0-alpha.2 "@hyperledger/cactus-plugin-ledger-connector-ethereum": 2.0.0-alpha.2 + "@hyperledger/cactus-test-geth-ledger": 2.0.0-alpha.2 "@hyperledger/cactus-test-tooling": 2.0.0-alpha.2 "@hyperledger/cactus-verifier-client": 2.0.0-alpha.2 - "@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 @@ -43239,6 +43241,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" @@ -43717,6 +43734,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"