Skip to content

Commit

Permalink
Problem: no testnet benchmark command (crypto-org-chain#1667)
Browse files Browse the repository at this point in the history
* add testnet benchmark scripts

temp

testnet command

changelog

* add sync option

* make it flag

* fix command

* fix log

* add stats cmd

* fix cmd

* more options

* Update CHANGELOG.md

Signed-off-by: yihuang <huang@crypto.com>

---------

Signed-off-by: yihuang <huang@crypto.com>
  • Loading branch information
yihuang authored Oct 28, 2024
1 parent 6de40de commit 0f94930
Show file tree
Hide file tree
Showing 8 changed files with 316 additions and 61 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

* [#1664](https://github.com/crypto-org-chain/cronos/pull/1664) Update cometbft to 0.38.13.
* [#1660](https://github.com/crypto-org-chain/cronos/pull/1660) Support async check tx.
* [#1667](https://github.com/crypto-org-chain/cronos/pull/1667) Add testnet benchmark command.

*Oct 24, 2024*

Expand Down
18 changes: 18 additions & 0 deletions testground/benchmark/benchmark/cosmostx.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
28 changes: 18 additions & 10 deletions testground/benchmark/benchmark/stats.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)
Expand Down
143 changes: 143 additions & 0 deletions testground/benchmark/benchmark/testnet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import asyncio
import json
import sys
import time
from pathlib import Path

import click
import requests
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 block_height, 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("--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, batch_size, tx_type, msg_version):
num_accounts = end - start + 1
txs = gen(
GLOBAL_SEQ,
num_accounts,
num_txs,
tx_type,
batch_size,
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)


@cli.command()
@click.argument("path", type=str)
@click.option("--rpc", default=TESTNET_RPC)
@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))


@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()
Loading

0 comments on commit 0f94930

Please sign in to comment.