From 4c951732a4462cdafa7ce647faddc2f33eab7588 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Wed, 9 Oct 2024 21:24:01 +0000 Subject: [PATCH 1/6] fix(base_types): Allow `Bytes()`, add `sha256` method --- src/ethereum_test_base_types/base_types.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ethereum_test_base_types/base_types.py b/src/ethereum_test_base_types/base_types.py index fb77e9e96b..cb6b7ca37a 100644 --- a/src/ethereum_test_base_types/base_types.py +++ b/src/ethereum_test_base_types/base_types.py @@ -2,6 +2,7 @@ Basic type primitives used to define other types. """ +from hashlib import sha256 from typing import Any, ClassVar, SupportsBytes, Type, TypeVar from Crypto.Hash import keccak @@ -162,7 +163,7 @@ class Bytes(bytes, ToStringSchema): Class that helps represent bytes of variable length in tests. """ - def __new__(cls, input: BytesConvertible): + def __new__(cls, input: BytesConvertible = b""): """ Creates a new Bytes object. """ @@ -204,6 +205,12 @@ def keccak256(self) -> "Hash": k = keccak.new(digest_bits=256) return Hash(k.update(bytes(self)).digest()) + def sha256(self) -> "Hash": + """ + Return the sha256 hash of the opcode byte representation. + """ + return Hash(sha256(self).digest()) + S = TypeVar("S", bound="FixedSizeHexNumber") From afc45c02b638d10cd3bd349a2ebb717bbe897fd7 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Wed, 9 Oct 2024 21:25:16 +0000 Subject: [PATCH 2/6] feat(base_types): Allow instantiating `Address` with a label --- src/ethereum_test_base_types/base_types.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/ethereum_test_base_types/base_types.py b/src/ethereum_test_base_types/base_types.py index cb6b7ca37a..e88b8c5524 100644 --- a/src/ethereum_test_base_types/base_types.py +++ b/src/ethereum_test_base_types/base_types.py @@ -348,6 +348,17 @@ class Address(FixedSizeBytes[20]): # type: ignore label: str | None = None + def __new__(cls, input: "FixedSizeBytesConvertible | Address", *, label: str | None = None): + """ + Creates a new Address object with an optional label. + """ + instance = super(Address, cls).__new__(cls, input) + if isinstance(input, Address) and label is None: + instance.label = input.label + else: + instance.label = label + return instance + class Hash(FixedSizeBytes[32]): # type: ignore """ From ad0814a273835f1ced261642cf4ac6f4cf4ab626 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Wed, 9 Oct 2024 21:25:51 +0000 Subject: [PATCH 3/6] fix(base_types): Fix `FixedSizeBytes` comparison --- src/ethereum_test_base_types/base_types.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ethereum_test_base_types/base_types.py b/src/ethereum_test_base_types/base_types.py index e88b8c5524..813fef9adc 100644 --- a/src/ethereum_test_base_types/base_types.py +++ b/src/ethereum_test_base_types/base_types.py @@ -286,6 +286,7 @@ class FixedSizeBytes(Bytes): """ byte_length: ClassVar[int] + _sized_: ClassVar[Type["FixedSizeBytes"]] def __class_getitem__(cls, length: int) -> Type["FixedSizeBytes"]: """ @@ -295,6 +296,7 @@ def __class_getitem__(cls, length: int) -> Type["FixedSizeBytes"]: class Sized(cls): # type: ignore byte_length = length + Sized._sized_ = Sized return Sized def __new__(cls, input: FixedSizeBytesConvertible | T): @@ -324,6 +326,8 @@ def __eq__(self, other: object) -> bool: """ Compares two FixedSizeBytes objects to be equal. """ + if other is None: + return False if not isinstance(other, FixedSizeBytes): assert ( isinstance(other, str) @@ -331,7 +335,7 @@ def __eq__(self, other: object) -> bool: or isinstance(other, bytes) or isinstance(other, SupportsBytes) ) - other = self.__class__(other) + other = self._sized_(other) return super().__eq__(other) def __ne__(self, other: object) -> bool: From e1430113486380da8e2449f9cb0b74ce4bf6c1db Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Fri, 11 Oct 2024 17:25:27 +0000 Subject: [PATCH 4/6] feat(specs): Add `BlockchainTestEngine` fixup: BlockchainEngine changelog fixup(docs): BlockchainTestEngine docs Co-authored-by: danceratopz --- docs/CHANGELOG.md | 3 ++- docs/writing_tests/writing_a_new_test.md | 6 ++++++ src/ethereum_test_specs/__init__.py | 13 ++++++++++++- src/ethereum_test_specs/blockchain.py | 14 ++++++++++++++ src/ethereum_test_tools/__init__.py | 4 ++++ 5 files changed, 38 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 7197aba7b5..c7d3f9d1fe 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -46,8 +46,9 @@ Test fixtures for use by clients are available for each release on the [Github r - ✨ 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)). - ✨ Pin EELS versions in `eels_resolutions.json` and include this file in fixture releases ([#872](https://github.com/ethereum/execution-spec-tests/pull/872)). - 🔀 Replace `ethereum.base_types` with `ethereum-types` ([#850](https://github.com/ethereum/execution-spec-tests/pull/850)). -- 💥 `PragueEIP7692` fork has been renamed to `Osaka` ([#869](https://github.com/ethereum/execution-spec-tests/pull/869)) +- 💥 Rename the `PragueEIP7692` fork to `Osaka` ([#869](https://github.com/ethereum/execution-spec-tests/pull/869)). - ✨ Improve `fill` terminal output to emphasize that filling tests is not actually testing a client ([#807](https://github.com/ethereum/execution-spec-tests/pull/887)). +- ✨ Add the `BlockchainTestEngine` test spec type that only generates a fixture in the `EngineFixture` (`blockchain_test_engine`) format ([#888](https://github.com/ethereum/execution-spec-tests/pull/888)). ### 🔧 EVM Tools diff --git a/docs/writing_tests/writing_a_new_test.md b/docs/writing_tests/writing_a_new_test.md index 9a972413a0..e5525a3269 100644 --- a/docs/writing_tests/writing_a_new_test.md +++ b/docs/writing_tests/writing_a_new_test.md @@ -76,6 +76,8 @@ def test_contract_creating_tx( The `state_test` and `blockchain_test` objects are actually wrapper classes to the `StateTest`, respectively `BlockchainTest` objects, that once called actually instantiate a new instance of these objects and fill the test case using the `evm` tool according to the pre and post states and the transactions defined within the test. +If a blockchain-type test should only generate a test fixture in the Engine format (`EngineFixture`), the `blockchain_test_engine` object can be specified. This object is a wrapper for the `BlockchainTestEngine` class. + ## `StateTest` Object The `StateTest` object represents a single test vector, and contains the @@ -100,6 +102,10 @@ Ethereum VM by attempting to append multiple blocks to the chain: created or modified after all blocks are executed. - `blocks`: All blocks to be appended to the blockchain during the test. +## `BlockchainTestEngine` Object + +The `BlockchainTestEngine` object has the same properties as the `BlockchainTest` but it's used to only generate a blockchain test in the Engine format. + ## Pre/Post State of the Test The `pre` and `post` states are elemental to setup and then verify the outcome diff --git a/src/ethereum_test_specs/__init__.py b/src/ethereum_test_specs/__init__.py index 6df10e103e..0e7d9b2b6b 100644 --- a/src/ethereum_test_specs/__init__.py +++ b/src/ethereum_test_specs/__init__.py @@ -5,7 +5,14 @@ from typing import List, Type from .base import BaseTest, TestSpec -from .blockchain import BlockchainTest, BlockchainTestFiller, BlockchainTestSpec +from .blockchain import ( + BlockchainTest, + BlockchainTestEngine, + BlockchainTestEngineFiller, + BlockchainTestEngineSpec, + BlockchainTestFiller, + BlockchainTestSpec, +) from .eof import ( EOFStateTest, EOFStateTestFiller, @@ -18,6 +25,7 @@ SPEC_TYPES: List[Type[BaseTest]] = [ BlockchainTest, + BlockchainTestEngine, StateTest, StateTestOnly, EOFTest, @@ -29,6 +37,9 @@ "SPEC_TYPES", "BaseTest", "BlockchainTest", + "BlockchainTestEngine", + "BlockchainTestEngineFiller", + "BlockchainTestEngineSpec", "BlockchainTestFiller", "BlockchainTestSpec", "EOFStateTest", diff --git a/src/ethereum_test_specs/blockchain.py b/src/ethereum_test_specs/blockchain.py index 3d732bda43..432b12a87f 100644 --- a/src/ethereum_test_specs/blockchain.py +++ b/src/ethereum_test_specs/blockchain.py @@ -751,3 +751,17 @@ def generate( BlockchainTestSpec = Callable[[str], Generator[BlockchainTest, None, None]] BlockchainTestFiller = Type[BlockchainTest] + + +class BlockchainTestEngine(BlockchainTest): + """ + Filler type that tests multiple blocks (valid or invalid) in a chain, only for the Engine API. + """ + + supported_fixture_formats: ClassVar[List[FixtureFormat]] = [ + BlockchainEngineFixture, + ] + + +BlockchainTestEngineSpec = Callable[[str], Generator[BlockchainTestEngine, None, None]] +BlockchainTestEngineFiller = Type[BlockchainTestEngine] diff --git a/src/ethereum_test_tools/__init__.py b/src/ethereum_test_tools/__init__.py index 54bff9d1fc..bcff5423ec 100644 --- a/src/ethereum_test_tools/__init__.py +++ b/src/ethereum_test_tools/__init__.py @@ -25,6 +25,8 @@ SPEC_TYPES, BaseTest, BlockchainTest, + BlockchainTestEngine, + BlockchainTestEngineFiller, BlockchainTestFiller, EOFStateTest, EOFStateTestFiller, @@ -93,6 +95,8 @@ "BaseTest", "Block", "BlockchainTest", + "BlockchainTestEngine", + "BlockchainTestEngineFiller", "BlockchainTestFiller", "BlockException", "Bytecode", From 2bffcef78a51aebb9a6e1c9585fdd1f8babdecab Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Thu, 10 Oct 2024 19:02:46 +0000 Subject: [PATCH 5/6] feat(types,plugins): Allow `nonce` in `fund_eoa` --- src/ethereum_test_types/types.py | 1 + src/pytest_plugins/filler/pre_alloc.py | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/ethereum_test_types/types.py b/src/ethereum_test_types/types.py index f8a80423e8..d06a0883f5 100644 --- a/src/ethereum_test_types/types.py +++ b/src/ethereum_test_types/types.py @@ -310,6 +310,7 @@ def fund_eoa( label: str | None = None, storage: Storage | None = None, delegation: Address | Literal["Self"] | None = None, + nonce: NumberConvertible | None = None, ) -> EOA: """ Add a previously unused EOA to the pre-alloc with the balance specified by `amount`. diff --git a/src/pytest_plugins/filler/pre_alloc.py b/src/pytest_plugins/filler/pre_alloc.py index be6aca0d49..5451638fa7 100644 --- a/src/pytest_plugins/filler/pre_alloc.py +++ b/src/pytest_plugins/filler/pre_alloc.py @@ -199,6 +199,7 @@ def fund_eoa( label: str | None = None, storage: Storage | None = None, delegation: Address | Literal["Self"] | None = None, + nonce: NumberConvertible | None = None, ) -> EOA: """ Add a previously unused EOA to the pre-alloc with the balance specified by `amount`. @@ -209,25 +210,34 @@ def fund_eoa( eoa = next(self._eoa_iterator) if amount is None: amount = self._eoa_fund_amount_default - if Number(amount) > 0 or storage is not None or delegation is not None: + if ( + Number(amount) > 0 + or storage is not None + or delegation is not None + or (nonce is not None and Number(nonce) > 0) + ): if storage is None and delegation is None: + nonce = Number(0 if nonce is None else nonce) account = Account( - nonce=0, + nonce=nonce, balance=amount, ) + if nonce > 0: + eoa.nonce = nonce else: # Type-4 transaction is sent to the EOA to set the storage, so the nonce must be 1 if not isinstance(delegation, Address) and delegation == "Self": delegation = eoa + nonce = Number(1 if nonce is None else nonce) account = Account( - nonce=1, + nonce=nonce, balance=amount, storage=storage if storage is not None else {}, code=DELEGATION_DESIGNATION + bytes(delegation) # type: ignore if delegation is not None else b"", ) - eoa.nonce = Number(1) + eoa.nonce = nonce super().__setitem__(eoa, account) return eoa From 818c87f5ddc4278501725d1917784450970986f3 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Thu, 10 Oct 2024 20:11:56 +0000 Subject: [PATCH 6/6] fix(tools): Allow `Bytes` in `Initcode` --- src/ethereum_test_tools/code/generators.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ethereum_test_tools/code/generators.py b/src/ethereum_test_tools/code/generators.py index df23d375ec..19431e3557 100644 --- a/src/ethereum_test_tools/code/generators.py +++ b/src/ethereum_test_tools/code/generators.py @@ -5,6 +5,7 @@ from dataclasses import dataclass from typing import List, SupportsBytes +from ethereum_test_base_types import Bytes from ethereum_test_types import ceiling_division from ethereum_test_vm import Bytecode, EVMCodeType from ethereum_test_vm import Opcodes as Op @@ -27,7 +28,7 @@ class Initcode(Bytecode): costs. """ - deploy_code: SupportsBytes + deploy_code: SupportsBytes | Bytes """ Bytecode to be deployed by the initcode. """ @@ -44,7 +45,7 @@ class Initcode(Bytecode): def __new__( cls, *, - deploy_code: SupportsBytes = Bytecode(), + deploy_code: SupportsBytes | Bytes = Bytecode(), initcode_length: int | None = None, initcode_prefix: Bytecode = Bytecode(), initcode_prefix_execution_gas: int = 0,