Skip to content

test_exchange_full_stack()

Documentation for tests/amsterdam/eip8024_dupn_swapn_exchange/test_exchange.py::test_exchange_full_stack@ffe48572.

Generate fixtures for these test cases for Bogota with:

fill -v tests/amsterdam/eip8024_dupn_swapn_exchange/test_exchange.py::test_exchange_full_stack --fork Bogota

Test EXCHANGE succeeds on a completely full stack.

EXCHANGE swaps in place without pushing, so it must work at the stack limit. The top 30 items hold their 1-indexed position from the top; EXCHANGE[n, m] swaps positions n + 1 and m + 1, then every window item is stored and checked. The topmost item is popped unstored to make room for the SSTORE keys: no valid pair can touch position 1, and a misplaced swap there still corrupts a checked slot.

Source code in tests/amsterdam/eip8024_dupn_swapn_exchange/test_exchange.py
@pytest.mark.parametrize(
    "n,m",
    [
        pytest.param(1, 2, id="adjacent"),
        pytest.param(14, 16, id="max_n"),
        pytest.param(1, 29, id="deepest"),
    ],
)
def test_exchange_full_stack(
    n: int,
    m: int,
    pre: Alloc,
    fork: Fork,
    state_test: StateTestFiller,
) -> None:
    """
    Test EXCHANGE succeeds on a completely full stack.

    EXCHANGE swaps in place without pushing, so it must work at the
    stack limit. The top 30 items hold their 1-indexed position from
    the top; EXCHANGE[n, m] swaps positions n + 1 and m + 1, then every
    window item is stored and checked. The topmost item is popped
    unstored to make room for the SSTORE keys: no valid pair can touch
    position 1, and a misplaced swap there still corrupts a checked
    slot.
    """
    window = Spec.EXCHANGE_MAX_M + 1  # deepest reachable position

    code = Op.PUSH0 * (fork.max_stack_height() - window)
    for depth in range(window, 0, -1):
        code += Op.PUSH1(depth)

    code += Op.EXCHANGE[n, m]

    # The stack is exactly full: pop position 1 so each SSTORE key
    # can be pushed without overflowing.
    code += Op.POP
    for slot in range(1, window):
        code += Op.PUSH1(slot) + Op.SSTORE
    code += Op.STOP

    expected = {slot: slot + 1 for slot in range(1, window)}
    expected[n], expected[m] = expected[m], expected[n]

    contract_address = pre.deploy_contract(code=code)
    tx = Transaction(to=contract_address, sender=pre.fund_eoa())

    post = {contract_address: Account(storage=expected)}

    state_test(pre=pre, post=post, tx=tx)

Parametrized Test Cases

This test generates 3 parametrized test cases across 2 forks.