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; - }); - }); -});