Skip to content

test_swap()

Documentation for tests/frontier/opcodes/test_swap.py::test_swap@b2fd7c77.

Generate fixtures for these test cases for Amsterdam with:

fill -v tests/frontier/opcodes/test_swap.py::test_swap --fork Amsterdam

The set of SWAP* opcodes swaps the top of the stack with a specific element.

In this test, we ensure that the set of SWAP* opcodes correctly swaps the top element with the nth element and stores the result in storage.

Source code in tests/frontier/opcodes/test_swap.py
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
@pytest.mark.ported_from(
    [
        "https://github.com/ethereum/tests/blob/v13.3/src/GeneralStateTestsFiller/VMTests/vmTests/swapFiller.yml"
    ],
    pr=["https://github.com/ethereum/execution-spec-tests/pull/1163"],
    coverage_missed_reason=(
        "Test isolation (1 contract per execution) reduces evmone state "
        "comparisons vs old dispatcher pattern (16 contracts per execution)"
    ),
)
@pytest.mark.parametrize(
    "swap_opcode",
    [getattr(Op, f"SWAP{i}") for i in range(1, 17)],
    ids=lambda op: str(op),
)
@pytest.mark.valid_from("Frontier")
def test_swap(
    state_test: StateTestFiller, fork: Fork, pre: Alloc, swap_opcode: Op
) -> None:
    """
    The set of `SWAP*` opcodes swaps the top of the stack with a specific
    element.

    In this test, we ensure that the set of `SWAP*` opcodes correctly swaps
    the top element with the nth element and stores the result in storage.
    """
    env = Environment()

    # Calculate which position we're swapping with (1-based index)
    swap_pos = swap_opcode.int() - 0x90 + 1

    # Generate stack values
    stack_values = list(range(swap_pos + 16))

    # Push the stack values onto the stack (in reverse order).
    contract_code = Bytecode()
    for value in reversed(stack_values):
        contract_code += Op.PUSH1(value)

    # Perform the SWAP operation.
    contract_code += swap_opcode

    # Store multiple values to storage.
    for slot in range(16):
        contract_code += Op.PUSH1(slot) + Op.SSTORE

    # Deploy the contract with the generated bytecode.
    contract_address = pre.deploy_contract(contract_code)

    gas_limit = 500_000
    if fork.is_eip_enabled(8037):
        gas_limit = 1_000_000

    # Create a transaction to execute the contract.
    tx = Transaction(
        sender=pre.fund_eoa(),
        to=contract_address,
        gas_limit=gas_limit,
        protected=fork.supports_protected_txs(),
    )

    # Calculate expected storage values after SWAP and storage operations
    # Initial stack (after pushes, before swap): [0, 1, 2, ..., swap_pos+15]
    # (top is index 0)
    # After SWAP at position swap_pos: top and position swap_pos are swapped
    # Then we do: PUSH1(slot) SSTORE 16 times, which pops values from stack

    # Build the stack state after SWAP
    stack_after_swap = stack_values.copy()
    stack_after_swap[0], stack_after_swap[swap_pos] = (
        stack_after_swap[swap_pos],
        stack_after_swap[0],
    )

    # Store the first 16 values from the post-swap stack
    storage = Storage()
    for value in stack_after_swap[:16]:
        storage.store_next(value)

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

    # Run the state test.
    state_test(env=env, pre=pre, post=post, tx=tx)

Parametrized Test Cases

This test generates 16 parametrized test cases across 14 forks.