From ec51503dd9afa97c07bd6cc39b6d4281d99d3bab Mon Sep 17 00:00:00 2001 From: Attila Klenik Date: Thu, 4 Jun 2020 16:15:39 +0000 Subject: [PATCH] Convert workload modules to classes - Create interface and base class for workload modules - Load workload modules with the factory approach - Update CI test workloads to class format - Extend CI workload arguments where needed to make workloads deterministic - Increase frequency of TX updates in CI tests to increase monitor accuracy - Update workload module paths in the benchconfig files to precise relative path - Create separate artifacts for Fabric v2 CI phase Signed-off-by: Attila Klenik --- package.json | 2 +- packages/caliper-core/index.js | 2 + .../lib/worker/client/caliper-local-client.js | 61 ++-- .../lib/worker/workload/workloadModuleBase.js | 100 ++++++ .../workload/workloadModuleInterface.js | 54 +++ .../besu_tests/caliper.yaml | 1 + .../besu_tests/open.js | 171 +++++----- .../besu_tests/phase2/benchconfig.yaml | 20 +- .../besu_tests/phase3/benchconfig.yaml | 20 +- .../besu_tests/query.js | 112 +++++-- .../besu_tests/transfer.js | 135 +++++--- .../ethereum_tests/benchconfig.yaml | 20 +- .../ethereum_tests/caliper.yaml | 1 + .../ethereum_tests/open.js | 171 +++++----- .../ethereum_tests/query.js | 114 +++++-- .../ethereum_tests/transfer.js | 135 +++++--- .../fabric_docker_distributed_tests/init.js | 78 +++-- .../fabric_docker_distributed_tests/query.js | 66 ++-- .../fabric_docker_local_tests/init.js | 78 +++-- .../fabric_docker_local_tests/query.js | 66 ++-- .../fabric_tests/caliper.yaml | 1 + .../fabric_tests/init.js | 80 +++-- .../fabric_tests/phase1/benchconfig.yaml | 6 +- .../fabric_tests/phase2/benchconfig.yaml | 6 +- .../fabric_tests/phase3/benchconfig.yaml | 6 +- .../fabric_tests/phase4/benchconfig.yaml | 6 +- .../fabric_tests/phase5/benchconfig.yaml | 6 +- .../fabric_tests/phase6/benchconfig.yaml | 46 ++- .../fabric_tests/phase6/networkconfig.yaml | 141 +++++++- .../fabric_tests/phase7/benchconfig.yaml | 44 +++ .../fabric_tests/phase7/networkconfig.yaml | 21 ++ .../fabric_tests/query.js | 68 ++-- .../fabric_tests/run.sh | 4 +- .../fisco-bcos_tests/benchconfig.yaml | 4 +- .../fisco-bcos_tests/get.js | 37 ++- .../fisco-bcos_tests/set.js | 47 ++- .../iroha_tests/benchconfig.yaml | 14 +- .../iroha_tests/create.js | 143 +++++--- .../iroha_tests/query.js | 99 +++++- .../iroha_tests/transfer.js | 127 +++++-- .../sawtooth_tests/benchconfig.yaml | 6 +- .../sawtooth_tests/query.js | 90 +++-- .../sawtooth_tests/smallbankOperations.js | 312 +++++++++--------- 43 files changed, 1860 insertions(+), 861 deletions(-) create mode 100644 packages/caliper-core/lib/worker/workload/workloadModuleBase.js create mode 100644 packages/caliper-core/lib/worker/workload/workloadModuleInterface.js create mode 100644 packages/caliper-tests-integration/fabric_tests/phase7/benchconfig.yaml create mode 100644 packages/caliper-tests-integration/fabric_tests/phase7/networkconfig.yaml diff --git a/package.json b/package.json index dab75f06b..3f75c48ff 100644 --- a/package.json +++ b/package.json @@ -67,4 +67,4 @@ } }, "license": "Apache-2.0" -} \ No newline at end of file +} diff --git a/packages/caliper-core/index.js b/packages/caliper-core/index.js index b97962493..fa0d8d4ce 100644 --- a/packages/caliper-core/index.js +++ b/packages/caliper-core/index.js @@ -26,3 +26,5 @@ module.exports.CaliperEngine = require('./lib/master/caliper-engine'); module.exports.MonitorOrchestrator = require('./lib/master/orchestrators/monitor-orchestrator'); module.exports.RoundOrchestrator = require('./lib/master/orchestrators/round-orchestrator'); module.exports.WorkerOrchestrator = require('./lib/master/orchestrators/worker-orchestrator'); +module.exports.WorkloadModuleInterface = require('./lib/worker/workload/workloadModuleInterface'); +module.exports.WorkloadModuleBase = require('./lib/worker/workload/workloadModuleBase'); 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 84955152f..48665a24a 100644 --- a/packages/caliper-core/lib/worker/client/caliper-local-client.js +++ b/packages/caliper-core/lib/worker/client/caliper-local-client.js @@ -37,6 +37,7 @@ class CaliperLocalClient { constructor(bcClient, clientIndex, messenger) { this.blockchain = new bc(bcClient); this.clientIndex = clientIndex; + this.currentRoundIndex = -1; this.messenger = messenger; this.context = undefined; this.txUpdateTime = Config.get(Config.keys.TxUpdateTime, 5000); @@ -55,6 +56,12 @@ class CaliperLocalClient { this.prometheusClient = new PrometheusClient(); this.totalTxCount = 0; this.totalTxDelay = 0; + + /** + * The workload module instance associated with the current round, updated by {CaliperLocalClient.prepareTest}. + * @type {WorkloadModuleInterface} + */ + this.workloadModule = undefined; } /** @@ -235,25 +242,26 @@ class CaliperLocalClient { /** * Perform test with specified number of transactions - * @param {Object} cb callback module * @param {Object} number number of transactions to submit * @param {Object} rateController rate controller object * @async */ - async runFixedNumber(cb, number, rateController) { - Logger.info('Info: client ' + this.clientIndex + ' start test runFixedNumber()' + (cb.info ? (':' + cb.info) : '')); + async runFixedNumber(number, rateController) { + Logger.info(`Worker ${this.clientIndex} is starting TX number-based round ${this.currentRoundIndex + 1} (${number} TXs)`); this.startTime = Date.now(); const circularArray = new CircularArray(this.maxTxPromises); + const self = this; while (this.txNum < number) { - // If this function calls cb.run() too quickly, micro task queue will be filled with unexecuted promises, + // If this function calls this.workloadModule.submitTransaction() too quickly, micro task queue will be filled with unexecuted promises, // and I/O task(s) will get no chance to be execute and fall into starvation, for more detail info please visit: // https://snyk.io/blog/nodejs-how-even-quick-async-functions-can-block-the-event-loop-starve-io/ await this.setImmediatePromise(() => { - circularArray.add(cb.run().then((result) => { - this.addResult(result); - return Promise.resolve(); - })); + circularArray.add(self.workloadModule.submitTransaction() + .then((result) => { + this.addResult(result); + return Promise.resolve(); + })); }); await rateController.applyRateControl(this.startTime, this.txNum, this.results, this.resultStats); } @@ -264,26 +272,27 @@ class CaliperLocalClient { /** * Perform test with specified test duration - * @param {Object} cb callback module * @param {Object} duration duration to run for * @param {Object} rateController rate controller object * @async */ - async runDuration(cb, duration, rateController) { - Logger.info('Info: client ' + this.clientIndex + ' start test runDuration()' + (cb.info ? (':' + cb.info) : '')); + async runDuration(duration, rateController) { + Logger.info(`Worker ${this.clientIndex} is starting duration-based round ${this.currentRoundIndex + 1} (${duration} seconds)`); this.startTime = Date.now(); // Use a circular array of Promises so that the Promise.all() call does not exceed the maximum permissable Array size const circularArray = new CircularArray(this.maxTxPromises); + const self = this; while ((Date.now() - this.startTime)/1000 < duration) { - // If this function calls cb.run() too quickly, micro task queue will be filled with unexecuted promises, + // If this function calls this.workloadModule.submitTransaction() too quickly, micro task queue will be filled with unexecuted promises, // and I/O task(s) will get no chance to be execute and fall into starvation, for more detail info please visit: // https://snyk.io/blog/nodejs-how-even-quick-async-functions-can-block-the-event-loop-starve-io/ await this.setImmediatePromise(() => { - circularArray.add(cb.run().then((result) => { - this.addResult(result); - return Promise.resolve(); - })); + circularArray.add(self.workloadModule.submitTransaction() + .then((result) => { + this.addResult(result); + return Promise.resolve(); + })); }); await rateController.applyRateControl(this.startTime, this.txNum, this.results, this.resultStats); } @@ -323,7 +332,10 @@ class CaliperLocalClient { */ async prepareTest(test) { Logger.debug('prepareTest() with:', test); - let cb = require(CaliperUtils.resolvePath(test.cb)); + this.currentRoundIndex = test.testRound; + + const workloadModuleFactory = CaliperUtils.loadModuleFunction(new Map(), test.cb, 'createWorkloadModule'); + this.workloadModule = workloadModuleFactory(); const self = this; let initUpdateInter = setInterval( () => { self.initUpdate(); } , self.txUpdateTime); @@ -344,15 +356,15 @@ class CaliperLocalClient { } // Run init phase of callback - Logger.info(`Info: client ${this.clientIndex} prepare test ${(cb.info ? (':' + cb.info + 'phase starting...') : 'phase starting...')}`); - await cb.init(this.blockchain, this.context, test.args); + Logger.info(`Info: client ${this.clientIndex} prepare test phase for round ${this.currentRoundIndex + 1} is starting...`); + await this.workloadModule.initializeWorkloadModule(this.clientIndex, test.totalClients, this.currentRoundIndex, test.args, this.blockchain, this.context); await CaliperUtils.sleep(this.txUpdateTime); } catch (err) { - Logger.info(`Client[${this.clientIndex}] encountered an error during prepare test phase: ${(err.stack ? err.stack : err)}`); + Logger.info(`Client[${this.clientIndex}] encountered an error during prepare test phase for round ${this.currentRoundIndex + 1}: ${(err.stack ? err.stack : err)}`); throw err; } finally { clearInterval(initUpdateInter); - Logger.info(`Info: client ${this.clientIndex} prepare test ${(cb.info ? (':' + cb.info + 'phase complete') : 'phase complete')}`); + Logger.info(`Info: client ${this.clientIndex} prepare test phase for round ${this.currentRoundIndex + 1} is completed`); } } @@ -374,7 +386,6 @@ class CaliperLocalClient { */ async doTest(test) { Logger.debug('doTest() with:', test); - let cb = require(CaliperUtils.resolvePath(test.cb)); this.beforeTest(test); @@ -391,15 +402,15 @@ class CaliperLocalClient { // Run the test loop if (test.txDuration) { const duration = test.txDuration; // duration in seconds - await this.runDuration(cb, duration, rateController); + await this.runDuration(duration, rateController); } else { const number = test.numb; - await this.runFixedNumber(cb, number, rateController); + await this.runFixedNumber(number, rateController); } // Clean up await rateController.end(); - await cb.end(); + await this.workloadModule.cleanupWorkloadModule(); await this.blockchain.releaseContext(this.context); this.clearUpdateInter(txUpdateInter); diff --git a/packages/caliper-core/lib/worker/workload/workloadModuleBase.js b/packages/caliper-core/lib/worker/workload/workloadModuleBase.js new file mode 100644 index 000000000..0abc43ccf --- /dev/null +++ b/packages/caliper-core/lib/worker/workload/workloadModuleBase.js @@ -0,0 +1,100 @@ +/* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +const WorkloadModuleInterface = require('./workloadModuleInterface'); +const Logger = require('./../../common/utils/caliper-utils').getLogger('workload-module-base'); + +/** + * Utility base class for the user-implemented workload modules used for assembling TXs for the SUT. + */ +class WorkloadModuleBase extends WorkloadModuleInterface { + /** + * Initialize an instance of the WorkloadModuleBase class. + */ + constructor() { + super(); + + Logger.debug('Constructing workload module'); + + /** + * The 0-based index of the worker instantiating the workload module. + * @type {number} + */ + this.workerIndex = -1; + + /** + * The total number of workers participating in the round. + * @type {number} + */ + this.totalWorkers = -1; + + /** + * The 0-based index of the currently executing round. + * @type {number} + */ + this.roundIndex = -1; + + /** + * The user-provided arguments for the round from the benchmark configuration file. + * @type {Object} + */ + this.roundArguments = undefined; + + /** + * The adapter of the underlying SUT. + * @type {BlockchainInterface} + */ + this.sutAdapter = undefined; + + /** + * The custom context object provided by the SUT adapter. + * @type {Object} + */ + this.sutContext = undefined; + } + + /** + * Initialize the workload module with the given parameters. + * @param {number} workerIndex The 0-based index of the worker instantiating the workload module. + * @param {number} totalWorkers The total number of workers participating in the round. + * @param {number} roundIndex The 0-based index of the currently executing round. + * @param {Object} roundArguments The user-provided arguments for the round from the benchmark configuration file. + * @param {BlockchainInterface} sutAdapter The adapter of the underlying SUT. + * @param {Object} sutContext The custom context object provided by the SUT adapter. + * @async + */ + async initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext) { + Logger.debug(`Workload module initialized with: workerIndex=${workerIndex}, totalWorkers=${totalWorkers}, roundIndex=${roundIndex}, roundArguments=${JSON.stringify(roundArguments)}`); + this.workerIndex = workerIndex; + this.totalWorkers = totalWorkers; + this.roundIndex = roundIndex; + this.roundArguments = roundArguments; + this.sutAdapter = sutAdapter; + this.sutContext = sutContext; + } + + /** + * Clean up the workload module at the end of the round. + * @async + */ + async cleanupWorkloadModule() { + // NOOP by default + Logger.debug('Cleaning up workload module: NOOP'); + } +} + + +module.exports = WorkloadModuleBase; diff --git a/packages/caliper-core/lib/worker/workload/workloadModuleInterface.js b/packages/caliper-core/lib/worker/workload/workloadModuleInterface.js new file mode 100644 index 000000000..92000ef51 --- /dev/null +++ b/packages/caliper-core/lib/worker/workload/workloadModuleInterface.js @@ -0,0 +1,54 @@ +/* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +/** + * Interface for the user-implemented workload modules used for assembling TXs for the SUT. + */ +class WorkloadModuleInterface { + /** + * Initialize the workload module with the given parameters. + * @param {number} workerIndex The 0-based index of the worker instantiating the workload module. + * @param {number} totalWorkers The total number of workers participating in the round. + * @param {number} roundIndex The 0-based index of the currently executing round. + * @param {object} roundArguments The user-provided arguments for the round from the benchmark configuration file. + * @param {BlockchainInterface} sutAdapter The adapter of the underlying SUT. + * @param {object} sutContext The custom context object provided by the SUT adapter. + * @async + */ + async initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext) { + throw new Error('WorkloadModuleInterface.initializeWorkloadModule() must be implemented in derived class'); + } + + /** + * Assemble the next TX content(s) and submit it to the SUT adapter. + * @return {Promise} The promises for the TX results as returned by the SUT adapter. + * @async + */ + async submitTransaction() { + throw new Error('WorkloadModuleInterface.submitTransaction() must be implemented in derived class'); + } + + /** + * Clean up the workload module at the end of the round. + * @async + */ + async cleanupWorkloadModule() { + throw new Error('WorkloadModuleInterface.cleanupWorkloadModule() must be implemented in derived class'); + } +} + + +module.exports = WorkloadModuleInterface; diff --git a/packages/caliper-tests-integration/besu_tests/caliper.yaml b/packages/caliper-tests-integration/besu_tests/caliper.yaml index 99daf1adb..733d8e984 100644 --- a/packages/caliper-tests-integration/besu_tests/caliper.yaml +++ b/packages/caliper-tests-integration/besu_tests/caliper.yaml @@ -15,6 +15,7 @@ caliper: benchconfig: benchconfig.yaml networkconfig: networkconfig.json + txupdatetime: 1000 workspace: ./ report: path: report.html diff --git a/packages/caliper-tests-integration/besu_tests/open.js b/packages/caliper-tests-integration/besu_tests/open.js index b769849da..b9444d5a2 100644 --- a/packages/caliper-tests-integration/besu_tests/open.js +++ b/packages/caliper-tests-integration/besu_tests/open.js @@ -14,94 +14,115 @@ 'use strict'; -module.exports.info = 'opening accounts'; - -let account_array = []; -let txnPerBatch; -let initMoney; -let bc, contx; -module.exports.init = function(blockchain, context, args) { - if(!args.hasOwnProperty('money')) { - return Promise.reject(new Error('simple.open - \'money\' is missed in the arguments')); - } - - if(!args.hasOwnProperty('txnPerBatch')) { - args.txnPerBatch = 1; - } - initMoney = args.money; - txnPerBatch = args.txnPerBatch; - bc = blockchain; - contx = context; +const { WorkloadModuleBase, CaliperUtils } = require('@hyperledger/caliper-core'); +const Logger = CaliperUtils.getLogger('simple-workload-open'); - return Promise.resolve(); -}; +const Dictionary = 'abcdefghijklmnopqrstuvwxyz'; -const dic = 'abcdefghijklmnopqrstuvwxyz'; /** - * Generate string by picking characters from dic variable - * @param {*} number character to select - * @returns {String} string generated based on @param number + * Workload module for initializing the SUT with various accounts. */ -function get26Num(number){ - let result = ''; - while(number > 0) { - result += dic.charAt(number % 26); - number = parseInt(number/26); +class SimpleOpenWorkload extends WorkloadModuleBase { + + /** + * Initializes the parameters of the workload. + */ + constructor() { + super(); + this.accountPrefix = ''; + this.txIndex = -1; } - return result; -} -let prefix; -/** - * Generate unique account key for the transaction - * @returns {String} account key - */ -function generateAccount() { - // should be [a-z]{1,9} - if(typeof prefix === 'undefined') { - prefix = get26Num(process.pid); + /** + * Generate string by picking characters from the dictionary variable. + * @param {number} number Character to select. + * @returns {string} string Generated string based on the input number. + * @private + */ + static _get26Num(number){ + let result = ''; + + while(number > 0) { + result += Dictionary.charAt(number % Dictionary.length); + number = parseInt(number / Dictionary.length); + } + + return result; } - return prefix + get26Num(account_array.length+1); -} -/** - * Generates simple workload - * @returns {Object} array of json objects - */ -function generateWorkload() { - let workload = []; - for(let i= 0; i < txnPerBatch; i++) { - let acc_id = generateAccount(); - account_array.push(acc_id); + /** + * Generate unique account key for the transaction. + * @returns {string} The account key. + * @private + */ + _generateAccount() { + return this.roundArguments.accountPhasePrefix + this.accountPrefix + SimpleOpenWorkload._get26Num(this.txIndex + 1); + } + + /** + * Generates simple workload. + * @returns {{verb: String, args: Object[]}[]} Array of workload argument objects. + */ + _generateWorkload() { + let workload = []; + for(let i= 0; i < this.roundArguments.txnPerBatch; i++) { + this.txIndex++; + + let accountId = this._generateAccount(); - if (bc.getType() === 'fabric') { - workload.push({ - chaincodeFunction: 'open', - chaincodeArguments: [acc_id, initMoney.toString()], - }); - } else if (bc.getType() === 'ethereum') { - workload.push({ - verb: 'open', - args: [acc_id, initMoney] - }); - } else { workload.push({ - 'verb': 'open', - 'account': acc_id, - 'money': initMoney + verb: 'open', + args: [accountId, this.roundArguments.money] }); } + return workload; + } + + /** + * Initialize the workload module with the given parameters. + * @param {number} workerIndex The 0-based index of the worker instantiating the workload module. + * @param {number} totalWorkers The total number of workers participating in the round. + * @param {number} roundIndex The 0-based index of the currently executing round. + * @param {Object} roundArguments The user-provided arguments for the round from the benchmark configuration file. + * @param {BlockchainInterface} sutAdapter The adapter of the underlying SUT. + * @param {Object} sutContext The custom context object provided by the SUT adapter. + * @async + */ + async initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext) { + await super.initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext); + + if(!this.roundArguments.money) { + throw new Error('simple.open - the "money" argument is missing'); + } + + if(!this.roundArguments.accountPhasePrefix) { + throw new Error('simple.open - the "accountPhasePrefix" argument is missing'); + } + + if(!this.roundArguments.txnPerBatch) { + this.roundArguments.txnPerBatch = 1; + } + + this.accountPrefix = SimpleOpenWorkload._get26Num(workerIndex); } - return workload; -} -module.exports.run = function() { - let args = generateWorkload(); - return bc.invokeSmartContract(contx, 'simple', 'v0', args, 100); -}; + /** + * Assemble TXs for opening new accounts. + * @return {Promise} + */ + async submitTransaction() { + let args = this._generateWorkload(); + Logger.debug(`Worker ${this.workerIndex} for TX ${this.txIndex}: ${JSON.stringify(args)}`); + return this.sutAdapter.invokeSmartContract(this.sutContext, 'simple', 'v0', args, 100); + } +} -module.exports.end = function() { - return Promise.resolve(); -}; +/** + * Create a new instance of the workload module. + * @return {WorkloadModuleInterface} + */ +function createWorkloadModule() { + return new SimpleOpenWorkload(); +} -module.exports.account_array = account_array; +module.exports.createWorkloadModule = createWorkloadModule; diff --git a/packages/caliper-tests-integration/besu_tests/phase2/benchconfig.yaml b/packages/caliper-tests-integration/besu_tests/phase2/benchconfig.yaml index caec02db4..b52da22b7 100644 --- a/packages/caliper-tests-integration/besu_tests/phase2/benchconfig.yaml +++ b/packages/caliper-tests-integration/besu_tests/phase2/benchconfig.yaml @@ -13,27 +13,37 @@ # --- +params: + numberOfAccounts: &accounts 100 + accountPhasePrefix: &prefix phase2 + test: workers: type: local number: 1 rounds: - label: open - txNumber: 100 + txNumber: *accounts rateControl: { type: 'fixed-rate', opts: { tps: 10 } } arguments: + accountPhasePrefix: *prefix money: 10000 - callback: ../open.js + callback: ./../open.js - label: query txNumber: 200 rateControl: { type: 'fixed-feedback-rate', opts: { tps: 10, unfinished_per_client: 5 } } - callback: ../query.js + callback: ./../query.js + arguments: + numberOfAccounts: *accounts + accountPhasePrefix: *prefix - label: transfer txNumber: 100 rateControl: { type: 'linear-rate', opts: { startingTps: 5, finishingTps: 20 } } arguments: - money: 10000 - callback: ../transfer.js + accountPhasePrefix: *prefix + numberOfAccounts: *accounts + money: 100 + callback: ./../transfer.js observer: interval: 1 type: local diff --git a/packages/caliper-tests-integration/besu_tests/phase3/benchconfig.yaml b/packages/caliper-tests-integration/besu_tests/phase3/benchconfig.yaml index fd691b4b9..16de5ecd4 100644 --- a/packages/caliper-tests-integration/besu_tests/phase3/benchconfig.yaml +++ b/packages/caliper-tests-integration/besu_tests/phase3/benchconfig.yaml @@ -13,27 +13,37 @@ # --- +params: + numberOfAccounts: &accounts 100 + accountPhasePrefix: &prefix phase3 + test: workers: type: local number: 2 rounds: - label: open - txNumber: 100 + txNumber: *accounts rateControl: { type: 'fixed-rate', opts: { tps: 10 } } arguments: + accountPhasePrefix: *prefix money: 10000 - callback: ../open.js + callback: ./../open.js - label: query txNumber: 200 rateControl: { type: 'fixed-feedback-rate', opts: { tps: 10, unfinished_per_client: 5 } } - callback: ../query.js + callback: ./../query.js + arguments: + numberOfAccounts: *accounts + accountPhasePrefix: *prefix - label: transfer txNumber: 100 rateControl: { type: 'linear-rate', opts: { startingTps: 5, finishingTps: 20 } } arguments: - money: 10000 - callback: ../transfer.js + accountPhasePrefix: *prefix + numberOfAccounts: *accounts + money: 100 + callback: ./../transfer.js observer: interval: 1 type: local diff --git a/packages/caliper-tests-integration/besu_tests/query.js b/packages/caliper-tests-integration/besu_tests/query.js index 3c131b736..9982f08af 100644 --- a/packages/caliper-tests-integration/besu_tests/query.js +++ b/packages/caliper-tests-integration/besu_tests/query.js @@ -14,38 +14,100 @@ 'use strict'; -module.exports.info = 'querying accounts'; +const { WorkloadModuleBase, CaliperUtils } = require('@hyperledger/caliper-core'); +const Logger = CaliperUtils.getLogger('simple-workload-query'); +const Dictionary = 'abcdefghijklmnopqrstuvwxyz'; -let bc, contx; -let account_array; +/** + * Workload module for querying various accounts. + */ +class SimpleQueryWorkload extends WorkloadModuleBase { -module.exports.init = function(blockchain, context, args) { - const open = require('./open.js'); - bc = blockchain; - contx = context; - account_array = open.account_array; + /** + * Initializes the parameters of the workload. + */ + constructor() { + super(); + this.accountPrefix = ''; + this.numberOfAccountsPerWorker = -1; - return Promise.resolve(); -}; + } + + /** + * Generate string by picking characters from the dictionary variable. + * @param {number} number Character to select. + * @returns {string} string Generated string based on the input number. + * @private + */ + static _get26Num(number){ + let result = ''; + + while(number > 0) { + result += Dictionary.charAt(number % Dictionary.length); + number = parseInt(number / Dictionary.length); + } + + return result; + } + + /** + * Generate unique account key for the transaction. + * @returns {string} The account key. + * @private + */ + _generateAccount() { + // choose a random TX/account index based on the existing range, and restore the account name from the fragments + return this.roundArguments.accountPhasePrefix + this.accountPrefix + SimpleQueryWorkload._get26Num(Math.floor(Math.random() * this.numberOfAccountsPerWorker) + 1); + } -module.exports.run = function() { - const acc = account_array[Math.floor(Math.random()*(account_array.length))]; + /** + * Initialize the workload module with the given parameters. + * @param {number} workerIndex The 0-based index of the worker instantiating the workload module. + * @param {number} totalWorkers The total number of workers participating in the round. + * @param {number} roundIndex The 0-based index of the currently executing round. + * @param {Object} roundArguments The user-provided arguments for the round from the benchmark configuration file. + * @param {BlockchainInterface} sutAdapter The adapter of the underlying SUT. + * @param {Object} sutContext The custom context object provided by the SUT adapter. + * @async + */ + async initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext) { + await super.initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext); - if (bc.getType() === 'fabric') { - let args = { - chaincodeFunction: 'query', - chaincodeArguments: [acc], + if(!this.roundArguments.numberOfAccounts) { + throw new Error('simple.query - the "numberOfAccounts" argument is missing'); + } + + if(!this.roundArguments.accountPhasePrefix) { + throw new Error('simple.open - the "accountPhasePrefix" argument is missing'); + } + + + this.accountPrefix = SimpleQueryWorkload._get26Num(workerIndex); + this.numberOfAccountsPerWorker = this.roundArguments.numberOfAccounts / this.totalWorkers; + } + + /** + * Assemble TXs for querying accounts. + * @return {Promise} + */ + async submitTransaction() { + const args = { + verb: 'query', + args: [this._generateAccount()] }; - return bc.bcObj.querySmartContract(contx, 'simple', 'v0', args, 10); - } else { - // NOTE: the query API is not consistent with the invoke API - return bc.queryState(contx, 'simple', 'v0', acc); + Logger.debug(`Worker ${this.workerIndex} TX parameters: ${JSON.stringify(args)}`); + return this.sutAdapter.querySmartContract(this.sutContext, 'simple', 'v0', args, 100); } -}; +} + +/** + * Create a new instance of the workload module. + * @return {WorkloadModuleInterface} + */ +function createWorkloadModule() { + return new SimpleQueryWorkload(); +} -module.exports.end = function() { - // do nothing - return Promise.resolve(); -}; +module.exports.createWorkloadModule = createWorkloadModule; diff --git a/packages/caliper-tests-integration/besu_tests/transfer.js b/packages/caliper-tests-integration/besu_tests/transfer.js index aa317312d..7667df07d 100644 --- a/packages/caliper-tests-integration/besu_tests/transfer.js +++ b/packages/caliper-tests-integration/besu_tests/transfer.js @@ -15,54 +15,103 @@ 'use strict'; -module.exports.info = 'transfering money'; +const { WorkloadModuleBase, CaliperUtils } = require('@hyperledger/caliper-core'); +const Logger = CaliperUtils.getLogger('simple-workload-query'); -let bc, contx; -let account_array; -let initmoney; +const Dictionary = 'abcdefghijklmnopqrstuvwxyz'; + +/** + * Workload module for transferring money between accounts. + */ +class SimpleTransferWorkload extends WorkloadModuleBase { + + /** + * Initializes the parameters of the workload. + */ + constructor() { + super(); + this.accountPrefix = ''; + this.numberOfAccountsPerWorker = -1; -module.exports.init = function (blockchain, context, args) { - const open = require('./open.js'); - if (!args.hasOwnProperty('money')) { - return Promise.reject(new Error('account.transfer - \'money\' is missed in the arguments')); } - bc = blockchain; - contx = context; - initmoney = args.money; - account_array = open.account_array; - - return Promise.resolve(); -}; - -module.exports.run = function () { - const account1 = account_array[Math.floor(Math.random() * (account_array.length))]; - const account2 = account_array[Math.floor(Math.random() * (account_array.length))]; - let args; - - if (bc.getType() === 'fabric') { - args = { - chaincodeFunction: 'transfer', - chaincodeArguments: [account1, account2, initmoney.toString()], - }; - } else if (bc.getType() === 'ethereum') { - args = { + + /** + * Generate string by picking characters from the dictionary variable. + * @param {number} number Character to select. + * @returns {string} string Generated string based on the input number. + * @private + */ + static _get26Num(number){ + let result = ''; + + while(number > 0) { + result += Dictionary.charAt(number % Dictionary.length); + number = parseInt(number / Dictionary.length); + } + + return result; + } + + /** + * Generate unique account key for the transaction. + * @returns {string} The account key. + * @private + */ + _generateAccount() { + // choose a random TX/account index based on the existing range, and restore the account name from the fragments + return this.roundArguments.accountPhasePrefix + this.accountPrefix + SimpleTransferWorkload._get26Num(Math.floor(Math.random() * this.numberOfAccountsPerWorker) + 1); + } + + /** + * Initialize the workload module with the given parameters. + * @param {number} workerIndex The 0-based index of the worker instantiating the workload module. + * @param {number} totalWorkers The total number of workers participating in the round. + * @param {number} roundIndex The 0-based index of the currently executing round. + * @param {Object} roundArguments The user-provided arguments for the round from the benchmark configuration file. + * @param {BlockchainInterface} sutAdapter The adapter of the underlying SUT. + * @param {Object} sutContext The custom context object provided by the SUT adapter. + * @async + */ + async initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext) { + await super.initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext); + + if(!this.roundArguments.money) { + throw new Error('simple.transfer - the "money" argument is missing'); + } + + if(!this.roundArguments.numberOfAccounts) { + throw new Error('simple.transfer - the "numberOfAccounts" argument is missing'); + } + + if(!this.roundArguments.accountPhasePrefix) { + throw new Error('simple.open - the "accountPhasePrefix" argument is missing'); + } + + this.accountPrefix = SimpleTransferWorkload._get26Num(workerIndex); + this.numberOfAccountsPerWorker = this.roundArguments.numberOfAccounts / this.totalWorkers; + } + + /** + * Assemble TXs for transferring money. + * @return {Promise} + */ + async submitTransaction() { + const args = { verb: 'transfer', - args: [account1, account2, initmoney] - }; - } else { - args = { - 'verb': 'transfer', - 'account1': account1, - 'account2': account2, - 'money': initmoney.toString() + args: [this._generateAccount(), this._generateAccount(), this.roundArguments.money.toString()] }; - } - return bc.invokeSmartContract(contx, 'simple', 'v0', args, 10); + Logger.debug(`Worker ${this.workerIndex} TX parameters: ${JSON.stringify(args)}`); + return this.sutAdapter.invokeSmartContract(this.sutContext, 'simple', 'v0', args, 100); + } +} -}; +/** + * Create a new instance of the workload module. + * @return {WorkloadModuleInterface} + */ +function createWorkloadModule() { + return new SimpleTransferWorkload(); +} -module.exports.end = function () { - // do nothing - return Promise.resolve(); -}; +module.exports.createWorkloadModule = createWorkloadModule; diff --git a/packages/caliper-tests-integration/ethereum_tests/benchconfig.yaml b/packages/caliper-tests-integration/ethereum_tests/benchconfig.yaml index 910bc5e0a..084a10528 100644 --- a/packages/caliper-tests-integration/ethereum_tests/benchconfig.yaml +++ b/packages/caliper-tests-integration/ethereum_tests/benchconfig.yaml @@ -13,27 +13,37 @@ # --- +params: + numberOfAccounts: &accounts 100 + accountPhasePrefix: &prefix phase3 + test: workers: type: local number: 2 rounds: - label: open - txNumber: 100 + txNumber: *accounts rateControl: { type: 'fixed-rate', opts: { tps: 10 } } arguments: + accountPhasePrefix: *prefix money: 10000 - callback: open.js + callback: ./open.js - label: query txNumber: 200 rateControl: { type: 'fixed-feedback-rate', opts: { tps: 10, unfinished_per_client: 5 } } - callback: query.js + callback: ./query.js + arguments: + numberOfAccounts: *accounts + accountPhasePrefix: *prefix - label: transfer txNumber: 100 rateControl: { type: 'linear-rate', opts: { startingTps: 5, finishingTps: 20 } } arguments: - money: 10000 - callback: transfer.js + accountPhasePrefix: *prefix + numberOfAccounts: *accounts + money: 100 + callback: ./transfer.js observer: interval: 1 type: local diff --git a/packages/caliper-tests-integration/ethereum_tests/caliper.yaml b/packages/caliper-tests-integration/ethereum_tests/caliper.yaml index 99daf1adb..733d8e984 100644 --- a/packages/caliper-tests-integration/ethereum_tests/caliper.yaml +++ b/packages/caliper-tests-integration/ethereum_tests/caliper.yaml @@ -15,6 +15,7 @@ caliper: benchconfig: benchconfig.yaml networkconfig: networkconfig.json + txupdatetime: 1000 workspace: ./ report: path: report.html diff --git a/packages/caliper-tests-integration/ethereum_tests/open.js b/packages/caliper-tests-integration/ethereum_tests/open.js index b769849da..b9444d5a2 100644 --- a/packages/caliper-tests-integration/ethereum_tests/open.js +++ b/packages/caliper-tests-integration/ethereum_tests/open.js @@ -14,94 +14,115 @@ 'use strict'; -module.exports.info = 'opening accounts'; - -let account_array = []; -let txnPerBatch; -let initMoney; -let bc, contx; -module.exports.init = function(blockchain, context, args) { - if(!args.hasOwnProperty('money')) { - return Promise.reject(new Error('simple.open - \'money\' is missed in the arguments')); - } - - if(!args.hasOwnProperty('txnPerBatch')) { - args.txnPerBatch = 1; - } - initMoney = args.money; - txnPerBatch = args.txnPerBatch; - bc = blockchain; - contx = context; +const { WorkloadModuleBase, CaliperUtils } = require('@hyperledger/caliper-core'); +const Logger = CaliperUtils.getLogger('simple-workload-open'); - return Promise.resolve(); -}; +const Dictionary = 'abcdefghijklmnopqrstuvwxyz'; -const dic = 'abcdefghijklmnopqrstuvwxyz'; /** - * Generate string by picking characters from dic variable - * @param {*} number character to select - * @returns {String} string generated based on @param number + * Workload module for initializing the SUT with various accounts. */ -function get26Num(number){ - let result = ''; - while(number > 0) { - result += dic.charAt(number % 26); - number = parseInt(number/26); +class SimpleOpenWorkload extends WorkloadModuleBase { + + /** + * Initializes the parameters of the workload. + */ + constructor() { + super(); + this.accountPrefix = ''; + this.txIndex = -1; } - return result; -} -let prefix; -/** - * Generate unique account key for the transaction - * @returns {String} account key - */ -function generateAccount() { - // should be [a-z]{1,9} - if(typeof prefix === 'undefined') { - prefix = get26Num(process.pid); + /** + * Generate string by picking characters from the dictionary variable. + * @param {number} number Character to select. + * @returns {string} string Generated string based on the input number. + * @private + */ + static _get26Num(number){ + let result = ''; + + while(number > 0) { + result += Dictionary.charAt(number % Dictionary.length); + number = parseInt(number / Dictionary.length); + } + + return result; } - return prefix + get26Num(account_array.length+1); -} -/** - * Generates simple workload - * @returns {Object} array of json objects - */ -function generateWorkload() { - let workload = []; - for(let i= 0; i < txnPerBatch; i++) { - let acc_id = generateAccount(); - account_array.push(acc_id); + /** + * Generate unique account key for the transaction. + * @returns {string} The account key. + * @private + */ + _generateAccount() { + return this.roundArguments.accountPhasePrefix + this.accountPrefix + SimpleOpenWorkload._get26Num(this.txIndex + 1); + } + + /** + * Generates simple workload. + * @returns {{verb: String, args: Object[]}[]} Array of workload argument objects. + */ + _generateWorkload() { + let workload = []; + for(let i= 0; i < this.roundArguments.txnPerBatch; i++) { + this.txIndex++; + + let accountId = this._generateAccount(); - if (bc.getType() === 'fabric') { - workload.push({ - chaincodeFunction: 'open', - chaincodeArguments: [acc_id, initMoney.toString()], - }); - } else if (bc.getType() === 'ethereum') { - workload.push({ - verb: 'open', - args: [acc_id, initMoney] - }); - } else { workload.push({ - 'verb': 'open', - 'account': acc_id, - 'money': initMoney + verb: 'open', + args: [accountId, this.roundArguments.money] }); } + return workload; + } + + /** + * Initialize the workload module with the given parameters. + * @param {number} workerIndex The 0-based index of the worker instantiating the workload module. + * @param {number} totalWorkers The total number of workers participating in the round. + * @param {number} roundIndex The 0-based index of the currently executing round. + * @param {Object} roundArguments The user-provided arguments for the round from the benchmark configuration file. + * @param {BlockchainInterface} sutAdapter The adapter of the underlying SUT. + * @param {Object} sutContext The custom context object provided by the SUT adapter. + * @async + */ + async initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext) { + await super.initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext); + + if(!this.roundArguments.money) { + throw new Error('simple.open - the "money" argument is missing'); + } + + if(!this.roundArguments.accountPhasePrefix) { + throw new Error('simple.open - the "accountPhasePrefix" argument is missing'); + } + + if(!this.roundArguments.txnPerBatch) { + this.roundArguments.txnPerBatch = 1; + } + + this.accountPrefix = SimpleOpenWorkload._get26Num(workerIndex); } - return workload; -} -module.exports.run = function() { - let args = generateWorkload(); - return bc.invokeSmartContract(contx, 'simple', 'v0', args, 100); -}; + /** + * Assemble TXs for opening new accounts. + * @return {Promise} + */ + async submitTransaction() { + let args = this._generateWorkload(); + Logger.debug(`Worker ${this.workerIndex} for TX ${this.txIndex}: ${JSON.stringify(args)}`); + return this.sutAdapter.invokeSmartContract(this.sutContext, 'simple', 'v0', args, 100); + } +} -module.exports.end = function() { - return Promise.resolve(); -}; +/** + * Create a new instance of the workload module. + * @return {WorkloadModuleInterface} + */ +function createWorkloadModule() { + return new SimpleOpenWorkload(); +} -module.exports.account_array = account_array; +module.exports.createWorkloadModule = createWorkloadModule; diff --git a/packages/caliper-tests-integration/ethereum_tests/query.js b/packages/caliper-tests-integration/ethereum_tests/query.js index 3c131b736..4d02d7eea 100644 --- a/packages/caliper-tests-integration/ethereum_tests/query.js +++ b/packages/caliper-tests-integration/ethereum_tests/query.js @@ -14,38 +14,102 @@ 'use strict'; -module.exports.info = 'querying accounts'; +'use strict'; + +const { WorkloadModuleBase, CaliperUtils } = require('@hyperledger/caliper-core'); +const Logger = CaliperUtils.getLogger('simple-workload-query'); + +const Dictionary = 'abcdefghijklmnopqrstuvwxyz'; + +/** + * Workload module for querying various accounts. + */ +class SimpleQueryWorkload extends WorkloadModuleBase { + /** + * Initializes the parameters of the workload. + */ + constructor() { + super(); + this.accountPrefix = ''; + this.numberOfAccountsPerWorker = -1; -let bc, contx; -let account_array; + } + + /** + * Generate string by picking characters from the dictionary variable. + * @param {number} number Character to select. + * @returns {string} string Generated string based on the input number. + * @private + */ + static _get26Num(number){ + let result = ''; -module.exports.init = function(blockchain, context, args) { - const open = require('./open.js'); - bc = blockchain; - contx = context; - account_array = open.account_array; + while(number > 0) { + result += Dictionary.charAt(number % Dictionary.length); + number = parseInt(number / Dictionary.length); + } - return Promise.resolve(); -}; + return result; + } -module.exports.run = function() { - const acc = account_array[Math.floor(Math.random()*(account_array.length))]; + /** + * Generate unique account key for the transaction. + * @returns {string} The account key. + * @private + */ + _generateAccount() { + // choose a random TX/account index based on the existing range, and restore the account name from the fragments + return this.roundArguments.accountPhasePrefix + this.accountPrefix + SimpleQueryWorkload._get26Num(Math.floor(Math.random() * this.numberOfAccountsPerWorker) + 1); + } - if (bc.getType() === 'fabric') { - let args = { - chaincodeFunction: 'query', - chaincodeArguments: [acc], + /** + * Initialize the workload module with the given parameters. + * @param {number} workerIndex The 0-based index of the worker instantiating the workload module. + * @param {number} totalWorkers The total number of workers participating in the round. + * @param {number} roundIndex The 0-based index of the currently executing round. + * @param {Object} roundArguments The user-provided arguments for the round from the benchmark configuration file. + * @param {BlockchainInterface} sutAdapter The adapter of the underlying SUT. + * @param {Object} sutContext The custom context object provided by the SUT adapter. + * @async + */ + async initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext) { + await super.initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext); + + if(!this.roundArguments.numberOfAccounts) { + throw new Error('simple.query - the "numberOfAccounts" argument is missing'); + } + + if(!this.roundArguments.accountPhasePrefix) { + throw new Error('simple.open - the "accountPhasePrefix" argument is missing'); + } + + + this.accountPrefix = SimpleQueryWorkload._get26Num(workerIndex); + this.numberOfAccountsPerWorker = this.roundArguments.numberOfAccounts / this.totalWorkers; + } + + /** + * Assemble TXs for querying accounts. + * @return {Promise} + */ + async submitTransaction() { + const args = { + verb: 'query', + args: [this._generateAccount()] }; - return bc.bcObj.querySmartContract(contx, 'simple', 'v0', args, 10); - } else { - // NOTE: the query API is not consistent with the invoke API - return bc.queryState(contx, 'simple', 'v0', acc); + Logger.debug(`Worker ${this.workerIndex} TX parameters: ${JSON.stringify(args)}`); + return this.sutAdapter.querySmartContract(this.sutContext, 'simple', 'v0', args, 100); } -}; +} + +/** + * Create a new instance of the workload module. + * @return {WorkloadModuleInterface} + */ +function createWorkloadModule() { + return new SimpleQueryWorkload(); +} -module.exports.end = function() { - // do nothing - return Promise.resolve(); -}; +module.exports.createWorkloadModule = createWorkloadModule; diff --git a/packages/caliper-tests-integration/ethereum_tests/transfer.js b/packages/caliper-tests-integration/ethereum_tests/transfer.js index aa317312d..7667df07d 100644 --- a/packages/caliper-tests-integration/ethereum_tests/transfer.js +++ b/packages/caliper-tests-integration/ethereum_tests/transfer.js @@ -15,54 +15,103 @@ 'use strict'; -module.exports.info = 'transfering money'; +const { WorkloadModuleBase, CaliperUtils } = require('@hyperledger/caliper-core'); +const Logger = CaliperUtils.getLogger('simple-workload-query'); -let bc, contx; -let account_array; -let initmoney; +const Dictionary = 'abcdefghijklmnopqrstuvwxyz'; + +/** + * Workload module for transferring money between accounts. + */ +class SimpleTransferWorkload extends WorkloadModuleBase { + + /** + * Initializes the parameters of the workload. + */ + constructor() { + super(); + this.accountPrefix = ''; + this.numberOfAccountsPerWorker = -1; -module.exports.init = function (blockchain, context, args) { - const open = require('./open.js'); - if (!args.hasOwnProperty('money')) { - return Promise.reject(new Error('account.transfer - \'money\' is missed in the arguments')); } - bc = blockchain; - contx = context; - initmoney = args.money; - account_array = open.account_array; - - return Promise.resolve(); -}; - -module.exports.run = function () { - const account1 = account_array[Math.floor(Math.random() * (account_array.length))]; - const account2 = account_array[Math.floor(Math.random() * (account_array.length))]; - let args; - - if (bc.getType() === 'fabric') { - args = { - chaincodeFunction: 'transfer', - chaincodeArguments: [account1, account2, initmoney.toString()], - }; - } else if (bc.getType() === 'ethereum') { - args = { + + /** + * Generate string by picking characters from the dictionary variable. + * @param {number} number Character to select. + * @returns {string} string Generated string based on the input number. + * @private + */ + static _get26Num(number){ + let result = ''; + + while(number > 0) { + result += Dictionary.charAt(number % Dictionary.length); + number = parseInt(number / Dictionary.length); + } + + return result; + } + + /** + * Generate unique account key for the transaction. + * @returns {string} The account key. + * @private + */ + _generateAccount() { + // choose a random TX/account index based on the existing range, and restore the account name from the fragments + return this.roundArguments.accountPhasePrefix + this.accountPrefix + SimpleTransferWorkload._get26Num(Math.floor(Math.random() * this.numberOfAccountsPerWorker) + 1); + } + + /** + * Initialize the workload module with the given parameters. + * @param {number} workerIndex The 0-based index of the worker instantiating the workload module. + * @param {number} totalWorkers The total number of workers participating in the round. + * @param {number} roundIndex The 0-based index of the currently executing round. + * @param {Object} roundArguments The user-provided arguments for the round from the benchmark configuration file. + * @param {BlockchainInterface} sutAdapter The adapter of the underlying SUT. + * @param {Object} sutContext The custom context object provided by the SUT adapter. + * @async + */ + async initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext) { + await super.initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext); + + if(!this.roundArguments.money) { + throw new Error('simple.transfer - the "money" argument is missing'); + } + + if(!this.roundArguments.numberOfAccounts) { + throw new Error('simple.transfer - the "numberOfAccounts" argument is missing'); + } + + if(!this.roundArguments.accountPhasePrefix) { + throw new Error('simple.open - the "accountPhasePrefix" argument is missing'); + } + + this.accountPrefix = SimpleTransferWorkload._get26Num(workerIndex); + this.numberOfAccountsPerWorker = this.roundArguments.numberOfAccounts / this.totalWorkers; + } + + /** + * Assemble TXs for transferring money. + * @return {Promise} + */ + async submitTransaction() { + const args = { verb: 'transfer', - args: [account1, account2, initmoney] - }; - } else { - args = { - 'verb': 'transfer', - 'account1': account1, - 'account2': account2, - 'money': initmoney.toString() + args: [this._generateAccount(), this._generateAccount(), this.roundArguments.money.toString()] }; - } - return bc.invokeSmartContract(contx, 'simple', 'v0', args, 10); + Logger.debug(`Worker ${this.workerIndex} TX parameters: ${JSON.stringify(args)}`); + return this.sutAdapter.invokeSmartContract(this.sutContext, 'simple', 'v0', args, 100); + } +} -}; +/** + * Create a new instance of the workload module. + * @return {WorkloadModuleInterface} + */ +function createWorkloadModule() { + return new SimpleTransferWorkload(); +} -module.exports.end = function () { - // do nothing - return Promise.resolve(); -}; +module.exports.createWorkloadModule = createWorkloadModule; diff --git a/packages/caliper-tests-integration/fabric_docker_distributed_tests/init.js b/packages/caliper-tests-integration/fabric_docker_distributed_tests/init.js index 56139eea1..c6b0bd5f0 100644 --- a/packages/caliper-tests-integration/fabric_docker_distributed_tests/init.js +++ b/packages/caliper-tests-integration/fabric_docker_distributed_tests/init.js @@ -14,34 +14,50 @@ 'use strict'; -const uuidv4 = require('uuid/v4'); - -module.exports.info = 'Creating marbles.'; - -let txIndex = 0; -let colors = ['red', 'blue', 'green', 'black', 'white', 'pink', 'rainbow']; -let owners = ['Alice', 'Bob', 'Claire', 'David']; -let bc, contx; - -module.exports.init = async function(blockchain, context, args) { - bc = blockchain; - contx = context; -}; - -module.exports.run = async function() { - txIndex++; - let marbleName = 'marble_' + txIndex.toString() + '_' + uuidv4(); - let marbleColor = colors[txIndex % colors.length]; - let marbleSize = (((txIndex % 10) + 1) * 10).toString(); // [10, 100] - let marbleOwner = owners[txIndex % owners.length]; - - let args = { - chaincodeFunction: 'initMarble', - chaincodeArguments: [marbleName, marbleColor, marbleSize, marbleOwner], - }; - - let targetCC = txIndex % 2 === 0 ? 'mymarbles' : 'yourmarbles'; - return bc.invokeSmartContract(contx, targetCC, 'v0', args, 5); -}; - -module.exports.end = async function() {}; +const { WorkloadModuleBase } = require('@hyperledger/caliper-core'); + +/** + * Workload module for initializing the SUT with various marbles. + */ +class MarblesInitWorkload extends WorkloadModuleBase { + /** + * Initializes the parameters of the marbles workload. + */ + constructor() { + super(); + this.txIndex = -1; + this.colors = ['red', 'blue', 'green', 'black', 'white', 'pink', 'rainbow']; + this.owners = ['Alice', 'Bob', 'Claire', 'David']; + } + + /** + * Assemble TXs for creating new marbles. + * @return {Promise} + */ + async submitTransaction() { + this.txIndex++; + + const marbleName = `marbles_${this.roundIndex}_${this.workerIndex}_${this.txIndex}`; + let marbleColor = this.colors[this.txIndex % this.colors.length]; + let marbleSize = (((this.txIndex % 10) + 1) * 10).toString(); // [10, 100] + let marbleOwner = this.owners[this.txIndex % this.owners.length]; + + let args = { + chaincodeFunction: 'initMarble', + chaincodeArguments: [marbleName, marbleColor, marbleSize, marbleOwner], + }; + + let targetCC = this.txIndex % 2 === 0 ? 'mymarbles' : 'yourmarbles'; + return this.sutAdapter.invokeSmartContract(this.sutContext, targetCC, 'v0', args, 5); + } +} + +/** + * Create a new instance of the workload module. + * @return {WorkloadModuleInterface} + */ +function createWorkloadModule() { + return new MarblesInitWorkload(); +} + +module.exports.createWorkloadModule = createWorkloadModule; diff --git a/packages/caliper-tests-integration/fabric_docker_distributed_tests/query.js b/packages/caliper-tests-integration/fabric_docker_distributed_tests/query.js index 1d084d354..d949970a2 100644 --- a/packages/caliper-tests-integration/fabric_docker_distributed_tests/query.js +++ b/packages/caliper-tests-integration/fabric_docker_distributed_tests/query.js @@ -14,27 +14,45 @@ 'use strict'; -module.exports.info = 'Querying marbles.'; - -let txIndex = 0; -let owners = ['Alice', 'Bob', 'Claire', 'David']; -let bc, contx; - -module.exports.init = async function(blockchain, context, args) { - bc = blockchain; - contx = context; -}; - -module.exports.run = async function() { - txIndex++; - let marbleOwner = owners[txIndex % owners.length]; - let args = { - chaincodeFunction: 'queryMarblesByOwner', - chaincodeArguments: [marbleOwner] - }; - - let targetCC = txIndex % 2 === 0 ? 'mymarbles' : 'yourmarbles'; - return bc.querySmartContract(contx, targetCC, '', args, 10); -}; - -module.exports.end = async function() {}; +const { WorkloadModuleBase } = require('@hyperledger/caliper-core'); + +/** + * Workload module for querying the SUT for various marbles. + */ +class MarblesQueryWorkload extends WorkloadModuleBase { + /** + * Initializes the parameters of the marbles workload. + */ + constructor() { + super(); + this.txIndex = -1; + this.owners = ['Alice', 'Bob', 'Claire', 'David']; + } + + /** + * Assemble TXs for querying existing marbles based on their owners. + * @return {Promise} + */ + async submitTransaction() { + this.txIndex++; + let marbleOwner = this.owners[this.txIndex % this.owners.length]; + + let args = { + chaincodeFunction: 'queryMarblesByOwner', + chaincodeArguments: [marbleOwner], + }; + + let targetCC = this.txIndex % 2 === 0 ? 'mymarbles' : 'yourmarbles'; + return this.sutAdapter.querySmartContract(this.sutContext, targetCC, 'v0', args, 10); + } +} + +/** + * Create a new instance of the workload module. + * @return {WorkloadModuleInterface} + */ +function createWorkloadModule() { + return new MarblesQueryWorkload(); +} + +module.exports.createWorkloadModule = createWorkloadModule; diff --git a/packages/caliper-tests-integration/fabric_docker_local_tests/init.js b/packages/caliper-tests-integration/fabric_docker_local_tests/init.js index 56139eea1..c6b0bd5f0 100644 --- a/packages/caliper-tests-integration/fabric_docker_local_tests/init.js +++ b/packages/caliper-tests-integration/fabric_docker_local_tests/init.js @@ -14,34 +14,50 @@ 'use strict'; -const uuidv4 = require('uuid/v4'); - -module.exports.info = 'Creating marbles.'; - -let txIndex = 0; -let colors = ['red', 'blue', 'green', 'black', 'white', 'pink', 'rainbow']; -let owners = ['Alice', 'Bob', 'Claire', 'David']; -let bc, contx; - -module.exports.init = async function(blockchain, context, args) { - bc = blockchain; - contx = context; -}; - -module.exports.run = async function() { - txIndex++; - let marbleName = 'marble_' + txIndex.toString() + '_' + uuidv4(); - let marbleColor = colors[txIndex % colors.length]; - let marbleSize = (((txIndex % 10) + 1) * 10).toString(); // [10, 100] - let marbleOwner = owners[txIndex % owners.length]; - - let args = { - chaincodeFunction: 'initMarble', - chaincodeArguments: [marbleName, marbleColor, marbleSize, marbleOwner], - }; - - let targetCC = txIndex % 2 === 0 ? 'mymarbles' : 'yourmarbles'; - return bc.invokeSmartContract(contx, targetCC, 'v0', args, 5); -}; - -module.exports.end = async function() {}; +const { WorkloadModuleBase } = require('@hyperledger/caliper-core'); + +/** + * Workload module for initializing the SUT with various marbles. + */ +class MarblesInitWorkload extends WorkloadModuleBase { + /** + * Initializes the parameters of the marbles workload. + */ + constructor() { + super(); + this.txIndex = -1; + this.colors = ['red', 'blue', 'green', 'black', 'white', 'pink', 'rainbow']; + this.owners = ['Alice', 'Bob', 'Claire', 'David']; + } + + /** + * Assemble TXs for creating new marbles. + * @return {Promise} + */ + async submitTransaction() { + this.txIndex++; + + const marbleName = `marbles_${this.roundIndex}_${this.workerIndex}_${this.txIndex}`; + let marbleColor = this.colors[this.txIndex % this.colors.length]; + let marbleSize = (((this.txIndex % 10) + 1) * 10).toString(); // [10, 100] + let marbleOwner = this.owners[this.txIndex % this.owners.length]; + + let args = { + chaincodeFunction: 'initMarble', + chaincodeArguments: [marbleName, marbleColor, marbleSize, marbleOwner], + }; + + let targetCC = this.txIndex % 2 === 0 ? 'mymarbles' : 'yourmarbles'; + return this.sutAdapter.invokeSmartContract(this.sutContext, targetCC, 'v0', args, 5); + } +} + +/** + * Create a new instance of the workload module. + * @return {WorkloadModuleInterface} + */ +function createWorkloadModule() { + return new MarblesInitWorkload(); +} + +module.exports.createWorkloadModule = createWorkloadModule; diff --git a/packages/caliper-tests-integration/fabric_docker_local_tests/query.js b/packages/caliper-tests-integration/fabric_docker_local_tests/query.js index 5ca6b09c3..d949970a2 100644 --- a/packages/caliper-tests-integration/fabric_docker_local_tests/query.js +++ b/packages/caliper-tests-integration/fabric_docker_local_tests/query.js @@ -14,27 +14,45 @@ 'use strict'; -module.exports.info = 'Querying marbles.'; - -let txIndex = 0; -let owners = ['Alice', 'Bob', 'Claire', 'David']; -let bc, contx; - -module.exports.init = async function(blockchain, context, args) { - bc = blockchain; - contx = context; -}; - -module.exports.run = async function() { - txIndex++; - let marbleOwner = owners[txIndex % owners.length]; - let args = { - chaincodeFunction: 'queryMarblesByOwner', - chaincodeArguments: [marbleOwner] - }; - - let targetCC = txIndex % 2 === 0 ? 'mymarbles' : 'yourmarbles'; - return bc.querySmartContract(contx, targetCC, '', args, 20); -}; - -module.exports.end = async function() {}; +const { WorkloadModuleBase } = require('@hyperledger/caliper-core'); + +/** + * Workload module for querying the SUT for various marbles. + */ +class MarblesQueryWorkload extends WorkloadModuleBase { + /** + * Initializes the parameters of the marbles workload. + */ + constructor() { + super(); + this.txIndex = -1; + this.owners = ['Alice', 'Bob', 'Claire', 'David']; + } + + /** + * Assemble TXs for querying existing marbles based on their owners. + * @return {Promise} + */ + async submitTransaction() { + this.txIndex++; + let marbleOwner = this.owners[this.txIndex % this.owners.length]; + + let args = { + chaincodeFunction: 'queryMarblesByOwner', + chaincodeArguments: [marbleOwner], + }; + + let targetCC = this.txIndex % 2 === 0 ? 'mymarbles' : 'yourmarbles'; + return this.sutAdapter.querySmartContract(this.sutContext, targetCC, 'v0', args, 10); + } +} + +/** + * Create a new instance of the workload module. + * @return {WorkloadModuleInterface} + */ +function createWorkloadModule() { + return new MarblesQueryWorkload(); +} + +module.exports.createWorkloadModule = createWorkloadModule; diff --git a/packages/caliper-tests-integration/fabric_tests/caliper.yaml b/packages/caliper-tests-integration/fabric_tests/caliper.yaml index 3585171f7..99e2e41a8 100644 --- a/packages/caliper-tests-integration/fabric_tests/caliper.yaml +++ b/packages/caliper-tests-integration/fabric_tests/caliper.yaml @@ -15,6 +15,7 @@ caliper: benchconfig: benchconfig.yaml networkconfig: networkconfig.yaml + txupdatetime: 1000 report: path: ../report.html logging: diff --git a/packages/caliper-tests-integration/fabric_tests/init.js b/packages/caliper-tests-integration/fabric_tests/init.js index 1949c2a93..011905787 100644 --- a/packages/caliper-tests-integration/fabric_tests/init.js +++ b/packages/caliper-tests-integration/fabric_tests/init.js @@ -14,34 +14,68 @@ 'use strict'; +const { WorkloadModuleBase } = require('@hyperledger/caliper-core'); +/** + * Workload module for initializing the SUT with various marbles. + */ +class MarblesInitWorkload extends WorkloadModuleBase { + /** + * Initializes the parameters of the marbles workload. + */ + constructor() { + super(); + this.txIndex = -1; + this.colors = ['red', 'blue', 'green', 'black', 'white', 'pink', 'rainbow']; + this.owners = ['Alice', 'Bob', 'Claire', 'David']; + } -module.exports.info = 'Creating marbles.'; + /** + * Initialize the workload module with the given parameters. + * @param {number} workerIndex The 0-based index of the worker instantiating the workload module. + * @param {number} totalWorkers The total number of workers participating in the round. + * @param {number} roundIndex The 0-based index of the currently executing round. + * @param {Object} roundArguments The user-provided arguments for the round from the benchmark configuration file. + * @param {BlockchainInterface} sutAdapter The adapter of the underlying SUT. + * @param {Object} sutContext The custom context object provided by the SUT adapter. + * @async + */ + async initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext) { + await super.initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext); -let txIndex = 0; -let colors = ['red', 'blue', 'green', 'black', 'white', 'pink', 'rainbow']; -let owners = ['Alice', 'Bob', 'Claire', 'David']; -let bc, contx; + if (!this.roundArguments.marblePrefix) { + throw new Error(`Argument "marblePrefix" is missing from benchmark configuration`); + } + } -module.exports.init = async function(blockchain, context, args) { - bc = blockchain; - contx = context; -}; + /** + * Assemble TXs for creating new marbles. + * @return {Promise} + */ + async submitTransaction() { + this.txIndex++; -module.exports.run = async function() { - txIndex++; - let marbleName = 'marble_' + txIndex.toString() + '_' + process.pid.toString(); - let marbleColor = colors[txIndex % colors.length]; - let marbleSize = (((txIndex % 10) + 1) * 10).toString(); // [10, 100] - let marbleOwner = owners[txIndex % owners.length]; + const marbleName = `${this.roundArguments.marblePrefix}_${this.roundIndex}_${this.workerIndex}_${this.txIndex}`; + let marbleColor = this.colors[this.txIndex % this.colors.length]; + let marbleSize = (((this.txIndex % 10) + 1) * 10).toString(); // [10, 100] + let marbleOwner = this.owners[this.txIndex % this.owners.length]; - let args = { - chaincodeFunction: 'initMarble', - chaincodeArguments: [marbleName, marbleColor, marbleSize, marbleOwner], - }; + let args = { + chaincodeFunction: 'initMarble', + chaincodeArguments: [marbleName, marbleColor, marbleSize, marbleOwner], + }; - let targetCC = txIndex % 2 === 0 ? 'mymarbles' : 'yourmarbles'; - return bc.invokeSmartContract(contx, targetCC, 'v0', args, 5); -}; + let targetCC = this.txIndex % 2 === 0 ? 'mymarbles' : 'yourmarbles'; + return this.sutAdapter.invokeSmartContract(this.sutContext, targetCC, 'v0', args, 5); + } +} -module.exports.end = async function() {}; +/** + * Create a new instance of the workload module. + * @return {WorkloadModuleInterface} + */ +function createWorkloadModule() { + return new MarblesInitWorkload(); +} + +module.exports.createWorkloadModule = createWorkloadModule; diff --git a/packages/caliper-tests-integration/fabric_tests/phase1/benchconfig.yaml b/packages/caliper-tests-integration/fabric_tests/phase1/benchconfig.yaml index a9555e37c..50bdbeefc 100644 --- a/packages/caliper-tests-integration/fabric_tests/phase1/benchconfig.yaml +++ b/packages/caliper-tests-integration/fabric_tests/phase1/benchconfig.yaml @@ -21,12 +21,12 @@ test: - label: init1 txNumber: 100 rateControl: { type: 'fixed-rate', opts: { tps: 20 } } - callback: ../init.js + callback: ./../init.js - label: init2 txNumber: 200 rateControl: { type: 'fixed-feedback-rate', opts: { tps: 20, unfinished_per_client: 5 } } - callback: ../init.js + callback: ./../init.js - label: query txNumber: 100 rateControl: { type: 'linear-rate', opts: { startingTps: 10, finishingTps: 20 } } - callback: ../query.js + callback: ./../query.js diff --git a/packages/caliper-tests-integration/fabric_tests/phase2/benchconfig.yaml b/packages/caliper-tests-integration/fabric_tests/phase2/benchconfig.yaml index 028b27009..614a8ad7a 100644 --- a/packages/caliper-tests-integration/fabric_tests/phase2/benchconfig.yaml +++ b/packages/caliper-tests-integration/fabric_tests/phase2/benchconfig.yaml @@ -21,15 +21,15 @@ test: - label: init1 txNumber: 100 rateControl: { type: 'fixed-rate', opts: { tps: 20 } } - callback: ../init.js + callback: ./../init.js - label: init2 txNumber: 200 rateControl: { type: 'fixed-feedback-rate', opts: { tps: 20, unfinished_per_client: 5 } } - callback: ../init.js + callback: ./../init.js - label: query txNumber: 100 rateControl: { type: 'linear-rate', opts: { startingTps: 10, finishingTps: 20 } } - callback: ../query.js + callback: ./../query.js observer: interval: 1 type: local diff --git a/packages/caliper-tests-integration/fabric_tests/phase3/benchconfig.yaml b/packages/caliper-tests-integration/fabric_tests/phase3/benchconfig.yaml index 30398a2c0..c173a520e 100644 --- a/packages/caliper-tests-integration/fabric_tests/phase3/benchconfig.yaml +++ b/packages/caliper-tests-integration/fabric_tests/phase3/benchconfig.yaml @@ -21,15 +21,15 @@ test: - label: init1 txNumber: 100 rateControl: { type: 'fixed-rate', opts: { tps: 20 } } - callback: ../init.js + callback: ./../init.js - label: init2 txNumber: 200 rateControl: { type: 'fixed-feedback-rate', opts: { tps: 20, unfinished_per_client: 5 } } - callback: ../init.js + callback: ./../init.js - label: query txNumber: 100 rateControl: { type: 'linear-rate', opts: { startingTps: 10, finishingTps: 20 } } - callback: ../query.js + callback: ./../query.js observer: interval: 1 type: local diff --git a/packages/caliper-tests-integration/fabric_tests/phase4/benchconfig.yaml b/packages/caliper-tests-integration/fabric_tests/phase4/benchconfig.yaml index 0d3c71f3a..15c167023 100644 --- a/packages/caliper-tests-integration/fabric_tests/phase4/benchconfig.yaml +++ b/packages/caliper-tests-integration/fabric_tests/phase4/benchconfig.yaml @@ -21,11 +21,13 @@ test: - label: init1 txNumber: 50 rateControl: { type: 'fixed-rate', opts: { tps: 5 } } - callback: ../init.js + callback: ./../init.js + arguments: + marblePrefix: marbles_phase_4 - label: query txNumber: 50 rateControl: { type: 'linear-rate', opts: { startingTps: 5, finishingTps: 10 } } - callback: ../query.js + callback: ./../query.js observer: interval: 1 type: prometheus diff --git a/packages/caliper-tests-integration/fabric_tests/phase5/benchconfig.yaml b/packages/caliper-tests-integration/fabric_tests/phase5/benchconfig.yaml index 64b8ac482..73f545e68 100644 --- a/packages/caliper-tests-integration/fabric_tests/phase5/benchconfig.yaml +++ b/packages/caliper-tests-integration/fabric_tests/phase5/benchconfig.yaml @@ -21,11 +21,13 @@ test: - label: init1 txNumber: 25 rateControl: { type: 'fixed-rate', opts: { tps: 5 } } - callback: ../init.js + callback: ./../init.js + arguments: + marblePrefix: marbles_phase_5 - label: query txNumber: 25 rateControl: { type: 'linear-rate', opts: { startingTps: 5, finishingTps: 10 } } - callback: ../query.js + callback: ./../query.js observer: interval: 1 type: prometheus diff --git a/packages/caliper-tests-integration/fabric_tests/phase6/benchconfig.yaml b/packages/caliper-tests-integration/fabric_tests/phase6/benchconfig.yaml index 028b27009..c4bbc68b8 100644 --- a/packages/caliper-tests-integration/fabric_tests/phase6/benchconfig.yaml +++ b/packages/caliper-tests-integration/fabric_tests/phase6/benchconfig.yaml @@ -19,22 +19,40 @@ test: number: 2 rounds: - label: init1 - txNumber: 100 - rateControl: { type: 'fixed-rate', opts: { tps: 20 } } - callback: ../init.js - - label: init2 - txNumber: 200 - rateControl: { type: 'fixed-feedback-rate', opts: { tps: 20, unfinished_per_client: 5 } } - callback: ../init.js + txNumber: 25 + rateControl: { type: 'fixed-rate', opts: { tps: 5 } } + callback: ./../init.js + arguments: + marblePrefix: marbles_phase_6 - label: query - txNumber: 100 - rateControl: { type: 'linear-rate', opts: { startingTps: 10, finishingTps: 20 } } - callback: ../query.js + txNumber: 25 + rateControl: { type: 'linear-rate', opts: { startingTps: 5, finishingTps: 10 } } + callback: ./../query.js observer: interval: 1 - type: local + type: prometheus monitor: interval: 1 - type: ['process'] - process: - processes: [{ command: 'node', arguments: 'fabricClientWorker.js', multiOutput: 'avg' }] + type: ['prometheus'] + prometheus: + url: "http://localhost:9090" + push_url: "http://localhost:9091" + metrics: + ignore: [prometheus, pushGateway, cadvisor, grafana, node-exporter] + include: + Endorse Time (s): + query: rate(endorser_propsal_duration_sum{chaincode="marbles:v0"}[1m])/rate(endorser_propsal_duration_count{chaincode="marbles:v0"}[1m]) + step: 1 + label: instance + statistic: avg + Max Memory (MB): + query: sum(container_memory_rss{name=~".+"}) by (name) + step: 10 + label: name + statistic: max + multiplier: 0.000001 + charting: + polar: + metrics: [Max Memory (MB)] + bar: + metrics: [all] diff --git a/packages/caliper-tests-integration/fabric_tests/phase6/networkconfig.yaml b/packages/caliper-tests-integration/fabric_tests/phase6/networkconfig.yaml index bc9bb5823..fb2f075a8 100644 --- a/packages/caliper-tests-integration/fabric_tests/phase6/networkconfig.yaml +++ b/packages/caliper-tests-integration/fabric_tests/phase6/networkconfig.yaml @@ -14,8 +14,145 @@ name: Fabric version: "1.0" +mutual-tls: true caliper: blockchain: fabric - command: - end: docker-compose -p caliper down;(test -z \"$(docker ps -aq)\") || docker rm $(docker ps -aq);(test -z \"$(docker images dev* -q)\") || docker rmi $(docker images dev* -q);rm -rf /tmp/hfc-* + +clients: + client0.org1.example.com: + client: + organization: Org1 + credentialStore: + path: /tmp/hfc-kvs/org1 + cryptoStore: + path: /tmp/hfc-cvs/org1 + clientPrivateKey: + path: ../config/crypto-config/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/keystore/key.pem + clientSignedCert: + path: ../config/crypto-config/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/signcerts/User1@org1.example.com-cert.pem + + client0.org2.example.com: + client: + organization: Org2 + credentialStore: + path: /tmp/hfc-kvs/org2 + cryptoStore: + path: /tmp/hfc-cvs/org2 + clientPrivateKey: + path: ../config/crypto-config/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp/keystore/key.pem + clientSignedCert: + path: ../config/crypto-config/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp/signcerts/User1@org2.example.com-cert.pem + +channels: + mychannel: + created: true + orderers: + - orderer0.example.com + - orderer1.example.com + peers: + peer0.org1.example.com: + eventSource: true + peer0.org2.example.com: + eventSource: true + + chaincodes: + - id: marbles + contractID: mymarbles + version: v0 + language: node + path: src/marbles/node + metadataPath: src/marbles/node/metadata + yourchannel: + created: true + orderers: + - orderer0.example.com + - orderer1.example.com + peers: + peer0.org1.example.com: + eventSource: true + peer0.org2.example.com: + eventSource: true + + chaincodes: + - id: marbles + contractID: yourmarbles + version: v0 + language: golang + path: marbles/go + metadataPath: src/marbles/go/metadata + +organizations: + Org1: + mspid: Org1MSP + peers: + - peer0.org1.example.com + certificateAuthorities: + - ca.org1.example.com + adminPrivateKey: + path: ../config/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/keystore/key.pem + signedCert: + path: ../config/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts/Admin@org1.example.com-cert.pem + + Org2: + mspid: Org2MSP + peers: + - peer0.org2.example.com + certificateAuthorities: + - ca.org2.example.com + adminPrivateKey: + path: ../config/crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp/keystore/key.pem + signedCert: + path: ../config/crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp/signcerts/Admin@org2.example.com-cert.pem + +orderers: + orderer0.example.com: + url: grpcs://localhost:7050 + grpcOptions: + ssl-target-name-override: orderer0.example.com + tlsCACerts: + path: ../config/crypto-config/ordererOrganizations/example.com/orderers/orderer0.example.com/msp/tlscacerts/tlsca.example.com-cert.pem + orderer1.example.com: + url: grpcs://localhost:8050 + grpcOptions: + ssl-target-name-override: orderer1.example.com + tlsCACerts: + path: ../config/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/msp/tlscacerts/tlsca.example.com-cert.pem + +peers: + peer0.org1.example.com: + url: grpcs://localhost:7051 + grpcOptions: + ssl-target-name-override: peer0.org1.example.com + grpc.keepalive_time_ms: 600000 + tlsCACerts: + path: ../config/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp/tlscacerts/tlsca.org1.example.com-cert.pem + + peer0.org2.example.com: + url: grpcs://localhost:8051 + grpcOptions: + ssl-target-name-override: peer0.org2.example.com + grpc.keepalive_time_ms: 600000 + tlsCACerts: + path: ../config/crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/msp/tlscacerts/tlsca.org2.example.com-cert.pem + +certificateAuthorities: + ca.org1.example.com: + url: https://localhost:7054 + httpOptions: + verify: false + tlsCACerts: + path: ../config/crypto-config/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem + registrar: + - enrollId: admin + enrollSecret: adminpw + + ca.org2.example.com: + url: https://localhost:8054 + httpOptions: + verify: false + tlsCACerts: + path: ../config/crypto-config/peerOrganizations/org2.example.com/tlsca/tlsca.org2.example.com-cert.pem + registrar: + - enrollId: admin + enrollSecret: adminpw diff --git a/packages/caliper-tests-integration/fabric_tests/phase7/benchconfig.yaml b/packages/caliper-tests-integration/fabric_tests/phase7/benchconfig.yaml new file mode 100644 index 000000000..725730052 --- /dev/null +++ b/packages/caliper-tests-integration/fabric_tests/phase7/benchconfig.yaml @@ -0,0 +1,44 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +--- +test: + workers: + type: local + number: 2 + rounds: + - label: init1 + txNumber: 100 + rateControl: { type: 'fixed-rate', opts: { tps: 20 } } + callback: ./../init.js + arguments: + marblePrefix: marbles_phase_5 + - label: init2 + txNumber: 200 + rateControl: { type: 'fixed-feedback-rate', opts: { tps: 20, unfinished_per_client: 5 } } + callback: ./../init.js + arguments: + marblePrefix: marbles_phase_5 + - label: query + txNumber: 100 + rateControl: { type: 'linear-rate', opts: { startingTps: 10, finishingTps: 20 } } + callback: ./../query.js +observer: + interval: 1 + type: local +monitor: + interval: 1 + type: ['process'] + process: + processes: [{ command: 'node', arguments: 'fabricClientWorker.js', multiOutput: 'avg' }] diff --git a/packages/caliper-tests-integration/fabric_tests/phase7/networkconfig.yaml b/packages/caliper-tests-integration/fabric_tests/phase7/networkconfig.yaml new file mode 100644 index 000000000..bc9bb5823 --- /dev/null +++ b/packages/caliper-tests-integration/fabric_tests/phase7/networkconfig.yaml @@ -0,0 +1,21 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name: Fabric +version: "1.0" + +caliper: + blockchain: fabric + command: + end: docker-compose -p caliper down;(test -z \"$(docker ps -aq)\") || docker rm $(docker ps -aq);(test -z \"$(docker images dev* -q)\") || docker rmi $(docker images dev* -q);rm -rf /tmp/hfc-* diff --git a/packages/caliper-tests-integration/fabric_tests/query.js b/packages/caliper-tests-integration/fabric_tests/query.js index d98c9cf46..26add3497 100644 --- a/packages/caliper-tests-integration/fabric_tests/query.js +++ b/packages/caliper-tests-integration/fabric_tests/query.js @@ -14,28 +14,46 @@ 'use strict'; -module.exports.info = 'Querying marbles.'; - -let txIndex = 0; -let owners = ['Alice', 'Bob', 'Claire', 'David']; -let bc, contx; - -module.exports.init = async function(blockchain, context, args) { - bc = blockchain; - contx = context; -}; - -module.exports.run = async function() { - txIndex++; - let marbleOwner = owners[txIndex % owners.length]; - let args = { - chaincodeFunction: 'queryMarblesByOwner', - chaincodeArguments: [marbleOwner], - targetPeers: ['peer0.org1.example.com'] - }; - - let targetCC = txIndex % 2 === 0 ? 'mymarbles' : 'yourmarbles'; - return bc.querySmartContract(contx, targetCC, '', args, 10); -}; - -module.exports.end = async function() {}; +const { WorkloadModuleBase } = require('@hyperledger/caliper-core'); + +/** + * Workload module for querying the SUT for various marbles. + */ +class MarblesQueryWorkload extends WorkloadModuleBase { + /** + * Initializes the parameters of the marbles workload. + */ + constructor() { + super(); + this.txIndex = -1; + this.owners = ['Alice', 'Bob', 'Claire', 'David']; + } + + /** + * Assemble TXs for querying existing marbles based on their owners. + * @return {Promise} + */ + async submitTransaction() { + this.txIndex++; + let marbleOwner = this.owners[this.txIndex % this.owners.length]; + + let args = { + chaincodeFunction: 'queryMarblesByOwner', + chaincodeArguments: [marbleOwner], + targetPeers: ['peer0.org1.example.com'] + }; + + let targetCC = this.txIndex % 2 === 0 ? 'mymarbles' : 'yourmarbles'; + return this.sutAdapter.querySmartContract(this.sutContext, targetCC, 'v0', args, 10); + } +} + +/** + * Create a new instance of the workload module. + * @return {WorkloadModuleInterface} + */ +function createWorkloadModule() { + return new MarblesQueryWorkload(); +} + +module.exports.createWorkloadModule = createWorkloadModule; diff --git a/packages/caliper-tests-integration/fabric_tests/run.sh b/packages/caliper-tests-integration/fabric_tests/run.sh index e320591e4..7107a9937 100755 --- a/packages/caliper-tests-integration/fabric_tests/run.sh +++ b/packages/caliper-tests-integration/fabric_tests/run.sh @@ -107,7 +107,7 @@ if [[ "${BIND_IN_PACKAGE_DIR}" = "true" ]]; then fi # PHASE 6: testing through the gateway API (v2 SDK) -${CALL_METHOD} launch master --caliper-workspace phase5 --caliper-flow-only-test --caliper-fabric-gateway-enabled +${CALL_METHOD} launch master --caliper-workspace phase6 --caliper-flow-only-test --caliper-fabric-gateway-enabled rc=$? if [[ ${rc} != 0 ]]; then echo "Failed CI step 7"; @@ -116,7 +116,7 @@ if [[ ${rc} != 0 ]]; then fi # PHASE 7: just disposing of the network -${CALL_METHOD} launch master --caliper-workspace phase6 --caliper-flow-only-end --caliper-fabric-gateway-enabled +${CALL_METHOD} launch master --caliper-workspace phase7 --caliper-flow-only-end --caliper-fabric-gateway-enabled rc=$? if [[ ${rc} != 0 ]]; then echo "Failed CI step 8"; diff --git a/packages/caliper-tests-integration/fisco-bcos_tests/benchconfig.yaml b/packages/caliper-tests-integration/fisco-bcos_tests/benchconfig.yaml index a42731757..e838ef5fb 100644 --- a/packages/caliper-tests-integration/fisco-bcos_tests/benchconfig.yaml +++ b/packages/caliper-tests-integration/fisco-bcos_tests/benchconfig.yaml @@ -21,11 +21,11 @@ test: - label: set txNumber: 1000 rateControl: { type: 'fixed-rate', opts: { tps: 100 } } - callback: set.js + callback: ./set.js - label: get txNumber: 1000 rateControl: { type: 'linear-rate', opts: { startingTps: 100, finishingTps: 200 } } - callback: get.js + callback: ./get.js observer: interval: 1 type: local diff --git a/packages/caliper-tests-integration/fisco-bcos_tests/get.js b/packages/caliper-tests-integration/fisco-bcos_tests/get.js index 088653804..33bb04135 100644 --- a/packages/caliper-tests-integration/fisco-bcos_tests/get.js +++ b/packages/caliper-tests-integration/fisco-bcos_tests/get.js @@ -14,22 +14,27 @@ 'use strict'; -module.exports.info = ' querying name'; +const { WorkloadModuleBase } = require('@hyperledger/caliper-core'); -let bc, contx; +/** + * Workload module for simple get operations. + */ +class HelloGetWorkload extends WorkloadModuleBase { + /** + * Assemble TXs for set operation. + * @return {Promise} + */ + async submitTransaction() { + return this.sutAdapter.queryState(this.sutContext, 'helloworld', 'v0', null, 'get()'); + } +} -module.exports.init = function (blockchain, context, args) { - // Do nothing - bc = blockchain; - contx = context; - return Promise.resolve(); -}; +/** + * Create a new instance of the workload module. + * @return {WorkloadModuleInterface} + */ +function createWorkloadModule() { + return new HelloGetWorkload(); +} -module.exports.run = function () { - return bc.queryState(contx, 'helloworld', 'v0', null, 'get()'); -}; - -module.exports.end = function () { - // Do nothing - return Promise.resolve(); -}; +module.exports.createWorkloadModule = createWorkloadModule; diff --git a/packages/caliper-tests-integration/fisco-bcos_tests/set.js b/packages/caliper-tests-integration/fisco-bcos_tests/set.js index fbb3ef35f..9a3a1b86e 100644 --- a/packages/caliper-tests-integration/fisco-bcos_tests/set.js +++ b/packages/caliper-tests-integration/fisco-bcos_tests/set.js @@ -14,40 +14,31 @@ 'use strict'; -module.exports.info = ' setting name'; - -let bc, contx; -let txnPerBatch; - -module.exports.init = function (blockchain, context, args) { - txnPerBatch = 1; - bc = blockchain; - contx = context; - return Promise.resolve(); -}; +const { WorkloadModuleBase } = require('@hyperledger/caliper-core'); /** - * Generates simple workload - * @return {Object} array of json objects + * Workload module for simple set operations. */ -function generateWorkload() { - let workload = []; - for (let i = 0; i < txnPerBatch; i++) { - let w = { +class HelloSetWorkload extends WorkloadModuleBase { + /** + * Assemble TXs for set operation. + * @return {Promise} + */ + async submitTransaction() { + const args = { 'transaction_type': 'set(string)', - 'name': 'hello! - from ' + process.pid.toString(), + 'name': 'hello! - from ' + this.workerIndex.toString(), }; - workload.push(w); + return this.sutAdapter.invokeSmartContract(this.sutContext, 'helloworld', 'v0', args, null); } - return workload; } -module.exports.run = function () { - let args = generateWorkload(); - return bc.invokeSmartContract(contx, 'helloworld', 'v0', args, null); -}; +/** + * Create a new instance of the workload module. + * @return {WorkloadModuleInterface} + */ +function createWorkloadModule() { + return new HelloSetWorkload(); +} -module.exports.end = function () { - // Do nothing - return Promise.resolve(); -}; +module.exports.createWorkloadModule = createWorkloadModule; diff --git a/packages/caliper-tests-integration/iroha_tests/benchconfig.yaml b/packages/caliper-tests-integration/iroha_tests/benchconfig.yaml index 158d77fab..8a6cc24e0 100644 --- a/packages/caliper-tests-integration/iroha_tests/benchconfig.yaml +++ b/packages/caliper-tests-integration/iroha_tests/benchconfig.yaml @@ -13,28 +13,34 @@ # --- +params: + numberOfAccounts: &accounts 100 + test: workers: type: local number: 2 rounds: - label: create - txNumber: 100 + txNumber: *accounts rateControl: { type: 'fixed-rate', opts: { tps: 10 } } arguments: txnPerBatch: 10 - callback: create.js + callback: ./create.js - label: query txNumber: 200 rateControl: { type: 'fixed-feedback-rate', opts: { tps: 10, unfinished_per_client: 5 } } - callback: query.js + callback: ./query.js + arguments: + numberOfAccounts: *accounts - label: transfer txNumber: 200 rateControl: { type: 'linear-rate', opts: { startingTps: 5, finishingTps: 20 } } arguments: txnPerBatch: 10 + numberOfAccounts: *accounts money: '10.00' - callback: transfer.js + callback: ./transfer.js observer: interval: 1 type: local diff --git a/packages/caliper-tests-integration/iroha_tests/create.js b/packages/caliper-tests-integration/iroha_tests/create.js index b1b31e7c4..577947415 100644 --- a/packages/caliper-tests-integration/iroha_tests/create.js +++ b/packages/caliper-tests-integration/iroha_tests/create.js @@ -14,68 +14,107 @@ "use strict"; -module.exports.info = "creating accounts"; +const { WorkloadModuleBase, CaliperUtils } = require('@hyperledger/caliper-core'); +const Logger = CaliperUtils.getLogger('simple-workload-open'); -let accounts = []; -let txnPerBatch; -let bc, contx; +const Dictionary = 'abcdefghijklmnopqrstuvwxyz'; -module.exports.init = function(blockchain, context, args) { - if (!args.hasOwnProperty("txnPerBatch")) { - args.txnPerBatch = 1; +/** + * Workload module for initializing the SUT with various accounts. + */ +class SimpleCreateWorkload extends WorkloadModuleBase { + + /** + * Initializes the parameters of the workload. + */ + constructor() { + super(); + this.accountPrefix = ''; + this.txIndex = -1; } - txnPerBatch = args.txnPerBatch; - bc = blockchain; - contx = context; - return Promise.resolve(); -}; + /** + * Generate string by picking characters from the dictionary variable. + * @param {number} number Character to select. + * @returns {string} string Generated string based on the input number. + * @private + */ + static _get26Num(number){ + let result = ''; -// generate random name, [a-z] -let seed = "abcdefghijklmnopqrstuvwxyz"; + while(number > 0) { + result += Dictionary.charAt(number % Dictionary.length); + number = parseInt(number / Dictionary.length); + } -/** - * Generate unique account name for the transaction - * @returns {String} account name - */ -const generateName = function() { - let name = ""; - for (let i = 0; i < 8; i++) { - name += seed.charAt(Math.floor(Math.random() * seed.length)); + return result; } - if (accounts.indexOf(name) < 0) { - return name; - } else { - return generateName(); + + /** + * Generate unique account key for the transaction. + * @returns {string} The account key. + * @private + */ + _generateAccount() { + return this.accountPrefix + SimpleCreateWorkload._get26Num(this.txIndex + 1); } -}; -/** - * Generates simple workload - * @returns {Object} array of json objects - */ -function generateWorkload() { - let workload = []; - for (let i = 0; i < txnPerBatch; i++) { - let acc_name = generateName(); - accounts.push(acc_name); - - workload.push({ - verb: "create", - account: acc_name - }); + /** + * Generates simple workload. + * @returns {{verb: String, args: Object[]}[]} Array of workload argument objects. + */ + _generateWorkload() { + let workload = []; + for(let i= 0; i < this.roundArguments.txnPerBatch; i++) { + this.txIndex++; + + let accountId = this._generateAccount(); + + workload.push({ + verb: 'create', + account: accountId + }); + } + return workload; + } + + /** + * Initialize the workload module with the given parameters. + * @param {number} workerIndex The 0-based index of the worker instantiating the workload module. + * @param {number} totalWorkers The total number of workers participating in the round. + * @param {number} roundIndex The 0-based index of the currently executing round. + * @param {Object} roundArguments The user-provided arguments for the round from the benchmark configuration file. + * @param {BlockchainInterface} sutAdapter The adapter of the underlying SUT. + * @param {Object} sutContext The custom context object provided by the SUT adapter. + * @async + */ + async initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext) { + await super.initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext); + + if(!this.roundArguments.txnPerBatch) { + this.roundArguments.txnPerBatch = 1; + } + + this.accountPrefix = SimpleCreateWorkload._get26Num(workerIndex); } - return workload; -} -module.exports.run = function() { - let args = generateWorkload(); - let resp = bc.invokeSmartContract(contx, "simple", "v0", args, 50); - return resp; -}; + /** + * Assemble TXs for opening new accounts. + * @return {Promise} + */ + async submitTransaction() { + let args = this._generateWorkload(); + Logger.debug(`Worker ${this.workerIndex} for TX ${this.txIndex}: ${JSON.stringify(args)}`); + return this.sutAdapter.invokeSmartContract(this.sutContext, 'simple', 'v0', args, 100); + } +} -module.exports.end = function() { - return Promise.resolve(); -}; +/** + * Create a new instance of the workload module. + * @return {WorkloadModuleInterface} + */ +function createWorkloadModule() { + return new SimpleCreateWorkload(); +} -module.exports.accounts = accounts; +module.exports.createWorkloadModule = createWorkloadModule; diff --git a/packages/caliper-tests-integration/iroha_tests/query.js b/packages/caliper-tests-integration/iroha_tests/query.js index f79ef80c1..3a22ef8bc 100644 --- a/packages/caliper-tests-integration/iroha_tests/query.js +++ b/packages/caliper-tests-integration/iroha_tests/query.js @@ -14,27 +14,90 @@ "use strict"; -module.exports.info = "querying accounts"; +const { WorkloadModuleBase, CaliperUtils } = require('@hyperledger/caliper-core'); +const Logger = CaliperUtils.getLogger('simple-workload-query'); -let bc, contx; -let accounts; +const Dictionary = 'abcdefghijklmnopqrstuvwxyz'; -module.exports.init = function(blockchain, context, args) { - const open = require("./create.js"); - bc = blockchain; - contx = context; - accounts = open.accounts; +/** + * Workload module for querying various accounts. + */ +class SimpleQueryWorkload extends WorkloadModuleBase { - return Promise.resolve(); -}; + /** + * Initializes the parameters of the workload. + */ + constructor() { + super(); + this.accountPrefix = ''; + this.numberOfAccountsPerWorker = -1; + } -module.exports.run = function() { - const acc = accounts[Math.floor(Math.random() * accounts.length)]; + /** + * Generate string by picking characters from the dictionary variable. + * @param {number} number Character to select. + * @returns {string} string Generated string based on the input number. + * @private + */ + static _get26Num(number){ + let result = ''; - return bc.queryState(contx, "simple", "v0", acc); -}; + while(number > 0) { + result += Dictionary.charAt(number % Dictionary.length); + number = parseInt(number / Dictionary.length); + } -module.exports.end = function() { - // do nothing - return Promise.resolve(); -}; + return result; + } + + /** + * Generate unique account key for the transaction. + * @returns {string} The account key. + * @private + */ + _generateAccount() { + // choose a random TX/account index based on the existing range, and restore the account name from the fragments + return this.accountPrefix + SimpleQueryWorkload._get26Num(Math.floor(Math.random() * this.numberOfAccountsPerWorker) + 1); + } + + /** + * Initialize the workload module with the given parameters. + * @param {number} workerIndex The 0-based index of the worker instantiating the workload module. + * @param {number} totalWorkers The total number of workers participating in the round. + * @param {number} roundIndex The 0-based index of the currently executing round. + * @param {Object} roundArguments The user-provided arguments for the round from the benchmark configuration file. + * @param {BlockchainInterface} sutAdapter The adapter of the underlying SUT. + * @param {Object} sutContext The custom context object provided by the SUT adapter. + * @async + */ + async initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext) { + await super.initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext); + + if(!this.roundArguments.numberOfAccounts) { + throw new Error('simple.query - the "numberOfAccounts" argument is missing'); + } + + this.accountPrefix = SimpleQueryWorkload._get26Num(workerIndex); + this.numberOfAccountsPerWorker = this.roundArguments.numberOfAccounts / this.totalWorkers; + } + + /** + * Assemble TXs for querying accounts. + * @return {Promise} + */ + async submitTransaction() { + const account = this._generateAccount(); + Logger.debug(`Worker ${this.workerIndex} TX parameters: ${account}`); + return this.sutAdapter.queryState(this.sutContext, 'simple', 'v0', account); + } +} + +/** + * Create a new instance of the workload module. + * @return {WorkloadModuleInterface} + */ +function createWorkloadModule() { + return new SimpleQueryWorkload(); +} + +module.exports.createWorkloadModule = createWorkloadModule; diff --git a/packages/caliper-tests-integration/iroha_tests/transfer.js b/packages/caliper-tests-integration/iroha_tests/transfer.js index 02c3d3d46..bb9c759ce 100644 --- a/packages/caliper-tests-integration/iroha_tests/transfer.js +++ b/packages/caliper-tests-integration/iroha_tests/transfer.js @@ -12,42 +12,101 @@ * limitations under the License. */ -"use strict"; +'use strict'; -module.exports.info = "transfering assets"; +const { WorkloadModuleBase, CaliperUtils } = require('@hyperledger/caliper-core'); +const Logger = CaliperUtils.getLogger('simple-workload-query'); -let bc, contx; -let accounts; -let amount; +const Dictionary = 'abcdefghijklmnopqrstuvwxyz'; -module.exports.init = function(blockchain, context, args) { - const open = require("./create.js"); - if (!args.hasOwnProperty("money")) { - args.money = "10.00"; +/** + * Workload module for transferring money between accounts. + */ +class SimpleTransferWorkload extends WorkloadModuleBase { + + /** + * Initializes the parameters of the workload. + */ + constructor() { + super(); + this.accountPrefix = ''; + this.numberOfAccountsPerWorker = -1; + } + + /** + * Generate string by picking characters from the dictionary variable. + * @param {number} number Character to select. + * @returns {string} string Generated string based on the input number. + * @private + */ + static _get26Num(number){ + let result = ''; + + while(number > 0) { + result += Dictionary.charAt(number % Dictionary.length); + number = parseInt(number / Dictionary.length); + } + + return result; + } + + /** + * Generate unique account key for the transaction. + * @returns {string} The account key. + * @private + */ + _generateAccount() { + // choose a random TX/account index based on the existing range, and restore the account name from the fragments + return this.accountPrefix + SimpleTransferWorkload._get26Num(Math.floor(Math.random() * this.numberOfAccountsPerWorker) + 1); + } + + /** + * Initialize the workload module with the given parameters. + * @param {number} workerIndex The 0-based index of the worker instantiating the workload module. + * @param {number} totalWorkers The total number of workers participating in the round. + * @param {number} roundIndex The 0-based index of the currently executing round. + * @param {Object} roundArguments The user-provided arguments for the round from the benchmark configuration file. + * @param {BlockchainInterface} sutAdapter The adapter of the underlying SUT. + * @param {Object} sutContext The custom context object provided by the SUT adapter. + * @async + */ + async initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext) { + await super.initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext); + + if(!this.roundArguments.money) { + throw new Error('simple.transfer - the "money" argument is missing'); + } + + if(!this.roundArguments.numberOfAccounts) { + throw new Error('simple.transfer - the "numberOfAccounts" argument is missing'); + } + + this.accountPrefix = SimpleTransferWorkload._get26Num(workerIndex); + this.numberOfAccountsPerWorker = this.roundArguments.numberOfAccounts / this.totalWorkers; } - if (!args.hasOwnProperty("txnPerBatch")) { - args.txnPerBatch = 1; + + /** + * Assemble TXs for transferring money. + * @return {Promise} + */ + async submitTransaction() { + const args = { + verb: 'transfer', + account: this._generateAccount(), + amount: this.roundArguments.money + }; + + Logger.debug(`Worker ${this.workerIndex} TX parameters: ${JSON.stringify(args)}`); + return this.sutAdapter.invokeSmartContract(this.sutContext, 'simple', 'v0', args, 10); } - bc = blockchain; - contx = context; - amount = args.money; - accounts = open.accounts; - - return Promise.resolve(); -}; - -module.exports.run = function() { - const account = accounts[Math.floor(Math.random() * accounts.length)]; - let args = { - verb: "transfer", - account: account, - amount: amount - }; - - return bc.invokeSmartContract(contx, "simple", "v0", args, 10); -}; - -module.exports.end = function() { - // do nothing - return Promise.resolve(); -}; +} + +/** + * Create a new instance of the workload module. + * @return {WorkloadModuleInterface} + */ +function createWorkloadModule() { + return new SimpleTransferWorkload(); +} + +module.exports.createWorkloadModule = createWorkloadModule; diff --git a/packages/caliper-tests-integration/sawtooth_tests/benchconfig.yaml b/packages/caliper-tests-integration/sawtooth_tests/benchconfig.yaml index 28580f078..4889e1c31 100644 --- a/packages/caliper-tests-integration/sawtooth_tests/benchconfig.yaml +++ b/packages/caliper-tests-integration/sawtooth_tests/benchconfig.yaml @@ -26,11 +26,13 @@ test: arguments: accounts: 30 txnPerBatch: 10 - callback: smallbankOperations.js + callback: ./smallbankOperations.js - label: query txNumber: 100 rateControl: { type: 'fixed-rate', opts: { tps: 50 } } - callback: query.js + callback: ./query.js + arguments: + accounts: 30 observer: interval: 1 type: local diff --git a/packages/caliper-tests-integration/sawtooth_tests/query.js b/packages/caliper-tests-integration/sawtooth_tests/query.js index 4836ec0cd..af5a02397 100644 --- a/packages/caliper-tests-integration/sawtooth_tests/query.js +++ b/packages/caliper-tests-integration/sawtooth_tests/query.js @@ -14,34 +14,68 @@ 'use strict'; -module.exports.info = 'querying accounts'; - - -let bc, contx; -let accounts; -module.exports.init = function(blockchain, context, args) { - let acc = require('./smallbankOperations.js'); - bc = blockchain; - contx = context; - accounts = acc.account_array; - return Promise.resolve(); -}; - -module.exports.run = function() { - let acc_num = accounts[Math.floor(Math.random()*(accounts.length))]; - if (bc.bcType === 'fabric') { - let args = { - chaincodeFunction: 'query', - chaincodeArguments: [acc_num.toString()], - }; - return bc.bcObj.querySmartContract(contx, 'smallbank', '1.0', args, 3); - } else { +const { WorkloadModuleBase, CaliperUtils } = require('@hyperledger/caliper-core'); +const Logger = CaliperUtils.getLogger('smallbank-query-workload'); + +/** + * Workload module for the smallbank workload. + */ +class SmallbankQueryWorkload extends WorkloadModuleBase { + + /** + * Initializes the parameters of the workload. + */ + constructor() { + super(); + this.prefix = -1; + } + + /** + * Get existing account. + * @return {Number} account key + */ + _getAccount() { + return parseInt(`${this.prefix}${Math.ceil(Math.random() * this.roundArguments.accounts)}`); + } + + /** + * Initialize the workload module with the given parameters. + * @param {number} workerIndex The 0-based index of the worker instantiating the workload module. + * @param {number} totalWorkers The total number of workers participating in the round. + * @param {number} roundIndex The 0-based index of the currently executing round. + * @param {Object} roundArguments The user-provided arguments for the round from the benchmark configuration file. + * @param {BlockchainInterface} sutAdapter The adapter of the underlying SUT. + * @param {Object} sutContext The custom context object provided by the SUT adapter. + * @async + */ + async initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext) { + await super.initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext); + + if (!this.roundArguments.accounts) { + throw new Error('smallbank.query - \'accounts\' argument missing'); + } + + this.prefix = workerIndex + 1; + } + + /** + * Assemble TXs for opening new accounts. + * @return {Promise} + */ + async submitTransaction() { + const account = this._getAccount(); + Logger.debug(`Worker ${this.workerIndex} TX args: ${account}`); // NOTE: the query API is inconsistent with the invoke API - return bc.queryState(contx, 'smallbank', '1.0', acc_num); + return this.sutAdapter.queryState(this.sutContext, 'smallbank', '1.0', account); } -}; +} + +/** + * Create a new instance of the workload module. + * @return {WorkloadModuleInterface} + */ +function createWorkloadModule() { + return new SmallbankQueryWorkload(); +} -module.exports.end = function() { - // do nothing - return Promise.resolve(); -}; +module.exports.createWorkloadModule = createWorkloadModule; diff --git a/packages/caliper-tests-integration/sawtooth_tests/smallbankOperations.js b/packages/caliper-tests-integration/sawtooth_tests/smallbankOperations.js index 14abc9fb4..68b509b6f 100644 --- a/packages/caliper-tests-integration/sawtooth_tests/smallbankOperations.js +++ b/packages/caliper-tests-integration/sawtooth_tests/smallbankOperations.js @@ -14,187 +14,177 @@ 'use strict'; -module.exports.info = 'small_bank_operations'; +const { WorkloadModuleBase, CaliperUtils } = require('@hyperledger/caliper-core'); +const Logger = CaliperUtils.getLogger('smallbank-workload'); -let bc, contx; -let no_accounts = 0; -let account_array = []; -let accounts, txnPerBatch; -const initial_balance = 1000000; -const operation_type = ['transact_savings','deposit_checking','send_payment','write_check', 'amalgamate']; -let prefix; +const InitialBalance = 1000000; +const OperationTypes = ['transact_savings', 'deposit_checking', 'send_payment', 'write_check', 'amalgamate']; +const Characters = 'ABCDEFGHIJKL MNOPQRSTUVWXYZ abcdefghij klmnopqrstuvwxyz'; /** - * Get account index - * @return {Number} index + * Workload module for the smallbank workload. */ -function getAccount() { - return Math.floor(Math.random()*Math.floor(account_array.length)); -} - -/** - * Get two accounts - * @return {Array} index of two accounts - */ -function get2Accounts() { - let idx1 = getAccount(); - let idx2 = getAccount(); - if(idx2 === idx1) { - idx2 = getAccount(); +class SmallbankWorkload extends WorkloadModuleBase { + + /** + * Initializes the parameters of the workload. + */ + constructor() { + super(); + this.accountsCreated = 0; + this.prefix = -1; } - return [idx1, idx2]; -} -/** - * Generate unique account key for the transaction - * @returns {Number} account key - **/ -function generateAccount() { - // should be [a-z]{1,9} - if(typeof prefix === 'undefined') { - prefix = process.pid; + /** + * Generates random string. + * @returns {string} random string from possible characters + **/ + static _randomString() { + let text = ''; + for (let i = 0; i < 12; i++) { + text += Characters.charAt(Math.floor(Math.random() * Characters.length)); + } + return text; } - let count = account_array.length+1; - let num = prefix.toString() + count.toString(); - return parseInt(num); -} -/** - * Generates random string. - * @returns {string} random string from possible characters - **/ -function random_string() { - let text = ''; - const possible = 'ABCDEFGHIJKL MNOPQRSTUVWXYZ abcdefghij klmnopqrstuvwxyz'; - - for (let i = 0; i < 12; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); + /** + * Get existing account. + * @return {Number} account key + */ + _getAccount() { + return parseInt(`${this.prefix}${Math.ceil(Math.random() * this.accountsCreated)}`); } - return text; -} -/** - * Generates small bank workload with specified number of accounts - * and operations. - * @returns {Object} array of json objects and each denotes - * one operations - **/ -function generateWorkload() { - let workload = []; - for(let i= 0; (i < txnPerBatch && no_accounts < accounts); i++,no_accounts++) { - let acc_id = generateAccount(); - account_array.push(acc_id); - let acc = { - 'customer_id': acc_id, - 'customer_name': random_string(), - 'initial_checking_balance': initial_balance, - 'initial_savings_balance': initial_balance, - 'transaction_type': 'create_account' - }; - workload.push(acc); + /** + * Generate unique account key for the transaction + * @returns {Number} account key + **/ + _generateAccount() { + this.accountsCreated++; + return parseInt(`${this.prefix}${this.accountsCreated}`); } - for(let j= workload.length; j} + */ + async submitTransaction() { + let args = this._generateWorkload(); + Logger.debug(`Worker ${this.workerIndex} TX args: ${JSON.stringify(args)}`); + return this.sutAdapter.invokeSmartContract(this.sutContext, 'smallbank', '1.0', args, 30); + } +} +/** + * Create a new instance of the workload module. + * @return {WorkloadModuleInterface} + */ +function createWorkloadModule() { + return new SmallbankWorkload(); +} -module.exports.account_array = account_array; +module.exports.createWorkloadModule = createWorkloadModule;