|
| 1 | +"""Test execution of EOF code in the context of the operand stack height.""" |
| 2 | + |
| 3 | +import pytest |
| 4 | + |
| 5 | +from ethereum_test_exceptions import EOFException |
| 6 | +from ethereum_test_tools import Account, EOFStateTestFiller |
| 7 | +from ethereum_test_tools.vm.opcode import Opcodes as Op |
| 8 | +from ethereum_test_types.eof.constants import MAX_RUNTIME_STACK_HEIGHT |
| 9 | +from ethereum_test_types.eof.v1 import Container, Section |
| 10 | +from ethereum_test_types.eof.v1.constants import ( |
| 11 | + MAX_CODE_INPUTS, |
| 12 | + MAX_STACK_INCREASE_LIMIT, |
| 13 | + NON_RETURNING_SECTION, |
| 14 | +) |
| 15 | + |
| 16 | +from .. import EOF_FORK_NAME |
| 17 | + |
| 18 | +REFERENCE_SPEC_GIT_PATH = "EIPS/eip-5450.md" |
| 19 | +REFERENCE_SPEC_VERSION = "f20b164b00ae5553f7536a6d7a83a0f254455e09" |
| 20 | + |
| 21 | +pytestmark = pytest.mark.valid_from(EOF_FORK_NAME) |
| 22 | + |
| 23 | + |
| 24 | +@pytest.mark.parametrize("code_inputs", [0, 1, 16, 127, 128]) |
| 25 | +@pytest.mark.parametrize("call_op", [Op.CALLF, Op.JUMPF]) |
| 26 | +def test_execution_at_max_stack_height( |
| 27 | + eof_state_test: EOFStateTestFiller, code_inputs: int, call_op: Op |
| 28 | +): |
| 29 | + """ |
| 30 | + Test execution at the maximum runtime operand stack height (1024). |
| 31 | + EOF doesn't allow to increase the stack height of a single code section more than 1023. |
| 32 | + The effect of the maximum runtime stack height is achieved by using non-zero number |
| 33 | + of the code section inputs and increasing the runtime stack to the limit accordingly. |
| 34 | + The test pushes consecutive numbers starting from 0 (including inputs). |
| 35 | + At the maximum stack height SSTORE is used so it should store 1022 at key 1023. |
| 36 | + """ |
| 37 | + max_stack_increase = MAX_RUNTIME_STACK_HEIGHT - code_inputs |
| 38 | + container = Container( |
| 39 | + sections=[ |
| 40 | + Section.Code( |
| 41 | + ( |
| 42 | + sum(Op.PUSH1(x) for x in range(code_inputs)) |
| 43 | + + call_op[1] |
| 44 | + + (Op.STOP if call_op == Op.CALLF else b"") |
| 45 | + ), |
| 46 | + ), |
| 47 | + Section.Code( |
| 48 | + sum(Op.PUSH2(x) for x in range(code_inputs, MAX_RUNTIME_STACK_HEIGHT)) |
| 49 | + + Op.SSTORE |
| 50 | + + Op.POP * (MAX_RUNTIME_STACK_HEIGHT - Op.SSTORE.popped_stack_items) |
| 51 | + + (Op.RETF if call_op == Op.CALLF else Op.STOP), |
| 52 | + code_inputs=code_inputs, |
| 53 | + code_outputs=0 if call_op == Op.CALLF else NON_RETURNING_SECTION, |
| 54 | + max_stack_increase=max_stack_increase, |
| 55 | + ), |
| 56 | + ], |
| 57 | + ) |
| 58 | + |
| 59 | + exception = None |
| 60 | + if max_stack_increase > MAX_STACK_INCREASE_LIMIT: |
| 61 | + exception = EOFException.MAX_STACK_INCREASE_ABOVE_LIMIT |
| 62 | + elif code_inputs > MAX_CODE_INPUTS: |
| 63 | + exception = EOFException.INPUTS_OUTPUTS_NUM_ABOVE_LIMIT |
| 64 | + |
| 65 | + eof_state_test( |
| 66 | + container=container, |
| 67 | + expect_exception=exception, |
| 68 | + container_post=Account( |
| 69 | + storage={MAX_RUNTIME_STACK_HEIGHT - 1: MAX_RUNTIME_STACK_HEIGHT - 2} |
| 70 | + ), |
| 71 | + ) |
0 commit comments