diff --git a/tests/TestHarness/Cluster.py b/tests/TestHarness/Cluster.py index ac70567e53..c0d66391d6 100644 --- a/tests/TestHarness/Cluster.py +++ b/tests/TestHarness/Cluster.py @@ -1763,7 +1763,7 @@ def stripValues(lowestMaxes,greaterThan): def launchTrxGenerators(self, contractOwnerAcctName: str, acctNamesList: list, acctPrivKeysList: list, nodeId: int=0, tpsPerGenerator: int=10, numGenerators: int=1, durationSec: int=60, - waitToComplete:bool=False, abiFile=None, actionName=None, actionData=None): + waitToComplete:bool=False, abiFile=None, actionsData=None, actionsAuths=None): Utils.Print("Configure txn generators") node=self.getNode(nodeId) p2pListenPort = self.getNodeP2pPort(nodeId) @@ -1779,8 +1779,8 @@ def launchTrxGenerators(self, contractOwnerAcctName: str, acctNamesList: list, a tpsTrxGensConfig = TpsTrxGensConfig(targetTps=targetTps, tpsLimitPerGenerator=tpsLimitPerGenerator) self.trxGenLauncher = TransactionGeneratorsLauncher(chainId=chainId, lastIrreversibleBlockId=lib_id, contractOwnerAccount=contractOwnerAcctName, accts=','.join(map(str, acctNamesList)), - privateKeys=','.join(map(str, acctPrivKeysList)), trxGenDurationSec=durationSec, - logDir=Utils.DataDir, abiFile=abiFile, actionName=actionName, actionData=actionData, + privateKeys=','.join(map(str, acctPrivKeysList)), trxGenDurationSec=durationSec, logDir=Utils.DataDir, + abiFile=abiFile, actionsData=actionsData, actionsAuths=actionsAuths, peerEndpoint=self.host, port=p2pListenPort, tpsTrxGensConfig=tpsTrxGensConfig) Utils.Print("Launch txn generators and start generating/sending transactions") diff --git a/tests/TestHarness/launch_transaction_generators.py b/tests/TestHarness/launch_transaction_generators.py index 4faf0e7cd8..2379987840 100755 --- a/tests/TestHarness/launch_transaction_generators.py +++ b/tests/TestHarness/launch_transaction_generators.py @@ -36,8 +36,9 @@ def __init__(self, targetTps: int, tpsLimitPerGenerator: int): class TransactionGeneratorsLauncher: - def __init__(self, chainId: int, lastIrreversibleBlockId: int, contractOwnerAccount: str, accts: str, privateKeys: str, - trxGenDurationSec: int, logDir: str, abiFile: Path, actionName: str, actionData, peerEndpoint: str, port: int, tpsTrxGensConfig: TpsTrxGensConfig): + def __init__(self, chainId: int, lastIrreversibleBlockId: int, contractOwnerAccount: str, accts: str, privateKeys: str, trxGenDurationSec: int, logDir: str, + abiFile: Path, actionsData, actionsAuths, + peerEndpoint: str, port: int, tpsTrxGensConfig: TpsTrxGensConfig): self.chainId = chainId self.lastIrreversibleBlockId = lastIrreversibleBlockId self.contractOwnerAccount = contractOwnerAccount @@ -47,18 +48,19 @@ def __init__(self, chainId: int, lastIrreversibleBlockId: int, contractOwnerAcco self.tpsTrxGensConfig = tpsTrxGensConfig self.logDir = logDir self.abiFile = abiFile - self.actionName = actionName - self.actionData = actionData + self.actionsData=actionsData + self.actionsAuths=actionsAuths self.peerEndpoint = peerEndpoint self.port = port def launch(self, waitToComplete=True): self.subprocess_ret_codes = [] - for targetTps in self.tpsTrxGensConfig.targetTpsPerGenList: - if self.abiFile is not None and self.actionName is not None and self.actionData is not None: + for id, targetTps in enumerate(self.tpsTrxGensConfig.targetTpsPerGenList): + if self.abiFile is not None and self.actionsData is not None and self.actionsAuths is not None: if Utils.Debug: Print( f'Running trx_generator: ./tests/trx_generator/trx_generator ' + f'--generator-id {id} ' f'--chain-id {self.chainId} ' f'--last-irreversible-block-id {self.lastIrreversibleBlockId} ' f'--contract-owner-account {self.contractOwnerAccount} ' @@ -67,15 +69,16 @@ def launch(self, waitToComplete=True): f'--trx-gen-duration {self.trxGenDurationSec} ' f'--target-tps {targetTps} ' f'--log-dir {self.logDir} ' - f'--action-name {self.actionName} ' - f'--action-data {self.actionData} ' f'--abi-file {self.abiFile} ' + f'--actions-data {self.actionsData} ' + f'--actions-auths {self.actionsAuths} ' f'--peer-endpoint {self.peerEndpoint} ' f'--port {self.port}' ) self.subprocess_ret_codes.append( subprocess.Popen([ './tests/trx_generator/trx_generator', + '--generator-id', f'{id}', '--chain-id', f'{self.chainId}', '--last-irreversible-block-id', f'{self.lastIrreversibleBlockId}', '--contract-owner-account', f'{self.contractOwnerAccount}', @@ -84,9 +87,9 @@ def launch(self, waitToComplete=True): '--trx-gen-duration', f'{self.trxGenDurationSec}', '--target-tps', f'{targetTps}', '--log-dir', f'{self.logDir}', - '--action-name', f'{self.actionName}', - '--action-data', f'{self.actionData}', '--abi-file', f'{self.abiFile}', + '--actions-data', f'{self.actionsData}', + '--actions-auths', f'{self.actionsAuths}', '--peer-endpoint', f'{self.peerEndpoint}', '--port', f'{self.port}' ]) @@ -95,6 +98,7 @@ def launch(self, waitToComplete=True): if Utils.Debug: Print( f'Running trx_generator: ./tests/trx_generator/trx_generator ' + f'--generator-id {id} ' f'--chain-id {self.chainId} ' f'--last-irreversible-block-id {self.lastIrreversibleBlockId} ' f'--contract-owner-account {self.contractOwnerAccount} ' @@ -109,6 +113,7 @@ def launch(self, waitToComplete=True): self.subprocess_ret_codes.append( subprocess.Popen([ './tests/trx_generator/trx_generator', + '--generator-id', f'{id}', '--chain-id', f'{self.chainId}', '--last-irreversible-block-id', f'{self.lastIrreversibleBlockId}', '--contract-owner-account', f'{self.contractOwnerAccount}', @@ -144,9 +149,9 @@ def parseArgs(): parser.add_argument("target_tps", type=int, help="Goal transactions per second") parser.add_argument("tps_limit_per_generator", type=int, help="Maximum amount of transactions per second a single generator can have.", default=4000) parser.add_argument("log_dir", type=str, help="Path to directory where trx logs should be written.") - parser.add_argument("action_name", type=str, help="The action name applied to the provided action data input") - parser.add_argument("action_data", type=str, help="The path to the json action data file or json action data description string to use") parser.add_argument("abi_file", type=str, help="The path to the contract abi file to use for the supplied transaction action data") + parser.add_argument("actions_data", type=str, help="The json actions data file or json actions data description string to use") + parser.add_argument("actions_auths", type=str, help="The json actions auth file or json actions auths description string to use, containting authAcctName to activePrivateKey pairs.") parser.add_argument("peer_endpoint", type=str, help="set the peer endpoint to send transactions to", default="127.0.0.1") parser.add_argument("port", type=int, help="set the peer endpoint port to send transactions to", default=9876) args = parser.parse_args() @@ -158,7 +163,7 @@ def main(): trxGenLauncher = TransactionGeneratorsLauncher(chainId=args.chain_id, lastIrreversibleBlockId=args.last_irreversible_block_id, contractOwnerAccount=args.contract_owner_account, accts=args.accounts, privateKeys=args.priv_keys, trxGenDurationSec=args.trx_gen_duration, logDir=args.log_dir, - abiFile=args.abi_file, actionName=args.action_name, actionData=args.action_data, + abiFile=args.abi_file, actionsData=args.actions_data, actionsAuths=args.actions_auths, peerEndpoint=args.peer_endpoint, port=args.port, tpsTrxGensConfig=TpsTrxGensConfig(targetTps=args.target_tps, tpsLimitPerGenerator=args.tps_limit_per_generator)) diff --git a/tests/performance_tests/CMakeLists.txt b/tests/performance_tests/CMakeLists.txt index 3d8ecc447d..08d232f60d 100644 --- a/tests/performance_tests/CMakeLists.txt +++ b/tests/performance_tests/CMakeLists.txt @@ -7,12 +7,16 @@ configure_file(nodeos_log_2_0_14.txt.gz nodeos_log_2_0_14.txt.gz COPYONLY) configure_file(nodeos_log_3_2.txt.gz nodeos_log_3_2.txt.gz COPYONLY) configure_file(genesis.json genesis.json COPYONLY) configure_file(validate_nodeos_plugin_args.py validate_nodeos_plugin_args.py COPYONLY) -configure_file(userTrxData.json userTrxData.json COPYONLY) +configure_file(userTrxDataTransfer.json userTrxDataTransfer.json COPYONLY) +configure_file(userTrxDataNewAccount.json userTrxDataNewAccount.json COPYONLY) -add_test(NAME performance_test_basic COMMAND tests/performance_tests/performance_test_basic.py -v -p 1 -n 1 --target-tps 20 --tps-limit-per-generator 10 --clean-run WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) -add_test(NAME performance_test_basic_ex_trx_spec COMMAND tests/performance_tests/performance_test_basic.py -v -p 1 -n 1 --target-tps 20 --tps-limit-per-generator 10 --clean-run --user-trx-data-file tests/performance_tests/userTrxData.json WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +add_test(NAME performance_test_basic COMMAND tests/performance_tests/performance_test_basic.py -v -p 1 -n 1 --target-tps 20 --tps-limit-per-generator 10 --test-duration-sec 5 --clean-run WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +add_test(NAME performance_test_basic_ex_transfer_trx_spec COMMAND tests/performance_tests/performance_test_basic.py -v -p 1 -n 1 --target-tps 20 --tps-limit-per-generator 10 --test-duration-sec 5 --clean-run --user-trx-data-file tests/performance_tests/userTrxDataTransfer.json WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +add_test(NAME performance_test_basic_ex_new_acct_trx_spec COMMAND tests/performance_tests/performance_test_basic.py -v -p 1 -n 1 --target-tps 20 --tps-limit-per-generator 10 --test-duration-sec 5 --clean-run --user-trx-data-file tests/performance_tests/userTrxDataNewAccount.json WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) add_test(NAME log_reader_tests COMMAND tests/performance_tests/log_reader_tests.py WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) add_test(NAME validate_nodeos_plugin_args COMMAND tests/performance_tests/validate_nodeos_plugin_args.py WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) set_property(TEST performance_test_basic PROPERTY LABELS nonparallelizable_tests) +set_property(TEST performance_test_basic_ex_transfer_trx_spec PROPERTY LABELS nonparallelizable_tests) +set_property(TEST performance_test_basic_ex_new_acct_trx_spec PROPERTY LABELS nonparallelizable_tests) add_subdirectory( NodeosPluginArgs ) diff --git a/tests/performance_tests/performance_test_basic.py b/tests/performance_tests/performance_test_basic.py index a7b914cdfb..b2d1df31bd 100755 --- a/tests/performance_tests/performance_test_basic.py +++ b/tests/performance_tests/performance_test_basic.py @@ -70,8 +70,10 @@ def __str__(self) -> str: @dataclass class SpecifiedContract: - accountName: str = "c" + accountName: str = "eosio" + ownerPrivateKey: str = "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3" ownerPublicKey: str = "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" + activePrivateKey: str = "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3" activePublicKey: str = "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" contractDir: str = "unittests/contracts/eosio.system" wasmFile: str = "eosio.system.wasm" @@ -86,6 +88,7 @@ class SpecifiedContract: genesisPath: Path = Path("tests")/"performance_tests"/"genesis.json" maximumP2pPerHost: int = 5000 maximumClients: int = 0 + loggingLevel: str = "info" loggingDict: dict = field(default_factory=lambda: { "bios": "off" }) prodsEnableTraceApi: bool = False nodeosVers: str = "" @@ -95,7 +98,7 @@ class SpecifiedContract: def log_transactions(self, trxDataFile, block): for trx in block['payload']['transactions']: for actions in trx['actions']: - if actions['account'] != 'eosio' and actions['action'] != 'onblock': + if actions['account'] != 'eosio' or actions['action'] != 'onblock': trxDataFile.write(f"{trx['id']},{trx['block_num']},{trx['block_time']},{trx['cpu_usage_us']},{trx['net_usage_words']},{trx['actions']}\n") def __post_init__(self): @@ -178,7 +181,7 @@ def __init__(self, testHelperConfig: TestHelperConfig=TestHelperConfig(), cluste # Setup cluster and its wallet manager self.walletMgr=WalletMgr(True) - self.cluster=Cluster(walletd=True, loggingLevel="info", loggingLevelDict=self.clusterConfig.loggingDict, + self.cluster=Cluster(walletd=True, loggingLevel=self.clusterConfig.loggingLevel, loggingLevelDict=self.clusterConfig.loggingDict, nodeosVers=self.clusterConfig.nodeosVers) self.cluster.setWalletMgr(self.walletMgr) @@ -294,17 +297,30 @@ def readUserTrxDataFromFile(self, userTrxDataFile: Path): self.userTrxDataDict = json.load(f) def setupContract(self): - specifiedAccount = Account(self.clusterConfig.specifiedContract.accountName) - specifiedAccount.ownerPublicKey = self.clusterConfig.specifiedContract.ownerPublicKey - specifiedAccount.activePublicKey = self.clusterConfig.specifiedContract.activePublicKey - self.cluster.createAccountAndVerify(specifiedAccount, self.cluster.eosioAccount, validationNodeIndex=self.validationNodeId) - print("Publishing contract") - transaction=self.cluster.biosNode.publishContract(specifiedAccount, self.clusterConfig.specifiedContract.contractDir, - self.clusterConfig.specifiedContract.wasmFile, - self.clusterConfig.specifiedContract.abiFile, waitForTransBlock=True) - if transaction is None: - print("ERROR: Failed to publish contract.") - return None + if (self.clusterConfig.specifiedContract.accountName != self.cluster.eosioAccount.name): + specifiedAccount = Account(self.clusterConfig.specifiedContract.accountName) + specifiedAccount.ownerPublicKey = self.clusterConfig.specifiedContract.ownerPublicKey + specifiedAccount.ownerPrivateKey = self.clusterConfig.specifiedContract.ownerPrivateKey + specifiedAccount.activePublicKey = self.clusterConfig.specifiedContract.activePublicKey + specifiedAccount.activePrivateKey = self.clusterConfig.specifiedContract.activePrivateKey + self.cluster.createAccountAndVerify(specifiedAccount, self.cluster.eosioAccount, validationNodeIndex=self.validationNodeId) + print("Publishing contract") + transaction=self.cluster.biosNode.publishContract(specifiedAccount, self.clusterConfig.specifiedContract.contractDir, + self.clusterConfig.specifiedContract.wasmFile, + self.clusterConfig.specifiedContract.abiFile, waitForTransBlock=True) + if transaction is None: + print("ERROR: Failed to publish contract.") + return None + else: + self.clusterConfig.specifiedContract.activePrivateKey = self.cluster.eosioAccount.activePrivateKey + self.clusterConfig.specifiedContract.activePublicKey = self.cluster.eosioAccount.activePublicKey + self.clusterConfig.specifiedContract.ownerPrivateKey = self.cluster.eosioAccount.ownerPrivateKey + self.clusterConfig.specifiedContract.ownerPublicKey = self.cluster.eosioAccount.ownerPublicKey + print(f"setupContract: default {self.clusterConfig.specifiedContract.accountName} \ + activePrivateKey: {self.clusterConfig.specifiedContract.activePrivateKey} \ + activePublicKey: {self.clusterConfig.specifiedContract.activePublicKey} \ + ownerPrivateKey: {self.clusterConfig.specifiedContract.ownerPrivateKey} \ + ownerPublicKey: {self.clusterConfig.specifiedContract.ownerPublicKey}") def runTpsTest(self) -> PtbTpsTestResult: completedRun = False @@ -318,14 +334,34 @@ def runTpsTest(self) -> PtbTpsTestResult: self.data = log_reader.chainData() abiFile=None - actionName=None - actionData=None + actionsDataJson=None + actionsAuthsJson=None + self.accountNames=[] + self.accountPrivKeys=[] if (self.ptbConfig.userTrxDataFile is not None): self.readUserTrxDataFromFile(self.ptbConfig.userTrxDataFile) - self.setupWalletAndAccounts(accountCnt=len(self.userTrxDataDict['accounts']), accountNames=self.userTrxDataDict['accounts']) + if self.userTrxDataDict['initAccounts']: + print(f"Creating accounts specified in userTrxData: {self.userTrxDataDict['initAccounts']}") + self.setupWalletAndAccounts(accountCnt=len(self.userTrxDataDict['initAccounts']), accountNames=self.userTrxDataDict['initAccounts']) abiFile = self.userTrxDataDict['abiFile'] - actionName = self.userTrxDataDict['actionName'] - actionData = json.dumps(self.userTrxDataDict['actionData']) + + actionsDataJson = json.dumps(self.userTrxDataDict['actions']) + + authorizations={} + for act in self.userTrxDataDict['actions']: + actionAuthAcct=act["actionAuthAcct"] + actionAuthPrivKey=None + if actionAuthAcct == self.cluster.eosioAccount.name: + actionAuthPrivKey = self.cluster.eosioAccount.activePrivateKey + else: + for account in self.cluster.accounts: + if actionAuthAcct == account.name: + actionAuthPrivKey = account.activePrivateKey + break + + if actionAuthPrivKey is not None: + authorizations[actionAuthAcct]=actionAuthPrivKey + actionsAuthsJson = json.dumps(authorizations) else: self.setupWalletAndAccounts() @@ -334,11 +370,11 @@ def runTpsTest(self) -> PtbTpsTestResult: self.data.startBlock = self.waitForEmptyBlocks(self.validationNode, self.emptyBlockGoal) tpsTrxGensConfig = TpsTrxGensConfig(targetTps=self.ptbConfig.targetTps, tpsLimitPerGenerator=self.ptbConfig.tpsLimitPerGenerator) - trxGenLauncher = TransactionGeneratorsLauncher(chainId=chainId, lastIrreversibleBlockId=lib_id, - contractOwnerAccount=self.clusterConfig.specifiedContract.accountName, accts=','.join(map(str, self.accountNames)), - privateKeys=','.join(map(str, self.accountPrivKeys)), trxGenDurationSec=self.ptbConfig.testTrxGenDurationSec, - logDir=self.trxGenLogDirPath, abiFile=abiFile, actionName=actionName, actionData=actionData, - peerEndpoint=self.producerNode.host, port=self.producerP2pPort, tpsTrxGensConfig=tpsTrxGensConfig) + trxGenLauncher = TransactionGeneratorsLauncher(chainId=chainId, lastIrreversibleBlockId=lib_id, contractOwnerAccount=self.clusterConfig.specifiedContract.accountName, + accts=','.join(map(str, self.accountNames)), privateKeys=','.join(map(str, self.accountPrivKeys)), + trxGenDurationSec=self.ptbConfig.testTrxGenDurationSec, logDir=self.trxGenLogDirPath, + abiFile=abiFile, actionsData=actionsDataJson, actionsAuths=actionsAuthsJson, + peerEndpoint=self.producerNode.host, port=self.producerP2pPort, tpsTrxGensConfig=tpsTrxGensConfig) trxGenExitCodes = trxGenLauncher.launch() print(f"Transaction Generator exit codes: {trxGenExitCodes}") @@ -497,6 +533,10 @@ def createBaseArgumentParser(): In \"heap\" mode database is preloaded in to swappable memory and will use huge pages if available. \ In \"locked\" mode database is preloaded, locked in to memory, and will use huge pages if available.", choices=["mapped", "heap", "locked"], default="mapped") + ptbBaseParserGroup.add_argument("--cluster-log-lvl", type=str, help="Cluster log level (\"all\", \"debug\", \"info\", \"warn\", \"error\", or \"off\"). \ + Performance Harness Test Basic relies on some logging at \"info\" level, so it is recommended lowest logging level to use. \ + However, there are instances where more verbose logging can be useful.", + choices=["all", "debug", "info", "warn", "error", "off"], default="info") ptbBaseParserGroup.add_argument("--net-threads", type=int, help="Number of worker threads in net_plugin thread pool", default=4) ptbBaseParserGroup.add_argument("--disable-subjective-billing", type=bool, help="Disable subjective CPU billing for API/P2P transactions", default=True) ptbBaseParserGroup.add_argument("--last-block-time-offset-us", type=int, help="Offset of last block producing time in microseconds. Valid range 0 .. -block_time_interval.", default=0) @@ -510,7 +550,7 @@ def createBaseArgumentParser(): ptbBaseParserGroup.add_argument("--quiet", help="Whether to quiet printing intermediate results and reports to stdout", action='store_true') ptbBaseParserGroup.add_argument("--prods-enable-trace-api", help="Determines whether producer nodes should have eosio::trace_api_plugin enabled", action='store_true') ptbBaseParserGroup.add_argument("--print-missing-transactions", type=bool, help="Toggles if missing transactions are be printed upon test completion.", default=False) - ptbBaseParserGroup.add_argument("--account-name", type=str, help="Name of the account to create and assign a contract to", default="c") + ptbBaseParserGroup.add_argument("--account-name", type=str, help="Name of the account to create and assign a contract to", default="eosio") ptbBaseParserGroup.add_argument("--owner-public-key", type=str, help="Owner public key to use with specified account name", default="EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV") ptbBaseParserGroup.add_argument("--active-public-key", type=str, help="Active public key to use with specified account name", default="EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV") ptbBaseParserGroup.add_argument("--contract-dir", type=str, help="Path to contract dir", default="unittests/contracts/eosio.system") @@ -531,7 +571,7 @@ def createArgumentParser(): ptbParserGroup.add_argument("--target-tps", type=int, help="The target transfers per second to send during test", default=8000) ptbParserGroup.add_argument("--test-duration-sec", type=int, help="The duration of transfer trx generation for the test in seconds", default=90) - ptbParserGroup.add_argument("--user-trx-data-file", type=str, help="Path to userTrxData.json") + ptbParserGroup.add_argument("--user-trx-data-file", type=str, help="Path to userTrxDataTransfer.json") return ptbParser @@ -566,11 +606,12 @@ def main(): netPluginArgs = NetPluginArgs(netThreads=args.net_threads) ENA = PerformanceTestBasic.ClusterConfig.ExtraNodeosArgs extraNodeosArgs = ENA(chainPluginArgs=chainPluginArgs, httpPluginArgs=httpPluginArgs, producerPluginArgs=producerPluginArgs, netPluginArgs=netPluginArgs) + SC = PerformanceTestBasic.ClusterConfig.SpecifiedContract + specifiedContract=SC(accountName=args.account_name, ownerPublicKey=args.owner_public_key, activePublicKey=args.active_public_key, + contractDir=args.contract_dir, wasmFile=args.wasm_file, abiFile=args.abi_file) testClusterConfig = PerformanceTestBasic.ClusterConfig(pnodes=args.p, totalNodes=args.n, topo=args.s, genesisPath=args.genesis, prodsEnableTraceApi=args.prods_enable_trace_api, extraNodeosArgs=extraNodeosArgs, - specifiedContract=PerformanceTestBasic.ClusterConfig.SpecifiedContract(accountName=args.account_name, - ownerPublicKey=args.owner_public_key, activePublicKey=args.active_public_key, contractDir=args.contract_dir, - wasmFile=args.wasm_file, abiFile=args.abi_file), + specifiedContract=specifiedContract, loggingLevel=args.cluster_log_lvl, nodeosVers=Utils.getNodeosVersion().split('.')[0]) ptbConfig = PerformanceTestBasic.PtbConfig(targetTps=args.target_tps, testTrxGenDurationSec=args.test_duration_sec, tpsLimitPerGenerator=args.tps_limit_per_generator, numAddlBlocksToPrune=args.num_blocks_to_prune, logDirRoot=".", delReport=args.del_report, quiet=args.quiet, delPerfLogs=args.del_perf_logs, diff --git a/tests/performance_tests/userTrxData.json b/tests/performance_tests/userTrxData.json deleted file mode 100644 index b247e44dc5..0000000000 --- a/tests/performance_tests/userTrxData.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "accounts": ["testacct1", "testacct2"], - "abiFile": "unittests/contracts/eosio.token/eosio.token.abi", - "actionName": "transfer", - "actionData": - { - "from":"testacct1", - "to":"testacct2", - "quantity":"0.0001 CUR", - "memo":"transaction specified" - } -} diff --git a/tests/performance_tests/userTrxDataNewAccount.json b/tests/performance_tests/userTrxDataNewAccount.json new file mode 100644 index 0000000000..de4cdc7647 --- /dev/null +++ b/tests/performance_tests/userTrxDataNewAccount.json @@ -0,0 +1,53 @@ +{ + "initAccounts": [], + "abiFile": "unittests/contracts/eosio.system/eosio.system.abi", + "actions": [ + { + "actionAuthAcct": "eosio", + "actionName": "newaccount", + "authorization": { + "actor": "eosio", + "permission": "active" + }, + "actionData": { + "creator": "eosio", + "name": "ACCT_PER_TRX", + "owner": { + "threshold": 1, + "keys": [ + { + "key": "EOS65rXebLhtk2aTTzP4e9x1AQZs7c5NNXJp89W8R3HyaA6Zyd4im", + "weight": 1 + } + ], + "accounts": [], + "waits": [] + }, + "active": { + "threshold": 1, + "keys": [ + { + "key": "EOS65rXebLhtk2aTTzP4e9x1AQZs7c5NNXJp89W8R3HyaA6Zyd4im", + "weight": 1 + } + ], + "accounts": [], + "waits": [] + } + } + }, + { + "actionAuthAcct": "eosio", + "actionName": "buyrambytes", + "authorization": { + "actor": "eosio", + "permission": "active" + }, + "actionData": { + "payer": "eosio", + "receiver": "ACCT_PER_TRX", + "bytes": "3000" + } + } + ] +} diff --git a/tests/performance_tests/userTrxDataTransfer.json b/tests/performance_tests/userTrxDataTransfer.json new file mode 100644 index 0000000000..4e17595eb1 --- /dev/null +++ b/tests/performance_tests/userTrxDataTransfer.json @@ -0,0 +1,23 @@ +{ + "initAccounts": [ + "testacct1", + "testacct2" + ], + "abiFile": "unittests/contracts/eosio.token/eosio.token.abi", + "actions": [ + { + "actionAuthAcct": "testacct1", + "actionName": "transfer", + "authorization": { + "actor": "testacct1", + "permission": "active" + }, + "actionData": { + "from": "testacct1", + "to": "testacct2", + "quantity": "0.0001 CUR", + "memo": "transaction specified" + } + } + ] +} diff --git a/tests/trx_generator/main.cpp b/tests/trx_generator/main.cpp index b7a6c8282a..20fea0444d 100644 --- a/tests/trx_generator/main.cpp +++ b/tests/trx_generator/main.cpp @@ -24,35 +24,38 @@ enum return_codes { }; int main(int argc, char** argv) { - const int64_t TRX_EXPIRATION_MAX = 3600; + const int64_t trx_expiration_max = 3600; + const uint16_t generator_id_max = 960; variables_map vmap; options_description cli("Transaction Generator command line options."); + uint16_t gen_id = 0; string chain_id_in; string contract_owner_acct; string accts; string p_keys; - int64_t trx_expr; - uint32_t gen_duration; - uint32_t target_tps; + int64_t trx_expr = 3600; + uint32_t gen_duration = 60; + uint32_t target_tps = 1; string lib_id_str; - int64_t spinup_time_us; - uint32_t max_lag_per; - int64_t max_lag_duration_us; + int64_t spinup_time_us = 1000000; + uint32_t max_lag_per = 5; + int64_t max_lag_duration_us = 1000000; string log_dir_in; bool stop_on_trx_failed; - std::string peer_endpoint; - unsigned short port; + std::string peer_endpoint = "127.0.0.1"; + unsigned short port = 9876; bool transaction_specified = false; - std::string action_name_in; - std::string action_data_file_or_str; std::string abi_file_path_in; + std::string actions_data_json_file_or_str; + std::string actions_auths_json_file_or_str; vector account_str_vector; vector private_keys_str_vector; cli.add_options() + ("generator-id", bpo::value(&gen_id)->default_value(0), "Id for the transaction generator. Allowed range (0-960). Defaults to 0.") ("chain-id", bpo::value(&chain_id_in), "set the chain id") ("contract-owner-account", bpo::value(&contract_owner_acct), "Account name of the contract account for the transaction actions") ("accounts", bpo::value(&accts), "comma-separated list of accounts that will be used for transfers. Minimum required accounts: 2.") @@ -65,10 +68,9 @@ int main(int argc, char** argv) { ("monitor-max-lag-percent", bpo::value(&max_lag_per)->default_value(5), "Max percentage off from expected transactions sent before being in violation. Defaults to 5.") ("monitor-max-lag-duration-us", bpo::value(&max_lag_duration_us)->default_value(1000000), "Max microseconds that transaction generation can be in violation before quitting. Defaults to 1000000 (1s).") ("log-dir", bpo::value(&log_dir_in), "set the logs directory") - ("action-name", bpo::value(&action_name_in), "The action name applied to the provided action data input") - ("action-data", bpo::value(&action_data_file_or_str), "The path to the json action data file or json action data description string to use") ("abi-file", bpo::value(&abi_file_path_in), "The path to the contract abi file to use for the supplied transaction action data") - ("stop-on-trx-failed", bpo::value(&stop_on_trx_failed)->default_value(true), "stop transaction generation if sending fails.") + ("actions-data", bpo::value(&actions_data_json_file_or_str), "The json actions data file or json actions data description string to use") + ("actions-auths", bpo::value(&actions_auths_json_file_or_str), "The json actions auth file or json actions auths description string to use, containting authAcctName to activePrivateKey pairs.") ("peer-endpoint", bpo::value(&peer_endpoint)->default_value("127.0.0.1"), "set the peer endpoint to send transactions to") ("port", bpo::value(&port)->default_value(9876), "set the peer endpoint port to send transactions to") ("help,h", "print this list") @@ -83,14 +85,15 @@ int main(int argc, char** argv) { return SUCCESS; } - if((vmap.count("action-name") || vmap.count("action-data") || vmap.count("abi-file")) && !(vmap.count("action-name") && vmap.count("action-data") && vmap.count("abi-file"))) { - ilog("Initialization error: If using action-name, action-data, or abi-file to specify a transaction type to generate, must provide all three inputs."); + if((vmap.count("abi-file") || vmap.count("actions-data") || vmap.count("actions-auths")) && + !(vmap.count("abi-file") && vmap.count("actions-data") && vmap.count("actions-auths"))) { + ilog("Initialization error: If using abi-file, actions-data, and actions-auths to specify a transaction type to generate, must provide all inputs."); cli.print(std::cerr); return INITIALIZE_FAIL; } - if(vmap.count("action-name") && vmap.count("action-data") && vmap.count("abi-file")) { - ilog("Specifying transaction to generate directly using action-name, action-data, and abi-file."); + if(vmap.count("abi-file") && vmap.count("actions-data") && vmap.count("actions-auths")) { + ilog("Specifying transaction to generate directly using abi-file, actions-data, and actions-auths."); transaction_specified = true; } @@ -126,11 +129,6 @@ int main(int argc, char** argv) { cli.print(std::cerr); return INITIALIZE_FAIL; } - if (transaction_specified && account_str_vector.size() < 1) { - ilog("Initialization error: Specifying transaction to generate requires at minimum 1 account."); - cli.print(std::cerr); - return INITIALIZE_FAIL; - } } else { ilog("Initialization error: did not specify transfer accounts. Auto transfer transaction generation requires at minimum 2 transfer accounts, while providing transaction action data requires at least one."); cli.print(std::cerr); @@ -144,20 +142,23 @@ int main(int argc, char** argv) { cli.print(std::cerr); return INITIALIZE_FAIL; } - if (transaction_specified && private_keys_str_vector.size() < 1) { - ilog("Initialization error: Specifying transaction to generate requires at minimum 1 private key"); - cli.print(std::cerr); - return INITIALIZE_FAIL; - } } else { ilog("Initialization error: did not specify accounts' private keys. Auto transfer transaction generation requires at minimum 2 private keys, while providing transaction action data requires at least one."); cli.print(std::cerr); return INITIALIZE_FAIL; } + if(vmap.count("generator-id")) { + if(gen_id > generator_id_max) { + ilog("Initialization error: Exceeded max value for generator id. Value must be less than ${max}.", ("max", generator_id_max)); + cli.print(std::cerr); + return INITIALIZE_FAIL; + } + } + if(vmap.count("trx-expiration")) { - if(trx_expr > TRX_EXPIRATION_MAX) { - ilog("Initialization error: Exceeded max value for transaction expiration. Value must be less than ${max}.", ("max", TRX_EXPIRATION_MAX)); + if(trx_expr > trx_expiration_max) { + ilog("Initialization error: Exceeded max value for transaction expiration. Value must be less than ${max}.", ("max", trx_expiration_max)); cli.print(std::cerr); return INITIALIZE_FAIL; } @@ -192,6 +193,7 @@ int main(int argc, char** argv) { return INITIALIZE_FAIL; } + ilog("Initial generator id ${id}", ("id", gen_id)); ilog("Initial chain id ${chainId}", ("chainId", chain_id_in)); ilog("Contract owner account ${acct}", ("acct", contract_owner_acct)); ilog("Transfer accounts ${accts}", ("accts", accts)); @@ -203,13 +205,19 @@ int main(int argc, char** argv) { ilog("Logs directory ${logDir}", ("logDir", log_dir_in)); ilog("Peer Endpoint ${peer-endpoint}:${peer-port}", ("peer-endpoint", peer_endpoint)("peer-port", port)); + if (transaction_specified) { + ilog("User Transaction Specified: Abi File ${abi}", ("abi", abi_file_path_in)); + ilog("User Transaction Specified: Actions Data ${acts}", ("acts", actions_data_json_file_or_str)); + ilog("User Transaction Specified: Actions Auths ${auths}", ("auths", actions_auths_json_file_or_str)); + } + fc::microseconds trx_expr_ms = fc::seconds(trx_expr); std::shared_ptr monitor; if (transaction_specified) { - auto generator = std::make_shared(chain_id_in, abi_file_path_in, contract_owner_acct, account_str_vector.at(0), action_name_in, - action_data_file_or_str, trx_expr_ms, private_keys_str_vector.at(0), lib_id_str, log_dir_in, - stop_on_trx_failed, peer_endpoint, port); + auto generator = std::make_shared(gen_id, chain_id_in, abi_file_path_in, contract_owner_acct, + actions_data_json_file_or_str, actions_auths_json_file_or_str, + trx_expr_ms, lib_id_str, log_dir_in, stop_on_trx_failed, peer_endpoint, port); monitor = std::make_shared(spinup_time_us, max_lag_per, max_lag_duration_us); trx_tps_tester tester{generator, monitor, gen_duration, target_tps}; @@ -217,7 +225,7 @@ int main(int argc, char** argv) { return OTHER_FAIL; } } else { - auto generator = std::make_shared(chain_id_in, contract_owner_acct, account_str_vector, trx_expr_ms, private_keys_str_vector, + auto generator = std::make_shared(gen_id, chain_id_in, contract_owner_acct, account_str_vector, trx_expr_ms, private_keys_str_vector, lib_id_str, log_dir_in, stop_on_trx_failed, peer_endpoint, port); monitor = std::make_shared(spinup_time_us, max_lag_per, max_lag_duration_us); diff --git a/tests/trx_generator/trx_generator.cpp b/tests/trx_generator/trx_generator.cpp index 438a837a3b..9df763505a 100644 --- a/tests/trx_generator/trx_generator.cpp +++ b/tests/trx_generator/trx_generator.cpp @@ -5,9 +5,7 @@ #include #include #include -#include #include -#include #include #include @@ -18,65 +16,65 @@ using namespace appbase; namespace bpo=boost::program_options; namespace eosio::testing { - struct action_pair_w_keys { - action_pair_w_keys(eosio::chain::action first_action, eosio::chain::action second_action, fc::crypto::private_key first_act_signer, fc::crypto::private_key second_act_signer) - : _first_act(first_action), _second_act(second_action), _first_act_priv_key(first_act_signer), _second_act_priv_key(second_act_signer) {} - eosio::chain::action _first_act; - eosio::chain::action _second_act; - fc::crypto::private_key _first_act_priv_key; - fc::crypto::private_key _second_act_priv_key; - }; + void trx_generator_base::set_transaction_headers(transaction& trx, const block_id_type& last_irr_block_id, const fc::microseconds& expiration, uint32_t delay_sec) { + trx.expiration = fc::time_point::now() + expiration; + trx.set_reference_block(last_irr_block_id); + + trx.max_net_usage_words = 0;// No limit + trx.max_cpu_usage_ms = 0; // No limit + trx.delay_sec = delay_sec; + } - signed_transaction_w_signer create_transfer_trx_w_signer(const action& act, const fc::crypto::private_key& priv_key, uint64_t& nonce_prefix, uint64_t& nonce, const fc::microseconds& trx_expiration, const chain_id_type& chain_id, const block_id_type& last_irr_block_id) { + signed_transaction_w_signer trx_generator_base::create_trx_w_actions_and_signer(std::vector acts, const fc::crypto::private_key& priv_key, uint64_t& nonce_prefix, uint64_t& nonce, const fc::microseconds& trx_expiration, const chain_id_type& chain_id, const block_id_type& last_irr_block_id) { signed_transaction trx; - trx.actions.push_back(act); + set_transaction_headers(trx, last_irr_block_id, trx_expiration); + for (auto& act : acts) { + trx.actions.emplace_back(std::move(act)); + } trx.context_free_actions.emplace_back(action({}, config::null_account_name, name("nonce"), fc::raw::pack(std::to_string(nonce_prefix) + ":" + std::to_string(++nonce) + ":" + fc::time_point::now().time_since_epoch().count()))); - trx.set_reference_block(last_irr_block_id); - trx.expiration = fc::time_point::now() + trx_expiration; - trx.max_net_usage_words = 100; trx.sign(priv_key, chain_id); return signed_transaction_w_signer(trx, priv_key); } - vector create_initial_transfer_transactions(const vector& action_pairs_vector, uint64_t& nonce_prefix, uint64_t& nonce, const fc::microseconds& trx_expiration, const chain_id_type& chain_id, const block_id_type& last_irr_block_id) { + vector transfer_trx_generator::create_initial_transfer_transactions(const vector& action_pairs_vector, uint64_t& nonce_prefix, uint64_t& nonce, const fc::microseconds& trx_expiration, const chain_id_type& chain_id, const block_id_type& last_irr_block_id) { std::vector trxs; trxs.reserve(2 * action_pairs_vector.size()); - for(action_pair_w_keys ap: action_pairs_vector) { - trxs.emplace_back(create_transfer_trx_w_signer(ap._first_act, ap._first_act_priv_key, nonce_prefix, nonce, trx_expiration, chain_id, last_irr_block_id)); - trxs.emplace_back(create_transfer_trx_w_signer(ap._second_act, ap._second_act_priv_key, nonce_prefix, nonce, trx_expiration, chain_id, last_irr_block_id)); + for (const action_pair_w_keys& ap : action_pairs_vector) { + trxs.emplace_back(create_trx_w_actions_and_signer({ap._first_act}, ap._first_act_priv_key, nonce_prefix, nonce, trx_expiration, chain_id, last_irr_block_id)); + trxs.emplace_back(create_trx_w_actions_and_signer({ap._second_act}, ap._second_act_priv_key, nonce_prefix, nonce, trx_expiration, chain_id, last_irr_block_id)); } return trxs; } - void update_resign_transaction(signed_transaction& trx, fc::crypto::private_key priv_key, uint64_t& nonce_prefix, uint64_t& nonce, const fc::microseconds& trx_expiration, const chain_id_type& chain_id, const block_id_type& last_irr_block_id) { + void trx_generator_base::update_resign_transaction(signed_transaction& trx, const fc::crypto::private_key& priv_key, uint64_t& nonce_prefix, uint64_t& nonce, const fc::microseconds& trx_expiration, + const chain_id_type& chain_id, const block_id_type& last_irr_block_id) { trx.context_free_actions.clear(); trx.context_free_actions.emplace_back(action({}, config::null_account_name, name("nonce"), fc::raw::pack(std::to_string(nonce_prefix) + ":" + std::to_string(++nonce) + ":" + fc::time_point::now().time_since_epoch().count()))); - trx.set_reference_block(last_irr_block_id); - trx.expiration = fc::time_point::now() + trx_expiration; + set_transaction_headers(trx, last_irr_block_id, trx_expiration); trx.signatures.clear(); trx.sign(priv_key, chain_id); } - chain::bytes make_transfer_data(const chain::name& from, const chain::name& to, const chain::asset& quantity, const std::string&& memo) { + chain::bytes transfer_trx_generator::make_transfer_data(const chain::name& from, const chain::name& to, const chain::asset& quantity, const std::string& memo) { return fc::raw::pack(from, to, quantity, memo); } - auto make_transfer_action(chain::name account, chain::name from, chain::name to, chain::asset quantity, std::string memo) { + auto transfer_trx_generator::make_transfer_action(chain::name account, chain::name from, chain::name to, chain::asset quantity, std::string memo) { return chain::action(std::vector{{from, chain::config::active_name}}, account, "transfer"_n, make_transfer_data(from, to, quantity, std::move(memo))); } - vector create_initial_transfer_actions(const std::string& salt, const uint64_t& period, const name& contract_owner_account, const vector& accounts, const vector& priv_keys) { + vector transfer_trx_generator::create_initial_transfer_actions(const std::string& salt, const uint64_t& period, const name& contract_owner_account, const vector& accounts, const vector& priv_keys) { vector actions_pairs_vector; - for(size_t i = 0; i < accounts.size(); ++i) { - for(size_t j = i + 1; j < accounts.size(); ++j) { + for (size_t i = 0; i < accounts.size(); ++i) { + for (size_t j = i + 1; j < accounts.size(); ++j) { //create the actions here ilog("create_initial_transfer_actions: creating transfer from ${acctA} to ${acctB}", ("acctA", accounts.at(i))("acctB", accounts.at(j))); action act_a_to_b = make_transfer_action(contract_owner_account, accounts.at(i), accounts.at(j), asset::from_string("1.0000 CUR"), salt); @@ -84,36 +82,36 @@ namespace eosio::testing { ilog("create_initial_transfer_actions: creating transfer from ${acctB} to ${acctA}", ("acctB", accounts.at(j))("acctA", accounts.at(i))); action act_b_to_a = make_transfer_action(contract_owner_account, accounts.at(j), accounts.at(i), asset::from_string("1.0000 CUR"), salt); - actions_pairs_vector.push_back(action_pair_w_keys(act_a_to_b, act_b_to_a, priv_keys.at(i), priv_keys.at(j))); + actions_pairs_vector.emplace_back(action_pair_w_keys(act_a_to_b, act_b_to_a, priv_keys.at(i), priv_keys.at(j))); } } ilog("create_initial_transfer_actions: total action pairs created: ${pairs}", ("pairs", actions_pairs_vector.size())); return actions_pairs_vector; } - trx_generator_base::trx_generator_base(std::string chain_id_in, std::string contract_owner_account, fc::microseconds trx_expr, std::string lib_id_str, std::string log_dir, + trx_generator_base::trx_generator_base(uint16_t generator_id, const std::string& chain_id_in, const std::string& contract_owner_account, const fc::microseconds& trx_expr, const std::string& lib_id_str, const std::string& log_dir, bool stop_on_trx_failed, const std::string& peer_endpoint, unsigned short port) - : _provider(peer_endpoint, port), _chain_id(chain_id_in), _contract_owner_account(contract_owner_account), _trx_expiration(trx_expr), + : _provider(peer_endpoint, port), _generator_id(generator_id), _chain_id(chain_id_in), _contract_owner_account(contract_owner_account), _trx_expiration(trx_expr), _last_irr_block_id(fc::variant(lib_id_str).as()), _log_dir(log_dir), _stop_on_trx_failed(stop_on_trx_failed) {} - transfer_trx_generator::transfer_trx_generator(std::string chain_id_in, std::string contract_owner_account, const std::vector& accts, - fc::microseconds trx_expr, const std::vector& private_keys_str_vector, std::string lib_id_str, std::string log_dir, bool stop_on_trx_failed, const std::string& peer_endpoint, unsigned short port) - : trx_generator_base(chain_id_in, contract_owner_account, trx_expr, lib_id_str, log_dir, stop_on_trx_failed, peer_endpoint, port), _accts(accts), _private_keys_str_vector(private_keys_str_vector) {} + transfer_trx_generator::transfer_trx_generator(uint16_t generator_id, const std::string& chain_id_in, const std::string& contract_owner_account, const std::vector& accts, + const fc::microseconds& trx_expr, const std::vector& private_keys_str_vector, const std::string& lib_id_str, const std::string& log_dir, bool stop_on_trx_failed, const std::string& peer_endpoint, unsigned short port) + : trx_generator_base(generator_id, chain_id_in, contract_owner_account, trx_expr, lib_id_str, log_dir, stop_on_trx_failed, peer_endpoint, port), _accts(accts), _private_keys_str_vector(private_keys_str_vector) {} vector transfer_trx_generator::get_accounts(const vector& account_str_vector) { vector acct_name_list; - for(string account_name: account_str_vector) { + for (const string& account_name : account_str_vector) { ilog("get_account about to try to create name for ${acct}", ("acct", account_name)); - acct_name_list.push_back(eosio::chain::name(account_name)); + acct_name_list.emplace_back(eosio::chain::name(account_name)); } return acct_name_list; } vector transfer_trx_generator::get_private_keys(const vector& priv_key_str_vector) { vector key_list; - for(const string& private_key: priv_key_str_vector) { + for (const string& private_key: priv_key_str_vector) { ilog("get_private_keys about to try to create private_key for ${key} : gen key ${newKey}", ("key", private_key)("newKey", fc::crypto::private_key(private_key))); - key_list.push_back(fc::crypto::private_key(private_key)); + key_list.emplace_back(fc::crypto::private_key(private_key)); } return key_list; } @@ -151,7 +149,7 @@ namespace eosio::testing { return true; } - fc::variant json_from_file_or_string(const string& file_or_str, fc::json::parse_type ptype = fc::json::parse_type::legacy_parser) + fc::variant trx_generator::json_from_file_or_string(const string& file_or_str, fc::json::parse_type ptype) { regex r("^[ \t]*[\{\[]"); if ( !regex_search(file_or_str, r) && fc::is_regular_file(file_or_str) ) { @@ -166,10 +164,100 @@ namespace eosio::testing { } } - trx_generator::trx_generator(std::string chain_id_in, const std::string& abi_data_file, std::string contract_owner_account, std::string auth_account, std::string action_name, - const std::string& action_data_file_or_str, fc::microseconds trx_expr, const std::string& private_key_str, std::string lib_id_str, std::string log_dir, bool stop_on_trx_failed, const std::string& peer_endpoint, unsigned short port) - : trx_generator_base(chain_id_in, contract_owner_account, trx_expr, lib_id_str, log_dir, stop_on_trx_failed, peer_endpoint, port), _abi_data_file_path(abi_data_file), _auth_account(auth_account), - _action(action_name), _action_data_file_or_str(action_data_file_or_str), _private_key(fc::crypto::private_key(private_key_str)) {} + void trx_generator::locate_key_words_in_action_mvo(std::vector& acct_gen_fields_out, fc::mutable_variant_object& action_mvo, const std::string& key_word) { + for (const mutable_variant_object::entry& e: action_mvo) { + if (e.value().get_type() == fc::variant::string_type && e.value() == key_word) { + acct_gen_fields_out.emplace_back(e.key()); + } else if (e.value().get_type() == fc::variant::object_type) { + auto inner_mvo = fc::mutable_variant_object(e.value()); + locate_key_words_in_action_mvo(acct_gen_fields_out, inner_mvo, key_word); + } + } + } + + void trx_generator::locate_key_words_in_action_array(std::map>& acct_gen_fields_out, fc::variants& action_array, const std::string& key_word) { + for (size_t i = 0; i < action_array.size(); ++i) { + auto action_mvo = fc::mutable_variant_object(action_array[i]); + locate_key_words_in_action_mvo(acct_gen_fields_out[i], action_mvo, key_word); + } + } + + void trx_generator::update_key_word_fields_in_sub_action(const std::string& key, fc::mutable_variant_object& action_mvo, const std::string& action_inner_key, const std::string& key_word) { + if (action_mvo.find(action_inner_key) != action_mvo.end()) { + if (action_mvo[action_inner_key].get_object().find(key) != action_mvo[action_inner_key].get_object().end()) { + fc::mutable_variant_object inner_mvo = fc::mutable_variant_object(action_mvo[action_inner_key].get_object()); + inner_mvo.set(key, key_word); + action_mvo.set(action_inner_key, std::move(inner_mvo)); + } + } + } + + void trx_generator::update_key_word_fields_in_action(std::vector& acct_gen_fields, fc::mutable_variant_object& action_mvo, const std::string& key_word) { + for (const auto& key: acct_gen_fields) { + if (action_mvo.find(key) != action_mvo.end()) { + action_mvo.set(key, key_word); + } else { + for (const auto& e: action_mvo) { + if (e.value().get_type() == fc::variant::object_type) { + update_key_word_fields_in_sub_action(key, action_mvo, e.key(), key_word); + } + } + } + } + } + + void trx_generator::update_resign_transaction(signed_transaction& trx, const fc::crypto::private_key& priv_key, uint64_t& nonce_prefix, uint64_t& nonce, const fc::microseconds& trx_expiration, const chain_id_type& chain_id, const block_id_type& last_irr_block_id) { + trx.actions.clear(); + update_actions(); + for (const auto& act: _actions) { + trx.actions.emplace_back(act); + } + trx_generator_base::update_resign_transaction(trx, priv_key, nonce_prefix, nonce, trx_expiration, chain_id, last_irr_block_id); + } + + trx_generator::trx_generator(uint16_t generator_id, const std::string& chain_id_in, const std::string& abi_data_file, const std::string& contract_owner_account, + const std::string& actions_data_json_file_or_str, const std::string& actions_auths_json_file_or_str, + const fc::microseconds& trx_expr, const std::string& lib_id_str, const std::string& log_dir, bool stop_on_trx_failed, + const std::string& peer_endpoint, unsigned short port) + : trx_generator_base(generator_id, chain_id_in, contract_owner_account, trx_expr, lib_id_str, log_dir, stop_on_trx_failed, peer_endpoint, port), + _abi_data_file_path(abi_data_file), + _actions_data_json_file_or_str(actions_data_json_file_or_str), _actions_auths_json_file_or_str(actions_auths_json_file_or_str), + _acct_name_generator() {} + + void trx_generator::update_actions() { + _actions.clear(); + + if (!_acct_gen_fields.empty()) { + std::string generated_account_name = _acct_name_generator.calc_name(); + _acct_name_generator.increment(); + + for (auto const& [key, val] : _acct_gen_fields) { + update_key_word_fields_in_action(_acct_gen_fields.at(key), _unpacked_actions.at(key), generated_account_name); + } + } + + for (const auto& action_mvo : _unpacked_actions) { + chain::name action_name = chain::name(action_mvo["actionName"].as_string()); + chain::name action_auth_acct = chain::name(action_mvo["actionAuthAcct"].as_string()); + bytes packed_action_data; + try { + auto action_type = _abi.get_action_type( action_name ); + FC_ASSERT( !action_type.empty(), "Unknown action ${action} in contract ${contract}", ("action", action_name)( "contract", action_auth_acct )); + packed_action_data = _abi.variant_to_binary( action_type, action_mvo["actionData"], abi_serializer::create_yield_function( abi_serializer_max_time ) ); + } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse unpacked action data JSON") + + eosio::chain::action act; + act.account = _contract_owner_account; + act.name = action_name; + + chain::name auth_actor = chain::name(action_mvo["authorization"].get_object()["actor"].as_string()); + chain::name auth_perm = chain::name(action_mvo["authorization"].get_object()["permission"].as_string()); + + act.authorization = vector{{auth_actor, auth_perm}}; + act.data = std::move(packed_action_data); + _actions.emplace_back(std::move(act)); + } + } bool trx_generator::setup() { _nonce_prefix = 0; @@ -179,31 +267,47 @@ namespace eosio::testing { stop_generation(); ilog("Create Initial Transaction with action data."); - abi_serializer abi = abi_serializer(fc::json::from_file(_abi_data_file_path).as(), abi_serializer::create_yield_function( abi_serializer_max_time )); - fc::variant unpacked_action_data_json = json_from_file_or_string(_action_data_file_or_str); - ilog("action data variant: ${data}", ("data", fc::json::to_pretty_string(unpacked_action_data_json))); + _abi = abi_serializer(fc::json::from_file(_abi_data_file_path).as(), abi_serializer::create_yield_function( abi_serializer_max_time )); + fc::variant unpacked_actions_data_json = json_from_file_or_string(_actions_data_json_file_or_str); + fc::variant unpacked_actions_auths_data_json = json_from_file_or_string(_actions_auths_json_file_or_str); + ilog("Loaded actions data: ${data}", ("data", fc::json::to_pretty_string(unpacked_actions_data_json))); + ilog("Loaded actions auths data: ${auths}", ("auths", fc::json::to_pretty_string(unpacked_actions_auths_data_json))); - bytes packed_action_data; - try { - auto action_type = abi.get_action_type( _action ); - FC_ASSERT( !action_type.empty(), "Unknown action ${action} in contract ${contract}", ("action", _action)( "contract", _auth_account )); - packed_action_data = abi.variant_to_binary( action_type, unpacked_action_data_json, abi_serializer::create_yield_function( abi_serializer_max_time ) ); + const std::string gen_acct_name_per_trx("ACCT_PER_TRX"); - } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse unpacked action data JSON") + auto action_array = unpacked_actions_data_json.get_array(); + for (size_t i =0; i < action_array.size(); ++i ) { + _unpacked_actions.emplace_back(fc::mutable_variant_object(action_array[i])); + } + locate_key_words_in_action_array(_acct_gen_fields, action_array, gen_acct_name_per_trx); - ilog("${packed_data}", ("packed_data", fc::to_hex(packed_action_data.data(), packed_action_data.size()))); + if (!_acct_gen_fields.empty()) { + ilog("Located the following account names that need to be generated and populated in each transaction:"); + for (const auto& e: _acct_gen_fields) { + ilog("acct_gen_fields entry: ${value}", ("value", e)); + } + ilog("Priming name generator for trx generator prefix."); + _acct_name_generator.setPrefix(_generator_id); + } - eosio::chain::action act; - act.account = _contract_owner_account; - act.name = _action; - act.authorization = vector{{_auth_account, config::active_name}}; - act.data = std::move(packed_action_data); + ilog("Setting up transaction signer."); + fc::crypto::private_key signer_key; + signer_key = fc::crypto::private_key(unpacked_actions_auths_data_json.get_object()[_unpacked_actions.at(0)["actionAuthAcct"].as_string()].as_string()); - _trxs.emplace_back(create_transfer_trx_w_signer(act, _private_key, ++_nonce_prefix, _nonce, _trx_expiration, _chain_id, _last_irr_block_id)); + ilog("Setting up initial transaction actions."); + update_actions(); + ilog("Initial actions (${count}):", ("count", _unpacked_actions.size())); + for (size_t i = 0; i < _unpacked_actions.size(); ++i) { + ilog("Initial action ${index}: ${act}", ("index", i)("act", fc::json::to_pretty_string(_unpacked_actions.at(i)))); + ilog("Initial action packed data ${index}: ${packed_data}", ("packed_data", fc::to_hex(_actions.at(i).data.data(), _actions.at(i).data.size()))); + } + + ilog("Populate initial transaction."); + _trxs.emplace_back(create_trx_w_actions_and_signer(_actions, signer_key, ++_nonce_prefix, _nonce, _trx_expiration, _chain_id, _last_irr_block_id)); ilog("Setup p2p transaction provider"); - ilog("Update each trx to qualify as unique and fresh timestamps, re-sign trx, and send each updated transactions via p2p transaction provider"); + ilog("Update each trx to qualify as unique and fresh timestamps and update each action with unique generated account name if necessary, re-sign trx, and send each updated transactions via p2p transaction provider"); _provider.setup(); return true; @@ -244,7 +348,7 @@ namespace eosio::testing { return true; } - void log_first_trx(const std::string& log_dir, const chain::signed_transaction& trx) { + void trx_generator_base::log_first_trx(const std::string& log_dir, const chain::signed_transaction& trx) { std::ostringstream fileName; fileName << log_dir << "/first_trx_" << getpid() << ".txt"; std::ofstream out(fileName.str()); @@ -264,7 +368,7 @@ namespace eosio::testing { void trx_generator_base::stop_generation() { ilog("Stopping transaction generation"); - if(_txcount) { + if (_txcount) { ilog("${d} transactions executed, ${t}us / transaction", ("d", _txcount)("t", _total_us / (double) _txcount)); _txcount = _total_us = 0; } diff --git a/tests/trx_generator/trx_generator.hpp b/tests/trx_generator/trx_generator.hpp index db3007672c..c00dea2805 100644 --- a/tests/trx_generator/trx_generator.hpp +++ b/tests/trx_generator/trx_generator.hpp @@ -5,6 +5,9 @@ #include #include #include +#include +#include +#include namespace eosio::testing { @@ -15,8 +18,87 @@ namespace eosio::testing { fc::crypto::private_key _signer; }; + struct action_pair_w_keys { + action_pair_w_keys(eosio::chain::action first_action, eosio::chain::action second_action, fc::crypto::private_key first_act_signer, fc::crypto::private_key second_act_signer) + : _first_act(std::move(first_action)), _second_act(std::move(second_action)), _first_act_priv_key(std::move(first_act_signer)), _second_act_priv_key(std::move(second_act_signer)) {} + + eosio::chain::action _first_act; + eosio::chain::action _second_act; + fc::crypto::private_key _first_act_priv_key; + fc::crypto::private_key _second_act_priv_key; + }; + + struct account_name_generator { + // This account_name_generator provides the means to generate 12 character account names where the left-most 2 characters are reserved + // to identify the trx generator. The right 10 characters are determined based on incrementing through the allowed char_map one at a + // before incrementing the next character to the left. + // The _name_index_vec tracks the index into the char_map for each of the 12 characters of the name. + // For example: + // Transaction Generators would create new account names as follows: + // generator ID: 5 generator ID: 41 + // 1a1111111111 2f1111111111 + // 1a1111111112 2f1111111112 + // 1a1111111113 2f1111111113 + // 1a1111111114 2f1111111114 + // 1a1111111115 2f1111111115 + // ... ... + // 1a111111111z 2f111111111z + // 1a1111111121 2f1111111121 + // 1a1111111122 2f1111111122 + account_name_generator() : _name_index_vec(acct_name_len, 0) {} + + static constexpr char char_map[] = "12345abcdefghijklmnopqrstuvwxyz"; + static constexpr int acct_name_char_cnt = sizeof(char_map) - 1; + const int acct_name_len = 12; + + // Reserving the first 2 characters in the 12 char account name to identify the transaction generator sending the trx to create the new account. + // So 31 ^ 2 gives 961 (so w/0 based index, 960) since more than 31 generators may be desired, but > 961 is likely unwarranted. + // This provides an easy way to deduplicate the names being generated by the parallel trx generators. + const int prefix_max = 960; + std::vector _name_index_vec; + + void increment() { + increment(_name_index_vec.size() - 1); + } + + void incrementPrefix() { + increment(1); + } + + void setPrefix(int generator_id) { + if (generator_id > prefix_max) { + elog("Account Name Generator Prefix above allowable ${max}", ("max", prefix_max)); + return; + } + _name_index_vec[0] = 0; + _name_index_vec[1] = 0; + for(int i = 0; i < generator_id; i++) { + incrementPrefix(); + } + }; + + std::string calc_name() { + std::string name; + name.reserve(12); + for(auto i: _name_index_vec) { + name += char_map[i]; + } + return name; + } + + private: + void increment(int index) { + _name_index_vec[index]++; + if(_name_index_vec[index] >= acct_name_char_cnt) { + _name_index_vec[index] = 0; + increment(index - 1); + } + } + }; + struct trx_generator_base { p2p_trx_provider _provider; + uint16_t _generator_id = 0; eosio::chain::chain_id_type _chain_id; eosio::chain::name _contract_owner_account; fc::microseconds _trx_expiration; @@ -33,12 +115,25 @@ namespace eosio::testing { bool _stop_on_trx_failed = true; - trx_generator_base(std::string chain_id_in, std::string contract_owner_account, fc::microseconds trx_expr, std::string lib_id_str, std::string log_dir, bool stop_on_trx_failed, + trx_generator_base(uint16_t generator_id, const std::string& chain_id_in, const std::string& contract_owner_account, const fc::microseconds& trx_expr, const std::string& lib_id_str, const std::string& log_dir, bool stop_on_trx_failed, const std::string& peer_endpoint="127.0.0.1", unsigned short port=9876); + virtual ~trx_generator_base() = default; + + virtual void update_resign_transaction(eosio::chain::signed_transaction& trx, const fc::crypto::private_key& priv_key, uint64_t& nonce_prefix, uint64_t& nonce, + const fc::microseconds& trx_expiration, const eosio::chain::chain_id_type& chain_id, const eosio::chain::block_id_type& last_irr_block_id); + void push_transaction(p2p_trx_provider& provider, signed_transaction_w_signer& trx, uint64_t& nonce_prefix, uint64_t& nonce, const fc::microseconds& trx_expiration, const eosio::chain::chain_id_type& chain_id, const eosio::chain::block_id_type& last_irr_block_id); + + void set_transaction_headers(eosio::chain::transaction& trx, const eosio::chain::block_id_type& last_irr_block_id, const fc::microseconds& expiration, uint32_t delay_sec = 0); + + signed_transaction_w_signer create_trx_w_actions_and_signer(std::vector act, const fc::crypto::private_key& priv_key, uint64_t& nonce_prefix, uint64_t& nonce, + const fc::microseconds& trx_expiration, const eosio::chain::chain_id_type& chain_id, const eosio::chain::block_id_type& last_irr_block_id); + + void log_first_trx(const std::string& log_dir, const eosio::chain::signed_transaction& trx); + bool generate_and_send(); bool tear_down(); void stop_generation(); @@ -49,29 +144,51 @@ namespace eosio::testing { const std::vector _accts; std::vector _private_keys_str_vector; - transfer_trx_generator(std::string chain_id_in, std::string contract_owner_account, const std::vector& accts, - fc::microseconds trx_expr, const std::vector& private_keys_str_vector, std::string lib_id_str, std::string log_dir, bool stop_on_trx_failed, + transfer_trx_generator(uint16_t generator_id, const std::string& chain_id_in, const std::string& contract_owner_account, const std::vector& accts, + const fc::microseconds& trx_expr, const std::vector& private_keys_str_vector, const std::string& lib_id_str, const std::string& log_dir, bool stop_on_trx_failed, const std::string& peer_endpoint="127.0.0.1", unsigned short port=9876); std::vector get_accounts(const std::vector& account_str_vector); std::vector get_private_keys(const std::vector& priv_key_str_vector); + std::vector create_initial_transfer_transactions(const std::vector& action_pairs_vector, uint64_t& nonce_prefix, uint64_t& nonce, const fc::microseconds& trx_expiration, const eosio::chain::chain_id_type& chain_id, const eosio::chain::block_id_type& last_irr_block_id); + eosio::chain::bytes make_transfer_data(const eosio::chain::name& from, const eosio::chain::name& to, const eosio::chain::asset& quantity, const std::string& memo); + auto make_transfer_action(eosio::chain::name account, eosio::chain::name from, eosio::chain::name to, eosio::chain::asset quantity, std::string memo); + std::vector create_initial_transfer_actions(const std::string& salt, const uint64_t& period, const eosio::chain::name& contract_owner_account, + const std::vector& accounts, const std::vector& priv_keys); + bool setup(); }; struct trx_generator : public trx_generator_base{ std::string _abi_data_file_path; - eosio::chain::name _auth_account; - eosio::chain::name _action; - std::string _action_data_file_or_str; - fc::crypto::private_key _private_key; + std::string _actions_data_json_file_or_str; + std::string _actions_auths_json_file_or_str; + account_name_generator _acct_name_generator; + + eosio::chain::abi_serializer _abi; + std::vector _unpacked_actions; + std::map> _acct_gen_fields; + std::vector _actions; const fc::microseconds abi_serializer_max_time = fc::seconds(10); // No risk to client side serialization taking a long time - trx_generator(std::string chain_id_in, const std::string& abi_data_file, std::string contract_owner_account, std::string auth_account, std::string action_name, const std::string& action_data_file_or_str, - fc::microseconds trx_expr, const std::string& private_key_str, std::string lib_id_str, std::string log_dir, bool stop_on_trx_failed, + trx_generator(uint16_t generator_id, const std::string& chain_id_in, const std::string& abi_data_file, const std::string& contract_owner_account, + const std::string& actions_data_json_file_or_str, const std::string& actions_auths_json_file_or_str, + const fc::microseconds& trx_expr, const std::string& lib_id_str, const std::string& log_dir, bool stop_on_trx_failed, const std::string& peer_endpoint="127.0.0.1", unsigned short port=9876); + void locate_key_words_in_action_mvo(std::vector& acct_gen_fields_out, fc::mutable_variant_object& action_mvo, const std::string& key_word); + void locate_key_words_in_action_array(std::map>& acct_gen_fields_out, fc::variants& action_array, const std::string& key_word); + void update_key_word_fields_in_sub_action(const std::string& key, fc::mutable_variant_object& action_mvo, const std::string& action_inner_key, const std::string& key_word); + void update_key_word_fields_in_action(std::vector& acct_gen_fields, fc::mutable_variant_object& action_mvo, const std::string& key_word); + + void update_actions(); + virtual void update_resign_transaction(eosio::chain::signed_transaction& trx, const fc::crypto::private_key& priv_key, uint64_t& nonce_prefix, uint64_t& nonce, + const fc::microseconds& trx_expiration, const eosio::chain::chain_id_type& chain_id, const eosio::chain::block_id_type& last_irr_block_id); + + fc::variant json_from_file_or_string(const std::string& file_or_str, fc::json::parse_type ptype = fc::json::parse_type::legacy_parser); + bool setup(); }; } diff --git a/tests/trx_generator/trx_generator_tests.cpp b/tests/trx_generator/trx_generator_tests.cpp index 9bb1a3804a..c5bb3832fd 100644 --- a/tests/trx_generator/trx_generator_tests.cpp +++ b/tests/trx_generator/trx_generator_tests.cpp @@ -60,7 +60,7 @@ BOOST_AUTO_TEST_CASE(tps_short_run_low_tps) fc::time_point start = fc::time_point::now(); t1.run(); fc::time_point end = fc::time_point::now(); - fc::microseconds runtime_us = end.time_since_epoch() - start.time_since_epoch() ; + fc::microseconds runtime_us = end.time_since_epoch() - start.time_since_epoch(); BOOST_REQUIRE_EQUAL(generator->_calls.size(), expected_trxs); BOOST_REQUIRE_GT(runtime_us.count(), minimum_runtime_us); @@ -75,8 +75,8 @@ BOOST_AUTO_TEST_CASE(tps_short_run_high_tps) constexpr uint64_t expected_runtime_us = test_duration_s * 1000000; constexpr uint64_t allowable_runtime_deviation_per = 20; constexpr uint64_t allowable_runtime_deviation_us = expected_runtime_us / allowable_runtime_deviation_per; - constexpr uint64_t minimum_runtime_us = expected_runtime_us - allowable_runtime_deviation_us; - constexpr uint64_t maximum_runtime_us = expected_runtime_us + allowable_runtime_deviation_us; + constexpr int64_t minimum_runtime_us = expected_runtime_us - allowable_runtime_deviation_us; + constexpr int64_t maximum_runtime_us = expected_runtime_us + allowable_runtime_deviation_us; std::shared_ptr generator = std::make_shared(expected_trxs); std::shared_ptr monitor = std::make_shared(expected_trxs); @@ -87,17 +87,16 @@ BOOST_AUTO_TEST_CASE(tps_short_run_high_tps) fc::time_point start = fc::time_point::now(); t1.run(); fc::time_point end = fc::time_point::now(); - fc::microseconds runtime_us = end.time_since_epoch() - start.time_since_epoch() ; + fc::microseconds runtime_us = end.time_since_epoch() - start.time_since_epoch(); BOOST_REQUIRE_EQUAL(generator->_calls.size(), expected_trxs); BOOST_REQUIRE_GT(runtime_us.count(), minimum_runtime_us); if (runtime_us.count() > maximum_runtime_us) { ilog("couldn't sustain transaction rate. ran ${rt}us vs expected max ${mx}us", - ("rt", runtime_us.count())("mx", maximum_runtime_us ) ); + ("rt", runtime_us.count())("mx", maximum_runtime_us)); BOOST_REQUIRE_LT(monitor->_calls.back().time_to_next_trx_us, 0); } - } BOOST_AUTO_TEST_CASE(tps_short_run_med_tps_med_delay) @@ -109,8 +108,8 @@ BOOST_AUTO_TEST_CASE(tps_short_run_med_tps_med_delay) constexpr uint64_t expected_runtime_us = test_duration_s * 1000000; constexpr uint64_t allowable_runtime_deviation_per = 20; constexpr uint64_t allowable_runtime_deviation_us = expected_runtime_us / allowable_runtime_deviation_per; - constexpr uint64_t minimum_runtime_us = expected_runtime_us - allowable_runtime_deviation_us; - constexpr uint64_t maximum_runtime_us = expected_runtime_us + allowable_runtime_deviation_us; + constexpr int64_t minimum_runtime_us = expected_runtime_us - allowable_runtime_deviation_us; + constexpr int64_t maximum_runtime_us = expected_runtime_us + allowable_runtime_deviation_us; std::shared_ptr generator = std::make_shared(expected_trxs, trx_delay_us); std::shared_ptr monitor = std::make_shared(expected_trxs); @@ -121,14 +120,14 @@ BOOST_AUTO_TEST_CASE(tps_short_run_med_tps_med_delay) fc::time_point start = fc::time_point::now(); t1.run(); fc::time_point end = fc::time_point::now(); - fc::microseconds runtime_us = end.time_since_epoch() - start.time_since_epoch() ; + fc::microseconds runtime_us = end.time_since_epoch() - start.time_since_epoch(); BOOST_REQUIRE_EQUAL(generator->_calls.size(), expected_trxs); BOOST_REQUIRE_GT(runtime_us.count(), minimum_runtime_us); if (runtime_us.count() > maximum_runtime_us) { ilog("couldn't sustain transaction rate. ran ${rt}us vs expected max ${mx}us", - ("rt", runtime_us.count())("mx", maximum_runtime_us ) ); + ("rt", runtime_us.count())("mx", maximum_runtime_us)); BOOST_REQUIRE_LT(monitor->_calls.back().time_to_next_trx_us, 0); } } @@ -142,8 +141,8 @@ BOOST_AUTO_TEST_CASE(tps_med_run_med_tps_med_delay) constexpr uint64_t expected_runtime_us = test_duration_s * 1000000; constexpr uint64_t allowable_runtime_deviation_per = 20; constexpr uint64_t allowable_runtime_deviation_us = expected_runtime_us / allowable_runtime_deviation_per; - constexpr uint64_t minimum_runtime_us = expected_runtime_us - allowable_runtime_deviation_us; - constexpr uint64_t maximum_runtime_us = expected_runtime_us + allowable_runtime_deviation_us; + constexpr int64_t minimum_runtime_us = expected_runtime_us - allowable_runtime_deviation_us; + constexpr int64_t maximum_runtime_us = expected_runtime_us + allowable_runtime_deviation_us; std::shared_ptr generator = std::make_shared(expected_trxs, trx_delay_us); std::shared_ptr monitor = std::make_shared(expected_trxs); @@ -154,17 +153,18 @@ BOOST_AUTO_TEST_CASE(tps_med_run_med_tps_med_delay) fc::time_point start = fc::time_point::now(); t1.run(); fc::time_point end = fc::time_point::now(); - fc::microseconds runtime_us = end.time_since_epoch() - start.time_since_epoch() ; + fc::microseconds runtime_us = end.time_since_epoch() - start.time_since_epoch(); BOOST_REQUIRE_EQUAL(generator->_calls.size(), expected_trxs); BOOST_REQUIRE_GT(runtime_us.count(), minimum_runtime_us); if (runtime_us.count() > maximum_runtime_us) { ilog("couldn't sustain transaction rate. ran ${rt}us vs expected max ${mx}us", - ("rt", runtime_us.count())("mx", maximum_runtime_us ) ); + ("rt", runtime_us.count())("mx", maximum_runtime_us)); BOOST_REQUIRE_LT(monitor->_calls.back().time_to_next_trx_us, 0); } } + BOOST_AUTO_TEST_CASE(tps_cant_keep_up) { constexpr uint32_t test_duration_s = 5; @@ -174,8 +174,8 @@ BOOST_AUTO_TEST_CASE(tps_cant_keep_up) constexpr uint64_t expected_runtime_us = test_duration_s * 1000000; constexpr uint64_t allowable_runtime_deviation_per = 20; constexpr uint64_t allowable_runtime_deviation_us = expected_runtime_us / allowable_runtime_deviation_per; - constexpr uint64_t minimum_runtime_us = expected_runtime_us - allowable_runtime_deviation_us; - constexpr uint64_t maximum_runtime_us = expected_runtime_us + allowable_runtime_deviation_us; + constexpr int64_t minimum_runtime_us = expected_runtime_us - allowable_runtime_deviation_us; + constexpr int64_t maximum_runtime_us = expected_runtime_us + allowable_runtime_deviation_us; std::shared_ptr generator = std::make_shared(expected_trxs, trx_delay_us); std::shared_ptr monitor = std::make_shared(expected_trxs); @@ -186,17 +186,18 @@ BOOST_AUTO_TEST_CASE(tps_cant_keep_up) fc::time_point start = fc::time_point::now(); t1.run(); fc::time_point end = fc::time_point::now(); - fc::microseconds runtime_us = end.time_since_epoch() - start.time_since_epoch() ; + fc::microseconds runtime_us = end.time_since_epoch() - start.time_since_epoch(); BOOST_REQUIRE_EQUAL(generator->_calls.size(), expected_trxs); BOOST_REQUIRE_GT(runtime_us.count(), minimum_runtime_us); if (runtime_us.count() > maximum_runtime_us) { ilog("couldn't sustain transaction rate. ran ${rt}us vs expected max ${mx}us", - ("rt", runtime_us.count())("mx", maximum_runtime_us ) ); + ("rt", runtime_us.count())("mx", maximum_runtime_us)); BOOST_REQUIRE_LT(monitor->_calls.back().time_to_next_trx_us, 0); } } + BOOST_AUTO_TEST_CASE(tps_med_run_med_tps_30us_delay) { constexpr uint32_t test_duration_s = 15; @@ -206,8 +207,8 @@ BOOST_AUTO_TEST_CASE(tps_med_run_med_tps_30us_delay) constexpr uint64_t expected_runtime_us = test_duration_s * 1000000; constexpr uint64_t allowable_runtime_deviation_per = 20; constexpr uint64_t allowable_runtime_deviation_us = expected_runtime_us / allowable_runtime_deviation_per; - constexpr uint64_t minimum_runtime_us = expected_runtime_us - allowable_runtime_deviation_us; - constexpr uint64_t maximum_runtime_us = expected_runtime_us + allowable_runtime_deviation_us; + constexpr int64_t minimum_runtime_us = expected_runtime_us - allowable_runtime_deviation_us; + constexpr int64_t maximum_runtime_us = expected_runtime_us + allowable_runtime_deviation_us; std::shared_ptr generator = std::make_shared(expected_trxs, trx_delay_us); std::shared_ptr monitor = std::make_shared(expected_trxs); @@ -218,17 +219,16 @@ BOOST_AUTO_TEST_CASE(tps_med_run_med_tps_30us_delay) fc::time_point start = fc::time_point::now(); t1.run(); fc::time_point end = fc::time_point::now(); - fc::microseconds runtime_us = end.time_since_epoch() - start.time_since_epoch() ; + fc::microseconds runtime_us = end.time_since_epoch() - start.time_since_epoch(); BOOST_REQUIRE_EQUAL(generator->_calls.size(), expected_trxs); BOOST_REQUIRE_GT(runtime_us.count(), minimum_runtime_us); if (runtime_us.count() > maximum_runtime_us) { ilog("couldn't sustain transaction rate. ran ${rt}us vs expected max ${mx}us", - ("rt", runtime_us.count())("mx", maximum_runtime_us ) ); + ("rt", runtime_us.count())("mx", maximum_runtime_us)); BOOST_REQUIRE_LT(monitor->_calls.back().time_to_next_trx_us, 0); } - } BOOST_AUTO_TEST_CASE(tps_performance_monitor_during_spin_up) @@ -241,11 +241,11 @@ BOOST_AUTO_TEST_CASE(tps_performance_monitor_during_spin_up) stats.trxs_sent = 90; // behind, but still within spin up window - stats.last_run = fc::time_point{fc::microseconds{100000}}; + stats.last_run = fc::time_point{fc::microseconds{100000}}; BOOST_REQUIRE(monitor.monitor_test(stats)); // violation, but still within spin up window - stats.last_run = fc::time_point{fc::microseconds{1100000}}; + stats.last_run = fc::time_point{fc::microseconds{1100000}}; BOOST_REQUIRE(monitor.monitor_test(stats)); } @@ -259,11 +259,11 @@ BOOST_AUTO_TEST_CASE(tps_performance_monitor_outside_spin_up) stats.trxs_sent = 90; // behind, out of spin up window - stats.last_run = fc::time_point{fc::microseconds{5500000}}; + stats.last_run = fc::time_point{fc::microseconds{5500000}}; BOOST_REQUIRE(monitor.monitor_test(stats)); // violation, out of spin up window - stats.last_run = fc::time_point{fc::microseconds{6600000}}; + stats.last_run = fc::time_point{fc::microseconds{6600000}}; BOOST_REQUIRE(!monitor.monitor_test(stats)); } @@ -277,25 +277,25 @@ BOOST_AUTO_TEST_CASE(tps_performance_monitor_outside_spin_up_within_limit) stats.trxs_sent = 90; // outside of limit, out of spin up window - stats.last_run = fc::time_point{fc::microseconds{5500000}}; + stats.last_run = fc::time_point{fc::microseconds{5500000}}; BOOST_REQUIRE(monitor.monitor_test(stats)); // outside of limit, less than max violation duration - stats.last_run = fc::time_point{fc::microseconds{6000000}}; + stats.last_run = fc::time_point{fc::microseconds{6000000}}; BOOST_REQUIRE(monitor.monitor_test(stats)); stats.trxs_sent = 98; // behind, but within limit, out of spin up window - stats.last_run = fc::time_point{fc::microseconds{6600000}}; + stats.last_run = fc::time_point{fc::microseconds{6600000}}; BOOST_REQUIRE(monitor.monitor_test(stats)); stats.expected_sent = 150; // outside of limit again, out of spin up window - stats.last_run = fc::time_point{fc::microseconds{7000000}}; + stats.last_run = fc::time_point{fc::microseconds{7000000}}; BOOST_REQUIRE(monitor.monitor_test(stats)); // outside of limit for too long - stats.last_run = fc::time_point{fc::microseconds{8100000}}; + stats.last_run = fc::time_point{fc::microseconds{8100000}}; BOOST_REQUIRE(!monitor.monitor_test(stats)); } @@ -316,30 +316,134 @@ BOOST_AUTO_TEST_CASE(tps_cant_keep_up_monitored) fc::time_point start = fc::time_point::now(); t1.run(); fc::time_point end = fc::time_point::now(); - fc::microseconds runtime_us = end.time_since_epoch() - start.time_since_epoch() ; + fc::microseconds runtime_us = end.time_since_epoch() - start.time_since_epoch(); BOOST_REQUIRE_LT(runtime_us.count(), expected_runtime_us); BOOST_REQUIRE_LT(generator->_calls.size(), expected_trxs); - } BOOST_AUTO_TEST_CASE(trx_generator_constructor) { + uint16_t generator_id = 1; std::string chain_id = "999"; - std::string contract_owner_account = "eosio"; - std::string acct = "aaa"; - std::string action_name = "transfer"; - const std::string action_data = "{\"from\":\"aaa\",\"to\":\"bbb\",\"quantity\":\"10.0000 SYS\",\"memo\":\"hello\"}"; const std::string abi_file = "../../unittests/contracts/eosio.token/eosio.token.abi"; + std::string contract_owner_account = "eosio"; + const std::string actions_data = "[{\"actionAuthAcct\": \"testacct1\",\"actionName\": \"transfer\",\"authorization\": {\"actor\": \"testacct1\",\"permission\": \"active\"},\"actionData\": {\"from\": \"testacct1\",\"to\": \"testacct2\",\"quantity\": \"0.0001 CUR\",\"memo\": \"transaction specified\"}}]"; + const std::string action_auths = "{\"testacct1\":\"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\",\"testacct2\":\"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\",\"eosio\":\"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\"}"; fc::microseconds trx_expr = fc::seconds(3600); std::string log_dir = "."; std::string lib_id_str = "00000062989f69fd251df3e0b274c3364ffc2f4fce73de3f1c7b5e11a4c92f21"; - std::string private_key_str = "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"; + bool stop_on_trx_failed = true; std::string peer_endpoint = "127.0.0.1"; unsigned short port = 9876; - bool stop_on_trx_failed = true; - auto generator = trx_generator(chain_id, abi_file, contract_owner_account, acct, action_name, action_data, trx_expr, private_key_str, lib_id_str, log_dir, stop_on_trx_failed, peer_endpoint, port); + auto generator = trx_generator(generator_id, chain_id, abi_file, contract_owner_account, + actions_data, action_auths, + trx_expr, lib_id_str, log_dir, stop_on_trx_failed, peer_endpoint, port); +} + +BOOST_AUTO_TEST_CASE(account_name_generator_tests) +{ + auto acct_gen = account_name_generator(); + BOOST_REQUIRE_EQUAL(acct_gen.calc_name(), "111111111111"); + + //Test account name prefixes for differentiating between transaction generator instances + acct_gen.setPrefix(1); + BOOST_REQUIRE_EQUAL(acct_gen.calc_name(), "121111111111"); + acct_gen.setPrefix(30); + BOOST_REQUIRE_EQUAL(acct_gen.calc_name(), "1z1111111111"); + acct_gen.setPrefix(31); + BOOST_REQUIRE_EQUAL(acct_gen.calc_name(), "211111111111"); + acct_gen.setPrefix(960); + BOOST_REQUIRE_EQUAL(acct_gen.calc_name(), "zz1111111111"); + + //Test account name generation + std::vector expected = { + "zz1111111111", + "zz1111111112", + "zz1111111113", + "zz1111111114", + "zz1111111115", + "zz111111111a", + "zz111111111b", + "zz111111111c", + "zz111111111d", + "zz111111111e", + "zz111111111f", + "zz111111111g", + "zz111111111h", + "zz111111111i", + "zz111111111j", + "zz111111111k", + "zz111111111l", + "zz111111111m", + "zz111111111n", + "zz111111111o", + "zz111111111p", + "zz111111111q", + "zz111111111r", + "zz111111111s", + "zz111111111t", + "zz111111111u", + "zz111111111v", + "zz111111111w", + "zz111111111x", + "zz111111111y", + "zz111111111z", + "zz1111111121", + "zz1111111122"}; + for(size_t i = 0; i < expected.size(); ++i) { + BOOST_REQUIRE_EQUAL(acct_gen.calc_name(), expected.at(i)); + acct_gen.increment(); + } + + + //Test account name generation starting at 31 ^ 5 - 1 = 28629150 + std::vector expected2 = { + "1211111zzzzz", + "121111211111", + "121111211112", + "121111211113", + "121111211114", + "121111211115", + "12111121111a", + "12111121111b", + "12111121111c", + "12111121111d", + "12111121111e", + "12111121111f", + "12111121111g", + "12111121111h", + "12111121111i", + "12111121111j", + "12111121111k", + "12111121111l", + "12111121111m", + "12111121111n", + "12111121111o", + "12111121111p", + "12111121111q", + "12111121111r", + "12111121111s", + "12111121111t", + "12111121111u", + "12111121111v", + "12111121111w", + "12111121111x", + "12111121111y", + "12111121111z", + "121111211121", + "121111211122"}; + auto acct_gen2 = account_name_generator(); + acct_gen2.setPrefix(1); + int initialVal = 28629150; + for(int i = 0; i < initialVal; ++i) { + acct_gen2.increment(); + } + for(size_t i = 0; i < expected2.size(); ++i) { + BOOST_REQUIRE_EQUAL(acct_gen2.calc_name(), expected2.at(i)); + acct_gen2.increment(); + } } BOOST_AUTO_TEST_SUITE_END()