Skip to content

Commit 68a4be2

Browse files
committed
test(eip7928): add EXTCODECOPY OOG memory expansion BAL test
1 parent e927a67 commit 68a4be2

File tree

2 files changed

+119
-0
lines changed

2 files changed

+119
-0
lines changed
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
"""
2+
Tests for EIP-7928 Block Access Lists - OOG Gas Check Ordering.
3+
4+
Verifies that account access is NOT recorded in BAL when EXTCODECOPY fails
5+
due to OOG at memory expansion. Gas for all components (cold access + copy +
6+
memory expansion) must be checked BEFORE recording account access.
7+
"""
8+
9+
import pytest
10+
from execution_testing import (
11+
Account,
12+
Alloc,
13+
BalAccountExpectation,
14+
Block,
15+
BlockAccessListExpectation,
16+
BlockchainTestFiller,
17+
Bytecode,
18+
Fork,
19+
Op,
20+
Transaction,
21+
)
22+
23+
from .spec import ref_spec_7928
24+
25+
REFERENCE_SPEC_GIT_PATH = ref_spec_7928.git_path
26+
REFERENCE_SPEC_VERSION = ref_spec_7928.version
27+
28+
29+
pytestmark = pytest.mark.valid_from("Amsterdam")
30+
31+
32+
@pytest.mark.parametrize(
33+
"memory_offset,copy_size,gas_shortfall",
34+
[
35+
pytest.param(0x10000, 32, "large", id="large_offset"),
36+
pytest.param(256, 32, "boundary", id="boundary"),
37+
],
38+
)
39+
def test_extcodecopy_oog_at_memory_expansion(
40+
pre: Alloc,
41+
blockchain_test: BlockchainTestFiller,
42+
fork: Fork,
43+
memory_offset: int,
44+
copy_size: int,
45+
gas_shortfall: str,
46+
) -> None:
47+
"""
48+
Test EXTCODECOPY OOG at memory expansion - target should NOT appear in BAL.
49+
50+
Gas for all components (cold access + copy + memory expansion) must be
51+
checked BEFORE recording account access.
52+
"""
53+
alice = pre.fund_eoa()
54+
gas_costs = fork.gas_costs()
55+
56+
target_contract = pre.deploy_contract(code=Bytecode(Op.STOP))
57+
58+
# Build EXTCODECOPY contract with appropriate PUSH sizes
59+
if memory_offset <= 0xFF:
60+
dest_push = Op.PUSH1(memory_offset)
61+
elif memory_offset <= 0xFFFF:
62+
dest_push = Op.PUSH2(memory_offset)
63+
else:
64+
dest_push = Op.PUSH3(memory_offset)
65+
66+
extcodecopy_contract_code = Bytecode(
67+
Op.PUSH1(copy_size)
68+
+ Op.PUSH1(0)
69+
+ dest_push
70+
+ Op.PUSH20(target_contract)
71+
+ Op.EXTCODECOPY
72+
+ Op.STOP
73+
)
74+
75+
extcodecopy_contract = pre.deploy_contract(code=extcodecopy_contract_code)
76+
77+
intrinsic_gas_calculator = fork.transaction_intrinsic_cost_calculator()
78+
intrinsic_gas_cost = intrinsic_gas_calculator()
79+
80+
push_cost = gas_costs.G_VERY_LOW * 4
81+
cold_access_cost = gas_costs.G_COLD_ACCOUNT_ACCESS
82+
copy_cost = gas_costs.G_COPY * ((copy_size + 31) // 32)
83+
84+
if gas_shortfall == "large":
85+
# Provide gas for push + cold access + copy, but NOT memory expansion
86+
tx_gas_limit = intrinsic_gas_cost + push_cost + cold_access_cost + copy_cost
87+
else:
88+
# Calculate memory cost and provide exactly 1 less than needed
89+
words = (memory_offset + copy_size + 31) // 32
90+
memory_cost = (words * gas_costs.G_MEMORY) + (words * words // 512)
91+
total_gas_needed = push_cost + cold_access_cost + copy_cost + memory_cost
92+
tx_gas_limit = intrinsic_gas_cost + total_gas_needed - 1
93+
94+
tx = Transaction(
95+
sender=alice,
96+
to=extcodecopy_contract,
97+
gas_limit=tx_gas_limit,
98+
)
99+
100+
block = Block(
101+
txs=[tx],
102+
expected_block_access_list=BlockAccessListExpectation(
103+
account_expectations={
104+
extcodecopy_contract: BalAccountExpectation.empty(),
105+
target_contract: None,
106+
}
107+
),
108+
)
109+
110+
blockchain_test(
111+
pre=pre,
112+
blocks=[block],
113+
post={
114+
alice: Account(nonce=1),
115+
extcodecopy_contract: Account(),
116+
target_contract: Account(),
117+
},
118+
)

tests/amsterdam/eip7928_block_level_access_lists/test_cases.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,4 @@
9494
| `test_call_to_pre_authorized_oog` | Ensure BAL handles OOG during EIP-7702 delegation access (pre-Amsterdam test with BAL) | Call to delegated account that OOGs before accessing delegation contract | BAL **MUST** include auth_signer (code read for delegation check) but **MUST NOT** include delegation contract (OOG before access) | ✅ Completed |
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 |
97+
| `test_extcodecopy_oog_at_memory_expansion` | Ensure BAL excludes target when EXTCODECOPY OOGs at memory expansion | Parameterized: (1) large_offset: 64KB memory offset, gas covers cold access + copy but NOT memory expansion. (2) boundary: 256 byte offset, gas is exactly 1 less than needed. | BAL **MUST NOT** include target contract. Gas must be checked for ALL components (cold access + copy + memory expansion) BEFORE recording account access. | ✅ Completed |

0 commit comments

Comments
 (0)