Skip to content

test_exchange_full_stack()

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

Generate fixtures for these test cases for Amsterdam with:

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

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
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
@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 1 fork.