Skip to content

Commit

Permalink
refactor(fw,tests): fix address and hash types (#422)
Browse files Browse the repository at this point in the history
* All changes

* test fix

* tests(fix): beacon root contract type

* completely remove `to_address`, `to_hash`

* completely remove `to_hash_bytes` too

* changelog

* tox
  • Loading branch information
marioevz authored Feb 1, 2024
1 parent 3ec3fa3 commit 6239499
Show file tree
Hide file tree
Showing 48 changed files with 834 additions and 840 deletions.
1 change: 1 addition & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Test fixtures for use by clients are available for each release on the [Github r

- ✨ Improve handling of the argument passed to `solc --evm-version` when compiling Yul code ([#418](https://github.com/ethereum/execution-spec-tests/pull/418)).
- 🐞 Fix `fill -m yul_test` which failed to filter tests that are (dynamically) marked as a yul test ([#418](https://github.com/ethereum/execution-spec-tests/pull/418)).
- 🔀 Helper methods `to_address`, `to_hash` and `to_hash_bytes` have been deprecated in favor of `Address` and `Hash`, which are automatically detected as opcode parameters and pushed to the stack in the resulting bytecode ([#422](https://github.com/ethereum/execution-spec-tests/pull/422)).

### 🔧 EVM Tools

Expand Down
12 changes: 4 additions & 8 deletions src/ethereum_test_tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@
from .common import (
AccessList,
Account,
Address,
Auto,
EngineAPIError,
Environment,
HistoryStorageAddress,
Hash,
JSONEncoder,
Removable,
Storage,
Expand All @@ -38,9 +39,6 @@
copy_opcode_cost,
cost_memory_bytes,
eip_2028_transaction_data_cost,
to_address,
to_hash,
to_hash_bytes,
transaction_list_root,
)
from .exceptions import BlockException, ExceptionList, ExceptionType, TransactionException
Expand All @@ -63,6 +61,7 @@
"SPEC_TYPES",
"AccessList",
"Account",
"Address",
"Auto",
"BaseFixture",
"BaseTest",
Expand All @@ -80,8 +79,8 @@
"ExceptionList",
"ExceptionType",
"FixtureCollector",
"Hash",
"Header",
"HistoryStorageAddress",
"Initcode",
"JSONEncoder",
"Opcode",
Expand Down Expand Up @@ -113,8 +112,5 @@
"cost_memory_bytes",
"eip_2028_transaction_data_cost",
"eip_2028_transaction_data_cost",
"to_address",
"to_hash_bytes",
"to_hash",
"transaction_list_root",
)
26 changes: 11 additions & 15 deletions src/ethereum_test_tools/common/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
"""
Common definitions and types.
"""
from .base_types import (
Address,
Bloom,
Bytes,
Hash,
HeaderNonce,
HexNumber,
Number,
ZeroPaddedHexNumber,
)
from .constants import (
AddrAA,
AddrBB,
EmptyTrieRoot,
EngineAPIError,
HistoryStorageAddress,
TestAddress,
TestAddress2,
TestPrivateKey,
Expand All @@ -21,29 +30,19 @@
copy_opcode_cost,
cost_memory_bytes,
eip_2028_transaction_data_cost,
to_address,
to_hash,
to_hash_bytes,
)
from .json import to_json
from .types import (
AccessList,
Account,
Address,
Alloc,
Auto,
Bloom,
Bytes,
Environment,
Hash,
HeaderNonce,
JSONEncoder,
Number,
Removable,
Storage,
Transaction,
Withdrawal,
ZeroPaddedHexNumber,
alloc_to_accounts,
serialize_transactions,
str_or_none,
Expand All @@ -66,7 +65,7 @@
"Environment",
"Hash",
"HeaderNonce",
"HistoryStorageAddress",
"HexNumber",
"JSONEncoder",
"Number",
"Removable",
Expand All @@ -89,9 +88,6 @@
"eip_2028_transaction_data_cost",
"serialize_transactions",
"str_or_none",
"to_address",
"to_hash_bytes",
"to_hash",
"to_json",
"transaction_list_root",
"withdrawals_root",
Expand Down
219 changes: 219 additions & 0 deletions src/ethereum_test_tools/common/base_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
"""
Basic type primitives used to define other types.
"""


from typing import ClassVar, SupportsBytes, Type, TypeVar

from .conversions import (
BytesConvertible,
FixedSizeBytesConvertible,
NumberConvertible,
to_bytes,
to_fixed_size_bytes,
to_number,
)
from .json import JSONEncoder, SupportsJSON

N = TypeVar("N", bound="Number")


class Number(int, SupportsJSON):
"""
Class that helps represent numbers in tests.
"""

def __new__(cls, input: NumberConvertible | N):
"""
Creates a new Number object.
"""
return super(Number, cls).__new__(cls, to_number(input))

def __str__(self) -> str:
"""
Returns the string representation of the number.
"""
return str(int(self))

def __json__(self, encoder: JSONEncoder) -> str:
"""
Returns the JSON representation of the number.
"""
return str(self)

def hex(self) -> str:
"""
Returns the hexadecimal representation of the number.
"""
return hex(self)

@classmethod
def or_none(cls: Type[N], input: N | NumberConvertible | None) -> N | None:
"""
Converts the input to a Number while accepting None.
"""
if input is None:
return input
return cls(input)


class HexNumber(Number):
"""
Class that helps represent an hexadecimal numbers in tests.
"""

def __str__(self) -> str:
"""
Returns the string representation of the number.
"""
return self.hex()


class ZeroPaddedHexNumber(HexNumber):
"""
Class that helps represent zero padded hexadecimal numbers in tests.
"""

def hex(self) -> str:
"""
Returns the hexadecimal representation of the number.
"""
if self == 0:
return "0x00"
hex_str = hex(self)[2:]
if len(hex_str) % 2 == 1:
return "0x0" + hex_str
return "0x" + hex_str


class Bytes(bytes, SupportsJSON):
"""
Class that helps represent bytes of variable length in tests.
"""

def __new__(cls, input: BytesConvertible):
"""
Creates a new Bytes object.
"""
return super(Bytes, cls).__new__(cls, to_bytes(input))

def __hash__(self) -> int:
"""
Returns the hash of the bytes.
"""
return super(Bytes, self).__hash__()

def __str__(self) -> str:
"""
Returns the hexadecimal representation of the bytes.
"""
return self.hex()

def __json__(self, encoder: JSONEncoder) -> str:
"""
Returns the JSON representation of the bytes.
"""
return str(self)

def hex(self, *args, **kwargs) -> str:
"""
Returns the hexadecimal representation of the bytes.
"""
return "0x" + super().hex(*args, **kwargs)

@classmethod
def or_none(cls, input: "Bytes | BytesConvertible | None") -> "Bytes | None":
"""
Converts the input to a Bytes while accepting None.
"""
if input is None:
return input
return cls(input)


T = TypeVar("T", bound="FixedSizeBytes")


class FixedSizeBytes(Bytes):
"""
Class that helps represent bytes of fixed length in tests.
"""

byte_length: ClassVar[int]

def __class_getitem__(cls, length: int) -> Type["FixedSizeBytes"]:
"""
Creates a new FixedSizeBytes class with the given length.
"""

class Sized(cls): # type: ignore
byte_length = length

return Sized

def __new__(cls, input: FixedSizeBytesConvertible | T):
"""
Creates a new FixedSizeBytes object.
"""
return super(FixedSizeBytes, cls).__new__(cls, to_fixed_size_bytes(input, cls.byte_length))

def __hash__(self) -> int:
"""
Returns the hash of the bytes.
"""
return super(FixedSizeBytes, self).__hash__()

@classmethod
def or_none(cls: Type[T], input: T | FixedSizeBytesConvertible | None) -> T | None:
"""
Converts the input to a Fixed Size Bytes while accepting None.
"""
if input is None:
return input
return cls(input)

def __eq__(self, other: object) -> bool:
"""
Compares two FixedSizeBytes objects.
"""
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)
return super().__eq__(other)


class Address(FixedSizeBytes[20]): # type: ignore
"""
Class that helps represent Ethereum addresses in tests.
"""

pass


class Hash(FixedSizeBytes[32]): # type: ignore
"""
Class that helps represent hashes in tests.
"""

pass


class Bloom(FixedSizeBytes[256]): # type: ignore
"""
Class that helps represent blooms in tests.
"""

pass


class HeaderNonce(FixedSizeBytes[8]): # type: ignore
"""
Class that helps represent the header nonce in tests.
"""

pass
14 changes: 7 additions & 7 deletions src/ethereum_test_tools/common/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@

from enum import IntEnum

TestAddress = "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"
TestAddress2 = "0x8a0a19589531694250d570040a0c4b74576919b8"
from .base_types import Address

TestAddress = Address("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")
TestAddress2 = Address("0x8a0a19589531694250d570040a0c4b74576919b8")

TestPrivateKey = "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8"
TestPrivateKey2 = "0x9e7645d0cfd9c3a04eb7a9db59a4eb7d359f2e75c9164a9d6b9a7d54e1b6a36f"

AddrAA = "0x00000000000000000000000000000000000000aa"
AddrBB = "0x00000000000000000000000000000000000000bb"
AddrAA = Address(0xAA)
AddrBB = Address(0xBB)

EmptyBloom = bytes([0] * 256)
EmptyOmmersRoot = bytes.fromhex("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")
EmptyTrieRoot = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
EmptyHash = bytes([0] * 32)
EmptyNonce = bytes([0] * 8)
ZeroAddress = bytes([0] * 20)

HistoryStorageAddress = "0x000000000000000000000000000000000000000b"
ZeroAddress = Address(0x00)


class EngineAPIError(IntEnum):
Expand Down
Loading

0 comments on commit 6239499

Please sign in to comment.