Skip to content

Commit 4521b62

Browse files
authored
refactor(tests): Merge EOF stack underflow tests (#1384)
This combines the cases of `test_all_opcodes_stack_underflow()` to `test_all_opcodes_variadic_stack_underflow()`. By the way, this also improves some details, e.g. avoids instruction sequence as `RETURN STOP`.
1 parent 44dc9ab commit 4521b62

File tree

3 files changed

+56
-80
lines changed

3 files changed

+56
-80
lines changed

tests/osaka/eip7692_eof_v1/eip3540_eof_v1/test_all_opcodes_in_container.py

Lines changed: 0 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -311,65 +311,6 @@ def test_all_unreachable_terminating_opcodes_before_stop(
311311
)
312312

313313

314-
@pytest.mark.parametrize(
315-
"opcode",
316-
sorted(op for op in valid_eof_opcodes if op.min_stack_height > 0)
317-
+ [
318-
# Opcodes that have variable min_stack_height
319-
Op.SWAPN[0x00],
320-
Op.SWAPN[0xFF],
321-
Op.DUPN[0x00],
322-
Op.DUPN[0xFF],
323-
Op.EXCHANGE[0x00],
324-
Op.EXCHANGE[0xFF],
325-
],
326-
)
327-
def test_all_opcodes_stack_underflow(
328-
eof_test: EOFTestFiller,
329-
opcode: Opcode,
330-
):
331-
"""Test stack underflow on all opcodes that require at least one item on the stack."""
332-
sections: List[Section]
333-
if opcode == Op.EOFCREATE:
334-
sections = [
335-
Section.Code(code=Op.PUSH0 * (opcode.min_stack_height - 1) + opcode[0] + Op.STOP),
336-
Section.Container(
337-
container=Container(
338-
sections=[
339-
Section.Code(code=Op.RETURNCODE[0](0, 0)),
340-
Section.Container(Container.Code(code=Op.STOP)),
341-
]
342-
)
343-
),
344-
]
345-
elif opcode == Op.RETURNCODE:
346-
sections = [
347-
Section.Code(code=Op.EOFCREATE[0](0, 0, 0, 0) + Op.STOP),
348-
Section.Container(
349-
container=Container(
350-
sections=[
351-
Section.Code(code=Op.PUSH0 * (opcode.min_stack_height - 1) + opcode[0]),
352-
Section.Container(Container.Code(code=Op.STOP)),
353-
]
354-
)
355-
),
356-
]
357-
else:
358-
bytecode = Op.PUSH0 * (opcode.min_stack_height - 1)
359-
if opcode.has_data_portion():
360-
bytecode += opcode[0]
361-
else:
362-
bytecode += opcode
363-
bytecode += Op.STOP
364-
sections = [Section.Code(code=bytecode)]
365-
eof_code = Container(sections=sections)
366-
367-
eof_test(
368-
container=eof_code,
369-
expect_exception=EOFException.STACK_UNDERFLOW,
370-
)
371-
372-
373314
@pytest.mark.parametrize(
374315
"opcode",
375316
sorted(op for op in valid_eof_opcodes if op.pushed_stack_items > op.popped_stack_items)

tests/osaka/eip7692_eof_v1/eip5450_stack/test_code_validation.py

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import itertools
44
from enum import Enum, auto, unique
5-
from typing import Generator, Tuple
5+
from typing import Tuple
66

77
import pytest
88

@@ -15,6 +15,10 @@
1515

1616
from .. import EOF_FORK_NAME
1717
from ..eip3540_eof_v1.test_all_opcodes_in_container import valid_eof_opcodes
18+
from ..eip7620_eof_create.helpers import (
19+
smallest_initcode_subcontainer,
20+
smallest_runtime_subcontainer,
21+
)
1822

1923
REFERENCE_SPEC_GIT_PATH = "EIPS/eip-5450.md"
2024
REFERENCE_SPEC_VERSION = "f20b164b00ae5553f7536a6d7a83a0f254455e09"
@@ -436,37 +440,42 @@ def test_rjumps_jumpf_nonreturning(
436440
eof_test(container=Container(sections=sections), expect_exception=possible_exceptions or None)
437441

438442

439-
def gen_stack_underflow_params() -> Generator[Tuple[Op, int], None, None]:
443+
def gen_stack_underflow_params():
440444
"""Generate parameters for stack underflow tests."""
441-
for op in sorted(valid_eof_opcodes):
442-
if op.min_stack_height == 0:
443-
continue
444-
if op in (Op.EOFCREATE, Op.RETURNCODE):
445-
continue
445+
opcodes = sorted(op for op in valid_eof_opcodes if op.min_stack_height > 0) + [
446+
# Opcodes that have variable min_stack_height
447+
Op.SWAPN[0x00],
448+
Op.SWAPN[0xFF],
449+
Op.DUPN[0x00],
450+
Op.DUPN[0xFF],
451+
Op.EXCHANGE[0x00],
452+
Op.EXCHANGE[0xFF],
453+
]
454+
for op in opcodes:
446455
yield op, 0
447456
if op.min_stack_height > 1:
448457
yield op, op.min_stack_height - 1
449458

450459

451-
@pytest.mark.parametrize("spread", [0, 1, MAX_OPERAND_STACK_HEIGHT])
460+
@pytest.mark.parametrize("spread", [-1, 0, 1, MAX_OPERAND_STACK_HEIGHT])
452461
@pytest.mark.parametrize("op,stack_height", gen_stack_underflow_params())
453-
def test_all_opcodes_variadic_stack_underflow(
462+
def test_all_opcodes_stack_underflow(
454463
eof_test: EOFTestFiller, op: Op, stack_height: int, spread: int
455464
):
456465
"""
457466
Test EOF validation failing due to stack overflow
458467
caused by the specific instruction `op`.
459-
There is similar non-variadic test variant in test_all_opcodes_stack_underflow().
460468
"""
461469
code = Bytecode()
462470

463-
# Check if the op increases the stack height (e.g. DUP instructions).
464-
# We need to leave space for this increase not to cause stack overflow.
465-
stack_height_increase = max(op.pushed_stack_items - op.popped_stack_items, 0)
466-
# Cap the spread if it would exceed the maximum stack height.
467-
spread = min(spread, MAX_OPERAND_STACK_HEIGHT - (stack_height + stack_height_increase))
468-
# Create a range stack height of 0-spread.
469-
code = Op.RJUMPI[spread](Op.CALLVALUE) + Op.PUSH0 * spread
471+
if spread >= 0:
472+
# Check if the op increases the stack height (e.g. DUP instructions).
473+
# We need to leave space for this increase not to cause stack overflow.
474+
stack_height_increase = max(op.pushed_stack_items - op.popped_stack_items, 0)
475+
# Cap the spread if it would exceed the maximum stack height.
476+
spread = min(spread, MAX_OPERAND_STACK_HEIGHT - (stack_height + stack_height_increase))
477+
# Create a range stack height of 0-spread.
478+
code += Op.RJUMPI[spread](Op.CALLVALUE) + Op.PUSH0 * spread
470479

471480
# Create the desired stack height.
472481
code += Op.PUSH0 * stack_height
@@ -479,9 +488,35 @@ def test_all_opcodes_variadic_stack_underflow(
479488
if not op.terminating:
480489
code += Op.STOP
481490

491+
sections = [
492+
Section.Code(
493+
code,
494+
# Set reasonable stack height. Don't rely on automatic calculation,
495+
# because we are in the invalid stack height scenario.
496+
max_stack_height=max(spread, stack_height, int(spread >= 0)),
497+
)
498+
]
499+
500+
if op == Op.EOFCREATE:
501+
# Make EOFCREATE valid by adding the target subcontainer.
502+
sections.append(Section.Container(smallest_initcode_subcontainer))
503+
elif op == Op.RETURNCODE:
504+
# Make RETURNCODE valid by wrapping it with a container with EOFCREATE.
505+
sections = [
506+
Section.Code(Op.EOFCREATE[0](0, 0, 0, 0) + Op.STOP),
507+
Section.Container(
508+
container=Container(
509+
sections=[
510+
sections[0],
511+
Section.Container(smallest_runtime_subcontainer),
512+
]
513+
)
514+
),
515+
]
516+
482517
eof_test(
483518
container=Container(
484-
sections=[Section.Code(code)],
519+
sections=sections,
485520
validity_error=EOFException.STACK_UNDERFLOW,
486521
)
487522
)

tests/osaka/eip7692_eof_v1/eof_tracker.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -237,10 +237,10 @@
237237

238238
#### Stack underflow
239239

240-
- [x] Stack underflows ([`tests/osaka/eip7692_eof_v1/eip5450_stack/test_code_validation.py::test_all_opcodes_variadic_stack_underflow`](./eip5450_stack/test_code_validation/test_all_opcodes_variadic_stack_underflow.md))
240+
- [x] Stack underflows ([`tests/osaka/eip7692_eof_v1/eip5450_stack/test_code_validation.py::test_all_opcodes_stack_underflow`](./eip5450_stack/test_code_validation/test_all_opcodes_stack_underflow.md))
241241
- [x] Stack underflow with enough items available in caller stack - can't dig into caller frame ([`tests/osaka/eip7692_eof_v1/eip4750_functions/test_code_validation.py::test_eof_validity`](./eip4750_functions/test_code_validation/test_eof_validity.md))
242-
- [x] Stack underflow in variable stack segment, only min underflow ([`tests/osaka/eip7692_eof_v1/eip5450_stack/test_code_validation.py::test_all_opcodes_variadic_stack_underflow`](./eip5450_stack/test_code_validation/test_all_opcodes_variadic_stack_underflow.md))
243-
- [x] Stack underflow in variable stack segment, both min and max underflow ([`tests/osaka/eip7692_eof_v1/eip5450_stack/test_code_validation.py::test_all_opcodes_variadic_stack_underflow`](./eip5450_stack/test_code_validation/test_all_opcodes_variadic_stack_underflow.md))
242+
- [x] Stack underflow in variable stack segment, only min underflow ([`tests/osaka/eip7692_eof_v1/eip5450_stack/test_code_validation.py::test_all_opcodes_stack_underflow`](./eip5450_stack/test_code_validation/test_all_opcodes_stack_underflow.md))
243+
- [x] Stack underflow in variable stack segment, both min and max underflow ([`tests/osaka/eip7692_eof_v1/eip5450_stack/test_code_validation.py::test_all_opcodes_stack_underflow`](./eip5450_stack/test_code_validation/test_all_opcodes_stack_underflow.md))
244244

245245
#### CALLF
246246

0 commit comments

Comments
 (0)