Skip to content

test_reservoir_restored_after_child_spill_and_halt()

Documentation for tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_call.py::test_reservoir_restored_after_child_spill_and_halt@5c024cbb.

Generate fixtures for these test cases for Amsterdam with:

fill -v tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_call.py::test_reservoir_restored_after_child_spill_and_halt --fork Amsterdam

Test parent gets reservoir back after child spill + halt.

The child performs two SSTOREs (zero-to-nonzero), exhausting the reservoir and spilling into gas_left, then hits INVALID causing an exceptional halt. The child's halt resets its frame to (0, R0_child) — only the reservoir-portion is returned to the parent; the spilled gas stays burned (re-classified as regular). The parent does two SSTOREs: the first drains the recovered reservoir, the second spills from the parent's own gas_left.

Source code in tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_call.py
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
@pytest.mark.valid_from("EIP8037")
def test_reservoir_restored_after_child_spill_and_halt(
    state_test: StateTestFiller,
    pre: Alloc,
    fork: Fork,
) -> None:
    """
    Test parent gets reservoir back after child spill + halt.

    The child performs two SSTOREs (zero-to-nonzero), exhausting the
    reservoir and spilling into `gas_left`, then hits INVALID causing
    an exceptional halt. The child's halt resets its frame to (0,
    R0_child) — only the reservoir-portion is returned to the
    parent; the spilled gas stays burned (re-classified as regular).
    The parent does two SSTOREs: the first drains the recovered
    reservoir, the second spills from the parent's own `gas_left`.
    """
    sstore_state_gas = Op.SSTORE(new_value=1).state_cost(fork)

    # Child does two SSTOREs then halts
    child = pre.deploy_contract(
        code=(Op.SSTORE(0, 1) + Op.SSTORE(1, 1) + Op.INVALID),
    )

    parent_storage = Storage()
    parent = pre.deploy_contract(
        code=(
            Op.POP(Op.CALL(gas=500_000, address=child))
            # First SSTORE drains the recovered reservoir; second
            # SSTORE spills from parent's gas_left (gas_limit_cap is
            # large enough to absorb it).
            + Op.SSTORE(parent_storage.store_next(1), 1)
            + Op.SSTORE(parent_storage.store_next(1), 1)
        ),
    )

    # Reservoir = 1 SSTORE's worth of state gas — child will spill
    tx = Transaction(
        to=parent,
        state_gas_reservoir=sstore_state_gas,
        sender=pre.fund_eoa(),
    )

    post = {parent: Account(storage=parent_storage)}
    state_test(pre=pre, post=post, tx=tx)

Parametrized Test Cases

This test generates 1 parametrized test case across 1 fork.