Skip to content

Commit ae9b597

Browse files
raxhvlfselmo
andauthored
✨ feat(tests): EIP-7928 Single-Opcode Success and OOG test cases (#2118)
* ✨ feat(EIP-7928): OOG: Intrinsic gas * 🚧 wip(EIP-7928): OOG tests * fix: BAL expectations for OOG 7928 tests - fix linter * remove equivalent test cases as covered by opcode cases * 🥢 nit: named parameters * fix: Tighten up validation for empty lists on BALs * chore: CHANGELOG entry * chore: fix docstring after refactor --------- Co-authored-by: raxhvl <[email protected]> Co-authored-by: fselmo <[email protected]>
1 parent 9a1cd16 commit ae9b597

File tree

5 files changed

+604
-144
lines changed

5 files changed

+604
-144
lines changed

docs/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,14 @@ Test fixtures for use by clients are available for each release on the [Github r
3030
- ✨ Add flexible API for absence checks for EIP-7928 (BAL) tests ([#2124](https://github.com/ethereum/execution-spec-tests/pull/2124)).
3131
- 🐞 Use `engine_newPayloadV5` for `>=Amsterdam` forks in `consume engine` ([#2170](https://github.com/ethereum/execution-spec-tests/pull/2170)).
3232
- 🔀 Refactor EIP-7928 (BAL) absence checks into a friendlier class-based DevEx ([#2175](https://github.com/ethereum/execution-spec-tests/pull/2175)).
33+
- 🐞 Tighten up validation for empty lists on Block-Level Access List tests ([#2118](https://github.com/ethereum/execution-spec-tests/pull/2118)).
3334

3435
### 🧪 Test Cases
3536

3637
- ✨ Add safe EIP-6110 workaround to allow Geth/Reth to pass invalid deposit request tests even thought they are out of spec ([#2177](https://github.com/ethereum/execution-spec-tests/pull/2177), [#2233](https://github.com/ethereum/execution-spec-tests/pull/2233)).
3738
- ✨ Add an EIP-7928 test case targeting the `SELFDESTRUCT` opcode. ([#2159](https://github.com/ethereum/execution-spec-tests/pull/2159)).
3839
- ✨ Add essential tests for coverage gaps in EIP-7951 (`p256verify` precompile) ([#2179](https://github.com/ethereum/execution-spec-tests/pull/2159), [#2203](https://github.com/ethereum/execution-spec-tests/pull/2203), [#2215](https://github.com/ethereum/execution-spec-tests/pull/2215), [#2216](https://github.com/ethereum/execution-spec-tests/pull/2216), [#2217](https://github.com/ethereum/execution-spec-tests/pull/2217), [#2218](https://github.com/ethereum/execution-spec-tests/pull/2218), [#2221](https://github.com/ethereum/execution-spec-tests/pull/2221), [#2229](https://github.com/ethereum/execution-spec-tests/pull/2229), [#2230](https://github.com/ethereum/execution-spec-tests/pull/2230), [#2237](https://github.com/ethereum/execution-spec-tests/pull/2237), [#2238](https://github.com/ethereum/execution-spec-tests/pull/2238)).
40+
- ✨ Add EIP-7928 successful and OOG single-opcode tests ([#2118](https://github.com/ethereum/execution-spec-tests/pull/2118)).
3941

4042
## [v5.0.0](https://github.com/ethereum/execution-spec-tests/releases/tag/v5.0.0) - 2025-09-05
4143

src/ethereum_test_types/block_access_list/expectations.py

Lines changed: 34 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,10 @@ def _compare_account_expectations(
305305
if field_name not in expected.model_fields_set:
306306
continue
307307

308+
# Check if explicitly set to empty but actual has values
309+
if not expected_list and actual_list:
310+
raise AssertionError(f"Expected {field_name} to be empty but found {actual_list}")
311+
308312
if field_name == "storage_reads":
309313
# storage_reads is a simple list of StorageKey
310314
actual_idx = 0
@@ -377,49 +381,39 @@ def _compare_account_expectations(
377381

378382
else:
379383
# Handle nonce_changes, balance_changes, code_changes
380-
if not expected_list and actual_list:
381-
# Empty expected but non-empty actual - error
382-
item_type = field_name.replace("_changes", "")
383-
raise AssertionError(
384-
f"Expected {field_name} to be empty but found {actual_list}"
385-
)
386-
384+
# Create tuples for comparison (ordering already validated)
385+
if field_name == "nonce_changes":
386+
expected_tuples = [(c.tx_index, c.post_nonce) for c in expected_list]
387+
actual_tuples = [(c.tx_index, c.post_nonce) for c in actual_list]
388+
item_type = "nonce"
389+
elif field_name == "balance_changes":
390+
expected_tuples = [(c.tx_index, int(c.post_balance)) for c in expected_list]
391+
actual_tuples = [(c.tx_index, int(c.post_balance)) for c in actual_list]
392+
item_type = "balance"
393+
elif field_name == "code_changes":
394+
expected_tuples = [(c.tx_index, bytes(c.new_code)) for c in expected_list]
395+
actual_tuples = [(c.tx_index, bytes(c.new_code)) for c in actual_list]
396+
item_type = "code"
387397
else:
388-
# Create tuples for comparison (ordering already validated)
389-
if field_name == "nonce_changes":
390-
expected_tuples = [(c.tx_index, c.post_nonce) for c in expected_list]
391-
actual_tuples = [(c.tx_index, c.post_nonce) for c in actual_list]
392-
item_type = "nonce"
393-
elif field_name == "balance_changes":
394-
expected_tuples = [
395-
(c.tx_index, int(c.post_balance)) for c in expected_list
396-
]
397-
actual_tuples = [(c.tx_index, int(c.post_balance)) for c in actual_list]
398-
item_type = "balance"
399-
elif field_name == "code_changes":
400-
expected_tuples = [(c.tx_index, bytes(c.new_code)) for c in expected_list]
401-
actual_tuples = [(c.tx_index, bytes(c.new_code)) for c in actual_list]
402-
item_type = "code"
403-
else:
404-
# sanity check
405-
raise ValueError(f"Unexpected field type: {field_name}")
406-
407-
# Check that expected forms a subsequence of actual
408-
actual_idx = 0
409-
for exp_tuple in expected_tuples:
410-
found = False
411-
while actual_idx < len(actual_tuples):
412-
if actual_tuples[actual_idx] == exp_tuple:
413-
found = True
414-
actual_idx += 1
415-
break
398+
# sanity check
399+
raise ValueError(f"Unexpected field type: {field_name}")
400+
401+
# Check that expected forms a subsequence of actual
402+
actual_idx = 0
403+
for exp_tuple in expected_tuples:
404+
found = False
405+
while actual_idx < len(actual_tuples):
406+
if actual_tuples[actual_idx] == exp_tuple:
407+
found = True
416408
actual_idx += 1
409+
break
410+
actual_idx += 1
417411

418-
if not found:
419-
raise AssertionError(
420-
f"{item_type.capitalize()} change {exp_tuple} not found "
421-
f"or not in correct order. Actual changes: {actual_tuples}"
422-
)
412+
if not found:
413+
raise AssertionError(
414+
f"{item_type.capitalize()} change {exp_tuple} not found "
415+
f"or not in correct order. Actual changes: {actual_tuples}"
416+
)
423417

424418

425419
__all__ = [

src/ethereum_test_types/tests/test_block_access_lists.py

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -77,47 +77,62 @@ def test_empty_list_validation():
7777
[
7878
BalAccountChange(
7979
address=alice,
80-
nonce_changes=[BalNonceChange(tx_index=1, post_nonce=1)],
81-
balance_changes=[], # no balance changes
80+
nonce_changes=[],
81+
balance_changes=[],
82+
code_changes=[],
83+
storage_changes=[],
84+
storage_reads=[],
8285
),
8386
]
8487
)
8588

8689
expectation = BlockAccessListExpectation(
8790
account_expectations={
8891
alice: BalAccountExpectation(
89-
nonce_changes=[BalNonceChange(tx_index=1, post_nonce=1)],
90-
balance_changes=[], # explicitly expect no balance changes
92+
nonce_changes=[],
93+
balance_changes=[],
94+
code_changes=[],
95+
storage_changes=[],
96+
storage_reads=[],
9197
),
9298
}
9399
)
94100

95101
expectation.verify_against(actual_bal)
96102

97103

98-
def test_empty_list_validation_fails():
104+
@pytest.mark.parametrize(
105+
"field,value",
106+
[
107+
["nonce_changes", BalNonceChange(tx_index=1, post_nonce=1)],
108+
["balance_changes", BalBalanceChange(tx_index=1, post_balance=100)],
109+
["code_changes", BalCodeChange(tx_index=1, new_code=b"code")],
110+
[
111+
"storage_changes",
112+
BalStorageSlot(
113+
slot=0x01,
114+
slot_changes=[BalStorageChange(tx_index=1, post_value=0x42)],
115+
),
116+
],
117+
["storage_reads", 0x01],
118+
],
119+
)
120+
def test_empty_list_validation_fails(field: str, value) -> None:
99121
"""Test that validation fails when expecting empty but field has values."""
100122
alice = Address(0xA)
101123

102-
actual_bal = BlockAccessList(
103-
[
104-
BalAccountChange(
105-
address=alice,
106-
balance_changes=[BalBalanceChange(tx_index=1, post_balance=100)],
107-
),
108-
]
109-
)
124+
bal_acct_change = BalAccountChange(address=alice)
125+
setattr(bal_acct_change, field, [value])
126+
actual_bal = BlockAccessList([bal_acct_change])
110127

111-
expectation = BlockAccessListExpectation(
112-
account_expectations={
113-
# expect no balance changes (wrongly)
114-
alice: BalAccountExpectation(balance_changes=[]),
115-
}
116-
)
128+
alice_acct_expectation = BalAccountExpectation()
129+
setattr(alice_acct_expectation, field, [])
130+
131+
expectation = BlockAccessListExpectation(account_expectations={alice: alice_acct_expectation})
117132

118133
with pytest.raises(
119134
BlockAccessListValidationError,
120-
match="Expected balance_changes to be empty",
135+
match=f"Expected {field} to be empty",
121136
):
122137
expectation.verify_against(actual_bal)
123138

tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists.py

Lines changed: 0 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
Block,
1212
BlockchainTestFiller,
1313
Initcode,
14-
Storage,
1514
Transaction,
1615
compute_create_address,
1716
)
@@ -128,89 +127,6 @@ def test_bal_balance_changes(
128127
)
129128

130129

131-
def test_bal_storage_writes(
132-
pre: Alloc,
133-
blockchain_test: BlockchainTestFiller,
134-
):
135-
"""Ensure BAL captures storage writes."""
136-
storage = Storage({0x01: 0}) # type: ignore
137-
storage_contract = pre.deploy_contract(
138-
code=Op.SSTORE(0x01, 0x42) + Op.STOP,
139-
# pre-fill with canary value to detect writes in post-state
140-
storage=storage.canary(),
141-
)
142-
alice = pre.fund_eoa()
143-
144-
tx = Transaction(
145-
sender=alice,
146-
to=storage_contract,
147-
gas_limit=100000,
148-
)
149-
150-
block = Block(
151-
txs=[tx],
152-
expected_block_access_list=BlockAccessListExpectation(
153-
account_expectations={
154-
storage_contract: BalAccountExpectation(
155-
storage_changes=[
156-
BalStorageSlot(
157-
slot=0x01,
158-
slot_changes=[BalStorageChange(tx_index=1, post_value=0x42)],
159-
)
160-
],
161-
),
162-
}
163-
),
164-
)
165-
166-
blockchain_test(
167-
pre=pre,
168-
blocks=[block],
169-
post={
170-
alice: Account(nonce=1),
171-
storage_contract: Account(storage={0x01: 0x42}),
172-
},
173-
)
174-
175-
176-
def test_bal_storage_reads(
177-
pre: Alloc,
178-
blockchain_test: BlockchainTestFiller,
179-
):
180-
"""Ensure BAL captures storage reads."""
181-
storage_contract = pre.deploy_contract(
182-
code=Op.SLOAD(0x01) + Op.STOP,
183-
storage={0x01: 0x42},
184-
)
185-
alice = pre.fund_eoa()
186-
187-
tx = Transaction(
188-
sender=alice,
189-
to=storage_contract,
190-
gas_limit=100000,
191-
)
192-
193-
block = Block(
194-
txs=[tx],
195-
expected_block_access_list=BlockAccessListExpectation(
196-
account_expectations={
197-
storage_contract: BalAccountExpectation(
198-
storage_reads=[0x01],
199-
),
200-
}
201-
),
202-
)
203-
204-
blockchain_test(
205-
pre=pre,
206-
blocks=[block],
207-
post={
208-
alice: Account(nonce=1),
209-
storage_contract: Account(storage={0x01: 0x42}),
210-
},
211-
)
212-
213-
214130
def test_bal_code_changes(
215131
pre: Alloc,
216132
blockchain_test: BlockchainTestFiller,

0 commit comments

Comments
 (0)