Skip to content

test_create_child_spill_not_double_charged()

Documentation for tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py::test_create_child_spill_not_double_charged@8acae1b0.

Generate fixtures for these test cases for Amsterdam with:

fill -v tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py::test_create_child_spill_not_double_charged --fork Amsterdam

Test CREATE/CREATE2 child state gas paid from gas_left is not recharged.

The factory executes below the Amsterdam tx gas cap, so the CREATE child pays new-account and storage state gas by spilling from gas_left. The gas limit covers that bill once, so charging the same state growth again at frame end would run the transaction out of gas.

Source code in tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
@pytest.mark.parametrize("enough_gas", [False, True])
@pytest.mark.with_all_create_opcodes
@pytest.mark.valid_from("EIP8037")
def test_create_child_spill_not_double_charged(
    state_test: StateTestFiller,
    pre: Alloc,
    fork: Fork,
    create_opcode: Op,
    enough_gas: bool,
) -> None:
    """
    Test CREATE/CREATE2 child state gas paid from `gas_left` is not recharged.

    The factory executes below the Amsterdam tx gas cap, so the CREATE child
    pays new-account and storage state gas by spilling from `gas_left`. The
    gas limit covers that bill once, so charging the same state growth again
    at frame end would run the transaction out of gas.
    """
    init_code = sum(Op.SSTORE(i, i + 1) for i in range(6)) + Op.STOP
    mstore_value, initcode_size = init_code_at_high_bytes(init_code)

    factory_code = Op.MSTORE(
        0,
        mstore_value,
        # gas accounting
        new_memory_size=32,
    ) + (
        create_opcode(
            value=0,
            offset=0,
            size=initcode_size,
            # gas accounting
            init_code_size=initcode_size,
        )
    )
    factory = pre.deploy_contract(code=factory_code)
    created = compute_create_address(
        address=factory,
        salt=0,
        nonce=1,
        initcode=init_code,
        opcode=create_opcode,
    )

    # The child's grant is short a 64th of what the factory holds at
    # dispatch, so its bill has to be grossed up by that fraction.
    child_gas = init_code.gas_cost(fork)
    gas_limit = (
        fork.transaction_intrinsic_cost_calculator()()
        + factory_code.gas_cost(fork)
        + child_gas * 64 // 63
    )
    if not enough_gas:
        gas_limit -= 1

    tx = Transaction(
        to=factory,
        gas_limit=gas_limit,
        sender=pre.fund_eoa(),
    )

    post = {
        created: Account(nonce=1, storage={i: i + 1 for i in range(6)})
        if enough_gas
        else Account.NONEXISTENT,
    }
    state_test(pre=pre, post=post, tx=tx)

Parametrized Test Cases

This test generates 4 parametrized test cases across 1 fork.