Skip to content

Commit 641036c

Browse files
temp
1 parent 21c38bb commit 641036c

File tree

5 files changed

+248
-2
lines changed

5 files changed

+248
-2
lines changed

src/ethereum_test_specs/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from .base import BaseTest, TestSpec
44
from .base_static import BaseStaticTest
55
from .benchmark import BenchmarkTest, BenchmarkTestFiller, BenchmarkTestSpec
6+
from .benchmark_state import BenchmarkStateTest, BenchmarkStateTestFiller, BenchmarkStateTestSpec
67
from .blobs import BlobsTest, BlobsTestFiller, BlobsTestSpec
78
from .blockchain import (
89
BlockchainTest,
@@ -27,6 +28,9 @@
2728
"BenchmarkTest",
2829
"BenchmarkTestFiller",
2930
"BenchmarkTestSpec",
31+
"BenchmarkStateTest",
32+
"BenchmarkStateTestFiller",
33+
"BenchmarkStateTestSpec",
3034
"BlobsTest",
3135
"BlobsTestFiller",
3236
"BlobsTestSpec",
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
"""Ethereum benchmark state test spec definition and filler."""
2+
3+
import math
4+
from typing import Callable, ClassVar, Generator, List, Sequence, Type
5+
6+
from ethereum_clis import TransitionTool
7+
from ethereum_test_base_types import HexNumber
8+
from ethereum_test_execution import (
9+
BaseExecute,
10+
ExecuteFormat,
11+
LabeledExecuteFormat,
12+
TransactionPost,
13+
)
14+
from ethereum_test_fixtures import (
15+
BaseFixture,
16+
BlockchainFixture,
17+
FixtureFormat,
18+
LabeledFixtureFormat,
19+
StateFixture,
20+
)
21+
from ethereum_test_forks import Fork
22+
from ethereum_test_tools import Bytecode
23+
from ethereum_test_types import Alloc, Environment, Transaction
24+
25+
from .base import BaseTest
26+
from .blockchain import Block, BlockchainTest
27+
28+
29+
class BenchmarkStateTest(BaseTest):
30+
"""
31+
Test type designed specifically for benchmark state test cases.
32+
33+
This test type is designed specifically for benchmark state test cases and supports:
34+
- High compatibility with state_test format
35+
- Support for EIP-7825 transaction gas limit cap (2^24)
36+
- Automatic splitting of tests when gas limit exceeds the cap
37+
- Converts state_test format to blockchain_test format internally
38+
"""
39+
40+
pre: Alloc
41+
post: Alloc
42+
tx: Transaction
43+
gas_benchmark_value: int
44+
setup_bytecode: Bytecode
45+
attack_bytecode: Bytecode
46+
env: Environment
47+
48+
# Support both state test and blockchain test formats
49+
supported_fixture_formats: ClassVar[Sequence[FixtureFormat | LabeledFixtureFormat]] = [
50+
StateFixture,
51+
] + [
52+
LabeledFixtureFormat(
53+
fixture_format,
54+
f"{fixture_format.format_name}_from_benchmark_state_test",
55+
f"A {fixture_format.format_name} generated from a benchmark_state_test",
56+
)
57+
for fixture_format in BlockchainTest.supported_fixture_formats
58+
]
59+
60+
supported_execute_formats: ClassVar[Sequence[LabeledExecuteFormat]] = [
61+
LabeledExecuteFormat(
62+
TransactionPost,
63+
"benchmark_state_test",
64+
"An execute test derived from a benchmark state test",
65+
),
66+
]
67+
68+
def split_transaction(self, tx: Transaction, gas_limit_cap: int | None) -> List[Transaction]:
69+
"""Split a transaction that exceeds the gas limit cap into multiple transactions."""
70+
if (gas_limit_cap is None) or (tx.gas_limit <= gas_limit_cap):
71+
return [tx]
72+
73+
# Calculate how many transactions we need
74+
total_gas = int(tx.gas_limit)
75+
num_splits = math.ceil(total_gas / gas_limit_cap)
76+
77+
split_transactions = []
78+
remaining_gas = total_gas
79+
for i in range(num_splits):
80+
split_tx = tx.model_copy()
81+
# Use the cap for all transactions except the last one, which gets the remainder
82+
split_tx.gas_limit = HexNumber(min(gas_limit_cap, remaining_gas))
83+
split_tx.nonce = HexNumber(tx.nonce + i)
84+
split_transactions.append(split_tx)
85+
remaining_gas -= gas_limit_cap
86+
87+
return split_transactions
88+
89+
def _generate_state_test_fixture(self, t8n: TransitionTool, fork: Fork) -> StateFixture:
90+
"""Generate a state test fixture from this benchmark state test."""
91+
# Create a temporary StateTest to generate the fixture
92+
from .state import StateTest
93+
94+
state_test = StateTest(
95+
env=self.env,
96+
pre=self.pre,
97+
post=self.post,
98+
tx=self.tx,
99+
)
100+
101+
return state_test.make_state_test_fixture(t8n, fork)
102+
103+
def generate_blockchain_test(self, fork: Fork) -> BlockchainTest:
104+
"""Create a BlockchainTest from this BenchmarkStateTest."""
105+
# Convert single transaction to a block
106+
gas_limit_cap = fork.transaction_gas_limit_cap()
107+
108+
transactions = self.split_transaction(self.tx, gas_limit_cap)
109+
110+
blocks = [Block(txs=transactions)]
111+
112+
return BlockchainTest.from_test(
113+
base_test=self,
114+
pre=self.pre,
115+
post=self.post,
116+
blocks=blocks,
117+
genesis_environment=self.env,
118+
)
119+
120+
def generate(
121+
self,
122+
t8n: TransitionTool,
123+
fork: Fork,
124+
fixture_format: FixtureFormat,
125+
) -> BaseFixture:
126+
"""Generate the test fixture."""
127+
self.check_exception_test(exception=self.tx.error is not None)
128+
if fixture_format in BlockchainTest.supported_fixture_formats:
129+
return self.generate_blockchain_test(fork=fork).generate(
130+
t8n=t8n, fork=fork, fixture_format=fixture_format
131+
)
132+
elif fixture_format == StateFixture:
133+
gas_limit_cap = fork.transaction_gas_limit_cap()
134+
transactions = self.split_transaction(self.tx, gas_limit_cap)
135+
136+
if len(transactions) == 1:
137+
return self._generate_state_test_fixture(t8n, fork)
138+
else:
139+
return self.generate_blockchain_test(fork=fork).generate(
140+
t8n=t8n, fork=fork, fixture_format=BlockchainFixture
141+
)
142+
143+
raise Exception(f"Unknown fixture format: {fixture_format}")
144+
145+
def execute(
146+
self,
147+
*,
148+
fork: Fork,
149+
execute_format: ExecuteFormat,
150+
) -> BaseExecute:
151+
"""Execute the benchmark state test by sending it to the live network."""
152+
if execute_format == TransactionPost:
153+
# Convert to blockchain test and use its execute method
154+
blockchain_test = self.generate_blockchain_test(fork=fork)
155+
return blockchain_test.execute(fork=fork, execute_format=execute_format)
156+
raise Exception(f"Unsupported execute format: {execute_format}")
157+
158+
159+
BenchmarkStateTestSpec = Callable[[str], Generator[BenchmarkStateTest, None, None]]
160+
BenchmarkStateTestFiller = Type[BenchmarkStateTest]

src/ethereum_test_tools/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
from ethereum_test_fixtures import BaseFixture, FixtureCollector
2626
from ethereum_test_specs import (
2727
BaseTest,
28+
BenchmarkStateTest,
29+
BenchmarkStateTestFiller,
2830
BenchmarkTest,
2931
BenchmarkTestFiller,
3032
BlobsTest,
@@ -102,6 +104,8 @@
102104
"BaseTest",
103105
"BenchmarkTest",
104106
"BenchmarkTestFiller",
107+
"BenchmarkStateTest",
108+
"BenchmarkStateTestFiller",
105109
"Blob",
106110
"BlobsTest",
107111
"BlobsTestFiller",
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
"""
2+
Test file demonstrating the benchmark_state_test usage scenarios.
3+
4+
This file shows how BenchmarkStateTest can be used to convert state_test format
5+
to blockchain_test format with EIP-7825 support.
6+
"""
7+
8+
import pytest
9+
10+
from ethereum_test_tools import (
11+
Alloc,
12+
BenchmarkStateTestFiller,
13+
Bytecode,
14+
Environment,
15+
Transaction,
16+
)
17+
18+
19+
@pytest.mark.valid_from("Osaka")
20+
def test_benchmark_state_basic_scenario(
21+
benchmark_state_test: BenchmarkStateTestFiller,
22+
pre: Alloc,
23+
env: Environment,
24+
):
25+
"""
26+
Basic scenario: Convert state_test format to blockchain_test format.
27+
28+
This demonstrates how BenchmarkStateTest converts a single transaction
29+
into a blockchain_test format internally.
30+
"""
31+
tx = Transaction(
32+
to=pre.fund_eoa(),
33+
value=1,
34+
gas_limit=21000, # Standard transfer gas
35+
sender=pre.fund_eoa(),
36+
)
37+
38+
# Use state_test format - this will be converted to blockchain_test format
39+
benchmark_state_test(
40+
env=env,
41+
pre=pre,
42+
post={},
43+
tx=tx,
44+
gas_benchmark_value=30_000_000,
45+
setup_bytecode=Bytecode(b""),
46+
attack_bytecode=Bytecode(b""),
47+
)
48+
49+
50+
@pytest.mark.valid_from("Osaka")
51+
def test_benchmark_state_eip7825_gas_limit_cap(
52+
benchmark_state_test: BenchmarkStateTestFiller,
53+
pre: Alloc,
54+
env: Environment,
55+
):
56+
"""
57+
Test EIP-7825 transaction gas limit cap functionality.
58+
59+
This test demonstrates how BenchmarkStateTest automatically handles transaction splitting
60+
when the gas limit exceeds the 2^24 cap introduced in Osaka.
61+
"""
62+
# Create a transaction with gas limit that exceeds the cap (2^24 = 16,777,216)
63+
tx = Transaction(
64+
to=pre.fund_eoa(),
65+
value=1,
66+
gas_limit=20_000_000, # Exceeds the 2^24 cap
67+
sender=pre.fund_eoa(),
68+
)
69+
70+
# This should automatically split the transaction due to EIP-7825
71+
benchmark_state_test(
72+
env=env,
73+
pre=pre,
74+
post={},
75+
tx=tx,
76+
gas_benchmark_value=30_000_000,
77+
setup_bytecode=Bytecode(b""),
78+
attack_bytecode=Bytecode(b""),
79+
)

tests/benchmark/test_worst_blocks.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
Alloc,
1515
BenchmarkTestFiller,
1616
Block,
17-
BlockchainTestFiller,
17+
Environment,
1818
StateTestFiller,
1919
Transaction,
2020
)
@@ -151,7 +151,6 @@ def test_block_full_of_ether_transfers(
151151
else {receiver: Account(balance=balance) for receiver, balance in balances.items()}
152152
)
153153

154-
blockchain_test(
155154
benchmark_test(
156155
genesis_environment=env,
157156
pre=pre,

0 commit comments

Comments
 (0)