Skip to content

Commit

Permalink
new(tests): EOF: eofExample validInvalid tests (ethereum#535)
Browse files Browse the repository at this point in the history
* fix formatting, add eofparse string to whitelist

* translate Ori's validInvalid eof tests

* fix eof opcodes implementation rjumpv and jumpf

* add a few more tweaks to eof Container class

* add new EOF exceptions

* add a few efExample ori tests

* fix unit tests and tox

* changelog, address pr review issues

* Apply suggestions from code review

---------

Co-authored-by: Mario Vega <[email protected]>
  • Loading branch information
winsvega and marioevz authored May 8, 2024
1 parent 54ac85a commit 0dee752
Show file tree
Hide file tree
Showing 10 changed files with 599 additions and 23 deletions.
1 change: 1 addition & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Test fixtures for use by clients are available for each release on the [Github r
### 🧪 Test Cases

- ✨ Add `test_double_kill` and `test_recreate` which test resurrection of accounts killed with `SELFDESTRUCT` ([#488](https://github.com/ethereum/execution-spec-tests/pull/488)).
- ✨ Add eof example valid invalid tests from ori, fetch EOF Container implementation ([#535](https://github.com/ethereum/execution-spec-tests/pull/535)).

### 🛠️ Framework

Expand Down
46 changes: 42 additions & 4 deletions src/ethereum_test_tools/eof/v1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,22 @@ class Section(CopyValidateModel):
Whether to automatically compute the best suggestion for the code_inputs,
code_outputs values for this code section.
"""
skip_header_listing: bool = False
"""
Skip section from listing in the header
"""
skip_body_listing: bool = False
"""
Skip section from listing in the body
"""
skip_types_body_listing: bool = False
"""
Skip section from listing in the types body (input, output, stack) bytes
"""
skip_types_header_listing: bool = False
"""
Skip section from listing in the types header (not calculating input, output, stack size)
"""

@cached_property
def header(self) -> bytes:
Expand Down Expand Up @@ -197,8 +213,18 @@ def list_header(sections: List["Section"]) -> bytes:
return b"".join(s.header for s in sections)

h = sections[0].kind.to_bytes(HEADER_SECTION_KIND_BYTE_LENGTH, "big")
h += len(sections).to_bytes(HEADER_SECTION_COUNT_BYTE_LENGTH, "big")

# Count only those sections that are not marked to be skipped for header calculation
header_registered_sections = 0
for cs in sections:
if not cs.skip_header_listing:
header_registered_sections += 1

h += header_registered_sections.to_bytes(HEADER_SECTION_COUNT_BYTE_LENGTH, "big")
for cs in sections:
# If section is marked to skip the header calculation, don't make header for it
if cs.skip_header_listing:
continue
size = cs.custom_size if "custom_size" in cs.model_fields_set else len(cs.data)
h += size.to_bytes(HEADER_SECTION_SIZE_BYTE_LENGTH, "big")

Expand Down Expand Up @@ -333,8 +359,20 @@ def bytecode(self) -> bytes:

# Add type section if needed
if self.auto_type_section.any() and count_sections(sections, SectionKind.TYPE) == 0:
type_section_data = b"".join(s.type_definition for s in sections)
sections = [Section(kind=SectionKind.TYPE, data=type_section_data)] + sections
# Calculate skipping flags
types_header_size = 0
type_section_data = b""
for s in sections:
types_header_size += (
len(s.type_definition) if not s.skip_types_header_listing else 0
)
type_section_data += s.type_definition if not s.skip_types_body_listing else b""

sections = [
Section(
kind=SectionKind.TYPE, data=type_section_data, custom_size=types_header_size
)
] + sections

# Add data section if needed
if self.auto_data_section and count_sections(sections, SectionKind.DATA) == 0:
Expand Down Expand Up @@ -371,7 +409,7 @@ def bytecode(self) -> bytes:
for s in body_sections:
if s.kind == SectionKind.TYPE and self.auto_type_section == AutoSection.ONLY_HEADER:
continue
if s.data:
if s.data and not s.skip_body_listing:
c += s.data

# Add extra (garbage)
Expand Down
7 changes: 6 additions & 1 deletion src/ethereum_test_tools/exceptions/evmone_exceptions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Evmone eof exceptions ENUM -> str mapper
"""

from dataclasses import dataclass

from bidict import frozenbidict
Expand Down Expand Up @@ -39,11 +40,15 @@ class EvmoneExceptionMapper:
ExceptionMessage(
EOFException.INVALID_SECTION_BODIES_SIZE, "err: invalid_section_bodies_size"
),
ExceptionMessage(EOFException.INVALID_TYPE_SIZE, "err: invalid_type_section_size"),
ExceptionMessage(EOFException.INVALID_TYPE_SECTION_SIZE, "err: invalid_type_section_size"),
ExceptionMessage(EOFException.INCOMPLETE_SECTION_SIZE, "err: incomplete_section_size"),
ExceptionMessage(EOFException.INCOMPLETE_SECTION_NUMBER, "err: incomplete_section_number"),
ExceptionMessage(EOFException.TOO_MANY_CODE_SECTIONS, "err: too_many_code_sections"),
ExceptionMessage(EOFException.ZERO_SECTION_SIZE, "err: zero_section_size"),
ExceptionMessage(EOFException.UNDEFINED_INSTRUCTION, "err: undefined_instruction"),
ExceptionMessage(EOFException.UNREACHABLE_INSTRUCTIONS, "err: unreachable_instructions"),
ExceptionMessage(EOFException.INVALID_RJUMP_DESTINATION, "err: invalid_rjump_destination"),
ExceptionMessage(EOFException.UNREACHABLE_CODE_SECTIONS, "err: unreachable_code_sections"),
)

def __init__(self) -> None:
Expand Down
19 changes: 18 additions & 1 deletion src/ethereum_test_tools/exceptions/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,11 @@ class EOFException(ExceptionBase):
Indicates that exception string is not mapped to an exception enum
"""

UNDEFINED_INSTRUCTION = auto()
"""
EOF container has undefined instruction in it's body code
"""

UNKNOWN_VERSION = auto()
"""
EOF container has an unknown version
Expand All @@ -214,11 +219,15 @@ class EOFException(ExceptionBase):
"""
EOF container version bytes mismatch
"""
INVALID_RJUMP_DESTINATION = auto()
"""
Code has RJUMP instruction with invalid parameters
"""
MISSING_TYPE_HEADER = auto()
"""
EOF container missing types section
"""
INVALID_TYPE_SIZE = auto()
INVALID_TYPE_SECTION_SIZE = auto()
"""
EOF container types section has wrong size
"""
Expand Down Expand Up @@ -286,6 +295,14 @@ class EOFException(ExceptionBase):
"""
EOF container's code missing STOP bytecode at it's end
"""
UNREACHABLE_INSTRUCTIONS = auto()
"""
EOF container's code have instructions that are unreachable
"""
UNREACHABLE_CODE_SECTIONS = auto()
"""
EOF container's body have code sections that are unreachable
"""


"""
Expand Down
8 changes: 4 additions & 4 deletions src/ethereum_test_tools/spec/eof/eof_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ class UnexpectedEOFException(EOFBaseException):
def __init__(self, *, code: Bytes, got: str):
message = (
"Expected EOF code to be valid, but an exception occurred:\n"
f" Code: {self.format_code(code)}\n"
"Expected: No Exception\n"
f" Got: {got}"
f" Code: {self.format_code(code)}\n"
f"Expected: No Exception\n"
f" Got: {got}"
)
super().__init__(message)

Expand All @@ -64,7 +64,7 @@ def __init__(self, *, code: Bytes, expected: str):
"Expected EOF code to be invalid, but no exception was raised:\n"
f" Code: {self.format_code(code)}\n"
f"Expected: {expected}\n"
" Got: No Exception"
f" Got: No Exception"
)
super().__init__(message)

Expand Down
11 changes: 7 additions & 4 deletions src/ethereum_test_tools/tests/test_vm.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@
[
Op.ORIGIN.int(),
Op.RJUMPV.int(),
0x03, # Data portion, defined by the [1, 2, 3] argument
0x02, # Data portion, defined by the [1, 2, 3] argument
0x00,
0x01,
0x00,
Expand All @@ -203,7 +203,7 @@
bytes(
[
Op.RJUMPV.int(),
0x03,
0x02,
0xFF,
0xFF,
0xFF,
Expand All @@ -218,7 +218,7 @@
bytes(
[
Op.RJUMPV.int(),
0x05,
0x04,
0x00,
0x00,
0x00,
Expand All @@ -238,7 +238,7 @@
[
Op.ORIGIN.int(),
Op.RJUMPV.int(),
0x03, # Data portion, defined by the [1, 2, 3] argument
0x02, # Data portion, defined by the [1, 2, 3] argument
0x00,
0x01,
0x00,
Expand All @@ -258,6 +258,9 @@
]
),
),
(Op.RJUMPV[0, 3, 6, 9], bytes.fromhex("e2030000000300060009")),
(Op.RJUMPV[2, 0], bytes.fromhex("e20100020000")),
(Op.RJUMPV[b"\x02\x00\x02\xFF\xFF"], bytes.fromhex("e2020002ffff")),
],
)
def test_opcodes(opcodes: bytes, expected: bytes):
Expand Down
6 changes: 3 additions & 3 deletions src/ethereum_test_tools/vm/opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,14 +382,14 @@ def _rjumpv_encoder(*args: int | bytes | Iterable[int]) -> bytes:
elif isinstance(args[0], Iterable):
int_args = list(args[0])
return b"".join(
[len(int_args).to_bytes(RJUMPV_MAX_INDEX_BYTE_LENGTH, "big")]
[(len(int_args) - 1).to_bytes(RJUMPV_MAX_INDEX_BYTE_LENGTH, "big")]
+ [
i.to_bytes(RJUMPV_BRANCH_OFFSET_BYTE_LENGTH, "big", signed=True)
for i in int_args
]
)
return b"".join(
[len(args).to_bytes(RJUMPV_MAX_INDEX_BYTE_LENGTH, "big")]
[(len(args) - 1).to_bytes(RJUMPV_MAX_INDEX_BYTE_LENGTH, "big")]
+ [
i.to_bytes(RJUMPV_BRANCH_OFFSET_BYTE_LENGTH, "big", signed=True)
for i in args
Expand Down Expand Up @@ -4932,7 +4932,7 @@ class Opcodes(Opcode, Enum):
3
"""

JUMPF = Opcode(0xB1, data_portion_length=2)
JUMPF = Opcode(0xE5, data_portion_length=2)
"""
!!! Note: This opcode is under development
Expand Down
12 changes: 6 additions & 6 deletions tests/prague/eip3540_eof_v1/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@
Section.Code(Op.STOP),
],
auto_type_section=AutoSection.NONE,
# TODO the exception must be about type section EOFException.INVALID_TYPE_SIZE,
# TODO the exception must be about type section EOFException.INVALID_TYPE_SECTION_SIZE,
validity_error=EOFException.ZERO_SECTION_SIZE,
),
Container(
Expand All @@ -435,7 +435,7 @@
Section.Code(Op.STOP),
],
auto_type_section=AutoSection.NONE,
validity_error=EOFException.INVALID_TYPE_SIZE,
validity_error=EOFException.INVALID_TYPE_SECTION_SIZE,
),
Container(
name="type_section_too_small_2",
Expand All @@ -444,7 +444,7 @@
Section.Code(Op.STOP),
],
auto_type_section=AutoSection.NONE,
validity_error=EOFException.INVALID_TYPE_SIZE,
validity_error=EOFException.INVALID_TYPE_SECTION_SIZE,
),
Container(
name="type_section_too_big",
Expand All @@ -453,7 +453,7 @@
Section.Code(Op.STOP),
],
auto_type_section=AutoSection.NONE,
validity_error=EOFException.INVALID_TYPE_SIZE,
validity_error=EOFException.INVALID_TYPE_SECTION_SIZE,
),
]

Expand Down Expand Up @@ -593,7 +593,7 @@
Section.Data(data="0x00", force_type_listing=True),
Section.Code(Op.STOP),
],
validity_error=EOFException.INVALID_TYPE_SIZE,
validity_error=EOFException.INVALID_TYPE_SECTION_SIZE,
),
Container(
name="code_sections_above_1024",
Expand All @@ -607,7 +607,7 @@
Section.Code(Op.STOP),
],
auto_type_section=AutoSection.NONE,
validity_error=EOFException.INVALID_TYPE_SIZE,
validity_error=EOFException.INVALID_TYPE_SECTION_SIZE,
),
Container(
name="single_code_section_incomplete_type_2",
Expand Down
Loading

0 comments on commit 0dee752

Please sign in to comment.