From 737bd9e4f197357e9170d7c554ef10038985280f Mon Sep 17 00:00:00 2001 From: mmsqe Date: Wed, 21 Aug 2024 09:40:35 +0800 Subject: [PATCH] Problem: fund_test_accounts is slow with big num_accounts in testground (#1551) * Problem: fund_test_accounts is slow with big num_accounts in testground fund by batch * fire txs to validator * log txs * use rocksdb * cleanup * larger timeout * more balance * larger timeout * Revert "larger timeout" This reverts commit 33bce0bbaab0f8ce9e9f7e494a90038163027b93. --- testground/benchmark/benchmark/peer.py | 3 +- testground/benchmark/benchmark/sendtx.py | 23 +++++-- testground/benchmark/benchmark/stateless.py | 11 ++-- testground/benchmark/benchmark/utils.py | 71 +++++++++++++++++++++ 4 files changed, 97 insertions(+), 11 deletions(-) diff --git a/testground/benchmark/benchmark/peer.py b/testground/benchmark/benchmark/peer.py index ea25490da7..900eef293f 100644 --- a/testground/benchmark/benchmark/peer.py +++ b/testground/benchmark/benchmark/peer.py @@ -13,7 +13,7 @@ VAL_ACCOUNT = "validator" VAL_INITIAL_AMOUNT = "100000000000000000000basecro" VAL_STAKED_AMOUNT = "10000000000000000000basecro" -ACC_INITIAL_AMOUNT = "100000000000000000000000basecro" +ACC_INITIAL_AMOUNT = "10000000000000000000000000basecro" MEMPOOL_SIZE = 50000 DEFAULT_DENOM = "basecro" VALIDATOR_GROUP = "validators" @@ -119,6 +119,7 @@ def gen_genesis(cli: ChainCommand, leader_home: Path, peers: List[PeerPacket]): def patch_configs(home: Path, group: str, peers: str, block_executor: str): # update persistent_peers and other configs in config.toml config_patch = { + "db_backend": "rocksdb", "p2p.persistent_peers": peers, "p2p.addr_book_strict": False, "mempool.recheck": "false", diff --git a/testground/benchmark/benchmark/sendtx.py b/testground/benchmark/benchmark/sendtx.py index 8dd74fc565..b9fefa5017 100644 --- a/testground/benchmark/benchmark/sendtx.py +++ b/testground/benchmark/benchmark/sendtx.py @@ -4,14 +4,22 @@ import web3 from eth_account import Account -from .utils import export_eth_account, send_transaction +from .utils import ( + broadcast_tx_json, + build_batch_tx, + export_eth_account, + send_transaction, +) TEST_AMOUNT = 1000000000000000000 GAS_PRICE = 1000000000 -def fund_test_accounts(w3, from_account, num_accounts) -> [Account]: +def fund_test_accounts(cli, w3, from_account, num_accounts, **kwargs) -> [Account]: accounts = [] + sender = from_account.address + nonce = w3.eth.get_transaction_count(sender) + txs = [] for i in range(num_accounts): acct = Account.create() tx = { @@ -19,11 +27,14 @@ def fund_test_accounts(w3, from_account, num_accounts) -> [Account]: "value": TEST_AMOUNT, "gas": 21000, "gasPrice": GAS_PRICE, + "nonce": nonce + i, } - receipt = send_transaction(w3, tx, from_account, wait=True) - assert receipt.status == 1 - print("fund test account", acct.address, "balance", TEST_AMOUNT) + txs.append(tx) accounts.append(acct) + cosmos_tx, hashes = build_batch_tx(w3, cli, txs, from_account, **kwargs) + print("batch size", len(hashes)) + rsp = broadcast_tx_json(cli, cosmos_tx, **kwargs) + assert rsp["code"] == 0, rsp["raw_log"] return accounts @@ -77,7 +88,7 @@ def generate_load(cli, num_accounts, num_txs, **kwargs): w3 = web3.Web3(web3.providers.HTTPProvider("http://localhost:8545")) assert w3.eth.chain_id == 777 genesis_account = export_eth_account(cli, "account", **kwargs) - accounts = fund_test_accounts(w3, genesis_account, num_accounts) + accounts = fund_test_accounts(cli, w3, genesis_account, num_accounts, **kwargs) with ThreadPoolExecutor(max_workers=num_accounts) as executor: futs = (executor.submit(sendtx, w3, acct, num_txs) for acct in accounts) for fut in as_completed(futs): diff --git a/testground/benchmark/benchmark/stateless.py b/testground/benchmark/benchmark/stateless.py index f5fe43341c..0a983c7e92 100644 --- a/testground/benchmark/benchmark/stateless.py +++ b/testground/benchmark/benchmark/stateless.py @@ -150,13 +150,16 @@ def run( wait_for_port(26657) wait_for_port(8545) wait_for_block(cli, 3) - + wait_for_w3() + generate_load( + cli, cfg["num_accounts"], cfg["num_txs"], home=home, output="json" + ) if group == VALIDATOR_GROUP: # validators quit when the chain is idle for a while detect_idle(20, 20) else: - wait_for_w3() - generate_load(cli, cfg["num_accounts"], cfg["num_txs"], home=home) + # wait more blocks to finish all tasks + detect_idle(4, 4) with (home / "block_stats.log").open("w") as logfile: dump_block_stats(logfile) @@ -282,7 +285,7 @@ def wait_for_peers(home: Path): for peer in peers.split(","): host = peer.split("@", 1)[1].split(":", 1)[0] print("wait for peer to be ready:", host) - wait_for_port(ECHO_SERVER_PORT, host=host, timeout=600) + wait_for_port(ECHO_SERVER_PORT, host=host, timeout=2400) def dump_block_stats(fp): diff --git a/testground/benchmark/benchmark/utils.py b/testground/benchmark/benchmark/utils.py index 1e9392eca3..4c46937c5d 100644 --- a/testground/benchmark/benchmark/utils.py +++ b/testground/benchmark/benchmark/utils.py @@ -1,5 +1,7 @@ import json import socket +import subprocess +import tempfile import time from pathlib import Path @@ -111,3 +113,72 @@ def send_transaction(w3, tx, acct, wait=True): def export_eth_account(cli, name: str, **kwargs) -> Account: kwargs.setdefault("keyring_backend", "test") return Account.from_key(cli("keys", "unsafe-export-eth-key", name, **kwargs)) + + +def build_batch_tx(w3, cli, txs, acct, **kwargs): + "return cosmos batch tx and eth tx hashes" + signed_txs = [sign_transaction(w3, tx, acct) for tx in txs] + tmp_txs = [ + json.loads( + cli( + "tx", + "evm", + "raw", + signed.rawTransaction.hex(), + "-y", + "--generate-only", + **kwargs, + ) + ) + for signed in signed_txs + ] + + msgs = [tx["body"]["messages"][0] for tx in tmp_txs] + fee = sum(int(tx["auth_info"]["fee"]["amount"][0]["amount"]) for tx in tmp_txs) + gas_limit = sum(int(tx["auth_info"]["fee"]["gas_limit"]) for tx in tmp_txs) + + tx_hashes = [signed.hash for signed in signed_txs] + + # build batch cosmos tx + return { + "body": { + "messages": msgs, + "memo": "", + "timeout_height": "0", + "extension_options": [ + {"@type": "/ethermint.evm.v1.ExtensionOptionsEthereumTx"} + ], + "non_critical_extension_options": [], + }, + "auth_info": { + "signer_infos": [], + "fee": { + "amount": [{"denom": "basecro", "amount": str(fee)}], + "gas_limit": str(gas_limit), + "payer": "", + "granter": "", + }, + }, + "signatures": [], + }, tx_hashes + + +def broadcast_tx_json(cli, tx, **kwargs): + with tempfile.NamedTemporaryFile("w") as fp: + json.dump(tx, fp) + fp.flush() + rsp = json.loads( + cli("tx", "broadcast", fp.name, node="tcp://127.0.0.1:26657", **kwargs) + ) + if rsp["code"] == 0: + rsp = json.loads( + cli( + "query", + "event-query-tx-for", + rsp["txhash"], + stderr=subprocess.DEVNULL, + timeout="120s", + **kwargs, + ) + ) + return rsp