From 26ae49ebe802dda745e85c89927bc6bc63b8c690 Mon Sep 17 00:00:00 2001 From: HuangYi Date: Tue, 22 Oct 2024 13:12:21 +0800 Subject: [PATCH 1/9] add testnet benchmark scripts temp testnet command changelog --- CHANGELOG.md | 6 + testground/benchmark/benchmark/cosmostx.py | 18 +++ testground/benchmark/benchmark/testnet.py | 121 ++++++++++++++++ testground/benchmark/benchmark/transaction.py | 134 +++++++++++++----- testground/benchmark/flake.nix | 4 + testground/benchmark/pyproject.toml | 1 + 6 files changed, 248 insertions(+), 36 deletions(-) create mode 100644 testground/benchmark/benchmark/testnet.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 8734ce4c5e..a57197e143 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## UNRELEASED + +### Improvements + +* [#]() Add testnet benchmark command. + *Oct 24, 2024* ## v1.4.0-rc2 diff --git a/testground/benchmark/benchmark/cosmostx.py b/testground/benchmark/benchmark/cosmostx.py index bdd47734b7..a449223588 100644 --- a/testground/benchmark/benchmark/cosmostx.py +++ b/testground/benchmark/benchmark/cosmostx.py @@ -78,5 +78,23 @@ class TxRaw(ProtoEntity): class MsgEthereumTx(ProtoEntity): + MSG_URL = "/ethermint.evm.v1.MsgEthereumTx" + + data = Field(ProtoAny, 1) + deprecated_hash = Field("string", 3) from_ = Field("bytes", 5) raw = Field("bytes", 6) + + +class LegacyTx(ProtoEntity): + MSG_URL = "/ethermint.evm.v1.LegacyTx" + + nonce = Field("uint64", 1) + gas_price = Field("string", 2) + gas = Field("uint64", 3) + to = Field("string", 4) + value = Field("string", 5) + data = Field("bytes", 6) + v = Field("bytes", 7) + r = Field("bytes", 8) + s = Field("bytes", 9) diff --git a/testground/benchmark/benchmark/testnet.py b/testground/benchmark/benchmark/testnet.py new file mode 100644 index 0000000000..b44560d552 --- /dev/null +++ b/testground/benchmark/benchmark/testnet.py @@ -0,0 +1,121 @@ +import asyncio +import json +import sys +import time +from pathlib import Path + +import click +import requests +import web3 +from hexbytes import HexBytes + +from .transaction import EthTx, build_cosmos_tx, gen, json_rpc_send_body, send +from .utils import gen_account, split_batch + +# arbitrarily picked for testnet, to not conflict with devnet benchmark accounts. +GLOBAL_SEQ = 999 +GAS_PRICE = 5050000000000 +CHAIN_ID = 338 +TESTNET_JSONRPC = "https://evm-t3.cronos.org" +TESTNET_RPC = "https://rpc-t3.cronos.org" +TESTNET_EVM_DENOM = "basetcro" + + +@click.group() +def cli(): + pass + + +@cli.command() +@click.option("--json-rpc", default=TESTNET_JSONRPC) +@click.option("--rpc", default=TESTNET_RPC) +@click.option("--batch-size", default=200) +@click.argument("start", type=int) +@click.argument("end", type=int) +def fund(json_rpc, rpc, batch_size, start, end): + w3 = web3.Web3(web3.HTTPProvider(json_rpc)) + fund_account = gen_account(GLOBAL_SEQ, 0) + fund_address = HexBytes(fund_account.address) + nonce = w3.eth.get_transaction_count(fund_account.address) + + batches = split_batch(end - start + 1, batch_size) + for begin, end in batches: + begin += start + end += start + txs = [] + for i in range(begin, end): + tx = { + "to": gen_account(GLOBAL_SEQ, i).address, + "value": 10 * 10**18, + "nonce": nonce, + "gas": 21000, + "gasPrice": GAS_PRICE, + "chainId": CHAIN_ID, + } + txs.append( + EthTx( + tx, fund_account.sign_transaction(tx).rawTransaction, fund_address + ) + ) + nonce += 1 + raw = build_cosmos_tx(*txs, msg_version="1.3", evm_denom=TESTNET_EVM_DENOM) + rsp = requests.post( + rpc, json=json_rpc_send_body(raw, method="broadcast_tx_sync") + ).json() + if rsp["result"]["code"] != 0: + print(rsp["result"]["log"]) + break + + # wait for nonce to change + while True: + if w3.eth.get_transaction_count(fund_account.address) >= nonce: + break + time.sleep(1) + + print("sent", begin, end) + + +@cli.command() +@click.option("--json-rpc", default=TESTNET_JSONRPC) +@click.argument("start", type=int) +@click.argument("end", type=int) +def check(json_rpc, start, end): + w3 = web3.Web3(web3.HTTPProvider(json_rpc)) + for i in range(start, end + 1): + addr = gen_account(GLOBAL_SEQ, i).address + nonce = w3.eth.get_transaction_count(addr) + balance = int(w3.eth.get_balance(addr)) + print(i, addr, nonce, balance) + + +@cli.command() +@click.argument("start", type=int) +@click.argument("end", type=int) +@click.option("--num-txs", default=1) +@click.option("--nonce", default=0) +@click.option("--msg-version", default="1.3") +def gen_txs(start, end, num_txs, nonce, msg_version): + num_accounts = end - start + 1 + txs = gen( + GLOBAL_SEQ, + num_accounts, + num_txs, + "simple-transfer", + 1, + start_account=start, + nonce=nonce, + msg_version=msg_version, + ) + json.dump(txs, sys.stdout) + + +@cli.command() +@click.argument("path", type=str) +@click.option("--rpc", default=TESTNET_RPC) +def send_txs(path, rpc): + txs = json.loads(Path(path).read_text()) + asyncio.run(send(txs, rpc)) + + +if __name__ == "__main__": + cli() diff --git a/testground/benchmark/benchmark/transaction.py b/testground/benchmark/benchmark/transaction.py index 5ef86bedfc..311985d354 100644 --- a/testground/benchmark/benchmark/transaction.py +++ b/testground/benchmark/benchmark/transaction.py @@ -10,6 +10,7 @@ import backoff import eth_abi import ujson +from eth_account._utils.legacy_transactions import Transaction from hexbytes import HexBytes from . import cosmostx @@ -21,6 +22,21 @@ CONNECTION_POOL_SIZE = 1024 TXS_DIR = "txs" +Job = namedtuple( + "Job", + [ + "chunk", + "global_seq", + "num_txs", + "tx_type", + "create_tx", + "batch", + "nonce", + "msg_version", + ], +) +EthTx = namedtuple("EthTx", ["tx", "raw", "sender"]) + def simple_transfer_tx(sender: str, nonce: int): return { @@ -53,11 +69,48 @@ def erc20_transfer_tx(sender: str, nonce: int): } -Job = namedtuple( - "Job", - ["chunk", "global_seq", "num_accounts", "num_txs", "tx_type", "create_tx", "batch"], -) -EthTx = namedtuple("EthTx", ["tx", "raw", "sender"]) +def build_evm_msg_1_3(tx: EthTx): + """ + build cronos v1.3 version of MsgEthereumTx + """ + txn = Transaction.from_bytes(tx.raw) + return cosmostx.build_any( + cosmostx.MsgEthereumTx.MSG_URL, + cosmostx.MsgEthereumTx( + data=cosmostx.build_any( + cosmostx.LegacyTx.MSG_URL, + cosmostx.LegacyTx( + nonce=txn.nonce, + gas_price=str(txn.gasPrice), + gas=txn.gas, + to=txn.to.hex(), + value=str(txn.value), + data=txn.data, + v=txn.v.to_bytes(32, byteorder="big"), + r=txn.r.to_bytes(32, byteorder="big"), + s=txn.s.to_bytes(32, byteorder="big"), + ), + ), + deprecated_hash=txn.hash().hex(), + from_=tx.sender, + ), + ) + + +def build_evm_msg_1_4(tx: EthTx): + return cosmostx.build_any( + cosmostx.MsgEthereumTx.MSG_URL, + cosmostx.MsgEthereumTx( + from_=tx.sender, + raw=tx.raw, + ), + ) + + +MSG_VERSIONS = { + "1.3": build_evm_msg_1_3, + "1.4": build_evm_msg_1_4, +} def _do_job(job: Job): @@ -67,7 +120,7 @@ def _do_job(job: Job): for acct in accounts: txs = [] for i in range(job.num_txs): - tx = job.create_tx(acct.address, i) + tx = job.create_tx(acct.address, job.nonce + i) raw = acct.sign_transaction(tx).rawTransaction txs.append(EthTx(tx, raw, HexBytes(acct.address))) total += 1 @@ -76,19 +129,37 @@ def _do_job(job: Job): # to keep it simple, only build batch inside the account txs = [ - build_cosmos_tx(*txs[start:end]) + build_cosmos_tx(*txs[start:end], msg_version=job.msg_version) for start, end in split_batch(len(txs), job.batch) ] acct_txs.append(txs) return acct_txs -def gen(global_seq, num_accounts, num_txs, tx_type: str, batch: int) -> [str]: +def gen( + global_seq, + num_accounts, + num_txs, + tx_type: str, + batch: int, + nonce: int = 0, + start_account: int = 0, + msg_version: str = "1.4", +) -> [str]: chunks = split(num_accounts, os.cpu_count()) create_tx = TX_TYPES[tx_type] jobs = [ - Job(chunk, global_seq, num_accounts, num_txs, tx_type, create_tx, batch) - for chunk in chunks + Job( + (start + start_account, end + start_account), + global_seq, + num_txs, + tx_type, + create_tx, + batch, + nonce, + msg_version, + ) + for start, end in chunks ] with multiprocessing.Pool() as pool: @@ -119,20 +190,12 @@ def load(datadir: Path, global_seq: int) -> [str]: return ujson.load(f) -def build_cosmos_tx(*txs: EthTx) -> str: +def build_cosmos_tx(*txs: EthTx, msg_version="1.4", evm_denom=DEFAULT_DENOM) -> str: """ return base64 encoded cosmos tx, support batch """ - msgs = [ - cosmostx.build_any( - "/ethermint.evm.v1.MsgEthereumTx", - cosmostx.MsgEthereumTx( - from_=tx.sender, - raw=tx.raw, - ), - ) - for tx in txs - ] + build_msg = MSG_VERSIONS[msg_version] + msgs = [build_msg(tx) for tx in txs] fee = sum(tx.tx["gas"] * tx.tx["gasPrice"] for tx in txs) gas = sum(tx.tx["gas"] for tx in txs) body = cosmostx.TxBody( @@ -143,7 +206,7 @@ def build_cosmos_tx(*txs: EthTx) -> str: ) auth_info = cosmostx.AuthInfo( fee=cosmostx.Fee( - amount=[cosmostx.Coin(denom=DEFAULT_DENOM, amount=str(fee))], + amount=[cosmostx.Coin(denom=evm_denom, amount=str(fee))], gas_limit=gas, ) ) @@ -154,20 +217,19 @@ def build_cosmos_tx(*txs: EthTx) -> str: ).decode() +def json_rpc_send_body(raw, method="broadcast_tx_async"): + return { + "jsonrpc": "2.0", + "method": method, + "params": {"tx": raw}, + "id": 1, + } + + @backoff.on_predicate(backoff.expo, max_time=60, max_value=5) @backoff.on_exception(backoff.expo, aiohttp.ClientError, max_time=60, max_value=5) -async def async_sendtx(session, raw): - async with session.post( - LOCAL_RPC, - json={ - "jsonrpc": "2.0", - "method": "broadcast_tx_async", - "params": { - "tx": raw, - }, - "id": 1, - }, - ) as rsp: +async def async_sendtx(session, raw, rpc): + async with session.post(rpc, json=json_rpc_send_body(raw)) as rsp: data = await rsp.json() if "error" in data: print("send tx error, will retry,", data["error"]) @@ -175,10 +237,10 @@ async def async_sendtx(session, raw): return True -async def send(txs): +async def send(txs, rpc=LOCAL_RPC): connector = aiohttp.TCPConnector(limit=CONNECTION_POOL_SIZE) async with aiohttp.ClientSession( connector=connector, json_serialize=ujson.dumps ) as session: - tasks = [asyncio.ensure_future(async_sendtx(session, raw)) for raw in txs] + tasks = [asyncio.ensure_future(async_sendtx(session, raw, rpc)) for raw in txs] await asyncio.gather(*tasks) diff --git a/testground/benchmark/flake.nix b/testground/benchmark/flake.nix index f29572553a..234cf58638 100644 --- a/testground/benchmark/flake.nix +++ b/testground/benchmark/flake.nix @@ -33,6 +33,10 @@ type = "app"; program = "${pkgs.benchmark-testcase}/bin/stateless-testcase"; }; + testnet = { + type = "app"; + program = "${pkgs.benchmark-testcase}/bin/testnet"; + }; }; devShells.default = pkgs.mkShell { buildInputs = [ pkgs.benchmark-testcase-env ]; diff --git a/testground/benchmark/pyproject.toml b/testground/benchmark/pyproject.toml index ed4953d96a..8ff96e6920 100644 --- a/testground/benchmark/pyproject.toml +++ b/testground/benchmark/pyproject.toml @@ -35,6 +35,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry.scripts] stateless-testcase = "benchmark.stateless:cli" +testnet = "benchmark.testnet:cli" [tool.black] line-length = 88 From 316fce397f74224e9eca04c7b1f49f489a148b94 Mon Sep 17 00:00:00 2001 From: HuangYi Date: Mon, 28 Oct 2024 11:55:13 +0800 Subject: [PATCH 2/9] add sync option --- testground/benchmark/benchmark/testnet.py | 5 +++-- testground/benchmark/benchmark/transaction.py | 11 +++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/testground/benchmark/benchmark/testnet.py b/testground/benchmark/benchmark/testnet.py index b44560d552..d7d9c6d7a9 100644 --- a/testground/benchmark/benchmark/testnet.py +++ b/testground/benchmark/benchmark/testnet.py @@ -112,9 +112,10 @@ def gen_txs(start, end, num_txs, nonce, msg_version): @cli.command() @click.argument("path", type=str) @click.option("--rpc", default=TESTNET_RPC) -def send_txs(path, rpc): +@click.option("--sync", default=False) +def send_txs(path, rpc, sync): txs = json.loads(Path(path).read_text()) - asyncio.run(send(txs, rpc)) + asyncio.run(send(txs, rpc, sync)) if __name__ == "__main__": diff --git a/testground/benchmark/benchmark/transaction.py b/testground/benchmark/benchmark/transaction.py index 311985d354..31853bb42d 100644 --- a/testground/benchmark/benchmark/transaction.py +++ b/testground/benchmark/benchmark/transaction.py @@ -228,8 +228,9 @@ def json_rpc_send_body(raw, method="broadcast_tx_async"): @backoff.on_predicate(backoff.expo, max_time=60, max_value=5) @backoff.on_exception(backoff.expo, aiohttp.ClientError, max_time=60, max_value=5) -async def async_sendtx(session, raw, rpc): - async with session.post(rpc, json=json_rpc_send_body(raw)) as rsp: +async def async_sendtx(session, raw, rpc, sync=False): + method = "broadcast_tx_sync" if sync else "broadcast_tx_async" + async with session.post(rpc, json=json_rpc_send_body(raw, method)) as rsp: data = await rsp.json() if "error" in data: print("send tx error, will retry,", data["error"]) @@ -237,10 +238,12 @@ async def async_sendtx(session, raw, rpc): return True -async def send(txs, rpc=LOCAL_RPC): +async def send(txs, rpc=LOCAL_RPC, sync=False): connector = aiohttp.TCPConnector(limit=CONNECTION_POOL_SIZE) async with aiohttp.ClientSession( connector=connector, json_serialize=ujson.dumps ) as session: - tasks = [asyncio.ensure_future(async_sendtx(session, raw, rpc)) for raw in txs] + tasks = [ + asyncio.ensure_future(async_sendtx(session, raw, rpc, sync)) for raw in txs + ] await asyncio.gather(*tasks) From 4f9ede45de1200d91b4d3345c2d5bbbaf752917e Mon Sep 17 00:00:00 2001 From: HuangYi Date: Mon, 28 Oct 2024 11:57:05 +0800 Subject: [PATCH 3/9] make it flag --- testground/benchmark/benchmark/testnet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testground/benchmark/benchmark/testnet.py b/testground/benchmark/benchmark/testnet.py index d7d9c6d7a9..fb3fdd9d11 100644 --- a/testground/benchmark/benchmark/testnet.py +++ b/testground/benchmark/benchmark/testnet.py @@ -112,7 +112,7 @@ def gen_txs(start, end, num_txs, nonce, msg_version): @cli.command() @click.argument("path", type=str) @click.option("--rpc", default=TESTNET_RPC) -@click.option("--sync", default=False) +@click.option("--sync/--async", default=False) def send_txs(path, rpc, sync): txs = json.loads(Path(path).read_text()) asyncio.run(send(txs, rpc, sync)) From 17849d529a9e372d06099ab956c127c0dfbd0208 Mon Sep 17 00:00:00 2001 From: HuangYi Date: Mon, 28 Oct 2024 12:13:23 +0800 Subject: [PATCH 4/9] fix command --- testground/benchmark/benchmark/testnet.py | 2 ++ testground/benchmark/benchmark/transaction.py | 29 ++++++++++++++----- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/testground/benchmark/benchmark/testnet.py b/testground/benchmark/benchmark/testnet.py index fb3fdd9d11..5137d0b953 100644 --- a/testground/benchmark/benchmark/testnet.py +++ b/testground/benchmark/benchmark/testnet.py @@ -105,6 +105,8 @@ def gen_txs(start, end, num_txs, nonce, msg_version): start_account=start, nonce=nonce, msg_version=msg_version, + tx_options={"gas_price": GAS_PRICE, "chain_id": CHAIN_ID}, + evm_denom=TESTNET_EVM_DENOM, ) json.dump(txs, sys.stdout) diff --git a/testground/benchmark/benchmark/transaction.py b/testground/benchmark/benchmark/transaction.py index 31853bb42d..30a7c5b1b2 100644 --- a/testground/benchmark/benchmark/transaction.py +++ b/testground/benchmark/benchmark/transaction.py @@ -33,23 +33,25 @@ "batch", "nonce", "msg_version", + "tx_options", + "evm_denom", ], ) EthTx = namedtuple("EthTx", ["tx", "raw", "sender"]) -def simple_transfer_tx(sender: str, nonce: int): +def simple_transfer_tx(sender: str, nonce: int, options: dict): return { "to": sender, "value": 1, "nonce": nonce, "gas": 21000, - "gasPrice": GAS_PRICE, - "chainId": CHAIN_ID, + "gasPrice": options.get("gas_price", GAS_PRICE), + "chainId": options.get("chain_id", CHAIN_ID), } -def erc20_transfer_tx(sender: str, nonce: int): +def erc20_transfer_tx(sender: str, nonce: int, options: dict): # data is erc20 transfer function call data = "0xa9059cbb" + eth_abi.encode(["address", "uint256"], [sender, 1]).hex() return { @@ -57,8 +59,8 @@ def erc20_transfer_tx(sender: str, nonce: int): "value": 0, "nonce": nonce, "gas": 51630, - "gasPrice": GAS_PRICE, - "chainId": CHAIN_ID, + "gasPrice": options.get("gas_price", GAS_PRICE), + "chainId": options.get("chain_id", CHAIN_ID), "data": data, } @@ -120,7 +122,7 @@ def _do_job(job: Job): for acct in accounts: txs = [] for i in range(job.num_txs): - tx = job.create_tx(acct.address, job.nonce + i) + tx = job.create_tx(acct.address, job.nonce + i, job.tx_options) raw = acct.sign_transaction(tx).rawTransaction txs.append(EthTx(tx, raw, HexBytes(acct.address))) total += 1 @@ -129,7 +131,9 @@ def _do_job(job: Job): # to keep it simple, only build batch inside the account txs = [ - build_cosmos_tx(*txs[start:end], msg_version=job.msg_version) + build_cosmos_tx( + *txs[start:end], msg_version=job.msg_version, evm_denom=job.evm_denom + ) for start, end in split_batch(len(txs), job.batch) ] acct_txs.append(txs) @@ -145,7 +149,10 @@ def gen( nonce: int = 0, start_account: int = 0, msg_version: str = "1.4", + tx_options: dict = None, + evm_denom: str = DEFAULT_DENOM, ) -> [str]: + tx_options = tx_options or {} chunks = split(num_accounts, os.cpu_count()) create_tx = TX_TYPES[tx_type] jobs = [ @@ -158,6 +165,8 @@ def gen( batch, nonce, msg_version, + tx_options, + evm_denom, ) for start, end in chunks ] @@ -232,9 +241,13 @@ async def async_sendtx(session, raw, rpc, sync=False): method = "broadcast_tx_sync" if sync else "broadcast_tx_async" async with session.post(rpc, json=json_rpc_send_body(raw, method)) as rsp: data = await rsp.json() + print("data", data) if "error" in data: print("send tx error, will retry,", data["error"]) return False + result = data["result"] + if result["code"] != 0: + print("tx is invalid, won't retry,", result["log"]) return True From ff7f24d55a573047ad1826084d2f58d3a4d2c2c1 Mon Sep 17 00:00:00 2001 From: HuangYi Date: Mon, 28 Oct 2024 12:21:54 +0800 Subject: [PATCH 5/9] fix log --- testground/benchmark/benchmark/transaction.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/testground/benchmark/benchmark/transaction.py b/testground/benchmark/benchmark/transaction.py index 30a7c5b1b2..5a5794a1cb 100644 --- a/testground/benchmark/benchmark/transaction.py +++ b/testground/benchmark/benchmark/transaction.py @@ -3,6 +3,7 @@ import itertools import multiprocessing import os +import sys from collections import namedtuple from pathlib import Path @@ -127,7 +128,9 @@ def _do_job(job: Job): txs.append(EthTx(tx, raw, HexBytes(acct.address))) total += 1 if total % 1000 == 0: - print("generated", total, "txs for node", job.global_seq) + print( + "generated", total, "txs for node", job.global_seq, file=sys.stderr + ) # to keep it simple, only build batch inside the account txs = [ @@ -241,7 +244,6 @@ async def async_sendtx(session, raw, rpc, sync=False): method = "broadcast_tx_sync" if sync else "broadcast_tx_async" async with session.post(rpc, json=json_rpc_send_body(raw, method)) as rsp: data = await rsp.json() - print("data", data) if "error" in data: print("send tx error, will retry,", data["error"]) return False From 224259e39c054722dfc26dca1a3206860285704e Mon Sep 17 00:00:00 2001 From: HuangYi Date: Mon, 28 Oct 2024 14:05:40 +0800 Subject: [PATCH 6/9] add stats cmd --- testground/benchmark/benchmark/stats.py | 28 +++++++++++++++-------- testground/benchmark/benchmark/testnet.py | 19 ++++++++++++++- testground/benchmark/benchmark/utils.py | 16 ++++++------- 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/testground/benchmark/benchmark/stats.py b/testground/benchmark/benchmark/stats.py index c5a74c8fa9..f59d4c91a9 100644 --- a/testground/benchmark/benchmark/stats.py +++ b/testground/benchmark/benchmark/stats.py @@ -1,6 +1,6 @@ from datetime import datetime -from .utils import block, block_eth, block_height +from .utils import LOCAL_JSON_RPC, LOCAL_RPC, block, block_eth, block_height # the tps calculation use the average of the last 10 blocks TPS_WINDOW = 5 @@ -19,33 +19,41 @@ def calculate_tps(blocks): return txs / time_diff -def get_block_info_cosmos(height): - blk = block(height) +def get_block_info_cosmos(height, rpc): + blk = block(height, rpc=rpc) timestamp = datetime.fromisoformat(blk["result"]["block"]["header"]["time"]) txs = len(blk["result"]["block"]["data"]["txs"]) return timestamp, txs -def get_block_info_eth(height): - blk = block_eth(height) +def get_block_info_eth(height, json_rpc): + blk = block_eth(height, json_rpc=json_rpc) timestamp = datetime.fromtimestamp(int(blk["timestamp"], 0)) txs = len(blk["transactions"]) return timestamp, txs -def dump_block_stats(fp, eth=True): +def dump_block_stats( + fp, + eth=True, + json_rpc=LOCAL_JSON_RPC, + rpc=LOCAL_RPC, + start: int = 2, + end: int = None, +): """ dump block stats using web3 json-rpc, which splits batch tx """ tps_list = [] - current = block_height() + if end is None: + end = block_height(rpc) blocks = [] # skip block 1 whose timestamp is not accurate - for i in range(2, current + 1): + for i in range(start, end + 1): if eth: - timestamp, txs = get_block_info_eth(i) + timestamp, txs = get_block_info_eth(i, json_rpc) else: - timestamp, txs = get_block_info_cosmos(i) + timestamp, txs = get_block_info_cosmos(i, rpc) blocks.append((txs, timestamp)) tps = calculate_tps(blocks[-TPS_WINDOW:]) tps_list.append(tps) diff --git a/testground/benchmark/benchmark/testnet.py b/testground/benchmark/benchmark/testnet.py index 5137d0b953..e16e1b9d66 100644 --- a/testground/benchmark/benchmark/testnet.py +++ b/testground/benchmark/benchmark/testnet.py @@ -9,8 +9,9 @@ import web3 from hexbytes import HexBytes +from .stats import dump_block_stats from .transaction import EthTx, build_cosmos_tx, gen, json_rpc_send_body, send -from .utils import gen_account, split_batch +from .utils import block_height, gen_account, split_batch # arbitrarily picked for testnet, to not conflict with devnet benchmark accounts. GLOBAL_SEQ = 999 @@ -120,5 +121,21 @@ def send_txs(path, rpc, sync): asyncio.run(send(txs, rpc, sync)) +@cli.command() +@click.option("--json-rpc", default=TESTNET_JSONRPC) +@click.option("--rpc", default=TESTNET_RPC) +@click.option("count", default=30) +def stats(json_rpc, rpc, count): + current = block_height(rpc) + dump_block_stats( + sys.stdout, + eth=True, + rpc=rpc, + json_rpc=json_rpc, + start=current - count, + end=current, + ) + + if __name__ == "__main__": cli() diff --git a/testground/benchmark/benchmark/utils.py b/testground/benchmark/benchmark/utils.py index 5d71f74122..786b7f17fa 100644 --- a/testground/benchmark/benchmark/utils.py +++ b/testground/benchmark/benchmark/utils.py @@ -162,18 +162,18 @@ def gen_account(global_seq: int, index: int) -> Account: return Account.from_key(((global_seq + 1) << 32 | index).to_bytes(32)) -def block_height(): - rsp = requests.get(f"{LOCAL_RPC}/status").json() +def block_height(rpc=LOCAL_RPC): + rsp = requests.get(f"{rpc}/status").json() return int(rsp["result"]["sync_info"]["latest_block_height"]) -def block(height): - return requests.get(f"{LOCAL_RPC}/block?height={height}").json() +def block(height, rpc=LOCAL_RPC): + return requests.get(f"{rpc}/block?height={height}").json() -def block_eth(height: int): +def block_eth(height: int, json_rpc=LOCAL_JSON_RPC): return requests.post( - f"{LOCAL_JSON_RPC}", + json_rpc, json={ "jsonrpc": "2.0", "method": "eth_getBlockByNumber", @@ -183,8 +183,8 @@ def block_eth(height: int): ).json()["result"] -def block_txs(height): - return block(height)["result"]["block"]["data"]["txs"] +def block_txs(height, rpc=LOCAL_RPC): + return block(height, rpc=rpc)["result"]["block"]["data"]["txs"] def split(a: int, n: int): From 8ae46a8c939fbc0e1cc9f5d9fe17c6d5d4a97050 Mon Sep 17 00:00:00 2001 From: HuangYi Date: Mon, 28 Oct 2024 14:06:08 +0800 Subject: [PATCH 7/9] fix cmd --- testground/benchmark/benchmark/testnet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testground/benchmark/benchmark/testnet.py b/testground/benchmark/benchmark/testnet.py index e16e1b9d66..c4c0c3acee 100644 --- a/testground/benchmark/benchmark/testnet.py +++ b/testground/benchmark/benchmark/testnet.py @@ -124,7 +124,7 @@ def send_txs(path, rpc, sync): @cli.command() @click.option("--json-rpc", default=TESTNET_JSONRPC) @click.option("--rpc", default=TESTNET_RPC) -@click.option("count", default=30) +@click.option("--count", default=30) def stats(json_rpc, rpc, count): current = block_height(rpc) dump_block_stats( From 041aa2f66771de91bead0c178b72de7e072c55c0 Mon Sep 17 00:00:00 2001 From: HuangYi Date: Mon, 28 Oct 2024 14:30:51 +0800 Subject: [PATCH 8/9] more options --- testground/benchmark/benchmark/testnet.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/testground/benchmark/benchmark/testnet.py b/testground/benchmark/benchmark/testnet.py index c4c0c3acee..d71ebcb4a5 100644 --- a/testground/benchmark/benchmark/testnet.py +++ b/testground/benchmark/benchmark/testnet.py @@ -94,15 +94,17 @@ def check(json_rpc, start, end): @click.argument("end", type=int) @click.option("--num-txs", default=1) @click.option("--nonce", default=0) +@click.option("--batch-size", default=1) +@click.option("--tx-type", default="simple-transfer") @click.option("--msg-version", default="1.3") -def gen_txs(start, end, num_txs, nonce, msg_version): +def gen_txs(start, end, num_txs, nonce, batch_size, tx_type, msg_version): num_accounts = end - start + 1 txs = gen( GLOBAL_SEQ, num_accounts, num_txs, - "simple-transfer", - 1, + tx_type, + batch_size, start_account=start, nonce=nonce, msg_version=msg_version, From f6224ffc159c2badb384ad76603e60ea78642d5d Mon Sep 17 00:00:00 2001 From: yihuang Date: Mon, 28 Oct 2024 14:45:08 +0800 Subject: [PATCH 9/9] Update CHANGELOG.md Signed-off-by: yihuang --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e194015794..d826efd595 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ ### Improvements * [#1664](https://github.com/crypto-org-chain/cronos/pull/1664) Update cometbft to 0.38.13. -* [#]() Add testnet benchmark command. +* [#1667](https://github.com/crypto-org-chain/cronos/pull/1667) Add testnet benchmark command. *Oct 24, 2024*