Skip to content

Commit a14169d

Browse files
marioevzspencer-tb
authored andcommitted
feat(tests): add initial eip-7825 test cases.
1 parent 6be0f58 commit a14169d

File tree

5 files changed

+226
-0
lines changed

5 files changed

+226
-0
lines changed

src/ethereum_test_exceptions/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,10 @@ class TransactionException(ExceptionBase):
363363
"""
364364
Transaction causes block to go over blob gas limit.
365365
"""
366+
GAS_LIMIT_EXCEEDS_MAXIMUM = auto()
367+
"""
368+
Transaction gas limit exceeds the maximum allowed limit of 30 million.
369+
"""
366370
TYPE_3_TX_ZERO_BLOBS = auto()
367371
"""
368372
Transaction is type 3, but has no blobs.

src/ethereum_test_forks/base_fork.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,12 @@ def contract_creating_tx_types(cls, block_number: int = 0, timestamp: int = 0) -
337337
"""Return list of the transaction types supported by the fork that can create contracts."""
338338
pass
339339

340+
@classmethod
341+
@abstractmethod
342+
def transaction_gas_limit_cap(cls, block_number: int = 0, timestamp: int = 0) -> int | None:
343+
"""Return the transaction gas limit cap, or None if no limit is imposed."""
344+
pass
345+
340346
@classmethod
341347
@abstractmethod
342348
def precompiles(cls, block_number: int = 0, timestamp: int = 0) -> List[Address]:

src/ethereum_test_forks/forks/forks.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,11 @@ def contract_creating_tx_types(cls, block_number: int = 0, timestamp: int = 0) -
354354
"""At Genesis, only legacy transactions are allowed."""
355355
return [0]
356356

357+
@classmethod
358+
def transaction_gas_limit_cap(cls, block_number: int = 0, timestamp: int = 0) -> int | None:
359+
"""At Genesis, no transaction gas limit cap is imposed."""
360+
return None
361+
357362
@classmethod
358363
def precompiles(cls, block_number: int = 0, timestamp: int = 0) -> List[Address]:
359364
"""At Genesis, no pre-compiles are present."""
@@ -1339,6 +1344,11 @@ def engine_get_blobs_version(cls, block_number: int = 0, timestamp: int = 0) ->
13391344
"""At Osaka, the engine get blobs version is 2."""
13401345
return 2
13411346

1347+
@classmethod
1348+
def transaction_gas_limit_cap(cls, block_number: int = 0, timestamp: int = 0) -> int | None:
1349+
"""At Osaka, transaction gas limit is capped at 30 million."""
1350+
return 30_000_000
1351+
13421352
@classmethod
13431353
def is_deployed(cls) -> bool:
13441354
"""
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""
2+
Test suite for
3+
[EIP-7825: Transaction Gas Limit Cap](https://eips.ethereum.org/EIPS/eip-7825).
4+
"""
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
"""
2+
abstract: Tests [EIP-7825 Transaction Gas Limit Cap](https://eips.ethereum.org/EIPS/eip-7825)
3+
Test cases for [EIP-7825 Transaction Gas Limit Cap](https://eips.ethereum.org/EIPS/eip-7825)].
4+
"""
5+
6+
from typing import List
7+
8+
import pytest
9+
10+
from ethereum_test_forks import Fork
11+
from ethereum_test_tools import (
12+
Account,
13+
Address,
14+
Alloc,
15+
AuthorizationTuple,
16+
Block,
17+
BlockchainTestFiller,
18+
Environment,
19+
StateTestFiller,
20+
Storage,
21+
Transaction,
22+
TransactionException,
23+
add_kzg_version,
24+
)
25+
from ethereum_test_tools.utility.pytest import ParameterSet
26+
from ethereum_test_tools.vm.opcode import Opcodes as Op
27+
28+
REFERENCE_SPEC_GIT_PATH = "EIPS/eip-7825.md"
29+
REFERENCE_SPEC_VERSION = "47cbfed315988c0bd4d10002c110ae402504cd94"
30+
31+
TX_GAS_LIMIT = 30_000_000
32+
BLOB_COMMITMENT_VERSION_KZG = 1
33+
34+
35+
def tx_gas_limit_cap_tests(fork: Fork) -> List[ParameterSet]:
36+
"""
37+
Return a list of tests for transaction gas limit cap parametrized for each different
38+
fork.
39+
"""
40+
fork_tx_gas_limit_cap = fork.transaction_gas_limit_cap()
41+
if fork_tx_gas_limit_cap is None:
42+
# Use a default value for forks that don't have a transaction gas limit cap
43+
return [
44+
pytest.param(TX_GAS_LIMIT + 1, None, id="tx_gas_limit_cap_none"),
45+
]
46+
47+
return [
48+
pytest.param(
49+
fork_tx_gas_limit_cap + 1,
50+
TransactionException.GAS_LIMIT_EXCEEDS_MAXIMUM,
51+
id="tx_gas_limit_cap_exceeds_maximum",
52+
marks=pytest.mark.exception_test,
53+
),
54+
pytest.param(fork_tx_gas_limit_cap, None, id="tx_gas_limit_cap_none"),
55+
]
56+
57+
58+
@pytest.mark.parametrize_by_fork("tx_gas_limit,error", tx_gas_limit_cap_tests)
59+
@pytest.mark.with_all_tx_types
60+
@pytest.mark.valid_from("Prague")
61+
def test_transaction_gas_limit_cap(
62+
state_test: StateTestFiller,
63+
pre: Alloc,
64+
fork: Fork,
65+
tx_gas_limit: int,
66+
error: TransactionException | None,
67+
tx_type: int,
68+
):
69+
"""
70+
TODO: Enter a one-line test summary here.
71+
72+
TODO: (Optional) Enter a more detailed test function description here.
73+
"""
74+
env = Environment()
75+
76+
# TODO: Modify pre-state allocations here.
77+
sender = pre.fund_eoa()
78+
storage = Storage()
79+
contract_address = pre.deploy_contract(
80+
code=Op.SSTORE(storage.store_next(1), 1) + Op.STOP,
81+
)
82+
83+
tx_kwargs = {
84+
"ty": tx_type,
85+
"to": contract_address,
86+
"gas_limit": tx_gas_limit,
87+
"data": b"",
88+
"value": 0,
89+
"sender": sender,
90+
"error": error,
91+
}
92+
93+
# Add extra required fields based on transaction type
94+
if tx_type >= 1:
95+
# Type 1: EIP-2930 Access List Transaction
96+
tx_kwargs["access_list"] = [
97+
{
98+
"address": contract_address,
99+
"storage_keys": [0],
100+
}
101+
]
102+
if tx_type == 3:
103+
# Type 3: EIP-4844 Blob Transaction
104+
tx_kwargs["max_fee_per_blob_gas"] = fork.min_base_fee_per_blob_gas()
105+
tx_kwargs["blob_versioned_hashes"] = add_kzg_version([0], BLOB_COMMITMENT_VERSION_KZG)
106+
elif tx_type == 4:
107+
# Type 4: EIP-7702 Set Code Transaction
108+
signer = pre.fund_eoa(amount=0)
109+
tx_kwargs["authorization_list"] = [
110+
AuthorizationTuple(
111+
signer=signer,
112+
address=Address(0),
113+
nonce=0,
114+
)
115+
]
116+
117+
tx = Transaction(**tx_kwargs)
118+
post = {contract_address: Account(storage=storage if error is None else {})}
119+
120+
state_test(env=env, pre=pre, post=post, tx=tx)
121+
122+
123+
@pytest.mark.valid_at_transition_to("Osaka", subsequent_forks=True)
124+
@pytest.mark.exception_test
125+
def test_transaction_gas_limit_cap_at_transition(
126+
blockchain_test: BlockchainTestFiller,
127+
pre: Alloc,
128+
fork: Fork,
129+
):
130+
"""
131+
Test transaction gas limit cap behavior at the Osaka transition.
132+
133+
Before timestamp 15000: No gas limit cap (transactions with gas > 30M are valid)
134+
At/after timestamp 15000: Gas limit cap of 30M is enforced
135+
"""
136+
sender = pre.fund_eoa()
137+
contract_address = pre.deploy_contract(
138+
code=Op.SSTORE(0, Op.ADD(Op.SLOAD(0), 1)) + Op.STOP,
139+
)
140+
141+
pre_cap = fork.transaction_gas_limit_cap()
142+
if pre_cap is None:
143+
pre_cap = TX_GAS_LIMIT
144+
145+
# Transaction with gas limit above 30M
146+
high_gas_tx = Transaction(
147+
ty=0, # Legacy transaction
148+
to=contract_address,
149+
gas_limit=pre_cap + 1,
150+
data=b"",
151+
value=0,
152+
sender=sender,
153+
)
154+
155+
post_cap = fork.transaction_gas_limit_cap(timestamp=15_000)
156+
post_cap_tx_error = TransactionException.GAS_LIMIT_EXCEEDS_MAXIMUM
157+
158+
assert post_cap is not None, "Post cap should not be None"
159+
assert post_cap <= pre_cap, (
160+
"Post cap should be less than or equal to pre cap, test needs update"
161+
)
162+
163+
# Transaction with gas limit at the cap
164+
cap_gas_tx = Transaction(
165+
ty=0, # Legacy transaction
166+
to=contract_address,
167+
gas_limit=post_cap + 1,
168+
data=b"",
169+
value=0,
170+
sender=sender,
171+
error=post_cap_tx_error,
172+
)
173+
174+
blocks = []
175+
176+
# Before transition (timestamp < 15000): high gas transaction should succeed
177+
blocks.append(
178+
Block(
179+
timestamp=14_999,
180+
txs=[high_gas_tx],
181+
)
182+
)
183+
184+
# At transition (timestamp = 15000): high gas transaction should fail
185+
blocks.append(
186+
Block(
187+
timestamp=15_000,
188+
txs=[cap_gas_tx], # Only transaction at the cap succeeds
189+
exception=post_cap_tx_error,
190+
)
191+
)
192+
193+
# Post state: storage should be updated by successful transactions
194+
post = {
195+
contract_address: Account(
196+
storage={
197+
0: 1, # Set by first transaction (before transition)
198+
}
199+
)
200+
}
201+
202+
blockchain_test(pre=pre, blocks=blocks, post=post)

0 commit comments

Comments
 (0)