Skip to content

Commit aab843f

Browse files
authored
Feature/custom block range (#115)
* add curl installation in dockerfile * change params of trace_replayBlockTransaction * add custom block range * add logs to failed http requests * add params validation and update dependencies
1 parent 16dc35d commit aab843f

File tree

13 files changed

+382
-330
lines changed

13 files changed

+382
-330
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ FROM python:3.10-slim-bookworm as prod
1919
COPY --from=venv /app/venv /app/venv/
2020
ENV PATH /app/venv/bin:$PATH
2121

22-
RUN apt-get update && apt-get install -y tini htop nano
22+
RUN apt-get update && apt-get install -y tini htop nano curl
2323

2424
WORKDIR /app
2525
COPY . ./

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ This will run a load test with a general BSC profile.
128128
- `--size`: Specifies the test data size. Available values are XS, S, M, L, XL. Default is S.
129129
- `--batch`: Runs the test using batch requests. This will send multiple requests in a single batch request. The number of requests in a batch can be specified using the `--batch-size` flag. Default batch size is 10.
130130
- `--ref-url`: Specifies the reference blockchain node URL that will be used to fetch test data from before the benchmark starts.
131+
- `--start-block`: Starting block number to use when generating test data. Must be used together with `--end-block`.
132+
- `--end-block`: Ending block number to use when generating test data. Must be greater than `--start-block`. Note: if `--use-latest-blocks` is set, this custom range is ignored.
131133

132134
You may also run `chainbench start --help` for the full list of parameters and flags.
133135

chainbench/main.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ def validate_profile_path(ctx: Context, param: Parameter, value: str) -> str:
215215
default=None,
216216
help="Reference Node URL for retrieving test data before test starts. If not specified, target url is used.",
217217
)
218+
@click.option("--start-block", default=None, help="Start block for test data", type=int)
219+
@click.option("--end-block", default=None, help="End block for test data", type=int)
218220
@click.pass_context
219221
def start(
220222
ctx: Context,
@@ -249,7 +251,16 @@ def start(
249251
size: str | None,
250252
method: str | None = None,
251253
ref_url: str | None = None,
254+
start_block: int | None = None,
255+
end_block: int | None = None,
252256
) -> None:
257+
if start_block is not None or end_block is not None:
258+
if start_block is None or end_block is None:
259+
raise ValueError("Both start-block and end-block are required for specifying custom block range.")
260+
else:
261+
if end_block < start_block:
262+
raise ValueError("end-block must be greater than start-block.")
263+
253264
if notify:
254265
click.echo(f"Notify when test is finished using topic: {notify}")
255266
notifier = Notifier(topic=notify)
@@ -386,6 +397,8 @@ def start(
386397
enable_class_picker=enable_class_picker,
387398
batch_size=batch_size,
388399
ref_url=ref_url,
400+
start_block=start_block,
401+
end_block=end_block,
389402
)
390403
# Start the Locust master
391404
master_command = locust_options.get_master_command()

chainbench/test_data/blockchain.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,13 @@ def fetch_random_block(self, block_numbers: list[BlockNumber]) -> B:
174174
return self.fetch_block(block_number)
175175

176176
def _get_start_and_end_blocks(self, parsed_options: Namespace) -> BlockRange:
177-
raise NotImplementedError
177+
if parsed_options.start_block is not None:
178+
self.start_block_number = parsed_options.start_block
179+
else:
180+
self.start_block_number = 1
181+
if parsed_options.end_block is not None:
182+
self.end_block_number = parsed_options.end_block
183+
return self.data.block_range
178184

179185
def get_block_from_data(self, data: dict[str, t.Any] | str) -> B:
180186
raise NotImplementedError
@@ -210,10 +216,18 @@ def get_random_block_number(self, rng: RNG | None = None) -> BlockNumber:
210216
def start_block_number(self) -> BlockNumber:
211217
return self.data.block_range.start
212218

219+
@start_block_number.setter
220+
def start_block_number(self, value: BlockNumber) -> None:
221+
self.data.block_range.start = value
222+
213223
@property
214224
def end_block_number(self) -> BlockNumber:
215225
return self.data.block_range.end
216226

227+
@end_block_number.setter
228+
def end_block_number(self, value: BlockNumber) -> None:
229+
self.data.block_range.end = value
230+
217231

218232
class SmartContract:
219233
def __init__(self, address: str):

chainbench/test_data/ethereum.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,12 @@ def fetch_latest_block_number(self) -> BlockNumber:
134134
return int(self.fetch_block_header("head")["slot"])
135135

136136
def _get_start_and_end_blocks(self, parsed_options: Namespace) -> BlockRange:
137-
end_block_number = self.fetch_latest_block_number()
137+
super()._get_start_and_end_blocks(parsed_options)
138138
if parsed_options.use_latest_blocks:
139-
start_block_number = end_block_number - self.data.size.blocks_len + 1
140-
else:
141-
start_block_number = 1
142-
logger.info("Using blocks from %s to %s as test data", start_block_number, end_block_number)
143-
return BlockRange(start_block_number, end_block_number)
139+
self.end_block_number = self.fetch_latest_block_number()
140+
self.start_block_number = self.end_block_number - self.data.size.blocks_len + 1
141+
logger.info("Using blocks from %s to %s as test data", self.start_block_number, self.end_block_number)
142+
return self.data.block_range
144143

145144
def get_block_from_data(self, data: dict[str, t.Any] | str) -> EthBeaconBlock:
146145
def get_committee(committee: dict[str, t.Any]) -> EthCommittee:

chainbench/test_data/evm.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -482,13 +482,29 @@ def fetch_latest_block(self) -> EvmBlock:
482482
return self.fetch_block("latest")
483483

484484
def _get_start_and_end_blocks(self, parsed_options: Namespace) -> BlockRange:
485-
end_block_number = self.fetch_latest_block_number()
486-
if parsed_options.use_latest_blocks:
487-
start_block_number = end_block_number - self.data.size.blocks_len + 1
485+
latest_block_number = self.fetch_latest_block_number()
486+
if parsed_options.start_block is not None:
487+
self.start_block_number = parsed_options.start_block
488+
else:
489+
self.start_block_number = self.network.start_block
490+
if parsed_options.end_block is not None:
491+
self.end_block_number = parsed_options.end_block
488492
else:
489-
start_block_number = self.network.start_block
490-
logger.info("Using blocks from %s to %s as test data", start_block_number, end_block_number)
491-
return BlockRange(start_block_number, end_block_number)
493+
self.end_block_number = latest_block_number
494+
if parsed_options.use_latest_blocks:
495+
self.end_block_number = latest_block_number
496+
self.start_block_number = self.end_block_number - self.data.size.blocks_len + 1
497+
if self.start_block_number < 0:
498+
logger.warning("start_block is before genesis block," "setting to 0.")
499+
self.start_block_number = 0
500+
if self.end_block_number > latest_block_number:
501+
logger.warning(
502+
f"end_block {self.end_block_number} is after latest_block_number {latest_block_number},"
503+
"setting to latest_block_number."
504+
)
505+
self.end_block_number = latest_block_number
506+
logger.info("Using blocks from %s to %s as test data", self.start_block_number, self.end_block_number)
507+
return self.data.block_range
492508

493509
def get_random_block_hash(self, rng: RNG | None = None) -> BlockHash:
494510
if rng is None:

chainbench/test_data/solana.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -98,24 +98,24 @@ def _fetch_first_available_block(self) -> Slot:
9898
return slot
9999

100100
def _get_start_and_end_blocks(self, parsed_options: Namespace) -> BlockRange:
101-
end_block_number = self.fetch_latest_block_number()
101+
super()._get_start_and_end_blocks(parsed_options)
102102
earliest_available_block_number = self._fetch_first_available_block()
103-
103+
latest_block_number = self.fetch_latest_block_number()
104104
# factor in run_time and add 10% buffer to ensure blocks used in test data are
105105
# not removed from the ledger
106106
earliest_available_block_number += int((parsed_options.run_time / self.BLOCK_TIME) * 1.1)
107-
start_block_number = earliest_available_block_number
108-
109107
if parsed_options.use_latest_blocks:
110-
start_block_number = end_block_number - self.data.size.blocks_len + 1
111-
112-
if start_block_number < earliest_available_block_number:
113-
raise ValueError(
114-
f"Earliest available block (with buffer) is {earliest_available_block_number}, "
115-
f"but start block is {start_block_number}"
108+
self.end_block_number = latest_block_number
109+
self.start_block_number = self.end_block_number - self.data.size.blocks_len + 1
110+
if self.start_block_number < earliest_available_block_number:
111+
logger.warning(
112+
"start_block is before earliest_available_block_number," "setting to earliest_available_block_number."
116113
)
117-
118-
return BlockRange(start_block_number, end_block_number)
114+
self.start_block_number = earliest_available_block_number
115+
if self.end_block_number > latest_block_number:
116+
logger.warning("end_block is after latest_block_number," "setting to latest_block_number.")
117+
self.end_block_number = latest_block_number
118+
return self.data.block_range
119119

120120
def get_random_block_hash(self, rng: RNG | None = None) -> BlockHash:
121121
if rng is None:

chainbench/user/http.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ def check_http_error(self, response: ResponseContextManager) -> None:
6161
"""Check the response for errors."""
6262
if response.status_code != 200:
6363
self.logger.error(f"Request failed with {response.status_code} code")
64+
self.logger.error(f"Request body of failed request: {response.request.body if response.request else ''}")
6465
self.logger.debug(
6566
f"Request to {response.url} failed with HTTP Error {response.status_code} code: {response.text}"
6667
)

chainbench/user/protocol/evm.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,9 @@ def _debug_trace_block_by_hash_params_factory(self, rng: RNG) -> list[BlockHash
8585
{"tracer": "callTracer", "timeout": self._default_trace_timeout},
8686
]
8787

88-
@staticmethod
89-
def _trace_replay_block_transaction_params_factory() -> list[str | list[str]]:
88+
def _trace_replay_block_transaction_params_factory(self, rng: RNG) -> list[str | list[str]]:
9089
return [
91-
"latest",
90+
hex(self.test_data.get_random_block_number(rng)),
9291
["vmTrace", "trace", "stateDiff"],
9392
]
9493

@@ -403,7 +402,8 @@ def trace_filter(self) -> RpcCall:
403402

404403
def trace_replay_block_transactions(self) -> RpcCall:
405404
return RpcCall(
406-
method="trace_replayBlockTransactions", params=self._trace_replay_block_transaction_params_factory()
405+
method="trace_replayBlockTransactions",
406+
params=self._trace_replay_block_transaction_params_factory(self.rng.get_rng()),
407407
)
408408

409409
def trace_replay_transaction(self) -> RpcCall:

chainbench/util/cli.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ class LocustOptions:
145145
enable_class_picker: bool = False
146146
batch_size: int | None = None
147147
ref_url: str | None = None
148+
start_block: int | None = None
149+
end_block: int | None = None
148150

149151
def get_master_command(self) -> str:
150152
"""Generate master command."""
@@ -198,6 +200,11 @@ def get_extra_options(self, command: str):
198200

199201
if self.use_latest_blocks:
200202
command += " --use-latest-blocks True"
203+
else:
204+
if self.start_block is not None:
205+
command += f" --start-block {self.start_block}"
206+
if self.end_block is not None:
207+
command += f" --end-block {self.end_block}"
201208

202209
if self.method is not None:
203210
command += f" --method {self.method}"

0 commit comments

Comments
 (0)