diff --git a/.github/actions/setup-uv/action.yaml b/.github/actions/setup-uv/action.yaml index 6c78479b96..8d5aa10b78 100644 --- a/.github/actions/setup-uv/action.yaml +++ b/.github/actions/setup-uv/action.yaml @@ -6,6 +6,6 @@ runs: - name: Install UV shell: bash run: | - UV_VERSION="0.4.2" + UV_VERSION="0.4.17" echo "Installing UV version $UV_VERSION..." curl -LsSf https://astral.sh/uv/${UV_VERSION}/install.sh | sh diff --git a/.github/workflows/tox_verify.yaml b/.github/workflows/tox_verify.yaml index dd5bdac4a3..b534eae85f 100644 --- a/.github/workflows/tox_verify.yaml +++ b/.github/workflows/tox_verify.yaml @@ -8,21 +8,22 @@ jobs: strategy: matrix: include: - - os: ubuntu-24.04 - python: "3.10" - evm-type: "stable" - tox-cmd: "uvx --with=tox-uv tox" # run-parallel --parallel-no-spinner" # TODO: disable parallelisation for uv testing - - os: ubuntu-24.04 + # Temporarily disable + # - os: ubuntu-latest + # python: "3.10" + # evm-type: "stable" + # tox-cmd: "uvx --with=tox-uv tox" # run-parallel --parallel-no-spinner" # TODO: disable parallelisation for uv testing + - os: ubuntu-latest python: "3.12" evm-type: "stable" tox-cmd: "uvx --with=tox-uv tox" # run-parallel --parallel-no-spinner" # Disabled due to unavailable evm implementation for devnet-1 - # - os: ubuntu-24.04 + # - os: ubuntu-latest # python: '3.11' # evm-type: 'develop' # tox-cmd: 'tox -e tests-develop' # Disabled to not be gated by evmone implementation - # - os: ubuntu-24.04 + # - os: ubuntu-latest # python: '3.11' # evm-type: 'eip7692' # tox-cmd: 'tox -e tests-eip7692' diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 63b078645e..60728028ce 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -42,6 +42,7 @@ Test fixtures for use by clients are available for each release on the [Github r - 🐞 Fix `Conditional` code generator in EOF mode ([#821](https://github.com/ethereum/execution-spec-tests/pull/821)) - 🔀 `ethereum_test_rpc` library has been created with what was previously `ethereum_test_tools.rpc` ([#822](https://github.com/ethereum/execution-spec-tests/pull/822)) - ✨ Add `Wei` type to `ethereum_test_base_types` which allows parsing wei amounts from strings like "1 ether", "1000 wei", "10**2 gwei", etc ([#825](https://github.com/ethereum/execution-spec-tests/pull/825)) +- 🔀 Replace `ethereum.base_types` with `ethereum-types` ([#850](https://github.com/ethereum/execution-spec-tests/pull/850)) ### 🔧 EVM Tools diff --git a/pyproject.toml b/pyproject.toml index 129d8100fb..7a9f9b4507 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,7 @@ dependencies = [ "rich>=13.7.0,<14", "solc-select>=1.0.4,<2", "filelock>=3.15.1,<4", + "ethereum-types>=0.2.1,<0.3", ] [project.urls] diff --git a/src/cli/gen_index.py b/src/cli/gen_index.py index 19e49ae6a2..8178e4d1c3 100644 --- a/src/cli/gen_index.py +++ b/src/cli/gen_index.py @@ -20,7 +20,7 @@ ) from ethereum_test_base_types import HexNumber -from ethereum_test_fixtures import FixtureFormats +from ethereum_test_fixtures import FIXTURE_FORMATS, BlockchainFixture, FixtureFormat from ethereum_test_fixtures.consume import IndexFile, TestCaseIndexFile from ethereum_test_fixtures.file import Fixtures @@ -48,21 +48,16 @@ def count_json_files_exclude_index(start_path: Path) -> int: return json_file_count -def infer_fixture_format_from_path(file: Path) -> FixtureFormats: +def infer_fixture_format_from_path(file: Path) -> FixtureFormat | None: """ Attempt to infer the fixture format from the file path. """ - if "blockchain_tests_engine" in file.parts: - return FixtureFormats.BLOCKCHAIN_TEST_ENGINE - if "blockchain_tests" in file.parts: - return FixtureFormats.BLOCKCHAIN_TEST + for fixture_type in FIXTURE_FORMATS.values(): + if fixture_type.output_base_dir_name() in file.parts: + return fixture_type if "BlockchainTests" in file.parts: # ethereum/tests - return FixtureFormats.BLOCKCHAIN_TEST - if "state_tests" in file.parts: - return FixtureFormats.STATE_TEST - if "eof_tests" in file.parts: - return FixtureFormats.EOF_TEST - return FixtureFormats.UNSET_TEST_FORMAT + return BlockchainFixture + return None @click.command( @@ -200,7 +195,7 @@ def generate_fixtures_index( json_path=relative_file_path, fixture_hash=fixture.info.get("hash", None), fork=fixture.get_fork(), - format=fixture.format, + format=fixture.__class__, ) ) diff --git a/src/ethereum_test_base_types/base_types.py b/src/ethereum_test_base_types/base_types.py index 01f80df796..fb77e9e96b 100644 --- a/src/ethereum_test_base_types/base_types.py +++ b/src/ethereum_test_base_types/base_types.py @@ -197,12 +197,12 @@ def or_none(cls, input: "Bytes | BytesConvertible | None") -> "Bytes | None": return input return cls(input) - def keccak256(self) -> "Bytes": + def keccak256(self) -> "Hash": """ Return the keccak256 hash of the opcode byte representation. """ k = keccak.new(digest_bits=256) - return Bytes(k.update(bytes(self)).digest()) + return Hash(k.update(bytes(self)).digest()) S = TypeVar("S", bound="FixedSizeHexNumber") diff --git a/src/ethereum_test_fixtures/__init__.py b/src/ethereum_test_fixtures/__init__.py index b6c5c9f317..455eac8a0b 100644 --- a/src/ethereum_test_fixtures/__init__.py +++ b/src/ethereum_test_fixtures/__init__.py @@ -2,33 +2,35 @@ Ethereum test fixture format definitions. """ -from typing import List, Type +from typing import Dict -from .base import BaseFixture +from .base import BaseFixture, FixtureFormat from .blockchain import EngineFixture as BlockchainEngineFixture from .blockchain import Fixture as BlockchainFixture from .blockchain import FixtureCommon as BlockchainFixtureCommon from .collector import FixtureCollector, TestInfo from .eof import Fixture as EOFFixture -from .formats import FixtureFormats from .state import Fixture as StateFixture from .verify import FixtureVerifier -FIXTURE_TYPES: List[Type[BaseFixture]] = [ - BlockchainFixture, - BlockchainEngineFixture, - EOFFixture, - StateFixture, -] +FIXTURE_FORMATS: Dict[str, FixtureFormat] = { + f.fixture_format_name: f # type: ignore + for f in [ + BlockchainFixture, + BlockchainEngineFixture, + EOFFixture, + StateFixture, + ] +} __all__ = [ - "FIXTURE_TYPES", + "FIXTURE_FORMATS", "BaseFixture", "BlockchainFixture", "BlockchainFixtureCommon", "BlockchainEngineFixture", "EOFFixture", "FixtureCollector", - "FixtureFormats", + "FixtureFormat", "FixtureVerifier", "StateFixture", "TestInfo", diff --git a/src/ethereum_test_fixtures/base.py b/src/ethereum_test_fixtures/base.py index 32298230b8..05e83ff62e 100644 --- a/src/ethereum_test_fixtures/base.py +++ b/src/ethereum_test_fixtures/base.py @@ -5,20 +5,30 @@ import hashlib import json from functools import cached_property -from typing import Any, ClassVar, Dict +from typing import Any, ClassVar, Dict, Type from pydantic import Field from ethereum_test_base_types import CamelModel, ReferenceSpec - -from .formats import FixtureFormats +from ethereum_test_forks import Fork class BaseFixture(CamelModel): """Represents a base Ethereum test fixture of any type.""" info: Dict[str, str] = Field(default_factory=dict, alias="_info") - format: ClassVar[FixtureFormats] = FixtureFormats.UNSET_TEST_FORMAT + + # Fixture format properties + fixture_format_name: ClassVar[str] = "unset" + output_file_extension: ClassVar[str] = ".json" + description: ClassVar[str] = "Unknown fixture format; it has not been set." + + @classmethod + def output_base_dir_name(cls) -> str: + """ + Returns the name of the subdirectory where this type of fixture should be dumped to. + """ + return cls.fixture_format_name.replace("test", "tests") @cached_property def json_dict(self) -> Dict[str, Any]: @@ -69,3 +79,16 @@ def get_fork(self) -> str | None: Returns the fork of the fixture as a string. """ raise NotImplementedError + + @classmethod + def supports_fork(cls, fork: Fork) -> bool: + """ + Returns whether the fixture can be generated for the given fork. + + By default, all fixtures support all forks. + """ + return True + + +# Type alias for a base fixture class +FixtureFormat = Type[BaseFixture] diff --git a/src/ethereum_test_fixtures/blockchain.py b/src/ethereum_test_fixtures/blockchain.py index 3a0d6a2ba9..9b770ce986 100644 --- a/src/ethereum_test_fixtures/blockchain.py +++ b/src/ethereum_test_fixtures/blockchain.py @@ -6,8 +6,7 @@ from typing import Annotated, Any, ClassVar, List, Literal, Tuple, Union, get_args, get_type_hints from ethereum import rlp as eth_rlp -from ethereum.base_types import Uint -from ethereum.crypto.hash import keccak256 +from ethereum_types.numeric import Uint from pydantic import AliasChoices, Field, PlainSerializer, computed_field from ethereum_test_base_types import ( @@ -24,7 +23,7 @@ ZeroPaddedHexNumber, ) from ethereum_test_exceptions import EngineAPIError, ExceptionInstanceOrList -from ethereum_test_forks import Fork +from ethereum_test_forks import Fork, Paris from ethereum_test_types.types import ( AuthorizationTupleGeneric, ConsolidationRequest, @@ -42,7 +41,6 @@ ) from .base import BaseFixture -from .formats import FixtureFormats class HeaderForkRequirement(str): @@ -182,7 +180,7 @@ def block_hash(self) -> Hash: """ Compute the RLP of the header """ - return Hash(keccak256(self.rlp)) + return self.rlp.keccak256() class FixtureExecutionPayload(CamelModel): @@ -500,19 +498,32 @@ class Fixture(FixtureCommon): Cross-client specific blockchain test model use in JSON fixtures. """ + fixture_format_name: ClassVar[str] = "blockchain_test" + description: ClassVar[str] = "Tests that generate a blockchain test fixture." + genesis_rlp: Bytes = Field(..., alias="genesisRLP") blocks: List[FixtureBlock | InvalidFixtureBlock] seal_engine: Literal["NoProof"] = Field("NoProof") - format: ClassVar[FixtureFormats] = FixtureFormats.BLOCKCHAIN_TEST - class EngineFixture(FixtureCommon): """ Engine specific test fixture information. """ + fixture_format_name: ClassVar[str] = "blockchain_test_engine" + description: ClassVar[ + str + ] = "Tests that generate a blockchain test fixture in Engine API format." + payloads: List[FixtureEngineNewPayload] = Field(..., alias="engineNewPayloads") sync_payload: FixtureEngineNewPayload | None = None - format: ClassVar[FixtureFormats] = FixtureFormats.BLOCKCHAIN_TEST_ENGINE + @classmethod + def supports_fork(cls, fork: Fork) -> bool: + """ + Returns whether the fixture can be generated for the given fork. + + The Engine API is available only on Paris and afterwards. + """ + return fork >= Paris diff --git a/src/ethereum_test_fixtures/collector.py b/src/ethereum_test_fixtures/collector.py index 112a1033a6..24542223fb 100644 --- a/src/ethereum_test_fixtures/collector.py +++ b/src/ethereum_test_fixtures/collector.py @@ -15,7 +15,6 @@ from .base import BaseFixture from .file import Fixtures -from .formats import FixtureFormats from .verify import FixtureVerifier @@ -139,8 +138,8 @@ def add_fixture(self, info: TestInfo, fixture: BaseFixture) -> Path: fixture_path = ( self.output_dir - / fixture.format.output_base_dir_name - / fixture_basename.with_suffix(fixture.format.output_file_extension) + / fixture.output_base_dir_name() + / fixture_basename.with_suffix(fixture.output_file_extension) ) if fixture_path not in self.all_fixtures.keys(): # relevant when we group by test function self.all_fixtures[fixture_path] = Fixtures(root={}) @@ -163,7 +162,7 @@ def dump_fixtures(self) -> None: os.makedirs(self.output_dir, exist_ok=True) for fixture_path, fixtures in self.all_fixtures.items(): os.makedirs(fixture_path.parent, exist_ok=True) - if len({fixture.format for fixture in fixtures.values()}) != 1: + if len({fixture.__class__ for fixture in fixtures.values()}) != 1: raise TypeError("All fixtures in a single file must have the same format.") fixtures.collect_into_file(fixture_path) @@ -173,11 +172,11 @@ def verify_fixture_files(self, evm_fixture_verification: FixtureVerifier) -> Non """ for fixture_path, name_fixture_dict in self.all_fixtures.items(): for fixture_name, fixture in name_fixture_dict.items(): - if FixtureFormats.is_verifiable(fixture.format): + if evm_fixture_verification.is_verifiable(fixture.__class__): info = self.json_path_to_test_item[fixture_path] verify_fixtures_dump_dir = self._get_verify_fixtures_dump_dir(info) evm_fixture_verification.verify_fixture( - fixture.format, + fixture.__class__, fixture_path, fixture_name=None, debug_output_path=verify_fixtures_dump_dir, diff --git a/src/ethereum_test_fixtures/consume.py b/src/ethereum_test_fixtures/consume.py index f155388e1d..ab46ee9e1f 100644 --- a/src/ethereum_test_fixtures/consume.py +++ b/src/ethereum_test_fixtures/consume.py @@ -5,15 +5,16 @@ import datetime import json from pathlib import Path -from typing import List, TextIO +from typing import Annotated, List, TextIO -from pydantic import BaseModel, RootModel +from pydantic import BaseModel, PlainSerializer, PlainValidator, RootModel from ethereum_test_base_types import HexNumber +from ethereum_test_fixtures import FIXTURE_FORMATS, FixtureFormat +from .blockchain import EngineFixture as BlockchainEngineFixture from .blockchain import Fixture as BlockchainFixture from .file import Fixtures -from .formats import FixtureFormats from .state import Fixture as StateFixture @@ -25,7 +26,11 @@ class TestCaseBase(BaseModel): id: str fixture_hash: HexNumber | None fork: str | None - format: FixtureFormats + format: Annotated[ + FixtureFormat, + PlainSerializer(lambda f: f.fixture_format_name), + PlainValidator(lambda f: FIXTURE_FORMATS[f] if f in FIXTURE_FORMATS else f), + ] __test__ = False # stop pytest from collecting this class as a test @@ -129,14 +134,14 @@ def from_stream(cls, fd: TextIO) -> "TestCases": fixtures = Fixtures.from_json_data(json.load(fd)) test_cases = [] for fixture_name, fixture in fixtures.items(): - if fixture.format == FixtureFormats.BLOCKCHAIN_TEST_ENGINE: + if fixture == BlockchainEngineFixture: print("Skipping engine fixture", fixture_name) test_cases.append( TestCaseStream( id=fixture_name, fixture_hash=fixture.hash, fork=fixture.get_fork(), - format=fixture.format, + format=fixture.__class__, fixture=fixture, ) ) diff --git a/src/ethereum_test_fixtures/eof.py b/src/ethereum_test_fixtures/eof.py index 987cea8ea0..ae43e0a1fa 100644 --- a/src/ethereum_test_fixtures/eof.py +++ b/src/ethereum_test_fixtures/eof.py @@ -12,7 +12,6 @@ from ethereum_test_types.eof.v1 import ContainerKind from .base import BaseFixture -from .formats import FixtureFormats class Result(CamelModel): @@ -50,9 +49,10 @@ class Fixture(BaseFixture): Fixture for a single EOFTest. """ - vectors: Mapping[Number, Vector] + fixture_format_name: ClassVar[str] = "eof_test" + description: ClassVar[str] = "Tests that generate an EOF test fixture." - format: ClassVar[FixtureFormats] = FixtureFormats.EOF_TEST + vectors: Mapping[Number, Vector] def get_fork(self) -> str | None: """ diff --git a/src/ethereum_test_fixtures/file.py b/src/ethereum_test_fixtures/file.py index ffed125bb5..3d6e573a7a 100644 --- a/src/ethereum_test_fixtures/file.py +++ b/src/ethereum_test_fixtures/file.py @@ -3,20 +3,16 @@ """ import json from pathlib import Path -from typing import Any, Dict, Literal, Optional +from typing import Any, Dict, Optional from pydantic import RootModel +from .base import FixtureFormat from .blockchain import EngineFixture as BlockchainEngineFixture from .blockchain import Fixture as BlockchainFixture from .eof import Fixture as EOFFixture -from .formats import FixtureFormats from .state import Fixture as StateFixture -FixtureFormatsValues = Literal[ - "blockchain_test_engine", "blockchain_test", "state_test", "eof_test", "unset_test_format" -] - FixtureModel = BlockchainFixture | BlockchainEngineFixture | StateFixture | EOFFixture @@ -75,7 +71,7 @@ def collect_into_file(self, file_path: Path): def from_file( cls, file_path: Path, - fixture_format: Optional[FixtureFormats | FixtureFormatsValues] = None, + fixture_format: Optional[FixtureFormat] = None, ) -> "BaseFixturesRootModel": """ Dynamically create a fixture model from the specified json file and, @@ -89,7 +85,7 @@ def from_file( def from_json_data( cls, json_data: Dict[str, Any], - fixture_format: Optional[FixtureFormats | FixtureFormatsValues] = None, + fixture_format: Optional[FixtureFormat] = None, ) -> "BaseFixturesRootModel": """ Dynamically create a fixture model from the specified json data and, @@ -101,17 +97,13 @@ def from_json_data( fixture_format will provide a speed-up. """ model_mapping = { - FixtureFormats.BLOCKCHAIN_TEST: BlockchainFixtures, - FixtureFormats.BLOCKCHAIN_TEST_ENGINE: BlockchainEngineFixtures, - FixtureFormats.STATE_TEST: StateFixtures, - FixtureFormats.EOF_TEST: EOFFixtures, - FixtureFormats.BLOCKCHAIN_TEST.value: BlockchainFixtures, - FixtureFormats.BLOCKCHAIN_TEST_ENGINE.value: BlockchainEngineFixtures, - FixtureFormats.STATE_TEST.value: StateFixtures, - FixtureFormats.EOF_TEST.value: EOFFixtures, + BlockchainFixture: BlockchainFixtures, + BlockchainEngineFixture: BlockchainEngineFixtures, + StateFixture: StateFixtures, + EOFFixture: EOFFixtures, } - if fixture_format not in [None, "unset_test_format", FixtureFormats.UNSET_TEST_FORMAT]: + if fixture_format is not None: if fixture_format not in model_mapping: raise TypeError(f"Unsupported fixture format: {fixture_format}") model_class = model_mapping[fixture_format] diff --git a/src/ethereum_test_fixtures/formats.py b/src/ethereum_test_fixtures/formats.py deleted file mode 100644 index 3acea77a1f..0000000000 --- a/src/ethereum_test_fixtures/formats.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Fixture formats enum. -""" -from enum import Enum -from pathlib import Path - - -class FixtureFormats(Enum): - """ - Helper class to define fixture formats. - """ - - UNSET_TEST_FORMAT = "unset_test_format" - STATE_TEST = "state_test" - BLOCKCHAIN_TEST = "blockchain_test" - BLOCKCHAIN_TEST_ENGINE = "blockchain_test_engine" - EOF_TEST = "eof_test" - - @classmethod - def is_state_test(cls, format): # noqa: D102 - return format == cls.STATE_TEST - - @classmethod - def is_blockchain_test(cls, format): # noqa: D102 - return format in (cls.BLOCKCHAIN_TEST, cls.BLOCKCHAIN_TEST_ENGINE) - - @classmethod - def is_hive_format(cls, format): # noqa: D102 - return format == cls.BLOCKCHAIN_TEST_ENGINE - - @classmethod - def is_standard_format(cls, format): # noqa: D102 - return format in (cls.STATE_TEST, cls.BLOCKCHAIN_TEST) - - @classmethod - def is_verifiable(cls, format): # noqa: D102 - return format in (cls.STATE_TEST, cls.BLOCKCHAIN_TEST) - - @classmethod - def get_format_description(cls, format): - """ - Returns a description of the fixture format. - - Used to add a description to the generated pytest marks. - """ - if format == cls.UNSET_TEST_FORMAT: - return "Unknown fixture format; it has not been set." - elif format == cls.STATE_TEST: - return "Tests that generate a state test fixture." - elif format == cls.BLOCKCHAIN_TEST: - return "Tests that generate a blockchain test fixture." - elif format == cls.BLOCKCHAIN_TEST_ENGINE: - return "Tests that generate a blockchain test fixture in Engine API format." - elif format == cls.EOF_TEST: - return "Tests that generate an EOF test fixture." - raise Exception(f"Unknown fixture format: {format}.") - - @property - def output_base_dir_name(self) -> Path: - """ - Returns the name of the subdirectory where this type of fixture should be dumped to. - """ - return Path(self.value.replace("test", "tests")) - - @property - def output_file_extension(self) -> str: - """ - Returns the file extension for this type of fixture. - - By default, fixtures are dumped as JSON files. - """ - return ".json" diff --git a/src/ethereum_test_fixtures/state.py b/src/ethereum_test_fixtures/state.py index d3c35b1496..7b2cbc57e2 100644 --- a/src/ethereum_test_fixtures/state.py +++ b/src/ethereum_test_fixtures/state.py @@ -18,7 +18,6 @@ ) from .base import BaseFixture -from .formats import FixtureFormats class FixtureEnvironment(EnvironmentGeneric[ZeroPaddedHexNumber]): @@ -108,13 +107,14 @@ class Fixture(BaseFixture): Fixture for a single StateTest. """ + fixture_format_name: ClassVar[str] = "state_test" + description: ClassVar[str] = "Tests that generate a state test fixture." + env: FixtureEnvironment pre: Alloc transaction: FixtureTransaction post: Mapping[str, List[FixtureForkPost]] - format: ClassVar[FixtureFormats] = FixtureFormats.STATE_TEST - def get_fork(self) -> str | None: """ Returns the fork of the fixture as a string. diff --git a/src/ethereum_test_fixtures/tests/test_blockchain.py b/src/ethereum_test_fixtures/tests/test_blockchain.py index d9f98ecc7e..ab01f5f946 100644 --- a/src/ethereum_test_fixtures/tests/test_blockchain.py +++ b/src/ethereum_test_fixtures/tests/test_blockchain.py @@ -620,8 +620,7 @@ "excessBlobGas": hex(18), "blockHash": "0xd90115b7fde329f64335763a446af150ab67e639281dccdb07a007d18bb80211", "transactions": [ - "0x" - + Transaction( + Transaction( to=0x1234, data=b"\x01\x00", access_list=[ @@ -735,8 +734,7 @@ "0x8eca4747db6a4b272018f2850e4208b863989ce9971bb1907467ae2204950695" ), "transactions": [ - "0x" - + Transaction( + Transaction( to=0x1234, data=b"\x01\x00", access_list=[ @@ -914,8 +912,7 @@ "0x78a4bf2520248e0b403d343c32b6746a43da1ebcf3cc8de14b959bc9f461fe76" ), "transactions": [ - "0x" - + Transaction( + Transaction( to=0x1234, data=b"\x01\x00", access_list=[ @@ -1082,8 +1079,7 @@ def test_json_deserialization( "blockHash": "0xd90115b7fde329f64335763a446af1" "50ab67e639281dccdb07a007d18bb80211", "transactions": [ - "0x" - + Transaction( + Transaction( to=0x1234, data=b"\x01\x00", access_list=[ @@ -1193,8 +1189,7 @@ def test_json_deserialization( "blockHash": "0xd90115b7fde329f64335763a446af1" "50ab67e639281dccdb07a007d18bb80211", "transactions": [ - "0x" - + Transaction( + Transaction( to=0x1234, data=b"\x01\x00", access_list=[ diff --git a/src/ethereum_test_fixtures/verify.py b/src/ethereum_test_fixtures/verify.py index c3a6faced5..234834d9ba 100644 --- a/src/ethereum_test_fixtures/verify.py +++ b/src/ethereum_test_fixtures/verify.py @@ -5,7 +5,7 @@ from abc import ABC, abstractmethod from pathlib import Path -from .formats import FixtureFormats +from .base import FixtureFormat class FixtureVerifier(ABC): @@ -13,10 +13,19 @@ class FixtureVerifier(ABC): Abstract class for verifying Ethereum test fixtures. """ + def is_verifiable( + self, + fixture_format: FixtureFormat, + ) -> bool: + """ + Returns whether the fixture format is verifiable by this verifier. + """ + return False + @abstractmethod def verify_fixture( self, - fixture_format: FixtureFormats, + fixture_format: FixtureFormat, fixture_path: Path, fixture_name: str | None = None, debug_output_path: Path | None = None, diff --git a/src/ethereum_test_specs/base.py b/src/ethereum_test_specs/base.py index 9e50eb7b31..a79bf6e8ab 100644 --- a/src/ethereum_test_specs/base.py +++ b/src/ethereum_test_specs/base.py @@ -13,7 +13,7 @@ from pydantic import BaseModel, Field from ethereum_test_base_types import to_hex -from ethereum_test_fixtures import BaseFixture, FixtureFormats +from ethereum_test_fixtures import BaseFixture, FixtureFormat from ethereum_test_forks import Fork from ethereum_test_types import Environment, Transaction, Withdrawal from evm_transition_tool import Result, TransitionTool @@ -73,7 +73,7 @@ class BaseTest(BaseModel): t8n_dump_dir: Path | None = Field(None, exclude=True) _t8n_call_counter: Iterator[int] = count(0) - supported_fixture_formats: ClassVar[List[FixtureFormats]] = [] + supported_fixture_formats: ClassVar[List[FixtureFormat]] = [] @abstractmethod def generate( @@ -82,7 +82,7 @@ def generate( request: pytest.FixtureRequest, t8n: TransitionTool, fork: Fork, - fixture_format: FixtureFormats, + fixture_format: FixtureFormat, eips: Optional[List[int]] = None, ) -> BaseFixture: """ diff --git a/src/ethereum_test_specs/blockchain.py b/src/ethereum_test_specs/blockchain.py index 3d0c232387..d91fb18180 100644 --- a/src/ethereum_test_specs/blockchain.py +++ b/src/ethereum_test_specs/blockchain.py @@ -21,7 +21,12 @@ Number, ) from ethereum_test_exceptions import BlockException, EngineAPIError, TransactionException -from ethereum_test_fixtures import BaseFixture, FixtureFormats +from ethereum_test_fixtures import ( + BaseFixture, + BlockchainEngineFixture, + BlockchainFixture, + FixtureFormat, +) from ethereum_test_fixtures.blockchain import ( EngineFixture, Fixture, @@ -319,9 +324,9 @@ class BlockchainTest(BaseTest): verify_sync: bool = False chain_id: int = 1 - supported_fixture_formats: ClassVar[List[FixtureFormats]] = [ - FixtureFormats.BLOCKCHAIN_TEST, - FixtureFormats.BLOCKCHAIN_TEST_ENGINE, + supported_fixture_formats: ClassVar[List[FixtureFormat]] = [ + BlockchainFixture, + BlockchainEngineFixture, ] def make_genesis( @@ -718,16 +723,16 @@ def generate( request: pytest.FixtureRequest, t8n: TransitionTool, fork: Fork, - fixture_format: FixtureFormats, + fixture_format: FixtureFormat, eips: Optional[List[int]] = None, ) -> BaseFixture: """ Generate the BlockchainTest fixture. """ t8n.reset_traces() - if fixture_format == FixtureFormats.BLOCKCHAIN_TEST_ENGINE: + if fixture_format == BlockchainEngineFixture: return self.make_hive_fixture(t8n, fork, eips) - elif fixture_format == FixtureFormats.BLOCKCHAIN_TEST: + elif fixture_format == BlockchainFixture: return self.make_fixture(t8n, fork, eips) raise Exception(f"Unknown fixture format: {fixture_format}") diff --git a/src/ethereum_test_specs/eof.py b/src/ethereum_test_specs/eof.py index 83a4f0417a..e251eff6f6 100644 --- a/src/ethereum_test_specs/eof.py +++ b/src/ethereum_test_specs/eof.py @@ -15,8 +15,15 @@ from ethereum_test_base_types import Account, Bytes from ethereum_test_exceptions import EvmoneExceptionMapper from ethereum_test_exceptions.exceptions import EOFExceptionInstanceOrList, to_pipe_str -from ethereum_test_fixtures import BaseFixture, FixtureFormats -from ethereum_test_fixtures.eof import Fixture, Result, Vector +from ethereum_test_fixtures import ( + BaseFixture, + BlockchainEngineFixture, + BlockchainFixture, + EOFFixture, + FixtureFormat, + StateFixture, +) +from ethereum_test_fixtures.eof import Result, Vector from ethereum_test_forks import Fork from ethereum_test_types import Alloc, Environment, Transaction from ethereum_test_types.eof.v1 import Container, ContainerKind @@ -143,8 +150,8 @@ class EOFTest(BaseTest): expect_exception: EOFExceptionInstanceOrList | None = None container_kind: ContainerKind | None = None - supported_fixture_formats: ClassVar[List[FixtureFormats]] = [ - FixtureFormats.EOF_TEST, + supported_fixture_formats: ClassVar[List[FixtureFormat]] = [ + EOFFixture, ] @model_validator(mode="before") @@ -192,7 +199,7 @@ def make_eof_test_fixture( request: pytest.FixtureRequest, fork: Fork, eips: Optional[List[int]], - ) -> Fixture: + ) -> EOFFixture: """ Generate the EOF test fixture. """ @@ -213,7 +220,7 @@ def make_eof_test_fixture( }, ) ] - fixture = Fixture(vectors=dict(enumerate(vectors))) + fixture = EOFFixture(vectors=dict(enumerate(vectors))) try: eof_parse = EOFParse() except FileNotFoundError as e: @@ -272,13 +279,13 @@ def generate( t8n: TransitionTool, fork: Fork, eips: Optional[List[int]] = None, - fixture_format: FixtureFormats, + fixture_format: FixtureFormat, **_, ) -> BaseFixture: """ Generate the BlockchainTest fixture. """ - if fixture_format == FixtureFormats.EOF_TEST: + if fixture_format == EOFFixture: return self.make_eof_test_fixture(request=request, fork=fork, eips=eips) raise Exception(f"Unknown fixture format: {fixture_format}") @@ -301,11 +308,11 @@ class EOFStateTest(EOFTest): container_post: Account = Field(default_factory=Account) pre: Alloc | None = None - supported_fixture_formats: ClassVar[List[FixtureFormats]] = [ - FixtureFormats.EOF_TEST, - FixtureFormats.STATE_TEST, - FixtureFormats.BLOCKCHAIN_TEST, - FixtureFormats.BLOCKCHAIN_TEST_ENGINE, + supported_fixture_formats: ClassVar[List[FixtureFormat]] = [ + EOFFixture, + StateFixture, + BlockchainFixture, + BlockchainEngineFixture, ] @model_validator(mode="before") @@ -372,22 +379,22 @@ def generate( t8n: TransitionTool, fork: Fork, eips: Optional[List[int]] = None, - fixture_format: FixtureFormats, + fixture_format: FixtureFormat, **_, ) -> BaseFixture: """ Generate the BlockchainTest fixture. """ - if fixture_format == FixtureFormats.EOF_TEST: + if fixture_format == EOFFixture: if self.data in existing_tests: # Gracefully skip duplicate tests because one EOFStateTest can generate multiple # state fixtures with the same data. pytest.skip(f"Duplicate EOF container on EOFStateTest: {request.node.nodeid}") return self.make_eof_test_fixture(request=request, fork=fork, eips=eips) elif fixture_format in ( - FixtureFormats.STATE_TEST, - FixtureFormats.BLOCKCHAIN_TEST, - FixtureFormats.BLOCKCHAIN_TEST_ENGINE, + StateFixture, + BlockchainFixture, + BlockchainEngineFixture, ): return self.generate_state_test().generate( request=request, t8n=t8n, fork=fork, fixture_format=fixture_format, eips=eips diff --git a/src/ethereum_test_specs/state.py b/src/ethereum_test_specs/state.py index dcdaac5a6b..2252393253 100644 --- a/src/ethereum_test_specs/state.py +++ b/src/ethereum_test_specs/state.py @@ -7,7 +7,13 @@ import pytest from ethereum_test_exceptions import EngineAPIError -from ethereum_test_fixtures import BaseFixture, FixtureFormats +from ethereum_test_fixtures import ( + BaseFixture, + BlockchainEngineFixture, + BlockchainFixture, + FixtureFormat, + StateFixture, +) from ethereum_test_fixtures.state import ( Fixture, FixtureEnvironment, @@ -39,10 +45,10 @@ class StateTest(BaseTest): blockchain_test_rlp_modifier: Optional[Header] = None chain_id: int = 1 - supported_fixture_formats: ClassVar[List[FixtureFormats]] = [ - FixtureFormats.BLOCKCHAIN_TEST, - FixtureFormats.BLOCKCHAIN_TEST_ENGINE, - FixtureFormats.STATE_TEST, + supported_fixture_formats: ClassVar[List[FixtureFormat]] = [ + BlockchainFixture, + BlockchainEngineFixture, + StateFixture, ] def _generate_blockchain_genesis_environment(self) -> Environment: @@ -166,7 +172,7 @@ def generate( request: pytest.FixtureRequest, t8n: TransitionTool, fork: Fork, - fixture_format: FixtureFormats, + fixture_format: FixtureFormat, eips: Optional[List[int]] = None, ) -> BaseFixture: """ @@ -176,7 +182,7 @@ def generate( return self.generate_blockchain_test().generate( request=request, t8n=t8n, fork=fork, fixture_format=fixture_format, eips=eips ) - elif fixture_format == FixtureFormats.STATE_TEST: + elif fixture_format == StateFixture: return self.make_state_test_fixture(t8n, fork, eips) raise Exception(f"Unknown fixture format: {fixture_format}") @@ -187,7 +193,7 @@ class StateTestOnly(StateTest): StateTest filler that only generates a state test fixture. """ - supported_fixture_formats: ClassVar[List[FixtureFormats]] = [FixtureFormats.STATE_TEST] + supported_fixture_formats: ClassVar[List[FixtureFormat]] = [StateFixture] StateTestSpec = Callable[[str], Generator[StateTest, None, None]] diff --git a/src/ethereum_test_specs/tests/test_expect.py b/src/ethereum_test_specs/tests/test_expect.py index 43df2775e7..36ee92f132 100644 --- a/src/ethereum_test_specs/tests/test_expect.py +++ b/src/ethereum_test_specs/tests/test_expect.py @@ -7,7 +7,7 @@ import pytest from ethereum_test_base_types import Account, Address -from ethereum_test_fixtures import FixtureFormats +from ethereum_test_fixtures import StateFixture from ethereum_test_forks import Fork, get_deployed_forks from ethereum_test_types import Alloc, Environment, Storage, Transaction from evm_transition_tool import ExecutionSpecsTransitionTool @@ -116,9 +116,7 @@ def test_post_storage_value_mismatch( Test post state `Account.storage` exceptions during state test fixture generation. """ with pytest.raises(Storage.KeyValueMismatch) as e_info: - state_test.generate( - request=None, t8n=t8n, fork=fork, fixture_format=FixtureFormats.STATE_TEST - ) + state_test.generate(request=None, t8n=t8n, fork=fork, fixture_format=StateFixture) assert e_info.value == expected_exception @@ -145,14 +143,10 @@ def test_post_nonce_value_mismatch(pre: Alloc, post: Alloc, state_test, t8n, for pre_nonce = pre_account.nonce post_nonce = post_account.nonce if "nonce" not in post_account.model_fields_set: # no exception - state_test.generate( - request=None, t8n=t8n, fork=fork, fixture_format=FixtureFormats.STATE_TEST - ) + state_test.generate(request=None, t8n=t8n, fork=fork, fixture_format=StateFixture) return with pytest.raises(Account.NonceMismatch) as e_info: - state_test.generate( - request=None, t8n=t8n, fork=fork, fixture_format=FixtureFormats.STATE_TEST - ) + state_test.generate(request=None, t8n=t8n, fork=fork, fixture_format=StateFixture) assert e_info.value == Account.NonceMismatch( address=ADDRESS_UNDER_TEST, want=post_nonce, got=pre_nonce ) @@ -181,14 +175,10 @@ def test_post_code_value_mismatch(pre: Alloc, post: Alloc, state_test, t8n, fork pre_code = pre_account.code post_code = post_account.code if "code" not in post_account.model_fields_set: # no exception - state_test.generate( - request=None, t8n=t8n, fork=fork, fixture_format=FixtureFormats.STATE_TEST - ) + state_test.generate(request=None, t8n=t8n, fork=fork, fixture_format=StateFixture) return with pytest.raises(Account.CodeMismatch) as e_info: - state_test.generate( - request=None, t8n=t8n, fork=fork, fixture_format=FixtureFormats.STATE_TEST - ) + state_test.generate(request=None, t8n=t8n, fork=fork, fixture_format=StateFixture) assert e_info.value == Account.CodeMismatch( address=ADDRESS_UNDER_TEST, want=post_code, got=pre_code ) @@ -217,14 +207,10 @@ def test_post_balance_value_mismatch(pre: Alloc, post: Alloc, state_test, t8n, f pre_balance = pre_account.balance post_balance = post_account.balance if "balance" not in post_account.model_fields_set: # no exception - state_test.generate( - request=None, t8n=t8n, fork=fork, fixture_format=FixtureFormats.STATE_TEST - ) + state_test.generate(request=None, t8n=t8n, fork=fork, fixture_format=StateFixture) return with pytest.raises(Account.BalanceMismatch) as e_info: - state_test.generate( - request=None, t8n=t8n, fork=fork, fixture_format=FixtureFormats.STATE_TEST - ) + state_test.generate(request=None, t8n=t8n, fork=fork, fixture_format=StateFixture) assert e_info.value == Account.BalanceMismatch( address=ADDRESS_UNDER_TEST, want=post_balance, got=pre_balance ) @@ -264,11 +250,7 @@ def test_post_account_mismatch(state_test, t8n, fork, exception_type: Type[Excep fixture generation. """ if exception_type is None: - state_test.generate( - request=None, t8n=t8n, fork=fork, fixture_format=FixtureFormats.STATE_TEST - ) + state_test.generate(request=None, t8n=t8n, fork=fork, fixture_format=StateFixture) return with pytest.raises(exception_type) as _: - state_test.generate( - request=None, t8n=t8n, fork=fork, fixture_format=FixtureFormats.STATE_TEST - ) + state_test.generate(request=None, t8n=t8n, fork=fork, fixture_format=StateFixture) diff --git a/src/ethereum_test_specs/tests/test_fixtures.py b/src/ethereum_test_specs/tests/test_fixtures.py index d31f83816d..de2b1d9cde 100644 --- a/src/ethereum_test_specs/tests/test_fixtures.py +++ b/src/ethereum_test_specs/tests/test_fixtures.py @@ -12,7 +12,12 @@ import cli.check_fixtures from ethereum_test_base_types import Account, Address, Hash from ethereum_test_exceptions import TransactionException -from ethereum_test_fixtures import BlockchainFixture, FixtureFormats +from ethereum_test_fixtures import ( + BlockchainEngineFixture, + BlockchainFixture, + FixtureFormat, + StateFixture, +) from ethereum_test_fixtures.blockchain import FixtureCommon from ethereum_test_forks import Berlin, Fork, Istanbul, London, Paris, Shanghai from ethereum_test_types import Alloc, Environment, Transaction @@ -96,7 +101,7 @@ def test_make_genesis(fork: Fork, hash: bytes): # noqa: D103 request=None, # type: ignore t8n=t8n, fork=fork, - fixture_format=FixtureFormats.BLOCKCHAIN_TEST, + fixture_format=BlockchainFixture, ) assert isinstance(fixture, BlockchainFixture) assert fixture.genesis is not None @@ -109,17 +114,17 @@ def test_make_genesis(fork: Fork, hash: bytes): # noqa: D103 @pytest.mark.parametrize( "fork,fixture_format", [ - (Istanbul, FixtureFormats.BLOCKCHAIN_TEST), - (London, FixtureFormats.BLOCKCHAIN_TEST), - (Paris, FixtureFormats.BLOCKCHAIN_TEST_ENGINE), - (Shanghai, FixtureFormats.BLOCKCHAIN_TEST_ENGINE), - (Paris, FixtureFormats.STATE_TEST), - (Shanghai, FixtureFormats.STATE_TEST), + (Istanbul, BlockchainFixture), + (London, BlockchainFixture), + (Paris, BlockchainEngineFixture), + (Shanghai, BlockchainEngineFixture), + (Paris, StateFixture), + (Shanghai, StateFixture), ], ) def test_fill_state_test( fork: Fork, - fixture_format: FixtureFormats, + fixture_format: FixtureFormat, ): """ Test `ethereum_test.filler.fill_fixtures` with `StateTest`. @@ -166,12 +171,12 @@ def test_fill_state_test( fork=fork, fixture_format=fixture_format, ) - assert generated_fixture.format == fixture_format + assert generated_fixture.__class__ == fixture_format fixture = { f"000/my_chain_id_test/{fork}": generated_fixture.json_dict_with_info(hash_only=True), } - expected_json_file = f"chainid_{fork.name().lower()}_{fixture_format.value}.json" + expected_json_file = f"chainid_{fork.name().lower()}_{fixture_format.fixture_format_name}.json" with open( os.path.join( "src", @@ -471,9 +476,7 @@ def genesis_environment(self): # noqa: D102 @pytest.fixture def fixture_format(self, check_hive: bool): # noqa: D102 - return ( - FixtureFormats.BLOCKCHAIN_TEST_ENGINE if check_hive else FixtureFormats.BLOCKCHAIN_TEST - ) + return BlockchainEngineFixture if check_hive else BlockchainFixture @pytest.fixture def blockchain_test_fixture( # noqa: D102 @@ -484,7 +487,7 @@ def blockchain_test_fixture( # noqa: D102 post: Mapping[Any, Any], blocks: List[Block], genesis_environment: Environment, - fixture_format: FixtureFormats, + fixture_format: FixtureFormat, ): t8n = ExecutionSpecsTransitionTool() return BlockchainTest( @@ -506,11 +509,11 @@ def test_fill_blockchain_valid_txs( # noqa: D102 self, fork: Fork, check_hive: bool, - fixture_format: FixtureFormats, + fixture_format: FixtureFormat, expected_json_file: str, blockchain_test_fixture: BlockchainFixture, ): - assert blockchain_test_fixture.format == fixture_format + assert blockchain_test_fixture.__class__ == fixture_format assert isinstance(blockchain_test_fixture, FixtureCommon) fixture_name = f"000/my_blockchain_test/{fork.name()}" @@ -870,8 +873,8 @@ def test_fill_blockchain_invalid_txs(fork: Fork, check_hive: bool, expected_json ) t8n = ExecutionSpecsTransitionTool() - fixture_format = ( - FixtureFormats.BLOCKCHAIN_TEST_ENGINE if check_hive else FixtureFormats.BLOCKCHAIN_TEST + fixture_format: FixtureFormat = ( + BlockchainEngineFixture if check_hive else BlockchainFixture # type: ignore ) generated_fixture = BlockchainTest( pre=pre, @@ -884,7 +887,7 @@ def test_fill_blockchain_invalid_txs(fork: Fork, check_hive: bool, expected_json fork=fork, fixture_format=fixture_format, ) - assert generated_fixture.format == fixture_format + assert generated_fixture.__class__ == fixture_format assert isinstance(generated_fixture, FixtureCommon) fixture_name = f"000/my_blockchain_test/{fork.name()}" diff --git a/src/ethereum_test_tools/__init__.py b/src/ethereum_test_tools/__init__.py index 33720ece20..54bff9d1fc 100644 --- a/src/ethereum_test_tools/__init__.py +++ b/src/ethereum_test_tools/__init__.py @@ -56,6 +56,7 @@ copy_opcode_cost, cost_memory_bytes, eip_2028_transaction_data_cost, + keccak256, ) from ethereum_test_vm import ( Bytecode, @@ -151,5 +152,6 @@ "eip_2028_transaction_data_cost", "eip_2028_transaction_data_cost", "extend_with_defaults", + "keccak256", "vm", ) diff --git a/src/ethereum_test_tools/tests/test_code.py b/src/ethereum_test_tools/tests/test_code.py index 71eb614f0c..18fd71bec2 100644 --- a/src/ethereum_test_tools/tests/test_code.py +++ b/src/ethereum_test_tools/tests/test_code.py @@ -9,7 +9,7 @@ from semver import Version from ethereum_test_base_types import Account, Address, Bytes, Hash, TestAddress, TestPrivateKey -from ethereum_test_fixtures import FixtureFormats +from ethereum_test_fixtures import BlockchainFixture from ethereum_test_forks import ( Cancun, Fork, @@ -640,7 +640,7 @@ def test_switch(tx_data: bytes, switch_bytecode: bytes, expected_storage: Mappin request=None, # type: ignore t8n=ExecutionSpecsTransitionTool(), fork=Cancun, - fixture_format=FixtureFormats.BLOCKCHAIN_TEST, + fixture_format=BlockchainFixture, eips=None, ) diff --git a/src/ethereum_test_types/__init__.py b/src/ethereum_test_types/__init__.py index b8b0cf5aba..028313de33 100644 --- a/src/ethereum_test_types/__init__.py +++ b/src/ethereum_test_types/__init__.py @@ -29,6 +29,7 @@ Transaction, Withdrawal, WithdrawalRequest, + keccak256, ) __all__ = ( @@ -64,5 +65,6 @@ "copy_opcode_cost", "cost_memory_bytes", "eip_2028_transaction_data_cost", + "keccak256", "to_json", ) diff --git a/src/ethereum_test_types/helpers.py b/src/ethereum_test_types/helpers.py index fda50ed820..66cf0fe09d 100644 --- a/src/ethereum_test_types/helpers.py +++ b/src/ethereum_test_types/helpers.py @@ -5,7 +5,6 @@ from dataclasses import MISSING, dataclass, fields from typing import List, SupportsBytes -from ethereum.crypto.hash import keccak256 from ethereum.rlp import encode from ethereum_test_base_types.base_types import Address, Bytes, Hash @@ -48,7 +47,7 @@ def compute_create_address( if nonce is None: nonce = 0 nonce_bytes = bytes() if nonce == 0 else nonce.to_bytes(length=1, byteorder="big") - hash = keccak256(encode([address, nonce_bytes])) + hash = Bytes(encode([address, nonce_bytes])).keccak256() return Address(hash[-20:]) if opcode == Op.CREATE2: return compute_create2_address(address, salt, initcode) @@ -62,7 +61,7 @@ def compute_create2_address( Compute address of the resulting contract created using the `CREATE2` opcode. """ - hash = keccak256(b"\xff" + Address(address) + Hash(salt) + keccak256(Bytes(initcode))) + hash = Bytes(b"\xff" + Address(address) + Hash(salt) + Bytes(initcode).keccak256()).keccak256() return Address(hash[-20:]) @@ -100,7 +99,9 @@ def compute_eofcreate_address( """ Compute address of the resulting contract created using the `EOFCREATE` opcode. """ - hash = keccak256(b"\xff" + Address(address) + Hash(salt) + keccak256(Bytes(init_container))) + hash = Bytes( + b"\xff" + Address(address) + Hash(salt) + Bytes(init_container).keccak256() + ).keccak256() return Address(hash[-20:]) diff --git a/src/ethereum_test_types/tests/test_transactions.py b/src/ethereum_test_types/tests/test_transactions.py index d830e1fde5..bbe61b476c 100644 --- a/src/ethereum_test_types/tests/test_transactions.py +++ b/src/ethereum_test_types/tests/test_transactions.py @@ -260,4 +260,4 @@ def test_transaction_signing( assert signature == expected_signature assert tx.sender is not None assert tx.sender.hex() == expected_sender - assert ("0x" + tx.rlp.hex()) == expected_serialized + assert (tx.rlp.hex()) == expected_serialized diff --git a/src/ethereum_test_types/types.py b/src/ethereum_test_types/types.py index 66bbff841f..543b361201 100644 --- a/src/ethereum_test_types/types.py +++ b/src/ethereum_test_types/types.py @@ -8,11 +8,10 @@ from coincurve.keys import PrivateKey, PublicKey from ethereum import rlp as eth_rlp -from ethereum.base_types import U256, Uint -from ethereum.crypto.hash import keccak256 from ethereum.frontier.fork_types import Account as FrontierAccount from ethereum.frontier.fork_types import Address as FrontierAddress from ethereum.frontier.state import State, set_account, set_storage, state_root +from ethereum_types.numeric import U256, Uint from pydantic import ( BaseModel, ConfigDict, @@ -50,6 +49,13 @@ from ethereum_test_vm import EVMCodeType +def keccak256(data: bytes) -> Hash: + """ + Calculates the keccak256 hash of the given data. + """ + return Bytes(data).keccak256() + + # Sentinel classes class Removable: """ @@ -494,16 +500,19 @@ def to_list(self) -> List[Any]: ] @cached_property - def signing_bytes(self) -> bytes: + def signing_bytes(self) -> Bytes: """ Returns the data to be signed. """ - return int.to_bytes(self.magic, length=1, byteorder="big") + eth_rlp.encode( - [ - Uint(self.chain_id), - self.address, - Uint(self.nonce), - ] + return Bytes( + int.to_bytes(self.magic, length=1, byteorder="big") + + eth_rlp.encode( + [ + Uint(self.chain_id), + self.address, + Uint(self.nonce), + ] + ) ) def signature(self, private_key: Hash) -> Tuple[int, int, int]: @@ -552,7 +561,7 @@ def model_post_init(self, __context: Any) -> None: + bytes([self.v]) ) public_key = PublicKey.from_signature_and_message( - signature_bytes, keccak256(self.signing_bytes), hasher=None + signature_bytes, self.signing_bytes.keccak256(), hasher=None ) self.signer = EOA( address=Address(keccak256(public_key.format(compressed=False)[1:])[32 - 20 :]) @@ -668,7 +677,7 @@ class Transaction(TransactionGeneric[HexNumber], TransactionTransitionToolConver error: List[TransactionException] | TransactionException | None = Field(None, exclude=True) protected: bool = Field(True, exclude=True) - rlp_override: bytes | None = Field(None, exclude=True) + rlp_override: Bytes | None = Field(None, exclude=True) wrapped_blob_transaction: bool = Field(False, exclude=True) blobs: Sequence[Bytes] | None = Field(None, exclude=True) @@ -788,7 +797,7 @@ def with_signature_and_sender(self, *, keep_secret_key: bool = False) -> "Transa return self public_key = PublicKey.from_signature_and_message( - self.signature_bytes, keccak256(self.signing_bytes), hasher=None + self.signature_bytes, self.signing_bytes.keccak256(), hasher=None ) updated_values["sender"] = Address( keccak256(public_key.format(compressed=False)[1:])[32 - 20 :] @@ -799,7 +808,7 @@ def with_signature_and_sender(self, *, keep_secret_key: bool = False) -> "Transa raise ValueError("secret_key must be set to sign a transaction") # Get the signing bytes - signing_hash = keccak256(self.signing_bytes) + signing_hash = self.signing_bytes.keccak256() # Sign the bytes signature_bytes = PrivateKey(secret=self.secret_key).sign_recoverable( @@ -984,7 +993,7 @@ def payload_body(self) -> List[Any]: return signing_envelope + [Uint(self.v), Uint(self.r), Uint(self.s)] @cached_property - def rlp(self) -> bytes: + def rlp(self) -> Bytes: """ Returns bytes of the serialized representation of the transaction, which is almost always RLP encoding. @@ -992,30 +1001,30 @@ def rlp(self) -> bytes: if self.rlp_override is not None: return self.rlp_override if self.ty > 0: - return bytes([self.ty]) + eth_rlp.encode(self.payload_body) + return Bytes(bytes([self.ty]) + eth_rlp.encode(self.payload_body)) else: - return eth_rlp.encode(self.payload_body) + return Bytes(eth_rlp.encode(self.payload_body)) @cached_property def hash(self) -> Hash: """ Returns hash of the transaction. """ - return Hash(keccak256(self.rlp)) + return self.rlp.keccak256() @cached_property - def signing_bytes(self) -> bytes: + def signing_bytes(self) -> Bytes: """ Returns the serialized bytes of the transaction used for signing. """ - return ( + return Bytes( bytes([self.ty]) + eth_rlp.encode(self.signing_envelope) if self.ty > 0 else eth_rlp.encode(self.signing_envelope) ) @cached_property - def signature_bytes(self) -> bytes: + def signature_bytes(self) -> Bytes: """ Returns the serialized bytes of the transaction signature. """ @@ -1027,7 +1036,7 @@ def signature_bytes(self) -> bytes: v -= 35 + (self.chain_id * 2) else: v -= 27 - return ( + return Bytes( self.r.to_bytes(32, byteorder="big") + self.s.to_bytes(32, byteorder="big") + bytes([v]) @@ -1074,7 +1083,7 @@ def created_contract(self) -> Address: ) if self.sender is None: raise ValueError("sender address is None") - hash = keccak256(eth_rlp.encode([self.sender, nonce_bytes])) + hash = Bytes(eth_rlp.encode([self.sender, nonce_bytes])).keccak256() return Address(hash[-20:]) diff --git a/src/ethereum_test_vm/bytecode.py b/src/ethereum_test_vm/bytecode.py index a9907df601..80055da92a 100644 --- a/src/ethereum_test_vm/bytecode.py +++ b/src/ethereum_test_vm/bytecode.py @@ -3,7 +3,7 @@ """ from typing import SupportsBytes -from ethereum.crypto.hash import keccak256 +from ethereum_test_base_types import Bytes, Hash class Bytecode: @@ -216,8 +216,8 @@ def hex(self) -> str: """ return bytes(self).hex() - def keccak256(self) -> bytes: + def keccak256(self) -> Hash: """ Return the keccak256 hash of the opcode byte representation. """ - return keccak256(self._bytes_) + return Bytes(self._bytes_).keccak256() diff --git a/src/evm_transition_tool/geth.py b/src/evm_transition_tool/geth.py index 02c4d86438..700d3a43d5 100644 --- a/src/evm_transition_tool/geth.py +++ b/src/evm_transition_tool/geth.py @@ -10,9 +10,10 @@ from re import compile from typing import Optional +from ethereum_test_fixtures import BlockchainFixture, StateFixture from ethereum_test_forks import Fork -from .transition_tool import FixtureFormats, TransitionTool, dump_files_to_directory +from .transition_tool import FixtureFormat, TransitionTool, dump_files_to_directory class GethTransitionTool(TransitionTool): @@ -67,9 +68,18 @@ def get_blocktest_help(self) -> str: raise Exception(f"Unexpected exception calling evm tool: {e}.") return result.stdout + def is_verifiable( + self, + fixture_format: FixtureFormat, + ) -> bool: + """ + Returns whether the fixture format is verifiable by this Geth's evm tool. + """ + return fixture_format in {StateFixture, BlockchainFixture} + def verify_fixture( self, - fixture_format: FixtureFormats, + fixture_format: FixtureFormat, fixture_path: Path, fixture_name: Optional[str] = None, debug_output_path: Optional[Path] = None, @@ -82,16 +92,16 @@ def verify_fixture( if debug_output_path: command += ["--debug", "--json", "--verbosity", "100"] - if FixtureFormats.is_state_test(fixture_format): + if fixture_format == StateFixture: assert self.statetest_subcommand, "statetest subcommand not set" command.append(self.statetest_subcommand) - elif FixtureFormats.is_blockchain_test(fixture_format): + elif fixture_format == BlockchainFixture: assert self.blocktest_subcommand, "blocktest subcommand not set" command.append(self.blocktest_subcommand) else: raise Exception(f"Invalid test fixture format: {fixture_format}") - if fixture_name and fixture_format == FixtureFormats.BLOCKCHAIN_TEST: + if fixture_name and fixture_format == BlockchainFixture: assert isinstance(fixture_name, str), "fixture_name must be a string" command.append("--run") command.append(fixture_name) @@ -130,7 +140,7 @@ def verify_fixture( f"EVM test failed.\n{' '.join(command)}\n\n Error:\n{result.stderr.decode()}" ) - if FixtureFormats.is_state_test(fixture_format): + if fixture_format == StateFixture: result_json = json.loads(result.stdout.decode()) if not isinstance(result_json, list): raise Exception(f"Unexpected result from evm statetest: {result_json}") diff --git a/src/evm_transition_tool/transition_tool.py b/src/evm_transition_tool/transition_tool.py index 863c9d15dc..b86b67fe2f 100644 --- a/src/evm_transition_tool/transition_tool.py +++ b/src/evm_transition_tool/transition_tool.py @@ -17,7 +17,7 @@ from requests_unixsocket import Session # type: ignore -from ethereum_test_fixtures import FixtureFormats, FixtureVerifier +from ethereum_test_fixtures import FixtureFormat, FixtureVerifier from ethereum_test_forks import Fork from ethereum_test_types import Alloc, Environment, Transaction @@ -616,7 +616,7 @@ def evaluate( def verify_fixture( self, - fixture_format: FixtureFormats, + fixture_format: FixtureFormat, fixture_path: Path, fixture_name: Optional[str] = None, debug_output_path: Optional[Path] = None, diff --git a/src/pytest_plugins/consume/consume.py b/src/pytest_plugins/consume/consume.py index 02999206f3..1a301ac4e8 100644 --- a/src/pytest_plugins/consume/consume.py +++ b/src/pytest_plugins/consume/consume.py @@ -191,7 +191,7 @@ def pytest_generate_tests(metafunc): ( pytest.param(test_case, id=test_case.id) for test_case in metafunc.config.test_cases - if test_case.format in metafunc.function.fixture_formats + if test_case.format in metafunc.function.fixture_format and (not fork or test_case.fork == fork) ), ) diff --git a/src/pytest_plugins/consume/decorator.py b/src/pytest_plugins/consume/decorator.py index 43b9641813..21f3c456e8 100644 --- a/src/pytest_plugins/consume/decorator.py +++ b/src/pytest_plugins/consume/decorator.py @@ -2,16 +2,16 @@ Consume test function decorator used to mark properties of a test function. """ -from ethereum_test_fixtures import FixtureFormats +from ethereum_test_fixtures import FixtureFormat -def fixture_format(*formats: FixtureFormats): +def fixture_format(*formats: FixtureFormat): """ Mark a test function as a test that consumes a specific fixture format. """ def decorator(func): - func.fixture_formats = formats + func.fixture_format = formats return func return decorator diff --git a/src/pytest_plugins/consume/direct/test_via_direct.py b/src/pytest_plugins/consume/direct/test_via_direct.py index 4e1f92ef59..0bdfe13731 100644 --- a/src/pytest_plugins/consume/direct/test_via_direct.py +++ b/src/pytest_plugins/consume/direct/test_via_direct.py @@ -9,7 +9,7 @@ import pytest -from ethereum_test_fixtures import FixtureFormats +from ethereum_test_fixtures import BlockchainFixture, StateFixture from ethereum_test_fixtures.consume import TestCaseIndexFile, TestCaseStream from evm_transition_tool import TransitionTool @@ -18,7 +18,7 @@ statetest_results: dict[Path, List[dict[str, Any]]] = {} -@fixture_format(FixtureFormats.BLOCKCHAIN_TEST) +@fixture_format(BlockchainFixture) def test_blocktest( # noqa: D103 test_case: TestCaseIndexFile | TestCaseStream, evm: TransitionTool, @@ -60,7 +60,7 @@ def run_statetest( @pytest.mark.usefixtures("run_statetest") -@fixture_format(FixtureFormats.STATE_TEST) +@fixture_format(StateFixture) def test_statetest( # noqa: D103 test_case: TestCaseIndexFile | TestCaseStream, fixture_path: Path, diff --git a/src/pytest_plugins/consume/hive_simulators/engine/test_via_engine.py b/src/pytest_plugins/consume/hive_simulators/engine/test_via_engine.py index df3ae5b9be..6871e7dafb 100644 --- a/src/pytest_plugins/consume/hive_simulators/engine/test_via_engine.py +++ b/src/pytest_plugins/consume/hive_simulators/engine/test_via_engine.py @@ -5,7 +5,7 @@ Each `engine_newPayloadVX` is verified against the appropriate VALID/INVALID responses. """ -from ethereum_test_fixtures import BlockchainEngineFixture, FixtureFormats +from ethereum_test_fixtures import BlockchainEngineFixture from ethereum_test_rpc import EngineRPC, EthRPC from ethereum_test_rpc.types import ForkchoiceState, PayloadStatusEnum from pytest_plugins.consume.hive_simulators.exceptions import GenesisBlockMismatchException @@ -14,7 +14,7 @@ from ..timing import TimingData -@fixture_format(FixtureFormats.BLOCKCHAIN_TEST_ENGINE) +@fixture_format(BlockchainEngineFixture) def test_via_engine( timing_data: TimingData, eth_rpc: EthRPC, diff --git a/src/pytest_plugins/consume/hive_simulators/rlp/test_via_rlp.py b/src/pytest_plugins/consume/hive_simulators/rlp/test_via_rlp.py index 6df6dd781e..e3dfdbe9d5 100644 --- a/src/pytest_plugins/consume/hive_simulators/rlp/test_via_rlp.py +++ b/src/pytest_plugins/consume/hive_simulators/rlp/test_via_rlp.py @@ -5,7 +5,7 @@ Clients consume the genesis and RLP-encoded blocks from input files upon start-up. """ -from ethereum_test_fixtures import BlockchainFixture, FixtureFormats +from ethereum_test_fixtures import BlockchainFixture from ethereum_test_rpc import EthRPC from pytest_plugins.consume.hive_simulators.exceptions import GenesisBlockMismatchException @@ -13,7 +13,7 @@ from ..timing import TimingData -@fixture_format(FixtureFormats.BLOCKCHAIN_TEST) +@fixture_format(BlockchainFixture) def test_via_rlp( timing_data: TimingData, eth_rpc: EthRPC, diff --git a/src/pytest_plugins/filler/filler.py b/src/pytest_plugins/filler/filler.py index 180ff55a20..010dac4e85 100644 --- a/src/pytest_plugins/filler/filler.py +++ b/src/pytest_plugins/filler/filler.py @@ -11,7 +11,7 @@ import tarfile import warnings from pathlib import Path -from typing import Generator, List, Type +from typing import Any, Dict, Generator, List, Type import pytest from filelock import FileLock @@ -19,10 +19,9 @@ from cli.gen_index import generate_fixtures_index from ethereum_test_base_types import Alloc, ReferenceSpec -from ethereum_test_fixtures import FixtureCollector, FixtureFormats, TestInfo +from ethereum_test_fixtures import FIXTURE_FORMATS, BaseFixture, FixtureCollector, TestInfo from ethereum_test_forks import ( Fork, - Paris, get_closest_fork_with_solc_support, get_forks_with_solc_support, ) @@ -207,13 +206,10 @@ def pytest_configure(config): called before the pytest-html plugin's pytest_configure to ensure that it uses the modified `htmlpath` option. """ - for fixture_format in FixtureFormats: + for fixture_format in FIXTURE_FORMATS.values(): config.addinivalue_line( "markers", - ( - f"{fixture_format.name.lower()}: " - f"{FixtureFormats.get_format_description(fixture_format)}" - ), + (f"{fixture_format.fixture_format_name.lower()}: {fixture_format.description}"), ) config.addinivalue_line( "markers", @@ -798,7 +794,7 @@ def base_test_parametrizer_func( When parametrize, indirect must be used along with the fixture format as value. """ fixture_format = request.param - assert isinstance(fixture_format, FixtureFormats) + assert issubclass(fixture_format, BaseFixture) class BaseTestWrapper(cls): # type: ignore def __init__(self, *args, **kwargs): @@ -830,7 +826,7 @@ def __init__(self, *args, **kwargs): request.node.config.fixture_path_relative = str( fixture_path.relative_to(output_dir) ) - request.node.config.fixture_format = fixture_format.value + request.node.config.fixture_format = fixture_format.fixture_format_name return BaseTestWrapper @@ -855,8 +851,8 @@ def pytest_generate_tests(metafunc: pytest.Metafunc): [ pytest.param( fixture_format, - id=fixture_format.name.lower(), - marks=[getattr(pytest.mark, fixture_format.name.lower())], + id=fixture_format.fixture_format_name.lower(), + marks=[getattr(pytest.mark, fixture_format.fixture_format_name.lower())], ) for fixture_format in test_type.supported_fixture_formats ], @@ -865,9 +861,9 @@ def pytest_generate_tests(metafunc: pytest.Metafunc): ) -def pytest_collection_modifyitems(config, items): +def pytest_collection_modifyitems(config: pytest.Config, items: List[pytest.Item]): """ - Remove pre-Paris tests parametrized to generate engine type fixtures; these + Remove pre-Paris tests parametrized to generate hive type fixtures; these can't be used in the Hive Pyspec Simulator. This can't be handled in this plugins pytest_generate_tests() as the fork @@ -876,23 +872,16 @@ def pytest_collection_modifyitems(config, items): for item in items[:]: # use a copy of the list, as we'll be modifying it if isinstance(item, EIPSpecTestItem): continue - if "fork" not in item.callspec.params or item.callspec.params["fork"] is None: + params: Dict[str, Any] = item.callspec.params # type: ignore + if "fork" not in params or params["fork"] is None: items.remove(item) continue - if item.callspec.params["fork"] < Paris: - # Even though the `state_test` test spec does not produce an engine STATE_TEST, it does - # produce a BLOCKCHAIN_TEST_ENGINE, so we need to remove it here. - # TODO: Ideally, the logic could be contained in the `FixtureFormat` class, we create - # a `fork_supported` method that returns True if the fork is supported. - if ("state_test" in item.callspec.params) and item.callspec.params[ - "state_test" - ].name.endswith("ENGINE"): + fork: Fork = params["fork"] + for spec_name in [spec_type.pytest_parameter_name() for spec_type in SPEC_TYPES]: + if spec_name in params and not params[spec_name].supports_fork(fork): items.remove(item) - if ("blockchain_test" in item.callspec.params) and item.callspec.params[ - "blockchain_test" - ].name.endswith("ENGINE"): - items.remove(item) - if "yul" in item.fixturenames: + break + if "yul" in item.fixturenames: # type: ignore item.add_marker(pytest.mark.yul_test) diff --git a/src/pytest_plugins/filler/gen_test_doc/gen_test_doc.py b/src/pytest_plugins/filler/gen_test_doc/gen_test_doc.py index 3d9d0c95bf..f70e1938fe 100644 --- a/src/pytest_plugins/filler/gen_test_doc/gen_test_doc.py +++ b/src/pytest_plugins/filler/gen_test_doc/gen_test_doc.py @@ -388,16 +388,20 @@ def create_function_page_props(self, test_functions: Dict["str", List[Item]]) -> values = [param_set[key] for key in keys] # TODO: This formatting of bytes objects should be moved elsewhere values = [ - " ".join( - f"{chunk}" for chunk in textwrap.wrap(value.hex(), 32) + ( + " ".join( + f"{chunk}" + for chunk in textwrap.wrap(value.hex(), 32) + ) + if isinstance(value, bytes) + else str(value) ) - if isinstance(value, bytes) - else str(value) for value in values ] fork = item.callspec.params.get("fork").name() # type: ignore test_type = get_test_function_test_type(item) - fixture_type = item.callspec.params.get(test_type).name # type: ignore + test_type_value = item.callspec.params.get(test_type) + fixture_type = test_type_value.fixture_format_name # type: ignore test_cases.append( TestCase( full_id=item.nodeid, diff --git a/src/pytest_plugins/forks/tests/test_forks.py b/src/pytest_plugins/forks/tests/test_forks.py index 29911a47f6..46b0e4ebed 100644 --- a/src/pytest_plugins/forks/tests/test_forks.py +++ b/src/pytest_plugins/forks/tests/test_forks.py @@ -4,13 +4,7 @@ import pytest -from ethereum_test_forks import ( - ArrowGlacier, - Paris, - forks_from_until, - get_deployed_forks, - get_forks, -) +from ethereum_test_forks import ArrowGlacier, forks_from_until, get_deployed_forks, get_forks from ethereum_test_tools import StateTest @@ -47,11 +41,17 @@ def test_all_forks({StateTest.pytest_parameter_name()}): stdout = "\n".join(result.stdout.lines) for fork in forks_under_test: for fixture_format in StateTest.supported_fixture_formats: - if fixture_format.name.endswith("ENGINE") and fork < Paris: + if not fixture_format.supports_fork(fork): expected_passed -= 1 - assert f":test_all_forks[fork_{fork}-{fixture_format.name.lower()}]" not in stdout + assert ( + f":test_all_forks[fork_{fork}-{fixture_format.fixture_format_name.lower()}]" + not in stdout + ) continue - assert f":test_all_forks[fork_{fork}-{fixture_format.name.lower()}]" in stdout + assert ( + f":test_all_forks[fork_{fork}-{fixture_format.fixture_format_name.lower()}]" + in stdout + ) result.assert_outcomes( passed=expected_passed, @@ -85,11 +85,17 @@ def test_all_forks({StateTest.pytest_parameter_name()}): stdout = "\n".join(result.stdout.lines) for fork in forks_under_test: for fixture_format in StateTest.supported_fixture_formats: - if fixture_format.name.endswith("ENGINE") and fork < Paris: + if not fixture_format.supports_fork(fork): expected_passed -= 1 - assert f":test_all_forks[fork_{fork}-{fixture_format.name.lower()}]" not in stdout + assert ( + f":test_all_forks[fork_{fork}-{fixture_format.fixture_format_name.lower()}]" + not in stdout + ) continue - assert f":test_all_forks[fork_{fork}-{fixture_format.name.lower()}]" in stdout + assert ( + f":test_all_forks[fork_{fork}-{fixture_format.fixture_format_name.lower()}]" + in stdout + ) result.assert_outcomes( passed=expected_passed, failed=0, @@ -123,11 +129,17 @@ def test_all_forks({StateTest.pytest_parameter_name()}): expected_passed -= len(StateTest.supported_fixture_formats) for fork in forks_under_test: for fixture_format in StateTest.supported_fixture_formats: - if fixture_format.name.endswith("ENGINE") and fork < Paris: + if not fixture_format.supports_fork(fork): expected_passed -= 1 - assert f":test_all_forks[fork_{fork}-{fixture_format.name.lower()}]" not in stdout + assert ( + f":test_all_forks[fork_{fork}-{fixture_format.fixture_format_name.lower()}]" + not in stdout + ) continue - assert f":test_all_forks[fork_{fork}-{fixture_format.name.lower()}]" in stdout + assert ( + f":test_all_forks[fork_{fork}-{fixture_format.fixture_format_name.lower()}]" + in stdout + ) result.assert_outcomes( passed=expected_passed, failed=0, @@ -158,7 +170,10 @@ def test_all_forks({StateTest.pytest_parameter_name()}): stdout = "\n".join(result.stdout.lines) for fork in forks_under_test: for fixture_format in StateTest.supported_fixture_formats: - assert f":test_all_forks[fork_{fork}-{fixture_format.name.lower()}]" in stdout + assert ( + f":test_all_forks[fork_{fork}-{fixture_format.fixture_format_name.lower()}]" + in stdout + ) result.assert_outcomes( passed=expected_passed, failed=0, diff --git a/tests/cancun/eip4788_beacon_root/conftest.py b/tests/cancun/eip4788_beacon_root/conftest.py index 1c8d91581c..c286bff0d1 100644 --- a/tests/cancun/eip4788_beacon_root/conftest.py +++ b/tests/cancun/eip4788_beacon_root/conftest.py @@ -6,7 +6,6 @@ from typing import Dict, Iterator, List import pytest -from ethereum.crypto.hash import keccak256 from ethereum_test_tools import ( AccessList, @@ -19,6 +18,7 @@ Storage, Transaction, add_kzg_version, + keccak256, ) from ethereum_test_tools.vm.opcode import Opcodes as Op diff --git a/tests/cancun/eip5656_mcopy/test_mcopy.py b/tests/cancun/eip5656_mcopy/test_mcopy.py index 1e8ca50c19..74b980301d 100644 --- a/tests/cancun/eip5656_mcopy/test_mcopy.py +++ b/tests/cancun/eip5656_mcopy/test_mcopy.py @@ -3,14 +3,14 @@ Test copy operations of [EIP-5656: MCOPY - Memory copying instruction](https://eips.ethereum.org/EIPS/eip-5656) """ # noqa: E501 + from typing import Mapping import pytest -from ethereum.crypto.hash import keccak256 from ethereum_test_tools import Account, Address, Alloc, Bytecode, Environment, Hash from ethereum_test_tools import Opcodes as Op -from ethereum_test_tools import StateTestFiller, Storage, Transaction, ceiling_division +from ethereum_test_tools import StateTestFiller, Storage, Transaction, ceiling_division, keccak256 from .common import REFERENCE_SPEC_GIT_PATH, REFERENCE_SPEC_VERSION, mcopy diff --git a/tests/constantinople/eip1014_create2/test_create_returndata.py b/tests/constantinople/eip1014_create2/test_create_returndata.py index 753b103a43..c4eb9672d5 100644 --- a/tests/constantinople/eip1014_create2/test_create_returndata.py +++ b/tests/constantinople/eip1014_create2/test_create_returndata.py @@ -5,10 +5,10 @@ """ import pytest -from ethereum.crypto.hash import keccak256 -from ethereum_test_tools import Account, Alloc, Environment, StateTestFiller, Transaction -from ethereum_test_tools.vm.opcode import Opcodes as Op +from ethereum_test_tools import Account, Alloc, Environment +from ethereum_test_tools import Opcodes as Op +from ethereum_test_tools import StateTestFiller, Transaction, keccak256 from .spec import ref_spec_1014 @@ -102,20 +102,22 @@ def test_create2_return_data( slot_returndatacopy_before_create_2: 0, # # the actual bytes returned by returndatacopy opcode after create - slot_returndatacopy_after_create: return_data_in_create - if return_type_in_create == Op.REVERT - else 0, + slot_returndatacopy_after_create: ( + return_data_in_create if return_type_in_create == Op.REVERT else 0 + ), slot_returndatasize_before_create: call_return_size, # # return datasize value after create - slot_returndatasize_after_create: 0x20 - if return_type_in_create == Op.REVERT - else 0, + slot_returndatasize_after_create: ( + 0x20 if return_type_in_create == Op.REVERT else 0 + ), # slot_return_data_hash_before_create: keccak256(expected_call_return_data), - slot_return_data_hash_after_create: keccak256(empty_data) - if return_type_in_create == Op.RETURN - else keccak256(int.to_bytes(return_data_in_create, 32, byteorder="big")), + slot_return_data_hash_after_create: ( + keccak256(empty_data) + if return_type_in_create == Op.RETURN + else keccak256(int.to_bytes(return_data_in_create, 32, byteorder="big")) + ), # # check that create 2 didn't mess up with initial memory space declared for return slot_begin_memory_after_create: expected_returndatacopy, diff --git a/tests/prague/eip2537_bls_12_381_precompiles/conftest.py b/tests/prague/eip2537_bls_12_381_precompiles/conftest.py index 78b7244191..9dba8490ce 100644 --- a/tests/prague/eip2537_bls_12_381_precompiles/conftest.py +++ b/tests/prague/eip2537_bls_12_381_precompiles/conftest.py @@ -1,13 +1,14 @@ """ Shared pytest definitions local to EIP-2537 tests. """ + from typing import SupportsBytes import pytest -from ethereum.crypto.hash import keccak256 -from ethereum_test_tools import EOA, Address, Alloc, Bytecode, Storage, Transaction -from ethereum_test_tools.vm import Opcodes as Op +from ethereum_test_tools import EOA, Address, Alloc, Bytecode +from ethereum_test_tools import Opcodes as Op +from ethereum_test_tools import Storage, Transaction, keccak256 from .spec import GAS_CALCULATION_FUNCTION_MAP diff --git a/tests/prague/eip7692_eof_v1/eip3540_eof_v1/test_extcode.py b/tests/prague/eip7692_eof_v1/eip3540_eof_v1/test_extcode.py index 344edc3736..eb5dc99dda 100644 --- a/tests/prague/eip7692_eof_v1/eip3540_eof_v1/test_extcode.py +++ b/tests/prague/eip7692_eof_v1/eip3540_eof_v1/test_extcode.py @@ -1,12 +1,13 @@ """ test execution semantics changes """ + import pytest -from ethereum.crypto.hash import keccak256 -from ethereum_test_tools import Account, Alloc, Environment, StateTestFiller, Storage, Transaction +from ethereum_test_tools import Account, Alloc, Environment +from ethereum_test_tools import Opcodes as Op +from ethereum_test_tools import StateTestFiller, Storage, Transaction, keccak256 from ethereum_test_tools.eof.v1 import Container, Section -from ethereum_test_tools.vm.opcode import Opcodes as Op from .. import EOF_FORK_NAME diff --git a/tests/prague/eip7702_set_code_tx/test_set_code_txs.py b/tests/prague/eip7702_set_code_tx/test_set_code_txs.py index 91531ddc9a..1f84924ebd 100644 --- a/tests/prague/eip7702_set_code_tx/test_set_code_txs.py +++ b/tests/prague/eip7702_set_code_tx/test_set_code_txs.py @@ -9,7 +9,6 @@ from typing import List import pytest -from ethereum.crypto.hash import keccak256 from ethereum_test_tools import ( AccessList, @@ -38,6 +37,7 @@ add_kzg_version, call_return_code, compute_create_address, + keccak256, ) from ethereum_test_tools.eof.v1 import Container, Section @@ -1404,12 +1404,12 @@ def test_ext_code_on_chain_delegating_set_code( set_code_2 = Spec.delegation_designation(auth_signer_1) callee_storage[slot_ext_code_size_result_1] = len(set_code_2) - callee_storage[slot_ext_code_hash_result_1] = keccak256(set_code_2) + callee_storage[slot_ext_code_hash_result_1] = set_code_2.keccak256() callee_storage[slot_ext_code_copy_result_1] = bytes(set_code_2).ljust(32, b"\x00")[:32] callee_storage[slot_ext_balance_result_1] = auth_signer_1_balance callee_storage[slot_ext_code_size_result_2] = len(set_code_1) - callee_storage[slot_ext_code_hash_result_2] = keccak256(set_code_1) + callee_storage[slot_ext_code_hash_result_2] = set_code_1.keccak256() callee_storage[slot_ext_code_copy_result_2] = bytes(set_code_1).ljust(32, b"\x00")[:32] callee_storage[slot_ext_balance_result_2] = auth_signer_2_balance diff --git a/tox.ini b/tox.ini index d8de391c66..7f6dbb6688 100644 --- a/tox.ini +++ b/tox.ini @@ -9,6 +9,7 @@ develop = Prague eip7692 = CancunEIP7692 [testenv] +runner = uv-venv-lock-runner package = wheel wheel_build_env = .pkg diff --git a/uv.lock b/uv.lock index 1aa15d67cc..7e4f655292 100644 --- a/uv.lock +++ b/uv.lock @@ -498,9 +498,10 @@ wheels = [ [[package]] name = "ethereum" version = "0.1.0" -source = { git = "https://github.com/ethereum/execution-specs#51fac24740e662844446439ceeb96a460aae0ba0" } +source = { git = "https://github.com/ethereum/execution-specs#9b95554a88d2a8485f8180254d0f6a493a593fda" } dependencies = [ { name = "coincurve" }, + { name = "ethereum-types" }, { name = "py-ecc" }, { name = "pycryptodome" }, { name = "typing-extensions" }, @@ -517,6 +518,7 @@ dependencies = [ { name = "colorlog" }, { name = "ethereum" }, { name = "ethereum-spec-evm-resolver" }, + { name = "ethereum-types" }, { name = "filelock" }, { name = "gitpython" }, { name = "hive-py" }, @@ -578,6 +580,7 @@ requires-dist = [ { name = "colorlog", specifier = ">=6.7.0,<7" }, { name = "ethereum", git = "https://github.com/ethereum/execution-specs" }, { name = "ethereum-spec-evm-resolver", git = "https://github.com/petertdavies/ethereum-spec-evm-resolver" }, + { name = "ethereum-types", specifier = ">=0.2.1,<0.3" }, { name = "filelock", specifier = ">=3.15.1,<4" }, { name = "flake8", marker = "extra == 'lint'", specifier = ">=6.1.0,<7" }, { name = "flake8-docstrings", marker = "extra == 'lint'", specifier = ">=1.6,<2" }, @@ -635,6 +638,18 @@ dependencies = [ { name = "urllib3" }, ] +[[package]] +name = "ethereum-types" +version = "0.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/07/a8/053c5fcc8581fe599a87b86431a50b7df5b84a88242a6ba33ab1138a2a34/ethereum_types-0.2.1.tar.gz", hash = "sha256:2b53c5f08aff52004d7a49cdb09e70163482b55a41e274a95a8d27d194c5d146", size = 14935 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/76/3bbd13a9637287388f23c0c4094c81adf2d1467e314f8974ea0c61d2fc94/ethereum_types-0.2.1-py3-none-any.whl", hash = "sha256:6f6c51b239c231c6e33c10451b8fffb2fc168c6a7f0975cac3893d7e38e9c3e9", size = 10288 }, +] + [[package]] name = "exceptiongroup" version = "1.2.2"