From 65e47156da4e2f1f5b74bc128ac5d4258f3a6594 Mon Sep 17 00:00:00 2001 From: Michal Bajer Date: Fri, 20 Oct 2023 15:08:48 +0000 Subject: [PATCH] feat(cactus-plugin-ledger-connector-sawtooth): add new connector plugin - Add new plugin for connecting with sawtooth ledgers. - New connector is based on already existing sawtooth-socketio connector. - Currently it supports only watchBlock and connector status endpoints. - Add new connector to cactus-verifier-client - Add integration tests in `cactus-test-plugin-ledger-connector-sawtooth`. Signed-off-by: Michal Bajer --- .cspell.json | 1 + .github/workflows/ci.yaml | 24 + .../Dockerfile | 5 + .../README.md | 147 ++ .../openapitools.json | 7 + .../package.json | 91 + .../src/main/json/openapi.json | 363 +++ .../src/main/json/sawtooth-openapi.json | 1225 ++++++++++ .../api-client/sawtooth-api-client.ts | 117 + .../openapi/typescript-axios/.gitignore | 4 + .../openapi/typescript-axios/.npmignore | 1 + .../.openapi-generator-ignore | 23 + .../typescript-axios/.openapi-generator/FILES | 8 + .../.openapi-generator/VERSION | 1 + .../generated/openapi/typescript-axios/api.ts | 514 +++++ .../openapi/typescript-axios/base.ts | 72 + .../openapi/typescript-axios/common.ts | 150 ++ .../openapi/typescript-axios/configuration.ts | 101 + .../openapi/typescript-axios/git_push.sh | 57 + .../openapi/typescript-axios/index.ts | 18 + .../src/main/typescript/index.ts | 1 + .../src/main/typescript/index.web.ts | 1 + .../plugin-factory-ledger-connector.ts | 20 + .../plugin-ledger-connector-sawtooth.ts | 199 ++ .../src/main/typescript/public-api.ts | 22 + .../main/typescript/sawtooth-api/.gitignore | 4 + .../main/typescript/sawtooth-api/.npmignore | 1 + .../sawtooth-api/.openapi-generator-ignore | 23 + .../sawtooth-api/.openapi-generator/FILES | 8 + .../sawtooth-api/.openapi-generator/VERSION | 1 + .../src/main/typescript/sawtooth-api/api.ts | 1986 +++++++++++++++++ .../src/main/typescript/sawtooth-api/base.ts | 72 + .../main/typescript/sawtooth-api/common.ts | 150 ++ .../typescript/sawtooth-api/configuration.ts | 101 + .../main/typescript/sawtooth-api/git_push.sh | 57 + .../src/main/typescript/sawtooth-api/index.ts | 18 + .../typescript/types/model-type-guards.ts | 18 + .../web-services/status-endpoint-v1.ts | 104 + .../web-services/watch-blocks-v1-endpoint.ts | 239 ++ .../integration/api-surface.test.ts | 5 + .../sawtooth-monitoring-endpoints.test.ts | 297 +++ .../tsconfig.json | 26 + packages/cactus-verifier-client/README.md | 3 +- packages/cactus-verifier-client/package.json | 1 + .../typescript/get-validator-api-client.ts | 11 + packages/cactus-verifier-client/tsconfig.json | 3 + tsconfig.json | 3 + yarn.lock | 57 + 48 files changed, 6359 insertions(+), 1 deletion(-) create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/Dockerfile create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/README.md create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/openapitools.json create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/package.json create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/json/openapi.json create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/json/sawtooth-openapi.json create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/api-client/sawtooth-api-client.ts create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.gitignore create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.npmignore create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator-ignore create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/FILES create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/VERSION create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/api.ts create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/base.ts create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/common.ts create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/configuration.ts create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/git_push.sh create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/index.ts create mode 100755 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/index.ts create mode 100755 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/index.web.ts create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/plugin-factory-ledger-connector.ts create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/plugin-ledger-connector-sawtooth.ts create mode 100755 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/public-api.ts create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.gitignore create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.npmignore create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.openapi-generator-ignore create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.openapi-generator/FILES create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.openapi-generator/VERSION create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/api.ts create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/base.ts create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/common.ts create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/configuration.ts create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/git_push.sh create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/index.ts create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/types/model-type-guards.ts create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/web-services/status-endpoint-v1.ts create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/web-services/watch-blocks-v1-endpoint.ts create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/test/typescript/integration/api-surface.test.ts create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/src/test/typescript/integration/sawtooth-monitoring-endpoints.test.ts create mode 100644 packages/cactus-plugin-ledger-connector-sawtooth/tsconfig.json diff --git a/.cspell.json b/.cspell.json index b44e387a7d..8d17edfe2f 100644 --- a/.cspell.json +++ b/.cspell.json @@ -22,6 +22,7 @@ "caio", "cbdc", "Cbdc", + "cbor", "cccg", "cccs", "ccep", diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5f385de063..cbe152149f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1335,6 +1335,30 @@ jobs: node-version: v16.14.2 - 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-sawtooth: + continue-on-error: false + env: + FULL_BUILD_DISABLED: true + JEST_TEST_PATTERN: packages/cactus-plugin-ledger-connector-sawtooth/src/test/typescript/(unit|integration|benchmark)/.*/*.test.ts + JEST_TEST_RUNNER_DISABLED: false + TAPE_TEST_RUNNER_DISABLED: true + needs: build-dev + runs-on: ubuntu-20.04 + steps: + - name: Use Node.js v16.14.2 + uses: actions/setup-node@v3.6.0 + with: + node-version: v16.14.2 + - uses: actions/checkout@v3.5.2 - id: yarn-cache name: Restore Yarn Cache uses: actions/cache@v3.3.1 diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/Dockerfile b/packages/cactus-plugin-ledger-connector-sawtooth/Dockerfile new file mode 100644 index 0000000000..f1d13987bc --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/Dockerfile @@ -0,0 +1,5 @@ +FROM ghcr.io/hyperledger/cactus-cmd-api-server:v1.0.0 + +ARG NPM_PKG_VERSION=latest + +RUN npm i @hyperledger/cactus-plugin-ledger-connector-sawtooth@${NPM_PKG_VERSION} --production diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/README.md b/packages/cactus-plugin-ledger-connector-sawtooth/README.md new file mode 100644 index 0000000000..fe15f4bf2d --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/README.md @@ -0,0 +1,147 @@ +# `@hyperledger/cactus-plugin-ledger-connector-sawtooth` + +This plugin provides `Cacti` a way to interact with Sawtooth networks. Using this we can perform: + +- Check plugin and sawtooth node status. +- Monitor new blocks and transactions on the ledger. + +## Summary + +- [Getting Started](#getting-started) +- [Usage](#usage) +- [SawtoothApiClient](#sawtoothapiclient) +- [Runing the tests](#running-the-tests) +- [Contributing](#contributing) +- [License](#license) +- [Acknowledgments](#acknowledgments) + +## Getting Started + +Clone the git repository on your local machine. Follow these instructions that will get you a copy of the project up and running on +your local machine for development and testing purposes. + +### Prerequisites + +In the root of the project to install the dependencies execute the command: + +```sh +npm run configure +``` + +## Usage + +Before running the connector please ensure that Sawtooth node is running and that connector will have direct access to it. Access through a proxy is not supported yet (see https://sawtooth.hyperledger.org/docs/1.2/sysadmin_guide/rest_auth_proxy.html). +To use this import public-api and create new **PluginLedgerConnectorSawtooth**. + +```typescript +const connector = new PluginLedgerConnectorSawtooth({ + instanceId: uuidV4(), + logLevel: testLogLevel, + sawtoothRestApiEndpoint: ledgerRestApi, + watchBlocksPollTime, +}); +``` + +You can make calls through the connector to the plugin API: + +```typescript +async getStatus(): Promise +``` + +Please note that `deployContract()` and `transact()`, although available to call, **are not implemented and will throw error on runtime!** + +## SawtoothApiClient + +All connector API endpoints are defined in [open-api specification](./src/main/json/openapi.json). You can use [SawtoothApiClient](./src/main/typescript/api-client) to call remote sawtooth connector functions. It also contain additional utility functions to ease integration. + +### REST Functions + +See [DefaultApi](./src/main/typescript/generated/openapi/typescript-axios/api.ts) for up-to-date listing of supported endpoints. + +- getStatusV1 + +### Asynchronous Functions (socket.io) + +- watchBlocksV1 + +## Running the tests + +To check that all has been installed correctly and that the plugin has no errors run jest test suites. + +- Run this command at the project's root: + +```sh +npx jest cactus-plugin-ledger-connector-sawtooth +``` + +### Building/running the container image locally + +In the Cactus project root say: + +```sh +DOCKER_BUILDKIT=1 docker build -f ./packages/cactus-plugin-ledger-connector-sawtooth/Dockerfile . -t cplcb +``` + +Build with a specific version of the npm package: + +```sh +DOCKER_BUILDKIT=1 docker build --build-arg NPM_PKG_VERSION=0.4.1 -f ./packages/cactus-plugin-ledger-connector-sawtooth/Dockerfile . -t cplcb +``` + +#### 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 PLUGINS='[{"packageName": "@hyperledger/cactus-plugin-ledger-connector-sawtooth", "type": "org.hyperledger.cactus.plugin_import_type.LOCAL", "action": "org.hyperledger.cactus.plugin_import_action.INSTALL", "options": {"rpcApiHttpHost": "http://localhost:8545", "instanceId": "some-unique-sawtooth-connector-instance-id"}}]' \ + cplcb +``` + +Launch container with plugin configuration as a **CLI argument**: + +```sh +docker run \ + --rm \ + --publish 3000:3000 \ + --publish 4000:4000 \ + cplcb \ + ./node_modules/@hyperledger/cactus-cmd-api-server/dist/lib/main/typescript/cmd/cactus-api.js \ + --authorization-protocol='NONE' \ + --authorization-config-json='{}' \ + --plugins='[{"packageName": "@hyperledger/cactus-plugin-ledger-connector-sawtooth", "type": "org.hyperledger.cactus.plugin_import_type.LOCAL", "action": "org.hyperledger.cactus.plugin_import_action.INSTALL", "options": {"rpcApiHttpHost": "http://localhost:8545", "instanceId": "some-unique-sawtooth-connector-instance-id"}}]' +``` + +Launch container with **configuration file** mounted from host machine: + +```sh + +echo '{"authorizationProtocol":"NONE","authorizationConfigJson":{},"plugins":[{"packageName":"@hyperledger/cactus-plugin-ledger-connector-sawtooth","type":"org.hyperledger.cactus.plugin_import_type.LOCAL","action":"org.hyperledger.cactus.plugin_import_action.INSTALL","options":{"rpcApiHttpHost":"http://localhost:8545","instanceId":"some-unique-sawtooth-connector-instance-id"}}]}' > cactus.json + +docker run \ + --rm \ + --publish 3000:3000 \ + --publish 4000:4000 \ + --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 +``` + +## 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. + +## License + +This distribution is published under the Apache License Version 2.0 found in the [LICENSE](../../LICENSE) file. + +## Acknowledgments +- This plugin uses Sawtooth REST API OpenAPI specification from https://sawtooth.hyperledger.org/docs/1.2/rest_api/openapi/. We did some changes to the original spec in order to simplify request handling (e.g. change type, mark fields as requried, etc...) diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/openapitools.json b/packages/cactus-plugin-ledger-connector-sawtooth/openapitools.json new file mode 100644 index 0000000000..03392961f6 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/openapitools.json @@ -0,0 +1,7 @@ +{ + "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", + "spaces": 2, + "generator-cli": { + "version": "6.3.0" + } +} diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/package.json b/packages/cactus-plugin-ledger-connector-sawtooth/package.json new file mode 100644 index 0000000000..bcdfc20d47 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/package.json @@ -0,0 +1,91 @@ +{ + "name": "@hyperledger/cactus-plugin-ledger-connector-sawtooth", + "version": "2.0.0-alpha.2", + "description": "Allows Cactus nodes to connect to a Sawtooth ledger.", + "keywords": [ + "Hyperledger", + "Cacti", + "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 Cacti Contributors", + "email": "cacti@lists.hyperledger.org", + "url": "https://www.hyperledger.org/use/cacti" + }, + "contributors": [ + { + "name": "Please add yourself to the list of contributors", + "email": "your.name@example.com", + "url": "https://example.com" + }, + { + "name": "Michal Bajer", + "email": "michal.bajer@fujitsu.com", + "url": "https://www.fujitsu.com/global/" + } + ], + "main": "dist/lib/main/typescript/index.js", + "module": "dist/lib/main/typescript/index.js", + "browser": "dist/cactus-plugin-ledger-connector-sawtooth.web.umd.js", + "types": "dist/lib/main/typescript/index.d.ts", + "files": [ + "dist/*" + ], + "scripts": { + "codegen": "run-p 'codegen:*'", + "codegen:openapi": "npm run generate-sdk", + "generate-sdk": "run-p 'generate-sdk:*'", + "generate-sdk:typescript-axios": "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", + "generate-sdk:sawtooth-axios": "openapi-generator-cli generate -i ./src/main/json/sawtooth-openapi.json -g typescript-axios -o ./src/main/typescript/sawtooth-api/ --reserved-words-mappings protected=protected", + "webpack": "npm-run-all webpack:dev", + "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" + }, + "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", + "axios": "1.5.1", + "cbor": "9.0.1", + "rxjs": "7.8.1", + "socket.io-client": "4.5.4" + }, + "devDependencies": { + "@hyperledger/cactus-test-tooling": "2.0.0-alpha.2", + "@types/express": "4.17.20", + "@types/uuid": "9.0.6", + "body-parser": "1.20.2", + "express": "4.18.2", + "socket.io": "4.5.4", + "uuid": "9.0.1" + }, + "engines": { + "node": ">=16", + "npm": ">=8" + }, + "publishConfig": { + "access": "public" + }, + "browserMinified": "dist/cactus-plugin-ledger-connector-sawtooth.web.umd.min.js", + "mainMinified": "dist/cactus-plugin-ledger-connector-sawtooth.node.umd.min.js", + "watch": { + "codegen:openapi": { + "patterns": [ + "./src/main/json/openapi.json" + ] + } + } +} diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/json/openapi.json b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/json/openapi.json new file mode 100644 index 0000000000..44f7a1b40a --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/json/openapi.json @@ -0,0 +1,363 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Hyperledger Cacti Plugin - Connector Sawtooth", + "description": "Can perform basic tasks on a Sawtooth ledger", + "version": "v2.0.0-alpha.2", + "license": { + "name": "Apache-2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "components": { + "schemas": { + "SawtoothTransactionHeaderV1": { + "required": [ + "batcher_public_key", + "dependencies", + "family_name", + "family_version", + "inputs", + "nonce", + "outputs", + "payload_sha512", + "signer_public_key" + ], + "properties": { + "batcher_public_key": { + "type": "string", + "example": "02d260a46457a064733153e09840c322bee1dff34445d7d49e19e60abd18fd0758" + }, + "dependencies": { + "type": "array", + "items": { + "type": "string", + "example": "1baee350bdb60bcee60e3d325d43283cf830b4c23b2cb17d3bb43935bd7af3761c2bee79847c72a9e396a9ae58f48add4e43f94eb83f84442c6085c1dd5d4dbe" + } + }, + "family_name": { + "type": "string", + "example": "intkey" + }, + "family_version": { + "type": "string", + "example": "1.0" + }, + "inputs": { + "type": "array", + "items": { + "type": "string", + "example": "1cf12650d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c" + } + }, + "nonce": { + "type": "string", + "example": "QAApS4L" + }, + "outputs": { + "type": "array", + "items": { + "type": "string", + "example": "1cf12650d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c" + } + }, + "payload_sha512": { + "type": "string", + "example": "fb6135ef73f4fe77367f9384b3bbbb158f4b8603c9d612157108e5c271868fce2242ee4abd7a29397ba63780c3ccab13783dfd4d9f0167beda03cdb0e37b87f4" + }, + "signer_public_key": { + "type": "string", + "example": "038bba5708acc262464c9fe30d3de9e905a9a5fa30cedd151dd9cd09ea26d46d00" + } + } + }, + "SawtoothTransactionV1": { + "required": ["header", "header_signature", "payload"], + "properties": { + "header": { + "$ref": "#/components/schemas/SawtoothTransactionHeaderV1" + }, + "header_signature": { + "type": "string", + "example": "540a6803971d1880ec73a96cb97815a95d374cbad5d865925e5aa0432fcf1931539afe10310c122c5eaae15df61236079abbf4f258889359c4d175516934484a" + }, + "payload": { + "type": "string" + } + } + }, + "SawtoothBatchHeaderV1": { + "required": ["signer_public_key", "transaction_ids"], + "properties": { + "signer_public_key": { + "type": "string", + "example": "038bba5708acc262464c9fe30d3de9e905a9a5fa30cedd151dd9cd09ea26d46d00" + }, + "transaction_ids": { + "type": "array", + "items": { + "type": "string", + "example": "540a6803971d1880ec73a96cb97815a95d374cbad5d865925e5aa0432fcf1931539afe10310c122c5eaae15df61236079abbf4f258889359c4d175516934484a" + } + } + } + }, + "SawtoothBatchV1": { + "required": ["header", "header_signature", "transactions"], + "properties": { + "header": { + "$ref": "#/components/schemas/SawtoothBatchHeaderV1" + }, + "header_signature": { + "type": "string", + "example": "89807bfc9089e37e00d87d97357de14cfbc455cd608438d426a625a30a0da9a31c406983803c4aa27e1f32a3ff61709e8ec4b56abbc553d7d330635b5d27029c" + }, + "transactions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SawtoothTransactionV1" + } + } + } + }, + "SawtoothBlockHeaderV1": { + "required": [ + "batch_ids", + "block_num", + "consensus", + "previous_block_id", + "signer_public_key", + "state_root_hash" + ], + "properties": { + "block_num": { + "type": "integer", + "example": 12345 + }, + "previous_block_id": { + "type": "string", + "example": "65cd3a3ce088b265b626f704b7f3db97b6f12e848dccb35d7806f3d0324c71b709ed360d602b8b658b94695374717e3bdb4b76f77886953777d5d008558247dd" + }, + "signer_public_key": { + "type": "string", + "example": "02d260a46457a064733153e09840c322bee1dff34445d7d49e19e60abd18fd0758" + }, + "batch_ids": { + "type": "array", + "items": { + "type": "string", + "example": "89807bfc9089e37e00d87d97357de14cfbc455cd608438d426a625a30a0da9a31c406983803c4aa27e1f32a3ff61709e8ec4b56abbc553d7d330635b5d27029c" + } + }, + "consensus": { + "type": "string" + }, + "state_root_hash": { + "type": "string", + "example": "708ca7fbb701799bb387f2e50deaca402e8502abe229f705693d2d4f350e1ad6" + } + } + }, + "SawtoothBlockV1": { + "required": ["header", "header_signature", "batches"], + "properties": { + "header": { + "$ref": "#/components/schemas/SawtoothBlockHeaderV1" + }, + "header_signature": { + "type": "string", + "example": "65cd3a3ce088b265b626f704b7f3db97b6f12e848dccb35d7806f3d0324c71b709ed360d602b8b658b94695374717e3bdb4b76f77886953777d5d008558247dd" + }, + "batches": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SawtoothBatchV1" + } + } + } + }, + "CactiTransactionV1": { + "description": "Sawtooth transaction with additional fields filled by Cacti connector.", + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/SawtoothTransactionV1" + }, + { + "type": "object", + "required": ["payload_decoded"], + "properties": { + "payload_decoded": { + "description": "Decoded payload of sawtooth transaction.", + "example": "[{'Verb':'inc','Name':'monitorTest3','Value':11}]}]" + } + } + } + ] + }, + "WatchBlocksV1": { + "type": "string", + "enum": [ + "org.hyperledger.cacti.api.async.sawtooth.WatchBlocksV1.Subscribe", + "org.hyperledger.cacti.api.async.sawtooth.WatchBlocksV1.Next", + "org.hyperledger.cacti.api.async.sawtooth.WatchBlocksV1.Unsubscribe", + "org.hyperledger.cacti.api.async.sawtooth.WatchBlocksV1.Error", + "org.hyperledger.cacti.api.async.sawtooth.WatchBlocksV1.Complete" + ], + "x-enum-varnames": [ + "Subscribe", + "Next", + "Unsubscribe", + "Error", + "Complete" + ] + }, + "WatchBlocksV1ListenerType": { + "type": "string", + "description": "Response type from WatchBlocks. 'Cacti*' are custom views, others correspond to plain sawtooth data.", + "enum": ["full", "cacti:transactions"], + "x-enum-varnames": ["Full", "CactiTransactions"] + }, + "WatchBlocksV1TransactionFilter": { + "type": "object", + "properties": { + "family_name": { + "type": "string" + } + } + }, + "WatchBlocksV1Options": { + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/WatchBlocksV1ListenerType", + "description": "Type of response block to return.", + "nullable": false + }, + "txFilterBy": { + "$ref": "#/components/schemas/WatchBlocksV1TransactionFilter", + "description": "Filter specification for transactions (only used when selected CactiTransactions response type).", + "nullable": false + } + } + }, + "WatchBlocksV1CactiTransactionsResponse": { + "type": "object", + "description": "Custom response containing block transactions summary.", + "required": ["cactiTransactionsEvents"], + "properties": { + "cactiTransactionsEvents": { + "description": "List of sawtooth transactions matching specifid (optional) filter", + "type": "array", + "items": { + "$ref": "#/components/schemas/CactiTransactionV1", + "nullable": false + } + } + } + }, + "WatchBlocksV1FullResponse": { + "type": "object", + "description": "Response that returns entire sawtooth block.", + "required": ["fullBlock"], + "properties": { + "fullBlock": { + "description": "Full commited block.", + "$ref": "#/components/schemas/SawtoothBlockV1", + "nullable": false + } + } + }, + "WatchBlocksV1Progress": { + "description": "Response block from WatchBlocks endpoint. Depends on 'type' passed in subscription options.", + "oneOf": [ + { + "$ref": "#/components/schemas/WatchBlocksV1CactiTransactionsResponse", + "nullable": false + }, + { + "$ref": "#/components/schemas/WatchBlocksV1FullResponse", + "nullable": false + } + ] + }, + "StatusResponseV1": { + "description": "Response with plugin and validator status report.", + "type": "object", + "required": ["instanceId"], + "properties": { + "instanceId": { + "type": "string", + "nullable": false, + "description": "Plugin instance id." + }, + "openApiSpecVersion": { + "type": "string", + "nullable": false, + "description": "Version of connectors Open API Spec." + }, + "initialized": { + "type": "boolean", + "nullable": false, + "description": "True if endpoints were created, false otherwise" + }, + "sawtoothStatus": { + "nullable": false, + "description": "Response from sawtooth Rest API status endpoint" + } + } + }, + "ErrorExceptionResponseV1": { + "type": "object", + "required": ["message", "error"], + "properties": { + "message": { + "type": "string", + "nullable": false + }, + "error": { + "type": "string", + "nullable": false + } + } + } + } + }, + "paths": { + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-sawtooth/status": { + "get": { + "x-hyperledger-cactus": { + "http": { + "verbLowerCase": "get", + "path": "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-sawtooth/status" + } + }, + "operationId": "getStatusV1", + "summary": "Get the status of the connector and the sawtooth validator", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/StatusResponseV1" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorExceptionResponseV1" + } + } + } + } + } + } + } + } +} diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/json/sawtooth-openapi.json b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/json/sawtooth-openapi.json new file mode 100644 index 0000000000..a1a81a995a --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/json/sawtooth-openapi.json @@ -0,0 +1,1225 @@ +{ + "openapi": "3.0.0", + "info": { + "version": "0.8.0", + "title": "Sawtooth REST API", + "description": "Official Sawtooth REST API specification with some modifications from Hyperledger Cacti project." + }, + "paths": { + "/batches": { + "post": { + "summary": "Sends a BatchList to the validator", + "description": "Accepts a protobuf formatted `BatchList` as an octet-stream binary\nfile and submits it to the validator to be committed.\n\nThe API will return immediately with a status of `202`. There will be\nno `data` object, only a `link` to a `/batch_statuses` endpoint to be\npolled to check the status of submitted batches.\n", + "requestBody": { + "content": { + "application/octet-stream": { + "schema": { + "$ref": "#/components/schemas/BatchList" + } + } + }, + "description": "A binary encoded protobuf BatchList", + "required": true + }, + "responses": { + "202": { + "description": "Batches submitted for validation, but not yet committed", + "content": { + "*/*": { + "schema": { + "properties": { + "link": { + "$ref": "#/components/schemas/Link" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/400BadRequest" + }, + "429": { + "$ref": "#/components/responses/429TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/500ServerError" + }, + "503": { + "$ref": "#/components/responses/503ServiceUnavailable" + } + } + }, + "get": { + "summary": "Fetches a list of batches", + "description": "Fetches a paginated list of batches from the validator.\n", + "parameters": [ + { + "$ref": "#/components/parameters/head" + }, + { + "$ref": "#/components/parameters/start" + }, + { + "$ref": "#/components/parameters/limit" + }, + { + "$ref": "#/components/parameters/reverse" + } + ], + "responses": { + "200": { + "description": "Successfully retrieved batches", + "content": { + "*/*": { + "schema": { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Batch" + } + }, + "head": { + "$ref": "#/components/schemas/Head" + }, + "link": { + "$ref": "#/components/schemas/Link" + }, + "paging": { + "$ref": "#/components/schemas/Paging" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/400BadRequest" + }, + "500": { + "$ref": "#/components/responses/500ServerError" + }, + "503": { + "$ref": "#/components/responses/503ServiceUnavailable" + } + } + } + }, + "/batches/{batch_id}": { + "parameters": [ + { + "$ref": "#/components/parameters/batch_id" + } + ], + "get": { + "summary": "Fetches a particular batch", + "responses": { + "200": { + "description": "Successfully retrieved batch", + "content": { + "*/*": { + "schema": { + "properties": { + "data": { + "$ref": "#/components/schemas/Batch" + }, + "head": { + "$ref": "#/components/schemas/Head" + }, + "link": { + "$ref": "#/components/schemas/Link" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/400BadRequest" + }, + "404": { + "$ref": "#/components/responses/404NotFound" + }, + "500": { + "$ref": "#/components/responses/500ServerError" + }, + "503": { + "$ref": "#/components/responses/503ServiceUnavailable" + } + } + } + }, + "/batch_statuses": { + "get": { + "summary": "Fetches the committed statuses for a set of batches", + "description": "Fetches an array of objects with a status and id for each batch\nrequested. There are four possible statuses with string values\n`'COMMITTED'`, `'INVALID'`, `'PENDING'`, and `'UNKNOWN'`.\n\nThe batch(es) you want to check can be specified using the `id` filter\nparameter. If a `wait` time is specified in the URL, the API will wait\nto respond until all batches are committed, or the time in seconds has\nelapsed. If the value of `wait` is not set (i.e., `?wait&id=...`), or\nit is set to any non-integer value other than `false`, the wait time\nwill be just under the API's specified timeout (usually 300).\n\nNote that because this route does not return full resources, the\nresponse will not be paginated, and there will be no `head` or\n`paging` properties.\n", + "parameters": [ + { + "name": "id", + "in": "query", + "description": "A comma-separated list of batch ids", + "required": true, + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/wait" + } + ], + "responses": { + "200": { + "description": "Successfully retrieved statuses", + "content": { + "*/*": { + "schema": { + "properties": { + "data": { + "$ref": "#/components/schemas/BatchStatuses" + }, + "link": { + "$ref": "#/components/schemas/Link" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/400BadRequest" + }, + "500": { + "$ref": "#/components/responses/500ServerError" + }, + "503": { + "$ref": "#/components/responses/503ServiceUnavailable" + } + } + }, + "post": { + "summary": "Fetches the committed statuses for a set of batches", + "description": "Identical to `GET /batch_statuses`, but takes ids of batches as a JSON\nformatted POST body rather than a query parameter. This allows for many\nmore batches to be checked and should be used for more than 15 ids.\n\nNote that because query information is not encoded in the URL, no `link`\nwill be returned with this query.\n", + "parameters": [ + { + "$ref": "#/components/parameters/wait" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string", + "example": "89807bfc9089e37e00d87d97357de14cfbc455cd608438d426a625a30a0da9a31c406983803c4aa27e1f32a3ff61709e8ec4b56abbc553d7d330635b5d27029c" + } + } + } + }, + "description": "A JSON array of batch id strings", + "required": true + }, + "responses": { + "200": { + "description": "Successfully retrieved statuses", + "content": { + "*/*": { + "schema": { + "properties": { + "data": { + "$ref": "#/components/schemas/BatchStatuses" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/400BadRequest" + }, + "500": { + "$ref": "#/components/responses/500ServerError" + }, + "503": { + "$ref": "#/components/responses/503ServiceUnavailable" + } + } + } + }, + "/state": { + "get": { + "summary": "Fetches the data for the current state", + "description": "Fetches a paginated list of entries for the current state, or relative to a particular head block. Using the `address` filter parameter will narrow the list to any entries that have an address beginning with the characters specified.\nNote that the partial address in `address` parameter should have even number of hexadecimal characters (i.e., complete bytes).\n", + "parameters": [ + { + "$ref": "#/components/parameters/head" + }, + { + "name": "address", + "in": "query", + "description": "A partial address to filter leaves by", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/start" + }, + { + "$ref": "#/components/parameters/limit" + }, + { + "$ref": "#/components/parameters/reverse" + } + ], + "responses": { + "200": { + "description": "Successfully retrieved state data", + "content": { + "*/*": { + "schema": { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Entry" + } + }, + "head": { + "$ref": "#/components/schemas/Head" + }, + "link": { + "$ref": "#/components/schemas/Link" + }, + "paging": { + "$ref": "#/components/schemas/Paging" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/400BadRequest" + }, + "500": { + "$ref": "#/components/responses/500ServerError" + }, + "503": { + "$ref": "#/components/responses/503ServiceUnavailable" + } + } + } + }, + "/state/{address}": { + "parameters": [ + { + "$ref": "#/components/parameters/address" + } + ], + "get": { + "summary": "Fetches a particular leaf from the current state", + "description": "Takes full 70-character address and fetches a particular leaf. For partial address (i.e., group of leaves) use `/state` above. \n", + "parameters": [ + { + "$ref": "#/components/parameters/head" + } + ], + "responses": { + "200": { + "description": "Successfully fetched leaves", + "content": { + "*/*": { + "schema": { + "properties": { + "data": { + "type": "string", + "format": "byte", + "example": "Zm9vOmJhcg==" + }, + "head": { + "$ref": "#/components/schemas/Head" + }, + "link": { + "$ref": "#/components/schemas/Link" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/400BadRequest" + }, + "404": { + "$ref": "#/components/responses/404NotFound" + }, + "500": { + "$ref": "#/components/responses/500ServerError" + }, + "503": { + "$ref": "#/components/responses/503ServiceUnavailable" + } + } + } + }, + "/blocks": { + "get": { + "summary": "Fetches a list of blocks", + "description": "Fetches a paginated list of blocks from the validator.\n", + "parameters": [ + { + "$ref": "#/components/parameters/head" + }, + { + "$ref": "#/components/parameters/start" + }, + { + "$ref": "#/components/parameters/limit" + }, + { + "$ref": "#/components/parameters/reverse" + } + ], + "responses": { + "200": { + "description": "Successfully retrieved blocks", + "content": { + "*/*": { + "schema": { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Block" + } + }, + "head": { + "$ref": "#/components/schemas/Head" + }, + "link": { + "$ref": "#/components/schemas/Link" + }, + "paging": { + "$ref": "#/components/schemas/Paging" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/400BadRequest" + }, + "500": { + "$ref": "#/components/responses/500ServerError" + }, + "503": { + "$ref": "#/components/responses/503ServiceUnavailable" + } + } + } + }, + "/blocks/{block_id}": { + "parameters": [ + { + "$ref": "#/components/parameters/block_id" + } + ], + "get": { + "summary": "Fetches a particular block", + "responses": { + "200": { + "description": "Successfully retrieved block", + "content": { + "*/*": { + "schema": { + "properties": { + "data": { + "$ref": "#/components/schemas/Block" + }, + "head": { + "$ref": "#/components/schemas/Head" + }, + "link": { + "$ref": "#/components/schemas/Link" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/400BadRequest" + }, + "404": { + "$ref": "#/components/responses/404NotFound" + }, + "500": { + "$ref": "#/components/responses/500ServerError" + }, + "503": { + "$ref": "#/components/responses/503ServiceUnavailable" + } + } + } + }, + "/transactions": { + "get": { + "summary": "Fetches a list of transactions", + "description": "Fetches a paginated list of transactions from the validator.\n", + "parameters": [ + { + "$ref": "#/components/parameters/head" + }, + { + "$ref": "#/components/parameters/start" + }, + { + "$ref": "#/components/parameters/limit" + }, + { + "$ref": "#/components/parameters/reverse" + } + ], + "responses": { + "200": { + "description": "Successfully retrieved transactions", + "content": { + "*/*": { + "schema": { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Transaction" + } + }, + "head": { + "$ref": "#/components/schemas/Head" + }, + "link": { + "$ref": "#/components/schemas/Link" + }, + "paging": { + "$ref": "#/components/schemas/Paging" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/400BadRequest" + }, + "500": { + "$ref": "#/components/responses/500ServerError" + }, + "503": { + "$ref": "#/components/responses/503ServiceUnavailable" + } + } + } + }, + "/transactions/{transaction_id}": { + "parameters": [ + { + "$ref": "#/components/parameters/transaction_id" + } + ], + "get": { + "summary": "Fetches a particular transaction", + "responses": { + "200": { + "description": "Successfully retrieved transaction", + "content": { + "*/*": { + "schema": { + "properties": { + "data": { + "$ref": "#/components/schemas/Block" + }, + "head": { + "$ref": "#/components/schemas/Head" + }, + "link": { + "$ref": "#/components/schemas/Link" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/400BadRequest" + }, + "404": { + "$ref": "#/components/responses/404NotFound" + }, + "500": { + "$ref": "#/components/responses/500ServerError" + }, + "503": { + "$ref": "#/components/responses/503ServiceUnavailable" + } + } + } + }, + "/receipts": { + "get": { + "summary": "Fetches the receipts for a set of transactions", + "description": "Fetches an array of objects for each receipt requested.\n\nThe receipt(s) you want to retrieve can be specified using the `id`\nfilter parameter, where `id` refers to the transaction id of the\ntransaction the receipt is associated with.\n", + "parameters": [ + { + "name": "id", + "in": "query", + "description": "A comma-separated list of transaction ids", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successfully retrieved transaction receipts", + "content": { + "*/*": { + "schema": { + "properties": { + "data": { + "$ref": "#/components/schemas/TransactionReceipts" + }, + "link": { + "$ref": "#/components/schemas/Link" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/400BadRequest" + }, + "500": { + "$ref": "#/components/responses/500ServerError" + }, + "503": { + "$ref": "#/components/responses/503ServiceUnavailable" + } + } + }, + "post": { + "summary": "Fetches the receipts for a set of transactions", + "description": "Identical to `GET /receipts`, but takes ids of transactions as a JSON\nformatted POST body rather than a query parameter. This allows for many\nmore receipts to be fetched and should be used with more than 15 ids.\n\nNote that because query information is not encoded in the URL, no `link`\nwill be returned with this request.\n", + "parameters": [ + { + "$ref": "#/components/parameters/wait" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string", + "example": "89807bfc9089e37e00d87d97357de14cfbc455cd608438d426a625a30a0da9a31c406983803c4aa27e1f32a3ff61709e8ec4b56abbc553d7d330635b5d27029c" + } + } + } + }, + "description": "A JSON array of transaction id strings", + "required": true + }, + "responses": { + "200": { + "description": "Successfully retrieved transaction receipts", + "content": { + "*/*": { + "schema": { + "properties": { + "data": { + "$ref": "#/components/schemas/TransactionReceipts" + }, + "link": { + "$ref": "#/components/schemas/Link" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/400BadRequest" + }, + "500": { + "$ref": "#/components/responses/500ServerError" + }, + "503": { + "$ref": "#/components/responses/503ServiceUnavailable" + } + } + } + }, + "/peers": { + "get": { + "summary": "Fetches the endpoints of the authorized peers of the validator", + "responses": { + "200": { + "description": "Successfully retrieved peers", + "content": { + "*/*": { + "schema": { + "properties": { + "data": { + "type": "array", + "items": { + "type": "string", + "example": "tcp://12.345.67.890:8800" + } + }, + "link": { + "$ref": "#/components/schemas/Link" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/400BadRequest" + }, + "500": { + "$ref": "#/components/responses/500ServerError" + }, + "503": { + "$ref": "#/components/responses/503ServiceUnavailable" + } + } + } + }, + "/status": { + "get": { + "summary": "Fetches information pertaining to the status of the validator", + "responses": { + "200": { + "description": "Successfully retrieved status", + "content": { + "*/*": { + "schema": { + "properties": { + "data": { + "type": "object", + "properties": { + "endpoint": { + "type": "string", + "example": "tcp://12.345.67.890:8800" + }, + "peers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "endpoint": { + "type": "string", + "example": "tcp://12.345.67.890:8800" + } + } + } + } + } + }, + "link": { + "$ref": "#/components/schemas/Link" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/400BadRequest" + }, + "500": { + "$ref": "#/components/responses/500ServerError" + }, + "503": { + "$ref": "#/components/responses/503ServiceUnavailable" + } + } + } + } + }, + "components": { + "parameters": { + "address": { + "name": "address", + "in": "path", + "required": true, + "description": "Radix address of a leaf", + "schema": { + "type": "string" + } + }, + "block_id": { + "name": "block_id", + "in": "path", + "required": true, + "description": "Block id", + "schema": { + "type": "string" + } + }, + "batch_id": { + "name": "batch_id", + "in": "path", + "required": true, + "description": "Batch id", + "schema": { + "type": "string" + } + }, + "transaction_id": { + "name": "transaction_id", + "in": "path", + "required": true, + "description": "Transaction id", + "schema": { + "type": "string" + } + }, + "head": { + "name": "head", + "in": "query", + "description": "Index or id of head block", + "schema": { + "type": "string", + "default": "latest" + } + }, + "wait": { + "name": "wait", + "in": "query", + "description": "A time in seconds to wait for commit", + "schema": { + "type": "integer" + } + }, + "limit": { + "name": "limit", + "in": "query", + "description": "Number of items to return", + "schema": { + "type": "integer", + "default": 1000 + } + }, + "start": { + "name": "start", + "in": "query", + "description": "Id to start paging (inclusive)", + "schema": { + "type": "string" + } + }, + "reverse": { + "name": "reverse", + "in": "query", + "description": "If the list should be reversed", + "schema": { + "type": "string" + } + } + }, + "responses": { + "400BadRequest": { + "description": "Request was malformed", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "404NotFound": { + "description": "Address or id did not match any resource", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "429TooManyRequests": { + "description": "Too many requests have been made to process batches", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "500ServerError": { + "description": "Something went wrong within the validator", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "503ServiceUnavailable": { + "description": "API is unable to reach the validator", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + }, + "schemas": { + "Head": { + "type": "string", + "example": "65cd3a3ce088b265b626f704b7f3db97b6f12e848dccb35d7806f3d0324c71b709ed360d602b8b658b94695374717e3bdb4b76f77886953777d5d008558247dd" + }, + "Link": { + "type": "string", + "example": "https://api.sawtooth.com/state?head=65cd3a3ce088b265b626f704b7f3db97b6f12e848dccb35d7806f3d0324c71b709ed360d602b8b658b94695374717e3bdb4b76f77886953777d5d008558247dd" + }, + "Paging": { + "properties": { + "start": { + "type": "string", + "example": "65cd3a3ce088b265b626f704b7f3db97b6f12e848dccb35d7806f3d0324c71b709ed360d602b8b658b94695374717e3bdb4b76f77886953777d5d008558247dd" + }, + "limit": { + "type": "integer", + "example": 54321 + }, + "next_position": { + "type": "string", + "example": "65cd3a3ce088b265b626f704b7f3db97b6f12e848dccb35d7806f3d0324c71b709ed360d602b8b658b94695374717e3bdb4b76f77886953777d5d008558247dd" + }, + "next": { + "type": "string", + "example": "https://api.sawtooth.com/state?head=65cd3a3ce088b265b626f704b7f3db97b6f12e848dccb35d7806f3d0324c71b709ed360d602b8b658b94695374717e3bdb4b76f77886953777d5d008558247dd&start=2000&limit=1000" + } + } + }, + "Error": { + "properties": { + "code": { + "type": "integer", + "example": 34 + }, + "title": { + "type": "string", + "example": "No Batches Submitted" + }, + "message": { + "type": "string", + "example": "The protobuf BatchList you submitted was empty and contained no Batches. You must submit at least one Batch.\n" + } + } + }, + "BatchStatuses": { + "type": "array", + "items": { + "properties": { + "id": { + "type": "string", + "example": "89807bfc9089e37e00d87d97357de14cfbc455cd608438d426a625a30a0da9a31c406983803c4aa27e1f32a3ff61709e8ec4b56abbc553d7d330635b5d27029c" + }, + "status": { + "type": "string", + "example": "INVALID", + "enum": ["COMMITTED", "INVALID", "PENDING", "UNKNOWN"] + }, + "invalid_transactions": { + "type": "array", + "items": { + "properties": { + "id": { + "type": "string", + "example": "540a6803971d1880ec73a96cb97815a95d374cbad5d865925e5aa0432fcf1931539afe10310c122c5eaae15df61236079abbf4f258889359c4d175516934484a" + }, + "message": { + "type": "string", + "example": "Verb is \\\"inc\\\" but name \\\"foo\\\" not in state" + }, + "extended_data": { + "type": "string", + "format": "byte", + "example": "ZXJyb3IgZGF0YQ==" + } + } + } + } + } + } + }, + "Entry": { + "properties": { + "address": { + "type": "string", + "example": "1cf12650d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c" + }, + "data": { + "type": "string", + "format": "byte", + "example": "Zm9vOmJhcg==" + } + } + }, + "TransactionHeader": { + "required": [ + "batcher_public_key", + "dependencies", + "family_name", + "family_version", + "inputs", + "nonce", + "outputs", + "payload_sha512", + "signer_public_key" + ], + "properties": { + "batcher_public_key": { + "type": "string", + "example": "02d260a46457a064733153e09840c322bee1dff34445d7d49e19e60abd18fd0758" + }, + "dependencies": { + "type": "array", + "items": { + "type": "string", + "example": "1baee350bdb60bcee60e3d325d43283cf830b4c23b2cb17d3bb43935bd7af3761c2bee79847c72a9e396a9ae58f48add4e43f94eb83f84442c6085c1dd5d4dbe" + } + }, + "family_name": { + "type": "string", + "example": "intkey" + }, + "family_version": { + "type": "string", + "example": "1.0" + }, + "inputs": { + "type": "array", + "items": { + "type": "string", + "example": "1cf12650d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c" + } + }, + "nonce": { + "type": "string", + "example": "QAApS4L" + }, + "outputs": { + "type": "array", + "items": { + "type": "string", + "example": "1cf12650d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c" + } + }, + "payload_sha512": { + "type": "string", + "example": "fb6135ef73f4fe77367f9384b3bbbb158f4b8603c9d612157108e5c271868fce2242ee4abd7a29397ba63780c3ccab13783dfd4d9f0167beda03cdb0e37b87f4" + }, + "signer_public_key": { + "type": "string", + "example": "038bba5708acc262464c9fe30d3de9e905a9a5fa30cedd151dd9cd09ea26d46d00" + } + } + }, + "Transaction": { + "required": ["header", "header_signature", "payload"], + "properties": { + "header": { + "$ref": "#/components/schemas/TransactionHeader" + }, + "header_signature": { + "type": "string", + "example": "540a6803971d1880ec73a96cb97815a95d374cbad5d865925e5aa0432fcf1931539afe10310c122c5eaae15df61236079abbf4f258889359c4d175516934484a" + }, + "payload": { + "type": "string" + } + } + }, + "TransactionReceipt": { + "properties": { + "transaction_id": { + "type": "string", + "example": "540a6803971d1880ec73a96cb97815a95d374cbad5d865925e5aa0432fcf1931539afe10310c122c5eaae15df61236079abbf4f258889359c4d175516934484a" + }, + "state_changes": { + "type": "array", + "items": { + "properties": { + "type": { + "type": "string", + "example": "SET" + }, + "address": { + "type": "string", + "example": "1cf12650d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c" + }, + "value": { + "type": "string" + } + } + } + }, + "events": { + "type": "array", + "items": { + "properties": { + "event_type": { + "type": "string", + "example": "sawtooth/block-commit" + }, + "attributes": { + "type": "array", + "items": { + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + } + } + }, + "data": { + "type": "string" + } + } + } + }, + "data": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "TransactionReceipts": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TransactionReceipt" + } + }, + "BatchHeader": { + "required": ["signer_public_key", "transaction_ids"], + "properties": { + "signer_public_key": { + "type": "string", + "example": "038bba5708acc262464c9fe30d3de9e905a9a5fa30cedd151dd9cd09ea26d46d00" + }, + "transaction_ids": { + "type": "array", + "items": { + "type": "string", + "example": "540a6803971d1880ec73a96cb97815a95d374cbad5d865925e5aa0432fcf1931539afe10310c122c5eaae15df61236079abbf4f258889359c4d175516934484a" + } + } + } + }, + "Batch": { + "required": ["header", "header_signature", "transactions"], + "properties": { + "header": { + "$ref": "#/components/schemas/BatchHeader" + }, + "header_signature": { + "type": "string", + "example": "89807bfc9089e37e00d87d97357de14cfbc455cd608438d426a625a30a0da9a31c406983803c4aa27e1f32a3ff61709e8ec4b56abbc553d7d330635b5d27029c" + }, + "transactions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Transaction" + } + } + } + }, + "BatchList": { + "required": ["batches"], + "properties": { + "batches": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Batch" + } + } + } + }, + "BlockHeader": { + "required": [ + "batch_ids", + "block_num", + "consensus", + "previous_block_id", + "signer_public_key", + "state_root_hash" + ], + "properties": { + "block_num": { + "type": "integer", + "example": 12345 + }, + "previous_block_id": { + "type": "string", + "example": "65cd3a3ce088b265b626f704b7f3db97b6f12e848dccb35d7806f3d0324c71b709ed360d602b8b658b94695374717e3bdb4b76f77886953777d5d008558247dd" + }, + "signer_public_key": { + "type": "string", + "example": "02d260a46457a064733153e09840c322bee1dff34445d7d49e19e60abd18fd0758" + }, + "batch_ids": { + "type": "array", + "items": { + "type": "string", + "example": "89807bfc9089e37e00d87d97357de14cfbc455cd608438d426a625a30a0da9a31c406983803c4aa27e1f32a3ff61709e8ec4b56abbc553d7d330635b5d27029c" + } + }, + "consensus": { + "type": "string" + }, + "state_root_hash": { + "type": "string", + "example": "708ca7fbb701799bb387f2e50deaca402e8502abe229f705693d2d4f350e1ad6" + } + } + }, + "Block": { + "required": ["header", "header_signature", "batches"], + "properties": { + "header": { + "$ref": "#/components/schemas/BlockHeader" + }, + "header_signature": { + "type": "string", + "example": "65cd3a3ce088b265b626f704b7f3db97b6f12e848dccb35d7806f3d0324c71b709ed360d602b8b658b94695374717e3bdb4b76f77886953777d5d008558247dd" + }, + "batches": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Batch" + } + } + } + } + } + } +} diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/api-client/sawtooth-api-client.ts b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/api-client/sawtooth-api-client.ts new file mode 100644 index 0000000000..6400c94c00 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/api-client/sawtooth-api-client.ts @@ -0,0 +1,117 @@ +import { Observable, ReplaySubject } from "rxjs"; +import { finalize } from "rxjs/operators"; +import { io } from "socket.io-client"; +import { + Logger, + Checks, + LogLevelDesc, + LoggerProvider, +} from "@hyperledger/cactus-common"; +import { Constants, ISocketApiClient } from "@hyperledger/cactus-core-api"; +import { + DefaultApi, + WatchBlocksV1, + WatchBlocksV1Options, + WatchBlocksV1Progress, + Configuration, +} from "../generated/openapi/typescript-axios"; + +export class SawtoothApiClientOptions extends Configuration { + readonly logLevel?: LogLevelDesc; + readonly wsApiHost?: string; + readonly wsApiPath?: string; +} + +export class SawtoothApiClient + extends DefaultApi + implements ISocketApiClient +{ + private readonly log: Logger; + private readonly wsApiHost: string; + private readonly wsApiPath: string; + + /** + * Registry of started monitoring sessions. + */ + private monitorSubjects = new Map< + string, + ReplaySubject + >(); + + public get className(): string { + return "SawtoothApiClient"; + } + + constructor(public readonly options: SawtoothApiClientOptions) { + super(options); + const fnTag = `${this.className}#constructor()`; + Checks.truthy(options, `${fnTag} arg options`); + + const level = this.options.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + + this.wsApiHost = options.wsApiHost || options.basePath || location.host; + this.wsApiPath = options.wsApiPath || Constants.SocketIoConnectionPathV1; + this.log.debug(`Created ${this.className} OK.`); + this.log.debug(`wsApiHost=${this.wsApiHost}`); + this.log.debug(`wsApiPath=${this.wsApiPath}`); + this.log.debug(`basePath=${this.options.basePath}`); + } + + /** + * Monitor for new blocks or transactions (depends on provided options) + * + * @param options monitoring configuration + * @returns rxjs observable + */ + public watchBlocksV1( + options?: WatchBlocksV1Options, + ): Observable { + const socket = io(this.wsApiHost, { path: this.wsApiPath }); + const subject = new ReplaySubject(0); + + socket.on(WatchBlocksV1.Next, (data: WatchBlocksV1Progress) => { + this.log.debug("Received WatchBlocksV1.Next"); + subject.next(data); + }); + + socket.on(WatchBlocksV1.Error, (ex: string) => { + this.log.warn("Received WatchBlocksV1.Error:", ex); + subject.error(ex); + }); + + socket.on(WatchBlocksV1.Complete, () => { + this.log.debug("Received WatchBlocksV1.Complete"); + subject.complete(); + }); + + socket.on("connect", () => { + this.log.info("Connected OK, sending WatchBlocksV1.Subscribe request..."); + this.monitorSubjects.set(socket.id, subject); + socket.emit(WatchBlocksV1.Subscribe, options); + }); + + socket.connect(); + + return subject.pipe( + finalize(() => { + this.log.info("FINALIZE - unsubscribing from the stream..."); + this.monitorSubjects.delete(socket.id); + socket.emit(WatchBlocksV1.Unsubscribe); + socket.close(); + }), + ); + } + + /** + * Stop all ongoing monitors, terminate connections. + * + * @note Might take few seconds to clean up all the connections. + */ + public close(): void { + this.log.debug("Close all running monitors."); + this.monitorSubjects.forEach((subject) => subject.complete()); + this.monitorSubjects.clear(); + } +} diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.gitignore b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.gitignore new file mode 100644 index 0000000000..149b576547 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.gitignore @@ -0,0 +1,4 @@ +wwwroot/*.js +node_modules +typings +dist diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.npmignore b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.npmignore new file mode 100644 index 0000000000..999d88df69 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.npmignore @@ -0,0 +1 @@ +# empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm \ No newline at end of file diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator-ignore b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator-ignore new file mode 100644 index 0000000000..7484ee590a --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# 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 diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/FILES b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/FILES new file mode 100644 index 0000000000..a80cd4f07b --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/FILES @@ -0,0 +1,8 @@ +.gitignore +.npmignore +api.ts +base.ts +common.ts +configuration.ts +git_push.sh +index.ts diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/VERSION b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/VERSION new file mode 100644 index 0000000000..e7e42a4b58 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/VERSION @@ -0,0 +1 @@ +6.3.0 \ No newline at end of file diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/api.ts b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/api.ts new file mode 100644 index 0000000000..53af0986a0 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/api.ts @@ -0,0 +1,514 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Hyperledger Cacti Plugin - Connector Sawtooth + * Can perform basic tasks on a Sawtooth ledger + * + * 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'; + +/** + * Sawtooth transaction with additional fields filled by Cacti connector. + * @export + * @interface CactiTransactionV1 + */ +export interface CactiTransactionV1 { + /** + * + * @type {SawtoothTransactionHeaderV1} + * @memberof CactiTransactionV1 + */ + 'header': SawtoothTransactionHeaderV1; + /** + * + * @type {string} + * @memberof CactiTransactionV1 + */ + 'header_signature': string; + /** + * + * @type {string} + * @memberof CactiTransactionV1 + */ + 'payload': string; + /** + * Decoded payload of sawtooth transaction. + * @type {any} + * @memberof CactiTransactionV1 + */ + 'payload_decoded': any; +} +/** + * + * @export + * @interface CactiTransactionV1AllOf + */ +export interface CactiTransactionV1AllOf { + /** + * Decoded payload of sawtooth transaction. + * @type {any} + * @memberof CactiTransactionV1AllOf + */ + 'payload_decoded': any; +} +/** + * + * @export + * @interface ErrorExceptionResponseV1 + */ +export interface ErrorExceptionResponseV1 { + /** + * + * @type {string} + * @memberof ErrorExceptionResponseV1 + */ + 'message': string; + /** + * + * @type {string} + * @memberof ErrorExceptionResponseV1 + */ + 'error': string; +} +/** + * + * @export + * @interface SawtoothBatchHeaderV1 + */ +export interface SawtoothBatchHeaderV1 { + /** + * + * @type {string} + * @memberof SawtoothBatchHeaderV1 + */ + 'signer_public_key': string; + /** + * + * @type {Array} + * @memberof SawtoothBatchHeaderV1 + */ + 'transaction_ids': Array; +} +/** + * + * @export + * @interface SawtoothBatchV1 + */ +export interface SawtoothBatchV1 { + /** + * + * @type {SawtoothBatchHeaderV1} + * @memberof SawtoothBatchV1 + */ + 'header': SawtoothBatchHeaderV1; + /** + * + * @type {string} + * @memberof SawtoothBatchV1 + */ + 'header_signature': string; + /** + * + * @type {Array} + * @memberof SawtoothBatchV1 + */ + 'transactions': Array; +} +/** + * + * @export + * @interface SawtoothBlockHeaderV1 + */ +export interface SawtoothBlockHeaderV1 { + /** + * + * @type {number} + * @memberof SawtoothBlockHeaderV1 + */ + 'block_num': number; + /** + * + * @type {string} + * @memberof SawtoothBlockHeaderV1 + */ + 'previous_block_id': string; + /** + * + * @type {string} + * @memberof SawtoothBlockHeaderV1 + */ + 'signer_public_key': string; + /** + * + * @type {Array} + * @memberof SawtoothBlockHeaderV1 + */ + 'batch_ids': Array; + /** + * + * @type {string} + * @memberof SawtoothBlockHeaderV1 + */ + 'consensus': string; + /** + * + * @type {string} + * @memberof SawtoothBlockHeaderV1 + */ + 'state_root_hash': string; +} +/** + * + * @export + * @interface SawtoothBlockV1 + */ +export interface SawtoothBlockV1 { + /** + * + * @type {SawtoothBlockHeaderV1} + * @memberof SawtoothBlockV1 + */ + 'header': SawtoothBlockHeaderV1; + /** + * + * @type {string} + * @memberof SawtoothBlockV1 + */ + 'header_signature': string; + /** + * + * @type {Array} + * @memberof SawtoothBlockV1 + */ + 'batches': Array; +} +/** + * + * @export + * @interface SawtoothTransactionHeaderV1 + */ +export interface SawtoothTransactionHeaderV1 { + /** + * + * @type {string} + * @memberof SawtoothTransactionHeaderV1 + */ + 'batcher_public_key': string; + /** + * + * @type {Array} + * @memberof SawtoothTransactionHeaderV1 + */ + 'dependencies': Array; + /** + * + * @type {string} + * @memberof SawtoothTransactionHeaderV1 + */ + 'family_name': string; + /** + * + * @type {string} + * @memberof SawtoothTransactionHeaderV1 + */ + 'family_version': string; + /** + * + * @type {Array} + * @memberof SawtoothTransactionHeaderV1 + */ + 'inputs': Array; + /** + * + * @type {string} + * @memberof SawtoothTransactionHeaderV1 + */ + 'nonce': string; + /** + * + * @type {Array} + * @memberof SawtoothTransactionHeaderV1 + */ + 'outputs': Array; + /** + * + * @type {string} + * @memberof SawtoothTransactionHeaderV1 + */ + 'payload_sha512': string; + /** + * + * @type {string} + * @memberof SawtoothTransactionHeaderV1 + */ + 'signer_public_key': string; +} +/** + * + * @export + * @interface SawtoothTransactionV1 + */ +export interface SawtoothTransactionV1 { + /** + * + * @type {SawtoothTransactionHeaderV1} + * @memberof SawtoothTransactionV1 + */ + 'header': SawtoothTransactionHeaderV1; + /** + * + * @type {string} + * @memberof SawtoothTransactionV1 + */ + 'header_signature': string; + /** + * + * @type {string} + * @memberof SawtoothTransactionV1 + */ + 'payload': string; +} +/** + * Response with plugin and validator status report. + * @export + * @interface StatusResponseV1 + */ +export interface StatusResponseV1 { + /** + * Plugin instance id. + * @type {string} + * @memberof StatusResponseV1 + */ + 'instanceId': string; + /** + * Version of connectors Open API Spec. + * @type {string} + * @memberof StatusResponseV1 + */ + 'openApiSpecVersion'?: string; + /** + * True if endpoints were created, false otherwise + * @type {boolean} + * @memberof StatusResponseV1 + */ + 'initialized'?: boolean; + /** + * Response from sawtooth Rest API status endpoint + * @type {any} + * @memberof StatusResponseV1 + */ + 'sawtoothStatus'?: any; +} +/** + * + * @export + * @enum {string} + */ + +export const WatchBlocksV1 = { + Subscribe: 'org.hyperledger.cacti.api.async.sawtooth.WatchBlocksV1.Subscribe', + Next: 'org.hyperledger.cacti.api.async.sawtooth.WatchBlocksV1.Next', + Unsubscribe: 'org.hyperledger.cacti.api.async.sawtooth.WatchBlocksV1.Unsubscribe', + Error: 'org.hyperledger.cacti.api.async.sawtooth.WatchBlocksV1.Error', + Complete: 'org.hyperledger.cacti.api.async.sawtooth.WatchBlocksV1.Complete' +} as const; + +export type WatchBlocksV1 = typeof WatchBlocksV1[keyof typeof WatchBlocksV1]; + + +/** + * Custom response containing block transactions summary. + * @export + * @interface WatchBlocksV1CactiTransactionsResponse + */ +export interface WatchBlocksV1CactiTransactionsResponse { + /** + * List of sawtooth transactions matching specifid (optional) filter + * @type {Array} + * @memberof WatchBlocksV1CactiTransactionsResponse + */ + 'cactiTransactionsEvents': Array; +} +/** + * Response that returns entire sawtooth block. + * @export + * @interface WatchBlocksV1FullResponse + */ +export interface WatchBlocksV1FullResponse { + /** + * + * @type {SawtoothBlockV1} + * @memberof WatchBlocksV1FullResponse + */ + 'fullBlock': SawtoothBlockV1; +} +/** + * Response type from WatchBlocks. \'Cacti*\' are custom views, others correspond to plain sawtooth data. + * @export + * @enum {string} + */ + +export const WatchBlocksV1ListenerType = { + Full: 'full', + CactiTransactions: 'cacti:transactions' +} as const; + +export type WatchBlocksV1ListenerType = typeof WatchBlocksV1ListenerType[keyof typeof WatchBlocksV1ListenerType]; + + +/** + * + * @export + * @interface WatchBlocksV1Options + */ +export interface WatchBlocksV1Options { + /** + * + * @type {WatchBlocksV1ListenerType} + * @memberof WatchBlocksV1Options + */ + 'type'?: WatchBlocksV1ListenerType; + /** + * + * @type {WatchBlocksV1TransactionFilter} + * @memberof WatchBlocksV1Options + */ + 'txFilterBy'?: WatchBlocksV1TransactionFilter; +} + + +/** + * @type WatchBlocksV1Progress + * Response block from WatchBlocks endpoint. Depends on \'type\' passed in subscription options. + * @export + */ +export type WatchBlocksV1Progress = WatchBlocksV1CactiTransactionsResponse | WatchBlocksV1FullResponse; + +/** + * + * @export + * @interface WatchBlocksV1TransactionFilter + */ +export interface WatchBlocksV1TransactionFilter { + /** + * + * @type {string} + * @memberof WatchBlocksV1TransactionFilter + */ + 'family_name'?: string; +} + +/** + * DefaultApi - axios parameter creator + * @export + */ +export const DefaultApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * + * @summary Get the status of the connector and the sawtooth validator + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getStatusV1: async (options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-sawtooth/status`; + // 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, + }; + }, + } +}; + +/** + * DefaultApi - functional programming interface + * @export + */ +export const DefaultApiFp = function(configuration?: Configuration) { + const localVarAxiosParamCreator = DefaultApiAxiosParamCreator(configuration) + return { + /** + * + * @summary Get the status of the connector and the sawtooth validator + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getStatusV1(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getStatusV1(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 Get the status of the connector and the sawtooth validator + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getStatusV1(options?: any): AxiosPromise { + return localVarFp.getStatusV1(options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * DefaultApi - object-oriented interface + * @export + * @class DefaultApi + * @extends {BaseAPI} + */ +export class DefaultApi extends BaseAPI { + /** + * + * @summary Get the status of the connector and the sawtooth validator + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public getStatusV1(options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).getStatusV1(options).then((request) => request(this.axios, this.basePath)); + } +} + + diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/base.ts b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/base.ts new file mode 100644 index 0000000000..e9e244665c --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/base.ts @@ -0,0 +1,72 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Hyperledger Cacti Plugin - Connector Sawtooth + * Can perform basic tasks on a Sawtooth ledger + * + * 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-sawtooth/src/main/typescript/generated/openapi/typescript-axios/common.ts b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/common.ts new file mode 100644 index 0000000000..b8312c3f70 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/common.ts @@ -0,0 +1,150 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Hyperledger Cacti Plugin - Connector Sawtooth + * Can perform basic tasks on a Sawtooth ledger + * + * 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-sawtooth/src/main/typescript/generated/openapi/typescript-axios/configuration.ts b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/configuration.ts new file mode 100644 index 0000000000..47239db827 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/configuration.ts @@ -0,0 +1,101 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Hyperledger Cacti Plugin - Connector Sawtooth + * Can perform basic tasks on a Sawtooth ledger + * + * 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-sawtooth/src/main/typescript/generated/openapi/typescript-axios/git_push.sh b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/git_push.sh new file mode 100644 index 0000000000..f53a75d4fa --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/git_push.sh @@ -0,0 +1,57 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 +git_host=$4 + +if [ "$git_host" = "" ]; then + git_host="github.com" + echo "[INFO] No command line input provided. Set \$git_host to $git_host" +fi + +if [ "$git_user_id" = "" ]; then + git_user_id="GIT_USER_ID" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="GIT_REPO_ID" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="Minor update" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=$(git remote) +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/index.ts b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/index.ts new file mode 100644 index 0000000000..9e1f0eae42 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/index.ts @@ -0,0 +1,18 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Hyperledger Cacti Plugin - Connector Sawtooth + * Can perform basic tasks on a Sawtooth ledger + * + * 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-sawtooth/src/main/typescript/index.ts b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/index.ts new file mode 100755 index 0000000000..87cb558397 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/index.ts @@ -0,0 +1 @@ +export * from "./public-api"; diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/index.web.ts b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/index.web.ts new file mode 100755 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/index.web.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/plugin-factory-ledger-connector.ts b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/plugin-factory-ledger-connector.ts new file mode 100644 index 0000000000..2ac7467d11 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/plugin-factory-ledger-connector.ts @@ -0,0 +1,20 @@ +import { + IPluginFactoryOptions, + PluginFactory, +} from "@hyperledger/cactus-core-api"; +import { + IPluginLedgerConnectorSawtoothOptions, + PluginLedgerConnectorSawtooth, +} from "./plugin-ledger-connector-sawtooth"; + +export class PluginFactoryLedgerConnector extends PluginFactory< + PluginLedgerConnectorSawtooth, + IPluginLedgerConnectorSawtoothOptions, + IPluginFactoryOptions +> { + async create( + pluginOptions: IPluginLedgerConnectorSawtoothOptions, + ): Promise { + return new PluginLedgerConnectorSawtooth(pluginOptions); + } +} diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/plugin-ledger-connector-sawtooth.ts b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/plugin-ledger-connector-sawtooth.ts new file mode 100644 index 0000000000..70a8a45783 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/plugin-ledger-connector-sawtooth.ts @@ -0,0 +1,199 @@ +import type { Express } from "express"; +import type { + Server as SocketIoServer, + Socket as SocketIoSocket, +} from "socket.io"; + +import { + Checks, + Logger, + LoggerProvider, + LogLevelDesc, +} from "@hyperledger/cactus-common"; +import { consensusHasTransactionFinality } from "@hyperledger/cactus-core"; +import { + ConsensusAlgorithmFamily, + IPluginLedgerConnector, + IWebServiceEndpoint, + IPluginWebService, + ICactusPlugin, + ICactusPluginOptions, +} from "@hyperledger/cactus-core-api"; + +import { + StatusResponseV1, + WatchBlocksV1, + WatchBlocksV1Options, +} from "./generated/openapi/typescript-axios"; +import { WatchBlocksV1Endpoint } from "./web-services/watch-blocks-v1-endpoint"; +import { StatusEndpointV1 } from "./web-services/status-endpoint-v1"; +import OAS from "../json/openapi.json"; +import { Configuration, DefaultApi as SawtoothRestApi } from "./sawtooth-api"; + +export interface IPluginLedgerConnectorSawtoothOptions + extends ICactusPluginOptions { + logLevel?: LogLevelDesc; + sawtoothRestApiEndpoint: string; + watchBlocksPollTime?: number; +} + +export class PluginLedgerConnectorSawtooth + implements + IPluginLedgerConnector, + ICactusPlugin, + IPluginWebService +{ + private readonly instanceId: string; + private readonly log: Logger; + private endpoints: IWebServiceEndpoint[] | undefined; + private runningWatchBlocksMonitors = new Set(); + private sawtoothApiClient: SawtoothRestApi; + + public get className(): string { + return "PluginLedgerConnectorSawtooth"; + } + + constructor(public readonly options: IPluginLedgerConnectorSawtoothOptions) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(options, `${fnTag} arg options`); + Checks.truthy( + options.sawtoothRestApiEndpoint, + `${fnTag} arg options.sawtoothRestApiEndpoint`, + ); + + const level = this.options.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + this.instanceId = options.instanceId; + + this.sawtoothApiClient = new SawtoothRestApi( + new Configuration({ basePath: this.options.sawtoothRestApiEndpoint }), + ); + } + + public getOpenApiSpec(): unknown { + return OAS; + } + + public getInstanceId(): string { + return this.instanceId; + } + + public async shutdown(): Promise { + this.log.info(`Shutting down ${this.className}...`); + this.runningWatchBlocksMonitors.forEach((m) => m.close()); + this.runningWatchBlocksMonitors.clear(); + } + + public async onPluginInit(): Promise { + return; + } + + async registerWebServices( + app: Express, + wsApi: SocketIoServer, + ): Promise { + const webServices = await this.getOrCreateWebServices(); + await Promise.all(webServices.map((ws) => ws.registerExpress(app))); + + // Register WatchBlocksV1Endpoint on SocketIO + wsApi.on("connection", (socket: SocketIoSocket) => { + this.log.debug(`New Socket connected. ID=${socket.id}`); + + socket.on( + WatchBlocksV1.Subscribe, + async (options?: WatchBlocksV1Options) => { + const watchBlocksEndpoint = new WatchBlocksV1Endpoint({ + socket, + sawtoothApiClient: this.sawtoothApiClient, + options, + pollTime: this.options.watchBlocksPollTime, + }); + this.runningWatchBlocksMonitors.add(watchBlocksEndpoint); + await watchBlocksEndpoint.subscribe(); + this.log.debug( + "Running WatchBlocksMonitors count:", + this.runningWatchBlocksMonitors.size, + ); + + socket.on("disconnect", () => { + this.runningWatchBlocksMonitors.delete(watchBlocksEndpoint); + this.log.debug( + "Running monitors count:", + this.runningWatchBlocksMonitors.size, + ); + }); + }, + ); + }); + + return webServices; + } + + public async getOrCreateWebServices(): Promise { + if (Array.isArray(this.endpoints)) { + return this.endpoints; + } + const endpoints: IWebServiceEndpoint[] = []; + + { + const endpoint = new StatusEndpointV1({ + connector: this, + logLevel: this.options.logLevel, + }); + endpoints.push(endpoint); + } + + this.endpoints = endpoints; + return endpoints; + } + + public getPackageName(): string { + return `@hyperledger/cactus-plugin-ledger-connector-sawtooth`; + } + + public async getConsensusAlgorithmFamily(): Promise { + return ConsensusAlgorithmFamily.Authority; + } + + public async hasTransactionFinality(): Promise { + const currentConsensusAlgorithmFamily = + await this.getConsensusAlgorithmFamily(); + + return consensusHasTransactionFinality(currentConsensusAlgorithmFamily); + } + + /** + * @warn Not implemented! + */ + deployContract(): Promise { + throw new Error("Method not implemented."); + } + + /** + * @warn Not implemented! + */ + transact(): Promise { + throw new Error("Method not implemented."); + } + + /** + * Get this connector status and response of /status endpoint from Sawtooth REST API. + * + * @returns StatusResponseV1 + */ + async getStatus(): Promise { + const openApiSpecVersion = + (this.getOpenApiSpec() as Record)?.info?.version ?? + "unknown"; + + const status = await this.sawtoothApiClient.statusGet(); + + return { + instanceId: this.instanceId, + openApiSpecVersion, + initialized: Boolean(this.endpoints), + sawtoothStatus: status.data.data, + }; + } +} diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/public-api.ts b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/public-api.ts new file mode 100755 index 0000000000..421f697142 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/public-api.ts @@ -0,0 +1,22 @@ +import { IPluginFactoryOptions } from "@hyperledger/cactus-core-api"; + +export { + PluginLedgerConnectorSawtooth, + IPluginLedgerConnectorSawtoothOptions, +} from "./plugin-ledger-connector-sawtooth"; + +import { PluginFactoryLedgerConnector } from "./plugin-factory-ledger-connector"; +export { PluginFactoryLedgerConnector } from "./plugin-factory-ledger-connector"; + +export * from "./types/model-type-guards"; +export * from "./generated/openapi/typescript-axios"; +export { + SawtoothApiClient, + SawtoothApiClientOptions, +} from "./api-client/sawtooth-api-client"; + +export async function createPluginFactory( + pluginFactoryOptions: IPluginFactoryOptions, +): Promise { + return new PluginFactoryLedgerConnector(pluginFactoryOptions); +} diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.gitignore b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.gitignore new file mode 100644 index 0000000000..149b576547 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.gitignore @@ -0,0 +1,4 @@ +wwwroot/*.js +node_modules +typings +dist diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.npmignore b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.npmignore new file mode 100644 index 0000000000..999d88df69 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.npmignore @@ -0,0 +1 @@ +# empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm \ No newline at end of file diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.openapi-generator-ignore b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.openapi-generator-ignore new file mode 100644 index 0000000000..7484ee590a --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# 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 diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.openapi-generator/FILES b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.openapi-generator/FILES new file mode 100644 index 0000000000..a80cd4f07b --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.openapi-generator/FILES @@ -0,0 +1,8 @@ +.gitignore +.npmignore +api.ts +base.ts +common.ts +configuration.ts +git_push.sh +index.ts diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.openapi-generator/VERSION b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.openapi-generator/VERSION new file mode 100644 index 0000000000..e7e42a4b58 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.openapi-generator/VERSION @@ -0,0 +1 @@ +6.3.0 \ No newline at end of file diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/api.ts b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/api.ts new file mode 100644 index 0000000000..16c82bb15b --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/api.ts @@ -0,0 +1,1986 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Sawtooth REST API + * Official Sawtooth REST API specification with some modifications from Hyperledger Cacti project. + * + * The version of the OpenAPI document: 0.8.0 + * + * + * 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 Batch + */ +export interface Batch { + /** + * + * @type {BatchHeader} + * @memberof Batch + */ + 'header': BatchHeader; + /** + * + * @type {string} + * @memberof Batch + */ + 'header_signature': string; + /** + * + * @type {Array} + * @memberof Batch + */ + 'transactions': Array; +} +/** + * + * @export + * @interface BatchHeader + */ +export interface BatchHeader { + /** + * + * @type {string} + * @memberof BatchHeader + */ + 'signer_public_key': string; + /** + * + * @type {Array} + * @memberof BatchHeader + */ + 'transaction_ids': Array; +} +/** + * + * @export + * @interface BatchList + */ +export interface BatchList { + /** + * + * @type {Array} + * @memberof BatchList + */ + 'batches': Array; +} +/** + * + * @export + * @interface BatchStatusesGet200Response + */ +export interface BatchStatusesGet200Response { + /** + * + * @type {Array} + * @memberof BatchStatusesGet200Response + */ + 'data'?: Array; + /** + * + * @type {string} + * @memberof BatchStatusesGet200Response + */ + 'link'?: string; +} +/** + * + * @export + * @interface BatchStatusesGet200Response1 + */ +export interface BatchStatusesGet200Response1 { + /** + * + * @type {Array} + * @memberof BatchStatusesGet200Response1 + */ + 'data'?: Array; +} +/** + * + * @export + * @interface BatchStatusesInner + */ +export interface BatchStatusesInner { + /** + * + * @type {string} + * @memberof BatchStatusesInner + */ + 'id'?: string; + /** + * + * @type {string} + * @memberof BatchStatusesInner + */ + 'status'?: BatchStatusesInnerStatusEnum; + /** + * + * @type {Array} + * @memberof BatchStatusesInner + */ + 'invalid_transactions'?: Array; +} + +export const BatchStatusesInnerStatusEnum = { + Committed: 'COMMITTED', + Invalid: 'INVALID', + Pending: 'PENDING', + Unknown: 'UNKNOWN' +} as const; + +export type BatchStatusesInnerStatusEnum = typeof BatchStatusesInnerStatusEnum[keyof typeof BatchStatusesInnerStatusEnum]; + +/** + * + * @export + * @interface BatchStatusesInnerInvalidTransactionsInner + */ +export interface BatchStatusesInnerInvalidTransactionsInner { + /** + * + * @type {string} + * @memberof BatchStatusesInnerInvalidTransactionsInner + */ + 'id'?: string; + /** + * + * @type {string} + * @memberof BatchStatusesInnerInvalidTransactionsInner + */ + 'message'?: string; + /** + * + * @type {string} + * @memberof BatchStatusesInnerInvalidTransactionsInner + */ + 'extended_data'?: string; +} +/** + * + * @export + * @interface BatchesBatchIdGet200Response + */ +export interface BatchesBatchIdGet200Response { + /** + * + * @type {Batch} + * @memberof BatchesBatchIdGet200Response + */ + 'data'?: Batch; + /** + * + * @type {string} + * @memberof BatchesBatchIdGet200Response + */ + 'head'?: string; + /** + * + * @type {string} + * @memberof BatchesBatchIdGet200Response + */ + 'link'?: string; +} +/** + * + * @export + * @interface BatchesGet200Response + */ +export interface BatchesGet200Response { + /** + * + * @type {Array} + * @memberof BatchesGet200Response + */ + 'data'?: Array; + /** + * + * @type {string} + * @memberof BatchesGet200Response + */ + 'head'?: string; + /** + * + * @type {string} + * @memberof BatchesGet200Response + */ + 'link'?: string; + /** + * + * @type {Paging} + * @memberof BatchesGet200Response + */ + 'paging'?: Paging; +} +/** + * + * @export + * @interface BatchesGet202Response + */ +export interface BatchesGet202Response { + /** + * + * @type {string} + * @memberof BatchesGet202Response + */ + 'link'?: string; +} +/** + * + * @export + * @interface Block + */ +export interface Block { + /** + * + * @type {BlockHeader} + * @memberof Block + */ + 'header': BlockHeader; + /** + * + * @type {string} + * @memberof Block + */ + 'header_signature': string; + /** + * + * @type {Array} + * @memberof Block + */ + 'batches': Array; +} +/** + * + * @export + * @interface BlockHeader + */ +export interface BlockHeader { + /** + * + * @type {number} + * @memberof BlockHeader + */ + 'block_num': number; + /** + * + * @type {string} + * @memberof BlockHeader + */ + 'previous_block_id': string; + /** + * + * @type {string} + * @memberof BlockHeader + */ + 'signer_public_key': string; + /** + * + * @type {Array} + * @memberof BlockHeader + */ + 'batch_ids': Array; + /** + * + * @type {string} + * @memberof BlockHeader + */ + 'consensus': string; + /** + * + * @type {string} + * @memberof BlockHeader + */ + 'state_root_hash': string; +} +/** + * + * @export + * @interface BlocksBlockIdGet200Response + */ +export interface BlocksBlockIdGet200Response { + /** + * + * @type {Block} + * @memberof BlocksBlockIdGet200Response + */ + 'data'?: Block; + /** + * + * @type {string} + * @memberof BlocksBlockIdGet200Response + */ + 'head'?: string; + /** + * + * @type {string} + * @memberof BlocksBlockIdGet200Response + */ + 'link'?: string; +} +/** + * + * @export + * @interface BlocksGet200Response + */ +export interface BlocksGet200Response { + /** + * + * @type {Array} + * @memberof BlocksGet200Response + */ + 'data'?: Array; + /** + * + * @type {string} + * @memberof BlocksGet200Response + */ + 'head'?: string; + /** + * + * @type {string} + * @memberof BlocksGet200Response + */ + 'link'?: string; + /** + * + * @type {Paging} + * @memberof BlocksGet200Response + */ + 'paging'?: Paging; +} +/** + * + * @export + * @interface Entry + */ +export interface Entry { + /** + * + * @type {string} + * @memberof Entry + */ + 'address'?: string; + /** + * + * @type {string} + * @memberof Entry + */ + 'data'?: string; +} +/** + * + * @export + * @interface ModelError + */ +export interface ModelError { + /** + * + * @type {number} + * @memberof ModelError + */ + 'code'?: number; + /** + * + * @type {string} + * @memberof ModelError + */ + 'title'?: string; + /** + * + * @type {string} + * @memberof ModelError + */ + 'message'?: string; +} +/** + * + * @export + * @interface Paging + */ +export interface Paging { + /** + * + * @type {string} + * @memberof Paging + */ + 'start'?: string; + /** + * + * @type {number} + * @memberof Paging + */ + 'limit'?: number; + /** + * + * @type {string} + * @memberof Paging + */ + 'next_position'?: string; + /** + * + * @type {string} + * @memberof Paging + */ + 'next'?: string; +} +/** + * + * @export + * @interface PeersGet200Response + */ +export interface PeersGet200Response { + /** + * + * @type {Array} + * @memberof PeersGet200Response + */ + 'data'?: Array; + /** + * + * @type {string} + * @memberof PeersGet200Response + */ + 'link'?: string; +} +/** + * + * @export + * @interface ReceiptsGet200Response + */ +export interface ReceiptsGet200Response { + /** + * + * @type {Array} + * @memberof ReceiptsGet200Response + */ + 'data'?: Array; + /** + * + * @type {string} + * @memberof ReceiptsGet200Response + */ + 'link'?: string; +} +/** + * + * @export + * @interface StateAddressGet200Response + */ +export interface StateAddressGet200Response { + /** + * + * @type {string} + * @memberof StateAddressGet200Response + */ + 'data'?: string; + /** + * + * @type {string} + * @memberof StateAddressGet200Response + */ + 'head'?: string; + /** + * + * @type {string} + * @memberof StateAddressGet200Response + */ + 'link'?: string; +} +/** + * + * @export + * @interface StateGet200Response + */ +export interface StateGet200Response { + /** + * + * @type {Array} + * @memberof StateGet200Response + */ + 'data'?: Array; + /** + * + * @type {string} + * @memberof StateGet200Response + */ + 'head'?: string; + /** + * + * @type {string} + * @memberof StateGet200Response + */ + 'link'?: string; + /** + * + * @type {Paging} + * @memberof StateGet200Response + */ + 'paging'?: Paging; +} +/** + * + * @export + * @interface StatusGet200Response + */ +export interface StatusGet200Response { + /** + * + * @type {StatusGet200ResponseData} + * @memberof StatusGet200Response + */ + 'data'?: StatusGet200ResponseData; + /** + * + * @type {string} + * @memberof StatusGet200Response + */ + 'link'?: string; +} +/** + * + * @export + * @interface StatusGet200ResponseData + */ +export interface StatusGet200ResponseData { + /** + * + * @type {string} + * @memberof StatusGet200ResponseData + */ + 'endpoint'?: string; + /** + * + * @type {Array} + * @memberof StatusGet200ResponseData + */ + 'peers'?: Array; +} +/** + * + * @export + * @interface StatusGet200ResponseDataPeersInner + */ +export interface StatusGet200ResponseDataPeersInner { + /** + * + * @type {string} + * @memberof StatusGet200ResponseDataPeersInner + */ + 'endpoint'?: string; +} +/** + * + * @export + * @interface Transaction + */ +export interface Transaction { + /** + * + * @type {TransactionHeader} + * @memberof Transaction + */ + 'header': TransactionHeader; + /** + * + * @type {string} + * @memberof Transaction + */ + 'header_signature': string; + /** + * + * @type {string} + * @memberof Transaction + */ + 'payload': string; +} +/** + * + * @export + * @interface TransactionHeader + */ +export interface TransactionHeader { + /** + * + * @type {string} + * @memberof TransactionHeader + */ + 'batcher_public_key': string; + /** + * + * @type {Array} + * @memberof TransactionHeader + */ + 'dependencies': Array; + /** + * + * @type {string} + * @memberof TransactionHeader + */ + 'family_name': string; + /** + * + * @type {string} + * @memberof TransactionHeader + */ + 'family_version': string; + /** + * + * @type {Array} + * @memberof TransactionHeader + */ + 'inputs': Array; + /** + * + * @type {string} + * @memberof TransactionHeader + */ + 'nonce': string; + /** + * + * @type {Array} + * @memberof TransactionHeader + */ + 'outputs': Array; + /** + * + * @type {string} + * @memberof TransactionHeader + */ + 'payload_sha512': string; + /** + * + * @type {string} + * @memberof TransactionHeader + */ + 'signer_public_key': string; +} +/** + * + * @export + * @interface TransactionReceipt + */ +export interface TransactionReceipt { + /** + * + * @type {string} + * @memberof TransactionReceipt + */ + 'transaction_id'?: string; + /** + * + * @type {Array} + * @memberof TransactionReceipt + */ + 'state_changes'?: Array; + /** + * + * @type {Array} + * @memberof TransactionReceipt + */ + 'events'?: Array; + /** + * + * @type {Array} + * @memberof TransactionReceipt + */ + 'data'?: Array; +} +/** + * + * @export + * @interface TransactionReceiptEventsInner + */ +export interface TransactionReceiptEventsInner { + /** + * + * @type {string} + * @memberof TransactionReceiptEventsInner + */ + 'event_type'?: string; + /** + * + * @type {Array} + * @memberof TransactionReceiptEventsInner + */ + 'attributes'?: Array; + /** + * + * @type {string} + * @memberof TransactionReceiptEventsInner + */ + 'data'?: string; +} +/** + * + * @export + * @interface TransactionReceiptEventsInnerAttributesInner + */ +export interface TransactionReceiptEventsInnerAttributesInner { + /** + * + * @type {string} + * @memberof TransactionReceiptEventsInnerAttributesInner + */ + 'key'?: string; + /** + * + * @type {string} + * @memberof TransactionReceiptEventsInnerAttributesInner + */ + 'value'?: string; +} +/** + * + * @export + * @interface TransactionReceiptStateChangesInner + */ +export interface TransactionReceiptStateChangesInner { + /** + * + * @type {string} + * @memberof TransactionReceiptStateChangesInner + */ + 'type'?: string; + /** + * + * @type {string} + * @memberof TransactionReceiptStateChangesInner + */ + 'address'?: string; + /** + * + * @type {string} + * @memberof TransactionReceiptStateChangesInner + */ + 'value'?: string; +} +/** + * + * @export + * @interface TransactionsGet200Response + */ +export interface TransactionsGet200Response { + /** + * + * @type {Array} + * @memberof TransactionsGet200Response + */ + 'data'?: Array; + /** + * + * @type {string} + * @memberof TransactionsGet200Response + */ + 'head'?: string; + /** + * + * @type {string} + * @memberof TransactionsGet200Response + */ + 'link'?: string; + /** + * + * @type {Paging} + * @memberof TransactionsGet200Response + */ + 'paging'?: Paging; +} + +/** + * DefaultApi - axios parameter creator + * @export + */ +export const DefaultApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * Fetches an array of objects with a status and id for each batch requested. There are four possible statuses with string values `\'COMMITTED\'`, `\'INVALID\'`, `\'PENDING\'`, and `\'UNKNOWN\'`. The batch(es) you want to check can be specified using the `id` filter parameter. If a `wait` time is specified in the URL, the API will wait to respond until all batches are committed, or the time in seconds has elapsed. If the value of `wait` is not set (i.e., `?wait&id=...`), or it is set to any non-integer value other than `false`, the wait time will be just under the API\'s specified timeout (usually 300). Note that because this route does not return full resources, the response will not be paginated, and there will be no `head` or `paging` properties. + * @summary Fetches the committed statuses for a set of batches + * @param {string} id A comma-separated list of batch ids + * @param {number} [wait] A time in seconds to wait for commit + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + batchStatusesGet: async (id: string, wait?: number, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists('batchStatusesGet', 'id', id) + const localVarPath = `/batch_statuses`; + // 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; + + if (id !== undefined) { + localVarQueryParameter['id'] = id; + } + + if (wait !== undefined) { + localVarQueryParameter['wait'] = wait; + } + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Identical to `GET /batch_statuses`, but takes ids of batches as a JSON formatted POST body rather than a query parameter. This allows for many more batches to be checked and should be used for more than 15 ids. Note that because query information is not encoded in the URL, no `link` will be returned with this query. + * @summary Fetches the committed statuses for a set of batches + * @param {Array} requestBody A JSON array of batch id strings + * @param {number} [wait] A time in seconds to wait for commit + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + batchStatusesPost: async (requestBody: Array, wait?: number, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'requestBody' is not null or undefined + assertParamExists('batchStatusesPost', 'requestBody', requestBody) + const localVarPath = `/batch_statuses`; + // 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; + + if (wait !== undefined) { + localVarQueryParameter['wait'] = wait; + } + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(requestBody, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary Fetches a particular batch + * @param {string} batchId Batch id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + batchesBatchIdGet: async (batchId: string, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'batchId' is not null or undefined + assertParamExists('batchesBatchIdGet', 'batchId', batchId) + const localVarPath = `/batches/{batch_id}` + .replace(`{${"batch_id"}}`, encodeURIComponent(String(batchId))); + // 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, + }; + }, + /** + * Fetches a paginated list of batches from the validator. + * @summary Fetches a list of batches + * @param {string} [head] Index or id of head block + * @param {string} [start] Id to start paging (inclusive) + * @param {number} [limit] Number of items to return + * @param {string} [reverse] If the list should be reversed + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + batchesGet: async (head?: string, start?: string, limit?: number, reverse?: string, options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/batches`; + // 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; + + if (head !== undefined) { + localVarQueryParameter['head'] = head; + } + + if (start !== undefined) { + localVarQueryParameter['start'] = start; + } + + if (limit !== undefined) { + localVarQueryParameter['limit'] = limit; + } + + if (reverse !== undefined) { + localVarQueryParameter['reverse'] = reverse; + } + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Accepts a protobuf formatted `BatchList` as an octet-stream binary file and submits it to the validator to be committed. The API will return immediately with a status of `202`. There will be no `data` object, only a `link` to a `/batch_statuses` endpoint to be polled to check the status of submitted batches. + * @summary Sends a BatchList to the validator + * @param {BatchList} batchList A binary encoded protobuf BatchList + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + batchesPost: async (batchList: BatchList, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'batchList' is not null or undefined + assertParamExists('batchesPost', 'batchList', batchList) + const localVarPath = `/batches`; + // 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/octet-stream'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(batchList, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary Fetches a particular block + * @param {string} blockId Block id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + blocksBlockIdGet: async (blockId: string, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'blockId' is not null or undefined + assertParamExists('blocksBlockIdGet', 'blockId', blockId) + const localVarPath = `/blocks/{block_id}` + .replace(`{${"block_id"}}`, encodeURIComponent(String(blockId))); + // 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, + }; + }, + /** + * Fetches a paginated list of blocks from the validator. + * @summary Fetches a list of blocks + * @param {string} [head] Index or id of head block + * @param {string} [start] Id to start paging (inclusive) + * @param {number} [limit] Number of items to return + * @param {string} [reverse] If the list should be reversed + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + blocksGet: async (head?: string, start?: string, limit?: number, reverse?: string, options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/blocks`; + // 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; + + if (head !== undefined) { + localVarQueryParameter['head'] = head; + } + + if (start !== undefined) { + localVarQueryParameter['start'] = start; + } + + if (limit !== undefined) { + localVarQueryParameter['limit'] = limit; + } + + if (reverse !== undefined) { + localVarQueryParameter['reverse'] = reverse; + } + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary Fetches the endpoints of the authorized peers of the validator + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + peersGet: async (options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/peers`; + // 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, + }; + }, + /** + * Fetches an array of objects for each receipt requested. The receipt(s) you want to retrieve can be specified using the `id` filter parameter, where `id` refers to the transaction id of the transaction the receipt is associated with. + * @summary Fetches the receipts for a set of transactions + * @param {string} id A comma-separated list of transaction ids + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + receiptsGet: async (id: string, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists('receiptsGet', 'id', id) + const localVarPath = `/receipts`; + // 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; + + if (id !== undefined) { + localVarQueryParameter['id'] = id; + } + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Identical to `GET /receipts`, but takes ids of transactions as a JSON formatted POST body rather than a query parameter. This allows for many more receipts to be fetched and should be used with more than 15 ids. Note that because query information is not encoded in the URL, no `link` will be returned with this request. + * @summary Fetches the receipts for a set of transactions + * @param {Array} requestBody A JSON array of transaction id strings + * @param {number} [wait] A time in seconds to wait for commit + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + receiptsPost: async (requestBody: Array, wait?: number, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'requestBody' is not null or undefined + assertParamExists('receiptsPost', 'requestBody', requestBody) + const localVarPath = `/receipts`; + // 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; + + if (wait !== undefined) { + localVarQueryParameter['wait'] = wait; + } + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(requestBody, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Takes full 70-character address and fetches a particular leaf. For partial address (i.e., group of leaves) use `/state` above. + * @summary Fetches a particular leaf from the current state + * @param {string} address Radix address of a leaf + * @param {string} [head] Index or id of head block + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + stateAddressGet: async (address: string, head?: string, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'address' is not null or undefined + assertParamExists('stateAddressGet', 'address', address) + const localVarPath = `/state/{address}` + .replace(`{${"address"}}`, encodeURIComponent(String(address))); + // 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; + + if (head !== undefined) { + localVarQueryParameter['head'] = head; + } + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Fetches a paginated list of entries for the current state, or relative to a particular head block. Using the `address` filter parameter will narrow the list to any entries that have an address beginning with the characters specified. Note that the partial address in `address` parameter should have even number of hexadecimal characters (i.e., complete bytes). + * @summary Fetches the data for the current state + * @param {string} [head] Index or id of head block + * @param {string} [address] A partial address to filter leaves by + * @param {string} [start] Id to start paging (inclusive) + * @param {number} [limit] Number of items to return + * @param {string} [reverse] If the list should be reversed + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + stateGet: async (head?: string, address?: string, start?: string, limit?: number, reverse?: string, options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/state`; + // 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; + + if (head !== undefined) { + localVarQueryParameter['head'] = head; + } + + if (address !== undefined) { + localVarQueryParameter['address'] = address; + } + + if (start !== undefined) { + localVarQueryParameter['start'] = start; + } + + if (limit !== undefined) { + localVarQueryParameter['limit'] = limit; + } + + if (reverse !== undefined) { + localVarQueryParameter['reverse'] = reverse; + } + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary Fetches information pertaining to the status of the validator + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + statusGet: async (options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/status`; + // 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, + }; + }, + /** + * Fetches a paginated list of transactions from the validator. + * @summary Fetches a list of transactions + * @param {string} [head] Index or id of head block + * @param {string} [start] Id to start paging (inclusive) + * @param {number} [limit] Number of items to return + * @param {string} [reverse] If the list should be reversed + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + transactionsGet: async (head?: string, start?: string, limit?: number, reverse?: string, options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/transactions`; + // 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; + + if (head !== undefined) { + localVarQueryParameter['head'] = head; + } + + if (start !== undefined) { + localVarQueryParameter['start'] = start; + } + + if (limit !== undefined) { + localVarQueryParameter['limit'] = limit; + } + + if (reverse !== undefined) { + localVarQueryParameter['reverse'] = reverse; + } + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary Fetches a particular transaction + * @param {string} transactionId Transaction id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + transactionsTransactionIdGet: async (transactionId: string, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'transactionId' is not null or undefined + assertParamExists('transactionsTransactionIdGet', 'transactionId', transactionId) + const localVarPath = `/transactions/{transaction_id}` + .replace(`{${"transaction_id"}}`, encodeURIComponent(String(transactionId))); + // 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, + }; + }, + } +}; + +/** + * DefaultApi - functional programming interface + * @export + */ +export const DefaultApiFp = function(configuration?: Configuration) { + const localVarAxiosParamCreator = DefaultApiAxiosParamCreator(configuration) + return { + /** + * Fetches an array of objects with a status and id for each batch requested. There are four possible statuses with string values `\'COMMITTED\'`, `\'INVALID\'`, `\'PENDING\'`, and `\'UNKNOWN\'`. The batch(es) you want to check can be specified using the `id` filter parameter. If a `wait` time is specified in the URL, the API will wait to respond until all batches are committed, or the time in seconds has elapsed. If the value of `wait` is not set (i.e., `?wait&id=...`), or it is set to any non-integer value other than `false`, the wait time will be just under the API\'s specified timeout (usually 300). Note that because this route does not return full resources, the response will not be paginated, and there will be no `head` or `paging` properties. + * @summary Fetches the committed statuses for a set of batches + * @param {string} id A comma-separated list of batch ids + * @param {number} [wait] A time in seconds to wait for commit + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async batchStatusesGet(id: string, wait?: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.batchStatusesGet(id, wait, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * Identical to `GET /batch_statuses`, but takes ids of batches as a JSON formatted POST body rather than a query parameter. This allows for many more batches to be checked and should be used for more than 15 ids. Note that because query information is not encoded in the URL, no `link` will be returned with this query. + * @summary Fetches the committed statuses for a set of batches + * @param {Array} requestBody A JSON array of batch id strings + * @param {number} [wait] A time in seconds to wait for commit + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async batchStatusesPost(requestBody: Array, wait?: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.batchStatusesPost(requestBody, wait, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * + * @summary Fetches a particular batch + * @param {string} batchId Batch id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async batchesBatchIdGet(batchId: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.batchesBatchIdGet(batchId, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * Fetches a paginated list of batches from the validator. + * @summary Fetches a list of batches + * @param {string} [head] Index or id of head block + * @param {string} [start] Id to start paging (inclusive) + * @param {number} [limit] Number of items to return + * @param {string} [reverse] If the list should be reversed + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async batchesGet(head?: string, start?: string, limit?: number, reverse?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.batchesGet(head, start, limit, reverse, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * Accepts a protobuf formatted `BatchList` as an octet-stream binary file and submits it to the validator to be committed. The API will return immediately with a status of `202`. There will be no `data` object, only a `link` to a `/batch_statuses` endpoint to be polled to check the status of submitted batches. + * @summary Sends a BatchList to the validator + * @param {BatchList} batchList A binary encoded protobuf BatchList + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async batchesPost(batchList: BatchList, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.batchesPost(batchList, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * + * @summary Fetches a particular block + * @param {string} blockId Block id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async blocksBlockIdGet(blockId: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.blocksBlockIdGet(blockId, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * Fetches a paginated list of blocks from the validator. + * @summary Fetches a list of blocks + * @param {string} [head] Index or id of head block + * @param {string} [start] Id to start paging (inclusive) + * @param {number} [limit] Number of items to return + * @param {string} [reverse] If the list should be reversed + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async blocksGet(head?: string, start?: string, limit?: number, reverse?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.blocksGet(head, start, limit, reverse, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * + * @summary Fetches the endpoints of the authorized peers of the validator + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async peersGet(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.peersGet(options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * Fetches an array of objects for each receipt requested. The receipt(s) you want to retrieve can be specified using the `id` filter parameter, where `id` refers to the transaction id of the transaction the receipt is associated with. + * @summary Fetches the receipts for a set of transactions + * @param {string} id A comma-separated list of transaction ids + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async receiptsGet(id: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.receiptsGet(id, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * Identical to `GET /receipts`, but takes ids of transactions as a JSON formatted POST body rather than a query parameter. This allows for many more receipts to be fetched and should be used with more than 15 ids. Note that because query information is not encoded in the URL, no `link` will be returned with this request. + * @summary Fetches the receipts for a set of transactions + * @param {Array} requestBody A JSON array of transaction id strings + * @param {number} [wait] A time in seconds to wait for commit + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async receiptsPost(requestBody: Array, wait?: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.receiptsPost(requestBody, wait, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * Takes full 70-character address and fetches a particular leaf. For partial address (i.e., group of leaves) use `/state` above. + * @summary Fetches a particular leaf from the current state + * @param {string} address Radix address of a leaf + * @param {string} [head] Index or id of head block + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async stateAddressGet(address: string, head?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.stateAddressGet(address, head, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * Fetches a paginated list of entries for the current state, or relative to a particular head block. Using the `address` filter parameter will narrow the list to any entries that have an address beginning with the characters specified. Note that the partial address in `address` parameter should have even number of hexadecimal characters (i.e., complete bytes). + * @summary Fetches the data for the current state + * @param {string} [head] Index or id of head block + * @param {string} [address] A partial address to filter leaves by + * @param {string} [start] Id to start paging (inclusive) + * @param {number} [limit] Number of items to return + * @param {string} [reverse] If the list should be reversed + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async stateGet(head?: string, address?: string, start?: string, limit?: number, reverse?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.stateGet(head, address, start, limit, reverse, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * + * @summary Fetches information pertaining to the status of the validator + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async statusGet(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.statusGet(options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * Fetches a paginated list of transactions from the validator. + * @summary Fetches a list of transactions + * @param {string} [head] Index or id of head block + * @param {string} [start] Id to start paging (inclusive) + * @param {number} [limit] Number of items to return + * @param {string} [reverse] If the list should be reversed + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async transactionsGet(head?: string, start?: string, limit?: number, reverse?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.transactionsGet(head, start, limit, reverse, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * + * @summary Fetches a particular transaction + * @param {string} transactionId Transaction id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async transactionsTransactionIdGet(transactionId: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.transactionsTransactionIdGet(transactionId, 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 { + /** + * Fetches an array of objects with a status and id for each batch requested. There are four possible statuses with string values `\'COMMITTED\'`, `\'INVALID\'`, `\'PENDING\'`, and `\'UNKNOWN\'`. The batch(es) you want to check can be specified using the `id` filter parameter. If a `wait` time is specified in the URL, the API will wait to respond until all batches are committed, or the time in seconds has elapsed. If the value of `wait` is not set (i.e., `?wait&id=...`), or it is set to any non-integer value other than `false`, the wait time will be just under the API\'s specified timeout (usually 300). Note that because this route does not return full resources, the response will not be paginated, and there will be no `head` or `paging` properties. + * @summary Fetches the committed statuses for a set of batches + * @param {string} id A comma-separated list of batch ids + * @param {number} [wait] A time in seconds to wait for commit + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + batchStatusesGet(id: string, wait?: number, options?: any): AxiosPromise { + return localVarFp.batchStatusesGet(id, wait, options).then((request) => request(axios, basePath)); + }, + /** + * Identical to `GET /batch_statuses`, but takes ids of batches as a JSON formatted POST body rather than a query parameter. This allows for many more batches to be checked and should be used for more than 15 ids. Note that because query information is not encoded in the URL, no `link` will be returned with this query. + * @summary Fetches the committed statuses for a set of batches + * @param {Array} requestBody A JSON array of batch id strings + * @param {number} [wait] A time in seconds to wait for commit + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + batchStatusesPost(requestBody: Array, wait?: number, options?: any): AxiosPromise { + return localVarFp.batchStatusesPost(requestBody, wait, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary Fetches a particular batch + * @param {string} batchId Batch id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + batchesBatchIdGet(batchId: string, options?: any): AxiosPromise { + return localVarFp.batchesBatchIdGet(batchId, options).then((request) => request(axios, basePath)); + }, + /** + * Fetches a paginated list of batches from the validator. + * @summary Fetches a list of batches + * @param {string} [head] Index or id of head block + * @param {string} [start] Id to start paging (inclusive) + * @param {number} [limit] Number of items to return + * @param {string} [reverse] If the list should be reversed + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + batchesGet(head?: string, start?: string, limit?: number, reverse?: string, options?: any): AxiosPromise { + return localVarFp.batchesGet(head, start, limit, reverse, options).then((request) => request(axios, basePath)); + }, + /** + * Accepts a protobuf formatted `BatchList` as an octet-stream binary file and submits it to the validator to be committed. The API will return immediately with a status of `202`. There will be no `data` object, only a `link` to a `/batch_statuses` endpoint to be polled to check the status of submitted batches. + * @summary Sends a BatchList to the validator + * @param {BatchList} batchList A binary encoded protobuf BatchList + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + batchesPost(batchList: BatchList, options?: any): AxiosPromise { + return localVarFp.batchesPost(batchList, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary Fetches a particular block + * @param {string} blockId Block id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + blocksBlockIdGet(blockId: string, options?: any): AxiosPromise { + return localVarFp.blocksBlockIdGet(blockId, options).then((request) => request(axios, basePath)); + }, + /** + * Fetches a paginated list of blocks from the validator. + * @summary Fetches a list of blocks + * @param {string} [head] Index or id of head block + * @param {string} [start] Id to start paging (inclusive) + * @param {number} [limit] Number of items to return + * @param {string} [reverse] If the list should be reversed + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + blocksGet(head?: string, start?: string, limit?: number, reverse?: string, options?: any): AxiosPromise { + return localVarFp.blocksGet(head, start, limit, reverse, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary Fetches the endpoints of the authorized peers of the validator + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + peersGet(options?: any): AxiosPromise { + return localVarFp.peersGet(options).then((request) => request(axios, basePath)); + }, + /** + * Fetches an array of objects for each receipt requested. The receipt(s) you want to retrieve can be specified using the `id` filter parameter, where `id` refers to the transaction id of the transaction the receipt is associated with. + * @summary Fetches the receipts for a set of transactions + * @param {string} id A comma-separated list of transaction ids + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + receiptsGet(id: string, options?: any): AxiosPromise { + return localVarFp.receiptsGet(id, options).then((request) => request(axios, basePath)); + }, + /** + * Identical to `GET /receipts`, but takes ids of transactions as a JSON formatted POST body rather than a query parameter. This allows for many more receipts to be fetched and should be used with more than 15 ids. Note that because query information is not encoded in the URL, no `link` will be returned with this request. + * @summary Fetches the receipts for a set of transactions + * @param {Array} requestBody A JSON array of transaction id strings + * @param {number} [wait] A time in seconds to wait for commit + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + receiptsPost(requestBody: Array, wait?: number, options?: any): AxiosPromise { + return localVarFp.receiptsPost(requestBody, wait, options).then((request) => request(axios, basePath)); + }, + /** + * Takes full 70-character address and fetches a particular leaf. For partial address (i.e., group of leaves) use `/state` above. + * @summary Fetches a particular leaf from the current state + * @param {string} address Radix address of a leaf + * @param {string} [head] Index or id of head block + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + stateAddressGet(address: string, head?: string, options?: any): AxiosPromise { + return localVarFp.stateAddressGet(address, head, options).then((request) => request(axios, basePath)); + }, + /** + * Fetches a paginated list of entries for the current state, or relative to a particular head block. Using the `address` filter parameter will narrow the list to any entries that have an address beginning with the characters specified. Note that the partial address in `address` parameter should have even number of hexadecimal characters (i.e., complete bytes). + * @summary Fetches the data for the current state + * @param {string} [head] Index or id of head block + * @param {string} [address] A partial address to filter leaves by + * @param {string} [start] Id to start paging (inclusive) + * @param {number} [limit] Number of items to return + * @param {string} [reverse] If the list should be reversed + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + stateGet(head?: string, address?: string, start?: string, limit?: number, reverse?: string, options?: any): AxiosPromise { + return localVarFp.stateGet(head, address, start, limit, reverse, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary Fetches information pertaining to the status of the validator + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + statusGet(options?: any): AxiosPromise { + return localVarFp.statusGet(options).then((request) => request(axios, basePath)); + }, + /** + * Fetches a paginated list of transactions from the validator. + * @summary Fetches a list of transactions + * @param {string} [head] Index or id of head block + * @param {string} [start] Id to start paging (inclusive) + * @param {number} [limit] Number of items to return + * @param {string} [reverse] If the list should be reversed + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + transactionsGet(head?: string, start?: string, limit?: number, reverse?: string, options?: any): AxiosPromise { + return localVarFp.transactionsGet(head, start, limit, reverse, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary Fetches a particular transaction + * @param {string} transactionId Transaction id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + transactionsTransactionIdGet(transactionId: string, options?: any): AxiosPromise { + return localVarFp.transactionsTransactionIdGet(transactionId, options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * DefaultApi - object-oriented interface + * @export + * @class DefaultApi + * @extends {BaseAPI} + */ +export class DefaultApi extends BaseAPI { + /** + * Fetches an array of objects with a status and id for each batch requested. There are four possible statuses with string values `\'COMMITTED\'`, `\'INVALID\'`, `\'PENDING\'`, and `\'UNKNOWN\'`. The batch(es) you want to check can be specified using the `id` filter parameter. If a `wait` time is specified in the URL, the API will wait to respond until all batches are committed, or the time in seconds has elapsed. If the value of `wait` is not set (i.e., `?wait&id=...`), or it is set to any non-integer value other than `false`, the wait time will be just under the API\'s specified timeout (usually 300). Note that because this route does not return full resources, the response will not be paginated, and there will be no `head` or `paging` properties. + * @summary Fetches the committed statuses for a set of batches + * @param {string} id A comma-separated list of batch ids + * @param {number} [wait] A time in seconds to wait for commit + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public batchStatusesGet(id: string, wait?: number, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).batchStatusesGet(id, wait, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Identical to `GET /batch_statuses`, but takes ids of batches as a JSON formatted POST body rather than a query parameter. This allows for many more batches to be checked and should be used for more than 15 ids. Note that because query information is not encoded in the URL, no `link` will be returned with this query. + * @summary Fetches the committed statuses for a set of batches + * @param {Array} requestBody A JSON array of batch id strings + * @param {number} [wait] A time in seconds to wait for commit + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public batchStatusesPost(requestBody: Array, wait?: number, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).batchStatusesPost(requestBody, wait, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary Fetches a particular batch + * @param {string} batchId Batch id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public batchesBatchIdGet(batchId: string, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).batchesBatchIdGet(batchId, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Fetches a paginated list of batches from the validator. + * @summary Fetches a list of batches + * @param {string} [head] Index or id of head block + * @param {string} [start] Id to start paging (inclusive) + * @param {number} [limit] Number of items to return + * @param {string} [reverse] If the list should be reversed + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public batchesGet(head?: string, start?: string, limit?: number, reverse?: string, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).batchesGet(head, start, limit, reverse, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Accepts a protobuf formatted `BatchList` as an octet-stream binary file and submits it to the validator to be committed. The API will return immediately with a status of `202`. There will be no `data` object, only a `link` to a `/batch_statuses` endpoint to be polled to check the status of submitted batches. + * @summary Sends a BatchList to the validator + * @param {BatchList} batchList A binary encoded protobuf BatchList + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public batchesPost(batchList: BatchList, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).batchesPost(batchList, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary Fetches a particular block + * @param {string} blockId Block id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public blocksBlockIdGet(blockId: string, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).blocksBlockIdGet(blockId, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Fetches a paginated list of blocks from the validator. + * @summary Fetches a list of blocks + * @param {string} [head] Index or id of head block + * @param {string} [start] Id to start paging (inclusive) + * @param {number} [limit] Number of items to return + * @param {string} [reverse] If the list should be reversed + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public blocksGet(head?: string, start?: string, limit?: number, reverse?: string, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).blocksGet(head, start, limit, reverse, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary Fetches the endpoints of the authorized peers of the validator + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public peersGet(options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).peersGet(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Fetches an array of objects for each receipt requested. The receipt(s) you want to retrieve can be specified using the `id` filter parameter, where `id` refers to the transaction id of the transaction the receipt is associated with. + * @summary Fetches the receipts for a set of transactions + * @param {string} id A comma-separated list of transaction ids + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public receiptsGet(id: string, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).receiptsGet(id, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Identical to `GET /receipts`, but takes ids of transactions as a JSON formatted POST body rather than a query parameter. This allows for many more receipts to be fetched and should be used with more than 15 ids. Note that because query information is not encoded in the URL, no `link` will be returned with this request. + * @summary Fetches the receipts for a set of transactions + * @param {Array} requestBody A JSON array of transaction id strings + * @param {number} [wait] A time in seconds to wait for commit + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public receiptsPost(requestBody: Array, wait?: number, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).receiptsPost(requestBody, wait, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Takes full 70-character address and fetches a particular leaf. For partial address (i.e., group of leaves) use `/state` above. + * @summary Fetches a particular leaf from the current state + * @param {string} address Radix address of a leaf + * @param {string} [head] Index or id of head block + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public stateAddressGet(address: string, head?: string, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).stateAddressGet(address, head, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Fetches a paginated list of entries for the current state, or relative to a particular head block. Using the `address` filter parameter will narrow the list to any entries that have an address beginning with the characters specified. Note that the partial address in `address` parameter should have even number of hexadecimal characters (i.e., complete bytes). + * @summary Fetches the data for the current state + * @param {string} [head] Index or id of head block + * @param {string} [address] A partial address to filter leaves by + * @param {string} [start] Id to start paging (inclusive) + * @param {number} [limit] Number of items to return + * @param {string} [reverse] If the list should be reversed + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public stateGet(head?: string, address?: string, start?: string, limit?: number, reverse?: string, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).stateGet(head, address, start, limit, reverse, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary Fetches information pertaining to the status of the validator + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public statusGet(options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).statusGet(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Fetches a paginated list of transactions from the validator. + * @summary Fetches a list of transactions + * @param {string} [head] Index or id of head block + * @param {string} [start] Id to start paging (inclusive) + * @param {number} [limit] Number of items to return + * @param {string} [reverse] If the list should be reversed + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public transactionsGet(head?: string, start?: string, limit?: number, reverse?: string, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).transactionsGet(head, start, limit, reverse, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary Fetches a particular transaction + * @param {string} transactionId Transaction id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public transactionsTransactionIdGet(transactionId: string, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).transactionsTransactionIdGet(transactionId, options).then((request) => request(this.axios, this.basePath)); + } +} + + diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/base.ts b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/base.ts new file mode 100644 index 0000000000..4783e46717 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/base.ts @@ -0,0 +1,72 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Sawtooth REST API + * Official Sawtooth REST API specification with some modifications from Hyperledger Cacti project. + * + * The version of the OpenAPI document: 0.8.0 + * + * + * 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-sawtooth/src/main/typescript/sawtooth-api/common.ts b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/common.ts new file mode 100644 index 0000000000..881ab993b0 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/common.ts @@ -0,0 +1,150 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Sawtooth REST API + * Official Sawtooth REST API specification with some modifications from Hyperledger Cacti project. + * + * The version of the OpenAPI document: 0.8.0 + * + * + * 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-sawtooth/src/main/typescript/sawtooth-api/configuration.ts b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/configuration.ts new file mode 100644 index 0000000000..878723253d --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/configuration.ts @@ -0,0 +1,101 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Sawtooth REST API + * Official Sawtooth REST API specification with some modifications from Hyperledger Cacti project. + * + * The version of the OpenAPI document: 0.8.0 + * + * + * 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-sawtooth/src/main/typescript/sawtooth-api/git_push.sh b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/git_push.sh new file mode 100644 index 0000000000..f53a75d4fa --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/git_push.sh @@ -0,0 +1,57 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 +git_host=$4 + +if [ "$git_host" = "" ]; then + git_host="github.com" + echo "[INFO] No command line input provided. Set \$git_host to $git_host" +fi + +if [ "$git_user_id" = "" ]; then + git_user_id="GIT_USER_ID" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="GIT_REPO_ID" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="Minor update" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=$(git remote) +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/index.ts b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/index.ts new file mode 100644 index 0000000000..94ed37341a --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/index.ts @@ -0,0 +1,18 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Sawtooth REST API + * Official Sawtooth REST API specification with some modifications from Hyperledger Cacti project. + * + * The version of the OpenAPI document: 0.8.0 + * + * + * 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-sawtooth/src/main/typescript/types/model-type-guards.ts b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/types/model-type-guards.ts new file mode 100644 index 0000000000..00f01198ec --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/types/model-type-guards.ts @@ -0,0 +1,18 @@ +import { + WatchBlocksV1CactiTransactionsResponse, + WatchBlocksV1FullResponse, +} from "../generated/openapi/typescript-axios/api"; + +export function isWatchBlocksV1CactiTransactionsResponse( + response: unknown, +): response is WatchBlocksV1CactiTransactionsResponse { + const typedResponse = response as WatchBlocksV1CactiTransactionsResponse; + return typeof typedResponse.cactiTransactionsEvents !== "undefined"; +} + +export function isWatchBlocksV1FullResponse( + response: unknown, +): response is WatchBlocksV1FullResponse { + const typedResponse = response as WatchBlocksV1FullResponse; + return typeof typedResponse.fullBlock !== "undefined"; +} diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/web-services/status-endpoint-v1.ts b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/web-services/status-endpoint-v1.ts new file mode 100644 index 0000000000..713c1b0d07 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/web-services/status-endpoint-v1.ts @@ -0,0 +1,104 @@ +/** + * OpenAPI endpoint (GET) for reading status of the plugin and validator + */ + +import { + Logger, + Checks, + LogLevelDesc, + LoggerProvider, + IAsyncProvider, + safeStringifyException, +} from "@hyperledger/cactus-common"; +import type { + IEndpointAuthzOptions, + IExpressRequestHandler, + IWebServiceEndpoint, +} from "@hyperledger/cactus-core-api"; +import { registerWebServiceEndpoint } from "@hyperledger/cactus-core"; + +import OAS from "../../json/openapi.json"; +import { PluginLedgerConnectorSawtooth } from "../plugin-ledger-connector-sawtooth"; + +import type { Express, Request, Response } from "express"; + +export interface IStatusEndpointV1Options { + logLevel?: LogLevelDesc; + connector: PluginLedgerConnectorSawtooth; +} + +/** + * OpenAPI endpoint (GET) for reading status of the plugin and validator + */ +export class StatusEndpointV1 implements IWebServiceEndpoint { + private readonly log: Logger; + + public get className(): string { + return "StatusEndpointV1"; + } + + constructor(public readonly options: IStatusEndpointV1Options) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(options, `${fnTag} arg options`); + Checks.truthy(options.connector, `${fnTag} arg options.connector`); + + const level = this.options.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + } + + public getOasPath(): any { + return OAS.paths[ + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-sawtooth/status" + ]; + } + + public getPath(): string { + const apiPath = this.getOasPath(); + return apiPath.get["x-hyperledger-cactus"].http.path; + } + + public getVerbLowerCase(): string { + const apiPath = this.getOasPath(); + return apiPath.get["x-hyperledger-cactus"].http.verbLowerCase; + } + + public getOperationId(): string { + return this.getOasPath().get.operationId; + } + + getAuthorizationOptionsProvider(): IAsyncProvider { + // TODO: make this an injectable dependency in the constructor + return { + get: async () => ({ + isProtected: true, + requiredRoles: [], + }), + }; + } + + public async registerExpress( + expressApp: Express, + ): Promise { + await registerWebServiceEndpoint(expressApp, this); + return this; + } + + public getExpressRequestHandler(): IExpressRequestHandler { + return this.handleRequest.bind(this); + } + + public async handleRequest(req: Request, res: Response): Promise { + const reqTag = `${this.getVerbLowerCase()} - ${this.getPath()}`; + this.log.debug(reqTag); + try { + res.status(200).json(await this.options.connector.getStatus()); + } catch (ex) { + this.log.error(`Crash while serving ${reqTag}`, ex); + res.status(500).json({ + message: "Internal Server Error", + error: safeStringifyException(ex), + }); + } + } +} diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/web-services/watch-blocks-v1-endpoint.ts b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/web-services/watch-blocks-v1-endpoint.ts new file mode 100644 index 0000000000..5f1095cece --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/web-services/watch-blocks-v1-endpoint.ts @@ -0,0 +1,239 @@ +import * as cbor from "cbor"; +import type { Socket as SocketIoSocket } from "socket.io"; + +import { + Logger, + LogLevelDesc, + LoggerProvider, + Checks, + safeStringifyException, +} from "@hyperledger/cactus-common"; +import { + WatchBlocksV1Progress, + WatchBlocksV1, + WatchBlocksV1ListenerType, + WatchBlocksV1TransactionFilter, + WatchBlocksV1Options, +} from "../generated/openapi/typescript-axios"; +import { Block, DefaultApi as SawtoothRestApi } from "../sawtooth-api"; + +const DEFAULT_POLL_TIME = 1000 * 5; // Poll every 5 seconds by default + +export interface IWatchBlocksV1EndpointConfiguration { + logLevel?: LogLevelDesc; + socket: SocketIoSocket; + sawtoothApiClient: SawtoothRestApi; + options?: WatchBlocksV1Options; + pollTime?: number; +} + +export class WatchBlocksV1Endpoint { + private readonly log: Logger; + private readonly socket: SocketIoSocket< + Record void>, + Record void> + >; + private currentBlockHeight = 0; + private monitoringInterval?: NodeJS.Timer; + private isRoutineRunning = false; + + public get className(): string { + return "WatchBlocksV1Endpoint"; + } + + constructor(public readonly config: IWatchBlocksV1EndpointConfiguration) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(config, `${fnTag} arg options`); + Checks.truthy(config.socket, `${fnTag} arg options.socket`); + Checks.truthy( + config.sawtoothApiClient, + `${fnTag} arg options.sawtoothApiClient`, + ); + + this.socket = config.socket; + + const level = this.config.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + } + + /** + * Convert block number (example: 1, 42, etc..) to Sawtooth block number (example: 0x000000000000002a) + * + * @param blockNumber decimal block number + * @returns hexadecimal padded block number + */ + private toSawtoothBlockNumber(blockNumber: number) { + return "0x" + blockNumber.toString(16).padStart(16, "0"); + } + + /** + * Send Sawtooth transaction with custom cacti fields to a client (through socket) + * + * @param block sawtooth block + * @param txFilterBy optional filtering configuration + */ + private async sendCactiTransactions( + block: Block, + txFilterBy?: WatchBlocksV1TransactionFilter, + ) { + const cactiTransactionsEvents = block.batches + .flatMap((block) => block.transactions) + .filter((tx) => { + if (txFilterBy && txFilterBy.family_name) { + return tx.header.family_name === txFilterBy.family_name; + } + }) + .map((tx) => { + return { + ...tx, + payload_decoded: cbor.decodeAllSync( + Buffer.from(tx.payload, "base64"), + ), + }; + }); + + if (cactiTransactionsEvents.length > 0) { + this.log.debug( + `Sending ${cactiTransactionsEvents.length} transactions to the client`, + ); + this.socket.emit(WatchBlocksV1.Next, { + cactiTransactionsEvents, + }); + } + } + + /** + * Routine called periodically to monitor for new blocks. + * Will fetch blocks not seen yet and push them to client (based on supplied type). + * + * @param type response type format (full block, transactions, etc...) + * @param txFilterBy transactions filter (only when returning transactions) + * @returns void + */ + private async monitoringRoutine( + type?: WatchBlocksV1ListenerType, + txFilterBy?: WatchBlocksV1TransactionFilter, + ) { + if (this.isRoutineRunning) { + this.log.info( + "Previous monitoring routine didn't finish, ignore this call...", + ); + return; + } + + this.isRoutineRunning = true; + + try { + // Fetch new blocks + const newBlocksResponse = await this.config.sawtoothApiClient.blocksGet( + undefined, + this.toSawtoothBlockNumber(this.currentBlockHeight), + undefined, + "", + ); + const newBlocks = newBlocksResponse.data.data ?? []; + this.log.debug( + `monitoringRoutine() - received ${newBlocks.length} blocks since block #${this.currentBlockHeight}`, + ); + + for (const block of newBlocks) { + const thisBlockNumber = block.header.block_num; + if (thisBlockNumber === this.currentBlockHeight) { + continue; + } + + // Prase block according to supplied type + switch (type) { + case WatchBlocksV1ListenerType.CactiTransactions: + await this.sendCactiTransactions(block, txFilterBy); + break; + // Return full block by default + case WatchBlocksV1ListenerType.Full: + default: + this.log.debug( + `Sending full block #${this.currentBlockHeight} to the client`, + ); + this.socket.emit(WatchBlocksV1.Next, { + fullBlock: block, + }); + break; + } + + if (this.currentBlockHeight < thisBlockNumber) { + this.currentBlockHeight = thisBlockNumber; + this.log.debug("New currentBlockHeight:", this.currentBlockHeight); + } + } + } catch (error) { + this.log.error("monitoringRoutine error:", safeStringifyException(error)); + } finally { + this.isRoutineRunning = false; + } + } + + /** + * Subscribe to new blocks from sawtooth + * + * @returns void + */ + public async subscribe(): Promise { + const { socket, log } = this; + log.debug(`${WatchBlocksV1.Subscribe} => ${socket.id}`); + + // Get latest block number + const latestBlockResponse = await this.config.sawtoothApiClient.blocksGet( + undefined, + undefined, + 1, + ); + const latestBlock = latestBlockResponse.data.data; + if (!latestBlock || latestBlock.length !== 1) { + throw new Error( + "Invalid response when fetching latest block from Sawtooth REST API", + ); + } + + this.currentBlockHeight = latestBlock[0].header?.block_num ?? 0; + if (this.currentBlockHeight > 0) { + this.currentBlockHeight--; + } + this.log.info( + this.className, + "currentBlockHeight:", + this.currentBlockHeight, + ); + + const pollTime = this.config.pollTime ?? DEFAULT_POLL_TIME; + this.log.info("Starting monitoring routine with poll time", pollTime); + this.monitoringInterval = setInterval(() => { + this.monitoringRoutine( + this.config.options?.type, + this.config.options?.txFilterBy, + ); + }, pollTime); + + socket.on("disconnect", async (reason: string) => { + log.debug("WebSocket:disconnect reason=%o", reason); + this.close(); + }); + + socket.on(WatchBlocksV1.Unsubscribe, async () => { + log.debug(`${WatchBlocksV1.Unsubscribe}: unsubscribing...`); + this.close(); + }); + } + + /** + * Disconnect the socket if connected and stop monitoring routine. + */ + close(): void { + if (this.monitoringInterval) { + clearInterval(this.monitoringInterval); + this.monitoringInterval = undefined; + } + if (this.socket.connected) { + this.socket.disconnect(true); + } + } +} diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/test/typescript/integration/api-surface.test.ts b/packages/cactus-plugin-ledger-connector-sawtooth/src/test/typescript/integration/api-surface.test.ts new file mode 100644 index 0000000000..a65ea58247 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/test/typescript/integration/api-surface.test.ts @@ -0,0 +1,5 @@ +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-sawtooth/src/test/typescript/integration/sawtooth-monitoring-endpoints.test.ts b/packages/cactus-plugin-ledger-connector-sawtooth/src/test/typescript/integration/sawtooth-monitoring-endpoints.test.ts new file mode 100644 index 0000000000..da7acc1024 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/test/typescript/integration/sawtooth-monitoring-endpoints.test.ts @@ -0,0 +1,297 @@ +/** + * Tests for Sawtooth connector monitoring endpoint and status endpoint (used for sanity check) + */ + +import "jest-extended"; +import express from "express"; +import bodyParser from "body-parser"; +import http from "http"; +import { v4 as uuidV4 } from "uuid"; +import { AddressInfo } from "net"; +import { Server as SocketIoServer } from "socket.io"; +import type { Subscription } from "rxjs"; + +import { + LogLevelDesc, + LoggerProvider, + Logger, + IListenOptions, + Servers, +} from "@hyperledger/cactus-common"; +import { Configuration, Constants } from "@hyperledger/cactus-core-api"; +import { + SawtoothTestLedger, + pruneDockerAllIfGithubAction, +} from "@hyperledger/cactus-test-tooling"; + +import { + isWatchBlocksV1CactiTransactionsResponse, + isWatchBlocksV1FullResponse, + PluginLedgerConnectorSawtooth, + SawtoothApiClient, + WatchBlocksV1ListenerType, + WatchBlocksV1Progress, +} from "../../../main/typescript/public-api"; + +////////////////////////////////// +// Constants +////////////////////////////////// + +const testLogLevel: LogLevelDesc = "info"; +const sutLogLevel: LogLevelDesc = "info"; +const testTimeout = 1000 * 60 * 5; // 5 minutes timeout for async tests +const setupTimeout = 1000 * 60 * 5; // 5 minutes timeout for setup +const watchBlocksPollTime = 1000 * 3; // 3 seconds + +// Ledger settings +const containerImageName = "ghcr.io/hyperledger/cactus-sawtooth-all-in-one"; +const containerImageVersion = "2022-11-21-9da24a0"; + +// 1. leaveLedgerRunning = true, useRunningLedger = false to run ledger and leave it running after test finishes. +// 2. leaveLedgerRunning = true, useRunningLedger = true to use that ledger in future runs. +const useRunningLedger = false; +const leaveLedgerRunning = false; + +// Logger setup +const log: Logger = LoggerProvider.getOrCreate({ + label: "sawtooth-monitoring-endpoints.test", + level: testLogLevel, +}); + +describe("Sawtooth monitoring endpoints tests", () => { + const expressApp = express(); + expressApp.use(bodyParser.json({ limit: "250mb" })); + const server = http.createServer(expressApp); + const wsApi = new SocketIoServer(server, { + path: Constants.SocketIoConnectionPathV1, + }); + let ledger: SawtoothTestLedger; + let connector: PluginLedgerConnectorSawtooth; + let apiClient: SawtoothApiClient; + + ////////////////////////////////// + // Helpers + ////////////////////////////////// + + /** + * Common logic for executing watchBlock monitoring tests. + * Will subscribe to new blocks and send new transaction, to trigger creation of the new block. + * + * @param monitorName Unique name, will be used for identification and in transaction argument. + * @param type Type of block to receive. + * @param checkEventCallback Callback called when received the event from the connector. + * + * @returns void - just await for test to finish + */ + async function testWatchBlock( + monitorName: string, + type: WatchBlocksV1ListenerType, + checkEventCallback: (event: WatchBlocksV1Progress) => void, + ) { + let subscription: Subscription | undefined = undefined; + + // Start monitoring + const monitorPromise = new Promise((resolve, reject) => { + const watchObservable = apiClient.watchBlocksV1({ + type, + txFilterBy: { + family_name: "intkey", + }, + }); + + subscription = watchObservable.subscribe({ + next(event) { + log.debug("Received event:", JSON.stringify(event)); + try { + checkEventCallback(event); + subscription?.unsubscribe(); + resolve(true); + } catch (err) { + log.error("watchBlocksV1() event check error:", err); + subscription?.unsubscribe(); + reject(err); + } + }, + error(err) { + log.error("watchBlocksV1() error:", err); + subscription?.unsubscribe(); + reject(err); + }, + }); + }); + + // Wait for at least one monitor routine to finish before sending transactions + await new Promise((resolve) => + setTimeout(resolve, 2 * watchBlocksPollTime), + ); + + // Create new asset to trigger new block creation + let keyId = 1; + while (keyId++) { + const sleepPromise: Promise = new Promise((resolve) => + setTimeout(resolve, watchBlocksPollTime), + ); + + // infinite loop + // Set new key + const keyName = monitorName + keyId; + await ledger.runSawtoothShell(["intkey", "set", keyName, "42"]); + await ledger.runSawtoothShell(["intkey", "inc", keyName, "11"]); + + // Wait for 2 seconds or for new block to arrive + const resolvedValue = await Promise.race([monitorPromise, sleepPromise]); + log.debug("Monitor: resolvedValue", resolvedValue); + if (resolvedValue) { + log.info("Resolved watchBlock promise"); + await Promise.all([monitorPromise, sleepPromise]); + break; + } + } + } + + ////////////////////////////////// + // Setup + ////////////////////////////////// + + beforeAll(async () => { + log.info("Prune Docker..."); + await pruneDockerAllIfGithubAction({ logLevel: testLogLevel }); + + log.info(`Start Ledger ${containerImageName}:${containerImageVersion}...`); + ledger = new SawtoothTestLedger({ + containerImageName, + containerImageVersion, + emitContainerLogs: false, + logLevel: sutLogLevel, + useRunningLedger, + }); + await ledger.start(); + const ledgerRestApi = await ledger.getRestApiHost(); + log.info(`Ledger started, API: ${ledgerRestApi}`); + + log.info("Setup ApiServer..."); + const listenOptions: IListenOptions = { + hostname: "0.0.0.0", + port: 0, + server, + }; + const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; + const { address, port } = addressInfo; + const apiHost = `http://${address}:${port}`; + apiClient = new SawtoothApiClient(new Configuration({ basePath: apiHost })); + + log.info("Setup Connector..."); + connector = new PluginLedgerConnectorSawtooth({ + instanceId: uuidV4(), + logLevel: testLogLevel, + sawtoothRestApiEndpoint: ledgerRestApi, + watchBlocksPollTime, + }); + await connector.getOrCreateWebServices(); + await connector.registerWebServices(expressApp, wsApi); + }, setupTimeout); + + afterAll(async () => { + log.info("FINISHING THE TESTS"); + + if (apiClient) { + log.info("Close ApiClient connections..."); + apiClient.close(); + } + + if (server) { + log.info("Stop the HTTP and SocketIO server connector..."); + await Servers.shutdown(server); + } + + if (connector) { + log.info("Shutdown Sawtooth connector..."); + connector.shutdown(); + } + + if (ledger && !leaveLedgerRunning) { + log.info("Stop the sawtooth ledger..."); + await ledger.stop(); + await ledger.destroy(); + } + + log.info("Prune Docker..."); + await pruneDockerAllIfGithubAction({ logLevel: testLogLevel }); + }); + + ////////////////////////////////// + // Tests + ////////////////////////////////// + + test("get status returns valid response", async () => { + const statusResponse = await apiClient.getStatusV1(); + expect(statusResponse).toBeTruthy(); + expect(statusResponse.status).toBe(200); + const status = statusResponse.data; + expect(status).toBeTruthy(); + expect(status.instanceId).toBeTruthy(); + expect(status.openApiSpecVersion).toBeTruthy(); + expect(status.initialized).toBeTrue(); + expect(status.sawtoothStatus).toBeTruthy(); + }); + + test( + "watchBlocksV1 returns cacti transactions", + async () => { + await testWatchBlock( + "cactiTx", + WatchBlocksV1ListenerType.CactiTransactions, + (event) => { + // Check response body + if (!isWatchBlocksV1CactiTransactionsResponse(event)) { + throw new Error( + `Unexpected response from the connector: ${JSON.stringify( + event, + )}`, + ); + } + + for (const tx of event.cactiTransactionsEvents) { + expect(tx).toBeTruthy(); + expect(tx.header).toBeTruthy(); + expect(tx.header_signature).toBeTruthy(); + expect(tx.payload).toBeTruthy(); + expect(tx.payload_decoded).toBeTruthy(); + } + + log.info("Received cactiTransactionsEvents passed validation - OK!"); + }, + ); + }, + testTimeout, + ); + + test( + "watchBlocksV1 returns full blocks", + async () => { + await testWatchBlock( + "cactiFullBlock", + WatchBlocksV1ListenerType.Full, + (event) => { + // Check response body + if (!isWatchBlocksV1FullResponse(event)) { + throw new Error( + `Unexpected response from the connector: ${JSON.stringify( + event, + )}`, + ); + } + + const block = event.fullBlock; + expect(block).toBeTruthy(); + expect(block.batches).toBeTruthy(); + expect(block.header).toBeTruthy(); + expect(block.header_signature).toBeTruthy(); + + log.info("Received cactiTransactionsEvents passed validation - OK!"); + }, + ); + }, + testTimeout, + ); +}); diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/tsconfig.json b/packages/cactus-plugin-ledger-connector-sawtooth/tsconfig.json new file mode 100644 index 0000000000..d1a49384e9 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-sawtooth/tsconfig.json @@ -0,0 +1,26 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./dist/lib", + "declarationDir": "./dist/lib", + "resolveJsonModule": true, + "rootDir": "./src", + "tsBuildInfoFile": "../../.build-cache/cactus-plugin-ledger-connector-sawtooth.tsbuildinfo" + }, + "include": ["./src", "src/**/*.json"], + "references": [ + { + "path": "../cactus-common/tsconfig.json" + }, + { + "path": "../cactus-core/tsconfig.json" + }, + { + "path": "../cactus-core-api/tsconfig.json" + }, + { + "path": "../cactus-test-tooling/tsconfig.json" + } + ] +} diff --git a/packages/cactus-verifier-client/README.md b/packages/cactus-verifier-client/README.md index 8131d0fc45..c4a514d724 100644 --- a/packages/cactus-verifier-client/README.md +++ b/packages/cactus-verifier-client/README.md @@ -14,7 +14,8 @@ This package provides `Verifier` and `VerifierFactory` components that can be us | IROHA_1X | cactus-plugin-ledger-connector-iroha | | IROHA_2X | cactus-plugin-ledger-connector-iroha2 | | FABRIC_2X | cactus-plugin-ledger-connector-fabric | -| legacy-socketio | cactus-plugin-ledger-connector-go-ethereum-socketio
cactus-plugin-ledger-connector-sawtooth-socketio | +| SAWTOOTH_1X | cactus-plugin-ledger-connector-sawtooth | +| legacy-socketio | cactus-plugin-ledger-connector-go-ethereum-socketio | ## VerifierFactory - Used to create single verifier per ledger based on pre-defined configuration. diff --git a/packages/cactus-verifier-client/package.json b/packages/cactus-verifier-client/package.json index c9a3c4a8d5..8fef9ba4e8 100644 --- a/packages/cactus-verifier-client/package.json +++ b/packages/cactus-verifier-client/package.json @@ -52,6 +52,7 @@ "@hyperledger/cactus-plugin-ledger-connector-iroha": "2.0.0-alpha.2", "@hyperledger/cactus-plugin-ledger-connector-iroha2": "2.0.0-alpha.2", "@hyperledger/cactus-plugin-ledger-connector-quorum": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-ledger-connector-sawtooth": "2.0.0-alpha.2", "jest-extended": "4.0.1", "rxjs": "7.8.1" }, diff --git a/packages/cactus-verifier-client/src/main/typescript/get-validator-api-client.ts b/packages/cactus-verifier-client/src/main/typescript/get-validator-api-client.ts index f56b79e326..673d91e58a 100644 --- a/packages/cactus-verifier-client/src/main/typescript/get-validator-api-client.ts +++ b/packages/cactus-verifier-client/src/main/typescript/get-validator-api-client.ts @@ -45,6 +45,11 @@ import { FabricApiClientOptions, } from "@hyperledger/cactus-plugin-ledger-connector-fabric"; +import { + SawtoothApiClient, + SawtoothApiClientOptions, +} from "@hyperledger/cactus-plugin-ledger-connector-sawtooth"; + /** * Configuration of ApiClients currently supported by Verifier and VerifierFactory * Each entry key defines the name of the connection type that has to be specified in VerifierFactory config. @@ -88,6 +93,10 @@ export type ClientApiConfig = { in: FabricApiClientOptions; out: FabricApiClient; }; + SAWTOOTH_1X: { + in: SawtoothApiClientOptions; + out: SawtoothApiClient; + }; }; /** @@ -121,6 +130,8 @@ export function getValidatorApiClient( return new Iroha2ApiClient(options as CordaApiClientOptions); case "FABRIC_2X": return new FabricApiClient(options as FabricApiClientOptions); + case "SAWTOOTH_1X": + return new SawtoothApiClient(options as SawtoothApiClientOptions); default: // Will not compile if any ClientApiConfig key was not handled by this switch const _: never = validatorType; diff --git a/packages/cactus-verifier-client/tsconfig.json b/packages/cactus-verifier-client/tsconfig.json index 4dd36a75cb..da75c3d8b9 100644 --- a/packages/cactus-verifier-client/tsconfig.json +++ b/packages/cactus-verifier-client/tsconfig.json @@ -40,6 +40,9 @@ }, { "path": "../cactus-plugin-ledger-connector-fabric/tsconfig.json" + }, + { + "path": "../cactus-plugin-ledger-connector-sawtooth/tsconfig.json" } ] } diff --git a/tsconfig.json b/tsconfig.json index 5f2373c806..6098243927 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -112,6 +112,9 @@ { "path": "./packages/cactus-test-plugin-ledger-connector-quorum/tsconfig.json" }, + { + "path": "./packages/cactus-plugin-ledger-connector-sawtooth/tsconfig.json" + }, { "path": "./packages/cactus-plugin-ledger-connector-sawtooth-socketio/tsconfig.json" }, diff --git a/yarn.lock b/yarn.lock index 53b4b2f1f7..b0efdd7271 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7954,6 +7954,27 @@ __metadata: languageName: unknown linkType: soft +"@hyperledger/cactus-plugin-ledger-connector-sawtooth@2.0.0-alpha.2, @hyperledger/cactus-plugin-ledger-connector-sawtooth@workspace:packages/cactus-plugin-ledger-connector-sawtooth": + version: 0.0.0-use.local + resolution: "@hyperledger/cactus-plugin-ledger-connector-sawtooth@workspace:packages/cactus-plugin-ledger-connector-sawtooth" + 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-test-tooling": 2.0.0-alpha.2 + "@types/express": 4.17.20 + "@types/uuid": 9.0.6 + axios: 1.5.1 + body-parser: 1.20.2 + cbor: 9.0.1 + express: 4.18.2 + rxjs: 7.8.1 + socket.io: 4.5.4 + socket.io-client: 4.5.4 + uuid: 9.0.1 + languageName: unknown + linkType: soft + "@hyperledger/cactus-plugin-ledger-connector-tcs-huawei-socketio@workspace:packages/cactus-plugin-ledger-connector-tcs-huawei-socketio": version: 0.0.0-use.local resolution: "@hyperledger/cactus-plugin-ledger-connector-tcs-huawei-socketio@workspace:packages/cactus-plugin-ledger-connector-tcs-huawei-socketio" @@ -8363,6 +8384,7 @@ __metadata: "@hyperledger/cactus-plugin-ledger-connector-iroha": 2.0.0-alpha.2 "@hyperledger/cactus-plugin-ledger-connector-iroha2": 2.0.0-alpha.2 "@hyperledger/cactus-plugin-ledger-connector-quorum": 2.0.0-alpha.2 + "@hyperledger/cactus-plugin-ledger-connector-sawtooth": 2.0.0-alpha.2 jest-extended: 4.0.1 rxjs: 7.8.1 languageName: unknown @@ -12793,6 +12815,18 @@ __metadata: languageName: node linkType: hard +"@types/express@npm:4.17.20": + version: 4.17.20 + resolution: "@types/express@npm:4.17.20" + dependencies: + "@types/body-parser": "*" + "@types/express-serve-static-core": ^4.17.33 + "@types/qs": "*" + "@types/serve-static": "*" + checksum: bf8a97d283128e5129f9ccabbeef728ff3f0484465e0ae74a304bd0588fa6cb715ae68845650caba9a641944b7791ba125d02ddbd47a7e62aaefdd036570c6c5 + languageName: node + linkType: hard + "@types/express@npm:^4.17.13": version: 4.17.17 resolution: "@types/express@npm:4.17.17" @@ -13713,6 +13747,13 @@ __metadata: languageName: node linkType: hard +"@types/uuid@npm:9.0.6": + version: 9.0.6 + resolution: "@types/uuid@npm:9.0.6" + checksum: 739dcb2e620ff097fa916edeab455eb75640c4883a850784fdb15b32f67b719e05275c6d6419bb6da11350d26bd14ed05ba5e992ff228411cdd98cbc772d71ef + languageName: node + linkType: hard + "@types/ws@npm:8.5.3, @types/ws@npm:^8.2.2": version: 8.5.3 resolution: "@types/ws@npm:8.5.3" @@ -18085,6 +18126,15 @@ __metadata: languageName: node linkType: hard +"cbor@npm:9.0.1": + version: 9.0.1 + resolution: "cbor@npm:9.0.1" + dependencies: + nofilter: ^3.1.0 + checksum: 42333ac3d42cc3f6fcc7a529e68417a2dd8099eda43ca4be1304cdc5bc7494efe058e2db8a3d3b46ae60d69c7331ea813c22dbd019c4ac592d23e599d72bbcc9 + languageName: node + linkType: hard + "cbor@npm:^5.2.0": version: 5.2.0 resolution: "cbor@npm:5.2.0" @@ -35435,6 +35485,13 @@ __metadata: languageName: node linkType: hard +"nofilter@npm:^3.1.0": + version: 3.1.0 + resolution: "nofilter@npm:3.1.0" + checksum: 58aa85a5b4b35cbb6e42de8a8591c5e338061edc9f3e7286f2c335e9e9b9b8fa7c335ae45daa8a1f3433164dc0b9a3d187fa96f9516e04a17a1f9ce722becc4f + languageName: node + linkType: hard + "noop-logger@npm:^0.1.1": version: 0.1.1 resolution: "noop-logger@npm:0.1.1"