Skip to content

test_child_clear_repays_own_spill_first()

Documentation for tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_cross_frame_refund.py::test_child_clear_repays_own_spill_first@8acae1b0.

Generate fixtures for these test cases for Amsterdam with:

fill -v tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_cross_frame_refund.py::test_child_clear_repays_own_spill_first --fork Amsterdam

Test the cross-slot LIFO split of a cross-frame refund in a child.

The parent spills two fresh sets; a delegated child spills a set of its own, then clears both parent slots. The first credit repays the child's borrow, the second parks in the reservoir and repays one parent spill at the merge, and a failing child discards the parked credit with its rollback. What the call costs gas_left is measured in test_child_clear_window_cost.

Source code in tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_cross_frame_refund.py
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
@pytest.mark.parametrize("child_ending", ["stop", "revert", "invalid"])
@pytest.mark.valid_from("EIP8037")
def test_child_clear_repays_own_spill_first(
    state_test: StateTestFiller,
    pre: Alloc,
    fork: Fork,
    child_ending: str,
) -> None:
    """
    Test the cross-slot LIFO split of a cross-frame refund in a child.

    The parent spills two fresh sets; a delegated child spills a set
    of its own, then clears both parent slots. The first credit repays
    the child's borrow, the second parks in the reservoir and repays
    one parent spill at the merge, and a failing child discards the
    parked credit with its rollback. What the call costs `gas_left` is
    measured in `test_child_clear_window_cost`.
    """
    intrinsic_cost = fork.transaction_intrinsic_cost_calculator()()
    sstore_state_gas = Op.SSTORE(new_value=1).state_cost(fork)

    child_code = clearing_child_code(child_ending)
    child = pre.deploy_contract(code=child_code)
    child_budget = budget_above_sstore_stipend(fork, child_code)

    code = (
        FRESH_SET(SLOT_X, 1)
        + FRESH_SET(SLOT_Y, 1)
        + Op.POP(
            Op.DELEGATECALL(
                gas=child_budget, address=child, address_warm=False
            )
        )
    )
    contract = pre.deploy_contract(code=code)

    parent_exec = code.execution_cost(fork)
    if child_ending == "stop":
        # Only the child's own slot survives. Its borrow was repaid by
        # the first clear's refund, and the second refund repaid one
        # parent spill at the merge.
        before_refund = (
            intrinsic_cost
            + parent_exec
            + child_code.execution_cost(fork)
            + sstore_state_gas
        )
        restore_refund = 2 * (WARM_CLEAR.refund(fork) - sstore_state_gas)
        expected_gas_used = before_refund - min(
            before_refund // fork.max_refund_quotient(), restore_refund
        )
    elif child_ending == "revert":
        expected_gas_used = (
            intrinsic_cost
            + parent_exec
            + child_code.execution_cost(fork)
            + 2 * sstore_state_gas
        )
    else:
        expected_gas_used = (
            intrinsic_cost + parent_exec + child_budget + 2 * sstore_state_gas
        )

    tx = Transaction(
        to=contract,
        state_gas_reservoir=0,
        sender=pre.fund_eoa(),
        expected_receipt=TransactionReceipt(
            cumulative_gas_used=expected_gas_used
        ),
    )

    post = {contract: Account(storage=clearing_child_storage(child_ending))}
    state_test(pre=pre, post=post, tx=tx)

Parametrized Test Cases

This test generates 3 parametrized test cases across 1 fork.