Test that DUPN aborts when pushing past the stack limit.
The callee pushes max_stack_height + stack_delta items and
executes DUPN: from one below the limit the duplicate fills the
stack exactly and succeeds, while from a full stack the push
overflows and aborts the frame. The caller stores the call's
success flag over a nonzero canary.
Source code in tests/amsterdam/eip8024_dupn_swapn_exchange/test_dupn.py
| @EIPChecklist.Opcode.Test.StackOverflow()
@pytest.mark.parametrize(
"stack_delta,call_succeeds",
[
pytest.param(-1, True, id="dupn_fills_stack_to_limit"),
pytest.param(0, False, id="dupn_stack_overflow"),
],
)
def test_dupn_stack_overflow(
stack_delta: int,
call_succeeds: bool,
pre: Alloc,
fork: Fork,
state_test: StateTestFiller,
) -> None:
"""
Test that DUPN aborts when pushing past the stack limit.
The callee pushes `max_stack_height + stack_delta` items and
executes DUPN: from one below the limit the duplicate fills the
stack exactly and succeeds, while from a full stack the push
overflows and aborts the frame. The caller stores the call's
success flag over a nonzero canary.
"""
stack_height = fork.max_stack_height() + stack_delta
callee_code = (
Op.PUSH0 * stack_height + Op.DUPN[Spec.MIN_STACK_INDEX] + Op.STOP
)
callee_address = pre.deploy_contract(callee_code)
caller_code = Op.SSTORE(0, Op.CALL(gas=Op.GAS, address=callee_address))
caller_address = pre.deploy_contract(caller_code, storage={0: 0xBA5E})
tx = Transaction(to=caller_address, sender=pre.fund_eoa())
post = {
caller_address: Account(
storage={0: 1 if call_succeeds else 0},
),
}
state_test(pre=pre, post=post, tx=tx)
|