From 3172fc68d93ff90a69c0651d60dac6da25122da0 Mon Sep 17 00:00:00 2001 From: Peter Somogyvari Date: Wed, 4 Sep 2024 19:32:32 -0700 Subject: [PATCH] test(connector-fabric): combine 3 tests into connector-fabric-baseline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. This is making the test case harder to read but shaves off easily 10 to 15 minutes from one of our slowest CI jobs which can take up to an hour to run when the GitHub runners are feeling lazy. 2. That above is my only justification for it. The test cases I'm consolidating are relatively stable at this point (took us years to get here but now they are passing with a high ratio and the false negatives have pretty much disappeared). 3. We are downloading and launching the fabirc AIO ledger 10+ times which is very resource intensive and this could help make a dent in it. Running this test right now looks like this on my machine: PASS packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/ integration/fabric-v2-2-x/connector-fabric-baseline.test.ts (277.062 s, 638 MB heap size) PluginLedgerConnectorFabric ✓ getBlockV1() -Get first block by it's number - decoded. (1216 ms) ✓ getBlockV1() - Get first block by it's number - encoded. (1084 ms) ✓ getBlockV1() - Get a block by transactionId it contains (4534 ms) ✓ getBlockV1() - Get a block by transactionId it contains - cacti transactions (4535 ms) ✓ getBlockV1() - Get a block by transactionId it contains - cacti full block (4559 ms) ✓ getBlockV1() - Get block by it's hash. (6727 ms) ✓ getBlockV1() - Reading block with invalid number returns an error. (1 ms) ✓ GetChainInfoV1() - Get test ledger chain info. (2134 ms) ✓ deployContractV1() - deploys Fabric 2.x contract from go source (38351 ms) ✓ deployContractV1() - deploys contract and performs transactions (40840 ms) Test Suites: 1 passed, 1 total Tests: 10 passed, 10 total Snapshots: 0 total Time: 277.117 s Signed-off-by: Peter Somogyvari --- .eslintignore | 1 + .github/workflows/ci.yaml | 36 +- .../src/test/typescript/common/get-block.ts | 93 ++ .../common/send-transaction-on-fabric.ts | 54 ++ .../connector-fabric-baseline.test.ts | 847 ++++++++++++++++++ ...cc-from-golang-source-private-data.test.ts | 363 -------- .../fabric-v2-2-x/deploy-lock-asset.test.ts | 328 ------- .../query-system-chain-methods.test.ts | 539 ----------- 8 files changed, 996 insertions(+), 1265 deletions(-) create mode 100644 packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/common/get-block.ts create mode 100644 packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/common/send-transaction-on-fabric.ts create mode 100644 packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/connector-fabric-baseline.test.ts delete mode 100644 packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-cc-from-golang-source-private-data.test.ts delete mode 100644 packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-lock-asset.test.ts delete mode 100644 packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/query-system-chain-methods.test.ts diff --git a/.eslintignore b/.eslintignore index 9729f1ad32..2e15772a42 100644 --- a/.eslintignore +++ b/.eslintignore @@ -4,6 +4,7 @@ examples/cactus-example-carbon-accounting-frontend/www/ examples/cactus-example-supply-chain-frontend/www/ weaver/core/identity-management/iin-agent/out/ +weaver/common/protos-js/build/ **/src/main/typescript/generated/proto/** **/src/main/typescript/generated/wasm-pack/** diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 41c5b75b08..ba8a70e63e 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1458,14 +1458,13 @@ jobs: continue-on-error: false env: CACTI_NPM_PACKAGE_NAME: "@hyperledger/cactus-plugin-ledger-connector-fabric" - HFC_LOGGING: '{"debug":"console","info":"console","warn": "console","error":"console"}' + HFC_LOGGING: '' FULL_BUILD_DISABLED: true FREE_UP_GITHUB_RUNNER_DISK_SPACE_DISABLED: false JEST_TEST_PATTERN: packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/(unit|integration|benchmark)/.*/*.test.ts JEST_TEST_RUNNER_DISABLED: false JEST_TEST_COVERAGE_PATH: ./code-coverage-ts/plc-fabric-0 JEST_TEST_CODE_COVERAGE_ENABLED: true - TAPE_TEST_PATTERN: "" TAPE_TEST_RUNNER_DISABLED: true runs-on: ubuntu-22.04 steps: @@ -1557,39 +1556,6 @@ jobs: - run: npm run configure - run: yarn ts-node ./packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-cc-from-typescript-source.test.ts - plc-fabric-3: - needs: - - build-dev - - compute_changed_packages - if: needs.compute_changed_packages.outputs.plugin-ledger-connector-fabric-changed == 'true' - continue-on-error: false - env: - CACTI_NPM_PACKAGE_NAME: "@hyperledger/cactus-plugin-ledger-connector-fabric" - HFC_LOGGING: '{"debug":"console","info":"console","warn": "console","error":"console"}' - FULL_BUILD_DISABLED: true - JEST_TEST_PATTERN: packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/(unit|integration|benchmark)/.*/*.test.ts - JEST_TEST_RUNNER_DISABLED: true - TAPE_TEST_PATTERN: "" - TAPE_TEST_RUNNER_DISABLED: true - runs-on: ubuntu-22.04 - steps: - - name: Use Node.js ${{ env.NODEJS_VERSION }} - uses: actions/setup-node@v4.0.3 - with: - node-version: ${{ env.NODEJS_VERSION }} - - uses: actions/checkout@v4.1.7 - - - id: yarn-cache - name: Restore Yarn Cache - uses: actions/cache@v4.0.2 - with: - key: ${{ runner.os }}-yarn-${{ hashFiles('./yarn.lock') }} - path: ./.yarn/ - restore-keys: | - ${{ runner.os }}-yarn-${{ hashFiles('./yarn.lock') }} - - run: npm run configure - - run: yarn jest ./packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-lock-asset.test.ts - plc-fabric-4: continue-on-error: false needs: diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/common/get-block.ts b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/common/get-block.ts new file mode 100644 index 0000000000..12afa0cf6f --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/common/get-block.ts @@ -0,0 +1,93 @@ +import { Logger } from "@hyperledger/cactus-common"; +import { + GatewayOptions, + GetBlockRequestV1Query, + GetBlockResponseTypeV1, + DefaultApi as FabricApi, +} from "../../../main/typescript/generated/openapi/typescript-axios/api"; + +/** + * Run get block endpoint using a query, do basic response checks. + * Can be reused throughout the tests. + * + * @param query how to find requested block + * @param responseType response type requested + * + * @returns block object / block buffer + */ +export async function getBlock(opts: { + readonly query: GetBlockRequestV1Query; + readonly responseType?: GetBlockResponseTypeV1; + readonly gatewayOptions: GatewayOptions; + readonly log: Logger; + readonly apiClient: FabricApi; + readonly ledgerChannelName: string; +}): Promise { + const { + responseType = GetBlockResponseTypeV1.Full, + ledgerChannelName, + gatewayOptions, + query, + log, + apiClient, + } = opts; + + const getBlockReq = { + channelName: ledgerChannelName, + gatewayOptions, + query, + responseType, + }; + + const getBlockResponse = await apiClient.getBlockV1(getBlockReq); + log.debug( + "getBlockResponse = ", + getBlockResponse.status, + getBlockResponse.data, + ); + + expect(getBlockResponse).toBeTruthy(); + expect(getBlockResponse.status).toEqual(200); + expect(getBlockResponse.data).toBeTruthy(); + + switch (responseType) { + case GetBlockResponseTypeV1.Full: + if (!("decodedBlock" in getBlockResponse.data)) { + throw new Error( + `Wrong response received - expected decoded, got: ${getBlockResponse.data}`, + ); + } + expect(getBlockResponse.data.decodedBlock).toBeTruthy(); + return getBlockResponse.data.decodedBlock; + case GetBlockResponseTypeV1.Encoded: + if (!("encodedBlock" in getBlockResponse.data)) { + throw new Error( + `Wrong response received - expected encoded, got: ${getBlockResponse.data}`, + ); + } + expect(getBlockResponse.data.encodedBlock).toBeTruthy(); + return getBlockResponse.data.encodedBlock; + case GetBlockResponseTypeV1.CactiTransactions: + if (!("cactiTransactionsEvents" in getBlockResponse.data)) { + throw new Error( + `Wrong response received - expected CactiTransactions, got: ${getBlockResponse.data}`, + ); + } + expect(getBlockResponse.data.cactiTransactionsEvents).toBeTruthy(); + return getBlockResponse.data.cactiTransactionsEvents; + case GetBlockResponseTypeV1.CactiFullBlock: + if (!("cactiFullEvents" in getBlockResponse.data)) { + throw new Error( + `Wrong response received - expected CactiFullBlock, got: ${getBlockResponse.data}`, + ); + } + expect(getBlockResponse.data.cactiFullEvents).toBeTruthy(); + return getBlockResponse.data.cactiFullEvents; + default: + // Will not compile if any type was not handled by above switch. + const unknownType: never = responseType; + const validTypes = Object.keys(GetBlockResponseTypeV1).join(";"); + const errorMessage = `Unknown get block response type '${unknownType}'. Accepted types for GetBlockResponseTypeV1 are: [${validTypes}]`; + throw new Error(errorMessage); + } +} diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/common/send-transaction-on-fabric.ts b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/common/send-transaction-on-fabric.ts new file mode 100644 index 0000000000..d05d7dc1a6 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/common/send-transaction-on-fabric.ts @@ -0,0 +1,54 @@ +import { Logger } from "@hyperledger/cactus-common"; + +import { FabricSigningCredential } from "../../../main/typescript/generated/openapi/typescript-axios/api"; +import { FabricContractInvocationType } from "../../../main/typescript/generated/openapi/typescript-axios/api"; +import { GatewayOptions } from "../../../main/typescript/generated/openapi/typescript-axios/api"; +import { DefaultApi as FabricApi } from "../../../main/typescript/generated/openapi/typescript-axios/api"; + +/** + * Create new asset on the ledger to trigger new transaction creation. + * + * @param assetName unique asset name to create + * @returns committed transaction id. + */ +export async function sendTransactionOnFabric(opts: { + readonly gatewayOptions: GatewayOptions; + readonly log: Logger; + readonly apiClient: FabricApi; + readonly assetName: string; + readonly ledgerChannelName: string; + readonly ledgerContractName: string; +}) { + const fn = "sendTransactionOnFabric()"; + + if (!opts) { + throw new TypeError(`${fn} arg opts cannot be falsy.`); + } + const { log, apiClient, gatewayOptions, assetName } = opts; + const { ledgerContractName, ledgerChannelName } = opts; + + if (!opts.gatewayOptions) { + throw new TypeError(`${fn} arg opts.gatewayOptions cannot be falsy.`); + } + if (!gatewayOptions.wallet) { + throw new TypeError(`${fn} arg opts.gatewayOptions.wallet cannot be falsy`); + } + + const createAssetResponse = await apiClient.runTransactionV1({ + signingCredential: gatewayOptions.wallet + .keychain as FabricSigningCredential, + channelName: ledgerChannelName, + invocationType: FabricContractInvocationType.Send, + contractName: ledgerContractName, + methodName: "CreateAsset", + params: [assetName, "green", "111", "someOwner", "299"], + }); + expect(createAssetResponse).toBeTruthy(); + expect(createAssetResponse.status).toEqual(200); + expect(createAssetResponse.data).toBeTruthy(); + const txId = createAssetResponse.data.transactionId; + expect(txId).toBeTruthy(); + + log.debug("Crated new transaction, txId:", txId); + return txId; +} diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/connector-fabric-baseline.test.ts b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/connector-fabric-baseline.test.ts new file mode 100644 index 0000000000..e7e2649e2f --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/connector-fabric-baseline.test.ts @@ -0,0 +1,847 @@ +import { AddressInfo } from "node:net"; +import http from "node:http"; +import path from "node:path"; + +import "jest-extended"; +import fs from "fs-extra"; +import { v4 as uuidv4 } from "uuid"; +import { DiscoveryOptions } from "fabric-network"; +import { StatusCodes } from "http-status-codes"; +import express from "express"; +import bodyParser from "body-parser"; + +// BlockDecoder is not exported in ts definition so we need to use legacy import. +// TODO(petermetz): Migrate over to the newer versions of the Fabric NodeJS SDK +// which will (hopefully) not have this problem with the exports. +// eslint-disable-next-line @typescript-eslint/no-var-requires +const { BlockDecoder } = require("fabric-common"); + +import { + DEFAULT_FABRIC_2_AIO_IMAGE_NAME, + FABRIC_25_LTS_AIO_FABRIC_VERSION, + FABRIC_25_LTS_AIO_IMAGE_VERSION, + FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_1, + FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_2, + FabricTestLedgerV1, + pruneDockerAllIfGithubAction, +} from "@hyperledger/cactus-test-tooling"; + +import { + Checks, + IListenOptions, + Logger, + LoggerProvider, + LogLevelDesc, + Servers, +} from "@hyperledger/cactus-common"; +import { PluginRegistry } from "@hyperledger/cactus-core"; +import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory"; +import { Configuration } from "@hyperledger/cactus-core-api"; + +import { + ChainCodeProgrammingLanguage, + DefaultEventHandlerStrategy, + FabricContractInvocationType, + FileBase64, + PluginLedgerConnectorFabric, +} from "../../../../main/typescript/public-api"; + +import { DefaultApi as FabricApi } from "../../../../main/typescript/public-api"; + +import { IPluginLedgerConnectorFabricOptions } from "../../../../main/typescript/plugin-ledger-connector-fabric"; +import { + CactiBlockFullEventV1, + GatewayOptions, + GetBlockResponseTypeV1, +} from "../../../../main/typescript/generated/openapi/typescript-axios/api"; + +import { sendTransactionOnFabric } from "../../common/send-transaction-on-fabric"; +import { getBlock } from "../../common/get-block"; + +describe("PluginLedgerConnectorFabric", () => { + const logLevel: LogLevelDesc = "INFO"; + const log: Logger = LoggerProvider.getOrCreate({ + label: "fabric-lock-asset", + level: logLevel, + }); + + let ledger: FabricTestLedgerV1; + let apiClient: FabricApi; + let keychainId: string; + let keychainEntryKey: string; + let server: http.Server; + let gatewayOptions: GatewayOptions; + + beforeAll(async () => { + const pruning = pruneDockerAllIfGithubAction({ logLevel }); + await expect(pruning).resolves.not.toThrow(); + }); + + beforeAll(async () => { + ledger = new FabricTestLedgerV1({ + emitContainerLogs: true, + publishAllPorts: true, + imageName: DEFAULT_FABRIC_2_AIO_IMAGE_NAME, + imageVersion: FABRIC_25_LTS_AIO_IMAGE_VERSION, + envVars: new Map([["FABRIC_VERSION", FABRIC_25_LTS_AIO_FABRIC_VERSION]]), + logLevel, + }); + + await ledger.start({ omitPull: false }); + + const connectionProfile = await ledger.getConnectionProfileOrg1(); + expect(connectionProfile).toBeTruthy(); + + const enrollAdminOut = await ledger.enrollAdmin(); + const adminWallet = enrollAdminOut[1]; + const [userIdentity] = await ledger.enrollUser(adminWallet); + const sshConfig = await ledger.getSshConfig(); + + const keychainInstanceId = uuidv4(); + keychainId = uuidv4(); + keychainEntryKey = "user2"; + const keychainEntryValue = JSON.stringify(userIdentity); + + const keychainPlugin = new PluginKeychainMemory({ + instanceId: keychainInstanceId, + keychainId, + logLevel, + backend: new Map([ + [keychainEntryKey, keychainEntryValue], + ["some-other-entry-key", "some-other-entry-value"], + ]), + }); + + gatewayOptions = { + identity: keychainEntryKey, + wallet: { + keychain: { + keychainId, + keychainRef: keychainEntryKey, + }, + }, + }; + + const pluginRegistry = new PluginRegistry({ plugins: [keychainPlugin] }); + + const discoveryOptions: DiscoveryOptions = { + enabled: true, + asLocalhost: true, + }; + + const pluginOptions: IPluginLedgerConnectorFabricOptions = { + instanceId: uuidv4(), + dockerBinary: "/usr/local/bin/docker", + peerBinary: "/fabric-samples/bin/peer", + goBinary: "/usr/local/go/bin/go", + pluginRegistry, + cliContainerEnv: FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_1, + sshConfig, + logLevel, + connectionProfile, + discoveryOptions, + eventHandlerOptions: { + strategy: DefaultEventHandlerStrategy.NetworkScopeAllfortx, + commitTimeout: 300, + }, + }; + + const plugin = new PluginLedgerConnectorFabric(pluginOptions); + + const expressApp = express(); + expressApp.use(bodyParser.json({ limit: "250mb" })); + server = http.createServer(expressApp); + const listenOptions: IListenOptions = { + hostname: "127.0.0.1", + port: 0, + server, + }; + const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; + const { port } = addressInfo; + apiClient = new FabricApi( + new Configuration({ basePath: `http://127.0.0.1:${port}` }), + ); + + await plugin.getOrCreateWebServices(); + await plugin.registerWebServices(expressApp); + }); + + afterAll(async () => { + await ledger.stop(); + await ledger.destroy(); + await pruneDockerAllIfGithubAction({ logLevel }); + await Servers.shutdown(server); + }); + + it("getBlockV1() -Get first block by it's number - decoded.", async () => { + const ledgerChannelName = "mychannel"; + // Check decoded + const decodedFirstBlock = await getBlock({ + apiClient, + gatewayOptions, + ledgerChannelName, + log, + query: { blockNumber: "0" }, + responseType: GetBlockResponseTypeV1.Full, + }); + log.debug("Received decodedFirstBlock:", decodedFirstBlock); + expect(decodedFirstBlock.header).toBeTruthy(); + expect(decodedFirstBlock.header.number.low).toBe(0); + expect(decodedFirstBlock.header.number.high).toBe(0); + expect(decodedFirstBlock.data).toBeTruthy(); + expect(decodedFirstBlock.metadata).toBeTruthy(); + }); + + it("getBlockV1() - Get first block by it's number - encoded.", async () => { + const ledgerChannelName = "mychannel"; + // Check decoded + const encodedFirstBlock = await getBlock({ + apiClient, + gatewayOptions, + ledgerChannelName, + log, + query: { blockNumber: "0" }, + responseType: GetBlockResponseTypeV1.Encoded, + }); + const decodedFirstBlockBuffer = Buffer.from(encodedFirstBlock, "base64"); + const decodedFirstBlock = BlockDecoder.decode(decodedFirstBlockBuffer); + log.debug("Received decodedFirstBlock:", decodedFirstBlock); + expect(decodedFirstBlock.header).toBeTruthy(); + expect(decodedFirstBlock.header.number.low).toBe(0); + expect(decodedFirstBlock.header.number.high).toBe(0); + expect(decodedFirstBlock.data).toBeTruthy(); + expect(decodedFirstBlock.metadata).toBeTruthy(); + }); + + /** + * GetBlock endpoint using transactionId + */ + it("getBlockV1() - Get a block by transactionId it contains", async () => { + const ledgerChannelName = "mychannel"; + const ledgerContractName = "basic"; + // Run some transaction + const assetName = `getBlockTx_${(Math.random() + 1).toString(36).substring(2)}`; + const txId = await sendTransactionOnFabric({ + apiClient, + assetName, + gatewayOptions, + ledgerChannelName, + ledgerContractName, + log, + }); + + // Get block using transactionId we've just sent + const blockByTx = await getBlock({ + apiClient, + gatewayOptions, + ledgerChannelName, + log, + query: { transactionId: txId }, + responseType: GetBlockResponseTypeV1.Full, + }); + expect(blockByTx).toBeTruthy(); + expect(blockByTx.header).toBeTruthy(); + expect(blockByTx.data).toBeTruthy(); + expect(blockByTx.metadata).toBeTruthy(); + }); + + it("getBlockV1() - Get a block by transactionId it contains - cacti transactions summary", async () => { + const ledgerChannelName = "mychannel"; + const ledgerContractName = "basic"; + // Run some transaction + const assetName = `cactiTx_${(Math.random() + 1).toString(36).substring(2)}`; + const txId = await sendTransactionOnFabric({ + apiClient, + assetName, + gatewayOptions, + ledgerChannelName, + ledgerContractName, + log, + }); + + // Get block using transactionId we've just sent + const cactiTxList = await getBlock({ + apiClient, + gatewayOptions, + ledgerChannelName, + log, + query: { transactionId: txId }, + responseType: GetBlockResponseTypeV1.CactiTransactions, + }); + expect(cactiTxList).toBeTruthy(); + expect(cactiTxList.length).toBeGreaterThanOrEqual(1); + const cactiTx = cactiTxList[0]; + expect(cactiTx).toBeTruthy(); + expect(cactiTx.chaincodeId).toBeTruthy(); + expect(cactiTx.transactionId).toBeTruthy(); + expect(cactiTx.functionName).toBeTruthy(); + expect(cactiTx.functionArgs).toBeTruthy(); + expect(cactiTx.functionArgs.length).toEqual(5); + }); + + it("getBlockV1() - Get a block by transactionId it contains - cacti full block summary", async () => { + const ledgerChannelName = "mychannel"; + const ledgerContractName = "basic"; + + // Run some transaction + const assetName = `cactiTx_${(Math.random() + 1).toString(36).substring(2)}`; + const txId = await sendTransactionOnFabric({ + apiClient, + assetName, + gatewayOptions, + ledgerChannelName, + ledgerContractName, + log, + }); + + // Get block using transactionId we've just sent + const cactiFullBlock = (await getBlock({ + apiClient, + gatewayOptions, + ledgerChannelName, + log, + query: { transactionId: txId }, + responseType: GetBlockResponseTypeV1.CactiFullBlock, + })) as CactiBlockFullEventV1; + + // Check block fields + expect(cactiFullBlock).toBeTruthy(); + expect(cactiFullBlock.blockNumber).toBeDefined(); + expect(cactiFullBlock.blockHash).toBeTruthy(); + expect(cactiFullBlock.previousBlockHash).toBeTruthy(); + expect(cactiFullBlock.transactionCount).toBeGreaterThanOrEqual(1); + + // Check transaction fields + for (const tx of cactiFullBlock.cactiTransactionsEvents) { + expect(tx.hash).toBeTruthy(); + expect(tx.channelId).toBeTruthy(); + expect(tx.timestamp).toBeTruthy(); + expect(tx.transactionType).toBeTruthy(); + expect(tx.protocolVersion).not.toBeUndefined(); + expect(tx.epoch).not.toBeUndefined(); + + // Check transaction actions fields + for (const action of tx.actions) { + expect(action.functionName).toBeTruthy(); + expect(action.functionArgs).toBeTruthy(); + expect(action.functionArgs.length).toEqual(5); + expect(action.chaincodeId).toBeTruthy(); + expect(action.creator.mspid).toBeTruthy(); + expect(action.creator.cert).toBeTruthy(); + + // Check transaction action endorsement fields + for (const endorsement of action.endorsements) { + expect(endorsement.signature).toBeTruthy(); + expect(endorsement.signer.mspid).toBeTruthy(); + expect(endorsement.signer.cert).toBeTruthy(); + } + } + } + }); + + /** + * GetBlock endpoint using block hash + */ + it("getBlockV1() - Get block by it's hash.", async () => { + const ledgerChannelName = "mychannel"; + const ledgerContractName = "basic"; + // Run transaction to ensure more than one block is present + const assetName = `txForNewBlock_${(Math.random() + 1).toString(36).substring(2)}`; + await sendTransactionOnFabric({ + assetName, + apiClient, + gatewayOptions, + ledgerChannelName, + ledgerContractName, + log, + }); + + // Get second block by it's number + const decodedSecondBlock = await getBlock({ + apiClient, + gatewayOptions, + ledgerChannelName, + log, + query: { blockNumber: "1" }, + responseType: GetBlockResponseTypeV1.Full, + }); + expect(decodedSecondBlock.header).toBeTruthy(); + const firstBlockHashJSON = decodedSecondBlock.header.previous_hash; + expect(firstBlockHashJSON).toBeTruthy(); + + // Get using default JSON hash representation + log.info("Get by JSON hash:", firstBlockHashJSON); + + const decodedFirstBlock = await getBlock({ + apiClient, + gatewayOptions, + ledgerChannelName, + log, + query: { + blockHash: { + buffer: firstBlockHashJSON, + }, + }, + responseType: GetBlockResponseTypeV1.Full, + }); + expect(decodedFirstBlock).toBeTruthy(); + expect(decodedFirstBlock.header).toBeTruthy(); + expect(decodedFirstBlock.header.number.low).toBe(0); + expect(decodedFirstBlock.header.number.high).toBe(0); + expect(decodedFirstBlock.data).toBeTruthy(); + expect(decodedFirstBlock.metadata).toBeTruthy(); + + // Get using HEX encoded hash representation + const firstBlockHashHex = Buffer.from(firstBlockHashJSON).toString("hex"); + log.info("Get by HEX hash:", firstBlockHashHex); + + const decodedBlockHex = await getBlock({ + apiClient, + gatewayOptions, + ledgerChannelName, + log, + query: { + blockHash: { + encoding: "hex", + buffer: firstBlockHashHex, + }, + }, + responseType: GetBlockResponseTypeV1.Full, + }); + expect(decodedBlockHex).toBeTruthy(); + expect(decodedBlockHex.header).toBeTruthy(); + expect(decodedBlockHex.header.number.low).toBe(0); + expect(decodedBlockHex.header.number.high).toBe(0); + expect(decodedBlockHex.data).toBeTruthy(); + expect(decodedBlockHex.metadata).toBeTruthy(); + }); + + /** + * Check error handling + */ + it("getBlockV1() - Reading block with invalid number returns an error.", async () => { + const ledgerChannelName = "mychannel"; + const getBlockReq = { + channelName: ledgerChannelName, + gatewayOptions, + query: { + blockNumber: "foo", // non existent block + }, + }; + + // FIXME(petermetz): This should fail with `StatusCodes.BAD_REQUEST` + expect( + apiClient.getBlockV1(getBlockReq).catch((ex: unknown) => { + log.debug("Dumping the exception thrown by getBlockV1()..."); + log.debug(ex); + throw ex; + }), + ).rejects.toMatchObject({ + message: + "Request failed with status code " + StatusCodes.INTERNAL_SERVER_ERROR, + name: "AxiosError", + code: "ERR_BAD_RESPONSE", + response: { + status: StatusCodes.INTERNAL_SERVER_ERROR, + statusText: "Internal Server Error", + }, + }); + }); + + it("GetChainInfoV1() - Get test ledger chain info.", async () => { + const ledgerChannelName = "mychannel"; + const chainInfoResponse = await apiClient.getChainInfoV1({ + channelName: ledgerChannelName, + gatewayOptions, + }); + + const chainInfo = chainInfoResponse.data; + expect(chainInfoResponse.status).toBe(200); + expect(chainInfo).toBeTruthy; + expect(chainInfo.height).toBeGreaterThanOrEqual(1); + expect(chainInfo.currentBlockHash).toBeTruthy; + expect(chainInfo.previousBlockHash).toBeTruthy; + }); + + it("deployContractV1() - deploys Fabric 2.x contract from go source", async () => { + const channelId = "mychannel"; + const channelName = channelId; + const contractName = "asset-transfer-private-data"; + + const contractRelPath = + "../../fixtures/go/asset-transfer-private-data/chaincode-go"; + const contractDir = path.join(__dirname, contractRelPath); + + const smartContractGoPath = path.join( + contractDir, + "./chaincode/", + "./asset_transfer.go", + ); + const smartContractGoBuf = await fs.readFile(smartContractGoPath); + const smartContractGo = { + body: smartContractGoBuf.toString("base64"), + filepath: "./chaincode/", + filename: `asset_transfer.go`, + }; + + const assetTransferGoPath = path.join(contractDir, "./main.go"); + const assetTransferGoBuf = await fs.readFile(assetTransferGoPath); + const assetTransferGo = { + body: assetTransferGoBuf.toString("base64"), + filename: `${contractName}.go`, + }; + + const goModPath = path.join(contractDir, "./go.mod"); + const goModBuf = await fs.readFile(goModPath); + const goMod = { + body: goModBuf.toString("base64"), + filename: "go.mod", + }; + + const goSumPath = path.join(contractDir, "./go.sum"); + const goSumBuf = await fs.readFile(goSumPath); + const goSum = { + body: goSumBuf.toString("base64"), + filename: "go.sum", + }; + + const privateDataCollectionName = "collections_config.json"; + const privateDataCollectionsPath = path.join( + contractDir, + "./" + privateDataCollectionName, + ); + const privateDataCollectionsBuf = await fs.readFile( + privateDataCollectionsPath, + ); + const privateDataCollections = { + body: privateDataCollectionsBuf.toString("base64"), + filename: privateDataCollectionName, + }; + + const res = await apiClient.deployContractV1({ + channelId, + ccVersion: "1.0.0", + sourceFiles: [ + assetTransferGo, + smartContractGo, + goMod, + goSum, + privateDataCollections, + ], + collectionsConfigFile: privateDataCollectionName, + ccName: contractName, + targetOrganizations: [ + FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_1, + FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_2, + ], + caFile: + FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_1.ORDERER_TLS_ROOTCERT_FILE, + ccLabel: contractName, + ccLang: ChainCodeProgrammingLanguage.Golang, + ccSequence: 1, + orderer: "orderer.example.com:7050", + ordererTLSHostnameOverride: "orderer.example.com", + connTimeout: 60, + }); + + const { packageIds, lifecycle, success } = res.data; + expect(res.status).toEqual(200); + expect(success).toBe(true); + + const { + approveForMyOrgList, + installList, + queryInstalledList, + commit, + packaging, + queryCommitted, + } = lifecycle; + + Checks.truthy(packageIds, `packageIds truthy OK`); + Checks.truthy( + Array.isArray(packageIds), + `Array.isArray(packageIds) truthy OK`, + ); + Checks.truthy(approveForMyOrgList, `approveForMyOrgList truthy OK`); + Checks.truthy( + Array.isArray(approveForMyOrgList), + `Array.isArray(approveForMyOrgList) truthy OK`, + ); + Checks.truthy(installList, `installList truthy OK`); + Checks.truthy( + Array.isArray(installList), + `Array.isArray(installList) truthy OK`, + ); + Checks.truthy(queryInstalledList, `queryInstalledList truthy OK`); + Checks.truthy( + Array.isArray(queryInstalledList), + `Array.isArray(queryInstalledList) truthy OK`, + ); + Checks.truthy(commit, `commit truthy OK`); + Checks.truthy(packaging, `packaging truthy OK`); + Checks.truthy(queryCommitted, `queryCommitted truthy OK`); + + const assetId = uuidv4(); + const assetType = "asset"; + + const assetData = { + objectType: assetType, + assetID: assetId, + color: "gray", + size: 3, + appraisedValue: 500, + }; + + //Chaincode-specific method requires attribute asset_properties + const rawTmpData = { + asset_properties: assetData, + }; + + // CreateAsset(id string, color string, size int, owner string, appraisedValue int) + const createRes = await apiClient.runTransactionV1({ + transientData: rawTmpData, + contractName, + channelName, + //objectType, assetID, color, size, appraisedvalue + params: [], + methodName: "CreateAsset", + invocationType: FabricContractInvocationType.Sendprivate, + signingCredential: { + keychainId, + keychainRef: keychainEntryKey, + }, + }); + expect(createRes).toBeTruthy(); + expect(createRes.status).toBeWithin(199, 300); + const getRes = await apiClient.runTransactionV1({ + contractName, + channelName, + params: [assetId], + methodName: "ReadAsset", + invocationType: FabricContractInvocationType.Send, + signingCredential: { + keychainId, + keychainRef: keychainEntryKey, + }, + }); + + expect(getRes).toBeTruthy(); + expect(getRes.data).toBeTruthy(); + expect(getRes.data.functionOutput).toBeTruthy(); + expect(getRes.status).toBeWithin(199, 300); + //TODO FIX: + //Error: failed to read asset details: GET_STATE failed: transaction ID: 0a41ae425e259ee6c1331d4d3c06bd9fc4727f9961abc0c1a2895c450fc8411a: tx creator does not have read access permission on privatedata in chaincodeName:asset-transfer-private-data collectionName: Org2MSPPrivateCollection + //This has probably to do with the state database supported by Fabric test ledger + /* + const collectionToParse = "Org1MSPPrivateCollection"; + const getResPrivate = await apiClient.runTransactionV1({ + contractName, + channelName, + params: [collectionToParse, assetId], + methodName: "ReadAssetPrivateDetails", + invocationType: FabricContractInvocationType.SEND, + signingCredential: { + keychainId, + keychainRef: keychainEntryKey, + }, + }); + + + */ + + const getResQuery = await apiClient.runTransactionV1({ + contractName, + channelName, + params: [assetId, assetId + "1"], + methodName: "GetAssetByRange", + invocationType: FabricContractInvocationType.Call, + signingCredential: { + keychainId, + keychainRef: keychainEntryKey, + }, + }); + + expect(getResQuery).toBeTruthy(); + expect(getResQuery.data).toBeTruthy(); + expect(getResQuery.data.functionOutput).toBeTruthy(); + expect(getResQuery.status).toBeWithin(199, 300); + }); + + it("deployContractV1() - deploys contract and performs transactions", async () => { + const channelId = "mychannel"; + const channelName = channelId; + const contractName = "basic-asset-transfer-2"; + + const contractRelPath = "../../fixtures/go/lock-asset/chaincode-typescript"; + const contractDir = path.join(__dirname, contractRelPath); + + // ├── package.json + // ├── src + // │ ├── assetTransfer.ts + // │ ├── asset.ts + // │ └── index.ts + // ├── tsconfig.json + const sourceFiles: FileBase64[] = []; + { + const filename = "./tsconfig.json"; + const relativePath = "./"; + const filePath = path.join(contractDir, relativePath, filename); + const buffer = await fs.readFile(filePath); + sourceFiles.push({ + body: buffer.toString("base64"), + filepath: relativePath, + filename, + }); + } + { + const filename = "./package.json"; + const relativePath = "./"; + const filePath = path.join(contractDir, relativePath, filename); + const buffer = await fs.readFile(filePath); + sourceFiles.push({ + body: buffer.toString("base64"), + filepath: relativePath, + filename, + }); + } + { + const filename = "./index.ts"; + const relativePath = "./src/"; + const filePath = path.join(contractDir, relativePath, filename); + const buffer = await fs.readFile(filePath); + sourceFiles.push({ + body: buffer.toString("base64"), + filepath: relativePath, + filename, + }); + } + { + const filename = "./asset.ts"; + const relativePath = "./src/"; + const filePath = path.join(contractDir, relativePath, filename); + const buffer = await fs.readFile(filePath); + sourceFiles.push({ + body: buffer.toString("base64"), + filepath: relativePath, + filename, + }); + } + { + const filename = "./assetTransfer.ts"; + const relativePath = "./src/"; + const filePath = path.join(contractDir, relativePath, filename); + const buffer = await fs.readFile(filePath); + sourceFiles.push({ + body: buffer.toString("base64"), + filepath: relativePath, + filename, + }); + } + + const res = await apiClient.deployContractV1({ + channelId, + ccVersion: "1.0.0", + sourceFiles, + ccName: contractName, + targetOrganizations: [ + FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_1, + FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_2, + ], + caFile: + FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_1.ORDERER_TLS_ROOTCERT_FILE, + ccLabel: "basic-asset-transfer-2", + ccLang: ChainCodeProgrammingLanguage.Typescript, + ccSequence: 1, + orderer: "orderer.example.com:7050", + ordererTLSHostnameOverride: "orderer.example.com", + connTimeout: 60, + }); + + expect(res.status).toBe(200); + expect(res.data.success).toBe(true); + + const { + packageIds, + lifecycle: { + approveForMyOrgList, + installList, + queryInstalledList, + commit, + packaging, + queryCommitted, + }, + } = res.data; + + expect(packageIds).toBeTruthy(); + expect(Array.isArray(packageIds)).toBe(true); + expect(approveForMyOrgList).toBeTruthy(); + expect(Array.isArray(approveForMyOrgList)).toBe(true); + expect(installList).toBeTruthy(); + expect(Array.isArray(installList)).toBe(true); + expect(queryInstalledList).toBeTruthy(); + expect(Array.isArray(queryInstalledList)).toBe(true); + expect(commit).toBeTruthy(); + expect(packaging).toBeTruthy(); + expect(queryCommitted).toBeTruthy(); + + const assetId = uuidv4(); + + const createRes = await apiClient.runTransactionV1({ + contractName, + channelName, + params: [assetId, "19"], + methodName: "CreateAsset", + invocationType: FabricContractInvocationType.Send, + signingCredential: { + keychainId, + keychainRef: keychainEntryKey, + }, + }); + expect(createRes).toBeTruthy(); + expect(createRes.status).toBeGreaterThan(199); + expect(createRes.status).toBeLessThan(300); + + const getRes = await apiClient.runTransactionV1({ + contractName, + channelName, + params: [assetId], + methodName: "ReadAsset", + invocationType: FabricContractInvocationType.Call, + signingCredential: { + keychainId, + keychainRef: keychainEntryKey, + }, + }); + expect(getRes).toBeTruthy(); + expect(getRes.data).toBeTruthy(); + expect(getRes.data.functionOutput).toBeTruthy(); + expect(getRes.status).toBeGreaterThan(199); + expect(getRes.status).toBeLessThan(300); + + const asset = JSON.parse(getRes.data.functionOutput); + + expect(asset).toBeTruthy(); + expect(asset.ID).toBeTruthy(); + expect(asset.ID).toBe(assetId); + + const lockRes = await apiClient.runTransactionV1({ + contractName, + channelName, + params: [assetId], + methodName: "LockAsset", + invocationType: FabricContractInvocationType.Send, + signingCredential: { + keychainId, + keychainRef: keychainEntryKey, + }, + }); + expect(lockRes).toBeTruthy(); + expect(lockRes.data).toBeTruthy(); + expect(lockRes.data.functionOutput).toBeTruthy(); + expect(lockRes.status).toBeGreaterThan(199); + expect(lockRes.status).toBeLessThan(300); + expect(lockRes.data.functionOutput).toBe("true"); + + log.warn(lockRes.data.functionOutput); + }); +}); diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-cc-from-golang-source-private-data.test.ts b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-cc-from-golang-source-private-data.test.ts deleted file mode 100644 index 90d420e35f..0000000000 --- a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-cc-from-golang-source-private-data.test.ts +++ /dev/null @@ -1,363 +0,0 @@ -import { AddressInfo } from "net"; -import http from "http"; -import fs from "fs-extra"; -import path from "path"; -import "jest-extended"; -import { v4 as uuidv4 } from "uuid"; - -import express from "express"; -import bodyParser from "body-parser"; - -import { - Containers, - DEFAULT_FABRIC_2_AIO_FABRIC_VERSION, - DEFAULT_FABRIC_2_AIO_IMAGE_NAME, - DEFAULT_FABRIC_2_AIO_IMAGE_VERSION, - FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_1, - FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_2, - FabricTestLedgerV1, - pruneDockerAllIfGithubAction, -} from "@hyperledger/cactus-test-tooling"; - -import { - Checks, - IListenOptions, - LogLevelDesc, - LoggerProvider, - Servers, -} from "@hyperledger/cactus-common"; -import { PluginRegistry } from "@hyperledger/cactus-core"; - -import { - ChainCodeProgrammingLanguage, - Configuration, - DefaultEventHandlerStrategy, - FabricContractInvocationType, - PluginLedgerConnectorFabric, -} from "../../../../main/typescript/public-api"; -import { DefaultApi as FabricApi } from "../../../../main/typescript/public-api"; - -import { IPluginLedgerConnectorFabricOptions } from "../../../../main/typescript/plugin-ledger-connector-fabric"; - -import { DiscoveryOptions } from "fabric-network"; -import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory"; - -const testCase = "deploys Fabric 2.x contract from go source"; -describe(testCase, () => { - const logLevel: LogLevelDesc = "INFO"; - const expressApp = express(); - expressApp.use(bodyParser.json({ limit: "250mb" })); - const server = http.createServer(expressApp); - const ledger = new FabricTestLedgerV1({ - emitContainerLogs: true, - publishAllPorts: true, - imageName: DEFAULT_FABRIC_2_AIO_IMAGE_NAME, - imageVersion: DEFAULT_FABRIC_2_AIO_IMAGE_VERSION, - envVars: new Map([["FABRIC_VERSION", DEFAULT_FABRIC_2_AIO_FABRIC_VERSION]]), - logLevel, - }); - let addressInfo, - port: number, - configuration, - apiUrl: string, - apiClient: FabricApi; - beforeAll(async () => { - const pruning = pruneDockerAllIfGithubAction({ logLevel }); - await expect(pruning).resolves.toBeTruthy(); - }); - - afterAll(async () => await Servers.shutdown(server)); - afterAll(async () => { - await Containers.logDiagnostics({ logLevel }); - }); - - afterAll(async () => { - await ledger.stop(); - await ledger.destroy(); - }); - - afterAll(async () => { - const pruning = pruneDockerAllIfGithubAction({ logLevel }); - await expect(pruning).resolves.toBeTruthy(); - }); - beforeAll(async () => { - await ledger.start({ omitPull: false }); - - const listenOptions: IListenOptions = { - hostname: "127.0.0.1", - port: 0, - server, - }; - addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; - ({ port } = addressInfo); - apiUrl = `http://127.0.0.1:${port}`; - - configuration = new Configuration({ basePath: apiUrl }); - apiClient = new FabricApi(configuration); - }); - - test(testCase, async () => { - const LOG = LoggerProvider.getOrCreate({ - label: "deploy-cc-from-golang-source-private-data", - level: logLevel, - }); - - const channelId = "mychannel"; - const channelName = channelId; - - const connectionProfile = await ledger.getConnectionProfileOrg1(); - - expect(connectionProfile).toBeTruthy(); - - const enrollAdminOut = await ledger.enrollAdmin(); - const adminWallet = enrollAdminOut[1]; - const [userIdentity] = await ledger.enrollUser(adminWallet); - const sshConfig = await ledger.getSshConfig(); - - const keychainInstanceId = uuidv4(); - const keychainId = uuidv4(); - const keychainEntryKey = "user1Org1"; - const keychainEntryValue = JSON.stringify(userIdentity); - - const keychainPlugin = new PluginKeychainMemory({ - instanceId: keychainInstanceId, - keychainId, - logLevel, - backend: new Map([ - [keychainEntryKey, keychainEntryValue], - ["some-other-entry-key", "some-other-entry-value"], - ]), - }); - - const pluginRegistry = new PluginRegistry({ plugins: [keychainPlugin] }); - - const discoveryOptions: DiscoveryOptions = { - enabled: true, - asLocalhost: true, - }; - - const pluginOptions: IPluginLedgerConnectorFabricOptions = { - instanceId: uuidv4(), - dockerBinary: "/usr/local/bin/docker", - peerBinary: "/fabric-samples/bin/peer", - goBinary: "/usr/local/go/bin/go", - pluginRegistry, - cliContainerEnv: FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_1, - sshConfig, - logLevel, - connectionProfile, - discoveryOptions, - eventHandlerOptions: { - strategy: DefaultEventHandlerStrategy.NetworkScopeAllfortx, - commitTimeout: 300, - }, - }; - - LOG.debug("Creating connector - connection profile: %o", connectionProfile); - LOG.debug("Creating connector - discovery options: %o", discoveryOptions); - LOG.debug("Creating connector - SSH config: %o", sshConfig); - - const connector = new PluginLedgerConnectorFabric(pluginOptions); - await connector.getOrCreateWebServices(); - await connector.registerWebServices(expressApp); - const contractName = "asset-transfer-private-data"; - - const contractRelPath = - "../../fixtures/go/asset-transfer-private-data/chaincode-go"; - const contractDir = path.join(__dirname, contractRelPath); - - const smartContractGoPath = path.join( - contractDir, - "./chaincode/", - "./asset_transfer.go", - ); - const smartContractGoBuf = await fs.readFile(smartContractGoPath); - const smartContractGo = { - body: smartContractGoBuf.toString("base64"), - filepath: "./chaincode/", - filename: `asset_transfer.go`, - }; - - const assetTransferGoPath = path.join(contractDir, "./main.go"); - const assetTransferGoBuf = await fs.readFile(assetTransferGoPath); - const assetTransferGo = { - body: assetTransferGoBuf.toString("base64"), - filename: `${contractName}.go`, - }; - - const goModPath = path.join(contractDir, "./go.mod"); - const goModBuf = await fs.readFile(goModPath); - const goMod = { - body: goModBuf.toString("base64"), - filename: "go.mod", - }; - - const goSumPath = path.join(contractDir, "./go.sum"); - const goSumBuf = await fs.readFile(goSumPath); - const goSum = { - body: goSumBuf.toString("base64"), - filename: "go.sum", - }; - - const privateDataCollectionName = "collections_config.json"; - const privateDataCollectionsPath = path.join( - contractDir, - "./" + privateDataCollectionName, - ); - const privateDataCollectionsBuf = await fs.readFile( - privateDataCollectionsPath, - ); - const privateDataCollections = { - body: privateDataCollectionsBuf.toString("base64"), - filename: privateDataCollectionName, - }; - - const res = await apiClient.deployContractV1({ - channelId, - ccVersion: "1.0.0", - sourceFiles: [ - assetTransferGo, - smartContractGo, - goMod, - goSum, - privateDataCollections, - ], - collectionsConfigFile: privateDataCollectionName, - ccName: contractName, - targetOrganizations: [ - FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_1, - FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_2, - ], - caFile: - FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_1.ORDERER_TLS_ROOTCERT_FILE, - ccLabel: "basic-asset-transfer-2", - ccLang: ChainCodeProgrammingLanguage.Golang, - ccSequence: 1, - orderer: "orderer.example.com:7050", - ordererTLSHostnameOverride: "orderer.example.com", - connTimeout: 60, - }); - - const { packageIds, lifecycle, success } = res.data; - expect(res.status).toEqual(200); - expect(success).toBe(true); - - const { - approveForMyOrgList, - installList, - queryInstalledList, - commit, - packaging, - queryCommitted, - } = lifecycle; - - Checks.truthy(packageIds, `packageIds truthy OK`); - Checks.truthy( - Array.isArray(packageIds), - `Array.isArray(packageIds) truthy OK`, - ); - Checks.truthy(approveForMyOrgList, `approveForMyOrgList truthy OK`); - Checks.truthy( - Array.isArray(approveForMyOrgList), - `Array.isArray(approveForMyOrgList) truthy OK`, - ); - Checks.truthy(installList, `installList truthy OK`); - Checks.truthy( - Array.isArray(installList), - `Array.isArray(installList) truthy OK`, - ); - Checks.truthy(queryInstalledList, `queryInstalledList truthy OK`); - Checks.truthy( - Array.isArray(queryInstalledList), - `Array.isArray(queryInstalledList) truthy OK`, - ); - Checks.truthy(commit, `commit truthy OK`); - Checks.truthy(packaging, `packaging truthy OK`); - Checks.truthy(queryCommitted, `queryCommitted truthy OK`); - - const assetId = uuidv4(); - const assetType = "asset"; - - const assetData = { - objectType: assetType, - assetID: assetId, - color: "gray", - size: 3, - appraisedValue: 500, - }; - - //Chaincode-specific method requires attribute asset_properties - const rawTmpData = { - asset_properties: assetData, - }; - - // CreateAsset(id string, color string, size int, owner string, appraisedValue int) - const createRes = await apiClient.runTransactionV1({ - transientData: rawTmpData, - contractName, - channelName, - //objectType, assetID, color, size, appraisedvalue - params: [], - methodName: "CreateAsset", - invocationType: FabricContractInvocationType.Sendprivate, - signingCredential: { - keychainId, - keychainRef: keychainEntryKey, - }, - }); - expect(createRes).toBeTruthy(); - expect(createRes.status).toBeWithin(199, 300); - const getRes = await apiClient.runTransactionV1({ - contractName, - channelName, - params: [assetId], - methodName: "ReadAsset", - invocationType: FabricContractInvocationType.Send, - signingCredential: { - keychainId, - keychainRef: keychainEntryKey, - }, - }); - - expect(getRes).toBeTruthy(); - expect(getRes.data).toBeTruthy(); - expect(getRes.data.functionOutput).toBeTruthy(); - expect(getRes.status).toBeWithin(199, 300); - //TODO FIX: - //Error: failed to read asset details: GET_STATE failed: transaction ID: 0a41ae425e259ee6c1331d4d3c06bd9fc4727f9961abc0c1a2895c450fc8411a: tx creator does not have read access permission on privatedata in chaincodeName:asset-transfer-private-data collectionName: Org2MSPPrivateCollection - //This has probably to do with the state database supported by Fabric test ledger - /* - const collectionToParse = "Org1MSPPrivateCollection"; - const getResPrivate = await apiClient.runTransactionV1({ - contractName, - channelName, - params: [collectionToParse, assetId], - methodName: "ReadAssetPrivateDetails", - invocationType: FabricContractInvocationType.SEND, - signingCredential: { - keychainId, - keychainRef: keychainEntryKey, - }, - }); - - - */ - - const getResQuery = await apiClient.runTransactionV1({ - contractName, - channelName, - params: [assetId, assetId + "1"], - methodName: "GetAssetByRange", - invocationType: FabricContractInvocationType.Call, - signingCredential: { - keychainId, - keychainRef: keychainEntryKey, - }, - }); - - expect(getResQuery).toBeTruthy(); - expect(getResQuery.data).toBeTruthy(); - expect(getResQuery.data.functionOutput).toBeTruthy(); - expect(getResQuery.status).toBeWithin(199, 300); - }); -}); diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-lock-asset.test.ts b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-lock-asset.test.ts deleted file mode 100644 index a927b390a7..0000000000 --- a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-lock-asset.test.ts +++ /dev/null @@ -1,328 +0,0 @@ -import { AddressInfo } from "net"; -import http from "http"; -import fs from "fs-extra"; -import path from "path"; - -import { v4 as uuidv4 } from "uuid"; - -import express from "express"; -import bodyParser from "body-parser"; - -import { - DEFAULT_FABRIC_2_AIO_IMAGE_NAME, - FABRIC_25_LTS_AIO_FABRIC_VERSION, - FABRIC_25_LTS_AIO_IMAGE_VERSION, - FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_1, - FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_2, - FabricTestLedgerV1, - pruneDockerAllIfGithubAction, -} from "@hyperledger/cactus-test-tooling"; - -import { - IListenOptions, - Logger, - LoggerProvider, - LogLevelDesc, - Servers, -} from "@hyperledger/cactus-common"; -import { PluginRegistry } from "@hyperledger/cactus-core"; - -import { - ChainCodeProgrammingLanguage, - DefaultEventHandlerStrategy, - FabricContractInvocationType, - FileBase64, - PluginLedgerConnectorFabric, -} from "../../../../main/typescript/public-api"; - -import { DefaultApi as FabricApi } from "../../../../main/typescript/public-api"; - -import { IPluginLedgerConnectorFabricOptions } from "../../../../main/typescript/plugin-ledger-connector-fabric"; - -import { DiscoveryOptions } from "fabric-network"; -import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory"; -import { Configuration } from "@hyperledger/cactus-core-api"; - -const testCase = "deploys Fabric 2.x contract from typescript source"; -const logLevel: LogLevelDesc = "INFO"; -const log: Logger = LoggerProvider.getOrCreate({ - label: "fabric-lock-asset", - level: logLevel, -}); - -beforeAll(async () => { - const pruning = pruneDockerAllIfGithubAction({ logLevel }); - await expect(pruning).resolves.not.toThrow(); -}); - -describe(testCase, () => { - let ledger: FabricTestLedgerV1; - let apiClient: FabricApi; - let keychainId: string; - let keychainEntryKey: string; - let server: http.Server; - - beforeAll(async () => { - ledger = new FabricTestLedgerV1({ - emitContainerLogs: true, - publishAllPorts: true, - imageName: DEFAULT_FABRIC_2_AIO_IMAGE_NAME, - imageVersion: FABRIC_25_LTS_AIO_IMAGE_VERSION, - envVars: new Map([["FABRIC_VERSION", FABRIC_25_LTS_AIO_FABRIC_VERSION]]), - logLevel, - }); - - await ledger.start({ omitPull: false }); - - const connectionProfile = await ledger.getConnectionProfileOrg1(); - expect(connectionProfile).toBeTruthy(); - - const enrollAdminOut = await ledger.enrollAdmin(); - const adminWallet = enrollAdminOut[1]; - const [userIdentity] = await ledger.enrollUser(adminWallet); - const sshConfig = await ledger.getSshConfig(); - - const keychainInstanceId = uuidv4(); - keychainId = uuidv4(); - keychainEntryKey = "user2"; - const keychainEntryValue = JSON.stringify(userIdentity); - - const keychainPlugin = new PluginKeychainMemory({ - instanceId: keychainInstanceId, - keychainId, - logLevel, - backend: new Map([ - [keychainEntryKey, keychainEntryValue], - ["some-other-entry-key", "some-other-entry-value"], - ]), - }); - - const pluginRegistry = new PluginRegistry({ plugins: [keychainPlugin] }); - - const discoveryOptions: DiscoveryOptions = { - enabled: true, - asLocalhost: true, - }; - - const pluginOptions: IPluginLedgerConnectorFabricOptions = { - instanceId: uuidv4(), - dockerBinary: "/usr/local/bin/docker", - peerBinary: "/fabric-samples/bin/peer", - goBinary: "/usr/local/go/bin/go", - pluginRegistry, - cliContainerEnv: FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_1, - sshConfig, - logLevel, - connectionProfile, - discoveryOptions, - eventHandlerOptions: { - strategy: DefaultEventHandlerStrategy.NetworkScopeAllfortx, - commitTimeout: 300, - }, - }; - - const plugin = new PluginLedgerConnectorFabric(pluginOptions); - - const expressApp = express(); - expressApp.use(bodyParser.json({ limit: "250mb" })); - server = http.createServer(expressApp); - const listenOptions: IListenOptions = { - hostname: "127.0.0.1", - port: 0, - server, - }; - const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; - const { port } = addressInfo; - apiClient = new FabricApi( - new Configuration({ basePath: `http://127.0.0.1:${port}` }), - ); - - await plugin.getOrCreateWebServices(); - await plugin.registerWebServices(expressApp); - }); - - afterAll(async () => { - await ledger.stop(); - await ledger.destroy(); - await pruneDockerAllIfGithubAction({ logLevel }); - await Servers.shutdown(server); - }); - - test("deploys contract and performs transactions", async () => { - const channelId = "mychannel"; - const channelName = channelId; - const contractName = "basic-asset-transfer-2"; - - const contractRelPath = "../../fixtures/go/lock-asset/chaincode-typescript"; - const contractDir = path.join(__dirname, contractRelPath); - - // ├── package.json - // ├── src - // │ ├── assetTransfer.ts - // │ ├── asset.ts - // │ └── index.ts - // ├── tsconfig.json - const sourceFiles: FileBase64[] = []; - { - const filename = "./tsconfig.json"; - const relativePath = "./"; - const filePath = path.join(contractDir, relativePath, filename); - const buffer = await fs.readFile(filePath); - sourceFiles.push({ - body: buffer.toString("base64"), - filepath: relativePath, - filename, - }); - } - { - const filename = "./package.json"; - const relativePath = "./"; - const filePath = path.join(contractDir, relativePath, filename); - const buffer = await fs.readFile(filePath); - sourceFiles.push({ - body: buffer.toString("base64"), - filepath: relativePath, - filename, - }); - } - { - const filename = "./index.ts"; - const relativePath = "./src/"; - const filePath = path.join(contractDir, relativePath, filename); - const buffer = await fs.readFile(filePath); - sourceFiles.push({ - body: buffer.toString("base64"), - filepath: relativePath, - filename, - }); - } - { - const filename = "./asset.ts"; - const relativePath = "./src/"; - const filePath = path.join(contractDir, relativePath, filename); - const buffer = await fs.readFile(filePath); - sourceFiles.push({ - body: buffer.toString("base64"), - filepath: relativePath, - filename, - }); - } - { - const filename = "./assetTransfer.ts"; - const relativePath = "./src/"; - const filePath = path.join(contractDir, relativePath, filename); - const buffer = await fs.readFile(filePath); - sourceFiles.push({ - body: buffer.toString("base64"), - filepath: relativePath, - filename, - }); - } - - const res = await apiClient.deployContractV1({ - channelId, - ccVersion: "1.0.0", - sourceFiles, - ccName: contractName, - targetOrganizations: [ - FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_1, - FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_2, - ], - caFile: - FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_1.ORDERER_TLS_ROOTCERT_FILE, - ccLabel: "basic-asset-transfer-2", - ccLang: ChainCodeProgrammingLanguage.Typescript, - ccSequence: 1, - orderer: "orderer.example.com:7050", - ordererTLSHostnameOverride: "orderer.example.com", - connTimeout: 60, - }); - - expect(res.status).toBe(200); - expect(res.data.success).toBe(true); - - const { - packageIds, - lifecycle: { - approveForMyOrgList, - installList, - queryInstalledList, - commit, - packaging, - queryCommitted, - }, - } = res.data; - - expect(packageIds).toBeTruthy(); - expect(Array.isArray(packageIds)).toBe(true); - expect(approveForMyOrgList).toBeTruthy(); - expect(Array.isArray(approveForMyOrgList)).toBe(true); - expect(installList).toBeTruthy(); - expect(Array.isArray(installList)).toBe(true); - expect(queryInstalledList).toBeTruthy(); - expect(Array.isArray(queryInstalledList)).toBe(true); - expect(commit).toBeTruthy(); - expect(packaging).toBeTruthy(); - expect(queryCommitted).toBeTruthy(); - - const assetId = uuidv4(); - - const createRes = await apiClient.runTransactionV1({ - contractName, - channelName, - params: [assetId, "19"], - methodName: "CreateAsset", - invocationType: FabricContractInvocationType.Send, - signingCredential: { - keychainId, - keychainRef: keychainEntryKey, - }, - }); - expect(createRes).toBeTruthy(); - expect(createRes.status).toBeGreaterThan(199); - expect(createRes.status).toBeLessThan(300); - - const getRes = await apiClient.runTransactionV1({ - contractName, - channelName, - params: [assetId], - methodName: "ReadAsset", - invocationType: FabricContractInvocationType.Call, - signingCredential: { - keychainId, - keychainRef: keychainEntryKey, - }, - }); - expect(getRes).toBeTruthy(); - expect(getRes.data).toBeTruthy(); - expect(getRes.data.functionOutput).toBeTruthy(); - expect(getRes.status).toBeGreaterThan(199); - expect(getRes.status).toBeLessThan(300); - - const asset = JSON.parse(getRes.data.functionOutput); - - expect(asset).toBeTruthy(); - expect(asset.ID).toBeTruthy(); - expect(asset.ID).toBe(assetId); - - const lockRes = await apiClient.runTransactionV1({ - contractName, - channelName, - params: [assetId], - methodName: "LockAsset", - invocationType: FabricContractInvocationType.Send, - signingCredential: { - keychainId, - keychainRef: keychainEntryKey, - }, - }); - expect(lockRes).toBeTruthy(); - expect(lockRes.data).toBeTruthy(); - expect(lockRes.data.functionOutput).toBeTruthy(); - expect(lockRes.status).toBeGreaterThan(199); - expect(lockRes.status).toBeLessThan(300); - expect(lockRes.data.functionOutput).toBe("true"); - - log.warn(lockRes.data.functionOutput); - }); -}); diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/query-system-chain-methods.test.ts b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/query-system-chain-methods.test.ts deleted file mode 100644 index 3906526818..0000000000 --- a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/query-system-chain-methods.test.ts +++ /dev/null @@ -1,539 +0,0 @@ -import "jest-extended"; -import http from "http"; -import { AddressInfo } from "net"; -import { v4 as uuidv4 } from "uuid"; -import bodyParser from "body-parser"; -import express from "express"; -import { DiscoveryOptions } from "fabric-network"; -// BlockDecoder is not exported in ts definition so we need to use legacy import. -const { BlockDecoder } = require("fabric-common"); - -import { - DEFAULT_FABRIC_2_AIO_IMAGE_NAME, - FABRIC_25_LTS_AIO_FABRIC_VERSION, - FABRIC_25_LTS_AIO_IMAGE_VERSION, - FabricTestLedgerV1, - pruneDockerAllIfGithubAction, -} from "@hyperledger/cactus-test-tooling"; -import { - LogLevelDesc, - LoggerProvider, - Logger, - IListenOptions, - Servers, -} from "@hyperledger/cactus-common"; -import { Configuration } from "@hyperledger/cactus-core-api"; -import { PluginRegistry } from "@hyperledger/cactus-core"; -import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory"; - -import { - PluginLedgerConnectorFabric, - DefaultEventHandlerStrategy, - DefaultApi as FabricApi, - GatewayOptions, - FabricContractInvocationType, - FabricSigningCredential, - GetBlockResponseTypeV1, - GetBlockRequestV1Query, - CactiBlockFullEventV1, -} from "../../../../main/typescript/public-api"; - -/** - * Functional test of GetBlockEndpointV1 on connector-fabric (packages/cactus-plugin-ledger-connector-fabric) - * Assumes sample CC was already deployed on the test ledger. - */ - -////////////////////////////////// -// Constants -////////////////////////////////// - -// Ledger settings -const imageName = DEFAULT_FABRIC_2_AIO_IMAGE_NAME; -const imageVersion = FABRIC_25_LTS_AIO_IMAGE_VERSION; -const fabricEnvVersion = FABRIC_25_LTS_AIO_FABRIC_VERSION; -const fabricEnvCAVersion = "1.4.9"; -const ledgerChannelName = "mychannel"; -const ledgerContractName = "basic"; - -// For development on local sawtooth network -// 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; - -// Log settings -const testLogLevel: LogLevelDesc = "info"; // default: info -const sutLogLevel: LogLevelDesc = "info"; // default: info - -// Logger setup -const log: Logger = LoggerProvider.getOrCreate({ - label: "query-system-chain-methods.test", - level: testLogLevel, -}); - -/** - * Main test suite - */ -describe("Query system chain methods and endpoints tests", () => { - let ledger: FabricTestLedgerV1; - let gatewayOptions: GatewayOptions; - let fabricConnectorPlugin: PluginLedgerConnectorFabric; - let connectorServer: http.Server; - let apiClient: FabricApi; - - ////////////////////////////////// - // Environment Setup - ////////////////////////////////// - - beforeAll(async () => { - log.info("Prune Docker..."); - await pruneDockerAllIfGithubAction({ logLevel: testLogLevel }); - - // Start Ledger - log.info("Start FabricTestLedgerV1..."); - log.debug("Version:", fabricEnvVersion, "CA Version:", fabricEnvCAVersion); - ledger = new FabricTestLedgerV1({ - emitContainerLogs: false, - publishAllPorts: true, - logLevel: testLogLevel, - imageName, - imageVersion, - envVars: new Map([ - ["FABRIC_VERSION", fabricEnvVersion], - ["CA_VERSION", fabricEnvCAVersion], - ]), - useRunningLedger, - }); - log.debug("Fabric image:", ledger.getContainerImageName()); - await ledger.start({ omitPull: false }); - - // Get connection profile - log.info("Get fabric connection profile for Org1..."); - const connectionProfile = await ledger.getConnectionProfileOrg1(); - expect(connectionProfile).toBeTruthy(); - - // Enroll admin and user - const userOrg = "org1"; - const enrollAdminOut = await ledger.enrollAdminV2({ - organization: userOrg, - }); - log.debug("Enrolled admin OK."); - const adminWallet = enrollAdminOut[1]; - const userId = `testUser_${(Math.random() + 1).toString(36).substring(2)}`; - const [userIdentity] = await ledger.enrollUserV2({ - enrollmentID: userId, - organization: userOrg, - wallet: adminWallet, - }); - log.debug(`Enrolled user '${userId}' OK.`); - - // Create Keychain Plugin - const keychainId = uuidv4(); - const keychainEntryKey = userId; - const keychainPlugin = new PluginKeychainMemory({ - instanceId: uuidv4(), - keychainId, - logLevel: sutLogLevel, - backend: new Map([[keychainEntryKey, JSON.stringify(userIdentity)]]), - }); - - gatewayOptions = { - identity: keychainEntryKey, - wallet: { - keychain: { - keychainId, - keychainRef: keychainEntryKey, - }, - }, - }; - - // Create Connector Plugin - const discoveryOptions: DiscoveryOptions = { - enabled: true, - asLocalhost: true, - }; - fabricConnectorPlugin = new PluginLedgerConnectorFabric({ - instanceId: uuidv4(), - pluginRegistry: new PluginRegistry({ plugins: [keychainPlugin] }), - sshConfig: await ledger.getSshConfig(), - cliContainerEnv: {}, - peerBinary: "/fabric-samples/bin/peer", - logLevel: sutLogLevel, - connectionProfile, - discoveryOptions, - eventHandlerOptions: { - strategy: DefaultEventHandlerStrategy.NetworkScopeAnyfortx, - commitTimeout: 300, - }, - }); - - // Run http server - const expressApp = express(); - expressApp.use(bodyParser.json({ limit: "250mb" })); - connectorServer = http.createServer(expressApp); - const listenOptions: IListenOptions = { - hostname: "127.0.0.1", - port: 0, - server: connectorServer, - }; - const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; - const apiHost = `http://${addressInfo.address}:${addressInfo.port}`; - - // Register services - await fabricConnectorPlugin.getOrCreateWebServices(); - await fabricConnectorPlugin.registerWebServices(expressApp); - - // Create ApiClient - const apiConfig = new Configuration({ basePath: apiHost }); - apiClient = new FabricApi(apiConfig); - }); - - afterAll(async () => { - log.info("FINISHING THE TESTS"); - - if (fabricConnectorPlugin) { - log.info("Close ApiClient connections..."); - fabricConnectorPlugin.shutdown(); - } - - if (connectorServer) { - log.info("Stop the HTTP server connector..."); - await new Promise((resolve) => - connectorServer.close(() => resolve()), - ); - } - - if (ledger && !leaveLedgerRunning) { - log.info("Stop the fabric ledger..."); - await ledger.stop(); - await ledger.destroy(); - } - - log.info("Prune Docker..."); - await pruneDockerAllIfGithubAction({ logLevel: testLogLevel }); - }); - - ////////////////////////////////// - // Helpers - ////////////////////////////////// - - /** - * Run get block endpoint using a query, do basic response checks. - * Can be reused throughout the tests. - * - * @param query how to find requested block - * @param responseType response type requested - * - * @returns block object / block buffer - */ - async function getBlock( - query: GetBlockRequestV1Query, - responseType: GetBlockResponseTypeV1 = GetBlockResponseTypeV1.Full, - ): Promise { - const getBlockReq = { - channelName: ledgerChannelName, - gatewayOptions, - query, - responseType, - }; - - const getBlockResponse = await apiClient.getBlockV1(getBlockReq); - log.debug( - "getBlockResponse = ", - getBlockResponse.status, - getBlockResponse.data, - ); - - expect(getBlockResponse).toBeTruthy(); - expect(getBlockResponse.status).toEqual(200); - expect(getBlockResponse.data).toBeTruthy(); - - switch (responseType) { - case GetBlockResponseTypeV1.Full: - if (!("decodedBlock" in getBlockResponse.data)) { - throw new Error( - `Wrong response received - expected decoded, got: ${getBlockResponse.data}`, - ); - } - expect(getBlockResponse.data.decodedBlock).toBeTruthy(); - return getBlockResponse.data.decodedBlock; - case GetBlockResponseTypeV1.Encoded: - if (!("encodedBlock" in getBlockResponse.data)) { - throw new Error( - `Wrong response received - expected encoded, got: ${getBlockResponse.data}`, - ); - } - expect(getBlockResponse.data.encodedBlock).toBeTruthy(); - return getBlockResponse.data.encodedBlock; - case GetBlockResponseTypeV1.CactiTransactions: - if (!("cactiTransactionsEvents" in getBlockResponse.data)) { - throw new Error( - `Wrong response received - expected CactiTransactions, got: ${getBlockResponse.data}`, - ); - } - expect(getBlockResponse.data.cactiTransactionsEvents).toBeTruthy(); - return getBlockResponse.data.cactiTransactionsEvents; - case GetBlockResponseTypeV1.CactiFullBlock: - if (!("cactiFullEvents" in getBlockResponse.data)) { - throw new Error( - `Wrong response received - expected CactiFullBlock, got: ${getBlockResponse.data}`, - ); - } - expect(getBlockResponse.data.cactiFullEvents).toBeTruthy(); - return getBlockResponse.data.cactiFullEvents; - default: - // Will not compile if any type was not handled by above switch. - const unknownType: never = responseType; - const validTypes = Object.keys(GetBlockResponseTypeV1).join(";"); - const errorMessage = `Unknown get block response type '${unknownType}'. Accepted types for GetBlockResponseTypeV1 are: [${validTypes}]`; - throw new Error(errorMessage); - } - } - - /** - * Create new asset on the ledger to trigger new transaction creation. - * - * @param assetName unique asset name to create - * @returns committed transaction id. - */ - async function sendTransactionOnFabric(assetName: string) { - const createAssetResponse = await apiClient.runTransactionV1({ - signingCredential: gatewayOptions.wallet - .keychain as FabricSigningCredential, - channelName: ledgerChannelName, - invocationType: FabricContractInvocationType.Send, - contractName: ledgerContractName, - methodName: "CreateAsset", - params: [assetName, "green", "111", "someOwner", "299"], - }); - expect(createAssetResponse).toBeTruthy(); - expect(createAssetResponse.status).toEqual(200); - expect(createAssetResponse.data).toBeTruthy(); - const txId = createAssetResponse.data.transactionId; - expect(txId).toBeTruthy(); - - log.debug("Crated new transaction, txId:", txId); - return txId; - } - - ////////////////////////////////// - // GetBlockV1 Endpoint Tests - ////////////////////////////////// - - describe("GetBlockV1 endpoint tests", () => { - /** - * GetBlock endpoint using block number - */ - test("Get first block by it's number - decoded.", async () => { - // Check decoded - const decodedFirstBlock = await getBlock( - { blockNumber: "0" }, - GetBlockResponseTypeV1.Full, - ); - log.debug("Received decodedFirstBlock:", decodedFirstBlock); - expect(decodedFirstBlock.header).toBeTruthy(); - expect(decodedFirstBlock.header.number.low).toBe(0); - expect(decodedFirstBlock.header.number.high).toBe(0); - expect(decodedFirstBlock.data).toBeTruthy(); - expect(decodedFirstBlock.metadata).toBeTruthy(); - }); - - test("Get first block by it's number - encoded.", async () => { - // Check decoded - const encodedFirstBlock = await getBlock( - { blockNumber: "0" }, - GetBlockResponseTypeV1.Encoded, - ); - const decodedFirstBlockBuffer = Buffer.from(encodedFirstBlock, "base64"); - const decodedFirstBlock = BlockDecoder.decode(decodedFirstBlockBuffer); - log.debug("Received decodedFirstBlock:", decodedFirstBlock); - expect(decodedFirstBlock.header).toBeTruthy(); - expect(decodedFirstBlock.header.number.low).toBe(0); - expect(decodedFirstBlock.header.number.high).toBe(0); - expect(decodedFirstBlock.data).toBeTruthy(); - expect(decodedFirstBlock.metadata).toBeTruthy(); - }); - - /** - * GetBlock endpoint using transactionId - */ - test("Get a block by transactionId it contains", async () => { - // Run some transaction - const assetName = `getBlockTx_${(Math.random() + 1).toString(36).substring(2)}`; - const txId = await sendTransactionOnFabric(assetName); - - // Get block using transactionId we've just sent - const blockByTx = await getBlock( - { transactionId: txId }, - GetBlockResponseTypeV1.Full, - ); - expect(blockByTx).toBeTruthy(); - expect(blockByTx.header).toBeTruthy(); - expect(blockByTx.data).toBeTruthy(); - expect(blockByTx.metadata).toBeTruthy(); - }); - - test("Get a block by transactionId it contains - cacti transactions summary", async () => { - // Run some transaction - const assetName = `cactiTx_${(Math.random() + 1).toString(36).substring(2)}`; - const txId = await sendTransactionOnFabric(assetName); - - // Get block using transactionId we've just sent - const cactiTxList = await getBlock( - { transactionId: txId }, - GetBlockResponseTypeV1.CactiTransactions, - ); - expect(cactiTxList).toBeTruthy(); - expect(cactiTxList.length).toBeGreaterThanOrEqual(1); - const cactiTx = cactiTxList[0]; - expect(cactiTx).toBeTruthy(); - expect(cactiTx.chaincodeId).toBeTruthy(); - expect(cactiTx.transactionId).toBeTruthy(); - expect(cactiTx.functionName).toBeTruthy(); - expect(cactiTx.functionArgs).toBeTruthy(); - expect(cactiTx.functionArgs.length).toEqual(5); - }); - - test("Get a block by transactionId it contains - cacti full block summary", async () => { - // Run some transaction - const assetName = `cactiTx_${(Math.random() + 1).toString(36).substring(2)}`; - const txId = await sendTransactionOnFabric(assetName); - - // Get block using transactionId we've just sent - const cactiFullBlock = (await getBlock( - { transactionId: txId }, - GetBlockResponseTypeV1.CactiFullBlock, - )) as CactiBlockFullEventV1; - - // Check block fields - expect(cactiFullBlock).toBeTruthy(); - expect(cactiFullBlock.blockNumber).toBeDefined(); - expect(cactiFullBlock.blockHash).toBeTruthy(); - expect(cactiFullBlock.previousBlockHash).toBeTruthy(); - expect(cactiFullBlock.transactionCount).toBeGreaterThanOrEqual(1); - - // Check transaction fields - for (const tx of cactiFullBlock.cactiTransactionsEvents) { - expect(tx.hash).toBeTruthy(); - expect(tx.channelId).toBeTruthy(); - expect(tx.timestamp).toBeTruthy(); - expect(tx.transactionType).toBeTruthy(); - expect(tx.protocolVersion).not.toBeUndefined(); - expect(tx.epoch).not.toBeUndefined(); - - // Check transaction actions fields - for (const action of tx.actions) { - expect(action.functionName).toBeTruthy(); - expect(action.functionArgs).toBeTruthy(); - expect(action.functionArgs.length).toEqual(5); - expect(action.chaincodeId).toBeTruthy(); - expect(action.creator.mspid).toBeTruthy(); - expect(action.creator.cert).toBeTruthy(); - - // Check transaction action endorsement fields - for (const endorsement of action.endorsements) { - expect(endorsement.signature).toBeTruthy(); - expect(endorsement.signer.mspid).toBeTruthy(); - expect(endorsement.signer.cert).toBeTruthy(); - } - } - } - }); - - /** - * GetBlock endpoint using block hash - */ - test("Get block by it's hash.", async () => { - // Run transaction to ensure more than one block is present - const assetName = `txForNewBlock_${(Math.random() + 1).toString(36).substring(2)}`; - await sendTransactionOnFabric(assetName); - - // Get second block by it's number - const decodedSecondBlock = await getBlock( - { blockNumber: "1" }, - GetBlockResponseTypeV1.Full, - ); - expect(decodedSecondBlock.header).toBeTruthy(); - const firstBlockHashJSON = decodedSecondBlock.header.previous_hash; - expect(firstBlockHashJSON).toBeTruthy(); - - // Get using default JSON hash representation - log.info("Get by JSON hash:", firstBlockHashJSON); - - const decodedFirstBlock = await getBlock( - { - blockHash: { - buffer: firstBlockHashJSON, - }, - }, - GetBlockResponseTypeV1.Full, - ); - expect(decodedFirstBlock).toBeTruthy(); - expect(decodedFirstBlock.header).toBeTruthy(); - expect(decodedFirstBlock.header.number.low).toBe(0); - expect(decodedFirstBlock.header.number.high).toBe(0); - expect(decodedFirstBlock.data).toBeTruthy(); - expect(decodedFirstBlock.metadata).toBeTruthy(); - - // Get using HEX encoded hash representation - const firstBlockHashHex = Buffer.from(firstBlockHashJSON).toString("hex"); - log.info("Get by HEX hash:", firstBlockHashHex); - - const decodedBlockHex = await getBlock( - { - blockHash: { - encoding: "hex", - buffer: firstBlockHashHex, - }, - }, - GetBlockResponseTypeV1.Full, - ); - expect(decodedBlockHex).toBeTruthy(); - expect(decodedBlockHex.header).toBeTruthy(); - expect(decodedBlockHex.header.number.low).toBe(0); - expect(decodedBlockHex.header.number.high).toBe(0); - expect(decodedBlockHex.data).toBeTruthy(); - expect(decodedBlockHex.metadata).toBeTruthy(); - }); - - /** - * Check error handling - */ - test("Reading block with invalid number returns an error.", async () => { - const getBlockReq = { - channelName: ledgerChannelName, - gatewayOptions, - query: { - blockNumber: "foo", // non existent block - }, - }; - - try { - await apiClient.getBlockV1(getBlockReq); - expect(true).toBe(false); // above call should always throw - } catch (err) { - expect(err).toBeTruthy(); - } - }); - }); - - ////////////////////////////////// - // GetChainInfoV1 Endpoint Tests - ////////////////////////////////// - - describe("GetChainInfoV1 endpoint tests", () => { - test("Get test ledger chain info.", async () => { - const chainInfoResponse = await apiClient.getChainInfoV1({ - channelName: ledgerChannelName, - gatewayOptions, - }); - - const chainInfo = chainInfoResponse.data; - expect(chainInfoResponse.status).toBe(200); - expect(chainInfo).toBeTruthy; - expect(chainInfo.height).toBeGreaterThanOrEqual(1); - expect(chainInfo.currentBlockHash).toBeTruthy; - expect(chainInfo.previousBlockHash).toBeTruthy; - }); - }); -});