Skip to content

Commit 3e75393

Browse files
qu0bfselmo
authored andcommitted
eip7928: add SELFDESTRUCT OOG BAL test
1 parent 67c1983 commit 3e75393

File tree

2 files changed

+71
-0
lines changed

2 files changed

+71
-0
lines changed

tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,76 @@ def test_bal_self_destruct(
373373
)
374374

375375

376+
def test_bal_self_destruct_oog(
377+
pre: Alloc,
378+
blockchain_test: BlockchainTestFiller,
379+
) -> None:
380+
"""
381+
Test that SELFDESTRUCT beneficiary is NOT included in BAL when OOG.
382+
383+
When SELFDESTRUCT runs out of gas, the operation fails and the beneficiary
384+
address should NOT be added to the Block Access List.
385+
386+
This test:
387+
1. Deploys a contract with SELFDESTRUCT bytecode
388+
2. Calls the contract with limited gas so SELFDESTRUCT fails OOG
389+
3. Verifies beneficiary is NOT in BAL (the CALL reverts, undoing BAL changes)
390+
391+
SELFDESTRUCT gas cost to cold new account: 5000 + 2600 + 25000 = 32600 gas
392+
"""
393+
alice = pre.fund_eoa()
394+
395+
# Beneficiary address for SELFDESTRUCT
396+
beneficiary = Address(0xBEEF)
397+
398+
# Contract: PUSH20 <beneficiary> SELFDESTRUCT
399+
selfdestruct_code = Op.SELFDESTRUCT(beneficiary)
400+
selfdestruct_contract = pre.deploy_contract(code=selfdestruct_code, balance=1000)
401+
402+
# Caller contract: CALL with limited gas to cause OOG on SELFDESTRUCT
403+
# SELFDESTRUCT needs 32600 gas, we give it only 100
404+
caller_code = (
405+
Op.CALL(gas=100, address=selfdestruct_contract, value=0,
406+
args_offset=0, args_size=0, ret_offset=0, ret_size=0)
407+
+ Op.STOP
408+
)
409+
caller_contract = pre.deploy_contract(code=caller_code)
410+
411+
tx = Transaction(
412+
sender=alice,
413+
to=caller_contract,
414+
gas_limit=100_000,
415+
gas_price=0xA,
416+
)
417+
418+
# The inner CALL fails OOG, so SELFDESTRUCT doesn't complete.
419+
# Beneficiary should NOT be in BAL.
420+
block = Block(
421+
txs=[tx],
422+
expected_block_access_list=BlockAccessListExpectation(
423+
account_expectations={
424+
alice: BalAccountExpectation(
425+
nonce_changes=[BalNonceChange(tx_index=1, post_nonce=1)],
426+
),
427+
caller_contract: BalAccountExpectation.empty(),
428+
selfdestruct_contract: BalAccountExpectation.empty(),
429+
# beneficiary should NOT appear - SELFDESTRUCT failed OOG
430+
}
431+
),
432+
)
433+
434+
blockchain_test(
435+
pre=pre,
436+
blocks=[block],
437+
post={
438+
alice: Account(nonce=1),
439+
caller_contract: Account(code=caller_code),
440+
# Contract still exists - SELFDESTRUCT failed
441+
selfdestruct_contract: Account(balance=1000, code=selfdestruct_code),
442+
},
443+
)
444+
445+
376446
@pytest.mark.parametrize(
377447
"account_access_opcode",
378448
[

tests/amsterdam/eip7928_block_level_access_lists/test_cases.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,4 @@
9595
| `test_selfdestruct_created_in_same_tx_with_revert` | Ensure BAL tracks selfdestruct with revert correctly (pre-Amsterdam test with BAL) | Contract created and selfdestructed in same tx with nested revert | BAL **MUST** track storage reads and balance changes for selfdestruct even with reverts | ✅ Completed |
9696
| `test_value_transfer_gas_calculation` | Ensure BAL correctly tracks OOG scenarios for CALL/CALLCODE/DELEGATECALL/STATICCALL (pre-Amsterdam test with BAL) | Nested calls with precise gas limits to test OOG behavior. For CALL with OOG: target account read for `is_account_alive` check. For CALLCODE/DELEGATECALL/STATICCALL with OOG: target account **NOT** read (OOG before state access) | For CALL: target in BAL even with OOG. For CALLCODE/DELEGATECALL/STATICCALL: target **NOT** in BAL when OOG (state access deferred until after gas check) | ✅ Completed |
9797
| `test_bal_cross_block_precompile_state_leak` | Ensure internal EVM state for precompile handling does not leak between blocks | Block 1: Alice calls RIPEMD-160 (0x03) with zero value (RIPEMD-160 must be pre-funded). Block 2: Bob's transaction triggers an exception (stack underflow). | BAL for Block 1 **MUST** include RIPEMD-160. BAL for Block 2 **MUST NOT** include RIPEMD-160 (never accessed in Block 2). Internal state from Parity Touch Bug (EIP-161) handling must be reset between blocks. | ✅ Completed |
98+
| `test_bal_self_destruct_oog` | Ensure BAL does not include SELFDESTRUCT beneficiary when operation fails due to OOG | Alice calls `Caller` contract which CALLs `SelfDestructContract` with limited gas (100). `SelfDestructContract` attempts SELFDESTRUCT to `Beneficiary`. SELFDESTRUCT requires 32600 gas (5000 base + 2600 cold + 25000 new account). | BAL **MUST** include Alice with `nonce_changes`, `Caller` with empty changes, `SelfDestructContract` with empty changes. BAL **MUST NOT** include `Beneficiary` (SELFDESTRUCT failed OOG, CALL reverted, BAL changes rolled back). Contract balance unchanged. | ✅ Completed |

0 commit comments

Comments
 (0)