Skip to content

Commit 676b68c

Browse files
authored
add --use-recent-blocks flag (#33)
* add --use-recent-blocks flag for testing nodes that return null for certain historical blocks, --use-recent-blocks flag is added to use recent blocks as test data * Update README.md * fix mypy issues * optimize test_data initialization
1 parent 860c827 commit 676b68c

File tree

6 files changed

+89
-45
lines changed

6 files changed

+89
-45
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ After the test is finished, the tool will automatically quit.
104104
- `--help`: Displays the help message.
105105
- `--debug-trace-methods`: Enables tasks tagged with debug or trace to be executed
106106
- `-E, --exclude-tags`: Exclude tasks tagged with custom tags from the test. You may specify this option multiple times --help Show this message and exit.
107-
107+
- `--use-recent-blocks`: Use recent blocks for test data generation.
108108
You may also run `chainbench start --help` for the full list of parameters and flags.
109109

110110
### Profiles

chainbench/main.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ def cli(ctx: click.Context):
136136
"--pg-username", default="postgres", help="PG username", show_default=True
137137
)
138138
@click.option("--pg-password", default=None, help="PG password")
139+
@click.option(
140+
"--use-recent-blocks", is_flag=True, help="Uses recent blocks for test data"
141+
)
139142
@click.pass_context
140143
def start(
141144
ctx: click.Context,
@@ -162,6 +165,7 @@ def start(
162165
pg_port: int,
163166
pg_username: str,
164167
pg_password: str | None,
168+
use_recent_blocks: bool,
165169
):
166170
if notify:
167171
click.echo(f"Notify when test is finished using topic: {notify}")
@@ -230,6 +234,7 @@ def start(
230234
pg_port=pg_port,
231235
pg_username=pg_username,
232236
pg_password=pg_password,
237+
use_recent_blocks=use_recent_blocks,
233238
)
234239
if headless:
235240
click.echo(f"Starting master in headless mode for {profile}")
@@ -259,6 +264,7 @@ def start(
259264
pg_port=pg_port,
260265
pg_username=pg_username,
261266
pg_password=pg_password,
267+
use_recent_blocks=use_recent_blocks,
262268
)
263269
worker_args = shlex.split(worker_command, posix=is_posix)
264270
worker_process = subprocess.Popen(worker_args)

chainbench/test_data/base.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ def __init__(self, rpc_version: str = "2.0"):
4444

4545
self._data: BlockchainData | None = None
4646

47-
def update(self, host_url: str):
47+
def update(self, host_url: str, use_recent_blocks: bool = False) -> BlockchainData:
4848
self._logger.info("Updating data")
4949
self._host = host_url
5050
self._logger.debug("Host: %s", self._host)
51-
data = self._get_init_data()
51+
data = self._get_init_data(use_recent_blocks)
5252
self._logger.info("Data fetched")
5353
self._logger.debug("Data: %s", data)
5454
self._data = data
@@ -57,7 +57,7 @@ def update(self, host_url: str):
5757
self._logger.info("Lock released")
5858
return data
5959

60-
def _get_init_data(self) -> BlockchainData:
60+
def _get_init_data(self, use_recent_blocks) -> BlockchainData:
6161
raise NotImplementedError
6262

6363
@property

chainbench/test_data/evm.py

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Mapping, TypedDict
22

3-
from chainbench.test_data.base import BaseTestData, BlockchainData
3+
from chainbench.test_data.base import BaseTestData, BlockchainData, Blocks
44
from chainbench.util.rng import get_rng
55

66

@@ -11,9 +11,9 @@ class ChainInfo(TypedDict):
1111

1212

1313
class EVMTestData(BaseTestData):
14-
TXS_REQUIRED = 50
15-
ACCOUNTS_REQUIRED = 100
16-
SAVE_LAST_BLOCKS = 100
14+
TXS_REQUIRED = 100
15+
ACCOUNTS_REQUIRED = 200
16+
SAVE_BLOCKS = 20
1717

1818
CHAIN_INFO: Mapping[int, ChainInfo] = {
1919
1: {
@@ -78,25 +78,45 @@ def _fetch_random_block(self, start, end, return_txs=True) -> tuple[int, dict]:
7878
return self._fetch_block(block_number, return_txs=return_txs)
7979

8080
# get initial data from blockchain
81-
def _get_init_data(self) -> BlockchainData:
81+
def _get_init_data(self, use_recent_blocks) -> BlockchainData:
8282
txs: list[dict] = []
8383
tx_hashes: list[str] = []
8484
accounts: set[str] = set()
85-
blocks = []
86-
chain_id = self._fetch_chain_id()
87-
start_block_number = self.CHAIN_INFO[chain_id]["start_block"]
88-
end_block_number = self.CHAIN_INFO[chain_id]["end_block"]
85+
blocks: Blocks = []
86+
chain_id: int = self._fetch_chain_id()
87+
start_block_number: int
88+
end_block_number: int
89+
return_txs: bool
8990

90-
while self.TXS_REQUIRED > len(txs) and self.ACCOUNTS_REQUIRED > len(accounts):
91+
if use_recent_blocks:
92+
end_block_number = int(self._make_call("eth_blockNumber"), 0)
93+
start_block_number = end_block_number - 20
94+
else:
95+
start_block_number = self.CHAIN_INFO[chain_id]["start_block"]
96+
end_block_number = self.CHAIN_INFO[chain_id]["end_block"]
97+
98+
while (
99+
self.TXS_REQUIRED > len(txs)
100+
or self.ACCOUNTS_REQUIRED > len(accounts)
101+
or self.SAVE_BLOCKS > len(blocks)
102+
):
103+
if self.ACCOUNTS_REQUIRED > len(accounts) or self.SAVE_BLOCKS > len(blocks):
104+
return_txs = True
105+
else:
106+
return_txs = False
91107
block_number, block = self._fetch_random_block(
92-
start_block_number, end_block_number
108+
start_block_number, end_block_number, return_txs
93109
)
94-
blocks.append((block_number, block["hash"]))
110+
if self.SAVE_BLOCKS > len(blocks):
111+
blocks.append((block_number, block["hash"]))
95112
for tx in block["transactions"]:
96-
self._append_if_not_none(txs, tx)
97-
self._append_if_not_none(tx_hashes, tx["hash"])
98-
self._append_if_not_none(accounts, tx["from"])
99-
self._append_if_not_none(accounts, tx["to"])
113+
if self.TXS_REQUIRED > len(txs):
114+
self._append_if_not_none(txs, tx)
115+
self._append_if_not_none(tx_hashes, tx["hash"])
116+
if self.ACCOUNTS_REQUIRED > len(accounts):
117+
self._append_if_not_none(accounts, tx["from"])
118+
if self.ACCOUNTS_REQUIRED > len(accounts):
119+
self._append_if_not_none(accounts, tx["to"])
100120

101121
return BlockchainData(
102122
end_block_number=end_block_number,

chainbench/util/cli.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ def get_master_command(
7070
pg_port: int | None = None,
7171
pg_username: str | None = None,
7272
pg_password: str | None = None,
73+
use_recent_blocks: bool = False,
7374
) -> str:
7475
"""Generate master command."""
7576
command = (
@@ -94,6 +95,8 @@ def get_master_command(
9495
if len(exclude_tags) > 0:
9596
command += f" --exclude-tags {' '.join(exclude_tags)}"
9697

98+
if use_recent_blocks:
99+
command += " --use-recent-blocks True"
97100
return command
98101

99102

@@ -112,6 +115,7 @@ def get_worker_command(
112115
pg_port: int | None = None,
113116
pg_username: str | None = None,
114117
pg_password: str | None = None,
118+
use_recent_blocks: bool = False,
115119
) -> str:
116120
"""Generate worker command."""
117121
command = (
@@ -131,6 +135,8 @@ def get_worker_command(
131135
if len(exclude_tags) > 0:
132136
command += f" --exclude-tags {' '.join(exclude_tags)}"
133137

138+
if use_recent_blocks:
139+
command += " --use-recent-blocks True"
134140
return command
135141

136142

chainbench/util/event.py

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,42 @@
88
logger = logging.getLogger(__name__)
99

1010

11+
def cli_custom_arguments(parser):
12+
parser.add_argument(
13+
"--use-recent-blocks",
14+
type=bool,
15+
default=False,
16+
help="Use recent blocks as test data",
17+
)
18+
19+
20+
# Listener for the init event
21+
def on_init(environment, **_kwargs):
22+
# It will be called for any runner (master, worker, local)
23+
logger.debug("init.add_listener: Init is started")
24+
logger.debug("init.add_listener: Environment: %s", environment.runner)
25+
logger.debug("init.add_listener: Host: %s", environment.host)
26+
27+
host_under_test = environment.host or "Default host"
28+
29+
if isinstance(environment.runner, MasterRunner):
30+
# Print master details to the log
31+
logger.info("I'm a master. Running tests for %s", host_under_test)
32+
33+
if isinstance(environment.runner, WorkerRunner):
34+
# Print worker details to the log
35+
logger.info("I'm a worker. Running tests for %s", host_under_test)
36+
logger.info("Initializing test data...")
37+
for user in environment.runner.user_classes:
38+
if not hasattr(user, "test_data"):
39+
continue
40+
41+
user.test_data.update(
42+
environment.host, environment.parsed_options.use_recent_blocks
43+
)
44+
logger.info("Test data is ready")
45+
46+
1147
def on_test_start(environment, **_kwargs):
1248
# It will be called for any runner (master, worker, local)
1349
if not isinstance(environment.runner, MasterRunner):
@@ -40,32 +76,8 @@ def on_test_stop(environment, **_kwargs):
4076
logger.info("Master: The test is stopped")
4177

4278

43-
# Listener for the init event
44-
def on_init(environment, **_kwargs):
45-
# It will be called for any runner (master, worker, local)
46-
logger.debug("init.add_listener: Init is started")
47-
logger.debug("init.add_listener: Environment: %s", environment.runner)
48-
logger.debug("init.add_listener: Host: %s", environment.host)
49-
50-
host_under_test = environment.host or "Default host"
51-
52-
if isinstance(environment.runner, MasterRunner):
53-
# Print master details to the log
54-
logger.info("I'm a master. Running tests for %s", host_under_test)
55-
56-
if isinstance(environment.runner, WorkerRunner):
57-
# Print worker details to the log
58-
logger.info("I'm a worker. Running tests for %s", host_under_test)
59-
logger.info("Initializing test data...")
60-
for user in environment.runner.user_classes:
61-
if not hasattr(user, "test_data"):
62-
continue
63-
64-
user.test_data.update(environment.host)
65-
logger.info("Test data is ready")
66-
67-
6879
def setup_event_listeners():
80+
events.init_command_line_parser.add_listener(cli_custom_arguments)
6981
events.test_start.add_listener(on_test_start)
7082
events.test_stop.add_listener(on_test_stop)
7183
events.init.add_listener(on_init)

0 commit comments

Comments
 (0)