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

bug(pytest, forks): fix yul_test marker and improve solc --evm-version param handling #418

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
3 changes: 3 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ Test fixtures for use by clients are available for each release on the [Github r

### 🛠️ Framework

- ✨ Improve handling of the argument passed to `solc --evm-version` when compiling Yul code ([#418](https://github.com/ethereum/execution-spec-tests/pull/418)).
- 🐞 Fix `fill -m yul_test` which failed to filter tests that are (dynamically) marked as a yul test ([#418](https://github.com/ethereum/execution-spec-tests/pull/418)).

### 🔧 EVM Tools

### 📋 Misc
Expand Down
6 changes: 6 additions & 0 deletions src/ethereum_test_forks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@
InvalidForkError,
forks_from,
forks_from_until,
get_closest_fork_with_solc_support,
get_deployed_forks,
get_development_forks,
get_forks,
get_forks_with_solc_support,
get_forks_without_solc_support,
get_transition_forks,
transition_fork_from_to,
transition_fork_to,
Expand Down Expand Up @@ -63,6 +66,9 @@
"get_deployed_forks",
"get_development_forks",
"get_forks",
"get_forks_with_solc_support",
"get_forks_without_solc_support",
"get_closest_fork_with_solc_support",
"transition_fork_from_to",
"transition_fork_to",
]
12 changes: 11 additions & 1 deletion src/ethereum_test_forks/base_fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from abc import ABC, ABCMeta, abstractmethod
from typing import Any, ClassVar, List, Mapping, Optional, Protocol, Type

from semver import Version

from .base_decorators import prefer_transition_to_method


Expand Down Expand Up @@ -252,12 +254,20 @@ def transition_tool_name(cls, block_number: int = 0, timestamp: int = 0) -> str:

@classmethod
@abstractmethod
def solc_name(cls, block_number: int = 0, timestamp: int = 0) -> str:
def solc_name(cls) -> str:
"""
Returns fork name as it's meant to be passed to the solc compiler.
"""
pass

@classmethod
@abstractmethod
def solc_min_version(cls) -> Version:
"""
Returns the minimum version of solc that supports this fork.
"""
pass

@classmethod
def blockchain_test_network_name(cls) -> str:
"""
Expand Down
30 changes: 23 additions & 7 deletions src/ethereum_test_forks/forks/forks.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
"""
from typing import List, Mapping, Optional

from semver import Version

from ..base_fork import BaseFork


# All forks must be listed here !!! in the order they were introduced !!!
class Frontier(BaseFork):
class Frontier(BaseFork, solc_name="homestead"):
"""
Frontier fork
"""
Expand All @@ -22,13 +24,20 @@ def transition_tool_name(cls, block_number: int = 0, timestamp: int = 0) -> str:
return cls.name()

@classmethod
def solc_name(cls, block_number: int = 0, timestamp: int = 0) -> str:
def solc_name(cls) -> str:
"""
Returns fork name as it's meant to be passed to the solc compiler.
"""
if cls._solc_name is not None:
return cls._solc_name
return cls.name()
return cls.name().lower()

@classmethod
def solc_min_version(cls) -> Version:
"""
Returns the minimum version of solc that supports this fork.
"""
return Version.parse("0.8.20")

@classmethod
def header_base_fee_required(cls, block_number: int = 0, timestamp: int = 0) -> bool:
Expand Down Expand Up @@ -200,7 +209,7 @@ def get_reward(cls, block_number: int = 0, timestamp: int = 0) -> int:
return 2_000_000_000_000_000_000


class ConstantinopleFix(Constantinople, solc_name="Constantinople"):
class ConstantinopleFix(Constantinople, solc_name="constantinople"):
"""
Constantinople Fix fork
"""
Expand All @@ -222,7 +231,7 @@ def precompiles(cls, block_number: int = 0, timestamp: int = 0) -> List[int]:


# Glacier forks skipped, unless explicitly specified
class MuirGlacier(Istanbul):
class MuirGlacier(Istanbul, solc_name="istanbul"):
"""
Muir Glacier fork
"""
Expand Down Expand Up @@ -264,15 +273,15 @@ def tx_types(cls, block_number: int = 0, timestamp: int = 0) -> List[int]:


# Glacier forks skipped, unless explicitly specified
class ArrowGlacier(London):
class ArrowGlacier(London, solc_name="london"):
"""
Arrow Glacier fork
"""

pass


class GrayGlacier(ArrowGlacier):
class GrayGlacier(ArrowGlacier, solc_name="london"):
"""
Gray Glacier fork
"""
Expand Down Expand Up @@ -355,6 +364,13 @@ def is_deployed(cls):
"""
return False

@classmethod
def solc_min_version(cls) -> Version:
"""
Returns the minimum version of solc that supports this fork.
"""
return Version.parse("0.8.24")

@classmethod
def header_excess_blob_gas_required(cls, block_number: int = 0, timestamp: int = 0) -> bool:
"""
Expand Down
36 changes: 33 additions & 3 deletions src/ethereum_test_forks/helpers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
"""
Helper methods to resolve forks during test filling
"""
from typing import List
from typing import List, Optional

from semver import Version

from .base_fork import BaseFork, Fork
from .forks import forks, transition
Expand Down Expand Up @@ -33,15 +35,15 @@ def get_forks() -> List[Fork]:
return all_forks


def get_deployed_forks():
def get_deployed_forks() -> List[Fork]:
"""
Returns a list of all the fork classes implemented by `ethereum_test_forks`
that have been deployed to mainnet, chronologically ordered by deployment.
"""
return [fork for fork in get_forks() if fork.is_deployed()]


def get_development_forks():
def get_development_forks() -> List[Fork]:
"""
Returns a list of all the fork classes implemented by `ethereum_test_forks`
that have been not yet deployed to mainnet and are currently under
Expand All @@ -57,6 +59,34 @@ def get_parent_fork(fork: Fork) -> Fork:
return fork.__base__


def get_forks_with_solc_support(solc_version: Version) -> List[Fork]:
"""
Returns a list of all fork classes that are supported by solc.
"""
return [fork for fork in get_forks() if solc_version >= fork.solc_min_version()]


def get_forks_without_solc_support(solc_version: Version) -> List[Fork]:
"""
Returns a list of all fork classes that aren't supported by solc.
"""
return [fork for fork in get_forks() if solc_version < fork.solc_min_version()]


def get_closest_fork_with_solc_support(fork: Fork, solc_version: Version) -> Optional[Fork]:
"""
Returns the closest fork, potentially the provided fork itself, that has
solc support.
"""
if fork is BaseFork:
return None
return (
fork
if solc_version >= fork.solc_min_version()
else get_closest_fork_with_solc_support(get_parent_fork(fork), solc_version)
)


def get_transition_forks() -> List[Fork]:
"""
Returns all the transition forks
Expand Down
15 changes: 15 additions & 0 deletions src/ethereum_test_forks/tests/test_forks.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@

from typing import Mapping, cast

from semver import Version

from ..base_fork import Fork
from ..forks.forks import Berlin, Cancun, Frontier, London, Paris, Shanghai
from ..forks.transition import BerlinToLondonAt5, ParisToShanghaiAtTime15k
from ..helpers import (
forks_from,
forks_from_until,
get_closest_fork_with_solc_support,
get_deployed_forks,
get_development_forks,
get_forks,
get_forks_with_solc_support,
transition_fork_from_to,
transition_fork_to,
)
Expand Down Expand Up @@ -214,3 +218,14 @@ def test_precompiles():

def test_tx_types():
Cancun.tx_types() == list(range(4))


def test_solc_versioning():
assert len(get_forks_with_solc_support(Version.parse("0.8.20"))) == 13
assert len(get_forks_with_solc_support(Version.parse("0.8.24"))) > 13


def test_closest_fork_supported_by_solc():
assert get_closest_fork_with_solc_support(Paris, Version.parse("0.8.20")) == Paris
assert get_closest_fork_with_solc_support(Cancun, Version.parse("0.8.20")) == Shanghai
assert get_closest_fork_with_solc_support(Cancun, Version.parse("0.8.24")) == Cancun
25 changes: 17 additions & 8 deletions src/ethereum_test_forks/transition_base_fork.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
"""
Base objects used to define transition forks.
"""
from typing import List, Type
from inspect import signature
from typing import Callable, List, Type

from .base_fork import BaseFork, Fork, ForkAttribute
from .base_fork import BaseFork, Fork

ALWAYS_TRANSITIONED_BLOCK_NUMBER = 10_000
ALWAYS_TRANSITIONED_BLOCK_TIMESTAMP = 10_000_000
Expand Down Expand Up @@ -59,21 +60,29 @@ class NewTransitionClass(
NewTransitionClass.name = lambda: transition_name # type: ignore

def make_transition_method(
base_method: ForkAttribute,
from_fork_method: ForkAttribute,
to_fork_method: ForkAttribute,
base_method: Callable,
from_fork_method: Callable,
to_fork_method: Callable,
):
base_method_parameters = signature(base_method).parameters

def transition_method(
cls,
block_number: int = ALWAYS_TRANSITIONED_BLOCK_NUMBER,
timestamp: int = ALWAYS_TRANSITIONED_BLOCK_TIMESTAMP,
):
kwargs = {}
if "block_number" in base_method_parameters:
kwargs["block_number"] = block_number
if "timestamp" in base_method_parameters:
kwargs["timestamp"] = timestamp

if getattr(base_method, "__prefer_transition_to_method__", False):
return to_fork_method(block_number=block_number, timestamp=timestamp)
return to_fork_method(**kwargs)
return (
to_fork_method(block_number=block_number, timestamp=timestamp)
to_fork_method(**kwargs)
if block_number >= at_block and timestamp >= at_timestamp
else from_fork_method(block_number=block_number, timestamp=timestamp)
else from_fork_method(**kwargs)
)

return classmethod(transition_method)
Expand Down
2 changes: 1 addition & 1 deletion src/ethereum_test_tools/code/yul.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def get_evm_version_from_fork(fork: Fork | None):
"""
if not fork:
return None
return fork.solc_name().lower()
return fork.solc_name()


class Yul(SupportsBytes, Sized):
Expand Down
8 changes: 3 additions & 5 deletions src/ethereum_test_tools/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@
import pytest
from semver import Version

from ..code import Yul
from ethereum_test_forks import Frontier

SUPPORTED_SOLC_VERSIONS = [
Version.parse(v) for v in ["0.8.20", "0.8.21", "0.8.22", "0.8.23", "0.8.24"]
]
from ..code import Yul

SOLC_PADDING_VERSION = Version.parse("0.8.21")

Expand All @@ -18,6 +16,6 @@
def solc_version() -> Version:
"""Return the version of solc being used for tests."""
solc_version = Yul("").version().finalize_version()
if solc_version not in SUPPORTED_SOLC_VERSIONS:
if solc_version < Frontier.solc_min_version():
raise Exception("Unsupported solc version: {}".format(solc_version))
return solc_version
12 changes: 6 additions & 6 deletions src/ethereum_test_tools/tests/test_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import pytest
from semver import Version

from ethereum_test_forks import Fork, Homestead, Shanghai, forks_from_until, get_deployed_forks
from ethereum_test_forks import Fork, Homestead, Shanghai, get_deployed_forks
from evm_transition_tool import FixtureFormats, GethTransitionTool

from ..code import CalldataCase, Case, Code, Conditional, Initcode, Switch, Yul
Expand Down Expand Up @@ -49,14 +49,14 @@ def test_code_operations(code: Code, expected_bytes: bytes):
assert bytes(code) == expected_bytes


@pytest.fixture(params=forks_from_until(get_deployed_forks()[1], get_deployed_forks()[-1]))
@pytest.fixture(params=get_deployed_forks())
def fork(request: pytest.FixtureRequest):
"""
Return the target evm-version (fork) for solc compilation.

Note:
- get_deployed_forks()[1] (Homestead) is the first fork that solc supports.
- forks_from_util: Used to remove the Glacier forks
- Homestead.
- forks_from_until: Used to remove the Glacier forks
"""
return request.param

Expand All @@ -81,15 +81,15 @@ def expected_bytes(request: pytest.FixtureRequest, solc_version: Version, fork:
"""Return the expected bytes for the test."""
expected_bytes = request.param
if isinstance(expected_bytes, Template):
if solc_version < SOLC_PADDING_VERSION or fork == Homestead:
if solc_version < SOLC_PADDING_VERSION or fork <= Homestead:
solc_padding = ""
else:
solc_padding = "00"
return bytes.fromhex(expected_bytes.substitute(solc_padding=solc_padding))
if isinstance(expected_bytes, bytes):
if fork == Shanghai:
expected_bytes = b"\x5f" + expected_bytes[2:]
if solc_version < SOLC_PADDING_VERSION or fork == Homestead:
if solc_version < SOLC_PADDING_VERSION or fork <= Homestead:
return expected_bytes
else:
return expected_bytes + b"\x00"
Expand Down
Loading
Loading