From e4a2cc9109638861d13b87c5489cf54a384990ca Mon Sep 17 00:00:00 2001 From: Anmol Bansal Date: Sat, 3 Feb 2024 17:50:05 +0000 Subject: [PATCH] feat(connector-polkadot): add connector pkg, openapi specs, test suite Primary Changes --------------- 1. Created openapi specs for get-prometheus-exporter-metrics, get-transaction-info, get-raw-transaction, sign-raw-transaction, run-transaction, deploy-contract-ink, and invoke-contract endpoints 2. Created relevant types for the request as well as response for the above endpoints 3. Added generated code for these endpoints 4. Created connector class with functions to interact with the polkadot ledger 5. Created webservices to interact with the endpoint and the relevant class method 6. Created unit and integration testcases in jest to test base functionality of the connector Secondary Changes ----------------- 1. Added an ink! contract for running the testcases 2. Added the polkadot connector to ci workflow 3. Created substrate.md to docs-cactus 4. Added the polkadot connector to tsconfig.json Signed-off-by: Anmol Bansal ======================================================================= feat(polkadot): creation of polkadot AIO image Primary Changes --------------- 1. Updated docker image to include multi-stage build 2. Added healthcheck to the image 3. Added Supervisord and removed unneccessary start script 4. Updated the Readme with relevant commands Signed-off-by: Anmol Bansal ======================================================================= feat(polkadot): update substrate test tooling Primary Changes --------------- 1. Added correct healthcheck for ledger container 2. Update the Substrate test ledger testcases Signed-off-by: Anmol Bansal ======================================================================= feat(polkadot): creation of readme and architecture reference diagrams Primary Changes --------------- 1. Added README.md for the connector 2. Added Architecture diagrams for the plugin Secondary Changes ----------------- 1. Added the docker image for the polkadot connector Signed-off-by: Anmol Bansal Signed-off-by: Peter Somogyvari --- .github/workflows/ci.yaml | 37 + docs-cactus/source/support.rst | 4 +- docs-cactus/source/support/substrate.md | 16 + .../Dockerfile | 10 + .../README.md | 240 ++++ ...on-endpoint-transact-cactuskeychainref.png | Bin 0 -> 37571 bytes ...ction-endpoint-transact-mnemonicstring.png | Bin 0 -> 27070 bytes ...n-transaction-endpoint-transact-signed.png | Bin 0 -> 27886 bytes .../run-transaction-endpoint-transact.png | Bin 0 -> 57291 bytes .../images/run-transaction-endpoint.png | Bin 0 -> 27880 bytes ...n-endpoint-transact-cactuskeychainref.puml | 29 + ...tion-endpoint-transact-mnemonicstring.puml | 23 + ...-transaction-endpoint-transact-signed.puml | 24 + .../run-transaction-endpoint-transact.puml | 36 + .../run-transaction-endpoint.puml | 27 + .../openapitools.json | 7 + .../package.json | 123 ++ .../src/main/json/openapi.json | 868 +++++++++++++ .../.openapi-generator-ignore | 27 + .../typescript-axios/.openapi-generator/FILES | 5 + .../.openapi-generator/VERSION | 1 + .../generated/openapi/typescript-axios/api.ts | 1151 +++++++++++++++++ .../openapi/typescript-axios/base.ts | 72 ++ .../openapi/typescript-axios/common.ts | 150 +++ .../openapi/typescript-axios/configuration.ts | 101 ++ .../openapi/typescript-axios/index.ts | 18 + .../src/main/typescript/index.ts | 1 + .../src/main/typescript/index.web.ts | 1 + .../src/main/typescript/model-type-guards.ts | 24 + ...lugin-factory-ledger-connector-polkadot.ts | 21 + .../plugin-ledger-connector-polkadot.ts | 925 +++++++++++++ .../prometheus-exporter/data-fetcher.ts | 12 + .../typescript/prometheus-exporter/metrics.ts | 10 + .../prometheus-exporter.ts | 39 + .../prometheus-exporter/response.type.ts | 3 + .../src/main/typescript/public-api.ts | 16 + .../deploy-contract-ink-endpoint.ts | 97 ++ ...et-prometheus-exporter-metrics-endpoint.ts | 105 ++ .../get-raw-transaction-endpoint.ts | 97 ++ .../get-transaction-info-endpoint.ts | 99 ++ .../web-services/invoke-contract-endpoint.ts | 96 ++ .../web-services/run-transaction-endpoint.ts | 96 ++ .../sign-raw-transaction-endpoint.ts | 100 ++ .../test/rust/fixtures/ink/flipper.contract | 1 + .../src/test/rust/fixtures/ink/flipper.wasm | Bin 0 -> 12443 bytes .../src/test/rust/fixtures/ink/metadata.json | 400 ++++++ .../src/test/rust/ink/README.md | 10 + .../src/test/rust/ink/flipper/.gitignore | 9 + .../src/test/rust/ink/flipper/Cargo.toml | 28 + .../src/test/rust/ink/flipper/lib.rs | 69 + .../integration/deploy-ink-contract.test.ts | 163 +++ .../integration/invoke-ink-contract.test.ts | 190 +++ .../integration/run-transaction.test.ts | 244 ++++ .../test/typescript/unit/api-surface.test.ts | 7 + .../unit/constructor-instantiation.test.ts | 57 + .../tsconfig.json | 32 + .../substrate-test-ledger.ts | 7 +- .../substrate-test-ledger-constructor.test.ts | 10 - ...te-test-ledger-multiple-concurrent.test.ts | 10 - tools/docker/substrate-all-in-one/Dockerfile | 57 +- tools/docker/substrate-all-in-one/README.md | 16 +- .../substrate-all-in-one/healthcheck.sh | 9 + tools/docker/substrate-all-in-one/start.sh | 8 - .../substrate-all-in-one/supervisord.conf | 22 + tsconfig.json | 3 + yarn.lock | 946 +++++++++++++- 66 files changed, 6900 insertions(+), 109 deletions(-) create mode 100644 docs-cactus/source/support/substrate.md create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/Dockerfile create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/README.md create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/images/run-transaction-endpoint-transact-cactuskeychainref.png create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/images/run-transaction-endpoint-transact-mnemonicstring.png create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/images/run-transaction-endpoint-transact-signed.png create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/images/run-transaction-endpoint-transact.png create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/images/run-transaction-endpoint.png create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/run-transaction-endpoint-transact-cactuskeychainref.puml create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/run-transaction-endpoint-transact-mnemonicstring.puml create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/run-transaction-endpoint-transact-signed.puml create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/run-transaction-endpoint-transact.puml create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/run-transaction-endpoint.puml create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/openapitools.json create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/package.json create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/json/openapi.json create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator-ignore create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/FILES create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/VERSION create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/api.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/base.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/common.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/configuration.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/index.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/index.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/index.web.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/model-type-guards.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/plugin-factory-ledger-connector-polkadot.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/plugin-ledger-connector-polkadot.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/prometheus-exporter/data-fetcher.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/prometheus-exporter/metrics.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/prometheus-exporter/prometheus-exporter.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/prometheus-exporter/response.type.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/public-api.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/deploy-contract-ink-endpoint.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/get-prometheus-exporter-metrics-endpoint.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/get-raw-transaction-endpoint.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/get-transaction-info-endpoint.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/invoke-contract-endpoint.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/run-transaction-endpoint.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/sign-raw-transaction-endpoint.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/fixtures/ink/flipper.contract create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/fixtures/ink/flipper.wasm create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/fixtures/ink/metadata.json create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/ink/README.md create mode 100755 packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/ink/flipper/.gitignore create mode 100755 packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/ink/flipper/Cargo.toml create mode 100755 packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/ink/flipper/lib.rs create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/test/typescript/integration/deploy-ink-contract.test.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/test/typescript/integration/invoke-ink-contract.test.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/test/typescript/integration/run-transaction.test.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/test/typescript/unit/api-surface.test.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/src/test/typescript/unit/constructor-instantiation.test.ts create mode 100644 packages/cactus-plugin-ledger-connector-polkadot/tsconfig.json create mode 100755 tools/docker/substrate-all-in-one/healthcheck.sh delete mode 100755 tools/docker/substrate-all-in-one/start.sh create mode 100644 tools/docker/substrate-all-in-one/supervisord.conf diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 86b6bf510ea..1f6dbe9e4fd 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -19,6 +19,7 @@ jobs: compute_changed_packages: outputs: cmd-api-server-changed: ${{ steps.changes.outputs.cmd-api-server-changed }} + plugin-ledger-connector-polkadot-changed: ${{ steps.changes.outputs.plugin-ledger-connector-polkadot-changed }} plugin-ledger-connector-aries-changed: ${{ steps.changes.outputs.plugin-ledger-connector-aries-changed }} plugin-ledger-connector-besu-changed: ${{ steps.changes.outputs.plugin-ledger-connector-besu-changed }} plugin-ledger-connector-corda-changed: ${{ steps.changes.outputs.plugin-ledger-connector-corda-changed }} @@ -49,6 +50,14 @@ jobs: - './packages/cactus-plugin-keychain-vault/**' # - './.github/workflows/ci.yaml' + plugin-ledger-connector-polkadot-changed: + - './packages/cactus-plugin-ledger-connector-polkadot/**!(*.md|*.css|*.html|*.jpg|*.jpeg|*.png)' + - './packages/cactus-common/**!(*.md|*.css|*.html|*.jpg|*.jpeg|*.png)' + - './packages/cactus-core/**!(*.md|*.css|*.html|*.jpg|*.jpeg|*.png)' + - './packages/cactus-core-api/**!(*.md|*.css|*.html|*.jpg|*.jpeg|*.png)' + - './packages/cactus-test-tooling/**!(*.md|*.css|*.html|*.jpg|*.jpeg|*.png)' + # - './.github/workflows/ci.yaml' + plugin-ledger-connector-aries-changed: - './packages/cactus-plugin-ledger-connector-aries/**' - './packages/cactus-common/**' @@ -982,6 +991,34 @@ jobs: node-version: ${{ env.NODEJS_VERSION }} - uses: actions/checkout@v3.5.2 + - id: yarn-cache + name: Restore Yarn Cache + uses: actions/cache@v3.3.1 + with: + key: ${{ runner.os }}-yarn-${{ hashFiles('./yarn.lock') }} + path: ./.yarn/ + restore-keys: | + ${{ runner.os }}-yarn-${{ hashFiles('./yarn.lock') }} + - run: ./tools/ci.sh + cactus-plugin-ledger-connector-polkadot: + continue-on-error: false + env: + FULL_BUILD_DISABLED: true + JEST_TEST_PATTERN: packages/cactus-plugin-ledger-connector-polkadot/src/test/typescript/(unit|integration|benchmark)/.*/*.test.ts + JEST_TEST_RUNNER_DISABLED: false + TAPE_TEST_RUNNER_DISABLED: true + needs: + - build-dev + - compute_changed_packages + if: needs.compute_changed_packages.outputs.plugin-ledger-connector-polkadot-changed == 'true' + runs-on: ubuntu-20.04 + steps: + - name: Use Node.js ${{ env.NODEJS_VERSION }} + uses: actions/setup-node@v3.6.0 + with: + node-version: ${{ env.NODEJS_VERSION }} + - uses: actions/checkout@v3.5.2 + - id: yarn-cache name: Restore Yarn Cache uses: actions/cache@v3.3.1 diff --git a/docs-cactus/source/support.rst b/docs-cactus/source/support.rst index 7f3c854f880..320a40e69e2 100644 --- a/docs-cactus/source/support.rst +++ b/docs-cactus/source/support.rst @@ -11,6 +11,6 @@ This section contains the ledger supported versions for connectors in Hyperledge Fabric Iroha Quorum - xDai - + xDai + Substrate diff --git a/docs-cactus/source/support/substrate.md b/docs-cactus/source/support/substrate.md new file mode 100644 index 00000000000..bad2192eae6 --- /dev/null +++ b/docs-cactus/source/support/substrate.md @@ -0,0 +1,16 @@ +Substrate Support +----------------- + + +```{note} +Substrate chains include Polkadot, Kusama, Rococco, etc. The deployContract feature is for development and test case authoring only, not recommended to be used in production environments for managing smart contracts. +``` + +
+ Hyperledger Cactus v1.0.0-rc3 + + | Substrate API version | deployContract* | invokeContract | runTransaction | + | --- | :---: | :---: | :---: | + | @polkadot/api 10.9.1 | ✅ [test]() | ✅ [test]() | ✅ [test]() | + +
diff --git a/packages/cactus-plugin-ledger-connector-polkadot/Dockerfile b/packages/cactus-plugin-ledger-connector-polkadot/Dockerfile new file mode 100644 index 00000000000..d3f0a96727f --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/Dockerfile @@ -0,0 +1,10 @@ +FROM ghcr.io/hyperledger/cactus-cmd-api-server:2024-01-02-1fb2551 + +RUN npm install -g yarn \ + && yarn set version 3.6.3 \ + && yarn config set nodeLinker node-modules + +ENV NODE_ENV=production +ARG NPM_PKG_VERSION=latest + +RUN yarn add @hyperledger/cactus-plugin-ledger-connector-polkadot@${NPM_PKG_VERSION} \ No newline at end of file diff --git a/packages/cactus-plugin-ledger-connector-polkadot/README.md b/packages/cactus-plugin-ledger-connector-polkadot/README.md new file mode 100644 index 00000000000..0af673825c7 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/README.md @@ -0,0 +1,240 @@ +# `@hyperledger/cactus-plugin-ledger-connector-polkadot` + +## Table of Contents + +- [1. Usage](#1-usage) + - [1.1. Installation](#11-installation) + - [1.2. Using as a Library](#12-using-as-a-library) + - [1.3. Using Via The API Client](#13-using-via-the-api-client) +- [2. Architecture](#2-architecture) + - [2.1. run-transaction-endpoint](#21-run-transaction-endpoint) +- [3. Containerization](#3-containerization) + - [3.1. Building/running the container image locally](#31-buildingrunning-the-container-image-locally) + - [3.2. Running the container](#32-running-the-container) +- [4. Prometheus Exporter](#4-prometheus-exporter) + - [4.1. Usage Prometheus](#41-usage-prometheus) + - [4.2. Prometheus Integration](#42-prometheus-integration) + - [4.3. Helper code](#43-helper-code) + - [4.3.1. response.type.ts](#431-responsetypets) + - [4.3.2. data-fetcher.ts](#432-data-fetcherts) + - [4.3.3. metrics.ts](#433-metricsts) +- [5. Contributing](#5-contributing) +- [6. License](#6-license) +- [7. Acknowledgments](#7-acknowledgments) + + +## 1. Usage + +This plugin provides a way to interact with Substrate networks. +Using this one can perform: +* Deploy smart contracts (ink! contract). +* Execute transactions on the ledger. +* Invoke ink! contract functions. + +The above functionality can either be accessed by importing the plugin directly as a library (embedding) or by hosting it as a REST API through the [Cactus API server](https://www.npmjs.com/package/@hyperledger/cactus-cmd-api-server) + +We also publish the [Cactus API server as a container image](https://github.com/hyperledger/cactus/pkgs/container/cactus-cmd-api-server) to the GitHub Container Registry that you can run easily with a one liner. +The API server is also embeddable in your own NodeJS project if you choose to do so. + +### 1.1. Installation + +**npm** + +```sh +npm install @hyperledger/cactus-plugin-ledger-connector-polkadot +``` + +**yarn** + +```sh +yarn add @hyperledger/cactus-plugin-ledger-connector-polkadot +``` + +### 1.2. Using as a Library + +```typescript +import { + PluginLedgerConnectorPolkadot, +} from "@hyperledger/cactus-plugin-ledger-connector-polkadot"; + +const plugin = new PluginLedgerConnectorPolkadot({ + // See test cases for exact details on what parameters are needed +}); + +const req: RunTransactionRequest = { + // See tests for specific examples on request properties +}; + +try { + const res = await plugin.transact(req); +} catch (ex: Error) { + // Make sure to handle errors gracefully (which is dependent on your use-case) + console.error(ex); + throw ex; +} +``` + +### 1.3. Using Via The API Client + +**Prerequisites** +- A running Substrate ledger (network) +- You have a running Cactus API server on `$HOST:$PORT` with the Polkadot connector plugin installed on it (and the latter configured to have access to the Substrate ledger from point 1) + +```typescript +import { + PluginLedgerConnectorPolkadot, + DefaultApi as PolkadotApi, +} from "@hyperledger/cactus-plugin-ledger-connector-polkadot"; + +// Step zero is to deploy your Substrate ledger and the Cactus API server + +const apiHost = `http://${address}:${port}`; + +const apiConfig = new Configuration({ basePath: apiHost }); + +const apiClient = new PolkadotApi(apiConfig); + +const req: RunTransactionRequest = { + // See tests for specific examples on request properties +}; + +try { + const res = await apiClient.runTransaction(req); +} catch (ex: Error) { + // Make sure to handle errors gracefully (which is dependent on your use-case) + console.error(ex); + throw ex; +} +``` +## 2. Architecture +The sequence diagrams for various endpoints are mentioned below + +### 2.1. run-transaction-endpoint + +![run-transaction-endpoint sequence diagram](docs/architecture/images/run-transaction-endpoint.png) +The above diagram shows the sequence diagram of run-transaction-endpoint. User A (One of the many Users) interacts with the API Client which in turn, calls the API server. API server then executes transact() method which is explained in detailed in the subsequent diagrams. +![run-transaction-endpoint transact() method](docs/architecture/images/run-transaction-endpoint-transact.png) +The above diagram shows the sequence diagram of transact() method of the PluginLedgerConnectorPolkadot class. The caller to this function, which in reference to the above sequence diagram is API server, sends RunTransactionRequest object as an argument to the transact() method. Based on the type of Web3SigningCredentialType, corresponding responses are sent back to the caller. +![run-transaction-endpoint transactCactusKeychainRef() method](docs/architecture/images/run-transaction-endpoint-transact-cactuskeychainref.png) +The above diagram shows transactCactusKeychainReference() method being called by the transact() method of the PluginLedgerConnector class when the Web3SigningCredentialType is CACTUSKEYCHAINREF. This method inturn calls transactMnemonicString() which calls the signAndSend() method of the Polkadot library. +![runtransaction-endpoint transactMnemonicString() method](docs/architecture/images/run-transaction-endpoint-transact-mnemonicstring.png) +The above diagram shows transactMnemonicString() method being called by the transact() method of the PluginLedgerConnector class when the Web3SigningCredentialType is MNEMONICSTRING. This method then calls the signAndSend() method of the Polkadot library. +![run-transaction-endpoint transactSigned() method](docs/architecture/images/run-transaction-endpoint-transact-signed.png) +The above diagram shows transactSigned() method being called by the transact() method of the PluginLedgerConnector class when the Web3SigningCredentialType is NONE. This method calls the api.rpc.author.submitAndWatchExtrinsic() of the Polkadot library. + + +## 3. Containerization +### 3.1. Building/running the container image locally + +In the Cactus project root say: + +```sh +DOCKER_BUILDKIT=1 docker build -f ./packages/cactus-plugin-ledger-connector-polkadot/Dockerfile . -t cplcb +``` + +Build with a specific version of the npm package: +```sh +DOCKER_BUILDKIT=1 docker build --build-arg NPM_PKG_VERSION=latest -f ./packages/cactus-plugin-ledger-connector-polkadot/Dockerfile . -t cplcb +``` + +### 3.2. Running the container + +Launch container with plugin configuration as an **environment variable**: + +```sh +docker run \ + --rm \ + --publish 3000:3000 \ + --publish 4000:4000 \ + --env AUTHORIZATION_PROTOCOL='NONE' \ + --env AUTHORIZATION_CONFIG_JSON='{}' \ + --env GRPC_TLS_ENABLED=false \ + cplcb \ + node_modules/@hyperledger/cactus-cmd-api-server/dist/lib/main/typescript/cmd/cactus-api.js \ + --env PLUGINS='[{"packageName": "cactus-plugin-ledger-connector-polkadot", "type": "org.hyperledger.cactus.plugin_import_type.LOCAL", "action": "org.hyperledger.cactus.plugin_import_action.INSTALL", "options": {"wsProviderUrl":"ws://127.0.0.1:9944", "instanceId": "some-unique-polkadot-connector-instance-id"}}]' +``` + +Launch container with plugin configuration as a **CLI argument**: +```sh +docker run \ + --rm \ + --publish 3000:3000 \ + --publish 4000:4000 \ + --publish 5000:5000 \ + --env AUTHORIZATION_PROTOCOL='NONE' \ + --env AUTHORIZATION_CONFIG_JSON='{}' \ + --env GRPC_TLS_ENABLED=false \ + cplcb \ + node_modules/@hyperledger/cactus-cmd-api-server/dist/lib/main/typescript/cmd/cactus-api.js \ + --plugins='[{"packageName": "cactus-plugin-ledger-connector-polkadot", "type": "org.hyperledger.cactus.plugin_import_type.LOCAL", "action": "org.hyperledger.cactus.plugin_import_action.INSTALL", "options": {"wsProviderUrl":"ws://127.0.0.1:9944", "instanceId": "some-unique-polkadot-connector-instance-id"}}]' +``` + +Launch container with **configuration file** mounted from host machine: +```sh + +echo '[{"packageName": "cactus-plugin-ledger-connector-polkadot", "type": "org.hyperledger.cactus.plugin_import_type.LOCAL", "action": "org.hyperledger.cactus.plugin_import_action.INSTALL", "options": {"wsProviderUrl":"ws://127.0.0.1:9944", "instanceId": "some-unique-polkadot-connector-instance-id"}}]' > cactus.json + +docker run \ + --rm \ + --publish 3000:3000 \ + --publish 4000:4000 \ + --env AUTHORIZATION_PROTOCOL='NONE' \ + --env AUTHORIZATION_CONFIG_JSON='{}' \ + --env GRPC_TLS_ENABLED=false \ + --mount type=bind,source="$(pwd)"/cactus.json,target=/cactus.json \ + cplcb \ + node_modules/@hyperledger/cactus-cmd-api-server/dist/lib/main/typescript/cmd/cactus-api.js \ + --config-file=/cactus.json +``` + +## 4. Prometheus Exporter + +This class creates a Prometheus exporter, which scraps the transactions (total transaction count) for the use cases incorporating the use of Fabric connector plugin. + + +### 4.1. Usage Prometheus +The Prometheus exporter object is initialized in the `PluginLedgerConnectorPolkadot` class constructor itself, so instantiating the object of the `PluginLedgerConnectorPolkadot` class, gives access to the exporter object. +You can also initialize the Prometheus exporter object separately and then pass it to the `IPluginLedgerConnectorPolkadotOptions` interface for `PluginLedgerConnectorPolkadot` constructor. + +`getPrometheusExporterMetricsEndpoint` function returns the Prometheus exporter metrics, currently displaying the total transaction count, which currently increments every time the `transact()` method of the `PluginLedgerConnectoPolkadot` class is called. + +### 4.2. 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 + +```(yaml) +- job_name: 'polkadot_ledger_connector_exporter' + metrics_path: api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/get-prometheus-exporter-metrics + scrape_interval: 5s + static_configs: + - targets: ['{host}:{port}'] +``` + +Here the `host:port` is where the Prometheus exporter metrics are exposed. The test cases (For example, packages/cactus-plugin-ledger-connector-polkadot/src/test/typescript/integration/run-transaction.test.ts) exposes it over `0.0.0.0` and a random port(). The random port can be found in the running logs of the test case and looks like (42379 in the below mentioned URL) +`Metrics URL: http://0.0.0.0:42379/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/get-prometheus-exporter-metrics` + +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_Polkadot_total_tx_count** and click **execute** + +### 4.3. Helper code + +#### 4.3.1. response.type.ts +This file contains the various responses of the metrics. + +#### 4.3.2. data-fetcher.ts +This file contains functions encasing the logic to process the data points + +#### 4.3.3. metrics.ts +This file lists all the Prometheus metrics and what they are used for. + +## 5. Contributing + +We welcome contributions to Hyperledger Cactus in many forms, and there’s always plenty to do! + +Please review [CONTIRBUTING.md](../../CONTRIBUTING.md) to get started. + +## 6. License + +This distribution is published under the Apache License Version 2.0 found in the [LICENSE](../../LICENSE) file. + +## 7. Acknowledgments \ No newline at end of file diff --git a/packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/images/run-transaction-endpoint-transact-cactuskeychainref.png b/packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/images/run-transaction-endpoint-transact-cactuskeychainref.png new file mode 100644 index 0000000000000000000000000000000000000000..2e45ff41264aa5586f69bff63b0eab1bb5caa11f GIT binary patch literal 37571 zcmb@tbyS>Bw z#qayhnKNhRt~K|r`6H`WUb^3^+O=!%XFvNX|4*`F7*7eGLLd+f32_kx2;@N|_^0>e z0eE6_RMi4LXdOh=9KKlFKrIZ793WzbR)%(Z4u%G#`cP662L~H_9wsIm3q30bM@tLF zFV>b$gS{l+6`H0>Y7YPL9D)Sime|pNwa+75xSDR;EhNe-*yzdTRN^H8T2RL=$CGaM8ZblCVr>)^k}x8R}s< z8QLE(Q(DUJ&4oSM<^ydmc@w{exi`n(Oe#gve(}d}JT*u0xmNkDX0&FV;JY4w%xg{oRx2sg;>SF&%ZPg6VH}s{y6->y z6PPPw$)ODPTs+^Zi9(9Q1K}~%i#O+{hAGzg*N7QD>6R8F-yM7Xqs{8uc>%()wWRzeCOKLeL?XunXr9f2BGAK zbXoyTNL7Po3paAxU$SFM2vIDU1#giMtmw08yv$qrQU%b3!SvTFMMs+C!54J&`PKaw zc(sI>pMxiT@GK4-PkB+?_GDN3&CUz>tkKG`(%PIyc0N=Sc0C(}r5!HYK?aqg&$uUo z>%3Hvjb&^ZH?UpG8W`Vw`&-QXnuBc1@>~77y(I=7pNp+0zm|WBwYvgg6K)f?MWP87 zs>Uhu)Cb!D9QC!NV%mtK6pBi_NmshZ;iUo2oDbeSM+CdoEtQE}KY0E)`0FJ5j1vUn z4UrHLQgYGRO+i1vGxyi zX=)+jw6YjU6P1>hhSx(^R#xg9w(NGNnQK0Al(?Tm9?`?El2MwiHu>AGbk28*n~Yvm zw2ShMf3}KhU-Y_LNEgF`Yo`kPm`&s40`8Cp_?&k`50^e{@_%qy zK3nu6!N>oICKXM;T!2QvXfruy-NaBy%;T`R)bc$&E$z>rKkNN*6x$bTF}j^WM5;wv zHSQNjcyubeVPdZ@4!cOW13BH#m;KyMw((xS9vtlQPooSNK%ITQ$H>ET$xKyW*qtco zQ*Y&!gtfY*gUH+juRixeLqgiPo zkxxcO2F$bq^9BnX>sGc9S2wtr$XBKE!}(`1rUY8CPLe@1pHd*jN4`WH7SC!94-XC6 zX0=2sw4}|VMD}JYa;5F;SggrQEaxiuphuBRx{b1lJb!-tBIb>@4|&Di>h{JwyFrn8 zB=7QM$6n9O#)B$h(BEiXh7+Y+a!{sFMp80OTM*IDnt=SC6oPpC747OPS)?1-Z=KuC z^bnH@l|qejKBN8@dzEYfHxsBAeiUO{rccECgoK3HXfr3qvgPcTiFuuVVwl=opG@hF zZQtFV35InKX225)!lF%`XK#2|fvC;+!YQE@qJj9B8*Qg$#zS_!JrPu*dhojAX)Vb}l`VnqDsIP{E&1g; zfdouPERe5|exgT=yD0k2V#6#{c1W*rF-ZijM>C}o80-C1NnAI{bUaqW#7x-P*dn7h z1{3>guobhUpA((#&BbtzPj=o<(@ChWoEIK?w$p9#ZX-YigOE*pH7|e7FjpCy&jG(K zy4v-`ELp&#yU^5vfi^pgdE*G@8`8ta^R**_Hp>=q&RxO8+?g(ie;70?6F4kqPj;rP zt#0e4xX44g9?kG03ayaS}B>Je_J;{Pir!DkUzJ7k@x@wvYuIw8}`Fgy~htYLuZMQYZA+%U{whc{uzTmg+xH*2E(`0y~CQi_MAiMG=jkL^hjaBC15 z&a=dJHLh9-_js6@I~S0*K9o#C zKp-YV$YRh9q9Jh{7GeT~u-<32f+p~K?l@-~n|E;z*|LejulXgnfM3_$oX&j^77oCp z`{*Q~-~J?5?^?MeO*HUGwPUu@lCIR`h0HC30R2Wg*sevVIm>aS+6kANlc`Mn(RYal zf;rAPFO^uM2>!J!q)&%e2Tc)Up_cV0lbW**JyI5}AZQ+fKzwvm+;l5hDHeqLMIGY3 z2m(fPWD>vIWPy6bb%@rd%hO2yfh+Li>{PQdqX8c0eXTqt8b1U#hu2#S$X_au;bL3Y zRqLEAZ}X}m4L=~VMx5%L_MQ?jN)Iq9<$Qv1Si;>7_-md)n$P|>$??nX#RvpKdY=jr zAX@~H2qqcNKgGZRC(q*QbWbjkhcX61F%2UgWKtubRm%AoL@391*bY2K1Zhw1Hxk4G zxu3|J==fkUIk7+~R{?A_tyc9rhNh!lIy-K`Zy}rl0*xSQUz^poUF^`IV7-nEtR-r@ zXhnh)f#baNI4ms8KwG=k;W-VJY{L2Rm>g7{8OO=j?wh5~Yld?QR%qx(=Ldibl z7#)r0DrVc^f8pS-Op`^;W%k1)mJ}Da;qpoh8c?PUUHi2VklPMbk7v@-CbMR-n109e zP+ndh#J-W)4_RAJT2 zr1Hslh;&(!AbYT=lOosh|X0D^y00+;FN8xN$Df7Zv!k{`Z~ zrmETvd-L|Kf+Ycy&UjG(4vWX7LkTwo>3%!BQkb3RDlIQAxZ9eHi;_?^`NgQ1h~lei zHFREc*?jgL+jcqer%GCK?KhfMnxbh@*Fa>!@MKrJW)=QDhN$wtR^#}0spVxnYjeKW z-3@1P0M3K=DF90?;dfJkV-lZ`uxn%KX{FjfXQN=|STP%ZsayetP0)}{Nc_AIthAU( zV@gRX(PWOQZlwLFom%eiiuZ3*bnA9dZ2syOs|JDerTV~+fsnv~x6>-pl7&SonhH&3 zV1sMH2Fo6{YWQPQdS0v%{Kx7~l$p1mX3C7`Lmm+V;AYiq^r_rr1cVs>m~3ARqRyIt zxZfaP@wHSQr$xIen2A;>NSG=!|Kl)45uJ6bb(vBz>l4C0Pct(!P0@ZP=KXv7!l&}+ zB01!XA3QSw_^`SKKoHF;D;e33PA{;rg$K{Z73D^3LhbQb^^6TXm4DSkH-}S4RWrsT z6zK8>M)Ui#x>Uw#S{!*2LS{f@Q&dp!#Ud&47}J?Gjb?SC%fgPy(;vr^gypCrJz)cf zwi>4tr|=;%W~}5Zh&8rN%nJygWOF=c7B)&V?9n{sV@L@0@AaVEcBH>=k(~YS@v1xV z{{mm%e{Lb^nY)luv2wR=zC}UvW}p9ifaFcEl>aUQz!ngmPY)LF035L6c1~G{`Jpuq zw}DaaC|rLw!>l<`y7{ivjXZAxpW#=7GxfioCgHqxgDfGR=Mj>S$W8sRTkAbEL#z+1 z+3kB5pP#TdRWc~7rpdaTniKsoq#HdAyZn`Fn+`Q5U`l3&&_|%%y@sg0ug!DXogP_E z*qtfxUfv!~{osfG5}TMIJ*Is1Mlpg%l;RTX>XIrWt#?0Uq=95AB>ZUy+9#T zax6>c3Na*D0gGi{0vXp^QK?{r(LzZw?-JGQchf6S&?Q^01mn)LDydn zDyZh)gdqTmmuD6;GB+LX^Xd2$JH{=a5a)4qdiaRU;F^Yp2BhQi`3wo1Bs&-yj9pVW zPd(XCCFI=0h37qOb5qxT^+f$vXXXzaolF_7si@Ps7mtmM(IG_Sx>1ndRI~ZCf*jdv zH&5iJ7<%JPW`aGH;k@j1?jFM~WFRQg9e^I~x?eGUI zQ~Qocs-p76lXs!L4?kY&J5$|DEAqlop+c>iQs)DBxgIFMsPom!jEa1pPpgjR4C`nT&~hXDD+{GagF9+}J3s&nt>1fpxXFCkT2aDGnxZx1l}4n(T;; z3UjhtE-9OVDz4`3EO)JgD(-p!mB9wdQjdm{Dr8_lg;jd+1%yMSkrwH*T*l7sa+@Ew zsFv|T?GB>`#?z+mvKGAf^CZ@O3axa>$(MFIx9vFRF z9l*=%H*emAVr&d0C#zQYnEojA&>}@xH~)yPF)yf4Z3F~v zw)2ZkPL9_4surAxf)oKNy#`o2pK0uJ7QH7Ln#}9Ge>!K4g^Eg}QZT*R69Hg*>VnFw zh(@O7vr_m2{n@X~7(8hBj zpfarvg`mmI&dv3Qb(1Nm%zjE@Gn=M5|{0O7z7@{xBT09h$0|0}a#cWlB#HD?sF(iOsK zHvZFVyd&&_l#0 zD2eC_K)KTZfr3(?9*XGf>T=#1U7IfXVwY->Hl2u^F^l<%t65j4SiA1jdC|k!T%9vC zq7!jeqn*GCb8c6QjcJgKqOI3U%#M& z&+U2lE$bh^!`7fP7o2JUchf2`YS-evdL`|D&~TO{zPbo`hST7J+oot7Yl0EW`R)-C zlJv5aFTQ1M$eb0Qf^=nOy!QRf#JMPyGz;a4{ukEfw-aUEpS<)k7 zD~NkU%($#byN-kMEfv++vjoNRY7gfG6C-%Q!;SEJ6V3?VLks*?b&qG^Q{Cn~Pm<(;JZ-6g# zrxMkTEj*5K->aA^-+C}e>74yBTp3}uHJZ7#DFVYH=4tmSn|mYQG;^UeBvh2N@Cx!6 z2}$X%a@*gq6kzy)h)?{0nwb^(I_FG9DVcviN%V^`>!zE|tw%wF#DluFU^}=9lLS2C z14mxm0FSG`9uXsv$A=0sjO7G#DwQW!vFwnZm@c=DGRG@-OLm#4;1!#V4E7ZXu$6$BAkgzd_)lZS$!@O^a*W)d9=5FYwg>e{LaOPT z;?dA})tUkPf6s+b@#InN{dWF)E;RD|d*$W+<=X=@1s5eyNyz{X`K8kZ+q4<_m3wP} zL`$QX^{efo|8zoCca6Rh=SbF5mTu{+VX8p=LyfJiEvQ*Dd|Xeq4^>|Fo=Ravs7z#w zHWbd}E!XdEe;AwkC?Y0iZvMHw8zwg7n&FC@j~|~Q7pbBLC*2?=^?v%=G?(g!(fF9G z6ihrHo-m-nm-dR+slY{V4+Oz$$(T9k>?A`i+od!ES63dnV~G=MP|;E#gc$!}BcvY= zIK!cSO>5VHT0AOGwU_`z&pHd3NZ`WDqCB}kSIv_=Hm17E;bRzpw(AR}hEieSsTcj4 z^M~T%C)6rs7U6nLbhQ08dnGk9|K6ll8`m#elI=O!iP0P*#jchd<}6ZssvRbzI;92@ zV(Y3M^Nn@%#xFT}Z7h58Rf_{u=Ozm^W5O<;;#n#HdL>CJQl$fBI$bPeeE`rJOi69V z)N{oPiJr}TeJ#aEJy4`u{DG&scCS3V9j1Y+DJ!{=`pZEd0 z^@cUQr18{Q>iWg4r9HD#Rhu>J;gTsR9M17DH_}f4rbnnYAVEA)|8FzhzqD8ne%!UxK_BV~jnE(e>mu>{aZGF1%cgpxfHI>q;|}Om*mux+m8m z-jA(eX8`HHm5Fyf{KMH=S&`<@o5^xybpOZh_WX63^lg8f>}`Hc?5LLWdzSuPTmCw~ z=_qa)bCt^A;TbOU%l%im4wIm|)i!!FrR=Wx9Jo;#M|ODk&;MpYnKa7JcV}SMIk}>g zx%FDe_mAsM;oyvoz{-V{bc*i#WDq#p!An-79byeKS|-)|$wRE;IY0i>577LZ1ZC1X zcdrycWc`oV0=inSCtTSz`F|b_M^1oyrsDvrDDfW?%Y*7RxH?{+_qbL?ix}o))HC0| zJ;7z7-XDibHk1<<)I^hLm;0;!<^6M(!%CUGlH#YVE*=`M`h zet$fdHCsB48+wHPz^6@hT?mOAh{ON^4{-cbz zUG^gZ5b_Ryo$i-})G@uEB)HpT3UnGUDgkZjSQ z;$W`SeT|`ieW9ASn6b;8y>_j?UfLgLqaSfw!mqR1mbo0a(**jY@i}^mjD|cp$WoO+ zsbwOTz+$(0?zUeO5(9SZ_D`Vsv1SQ1%i8A&@9GNw`e&kBR(lliTed|EE*BSzH?4(#FzSyj6x5}+b}B@UV?X%2T*mKuOrujT0b3NZn)xg>7;mXY)g6R}9U^?t)Vy#u%x{1|%JQL)xX702#Pu866E z;fY_B@Z$M%+uLhZa@(P#HuqUeH~Ot2(UIF5Do_dG;@}XydZm@~N&4n8XtHRbSKZh6 z1C#1$TC}tN5Z>Cgni@dD>572z_3(%e98Gc;C8-ge^{6Rfdc$W9x4KwRJfzFx7JL4) z=vr61_L#&Y&Dmg@?`g~QbhS$1Y{OA+FqXjid0`cth{Zqzv0iurmfWUoAf+M4@37fs zw}uV6V(zEAcpg2kdcQ&IsEgvzVMSaH4JM%zFvW!pUc8qQN{2jV#KDS_pae-nAn-crj$3rUF)0IJ>@toMX-1X+f zdCPNuuj&ji%x)uTcnRh%9B*ne%j$95;NuUmj*E2~@E1tNWRe7?@cbi+_bIBx=SHl6 zi($o|aW1;AM86bsxy821cu#x86s9jfdwk z#DC_0_kmGDTlkAiZ=p)zI=n%Q+kCRXc#}u8$_0?aFgDY-RPhsti;bzSAect2gZiv? z7SDyd-6N(=kyq1S z(wTH`gRygwBqXBBJ2r1?trz`92Vi+zb4Fr?{P-Q~qh!UJF_QVS>F@Y{IHZ?}MTHP? zidu-*UhNkx)SirHJ;}W@y`i^)W;>jkw71d3FnUN;rG^L!bcVhB9N?P?J9}U)+7KPC zK=I~{*Fe1E=Hr;6k#g?B(M6A|VB`Hm!8TuhAp+aHA9?Y-&S5!PCFYhcFJ2#+7w@G# zK*36SUSXQajl(%SnY%|%@FmJ3#p}+`^!x>A7~h#22nP3;Z6r=d7`%8l`$e8j%{Ddz zji5;M=grdu2n6YYVwo)cpCIy_1cj3$p~+uh|Zi z^Hplhi{$eB%pw%aLdc)RFlvisUp1TskAw!2xMdlis@U1}Ml)ECd%cyjM9jF`}VCfI8hn~$a{E<&TRYIz&7-Jxofj= z_hLg;0Y8f11C9qmHn~xR9k$R?V@NRT#aANv+|=ol*5{M$0YC$$ZQ=?I5em4|2kCE- zJD&fY2_n*!5~pT&*z9)Li151f^I8d7(E4>tk`wL>mr!Jz*tN)pkyPA%GpVkrobDpY zcHW=oXD%T(&V-Q&*Y58o@?Tl=h>l~IFqj^#s)O$K$iH@t#;QY>hS}@(6N{>(dZh(_ zVw?=D(%zefFuG_L86NP|dhV3z>6_UEtptuYck2^#R_48wZfCEb5oyK-?+mAgtoN&n zPYQV5>RVF!)=m2QEt8FHr6E2Nn71L7|FJOP?dwq6o5&|wU<=J0A?t^oxi7bhp6@~# zPRm))UhI{`va8{;^7T6ykB?X)Eif>sm&UuwTs=tCZ)@~G86zfhKHQxd3NJg_ANPnf zbTBm6-QT-ENmUr~kI0>Wy^t^gIY?5Y(fxyq3zwH`3+2{!n>thdZ(2}C;J(7`3YMm0 ziOBJDjb|hW$naa^uSTc?e;RJUI}g2vf<57@kZgv}z~aUG zy5Jnqg|I`cTe_a^_Vqc)WN2z=beHB{mNDRsIftGCGjkjJ@TMZyA{jeN3~fKlC_6iy zI~d!(I!J!zGnP1Ibt%W;?yPzvwr`Uypd*$f5uNEvQG9dS$*fqiBCLB&J+7 zzyY!>k&GJVr(B30sScnQQWBP@5+GrotLRRPro)Wuv}C)c{gm>4p%%l0?W3`R0nMOq zWto;gfLz7HWW$Bur?c>$8rtGKLgn+So6Rob;8wURJ1z-|yKi=M5s8Z)M})5G!v=eFL8G4z@+ z{q%()SSq3)o3R@4$;1#{%B|>ce+V6X{qk{PzzAO8p#RF`P>0X>UHL6;6?xdDJ_y(= zD}iI(QAHlOJ+rMw81|V28HIxhHT24@cf7NY$816s1g>_?%OuN}g&0&{+0`N%?ZL@% z;2o{DWv!JS6PMDATAmqpik=Qso8L61v^BZZ*>Z!ip(Yz+<+6 z@7U}Kd>1b>6^G5|I`3+8b9uLP<@1LGoAwL#1XpK<1nMF?NgTxb52_7klx}7?c+E_4 z$_7pV@rJ=DPyY2FY{HK;k-u>TKlyhl{i5xa;=Gx&WOlHub(?R{g=n%VNBwW zS#_ub;+u;B#W=jnzZ&)3Tqtk%u8vUnCWZM(=5&nPl`y$;zuN(vXh^N`=2X*kW{mnl zm~~jY43E@yq2(NW#A);g>X-;y>`8tlhv!eR6{#{;+@U|{u&In5V(0l0N!lJ>$38}` zqxP6rS+a?RTsg;@a>94FSYyGPcM*Xjg>*9)tv-Dfb7t%Y-9JAD#Sh=P$1pB%i;gIj zm03ECkG?g6?UZe9OHZMB;wXDOQ3y$P^qi#R_l#vT&8irxy{5OS;9ZLuKNreLqY#Ov zYe-p?)L0X%uI*ZvP*))@#r2~%?Fb0MMRnH>21fKgJ+bDmCZ+K3?c9h|MOlR;=JPziTk# z+Bh@Fd+H#p9sZ##MHi^0aof(VS@m+bnyj%u)9?7xbpFRplpnO7)qX$osXqP_qKDY@ zW#p**eV+FMy7wg{X#H$xp)@d%pvm9ge{XzCC(mGSwh;6Wm0v~PzTrC_eplN(GQd>~ z1TxX5QmXwQ`~)&Ea*_TBhB_=Y)6}L8f_@fppg5a(-pq{3s|q>-5f6O-eJVh-_w@yQ z7*=VOlr7?LJr*gE+@3D!^Ef*h{|^jM!JB#5MYHittIk=pZB%;`xvelmnUJ)zvs2zi z;rqs*c7~VUz;A5J=w-5|%Wc_hLqZMtHfj%WG}ro6pXs9Z99e8C;s$L^h zB;rODVQ(N{b}G9oA38i@`vv{s+ndQ#IzkJlSc7~A87_~@aHpn~P8kVt>da!@Ix>c{ zE!G^HW%6-khuPe~ErlNSZGlpJZKEqmXLDWg)$w}J(V=__CI_Zm%*6*DKAh|I!g) zV78QBKYymw4aT$Y)Q4TjR;cqk;1RJJVf(z*rraV~@~3)B9WR!)RCjj^R|*$SAkX^X zCktS({R-n`u~!ryjL|ZPa6uNSP=NYoD-C8|OO<)|UI%3Bnck=f8cIIC`j0`g_UrVx zloCqDGo@=4G)6M)ymMRYVtap(<-ML<>wR^}?AU!)9`$<@`vY=2tyh!%LakC*TGVCo zc;^b}{aNK4q&?A9^aqd5O89T8T@P#e0B;g6@*>DK&O@c$jzJgtDK*r-LE-nt}X;2;0jN+}|%-au9Qc{kPHj4OT19k$ptiYxO zh|9(S8)sK#y*RivBmDg5r=q}puS_P9%W{+I_fsr~4=o3ERq{UX#Y8=;`V^hEtPPs* z&>1Ryxw+4R(L^9hiLKux-(6EtQ*S0Z!^2ov)Il$Cf58DQ`WA>y?z=^*#X1RAM(_wK zIgqi)xsSooy%+OjNyiiyB>>MWVh`OS|E+1AkKJU+8TsmLBg18XSJfL|?^REgMdH(?@giGfLu#?yi9-?3ZwE;OJBO^Ka zw@ziHb5Lyezhge25DOj_M#8A?0r==mfOdnc@~mhjmkp`Rwf@6LGa|SV|1tB|*!n{P zy`^C54B#MffsTa-kSTq~5)l8s1tgmV>Sez3F@mQYY}So<(@kD?*;TDTF1^ss;svBd z5ps{)zY})*i7?Z^_uf7FFC}$%7!(Is9n@lpk=>Yy(b4PfETMz`fC5s4aR&W1&1ukU zdn_ZvX&VT|0b(%%T`k`fEdo)Bj*M4XJfMP~^8ppS)-DI#YDWALUC4S9zI5=mQMpUIJBo5G>PD# zJ!U@%s1G2LHRW3XjTbPsu_?SA=nwOi^YO8<6+pWTaHkBqjR|85hVXXK>Eg6oHEAc0 ztV?^6!IfbW`EdMEJHsmDJ#Djl>eGy|z85$gm&QxXhxW}*3fB$y>v=)5pD0oaMs zNdjr_cxPXP`taom5#T@~<7*Y~w4dBlN&nDD)TgCak-voIg(vSh4m&u(OKvu6u_E%S zPHcxin6JcPoCjNRtQ-BY%tYV#r~;S+*aA5F?us6?Gek1nQ%}w(K5q2ys4_AQS74US z0x2ur{6Yrb-v9hM&v|fxC_99){FjGFv7Eo3@L*2kh`fFB;|1s|2L9*w|K~q-dmu1> zaH3sfJG}Z$^;7!U6XwvXe_D>-7XN#xGsW2e?=v|4U#Vf+<|I-UA=38u`!HwH`muwU z|Cb^DuZ(`40R^(D13PR$;yrt}+6b*XHv;sC7%^rSAh79_a-Lyh9|3{JEok0>ws#On zv%$fhe_K$zZZC`(p`h6u&7cY7imHVgj?1k+<#>3F6)a1SwRX86_2}d{kA%_xd8e!( z%o*q)0VUxF7)2iipnDk2Rix6OB(MhA@3&rCwE;K@2Q?c>_hKrb70w6Bg#Nh?)zh{R zmzVz-`!6%%_FjB;0u<JI>>E%U8TVU6Ky;hUI@jQ%An!UOIaTrt=y8y0s(tVRPR< zX7mn;I|KT?-h7}OOq4S@7G zn%Orbcy~`1@1^$BGPjvn8!Z{A~Pl5a(QxZ?3`Tn_u6j;OQzypmW6uS^H5|7Bv z#g2qwVSW8HebU!Yihn?KAN^wnyO#PHB5y2tAZ_P&JMGTpSxtIO%K79+F)9-K*JlrF zzQFAi-hvKY#{30ojLU9G7ieGOk&p}-X#oTXKoo;U`A;!;KyY6-EdivMSrEcF|6Yc) z-MgWmmm*(liF<^Y(F~)&sHP^QYE+Y0v()}(^>>Q_Z?2-Nwe*IgD{R!s@GX#O@pmpcN z{?B6wr22U*rw!i!m;e0NS3NFD+b48ngy>K_Ykd3YUqgS*&#C*Eh~_=@X0f8YwpCxBzU*7xu3?l$gE)q+0!j0H|T)#hwPenO-SAZBU0IRDbT z_PVy^?h%^?ZVkF%47^3pXIKwF1_ByRN-k?16tMFUzV}K>akI`SekEwRzzd9x6#QRoX%(396YVF`=;(`>=1~l}c zL%p&I{!;5VV{(EW?d_km0X3rq^ns4xw_yGF0Be+2T zGu#3t~-8thINmAA*Uy@6^hV7Ip@)*1Be zOjkga(5+|nNU#{hkqzhxIKLoY@CDt_Ff%ahO_o}{pVh|ffm(7Gx1_riK^Z4^^_d}d zXt7mZCZ}1P$NmC&y%jt1(%Z?&#(Hp|{X&2GlveF`O5m6M7FjqrjygacOTWj?%^in5 zl*oH8wD@fF7?53NKrmooLBGHWP+n$61|I;(2`5|G$)}qm6aX@CadEM*gwJy5wLTgQ zC$#>WF!kOY%nhKj3=YLU#9E)2vmh4#ai~@|wox{n<_ncSf4&k~?Vy(O`GNDX+Bpy1 zNSr<1>9vI7d?-h5M;iUcq59ly7NZF$mG>9w#>_-S9zp%PCs+W}4YKqhFEyY(U#Z&N zUS9yYqr4=TAs1!emyuH+fwkHT|L~^>TK3prd3o>DPNiZU51;pM^6`ix?uHTjQ9E5Se z&`T2Zf+(S!tKbv3H(m0*R0Bx1`c`)uTu)q=zM_~-7R(f=vzj8bRzZPKtWp@?cA@ea z2(EK)f(Y5utRnijQ>DrhGTL_?3wwjG%WGVHk(^D5(aYh zG-w8zu)lfpBN_-n$es?f2NP7k;7r=LeeM2NsMc;I(*_g^v-CQi*N0uE*-DxU3Zw7_ zw-a{i02fba|L>l)$eRqyPxYUg#&e+2xum4xj>(!aS5(IitOM`<9)XmjH_wO z+!k#gjc>^_Rd2r`yZtjKhx!QhC~D5CZf}5dahw40YTI6%4_s!^N&%6f{GDMhZdlY> z<*%8q;#=WR&QPjsaf zD<-FL$t&pTWHu3roeCRl#T=pI!p+2u;+b z6m&m-r=0|p-Ew_ERmtW+s+wY0>g4%r2=>F*_Y_=`2#jLX2I{h=^~)a5?g(1dqF2wK z3t=qD^hVOG*aVC6IPIPT$uFuVc7^TAKlei%Dl+4N_N_v_Ui#s4!kMnFF5ZN_k*O5w ztekp~V#=kFt1}+OWJs+7CPv73#fL!WQ*ts;-Hp*|%bDhgy{31{fCmJ=tkl|894SmQ z(g6BPhL=wF7fpUCH{AwRF|@X}o+n9bN`EUmt`4V574Unsz{Hb~OvZmY{iXbUwy=R= z4MUsT6v@xX+v~#hM8n+?|FkBsad3+=mrmE|<$6l21*egLeM(U7z2AF-&_l?P7Bfs`1 z5zBdv**Xe@LbcbOrzJiU79rcuXrO`EvzdLTy06bHx@HiEIglNTtrgCALoVv!=^_R+ zd38nHg5?w^D)koAc42fUxsl@En?Pl$*z~u`=>BrhW}xZw7$a0wJIe8mPYEbI)Ay>% zQWuUCxXNcIQiV%IRlIX_Nf)@&rJ~erV*he)qY-3|m39CzOa&$6N%|NjOrHg*CR6AB zR?EENsEy+U~S3y|y%^7JhvEfw_S*o2Q%)U(3+%2q>5IXWLDGASZC~MwPt8peKAM?&SW$ z*i>T3PpI-0c|Rz6l08nV38+Tb!ev{2y=2{V#;Vu zQC{aa=zi8!NHSvRf1Dh>CI|<3G3UL4g2H`!H9>e>|38a@{toXiVEOQj)D^5D=G<0UmUY= zaGU~7lvZqtEjp8l6w5ZEjp6`k&~=&|#kLE}-NH0H7gi}n_THwZrebc`BiCx5uSUp~ ztIryu4vwx!7XA41N+IIh!vfWe)$6)oS_REIQ!o_h;N@L`=p1?iY8hvfB#t_4H&F1e zt^sVTgo|H8F53Fr`%>)~W};ZiCtpv?F;!G)wGiJ#Y^LIUx8ol~#JO!xiL1W<02oEr zqf5q-OH98t#eSx~M|}qUClG6AXJ#IPbJ#^( z)oGjKiOOt)I0M!B0%cy%MtMb4ZhDXbaKWpu z_e6tre#h>6khnV4aXq!Ln8AINLC0!*H^prDITIz2`lWlI2_4oHJK2BzPg{+y&rSef zu6wOWgilHhe>JGe2%LyF%-qF$)Yl4S5aZ&9g)#h*7tF^B(PXaxNGFvV{g^)t)Oe15c z{={z`e`!9nxO-)Nk27>HtmEhiV4#bqy z-XBhF1L>Wfpfo^?4h534tgHXk{;K3Nobz6Wc8$(e1(5Ip&>lo?;Jec1|sZ}2#d<)QsQG}jF(tU$|nDhz$PWq%5 zuce-Br*zWRzN%41K2ngre#~3!xW;MhTJ=s;X2D_I-Xl6@;>-E;&&_Y+v3}VDWfBqY zqPJ(I*pb>7x}f6w=f@Usn5-^`)_`M86~Dcr^Q8I6+tSae*Qt889XPWmyLH>cVwd{r zW`?s{DJdy4n($F$A@$xnP`Vka-V%U{vb(mxF=1!D;i8S{*gq*cjellB(Y(snmtd1q zZ}#%bhNI$s%y`mWCA!+);VoH*OD%Ta8|hs*eCkOW_r zkjwP#h5(PTp8HlQ6tOmUWP6!HQzjKD^@adFnj?h>100Pm@Ap**x0JefO3p8zY^3D>3Mv z2~n65en2$%q)}$lDs3~n7o1M}lqyfeZs=kc(!Ls-?m`ooUo!dOx z(1kwNmS0X5wditG7G;_(rCK1}W+EyT-Q*QMsyprYb0;jaFfYT%Bqm~q!(zN>rBjh$ zt2*ah|J)oH3x z>dE^~qChFvbXvqQ^_#z3^1Gvk%YLCpf`EQYATk=kbL3-&R5O~ykEgS}>NACs1`_K7 zC-k#W?>@f6c@~uZIr0DQUq<^T)slo2CZayu7367YZRF%1+R#4j(~;;h&j)w}_EJrm zi%`-{=G=_W76wR8>zbbLNFvs)=Q0`i%n_%c`Yt;f4_~{I9c~SG793U??6p!>h*pm< zvi4Mr^ds$VLIx+JPot{`KdM&oKuteNY_u&|r*-jHe5otiRfLv*=Z2cnZDT401scAB zQY(e^p^5gwYtw4Jqc~09K3@hJ2|!LGWzd&9{bWmq#T3+V^y{K@6DPz>5XJO}HiNX^L3KYMOREU2G7O`xVSkkn=-@ zsN77h?Rf1VS$g2QUvnfW@^KJR`F=CWyO_cRcjr5zMMX|?i9CcQ;`f8+58n=|d0h+V zA=XLxCMDQ|O_%%_e4rz+$*mhT|s6huVleIxB2x{n87)mt8!mLUSm zVe%P55yo5gZb1pp@NkF-bx4^rJHdgON$z(K_D#jhna8kqe+vF^$+`)Td@2y!lZ$jX4m(U95pVC{hB;(3y@nv2%qkJExND{pU&Btk#<9mzfKE6WL2bi2odKq28WzYXJIl&5OW_y6&h6kjVeUMt9-= z=>+^MzW$f5tgQu)HT|@bi8j6{uTPz$Y#6grSAQLLnlNQP6YZtXzoETkc(K?g`N?U` z3$S0n)P2OpM(|bso)bd6UqKWkPeBlx%;_l@nBz8I&)UN?pCs!;4g5*3U2?Q3N_R5h zDzhK|nvET0-O>qjz~Fq3eTozK>WBN#4aR1*4^|auEL{p(;B%h`hue+hgIoYWRONVd8326&f9cH1K9?4 zw?0ia0sN-K(R%wT5bfE*ttPh`gR&X3jCi^z#*(x2=w6a#?r5+2La|=TH<~$W=={C3 zqHS9LtDgI7@+*4w`09hpT<#8EBjkQsY4+sTk@=&tU(H;0a|ca(^VrSOif4Y;>!A(2 z{k*2A$7N32PxNh<=3k-DYA?(l`t3V(zaV3~qNn_^AbU)-8=t_({_&XS5MNSLQ;YXkG3(7- z<_I(A#to(>ODNLK2KX)%?(a~lXgV}BiTj6_>-m_mUz|z*co-*>+kHKIBq{yX%xU*X>;I|kt>dEnx_4n46;PB?QbD?Nq@}yNr5mIjQj|tOq+5~hMqmIH zknXNQYUu7hdoUiq&vV|-`~1%5od1|%?tS0)-fORQt!wXnEpo6sEUu$9zN}lAB`RH~ zqZrmP_m61aMmPlRF8of&+4JhNaX43V6iEu|`=UA2TYC0JIWxMr6h6X2h_>WpzEYa+ycqq*A3Vq#0rHjZhQj)q$t!(79a-=R)X??`ddo2$e#Fy=r=5 zI^#@iZK?kJXCv~C;iv9%tViv{c0YH23*8SIsIHC$xpVfER&O zZWDP7y>+wLl8jemNt`L)BQ(3z%3=l|Fprd zNonlCZ8db9cL)Y_KWa$J-{pEY6%pqfG8YJqSB^2~<;jHKxiEyH*D=BozRfAd^~k=6 z-{`j-RH}CY57gE2)4iz5aOS&w8vA5`x_uIf8I}L(GamafM_BdQFqBErWRrRB=xyTr zxJ81Ka_Cv%#u%5=6mA~~F(qzxY}Dcb!uKT{p$qavy!o65diCtiIDs^+e5cw4!uTF9 z@*E%4Q?a2Fwh3>u=WZnHi}bx&TJ7E#R_vHGfY270ejM@6@GVKl8@#&h>n8IHGM(hk zlq#B630oI1It$O7;U{PLc~g3Xb$oK6THWzK3>qTj$>>sOJJ(V9m-F-JwzW=H=v&1W z(CC1}YP5V(IfLUvT%59aabI5%MH>9chnaESwDd6=?+xeVT` zX0zJaQtEp)SFYMpen7f*$sSvf{$Q#qUxGbroXy^;kUDHIv#4l=a_)RZJFM9FhS!c? zMJ3!s$o=x2-ErteJI8Bx8=9IF-$sgwWoqTQDA*0)q2r+^@+MCTs|p$4pr^W=bp|$} zwBz3wXyUI-xy6tVb}BzbFI$FK@C&Y16D>gYBMC>Z_)Cf1Z^uWVPhbILT}xizRw*PP z&TO+Pr+J?Q^F4Sts2~I&V;HSRmlj`tly|fDldmv6c(?S&nMXFQySi$Eh63+@J)i@D zq3mPk$91?r;r9RJgZN85^q&t=oPiPh?unZ++TR~zKypw;^{ahxMw=LjgCdAPNyRv^ zNT5DcaiTYsc!N8ot1KQ-cf?@61Tf)p2`rbWzDYy~H}yL=w^fpKTq=05->v2flgIC; z_eG_b&dBY?rM|<(5g{-nyP`o4Avp?Id{Tp)uJ?Foj55eP4}WIcGS!7>!`i? z*&!=b7GaB3P@A(2RQHsOG*elpT>!Q|PF+8ejwuH-KtsD~e}U(!A9tnOn_9!Dxcw!r zHFN9;I%IkQ24pXythv8xrs~tYfPmm36{p#c)YydMg#@3?v^GRXv`t&n;+yn#dU^UV zEkyl#n!5@2Bj0L#PAJawwNqBWffdCsud_HwNcHGPJWxzscw<0ro1#aSicZ06 z)mP)}V5v9iDxlxHgI@5k+)wjZVf5iBd+xJq!Kr3w9s4+eQ4!g4Sxp;7qXxLGD#`{) zp5mq1&n*+m#oORqNxdJK(3j10Pm>d579GCtRKKA`{h2luXXFZ}yUcdvRFtiYeHw_C z)lP}9Wyn`QDR`45Q!i;Y{JE1bPpF988D6s2Tq$kBYvbZP1cDfqd&^pV1h3Hmjz&5j z4(u#66i-Ef1SL-^gIE0#ouXHoYbbb1RWm{Ay3JV=nm(E6e6?8Lp1>$t&+{i~b%o0q zL!?xi%Hkc}^b|u@!X3yGY=naS|r7RsTIDsC;x2nes)x^nPEX*LgYlYP?RBDt1BfD<#Rh`7oGhV z*aRIiRR1!;|CeyQi*Cw??JsjL7EW91=ZaFB3)|Vqh~9vF0-@;J*%Q^2O%9yMI-7;& z=GQ)0ksHmL+#0GCqImS7ZiuL{B!3E9n~i1%Rv(mSaz5d@)37l7 zEl|R=j6Y|ORZ92ltfjxb3x>TaZr1&ofBDYIW&4rV7U%uCO&-URs8r5L_PaC~OwXs^ z?lv)`a_Z7AbDIt~VB^yvvxHKuH0w7Yp4Yf7^=1naE9cs7v)E)&7u-UiZ~I!`CPcXy z;B^`+*PUCTZ%NSOxFwUvEM~nkV}`pot^`oA-H?v$I5R}pv!dn6)!?WbixF$Co{AzGz#=okCaa zruq!e8{Z}S$8C6LJNG?KWXv6P3*>`T7TR8F{OjiehKQ;pR|{gkDc$1Z7XXf|y6g=^ zZ>Imlm)dKyK&%+v%TWWbB&6i9-8qp8ng5vZXEp*?Ir+c;aOE*G4-=WG%u?=9G2MA* z<$m1_oS0uU#|o8@arU<2d?4HMyN|xYLQ(l@(4CaXtSScgN>!pfZohA+7K}Cv>D{*P zczl|XX!?r0e2s|A2fdxal5=v+#PYQPrL;PPTau#iTqXTK%!k~y3(OEnF)5HusPI;f zzJ@r3s>8CCd?ZfVsJm(_Rk8ZILSvUsj3N<<)xttZ>cRXcuCCItGH1`2>E}#i^;|fy zs>XrU#@h`M@aFJ*p`GVQESvit`c5&?m`hr@bNANvUOF&~NRcA+>oJjKl>_)WtCd=+B!I~(mA!06B`lQ}M+hd(mtpawo_&!_nL2W5b}?VBp$)m6f_p{G3sP2oDB>Llo^}@qdQKBFxOx@;wWcM@_=~=IkWVA0TVPP79O+K#O74 zPdzhL0F2EXDz*ZKV_deVir%*y9Dz8BC@+ru%Yf8_q*$8Xt8@hBNpY=qqh*z7S~+FR zU@d|tl1TYz56~!t9TKamq`@YYk(fPyR9XT(PX4Q3<2a&9vnf?7pv%Cc*B1nYJ(Tq@ zD_I^$H@4@Lv8wVo>&D$DfWByda)#wL(PJ;YzNYa}qoY3;!O_tRQ|y>O>L0oRRYJlj zq$wHEF=L0MX)9xCdal4?no3^A+PQz1v8@^Hx0Gqhk{;?JmZ;zKwOXUaQjGvTo2|XI zhNAUm1eP-D{xIF1v;%R}55eBb{QZ2l!xjn6=8wSW)(3|k3JT<&K8p|f8|&R>{XJ+{ zBz^X+yOq2qHgKJMf!Wine7^%5&X-O1vV!@Y>0H1z0U8g6Zl6{sd|i#=Gi6PaUVX2`u%naX6d@j7knrZ?O>5$ZXN$-5`g<5pZ&+9 zG_M={$X6^VWdCsVDsu=U8PklNGcal7xB-ea>$z)hpasBBU*+ zG*4+Pm(HelDbuG7(5b0-AmLKq_-6(GJ{vC1sjPaHk6)_#BWk!-`OJY-IAF-l4PQtaOV3999}a)M+*CEg4Y zE|pnI=FB=#q2pJq56EJLuPCmkD_FrsXFFX7i8v}2+vmYNj8m#9*SjjDEa^m?K0uXI z-p#CB{9_m2*ruFIBY!emdyaEAy2d#n3io{Z2Vh0p@1uvdy>RvoU=O3v|Dy7DkKjM% zASeJ>SYA%98vvk}j^B!R<$d&pm~cBZ`@iQ%?i0-oEG}1EN14a2X3sTmJPQZZ1&UO{ z$3*=$OXr+B9%86S=CfR2MksuSc|1`sMS3>iL1E-?GjGKVqdFvk66N`diWGe#A=#em zssls~`YL!W-^C;Sz#H!Xw-u&UAaDB8E0E2cugoc@_HCx0W;H;HqqngGj*zXYrx5;U ztJAHeeWg_=r{Q)*{fVRcsPJLw9M*)nflY>d$4}4__Qb=JUnJ&A2+ByWfmOXw$;|!U zHJQYi>MbF(VTs?KYc8O<-_>LSkrzJ>?TV{6F@w%|DTeEgCd3^wZ2+j$WR%GrtXqB8 zbviP%fOf+5t#8H}^E%nO$1~UQlPO$Q+BF^*eSpXt*C=z4NJ+?8wf1ncTs~#f_Tg$L zqx@vQ*1z|AI{e)rG1uf$Z4&5gfqGOd2E}|}O9}9DQDP4Sx9JwG24RJvbeOlop-dAya8soGAG%;P^M1&bo9q-xs> zn)-&7R=UUoFYbF+%Rw58T;ws7g_1CjU=hcjIeJB*-L7$>=H50$9q}z%q33eCC>EFPfti{*VJZ@>;}yzC$vAhXo;O8ziy$ zVifq~q7d5WPq)c!*q%?L;W2fA%;}O7|NDBv5m--{a=u|s;--}&&jZ7yE7QhHa03Kg8nV;k|xeJERY7JsLSb?ehGI2`$t1qM0 zxx(aX1^iHsY))shW7lgzZb%1`?5kw-KeH4WNCNUc?opm?|7KO;iJ_D1UBoyZu1)lY zy(Ec*VK*j0(tnEv&KmN6cx!(t!4bW5nSz%8GMot*{5>QntUyNc(Q3JIbQ~+V z1;BLL${&CHXk1D3{;zNeOdlH&L1U-ty0dFqhqqR>v->$|`PRSNF7=A2)ajx$)o3`~ z;<6X?*!zz&UJa{UTj226i07kCV13am<@8B(p7t$+HoHx&o1vqxqP0C!{YcraqHRW8 z*BD*LP2P*eKRsvG1eNhzG8av7oK4+zCm52Ws|iqT@Lz!>-H;FF0(j}=&_vI}{xqd< zs*1`O4=1botj`_P;>w*j^KFu*N~ewKdd9K`Vd!H9!!JvYc00C1{@tL_JKhINGce7u zzJ2 ztNOgNSaKoA?@L#i;#I~Axyyt+A1RRTu1@V(c97;z24&3sTb$y6IA*{T8nQ^!p zYaFSqOC;W!T!WZ}j{JJ?W@d{}%Ecq@>7$yL8vkzQC;aQkSdsG{KUTWZ%Gk_T(KcTB zy*VrC=M)~_zQFi0A)6eMwp<=xWcN*Sum8g;e`)qjs4DA4wSR$mbK7eJhYdA_!I zrc=kgO<63i23pOFTbzC)kINfS=BT$e5qH0+hg{aZ_uHQuL72@lc^r;TA~<)aXJ#ay zJZT?2sH&A|u85d;u)*=iZ!d?n2(Bs^wJJ%;Jf4@KRKDH0N^Hk-!|hK35NQPR9w+@q z34meg5Z(!{&5GMcp^4nCefUbZP8*BY=@tFb)6(RfGdOyho@LWpx=H>XJaB0gmFk(V zc2iS{6=q`)E-^P0NBh()_Dq3HZEx3zEvu?<^AEYE6cr{;x_)^kLnTuyO-SI1ZYwyW z8cmn{)ZzQd896Bj?eQ9OLHg2kL&hKSC|?vYPG>U#U&{wd1Jf-vL6aLcFq5@tebRl3 z-*2eoM5>6^aG1z;05SCZ{bj^lbE?He^YO9TF5x)1CJ z7j42x_a%Em-Z5F;Ml0{BWn9f7>D@jX(CqD|aY1!gQOP?moy{pmQ>FnH zYFDE?P1&wG!Z&zqe~BVcd@svcF^gTt%5I*=A~a#l0kNI#>@1zFGgf#xC7Z1JZr@Ub zvZiS)wfe>pdI@BVkZ#CkAm1+W2r#2EZ9(UbFBahj#sR)fMxVLrg~T3%tYNnAHMAb~ zF@8&^sp#JO`pqRkU|qj{oer$wt5VQv5-U@RVA=P;gsoszV4r|aB66R=#(9M^dABmw zAs&V0$YXEe;zGd(?EDeJIN0xmF|H{YVi;jVw3u3P?njV4GsM0;a-V&K{^_fm%$5Sr z2CrdV6KY^Q7)Kl$Bih?JK838yN>CdqtIk`y+w6DjCr-93=I7^UuN^siYVYq!N6snZ zc3DY)~1hnzkCy||<0J39AVtC&nK`Vh55D2ZGVvZb#`YD&%57>4ckEeX@jW>om3cTi5pMKer^T4aStCPX5*&rg`vk)xE%%4 zddBX**7nQ#(!3C}b?=@d9%PPK9bd)yQ&|T6<{v#f(9%g-DK$G0 zW3Io{g&OutSuv;aT)OJENCw%iYo{W?Dix!F`B!gb(5t1Vzj^mo>XOiN_!rk-C$XA= ztP}iA$kjb?Cg#Znr-9}5`;#IQ7|JVzC@Z%}FoD8=9{j^i8crGav2pGRg8CXXOrAyt zpG!gi9HUMw$3?`0HKUjXDi5k_YTnpkw+MhZB?3LLFlELeJyR0W^n#&YNOn{5@fQ;C z$UjVf-c(PxiAmATxHVoGGn_OfWqpzB)x%WNC>p!2@@}h)`>s<-F`JJmT^N#DZdU*loHuJ3FJPnJ{SlaUU^P~?91GRr^bQXaK2_0-K3fr zspyPw^zcvk#*7Kj@KrWuE9&4F7RU+)W(UO$GV*!Ll*eU~ETkFnpo!=IY{KnI$0B~Y zpO4R`1M_-ro+}#5gHBk>USN(^*?u8Kn$Vdeo2JouEH{`tH##)GD`h@-h>3p>TY53R zQ`=zCy7M;m;GNe#pvzz7f%9|gMJY3>Vf!&0K`k!OD$cFiF#ZQnNfw)}-fXfVw~Z{^ z2B87xk(eUmx3F5j0r9 zt~6O@HScv@piDX&bB0@Sm97q2XrJNI)?Xz}(mT!u&DTT`aVKgO@E0_RSe~s^bvjSN z?IUo=>9r*TB=?d{dzWOQte=u*+9xg_b&os z;IB`Q@3!N=y!-1&BosN+63?5AtPa7lT43EI(Mu^0zriCo(Htp>gQg@F86z{lIFMRL zS{n;f#)lsTdzo|ob@cYcWd!>bQ3QWxXec(Ji9Vm(Ug0zSUZPOBHZQ2A-^D~ht`NQ< zIf2{e$Z9<&J^`%8Jz1%^@8b)H54SPR2dFgj3Dgs(auaFer>-R~Stik+_9yqIrYH{9 znhX;mLVQ`mmo!pFvrXYudzryQkql9Q>vlD~lJO=v@as<>YRG3<=HA6?A*164IS-U% zv7l&6m+3Zp8V|-JdNDP{d>hS#e4tFIOu6SLj@CXj-PDrL3R_7gX#V)4s!G%s)5DndJo&qH0arxwIvwe zbQ@nxx~FT-RB(K37}6!P;6EZyU_Ie&q-5c&L<|!1GY&kKng2Lo*1Q zP^JS|Z^+6dCDicKVP2ZtWM#hmf`HNH2ak?30lxoCqW$xGI-=y4WG*A8U{grFr`lDV zsXrb3e-`k5o^bx3I1DZf2SUcD=>m-nqmBd8A>^}Mcb;N_L%vXnDrndE$z{TxBNpB4 z_>;zOcSGf{mBOl87Oxe}Tw=T&c1s}Xz8)N(_fSIG@TDG{JnbQKUs+opb;9cbLX=m| z;J|M)W-+2e>+p{^82@g6-9kotY5Y7aCeT*C2zzaj>pAeoz-{T+oM*7L0f?PTr zmXOz5t6nC& zTcb~OqBYp)3V2ST`(B}{xmrGWv08j&7RyB1DK2lTmK*|zz=Xd1g9|uRFWf-_WB97; zp1S?kboHSsWtvrmPTu+|f6nZSjWL=t8m z)uT7`w-4*kTsGN?)tGF1;0C3x-&=#hfu_#0KJ=L(W$8AnD)`Vs2>Uw0(>dxB43Czx z@G7Bt1J>M5#vVm5xi7h)Akm8=lV2>P5>Hbby3-kxTes+7 zhw$Xa`)@JXnoQLI|C|A0;iZE+7l4S+7(R~LGH=R8U4Dg>nLLU+(znq!DvL8yuPRR3 z>U+BV5YdB8DZ82*3NA~pjifayg8T!mho|?3RIS*l!pq#W;YPO3;X=C)&~y68TmURZ zdA0tC8N{QpEJO|WK3X3ADWz=Z-f=~XvCWud4y(6O4y*oN4=liJxnj9IpU>6t%K}r8 zZKT#w_Q)g6BegL`dFQtTMV#A05D@w=Z4e5Pzk3Dc0=l3oiPesfa?DonZM6qV zv#f@a0Q;P;wX=(4pPBHKfsXdD2Ijv8;9K0A4%d#BAZ-+GjgJa$V9{BBwMwwbxNwK) zjc;*RtZ15S#(-^*9l#%Gq5hWLu(>`P!$WI$N%=~8-bxzBcO>fF_$TUJrFQd6sNk;j zxP6>G_OxdKbQX)$6NPz(H07dG^53K3&HdN{zNa_xk(<-ZCKsM!UHn)Z1x-e~P)Zu| zq!i3RT;vRV4)^*oWug;Um&O?!`j5H!UnAnCn2MwFN@x{_uj@l{mq6Xyw{TP(2rkvW z0nh=YKF&-5Nz>5+0EGP+Onij4R7%D{+Pmj}X3OPmrx++7%UnlxKnpl(yuU;E1xhZ+WEPLRU%x`*DG!F0?iu?lqUfR|TjC5aP47h_j-WI zNbQnkyjy{I+hlN#!fIKC%0>zrfYKFD{iOLtx=@tb2otNSSW8~SVd;DR9%F$d49Gfk zo!Bskp-V?9y{)gsx1|Riz|``VJ-k@uWm@ug`$f}CMAPZOqE0bu>Uu zb(eSdbxh_FJ-%Mu4k864hzc$udm=5Njp|Pwef-PDaA-L1T$%)u81E<=I;sIMV)fMt zb1j$oL|)?;GQC*mK0i?|IA&clIOqA5nVWI?AxIF$K!ULNwGV7v-EpyOKy>9VyX~*? zh??n5_m)L`Harg?&szfswB*IoZF$HD#;-eExdkgkFm4+c=i?v9uThO{>Y0uDEdA*vAh$M_Y=sFdgtZ5m_`1 zMx0rhquljKEXf41so#6RuA|CpGV&2t*qhc=n@C44@!N{Ueq`u(vfKQ3Ab3AZ%rDfF zhnAL>n?ylk{!bfUV2M`n=~n|U24(7NJdR~&S}l{R1fBGkr%_;fCK4lu_gfc}FGv5;E(rw#^($wa? zL>GMtApMT^uij*E`ks!DnPh#^zhdHu<4ACc7o=Q|Whvt!8$KZHE=~zbSQ{9GOqI_u~|!IrlxEr0Wwz+RIK}Uf!zfjwC%DD;`pto)G(RTld7PR_u2<= zhbzDCfb1HfqlI|s&W@9q4=JrbS-^U`)OA?(UAKfAgQdCgrJB<724eigZmf9dL2o zvqx0981>@SxA3aBcL2K*h~S|nEm4v9#B9`7)CITl{}a(YsEtLzX1(ceZR)71vq@pS z7q7{-&PKto)l!LRF+|A8{^mYdv(rm)cDZ*9Q8(ba zbbml>jlQg#_W;NMsa?B3l&eN?7Rc%fo2l6EBrvfP1Q=Dx{6qag0mpFZjjEYTg&>;% zxNhtl46LlUNT~EhIfFtsPsk|z;L#7?GTBErC}(|dT3~PJ3%UA;5dcadamYG2JgKnO zq}bR0+U@HpO)z?yMSr$3L)zoCK7mY5=f+m$=E^3zRuHdH_POr7dWK{|KF(tqHW)S; zwsIGCw1We|6A|&J(f9cjRwBRqjXL6|J{iiCuaTP_|CG_5GEX%~vxD1qStnhmxwW;m zqa*S-SMIa$>*w+-bixzYFPZR*NI6NRw_hD=hk=u?nynKn2v78F+(sRv^K}xmBy_K0T$+8Rl)851Rmme#axQX3gpQdpJaqy8eB+4( zT{CVrt_hrV%Y)ioLn1t#&3w>sNJl{63dhkxmOANM5pT6SC(J-$=oP;R0X{yBixCrI z;*%fvhAW(P`#V{?iQ?D2t*c?T@m=G~^}2*95sh+_3?udttNtsrXv=-yLw0LguC#c> z>AL4R*W6vt4+Ku6h%Jwy!WfQ<3y_oiSJ!32uIEl;O|^i}gh# z1TxcR)HEd2RnY3{s=ID(!=GDpFo=okbQwmo*REKKRa@_81zfrYQivDUXzz;N*A%Pg zwY(URZL8#FwCM$sJ?`hHH3s?ylKdFg_%RH<^bHJviAej>xE4O@YL8z=iaY>)KY{|K zm1a%&<=2mHzLt2^tk!tmpijckF@}qMoLG?nuDr(o?okW``3Hv-qs>k5JG&ULvJ~Ab z&2WSND>exx^)2MSGnkT!N)-4#JI24h5&z8nhyrXCi$rRb3F>4&(IR?>33ir92%ug2 z3L}g3E+KgTYyTK%1jB8c{+11ncj~Y0X2@Sn!X6{Yi~9RKwD(J6l~?SnLs{W-PCc(TLBy<8DfQc<|{$!9V1T%75v9W=_Gkw=49F{8giAK3prG24YR$Dq&G&yaG0+^eYc zd@=h#IuDfCQvM_o3^WFSY~t=mKO#;bG?B2le>(+wezM(_m1S_x=gS>Aq~iZ}Rkq@*}IRf=aqT&rnTH?#0$@3n+@TyE3BKx$g?{I#6XYJYhCTr9h{| zlaKfCvXj@(DgeU%nm?Q|T=%|q)MCu#KtahY>9QqI{Va^m(8L4+RG)#qDyUimL~|%* zAx!JgZ3K`r>VCuvlvKcB7{0fKmO!uD64VQ`ny7W>lH3?4iRx&PS(G~jrlX~cpct!qRVS>kuMg&e09G(8ac`j7%-EteL>5*Ih7w4#@zMY zxa4l*p3jry_T!mQK5{{_Pmz!ZWEx}Ois^%n0>zU!!96*1!NmGv_T~rTp%MB zr4Gu1m2c+hm?GL~Prh9qMh_GZl;Tjk{`jw_*VyKDJ9q(guBfIZ*b1`lc;vF4C6Tj|JY#BCO7e z4nz6Q9^m710X+;nDNy8=ih{y#JlcKL$`rmL{{txKB%kZloooe_1`Wk=vMKWDezCP2 zE`0O)G2c0=WB45gr_G4=_R5wPHc;_M8oMZh$>96`GUOE<#A?)SbDF@B;RdoO!)uHR z_Q#jxm40MsSl)N_;J~T-qOrA@p_>(d5~37ccZ7Tx>7~)KW%vCF3NmKD&W;W`SP*t$ z11M@2`%5s=X;tH6Dv7!k+-pUHp_^@exM=&$4RmQKDQeAfEnuxKdv4E<_uQA();{EX zb5BKOV_0VvHV-GSK+L)48}D6bSi7m~wmFfdhzk^IMIFO$(!^4clkcrnSFcPQ7sW}> z9c*?rmuE=FyH7V<&-Pd7Ogeop@uOgoUI&(~Q-h^_wVh!UBSa!j*pnAa&2x|H+59HXmxy>!mzN(t*fy&gqlnf%CBXOWpz78v?KinSbPvD&f`#NsX zfbt4!PaWzOhKm$yT+sV1sr#>&&QK(4%~8!X)p;DR<{eT`G*YX))hsa%s| z1!~nBpqS5a`dnKy#gix9FROsvjf{+>1+v)mf})YuHM@gAa86WI6zDIy00lAH2~cHn z*R@D!u+?+3=F+9RQ{qDWN+=v0@s!r4rqw{pq-uiIdc0JtatoVUX=TXfQr#VOnWg)9 z7QKU-HZ@k~fFJDJS+rl^74BlH7*}xbO*NyHSM40gIp?w-p9Mo8=LqV%gYs{-(^ z`JLIy+bM1Z0$XBCBo6N1G((z0L;o0HeUf zIpf^-=x-lvvZ51{l9DcJkRDSxZm(P?-+O{~RfHQS{6;(hx$R;vq^JnHxCG*~@Zv7d zY(Qk(ikP-kthys;&|qzxESGbemzt8kiaR^!O961gcM+?PVy{gD72QP#;-@d(`)aKi zbiIzR8Auf`3C5qy`1r)<$9QGXP{E`~y_SU8aH0J9S*Slpiz6j7sQCgfHlo3EP~h|t zQ&SrrE?O)-4k0{N%~k8#T5g3NE#fXzuH;oj^^v!B`H`D%1NnFXvgM>7Ckj3Cjq9kxE0z;=4o@EUuif#&u0@?cGLf|mWx0@HP$zj)!{*X zA+wWUIqtYxy%SXAQLnVa=FI;j%9qGwz^m@6GAK}lQGYQ`hMd|C@C9S5r?1#K@byh9 zALf8Mvl)Ijidi|m)l1;n3Tb#;u9t&&?rDGVZ%VOX>@t2CbKOT?H1zsTX{o6^`)=I9 zUUf?dD9|A+>zy0K!ZQE9qaxhz+4QA!U?p>b}3J@G)D=5 z2-Q!Fq-P5BT;X}e;!H)khOET!6@|EGNt8Gd5sP|zeZqkZ|7_#?NeX(}=edA>-2KVP z(20jsmvo>;uVd7Q7@b|bCTSLRWOL#n=X)$h8fgLxvG0l6OtplgzMy{0oS6(=$VS9x z&XM~`&9LKn2m_B{J`g2h;6Q#z9$xa;DT=GeLEPbRfN7eb7RYJTtI()LgT$_2Hr`liRD6oJ}(Iu(asEyv`Wkv?y(n~x@w@TGpsLYu$j%J$cHAky$1f78aP4@$zH9|K= zbG19cyk=fW6AkSC*If+ZwW^QdLJeqDZ}cB7gh=6D;wcUEv6-wb=$BSB)R#V5DOoOc z94U5G0`h8;a`HQtyjG)@613%6OC$8%1Z-WTa13;3leT^hyiDoDBk~FB1qp+Z_PhL? zu7(dvTSZmTI4ROR&TF1F{pnVVK}(>omKsJgaZLHXvAxQ-vJbUa$E3@H3j13=VbNDs zg4Bq%=(+)H5?_?E@!5B%x0AU_N+8`cVt#k_yRG6xH1y>`3|yhrLDf~kufO9ZLpT{k zcmjv>(y$)c!z6~voZ{st`Bii;8x%yI<2=2GlyXS zz)O8^LiFA-rY@yoX&dyL>?b5hTPl9hmlZ&dx$^Op937`5z_myk=y;RRMOS9^`b^)w zTt%pkvY3SZ+Gz>73Ux|fNkY$?^|ofE z!rxFIoQgkSnl{V!&zFq~Cem){obS@5UZdGsDPgFv7z)cjL?I?v=V;qgQ~hwvv9i#>To%bhtPNbTKJ8i zkzsO=m>o6UvL!l$ru6j@`-r|&ahAmiWyhv?P_kiafM>UV8^(DHE(#Zp7S}#oMHTmA zU+>G&1_Pf zh7Hwje9KumwXaj+pSVY?TM3FUWxWHCa41{3Y4$^Z_{MB_R}Xp!(f5%hNrvDwm&Fk1 z&H_a#DcdVuuZF4m!<}c^z{hgpBxPD;uUWFdHy-NRN`s_crpc0t-+*I z>qBj!v6}zqmW_86mLtM7Qi3$*57JCbY%)~~E}x5w6`2T1Gnuvj_*!pMna0UOtSGF+ z6|0pMPt1lwzDFsxu%HI2HKhO8Uxk&vw0NHJ*GR(RIEw(7n)Yl~ zC;+9b;VIlZ4uiF)6Ih-wKZjFLx{3);4ovTE#d)iWiK0~m*Fcrs0{z~oMK~qvBE2~7 z7n%YoMl&M*ulr4WyfPeA4OIF-mA%@2o}W*p$hZS%%d)Dh%)%M>dg&p5Rz?s}{$gxI z{Jphm7yZEetf~_24}9hj8~>%pg$$?%G;~#h z7^r&PbiT|R%6rEE18Q>%Hvm^z^jSHcVjv$8CQ|@qSlGL-Y9J z(yTqUh+~E^$I?+BFolZ0arA?(*YI#Utvnf!ynB>(+q2lH*noe^eyTaF3nU!G_wMy% zDXN}r=jk6e2QBmtDE}C$eTOBto%EDR8-l?3$Q_{wA0cMGC4}LyaHx93pZe+J)Rl?0 z13pullP(E#*m0MHzor~5D@;-%`)d>-P_TU?_<(QEC-qD0K5)7?3_4ltLl~`QjmH+C zmzdq8FI|gETTgaz;4MHmlsgC%d zS_Pws*B`z}1U~zd3_tcnRsw_~&Bz3vBttjfGiz$S1hNexi1m97+)Mo4`tN_;_4z$e z;!n=$@~&qK_~*R z!{HUH-BDOV@+E9nFrHK1p~9Phj*E<6Ie!g)xVDe{;r{FEN$&H~hjSz+sq3}JQ$S*s zpZ4_+0l=+@^-!W@iU3-J0ZD80)&2l4h+AuAeG-*;d#-fa%03@laMp;cxT%hD69-uZ z0L#zLH14)7sv8P;JJ>R6`j=P;6`k~P-GC6~-MI+oy$X+`#f-T&dj&;`DlOuJBl(F6rbiXXT<6nVMluJE&t_=xCeS$HnKCvHe3g(49Yf+WUX zTT^!GdAA7-3b0pn&2NQ@Dfo}-N|C9FnC;3*~4TD$?znk+t+Fxg& zco-lNPV)*}_~AYkYR8zgk>2b@dr9Dk=Kri+u+1JxJJy5E^~fjl1H0w_?=+zJuHzU< z)_w81x>x|a;5;7%zczydLPb(TW3S8XUyU34ZDqbu$p}cn)_WB%=gGpU<`yJGElRU1))e3ocrT5r3xA%@9 z&wcACf$||36#BM~v8MA{l2Zm;e4V#M zIOG}ft^pA~h#I>&3D1sQ@i{p&J8EP%M2e}K!jX5mrvCW<&q$6u;V7yV<(KL2Ar<4r Mg=K{D1)jhDUnH3lWB>pF literal 0 HcmV?d00001 diff --git a/packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/images/run-transaction-endpoint-transact-mnemonicstring.png b/packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/images/run-transaction-endpoint-transact-mnemonicstring.png new file mode 100644 index 0000000000000000000000000000000000000000..9544dbccf50b30043f7974fe2ed0b754df8e3e7a GIT binary patch literal 27070 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Ν$+@D{`N8!Y$_BA#i4>)zC~Va$Np|R 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

a zBJq>t^_`hLnp^LUH7s;>zhCgvDtJRAx%%5*8D$U7k*~+7J|ZnM(+D*|TWp|DM2Uzz z=;CB6V&;oVMqTA<485-P`IS_4z|U#*a<}HhzCfPh;?<5;42wpc8@W9jvEOU7qNl^P zh|+4^#)E>o+ArHb6SY-j9-gTbh23^_`}H!O&-Q%jBO3FNNcHqzd{zwB0lF*c_Zp(! zP&q9P>9mUZ1{D_P)L4#LVQXLDr6W| z&CkBtO)dv+D<=7RZSVZ`wm|gzCI3!AbNJ9 z$ze0ilGJ^Dx?^kW0+d(@I<5^7l9xF<_D)#`HoUZ*ulV&cR%SMkIM3QTTle-RUA0EF zRqIB~WzPuT!n0=?uadv&61JTeAnTE{wuyDB^Rg2dw=;WkNvB~68?I3 zopHi^=_LU=)o)q)0vLTKe#NS?{eyjaqn+W(94OX|9NQp*h?b6z&wlygHA(@W#K)Am zDhVzg1osRvkC zl2uFOD+}#MkGc~QHBEXlb$;wECqz?3u#EQ{{~4rtAJ?7Bdddspgl80tPDHL~}q!L%8Q z@riJ{2svSW2o*cRaZR1fp_7E0$JVz(Ej`O}|8>-KjdR`j%vWIAy+oD#YIEKdHFsXLX?wce4+^JAew?!P z2;#d+DdfU!+;#N_!kYjVmztJ#6KFKQ%@E=AUeT*Qftu|^dVAllHYQ5_fQvgLFW-G} z^}aMUk}wcRDyAmQo;g%eU__=!z233oZ% z557T6BWl!SVA!EvVgv}$6I~RUmm$>$nLU~RJ+t@!{*N_3(eC>T+Q18&Qupl|Ha*C2 zZoeH3j4)g@G)O4X--wC!$pLf@PRg4D0}L=a1q1{by*`+raE8zR=y!Gp0iz?ZZlAD^ zh|cmZ$D(R*^ix7ffePe+Lck86Aj`W2*9ZG)nAi&E0kA8AIl3Bck^-3}7Az*n6@_m*87PV;cqE9s%EN}-@~ss OkHlpj^wpzY}^WtW9`f#8UI)*IoLvzzu zbCSpLsoRDlDu+G+@6qT>bdaG@sgiOmiNq%W3!F7md~JZ`>Cj zg4N%8h1PfMh8x)gw9%oN>vU{?)?Hu^6=e6xduNe+WWI-J+>L=A+2)KUuM5`^7D-{Z)!`ofoiO3 z;SZ`C_uX#X%A)!4Y{kCs%18wBspkZXE~MJ;hx^VgM*pED(fp4qcZg%BqC`w*GHI%` zrnuGTmigWWDF(b{yL&s`pKUm)zBTOqtGozhC-jTUqYL*S>R(?gZ{g7OP+GIlyiK&Q z6-{v(P3)7Pi#kj~W(bxTRR?yx%u@HwrTQ{>f-lMZgc~|!O2AyjywBHtau-IG9o#u> z<1W-#|KM(ey2FemtEbET35i^X{-t~Du^4>reK0B#Ui1%#DfK<5JFc1#!L?+|32Lj; zwgf`ay;a6N$~N4N9I2OPO>=6>rWr;{X!uVBgV~t8b`w>ve#m(-xu>Uko%*}bjHSSP zoG6Rqt*=UwX_jg%9^Mq{m8fF%Db%sMyvZCzlpmqlEOg_BO^B?7sD``YdODi>pw{q_ zfg=R^x}g&L+nXB&f%xxN@bst&Q}I1{dyPNe7lR~O*vN=sL_Lao_)Kgo=>{Xr@oNJM zed-GWvD&7Fsf5O@<_2%yzAs`j4?Lb`kjl@VXYL)>!xxrUBR2b%aZtpx5LaTb-ob}gtwcLsw+Ui2MYh6J{TsbPvU=d3bPiK6(g`=<1nu5 z()5-}sG3(`;nMl;WtyKhHG?aBob~GLkcNhah*cvfC}?@SZY!J&k{2(>l8T#|uPe9} z_JT!DxHt_r)c0~PGcw-OG@pFXWHnym>ZH$CO-;?p?aP-h*kl4>At7R(m**#l_xW+X zfqjV5b#JMUa%?Ag_{KX+B3HjUI?#f9Qz7d8iAFxMk{yv$Bx42n`QTEV)YMsW@vMS^ zf^Rc@&*!L;laq(uYwm4tqvPUcJ~a7ibakrDniDVw4teTcQC`lK{f5UsqlhoCf^MDj z;X}s|-9as8gBlx6E2|>Y;t>6IL)mxPSvI7kq*rGxSMtdahsivYdY3h=Q~~>GV(#?B zMD5-beiAOj^K#z{c`HL5okzx>Z)iBWxk)dN)YxiwF=d(t@A-2_+f6pOl{+u}c%#9{ME(O6^1=`cy3Ql zuR(*W>H-eMW8;B=0bUlCVCG*R&E?yK#l#8U1Rtyp-MaZsA)AS{w-W1pSb7exkB?8a zEcV{+?mIPhvM($z!JWKkT#1(mf50!6K2~F^P7sVk`E+@4QLD-G;9EEu)-4{dLp#^n7=y10aWIa*VO*(cU- zbVaVt_qKemyiBUJ%MFXj2?)efH^%GSxXUfKrdpH*>l~(tSqCN=*wkeUUJy`!W7O)Z z4mJ>R-+ql?=LlNS`jLY{_wFtpB2Jcwxim?@ehf0@Eq?NKKCidZtOLQQuj(moun4q^@ zCL{P4$8(Y8EtiiS1C|vWBSu5ySiX_-+YDBPX9Te(hCqe94)e%wR|Lu4v%6eT_VpY~ z(wsDf$5+kA$~E%%yKYVd!{S*rKUP&myRYOYzDvk1=vB_SgtV*h%V%cdLoUbu|c z+KUI`v^Q6Kg`0!(4BwA`0 z{5;uPD&B3mJSdN4QWo|;R}9$GAu z4*hE2|EtOp%Z;ewb6x``B_$>EgGY|k2xj|%0&L9!n`$0x42+Z6P%h>ruovQ9GfxnR zDXBesc7q4)?)}&MmltOYLOp$bBnf@R#l_5GXzyxEok#}C(ioB-Mw-L?EPkZP3K|+3 z&eq_XpPrtEY_YD7R{F`UoHmt|*p}(ltPYh(JLa(|YN-1|SFZzsoPr|!B1anpf`0=h zb9+qqaE#b>AQUJ;eoCB6poY)`6-izCQ;*#|+1Q6itlA&KhDuaIKC!T|m3SVkqBGyP_nMlQcdrFNi+{i> zOx+rXL%;TJLy1l$Epdjmwe_HuV~x!aD|>!FL#c8{IN6d~qoI~t$tas7N>*oQXBHW8 zeU{GFWK+3OOT_uq`68rDzwU5<|1K^r+EztQ94ssY9Rmw1mq`o`TcIVS!Mip64$C#} zWTS4X`Fyu~nN{b!Uz$qRGw505jc{c&e$ZQ2kP=W|4K8c0OFt;Ag>D5; zFPQrLWPkMNlh4H|uSL)COd!=e%vmnp3Y~oX0Y&i#f=={`2k$jY1nfpDoEExde8D#m zo~ZZ~4iXX)v5bn#s3oB%+(BcV`XO79W~8zT5bR>p_K>7;kk5#mph)<^khYPk z-a@Zcq^8j)DeE1A!&yb5It^Y94*S`*;O)E1?9V?%g~%irCKk-9)5=<6Vq)gx<{I=& zIjeXVS_C^ds(aLs=QC_Ys=LW3GAgFLyN3?`U}5=P8Tp(n)QlMVq1k=VttxQuWA6=K zDWEY?GUe1PH+0OaT&e;EOg#po!KvwjOB+Es`AMq6%_sp;p|Be|QmK`dl|?Lf(SsD?b@d3q z>ZI`75-Z-vpG^!43%feHx}2JenG7qq{K_xKhEN0$$DMpH^c7h9YL-R~1Y!*__)1Pf z(iDEN;ozIw1DSLeHx#|VhO(9?cs*a2+r50`5;#atLxVC&`P7|yk`VPpXAFawb3y@l zgb8>xE-MKCgd+M=Xz^)k|Dchqgta8RxFLUkxj&aGRrgscczV<#35Lvho9eWz_ygE{ zg$nIQ*(EJs6c>h^-m-O`%?(l@7C*h5qYRaLEpRUcQSgwOy$mduqzSD-ix0?OW1PcH z<-HH!EX{ce$g)ob-sO987MKYHrNdInAAjlqBc_ z{2TP8yObp5F|3;J_dn%9a{EJi5LhD`hH3?yTF=#+tyf&ESA`j3jtMteB_t(H4w%Zb zE>R)z+TEQ7(SsCnDMbz+s~HPY10<>#*x411X`YbmVLxHj^x#c#)P7_5iLpNA!(|K) zQC&<*jAHyqe(`Z!kTU8XvTS)DhP>JvHASmNWQ#< z?nJlMK@4?@#_j$6+Yg92^ynMkXkG8d&`flcMg#&uuPj#7lBdva9~}D&JSGiqISFBd z9=2bfKYxb1tSO}mD0;W(d>p_B=ijvfEH?%&E=zErm@wQ1RawC}b_~|SU@u?w~FI|G(tk5Z4lK3xT8Sl6JF)JZ6zhbUNeuJu1 z)jSY)>*QW~0X;5mWKvR6G@bP2`CcEw^DxTY2w%3uMt{(USt6ARpbi|1rx;w2;~lslP)$hQ+dh) zjdX3eY;AQl1XiL|woGKF@k_0aA;vv{M0c0Y;}-KP$nb(L{LQ+mML%PTN-_UM-7a4R zpD8;TeL62YQLyq8y=2;pPNqy5)}n!4L^m~uY&uk+F|=O|?S`CPL0;ZZ#`CAwyl555 z(ey1w!Yg`ODn`~}@dp2LLs2Tkp#T#=2r=vR4QsGQfBGInWjlF8ZmXS8E)}rT{Neqn z(}8DdndVh_&H?$fk2l&@5&7Llm%AjgcU<7Qo}_sU#RI(oZrpmSH~e$(Ohc|&Yx4!5 zJs-c7<&!i;7kzw;T+U~Fi}vs>YmiZ&ZSgq1YVoWK#6@Jdkxtp2i!mN^s;{o#YB&=$ z=9E2^;rga93Xy^-`CKb(D1*~1G~g>ii$4WMr1jPE_DfpX0kbs%0ZqoF$UumAFKGFtof@xR^~P< z*jaH2aS7(;Hr9KU{`bs&QLm(#6w5f#`kby5#?hq(t!@QtOjYNt0hgTsY%;yM>m8tigu4`@Ptu1U&&-v|Ts z=dGf|L=yWDHaaPSDa~gDtQ_)J45^VEVfVXHWtZ;COYRa>dL3A>n^YKw#zt4`tbG6ldib+)5?=z%<7wlW!^bo z5ua3xC1$}s?@r(-d0y`X7;#4!X>39Qqr(%A-Fewq#+fVP_vV(CSlCpF*;g^W9mG;$ z=6>$$I;AlTmU!}KrtnG~hR~G)X;68V#(>P93o}DbVmE!-OKQM=ba_C|T zAfYq(qxIo=LepUcdR=ZZ zDfNwd&k_jQcJ<@<9_)3NK$d!v{WgQW*3eBzsw{dRJ1>U8@GHVNy_b)dhx)kVetb`> z-1#PT;%&x8(rWoexeZZx7zb^+YNe7=OuqcGQNG~a|8~Lm!mPj#1#JnGp`ZYb%Q#LU zY>Kr6nH6zU^VjgI;5}i_v>OROxaXBKggh=H*A|>htEDoE5n89(e^SxfPajdO^B*!H5KVuj;aR z?5{FP$vpWMy(yEElh;*!c(|gXV!Re>c-nw7jI8j@Ik~u!r-kQcWWDXimx7O86$mZi z=O=hWQ7h!@D*Mw&qmW-kis<`OGl_c-i>r=Be3ZSoJ9!hY zTs>fFe6I5gOGDd30bG&p^`4=tAD|z0Q-1$Ia$#W#Z>bpNvLI;7!b#)N~6CaoH2v=O!hB;0H+&<#p%)zM?g(h4$T%}c&bX?Y}9*~lzx^<#hk!I=|o)v z`t&z7q*JIa=d35a4gAXA)12)iqP}gu7bpr@tVlxT9wiL?fv@vEvmR&iMw?2Fs%0) zcUA(*VjBu@9AYMlO;sN9xJ{if@_cx(J7+tz`P541U`$qKq-ewuKo16vfq}tV(*$aM zY(ja}ymY_tQy)xTMen=jB;>U8aT#+7l7T-PJ@&q|=a8pZB%R2#fHF%X*$0K|$rF96 zAeNCrWh(aJ=RG2CJuMje(s#b=Qa1n=~`+LWk-W@<7qPW5;9xl#@@DQy?U76|C+F^)*`V|Wk)AOSj z+q+m%?+6;9rBQX}pN8}>{gVN?(zB>YbLyPDq*HF04uO!~3-Nx_+SY2E(YB{{0-s17 zsSkg0H_;(JOAHJaWMgEDMZ>{*D`#6Yq%bSK;?zX=%ePaKOh~~a$ z_6C+rYw1?T{Pqs1>~|H3I%}-N&9n6D`rwAfFFendMbmOf1X-f%D!n0fWO zhlLG-XhtdD6qlxP%w-InXo zCU(W%k7}Kmcs@^{2`X5|!A9pL9EPF?2hXD(sf$6v1Q9^71WEj2e?S?dR|}KrmJ-Iy zzRpLR&QJDk_ghHTeWb<|(9^+hY88}~fzWP_oO5WFG zw_`z*4w2c5RTjQir%B?CS=h0j_G?yGLYSed@@yi#aUHnX3>yu@WT15oWESH zIOwSktD2k4%7|7A5!qE#To@Ƥ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 tckr: req +activate caller +group #LightBlue if web3SigningCredential == CACTUSKEYCHAINREF + activate tckr + tckr -> tms: [transactionConfig, web3SigningCredential] + activate tms + tms -> sas: [transactionConfig, mnemonicString] + activate sas + sas --> tms: return [success, blockhash, transactionHash] + deactivate sas + tms --> tckr: return [success, blockhash, transactionHash] + tckr --> caller : return {success, blockhash, transactionHash} as resBody + deactivate tckr + deactivate tms +end +@enduml \ No newline at end of file diff --git a/packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/run-transaction-endpoint-transact-mnemonicstring.puml b/packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/run-transaction-endpoint-transact-mnemonicstring.puml new file mode 100644 index 00000000000..48a23798182 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/run-transaction-endpoint-transact-mnemonicstring.puml @@ -0,0 +1,23 @@ +@startuml +title Hyperledger Cactus\nSequence Diagram\nRun Transaction Endpoint\ntransactMnemonicString() method + +skinparam sequenceArrowThickness 2 +skinparam roundcorner 20 +skinparam maxmessagesize 120 +skinparam sequenceParticipant underline + +actor "Caller" as caller +participant "transactMnemonicString()" as tms +participant ".signAndSend()" as sas + +caller -> tms: req +activate caller +alt #LightGreen web3SigningCredential == MNEMONICSTRING + activate tms + tms -> sas: [transactionConfig, mnemonicString] + activate sas + sas --> tms: return [success, txHash, blockHash] + deactivate sas + tms --> caller: return [success, txHash, blockHash] as resBody +end +@enduml \ No newline at end of file diff --git a/packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/run-transaction-endpoint-transact-signed.puml b/packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/run-transaction-endpoint-transact-signed.puml new file mode 100644 index 00000000000..94ab78b369f --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/run-transaction-endpoint-transact-signed.puml @@ -0,0 +1,24 @@ +@startuml +title Hyperledger Cactus\nSequence Diagram\nRun Transaction Endpoint\ntransactSigned() method + +skinparam sequenceArrowThickness 2 +skinparam roundcorner 20 +skinparam maxmessagesize 120 +skinparam sequenceParticipant underline + +actor "Caller" as caller +participant "transactSigned()" as ts +participant "api.rpc.author.submitAndWatchExtrinsic()" as aras + +caller -> ts: req +activate caller +group #e6e632 if web3SigningCredential == NONE + activate ts + ts -> aras: deserializedTransaction + activate aras + aras --> ts: success, txHash, blockHash + deactivate aras +ts --> caller: returns [success, txHash, blockHash] as resBody +end +deactivate caller +@enduml \ No newline at end of file diff --git a/packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/run-transaction-endpoint-transact.puml b/packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/run-transaction-endpoint-transact.puml new file mode 100644 index 00000000000..06aae5d4a67 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/run-transaction-endpoint-transact.puml @@ -0,0 +1,36 @@ +@startuml +title Hyperledger Cactus\nSequence Diagram\nRun Transaction Endpoint\ntransact() method + +skinparam sequenceArrowThickness 2 +skinparam roundcorner 20 +skinparam maxmessagesize 120 +skinparam sequenceParticipant underline + +actor "Caller" as caller +participant "PluginLedgerConnectorPolkadot" as t << (C,#ADD1B2) class >> + +autoactivate on + +activate caller +caller -> t: transact(RunTransactionRequest) + +alt #LightBlue web3SigningCredential == CACTUSKEYCHAINREF + t -> t: transactCactusKeychainRef(RunTransactionRequest) + return RunTransactionResponse + t --> caller: return RunTransactionResponse +else #LightGreen web3SigningCredential == MNEMONICSTRING + t -> t: transactMnemonicString(RunTransactionRequest) + return RunTransactionResponse + t --> caller: return RunTransactionResponse +else #e6e632 web3SigningCredential == NONE + group #LightGray if defined: req.transactionConfig.transferSubmittable + t -> t: transactSigned(RunTransactionRequest) + return RunTransactionResponse + t --> caller: return RunTransactionResponse + else #LightCoral + t --> caller: throw Error: Expected pre-signed raw transaction + end +else #LightCoral default + t --> caller: throw Error: Unrecognized Web3SigningCredentialType +end +@enduml \ No newline at end of file diff --git a/packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/run-transaction-endpoint.puml b/packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/run-transaction-endpoint.puml new file mode 100644 index 00000000000..1885b8d3f91 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/docs/architecture/run-transaction-endpoint.puml @@ -0,0 +1,27 @@ +@startuml +title Hyperledger Cactus\nSequence Diagram\nRun Transaction Endpoint + +skinparam sequenceArrowThickness 2 +skinparam roundcorner 20 +skinparam maxmessagesize 120 +skinparam sequenceParticipant underline + +box "Users" #LightBlue +actor "User A" as a +end box + +box "Hyperledger Cactus" #LightGray +entity "API Client" as apic +entity "API Server" as apis +end box + +box "Ledger Connector" #LightGreen +database "Polkadot" as polkadotcon +end box + +a --> apic : Tx Polkadot Ledger +apic --> apis: Request +apis --> polkadotcon: transact() +polkadotcon --> apis: Response +apis --> apic: Formatted Response +@enduml \ No newline at end of file diff --git a/packages/cactus-plugin-ledger-connector-polkadot/openapitools.json b/packages/cactus-plugin-ledger-connector-polkadot/openapitools.json new file mode 100644 index 00000000000..225ff1aaaee --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/openapitools.json @@ -0,0 +1,7 @@ +{ + "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", + "spaces": 2, + "generator-cli": { + "version": "6.6.0" + } +} diff --git a/packages/cactus-plugin-ledger-connector-polkadot/package.json b/packages/cactus-plugin-ledger-connector-polkadot/package.json new file mode 100644 index 00000000000..67ae96c3ddb --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/package.json @@ -0,0 +1,123 @@ +{ + "name": "@hyperledger/cactus-plugin-ledger-connector-polkadot", + "version": "2.0.0-alpha.2", + "description": "Allows Cactus nodes to connect to a Substrate ledger.", + "keywords": [ + "Hyperledger", + "Cactus", + "Integration", + "Blockchain", + "Distributed Ledger Technology" + ], + "homepage": "https://github.com/hyperledger/cacti#readme", + "bugs": { + "url": "https://github.com/hyperledger/cacti/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/hyperledger/cacti.git" + }, + "license": "Apache-2.0", + "author": { + "name": "Hyperledger Cactus Contributors", + "email": "cactus@lists.hyperledger.org", + "url": "https://www.hyperledger.org/use/cactus" + }, + "contributors": [ + { + "name": "Please add yourself to the list of contributors", + "email": "your.name@example.com", + "url": "https://example.com" + }, + { + "name": "Catarina Pedreira" + }, + { + "name": "Rafael Belchior" + }, + { + "name": "Anmol Bansal", + "email": "anmolbansal1807@gmail.com" + } + ], + "main": "dist/lib/main/typescript/index.js", + "module": "dist/lib/main/typescript/index.js", + "browser": "dist/cactus-plugin-ledger-connector-polkadot.web.umd.js", + "types": "dist/types/main/typescript/index.d.ts", + "files": [ + "dist/*" + ], + "scripts": { + "codegen": "run-p 'codegen:*'", + "codegen:openapi": "npm run generate-sdk", + "generate-sdk": "openapi-generator-cli generate -i ./src/main/json/openapi.json -g typescript-axios -o ./src/main/typescript/generated/openapi/typescript-axios/ --reserved-words-mappings protected=protected", + "lint": "tslint tests/*.ts -t verbose", + "lint-fix": "tslint --fix tests/*.ts -t verbose", + "watch": "npm-watch", + "webpack": "npm-run-all webpack:dev webpack:prod", + "webpack:dev": "npm-run-all webpack:dev:node webpack:dev:web", + "webpack:dev:node": "webpack --env=dev --target=node --config ../../webpack.config.js", + "webpack:dev:web": "webpack --env=dev --target=web --config ../../webpack.config.js", + "webpack:prod": "npm-run-all webpack:prod:node webpack:prod:web", + "webpack:prod:node": "webpack --env=prod --target=node --config ../../webpack.config.js", + "webpack:prod:web": "webpack --env=prod --target=web --config ../../webpack.config.js" + }, + "dependencies": { + "@hyperledger/cactus-common": "2.0.0-alpha.2", + "@hyperledger/cactus-core": "2.0.0-alpha.2", + "@hyperledger/cactus-core-api": "2.0.0-alpha.2", + "@polkadot/api": "10.9.1", + "@polkadot/api-contract": "10.9.1", + "@polkadot/rpc-provider": "10.9.1", + "@polkadot/types": "10.9.1", + "@polkadot/util": "12.6.2", + "bl": "5.0.0", + "eslint": "7.21.0", + "express": "4.17.1", + "express-openapi-validator": "4.13.1", + "form-data": "4.0.0", + "fs-extra": "11.2.0", + "http-errors": "2.0.0", + "http-status-codes": "2.1.4", + "joi": "14.3.1", + "multer": "1.4.2", + "ngo": "2.6.2", + "openapi-types": "9.1.0", + "prom-client": "13.2.0", + "run-time-error": "1.4.0", + "temp": "0.9.1", + "tslint": "6.1.3", + "typescript-optional": "2.0.1", + "uuid": "8.3.2" + }, + "devDependencies": { + "@hyperledger/cactus-plugin-keychain-memory": "2.0.0-alpha.2", + "@hyperledger/cactus-test-tooling": "2.0.0-alpha.2", + "@types/express": "4.17.19", + "@types/http-errors": "2.0.4", + "@types/joi": "14.3.4", + "@types/multer": "1.4.7", + "@types/ssh2": "0.5.44", + "@types/supertest": "2.0.11", + "@types/temp": "0.9.1", + "@types/uuid": "8.3.1", + "axios": "0.22.0", + "supertest": "6.1.6" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + }, + "publishConfig": { + "access": "public" + }, + "browserMinified": "dist/cactus-plugin-ledger-connector-polkadot.web.umd.min.js", + "mainMinified": "dist/cactus-plugin-ledger-connector-polkadot.node.umd.min.js", + "watch": { + "codegen:openapi": { + "patterns": [ + "./src/main/json/openapi.json" + ] + } + } +} diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/json/openapi.json b/packages/cactus-plugin-ledger-connector-polkadot/src/main/json/openapi.json new file mode 100644 index 00000000000..6a41999fa3f --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/json/openapi.json @@ -0,0 +1,868 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Hyperledger Cactus Plugin - Connector Polkadot", + "description": "Can perform basic tasks on a Polkadot parachain", + "version": "v2.0.0-alpha.2", + "license": { + "name": "Apache-2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "components": { + "schemas": { + "PrometheusExporterMetricsResponse": { + "type": "string", + "nullable": false + }, + "TransactionInfoRequest": { + "type": "object", + "required": [ + "accountAddress", + "transactionExpiration" + ], + "additionalProperties": false, + "properties": { + "accountAddress": { + "type": "string", + "nullable": false + }, + "transactionExpiration":{ + "type": "number", + "nullable": true + } + } + }, + "TransactionInfoResponseData": { + "type": "object", + "required": [ + "nonce", + "blockHash", + "era" + ], + "additionalProperties": false, + "properties": { + "nonce": { + "type": "object", + "nullable": false + }, + "blockHash": { + "type": "object", + "nullable": false + }, + "era": { + "type": "object", + "nullable": true + } + } + }, + "TransactionInfoResponse": { + "type": "object", + "required": [ + "responseContainer" + ], + "additionalProperties": false, + "properties": { + "responseContainer": { + "type": "object", + "required": [ + "response_data", + "succeeded", + "message", + "error" + ], + "additionalProperties": false, + "properties": { + "response_data": { + "$ref": "#/components/schemas/TransactionInfoResponseData", + "nullable": false + }, + "succeeded": { + "type": "boolean", + "nullable": false + }, + "message": { + "type": "string", + "nullable": false + }, + "error": { + "type": "string", + "nullable": true + } + } + } + } + }, + "RawTransactionRequest": { + "type": "object", + "required": ["to", "value"], + "additionalProperties": false, + "properties": { + "to": { + "type": "string", + "nullable": false + }, + "value": { + "type": "number", + "nullable": false + } + } + }, + "RawTransactionResponseData": { + "type": "object", + "required": [ + "rawTransaction" + ], + "additionalProperties": false, + "properties": { + "rawTransaction": { + "type": "string", + "nullable": false + } + } + }, + "RawTransactionResponse": { + "type": "object", + "required": ["responseContainer"], + "additionalProperties": false, + "properties": { + "responseContainer": { + "type": "object", + "required": [ + "response_data", + "succeeded", + "message", + "error" + ], + "additionalProperties": false, + "properties": { + "response_data": { + "$ref": "#/components/schemas/RawTransactionResponseData", + "nullable": false + }, + "succeeded": { + "type": "boolean", + "nullable": false + }, + "message": { + "type": "string", + "nullable": false + }, + "error": { + "type": "string", + "nullable": true + } + } + } + } + }, + "SignRawTransactionRequest": { + "type": "object", + "required": ["rawTransaction", "mnemonic"], + "additionalProperties": false, + "properties": { + "rawTransaction": { + "type": "string", + "nullable": false + }, + "mnemonic": { + "type": "string", + "nullable": false + }, + "signingOptions": { + "type": "object", + "nullable": false + } + } + }, + "SignRawTransactionResponse": { + "type": "object", + "required": ["success", "signedTransaction"], + "additionalProperties": false, + "properties": { + "success": { + "type": "boolean", + "nullable": false + }, + "signedTransaction": { + "type": "string", + "nullable": false + } + } + }, + "web3SigningCredential": { + "type": "object", + "required": ["type"], + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/Web3SigningCredentialCactusKeychainRef" + }, + { + "$ref": "#/components/schemas/Web3SigningCredentialMnemonicString" + }, + { + "$ref": "#/components/schemas/Web3SigningCredentialNone" + } + ], + "properties": { + "type": { + "$ref": "#/components/schemas/Web3SigningCredentialType" + } + } + }, + "Web3SigningCredentialCactusKeychainRef": { + "type": "object", + "required": ["type", "ethAccount", "keychainId", "keychainEntryKey"], + "properties": { + "type": { + "$ref": "#/components/schemas/Web3SigningCredentialType" + }, + "keychainEntryKey": { + "type": "string", + "description": "The key to use when looking up the the keychain entry holding the secret pointed to by the keychainEntryKey parameter.", + "minLength": 0, + "maxLength": 1024 + }, + "keychainId": { + "type": "string", + "description": "The keychain ID to use when looking up the the keychain plugin instance that will be used to retrieve the secret pointed to by the keychainEntryKey parameter.", + "minLength": 0, + "maxLength": 1024 + } + } + }, + "Web3SigningCredentialMnemonicString": { + "type": "object", + "required": ["type", "mnemonic"], + "properties": { + "type": { + "$ref": "#/components/schemas/Web3SigningCredentialType" + }, + "mnemonic": { + "type": "string", + "description": "The Polkadot account's seed phrase for signing transaction", + "nullable": false + } + } + }, + "Web3SigningCredentialNone": { + "type": "object", + "required": ["type"], + "description": "Using this denotes that there is no signing required because the transaction is pre-signed.", + "properties": { + "type": { + "$ref": "#/components/schemas/Web3SigningCredentialType" + } + } + }, + "Web3SigningCredentialType": { + "type": "string", + "enum": [ + "CACTUS_KEYCHAIN_REF", + "MNEMONIC_STRING", + "NONE" + ] + }, + "PolkadotTransactionConfig": { + "type": "object", + "additionalProperties": true, + "properties": { + "transferSubmittable": { + "oneOf": [ + { + "type": "string" + } + ] + }, + "to": { + "oneOf": [ + { + "type": "string" + } + ] + }, + "value": { + "oneOf": [ + { + "type": "number" + } + ] + } + } + }, + "RunTransactionRequest": { + "type": "object", + "required": [ + "web3SigningCredential", "transactionConfig" + ], + "additionalProperties": false, + "properties": { + "web3SigningCredential": { + "$ref": "#/components/schemas/web3SigningCredential", + "nullable": false + }, + "transactionConfig": { + "$ref": "#/components/schemas/PolkadotTransactionConfig", + "nullable": false + } + } + }, + "RunTransactionResponse": { + "type": "object", + "required": [ + "success" + ], + "additionalProperties": false, + "properties": { + "success": { + "type": "boolean", + "nullable": false + }, + "txHash": { + "type": "string", + "nullable": false + }, + "blockHash": { + "type": "string", + "nullable": false + } + } + }, + "DeployContractInkRequest": { + "type": "object", + "required": [ + "web3SigningCredential", + "wasm", + "metadata", + "gasLimit" + ], + "additionalProperties": false, + "properties": { + "web3SigningCredential": { + "$ref": "#/components/schemas/web3SigningCredential", + "nullable": false + }, + "wasm": { + "description": "raw wasm for the compiled contract in base64 format", + "type": "string", + "format": "byte", + "nullable": false + }, + "constructorMethod": { + "oneOf": [ + { + "type": "string" + } + ] + }, + "metadata": { + "oneOf": [ + { + "type": "string" + } + ] + }, + "gasLimit": { + "type": "object", + "required": ["refTime", "proofSize"], + "properties": { + "refTime": { + "type": "integer", + "minimum": 0 + }, + "proofSize": { + "type": "integer", + "minimum": 0 + } + } + }, + "storageDepositLimit": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "integer", + "minimum": 0 + } + ], + "nullable": true + }, + "params": { + "description": "The list of arguments to pass in to the contract method being deployed", + "type": "array", + "default": [], + "items": {} + }, + "balance": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "integer", + "minimum": 0 + } + ] + }, + "salt": { + "oneOf": [ + { + "type": "string" + } + ], + "nullable": true + } + } + }, + "DeployContractInkResponse": { + "type": "object", + "required": ["success"], + "additionalProperties": false, + "properties": { + "success": { + "type": "boolean", + "nullable": false + }, + "contractAddress": { + "type": "string", + "nullable": false + } + } + }, + "PolkadotContractInvocationType": { + "type": "string", + "enum": ["SEND", "QUERY"] + }, + "InvokeContractRequest": { + "type": "object", + "required": [ + "invocationType", + "metadata", + "contractAddress", + "methodName", + "gasLimit", + "accountAddress", + "web3SigningCredential" + ], + "additionalProperties": false, + "properties": { + "invocationType": { + "$ref": "#/components/schemas/PolkadotContractInvocationType", + "nullable": false, + "description": "Indicates whether it is a QUERY or a SEND type of invocation where only SEND ends up creating an actual transaction on the ledger." + }, + "accountAddress": { + "type": "string", + "nullable": false + }, + "web3SigningCredential": { + "$ref": "#/components/schemas/web3SigningCredential", + "nullable": false + }, + "metadata": { + "oneOf": [ + { + "type": "string" + } + ] + }, + "contractAddress": { + "type": "string", + "nullable": false + }, + "methodName": { + "description": "The name of the contract method to invoke.", + "type": "string", + "nullable": false, + "minLength": 1, + "maxLength": 2048 + }, + "gasLimit": { + "type": "object", + "required": ["refTime", "proofSize"], + "properties": { + "refTime": { + "type": "integer", + "minimum": 0 + }, + "proofSize": { + "type": "integer", + "minimum": 0 + } + } + }, + "storageDepositLimit": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "integer", + "minimum": 0 + } + ], + "nullable": true + }, + "balance": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "integer", + "minimum": 0 + } + ] + }, + "params": { + "description": "The list of arguments to pass in to the contract method being invoked", + "type": "array", + "default": [], + "items": {} + } + } + }, + "InvokeContractResponse": { + "type": "object", + "required": ["success"], + "properties": { + "callOutput": {}, + "success": { + "type": "boolean", + "nullable": false + }, + "txHash": { + "type": "string", + "nullable": false + }, + "blockHash": { + "type": "string", + "nullable": false + } + } + }, + "ErrorExceptionResponse": { + "type": "object", + "description": "Error response from the connector.", + "required": ["message", "error"], + "properties": { + "message": { + "type": "string", + "description": "Short error description message.", + "nullable": false + }, + "error": { + "type": "string", + "description": "Detailed error information.", + "nullable": false + } + } + } + } + }, + "paths": { + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/get-prometheus-exporter-metrics": { + "get": { + "x-hyperledger-cactus": { + "http": { + "verbLowerCase": "get", + "path": "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/get-prometheus-exporter-metrics" + } + }, + "operationId": "getPrometheusMetrics", + "summary": "Get the Prometheus Metrics", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/PrometheusExporterMetricsResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorExceptionResponse" + } + } + } + } + } + } + }, + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/get-transaction-info": { + "post": { + "x-hyperledger-cactus": { + "http": { + "verbLowerCase": "post", + "path": "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/get-transaction-info" + } + }, + "operationId": "getTransactionInfo", + "summary": "Get the necessary Transaction Info for a account", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TransactionInfoRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TransactionInfoResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorExceptionResponse" + } + } + } + } + } + } + }, + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/get-raw-transaction": { + "post": { + "x-hyperledger-cactus": { + "http": { + "verbLowerCase": "post", + "path": "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/get-raw-transaction" + } + }, + "operationId": "getRawTransaction", + "summary": "Get raw unsigned transaction", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RawTransactionRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RawTransactionResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorExceptionResponse" + } + } + } + } + } + } + }, + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/sign-raw-transaction": { + "post": { + "x-hyperledger-cactus": { + "http": { + "verbLowerCase": "post", + "path": "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/sign-raw-transaction" + } + }, + "operationId": "signRawTransaction", + "summary": "sign the raw transaction", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SignRawTransactionRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SignRawTransactionResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorExceptionResponse" + } + } + } + } + } + } + }, + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/run-transaction": { + "post": { + "x-hyperledger-cactus": { + "http": { + "verbLowerCase": "post", + "path": "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/run-transaction" + } + }, + "operationId": "runTransaction", + "summary": "Executes a transaction on a Polkadot ledger", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RunTransactionRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RunTransactionResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorExceptionResponse" + } + } + } + } + } + } + }, + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/deploy-contract-ink": { + "post": { + "x-hyperledger-cactus": { + "http": { + "verbLowerCase": "post", + "path": "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/deploy-contract-ink" + } + }, + "operationId": "deployContractInk", + "summary": "Deploys the ink! contract", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeployContractInkRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeployContractInkResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorExceptionResponse" + } + } + } + } + } + } + }, + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/invoke-contract": { + "post": { + "x-hyperledger-cactus": { + "http": { + "verbLowerCase": "post", + "path": "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/invoke-contract" + } + }, + "operationId": "invokeContract", + "summary": "Invokes a contract on a polkadot ledger", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvokeContractRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvokeContractResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorExceptionResponse" + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator-ignore b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator-ignore new file mode 100644 index 00000000000..6ff76cf80a2 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator-ignore @@ -0,0 +1,27 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md + +.npmignore +.gitignore +git_push.sh \ No newline at end of file diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/FILES b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/FILES new file mode 100644 index 00000000000..53250c02696 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/FILES @@ -0,0 +1,5 @@ +api.ts +base.ts +common.ts +configuration.ts +index.ts diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/VERSION b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/VERSION new file mode 100644 index 00000000000..cd802a1ec4e --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/VERSION @@ -0,0 +1 @@ +6.6.0 \ No newline at end of file diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/api.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/api.ts new file mode 100644 index 00000000000..9f2cae5f0c4 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/api.ts @@ -0,0 +1,1151 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Hyperledger Cactus Plugin - Connector Polkadot + * Can perform basic tasks on a Polkadot parachain + * + * The version of the OpenAPI document: v2.0.0-alpha.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from './configuration'; +import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from 'axios'; +import globalAxios from 'axios'; +// Some imports not used depending on template conditions +// @ts-ignore +import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from './common'; +import type { RequestArgs } from './base'; +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS, BaseAPI, RequiredError } from './base'; + +/** + * + * @export + * @interface DeployContractInkRequest + */ +export interface DeployContractInkRequest { + /** + * + * @type {Web3SigningCredential} + * @memberof DeployContractInkRequest + */ + 'web3SigningCredential': Web3SigningCredential; + /** + * raw wasm for the compiled contract in base64 format + * @type {string} + * @memberof DeployContractInkRequest + */ + 'wasm': string; + /** + * + * @type {PolkadotTransactionConfigTransferSubmittable} + * @memberof DeployContractInkRequest + */ + 'constructorMethod'?: PolkadotTransactionConfigTransferSubmittable; + /** + * + * @type {PolkadotTransactionConfigTransferSubmittable} + * @memberof DeployContractInkRequest + */ + 'metadata': PolkadotTransactionConfigTransferSubmittable; + /** + * + * @type {DeployContractInkRequestGasLimit} + * @memberof DeployContractInkRequest + */ + 'gasLimit': DeployContractInkRequestGasLimit; + /** + * + * @type {DeployContractInkRequestStorageDepositLimit} + * @memberof DeployContractInkRequest + */ + 'storageDepositLimit'?: DeployContractInkRequestStorageDepositLimit | null; + /** + * The list of arguments to pass in to the contract method being deployed + * @type {Array} + * @memberof DeployContractInkRequest + */ + 'params'?: Array; + /** + * + * @type {DeployContractInkRequestBalance} + * @memberof DeployContractInkRequest + */ + 'balance'?: DeployContractInkRequestBalance; + /** + * + * @type {DeployContractInkRequestSalt} + * @memberof DeployContractInkRequest + */ + 'salt'?: DeployContractInkRequestSalt | null; +} +/** + * @type DeployContractInkRequestBalance + * @export + */ +export type DeployContractInkRequestBalance = number | string; + +/** + * + * @export + * @interface DeployContractInkRequestGasLimit + */ +export interface DeployContractInkRequestGasLimit { + /** + * + * @type {number} + * @memberof DeployContractInkRequestGasLimit + */ + 'refTime': number; + /** + * + * @type {number} + * @memberof DeployContractInkRequestGasLimit + */ + 'proofSize': number; +} +/** + * @type DeployContractInkRequestSalt + * @export + */ +export type DeployContractInkRequestSalt = string; + +/** + * @type DeployContractInkRequestStorageDepositLimit + * @export + */ +export type DeployContractInkRequestStorageDepositLimit = number | string; + +/** + * + * @export + * @interface DeployContractInkResponse + */ +export interface DeployContractInkResponse { + /** + * + * @type {boolean} + * @memberof DeployContractInkResponse + */ + 'success': boolean; + /** + * + * @type {string} + * @memberof DeployContractInkResponse + */ + 'contractAddress'?: string; +} +/** + * Error response from the connector. + * @export + * @interface ErrorExceptionResponse + */ +export interface ErrorExceptionResponse { + /** + * Short error description message. + * @type {string} + * @memberof ErrorExceptionResponse + */ + 'message': string; + /** + * Detailed error information. + * @type {string} + * @memberof ErrorExceptionResponse + */ + 'error': string; +} +/** + * + * @export + * @interface InvokeContractRequest + */ +export interface InvokeContractRequest { + /** + * + * @type {PolkadotContractInvocationType} + * @memberof InvokeContractRequest + */ + 'invocationType': PolkadotContractInvocationType; + /** + * + * @type {string} + * @memberof InvokeContractRequest + */ + 'accountAddress': string; + /** + * + * @type {Web3SigningCredential} + * @memberof InvokeContractRequest + */ + 'web3SigningCredential': Web3SigningCredential; + /** + * + * @type {PolkadotTransactionConfigTransferSubmittable} + * @memberof InvokeContractRequest + */ + 'metadata': PolkadotTransactionConfigTransferSubmittable; + /** + * + * @type {string} + * @memberof InvokeContractRequest + */ + 'contractAddress': string; + /** + * The name of the contract method to invoke. + * @type {string} + * @memberof InvokeContractRequest + */ + 'methodName': string; + /** + * + * @type {DeployContractInkRequestGasLimit} + * @memberof InvokeContractRequest + */ + 'gasLimit': DeployContractInkRequestGasLimit; + /** + * + * @type {DeployContractInkRequestStorageDepositLimit} + * @memberof InvokeContractRequest + */ + 'storageDepositLimit'?: DeployContractInkRequestStorageDepositLimit | null; + /** + * + * @type {DeployContractInkRequestBalance} + * @memberof InvokeContractRequest + */ + 'balance'?: DeployContractInkRequestBalance; + /** + * The list of arguments to pass in to the contract method being invoked + * @type {Array} + * @memberof InvokeContractRequest + */ + 'params'?: Array; +} + + +/** + * + * @export + * @interface InvokeContractResponse + */ +export interface InvokeContractResponse { + /** + * + * @type {any} + * @memberof InvokeContractResponse + */ + 'callOutput'?: any; + /** + * + * @type {boolean} + * @memberof InvokeContractResponse + */ + 'success': boolean; + /** + * + * @type {string} + * @memberof InvokeContractResponse + */ + 'txHash'?: string; + /** + * + * @type {string} + * @memberof InvokeContractResponse + */ + 'blockHash'?: string; +} +/** + * + * @export + * @enum {string} + */ + +export const PolkadotContractInvocationType = { + Send: 'SEND', + Query: 'QUERY' +} as const; + +export type PolkadotContractInvocationType = typeof PolkadotContractInvocationType[keyof typeof PolkadotContractInvocationType]; + + +/** + * + * @export + * @interface PolkadotTransactionConfig + */ +export interface PolkadotTransactionConfig { + [key: string]: any; + + /** + * + * @type {PolkadotTransactionConfigTransferSubmittable} + * @memberof PolkadotTransactionConfig + */ + 'transferSubmittable'?: PolkadotTransactionConfigTransferSubmittable; + /** + * + * @type {PolkadotTransactionConfigTransferSubmittable} + * @memberof PolkadotTransactionConfig + */ + 'to'?: PolkadotTransactionConfigTransferSubmittable; + /** + * + * @type {PolkadotTransactionConfigValue} + * @memberof PolkadotTransactionConfig + */ + 'value'?: PolkadotTransactionConfigValue; +} +/** + * @type PolkadotTransactionConfigTransferSubmittable + * @export + */ +export type PolkadotTransactionConfigTransferSubmittable = string; + +/** + * @type PolkadotTransactionConfigValue + * @export + */ +export type PolkadotTransactionConfigValue = number; + +/** + * + * @export + * @interface RawTransactionRequest + */ +export interface RawTransactionRequest { + /** + * + * @type {string} + * @memberof RawTransactionRequest + */ + 'to': string; + /** + * + * @type {number} + * @memberof RawTransactionRequest + */ + 'value': number; +} +/** + * + * @export + * @interface RawTransactionResponse + */ +export interface RawTransactionResponse { + /** + * + * @type {RawTransactionResponseResponseContainer} + * @memberof RawTransactionResponse + */ + 'responseContainer': RawTransactionResponseResponseContainer; +} +/** + * + * @export + * @interface RawTransactionResponseData + */ +export interface RawTransactionResponseData { + /** + * + * @type {string} + * @memberof RawTransactionResponseData + */ + 'rawTransaction': string; +} +/** + * + * @export + * @interface RawTransactionResponseResponseContainer + */ +export interface RawTransactionResponseResponseContainer { + /** + * + * @type {RawTransactionResponseData} + * @memberof RawTransactionResponseResponseContainer + */ + 'response_data': RawTransactionResponseData; + /** + * + * @type {boolean} + * @memberof RawTransactionResponseResponseContainer + */ + 'succeeded': boolean; + /** + * + * @type {string} + * @memberof RawTransactionResponseResponseContainer + */ + 'message': string; + /** + * + * @type {string} + * @memberof RawTransactionResponseResponseContainer + */ + 'error': string | null; +} +/** + * + * @export + * @interface RunTransactionRequest + */ +export interface RunTransactionRequest { + /** + * + * @type {Web3SigningCredential} + * @memberof RunTransactionRequest + */ + 'web3SigningCredential': Web3SigningCredential; + /** + * + * @type {PolkadotTransactionConfig} + * @memberof RunTransactionRequest + */ + 'transactionConfig': PolkadotTransactionConfig; +} +/** + * + * @export + * @interface RunTransactionResponse + */ +export interface RunTransactionResponse { + /** + * + * @type {boolean} + * @memberof RunTransactionResponse + */ + 'success': boolean; + /** + * + * @type {string} + * @memberof RunTransactionResponse + */ + 'txHash'?: string; + /** + * + * @type {string} + * @memberof RunTransactionResponse + */ + 'blockHash'?: string; +} +/** + * + * @export + * @interface SignRawTransactionRequest + */ +export interface SignRawTransactionRequest { + /** + * + * @type {string} + * @memberof SignRawTransactionRequest + */ + 'rawTransaction': string; + /** + * + * @type {string} + * @memberof SignRawTransactionRequest + */ + 'mnemonic': string; + /** + * + * @type {object} + * @memberof SignRawTransactionRequest + */ + 'signingOptions'?: object; +} +/** + * + * @export + * @interface SignRawTransactionResponse + */ +export interface SignRawTransactionResponse { + /** + * + * @type {boolean} + * @memberof SignRawTransactionResponse + */ + 'success': boolean; + /** + * + * @type {string} + * @memberof SignRawTransactionResponse + */ + 'signedTransaction': string; +} +/** + * + * @export + * @interface TransactionInfoRequest + */ +export interface TransactionInfoRequest { + /** + * + * @type {string} + * @memberof TransactionInfoRequest + */ + 'accountAddress': string; + /** + * + * @type {number} + * @memberof TransactionInfoRequest + */ + 'transactionExpiration': number | null; +} +/** + * + * @export + * @interface TransactionInfoResponse + */ +export interface TransactionInfoResponse { + /** + * + * @type {TransactionInfoResponseResponseContainer} + * @memberof TransactionInfoResponse + */ + 'responseContainer': TransactionInfoResponseResponseContainer; +} +/** + * + * @export + * @interface TransactionInfoResponseData + */ +export interface TransactionInfoResponseData { + /** + * + * @type {object} + * @memberof TransactionInfoResponseData + */ + 'nonce': object; + /** + * + * @type {object} + * @memberof TransactionInfoResponseData + */ + 'blockHash': object; + /** + * + * @type {object} + * @memberof TransactionInfoResponseData + */ + 'era': object | null; +} +/** + * + * @export + * @interface TransactionInfoResponseResponseContainer + */ +export interface TransactionInfoResponseResponseContainer { + /** + * + * @type {TransactionInfoResponseData} + * @memberof TransactionInfoResponseResponseContainer + */ + 'response_data': TransactionInfoResponseData; + /** + * + * @type {boolean} + * @memberof TransactionInfoResponseResponseContainer + */ + 'succeeded': boolean; + /** + * + * @type {string} + * @memberof TransactionInfoResponseResponseContainer + */ + 'message': string; + /** + * + * @type {string} + * @memberof TransactionInfoResponseResponseContainer + */ + 'error': string | null; +} +/** + * @type Web3SigningCredential + * @export + */ +export type Web3SigningCredential = Web3SigningCredentialCactusKeychainRef | Web3SigningCredentialMnemonicString | Web3SigningCredentialNone; + +/** + * + * @export + * @interface Web3SigningCredentialCactusKeychainRef + */ +export interface Web3SigningCredentialCactusKeychainRef { + /** + * + * @type {Web3SigningCredentialType} + * @memberof Web3SigningCredentialCactusKeychainRef + */ + 'type': Web3SigningCredentialType; + /** + * The key to use when looking up the the keychain entry holding the secret pointed to by the keychainEntryKey parameter. + * @type {string} + * @memberof Web3SigningCredentialCactusKeychainRef + */ + 'keychainEntryKey': string; + /** + * The keychain ID to use when looking up the the keychain plugin instance that will be used to retrieve the secret pointed to by the keychainEntryKey parameter. + * @type {string} + * @memberof Web3SigningCredentialCactusKeychainRef + */ + 'keychainId': string; +} + + +/** + * + * @export + * @interface Web3SigningCredentialMnemonicString + */ +export interface Web3SigningCredentialMnemonicString { + /** + * + * @type {Web3SigningCredentialType} + * @memberof Web3SigningCredentialMnemonicString + */ + 'type': Web3SigningCredentialType; + /** + * The Polkadot account\'s seed phrase for signing transaction + * @type {string} + * @memberof Web3SigningCredentialMnemonicString + */ + 'mnemonic': string; +} + + +/** + * Using this denotes that there is no signing required because the transaction is pre-signed. + * @export + * @interface Web3SigningCredentialNone + */ +export interface Web3SigningCredentialNone { + /** + * + * @type {Web3SigningCredentialType} + * @memberof Web3SigningCredentialNone + */ + 'type': Web3SigningCredentialType; +} + + +/** + * + * @export + * @enum {string} + */ + +export const Web3SigningCredentialType = { + CactusKeychainRef: 'CACTUS_KEYCHAIN_REF', + MnemonicString: 'MNEMONIC_STRING', + None: 'NONE' +} as const; + +export type Web3SigningCredentialType = typeof Web3SigningCredentialType[keyof typeof Web3SigningCredentialType]; + + + +/** + * DefaultApi - axios parameter creator + * @export + */ +export const DefaultApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * + * @summary Deploys the ink! contract + * @param {DeployContractInkRequest} [deployContractInkRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deployContractInk: async (deployContractInkRequest?: DeployContractInkRequest, options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/deploy-contract-ink`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(deployContractInkRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary Get the Prometheus Metrics + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getPrometheusMetrics: async (options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/get-prometheus-exporter-metrics`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary Get raw unsigned transaction + * @param {RawTransactionRequest} [rawTransactionRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getRawTransaction: async (rawTransactionRequest?: RawTransactionRequest, options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/get-raw-transaction`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(rawTransactionRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary Get the necessary Transaction Info for a account + * @param {TransactionInfoRequest} [transactionInfoRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getTransactionInfo: async (transactionInfoRequest?: TransactionInfoRequest, options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/get-transaction-info`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(transactionInfoRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary Invokes a contract on a polkadot ledger + * @param {InvokeContractRequest} [invokeContractRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + invokeContract: async (invokeContractRequest?: InvokeContractRequest, options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/invoke-contract`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(invokeContractRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary Executes a transaction on a Polkadot ledger + * @param {RunTransactionRequest} [runTransactionRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + runTransaction: async (runTransactionRequest?: RunTransactionRequest, options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/run-transaction`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(runTransactionRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary sign the raw transaction + * @param {SignRawTransactionRequest} [signRawTransactionRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + signRawTransaction: async (signRawTransactionRequest?: SignRawTransactionRequest, options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/sign-raw-transaction`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(signRawTransactionRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + } +}; + +/** + * DefaultApi - functional programming interface + * @export + */ +export const DefaultApiFp = function(configuration?: Configuration) { + const localVarAxiosParamCreator = DefaultApiAxiosParamCreator(configuration) + return { + /** + * + * @summary Deploys the ink! contract + * @param {DeployContractInkRequest} [deployContractInkRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async deployContractInk(deployContractInkRequest?: DeployContractInkRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.deployContractInk(deployContractInkRequest, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * + * @summary Get the Prometheus Metrics + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getPrometheusMetrics(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getPrometheusMetrics(options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * + * @summary Get raw unsigned transaction + * @param {RawTransactionRequest} [rawTransactionRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getRawTransaction(rawTransactionRequest?: RawTransactionRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getRawTransaction(rawTransactionRequest, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * + * @summary Get the necessary Transaction Info for a account + * @param {TransactionInfoRequest} [transactionInfoRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getTransactionInfo(transactionInfoRequest?: TransactionInfoRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getTransactionInfo(transactionInfoRequest, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * + * @summary Invokes a contract on a polkadot ledger + * @param {InvokeContractRequest} [invokeContractRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async invokeContract(invokeContractRequest?: InvokeContractRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.invokeContract(invokeContractRequest, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * + * @summary Executes a transaction on a Polkadot ledger + * @param {RunTransactionRequest} [runTransactionRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async runTransaction(runTransactionRequest?: RunTransactionRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.runTransaction(runTransactionRequest, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * + * @summary sign the raw transaction + * @param {SignRawTransactionRequest} [signRawTransactionRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async signRawTransaction(signRawTransactionRequest?: SignRawTransactionRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.signRawTransaction(signRawTransactionRequest, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + } +}; + +/** + * DefaultApi - factory interface + * @export + */ +export const DefaultApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + const localVarFp = DefaultApiFp(configuration) + return { + /** + * + * @summary Deploys the ink! contract + * @param {DeployContractInkRequest} [deployContractInkRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deployContractInk(deployContractInkRequest?: DeployContractInkRequest, options?: any): AxiosPromise { + return localVarFp.deployContractInk(deployContractInkRequest, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary Get the Prometheus Metrics + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getPrometheusMetrics(options?: any): AxiosPromise { + return localVarFp.getPrometheusMetrics(options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary Get raw unsigned transaction + * @param {RawTransactionRequest} [rawTransactionRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getRawTransaction(rawTransactionRequest?: RawTransactionRequest, options?: any): AxiosPromise { + return localVarFp.getRawTransaction(rawTransactionRequest, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary Get the necessary Transaction Info for a account + * @param {TransactionInfoRequest} [transactionInfoRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getTransactionInfo(transactionInfoRequest?: TransactionInfoRequest, options?: any): AxiosPromise { + return localVarFp.getTransactionInfo(transactionInfoRequest, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary Invokes a contract on a polkadot ledger + * @param {InvokeContractRequest} [invokeContractRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + invokeContract(invokeContractRequest?: InvokeContractRequest, options?: any): AxiosPromise { + return localVarFp.invokeContract(invokeContractRequest, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary Executes a transaction on a Polkadot ledger + * @param {RunTransactionRequest} [runTransactionRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + runTransaction(runTransactionRequest?: RunTransactionRequest, options?: any): AxiosPromise { + return localVarFp.runTransaction(runTransactionRequest, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary sign the raw transaction + * @param {SignRawTransactionRequest} [signRawTransactionRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + signRawTransaction(signRawTransactionRequest?: SignRawTransactionRequest, options?: any): AxiosPromise { + return localVarFp.signRawTransaction(signRawTransactionRequest, options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * DefaultApi - object-oriented interface + * @export + * @class DefaultApi + * @extends {BaseAPI} + */ +export class DefaultApi extends BaseAPI { + /** + * + * @summary Deploys the ink! contract + * @param {DeployContractInkRequest} [deployContractInkRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public deployContractInk(deployContractInkRequest?: DeployContractInkRequest, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).deployContractInk(deployContractInkRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary Get the Prometheus Metrics + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public getPrometheusMetrics(options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).getPrometheusMetrics(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary Get raw unsigned transaction + * @param {RawTransactionRequest} [rawTransactionRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public getRawTransaction(rawTransactionRequest?: RawTransactionRequest, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).getRawTransaction(rawTransactionRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary Get the necessary Transaction Info for a account + * @param {TransactionInfoRequest} [transactionInfoRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public getTransactionInfo(transactionInfoRequest?: TransactionInfoRequest, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).getTransactionInfo(transactionInfoRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary Invokes a contract on a polkadot ledger + * @param {InvokeContractRequest} [invokeContractRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public invokeContract(invokeContractRequest?: InvokeContractRequest, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).invokeContract(invokeContractRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary Executes a transaction on a Polkadot ledger + * @param {RunTransactionRequest} [runTransactionRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public runTransaction(runTransactionRequest?: RunTransactionRequest, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).runTransaction(runTransactionRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary sign the raw transaction + * @param {SignRawTransactionRequest} [signRawTransactionRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public signRawTransaction(signRawTransactionRequest?: SignRawTransactionRequest, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).signRawTransaction(signRawTransactionRequest, options).then((request) => request(this.axios, this.basePath)); + } +} + + diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/base.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/base.ts new file mode 100644 index 00000000000..15c10077e03 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/base.ts @@ -0,0 +1,72 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Hyperledger Cactus Plugin - Connector Polkadot + * Can perform basic tasks on a Polkadot parachain + * + * The version of the OpenAPI document: v2.0.0-alpha.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from './configuration'; +// Some imports not used depending on template conditions +// @ts-ignore +import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from 'axios'; +import globalAxios from 'axios'; + +export const BASE_PATH = "http://localhost".replace(/\/+$/, ""); + +/** + * + * @export + */ +export const COLLECTION_FORMATS = { + csv: ",", + ssv: " ", + tsv: "\t", + pipes: "|", +}; + +/** + * + * @export + * @interface RequestArgs + */ +export interface RequestArgs { + url: string; + options: AxiosRequestConfig; +} + +/** + * + * @export + * @class BaseAPI + */ +export class BaseAPI { + protected configuration: Configuration | undefined; + + constructor(configuration?: Configuration, protected basePath: string = BASE_PATH, protected axios: AxiosInstance = globalAxios) { + if (configuration) { + this.configuration = configuration; + this.basePath = configuration.basePath || this.basePath; + } + } +}; + +/** + * + * @export + * @class RequiredError + * @extends {Error} + */ +export class RequiredError extends Error { + constructor(public field: string, msg?: string) { + super(msg); + this.name = "RequiredError" + } +} diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/common.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/common.ts new file mode 100644 index 00000000000..c9590939d0e --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/common.ts @@ -0,0 +1,150 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Hyperledger Cactus Plugin - Connector Polkadot + * Can perform basic tasks on a Polkadot parachain + * + * The version of the OpenAPI document: v2.0.0-alpha.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from "./configuration"; +import type { RequestArgs } from "./base"; +import type { AxiosInstance, AxiosResponse } from 'axios'; +import { RequiredError } from "./base"; + +/** + * + * @export + */ +export const DUMMY_BASE_URL = 'https://example.com' + +/** + * + * @throws {RequiredError} + * @export + */ +export const assertParamExists = function (functionName: string, paramName: string, paramValue: unknown) { + if (paramValue === null || paramValue === undefined) { + throw new RequiredError(paramName, `Required parameter ${paramName} was null or undefined when calling ${functionName}.`); + } +} + +/** + * + * @export + */ +export const setApiKeyToObject = async function (object: any, keyParamName: string, configuration?: Configuration) { + if (configuration && configuration.apiKey) { + const localVarApiKeyValue = typeof configuration.apiKey === 'function' + ? await configuration.apiKey(keyParamName) + : await configuration.apiKey; + object[keyParamName] = localVarApiKeyValue; + } +} + +/** + * + * @export + */ +export const setBasicAuthToObject = function (object: any, configuration?: Configuration) { + if (configuration && (configuration.username || configuration.password)) { + object["auth"] = { username: configuration.username, password: configuration.password }; + } +} + +/** + * + * @export + */ +export const setBearerAuthToObject = async function (object: any, configuration?: Configuration) { + if (configuration && configuration.accessToken) { + const accessToken = typeof configuration.accessToken === 'function' + ? await configuration.accessToken() + : await configuration.accessToken; + object["Authorization"] = "Bearer " + accessToken; + } +} + +/** + * + * @export + */ +export const setOAuthToObject = async function (object: any, name: string, scopes: string[], configuration?: Configuration) { + if (configuration && configuration.accessToken) { + const localVarAccessTokenValue = typeof configuration.accessToken === 'function' + ? await configuration.accessToken(name, scopes) + : await configuration.accessToken; + object["Authorization"] = "Bearer " + localVarAccessTokenValue; + } +} + +function setFlattenedQueryParams(urlSearchParams: URLSearchParams, parameter: any, key: string = ""): void { + if (parameter == null) return; + if (typeof parameter === "object") { + if (Array.isArray(parameter)) { + (parameter as any[]).forEach(item => setFlattenedQueryParams(urlSearchParams, item, key)); + } + else { + Object.keys(parameter).forEach(currentKey => + setFlattenedQueryParams(urlSearchParams, parameter[currentKey], `${key}${key !== '' ? '.' : ''}${currentKey}`) + ); + } + } + else { + if (urlSearchParams.has(key)) { + urlSearchParams.append(key, parameter); + } + else { + urlSearchParams.set(key, parameter); + } + } +} + +/** + * + * @export + */ +export const setSearchParams = function (url: URL, ...objects: any[]) { + const searchParams = new URLSearchParams(url.search); + setFlattenedQueryParams(searchParams, objects); + url.search = searchParams.toString(); +} + +/** + * + * @export + */ +export const serializeDataIfNeeded = function (value: any, requestOptions: any, configuration?: Configuration) { + const nonString = typeof value !== 'string'; + const needsSerialization = nonString && configuration && configuration.isJsonMime + ? configuration.isJsonMime(requestOptions.headers['Content-Type']) + : nonString; + return needsSerialization + ? JSON.stringify(value !== undefined ? value : {}) + : (value || ""); +} + +/** + * + * @export + */ +export const toPathString = function (url: URL) { + return url.pathname + url.search + url.hash +} + +/** + * + * @export + */ +export const createRequestFunction = function (axiosArgs: RequestArgs, globalAxios: AxiosInstance, BASE_PATH: string, configuration?: Configuration) { + return >(axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => { + const axiosRequestArgs = {...axiosArgs.options, url: (configuration?.basePath || basePath) + axiosArgs.url}; + return axios.request(axiosRequestArgs); + }; +} diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/configuration.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/configuration.ts new file mode 100644 index 00000000000..b8927c55bd0 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/configuration.ts @@ -0,0 +1,101 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Hyperledger Cactus Plugin - Connector Polkadot + * Can perform basic tasks on a Polkadot parachain + * + * The version of the OpenAPI document: v2.0.0-alpha.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export interface ConfigurationParameters { + apiKey?: string | Promise | ((name: string) => string) | ((name: string) => Promise); + username?: string; + password?: string; + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise); + basePath?: string; + baseOptions?: any; + formDataCtor?: new () => any; +} + +export class Configuration { + /** + * parameter for apiKey security + * @param name security name + * @memberof Configuration + */ + apiKey?: string | Promise | ((name: string) => string) | ((name: string) => Promise); + /** + * parameter for basic security + * + * @type {string} + * @memberof Configuration + */ + username?: string; + /** + * parameter for basic security + * + * @type {string} + * @memberof Configuration + */ + password?: string; + /** + * parameter for oauth2 security + * @param name security name + * @param scopes oauth2 scope + * @memberof Configuration + */ + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise); + /** + * override base path + * + * @type {string} + * @memberof Configuration + */ + basePath?: string; + /** + * base options for axios calls + * + * @type {any} + * @memberof Configuration + */ + baseOptions?: any; + /** + * The FormData constructor that will be used to create multipart form data + * requests. You can inject this here so that execution environments that + * do not support the FormData class can still run the generated client. + * + * @type {new () => FormData} + */ + formDataCtor?: new () => any; + + constructor(param: ConfigurationParameters = {}) { + this.apiKey = param.apiKey; + this.username = param.username; + this.password = param.password; + this.accessToken = param.accessToken; + this.basePath = param.basePath; + this.baseOptions = param.baseOptions; + this.formDataCtor = param.formDataCtor; + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + public isJsonMime(mime: string): boolean { + const jsonMime: RegExp = new RegExp('^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i'); + return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json'); + } +} diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/index.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/index.ts new file mode 100644 index 00000000000..5e9005e050e --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/generated/openapi/typescript-axios/index.ts @@ -0,0 +1,18 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Hyperledger Cactus Plugin - Connector Polkadot + * Can perform basic tasks on a Polkadot parachain + * + * The version of the OpenAPI document: v2.0.0-alpha.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export * from "./api"; +export * from "./configuration"; + diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/index.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/index.ts new file mode 100644 index 00000000000..87cb558397c --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/index.ts @@ -0,0 +1 @@ +export * from "./public-api"; diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/index.web.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/index.web.ts new file mode 100644 index 00000000000..cb0ff5c3b54 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/index.web.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/model-type-guards.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/model-type-guards.ts new file mode 100644 index 00000000000..e5bfc5822f8 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/model-type-guards.ts @@ -0,0 +1,24 @@ +import { + Web3SigningCredentialNone, + Web3SigningCredentialMnemonicString, + Web3SigningCredentialType, + Web3SigningCredentialCactusKeychainRef, +} from "./generated/openapi/typescript-axios/api"; + +export function isWeb3SigningCredentialMnemonicString(x?: { + type?: Web3SigningCredentialType; +}): x is Web3SigningCredentialMnemonicString { + return x?.type === Web3SigningCredentialType.MnemonicString; +} + +export function isWeb3SigningCredentialCactusRef(x?: { + type?: Web3SigningCredentialType; +}): x is Web3SigningCredentialCactusKeychainRef { + return x?.type === Web3SigningCredentialType.CactusKeychainRef; +} + +export function isWeb3SigningCredentialNone(x?: { + type?: Web3SigningCredentialType; +}): x is Web3SigningCredentialNone { + return x?.type === Web3SigningCredentialType.None; +} diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/plugin-factory-ledger-connector-polkadot.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/plugin-factory-ledger-connector-polkadot.ts new file mode 100644 index 00000000000..ea2db2248d6 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/plugin-factory-ledger-connector-polkadot.ts @@ -0,0 +1,21 @@ +import { + IPluginFactoryOptions, + PluginFactory, +} from "@hyperledger/cactus-core-api"; + +import { + IPluginLedgerConnectorPolkadotOptions, + PluginLedgerConnectorPolkadot, +} from "./plugin-ledger-connector-polkadot"; + +export class PluginFactoryLedgerConnector extends PluginFactory< + PluginLedgerConnectorPolkadot, + IPluginLedgerConnectorPolkadotOptions, + IPluginFactoryOptions +> { + async create( + pluginOptions: IPluginLedgerConnectorPolkadotOptions, + ): Promise { + return new PluginLedgerConnectorPolkadot(pluginOptions); + } +} diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/plugin-ledger-connector-polkadot.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/plugin-ledger-connector-polkadot.ts new file mode 100644 index 00000000000..cdab9b5236b --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/plugin-ledger-connector-polkadot.ts @@ -0,0 +1,925 @@ +import { Server } from "http"; +import { Server as SecureServer } from "https"; +import { Express } from "express"; +import { ApiPromise, Keyring } from "@polkadot/api"; +import { WsProvider } from "@polkadot/rpc-provider/ws"; +import { WeightV2 } from "@polkadot/types/interfaces"; +import { CodePromise, Abi, ContractPromise } from "@polkadot/api-contract"; +import { isHex, stringCamelCase } from "@polkadot/util"; +import { PrometheusExporter } from "./prometheus-exporter/prometheus-exporter"; +import { + GetPrometheusMetricsEndpoint, + IGetPrometheusMetricsEndpointOptions, +} from "./web-services/get-prometheus-exporter-metrics-endpoint"; + +import "multer"; +import { Optional } from "typescript-optional"; + +import OAS from "../json/openapi.json"; + +import { + consensusHasTransactionFinality, + PluginRegistry, +} from "@hyperledger/cactus-core"; + +import { + IPluginLedgerConnector, + ConsensusAlgorithmFamily, + IPluginWebService, + IWebServiceEndpoint, + ICactusPlugin, + ICactusPluginOptions, +} from "@hyperledger/cactus-core-api"; + +import { + Logger, + Checks, + LogLevelDesc, + LoggerProvider, +} from "@hyperledger/cactus-common"; +import { promisify } from "util"; +import { + DeployContractInkRequest, + DeployContractInkResponse, + InvokeContractRequest, + InvokeContractResponse, + PolkadotContractInvocationType, + RawTransactionRequest, + RawTransactionResponse, + RunTransactionRequest, + RunTransactionResponse, + SignRawTransactionRequest, + SignRawTransactionResponse, + TransactionInfoRequest, + TransactionInfoResponse, + Web3SigningCredential, + Web3SigningCredentialCactusKeychainRef, + Web3SigningCredentialMnemonicString, + Web3SigningCredentialType, +} from "./generated/openapi/typescript-axios/index"; +import { + GetTransactionInfoEndpoint, + IGetTransactionInfoEndpointOptions, +} from "./web-services/get-transaction-info-endpoint"; + +import { + RunTransactionEndpoint, + IRunTransactionEndpointOptions, +} from "./web-services/run-transaction-endpoint"; +import { + GetRawTransactionEndpoint, + IGetRawTransactionEndpointOptions, +} from "./web-services/get-raw-transaction-endpoint"; +import { + ISignRawTransactionEndpointOptions, + SignRawTransactionEndpoint, +} from "./web-services/sign-raw-transaction-endpoint"; +import { + DeployContractInkEndpoint, + IDeployContractInkEndpointOptions, +} from "./web-services/deploy-contract-ink-endpoint"; +import { + isWeb3SigningCredentialCactusRef, + isWeb3SigningCredentialMnemonicString, + isWeb3SigningCredentialNone, +} from "./model-type-guards"; +import { + IInvokeContractEndpointOptions, + InvokeContractEndpoint, +} from "./web-services/invoke-contract-endpoint"; +import createHttpError from "http-errors"; + +export interface IPluginLedgerConnectorPolkadotOptions + extends ICactusPluginOptions { + logLevel?: LogLevelDesc; + pluginRegistry?: PluginRegistry; + prometheusExporter?: PrometheusExporter; + wsProviderUrl: string; + instanceId: string; + autoConnect?: boolean; +} + +export class PluginLedgerConnectorPolkadot + implements + IPluginLedgerConnector< + DeployContractInkRequest, + DeployContractInkResponse, + RunTransactionRequest, + RunTransactionResponse + >, + ICactusPlugin, + IPluginWebService +{ + public static readonly CLASS_NAME = "PluginLedgerConnectorPolkadot"; + private readonly instanceId: string; + private readonly log: Logger; + private readonly pluginRegistry: PluginRegistry; + public wsProvider: WsProvider | undefined; + public api: ApiPromise | undefined; + public prometheusExporter: PrometheusExporter; + private endpoints: IWebServiceEndpoint[] | undefined; + private autoConnect: false | number | undefined; + + public getOpenApiSpec(): unknown { + return OAS; + } + + public get className(): string { + return PluginLedgerConnectorPolkadot.CLASS_NAME; + } + + constructor(public readonly opts: IPluginLedgerConnectorPolkadotOptions) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(opts, `${fnTag} arg options`); + if (typeof opts.logLevel !== "undefined") { + Checks.truthy(opts.logLevel, `${fnTag} options.logLevelDesc`); + } + Checks.truthy(opts.wsProviderUrl, `${fnTag} options.wsProviderUrl`); + Checks.truthy(opts.instanceId, `${fnTag} options.instanceId`); + this.pluginRegistry = opts.pluginRegistry || new PluginRegistry({}); + this.prometheusExporter = + opts.prometheusExporter || + new PrometheusExporter({ pollingIntervalInMin: 1 }); + Checks.truthy( + this.prometheusExporter, + `${fnTag} options.prometheusExporter`, + ); + + const level = this.opts.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + + this.instanceId = opts.instanceId; + if (opts.autoConnect) { + this.autoConnect = 1; + } + this.setProvider(opts.wsProviderUrl); + this.prometheusExporter.startMetricsCollection(); + } + + public setProvider(wsProviderUrl: string): void { + try { + this.wsProvider = new WsProvider(wsProviderUrl, this.autoConnect); + } catch (err) { + const errorMessage = `Could not create wsProvider. InnerException: + ${err}`; + throw createHttpError[500](errorMessage); + } + } + + public async getOrCreateWebServices(): Promise { + if (Array.isArray(this.endpoints)) { + return this.endpoints; + } + + const { log } = this; + log.info(`Installing web services for plugin ${this.getPackageName()}...`); + + const endpoints: IWebServiceEndpoint[] = []; + { + const opts: IGetPrometheusMetricsEndpointOptions = { + connector: this, + logLevel: this.opts.logLevel, + }; + + const endpoint = new GetPrometheusMetricsEndpoint(opts); + endpoints.push(endpoint); + } + { + const opts: IGetTransactionInfoEndpointOptions = { + connector: this, + logLevel: this.opts.logLevel, + }; + + const endpoint = new GetTransactionInfoEndpoint(opts); + endpoints.push(endpoint); + } + { + const opts: IRunTransactionEndpointOptions = { + connector: this, + logLevel: this.opts.logLevel, + }; + + const endpoint = new RunTransactionEndpoint(opts); + endpoints.push(endpoint); + } + { + const opts: IGetRawTransactionEndpointOptions = { + connector: this, + logLevel: this.opts.logLevel, + }; + + const endpoint = new GetRawTransactionEndpoint(opts); + endpoints.push(endpoint); + } + { + const opts: ISignRawTransactionEndpointOptions = { + connector: this, + logLevel: this.opts.logLevel, + }; + + const endpoint = new SignRawTransactionEndpoint(opts); + endpoints.push(endpoint); + } + { + const opts: IDeployContractInkEndpointOptions = { + connector: this, + logLevel: this.opts.logLevel, + }; + + const endpoint = new DeployContractInkEndpoint(opts); + endpoints.push(endpoint); + } + { + const opts: IInvokeContractEndpointOptions = { + connector: this, + logLevel: this.opts.logLevel, + }; + + const endpoint = new InvokeContractEndpoint(opts); + endpoints.push(endpoint); + } + + this.endpoints = endpoints; + + const pkg = this.getPackageName(); + log.info(`Installed web services for plugin ${pkg} OK`, { endpoints }); + return endpoints; + } + + async registerWebServices(app: Express): Promise { + const webServices = await this.getOrCreateWebServices(); + await Promise.all(webServices.map((ws) => ws.registerExpress(app))); + return webServices; + } + + public async shutdown(): Promise { + const serverMaybe = this.getHttpServer(); + if (serverMaybe.isPresent()) { + const server = serverMaybe.get(); + await promisify(server.close.bind(server))(); + } + } + + public getInstanceId(): string { + return this.instanceId; + } + + public getPackageName(): string { + return `@hyperledger/cactus-plugin-ledger-connector-polkadot`; + } + + public getHttpServer(): Optional { + return Optional.empty(); + } + + public async onPluginInit(): Promise { + try { + this.api = await ApiPromise.create({ provider: this.wsProvider }); + } catch (err) { + const errorMessage = `Could not create API. InnerException: + ${err}`; + throw createHttpError[500](errorMessage); + } + } + + public async getConsensusAlgorithmFamily(): Promise { + return ConsensusAlgorithmFamily.Stake; + } + + public async hasTransactionFinality(): Promise { + const currentConsensusAlgorithmFamily = + await this.getConsensusAlgorithmFamily(); + + return consensusHasTransactionFinality(currentConsensusAlgorithmFamily); + } + + public rawTransaction(req: RawTransactionRequest): RawTransactionResponse { + const fnTag = `${this.className}#rawTx()`; + Checks.truthy(req, `${fnTag} req`); + if (!this.api) { + throw createHttpError[400]( + `The operation has failed because the API is not connected to Substrate Node`, + ); + } + try { + const accountAddress = req.to; + const transferValue = req.value; + const rawTransaction = this.api.tx.balances.transferAllowDeath( + accountAddress, + transferValue, + ); + const responseContainer = { + response_data: { + rawTransaction: rawTransaction.toHex(), + }, + succeeded: true, + message: "obtainRawTransaction", + error: null, + }; + + const response: RawTransactionResponse = { + responseContainer: responseContainer, + }; + return response; + } catch (err) { + const errorMessage = + `${fnTag} Obtaining raw transaction has failed. ` + + `InnerException: ${err}`; + throw createHttpError[500](errorMessage); + } + } + + public async signTransaction( + req: SignRawTransactionRequest, + ): Promise { + const fnTag = `${this.className}#signTx()`; + Checks.truthy(req, `${fnTag} req`); + if (!this.api) { + throw createHttpError[400]( + `The operation has failed because the API is not connected to Substrate Node`, + ); + } + try { + const keyring = new Keyring({ type: "sr25519" }); + const accountPair = keyring.createFromUri(req.mnemonic); + const deserializedRawTransaction = this.api.tx(req.rawTransaction); + const signedTransaction = await deserializedRawTransaction.signAsync( + accountPair, + req.signingOptions, + ); + const serializedSignedTransaction = signedTransaction.toHex(); + const response: SignRawTransactionResponse = { + success: true, + signedTransaction: serializedSignedTransaction, + }; + return response; + } catch (err) { + const errorMessage = + `${fnTag} signing raw transaction has failed. ` + + `InnerException: ${err}`; + throw createHttpError[500](errorMessage); + } + } + + // Perform a monetary transaction to Polkadot; + public async transact( + req: RunTransactionRequest, + ): Promise { + const fnTag = `${this.className}#transact()`; + switch (req.web3SigningCredential.type) { + case Web3SigningCredentialType.CactusKeychainRef: { + return this.transactCactusKeychainRef(req); + } + case Web3SigningCredentialType.MnemonicString: { + return this.transactMnemonicString(req); + } + case Web3SigningCredentialType.None: { + if (req.transactionConfig.transferSubmittable) { + return this.transactSigned(req); + } else { + const errorMessage = + `${fnTag} Expected pre-signed raw transaction ` + + ` since signing credential is specified as` + + `Web3SigningCredentialType.NONE`; + throw createHttpError[400](errorMessage); + } + } + default: { + const errorMessage = + `${fnTag} Unrecognized Web3SigningCredentialType: ` + + `Supported ones are: ` + + `${Object.values(Web3SigningCredentialType).join(";")}`; + throw createHttpError[400](errorMessage); + } + } + } + public async transactCactusKeychainRef( + req: RunTransactionRequest, + ): Promise { + const fnTag = `${this.className}#transactCactusKeychainRef()`; + const { transactionConfig, web3SigningCredential } = req; + const { keychainEntryKey, keychainId } = + web3SigningCredential as Web3SigningCredentialCactusKeychainRef; + + // locate the keychain plugin that has access to the keychain backend + // denoted by the keychainID from the request. + const keychainPlugin = this.pluginRegistry.findOneByKeychainId(keychainId); + + Checks.truthy(keychainPlugin, `${fnTag} keychain for ID:"${keychainId}"`); + + // Now use the found keychain plugin to actually perform the lookup of + // the private key that we need to run the transaction. + const mnemonic = await keychainPlugin?.get(keychainEntryKey); + return this.transactMnemonicString({ + web3SigningCredential: { + type: Web3SigningCredentialType.MnemonicString, + mnemonic, + }, + transactionConfig, + }); + } + public async transactMnemonicString( + req: RunTransactionRequest, + ): Promise { + const fnTag = `${this.className}#transactMnemonicString()`; + Checks.truthy(req, `${fnTag} req`); + if (!this.api) { + throw createHttpError[400]( + `The operation has failed because the API is not connected to Substrate Node`, + ); + } + const { transactionConfig, web3SigningCredential } = req; + const { mnemonic } = + web3SigningCredential as Web3SigningCredentialMnemonicString; + if (!mnemonic) { + throw createHttpError[400]( + `cannot perform transaction without mnemonic string`, + ); + } + let success = false; + const keyring = new Keyring({ type: "sr25519" }); + const accountPair = keyring.createFromUri(mnemonic); + const accountAddress = transactionConfig.to; + const transferValue = transactionConfig.value; + const txResult = await new Promise<{ + success: boolean; + transactionHash: string; + blockhash: string; + }>((resolve, reject) => { + if (!this.api) { + reject("transaction not successful"); + throw createHttpError[400]( + `The operation has failed because the API is not connected to Substrate Node`, + ); + } + this.api.tx.balances + .transferAllowDeath(accountAddress, transferValue) + .signAndSend(accountPair, ({ status, txHash, dispatchError }) => { + if (!this.api) { + throw createHttpError[400]( + `The operation has failed because the API is not connected to Substrate Node`, + ); + } + if (status.isInBlock) { + if (dispatchError) { + reject("transaction not successful"); + if (dispatchError.isModule) { + const decoded = this.api.registry.findMetaError( + dispatchError.asModule, + ); + const { docs, name, section } = decoded; + throw createHttpError[400]( + `${section}.${name}: ${docs.join(" ")}`, + ); + } else { + throw createHttpError[400](dispatchError.toString()); + } + } + this.prometheusExporter.addCurrentTransaction(); + resolve({ + success: true, + blockhash: status.asInBlock.toHex(), + transactionHash: txHash.toHex(), + }); + } + }); + }); + success = txResult.success; + const transactionHash = txResult.transactionHash; + const blockHash = txResult.blockhash; + return { + success, + txHash: transactionHash, + blockHash: blockHash, + }; + } + public async transactSigned( + req: RunTransactionRequest, + ): Promise { + const fnTag = `${this.className}#transactSigned()`; + Checks.truthy( + req.transactionConfig.transferSubmittable, + `${fnTag}:req.transactionConfig.transferSubmittable`, + ); + const signedTx = req.transactionConfig.transferSubmittable as string; + + this.log.debug( + "Starting api.rpc.author.submitAndWatchExtrinsic(transferSubmittable) ", + ); + let success = false; + Checks.truthy(req, `${fnTag} req`); + if (!this.api) { + throw createHttpError[400]( + `The operation has failed because the API is not connected to Substrate Node`, + ); + } + const deserializedTransaction = this.api.tx(signedTx); + const signature = deserializedTransaction.signature.toHex(); + if (!signature) { + throw createHttpError[400](`${fnTag} Transaction is not signed.`); + } + + if (!isHex(signature)) { + throw createHttpError[400]( + `${fnTag} Transaction signature is not valid.`, + ); + } + + const txResult = await new Promise<{ + success: boolean; + transactionHash: string; + blockhash: string; + }>((resolve, reject) => { + if (!this.api) { + reject("transaction not successful"); + throw createHttpError[400]( + `The operation has failed because the API is not connected to Substrate Node`, + ); + } + this.api.rpc.author.submitAndWatchExtrinsic( + deserializedTransaction, + ({ isInBlock, hash, asInBlock, type }) => { + if (isInBlock) { + this.prometheusExporter.addCurrentTransaction(); + resolve({ + success: true, + blockhash: asInBlock.toHex(), + transactionHash: hash.toHex(), + }); + } else { + reject("transaction not successful"); + const errorMessage = `transaction not submitted with status: ${type}`; + throw new createHttpError[400](errorMessage); + } + }, + ); + }); + + success = txResult.success; + const txHash = txResult.transactionHash; + const blockHash = txResult.blockhash; + return { success, txHash, blockHash }; + } + + private async getMnemonicStringFromWeb3SigningCredential( + fnTag: string, + type: "deploy" | "invoke", + web3SigningCredential: Web3SigningCredential, + ): Promise { + if (isWeb3SigningCredentialNone(web3SigningCredential)) { + throw createHttpError[400]( + `${fnTag} Cannot ${type} contract with pre-signed TX`, + ); + } + let mnemonic: string; + if (isWeb3SigningCredentialMnemonicString(web3SigningCredential)) { + const Credential = + web3SigningCredential as Web3SigningCredentialMnemonicString; + mnemonic = Credential.mnemonic; + if (!mnemonic) { + const errorMessage = `${fnTag} Cannot ${type} contract without mnemonic string.`; + throw createHttpError[400](errorMessage); + } + return mnemonic; + } else if (isWeb3SigningCredentialCactusRef(web3SigningCredential)) { + const Credential = + web3SigningCredential as Web3SigningCredentialCactusKeychainRef; + const { keychainEntryKey, keychainId } = Credential; + if (!keychainId || !keychainEntryKey) { + const errorMessage = `${fnTag} Cannot ${type} contract without keychainId and the keychainEntryKey.`; + throw createHttpError[400](errorMessage); + } + // locate the keychain plugin that has access to the keychain backend + // denoted by the keychainID from the request. + const keychainPlugin = + this.pluginRegistry.findOneByKeychainId(keychainId); + if (!keychainPlugin) { + const errorMessage = + `${fnTag} The plugin registry does not contain` + + ` a keychain plugin for ID:"${keychainId}"`; + throw createHttpError[400](errorMessage); + } + // Now use the found keychain plugin to actually perform the lookup of + // the private key that we need to run the transaction. + mnemonic = await keychainPlugin.get(keychainEntryKey); + if (!mnemonic) { + const errorMessage = + `${fnTag} Cannot ${type} contract because` + + `the mnemonic string does not exist on the keychain`; + throw new createHttpError[400](errorMessage); + } + return mnemonic; + } else { + const errorMessage = + `${fnTag} Unrecognized Web3SigningCredentialType: ` + + `Supported ones are: ` + + `${Object.values(Web3SigningCredentialType).join(";")}`; + throw createHttpError[400](errorMessage); + } + } + + // Deploy and instantiate a smart contract in Polkadot + public async deployContract( + req: DeployContractInkRequest, + ): Promise { + const fnTag = `${this.className}#deployContract()`; + Checks.truthy(req, `${fnTag} req`); + if (!this.api) { + throw createHttpError[400]( + `The operation has failed because the API is not connected to Substrate Node`, + ); + } + const mnemonic = await this.getMnemonicStringFromWeb3SigningCredential( + fnTag, + "deploy", + req.web3SigningCredential, + ); + let success = false; + const contractAbi = new Abi( + req.metadata, + this.api.registry.getChainProperties(), + ); + const contractCode = new CodePromise( + this.api, + contractAbi, + Buffer.from(req.wasm, "base64"), + ); + const gasLimit: WeightV2 = this.api.registry.createType("WeightV2", { + refTime: req.gasLimit.refTime, + proofSize: req.gasLimit.proofSize, + }); + const keyring = new Keyring({ type: "sr25519" }); + const accountPair = keyring.createFromUri(mnemonic); + const params = req.params ?? []; + const constructorMethod = req.constructorMethod ?? "new"; + const tx = contractCode.tx[stringCamelCase(constructorMethod)]( + { + gasLimit, + storageDepositLimit: req.storageDepositLimit, + salt: req.salt, + value: req.balance, + }, + ...params, + ); + const txResult = await new Promise<{ + success: boolean; + address: string | undefined; + }>((resolve, reject) => { + tx.signAndSend( + accountPair, + //https://github.com/polkadot-js/api/issues/5722 + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + ({ contract, status, dispatchError }) => { + if (!this.api) { + throw createHttpError[400]( + `The operation has failed because the API is not connected to Substrate Node`, + ); + } + if (status.isInBlock || status.isFinalized) { + if (dispatchError) { + reject("deployment not successful"); + if (dispatchError.isModule) { + const decoded = this.api.registry.findMetaError( + dispatchError.asModule, + ); + const { docs, name, section } = decoded; + throw createHttpError[400]( + `${section}.${name}: ${docs.join(" ")}`, + ); + } else { + throw createHttpError[400](dispatchError.toString()); + } + } + this.prometheusExporter.addCurrentTransaction(); + resolve({ + success: true, + address: contract.address.toString(), + }); + } + }, + ); + }); + success = txResult.success; + const contractAddress = txResult.address; + return { + success: success, + contractAddress: contractAddress, + }; + } + + public async isSafeToCallContractMethod( + abi: Abi, + name: string, + ): Promise { + Checks.truthy(abi, `${this.className}#isSafeToCallContractMethod():abi`); + Checks.truthy( + abi.messages, + `${this.className}#isSafeToCallContractMethod():abi.messages`, + ); + Checks.nonBlankString( + name, + `${this.className}#isSafeToCallContractMethod():name`, + ); + const methods = abi.messages.map((m) => m.method); + return methods.includes(name); + } + + // invoke the smart contract + public async invokeContract( + req: InvokeContractRequest, + ): Promise { + const fnTag = `${this.className}#invokeContract()`; + Checks.truthy(req, `${fnTag} req`); + if (!this.api) { + throw createHttpError[400]( + `The operation has failed because the API is not connected to Substrate Node`, + ); + } + const contractAbi = new Abi( + req.metadata, + this.api.registry.getChainProperties(), + ); + const methodName = stringCamelCase(req.methodName); + const isSafeToCall = await this.isSafeToCallContractMethod( + contractAbi, + methodName, + ); + if (!isSafeToCall) { + throw createHttpError[400]( + `Invalid method name provided in request. ${req.methodName} does not exist on the contract abi.messages object's "method" property.`, + ); + } + const contract = new ContractPromise( + this.api, + req.metadata, + req.contractAddress, + ); + const gasLimit: WeightV2 = this.api.registry.createType("WeightV2", { + refTime: req.gasLimit.refTime, + proofSize: req.gasLimit.proofSize, + }); + if (req.invocationType === PolkadotContractInvocationType.Query) { + let success = false; + const params = req.params ?? []; + const query = contract.query[methodName]( + req.accountAddress, + { + gasLimit, + storageDepositLimit: req.storageDepositLimit, + value: req.balance, + }, + ...params, + ); + const callOutput = await query; + success = true; + return { success, callOutput }; + } else if (req.invocationType === PolkadotContractInvocationType.Send) { + const mnemonic = await this.getMnemonicStringFromWeb3SigningCredential( + fnTag, + "invoke", + req.web3SigningCredential, + ); + const keyring = new Keyring({ type: "sr25519" }); + const accountPair = keyring.createFromUri(mnemonic); + let success = false; + const params = req.params ?? []; + const tx = contract.tx[methodName]( + { + gasLimit, + storageDepositLimit: req.storageDepositLimit, + value: req.balance, + }, + ...params, + ); + const txResult = await new Promise<{ + success: boolean; + transactionHash: string; + blockHash: string; + }>((resolve, reject) => { + tx.signAndSend(accountPair, ({ status, txHash, dispatchError }) => { + if (!this.api) { + throw createHttpError[400]( + `The operation has failed because the API is not connected to Substrate Node`, + ); + } + if (status.isInBlock || status.isFinalized) { + if (dispatchError) { + reject("TX not successful"); + if (dispatchError.isModule) { + const decoded = this.api.registry.findMetaError( + dispatchError.asModule, + ); + const { docs, name, section } = decoded; + throw createHttpError[400]( + `${section}.${name}: ${docs.join(" ")}`, + ); + } else { + throw createHttpError[400](dispatchError.toString()); + } + } + this.prometheusExporter.addCurrentTransaction(); + resolve({ + success: true, + transactionHash: txHash.toHex(), + blockHash: status.asInBlock.toHex(), + }); + } + }); + }); + success = txResult.success; + const txHash = txResult.transactionHash; + const blockHash = txResult.blockHash; + return { success, txHash, blockHash }; + } else { + throw createHttpError[400]( + `${fnTag} Unsupported invocation type ${req.invocationType}`, + ); + } + } + + public getPrometheusExporter(): PrometheusExporter { + return this.prometheusExporter; + } + + public async getPrometheusExporterMetrics(): Promise { + const fnTag = `${this.className}#getPrometheusExporterMetrics()`; + try { + const res: string = await this.prometheusExporter.getPrometheusMetrics(); + this.log.debug(`getPrometheusExporterMetrics() response: %o`, res); + return res; + } catch (err) { + throw createHttpError[500]( + `${fnTag} Obtaining Prometheus Exporter Metrics has failed. ` + + `InnerException: ${err}`, + ); + } + } + + // Obtains information to sign a transaction + public async obtainTransactionInformation( + req: TransactionInfoRequest, + ): Promise { + const fnTag = `${this.className}#obtainTxInformation()`; + Checks.truthy(req, `${fnTag} req`); + this.log.info(`getTxFee`); + if (!this.api) { + throw createHttpError[400]( + `The operation has failed because the API is not connected to Substrate Node`, + ); + } + const accountAddress = req.accountAddress; + const transactionExpiration = (req.transactionExpiration as number) || 50; + try { + const signedBlock = await this.api.rpc.chain.getBlock(); + const nonce = (await this.api.derive.balances.account(accountAddress)) + .accountNonce; + const blockHash = signedBlock.block.header.hash; + const era = this.api.createType("ExtrinsicEra", { + current: signedBlock.block.header.number, + period: transactionExpiration, + }); + + const options = { + nonce: nonce, + blockHash: blockHash, + era: era, + }; + + const responseContainer = { + response_data: options, + succeeded: true, + message: "obtainTransactionInformation", + error: null, + }; + + const response: TransactionInfoResponse = { + responseContainer: responseContainer, + }; + + return response; + } catch (err) { + throw createHttpError[500]( + `${fnTag} Obtaining info for this transaction has failed. ` + + `InnerException: ${err}`, + ); + } + } + + public async shutdownConnectionToSubstrate(): Promise { + try { + if (this.api) { + this.log.info("Shutting down connection to substrate..."); + this.api.disconnect(); + } else { + this.log.warn( + "Trying to shutdown connection to substrate, but no connection is available", + ); + } + } catch (err) { + this.log.error("Could not disconnect from Substrate Ledger"); + throw createHttpError[500]( + `Could not disconnect from Substrate Ledger. InnerException: ${err} `, + ); + } + } +} diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/prometheus-exporter/data-fetcher.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/prometheus-exporter/data-fetcher.ts new file mode 100644 index 00000000000..140bb1a540f --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/prometheus-exporter/data-fetcher.ts @@ -0,0 +1,12 @@ +import { Transactions } from "./response.type"; + +import { totalTxCount, K_CACTUS_POLKADOT_TOTAL_TX_COUNT } from "./metrics"; + +export async function collectMetrics( + transactions: Transactions, +): Promise { + transactions.counter++; + totalTxCount + .labels(K_CACTUS_POLKADOT_TOTAL_TX_COUNT) + .set(transactions.counter); +} diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/prometheus-exporter/metrics.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/prometheus-exporter/metrics.ts new file mode 100644 index 00000000000..4de9b34e6df --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/prometheus-exporter/metrics.ts @@ -0,0 +1,10 @@ +import { Gauge } from "prom-client"; + +export const K_CACTUS_POLKADOT_TOTAL_TX_COUNT = + "cactus_polkadot_total_tx_count"; + +export const totalTxCount = new Gauge({ + name: "cactus_polkadot_total_tx_count", + help: "Total transactions executed", + labelNames: ["type"], +}); diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/prometheus-exporter/prometheus-exporter.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/prometheus-exporter/prometheus-exporter.ts new file mode 100644 index 00000000000..81454cae5bb --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/prometheus-exporter/prometheus-exporter.ts @@ -0,0 +1,39 @@ +import promClient, { Registry } from "prom-client"; +import { Transactions } from "./response.type"; +import { collectMetrics } from "./data-fetcher"; +import { K_CACTUS_POLKADOT_TOTAL_TX_COUNT } from "./metrics"; +import { totalTxCount } from "./metrics"; + +export interface IPrometheusExporterOptions { + pollingIntervalInMin?: number; +} + +export class PrometheusExporter { + public readonly metricsPollingIntervalInMin: number; + public readonly transactions: Transactions = { counter: 0 }; + public readonly registry: Registry; + + constructor( + public readonly prometheusExporterOptions: IPrometheusExporterOptions, + ) { + this.metricsPollingIntervalInMin = + prometheusExporterOptions.pollingIntervalInMin || 1; + this.registry = new Registry(); + } + + public addCurrentTransaction(): void { + collectMetrics(this.transactions); + } + + public async getPrometheusMetrics(): Promise { + const result = await this.registry.getSingleMetricAsString( + K_CACTUS_POLKADOT_TOTAL_TX_COUNT, + ); + return result; + } + + public startMetricsCollection(): void { + this.registry.registerMetric(totalTxCount); + promClient.collectDefaultMetrics({ register: this.registry }); + } +} diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/prometheus-exporter/response.type.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/prometheus-exporter/response.type.ts new file mode 100644 index 00000000000..3f1bc7f4911 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/prometheus-exporter/response.type.ts @@ -0,0 +1,3 @@ +export type Transactions = { + counter: number; +}; diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/public-api.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/public-api.ts new file mode 100644 index 00000000000..a490e3bbe5a --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/public-api.ts @@ -0,0 +1,16 @@ +export * from "./generated/openapi/typescript-axios/index"; + +export { + PluginLedgerConnectorPolkadot, + IPluginLedgerConnectorPolkadotOptions, +} from "./plugin-ledger-connector-polkadot"; +export { PluginFactoryLedgerConnector } from "./plugin-factory-ledger-connector-polkadot"; + +import { IPluginFactoryOptions } from "@hyperledger/cactus-core-api"; +import { PluginFactoryLedgerConnector } from "./plugin-factory-ledger-connector-polkadot"; + +export async function createPluginFactory( + pluginFactoryOptions: IPluginFactoryOptions, +): Promise { + return new PluginFactoryLedgerConnector(pluginFactoryOptions); +} diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/deploy-contract-ink-endpoint.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/deploy-contract-ink-endpoint.ts new file mode 100644 index 00000000000..46329a1a71a --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/deploy-contract-ink-endpoint.ts @@ -0,0 +1,97 @@ +import type { Express, Request, Response } from "express"; +import { + Logger, + LoggerProvider, + LogLevelDesc, + Checks, + IAsyncProvider, +} from "@hyperledger/cactus-common"; + +import { + IWebServiceEndpoint, + IExpressRequestHandler, + IEndpointAuthzOptions, +} from "@hyperledger/cactus-core-api"; + +import { handleRestEndpointException, registerWebServiceEndpoint } from "@hyperledger/cactus-core"; + +import { PluginLedgerConnectorPolkadot } from "../plugin-ledger-connector-polkadot"; +import OAS from "../../json/openapi.json"; + +export interface IDeployContractInkEndpointOptions { + logLevel?: LogLevelDesc; + connector: PluginLedgerConnectorPolkadot; +} + +export class DeployContractInkEndpoint implements IWebServiceEndpoint { + private readonly log: Logger; + public static readonly CLASS_NAME = "DeployContractInkEndpoint"; + + constructor(public readonly opts: IDeployContractInkEndpointOptions) { + const fnTag = "DeployContractInkEndpoint#constructor()"; + + Checks.truthy(opts, `${fnTag} options`); + Checks.truthy(opts.connector, `${fnTag} arg options.connector`); + + const level = this.opts.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + } + + public get className(): string { + return DeployContractInkEndpoint.CLASS_NAME; + } + + getAuthorizationOptionsProvider(): IAsyncProvider { + // TODO: make this an injectable dependency in the constructor + return { + get: async () => ({ + isProtected: true, + requiredRoles: [], + }), + }; + } + + public getExpressRequestHandler(): IExpressRequestHandler { + return this.handleRequest.bind(this); + } + + public getPath(): string { + return this.oasPath.post["x-hyperledger-cactus"].http.path; + } + + public getVerbLowerCase(): string { + return this.oasPath.post["x-hyperledger-cactus"].http.verbLowerCase; + } + + public getOperationId(): string { + return this.oasPath.post.operationId; + } + + public get oasPath(): (typeof OAS.paths)["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/deploy-contract-ink"] { + return OAS.paths[ + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/deploy-contract-ink" + ]; + } + + public async registerExpress( + expressApp: Express, + ): Promise { + await registerWebServiceEndpoint(expressApp, this); + return this; + } + + async handleRequest(req: Request, res: Response): Promise { + const fnTag = `${this.className}#handleRequest()`; + const reqTag = `${this.getVerbLowerCase()} - ${this.getPath()}`; + this.log.debug(reqTag); + const reqBody = req.body; + try { + const resBody = await this.opts.connector.deployContract(reqBody); + res.json(resBody); + } catch (ex) { + const errorMsg = `${reqTag} ${fnTag} Failed to deploy contract:`; + handleRestEndpointException({ errorMsg, log: this.log, error: ex, res }); + } + } +} diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/get-prometheus-exporter-metrics-endpoint.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/get-prometheus-exporter-metrics-endpoint.ts new file mode 100644 index 00000000000..fc1025b55af --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/get-prometheus-exporter-metrics-endpoint.ts @@ -0,0 +1,105 @@ +import type { Express, Request, Response } from "express"; + +import { handleRestEndpointException, registerWebServiceEndpoint } from "@hyperledger/cactus-core"; + +import OAS from "../../json/openapi.json"; + +import { + IWebServiceEndpoint, + IExpressRequestHandler, + IEndpointAuthzOptions, +} from "@hyperledger/cactus-core-api"; + +import { + LogLevelDesc, + Logger, + LoggerProvider, + Checks, + IAsyncProvider, +} from "@hyperledger/cactus-common"; + +import { PluginLedgerConnectorPolkadot } from "../plugin-ledger-connector-polkadot"; + +export interface IGetPrometheusMetricsEndpointOptions { + connector: PluginLedgerConnectorPolkadot; + logLevel?: LogLevelDesc; +} + +export class GetPrometheusMetricsEndpoint + implements IWebServiceEndpoint +{ + private readonly log: Logger; + public static readonly CLASS_NAME = "GetPrometheusExporterEndpoint"; + constructor( + public readonly options: IGetPrometheusMetricsEndpointOptions, + ) { + const fnTag = `${this.className}#constructor()`; + + Checks.truthy(options, `${fnTag} options`); + Checks.truthy(options.connector, `${fnTag} options.connector`); + + const label = this.className;; + const level = options.logLevel || "INFO"; + this.log = LoggerProvider.getOrCreate({ label, level }); + } + + public get className(): string { + return GetPrometheusMetricsEndpoint.CLASS_NAME; + } + + getAuthorizationOptionsProvider(): IAsyncProvider { + // TODO: make this an injectable dependency in the constructor + return { + get: async () => ({ + isProtected: true, + requiredRoles: [], + }), + }; + } + + public getExpressRequestHandler(): IExpressRequestHandler { + return this.handleRequest.bind(this); + } + + public get oasPath(): (typeof OAS.paths)["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/get-prometheus-exporter-metrics"] { + return OAS.paths[ + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/get-prometheus-exporter-metrics" + ]; + } + + public getPath(): string { + return this.oasPath.get["x-hyperledger-cactus"].http.path; + } + + public getVerbLowerCase(): string { + return this.oasPath.get["x-hyperledger-cactus"].http.verbLowerCase; + } + + public getOperationId(): string { + return this.oasPath.get.operationId; + } + + public async registerExpress( + expressApp: Express, + ): Promise { + await registerWebServiceEndpoint(expressApp, this); + return this; + } + + async handleRequest(req: Request, res: Response): Promise { + const fnTag = `${this.className}#handleRequest()`; + const reqTag = `${this.getVerbLowerCase()} - ${this.getPath()}`; + const verbUpper = this.getVerbLowerCase().toUpperCase(); + this.log.debug(`${verbUpper} ${this.getPath()}`); + + try { + const resBody = + await this.options.connector.getPrometheusExporterMetrics(); + res.status(200); + res.send(resBody); + } catch (ex) { + const errorMsg = `${reqTag} ${fnTag} Failed to get Prometheus Exporter Metrics:`; + handleRestEndpointException({ errorMsg, log: this.log, error: ex, res }); + } + } +} diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/get-raw-transaction-endpoint.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/get-raw-transaction-endpoint.ts new file mode 100644 index 00000000000..f1a23bf8f78 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/get-raw-transaction-endpoint.ts @@ -0,0 +1,97 @@ +import type { Express, Request, Response } from "express"; +import { + Logger, + LoggerProvider, + LogLevelDesc, + Checks, + IAsyncProvider, +} from "@hyperledger/cactus-common"; + +import { + IWebServiceEndpoint, + IExpressRequestHandler, + IEndpointAuthzOptions, +} from "@hyperledger/cactus-core-api"; + +import { handleRestEndpointException, registerWebServiceEndpoint } from "@hyperledger/cactus-core"; + +import { PluginLedgerConnectorPolkadot } from "../plugin-ledger-connector-polkadot"; +import OAS from "../../json/openapi.json"; + +export interface IGetRawTransactionEndpointOptions { + logLevel?: LogLevelDesc; + connector: PluginLedgerConnectorPolkadot; +} + +export class GetRawTransactionEndpoint implements IWebServiceEndpoint { + private readonly log: Logger; + public static readonly CLASS_NAME = "GetRawTransactionEndpoint"; + + constructor(public readonly opts: IGetRawTransactionEndpointOptions) { + const fnTag = "GetRawTransactionEndpoint#constructor()"; + + Checks.truthy(opts, `${fnTag} options`); + Checks.truthy(opts.connector, `${fnTag} arg options.connector`); + + const level = this.opts.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + } + + public get className(): string { + return GetRawTransactionEndpoint.CLASS_NAME; + } + + getAuthorizationOptionsProvider(): IAsyncProvider { + // TODO: make this an injectable dependency in the constructor + return { + get: async () => ({ + isProtected: true, + requiredRoles: [], + }), + }; + } + + public getExpressRequestHandler(): IExpressRequestHandler { + return this.handleRequest.bind(this); + } + + public getPath(): string { + return this.oasPath.post["x-hyperledger-cactus"].http.path; + } + + public getVerbLowerCase(): string { + return this.oasPath.post["x-hyperledger-cactus"].http.verbLowerCase; + } + + public getOperationId(): string { + return this.oasPath.post.operationId; + } + + public get oasPath(): (typeof OAS.paths)["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/get-raw-transaction"] { + return OAS.paths[ + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/get-raw-transaction" + ]; + } + + public async registerExpress( + expressApp: Express, + ): Promise { + await registerWebServiceEndpoint(expressApp, this); + return this; + } + + handleRequest(req: Request, res: Response): void { + const fnTag = `${this.className}#handleRequest()`; + const reqTag = `${this.getVerbLowerCase()} - ${this.getPath()}`; + this.log.debug(reqTag); + const reqBody = req.body; + try { + const resBody = this.opts.connector.rawTransaction(reqBody); + res.json(resBody); + } catch (ex) { + const errorMsg = `${reqTag} ${fnTag} Failed to get Raw Transaction:`; + handleRestEndpointException({ errorMsg, log: this.log, error: ex, res }); + } + } +} diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/get-transaction-info-endpoint.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/get-transaction-info-endpoint.ts new file mode 100644 index 00000000000..757e1b26ced --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/get-transaction-info-endpoint.ts @@ -0,0 +1,99 @@ +import type { Express, Request, Response } from "express"; +import { + Logger, + LoggerProvider, + LogLevelDesc, + Checks, + IAsyncProvider, +} from "@hyperledger/cactus-common"; + +import { + IWebServiceEndpoint, + IExpressRequestHandler, + IEndpointAuthzOptions, +} from "@hyperledger/cactus-core-api"; + +import { handleRestEndpointException, registerWebServiceEndpoint } from "@hyperledger/cactus-core"; + +import { PluginLedgerConnectorPolkadot } from "../plugin-ledger-connector-polkadot"; +import OAS from "../../json/openapi.json"; + +export interface IGetTransactionInfoEndpointOptions { + logLevel?: LogLevelDesc; + connector: PluginLedgerConnectorPolkadot; +} + +export class GetTransactionInfoEndpoint implements IWebServiceEndpoint { + private readonly log: Logger; + public static readonly CLASS_NAME = "GetTransactionInfoEndpoint"; + + constructor(public readonly opts: IGetTransactionInfoEndpointOptions) { + const fnTag = "GetTransactionInfoEndpoint#constructor()"; + + Checks.truthy(opts, `${fnTag} options`); + Checks.truthy(opts.connector, `${fnTag} arg options.connector`); + + const level = this.opts.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + } + + public get className(): string { + return GetTransactionInfoEndpoint.CLASS_NAME; + } + + getAuthorizationOptionsProvider(): IAsyncProvider { + // TODO: make this an injectable dependency in the constructor + return { + get: async () => ({ + isProtected: true, + requiredRoles: [], + }), + }; + } + + public getExpressRequestHandler(): IExpressRequestHandler { + return this.handleRequest.bind(this); + } + + public getPath(): string { + return this.oasPath.post["x-hyperledger-cactus"].http.path; + } + + public getVerbLowerCase(): string { + return this.oasPath.post["x-hyperledger-cactus"].http.verbLowerCase; + } + + public getOperationId(): string { + return this.oasPath.post.operationId; + } + + public get oasPath(): (typeof OAS.paths)["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/get-transaction-info"] { + return OAS.paths[ + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/get-transaction-info" + ]; + } + + public async registerExpress( + expressApp: Express, + ): Promise { + await registerWebServiceEndpoint(expressApp, this); + return this; + } + + async handleRequest(req: Request, res: Response): Promise { + const fnTag = `${this.className}#handleRequest()`; + const reqTag = `${this.getVerbLowerCase()} - ${this.getPath()}`; + this.log.debug(reqTag); + const reqBody = req.body; + try { + const resBody = await this.opts.connector.obtainTransactionInformation( + reqBody, + ); + res.json(resBody); + } catch (ex) { + const errorMsg = `${reqTag} ${fnTag} Failed to obtain Transaction Information:`; + handleRestEndpointException({ errorMsg, log: this.log, error: ex, res }); + } + } +} diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/invoke-contract-endpoint.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/invoke-contract-endpoint.ts new file mode 100644 index 00000000000..daae2a3a3c7 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/invoke-contract-endpoint.ts @@ -0,0 +1,96 @@ +import type { Express, Request, Response } from "express"; + +import { + Logger, + LoggerProvider, + LogLevelDesc, + Checks, + IAsyncProvider, +} from "@hyperledger/cactus-common"; +import { + IWebServiceEndpoint, + IExpressRequestHandler, + IEndpointAuthzOptions, +} from "@hyperledger/cactus-core-api"; + +import { + handleRestEndpointException, + registerWebServiceEndpoint, +} from "@hyperledger/cactus-core"; + +import { PluginLedgerConnectorPolkadot } from "../plugin-ledger-connector-polkadot"; +import OAS from "../../json/openapi.json"; +export interface IInvokeContractEndpointOptions { + logLevel?: LogLevelDesc; + connector: PluginLedgerConnectorPolkadot; +} + +export class InvokeContractEndpoint implements IWebServiceEndpoint { + private readonly log: Logger; + public static readonly CLASS_NAME = "InvokeContractEndpoint"; + + constructor(public readonly opts: IInvokeContractEndpointOptions) { + const fnTag = `${this.className}#constructor()`; + + Checks.truthy(opts, `${fnTag} arg options`); + Checks.truthy(opts.connector, `${fnTag} arg options.connector`); + const level = this.opts.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + } + public get className(): string { + return InvokeContractEndpoint.CLASS_NAME; + } + getAuthorizationOptionsProvider(): IAsyncProvider { + // TODO: make this an injectable dependency in the constructor + return { + get: async () => ({ + isProtected: true, + requiredRoles: [], + }), + }; + } + + public getExpressRequestHandler(): IExpressRequestHandler { + return this.handleRequest.bind(this); + } + + public getPath(): string { + return this.oasPath.post["x-hyperledger-cactus"].http.path; + } + + public getVerbLowerCase(): string { + return this.oasPath.post["x-hyperledger-cactus"].http.verbLowerCase; + } + + public getOperationId(): string { + return this.oasPath.post.operationId; + } + + public get oasPath(): (typeof OAS.paths)["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/invoke-contract"] { + return OAS.paths[ + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/invoke-contract" + ]; + } + + public async registerExpress( + expressApp: Express, + ): Promise { + await registerWebServiceEndpoint(expressApp, this); + return this; + } + + async handleRequest(req: Request, res: Response): Promise { + const fnTag = `${this.className}#handleRequest()`; + const reqTag = `${this.getVerbLowerCase()} - ${this.getPath()}`; + this.log.debug(reqTag); + const reqBody = req.body; + try { + const resBody = await this.opts.connector.invokeContract(reqBody); + res.json(resBody); + } catch (ex) { + const errorMsg = `${reqTag} ${fnTag} Failed to invoke contract:`; + handleRestEndpointException({ errorMsg, log: this.log, error: ex, res }); + } + } +} diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/run-transaction-endpoint.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/run-transaction-endpoint.ts new file mode 100644 index 00000000000..89e5366c011 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/run-transaction-endpoint.ts @@ -0,0 +1,96 @@ +import type { Express, Request, Response } from "express"; + +import { + Logger, + LoggerProvider, + LogLevelDesc, + Checks, + IAsyncProvider, +} from "@hyperledger/cactus-common"; +import { + IWebServiceEndpoint, + IExpressRequestHandler, + IEndpointAuthzOptions, +} from "@hyperledger/cactus-core-api"; + +import { + handleRestEndpointException, + registerWebServiceEndpoint, +} from "@hyperledger/cactus-core"; + +import { PluginLedgerConnectorPolkadot } from "../plugin-ledger-connector-polkadot"; +import OAS from "../../json/openapi.json"; +export interface IRunTransactionEndpointOptions { + logLevel?: LogLevelDesc; + connector: PluginLedgerConnectorPolkadot; +} + +export class RunTransactionEndpoint implements IWebServiceEndpoint { + private readonly log: Logger; + public static readonly CLASS_NAME = "RunTransactionEndpoint"; + + constructor(public readonly opts: IRunTransactionEndpointOptions) { + const fnTag = `${this.className}#constructor()`; + + Checks.truthy(opts, `${fnTag} arg options`); + Checks.truthy(opts.connector, `${fnTag} arg options.connector`); + const level = this.opts.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + } + public get className(): string { + return RunTransactionEndpoint.CLASS_NAME; + } + getAuthorizationOptionsProvider(): IAsyncProvider { + // TODO: make this an injectable dependency in the constructor + return { + get: async () => ({ + isProtected: true, + requiredRoles: [], + }), + }; + } + + public getExpressRequestHandler(): IExpressRequestHandler { + return this.handleRequest.bind(this); + } + + public getPath(): string { + return this.oasPath.post["x-hyperledger-cactus"].http.path; + } + + public getVerbLowerCase(): string { + return this.oasPath.post["x-hyperledger-cactus"].http.verbLowerCase; + } + + public getOperationId(): string { + return this.oasPath.post.operationId; + } + + public get oasPath(): (typeof OAS.paths)["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/run-transaction"] { + return OAS.paths[ + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/run-transaction" + ]; + } + + public async registerExpress( + expressApp: Express, + ): Promise { + await registerWebServiceEndpoint(expressApp, this); + return this; + } + + async handleRequest(req: Request, res: Response): Promise { + const fnTag = `${this.className}#handleRequest()`; + const reqTag = `${this.getVerbLowerCase()} - ${this.getPath()}`; + this.log.debug(reqTag); + const reqBody = req.body; + try { + const resBody = await this.opts.connector.transact(reqBody); + res.json(resBody); + } catch (ex) { + const errorMsg = `${reqTag} ${fnTag} Failed to perform transaction:`; + handleRestEndpointException({ errorMsg, log: this.log, error: ex, res }); + } + } +} diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/sign-raw-transaction-endpoint.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/sign-raw-transaction-endpoint.ts new file mode 100644 index 00000000000..ee457b567da --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/main/typescript/web-services/sign-raw-transaction-endpoint.ts @@ -0,0 +1,100 @@ +import type { Express, Request, Response } from "express"; +import { + Logger, + LoggerProvider, + LogLevelDesc, + Checks, + IAsyncProvider, +} from "@hyperledger/cactus-common"; + +import { + IWebServiceEndpoint, + IExpressRequestHandler, + IEndpointAuthzOptions, +} from "@hyperledger/cactus-core-api"; + +import { + handleRestEndpointException, + registerWebServiceEndpoint, +} from "@hyperledger/cactus-core"; + +import { PluginLedgerConnectorPolkadot } from "../plugin-ledger-connector-polkadot"; +import OAS from "../../json/openapi.json"; + +export interface ISignRawTransactionEndpointOptions { + logLevel?: LogLevelDesc; + connector: PluginLedgerConnectorPolkadot; +} + +export class SignRawTransactionEndpoint implements IWebServiceEndpoint { + private readonly log: Logger; + public static readonly CLASS_NAME = "SignRawTransactionEndpoint"; + + constructor(public readonly opts: ISignRawTransactionEndpointOptions) { + const fnTag = "SignRawTransactionEndpoint#constructor()"; + + Checks.truthy(opts, `${fnTag} options`); + Checks.truthy(opts.connector, `${fnTag} arg options.connector`); + + const level = this.opts.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + } + + public get className(): string { + return SignRawTransactionEndpoint.CLASS_NAME; + } + + getAuthorizationOptionsProvider(): IAsyncProvider { + // TODO: make this an injectable dependency in the constructor + return { + get: async () => ({ + isProtected: true, + requiredRoles: [], + }), + }; + } + + public getExpressRequestHandler(): IExpressRequestHandler { + return this.handleRequest.bind(this); + } + + public getPath(): string { + return this.oasPath.post["x-hyperledger-cactus"].http.path; + } + + public getVerbLowerCase(): string { + return this.oasPath.post["x-hyperledger-cactus"].http.verbLowerCase; + } + + public getOperationId(): string { + return this.oasPath.post.operationId; + } + + public get oasPath(): (typeof OAS.paths)["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/sign-raw-transaction"] { + return OAS.paths[ + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-polkadot/sign-raw-transaction" + ]; + } + + public async registerExpress( + expressApp: Express, + ): Promise { + await registerWebServiceEndpoint(expressApp, this); + return this; + } + + async handleRequest(req: Request, res: Response): Promise { + const fnTag = `${this.className}#handleRequest()`; + const reqTag = `${this.getVerbLowerCase()} - ${this.getPath()}`; + this.log.debug(reqTag); + const reqBody = req.body; + try { + const resBody = await this.opts.connector.signTransaction(reqBody); + res.json(resBody); + } catch (ex) { + const errorMsg = `${reqTag} ${fnTag} Failed to sign Transaction:`; + handleRestEndpointException({ errorMsg, log: this.log, error: ex, res }); + } + } +} diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/fixtures/ink/flipper.contract b/packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/fixtures/ink/flipper.contract new file mode 100644 index 00000000000..d346e67f8fa --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/fixtures/ink/flipper.contract @@ -0,0 +1 @@ +{"source":{"hash":"0x7b5ef2f314c9ada626e2ef1b54c3fad8f02fd09c8f349414b762dcfc8dee6353","language":"ink! 4.2.1","compiler":"rustc 1.69.0","wasm":"0x0061736d0100000001450c60027f7f017f60037f7f7f017f60027f7f0060037f7f7f0060017f0060047f7f7f7f017f60000060047f7f7f7f0060017f017f60017f017e60057f7f7f7f7f006000017f028a0107057365616c310b6765745f73746f726167650005057365616c301176616c75655f7472616e736665727265640002057365616c3005696e7075740002057365616c300d64656275675f6d6573736167650000057365616c320b7365745f73746f726167650005057365616c300b7365616c5f72657475726e000303656e76066d656d6f7279020102100336350102030b08030400020801020604030202060600010103000300070204060202000400040900000a0507050000030a01000000000704050170010f0f0608017f01418080040b0711020463616c6c0017066465706c6f7900180914010041010b0e0d31263928323738271b1d1f362a0a8744352b01017f037f2002200346047f200005200020036a200120036a2d00003a0000200341016a21030c010b0b0b2601017f230041106b22022400200220003a000f20012002410f6a41011008200241106a24000b5c01037f02402000280208220420026a220320044f04402003200028020422054b0d01200028020020046a200320046b2001200241c897041034200020033602080f0b41f09504411c41a89704101e000b2003200541b89704100b000b5502027f027e230041206b22002400200041106a22014200370300200042003703082000411036021c200041086a2000411c6a10012001290300210220002903082103200041206a2400410541042002200384501b0b1b002000418180014f044020004180800141dc8104100b000b20000b7501017f230041306b220324002003200136020420032000360200200341146a41023602002003411c6a41023602002003412c6a41033602002003418c8f0436021020034100360208200341033602242003200341206a3602182003200341046a36022820032003360220200341086a2002100e000b5201017f230041206b220124002001410c6a4101360200200141146a4101360200200141e49404360208200141003602002001410136021c200120003602182001200141186a360210200141c88204100e000b910101017f230041306b22022400200241146a41013602002002411c6a4101360200200241e494043602102002410036020820024102360224200220002d0000410274220041949a046a28020036022c2002200041a89a046a280200360228200141046a28020021002002200241206a3602182002200241286a36022020012802002000200241086a1035200241306a24000b3c01017f230041206b22022400200241013a00182002200136021420022000360210200241d08a0436020c200241e49504360208200241086a1029000b4001017f230041106b22012400200141003a000f20002001410f6a41011010047f4102054101410220012d000f22004101461b410020001b0b200141106a24000b6001047f230041106b22032400200028020422042002492205450440200341086a4100200220002802002206103a200120022003280208200328020c41849a0410342003200220042006103a200020032903003702000b200341106a240020050b3f01017f230041106b22022400200242808001370204200241c49a0436020020022001047f20024101101641010541000b101620002002280208100a1015000b3701017f230041106b22002400200042808001370204200041c49a0436020020004100101620004100101641002000280208100a1015000bae0102057f017e230041306b2201240020014100360218200142808001370224200141c49a043602202001410036021c200141206a22022001411c6a4104100820012001290320370310200141086a200141106a220320012802281014200128020c2104200128020820012903102106200141003602282001200637032020002002100720012001290320370310200120032001280228101420042001280200200128020410041a200141306a24000b4501017f2002200128020422034b0440418c9604412341e89704101e000b2001200320026b36020420012001280200220120026a36020020002002360204200020013602000b0d00200041c49a0420011005000ba10101027f20002802082202200028020422034904402000200241016a360208200028020020026a20013a00000f0b230041306b220024002000200336020420002002360200200041146a41023602002000411c6a41023602002000412c6a4103360200200041a48a0436021020004100360208200041033602242000200041206a360218200020003602282000200041046a360220200041086a41d89704100e000baf0501077f230041406a22002400024002400240024002400240100941ff0171410546044020004180800136022041c49a04200041206a100220002802202201418180014f0d0120002001360224200041c49a0436022020004100360210200041206a200041106a410410100d0520002802102201411876210220014110762104200141087621030240200141ff01712201412f470440200141e30047200341ff0171413a4772200441ff017141a50147720d0741012101200241d100460d010c070b200341ff017141860147200441ff017141db0047720d0641002101200241d901470d060b20004100360218200042808001370224200041c49a043602202000410036023c200041206a22032000413c6a4104100820002000290320370310200041086a200041106a20002802281014200028020c210520002802082000280210210220002000280214220436022020052002200310002103200420002802202205490d02024002400240410c20032003410c4f1b0e0402000001000b2000412c6a4101360200200041346a4100360200200041888204360228200041e4950436023020004100360220200041206a41908204100e000b2000412c6a4101360200200041346a4100360200200041a083043602280c070b2000200536022420002002360220200041206a100f41ff017122024102460d042001450d032002451013410041001011000b200041043a0020200041206a100c000b20014180800141e48004100b000b2005200441e48004100b000b230041106b22002400200042808001370204200041c49a0436020020004100101620024100472000100741002000280208100a1015000b2000412c6a4101360200200041346a41003602002000418083043602280c010b410141011011000b200041e4950436023020004100360220200041206a41c88204100e000b8f0201057f230041106b2200240002400240100941ff01712201410546044020004180800136020041c49a042000100220002802002201418180014f0d0120002001360204200041c49a043602002000410036020802402000200041086a410410100d002000280208220141187621022001411076210320014108762104200141ff01712201419b01470440200141ed0147200441ff017141cb004772200341ff0171419d01472002411b4772720d01410010131012000b200441ff017141ae0147200341ff0171419d014772200241de0047720d002000100f41ff017122004102470d030b410141011011000b200020013a00002000100c000b20014180800141e48004100b000b200010131012000b5501017f230041206b2202240020022000360204200241186a200141106a290200370300200241106a200141086a29020037030020022001290200370308200241046a41a88304200241086a101a200241206a24000bee0301057f230041406a22032400200341033a003820034280808080800437033020034100360228200341003602202003200136021c20032000360218027f0240024020022802002201450440200241146a28020022004103742105200041ffffffff017121072002280210210441002101034020012005460d02200228020820016a220041046a28020022060440200328021820002802002006200328021c28020c1101000d040b200141086a2101200428020020042802042106200441086a2104200341186a2006110000450d000b0c020b200228020422074105742100200741ffffff3f71210703402000450d01200228020820046a220541046a28020022060440200328021820052802002006200328021c28020c1101000d030b20032001411c6a2d00003a00382003200141146a290200370330200341106a200228021022052001410c6a103320032003290310370320200341086a2005200141046a103320032003290308370328200441086a2104200041206b210020012802002106200141206a2101200520064103746a2205280200200341186a2005280204110000450d000b0c010b2002410c6a28020020074b04402003280218200228020820074103746a22002802002000280204200328021c28020c1101000d010b41000c010b41010b200341406b24000b0f00200028020020012002101c41000b7701027f230041106b2204240020022000280200200028020822036b4b0440200441086a20002003200210202004280208200428020c1021200028020821030b200028020420036a2001200210061a2003200220036a22014b044041c08304411c41a08904101e000b20002001360208200441106a24000bdd0201037f230041106b220224000240024002400240200028020022002002410c6a027f0240024020014180014f04402002410036020c2001418010490d012001418080044f0d0220022001413f71418001723a000e20022001410c7641e001723a000c20022001410676413f71418001723a000d41030c030b200028020822032000280200460d030c040b20022001413f71418001723a000d2002200141067641c001723a000c41020c010b20022001413f71418001723a000f20022001410676413f71418001723a000e20022001410c76413f71418001723a000d2002200141127641077141f001723a000c41040b101c0c020b230041106b22042400200441086a20002003410110202004280208200428020c1021200441106a2400200028020821030b200028020420036a20013a0000200341016a2201450d01200020013602080b200241106a240041000f0b41c08304411c41908904101e000b5001017f230041206b220324002003410c6a4101360200200341146a4100360200200341e49504360210200341003602002003200136021c200320003602182003200341186a36020820032002100e000b4a01017f230041206b220224002000280200200241186a200141106a290200370300200241106a200141086a29020037030020022001290200370308200241086a1019200241206a24000bac0401067f230041206b2204240002402000027f4100200220036a22032002490d001a2001280200220220026a22062002490d0141082006200320032006491b2203200341084d1b2203417f73411f7621050240200204402004410136021820042002360214200420012802043602100c010b200441003602180b200441106a2107230041106b220624002004027f0240027f0240200504400240200341004e044020072802080d012006200310242006280204210220062802000c040b0c040b20072802042209450440200641086a20031024200628020c210220062802080c030b20032102410041bc9a04280200220520036a22082005490d021a2007280200210741c09a042802002008490440200341ffff036a220841107640002202417f46200241ffff0371200247720d022002411074220520084180807c716a22022005490d0241c09a042002360200200321024100200320056a22082005490d031a0b41bc9a04200836020041002005450d021a20052007200910060c020b200420033602040c020b2003210241000b2205044020042005360204200441086a200236020041000c020b20042003360204200441086a410136020041010c010b200441086a410036020041010b360200200641106a240020042802004504402004280204210220012003360200200120023602044181808080780c010b20042802042103200441086a2802000b36020420002003360200200441206a24000f0b41e08304412141848504101e000b1f00024020014181808080784704402001450d0120001022000b0f0b1023000b900101017f230041306b220124002001200036020c2001411c6a4102360200200141246a4101360200200141d08604360218200141003602102001410336022c2001200141286a36022020012001410c6a360228230041206b22002400200041003a0018200041e086043602142000200141106a360210200041d08a0436020c200041e49504360208200041086a1029000b4601017f230041206b22002400200041146a41013602002000411c6a4100360200200041a88504360210200041e4950436021820004100360208200041086a41b08504100e000ba10101027f027f410041bc9a04280200220220016a22032002490d001a024041c09a042802002003490440200141ffff036a22032001490d01200341107640002202417f46200241ffff0371200247720d012002411074220220034180807c716a22032002490d0141c09a0420033602004100200120026a22032002490d021a0b41bc9a04200336020020020c010b41000b210320002001360204200020033602000b5301027f230041106b2202240002402001450440410121030c010b200141004e0440200241086a20011024200228020822030d0120011022000b1023000b2000200336020420002001360200200241106a24000bd806020b7f027e230041406a2203240020002802002202ad210d0240024002400240024002400240024020024190ce004f044041272100200d210e0240034020004104490d01200341196a20006a220241046b200e200e4290ce0080220d4290ce007e7da7220441ffff037141e4006e220641017441818c046a2f00003b0000200241026b2004200641e4006c6b41ffff037141017441818c046a2f00003b0000200041046b2100200e42ffc1d72f56200d210e0d000b200da7220241e3004d0d0320004102490d090c020b0c080b41272100200241e3004b0d002002410a490d040c020b200041026b2200200341196a6a200da72202200241ffff037141e4006e220241e4006c6b41ffff037141017441818c046a2f00003b00000b2002410a490d01200041024f0d000c050b200041026b2200200341196a6a200241017441818c046a2f00003b00000c020b2000450d030b200041016b2200200341196a6a200241306a3a00000b200041274b0d01412820006b412720006b22062001280218220541017122071b2102410021042005410471044041e495042104200241e4950441e49504102b20026a22024b0d010b412b418080c40020071b2107200341196a20006a2108024020012802084504404101210020012802002202200141046a280200220120072004102e0d01200220082006200128020c11010021000c010b024020022001410c6a28020022094904402005410871450d01200128021c210b2001413036021c20012d0020210c41012100200141013a002020012802002205200141046a280200220a20072004102e0d02200341106a2001200920026b4101102f20032802142202418080c400460d022003280210200520082006200a28020c1101000d0220022005200a10300d022001200c3a00202001200b36021c410021000c020b4101210020012802002202200141046a280200220120072004102e0d01200220082006200128020c11010021000c010b41012100200341086a2001200920026b4101102f200328020c2205418080c400460d00200328020820012802002202200141046a280200220120072004102e0d00200220082006200128020c1101000d00200520022001103021000b200341406b240020000f0b41b08904411c41b88e04101e000b41d08904412141cc9404101e000b0300010b0e0020002802001a03400c000b000baa05020a7f017e230041406a220124002001200036020c2001412c6a4102360200200141346a4101360200200141b09604360228200141003602202001410436023c2001200141386a36023020012001410c6a360238200141106a210641002100230041306b22022400200141206a220441146a2802002107200428020821050240024002400240200241086a027f024002400240200241106a027f024002402004410c6a28020022080e020001040b20070d0241e49504210341000c010b20070d022005280200210320052802040b22001025200228021021042006200228021422053602042006200436020020052003200010061a200620003602080c040b200428021021090c010b200541046a21032008410374210a2004280210210903402000200020032802006a22004b0d04200341086a2103200a41086b220a0d000b20002007450d011a2000410f4b0d0041002005280204450d011a0b200020006a22034100200020034d1b0b10252002290308210b200641003602082006200b3702002002200736022c200220093602282002200836022420022005360220200220042902003703182006200241186a10190d020b200241306a24000c020b41c08304411c41bc9404101e000b230041406a220024002000413336020c200041f086043602082000418484043602142000200241186a360210200041246a41023602002000412c6a41023602002000413c6a4106360200200041848b0436022020004100360218200041023602342000200041306a3602282000200041106a3602382000200041086a360230200041186a41908804100e000b2001280214210020012802182101024041c49a052d000045044041c59a052d00004101710d010b410c20002001100322002000410c4f1b410947044041c49a0541013a00000b41c59a0541013a00000b000b0d0042e8a1eef1be9ca9fda07f0ba704010a7f230041106b2203240002400240200020016b22024110490d002002200141036a417c7120016b220049200041044b720d00200220006b22044104490d0020012000102c2206200020016a22082004417c716a2004410371102c6a220220064f0440200441027621050240024003402005450d0520032008200541c0012005200541c0014f1b418c9004102d200328020c21052003280208210820032003280200200328020422002000417c7141f49104102d200328020c210920032802082107024020032802042200450440410021010c010b2003280200220420004102746a210a4100210103402004220641106a2104410021000240034020012001200020066a280200220b417f73410776200b410676724181828408716a22014d0440200041046a22004110470d010c020b0b41b08904411c41849204101e000b2004200a470d000b0b20022002200141087641ff81fc0771200141ff81fc07716a418180046c4110766a22024b0d012009450d000b200941027421004100210103402001200120072802002204417f734107762004410676724181828408716a22014b0d02200741046a2107200041046b22000d000b20022002200141087641ff81fc0771200141ff81fc07716a418180046c4110766a22024d0d0441b08904411c41b49204101e000b41b08904411c41949204101e000b41b08904411c41a49204101e000b41b08904411c41e49104101e000b20012002102c21020b200341106a240020020b4601017f200145044041000f0b024003402002200220002c000041bf7f4a6a22024b0d01200041016a2100200141016b22010d000b20020f0b41b08904411c41bc9404101e000b3e00200220034f044020002003360204200020013602002000410c6a200220036b3602002000200120034102746a3602080f0b418c960441232004101e000b39000240027f2002418080c40047044041012000200220012802101100000d011a0b20030d0141000b0f0b200020034100200128020c1101000bae0101027f20022104024002400240200320012d0020220320034103461b41ff0171220341016b0e03010001020b200241016a2203044020034101762104200241017621030c020b41b08904411c41c88e04101e000b41002104200221030b200341016a2102200128021c2103200128020421052001280200210102400340200241016b2202450d01200120032005280210110000450d000b418080c40021030b20002003360204200020043602000b3201017f027f0340200020002004460d011a200441016a2104200220012003280210110000450d000b200441016b0b2000490bea04010b7f230041106b2209240020002802042104200028020021030240024002402001280208220b410147200128021022024101477145044020024101470d02200320046a210c200141146a28020041016a210a410021022003210003402000200c460d03027f024020002c0000220641004e0440200041016a2105200641ff017121070c010b20002d0001413f7121052006411f7121072006415f4d044020074106742005722107200041026a21050c010b20002d0002413f7120054106747221082006417049044020082007410c74722107200041036a21050c010b200041046a210520022106418080c4002007411274418080f0007120002d0003413f71200841067472722207418080c400460d011a0b2002200520006b6a22062002490d0320070b2108200a41016b220a044020052100200621022008418080c400470d010c040b0b2008418080c400460d02024002402002450d00200220044f04404100210020022004460d010c020b41002100200220036a2c00004140480d010b200321000b2002200420001b21042000200320001b21030c020b200128020020032004200128020428020c11010021000c020b41b08904411c41b49304101e000b200b450440200128020020032004200128020428020c11010021000c010b2001410c6a2802002200200320046a2003102b22024b0440200941086a2001200020026b4100102f41012100200928020c2202418080c400460d0120092802082001280200220520032004200141046a280200220128020c1101000d01200220052001103021000c010b200128020020032004200128020428020c11010021000b200941106a240020000b140020002802002001200028020428020c1100000b5501027f0240027f02400240200228020041016b0e020103000b200241046a0c010b200120022802044103746a22012802044105470d0120012802000b2802002104410121030b20002004360204200020033602000b8501002001200346044020002002200110061a0f0b230041306b220024002000200336020420002001360200200041146a41033602002000411c6a41023602002000412c6a4103360200200041dc900436021020004100360208200041033602242000200041206a360218200020003602282000200041046a360220200041086a2004100e000b4901017f230041206b22032400200341186a200241106a290200370300200341106a200241086a2902003703002003200229020037030820002001200341086a101a200341206a24000b1800200128020041dc94044105200128020428020c1101000b5801027f230041206b22022400200128020421032001280200200241186a2000280200220041106a290200370300200241106a200041086a290200370300200220002902003703082003200241086a101a200241206a24000b0b002000280200200110310b990301037f230041406a22022400200028020021034101210002402001280200220441e08a04410c200141046a280200220128020c1101000d0002402003280208220004402002200036020c200241346a4102360200410121002002413c6a4101360200200241f08a0436023020024100360228200241073602142002200241106a36023820022002410c6a36021020042001200241286a1035450d010c020b20032802002200200328020428020c11090042c8b5e0cfca86dbd3897f520d002002200036020c200241346a4102360200410121002002413c6a4101360200200241f08a0436023020024100360228200241083602142002200241106a36023820022002410c6a36021020042001200241286a10350d010b200328020c21002002411c6a4103360200200241246a41033602002002413c6a4103360200200241346a4103360200200241b88a043602182002410036021020022000410c6a3602382002200041086a3602302002410236022c200220003602282002200241286a36022020042001200241106a103521000b200241406b240020000b2c00200120024d04402000200220016b3602042000200120036a3602000f0b41809904412141ec9804101e000b0bc01a0200418080040be4152f686f6d652f616e6d6f6c2f2e636172676f2f72656769737472792f7372632f6769746875622e636f6d2d316563633632393964623965633832332f696e6b5f656e762d342e322e312f7372632f656e67696e652f6f6e5f636861696e2f6578742e72730000010064000000e4000000140000002f686f6d652f616e6d6f6c2f2e636172676f2f72656769737472792f7372632f6769746875622e636f6d2d316563633632393964623965633832332f696e6b5f656e762d342e322e312f7372632f656e67696e652f6f6e5f636861696e2f696d706c732e7273000074000100660000002401000023000000656e636f756e746572656420756e6578706563746564206572726f72ec0001001c0000007400010066000000ed000000170000002f686f6d652f616e6d6f6c2f646f636b65722d7475742f666c69707065722f6c69622e727300000020010100250000000600000005000000636f756c64206e6f742070726f7065726c79206465636f64652073746f7261676520656e74727900580101002700000073746f7261676520656e7472792077617320656d7074790088010100170000000900000004000000040000000a0000000b0000000c000000617474656d707420746f206164642077697468206f766572666c6f7700000000617474656d707420746f206d756c7469706c792077697468206f766572666c6f770000000900000000000000010000000d0000002f686f6d652f616e6d6f6c2f2e7275737475702f746f6f6c636861696e732f312e36392d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f7261775f7665632e72730000140201006e0000008f0100001c0000006361706163697479206f766572666c6f770000009402010011000000140201006e0000000d020000050000002f686f6d652f616e6d6f6c2f2e7275737475702f746f6f6c636861696e732f312e36392d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f616c6c6f632e72736d656d6f727920616c6c6f636174696f6e206f6620206279746573206661696c656400002c03010015000000410301000d000000c00201006c0000009f0100000d0000006120666f726d617474696e6720747261697420696d706c656d656e746174696f6e2072657475726e656420616e206572726f722f686f6d652f616e6d6f6c2f2e7275737475702f746f6f6c636861696e732f312e36392d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f666d742e7273000000a30301006a00000064020000200000002f686f6d652f616e6d6f6c2f2e7275737475702f746f6f6c636861696e732f312e36392d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f7665632f6d6f642e72730000200401006e000000350700000d000000200401006e000000a307000009000000617474656d707420746f206164642077697468206f766572666c6f7700000000617474656d707420746f2073756274726163742077697468206f766572666c6f7729696e646578206f7574206f6620626f756e64733a20746865206c656e20697320206275742074686520696e64657820697320f20401002000000012050100120000003a000000e40a010000000000340501000100000034050100010000000900000000000000010000000e00000070616e69636b65642061742027272c206c050100010000006d050100030000003a200000e40a01000000000080050100020000002f686f6d652f616e6d6f6c2f2e7275737475702f746f6f6c636861696e732f312e36392d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f666d742f6e756d2e727330303031303230333034303530363037303830393130313131323133313431353136313731383139323032313232323332343235323632373238323933303331333233333334333533363337333833393430343134323433343434353436343734383439353035313532353335343535353635373538353936303631363236333634363536363637363836393730373137323733373437353736373737383739383038313832383338343835383638373838383939303931393239333934393539363937393839392f686f6d652f616e6d6f6c2f2e7275737475702f746f6f6c636861696e732f312e36392d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f666d742f6d6f642e72730000c90601006d000000750500000d000000c90601006d0000000506000038000000206f7574206f662072616e676520666f7220736c696365206f66206c656e6774682072616e676520656e6420696e6465782000007a0701001000000058070100220000002f686f6d652f616e6d6f6c2f2e7275737475702f746f6f6c636861696e732f312e36392d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f736c6963652f697465722e72739c07010070000000c005000025000000736f7572636520736c696365206c656e67746820282920646f6573206e6f74206d617463682064657374696e6174696f6e20736c696365206c656e67746820281c08010015000000310801002b000000f1040100010000002f686f6d652f616e6d6f6c2f2e7275737475702f746f6f6c636861696e732f312e36392d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f7374722f636f756e742e727300740801006f0000004700000015000000740801006f0000004f00000032000000740801006f0000005400000011000000740801006f0000005a00000009000000740801006f0000006400000011000000740801006f000000660000000d0000002f686f6d652f616e6d6f6c2f2e7275737475702f746f6f6c636861696e732f312e36392d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f7374722f697465722e72730000440901006e00000091000000110000002f686f6d652f616e6d6f6c2f2e7275737475702f746f6f6c636861696e732f312e36392d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f697465722f7472616974732f616363756d2e727300c4090100770000009500000001000000940501006d000000cd010000050000004572726f72000000e40a0100000000007061696420616e20756e70617961626c65206d657373616765636f756c64206e6f74207265616420696e707574756e61626c6520746f206465636f646520696e707574656e636f756e746572656420756e6b6e6f776e2073656c6563746f72756e61626c6520746f206465636f64652073656c6563746f720041f095040bcb04617474656d707420746f206164642077697468206f766572666c6f77617373657274696f6e206661696c65643a206d6964203c3d2073656c662e6c656e28290ae40a0100000000002f0b0100010000002f686f6d652f616e6d6f6c2f2e636172676f2f72656769737472792f7372632f6769746875622e636f6d2d316563633632393964623965633832332f696e6b5f656e762d342e322e312f7372632f656e67696e652f6f6e5f636861696e2f6275666665722e727300400b0100670000005a0000001c000000400b0100670000005a00000009000000400b0100670000005a00000031000000400b0100670000006500000009000000400b0100670000008d000000210000002f686f6d652f616e6d6f6c2f2e7275737475702f746f6f6c636861696e732f312e36392d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f736c6963652f696e6465782e7273000000f80b010071000000820100004700000000000000617474656d707420746f2073756274726163742077697468206f766572666c6f772f686f6d652f616e6d6f6c2f2e636172676f2f72656769737472792f7372632f6769746875622e636f6d2d316563633632393964623965633832332f7061726974792d7363616c652d636f6465632d332e362e342f7372632f636f6465632e72730000a10c010061000000770000000e000000190000001c000000160000001400000019000000cb0a0100af0a0100990a0100850a01006c0a01","build_info":{"build_mode":"Debug","cargo_contract_version":"2.0.0-rc","rust_toolchain":"stable-x86_64-unknown-linux-gnu","wasm_opt_settings":{"keep_debug_symbols":false,"optimization_passes":"Z"}}},"contract":{"name":"flipper","version":"0.1.0","authors":["[your_name] <[your_email]>"]},"spec":{"constructors":[{"args":[{"label":"init_value","type":{"displayName":["bool"],"type":0}}],"default":false,"docs":["Constructor that initializes the `bool` value to the given `init_value`."],"label":"new","payable":false,"returnType":{"displayName":["ink_primitives","ConstructorResult"],"type":1},"selector":"0x9bae9d5e"},{"args":[],"default":false,"docs":["Constructor that initializes the `bool` value to `false`.","","Constructors can delegate to other constructors."],"label":"default","payable":false,"returnType":{"displayName":["ink_primitives","ConstructorResult"],"type":1},"selector":"0xed4b9d1b"}],"docs":[],"environment":{"accountId":{"displayName":["AccountId"],"type":5},"balance":{"displayName":["Balance"],"type":8},"blockNumber":{"displayName":["BlockNumber"],"type":11},"chainExtension":{"displayName":["ChainExtension"],"type":12},"hash":{"displayName":["Hash"],"type":9},"maxEventTopics":4,"timestamp":{"displayName":["Timestamp"],"type":10}},"events":[],"lang_error":{"displayName":["ink","LangError"],"type":3},"messages":[{"args":[],"default":false,"docs":[" A message that can be called on instantiated contracts."," This one flips the value of the stored `bool` from `true`"," to `false` and vice versa."],"label":"flip","mutates":true,"payable":false,"returnType":{"displayName":["ink","MessageResult"],"type":1},"selector":"0x633aa551"},{"args":[],"default":false,"docs":[" Simply returns the current value of our `bool`."],"label":"get","mutates":false,"payable":false,"returnType":{"displayName":["ink","MessageResult"],"type":4},"selector":"0x2f865bd9"}]},"storage":{"root":{"layout":{"struct":{"fields":[{"layout":{"leaf":{"key":"0x00000000","ty":0}},"name":"value"}],"name":"Flipper"}},"root_key":"0x00000000"}},"types":[{"id":0,"type":{"def":{"primitive":"bool"}}},{"id":1,"type":{"def":{"variant":{"variants":[{"fields":[{"type":2}],"index":0,"name":"Ok"},{"fields":[{"type":3}],"index":1,"name":"Err"}]}},"params":[{"name":"T","type":2},{"name":"E","type":3}],"path":["Result"]}},{"id":2,"type":{"def":{"tuple":[]}}},{"id":3,"type":{"def":{"variant":{"variants":[{"index":1,"name":"CouldNotReadInput"}]}},"path":["ink_primitives","LangError"]}},{"id":4,"type":{"def":{"variant":{"variants":[{"fields":[{"type":0}],"index":0,"name":"Ok"},{"fields":[{"type":3}],"index":1,"name":"Err"}]}},"params":[{"name":"T","type":0},{"name":"E","type":3}],"path":["Result"]}},{"id":5,"type":{"def":{"composite":{"fields":[{"type":6,"typeName":"[u8; 32]"}]}},"path":["ink_primitives","types","AccountId"]}},{"id":6,"type":{"def":{"array":{"len":32,"type":7}}}},{"id":7,"type":{"def":{"primitive":"u8"}}},{"id":8,"type":{"def":{"primitive":"u128"}}},{"id":9,"type":{"def":{"composite":{"fields":[{"type":6,"typeName":"[u8; 32]"}]}},"path":["ink_primitives","types","Hash"]}},{"id":10,"type":{"def":{"primitive":"u64"}}},{"id":11,"type":{"def":{"primitive":"u32"}}},{"id":12,"type":{"def":{"variant":{}},"path":["ink_env","types","NoChainExtension"]}}],"version":"4"} \ No newline at end of file diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/fixtures/ink/flipper.wasm b/packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/fixtures/ink/flipper.wasm new file mode 100644 index 0000000000000000000000000000000000000000..b399579659c391d4868c5369db39223a30da265c GIT binary patch literal 12443 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 lBY|lmi*T%beGeb$_)UCtR#E+{" + ] + }, + "spec": { + "constructors": [ + { + "args": [ + { + "label": "init_value", + "type": { + "displayName": [ + "bool" + ], + "type": 0 + } + } + ], + "default": false, + "docs": [ + "Constructor that initializes the `bool` value to the given `init_value`." + ], + "label": "new", + "payable": false, + "returnType": { + "displayName": [ + "ink_primitives", + "ConstructorResult" + ], + "type": 1 + }, + "selector": "0x9bae9d5e" + }, + { + "args": [], + "default": false, + "docs": [ + "Constructor that initializes the `bool` value to `false`.", + "", + "Constructors can delegate to other constructors." + ], + "label": "default", + "payable": false, + "returnType": { + "displayName": [ + "ink_primitives", + "ConstructorResult" + ], + "type": 1 + }, + "selector": "0xed4b9d1b" + } + ], + "docs": [], + "environment": { + "accountId": { + "displayName": [ + "AccountId" + ], + "type": 5 + }, + "balance": { + "displayName": [ + "Balance" + ], + "type": 8 + }, + "blockNumber": { + "displayName": [ + "BlockNumber" + ], + "type": 11 + }, + "chainExtension": { + "displayName": [ + "ChainExtension" + ], + "type": 12 + }, + "hash": { + "displayName": [ + "Hash" + ], + "type": 9 + }, + "maxEventTopics": 4, + "timestamp": { + "displayName": [ + "Timestamp" + ], + "type": 10 + } + }, + "events": [], + "lang_error": { + "displayName": [ + "ink", + "LangError" + ], + "type": 3 + }, + "messages": [ + { + "args": [], + "default": false, + "docs": [ + " A message that can be called on instantiated contracts.", + " This one flips the value of the stored `bool` from `true`", + " to `false` and vice versa." + ], + "label": "flip", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 1 + }, + "selector": "0x633aa551" + }, + { + "args": [], + "default": false, + "docs": [ + " Simply returns the current value of our `bool`." + ], + "label": "get", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 4 + }, + "selector": "0x2f865bd9" + } + ] + }, + "storage": { + "root": { + "layout": { + "struct": { + "fields": [ + { + "layout": { + "leaf": { + "key": "0x00000000", + "ty": 0 + } + }, + "name": "value" + } + ], + "name": "Flipper" + } + }, + "root_key": "0x00000000" + } + }, + "types": [ + { + "id": 0, + "type": { + "def": { + "primitive": "bool" + } + } + }, + { + "id": 1, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 2 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 3 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 2 + }, + { + "name": "E", + "type": 3 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 2, + "type": { + "def": { + "tuple": [] + } + } + }, + { + "id": 3, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 1, + "name": "CouldNotReadInput" + } + ] + } + }, + "path": [ + "ink_primitives", + "LangError" + ] + } + }, + { + "id": 4, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 0 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 3 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 0 + }, + { + "name": "E", + "type": 3 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 5, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 6, + "typeName": "[u8; 32]" + } + ] + } + }, + "path": [ + "ink_primitives", + "types", + "AccountId" + ] + } + }, + { + "id": 6, + "type": { + "def": { + "array": { + "len": 32, + "type": 7 + } + } + } + }, + { + "id": 7, + "type": { + "def": { + "primitive": "u8" + } + } + }, + { + "id": 8, + "type": { + "def": { + "primitive": "u128" + } + } + }, + { + "id": 9, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 6, + "typeName": "[u8; 32]" + } + ] + } + }, + "path": [ + "ink_primitives", + "types", + "Hash" + ] + } + }, + { + "id": 10, + "type": { + "def": { + "primitive": "u64" + } + } + }, + { + "id": 11, + "type": { + "def": { + "primitive": "u32" + } + } + }, + { + "id": 12, + "type": { + "def": { + "variant": {} + }, + "path": [ + "ink_env", + "types", + "NoChainExtension" + ] + } + } + ], + "version": "4" +} \ No newline at end of file diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/ink/README.md b/packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/ink/README.md new file mode 100644 index 00000000000..33e552b43ea --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/ink/README.md @@ -0,0 +1,10 @@ +## Getting Started + +#### Preparation +To build the artifacts, run: +``cargo install cargo-contract --force`` + +#### Compile + +Run: +``cargo +nightly contract build`` diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/ink/flipper/.gitignore b/packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/ink/flipper/.gitignore new file mode 100755 index 00000000000..8de8f877e47 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/ink/flipper/.gitignore @@ -0,0 +1,9 @@ +# Ignore build artifacts from the local tests sub-crate. +/target/ + +# Ignore backup files creates by cargo fmt. +**/*.rs.bk + +# Remove Cargo.lock when creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/ink/flipper/Cargo.toml b/packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/ink/flipper/Cargo.toml new file mode 100755 index 00000000000..2fe9e4bfd0c --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/ink/flipper/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "flipper" +version = "0.1.0" +authors = ["[your_name] <[your_email]>"] +edition = "2021" + +[dependencies] +ink = { version = "4.0.0-beta", default-features = false } + +scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } +scale-info = { version = "2.3", default-features = false, features = ["derive"], optional = true } + +[lib] +name = "flipper" +path = "lib.rs" +crate-type = [ + # Used for normal contract Wasm blobs. + "cdylib", +] + +[features] +default = ["std"] +std = [ + "ink/std", + "scale/std", + "scale-info/std", +] +ink-as-dependency = [] diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/ink/flipper/lib.rs b/packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/ink/flipper/lib.rs new file mode 100755 index 00000000000..ccbad9954ba --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/ink/flipper/lib.rs @@ -0,0 +1,69 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +#[ink::contract] +mod flipper { + + /// Defines the storage of your contract. + /// Add new fields to the below struct in order + /// to add new static storage fields to your contract. + #[ink(storage)] + pub struct Flipper { + /// Stores a single `bool` value on the storage. + value: bool, + } + + impl Flipper { + /// Constructor that initializes the `bool` value to the given `init_value`. + #[ink(constructor)] + pub fn new(init_value: bool) -> Self { + Self { value: init_value } + } + + /// Constructor that initializes the `bool` value to `false`. + /// + /// Constructors can delegate to other constructors. + #[ink(constructor)] + pub fn default() -> Self { + Self::new(Default::default()) + } + + /// A message that can be called on instantiated contracts. + /// This one flips the value of the stored `bool` from `true` + /// to `false` and vice versa. + #[ink(message)] + pub fn flip(&mut self) { + self.value = !self.value; + } + + /// Simply returns the current value of our `bool`. + #[ink(message)] + pub fn get(&self) -> bool { + self.value + } + } + + /// Unit tests in Rust are normally defined within such a `#[cfg(test)]` + /// module and test functions are marked with a `#[test]` attribute. + /// The below code is technically just normal Rust code. + #[cfg(test)] + mod tests { + /// Imports all the definitions from the outer scope so we can use them here. + use super::*; + + /// We test if the default constructor does its job. + #[ink::test] + fn default_works() { + let flipper = Flipper::default(); + assert_eq!(flipper.get(), false); + } + + /// We test a simple use case of our contract. + #[ink::test] + fn it_works() { + let mut flipper = Flipper::new(false); + assert_eq!(flipper.get(), false); + flipper.flip(); + assert_eq!(flipper.get(), true); + } + } +} diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/test/typescript/integration/deploy-ink-contract.test.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/test/typescript/integration/deploy-ink-contract.test.ts new file mode 100644 index 00000000000..6df0213e856 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/test/typescript/integration/deploy-ink-contract.test.ts @@ -0,0 +1,163 @@ +import { + IListenOptions, + LogLevelDesc, + Servers, +} from "@hyperledger/cactus-common"; +import { SubstrateTestLedger } from "../../../../../cactus-test-tooling/src/main/typescript/substrate-test-ledger/substrate-test-ledger"; +import metadata from "../../rust/fixtures/ink/metadata.json"; +import fs from "fs-extra"; +import { v4 as uuidv4 } from "uuid"; +import { pruneDockerAllIfGithubAction } from "@hyperledger/cactus-test-tooling"; +import express from "express"; +import http from "http"; +import { + PluginLedgerConnectorPolkadot, + IPluginLedgerConnectorPolkadotOptions, + DefaultApi as PolkadotApi, + Web3SigningCredentialType, + PluginFactoryLedgerConnector, +} from "../../../main/typescript/public-api"; +import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory"; +import { PluginRegistry } from "@hyperledger/cactus-core"; +import { AddressInfo } from "net"; +import { Configuration, PluginImportType } from "@hyperledger/cactus-core-api"; +import "jest-extended"; + +const testCase = "deploy contract through all available methods"; +describe(testCase, () => { + const logLevel: LogLevelDesc = "TRACE"; + + const DEFAULT_WSPROVIDER = "ws://127.0.0.1:9944"; + const instanceId = "test-polkadot-connector"; + const ledgerOptions = { + publishAllPorts: false, + logLevel: logLevel, + emitContainerLogs: true, + }; + const ledger = new SubstrateTestLedger(ledgerOptions); + const expressApp = express(); + expressApp.use(express.json()); + expressApp.use(express.urlencoded({ extended: false })); + const server = http.createServer(expressApp); + let addressInfo: AddressInfo, + address: string, + port: number, + apiHost: string, + plugin: PluginLedgerConnectorPolkadot, + keychainEntryKey: string, + keychainEntryValue: string, + keychainPlugin: PluginKeychainMemory, + apiClient: PolkadotApi, + apiConfig: Configuration, + rawWasm: Buffer; + + beforeAll(async () => { + const pruning = pruneDockerAllIfGithubAction({ logLevel }); + await expect(pruning).toResolve(); + }); + afterAll(async () => { + await ledger.stop(); + await plugin.shutdownConnectionToSubstrate(); + }); + afterAll(async () => { + const pruning = pruneDockerAllIfGithubAction({ logLevel }); + await expect(pruning).resolves.toBeTruthy(); + }); + afterAll(async () => await Servers.shutdown(server)); + beforeAll(async () => { + const ledgerContainer = await ledger.start(); + expect(ledgerContainer).toBeTruthy(); + keychainEntryKey = uuidv4(); + keychainEntryValue = "//Bob"; + 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, + }); + const connectorOptions: IPluginLedgerConnectorPolkadotOptions = { + logLevel: logLevel, + pluginRegistry: new PluginRegistry({ plugins: [keychainPlugin] }), + wsProviderUrl: DEFAULT_WSPROVIDER, + instanceId: instanceId, + }; + const factory = new PluginFactoryLedgerConnector({ + pluginImportType: PluginImportType.Local, + }); + + plugin = await factory.create(connectorOptions); + await plugin.onPluginInit(); + + const listenOptions: IListenOptions = { + hostname: "127.0.0.1", + port: 0, + server, + }; + addressInfo = await Servers.listen(listenOptions); + ({ address, port } = addressInfo); + apiHost = `http://${address}:${port}`; + apiConfig = new Configuration({ basePath: apiHost }); + apiClient = new PolkadotApi(apiConfig); + await plugin.registerWebServices(expressApp); + await plugin.getOrCreateWebServices(); + rawWasm = await fs.readFile( + "packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/fixtures/ink/flipper.wasm", + ); + }); + const proofSize = 131072; + const refTime = 6219235328; + const gasLimit = { + refTime, + proofSize, + }; + test("deploy contract with pre-signed TX", async () => { + const result = apiClient.deployContractInk({ + wasm: rawWasm.toString("base64"), + metadata: JSON.stringify(metadata), + gasLimit: gasLimit, + storageDepositLimit: null, + salt: new Uint8Array().toString(), + web3SigningCredential: { type: Web3SigningCredentialType.None }, + params: [false], + }); + await expect(result).rejects.toHaveProperty(["response", "status"], 400); + }); + test("deploy contract using passing mnemonic string", async () => { + const result = await apiClient.deployContractInk({ + wasm: rawWasm.toString("base64"), + metadata: JSON.stringify(metadata), + gasLimit: gasLimit, + storageDepositLimit: null, + salt: new Uint8Array().toString(), + web3SigningCredential: { + type: Web3SigningCredentialType.MnemonicString, + mnemonic: "//Alice", + }, + params: [false], + }); + expect(result).toBeTruthy(); + expect(result.data.success).toBeTrue; + expect(result.data.contractAddress).toBeTruthy(); + }); + test("deploy contract using passing cactus keychain ref", async () => { + const result = await apiClient.deployContractInk({ + wasm: rawWasm.toString("base64"), + metadata: JSON.stringify(metadata), + gasLimit: gasLimit, + storageDepositLimit: null, + salt: new Uint8Array().toString(), + web3SigningCredential: { + type: Web3SigningCredentialType.CactusKeychainRef, + keychainEntryKey: keychainEntryKey, + keychainId: keychainPlugin.getKeychainId(), + }, + params: [false], + }); + expect(result).toBeTruthy(); + expect(result.data.success).toBeTrue; + expect(result.data.contractAddress).toBeTruthy(); + }); +}); diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/test/typescript/integration/invoke-ink-contract.test.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/test/typescript/integration/invoke-ink-contract.test.ts new file mode 100644 index 00000000000..1a8f60cfb2b --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/test/typescript/integration/invoke-ink-contract.test.ts @@ -0,0 +1,190 @@ +import { + IListenOptions, + LogLevelDesc, + Servers, +} from "@hyperledger/cactus-common"; +import { SubstrateTestLedger } from "../../../../../cactus-test-tooling/src/main/typescript/substrate-test-ledger/substrate-test-ledger"; +import metadata from "../../rust/fixtures/ink/metadata.json"; +import fs from "fs-extra"; +import { v4 as uuidv4 } from "uuid"; +import { pruneDockerAllIfGithubAction } from "@hyperledger/cactus-test-tooling"; +import express from "express"; +import http from "http"; +import { + PluginLedgerConnectorPolkadot, + IPluginLedgerConnectorPolkadotOptions, + DefaultApi as PolkadotApi, + Web3SigningCredentialType, + PolkadotContractInvocationType, + PluginFactoryLedgerConnector, +} from "../../../main/typescript/public-api"; +import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory"; +import { PluginRegistry } from "@hyperledger/cactus-core"; +import { AddressInfo } from "net"; +import { Configuration, PluginImportType } from "@hyperledger/cactus-core-api"; +import "jest-extended"; + +const testCase = "invoke contract with all invocation types"; +describe(testCase, () => { + const logLevel: LogLevelDesc = "TRACE"; + + const DEFAULT_WSPROVIDER = "ws://127.0.0.1:9944"; + const instanceId = "test-polkadot-connector"; + const ledgerOptions = { + publishAllPorts: false, + logLevel: logLevel, + emitContainerLogs: true, + }; + const ledger = new SubstrateTestLedger(ledgerOptions); + const expressApp = express(); + expressApp.use(express.json()); + expressApp.use(express.urlencoded({ extended: false })); + const server = http.createServer(expressApp); + let addressInfo: AddressInfo, + address: string, + port: number, + apiHost: string, + plugin: PluginLedgerConnectorPolkadot, + keychainEntryKey: string, + keychainEntryValue: string, + keychainPlugin: PluginKeychainMemory, + apiClient: PolkadotApi, + apiConfig: Configuration, + contractAddress: string; + + const proofSize = 131072; + const refTime = 6219235328; + const gasLimit = { + refTime, + proofSize, + }; + beforeAll(async () => { + const pruning = pruneDockerAllIfGithubAction({ logLevel }); + await expect(pruning).toResolve(); + }); + afterAll(async () => { + await ledger.stop(); + await plugin.shutdownConnectionToSubstrate(); + }); + afterAll(async () => { + const pruning = pruneDockerAllIfGithubAction({ logLevel }); + await expect(pruning).resolves.toBeTruthy(); + }); + afterAll(async () => await Servers.shutdown(server)); + beforeAll(async () => { + const ledgerContainer = await ledger.start(); + expect(ledgerContainer).toBeTruthy(); + keychainEntryKey = uuidv4(); + keychainEntryValue = "//Bob"; + 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, + }); + const connectorOptions: IPluginLedgerConnectorPolkadotOptions = { + logLevel: logLevel, + pluginRegistry: new PluginRegistry({ plugins: [keychainPlugin] }), + wsProviderUrl: DEFAULT_WSPROVIDER, + instanceId: instanceId, + }; + + const factory = new PluginFactoryLedgerConnector({ + pluginImportType: PluginImportType.Local, + }); + + plugin = await factory.create(connectorOptions); + await plugin.onPluginInit(); + + const listenOptions: IListenOptions = { + hostname: "127.0.0.1", + port: 0, + server, + }; + addressInfo = await Servers.listen(listenOptions); + ({ address, port } = addressInfo); + apiHost = `http://${address}:${port}`; + apiConfig = new Configuration({ basePath: apiHost }); + apiClient = new PolkadotApi(apiConfig); + await plugin.registerWebServices(expressApp); + await plugin.getOrCreateWebServices(); + }); + beforeAll(async () => { + const rawWasm = await fs.readFile( + "packages/cactus-plugin-ledger-connector-polkadot/src/test/rust/fixtures/ink/flipper.wasm", + ); + const result = await apiClient.deployContractInk({ + wasm: rawWasm.toString("base64"), + metadata: JSON.stringify(metadata), + gasLimit: gasLimit, + storageDepositLimit: null, + salt: new Uint8Array().toString(), + web3SigningCredential: { + type: Web3SigningCredentialType.MnemonicString, + mnemonic: "//Alice", + }, + params: [false], + }); + expect(result).toBeTruthy(); + expect(result.data.success).toBeTrue; + expect(result.data.contractAddress).toBeTruthy(); + if (!result.data.contractAddress) { + throw new Error("contract address cannot be undefined"); + } + contractAddress = result.data.contractAddress; + }); + test("invalid methodName contract invocation", async () => { + const result = apiClient.invokeContract({ + invocationType: PolkadotContractInvocationType.Send, + contractAddress, + gasLimit, + metadata: JSON.stringify(metadata), + methodName: "invalid", + accountAddress: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", //Alice account address + web3SigningCredential: { + type: Web3SigningCredentialType.CactusKeychainRef, + keychainEntryKey: keychainEntryKey, + keychainId: keychainPlugin.getKeychainId(), + }, + }); + await expect(result).rejects.toHaveProperty(["response", "status"], 400); + }); + test("query ink! contract", async () => { + const result = await apiClient.invokeContract({ + invocationType: PolkadotContractInvocationType.Query, + contractAddress, + gasLimit, + metadata: JSON.stringify(metadata), + methodName: "get", + accountAddress: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", //Alice account address + web3SigningCredential: { + type: Web3SigningCredentialType.None, + }, + }); + expect(result).toBeTruthy(); + expect(result.data.success).toBeTrue; + expect(result.data.callOutput).toBeTruthy(); + }); + test("flip() invocation", async () => { + const result = await apiClient.invokeContract({ + invocationType: PolkadotContractInvocationType.Send, + contractAddress, + gasLimit, + metadata: JSON.stringify(metadata), + methodName: "flip", + accountAddress: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", //Alice account address + web3SigningCredential: { + type: Web3SigningCredentialType.CactusKeychainRef, + keychainEntryKey: keychainEntryKey, + keychainId: keychainPlugin.getKeychainId(), + }, + }); + expect(result).toBeTruthy(); + expect(result.data.success).toBeTrue; + expect(result.data.blockHash).toBeTruthy(); + expect(result.data.txHash).toBeTruthy(); + }); +}); diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/test/typescript/integration/run-transaction.test.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/test/typescript/integration/run-transaction.test.ts new file mode 100644 index 00000000000..5375995cc9e --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/test/typescript/integration/run-transaction.test.ts @@ -0,0 +1,244 @@ +import { + IListenOptions, + LogLevelDesc, + Servers, +} from "@hyperledger/cactus-common"; +import { SubstrateTestLedger } from "../../../../../cactus-test-tooling/src/main/typescript/substrate-test-ledger/substrate-test-ledger"; +import { v4 as uuidv4 } from "uuid"; +import { pruneDockerAllIfGithubAction } from "@hyperledger/cactus-test-tooling"; +import express from "express"; +import http from "http"; +import { + PluginLedgerConnectorPolkadot, + IPluginLedgerConnectorPolkadotOptions, + DefaultApi as PolkadotApi, + Web3SigningCredentialType, + PluginFactoryLedgerConnector, +} from "../../../main/typescript/public-api"; +import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory"; +import { PluginRegistry } from "@hyperledger/cactus-core"; +import { AddressInfo } from "net"; +import { Configuration, PluginImportType } from "@hyperledger/cactus-core-api"; +import { Keyring } from "@polkadot/api"; +import { K_CACTUS_POLKADOT_TOTAL_TX_COUNT } from "../../../main/typescript/prometheus-exporter/metrics"; +import "jest-extended"; + +const testCase = "transact through all available methods"; +describe(testCase, () => { + const logLevel: LogLevelDesc = "TRACE"; + + const DEFAULT_WSPROVIDER = "ws://127.0.0.1:9944"; + const instanceId = "test-polkadot-connector"; + const ledgerOptions = { + publishAllPorts: false, + logLevel: logLevel, + emitContainerLogs: true, + }; + const ledger = new SubstrateTestLedger(ledgerOptions); + const expressApp = express(); + expressApp.use(express.json()); + expressApp.use(express.urlencoded({ extended: false })); + const server = http.createServer(expressApp); + let addressInfo: AddressInfo, + address: string, + port: number, + apiHost: string, + plugin: PluginLedgerConnectorPolkadot, + keychainEntryKey: string, + keychainEntryValue: string, + keychainPlugin: PluginKeychainMemory, + apiClient: PolkadotApi, + apiConfig: Configuration; + + beforeAll(async () => { + const pruning = pruneDockerAllIfGithubAction({ logLevel }); + await expect(pruning).toResolve(); + }); + afterAll(async () => { + await ledger.stop(); + await plugin.shutdownConnectionToSubstrate(); + }); + afterAll(async () => { + const pruning = pruneDockerAllIfGithubAction({ logLevel }); + await expect(pruning).resolves.toBeTruthy(); + }); + afterAll(async () => await Servers.shutdown(server)); + beforeAll(async () => { + const ledgerContainer = await ledger.start(); + expect(ledgerContainer).toBeTruthy(); + keychainEntryKey = uuidv4(); + keychainEntryValue = "//Alice"; + 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, + }); + const connectorOptions: IPluginLedgerConnectorPolkadotOptions = { + logLevel: logLevel, + pluginRegistry: new PluginRegistry({ plugins: [keychainPlugin] }), + wsProviderUrl: DEFAULT_WSPROVIDER, + instanceId: instanceId, + }; + + const factory = new PluginFactoryLedgerConnector({ + pluginImportType: PluginImportType.Local, + }); + + plugin = await factory.create(connectorOptions); + await plugin.onPluginInit(); + + const listenOptions: IListenOptions = { + hostname: "127.0.0.1", + port: 0, + server, + }; + addressInfo = await Servers.listen(listenOptions); + ({ address, port } = addressInfo); + apiHost = `http://${address}:${port}`; + apiConfig = new Configuration({ basePath: apiHost }); + apiClient = new PolkadotApi(apiConfig); + await plugin.registerWebServices(expressApp); + await plugin.getOrCreateWebServices(); + }); + test("transact using pre-signed transaction", async () => { + const keyring = new Keyring({ type: "sr25519" }); + const alicePair = keyring.createFromUri("//Alice"); + const bobPair = keyring.createFromUri("//Bob"); + + const infoForSigningTransaction = await apiClient.getTransactionInfo({ + accountAddress: alicePair.address, + transactionExpiration: 500, + }); + expect(infoForSigningTransaction.status).toEqual(200); + const response = infoForSigningTransaction.data; + expect(response).toBeTruthy(); + const nonce = response.responseContainer.response_data.nonce; + expect(nonce).toBeTruthy(); + const blockHash = response.responseContainer.response_data.blockHash; + expect(blockHash).toBeTruthy(); + const era = response.responseContainer.response_data.era; + expect(era).toBeTruthy(); + + const signingOptions = { + nonce: nonce, + blockHash: blockHash, + era: era, + }; + + const transaction = await apiClient.getRawTransaction({ + to: bobPair.address, + value: 20, + }); + expect(transaction).toBeTruthy(); + expect( + transaction.data.responseContainer.response_data.rawTransaction, + ).toBeTruthy(); + const rawTransaction = + transaction.data.responseContainer.response_data.rawTransaction; + + const signedTransactionResponse = await apiClient.signRawTransaction({ + rawTransaction: rawTransaction, + mnemonic: "//Alice", + signingOptions: signingOptions, + }); + expect(signedTransactionResponse.data.success).toBeTrue(); + expect(signedTransactionResponse.data.signedTransaction).toBeTruthy(); + const signedTransaction = signedTransactionResponse.data.signedTransaction; + const TransactionDetails = await apiClient.runTransaction({ + web3SigningCredential: { type: Web3SigningCredentialType.None }, + transactionConfig: { + transferSubmittable: signedTransaction, + }, + }); + expect(TransactionDetails.status).toEqual(200); + const transactionResponse = TransactionDetails.data; + expect(transactionResponse).toBeTruthy(); + expect(transactionResponse.success).toBeTrue(); + expect(transactionResponse.txHash).toBeTruthy(); + expect(transactionResponse.blockHash).toBeTruthy(); + }); + + test("transact by omiting mnemonic string", async () => { + const keyring = new Keyring({ type: "sr25519" }); + const bobPair = keyring.createFromUri("//Bob"); + const TransactionDetails = apiClient.runTransaction({ + web3SigningCredential: { + type: Web3SigningCredentialType.MnemonicString, + mnemonic: "", + }, + transactionConfig: { + to: bobPair.address, + value: 30, + }, + }); + + await expect(TransactionDetails).rejects.toHaveProperty( + ["response", "status"], + 400, + ); + }); + test("transact using passing mnemonic string", async () => { + const keyring = new Keyring({ type: "sr25519" }); + const bobPair = keyring.createFromUri("//Bob"); + const TransactionDetails = await apiClient.runTransaction({ + web3SigningCredential: { + type: Web3SigningCredentialType.MnemonicString, + mnemonic: "//Alice", + }, + transactionConfig: { + to: bobPair.address, + value: 30, + }, + }); + expect(TransactionDetails.status).toEqual(200); + const transactionResponse = TransactionDetails.data; + expect(transactionResponse).toBeTruthy(); + expect(transactionResponse.success).toBeTrue(); + expect(transactionResponse.txHash).toBeTruthy(); + expect(transactionResponse.blockHash).toBeTruthy(); + }); + test("transact using passing cactus keychain ref", async () => { + const keyring = new Keyring({ type: "sr25519" }); + const bobPair = keyring.createFromUri("//Bob"); + const TransactionDetails = await apiClient.runTransaction({ + web3SigningCredential: { + type: Web3SigningCredentialType.CactusKeychainRef, + keychainEntryKey: keychainEntryKey, + keychainId: keychainPlugin.getKeychainId(), + }, + transactionConfig: { + to: bobPair.address, + value: 30, + }, + }); + expect(TransactionDetails.status).toEqual(200); + const transactionResponse = TransactionDetails.data; + expect(transactionResponse).toBeTruthy(); + expect(transactionResponse.success).toBeTrue(); + expect(transactionResponse.txHash).toBeTruthy(); + expect(transactionResponse.blockHash).toBeTruthy(); + }); + + test("get prometheus exporter metrics", async () => { + const res = await apiClient.getPrometheusMetrics(); + const promMetricsOutput = + "# HELP " + + K_CACTUS_POLKADOT_TOTAL_TX_COUNT + + " Total transactions executed\n" + + "# TYPE " + + K_CACTUS_POLKADOT_TOTAL_TX_COUNT + + " gauge\n" + + K_CACTUS_POLKADOT_TOTAL_TX_COUNT + + '{type="' + + K_CACTUS_POLKADOT_TOTAL_TX_COUNT + + '"} 3'; + 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-polkadot/src/test/typescript/unit/api-surface.test.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/test/typescript/unit/api-surface.test.ts new file mode 100644 index 00000000000..84b652f4a78 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/test/typescript/unit/api-surface.test.ts @@ -0,0 +1,7 @@ +import "jest-extended"; + +import * as apiSurface from "../../../main/typescript/public-api"; + +test("Library can be loaded", async () => { + expect(apiSurface).toBeTruthy(); +}); diff --git a/packages/cactus-plugin-ledger-connector-polkadot/src/test/typescript/unit/constructor-instantiation.test.ts b/packages/cactus-plugin-ledger-connector-polkadot/src/test/typescript/unit/constructor-instantiation.test.ts new file mode 100644 index 00000000000..9ce0eb5370a --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/src/test/typescript/unit/constructor-instantiation.test.ts @@ -0,0 +1,57 @@ +import { PrometheusExporter } from "../../../main/typescript/prometheus-exporter/prometheus-exporter"; +import { LogLevelDesc } from "@hyperledger/cactus-common"; +import { SubstrateTestLedger } from "../../../../../cactus-test-tooling/src/main/typescript/substrate-test-ledger/substrate-test-ledger"; +import { pruneDockerAllIfGithubAction } from "@hyperledger/cactus-test-tooling"; +import { + PluginLedgerConnectorPolkadot, + IPluginLedgerConnectorPolkadotOptions, + PluginFactoryLedgerConnector, +} from "../../../main/typescript"; +import { PluginRegistry } from "@hyperledger/cactus-core"; +import "jest-extended"; +import { PluginImportType } from "@hyperledger/cactus-core-api"; + +const testCase = "Instantiate plugin"; +const logLevel: LogLevelDesc = "TRACE"; +const DEFAULT_WSPROVIDER = "ws://127.0.0.1:9944"; +const instanceId = "test-polkadot-connector"; +const prometheus: PrometheusExporter = new PrometheusExporter({ + pollingIntervalInMin: 1, +}); + +describe(testCase, () => { + let plugin: PluginLedgerConnectorPolkadot; + const connectorOptions: IPluginLedgerConnectorPolkadotOptions = { + logLevel: logLevel, + prometheusExporter: prometheus, + pluginRegistry: new PluginRegistry({ plugins: [] }), + wsProviderUrl: DEFAULT_WSPROVIDER, + instanceId: instanceId, + }; + const ledgerOptions = { + publishAllPorts: false, + logLevel: logLevel, + emitContainerLogs: true, + }; + const ledger = new SubstrateTestLedger(ledgerOptions); + const factory = new PluginFactoryLedgerConnector({ + pluginImportType: PluginImportType.Local, + }); + + beforeAll(async () => { + const pruning = pruneDockerAllIfGithubAction({ logLevel }); + await expect(pruning).toResolve(); + }); + afterAll(async () => { + await ledger.stop(); + await plugin.shutdownConnectionToSubstrate(); + await pruneDockerAllIfGithubAction({ logLevel }); + }); + test(testCase, async () => { + await ledger.start(); + expect(ledger).toBeTruthy(); + plugin = await factory.create(connectorOptions); + await plugin.onPluginInit(); + await plugin.getOrCreateWebServices(); + }); +}); diff --git a/packages/cactus-plugin-ledger-connector-polkadot/tsconfig.json b/packages/cactus-plugin-ledger-connector-polkadot/tsconfig.json new file mode 100644 index 00000000000..8d5d434fb1d --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-polkadot/tsconfig.json @@ -0,0 +1,32 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./dist/lib/", + "declarationDir": "dist/lib", + "resolveJsonModule": true, + "rootDir": "./src", + "tsBuildInfoFile": "../../.build-cache/cactus-plugin-ledger-connector-polkadot.tsbuildinfo" + }, + "include": [ + "./src", + "src/**/*.json" + ], + "references": [ + { + "path": "../cactus-common/tsconfig.json" + }, + { + "path": "../cactus-core/tsconfig.json" + }, + { + "path": "../cactus-core-api/tsconfig.json" + }, + { + "path": "../cactus-plugin-keychain-memory/tsconfig.json" + }, + { + "path": "../cactus-test-tooling/tsconfig.json" + } + ] +} \ No newline at end of file diff --git a/packages/cactus-test-tooling/src/main/typescript/substrate-test-ledger/substrate-test-ledger.ts b/packages/cactus-test-tooling/src/main/typescript/substrate-test-ledger/substrate-test-ledger.ts index 784070a1c79..560ab29d4d9 100644 --- a/packages/cactus-test-tooling/src/main/typescript/substrate-test-ledger/substrate-test-ledger.ts +++ b/packages/cactus-test-tooling/src/main/typescript/substrate-test-ledger/substrate-test-ledger.ts @@ -56,9 +56,8 @@ export class SubstrateTestLedger { this.publishAllPorts = opts.publishAllPorts; this._containerId = Optional.empty(); - this.imageName = - opts.imageName || "ghcr.io/hyperledger/cactus-substrate-all-in-one"; - this.imageTag = opts.imageTag || "2021-09-24---feat-1274"; + this.imageName = opts.imageName || "ghcr.io/hyperledger/cactus-substrate-all-in-one"; + this.imageTag = opts.imageTag || "2024-01-16-pr-2877"; this.imageFqn = `${this.imageName}:${this.imageTag}`; this.envVars = opts.envVars || new Map(); this.emitContainerLogs = Bools.isBooleanStrict(opts.emitContainerLogs) @@ -104,7 +103,7 @@ export class SubstrateTestLedger { Healthcheck: { Test: [ "CMD-SHELL", - `rustup --version && rustc --version && cargo --version`, + `(echo '{"id":1,"jsonrpc":"2.0","method":"system_health","params":[]}' | websocat -n1 ws://127.0.0.1:9944) > /dev/null; echo $?`, ], Interval: 1000000000, // 1 second Timeout: 3000000000, // 3 seconds diff --git a/packages/cactus-test-tooling/src/test/typescript/integration/substrate/substrate-test-ledger-constructor.test.ts b/packages/cactus-test-tooling/src/test/typescript/integration/substrate/substrate-test-ledger-constructor.test.ts index 9da9d4f3c4f..f2da95a35a2 100644 --- a/packages/cactus-test-tooling/src/test/typescript/integration/substrate/substrate-test-ledger-constructor.test.ts +++ b/packages/cactus-test-tooling/src/test/typescript/integration/substrate/substrate-test-ledger-constructor.test.ts @@ -17,16 +17,6 @@ test(testCase, async (t: Test) => { publishAllPorts: true, logLevel: logLevel, emitContainerLogs: true, - imageName: "ghcr.io/hyperledger/cactus-substrate-all-in-one", - imageTag: "2022-03-29--1496", - envVars: new Map([ - ["WORKING_DIR", "/var/www/node-template"], - ["CONTAINER_NAME", "contracts-node-template-cactus"], - ["WS_PORT", "9944"], - ["PORT", "9944"], - ["DOCKER_PORT", "9944"], - ["CARGO_HOME", "/var/www/node-template/.cargo"], - ]), }; const ledger = new SubstrateTestLedger(options); diff --git a/packages/cactus-test-tooling/src/test/typescript/integration/substrate/substrate-test-ledger-multiple-concurrent.test.ts b/packages/cactus-test-tooling/src/test/typescript/integration/substrate/substrate-test-ledger-multiple-concurrent.test.ts index dbc4c1c501a..f89eba6197d 100644 --- a/packages/cactus-test-tooling/src/test/typescript/integration/substrate/substrate-test-ledger-multiple-concurrent.test.ts +++ b/packages/cactus-test-tooling/src/test/typescript/integration/substrate/substrate-test-ledger-multiple-concurrent.test.ts @@ -17,15 +17,10 @@ test(testCase, async (t: Test) => { publishAllPorts: true, logLevel: logLevel, emitContainerLogs: true, - imageName: "ghcr.io/hyperledger/cactus-substrate-all-in-one", - imageTag: "2022-03-29--1496", envVars: new Map([ - ["WORKING_DIR", "/var/www/node-template"], - ["CONTAINER_NAME", "contracts-node-template-cactus"], ["WS_PORT", "9945"], ["PORT", "9944"], ["DOCKER_PORT", "9945"], - ["CARGO_HOME", "/var/www/node-template/.cargo"], ]), }; @@ -35,15 +30,10 @@ test(testCase, async (t: Test) => { publishAllPorts: true, logLevel: logLevel, emitContainerLogs: true, - imageName: "ghcr.io/hyperledger/cactus-substrate-all-in-one", - imageTag: "2022-03-29--1496", envVars: new Map([ - ["WORKING_DIR", "/var/www/node-template"], - ["CONTAINER_NAME", "contracts-node-template-cactus"], ["WS_PORT", "9947"], ["PORT", "9946"], ["DOCKER_PORT", "9947"], - ["CARGO_HOME", "/var/www/node-template/.cargo"], ]), }; diff --git a/tools/docker/substrate-all-in-one/Dockerfile b/tools/docker/substrate-all-in-one/Dockerfile index 3a595f59043..ebac55bf10b 100644 --- a/tools/docker/substrate-all-in-one/Dockerfile +++ b/tools/docker/substrate-all-in-one/Dockerfile @@ -1,48 +1,31 @@ -FROM paritytech/ci-linux:production -LABEL AUTHORS="Rafael Belchior, Catarina Pedreira" -LABEL VERSION="2021-11-01" -LABEL org.opencontainers.image.source=https://github.com/hyperledger/cactus +FROM paritytech/ci-linux:production as builder -WORKDIR / -ENV WORKING_DIR=/var/www/node-template -ENV CONTAINER_NAME=contracts-node-template-cactus -# Specify p2p protocol TCP port -ENV PORT=9614 +RUN cargo install contracts-node -# Specify HTTP RPC server TCP port -ENV RPC_PORT=9618 +## second stage +FROM ubuntu:20.04 -#Specify WebSockets RPC server TCP port -ENV WS_PORT=9944 +COPY --from=builder /usr/local/cargo/bin/substrate-contracts-node /usr/local/bin/ -ENV DOCKER_PORT=9944 -ENV CARGO_HOME=/var/www/node-template/.cargo -ENV CACTUS_CFG_PATH=/etc/hyperledger/cactus +RUN apt-get update && apt-get install -y supervisor \ + && apt-get install wget -y \ + && wget -qO /usr/local/bin/websocat https://github.com/vi/websocat/releases/download/v1.11.0/websocat.x86_64-unknown-linux-musl \ + && chmod a+x /usr/local/bin/websocat -VOLUME .:/var/www/node-template +COPY ./healthcheck.sh /usr/bin/ -RUN apt update +RUN chmod a+x /usr/bin/healthcheck.sh -# Get ubuntu and rust packages -RUN apt install -y build-essential pkg-config git clang curl libssl-dev llvm libudev-dev +HEALTHCHECK --interval=10s --timeout=10s --start-period=30s --retries=10 \ + CMD ["/usr/bin/healthcheck.sh"] -ENV CACTUS_CFG_PATH=/etc/hyperledger/cactus -RUN mkdir -p $CACTUS_CFG_PATH +# for substrate node +EXPOSE 9944 +# for supervisord +EXPOSE 9001 -RUN set -e +COPY supervisord.conf /etc/supervisord.conf -RUN echo "*** Instaling Rust environment ***" -RUN curl https://sh.rustup.rs -y -sSf | sh -RUN echo 'source $HOME/.cargo/env' >> $HOME/.bashrc -RUN rustup default nightly +ENTRYPOINT ["/usr/bin/supervisord"] -RUN echo "*** Initializing WASM build environment" -RUN rustup target add wasm32-unknown-unknown --toolchain nightly - -RUN echo "*** Installing Substrate node environment ***" -RUN cargo install contracts-node --git https://github.com/paritytech/substrate-contracts-node.git --force --locked - -COPY start.sh / - -RUN echo "*** Start Substrate node template ***" -CMD /start.sh +CMD ["--configuration", "/etc/supervisord.conf", "--nodaemon"] diff --git a/tools/docker/substrate-all-in-one/README.md b/tools/docker/substrate-all-in-one/README.md index 0028ebfd961..9aa2a5ca119 100644 --- a/tools/docker/substrate-all-in-one/README.md +++ b/tools/docker/substrate-all-in-one/README.md @@ -14,23 +14,11 @@ This is the test ledger used by the `polkadot-connector` package. To run the test ledger, use: ```sh -docker run --expose HOST_PORT --env WS_PORT=WS_PORT --env WORKING_DIR=/var/www/node-template --env CONTAINER_NAME=contracts-node-template-ovl --env CARGO_HOME=/var/www/node-template/.cargo -p HOST_PORT:WS_PORT rafaelapb/cactus-substrate-aio:2021-11-02-fix - - - -``` - -Example: - - -``` -docker run --expose 9946 --env WS_PORT=9947 --env WORKING_DIR=/var/www/node-template --env CONTAINER_NAME=contracts-node-template-ovl --env CARGO_HOME=/var/www/node-template/.cargo -p 9940:9947 rafaelapb/cactus-substrate-aio:2021-11-02-fix - - +docker run -p 9944:9944 anmol02/cactus-substrate-aio:2023-10-28 ``` ## Build ```sh -DOCKER_BUILDKIT=1 docker build ./tools/docker/substrate-all-in-one/ -f ./tools/docker/substrate-all-in-one/Dockerfile --tag saio +docker build ./tools/docker/substrate-all-in-one/ -f ./tools/docker/substrate-all-in-one/Dockerfile --tag saio ``` diff --git a/tools/docker/substrate-all-in-one/healthcheck.sh b/tools/docker/substrate-all-in-one/healthcheck.sh new file mode 100755 index 00000000000..eb436e45a3b --- /dev/null +++ b/tools/docker/substrate-all-in-one/healthcheck.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env sh + +# Fail on first wrong command +set -e + +# health check command +(echo '{"id":1,"jsonrpc":"2.0","method":"system_health","params":[]}' | websocat -n1 ws://127.0.0.1:9944) > /dev/null; + + echo $? diff --git a/tools/docker/substrate-all-in-one/start.sh b/tools/docker/substrate-all-in-one/start.sh deleted file mode 100755 index cde8da5efda..00000000000 --- a/tools/docker/substrate-all-in-one/start.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -set -euxo pipefail - -echo "ENV:PORT=${PORT}" -echo "ENV:WS_PORT=${WS_PORT}" - -exec /var/www/node-template/.cargo/bin/substrate-contracts-node --dev --port ${PORT} --ws-port ${WS_PORT} diff --git a/tools/docker/substrate-all-in-one/supervisord.conf b/tools/docker/substrate-all-in-one/supervisord.conf new file mode 100644 index 00000000000..05652e80a64 --- /dev/null +++ b/tools/docker/substrate-all-in-one/supervisord.conf @@ -0,0 +1,22 @@ +[supervisord] +logfile = /var/log/supervisord.log +logfile_maxbytes = 50MB +logfile_backups=10 +loglevel = info + +[rpcinterface:supervisor] +supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface + +[supervisorctl] +serverurl=http://127.0.0.1:9001/ + +[inet_http_server] +port = 0.0.0.0:9001 + +[program:polkadot] +command=/usr/local/bin/substrate-contracts-node --dev --rpc-external -ldebug +autostart=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 9239b9d4fb4..27a1cb17672 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -55,6 +55,9 @@ { "path": "./packages/cactus-plugin-ledger-connector-besu/tsconfig.json" }, + { + "path": "./packages/cactus-plugin-ledger-connector-polkadot/tsconfig.json" + }, { "path": "./packages/cactus-plugin-ledger-connector-corda/tsconfig.json" }, diff --git a/yarn.lock b/yarn.lock index a590a39b7e4..13f0a98671e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -440,6 +440,18 @@ __metadata: languageName: node linkType: hard +"@apidevtools/json-schema-ref-parser@npm:9.0.9": + version: 9.0.9 + resolution: "@apidevtools/json-schema-ref-parser@npm:9.0.9" + dependencies: + "@jsdevtools/ono": ^7.1.3 + "@types/json-schema": ^7.0.6 + call-me-maybe: ^1.0.1 + js-yaml: ^4.1.0 + checksum: b21f6bdd37d2942c3967ee77569bc74fadd1b922f688daf5ef85057789a2c3a7f4afc473aa2f3a93ec950dabb6ef365f8bd9cf51e4e062a1ee1e59b989f8f9b4 + languageName: node + linkType: hard + "@apidevtools/json-schema-ref-parser@npm:^9.1.2": version: 9.1.2 resolution: "@apidevtools/json-schema-ref-parser@npm:9.1.2" @@ -5979,7 +5991,7 @@ __metadata: languageName: node linkType: hard -"@eslint/eslintrc@npm:^0.4.3": +"@eslint/eslintrc@npm:^0.4.0, @eslint/eslintrc@npm:^0.4.3": version: 0.4.3 resolution: "@eslint/eslintrc@npm:0.4.3" dependencies: @@ -8754,6 +8766,51 @@ __metadata: languageName: unknown linkType: soft +"@hyperledger/cactus-plugin-ledger-connector-polkadot@workspace:packages/cactus-plugin-ledger-connector-polkadot": + version: 0.0.0-use.local + resolution: "@hyperledger/cactus-plugin-ledger-connector-polkadot@workspace:packages/cactus-plugin-ledger-connector-polkadot" + dependencies: + "@hyperledger/cactus-common": 2.0.0-alpha.2 + "@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-tooling": 2.0.0-alpha.2 + "@polkadot/api": 10.9.1 + "@polkadot/api-contract": 10.9.1 + "@polkadot/rpc-provider": 10.9.1 + "@polkadot/types": 10.9.1 + "@polkadot/util": 12.6.2 + "@types/express": 4.17.19 + "@types/http-errors": 2.0.4 + "@types/joi": 14.3.4 + "@types/multer": 1.4.7 + "@types/ssh2": 0.5.44 + "@types/supertest": 2.0.11 + "@types/temp": 0.9.1 + "@types/uuid": 8.3.1 + axios: 0.22.0 + bl: 5.0.0 + eslint: 7.21.0 + express: 4.17.1 + express-openapi-validator: 4.13.1 + form-data: 4.0.0 + fs-extra: 11.2.0 + http-errors: 2.0.0 + http-status-codes: 2.1.4 + joi: 14.3.1 + multer: 1.4.2 + ngo: 2.6.2 + openapi-types: 9.1.0 + prom-client: 13.2.0 + run-time-error: 1.4.0 + supertest: 6.1.6 + temp: 0.9.1 + tslint: 6.1.3 + typescript-optional: 2.0.1 + uuid: 8.3.2 + languageName: unknown + linkType: soft + "@hyperledger/cactus-plugin-ledger-connector-quorum@2.0.0-alpha.2, @hyperledger/cactus-plugin-ledger-connector-quorum@workspace:packages/cactus-plugin-ledger-connector-quorum": version: 0.0.0-use.local resolution: "@hyperledger/cactus-plugin-ledger-connector-quorum@workspace:packages/cactus-plugin-ledger-connector-quorum" @@ -11343,6 +11400,15 @@ __metadata: languageName: node linkType: hard +"@noble/curves@npm:^1.3.0": + version: 1.3.0 + resolution: "@noble/curves@npm:1.3.0" + dependencies: + "@noble/hashes": 1.3.3 + checksum: b65342ee66c4a440eee2978524412eabba9a9efdd16d6370e15218c6a7d80bddf35e66bb57ed52c0dfd32cb9a717b439ab3a72db618f1a0066dfebe3fd12a421 + languageName: node + linkType: hard + "@noble/ed25519@npm:^1.6.0": version: 1.7.3 resolution: "@noble/ed25519@npm:1.7.3" @@ -11378,6 +11444,13 @@ __metadata: languageName: node linkType: hard +"@noble/hashes@npm:1.3.3, @noble/hashes@npm:^1.3.3": + version: 1.3.3 + resolution: "@noble/hashes@npm:1.3.3" + checksum: 8a6496d1c0c64797339bc694ad06cdfaa0f9e56cd0c3f68ae3666cfb153a791a55deb0af9c653c7ed2db64d537aa3e3054629740d2f2338bb1dcb7ab60cd205b + languageName: node + linkType: hard + "@noble/secp256k1@npm:1.7.1, @noble/secp256k1@npm:^1.5.4, @noble/secp256k1@npm:~1.7.0": version: 1.7.1 resolution: "@noble/secp256k1@npm:1.7.1" @@ -12238,6 +12311,433 @@ __metadata: languageName: node linkType: hard +"@polkadot/api-augment@npm:10.9.1": + version: 10.9.1 + resolution: "@polkadot/api-augment@npm:10.9.1" + dependencies: + "@polkadot/api-base": 10.9.1 + "@polkadot/rpc-augment": 10.9.1 + "@polkadot/types": 10.9.1 + "@polkadot/types-augment": 10.9.1 + "@polkadot/types-codec": 10.9.1 + "@polkadot/util": ^12.3.1 + tslib: ^2.5.3 + checksum: b0aeed5ebf640c58a252a29a33f12d4c39d0dcdf10b875501012c3b4b05955ed8be85efbf75e17ad237a561e1171821979ffdddf7e6a64cb0806badb2752c190 + languageName: node + linkType: hard + +"@polkadot/api-base@npm:10.9.1": + version: 10.9.1 + resolution: "@polkadot/api-base@npm:10.9.1" + dependencies: + "@polkadot/rpc-core": 10.9.1 + "@polkadot/types": 10.9.1 + "@polkadot/util": ^12.3.1 + rxjs: ^7.8.1 + tslib: ^2.5.3 + checksum: a761f4ade747a295c16b7e6f24c1bb93e1736aa7fa9f1cb3c651c84d02a99cc62658e83326fa339882423966a55bf0046b74a69a1a4e4567c8d6c1c4db4eb306 + languageName: node + linkType: hard + +"@polkadot/api-contract@npm:10.9.1": + version: 10.9.1 + resolution: "@polkadot/api-contract@npm:10.9.1" + dependencies: + "@polkadot/api": 10.9.1 + "@polkadot/api-augment": 10.9.1 + "@polkadot/types": 10.9.1 + "@polkadot/types-codec": 10.9.1 + "@polkadot/types-create": 10.9.1 + "@polkadot/util": ^12.3.1 + "@polkadot/util-crypto": ^12.3.1 + rxjs: ^7.8.1 + tslib: ^2.5.3 + checksum: 2a4b4818e5fd4ef1b385a0ad1144d986b449b531c278ccc3a441770c1e95a2fd24cd5da401d56040dcd594254e2a5c2e4a09a9de5827ee8bb951e3aad9558bed + languageName: node + linkType: hard + +"@polkadot/api-derive@npm:10.9.1": + version: 10.9.1 + resolution: "@polkadot/api-derive@npm:10.9.1" + dependencies: + "@polkadot/api": 10.9.1 + "@polkadot/api-augment": 10.9.1 + "@polkadot/api-base": 10.9.1 + "@polkadot/rpc-core": 10.9.1 + "@polkadot/types": 10.9.1 + "@polkadot/types-codec": 10.9.1 + "@polkadot/util": ^12.3.1 + "@polkadot/util-crypto": ^12.3.1 + rxjs: ^7.8.1 + tslib: ^2.5.3 + checksum: 072a43bcc55787beb6c29afe0f011c03cdde3a9b6ac38d972d0b13ff93a1e14198d769a926edfd324c3947735dd8c8fcb7a61629409322230fd8559e7c17a1d7 + languageName: node + linkType: hard + +"@polkadot/api@npm:10.9.1": + version: 10.9.1 + resolution: "@polkadot/api@npm:10.9.1" + dependencies: + "@polkadot/api-augment": 10.9.1 + "@polkadot/api-base": 10.9.1 + "@polkadot/api-derive": 10.9.1 + "@polkadot/keyring": ^12.3.1 + "@polkadot/rpc-augment": 10.9.1 + "@polkadot/rpc-core": 10.9.1 + "@polkadot/rpc-provider": 10.9.1 + "@polkadot/types": 10.9.1 + "@polkadot/types-augment": 10.9.1 + "@polkadot/types-codec": 10.9.1 + "@polkadot/types-create": 10.9.1 + "@polkadot/types-known": 10.9.1 + "@polkadot/util": ^12.3.1 + "@polkadot/util-crypto": ^12.3.1 + eventemitter3: ^5.0.1 + rxjs: ^7.8.1 + tslib: ^2.5.3 + checksum: 6b37d9bacf0599bb7c385ddefca929547299a6f1d242ce3215f8480672297c81ec30c251bc9aac3889c5956bd9ef3918d69364819861eec308f4aa347c08110d + languageName: node + linkType: hard + +"@polkadot/keyring@npm:^12.3.1": + version: 12.6.2 + resolution: "@polkadot/keyring@npm:12.6.2" + dependencies: + "@polkadot/util": 12.6.2 + "@polkadot/util-crypto": 12.6.2 + tslib: ^2.6.2 + peerDependencies: + "@polkadot/util": 12.6.2 + "@polkadot/util-crypto": 12.6.2 + checksum: b8591690cdd2b9c0fea5de88efe0be19190466572ecb696cc284eec61343b1a2fe0b61a7cfad54933730a132f65d7444619bcb2c8620f38bc0246bfaaa5026f4 + languageName: node + linkType: hard + +"@polkadot/networks@npm:12.6.2, @polkadot/networks@npm:^12.3.1": + version: 12.6.2 + resolution: "@polkadot/networks@npm:12.6.2" + dependencies: + "@polkadot/util": 12.6.2 + "@substrate/ss58-registry": ^1.44.0 + tslib: ^2.6.2 + checksum: 7f3dbdd02d8429f82c36ce284ca279af663d45c1a40ce4ce1e38ec2a06fc9d6d27c66d374f32b91ae3058257f33d60701481c9e95ceab19bd2eb70d83465b026 + languageName: node + linkType: hard + +"@polkadot/rpc-augment@npm:10.9.1": + version: 10.9.1 + resolution: "@polkadot/rpc-augment@npm:10.9.1" + dependencies: + "@polkadot/rpc-core": 10.9.1 + "@polkadot/types": 10.9.1 + "@polkadot/types-codec": 10.9.1 + "@polkadot/util": ^12.3.1 + tslib: ^2.5.3 + checksum: 4f7b090be6d88ef6a56679a80da856bf007994e2142e16fbac6030132789b5a2411421650935ed4b18334afca399edfc0387135731836c6d9f8420acf510f11b + languageName: node + linkType: hard + +"@polkadot/rpc-core@npm:10.9.1": + version: 10.9.1 + resolution: "@polkadot/rpc-core@npm:10.9.1" + dependencies: + "@polkadot/rpc-augment": 10.9.1 + "@polkadot/rpc-provider": 10.9.1 + "@polkadot/types": 10.9.1 + "@polkadot/util": ^12.3.1 + rxjs: ^7.8.1 + tslib: ^2.5.3 + checksum: 538a207f5d321b4b18b0580da438598dd78e496dbc7069a776abcc39ede36903981ba2b9897eea73ecfe2f48a4d0cbd5b5cd738b3184f5c333709e6f4603f22a + languageName: node + linkType: hard + +"@polkadot/rpc-provider@npm:10.9.1": + version: 10.9.1 + resolution: "@polkadot/rpc-provider@npm:10.9.1" + dependencies: + "@polkadot/keyring": ^12.3.1 + "@polkadot/types": 10.9.1 + "@polkadot/types-support": 10.9.1 + "@polkadot/util": ^12.3.1 + "@polkadot/util-crypto": ^12.3.1 + "@polkadot/x-fetch": ^12.3.1 + "@polkadot/x-global": ^12.3.1 + "@polkadot/x-ws": ^12.3.1 + "@substrate/connect": 0.7.26 + eventemitter3: ^5.0.1 + mock-socket: ^9.2.1 + nock: ^13.3.1 + tslib: ^2.5.3 + dependenciesMeta: + "@substrate/connect": + optional: true + checksum: 4521ba64a1e69ed323910796a4598755e8101704aae3be33b6c363be4ebb9ea1a99ced17b8cd9fa3ab15abf5900e1055279f532f47b8472e8a143a299bfa046d + languageName: node + linkType: hard + +"@polkadot/types-augment@npm:10.9.1": + version: 10.9.1 + resolution: "@polkadot/types-augment@npm:10.9.1" + dependencies: + "@polkadot/types": 10.9.1 + "@polkadot/types-codec": 10.9.1 + "@polkadot/util": ^12.3.1 + tslib: ^2.5.3 + checksum: d643f83ab0a9498267037d95b878fa4e3b0087882195c3bd609038e8c934a092d9c82f7164ac97989305805aabe0d9186736c50a372498c81c22b3d7f4cfcccb + languageName: node + linkType: hard + +"@polkadot/types-codec@npm:10.9.1": + version: 10.9.1 + resolution: "@polkadot/types-codec@npm:10.9.1" + dependencies: + "@polkadot/util": ^12.3.1 + "@polkadot/x-bigint": ^12.3.1 + tslib: ^2.5.3 + checksum: ac11b770fa4328f55daf6dd78fc8fc4d6906fb0d4b2bf92eaece58332c74f2b178d598a310a6dd068c72856acefddf5f7d23cac56991fa12f61d6853fb73d582 + languageName: node + linkType: hard + +"@polkadot/types-create@npm:10.9.1": + version: 10.9.1 + resolution: "@polkadot/types-create@npm:10.9.1" + dependencies: + "@polkadot/types-codec": 10.9.1 + "@polkadot/util": ^12.3.1 + tslib: ^2.5.3 + checksum: 43f8fbd70a7891d6b49f1edb00b4a918c21924f2c1e44eb81ef7c9327e1fcc7eac65dbc2a9d0e3ba49079fdddda5498115e47f5fd99ec2a91f79c7f305bf553a + languageName: node + linkType: hard + +"@polkadot/types-known@npm:10.9.1": + version: 10.9.1 + resolution: "@polkadot/types-known@npm:10.9.1" + dependencies: + "@polkadot/networks": ^12.3.1 + "@polkadot/types": 10.9.1 + "@polkadot/types-codec": 10.9.1 + "@polkadot/types-create": 10.9.1 + "@polkadot/util": ^12.3.1 + tslib: ^2.5.3 + checksum: 8a3dd0dead1759112b9011c5ff47bf9fa0f5a00d0d5cba841d724494a9434a2f565fad8ab654ae8cc3949a10c28f3966034bfc23e493b7cc373d3532de508953 + languageName: node + linkType: hard + +"@polkadot/types-support@npm:10.9.1": + version: 10.9.1 + resolution: "@polkadot/types-support@npm:10.9.1" + dependencies: + "@polkadot/util": ^12.3.1 + tslib: ^2.5.3 + checksum: f5df33f215f529c33d4fd7ad7d6877a4567954488971c2986da416b6578ccb6d5c6eeadab4602abe0e3ce17373cdd6de0ce6f09529852b6e2fd6bc28b9183f9b + languageName: node + linkType: hard + +"@polkadot/types@npm:10.9.1": + version: 10.9.1 + resolution: "@polkadot/types@npm:10.9.1" + dependencies: + "@polkadot/keyring": ^12.3.1 + "@polkadot/types-augment": 10.9.1 + "@polkadot/types-codec": 10.9.1 + "@polkadot/types-create": 10.9.1 + "@polkadot/util": ^12.3.1 + "@polkadot/util-crypto": ^12.3.1 + rxjs: ^7.8.1 + tslib: ^2.5.3 + checksum: c9b0873b52f33c5d7913bc1e474c67d797411ac592c10af987dfecfee7480aeda02b9fc100ff506bc8af704a7fc239162a8ec7eec580e2e7a62ac7f7b95f3900 + languageName: node + linkType: hard + +"@polkadot/util-crypto@npm:12.6.2, @polkadot/util-crypto@npm:^12.3.1": + version: 12.6.2 + resolution: "@polkadot/util-crypto@npm:12.6.2" + dependencies: + "@noble/curves": ^1.3.0 + "@noble/hashes": ^1.3.3 + "@polkadot/networks": 12.6.2 + "@polkadot/util": 12.6.2 + "@polkadot/wasm-crypto": ^7.3.2 + "@polkadot/wasm-util": ^7.3.2 + "@polkadot/x-bigint": 12.6.2 + "@polkadot/x-randomvalues": 12.6.2 + "@scure/base": ^1.1.5 + tslib: ^2.6.2 + peerDependencies: + "@polkadot/util": 12.6.2 + checksum: 63d4bd9bdc3a7089a0a68555cd6a510b8da3cfab142a8f96ba4b43d5d1db2a543433079bc88c2daf15a329d19ba2cc60f6cca6dbebaefd25e96169cb6343794b + languageName: node + linkType: hard + +"@polkadot/util@npm:12.6.2, @polkadot/util@npm:^12.3.1": + version: 12.6.2 + resolution: "@polkadot/util@npm:12.6.2" + dependencies: + "@polkadot/x-bigint": 12.6.2 + "@polkadot/x-global": 12.6.2 + "@polkadot/x-textdecoder": 12.6.2 + "@polkadot/x-textencoder": 12.6.2 + "@types/bn.js": ^5.1.5 + bn.js: ^5.2.1 + tslib: ^2.6.2 + checksum: a42a226f3c299026458d82e48516abf59c1cd8638167edaa3fc1a17aec0ebab203e0ad68a096a4a4fa188afd55093535a98e5083d682a79242a3c5ad79342599 + languageName: node + linkType: hard + +"@polkadot/wasm-bridge@npm:7.3.2": + version: 7.3.2 + resolution: "@polkadot/wasm-bridge@npm:7.3.2" + dependencies: + "@polkadot/wasm-util": 7.3.2 + tslib: ^2.6.2 + peerDependencies: + "@polkadot/util": "*" + "@polkadot/x-randomvalues": "*" + checksum: 8c68b78cbd62347ebdf3fa66f2ffd1f7e883df71d770f5099ff652b083a79f1d7e9e7826a6acd8e986e9da0b07c0170a3f77b6a35726c6b24d856e3f8d08d201 + languageName: node + linkType: hard + +"@polkadot/wasm-crypto-asmjs@npm:7.3.2": + version: 7.3.2 + resolution: "@polkadot/wasm-crypto-asmjs@npm:7.3.2" + dependencies: + tslib: ^2.6.2 + peerDependencies: + "@polkadot/util": "*" + checksum: 669ea001565301f9b1a8feecb0e301c854fc318e5605316b57be7e83d717e7ee8ac460001cd44b18075a3d028c32c4a605c0e0e2e95ae00865282321b009ed26 + languageName: node + linkType: hard + +"@polkadot/wasm-crypto-init@npm:7.3.2": + version: 7.3.2 + resolution: "@polkadot/wasm-crypto-init@npm:7.3.2" + dependencies: + "@polkadot/wasm-bridge": 7.3.2 + "@polkadot/wasm-crypto-asmjs": 7.3.2 + "@polkadot/wasm-crypto-wasm": 7.3.2 + "@polkadot/wasm-util": 7.3.2 + tslib: ^2.6.2 + peerDependencies: + "@polkadot/util": "*" + "@polkadot/x-randomvalues": "*" + checksum: af7bc62bba16f1fbbfd76601ecf18ed8f4dfc685807e2e89ef8e8d02f824d1a1ed1635e9c2448c6c12a9a183192b18943f9ce077d6b7781c4d43cdb5c45c9161 + languageName: node + linkType: hard + +"@polkadot/wasm-crypto-wasm@npm:7.3.2": + version: 7.3.2 + resolution: "@polkadot/wasm-crypto-wasm@npm:7.3.2" + dependencies: + "@polkadot/wasm-util": 7.3.2 + tslib: ^2.6.2 + peerDependencies: + "@polkadot/util": "*" + checksum: e112ea3d4f8858a95fdaad47341b422db3db3256b7e7d709d1c3e0bc4c4bbdf81028eaa556b688078b32ff15be33af093b903c680f54eb1552072afede621a6a + languageName: node + linkType: hard + +"@polkadot/wasm-crypto@npm:^7.3.2": + version: 7.3.2 + resolution: "@polkadot/wasm-crypto@npm:7.3.2" + dependencies: + "@polkadot/wasm-bridge": 7.3.2 + "@polkadot/wasm-crypto-asmjs": 7.3.2 + "@polkadot/wasm-crypto-init": 7.3.2 + "@polkadot/wasm-crypto-wasm": 7.3.2 + "@polkadot/wasm-util": 7.3.2 + tslib: ^2.6.2 + peerDependencies: + "@polkadot/util": "*" + "@polkadot/x-randomvalues": "*" + checksum: 574006cdcc3e76af28cc79102726a79fdcd765ca5b45cbc4807d70917d82131b59f50b5cc07bd165b2863ed131b8764fef74b00c68ba5ec30a21c04c72061f8f + languageName: node + linkType: hard + +"@polkadot/wasm-util@npm:7.3.2, @polkadot/wasm-util@npm:^7.3.2": + version: 7.3.2 + resolution: "@polkadot/wasm-util@npm:7.3.2" + dependencies: + tslib: ^2.6.2 + peerDependencies: + "@polkadot/util": "*" + checksum: 44bd445043714aac6d184ce02d62fbdb97a117fd4d8bdbf3f2c1d14f6911a7d87ed6bb4682035eb757524ade995f7f4f8aaa07c8a194f761884ded25a6b383a9 + languageName: node + linkType: hard + +"@polkadot/x-bigint@npm:12.6.2, @polkadot/x-bigint@npm:^12.3.1": + version: 12.6.2 + resolution: "@polkadot/x-bigint@npm:12.6.2" + dependencies: + "@polkadot/x-global": 12.6.2 + tslib: ^2.6.2 + checksum: 12b2d5c3a7b994f5bd4f7aeda9e268384b04bd080892400c65b88fb5aa4951df6c4abe3baf9820f3adf3da92e2add710858dd35dcd597d2527bbfd1cd0efe534 + languageName: node + linkType: hard + +"@polkadot/x-fetch@npm:^12.3.1": + version: 12.6.2 + resolution: "@polkadot/x-fetch@npm:12.6.2" + dependencies: + "@polkadot/x-global": 12.6.2 + node-fetch: ^3.3.2 + tslib: ^2.6.2 + checksum: 2f0269b17ebbb907f4f4fa777898fd8ea16ecd37abfc2c0b69cfc49bd5ab0ed38cf836a4941e85f9100192f7005731a9a8c6b135799efd17b4261c3cc1ebf844 + languageName: node + linkType: hard + +"@polkadot/x-global@npm:12.6.2, @polkadot/x-global@npm:^12.3.1": + version: 12.6.2 + resolution: "@polkadot/x-global@npm:12.6.2" + dependencies: + tslib: ^2.6.2 + checksum: eb17e039cb1668743c84f5eafbf518cf6248e93090e4877f81f338b418b3e6b0173f2414c62bd9cbe7bf8911ec566527ca7c49c4354ba90d57e62e90195329d0 + languageName: node + linkType: hard + +"@polkadot/x-randomvalues@npm:12.6.2": + version: 12.6.2 + resolution: "@polkadot/x-randomvalues@npm:12.6.2" + dependencies: + "@polkadot/x-global": 12.6.2 + tslib: ^2.6.2 + peerDependencies: + "@polkadot/util": 12.6.2 + "@polkadot/wasm-util": "*" + checksum: 7faccf2dbcf0c7383b5ecfd7beb098c8c8ad5cf4c8f5bafd601657f8271af8f00b66741531ecf8b2f7c59911d96f77e358184a7c5034c70bf387a8e929a4c210 + languageName: node + linkType: hard + +"@polkadot/x-textdecoder@npm:12.6.2": + version: 12.6.2 + resolution: "@polkadot/x-textdecoder@npm:12.6.2" + dependencies: + "@polkadot/x-global": 12.6.2 + tslib: ^2.6.2 + checksum: c7e4b7f7ff943095a96bef3e3e56216d33d6ff38c965931356a06d01594b2c523ccbeada697a31b5457b134e578618f35425e0883f45187adffa98df99a45f27 + languageName: node + linkType: hard + +"@polkadot/x-textencoder@npm:12.6.2": + version: 12.6.2 + resolution: "@polkadot/x-textencoder@npm:12.6.2" + dependencies: + "@polkadot/x-global": 12.6.2 + tslib: ^2.6.2 + checksum: d3eacdc0eb2e1ef8b8132d52a1f1033be62bc64360753a117f2e6517ccf7c9cde628558bbd016a73836eacd91cb1e2ac382dce0ce9c8d32c2f7db3fcc8863911 + languageName: node + linkType: hard + +"@polkadot/x-ws@npm:^12.3.1": + version: 12.6.2 + resolution: "@polkadot/x-ws@npm:12.6.2" + dependencies: + "@polkadot/x-global": 12.6.2 + tslib: ^2.6.2 + ws: ^8.15.1 + checksum: a6bddc7ac81690f222fbc192f87f2d9b951d67414ea31a0377fb20844db8fde05d7771df5291633417aa4616bf968a31005ff22d416b2d4fecda2109f820abf7 + languageName: node + linkType: hard + "@popperjs/core@npm:^2.11.8": version: 2.11.8 resolution: "@popperjs/core@npm:2.11.8" @@ -12584,6 +13084,13 @@ __metadata: languageName: node linkType: hard +"@scure/base@npm:^1.1.5": + version: 1.1.5 + resolution: "@scure/base@npm:1.1.5" + checksum: 9e9ee6088cb3aa0fb91f5a48497d26682c7829df3019b1251d088d166d7a8c0f941c68aaa8e7b96bbad20c71eb210397cb1099062cde3e29d4bad6b975c18519 + languageName: node + linkType: hard + "@scure/base@npm:~1.1.0": version: 1.1.1 resolution: "@scure/base@npm:1.1.1" @@ -13064,6 +13571,31 @@ __metadata: languageName: node linkType: hard +"@substrate/connect-extension-protocol@npm:^1.0.1": + version: 1.0.1 + resolution: "@substrate/connect-extension-protocol@npm:1.0.1" + checksum: 116dee587e81e832e14c25038bd849438c9493c6089aa6c1bf1760780d463880d44d362ed983d57ac3695368ac46f3c9df3dbaed92f36de89626c9735cecd1e4 + languageName: node + linkType: hard + +"@substrate/connect@npm:0.7.26": + version: 0.7.26 + resolution: "@substrate/connect@npm:0.7.26" + dependencies: + "@substrate/connect-extension-protocol": ^1.0.1 + eventemitter3: ^4.0.7 + smoldot: 1.0.4 + checksum: 3179d241f073318d5973deb61c9c8d9b89ae28909a594b6b9fbcdfffd030a70ba58e8428eaa9d72484810bad10c93de1ad9c440b878d0fcfaaf4559d2e6f4502 + languageName: node + linkType: hard + +"@substrate/ss58-registry@npm:^1.44.0": + version: 1.46.0 + resolution: "@substrate/ss58-registry@npm:1.46.0" + checksum: 10e9bfd8d509abf78759d3e62cc41de515fc832dc2eab503071dda4cf0fb71c9d52247d0c32f93af6f8c4463add50d7f7f3483b52cbf43de621ac255226efb5f + languageName: node + linkType: hard + "@supabase/functions-js@npm:^1.3.4": version: 1.3.4 resolution: "@supabase/functions-js@npm:1.3.4" @@ -13900,6 +14432,15 @@ __metadata: languageName: node linkType: hard +"@types/bn.js@npm:^5.1.5": + version: 5.1.5 + resolution: "@types/bn.js@npm:5.1.5" + dependencies: + "@types/node": "*" + checksum: c87b28c4af74545624f8a3dae5294b16aa190c222626e8d4b2e327b33b1a3f1eeb43e7a24d914a9774bca43d8cd6e1cb0325c1f4b3a244af6693a024e1d918e6 + languageName: node + linkType: hard + "@types/body-parser@npm:*, @types/body-parser@npm:1.19.2": version: 1.19.2 resolution: "@types/body-parser@npm:1.19.2" @@ -14048,6 +14589,13 @@ __metadata: languageName: node linkType: hard +"@types/cookiejar@npm:^2.1.5": + version: 2.1.5 + resolution: "@types/cookiejar@npm:2.1.5" + checksum: 04d5990e87b6387532d15a87d9ec9b2eb783039291193863751dcfd7fc723a3b3aa30ce4c06b03975cba58632e933772f1ff031af23eaa3ac7f94e71afa6e073 + languageName: node + linkType: hard + "@types/cordova@latest": version: 0.0.34 resolution: "@types/cordova@npm:0.0.34" @@ -14536,6 +15084,13 @@ __metadata: languageName: node linkType: hard +"@types/joi@npm:14.3.4": + version: 14.3.4 + resolution: "@types/joi@npm:14.3.4" + checksum: 084c0fe211b43a84d25317d772bb8f6426382f1975cb4065fbfa1da7a64c600e9068a9b13ec2a32ddbe71d14f25c90da14a196651acb27e35d02856dbf6d4798 + languageName: node + linkType: hard + "@types/js-yaml@npm:4.0.3": version: 4.0.3 resolution: "@types/js-yaml@npm:4.0.3" @@ -14702,6 +15257,13 @@ __metadata: languageName: node linkType: hard +"@types/methods@npm:^1.1.4": + version: 1.1.4 + resolution: "@types/methods@npm:1.1.4" + checksum: ad2a7178486f2fd167750f3eb920ab032a947ff2e26f55c86670a6038632d790b46f52e5b6ead5823f1e53fc68028f1e9ddd15cfead7903e04517c88debd72b1 + languageName: node + linkType: hard + "@types/mime@npm:*": version: 3.0.1 resolution: "@types/mime@npm:3.0.1" @@ -15332,6 +15894,16 @@ __metadata: languageName: node linkType: hard +"@types/ssh2@npm:0.5.44": + version: 0.5.44 + resolution: "@types/ssh2@npm:0.5.44" + dependencies: + "@types/node": "*" + "@types/ssh2-streams": "*" + checksum: bb45290b67b9fc3ca6539e23179e378c2400df1dfa3549b13cb9263c511002db2dea9d812e1c494291671defb6cbc130edde34020558380e5f2e13afca0f60e1 + languageName: node + linkType: hard + "@types/ssh2@npm:0.5.47": version: 0.5.47 resolution: "@types/ssh2@npm:0.5.47" @@ -15358,6 +15930,26 @@ __metadata: languageName: node linkType: hard +"@types/superagent@npm:*": + version: 8.1.4 + resolution: "@types/superagent@npm:8.1.4" + dependencies: + "@types/cookiejar": ^2.1.5 + "@types/methods": ^1.1.4 + "@types/node": "*" + checksum: e1c47f20c93a87cf14a016fa1a154b538291b0ed370f6eb4255ca58a693a6d0470896079d86e58e3489e3107e5796370b6208a983e90696549709c43cc51bfdf + languageName: node + linkType: hard + +"@types/supertest@npm:2.0.11": + version: 2.0.11 + resolution: "@types/supertest@npm:2.0.11" + dependencies: + "@types/superagent": "*" + checksum: 291abc0d37abe833d517fcfd0c22d51e7d5ffea85ce990603a0d058afa7fe2465b1251d50642ddfd640f66d047029af512793215b612c39adbee72fae5b2ef4f + languageName: node + linkType: hard + "@types/tape-promise@npm:4.0.1": version: 4.0.1 resolution: "@types/tape-promise@npm:4.0.1" @@ -15445,6 +16037,13 @@ __metadata: languageName: node linkType: hard +"@types/uuid@npm:8.3.1": + version: 8.3.1 + resolution: "@types/uuid@npm:8.3.1" + checksum: b41bdc5e86c6f0f1899306be10455636da0f2168c9489b869edd6837ddeb7c0e501b1ff7d857402462986bada2a379125743dd895fa801d03437cd632116a373 + languageName: node + linkType: hard + "@types/uuid@npm:8.3.4": version: 8.3.4 resolution: "@types/uuid@npm:8.3.4" @@ -16961,7 +17560,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^6.10.0, ajv@npm:^6.10.2, ajv@npm:^6.11.0, ajv@npm:^6.12.2, ajv@npm:^6.12.3, ajv@npm:^6.12.4, ajv@npm:^6.12.5": +"ajv@npm:^6.10.0, ajv@npm:^6.10.2, ajv@npm:^6.11.0, ajv@npm:^6.12.2, ajv@npm:^6.12.3, ajv@npm:^6.12.4, ajv@npm:^6.12.5, ajv@npm:^6.12.6": version: 6.12.6 resolution: "ajv@npm:6.12.6" dependencies: @@ -19755,6 +20354,16 @@ __metadata: languageName: node linkType: hard +"busboy@npm:^0.2.11": + version: 0.2.14 + resolution: "busboy@npm:0.2.14" + dependencies: + dicer: 0.2.5 + readable-stream: 1.1.x + checksum: 9df9fca6d96dab9edd03f568bde31f215794e6fabd73c75d2b39a4be2e8b73a45121d987dea5db881f3fb499737c261b372106fe72d08b8db92afaed8d751165 + languageName: node + linkType: hard + "busboy@npm:^1.0.0, busboy@npm:^1.6.0": version: 1.6.0 resolution: "busboy@npm:1.6.0" @@ -21291,6 +21900,13 @@ __metadata: languageName: node linkType: hard +"component-emitter@npm:^1.3.0": + version: 1.3.1 + resolution: "component-emitter@npm:1.3.1" + checksum: 94550aa462c7bd5a61c1bc480e28554aa306066930152d1b1844a0dd3845d4e5db7e261ddec62ae184913b3e59b55a2ad84093b9d3596a8f17c341514d6c483d + languageName: node + linkType: hard + "compressible@npm:~2.0.16": version: 2.0.18 resolution: "compressible@npm:2.0.18" @@ -21509,7 +22125,7 @@ __metadata: languageName: node linkType: hard -"content-type@npm:^1.0.5, content-type@npm:~1.0.5": +"content-type@npm:^1.0.4, content-type@npm:^1.0.5, content-type@npm:~1.0.5": version: 1.0.5 resolution: "content-type@npm:1.0.5" checksum: 566271e0a251642254cde0f845f9dd4f9856e52d988f4eb0d0dcffbb7a1f8ec98de7a5215fc628f3bce30fe2fb6fd2bc064b562d721658c59b544e2d34ea2766 @@ -21746,7 +22362,7 @@ __metadata: languageName: node linkType: hard -"cookiejar@npm:^2.1.1": +"cookiejar@npm:^2.1.1, cookiejar@npm:^2.1.2": version: 2.1.4 resolution: "cookiejar@npm:2.1.4" checksum: c4442111963077dc0e5672359956d6556a195d31cbb35b528356ce5f184922b99ac48245ac05ed86cf993f7df157c56da10ab3efdadfed79778a0d9b1b092d5b @@ -23576,6 +24192,16 @@ __metadata: languageName: node linkType: hard +"dicer@npm:0.2.5": + version: 0.2.5 + resolution: "dicer@npm:0.2.5" + dependencies: + readable-stream: 1.1.x + streamsearch: 0.1.2 + checksum: a6f0ce9ac5099c7ffeaec7398d711eea1dd803eb99036d0f05342e9ed46a4235a5ed0ea01ad5d6c785fdb0aae6d61d2722e6e64f9fabdfe39885f7f52eb635ee + languageName: node + linkType: hard + "did-resolver@npm:^4.0.0, did-resolver@npm:^4.1.0": version: 4.1.0 resolution: "did-resolver@npm:4.1.0" @@ -25670,6 +26296,53 @@ __metadata: languageName: node linkType: hard +"eslint@npm:7.21.0": + version: 7.21.0 + resolution: "eslint@npm:7.21.0" + dependencies: + "@babel/code-frame": 7.12.11 + "@eslint/eslintrc": ^0.4.0 + ajv: ^6.10.0 + chalk: ^4.0.0 + cross-spawn: ^7.0.2 + debug: ^4.0.1 + doctrine: ^3.0.0 + enquirer: ^2.3.5 + eslint-scope: ^5.1.1 + eslint-utils: ^2.1.0 + eslint-visitor-keys: ^2.0.0 + espree: ^7.3.1 + esquery: ^1.4.0 + esutils: ^2.0.2 + file-entry-cache: ^6.0.1 + functional-red-black-tree: ^1.0.1 + glob-parent: ^5.0.0 + globals: ^12.1.0 + ignore: ^4.0.6 + import-fresh: ^3.0.0 + imurmurhash: ^0.1.4 + is-glob: ^4.0.0 + js-yaml: ^3.13.1 + json-stable-stringify-without-jsonify: ^1.0.1 + levn: ^0.4.1 + lodash: ^4.17.20 + minimatch: ^3.0.4 + natural-compare: ^1.4.0 + optionator: ^0.9.1 + progress: ^2.0.0 + regexpp: ^3.1.0 + semver: ^7.2.1 + strip-ansi: ^6.0.0 + strip-json-comments: ^3.1.0 + table: ^6.0.4 + text-table: ^0.2.0 + v8-compile-cache: ^2.0.3 + bin: + eslint: bin/eslint.js + checksum: 2ac78e255ff27c6462420adff9b6c6bf8d785ce121d5d2a41d9eddd4e9584027f70cfca1f4c30eaa144c7980cbf7017be1c2c6778b9569cb265fcd37b821146c + languageName: node + linkType: hard + "eslint@npm:7.32.0": version: 7.32.0 resolution: "eslint@npm:7.32.0" @@ -26477,7 +27150,7 @@ __metadata: languageName: node linkType: hard -"eventemitter3@npm:^4.0.0": +"eventemitter3@npm:^4.0.0, eventemitter3@npm:^4.0.7": version: 4.0.7 resolution: "eventemitter3@npm:4.0.7" checksum: 1875311c42fcfe9c707b2712c32664a245629b42bb0a5a84439762dd0fd637fc54d078155ea83c2af9e0323c9ac13687e03cfba79b03af9f40c89b4960099374 @@ -26792,6 +27465,26 @@ __metadata: languageName: node linkType: hard +"express-openapi-validator@npm:4.13.1": + version: 4.13.1 + resolution: "express-openapi-validator@npm:4.13.1" + dependencies: + "@types/multer": ^1.4.7 + ajv: ^6.12.6 + content-type: ^1.0.4 + json-schema-ref-parser: ^9.0.9 + lodash.clonedeep: ^4.5.0 + lodash.get: ^4.4.2 + lodash.uniq: ^4.5.0 + lodash.zipobject: ^4.1.3 + media-typer: ^1.1.0 + multer: ^1.4.3 + ono: ^7.1.3 + path-to-regexp: ^6.2.0 + checksum: 5e1eb8f5e68727925a56aeac07716354940d3615d85ff219bebe076afed15a2b35a785774ae22128dd6b6e44b74148342e7b8d5bcb6473e2562bb308073bb541 + languageName: node + linkType: hard + "express-openapi-validator@npm:5.0.4": version: 5.0.4 resolution: "express-openapi-validator@npm:5.0.4" @@ -27273,7 +27966,7 @@ __metadata: languageName: node linkType: hard -"fast-safe-stringify@npm:2.1.1, fast-safe-stringify@npm:^2.1.1": +"fast-safe-stringify@npm:2.1.1, fast-safe-stringify@npm:^2.0.7, fast-safe-stringify@npm:^2.1.1": version: 2.1.1 resolution: "fast-safe-stringify@npm:2.1.1" checksum: a851cbddc451745662f8f00ddb622d6766f9bd97642dabfd9a405fb0d646d69fc0b9a1243cbf67f5f18a39f40f6fa821737651ff1bceeba06c9992ca2dc5bd3d @@ -27994,6 +28687,13 @@ __metadata: languageName: node linkType: hard +"formidable@npm:^1.2.2": + version: 1.2.6 + resolution: "formidable@npm:1.2.6" + checksum: 2b68ed07ba88302b9c63f8eda94f19a460cef6017bfda48348f09f41d2a36660c9353137991618e0e4c3db115b41e4b8f6fa63bc973b7a7c91dec66acdd02a56 + languageName: node + linkType: hard + "forwarded@npm:0.2.0": version: 0.2.0 resolution: "forwarded@npm:0.2.0" @@ -28091,6 +28791,17 @@ __metadata: languageName: node linkType: hard +"fs-extra@npm:11.2.0, fs-extra@npm:^11.2.0": + version: 11.2.0 + resolution: "fs-extra@npm:11.2.0" + dependencies: + graceful-fs: ^4.2.0 + jsonfile: ^6.0.1 + universalify: ^2.0.0 + checksum: b12e42fa40ba47104202f57b8480dd098aa931c2724565e5e70779ab87605665594e76ee5fb00545f772ab9ace167fe06d2ab009c416dc8c842c5ae6df7aa7e8 + languageName: node + linkType: hard + "fs-extra@npm:^0.30.0": version: 0.30.0 resolution: "fs-extra@npm:0.30.0" @@ -28115,17 +28826,6 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:^11.2.0": - version: 11.2.0 - resolution: "fs-extra@npm:11.2.0" - dependencies: - graceful-fs: ^4.2.0 - jsonfile: ^6.0.1 - universalify: ^2.0.0 - checksum: b12e42fa40ba47104202f57b8480dd098aa931c2724565e5e70779ab87605665594e76ee5fb00545f772ab9ace167fe06d2ab009c416dc8c842c5ae6df7aa7e8 - languageName: node - linkType: hard - "fs-extra@npm:^4.0.2": version: 4.0.3 resolution: "fs-extra@npm:4.0.3" @@ -33959,6 +34659,15 @@ __metadata: languageName: node linkType: hard +"json-schema-ref-parser@npm:^9.0.9": + version: 9.0.9 + resolution: "json-schema-ref-parser@npm:9.0.9" + dependencies: + "@apidevtools/json-schema-ref-parser": 9.0.9 + checksum: e05166a84c702f54f192edb2eb2e39236c3b03c30561777d63fd156ecd3aa3d2fffc0806a5703384bfba3c78800b1dc05f8da1ea25e6470b35a823210f7d48c4 + languageName: node + linkType: hard + "json-schema-traverse@npm:^0.3.0": version: 0.3.1 resolution: "json-schema-traverse@npm:0.3.1" @@ -36658,6 +37367,15 @@ __metadata: languageName: node linkType: hard +"mime@npm:^2.4.6": + version: 2.6.0 + resolution: "mime@npm:2.6.0" + bin: + mime: cli.js + checksum: 1497ba7b9f6960694268a557eae24b743fd2923da46ec392b042469f4b901721ba0adcf8b0d3c2677839d0e243b209d76e5edcbd09cfdeffa2dfb6bb4df4b862 + languageName: node + linkType: hard + "mimic-fn@npm:^1.0.0": version: 1.2.0 resolution: "mimic-fn@npm:1.2.0" @@ -37041,7 +37759,7 @@ __metadata: languageName: node linkType: hard -"mkdirp@npm:^0.5.0, mkdirp@npm:^0.5.1, mkdirp@npm:^0.5.4, mkdirp@npm:^0.5.5, mkdirp@npm:~0.5.1": +"mkdirp@npm:^0.5.0, mkdirp@npm:^0.5.1, mkdirp@npm:^0.5.3, mkdirp@npm:^0.5.4, mkdirp@npm:^0.5.5, mkdirp@npm:~0.5.1": version: 0.5.6 resolution: "mkdirp@npm:0.5.6" dependencies: @@ -37154,7 +37872,7 @@ __metadata: languageName: node linkType: hard -"mock-socket@npm:^9.3.0": +"mock-socket@npm:^9.2.1, mock-socket@npm:^9.3.0": version: 9.3.1 resolution: "mock-socket@npm:9.3.1" checksum: cb2dde4fc5dde280dd5ccb78eaaa223382ee16437f46b86558017655584ad08c22e733bde2dd5cc86927def506b6caeb0147e3167b9a62d70d5cf19d44103853 @@ -37257,6 +37975,22 @@ __metadata: languageName: node linkType: hard +"multer@npm:1.4.2": + version: 1.4.2 + resolution: "multer@npm:1.4.2" + dependencies: + append-field: ^1.0.0 + busboy: ^0.2.11 + concat-stream: ^1.5.2 + mkdirp: ^0.5.1 + object-assign: ^4.1.1 + on-finished: ^2.3.0 + type-is: ^1.6.4 + xtend: ^4.0.0 + checksum: a77ba79ec96b8376fdd09531c1cfd36f1d04e195b5e944a2cd4979c992cdc9b521887abed0893f75a21c59bb4dffe6356046da966059c268801d1f7c83e6ea16 + languageName: node + linkType: hard + "multer@npm:1.4.5-lts.1, multer@npm:^1.4.5-lts.1": version: 1.4.5-lts.1 resolution: "multer@npm:1.4.5-lts.1" @@ -37272,6 +38006,22 @@ __metadata: languageName: node linkType: hard +"multer@npm:^1.4.3": + version: 1.4.4 + resolution: "multer@npm:1.4.4" + dependencies: + append-field: ^1.0.0 + busboy: ^0.2.11 + concat-stream: ^1.5.2 + mkdirp: ^0.5.4 + object-assign: ^4.1.1 + on-finished: ^2.3.0 + type-is: ^1.6.4 + xtend: ^4.0.0 + checksum: b5550d250aeee9c4d630eaecd133af0899239f6b10cec4b448ddd0a808025b383520b8227198a8612f60c2cd2094bcb60de93d973084f889d4e40efe6dbd641e + languageName: node + linkType: hard + "multibase@npm:^0.7.0": version: 0.7.0 resolution: "multibase@npm:0.7.0" @@ -37684,6 +38434,21 @@ __metadata: languageName: node linkType: hard +"ngo@npm:2.6.2": + version: 2.6.2 + resolution: "ngo@npm:2.6.2" + dependencies: + execa: ^4.0.0 + go-bin: ^1.4.0 + go-versions: ^1.3.2 + bin: + ngo: cli.js + ngo-binary: cli-binary.js + ngo-update: cli-update.js + checksum: 17cf87495689d45eade7f1675e6a050770fdc7ec1e09f66ab62faa3f661c85a4537d76f4add5e3ad7bf2279b226ea6688b511ecf152998208e6ea9b1a4715cd8 + languageName: node + linkType: hard + "ngo@npm:2.7.0": version: 2.7.0 resolution: "ngo@npm:2.7.0" @@ -37751,6 +38516,17 @@ __metadata: languageName: node linkType: hard +"nock@npm:^13.3.1": + version: 13.5.4 + resolution: "nock@npm:13.5.4" + dependencies: + debug: ^4.1.0 + json-stringify-safe: ^5.0.1 + propagate: ^2.0.0 + checksum: d31f924e34c87ae985edfb7b5a56e8a4dcfc3a072334ceb6d686326581f93090b3e23492663a64ce61b8df4f365b113231d926bc300bcfe9e5eb309c3e4b8628 + languageName: node + linkType: hard + "node-abi@npm:^2.7.0": version: 2.30.1 resolution: "node-abi@npm:2.30.1" @@ -39073,7 +39849,7 @@ __metadata: languageName: node linkType: hard -"on-finished@npm:2.4.1": +"on-finished@npm:2.4.1, on-finished@npm:^2.3.0": version: 2.4.1 resolution: "on-finished@npm:2.4.1" dependencies: @@ -39839,6 +40615,13 @@ __metadata: languageName: node linkType: hard +"pako@npm:^2.0.4": + version: 2.1.0 + resolution: "pako@npm:2.1.0" + checksum: 71666548644c9a4d056bcaba849ca6fd7242c6cf1af0646d3346f3079a1c7f4a66ffec6f7369ee0dc88f61926c10d6ab05da3e1fca44b83551839e89edd75a3e + languageName: node + linkType: hard + "param-case@npm:^2.1.0": version: 2.1.1 resolution: "param-case@npm:2.1.1" @@ -42159,6 +42942,13 @@ __metadata: languageName: node linkType: hard +"propagate@npm:^2.0.0": + version: 2.0.1 + resolution: "propagate@npm:2.0.1" + checksum: c4febaee2be0979e82fb6b3727878fd122a98d64a7fa3c9d09b0576751b88514a9e9275b1b92e76b364d488f508e223bd7e1dcdc616be4cdda876072fbc2a96c + languageName: node + linkType: hard + "proto-list@npm:~1.2.1": version: 1.2.4 resolution: "proto-list@npm:1.2.4" @@ -42398,7 +43188,7 @@ __metadata: languageName: node linkType: hard -"qs@npm:^6.11.0": +"qs@npm:^6.11.0, qs@npm:^6.9.4": version: 6.11.2 resolution: "qs@npm:6.11.2" dependencies: @@ -43054,7 +43844,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:1.1.14, readable-stream@npm:^1.0.26-4, readable-stream@npm:^1.0.33": +"readable-stream@npm:1.1.14, readable-stream@npm:1.1.x, readable-stream@npm:^1.0.26-4, readable-stream@npm:^1.0.33": version: 1.1.14 resolution: "readable-stream@npm:1.1.14" dependencies: @@ -45678,6 +46468,16 @@ __metadata: languageName: node linkType: hard +"smoldot@npm:1.0.4": + version: 1.0.4 + resolution: "smoldot@npm:1.0.4" + dependencies: + pako: ^2.0.4 + ws: ^8.8.1 + checksum: 81ecc38b98f7ac4dd093753e85956262608dca3c8a288c20a25fe1762a6afcdbe6f3622ea30a346df3f4145e0900ef0595e56e96e9e0de83c59f0649d1ab4786 + languageName: node + linkType: hard + "snake-case@npm:^2.1.0": version: 2.1.0 resolution: "snake-case@npm:2.1.0" @@ -46723,6 +47523,13 @@ __metadata: languageName: node linkType: hard +"streamsearch@npm:0.1.2": + version: 0.1.2 + resolution: "streamsearch@npm:0.1.2" + checksum: d2db57cbfbf7947ab9c75a7b4c80a8ef8d24850cf0a1a24258bb6956c97317ce1eab7dbcbf9c5aba3e6198611af1053b02411057bbedb99bf9c64b8275248997 + languageName: node + linkType: hard + "streamsearch@npm:^1.1.0": version: 1.1.0 resolution: "streamsearch@npm:1.1.0" @@ -47229,6 +48036,35 @@ __metadata: languageName: node linkType: hard +"superagent@npm:^6.1.0": + version: 6.1.0 + resolution: "superagent@npm:6.1.0" + dependencies: + component-emitter: ^1.3.0 + cookiejar: ^2.1.2 + debug: ^4.1.1 + fast-safe-stringify: ^2.0.7 + form-data: ^3.0.0 + formidable: ^1.2.2 + methods: ^1.1.2 + mime: ^2.4.6 + qs: ^6.9.4 + readable-stream: ^3.6.0 + semver: ^7.3.2 + checksum: 32ca1bc9805679cddeffdf5cf369da47359a0d38ee45ea668bba4116e17c247739e4084db9cc88217dd594a816e766a3dbf2431de017fbac0bc80efd6af30c1d + languageName: node + linkType: hard + +"supertest@npm:6.1.6": + version: 6.1.6 + resolution: "supertest@npm:6.1.6" + dependencies: + methods: ^1.1.2 + superagent: ^6.1.0 + checksum: 51d11b794a022b49f0f9664e6d8ffef5e8d18467f65d2b4326e52ec96f0801d7066bf9a97eb06e8b6cd3eb6f662ee5bed937cd5764ab0617a822d7ee4c72ff36 + languageName: node + linkType: hard + "supports-color@npm:5.4.0": version: 5.4.0 resolution: "supports-color@npm:5.4.0" @@ -47525,6 +48361,19 @@ __metadata: languageName: node linkType: hard +"table@npm:^6.0.4": + version: 6.8.1 + resolution: "table@npm:6.8.1" + dependencies: + ajv: ^8.0.1 + lodash.truncate: ^4.4.2 + slice-ansi: ^4.0.0 + string-width: ^4.2.3 + strip-ansi: ^6.0.1 + checksum: 08249c7046125d9d0a944a6e96cfe9ec66908d6b8a9db125531be6eb05fa0de047fd5542e9d43b4f987057f00a093b276b8d3e19af162a9c40db2681058fd306 + languageName: node + linkType: hard + "table@npm:^6.0.9": version: 6.8.0 resolution: "table@npm:6.8.0" @@ -47896,6 +48745,15 @@ __metadata: languageName: node linkType: hard +"temp@npm:0.9.1": + version: 0.9.1 + resolution: "temp@npm:0.9.1" + dependencies: + rimraf: ~2.6.2 + checksum: 568be30ec54f2a9d74b10caebb0c8e169577741122822aaeee13150af83fab2a73954ef12d7772b85f136529ffa80e940a51b42735d010dea0a634fbc70c0223 + languageName: node + linkType: hard + "temp@npm:0.9.4": version: 0.9.4 resolution: "temp@npm:0.9.4" @@ -48713,14 +49571,14 @@ __metadata: languageName: node linkType: hard -"tslib@npm:2.6.2, tslib@npm:^2.5.0, tslib@npm:^2.6.0, tslib@npm:^2.6.1, tslib@npm:^2.6.2": +"tslib@npm:2.6.2, tslib@npm:^2.5.0, tslib@npm:^2.5.3, tslib@npm:^2.6.0, tslib@npm:^2.6.1, tslib@npm:^2.6.2": version: 2.6.2 resolution: "tslib@npm:2.6.2" checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad languageName: node linkType: hard -"tslib@npm:^1.10.0, tslib@npm:^1.8.0, tslib@npm:^1.8.1, tslib@npm:^1.9.0, tslib@npm:^1.9.3": +"tslib@npm:^1.10.0, tslib@npm:^1.13.0, tslib@npm:^1.8.0, tslib@npm:^1.8.1, tslib@npm:^1.9.0, tslib@npm:^1.9.3": version: 1.14.1 resolution: "tslib@npm:1.14.1" checksum: dbe628ef87f66691d5d2959b3e41b9ca0045c3ee3c7c7b906cc1e328b39f199bb1ad9e671c39025bd56122ac57dfbf7385a94843b1cc07c60a4db74795829acd @@ -48805,6 +49663,31 @@ __metadata: languageName: node linkType: hard +"tslint@npm:6.1.3": + version: 6.1.3 + resolution: "tslint@npm:6.1.3" + dependencies: + "@babel/code-frame": ^7.0.0 + builtin-modules: ^1.1.1 + chalk: ^2.3.0 + commander: ^2.12.1 + diff: ^4.0.1 + glob: ^7.1.1 + js-yaml: ^3.13.1 + minimatch: ^3.0.4 + mkdirp: ^0.5.3 + resolve: ^1.3.2 + semver: ^5.3.0 + tslib: ^1.13.0 + tsutils: ^2.29.0 + peerDependencies: + typescript: ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 4.0.0-dev" + bin: + tslint: bin/tslint + checksum: 7348b7abd6b8939f0b295c4dddbb104adfe2e96c9cc5be4a946a87a28df648bde61046cfb07951708938192f553eba6254f7729b85fd230efebbd72f7988255e + languageName: node + linkType: hard + "tsort@npm:0.0.1": version: 0.0.1 resolution: "tsort@npm:0.0.1" @@ -54316,6 +55199,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:^8.15.1": + version: 8.16.0 + resolution: "ws@npm:8.16.0" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: feb3eecd2bae82fa8a8beef800290ce437d8b8063bdc69712725f21aef77c49cb2ff45c6e5e7fce622248f9c7abaee506bae0a9064067ffd6935460c7357321b + languageName: node + linkType: hard + "ws@npm:^8.4.0": version: 8.8.1 resolution: "ws@npm:8.8.1"