Skip to content

test_reservoir_restored_after_child_spill_and_revert()

Documentation for tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_call.py::test_reservoir_restored_after_child_spill_and_revert@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_revert --fork Amsterdam

Test all state gas recovered when child spills then reverts.

The child performs two SSTOREs (zero-to-nonzero) but only one SSTORE's worth of state gas fits in the reservoir, so the second spills into gas_left. The child then REVERTs. Because state changes are rolled back, the state gas is refilled LIFO: the spilled portion returns to gas_left and the reservoir-funded portion restores the reservoir to its start value. The parent then performs two SSTOREs, drawing one from the restored reservoir and spilling the other from the recovered gas_left.

Source code in tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_call.py
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
237
238
239
240
241
242
243
244
245
246
247
@pytest.mark.valid_from("EIP8037")
def test_reservoir_restored_after_child_spill_and_revert(
    state_test: StateTestFiller,
    pre: Alloc,
    fork: Fork,
) -> None:
    """
    Test all state gas recovered when child spills then reverts.

    The child performs two SSTOREs (zero-to-nonzero) but only one
    SSTORE's worth of state gas fits in the reservoir, so the second
    spills into `gas_left`. The child then REVERTs. Because state
    changes are rolled back, the state gas is refilled LIFO: the
    spilled portion returns to `gas_left` and the reservoir-funded
    portion restores the reservoir to its start value. The parent
    then performs two SSTOREs, drawing one from the restored
    reservoir and spilling the other from the recovered `gas_left`.
    """
    sstore_state_gas = Op.SSTORE(new_value=1).state_cost(fork)

    # Child does two SSTOREs then reverts — the second SSTORE's
    # state gas spills from the reservoir into `gas_left`
    child = pre.deploy_contract(
        code=(Op.SSTORE(0, 1) + Op.SSTORE(1, 1) + Op.REVERT(0, 0)),
    )

    parent_storage = Storage()
    parent = pre.deploy_contract(
        code=(
            Op.POP(Op.CALL(gas=500_000, address=child))
            # State gas recovered LIFO: the spilled SSTORE returns to
            # gas_left, the other restores the reservoir
            + 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.