Test SWAPN succeeds on a completely full stack.
SWAPN swaps in place without pushing, so it must work at the stack
limit. The top marker is swapped down to position stack_index + 1;
popping stack_index items then exposes it. If a faulty
implementation had not swapped, the popped-to item would hold the
planted deep marker instead, so either direction of failure is
visible in storage.
Source code in tests/amsterdam/eip8024_dupn_swapn_exchange/test_swapn.py
| @EIPChecklist.Opcode.Test.StackComplexOperations.DataPortionVariables.Top()
@EIPChecklist.Opcode.Test.StackComplexOperations.DataPortionVariables.Middle()
@pytest.mark.parametrize(
"stack_index",
[17, 126, 235],
ids=lambda x: f"swapn_full_stack_{x}",
)
def test_swapn_full_stack(
stack_index: int,
pre: Alloc,
fork: Fork,
state_test: StateTestFiller,
) -> None:
"""
Test SWAPN succeeds on a completely full stack.
SWAPN swaps in place without pushing, so it must work at the stack
limit. The top marker is swapped down to position `stack_index + 1`;
popping `stack_index` items then exposes it. If a faulty
implementation had not swapped, the popped-to item would hold the
planted deep marker instead, so either direction of failure is
visible in storage.
"""
sender = pre.fund_eoa()
top_marker = 0xAAAA
deep_marker = 0xBBBB
# Full stack, top-down: the top marker at position 1, the deep
# marker at the swap target, position stack_index + 1.
stack = [0] * fork.max_stack_height()
stack[0] = top_marker
stack[stack_index] = deep_marker
code = Bytecode()
for value in reversed(stack):
code += Op.PUSH2(value) if value else Op.PUSH0
code += Op.SWAPN[stack_index]
# Pop down to the swap target and store the item now there.
code += Op.POP * stack_index
code += Op.PUSH1(0) + Op.SSTORE
code += Op.STOP
contract_address = pre.deploy_contract(code=code, storage={0: 0xBA5E})
tx = Transaction(to=contract_address, sender=sender)
post = {contract_address: Account(storage={0: top_marker})}
state_test(pre=pre, post=post, tx=tx)
|