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

feat(fw,forks,tests): Add EVM code type marker #610

Merged
merged 26 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ce46627
refactor(fw): Split EOF types tests
marioevz Jul 29, 2024
391b2cc
feat(ethereum_test_vm): Add `EVMCodeType`
marioevz Jul 29, 2024
8e29f69
feat(types): Add `evm_code_type` as optional parameter to `deploy_con…
marioevz Jul 31, 2024
6ad7221
feat(ethereum_test_forks): Add `evm_code_types` to forks
marioevz Jul 29, 2024
0e66b34
feat(plugins/forks): Add `with_all_evm_code_types` marker
marioevz Jul 29, 2024
67fd151
feat(plugins/filler): Add `evm_code_type` fixture
marioevz Jul 29, 2024
2cee31a
feat(plugins/forks): Add `with_all_call_opcodes` marker
marioevz Jul 30, 2024
df307a0
feat(fw): add `call_return_code`
marioevz Jul 30, 2024
54ad663
fix(plugins): Add pre-alloc plugin to help
marioevz Jul 31, 2024
c0b2344
fix(tests): Incorrect import
marioevz Jul 30, 2024
8e4e4e3
feat(tests): parametrize dup test with all evm code types
marioevz Jul 29, 2024
e003515
feat(tests): EIP-4844: Precompile tests on all evm code types
marioevz Jul 31, 2024
35a1910
fix(fw): fix call_return_code
marioevz Jul 31, 2024
a0a1ee4
fix(plugins/forks): covariant descriptor description
marioevz Jul 31, 2024
53c7649
fix(tests): fix EIP-4844 test expected outcome
marioevz Jul 31, 2024
4ce8cd6
fix(fw): test
marioevz Aug 1, 2024
c0602d0
fix(tools): Export `EVMCodeType`
marioevz Aug 1, 2024
e3e76ea
feat(tools): make `Switch` EOF compatible
marioevz Aug 1, 2024
9ef68a4
feat(tools): make `Conditional` EOF compatible
marioevz Aug 1, 2024
4b68894
docs: changelog
marioevz Aug 1, 2024
d614fb0
changelog
marioevz Aug 1, 2024
765324a
feat(vm): Add `terminating` property to `Bytecode`/`Opcode`
marioevz Aug 4, 2024
515e63a
feat(plugins/filler): Add `Op.STOP` on non-terminating bytecodes
marioevz Aug 4, 2024
aebb48d
feat(tools): Use `terminating` property in `Switch` generator
marioevz Aug 4, 2024
0961c2c
fix(tests): Remove `Op.STOP` from test_dup.py
marioevz Aug 4, 2024
c2bb6d9
Merge branch 'main' into evm-code-type-marker
marioevz Aug 8, 2024
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
6 changes: 6 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,19 @@ Test fixtures for use by clients are available for each release on the [Github r

### 🧪 Test Cases

- ✨ EIP-4844 test `tests/cancun/eip4844_blobs/test_point_evaluation_precompile.py` includes an EOF test case ([#610](https://github.com/ethereum/execution-spec-tests/pull/610)).
- ✨ Example test `tests/frontier/opcodes/test_dup.py` now includes EOF parametrization ([#610](https://github.com/ethereum/execution-spec-tests/pull/610)).

### 🛠️ Framework

- 🐞 Fixed consume hive commands from spawning different hive test suites during the same test execution when using xdist ([#712](https://github.com/ethereum/execution-spec-tests/pull/712)).
- ✨ `consume hive` command is now available to run all types of hive tests ([#712](https://github.com/ethereum/execution-spec-tests/pull/712)).
- ✨ Generated fixtures now contain the test index `index.json` by default ([#716](https://github.com/ethereum/execution-spec-tests/pull/716)).
- ✨ A metadata folder `.meta/` now stores all fixture metadata files by default ([#721](https://github.com/ethereum/execution-spec-tests/pull/721)).
- 🐞 Fixed `fill` command index generation issue due to concurrency ([#725](https://github.com/ethereum/execution-spec-tests/pull/725)).
- ✨ Added `with_all_evm_code_types` and `with_all_call_opcodes` markers, which allow automatic parametrization of tests to EOF ([#610](https://github.com/ethereum/execution-spec-tests/pull/610)).
- ✨ Code generators `Conditional` and `Switch` now support EOF by adding parameter `evm_code_type` ([#610](https://github.com/ethereum/execution-spec-tests/pull/610)).
- ✨ `fill` command now supports parameter `--evm-code-type` that can be (currently) set to `legacy` or `eof_v1` to force all test smart contracts to deployed in normal or in EOF containers ([#610](https://github.com/ethereum/execution-spec-tests/pull/610)).
- 🐞 Fixed fixture index generation on EOF tests ([#728](https://github.com/ethereum/execution-spec-tests/pull/728)).

### 🔧 EVM Tools
Expand Down
59 changes: 57 additions & 2 deletions docs/writing_tests/test_markers.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import pytest

@pytest.mark.with_all_tx_types
@pytest.mark.valid_from("Berlin")
def test_something_with_all_tx_types(tx_type):
def test_something_with_all_tx_types(tx_type: int):
pass
```

Expand All @@ -74,12 +74,67 @@ import pytest

@pytest.mark.with_all_precompiles
@pytest.mark.valid_from("Shanghai")
def test_something_with_all_precompiles(precompile):
def test_something_with_all_precompiles(precompile: int):
pass
```

In this example, the test will be parameterized for parameter `precompile` with values `[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]` for fork Shanghai, but with values `[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]` for fork Cancun (because of EIP-4844).

### pytest.mark.with_all_evm_code_types

This marker is used to automatically parameterize a test with all EVM code types that are valid for the fork being tested.

```python
import pytest

@pytest.mark.with_all_evm_code_types
@pytest.mark.valid_from("Frontier")
def test_something_with_all_evm_code_types(pre: Alloc):
pass
```

In this example, the test will be parameterized for parameter `evm_code_type` only with value `[EVMCodeType.LEGACY]` starting on fork Frontier, and eventually it will be parametrized with with values `[EVMCodeType.LEGACY, EVMCodeType.EOF_V1]` on the EOF activation fork.

In all calls to `pre.deploy_contract`, if the code parameter is `Bytecode` type, and `evm_code_type==EVMCodeType.EOF_V1`, the bytecode will be automatically wrapped in an EOF V1 container.

Code wrapping might fail in the following circumstances:

- The code contains invalid EOF V1 opcodes.
- The code does not end with a valid EOF V1 terminating opcode (such as `Op.STOP` or `Op.REVERT` or `Op.RETURN`).
marioevz marked this conversation as resolved.
Show resolved Hide resolved

In the case where the code wrapping fails, `evm_code_type` can be added as a parameter to the test and the bytecode can be dynamically modified to be compatible with the EOF V1 container.

```python
import pytest

@pytest.mark.with_all_evm_code_types
@pytest.mark.valid_from("Frontier")
def test_something_with_all_evm_code_types(pre: Alloc, evm_code_type: EVMCodeType):
code = Op.SSTORE(1, 1)
if evm_code_type == EVMCodeType.EOF_V1:
# Modify the bytecode to be compatible with EOF V1 container
code += Op.STOP
pre.deploy_contract(code)
...
```

### pytest.mark.with_all_call_opcodes

This marker is used to automatically parameterize a test with all EVM call opcodes that are valid for the fork being tested.

```python
import pytest

@pytest.mark.with_all_call_opcodes
@pytest.mark.valid_from("Frontier")
def test_something_with_all_call_opcodes(pre: Alloc, call_opcode: Op):
...
```

In this example, the test will be parametrized for parameter `call_opcode` with values `[Op.CALL, Op.CALLCODE]` starting on fork Frontier, `[Op.CALL, Op.CALLCODE, Op.DELEGATECALL]` on fork Homestead, `[Op.CALL, Op.CALLCODE, Op.DELEGATECALL, Op.STATICCALL]` on fork Byzantium, and eventually it will be parametrized with with values `[Op.CALL, Op.CALLCODE, Op.DELEGATECALL, Op.STATICCALL, Op.EXTCALL, Op.EXTSTATICCALL, Op.EXTDELEGATECALL]` on the EOF activation fork.
marioevz marked this conversation as resolved.
Show resolved Hide resolved

Parameter `evm_code_type` will also be parametrized with the correct EVM code type for the opcode under test.

## Other Markers

### pytest.mark.slow
Expand Down
4 changes: 3 additions & 1 deletion src/cli/tests/test_pytest_fill_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ def test_fill_help(runner):
"""
result = runner.invoke(fill, ["--help"])
assert result.exit_code == pytest.ExitCode.OK
assert "[--evm-bin EVM_BIN] [--traces]" in result.output
assert "[--evm-bin EVM_BIN]" in result.output
assert "[--traces]" in result.output
assert "[--evm-code-type EVM_CODE_TYPE]" in result.output
assert "--help" in result.output
assert "Arguments defining evm executable behavior:" in result.output

Expand Down
22 changes: 21 additions & 1 deletion src/ethereum_test_forks/base_fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
"""

from abc import ABC, ABCMeta, abstractmethod
from typing import Any, ClassVar, List, Mapping, Optional, Protocol, Type
from typing import Any, ClassVar, List, Mapping, Optional, Protocol, Tuple, Type

from semver import Version

from ethereum_test_vm import EVMCodeType, Opcodes

from .base_decorators import prefer_transition_to_method


Expand Down Expand Up @@ -260,6 +262,24 @@ def engine_forkchoice_updated_version(
"""
pass

@classmethod
@abstractmethod
def evm_code_types(cls, block_number: int = 0, timestamp: int = 0) -> List[EVMCodeType]:
"""
Returns the list of EVM code types supported by the fork.
"""
pass

@classmethod
@abstractmethod
def call_opcodes(
cls, block_number: int = 0, timestamp: int = 0
) -> List[Tuple[Opcodes, EVMCodeType]]:
"""
Returns the list of tuples with the call opcodes and its corresponding EVM code type.
"""
pass

# Meta information about the fork
@classmethod
def name(cls) -> str:
Expand Down
72 changes: 71 additions & 1 deletion src/ethereum_test_forks/forks/forks.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
from hashlib import sha256
from os.path import realpath
from pathlib import Path
from typing import List, Mapping, Optional
from typing import List, Mapping, Optional, Tuple

from semver import Version

from ethereum_test_vm import EVMCodeType, Opcodes

from ..base_fork import BaseFork

CURRENT_FILE = Path(realpath(__file__))
Expand Down Expand Up @@ -170,6 +172,25 @@ def precompiles(cls, block_number: int = 0, timestamp: int = 0) -> List[int]:
"""
return []

@classmethod
def evm_code_types(cls, block_number: int = 0, timestamp: int = 0) -> List[EVMCodeType]:
"""
At Genesis, only legacy EVM code is supported.
"""
return [EVMCodeType.LEGACY]

@classmethod
def call_opcodes(
cls, block_number: int = 0, timestamp: int = 0
) -> List[Tuple[Opcodes, EVMCodeType]]:
"""
Returns the list of call opcodes supported by the fork.
"""
return [
(Opcodes.CALL, EVMCodeType.LEGACY),
(Opcodes.CALLCODE, EVMCodeType.LEGACY),
]

@classmethod
def pre_allocation(cls) -> Mapping:
"""
Expand Down Expand Up @@ -201,6 +222,17 @@ def precompiles(cls, block_number: int = 0, timestamp: int = 0) -> List[int]:
"""
return [1, 2, 3, 4] + super(Homestead, cls).precompiles(block_number, timestamp)

@classmethod
def call_opcodes(
cls, block_number: int = 0, timestamp: int = 0
) -> List[Tuple[Opcodes, EVMCodeType]]:
"""
At Homestead, DELEGATECALL opcode was introduced.
"""
return [(Opcodes.DELEGATECALL, EVMCodeType.LEGACY),] + super(
Homestead, cls
).call_opcodes(block_number, timestamp)


class Byzantium(Homestead):
"""
Expand All @@ -224,6 +256,17 @@ def precompiles(cls, block_number: int = 0, timestamp: int = 0) -> List[int]:
"""
return [5, 6, 7, 8] + super(Byzantium, cls).precompiles(block_number, timestamp)

@classmethod
def call_opcodes(
cls, block_number: int = 0, timestamp: int = 0
) -> List[Tuple[Opcodes, EVMCodeType]]:
"""
At Byzantium, STATICCALL opcode was introduced.
"""
return [(Opcodes.STATICCALL, EVMCodeType.LEGACY),] + super(
Byzantium, cls
).call_opcodes(block_number, timestamp)


class Constantinople(Byzantium):
"""
Expand Down Expand Up @@ -625,6 +668,33 @@ class CancunEIP7692( # noqa: SC200
Cancun + EIP-7692 (EOF) fork
"""

@classmethod
def evm_code_types(cls, block_number: int = 0, timestamp: int = 0) -> List[EVMCodeType]:
"""
EOF V1 is supported starting from this fork.
"""
return super(CancunEIP7692, cls,).evm_code_types( # noqa: SC200
block_number,
timestamp,
) + [EVMCodeType.EOF_V1]

@classmethod
def call_opcodes(
cls, block_number: int = 0, timestamp: int = 0
) -> List[Tuple[Opcodes, EVMCodeType]]:
"""
EOF V1 introduces EXTCALL, EXTSTATICCALL, EXTDELEGATECALL.
"""
return [
(Opcodes.EXTCALL, EVMCodeType.EOF_V1),
(Opcodes.EXTSTATICCALL, EVMCodeType.EOF_V1),
(Opcodes.EXTDELEGATECALL, EVMCodeType.EOF_V1),
] + super(
CancunEIP7692, cls # noqa: SC200
).call_opcodes(
block_number, timestamp
)

@classmethod
def is_deployed(cls) -> bool:
"""
Expand Down
4 changes: 4 additions & 0 deletions src/ethereum_test_tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,14 @@
)
from ethereum_test_vm import (
Bytecode,
EVMCodeType,
Macro,
Macros,
Opcode,
OpcodeCallArg,
Opcodes,
UndefinedOpcodes,
call_return_code,
)

from .code import (
Expand Down Expand Up @@ -104,6 +106,7 @@
"EOFStateTestFiller",
"EOFTest",
"EOFTestFiller",
"EVMCodeType",
"FixtureCollector",
"Hash",
"Header",
Expand Down Expand Up @@ -135,6 +138,7 @@
"Yul",
"YulCompiler",
"add_kzg_version",
"call_return_code",
"ceiling_division",
"compute_create_address",
"compute_create2_address",
Expand Down
Loading