Skip to content

test_account_warmth_reverts_on_subcall_revert()

Documentation for tests/amsterdam/eip8038_state_access_gas_cost_increase/test_call_gas.py::test_account_warmth_reverts_on_subcall_revert@c74f1a67.

Generate fixtures for these test cases for Amsterdam with:

fill -v tests/amsterdam/eip8038_state_access_gas_cost_increase/test_call_gas.py::test_account_warmth_reverts_on_subcall_revert --fork Amsterdam

Account warmth acquired inside a reverted sub-call does not persist.

An inner contract reads an address's BALANCE via DELEGATECALL (so the warmed address belongs to the shared accessed-addresses set) then REVERTs. Back in the outer frame, that same address's first BALANCE is cold again and is charged COLD_ACCOUNT_ACCESS (3,000), proving the warm-address set is rolled back on revert (mirrors the SLOAD warmth-revert case for the account dimension).

Source code in tests/amsterdam/eip8038_state_access_gas_cost_increase/test_call_gas.py
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
@EIPChecklist.GasCostChanges.Test.GasUpdatesMeasurement()
def test_account_warmth_reverts_on_subcall_revert(
    state_test: StateTestFiller,
    env: Environment,
    pre: Alloc,
    fork: Fork,
) -> None:
    """
    Account warmth acquired inside a reverted sub-call does not persist.

    An inner contract reads an address's ``BALANCE`` via
    ``DELEGATECALL`` (so the warmed address belongs to the shared
    accessed-addresses set) then ``REVERT``s. Back in the outer frame,
    that same address's first ``BALANCE`` is cold again and is charged
    ``COLD_ACCOUNT_ACCESS`` (3,000), proving the warm-address set is
    rolled back on revert (mirrors the ``SLOAD`` warmth-revert case for
    the account dimension).
    """
    gas_costs = fork.gas_costs()
    cold_gas = Op.BALANCE(address_warm=False).gas_cost(fork)
    assert cold_gas == gas_costs.COLD_ACCOUNT_ACCESS

    # Address whose warmth we probe; left out of the access list so its
    # first runtime touch is cold.
    probed = pre.fund_eoa(amount=1)

    # Inner: warm `probed` by reading its balance, then revert.
    inner = pre.deploy_contract(
        code=Op.POP(Op.BALANCE(probed)) + Op.REVERT(0, 0),
    )

    # Outer: DELEGATECALL inner (which warms `probed` in the shared
    # accessed set, then reverts, discarding that warmth), then measure
    # its own first BALANCE of `probed`, which must be cold again.
    measured_code = Op.BALANCE(probed)
    overhead_cost = measured_code.gas_cost(fork) - Op.BALANCE(
        address_warm=False
    ).gas_cost(fork)
    outer_code: Bytecode = Op.POP(
        Op.DELEGATECALL(gas=100_000, address=inner)
    ) + CodeGasMeasure(
        code=measured_code,
        overhead_cost=overhead_cost,
        extra_stack_items=1,
    )
    outer = pre.deploy_contract(code=outer_code)

    tx = Transaction(to=outer, sender=pre.fund_eoa())

    # Slot 0 holds the measured (cold) BALANCE read.
    post = {outer: Account(storage={0: cold_gas})}
    state_test(env=env, pre=pre, post=post, tx=tx)

Parametrized Test Cases

This test generates 1 parametrized test case across 1 fork.