Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem: no testnet benchmark command #1667

Merged
merged 11 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
yihuang marked this conversation as resolved.
Show resolved Hide resolved
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)

yihuang marked this conversation as resolved.
Show resolved Hide resolved
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
yihuang marked this conversation as resolved.
Show resolved Hide resolved

# wait for nonce to change
while True:
if w3.eth.get_transaction_count(fund_account.address) >= nonce:
break
time.sleep(1)

yihuang marked this conversation as resolved.
Show resolved Hide resolved
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)

yihuang marked this conversation as resolved.
Show resolved Hide resolved

@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))
yihuang marked this conversation as resolved.
Show resolved Hide resolved


@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,
)
yihuang marked this conversation as resolved.
Show resolved Hide resolved


if __name__ == "__main__":
cli()
Loading
Loading