Skip to content

Commit

Permalink
Merge pull request #888 from ethereum/pre-reqs-for-devnet-4-updates
Browse files Browse the repository at this point in the history
feat(fw): Changes required for Devnet-4 Tests
  • Loading branch information
marioevz authored Oct 15, 2024
2 parents 0900aa8 + 818c87f commit e91a712
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 10 deletions.
3 changes: 2 additions & 1 deletion docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 6 additions & 0 deletions docs/writing_tests/writing_a_new_test.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
26 changes: 24 additions & 2 deletions src/ethereum_test_base_types/base_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
"""
Expand Down Expand Up @@ -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")

Expand Down Expand Up @@ -279,6 +286,7 @@ class FixedSizeBytes(Bytes):
"""

byte_length: ClassVar[int]
_sized_: ClassVar[Type["FixedSizeBytes"]]

def __class_getitem__(cls, length: int) -> Type["FixedSizeBytes"]:
"""
Expand All @@ -288,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):
Expand Down Expand Up @@ -317,14 +326,16 @@ 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)
or isinstance(other, int)
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:
Expand All @@ -341,6 +352,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
"""
Expand Down
13 changes: 12 additions & 1 deletion src/ethereum_test_specs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -18,6 +25,7 @@

SPEC_TYPES: List[Type[BaseTest]] = [
BlockchainTest,
BlockchainTestEngine,
StateTest,
StateTestOnly,
EOFTest,
Expand All @@ -29,6 +37,9 @@
"SPEC_TYPES",
"BaseTest",
"BlockchainTest",
"BlockchainTestEngine",
"BlockchainTestEngineFiller",
"BlockchainTestEngineSpec",
"BlockchainTestFiller",
"BlockchainTestSpec",
"EOFStateTest",
Expand Down
14 changes: 14 additions & 0 deletions src/ethereum_test_specs/blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
4 changes: 4 additions & 0 deletions src/ethereum_test_tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
SPEC_TYPES,
BaseTest,
BlockchainTest,
BlockchainTestEngine,
BlockchainTestEngineFiller,
BlockchainTestFiller,
EOFStateTest,
EOFStateTestFiller,
Expand Down Expand Up @@ -93,6 +95,8 @@
"BaseTest",
"Block",
"BlockchainTest",
"BlockchainTestEngine",
"BlockchainTestEngineFiller",
"BlockchainTestFiller",
"BlockException",
"Bytecode",
Expand Down
5 changes: 3 additions & 2 deletions src/ethereum_test_tools/code/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -27,7 +28,7 @@ class Initcode(Bytecode):
costs.
"""

deploy_code: SupportsBytes
deploy_code: SupportsBytes | Bytes
"""
Bytecode to be deployed by the initcode.
"""
Expand All @@ -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,
Expand Down
1 change: 1 addition & 0 deletions src/ethereum_test_types/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
18 changes: 14 additions & 4 deletions src/pytest_plugins/filler/pre_alloc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand All @@ -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
Expand Down

0 comments on commit e91a712

Please sign in to comment.