Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new(test): add tests for EOF/EIP-663 DUPN SWAPN #502

Merged
merged 8 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Test fixtures for use by clients are available for each release on the [Github r

- ✨ 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)).
- ✨ [EIP-663](https://eips.ethereum.org/EIPS/eip-663): Add `test_dupn.py` and `test_swapn.py` ([#502](https://github.com/ethereum/execution-spec-tests/pull/502)).

### πŸ› οΈ Framework

Expand Down
5 changes: 5 additions & 0 deletions src/ethereum_test_tools/exceptions/evmone_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ class EvmoneExceptionMapper:
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"),
ExceptionMessage(EOFException.STACK_UNDERFLOW, "err: stack_underflow"),
ExceptionMessage(
EOFException.MAX_STACK_HEIGHT_ABOVE_LIMIT, "err: max_stack_height_above_limit"
),
ExceptionMessage(EOFException.INVALID_MAX_STACK_HEIGHT, "err: invalid_max_stack_height"),
ExceptionMessage(EOFException.INVALID_DATALOADN_INDEX, "err: invalid_dataloadn_index"),
)

Expand Down
12 changes: 12 additions & 0 deletions src/ethereum_test_tools/exceptions/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,18 @@ class EOFException(ExceptionBase):
"""
EOF container's body have code sections that are unreachable
"""
STACK_UNDERFLOW = auto()
"""
EOF container's code produces an stack underflow
"""
MAX_STACK_HEIGHT_ABOVE_LIMIT = auto()
"""
EOF container's specified max stack height is above the limit
"""
INVALID_MAX_STACK_HEIGHT = auto()
"""
EOF container section's specified max stack height does not match the actual stack height
"""
INVALID_DATALOADN_INDEX = auto()
"""
A DATALOADN instruction has out-of-bounds index for the data section
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"""
abstract: Tests [EIP-663: SWAPN, DUPN and EXCHANGE instructions](https://eips.ethereum.org/EIPS/eip-663)
Tests for [EIP-663: SWAPN, DUPN and EXCHANGE instructions](https://eips.ethereum.org/EIPS/eip-663).
""" # noqa: E501

REFERENCE_SPEC_GIT_PATH = "EIPS/eip-663.md"
REFERENCE_SPEC_VERSION = "b658bb87fe039d29e9475d5cfaebca9b92e0fca2"
14 changes: 14 additions & 0 deletions tests/prague/eip7692_eof_v1/eip663_dupn_swapn_exchange/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""
Pytest fixtures for EIP-663 tests
"""
import pytest

from ethereum_test_tools import Transaction


@pytest.fixture
def tx() -> Transaction:
"""
Produces the default Transaction.
"""
return Transaction(to=0xC0DE, gas_limit=10_000_000)
139 changes: 139 additions & 0 deletions tests/prague/eip7692_eof_v1/eip663_dupn_swapn_exchange/test_dupn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
"""
abstract: Tests [EIP-663: SWAPN, DUPN and EXCHANGE instructions](https://eips.ethereum.org/EIPS/eip-663)
Tests for the DUPN instruction.
""" # noqa: E501

import pytest

from ethereum_test_tools import (
Account,
Environment,
EOFException,
EOFTestFiller,
StateTestFiller,
TestAddress,
Transaction,
)
from ethereum_test_tools.eof.v1 import Container, Section
from ethereum_test_tools.eof.v1.constants import MAX_OPERAND_STACK_HEIGHT, NON_RETURNING_SECTION
from ethereum_test_tools.vm.opcode import Opcodes as Op

from ..eip3540_eof_v1.spec import EOF_FORK_NAME
from . import REFERENCE_SPEC_GIT_PATH, REFERENCE_SPEC_VERSION

REFERENCE_SPEC_GIT_PATH = REFERENCE_SPEC_GIT_PATH
REFERENCE_SPEC_VERSION = REFERENCE_SPEC_VERSION


@pytest.mark.valid_from(EOF_FORK_NAME)
def test_dupn_all_valid_immediates(
tx: Transaction,
state_test: StateTestFiller,
):
"""
Test case for all valid DUPN immediates.
"""
n = 2**8
values = range(0xD00, 0xD00 + n)

eof_code = Container(
sections=[
Section.Code(
code=b"".join(Op.PUSH2(v) for v in values)
+ b"".join(Op.SSTORE(x, Op.DUPN[x]) for x in range(0, n))
+ Op.STOP,
code_inputs=0,
code_outputs=NON_RETURNING_SECTION,
max_stack_height=n + 2,
)
],
)

pre = {
TestAddress: Account(balance=1_000_000_000),
tx.to: Account(code=eof_code),
}

post = {tx.to: Account(storage=dict(zip(range(0, n), reversed(values))))}

state_test(
env=Environment(),
pre=pre,
post=post,
tx=tx,
)


@pytest.mark.parametrize(
"stack_height,max_stack_height",
[
[0, 0],
[0, 1],
[1, 1],
[1, 2],
[2**8 - 1, 2**8 - 1],
[2**8 - 1, 2**8],
],
)
@pytest.mark.valid_from(EOF_FORK_NAME)
def test_dupn_stack_underflow(
stack_height: int,
max_stack_height: int,
eof_test: EOFTestFiller,
):
"""
Test case out of bounds DUPN immediate.
"""
eof_code = Container(
sections=[
Section.Code(
code=b"".join(Op.PUSH2(v) for v in range(0, stack_height))
+ Op.DUPN[stack_height]
+ Op.STOP,
code_inputs=0,
code_outputs=NON_RETURNING_SECTION,
max_stack_height=max_stack_height,
)
],
)
eof_test(
data=eof_code,
expect_exception=EOFException.STACK_UNDERFLOW,
)


@pytest.mark.parametrize(
"dupn_operand,max_stack_height,expect_exception",
[
[0, MAX_OPERAND_STACK_HEIGHT, EOFException.INVALID_MAX_STACK_HEIGHT],
[0, MAX_OPERAND_STACK_HEIGHT + 1, EOFException.MAX_STACK_HEIGHT_ABOVE_LIMIT],
[2**8 - 1, MAX_OPERAND_STACK_HEIGHT, EOFException.INVALID_MAX_STACK_HEIGHT],
[2**8 - 1, MAX_OPERAND_STACK_HEIGHT + 1, EOFException.MAX_STACK_HEIGHT_ABOVE_LIMIT],
],
)
@pytest.mark.valid_from(EOF_FORK_NAME)
def test_dupn_stack_overflow(
dupn_operand: int,
max_stack_height: int,
expect_exception: EOFException,
eof_test: EOFTestFiller,
):
"""
Test case where DUPN produces an stack overflow.
"""
eof_code = Container(
sections=[
Section.Code(
code=b"".join(Op.PUSH2(v) for v in range(0, MAX_OPERAND_STACK_HEIGHT))
+ Op.DUPN[dupn_operand]
+ Op.STOP,
code_inputs=0,
code_outputs=NON_RETURNING_SECTION,
max_stack_height=max_stack_height,
)
],
)
eof_test(
data=eof_code,
expect_exception=expect_exception,
)
131 changes: 131 additions & 0 deletions tests/prague/eip7692_eof_v1/eip663_dupn_swapn_exchange/test_swapn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
"""
abstract: Tests [EIP-663: SWAPN, DUPN and EXCHANGE instructions](https://eips.ethereum.org/EIPS/eip-663)
Tests for the SWAPN instruction.
""" # noqa: E501

import pytest

from ethereum_test_tools import (
Account,
Environment,
EOFException,
EOFTestFiller,
StateTestFiller,
TestAddress,
Transaction,
)
from ethereum_test_tools.eof.v1 import Container, Section
from ethereum_test_tools.eof.v1.constants import MAX_OPERAND_STACK_HEIGHT, NON_RETURNING_SECTION
from ethereum_test_tools.vm.opcode import Opcodes as Op

from ..eip3540_eof_v1.spec import EOF_FORK_NAME
from . import REFERENCE_SPEC_GIT_PATH, REFERENCE_SPEC_VERSION

REFERENCE_SPEC_GIT_PATH = REFERENCE_SPEC_GIT_PATH
REFERENCE_SPEC_VERSION = REFERENCE_SPEC_VERSION


@pytest.mark.valid_from(EOF_FORK_NAME)
def test_swapn_all_valid_immediates(
tx: Transaction,
state_test: StateTestFiller,
):
"""
Test case for all valid SWAPN immediates.
"""
n = 256
values = range(0x500, 0x500 + 257)

eof_code = Container(
sections=[
Section.Code(
code=b"".join(Op.PUSH2(v) for v in values)
+ b"".join(Op.SSTORE(x, Op.SWAPN[0xFF - x]) for x in range(0, n))
+ Op.STOP,
code_inputs=0,
code_outputs=NON_RETURNING_SECTION,
max_stack_height=n + 2,
)
],
)

pre = {
TestAddress: Account(balance=1_000_000_000),
tx.to: Account(code=eof_code),
}

values_rotated = list(values[1:]) + [values[0]]
post = {tx.to: Account(storage=dict(zip(range(0, n), reversed(values_rotated))))}

state_test(
env=Environment(),
pre=pre,
post=post,
tx=tx,
)


@pytest.mark.parametrize(
"swapn_operand",
[
0,
2**8 - 1,
],
)
@pytest.mark.valid_from(EOF_FORK_NAME)
def test_swapn_on_max_stack(
swapn_operand: int,
eof_test: EOFTestFiller,
):
"""
Test case out of bounds DUPN immediate.
"""
eof_code = Container(
sections=[
Section.Code(
code=b"".join(Op.PUSH2(v) for v in range(0, MAX_OPERAND_STACK_HEIGHT))
+ Op.SWAPN[swapn_operand]
+ Op.STOP,
code_inputs=0,
code_outputs=NON_RETURNING_SECTION,
max_stack_height=MAX_OPERAND_STACK_HEIGHT,
)
],
)
eof_test(
data=eof_code,
)


@pytest.mark.parametrize(
"stack_height",
[
0,
1,
2**8 - 1,
],
)
@pytest.mark.valid_from(EOF_FORK_NAME)
def test_swapn_stack_underflow(
stack_height: int,
eof_test: EOFTestFiller,
):
"""
Test case out of bounds DUPN immediate.
"""
eof_code = Container(
sections=[
Section.Code(
code=b"".join(Op.PUSH2(v) for v in range(0, stack_height))
+ Op.SWAPN[stack_height]
+ Op.STOP,
code_inputs=0,
code_outputs=NON_RETURNING_SECTION,
max_stack_height=MAX_OPERAND_STACK_HEIGHT,
)
],
)
eof_test(
data=eof_code,
expect_exception=EOFException.STACK_UNDERFLOW,
)
12 changes: 12 additions & 0 deletions tests/prague/eip7692_eof_v1/tracker.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,18 @@

## EIP-663: SWAPN, DUPN and EXCHANGE instructions

### Validation

- [ ] A DUPN instruction causes stack overflow
- [ ] A DUPN instruction causes stack underflow
- [ ] A DUPN instruction causes max stack height mismatch
- [ ] A SWAPN instruction causes stack underflow

### Execution

- [x] Positive tests for DUPN instructions (./eip663_dupn_swapn_exchange/test_dupn.py::test_dupn_all_valid_immediates)
- [x] Positive tests for SWAPN instructions (./eip663_dupn_swapn_exchange/test_swapn.py::test_swapn_all_valid_immediates)

## EIP-7069: Revamped CALL instructions

## EIP-7620: EOF Contract Creation
Expand Down
2 changes: 2 additions & 0 deletions whitelist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ dup
dunder
EEST
eip
eip3540
eips
EIPs
endianness
Expand Down Expand Up @@ -163,6 +164,7 @@ hyperledger
iat
ignoreRevsFile
img
immediates
incrementing
init
initcode
Expand Down