Skip to content

Commit cec90c2

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

File tree

2 files changed

+90
-0
lines changed

2 files changed

+90
-0
lines changed

tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_opcodes.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,95 @@ def test_bal_extcodecopy_and_oog(
611611
)
612612

613613

614+
@pytest.mark.parametrize(
615+
"memory_offset,copy_size,gas_shortfall",
616+
[
617+
pytest.param(0x10000, 32, "large", id="large_offset"),
618+
pytest.param(256, 32, "boundary", id="boundary"),
619+
],
620+
)
621+
def test_bal_extcodecopy_oog_at_memory_expansion(
622+
pre: Alloc,
623+
blockchain_test: BlockchainTestFiller,
624+
fork: Fork,
625+
memory_offset: int,
626+
copy_size: int,
627+
gas_shortfall: str,
628+
) -> None:
629+
"""
630+
Test EXTCODECOPY OOG at memory expansion - target should NOT appear in BAL.
631+
632+
Gas for all components (cold access + copy + memory expansion) must be
633+
checked BEFORE recording account access.
634+
"""
635+
alice = pre.fund_eoa()
636+
gas_costs = fork.gas_costs()
637+
638+
target_contract = pre.deploy_contract(code=Bytecode(Op.STOP))
639+
640+
# Build EXTCODECOPY contract with appropriate PUSH sizes
641+
if memory_offset <= 0xFF:
642+
dest_push = Op.PUSH1(memory_offset)
643+
elif memory_offset <= 0xFFFF:
644+
dest_push = Op.PUSH2(memory_offset)
645+
else:
646+
dest_push = Op.PUSH3(memory_offset)
647+
648+
extcodecopy_contract_code = Bytecode(
649+
Op.PUSH1(copy_size)
650+
+ Op.PUSH1(0)
651+
+ dest_push
652+
+ Op.PUSH20(target_contract)
653+
+ Op.EXTCODECOPY
654+
+ Op.STOP
655+
)
656+
657+
extcodecopy_contract = pre.deploy_contract(code=extcodecopy_contract_code)
658+
659+
intrinsic_gas_calculator = fork.transaction_intrinsic_cost_calculator()
660+
intrinsic_gas_cost = intrinsic_gas_calculator()
661+
662+
push_cost = gas_costs.G_VERY_LOW * 4
663+
cold_access_cost = gas_costs.G_COLD_ACCOUNT_ACCESS
664+
copy_cost = gas_costs.G_COPY * ((copy_size + 31) // 32)
665+
666+
if gas_shortfall == "large":
667+
# Provide gas for push + cold access + copy, but NOT memory expansion
668+
tx_gas_limit = intrinsic_gas_cost + push_cost + cold_access_cost + copy_cost
669+
else:
670+
# Calculate memory cost and provide exactly 1 less than needed
671+
words = (memory_offset + copy_size + 31) // 32
672+
memory_cost = (words * gas_costs.G_MEMORY) + (words * words // 512)
673+
total_gas_needed = push_cost + cold_access_cost + copy_cost + memory_cost
674+
tx_gas_limit = intrinsic_gas_cost + total_gas_needed - 1
675+
676+
tx = Transaction(
677+
sender=alice,
678+
to=extcodecopy_contract,
679+
gas_limit=tx_gas_limit,
680+
)
681+
682+
block = Block(
683+
txs=[tx],
684+
expected_block_access_list=BlockAccessListExpectation(
685+
account_expectations={
686+
extcodecopy_contract: BalAccountExpectation.empty(),
687+
target_contract: None,
688+
}
689+
),
690+
)
691+
692+
blockchain_test(
693+
pre=pre,
694+
blocks=[block],
695+
post={
696+
alice: Account(nonce=1),
697+
extcodecopy_contract: Account(),
698+
target_contract: Account(),
699+
},
700+
)
701+
702+
614703
def test_bal_storage_write_read_same_frame(
615704
pre: Alloc,
616705
blockchain_test: BlockchainTestFiller,

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_bal_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)