From 4631bfd38e7f252af5e66520c4f0664c9146d07c Mon Sep 17 00:00:00 2001 From: "nkl199@yahoo.co.uk" Date: Tue, 21 Jan 2020 10:16:48 +0000 Subject: [PATCH] Add ability to pass transient data and peer targets to a gateway transaction Signed-off-by: nkl199@yahoo.co.uk --- .../lib/common/config/default.yaml | 2 +- .../lib/worker/client/caliper-local-client.js | 2 +- .../adaptor-versions/v2/fabric-gateway-v2.js | 110 +++++++++++------- 3 files changed, 67 insertions(+), 47 deletions(-) diff --git a/packages/caliper-core/lib/common/config/default.yaml b/packages/caliper-core/lib/common/config/default.yaml index de9ff20740..1eab8f373b 100644 --- a/packages/caliper-core/lib/common/config/default.yaml +++ b/packages/caliper-core/lib/common/config/default.yaml @@ -51,7 +51,7 @@ caliper: # Path to the blockchain configuration file that contains information required to interact with the SUT networkconfig: # Sets the frequency of the progress reports in milliseconds - txupdatetime: 1000 + txupdatetime: 5000 # Configurations related to the logging mechanism logging: # Specifies the message structure through placeholders diff --git a/packages/caliper-core/lib/worker/client/caliper-local-client.js b/packages/caliper-core/lib/worker/client/caliper-local-client.js index 35100f1294..69ea3b4ca3 100644 --- a/packages/caliper-core/lib/worker/client/caliper-local-client.js +++ b/packages/caliper-core/lib/worker/client/caliper-local-client.js @@ -320,7 +320,7 @@ class CaliperLocalClient { Logger.debug('prepareTest() with:', test); let cb = require(CaliperUtils.resolvePath(test.cb)); - this.txUpdateTime = Config.get(Config.keys.TxUpdateTime, 1000); + this.txUpdateTime = Config.get(Config.keys.TxUpdateTime, 5000); const self = this; let initUpdateInter = setInterval( () => { self.initUpdate(); } , self.txUpdateTime); diff --git a/packages/caliper-fabric/lib/adaptor-versions/v2/fabric-gateway-v2.js b/packages/caliper-fabric/lib/adaptor-versions/v2/fabric-gateway-v2.js index 289acbbf30..9bbe90e712 100644 --- a/packages/caliper-fabric/lib/adaptor-versions/v2/fabric-gateway-v2.js +++ b/packages/caliper-fabric/lib/adaptor-versions/v2/fabric-gateway-v2.js @@ -160,6 +160,7 @@ class Fabric extends BlockchainInterface { this.wallet = undefined; this.userContracts = new Map(); this.userGateways = new Map(); + this.peerCache = new Map(); // this value is hardcoded, if it's used, that means that the provided timeouts are not sufficient this.configSmallestTimeout = 1000; @@ -974,6 +975,28 @@ class Fabric extends BlockchainInterface { return gateway; } + /** + * Initialize channel objects for use in peer targeting. Requires user gateways to have been + * formed in advance. + */ + async _initializePeerCache() { + + for (const userName of this.userGateways.keys()) { + const gateway = this.userGateways.get(userName); + // Loop over known channel names + const channelNames = this.networkUtil.getChannels(); + for (const channelName of channelNames) { + const network = await gateway.getNetwork(channelName); + const channel = network.getChannel(); + + // Add all peers + for (const peerObject of channel.getPeers()) { + this.peerCache.set(peerObject.getName(), peerObject); + } + } + } + } + /** * Install the specified chaincodes to their target peers. * @private @@ -1519,73 +1542,67 @@ class Fabric extends BlockchainInterface { } /** - * Submit a transaction using a Gateway contract + * Perform a transaction using a Gateway contract * @param {object} context The context previously created by the Fabric adapter. * @param {ChaincodeInvokeSettings} invokeSettings The settings associated with the transaction submission. + * @param {boolean} isSubmit boolean flag to indicate if the transaction is a submit or evaluate * @return {Promise} The result and stats of the transaction invocation. * @async */ - async _submitGatewayTransaction(context, invokeSettings) { + async _performGatewayTransaction(context, invokeSettings, isSubmit) { // Retrieve the existing contract and a client - const contract = await this._getUserContract(invokeSettings.invokerIdentity, invokeSettings.chaincodeId); - const client = this.clientProfiles.get(invokeSettings.invokerIdentity); + const smartContract = await this._getUserContract(invokeSettings.invokerIdentity, invokeSettings.chaincodeId); + + // Create a transaction + const transaction = smartContract.createTransaction(invokeSettings.chaincodeFunction); // Build the Caliper TxStatus, this is a reduced item when compared to the low level API capabilities - const txIdObject = client.newTransactionID(); - const txId = txIdObject.getTransactionID(); + const txId = transaction.getTransactionID(); let invokeStatus = new TxStatus(txId); - invokeStatus.Set('request_type', 'transaction'); - if(context.engine) { + if (context.engine) { context.engine.submitCallback(1); } - try { - const result = await contract.submitTransaction(invokeSettings.chaincodeFunction, ...invokeSettings.chaincodeArguments); - invokeStatus.result = result; - invokeStatus.verified = true; - invokeStatus.SetStatusSuccess(); - return invokeStatus; - } catch (err) { - logger.error(`Failed to submit transaction [${invokeSettings.chaincodeFunction}] using arguments [${invokeSettings.chaincodeArguments}], with error: ${err.stack ? err.stack : err}`); - invokeStatus.SetStatusFail(); - invokeStatus.result = []; - return invokeStatus; + // Add transient data if present + // - passed as key value pairing such as {"hello":"world"} + if (invokeSettings.transientData) { + const transientData = {}; + const keys = Array.from(Object.keys(invokeSettings.transientData)); + keys.forEach((key) => { + transientData[key] = Buffer.from(transientData[key]); + }); + transaction.setTransient(transientData); } - } - - /** - * Submit a transaction using a Gateway contract - * @param {object} context The context previously created by the Fabric adapter. - * @param {ChaincodeQuerySettings} querySettings The settings associated with the transaction evaluation. - * @return {Promise} The result and stats of the transaction invocation. - * @async - */ - async _evaluateGatewayTransaction(context, querySettings) { - - // Retrieve the existing contract and a client - const contract = await this._getUserContract(querySettings.invokerIdentity, querySettings.chaincodeId); - const client = this.clientProfiles.get(querySettings.invokerIdentity); - // Build the Caliper TxStatus, this is a reduced item when compared to the low level API capabilities - const txIdObject = client.newTransactionID(); - const txId = txIdObject.getTransactionID(); - let invokeStatus = new TxStatus(txId); - invokeStatus.Set('request_type', 'query'); - - if (context.engine) { - context.engine.submitCallback(1); + // Set endorsing peers if passed as a string array + if (invokeSettings.targetPeers) { + // Retrieved cached peer objects + const targetPeerObjects = []; + for (const name of invokeSettings.targetPeers) { + const peer = this.peerCache.get(name); + targetPeerObjects.push(peer); + } + // Set the peer objects in the transaction + transaction.setEndorsingPeers(targetPeerObjects); } try { - const result = await contract.evaluateTransaction(querySettings.chaincodeFunction, ...querySettings.chaincodeArguments); + let result; + if (isSubmit) { + invokeStatus.Set('request_type', 'transaction'); + result = await transaction.submit(...invokeSettings.chaincodeArguments); + } else { + invokeStatus.Set('request_type', 'query'); + result = await transaction.evaluate(...invokeSettings.chaincodeArguments); + } invokeStatus.result = result; invokeStatus.verified = true; invokeStatus.SetStatusSuccess(); return invokeStatus; } catch (err) { - logger.error(`Failed to evaluate transaction [${querySettings.chaincodeFunction}] using arguments [${querySettings.chaincodeArguments}], with error: ${err.stack ? err.stack : err}`); + logger.error(`Failed to perform ${isSubmit ? 'submit' : 'query' } transaction [${invokeSettings.chaincodeFunction}] using arguments [${invokeSettings.chaincodeArguments}], with error: ${err.stack ? err.stack : err}`); invokeStatus.SetStatusFail(); invokeStatus.result = []; return invokeStatus; @@ -1658,6 +1675,9 @@ class Fabric extends BlockchainInterface { // - within submit/evaluate, a contract will be used for a nominated user await this._initializeContracts(); + // - use gateways to build a peer cache + await this._initializePeerCache(); + // We are done - return the networkUtil object return { networkInfo: this.networkUtil, @@ -1742,7 +1762,7 @@ class Fabric extends BlockchainInterface { settings.invokerIdentity = this.defaultInvoker; } - promises.push(this._submitGatewayTransaction(context, settings)); + promises.push(this._performGatewayTransaction(context, settings, true)); } return await Promise.all(promises); @@ -1782,7 +1802,7 @@ class Fabric extends BlockchainInterface { settings.invokerIdentity = this.defaultInvoker; } - promises.push(this._evaluateGatewayTransaction(context, settings)); + promises.push(this._performGatewayTransaction(context, settings, false)); } return await Promise.all(promises);