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 | @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)
# Create a transaction to execute the contract.
tx = Transaction(
sender=pre.fund_eoa(),
to=contract_address,
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)
|