Skip to content

Commit 377e175

Browse files
authored
feat(specs,tests): Update all benchmark tests to use gas_benchmark_value (#1935)
* feat(specs): Check gas used by benchmark tests matches expectation * feat(tests): Update all benchmark tests to use `gas_benchmark_value` * fix: tox * fix(plugins/benchmarking): Increase GIGA_GAS * fix(tests): Environment usages * docs: Changelog * fix(tox): tests-deployed-benchmark command line * fix: Remove unused parameter * fix: `test_worst_storage_access_cold` and add more cases * fix: `test_worst_bytecode_single_opcode` incorrect env overwrite * fix(plugins): Bump `BENCHMARKING_MAX_GAS` so all benchmarking tests can run from a single genesis * feat(plugins): Pass `env` fixture to the test spec builder by default if not specified * fix(benchmark): Remove `env` from tests where it's unused * fix(benchmark): tests referencing the `fee_recipient` from the environment * fix(plugins): Set benchmark environment based on test mark * fix: minor fix * fix(plugins): Defaults for `env` and `genesis_environment`
1 parent 9285bd7 commit 377e175

File tree

15 files changed

+471
-262
lines changed

15 files changed

+471
-262
lines changed

docs/CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ Additionally, writing debugging information to the EVM "dump directory" by defau
4747

4848
Due to the crossover between `zkevm` and `benchmark` tests, all instances of the former have been replaced with the latter nomenclature. Repository PR labels and titles are additionally updated to reflect this change.
4949

50-
This update renames the `zkevm` feature release to `benchmark_30M` and further expands the latter for 1M, 10M, 60M, 90M, and 120M block gas limits in `fixtures_benchmark_1M.tar.gz`, `fixtures_benchmark_10M.tar.gz`, `fixtures_benchmark_30M.tar.gz`, `fixtures_benchmark_60M.tar.gz`, `fixtures_benchmark_90M.tar.gz`, and `fixtures_benchmark_120M.tar.gz` respectively.
50+
This update renames the `zkevm` feature release to `benchmark` and further expands the latter for 1M, 10M, 30M, 45M, 60M, 90M, and 120M block gas limits in `fixtures_benchmark.tar.gz`.
51+
52+
To select a test for a given gas limit, the IDs of the tests have been expanded to contain `benchmark-gas-value_XM`, where `X` can be any of the aforementioned values.
53+
54+
The benchmark release also now includes BlockchainEngineX format that combines most of the tests into a minimal amount of genesis files. For more info see [Blockchain Engine X Tests](https://eest.ethereum.org/main/running_tests/test_formats/blockchain_test_engine_x/) in the EEST documentation.
5155

5256
Users can select any of the artifacts depending on their testing needs for their provers.
5357

src/ethereum_test_specs/base.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import hashlib
44
from abc import abstractmethod
5+
from enum import StrEnum, unique
56
from functools import reduce
67
from os import path
78
from pathlib import Path
@@ -49,12 +50,23 @@ def verify_result(result: Result, env: Environment):
4950
assert result.withdrawals_root == to_hex(Withdrawal.list_root(env.withdrawals))
5051

5152

53+
@unique
54+
class OpMode(StrEnum):
55+
"""Operation mode for the fill and execute."""
56+
57+
CONSENSUS = "consensus"
58+
BENCHMARKING = "benchmarking"
59+
60+
5261
class BaseTest(BaseModel):
5362
"""Represents a base Ethereum test which must return a single test fixture."""
5463

5564
tag: str = ""
5665

5766
_request: pytest.FixtureRequest | None = PrivateAttr(None)
67+
_operation_mode: OpMode | None = PrivateAttr(None)
68+
69+
expected_benchmark_gas_used: int | None = None
5870

5971
spec_types: ClassVar[Dict[str, Type["BaseTest"]]] = {}
6072

@@ -98,9 +110,11 @@ def from_test(
98110
new_instance = cls(
99111
tag=base_test.tag,
100112
t8n_dump_dir=base_test.t8n_dump_dir,
113+
expected_benchmark_gas_used=base_test.expected_benchmark_gas_used,
101114
**kwargs,
102115
)
103116
new_instance._request = base_test._request
117+
new_instance._operation_mode = base_test._operation_mode
104118
return new_instance
105119

106120
@classmethod

src/ethereum_test_specs/blockchain.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
from ethereum_test_forks import Fork
5454
from ethereum_test_types import Alloc, Environment, Removable, Requests, Transaction, Withdrawal
5555

56-
from .base import BaseTest, verify_result
56+
from .base import BaseTest, OpMode, verify_result
5757
from .debugging import print_traces
5858
from .helpers import verify_block, verify_transactions
5959

@@ -489,6 +489,7 @@ def generate_block_data(
489489
block: Block,
490490
previous_env: Environment,
491491
previous_alloc: Alloc,
492+
last_block: bool,
492493
) -> BuiltBlock:
493494
"""Generate common block data for both make_fixture and make_hive_fixture."""
494495
env = block.set_environment(previous_env)
@@ -554,6 +555,18 @@ def generate_block_data(
554555
# Verify the header after transition tool processing.
555556
block.header_verify.verify(header)
556557

558+
if last_block and self._operation_mode == OpMode.BENCHMARKING:
559+
expected_benchmark_gas_used = self.expected_benchmark_gas_used
560+
assert expected_benchmark_gas_used is not None, (
561+
"expected_benchmark_gas_used is not set"
562+
)
563+
gas_used = int(transition_tool_output.result.gas_used)
564+
assert gas_used == expected_benchmark_gas_used, (
565+
f"gas_used ({gas_used}) does not match expected_benchmark_gas_used "
566+
f"({expected_benchmark_gas_used})"
567+
f", difference: {gas_used - expected_benchmark_gas_used}"
568+
)
569+
557570
requests_list: List[Bytes] | None = None
558571
if fork.header_requests_required(header.number, header.timestamp):
559572
assert transition_tool_output.result.requests is not None, (
@@ -659,7 +672,7 @@ def make_fixture(
659672
env = environment_from_parent_header(genesis.header)
660673
head = genesis.header.block_hash
661674
invalid_blocks = 0
662-
for block in self.blocks:
675+
for i, block in enumerate(self.blocks):
663676
# This is the most common case, the RLP needs to be constructed
664677
# based on the transactions to be included in the block.
665678
# Set the environment according to the block to execute.
@@ -669,6 +682,7 @@ def make_fixture(
669682
block=block,
670683
previous_env=env,
671684
previous_alloc=alloc,
685+
last_block=i == len(self.blocks) - 1,
672686
)
673687
fixture_blocks.append(built_block.get_fixture_block())
674688
if block.exception is None:
@@ -718,13 +732,14 @@ def make_hive_fixture(
718732
env = environment_from_parent_header(genesis.header)
719733
head_hash = genesis.header.block_hash
720734
invalid_blocks = 0
721-
for block in self.blocks:
735+
for i, block in enumerate(self.blocks):
722736
built_block = self.generate_block_data(
723737
t8n=t8n,
724738
fork=fork,
725739
block=block,
726740
previous_env=env,
727741
previous_alloc=alloc,
742+
last_block=i == len(self.blocks) - 1,
728743
)
729744
fixture_payloads.append(built_block.get_fixture_engine_new_payload())
730745
if block.exception is None:
@@ -765,6 +780,7 @@ def make_hive_fixture(
765780
block=Block(),
766781
previous_env=env,
767782
previous_alloc=alloc,
783+
last_block=False,
768784
)
769785
sync_payload = sync_built_block.get_fixture_engine_new_payload()
770786

src/ethereum_test_specs/state.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
from ethereum_test_forks import Fork
3333
from ethereum_test_types import Alloc, Environment, Transaction
3434

35-
from .base import BaseTest
35+
from .base import BaseTest, OpMode
3636
from .blockchain import Block, BlockchainTest, Header
3737
from .debugging import print_traces
3838
from .helpers import verify_transactions
@@ -219,6 +219,18 @@ def make_state_test_fixture(
219219
pprint(transition_tool_output.alloc)
220220
raise e
221221

222+
if self._operation_mode == OpMode.BENCHMARKING:
223+
expected_benchmark_gas_used = self.expected_benchmark_gas_used
224+
gas_used = int(transition_tool_output.result.gas_used)
225+
assert expected_benchmark_gas_used is not None, (
226+
"expected_benchmark_gas_used is not set"
227+
)
228+
assert gas_used == expected_benchmark_gas_used, (
229+
f"gas_used ({gas_used}) does not match expected_benchmark_gas_used "
230+
f"({expected_benchmark_gas_used})"
231+
f", difference: {gas_used - expected_benchmark_gas_used}"
232+
)
233+
222234
return StateFixture(
223235
env=FixtureEnvironment(**env.model_dump(exclude_none=True)),
224236
pre=pre_alloc,

src/pytest_plugins/execute/execute.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from ethereum_test_tools import BaseTest
1414
from ethereum_test_types import EnvironmentDefaults, TransactionDefaults
1515

16+
from ..shared.execute_fill import ALL_FIXTURE_PARAMETERS
1617
from ..shared.helpers import (
1718
get_spec_format_for_item,
1819
is_help_or_collectonly_mode,
@@ -252,6 +253,7 @@ def base_test_parametrizer(cls: Type[BaseTest]):
252253
Implementation detail: All spec fixtures must be scoped on test function level to avoid
253254
leakage between tests.
254255
"""
256+
cls_fixture_parameters = [p for p in ALL_FIXTURE_PARAMETERS if p in cls.model_fields]
255257

256258
@pytest.fixture(
257259
scope="function",
@@ -289,6 +291,11 @@ def __init__(self, *args, **kwargs):
289291
kwargs["pre"] = pre
290292
elif kwargs["pre"] != pre:
291293
raise ValueError("The pre-alloc object was modified by the test.")
294+
kwargs |= {
295+
p: request.getfixturevalue(p)
296+
for p in cls_fixture_parameters
297+
if p not in kwargs
298+
}
292299

293300
request.node.config.sender_address = str(pre._sender)
294301

src/pytest_plugins/filler/filler.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
)
4343
from ethereum_test_types import EnvironmentDefaults
4444

45+
from ..shared.execute_fill import ALL_FIXTURE_PARAMETERS
4546
from ..shared.helpers import (
4647
get_spec_format_for_item,
4748
is_help_or_collectonly_mode,
@@ -873,6 +874,7 @@ def base_test_parametrizer(cls: Type[BaseTest]):
873874
Implementation detail: All spec fixtures must be scoped on test function level to avoid
874875
leakage between tests.
875876
"""
877+
cls_fixture_parameters = [p for p in ALL_FIXTURE_PARAMETERS if p in cls.model_fields]
876878

877879
@pytest.fixture(
878880
scope="function",
@@ -889,6 +891,7 @@ def base_test_parametrizer_func(
889891
fixture_collector: FixtureCollector,
890892
test_case_description: str,
891893
fixture_source_url: str,
894+
gas_benchmark_value: int,
892895
):
893896
"""
894897
Fixture used to instantiate an auto-fillable BaseTest object from within
@@ -914,8 +917,17 @@ def __init__(self, *args, **kwargs):
914917
kwargs["t8n_dump_dir"] = dump_dir_parameter_level
915918
if "pre" not in kwargs:
916919
kwargs["pre"] = pre
920+
if "expected_benchmark_gas_used" not in kwargs:
921+
kwargs["expected_benchmark_gas_used"] = gas_benchmark_value
922+
kwargs |= {
923+
p: request.getfixturevalue(p)
924+
for p in cls_fixture_parameters
925+
if p not in kwargs
926+
}
927+
917928
super(BaseTestWrapper, self).__init__(*args, **kwargs)
918929
self._request = request
930+
self._operation_mode = request.config.op_mode
919931

920932
# Phase 1: Generate pre-allocation groups
921933
if fixture_format is BlockchainEngineXFixture and request.config.getoption(

src/pytest_plugins/shared/benchmarking.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,20 @@ def gas_benchmark_value(request: pytest.FixtureRequest) -> int:
5050
return EnvironmentDefaults.gas_limit
5151

5252

53-
GIGA_GAS = 1_000_000_000
53+
BENCHMARKING_MAX_GAS = 500_000_000_000
54+
55+
56+
@pytest.fixture
57+
def genesis_environment(request: pytest.FixtureRequest) -> Environment: # noqa: D103
58+
"""Return an Environment instance with appropriate gas limit based on test type."""
59+
if request.node.get_closest_marker("benchmark") is not None:
60+
return Environment(gas_limit=BENCHMARKING_MAX_GAS)
61+
return Environment()
5462

5563

5664
@pytest.fixture
5765
def env(request: pytest.FixtureRequest) -> Environment: # noqa: D103
58-
"""Return an Environment instance with appropriate gas limit based on operation mode."""
59-
if request.config.op_mode == OpMode.BENCHMARKING: # type: ignore[attr-defined]
60-
return Environment(gas_limit=GIGA_GAS)
66+
"""Return an Environment instance with appropriate gas limit based on test type."""
67+
if request.node.get_closest_marker("benchmark") is not None:
68+
return Environment(gas_limit=BENCHMARKING_MAX_GAS)
6169
return Environment()

src/pytest_plugins/shared/execute_fill.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from ethereum_test_execution import BaseExecute, LabeledExecuteFormat
99
from ethereum_test_fixtures import BaseFixture, LabeledFixtureFormat
1010
from ethereum_test_specs import BaseTest
11-
from ethereum_test_tools import Environment
1211
from ethereum_test_types import EOA, Alloc
1312

1413
from ..spec_version_checker.spec_version_checker import EIPSpecTestItem
@@ -22,6 +21,19 @@ class OpMode(StrEnum):
2221
BENCHMARKING = "benchmarking"
2322

2423

24+
ALL_FIXTURE_PARAMETERS = {
25+
"genesis_environment",
26+
"env",
27+
}
28+
"""
29+
List of test parameters that have a default fixture value which can be retrieved and used
30+
for the test instance if it was not explicitly specified when calling from the test
31+
function.
32+
33+
All parameter names included in this list must define a fixture in one of the plugins.
34+
"""
35+
36+
2537
@pytest.hookimpl(tryfirst=True)
2638
def pytest_configure(config: pytest.Config):
2739
"""
@@ -194,8 +206,3 @@ def pytest_addoption(parser: pytest.Parser):
194206
default=None,
195207
help=("Enable reading and filling from static test files."),
196208
)
197-
198-
199-
@pytest.fixture
200-
def env(request: pytest.FixtureRequest) -> Environment: # noqa: D103
201-
return Environment()

tests/benchmark/test_worst_blocks.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
Alloc,
1515
Block,
1616
BlockchainTestFiller,
17-
Environment,
1817
StateTestFiller,
1918
Transaction,
2019
)
@@ -151,11 +150,11 @@ def test_block_full_of_ether_transfers(
151150
)
152151

153152
blockchain_test(
154-
genesis_environment=Environment(),
155153
pre=pre,
156154
post=post_state,
157155
blocks=[Block(txs=txs)],
158156
exclude_full_post_state_in_output=True,
157+
expected_benchmark_gas_used=iteration_count * intrinsic_cost,
159158
)
160159

161160

@@ -174,7 +173,6 @@ def test_block_full_data(
174173
intrinsic_cost: int,
175174
total_cost_floor_per_token: int,
176175
gas_benchmark_value: int,
177-
env: Environment,
178176
):
179177
"""Test a block with empty payload."""
180178
# Gas cost calculation based on EIP-7683: (https://eips.ethereum.org/EIPS/eip-7683)
@@ -217,7 +215,6 @@ def test_block_full_data(
217215
)
218216

219217
state_test(
220-
env=env,
221218
pre=pre,
222219
post={},
223220
tx=tx,

0 commit comments

Comments
 (0)