From 84686b25e16ced644ab42acefb2d83aacfb10782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 19 May 2026 08:32:22 +0200 Subject: [PATCH] feat(tests): EIP-8037 CREATE-tx collision refunds state-gas reservoir Add `test_create_tx_collision_refunds_reservoir`: a depth-0 CREATE-tx with `gas_limit > TX_MAX_GAS_LIMIT` that collides at the deterministic CREATE address. The existing collision test uses `gas_limit` near the intrinsic, so the state-gas reservoir is empty and the reservoir-preservation path is untested. Block-level `gas_used` is the same under both buggy and correct behaviors; the sender's post-balance is the discriminator. A buggy implementation that drops the reservoir on collision overcharges the sender by `reservoir * gas_price`. --- .../test_state_gas_create.py | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py b/tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py index 2a52558ee87..08d65b40689 100644 --- a/tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py +++ b/tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py @@ -1938,6 +1938,68 @@ def test_create_tx_collision_refunds_intrinsic_new_account( ) +@pytest.mark.pre_alloc_mutable() +@pytest.mark.execute(pytest.mark.skip(reason="Requires specific gas price")) +@pytest.mark.valid_from("EIP8037") +def test_create_tx_collision_refunds_reservoir( + blockchain_test: BlockchainTestFiller, + pre: Alloc, + fork: Fork, +) -> None: + """ + Verify the state-gas reservoir is refunded on a depth-0 CREATE-tx + address collision when `gas_limit > TX_MAX_GAS_LIMIT`. + + EIP-8037 splits `gas_limit` into the capped regular budget and a + state-gas reservoir. On collision the inner regular gas is burnt + and `intrinsic_state_gas` is refunded; the reservoir must also + be refunded to the sender. `header.gas_used` is fixed at the + regular cap regardless of reservoir handling, so the sender's + post-balance is the primary discriminating assertion. + """ + gas_limit_cap = fork.transaction_gas_limit_cap() + assert gas_limit_cap is not None + + init_code = Op.STOP + # +1 above intrinsic_state_gas (= create_state_gas(code_size=0) + # for empty-code CREATE-tx) makes message.state_gas_reservoir > 0. + reservoir = fork.create_state_gas(code_size=0) + 1 + gas_limit = gas_limit_cap + reservoir + initial_fund = 10**18 + + sender = pre.fund_eoa(initial_fund) + collision_target = compute_create_address(address=sender, nonce=0) + pre[collision_target] = Account(nonce=1) + + tx = Transaction( + to=None, + data=init_code, + gas_limit=gas_limit, + sender=sender, + ) + tx_gas_price = ( + tx.gas_price if tx.gas_price is not None else tx.max_fee_per_gas + ) + assert tx_gas_price is not None + + blockchain_test( + pre=pre, + blocks=[ + Block( + txs=[tx], + header_verify=Header(gas_used=gas_limit_cap), + ), + ], + post={ + sender: Account( + balance=initial_fund - gas_limit_cap * tx_gas_price, + nonce=1, + ), + collision_target: Account(nonce=1, code=b"", storage={}), + }, + ) + + @pytest.mark.parametrize( "initcode_size_delta", [