From e1d86c3e3f07dcf7f09d0957a75678c6cccc2819 Mon Sep 17 00:00:00 2001 From: Fabricius Zatti Date: Wed, 22 May 2024 19:09:15 +0000 Subject: [PATCH] build(connector-stellar): add a deploy contract endpoint - Add a Stellar Connector plugin following the same pattern as the **Besu Connector plugin**. - Add a deploy contract endpoint to the Stellar Connector plugin. **Initialization remarks:** Supports a network configuration object to define all integration services that seamlessly integrate with the Stellar test ledger within the Cacti test tooling. **Deploy remarks:** The deploy process supports both the compiled smart contract WASM as well as the on-chain WASM hash as inputs. This follows the smart contract deployment design on Soroban (Stellar's smart contract platform). Refer to the Stellar documentation at: https://developers.stellar.org/docs/learn/fundamentals/stellar-data-structures/contracts for further detail on this process. More details can be found in the `README.md` file under the connector root directory. Signed-off-by: Fabricius Zatti --- .github/workflows/ci.yaml | 40 +++ .../README.md | 128 ++++++++-- .../package.json | 9 +- .../src/main/json/openapi.json | 128 +++++++--- .../main/typescript/core/contract-engine.ts | 78 ++++++ .../generated/openapi/typescript-axios/api.ts | 181 +++++++------ .../openapi/typescript-axios/base.ts | 2 +- .../openapi/typescript-axios/common.ts | 2 +- .../openapi/typescript-axios/configuration.ts | 2 +- .../openapi/typescript-axios/index.ts | 2 +- .../plugin-ledger-connector-stellar.ts | 82 ++++-- .../src/main/typescript/public-api.ts | 38 ++- .../src/main/typescript/utils/index.ts | 37 +++ .../web-services/deploy-contract-endpoint.ts | 103 ++++++++ ...prometheus-exporter-metrics-endpoint-v1.ts | 102 ++++++++ .../soroban_token_contract.wasm | Bin 0 -> 7456 bytes .../deploy-contract/index.test.ts | 238 ++++++++++++++++++ .../plugin-ledger-connector-stellar/index.ts | 1 + .../validate-test-ledger.test.ts | 67 +++++ ...open-api-spec-v1-connector-stellar.test.ts | 153 ++++++----- yarn.lock | 118 ++++++++- 21 files changed, 1246 insertions(+), 265 deletions(-) create mode 100644 packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/core/contract-engine.ts create mode 100644 packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/utils/index.ts create mode 100644 packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/web-services/deploy-contract-endpoint.ts create mode 100644 packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/web-services/get-prometheus-exporter-metrics-endpoint-v1.ts create mode 100644 packages/cacti-plugin-ledger-connector-stellar/src/test/rust/token-contract/soroban_token_contract.wasm create mode 100644 packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/integration/plugin-ledger-connector-stellar/deploy-contract/index.test.ts create mode 100644 packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/integration/plugin-ledger-connector-stellar/index.ts create mode 100644 packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/integration/plugin-ledger-connector-stellar/validate-test-ledger.test.ts diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a8db749e20..00c17c4fd7 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -39,6 +39,7 @@ jobs: plugin-ledger-connector-ethereum-changed: ${{ steps.changes.outputs.plugin-ledger-connector-ethereum-changed }} plugin-ledger-connector-iroha2-changed: ${{ steps.changes.outputs.plugin-ledger-connector-iroha2-changed }} plugin-ledger-connector-quorum-changed: ${{ steps.changes.outputs.plugin-ledger-connector-quorum-changed }} + plugin-ledger-connector-stellar-changed: ${{ steps.changes.outputs.plugin-ledger-connector-stellar-changed }} plugin-htlc-coordinator-besu-changed: ${{ steps.changes.outputs.plugin-htlc-coordinator-besu-changed }} test-tooling-changed: ${{ steps.changes.outputs.test-tooling-changed }} ghcr-corda-all-in-one-obligation-changed: ${{ steps.changes.outputs.ghcr-corda-all-in-one-obligation-changed }} @@ -134,6 +135,15 @@ jobs: - './packages/cactus-plugin-keychain-memory/**' # - './.github/workflows/ci.yaml' + plugin-ledger-connector-stellar-changed: + - './packages/cacti-plugin-ledger-connector-stellar/**' + - './packages/cactus-common/**' + - './packages/cactus-core/**' + - './packages/cactus-core-api/**' + - './packages/cactus-test-tooling/**' + - './packages/cactus-plugin-keychain-memory/**' + # - './.github/workflows/ci.yaml' + test-tooling-changed: - './packages/cactus-test-tooling/**' - './packages/cactus-common/**' @@ -1105,6 +1115,36 @@ jobs: restore-keys: | ${{ runner.os }}-yarn-${{ hashFiles('./yarn.lock') }} - run: ./tools/ci.sh + + cpl-connector-stellar: + continue-on-error: false + needs: + - build-dev + - compute_changed_packages + if: needs.compute_changed_packages.outputs.plugin-ledger-connector-stellar-changed == 'true' + env: + FULL_BUILD_DISABLED: true + JEST_TEST_PATTERN: packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/(unit|integration|benchmark)/.*/*.test.ts + JEST_TEST_RUNNER_DISABLED: false + TAPE_TEST_RUNNER_DISABLED: true + runs-on: ubuntu-22.04 + steps: + - name: Use Node.js ${{ env.NODEJS_VERSION }} + uses: actions/setup-node@v4.0.2 + with: + node-version: ${{ env.NODEJS_VERSION }} + - uses: actions/checkout@v4.1.1 + + - id: yarn-cache + name: Restore Yarn Cache + uses: actions/cache@v4.0.1 + with: + key: ${{ runner.os }}-yarn-${{ hashFiles('./yarn.lock') }} + path: ./.yarn/ + restore-keys: | + ${{ runner.os }}-yarn-${{ hashFiles('./yarn.lock') }} + - run: ./tools/ci.sh + plc-fabric-0: needs: diff --git a/packages/cacti-plugin-ledger-connector-stellar/README.md b/packages/cacti-plugin-ledger-connector-stellar/README.md index 13ccc7ff20..a0b4f10542 100644 --- a/packages/cacti-plugin-ledger-connector-stellar/README.md +++ b/packages/cacti-plugin-ledger-connector-stellar/README.md @@ -1,28 +1,22 @@ # `@hyperledger/cacti-plugin-ledger-connector-stellar` +This plugin provides `Cacti` a way to interact with Stellar networks. Using this we can perform: -## 🚧 **Package Under Construction** 🚧 - -This package is a skeleton for now with no exported modules at all. -Stay tuned for later improvements. - -## 🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧 +- Deploy Smart-contracts over the network. +- Build and sign transactions. (WIP) +- Invoke smart-contract functions that we have deployed on the network. (WIP) -This plugin provides `Cacti` a way to interact with Stellar networks. Using this we can perform: -* Deploy Smart-contracts over the network. -* Build and sign transactions using different keystores. -* Invoke smart-contract functions that we have deployed on the network. ## Summary - - [Getting Started](#getting-started) - - [Architecture](#architecture) - - [Usage](#usage) - - [Prometheus Exporter](#prometheus-exporter) - - [Runing the tests](#running-the-tests) - - [Built With](#built-with) - - [Contributing](#contributing) - - [License](#license) - - [Acknowledgments](#acknowledgments) +- [Getting Started](#getting-started) +- [Architecture](#architecture) +- [Usage](#usage) +- [Prometheus Exporter](#prometheus-exporter) +- [Runing the tests](#running-the-tests) +- [Built With](#built-with) +- [Contributing](#contributing) +- [License](#license) +- [Acknowledgments](#acknowledgments) ## Getting Started @@ -31,7 +25,8 @@ your local machine for development and testing purposes. ### Prerequisites -In the root of the project to install the dependencies execute the command: +In the root of the Cacti project to install the dependencies execute the command: + ```sh npm run enable-corepack yarn configure @@ -40,17 +35,81 @@ yarn configure ### Compiling In the project root folder, run this command to compile the plugin and create the dist directory: + ```sh yarn tsc ``` ### Architecture -TODO +The Stellar Ledger Connector offers a variaty of endpoints to handle specific actions for a given Stellar network. + +#### `deploy-contract` endpoint + +This endpoint is responsible for deploying smart contracts to Soroban (Stellar's smart contract platform). + +**Core Aspects**: + +- **Input**: Accepts either a compiled WASM buffer or a WASM hash. + - **WASM Buffer**: Uploads compiled code to Stellar, generates a on-chain WASM hash, then deploys the contract. Refer to the [Stellar documentation](https://developers.stellar.org/docs/learn/fundamentals/stellar-data-structures/contracts) for further detail on this process. + - **WASM Hash**: Directly deploys the contract using the existing WASM hash. +- **Output**: An object containing the on-chain WASM hash and a unique contract ID of the newly deployed instance. + +- **Transaction Invocation**: Provides data for assembling and executing the transactions. ### Usage -TODO +#### Initialization + +To use this import public-api and create new **PluginFactoryLedgerConnector**. Then use it to create a connector. + +```typescript +const factory = new PluginFactoryLedgerConnector({ + pluginImportType: PluginImportType.LOCAL, +}); +const connector: PluginLedgerConnectorStellar = await factory.create({ + networkConfig, + pluginRegistry: new PluginRegistry({}), + instanceId: uuidV4(), +}); +``` + +A key element of the connector intialization is the provided `networkConfig` object. It follows the standard format defined in the open source library [Stellar Plus](https://github.com/CheesecakeLabs/stellar-plus) and can be assembled by the `CustomNet()` function provided by it as well as the most common network environments for Stellar such as the `TestNet()` for Stellar testnet or `FutureNet()` for early features and protocol changes validation. + +When using the _Stellar Test Ledger_ provided under the Cacti Test tooling, the network can be assembled using the `CustomNet()` function combined with the test ledger `getNetworkConfiguration()` method. Once the test ledger has been fully started, it exposes the configuration parameters for all its services. See the following example: + +```typescript +const stellarTestLedger = new StellarTestLedger({ logLevel }); + +await stellarTestLedger.start(); +const networkConfig = CustomNet( + await stellarTestLedger.getNetworkConfiguration(), +); +``` + +#### Making Calls + +One can make calls through the connector to the plugin API: + +```typescript +async deployContract(req: DeployContractV1Request):Promise; +``` + +Call example to deploy a contract: + +```typescript +const deployOut = await connector.deployContract({ + wasmBuffer: wasmBuffer.toString("base64"), + transactionInvocation: { + header: { + source: deployerAccount.getPublicKey(), + fee: 100, + timeout: 30, + }, + signers: [deployerAccount.getSecretKey()], + }, +}); +``` ### Building/running the container image locally @@ -61,6 +120,7 @@ DOCKER_BUILDKIT=1 docker build -f ./packages/cacti-plugin-ledger-connector-stell ``` Build with a specific version of the npm package: + ```sh DOCKER_BUILDKIT=1 docker build --build-arg NPM_PKG_VERSION=2.0.0-alpha.2 -f ./packages/cacti-plugin-ledger-connector-stellar/Dockerfile . --tag cplcs --tag cacti-plugin-ledger-connector-stellar ``` @@ -68,6 +128,7 @@ DOCKER_BUILDKIT=1 docker build --build-arg NPM_PKG_VERSION=2.0.0-alpha.2 -f ./pa #### Running the container Launch container with plugin configuration as an **environment variable**: + ```sh docker run \ --rm \ @@ -78,6 +139,7 @@ docker run \ ``` Launch container with plugin configuration as a **CLI argument**: + ```sh docker run \ --rm \ @@ -89,6 +151,7 @@ docker run \ ``` Launch container with **configuration file** mounted from host machine: + ```sh echo '[{"packageName": "@hyperledger/cacti-plugin-ledger-connector-stellar", "type": "org.hyperledger.cactus.plugin_import_type.LOCAL", "action": "org.hyperledger.cactus.plugin_import_action.INSTALL", "options": { "instanceId": "some-unique-stellar-connector-instance-id"}}]' > cactus.json @@ -107,8 +170,7 @@ docker run \ Don't have a Stellar network on hand to test with? Test or develop against our Stellar All-In-One container! -TODO - +TODO (WIP) ## Prometheus Exporter @@ -119,7 +181,10 @@ This class creates a prometheus exporter, which scrapes the transactions (total The prometheus exporter object is initialized in the `PluginLedgerConnectorStellar` class constructor itself, so instantiating the object of the `PluginLedgerConnectorStellar` class, gives access to the exporter object. You can also initialize the prometheus exporter object seperately and then pass it to the `IPluginLedgerConnectorStellarOptions` interface for `PluginLedgerConnectorStellar` constructor. -`getPrometheusMetricsV1` function returns the prometheus exporter metrics, currently displaying the total transaction count, which currently increments everytime the `transact()` method of the `PluginLedgerConnectorStellar` class is called. +`getPrometheusMetricsV1` function returns the prometheus exporter metrics, currently displaying the total transaction count, which currently increments everytime a Stellar transaction is executed through the connector internal methods. This includes the methods + +- `deployContract` + - `runSorobanTransaction()` (_Soon_) ### Prometheus Integration @@ -143,17 +208,26 @@ On the prometheus graphical interface (defaulted to http://localhost:9090), choo ### Helper code ###### response.type.ts + This file contains the various responses of the metrics. ###### data-fetcher.ts + This file contains functions encasing the logic to process the data points ###### metrics.ts + This file lists all the prometheus metrics and what they are used for. ## Running the tests -TODO +To check that all has been installed correctly and that the pugin has no errors run the tests: + +- Run this command at the project's root: + +```sh +yarn jest packages/cacti-plugin-ledger-connector-stellar/ +``` ## Contributing @@ -165,4 +239,4 @@ Please review [CONTIRBUTING.md](../../CONTRIBUTING.md) to get started. This distribution is published under the Apache License Version 2.0 found in the [LICENSE](../../LICENSE) file. -## Acknowledgments +## Acknowledgments diff --git a/packages/cacti-plugin-ledger-connector-stellar/package.json b/packages/cacti-plugin-ledger-connector-stellar/package.json index ab039db462..64d749ecde 100644 --- a/packages/cacti-plugin-ledger-connector-stellar/package.json +++ b/packages/cacti-plugin-ledger-connector-stellar/package.json @@ -1,7 +1,6 @@ { "name": "@hyperledger/cacti-plugin-ledger-connector-stellar", "version": "2.0.0-alpha.2", - "private": true, "description": "Allows Cacti nodes to connect to a Stellar ledger.", "keywords": [ "Hyperledger", @@ -34,6 +33,11 @@ "name": "Peter Somogyvari", "email": "peter.somogyvari@accenture.com", "url": "https://accenture.com" + }, + { + "name": "Fabricius Zatti", + "email": "fazzatti@gmail.com", + "url": "https://oififo.com" } ], "main": "dist/lib/main/typescript/index.js", @@ -67,6 +71,7 @@ "run-time-error-cjs": "1.4.0", "rxjs": "7.8.1", "socket.io-client-fixed-types": "4.5.4", + "stellar-plus": "0.8.4", "typescript-optional": "2.0.1" }, "devDependencies": { @@ -86,7 +91,7 @@ "npm": ">=8" }, "publishConfig": { - "access": "restricted" + "access": "public" }, "browserMinified": "dist/cacti-plugin-ledger-connector-stellar.web.umd.min.js", "mainMinified": "dist/cacti-plugin-ledger-connector-stellar.node.umd.min.js", diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/main/json/openapi.json b/packages/cacti-plugin-ledger-connector-stellar/src/main/json/openapi.json index 7c4d1b367c..077a180093 100644 --- a/packages/cacti-plugin-ledger-connector-stellar/src/main/json/openapi.json +++ b/packages/cacti-plugin-ledger-connector-stellar/src/main/json/openapi.json @@ -2,7 +2,7 @@ "openapi": "3.0.3", "info": { "title": "Hyperledger Cacti Plugin - Connector Stellar", - "description": "Can perform basic tasks on a Stellar ledger", + "description": "Can perform basic smart contract tasks on a Stellar ledger", "version": "v2.0.0-alpha.2", "license": { "name": "Apache-2.0", @@ -38,27 +38,110 @@ } } }, - "RunTransactionRequest": { + "TransactionInvocation": { "type": "object", - "additionalProperties": false, + "required": ["header", "signers"], "properties": { - + "header": { + "$ref": "#/components/schemas/TransactionHeader" + }, + "signers": { + "type": "array", + "items": { + "type": "string", + "pattern": "^S[a-zA-Z0-9]{55}$", + "nullable": false, + "minLength": 56, + "maxLength": 56, + "description": "The secret key of the account that will sign the transaction." + }, + "nullable": false + } } }, - "RunTransactionResponse": { + "TransactionHeader": { "type": "object", + "required": ["source", "fee", "timeout"], "properties": { + "source": { + "type": "string", + "pattern": "^G[a-zA-Z0-9]{55}$", + "nullable": false, + "minLength": 56, + "maxLength": 56, + "description": "The public key of the source account of the transaction." + }, + "fee": { + "type": "integer", + "nullable": false, + "minimum": 100, + "description": "The maximum base fee in stroops to be paid for the transaction." + }, + "timeout": { + "type": "integer", + "nullable": false, + "minimum": 0, + "description": "The maximum number of ledger close time in seconds that the transaction is valid for. '0' equals to no timeout." + } } }, + "DeployContractV1Request": { "type": "object", "additionalProperties": false, + "required": ["transactionInvocation"], + "anyOf": [ + { + "required": ["wasmHash"] + }, + { + "required": ["wasmBuffer"] + } + ], "properties": { + "wasmHash": { + "type": "string", + "pattern": "^[a-f0-9]{64}$", + "nullable": true, + "minLength": 64, + "maxLength": 64, + "description": "The hash of the wasm code installed in the ledger to be deployed into a new instance." + }, + "wasmBuffer": { + "type": "string", + "format": "byte", + "nullable": true, + "minLength": 100, + "maxLength": 87382, + "description": "A Base64-encoded string that contains the binary data of the WASM code. This buffer is used to deploy WebAssembly code to the ledger." + }, + "transactionInvocation": { + "$ref": "#/components/schemas/TransactionInvocation", + "nullable": true, + "description": "The transaction invocation that will be used to deploy the contract." + } } }, "DeployContractV1Response": { "type": "object", + "required": ["contractId", "wasmHash"], "properties": { + "contractId": { + "type": "string", + "pattern": "^C[a-f0-9]{55}$", + "nullable": true, + "minLength": 56, + "maxLength": 56, + "description": "The ID of the contract that was deployed." + }, + "wasmHash": { + "type": "string", + "pattern": "^[a-f0-9]{64}$", + "nullable": true, + "minLength": 64, + "maxLength": 64, + "description": "The hash of the wasm code installed in the ledger." + } } }, "PrometheusExporterMetricsResponse": { @@ -127,40 +210,7 @@ } } }, - "/api/v1/plugins/@hyperledger/cacti-plugin-ledger-connector-stellar/run-transaction": { - "post": { - "x-hyperledger-cacti": { - "http": { - "verbLowerCase": "post", - "path": "/api/v1/plugins/@hyperledger/cacti-plugin-ledger-connector-stellar/run-transaction" - } - }, - "operationId": "runTransactionV1", - "summary": "Executes a transaction on a stellar ledger", - "parameters": [], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RunTransactionRequest" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RunTransactionResponse" - } - } - } - } - } - } - }, + "/api/v1/plugins/@hyperledger/cacti-plugin-ledger-connector-stellar/get-prometheus-exporter-metrics": { "get": { "x-hyperledger-cacti": { diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/core/contract-engine.ts b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/core/contract-engine.ts new file mode 100644 index 0000000000..b1a0f5291e --- /dev/null +++ b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/core/contract-engine.ts @@ -0,0 +1,78 @@ +import { ContractEngine } from "stellar-plus/lib/stellar-plus/core/contract-engine"; +import { SorobanTransactionPipelineOptions } from "stellar-plus/lib/stellar-plus/core/pipelines/soroban-transaction/types"; +import { NetworkConfig } from "stellar-plus/lib/stellar-plus/network"; +import { TransactionInvocation } from "stellar-plus/lib/stellar-plus/types"; + +export interface DeployContractWithWasmOptions + extends BaseContractEngineInvocation { + wasmBuffer: Buffer; +} + +export interface DeployContractWithWasmHashOptions + extends BaseContractEngineInvocation { + wasmHash: string; +} + +export interface DeployContractOutput { + contractId: string; + wasmHash: string; +} + +interface BaseContractEngineInvocation { + txInvocation: TransactionInvocation; + pipelineOptions?: SorobanTransactionPipelineOptions; + fnLogPrefix: string; + networkConfig: NetworkConfig; +} + +/** + * Deploys a contract to the Stellar network. Accepts either a WebAssembly binary buffer or a hash of the WebAssembly binary code in the ledger. + * + * @param {DeployContractWithWasmOptions | DeployContractWithWasmHashOptions} options - Options for deploying a contract. + * @param {TransactionInvocation} options.txInvocation - Transaction invocation object containing the parameters to configure the transaction envelope. + * @param {SorobanTransactionPipelineOptions} [options.pipelineOptions] - Options for the Soroban transaction pipeline. + * @param {string} options.fnLogPrefix - Prefix for log messages. + * @param {NetworkConfig} options.networkConfig - Network configuration object. Contains the details of the services available to interact with the Stellar network. + * @param {Buffer} [options.wasmBuffer] - Buffer containing the WebAssembly binary code of the contract. + * @param {string} [options.wasmHash] - Hash of the WebAssembly binary code of the contract from the ledger. + * + * @returns {Promise} - Returns the contract ID and the hash of the WebAssembly binary code of the contract. + * + * @throws {Error} - Throws an error if the contract deployment fails. + * + */ +export const deployContract = async ( + options: DeployContractWithWasmOptions | DeployContractWithWasmHashOptions, +): Promise => { + const { networkConfig, txInvocation, fnLogPrefix, pipelineOptions } = options; + + const fnTag = `${fnLogPrefix}#deployContract()`; + + const contractEngine = new ContractEngine({ + networkConfig, + contractParameters: { + wasm: (options as DeployContractWithWasmOptions).wasmBuffer, + wasmHash: (options as DeployContractWithWasmHashOptions).wasmHash, + }, + options: { + sorobanTransactionPipeline: pipelineOptions, + }, + }); + + if ((options as DeployContractWithWasmOptions).wasmBuffer) { + await contractEngine.uploadWasm(txInvocation).catch((error) => { + throw new Error(`${fnTag} Failed to upload wasm. ` + error); + }); + } + + await contractEngine.deploy(txInvocation).catch((error) => { + throw new Error(`${fnTag} Failed to deploy contract. ` + error); + }); + + const output: DeployContractOutput = { + contractId: contractEngine.getContractId(), + wasmHash: contractEngine.getWasmHash(), + }; + + return output; +}; diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/api.ts b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/api.ts index bc9085aec2..1b89758a3f 100644 --- a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/api.ts +++ b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/api.ts @@ -2,7 +2,7 @@ /* eslint-disable */ /** * Hyperledger Cacti Plugin - Connector Stellar - * Can perform basic tasks on a Stellar ledger + * Can perform basic smart contract tasks on a Stellar ledger * * The version of the OpenAPI document: v2.0.0-alpha.2 * @@ -23,6 +23,94 @@ import type { RequestArgs } from './base'; // @ts-ignore import { BASE_PATH, COLLECTION_FORMATS, BaseAPI, RequiredError } from './base'; +/** + * + * @export + * @interface DeployContractV1Request + */ +export interface DeployContractV1Request { + /** + * The hash of the wasm code installed in the ledger to be deployed into a new instance. + * @type {string} + * @memberof DeployContractV1Request + */ + 'wasmHash'?: string | null; + /** + * A Base64-encoded string that contains the binary data of the WASM code. This buffer is used to deploy WebAssembly code to the ledger. + * @type {string} + * @memberof DeployContractV1Request + */ + 'wasmBuffer'?: string | null; + /** + * + * @type {TransactionInvocation} + * @memberof DeployContractV1Request + */ + 'transactionInvocation': TransactionInvocation; +} +/** + * + * @export + * @interface DeployContractV1Response + */ +export interface DeployContractV1Response { + /** + * The ID of the contract that was deployed. + * @type {string} + * @memberof DeployContractV1Response + */ + 'contractId': string | null; + /** + * The hash of the wasm code installed in the ledger. + * @type {string} + * @memberof DeployContractV1Response + */ + 'wasmHash': string | null; +} +/** + * + * @export + * @interface TransactionHeader + */ +export interface TransactionHeader { + /** + * The public key of the source account of the transaction. + * @type {string} + * @memberof TransactionHeader + */ + 'source': string; + /** + * The maximum base fee in stroops to be paid for the transaction. + * @type {number} + * @memberof TransactionHeader + */ + 'fee': number; + /** + * The maximum number of ledger close time in seconds that the transaction is valid for. \'0\' equals to no timeout. + * @type {number} + * @memberof TransactionHeader + */ + 'timeout': number; +} +/** + * + * @export + * @interface TransactionInvocation + */ +export interface TransactionInvocation { + /** + * + * @type {TransactionHeader} + * @memberof TransactionInvocation + */ + 'header': TransactionHeader; + /** + * + * @type {Array} + * @memberof TransactionInvocation + */ + 'signers': Array; +} /** * * @export @@ -63,11 +151,11 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati /** * * @summary Deploys a smart contract to the Stellar ledger associated with the connector. - * @param {object} [body] + * @param {DeployContractV1Request} [deployContractV1Request] * @param {*} [options] Override http request option. * @throws {RequiredError} */ - deployContractV1: async (body?: object, options: AxiosRequestConfig = {}): Promise => { + deployContractV1: async (deployContractV1Request?: DeployContractV1Request, options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/api/v1/plugins/@hyperledger/cacti-plugin-ledger-connector-stellar/deploy-contract`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); @@ -87,7 +175,7 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(body, localVarRequestOptions, configuration) + localVarRequestOptions.data = serializeDataIfNeeded(deployContractV1Request, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), @@ -149,40 +237,6 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @summary Executes a transaction on a stellar ledger - * @param {object} [body] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - runTransactionV1: async (body?: object, options: AxiosRequestConfig = {}): Promise => { - const localVarPath = `/api/v1/plugins/@hyperledger/cacti-plugin-ledger-connector-stellar/run-transaction`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(body, localVarRequestOptions, configuration) - return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, @@ -201,12 +255,12 @@ export const DefaultApiFp = function(configuration?: Configuration) { /** * * @summary Deploys a smart contract to the Stellar ledger associated with the connector. - * @param {object} [body] + * @param {DeployContractV1Request} [deployContractV1Request] * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async deployContractV1(body?: object, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.deployContractV1(body, options); + async deployContractV1(deployContractV1Request?: DeployContractV1Request, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.deployContractV1(deployContractV1Request, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** @@ -229,17 +283,6 @@ export const DefaultApiFp = function(configuration?: Configuration) { const localVarAxiosArgs = await localVarAxiosParamCreator.getPrometheusMetricsV1(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, - /** - * - * @summary Executes a transaction on a stellar ledger - * @param {object} [body] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async runTransactionV1(body?: object, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.runTransactionV1(body, options); - return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); - }, } }; @@ -253,12 +296,12 @@ export const DefaultApiFactory = function (configuration?: Configuration, basePa /** * * @summary Deploys a smart contract to the Stellar ledger associated with the connector. - * @param {object} [body] + * @param {DeployContractV1Request} [deployContractV1Request] * @param {*} [options] Override http request option. * @throws {RequiredError} */ - deployContractV1(body?: object, options?: any): AxiosPromise { - return localVarFp.deployContractV1(body, options).then((request) => request(axios, basePath)); + deployContractV1(deployContractV1Request?: DeployContractV1Request, options?: any): AxiosPromise { + return localVarFp.deployContractV1(deployContractV1Request, options).then((request) => request(axios, basePath)); }, /** * @@ -278,16 +321,6 @@ export const DefaultApiFactory = function (configuration?: Configuration, basePa getPrometheusMetricsV1(options?: any): AxiosPromise { return localVarFp.getPrometheusMetricsV1(options).then((request) => request(axios, basePath)); }, - /** - * - * @summary Executes a transaction on a stellar ledger - * @param {object} [body] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - runTransactionV1(body?: object, options?: any): AxiosPromise { - return localVarFp.runTransactionV1(body, options).then((request) => request(axios, basePath)); - }, }; }; @@ -301,13 +334,13 @@ export class DefaultApi extends BaseAPI { /** * * @summary Deploys a smart contract to the Stellar ledger associated with the connector. - * @param {object} [body] + * @param {DeployContractV1Request} [deployContractV1Request] * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DefaultApi */ - public deployContractV1(body?: object, options?: AxiosRequestConfig) { - return DefaultApiFp(this.configuration).deployContractV1(body, options).then((request) => request(this.axios, this.basePath)); + public deployContractV1(deployContractV1Request?: DeployContractV1Request, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).deployContractV1(deployContractV1Request, options).then((request) => request(this.axios, this.basePath)); } /** @@ -331,18 +364,6 @@ export class DefaultApi extends BaseAPI { public getPrometheusMetricsV1(options?: AxiosRequestConfig) { return DefaultApiFp(this.configuration).getPrometheusMetricsV1(options).then((request) => request(this.axios, this.basePath)); } - - /** - * - * @summary Executes a transaction on a stellar ledger - * @param {object} [body] - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof DefaultApi - */ - public runTransactionV1(body?: object, options?: AxiosRequestConfig) { - return DefaultApiFp(this.configuration).runTransactionV1(body, options).then((request) => request(this.axios, this.basePath)); - } } diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/base.ts b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/base.ts index a5466bb109..7a503b7d23 100644 --- a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/base.ts +++ b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/base.ts @@ -2,7 +2,7 @@ /* eslint-disable */ /** * Hyperledger Cacti Plugin - Connector Stellar - * Can perform basic tasks on a Stellar ledger + * Can perform basic smart contract tasks on a Stellar ledger * * The version of the OpenAPI document: v2.0.0-alpha.2 * diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/common.ts b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/common.ts index eef217fde3..c2fdc6b1c0 100644 --- a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/common.ts +++ b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/common.ts @@ -2,7 +2,7 @@ /* eslint-disable */ /** * Hyperledger Cacti Plugin - Connector Stellar - * Can perform basic tasks on a Stellar ledger + * Can perform basic smart contract tasks on a Stellar ledger * * The version of the OpenAPI document: v2.0.0-alpha.2 * diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/configuration.ts b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/configuration.ts index c5aafd4cd7..e8f0d92072 100644 --- a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/configuration.ts +++ b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/configuration.ts @@ -2,7 +2,7 @@ /* eslint-disable */ /** * Hyperledger Cacti Plugin - Connector Stellar - * Can perform basic tasks on a Stellar ledger + * Can perform basic smart contract tasks on a Stellar ledger * * The version of the OpenAPI document: v2.0.0-alpha.2 * diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/index.ts b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/index.ts index fb171dce81..9d7e65a400 100644 --- a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/index.ts +++ b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/index.ts @@ -2,7 +2,7 @@ /* eslint-disable */ /** * Hyperledger Cacti Plugin - Connector Stellar - * Can perform basic tasks on a Stellar ledger + * Can perform basic smart contract tasks on a Stellar ledger * * The version of the OpenAPI document: v2.0.0-alpha.2 * diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/plugin-ledger-connector-stellar.ts b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/plugin-ledger-connector-stellar.ts index cfb5142bc6..e23bed8e67 100644 --- a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/plugin-ledger-connector-stellar.ts +++ b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/plugin-ledger-connector-stellar.ts @@ -14,8 +14,6 @@ import { ICactusPluginOptions, } from "@hyperledger/cactus-core-api"; -import { PluginRegistry } from "@hyperledger/cactus-core"; - import { Checks, Logger, @@ -23,7 +21,11 @@ import { LogLevelDesc, } from "@hyperledger/cactus-common"; -import { WatchBlocksV1 } from "./generated/openapi/typescript-axios"; +import { + DeployContractV1Request, + DeployContractV1Response, + WatchBlocksV1, +} from "./generated/openapi/typescript-axios"; import { PrometheusExporter } from "./prometheus-exporter/prometheus-exporter"; import { WatchBlocksV1Endpoint } from "./web-services/watch-blocks-v1-endpoint"; @@ -31,14 +33,22 @@ import { GetOpenApiSpecV1Endpoint, IGetOpenApiSpecV1EndpointOptions, } from "./web-services/get-open-api-spec-v1-endpoint"; +import { NetworkConfig } from "stellar-plus/lib/stellar-plus/network"; +import { PluginRegistry } from "@hyperledger/cactus-core"; +import { deployContract } from "./core/contract-engine"; +import { convertApiTransactionInvocationToStellarPlus } from "./utils"; +import { + GetPrometheusExporterMetricsEndpointV1, + IGetPrometheusExporterMetricsEndpointV1Options, +} from "./web-services/get-prometheus-exporter-metrics-endpoint-v1"; +import { DeployContractEndpoint } from "./web-services/deploy-contract-endpoint"; export const E_KEYCHAIN_NOT_FOUND = "cacti.connector.stellar.keychain_not_found"; export interface IPluginLedgerConnectorStellarOptions extends ICactusPluginOptions { - rpcApiHttpHost: string; - rpcApiWsHost: string; + networkConfig: NetworkConfig; pluginRegistry: PluginRegistry; prometheusExporter?: PrometheusExporter; logLevel?: LogLevelDesc; @@ -46,16 +56,21 @@ export interface IPluginLedgerConnectorStellarOptions export class PluginLedgerConnectorStellar implements - IPluginLedgerConnector, + IPluginLedgerConnector< + DeployContractV1Request, + DeployContractV1Response, + unknown, + unknown + >, ICactusPlugin, IPluginWebService { + private networkConfig: NetworkConfig; private readonly instanceId: string; - public prometheusExporter: PrometheusExporter; private readonly log: Logger; - private readonly pluginRegistry: PluginRegistry; - private endpoints: IWebServiceEndpoint[] | undefined; + private readonly pluginRegistry: PluginRegistry; + public prometheusExporter: PrometheusExporter; public static readonly CLASS_NAME = "PluginLedgerConnectorStellar"; @@ -66,11 +81,14 @@ export class PluginLedgerConnectorStellar constructor(public readonly options: IPluginLedgerConnectorStellarOptions) { const fnTag = `${this.className}#constructor()`; Checks.truthy(options, `${fnTag} arg options`); - Checks.truthy(options.rpcApiHttpHost, `${fnTag} options.rpcApiHttpHost`); - Checks.truthy(options.rpcApiWsHost, `${fnTag} options.rpcApiWsHost`); + Checks.truthy(options.networkConfig, `${fnTag} options.networkConfig`); + Checks.truthy(options.pluginRegistry, `${fnTag} options.pluginRegistry`); + Checks.truthy(options.instanceId, `${fnTag} options.instanceId`); + this.networkConfig = options.networkConfig; + const level = this.options.logLevel || "INFO"; const label = this.className; this.log = LoggerProvider.getOrCreate({ level, label }); @@ -88,12 +106,33 @@ export class PluginLedgerConnectorStellar this.prometheusExporter.startMetricsCollection(); } - public async deployContract(): Promise { - throw new InternalServerError("Method not implemented."); + public async deployContract( + req: DeployContractV1Request, + ): Promise { + // const { logLevel } = this.options; + + const wasm = req.wasmBuffer + ? { wasmBuffer: Buffer.from(req.wasmBuffer, "base64") } + : { wasmHash: req.wasmHash as string }; + + const response = await deployContract({ + ...wasm, + txInvocation: convertApiTransactionInvocationToStellarPlus( + req.transactionInvocation, + this.networkConfig, + ), + networkConfig: this.networkConfig, + fnLogPrefix: this.className, + }); + + this.prometheusExporter.addCurrentTransaction(); + return response; } - public async transact(): Promise { + + public transact(): Promise { throw new InternalServerError("Method not implemented."); } + public async getConsensusAlgorithmFamily(): Promise { throw new InternalServerError("Method not implemented."); } @@ -152,6 +191,21 @@ export class PluginLedgerConnectorStellar } const endpoints: IWebServiceEndpoint[] = []; + { + const endpoint = new DeployContractEndpoint({ + connector: this, + logLevel: this.options.logLevel, + }); + endpoints.push(endpoint); + } + { + const opts: IGetPrometheusExporterMetricsEndpointV1Options = { + connector: this, + logLevel: this.options.logLevel, + }; + const endpoint = new GetPrometheusExporterMetricsEndpointV1(opts); + endpoints.push(endpoint); + } { const oasPath = diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/public-api.ts b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/public-api.ts index b430869147..7f26048ad4 100755 --- a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/public-api.ts +++ b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/public-api.ts @@ -1,24 +1,22 @@ -// export { -// E_KEYCHAIN_NOT_FOUND, -// IPluginLedgerConnectorStellarOptions, -// PluginLedgerConnectorStellar, -// } from "./plugin-ledger-connector-stellar"; -// export { PluginFactoryLedgerConnector } from "./plugin-factory-ledger-connector"; +export { + E_KEYCHAIN_NOT_FOUND, + IPluginLedgerConnectorStellarOptions, + PluginLedgerConnectorStellar, +} from "./plugin-ledger-connector-stellar"; +export { PluginFactoryLedgerConnector } from "./plugin-factory-ledger-connector"; -// import { IPluginFactoryOptions } from "@hyperledger/cactus-core-api"; -// import { PluginFactoryLedgerConnector } from "./plugin-factory-ledger-connector"; +import { IPluginFactoryOptions } from "@hyperledger/cactus-core-api"; +import { PluginFactoryLedgerConnector } from "./plugin-factory-ledger-connector"; -// export { -// StellarApiClient, -// StellarApiClientOptions, -// } from "./api-client/stellar-api-client"; +export { + StellarApiClient, + StellarApiClientOptions, +} from "./api-client/stellar-api-client"; -// export * from "./generated/openapi/typescript-axios/api"; +export * from "./generated/openapi/typescript-axios/api"; -// export async function createPluginFactory( -// pluginFactoryOptions: IPluginFactoryOptions, -// ): Promise { -// return new PluginFactoryLedgerConnector(pluginFactoryOptions); -// } - -export {}; +export async function createPluginFactory( + pluginFactoryOptions: IPluginFactoryOptions, +): Promise { + return new PluginFactoryLedgerConnector(pluginFactoryOptions); +} diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/utils/index.ts b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/utils/index.ts new file mode 100644 index 0000000000..9141fe8b0d --- /dev/null +++ b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/utils/index.ts @@ -0,0 +1,37 @@ +import { TransactionInvocation } from "stellar-plus/lib/stellar-plus/types"; +import { TransactionInvocation as RawTransactionInvocation } from "../generated/openapi/typescript-axios"; +import { DefaultAccountHandler } from "stellar-plus/lib/stellar-plus/account"; +import { NetworkConfig } from "stellar-plus/lib/stellar-plus/network"; +import { readFile } from "fs/promises"; + +export const convertApiTransactionInvocationToStellarPlus = ( + rawTransactionInvocation: RawTransactionInvocation, + networkConfig: NetworkConfig, +): TransactionInvocation => { + const header = { + ...rawTransactionInvocation.header, + fee: rawTransactionInvocation.header.fee.toString(), + }; + + const signers = rawTransactionInvocation.signers.map((signer) => { + return new DefaultAccountHandler({ + networkConfig, + secretKey: signer, + }); + }); + + return { + header, + signers, + }; +}; + +export const loadWasmFile = async (wasmFilePath: string): Promise => { + try { + const buffer = await readFile(wasmFilePath); + return buffer; + } catch (error) { + console.error(`Error reading the WASM file: ${error}`); + throw error; + } +}; diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/web-services/deploy-contract-endpoint.ts b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/web-services/deploy-contract-endpoint.ts new file mode 100644 index 0000000000..997bfa0ada --- /dev/null +++ b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/web-services/deploy-contract-endpoint.ts @@ -0,0 +1,103 @@ +import type { Express, Request, Response } from "express"; + +import { + IWebServiceEndpoint, + IExpressRequestHandler, + IEndpointAuthzOptions, +} from "@hyperledger/cactus-core-api"; + +import { + Logger, + Checks, + LogLevelDesc, + LoggerProvider, + IAsyncProvider, +} from "@hyperledger/cactus-common"; + +import { + handleRestEndpointException, + registerWebServiceEndpoint, +} from "@hyperledger/cactus-core"; + +import { PluginLedgerConnectorStellar } from "../plugin-ledger-connector-stellar"; +import { DeployContractV1Request } from "../generated/openapi/typescript-axios"; +import OAS from "../../json/openapi.json"; + +export interface IDeployContractOptions { + logLevel?: LogLevelDesc; + connector: PluginLedgerConnectorStellar; +} + +export class DeployContractEndpoint implements IWebServiceEndpoint { + public static readonly CLASS_NAME = "DeployContractEndpoint"; + + private readonly log: Logger; + + public get className(): string { + return DeployContractEndpoint.CLASS_NAME; + } + + constructor(public readonly options: IDeployContractOptions) { + 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 get oasPath(): (typeof OAS.paths)["/api/v1/plugins/@hyperledger/cacti-plugin-ledger-connector-stellar/deploy-contract"] { + return OAS.paths[ + "/api/v1/plugins/@hyperledger/cacti-plugin-ledger-connector-stellar/deploy-contract" + ]; + } + + public getPath(): string { + return this.oasPath.post["x-hyperledger-cacti"].http.path; + } + + public getVerbLowerCase(): string { + return this.oasPath.post["x-hyperledger-cacti"].http.verbLowerCase; + } + + public getOperationId(): string { + return this.oasPath.post.operationId; + } + + getAuthorizationOptionsProvider(): IAsyncProvider { + // TODO: make this an injectable dependency in the constructor + return { + get: async () => ({ + isProtected: true, + requiredRoles: [], + }), + }; + } + + public async registerExpress( + expressApp: Express, + ): Promise { + await registerWebServiceEndpoint(expressApp, this); + return this; + } + + public getExpressRequestHandler(): IExpressRequestHandler { + return this.handleRequest.bind(this); + } + + public async handleRequest(req: Request, res: Response): Promise { + const { log } = this; + const fnTag = `${this.className}#handleRequest()`; + const reqTag = `${this.getVerbLowerCase()} - ${this.getPath()}`; + this.log.debug(reqTag); + const reqBody: DeployContractV1Request = req.body; + try { + const resBody = await this.options.connector.deployContract(reqBody); + res.json(resBody); + } catch (ex) { + const errorMsg = `${reqTag} ${fnTag} Failed to deploy contract:`; + await handleRestEndpointException({ errorMsg, log, error: ex, res }); + } + } +} diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/web-services/get-prometheus-exporter-metrics-endpoint-v1.ts b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/web-services/get-prometheus-exporter-metrics-endpoint-v1.ts new file mode 100644 index 0000000000..2e23e0bb51 --- /dev/null +++ b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/web-services/get-prometheus-exporter-metrics-endpoint-v1.ts @@ -0,0 +1,102 @@ +import { Express, Request, Response } from "express"; + +import { registerWebServiceEndpoint } from "@hyperledger/cactus-core"; + +import OAS from "../../json/openapi.json"; + +import { + IWebServiceEndpoint, + IExpressRequestHandler, + IEndpointAuthzOptions, +} from "@hyperledger/cactus-core-api"; + +import { + LogLevelDesc, + Logger, + LoggerProvider, + Checks, + IAsyncProvider, +} from "@hyperledger/cactus-common"; + +import { PluginLedgerConnectorStellar } from "../plugin-ledger-connector-stellar"; + +export interface IGetPrometheusExporterMetricsEndpointV1Options { + connector: PluginLedgerConnectorStellar; + logLevel?: LogLevelDesc; +} + +export class GetPrometheusExporterMetricsEndpointV1 + implements IWebServiceEndpoint +{ + private readonly log: Logger; + + constructor( + public readonly options: IGetPrometheusExporterMetricsEndpointV1Options, + ) { + const fnTag = "GetPrometheusExporterMetricsEndpointV1#constructor()"; + + Checks.truthy(options, `${fnTag} options`); + Checks.truthy(options.connector, `${fnTag} options.connector`); + + const label = "get-prometheus-exporter-metrics-endpoint"; + const level = options.logLevel || "INFO"; + this.log = LoggerProvider.getOrCreate({ label, level }); + } + + getAuthorizationOptionsProvider(): IAsyncProvider { + // TODO: make this an injectable dependency in the constructor + return { + get: async () => ({ + isProtected: true, + requiredRoles: [], + }), + }; + } + + public getExpressRequestHandler(): IExpressRequestHandler { + return this.handleRequest.bind(this); + } + + public get oasPath(): (typeof OAS.paths)["/api/v1/plugins/@hyperledger/cacti-plugin-ledger-connector-stellar/get-prometheus-exporter-metrics"] { + return OAS.paths[ + "/api/v1/plugins/@hyperledger/cacti-plugin-ledger-connector-stellar/get-prometheus-exporter-metrics" + ]; + } + + public getPath(): string { + return this.oasPath.get["x-hyperledger-cacti"].http.path; + } + + public getVerbLowerCase(): string { + return this.oasPath.get["x-hyperledger-cacti"].http.verbLowerCase; + } + + public getOperationId(): string { + return this.oasPath.get.operationId; + } + + public async registerExpress( + expressApp: Express, + ): Promise { + await registerWebServiceEndpoint(expressApp, this); + return this; + } + + async handleRequest(req: Request, res: Response): Promise { + const fnTag = "GetPrometheusExporterMetrics#handleRequest()"; + const verbUpper = this.getVerbLowerCase().toUpperCase(); + this.log.debug(`${verbUpper} ${this.getPath()}`); + + try { + const resBody = + await this.options.connector.getPrometheusExporterMetrics(); + res.status(200); + res.send(resBody); + } catch (ex) { + this.log.error(`${fnTag} failed to serve request`, ex); + res.status(500); + res.statusMessage = ex.message; + res.json({ error: ex.stack }); + } + } +} diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/token-contract/soroban_token_contract.wasm b/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/token-contract/soroban_token_contract.wasm new file mode 100644 index 0000000000000000000000000000000000000000..82f8e2ce0f7dd4781ead9066a8b52a4a62e341bc GIT binary patch literal 7456 zcmc&(eT-aH6~FhsH?y;SJKa}U&C(jYGsU;U^Q zGt2I@1+(o8bhlK6QjscxLPZ2MmiQHjG0{~+3{jHCgpl}$_=oZbi6&~|#~+HnbMC$G z&CV_$F~n*5-o5AEd+z!8opbKHD@u!1DTI(M`OXRdIDYc@gyN@XI+7>?|7BB5_|4-@ zykwJRHRL}M<6^>V9>+6-*+fxIbOgpTv;znNtf+~J{KRUZZtj(Has;x> z$z4L?IWCke$wA@CJD74r-U|A(90g?>GwC@D%A}mZvnuyuepQyh;SRZ5_~0Fcug6# zTB{;xis;PUZSSt{$t~GxrCMLOQ;AK}wduxmX>R&c74crbI$djsf$qghW4ttlj<~$L zG&fg2TB?;R;>xbl{QN@wNJZ@Ink>yR?W$a3p;TMErLrIf^R795%R;>>hWyFH3pKI3 zn_h;rCpT3oPghHGi(=TXm8umnnq9oJI$56+dttC^HjR%@l^Uh-N^Q!M!T9)XmD2q9 zWNESDsZGxfT+t`Ah@sRkFO6Eies_4L)oO|0Js^gkqo$Txosad&*k9J3ZG;$TiLJrE zr6Rv?=WZ0v7UgB!RAcEsEe4d>oDouaz6he#yH;Bw>JA?u4h047A_`}Acw)rMQD=vj z8}Woz(J7vY36TmvPm{w3&5|BJh@R)cF2jWvC8J&tUEtBoMx!c-UVQyg8DP8Lw2zZ4 zGLqq0vp0>0Ge$|Rf@h>Bn+hn8ph_$X9;xk(K@W9Z9*?N|y1a^I!B=A%I?Y@uL@^V+ zEUwE7ojK3sOtgO{+VqjUz?4k%ljcmc6&*AmL-Ue-`Hc4CYnKXsj|$M*M=eO{$2X!C zUM{pJGCbb7SglL zDVy8Tg6H65<@DE*oTfB}qMZ>_S_2>^4GBK?fr9ch8 z!t+(EPd!!i_Z73-3Xo7|G#UUY>`SNsAV_7Q{b3bi>8h|^=ndeNA*c@E_zr;D0WL`~ zNzw^F%hnD~m|SB`CZCF4leZUD^c)2fF^hr^0Tz@@V6YF|V>Xv@4y_yokBCoDY9rwg zk&;LKXDU9u9hq8BCS@IVJHq}WUOG>z#S<+JcWg+-GL?! zm*4^EQ@v@cIfzyNGTH``0sb27!-uI$>c9X+Sm;nOi$N|j#V+j~)OZ}&W>C}aC`v0N z9+n)VImdMN{4wnhsQ&O#Vu`eyM0{`H-1rFLQn?y9*p?n+U=3m5f*wI)3WQcR-u+mn z2~BY*gTh(O2BkuLa!@J&0p!W(McpXE1K4ygZL>>{udluZD&iAm7ujMhAt5eRPkCcYX zp-PICmt^XX6uD{)Q-!T!CLE}uA^8G5On#oD#A|E&rZ5ra4VYYQM8X!~m)L4H1obCD z!wG?04x3YYfzUCgwC1tPG9|>*4?XtE&;R+;!RQ}cDm)IR$TJ78wBn`sj}_-}XNX_C zP7rh`#u%4tu?-!m;K2O`;zciflEe?cK+mEdo}l;E@SF6~6rW!B1h@I%F+gR6V65WP zfBF7PJ)|XV6Ml>40MW`I+GR=JE)18MDFz*gtwP&GOhc`6f3S@+e-q(po*X=BD$sTs z7ZKt~8caeY99u&&EVpogwMSX1@ezdZj*}sxepD#xsxgndRKX>My`Y#A<`fJaA#p)) z9j8&a!Ja-b*@>%Rf4A0D|8&(2Qu2NlQ3vt@Y1x`nc9_#ZB|iPi(?5Lm-rv7CiuCzy zQV$?9CW=+?piJu=P`lyr4{CX=B|_C`>9LmRnv*cfvWH_39tc9pixhzNz^V}5Yfg{u zT%{$nq){w0TBfTSX(+)JRwy#_xlE2&Fug(<@OA0Z!oV>iEJ>W=;xA8=MPhq zqL(>h?Wa%ym5%Do0A8HjQLZ`e+arPtC9yuoo~DlnG+Z(m#Zt%tI1g$ns)LDZT+sOG zVa`U)WaYkl4FOpiSl(TC$*p8Hsv7deV2P)BBeFPt*J2p z%;a>k)-CbrKfQYP$ItxsukaGma_Sziri7Iq>p*~8YTdk`fr#p4nIF3yYPwR$} zm13D>#U3Q9mwJ*8^rncoxFL#>4xxwk4_Imn`peM5f!@@!kEXs$PZ7wD&;F|$+&MD> zm|y_+pJNv20a?yH=|ScH0a!@^3(1^II^k=SUI*CQ+mCOEEMZKd#?)AHFmT5UJ|nkN z&1v;nV+(j=e_m3eq-r;_N0DNP?`$M{54)U`c1IoK#Q zDiPh@+Ur_-=Sugj_OkUAyU!o-rhWfx`HCNH+FY*JSTV>}J~D_w_iMi?CK@fIU&h=N zbNga`3ejcvq^_H1E_?;HGCz0oOFc)kL&97{3!yAQHR{}Q{IkYFOhXW_`L*3%GhN!z z?po!jz13!l`M~K<6o-9k(^xy++vA{p-1(7cVG`*d#NS8uRqh1s zt>BY*&~J0X=cXj7IX)Zd_eS`0>xgexDwfEg?_}>z_M=%$@4A5ZM*6rlT{fB7`ALjX zKf@W0T4oQ2ZR%$@@lh)sGd+xPJ~TbI5B2hg>a&&F^_516{_bSP6HL729Cqmzmpm)A zBULa>@@F5jt|!TL&0uC>5#_^dp53)`GUOh52H8L_eTrTb2GdWbA+AID8UtASU literal 0 HcmV?d00001 diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/integration/plugin-ledger-connector-stellar/deploy-contract/index.test.ts b/packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/integration/plugin-ledger-connector-stellar/deploy-contract/index.test.ts new file mode 100644 index 0000000000..a371872212 --- /dev/null +++ b/packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/integration/plugin-ledger-connector-stellar/deploy-contract/index.test.ts @@ -0,0 +1,238 @@ +import { + IListenOptions, + LogLevelDesc, + Servers, +} from "@hyperledger/cactus-common"; +import { StellarTestLedger } from "@hyperledger/cactus-test-tooling"; +import { Network } from "stellar-plus/lib/stellar-plus"; +import { NetworkConfig } from "stellar-plus/lib/stellar-plus/network"; +import { pluginName } from ".."; +import { PluginFactoryLedgerConnector } from "../../../../../main/typescript/plugin-factory-ledger-connector"; +import { Constants, PluginImportType } from "@hyperledger/cactus-core-api"; +import { PluginLedgerConnectorStellar } from "../../../../../main/typescript/plugin-ledger-connector-stellar"; +import { PluginRegistry } from "@hyperledger/cactus-core"; +import { v4 as uuidV4 } from "uuid"; +import { loadWasmFile } from "../../../../../main/typescript/utils"; +import express from "express"; +import bodyParser from "body-parser"; +import http from "http"; +import { Server as SocketIoServer } from "socket.io"; +import { AddressInfo } from "net"; +import { + StellarApiClient, + StellarApiClientOptions, +} from "../../../../../main/typescript/api-client/stellar-api-client"; +import { DefaultAccountHandler } from "stellar-plus/lib/stellar-plus/account"; +import { K_CACTUS_STELLAR_TOTAL_TX_COUNT } from "../../../../../main/typescript/prometheus-exporter/metrics"; + +const testCaseName = pluginName + " / deploys contracts"; +const deployContractFnTag = `PluginLedgerConnectorStellar#deployContract()`; + +describe(testCaseName, () => { + const logLevel: LogLevelDesc = "TRACE"; + const stellarTestLedger = new StellarTestLedger({ logLevel }); + let networkConfig: NetworkConfig; + let wasmBuffer: Buffer; + let connector: PluginLedgerConnectorStellar; + let server: http.Server; + let apiClient: StellarApiClient; + const contractIdPattern = /^C[A-Z0-9]{55}$/; + const wasmHashPattern = /^[a-f0-9]{64}$/; + + beforeAll(async () => { + wasmBuffer = await loadWasmFile( + "./packages/cacti-plugin-ledger-connector-stellar/src/test/rust/token-contract/soroban_token_contract.wasm", + ); + expect(wasmBuffer).toBeDefined(); + + await stellarTestLedger.start(); + networkConfig = Network.CustomNet( + await stellarTestLedger.getNetworkConfiguration(), + ); + + expect(networkConfig.horizonUrl).toBeDefined(); + expect(networkConfig.networkPassphrase).toBeDefined(); + expect(networkConfig.rpcUrl).toBeDefined(); + expect(networkConfig.friendbotUrl).toBeDefined(); + + const factory = new PluginFactoryLedgerConnector({ + pluginImportType: PluginImportType.Local, + }); + + connector = await factory.create({ + networkConfig, + pluginRegistry: new PluginRegistry({}), + instanceId: uuidV4(), + }); + + await connector.onPluginInit(); + + expect(connector).toBeInstanceOf(PluginLedgerConnectorStellar); + + const expressApp = express(); + expressApp.use(bodyParser.json({ limit: "250mb" })); + server = http.createServer(expressApp); + + const wsApi = new SocketIoServer(server, { + path: Constants.SocketIoConnectionPathV1, + }); + + const listenOptions: IListenOptions = { + hostname: "127.0.0.1", + port: 0, + server, + }; + const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; + + const { address, port } = addressInfo; + const apiHost = `http://${address}:${port}`; + console.log( + `Metrics URL: ${apiHost}/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/get-prometheus-exporter-metrics`, + ); + const stellarApiClientOptions = new StellarApiClientOptions({ + basePath: apiHost, + }); + apiClient = new StellarApiClient(stellarApiClientOptions); + await connector.getOrCreateWebServices(); + await connector.registerWebServices(expressApp, wsApi); + }); + + afterAll(async () => { + await stellarTestLedger.stop(); + await stellarTestLedger.destroy(); + await Servers.shutdown(server); + }); + + describe("core features", () => { + it("should deploy a contract to the ledger with just the WASM file", async () => { + const deployerAccount = new DefaultAccountHandler({ networkConfig }); + await deployerAccount.initializeWithFriendbot(); + + const res = await connector.deployContract({ + wasmBuffer: wasmBuffer.toString("base64"), + transactionInvocation: { + header: { + source: deployerAccount.getPublicKey(), + fee: 100, + timeout: 30, + }, + signers: [deployerAccount.getSecretKey()], + }, + }); + + expect(res).toBeDefined(); + expect(res.contractId).toMatch(contractIdPattern); + expect(res.wasmHash).toMatch(wasmHashPattern); + }); + + it("should deploy a contract to the ledger using the WASM hash of a previously deployed contract", async () => { + const deployerAccount = new DefaultAccountHandler({ networkConfig }); + await deployerAccount.initializeWithFriendbot(); + const responseFromDeployedWasm = await connector.deployContract({ + wasmBuffer: wasmBuffer.toString("base64"), + transactionInvocation: { + header: { + source: deployerAccount.getPublicKey(), + fee: 100, + timeout: 30, + }, + signers: [deployerAccount.getSecretKey()], + }, + }); + const { contractId, wasmHash } = responseFromDeployedWasm; + + const responseFromDeployedWasmHash = await connector.deployContract({ + wasmHash, + transactionInvocation: { + header: { + source: deployerAccount.getPublicKey(), + fee: 100, + timeout: 30, + }, + signers: [deployerAccount.getSecretKey()], + }, + }); + + expect(responseFromDeployedWasmHash).toBeDefined(); + expect(responseFromDeployedWasmHash.wasmHash).toMatch(wasmHashPattern); + expect(responseFromDeployedWasmHash.wasmHash).toBe(wasmHash); + expect(responseFromDeployedWasmHash.contractId).toMatch( + contractIdPattern, + ); + expect(responseFromDeployedWasmHash.contractId).not.toBe(contractId); + }); + }); + + describe("Error handling", () => { + it("should throw if the wasm upload fails", async () => { + const deployerAccount = new DefaultAccountHandler({ networkConfig }); + await deployerAccount.initializeWithFriendbot(); + + await expect( + connector.deployContract({ + wasmBuffer: wasmBuffer.toString("base64"), + transactionInvocation: { + header: { + source: "MOCKED_PK", + fee: 100, + timeout: 30, + }, + signers: [deployerAccount.getSecretKey()], + }, + }), + ).rejects.toThrow(`${deployContractFnTag} Failed to upload wasm.`); + }); + + it("should throw if the wasmHash deploy fails", async () => { + const deployerAccount = new DefaultAccountHandler({ networkConfig }); + await deployerAccount.initializeWithFriendbot(); + const responseFromDeployedWasm = await connector.deployContract({ + wasmBuffer: wasmBuffer.toString("base64"), + transactionInvocation: { + header: { + source: deployerAccount.getPublicKey(), + fee: 100, + timeout: 30, + }, + signers: [deployerAccount.getSecretKey()], + }, + }); + const wasmHash = responseFromDeployedWasm.wasmHash; + + await expect( + connector.deployContract({ + wasmHash, + transactionInvocation: { + header: { + source: "MOCKED_PK", + fee: 100, + timeout: 30, + }, + signers: [deployerAccount.getSecretKey()], + }, + }), + ).rejects.toThrow(`${deployContractFnTag} Failed to deploy contract.`); + }); + }); + + describe("Prometheus", () => { + it("should provide transaction metrics", async () => { + const promMetricsOutput = + "# HELP " + + K_CACTUS_STELLAR_TOTAL_TX_COUNT + + " Total transactions executed\n" + + "# TYPE " + + K_CACTUS_STELLAR_TOTAL_TX_COUNT + + " gauge\n" + + K_CACTUS_STELLAR_TOTAL_TX_COUNT + + '{type="' + + K_CACTUS_STELLAR_TOTAL_TX_COUNT + + '"} 4'; + + const res = await apiClient.getPrometheusMetricsV1(); + + expect(res).toBeDefined(); + expect(res.data.includes(promMetricsOutput)).toBe(true); + }); + }); +}); diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/integration/plugin-ledger-connector-stellar/index.ts b/packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/integration/plugin-ledger-connector-stellar/index.ts new file mode 100644 index 0000000000..cbad399324 --- /dev/null +++ b/packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/integration/plugin-ledger-connector-stellar/index.ts @@ -0,0 +1 @@ +export const pluginName = "plugin-ledger-connector-stellar"; diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/integration/plugin-ledger-connector-stellar/validate-test-ledger.test.ts b/packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/integration/plugin-ledger-connector-stellar/validate-test-ledger.test.ts new file mode 100644 index 0000000000..7879b1b154 --- /dev/null +++ b/packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/integration/plugin-ledger-connector-stellar/validate-test-ledger.test.ts @@ -0,0 +1,67 @@ +import "jest-extended"; +import { LogLevelDesc } from "@hyperledger/cactus-common"; +import { Network } from "stellar-plus/lib/stellar-plus"; +import { DefaultAccountHandler } from "stellar-plus/lib/stellar-plus/account"; +import { ClassicAssetHandler } from "stellar-plus/lib/stellar-plus/asset"; +import { TransactionInvocation } from "stellar-plus/lib/stellar-plus/types"; +import { NetworkConfig } from "stellar-plus/lib/stellar-plus/network"; +import { StellarTestLedger } from "@hyperledger/cactus-test-tooling"; + +describe("PluginLedgerConnectorStellar", () => { + const logLevel: LogLevelDesc = "TRACE"; + const stellarTestLedger = new StellarTestLedger({ logLevel }); + let networkConfig: NetworkConfig; + + beforeAll(async () => { + await stellarTestLedger.start(); + networkConfig = Network.CustomNet( + await stellarTestLedger.getNetworkConfiguration(), + ); + }); + + afterAll(async () => { + await stellarTestLedger.stop(); + await stellarTestLedger.destroy(); + }); + + test("can initialize an account with 10k XLM", async () => { + const XLM = new ClassicAssetHandler({ + code: "XLM", + networkConfig, + }); + const stellarAccount = new DefaultAccountHandler({ networkConfig }); + + await stellarAccount.initializeWithFriendbot(); + + expect(await XLM.balance(stellarAccount.getPublicKey())).toBe(10000); + }); + + test("can perform classic transactions", async () => { + const XLM = new ClassicAssetHandler({ + code: "XLM", + networkConfig, + }); + const accountA = new DefaultAccountHandler({ networkConfig }); + const accountB = new DefaultAccountHandler({ networkConfig }); + const txInvocation: TransactionInvocation = { + header: { + source: accountA.getPublicKey(), + fee: "100", + timeout: 30, + }, + signers: [accountA], + }; + await accountA.initializeWithFriendbot(); + await accountB.initializeWithFriendbot(); + + await XLM.transfer({ + from: accountA.getPublicKey(), + to: accountB.getPublicKey(), + amount: 1000, + ...txInvocation, + }); + + expect(await XLM.balance(accountA.getPublicKey())).toBe(8999.99999); //original balance minus 1k minus fee + expect(await XLM.balance(accountB.getPublicKey())).toBe(11000); + }); +}); diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/unit/get-open-api-spec-v1-connector-stellar.test.ts b/packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/unit/get-open-api-spec-v1-connector-stellar.test.ts index 16136b158a..86f917c726 100644 --- a/packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/unit/get-open-api-spec-v1-connector-stellar.test.ts +++ b/packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/unit/get-open-api-spec-v1-connector-stellar.test.ts @@ -1,91 +1,88 @@ // FIXME - uncomment this once we have the public-api.ts exports uncommented. -// import { -// IListenOptions, -// LogLevelDesc, -// LoggerProvider, -// Servers, -// } from "@hyperledger/cactus-common"; -// import { PluginRegistry } from "@hyperledger/cactus-core"; -// import { Constants, PluginImportType } from "@hyperledger/cactus-core-api"; -// import bodyParser from "body-parser"; -// import express from "express"; -// import http from "http"; -// import "jest-extended"; -// import { AddressInfo } from "net"; -// import { Server as SocketIoServer } from "socket.io"; -// import { v4 as uuidv4 } from "uuid"; -// import { -// StellarApiClient, -// StellarApiClientOptions, -// PluginFactoryLedgerConnector, -// PluginLedgerConnectorStellar, -// } from "../../../main/typescript/public-api"; +import { + IListenOptions, + LogLevelDesc, + LoggerProvider, + Servers, +} from "@hyperledger/cactus-common"; +import { PluginRegistry } from "@hyperledger/cactus-core"; +import { Constants, PluginImportType } from "@hyperledger/cactus-core-api"; +import bodyParser from "body-parser"; +import express from "express"; +import http from "http"; +import "jest-extended"; +import { AddressInfo } from "net"; +import { Server as SocketIoServer } from "socket.io"; +import { v4 as uuidv4 } from "uuid"; +import { + StellarApiClient, + StellarApiClientOptions, + PluginFactoryLedgerConnector, + PluginLedgerConnectorStellar, +} from "../../../main/typescript/public-api"; +import { TestNet } from "stellar-plus/lib/stellar-plus/network"; -// describe(__filename, () => { -// const logLevel: LogLevelDesc = "INFO"; +describe(__filename, () => { + const logLevel: LogLevelDesc = "INFO"; -// const log = LoggerProvider.getOrCreate({ -// label: __filename, -// level: logLevel, -// }); + const log = LoggerProvider.getOrCreate({ + label: __filename, + level: logLevel, + }); -// const rpcApiHttpHost = "http://127.0.0.1:8000"; -// const rpcApiWsHost = "ws://127.0.0.1:9000"; + const expressApp = express(); + expressApp.use(bodyParser.json({ limit: "250mb" })); + const server = http.createServer(expressApp); + let apiClient: StellarApiClient; -// const expressApp = express(); -// expressApp.use(bodyParser.json({ limit: "250mb" })); -// const server = http.createServer(expressApp); -// let apiClient: StellarApiClient; + afterAll(async () => { + await Servers.shutdown(server); + }); -// afterAll(async () => { -// await Servers.shutdown(server); -// }); + beforeAll(async () => { + const factory = new PluginFactoryLedgerConnector({ + pluginImportType: PluginImportType.Local, + }); -// beforeAll(async () => { -// const factory = new PluginFactoryLedgerConnector({ -// pluginImportType: PluginImportType.Local, -// }); + const connector: PluginLedgerConnectorStellar = await factory.create({ + networkConfig: TestNet(), + logLevel, + instanceId: uuidv4(), + pluginRegistry: new PluginRegistry({ plugins: [] }), + }); -// const connector: PluginLedgerConnectorStellar = await factory.create({ -// rpcApiHttpHost, -// rpcApiWsHost, -// logLevel, -// instanceId: uuidv4(), -// pluginRegistry: new PluginRegistry({ plugins: [] }), -// }); + const wsApi = new SocketIoServer(server, { + path: Constants.SocketIoConnectionPathV1, + }); -// const wsApi = new SocketIoServer(server, { -// path: Constants.SocketIoConnectionPathV1, -// }); + await connector.registerWebServices(expressApp, wsApi); -// await connector.registerWebServices(expressApp, wsApi); + const listenOptions: IListenOptions = { + hostname: "127.0.0.1", + port: 0, + server, + }; + const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; + const { address, port } = addressInfo; + const apiHost = `http://${address}:${port}`; -// const listenOptions: IListenOptions = { -// hostname: "127.0.0.1", -// port: 0, -// server, -// }; -// const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; -// const { address, port } = addressInfo; -// const apiHost = `http://${address}:${port}`; + const stellarApiClientOptions = new StellarApiClientOptions({ + basePath: apiHost, + }); + apiClient = new StellarApiClient(stellarApiClientOptions); + log.debug("Instantiated StellarApiClient OK"); + }); -// const stellarApiClientOptions = new StellarApiClientOptions({ -// basePath: apiHost, -// }); -// apiClient = new StellarApiClient(stellarApiClientOptions); -// log.debug("Instantiated StellarApiClient OK"); -// }); - -// it("Returns a JSON document containing the Open API specification of the plugin.", async () => { -// const res1Promise = apiClient.getOpenApiSpecV1(); -// await expect(res1Promise).resolves.not.toThrow(); -// const res1 = await res1Promise; -// expect(res1.status).toEqual(200); -// expect(res1.data).toBeTruthy(); -// expect(res1.config).toBeTruthy(); -// expect(res1.config.url).toBeString(); -// log.debug("Fetched URL OK=%s", res1.config.url); -// expect(res1.data).toBeObject(); -// }); -// }); + it("Returns a JSON document containing the Open API specification of the plugin.", async () => { + const res1Promise = apiClient.getOpenApiSpecV1(); + await expect(res1Promise).resolves.not.toThrow(); + const res1 = await res1Promise; + expect(res1.status).toEqual(200); + expect(res1.data).toBeTruthy(); + expect(res1.config).toBeTruthy(); + expect(res1.config.url).toBeString(); + log.debug("Fetched URL OK=%s", res1.config.url); + expect(res1.data).toBeObject(); + }); +}); diff --git a/yarn.lock b/yarn.lock index b9f6a0b648..06be8ed609 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7375,6 +7375,7 @@ __metadata: rxjs: "npm:7.8.1" socket.io: "npm:4.5.4" socket.io-client-fixed-types: "npm:4.5.4" + stellar-plus: "npm:0.8.4" typescript-optional: "npm:2.0.1" uuid: "npm:9.0.1" languageName: unknown @@ -13526,6 +13527,53 @@ __metadata: languageName: node linkType: hard +"@stellar/freighter-api@npm:^1.7.1": + version: 1.7.1 + resolution: "@stellar/freighter-api@npm:1.7.1" + checksum: 10/5cec07fd3b5fe7565db3ebd1698182b605f9060da782d6e081a9d8cf8325c43222599d417730fa21474a3acca1f4ac8e68eb93122e1055ec11a7d6458c5223e6 + languageName: node + linkType: hard + +"@stellar/js-xdr@npm:^3.1.1": + version: 3.1.1 + resolution: "@stellar/js-xdr@npm:3.1.1" + checksum: 10/3bc8ee3f1611b55938ef0249b7a90b3d689177d45b4c8c24c5562b8fe32372148f7544a0aab67776f4c7b95aef8cb0f97606b86ec800bb087302ff404cb8ccbc + languageName: node + linkType: hard + +"@stellar/stellar-base@npm:^11.0.1": + version: 11.0.1 + resolution: "@stellar/stellar-base@npm:11.0.1" + dependencies: + "@stellar/js-xdr": "npm:^3.1.1" + base32.js: "npm:^0.1.0" + bignumber.js: "npm:^9.1.2" + buffer: "npm:^6.0.3" + sha.js: "npm:^2.3.6" + sodium-native: "npm:^4.0.10" + tweetnacl: "npm:^1.0.3" + dependenciesMeta: + sodium-native: + optional: true + checksum: 10/03ad775791793548563f4dfecc62843d84f0fcf41ccc7f3bb30090dd0506061fb38ca505fbb4ebd8b97d48a06c026733fa7b1146dd04216f3363843f8d09f0f3 + languageName: node + linkType: hard + +"@stellar/stellar-sdk@npm:^11.2.2": + version: 11.3.0 + resolution: "@stellar/stellar-sdk@npm:11.3.0" + dependencies: + "@stellar/stellar-base": "npm:^11.0.1" + axios: "npm:^1.6.8" + bignumber.js: "npm:^9.1.2" + eventsource: "npm:^2.0.2" + randombytes: "npm:^2.1.0" + toml: "npm:^3.0.0" + urijs: "npm:^1.19.1" + checksum: 10/a4f35793aa6f43fb4384fddb9c4870751952228d2196f3bc9fbe6f7cdb796d960af1bce02aac957bb52e594c010a85326c909c0f8390bf738d98ccc23fc015db + languageName: node + linkType: hard + "@stencil/core@npm:^2.18.0": version: 2.22.3 resolution: "@stencil/core@npm:2.22.3" @@ -18754,6 +18802,13 @@ __metadata: languageName: node linkType: hard +"base32.js@npm:^0.1.0": + version: 0.1.0 + resolution: "base32.js@npm:0.1.0" + checksum: 10/7d7401a8f5c4ec45336ff72c97ee554b9bc22c2153b301acf657e6f9e25928a6205b2cfc55731072ba5686515e4903e2d3e462bea49db5f1515bbd01da1276ad + languageName: node + linkType: hard + "base64-js@npm:*, base64-js@npm:^1.0.2, base64-js@npm:^1.2.0, base64-js@npm:^1.3.0, base64-js@npm:^1.3.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" @@ -18907,6 +18962,13 @@ __metadata: languageName: node linkType: hard +"bignumber.js@npm:^9.1.2": + version: 9.1.2 + resolution: "bignumber.js@npm:9.1.2" + checksum: 10/d89b8800a987225d2c00dcbf8a69dc08e92aa0880157c851c287b307d31ceb2fc2acb0c62c3e3a3d42b6c5fcae9b004035f13eb4386e56d529d7edac18d5c9d8 + languageName: node + linkType: hard + "bin-links@npm:^4.0.1": version: 4.0.1 resolution: "bin-links@npm:4.0.1" @@ -26286,6 +26348,13 @@ __metadata: languageName: node linkType: hard +"eventsource@npm:^2.0.2": + version: 2.0.2 + resolution: "eventsource@npm:2.0.2" + checksum: 10/e1c4c3664cebf9efdd55c90818ef847099f298bf521768d479cf22d8a681e666b3042de85327711ba6a8414ac6a04c70d2aeb4f405bba8239a8c36e06a019374 + languageName: node + linkType: hard + "evp_bytestokey@npm:^1.0.0, evp_bytestokey@npm:^1.0.3": version: 1.0.3 resolution: "evp_bytestokey@npm:1.0.3" @@ -37735,6 +37804,17 @@ __metadata: languageName: node linkType: hard +"node-gyp-build@npm:^4.8.0": + version: 4.8.0 + resolution: "node-gyp-build@npm:4.8.0" + bin: + node-gyp-build: bin.js + node-gyp-build-optional: optional.js + node-gyp-build-test: build-test.js + checksum: 10/80f410ab412df38e84171d3634a5716b6c6f14ecfa4eb971424d289381fb76f8bcbe1b666419ceb2c81060e558fd7c6d70cc0f60832bcca6a1559098925d9657 + languageName: node + linkType: hard + "node-gyp-build@npm:~4.1.0": version: 4.1.1 resolution: "node-gyp-build@npm:4.1.1" @@ -45087,7 +45167,7 @@ __metadata: languageName: node linkType: hard -"sha.js@npm:^2.4.0, sha.js@npm:^2.4.11, sha.js@npm:^2.4.8": +"sha.js@npm:^2.3.6, sha.js@npm:^2.4.0, sha.js@npm:^2.4.11, sha.js@npm:^2.4.8": version: 2.4.11 resolution: "sha.js@npm:2.4.11" dependencies: @@ -45633,6 +45713,16 @@ __metadata: languageName: node linkType: hard +"sodium-native@npm:^4.0.10": + version: 4.1.1 + resolution: "sodium-native@npm:4.1.1" + dependencies: + node-gyp: "npm:latest" + node-gyp-build: "npm:^4.8.0" + checksum: 10/cb30564f07c55b2a7f8016b4df16fd2b0ede10981c6d0b4dfe20ef884f702d55884d2128f22da1ffe9a378aec43a4f0c5539aadf23c4fd3fabc5d05ead1301d2 + languageName: node + linkType: hard + "solc@npm:0.7.3": version: 0.7.3 resolution: "solc@npm:0.7.3" @@ -46340,6 +46430,18 @@ __metadata: languageName: node linkType: hard +"stellar-plus@npm:0.8.4": + version: 0.8.4 + resolution: "stellar-plus@npm:0.8.4" + dependencies: + "@stellar/freighter-api": "npm:^1.7.1" + "@stellar/stellar-sdk": "npm:^11.2.2" + axios: "npm:^1.6.2" + uuid: "npm:^9.0.1" + checksum: 10/b5acd2a69c4616011990cca3000815beb12d746bfa358d3e12ab20f38a4e55b8f5f28edbd883ba903e8d8ed63f07c3800c12b2282dcd156e2342f8e3365cfe7b + languageName: node + linkType: hard + "stop-iteration-iterator@npm:^1.0.0": version: 1.0.0 resolution: "stop-iteration-iterator@npm:1.0.0" @@ -48036,6 +48138,13 @@ __metadata: languageName: node linkType: hard +"toml@npm:^3.0.0": + version: 3.0.0 + resolution: "toml@npm:3.0.0" + checksum: 10/cfef0966868d552bd02e741f30945a611f70841b7cddb07ea2b17441fe32543985bc0a7c0dcf7971af26fcaf8a17712a485d911f46bfe28644536e9a71a2bd09 + languageName: node + linkType: hard + "topo@npm:3.x.x": version: 3.0.3 resolution: "topo@npm:3.0.3" @@ -49445,6 +49554,13 @@ __metadata: languageName: node linkType: hard +"urijs@npm:^1.19.1": + version: 1.19.11 + resolution: "urijs@npm:1.19.11" + checksum: 10/2aa5547b53c37ebee03a8ad70feae1638a37cc4c7e543abbffb14fc86b17f84f303d08e45c501441410c025bab22aa84673c97604b7b2619967f1dd49f69931f + languageName: node + linkType: hard + "urix@npm:^0.1.0": version: 0.1.0 resolution: "urix@npm:0.1.0"