From db7cedecd16ca7a90fe8bfeed1dfd33bef0d67b4 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Fri, 20 Sep 2024 19:17:01 +0000 Subject: [PATCH 1/7] feat(forks): Add `engine_get_payload_version` method to forks --- src/ethereum_test_forks/base_fork.py | 11 +++++++++++ src/ethereum_test_forks/forks/forks.py | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/src/ethereum_test_forks/base_fork.py b/src/ethereum_test_forks/base_fork.py index ef1d525309..53c161520f 100644 --- a/src/ethereum_test_forks/base_fork.py +++ b/src/ethereum_test_forks/base_fork.py @@ -271,6 +271,17 @@ def engine_forkchoice_updated_version( """ pass + @classmethod + @abstractmethod + def engine_get_payload_version( + cls, block_number: int = 0, timestamp: int = 0 + ) -> Optional[int]: + """ + Returns `None` if the forks canonical chain cannot be set using the forkchoice method. + """ + pass + + # EVM information abstract methods @classmethod @abstractmethod def evm_code_types(cls, block_number: int = 0, timestamp: int = 0) -> List[EVMCodeType]: diff --git a/src/ethereum_test_forks/forks/forks.py b/src/ethereum_test_forks/forks/forks.py index f56426c78f..c965307cf0 100644 --- a/src/ethereum_test_forks/forks/forks.py +++ b/src/ethereum_test_forks/forks/forks.py @@ -144,6 +144,15 @@ def engine_forkchoice_updated_version( """ return cls.engine_new_payload_version(block_number, timestamp) + @classmethod + def engine_get_payload_version( + cls, block_number: int = 0, timestamp: int = 0 + ) -> Optional[int]: + """ + At genesis, payloads cannot be retrieved through the engine API. + """ + return cls.engine_new_payload_version(block_number, timestamp) + @classmethod def get_reward(cls, block_number: int = 0, timestamp: int = 0) -> int: """ From 0a358a42652b3849c039079f6137c82167a3970f Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Fri, 20 Sep 2024 19:17:54 +0000 Subject: [PATCH 2/7] fix(base_types): add `items` to `Storage` --- src/ethereum_test_base_types/composite_types.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ethereum_test_base_types/composite_types.py b/src/ethereum_test_base_types/composite_types.py index 61ccb9ed7f..7627c5b107 100644 --- a/src/ethereum_test_base_types/composite_types.py +++ b/src/ethereum_test_base_types/composite_types.py @@ -177,6 +177,10 @@ def set_next_slot(self, slot: int) -> "Storage": self._current_slot = slot return self + def items(self): + """Returns the items of the storage""" + return self.root.items() + def store_next( self, value: StorageKeyValueTypeConvertible | StorageKeyValueType | bool ) -> StorageKeyValueType: From cc59e3b49a2716180ff7beccd4c9203c04f554f3 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Fri, 20 Sep 2024 19:19:25 +0000 Subject: [PATCH 3/7] fix(types): add `items` to `Alloc` --- src/ethereum_test_types/types.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ethereum_test_types/types.py b/src/ethereum_test_types/types.py index 543b361201..b7feef3d8c 100644 --- a/src/ethereum_test_types/types.py +++ b/src/ethereum_test_types/types.py @@ -175,6 +175,12 @@ def __iter__(self): """ return iter(self.root) + def items(self): + """ + Returns an iterator over the allocation items. + """ + return self.root.items() + def __getitem__(self, address: Address | FixedSizeBytesConvertible) -> Account | None: """ Returns the account associated with an address. From 515aa3eba10ef68219687ad590bd6b779ba95049 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Fri, 20 Sep 2024 19:21:43 +0000 Subject: [PATCH 4/7] feat(types): Add `int_to_bytes` helper --- src/ethereum_test_types/helpers.py | 5 ++--- src/ethereum_test_types/types.py | 15 +++++++++++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/ethereum_test_types/helpers.py b/src/ethereum_test_types/helpers.py index 66cf0fe09d..6d479cf3d8 100644 --- a/src/ethereum_test_types/helpers.py +++ b/src/ethereum_test_types/helpers.py @@ -11,7 +11,7 @@ from ethereum_test_base_types.conversions import BytesConvertible, FixedSizeBytesConvertible from ethereum_test_vm import Opcodes as Op -from .types import EOA +from .types import EOA, int_to_bytes """ Helper functions @@ -46,8 +46,7 @@ def compute_create_address( address = Address(address) if nonce is None: nonce = 0 - nonce_bytes = bytes() if nonce == 0 else nonce.to_bytes(length=1, byteorder="big") - hash = Bytes(encode([address, nonce_bytes])).keccak256() + hash = Bytes(encode([address, int_to_bytes(nonce)])).keccak256() return Address(hash[-20:]) if opcode == Op.CREATE2: return compute_create2_address(address, salt, initcode) diff --git a/src/ethereum_test_types/types.py b/src/ethereum_test_types/types.py index b7feef3d8c..6ba1706a74 100644 --- a/src/ethereum_test_types/types.py +++ b/src/ethereum_test_types/types.py @@ -56,6 +56,16 @@ def keccak256(data: bytes) -> Hash: return Bytes(data).keccak256() +def int_to_bytes(value: int) -> bytes: + """ + Converts an integer to its big-endian representation. + """ + if value == 0: + return b"" + + return int_to_bytes(value // 256) + bytes([value % 256]) + + # Sentinel classes class Removable: """ @@ -1084,12 +1094,9 @@ def created_contract(self) -> Address: """ if self.to is not None: raise ValueError("transaction is not a contract creation") - nonce_bytes = ( - bytes() if self.nonce == 0 else self.nonce.to_bytes(length=1, byteorder="big") - ) if self.sender is None: raise ValueError("sender address is None") - hash = Bytes(eth_rlp.encode([self.sender, nonce_bytes])).keccak256() + hash = Bytes(eth_rlp.encode([self.sender, int_to_bytes(self.nonce)])).keccak256() return Address(hash[-20:]) From 2c0a9d908bf63194d92d15473398f75071eb8656 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Fri, 20 Sep 2024 19:23:19 +0000 Subject: [PATCH 5/7] refactor(types): add `TransactionDefaults` class --- src/ethereum_test_types/__init__.py | 2 ++ src/ethereum_test_types/types.py | 22 ++++++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/ethereum_test_types/__init__.py b/src/ethereum_test_types/__init__.py index 028313de33..4205ea22b2 100644 --- a/src/ethereum_test_types/__init__.py +++ b/src/ethereum_test_types/__init__.py @@ -27,6 +27,7 @@ Requests, Storage, Transaction, + TransactionDefaults, Withdrawal, WithdrawalRequest, keccak256, @@ -54,6 +55,7 @@ "TestPrivateKey", "TestPrivateKey2", "Transaction", + "TransactionDefaults", "Withdrawal", "WithdrawalRequest", "ZeroPaddedHexNumber", diff --git a/src/ethereum_test_types/types.py b/src/ethereum_test_types/types.py index 6ba1706a74..c8086e468a 100644 --- a/src/ethereum_test_types/types.py +++ b/src/ethereum_test_types/types.py @@ -597,13 +597,27 @@ def sign(self, private_key: Hash) -> None: self.s = HexNumber(signature[2]) +@dataclass +class TransactionDefaults: + """ + Default values for transactions. + """ + + chain_id: int = 1 + gas_price = 10 + max_fee_per_gas = 7 + max_priority_fee_per_gas: int = 0 + + class TransactionGeneric(BaseModel, Generic[NumberBoundTypeVar]): """ Generic transaction type used as a parent for Transaction and FixtureTransaction (blockchain). """ ty: NumberBoundTypeVar = Field(0, alias="type") # type: ignore - chain_id: NumberBoundTypeVar = Field(1) # type: ignore + chain_id: NumberBoundTypeVar = Field( + default_factory=lambda: TransactionDefaults.chain_id + ) # type: ignore nonce: NumberBoundTypeVar = Field(0) # type: ignore gas_price: NumberBoundTypeVar | None = None max_priority_fee_per_gas: NumberBoundTypeVar | None = None @@ -759,16 +773,16 @@ def model_post_init(self, __context): # Set default values for fields that are required for certain tx types if self.ty <= 1 and self.gas_price is None: - self.gas_price = 10 + self.gas_price = TransactionDefaults.gas_price if self.ty >= 1 and self.access_list is None: self.access_list = [] if self.ty < 1: assert self.access_list is None, "access_list must be None" if self.ty >= 2 and self.max_fee_per_gas is None: - self.max_fee_per_gas = 7 + self.max_fee_per_gas = TransactionDefaults.max_fee_per_gas if self.ty >= 2 and self.max_priority_fee_per_gas is None: - self.max_priority_fee_per_gas = 0 + self.max_priority_fee_per_gas = TransactionDefaults.max_priority_fee_per_gas if self.ty < 2: assert self.max_fee_per_gas is None, "max_fee_per_gas must be None" assert self.max_priority_fee_per_gas is None, "max_priority_fee_per_gas must be None" From 065769afe5a825915797f1267c73980b68a3ba7b Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Fri, 20 Sep 2024 19:26:14 +0000 Subject: [PATCH 6/7] refactor(types): Add `_eoa_fund_amount_default` private attr to `Alloc` --- src/ethereum_test_types/types.py | 5 ++++- src/pytest_plugins/filler/pre_alloc.py | 8 ++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/ethereum_test_types/types.py b/src/ethereum_test_types/types.py index c8086e468a..d695aa4ee7 100644 --- a/src/ethereum_test_types/types.py +++ b/src/ethereum_test_types/types.py @@ -16,6 +16,7 @@ BaseModel, ConfigDict, Field, + PrivateAttr, RootModel, computed_field, model_serializer, @@ -129,6 +130,8 @@ class Alloc(BaseAlloc): Allocation of accounts in the state, pre and post test execution. """ + _eoa_fund_amount_default: int = PrivateAttr(10**21) + @dataclass(kw_only=True) class UnexpectedAccount(Exception): """ @@ -302,7 +305,7 @@ def deploy_contract( def fund_eoa( self, - amount: NumberConvertible = 10**21, + amount: NumberConvertible | None = None, label: str | None = None, storage: Storage | None = None, delegation: Address | Literal["Self"] | None = None, diff --git a/src/pytest_plugins/filler/pre_alloc.py b/src/pytest_plugins/filler/pre_alloc.py index d1b46bfce3..be6aca0d49 100644 --- a/src/pytest_plugins/filler/pre_alloc.py +++ b/src/pytest_plugins/filler/pre_alloc.py @@ -39,7 +39,9 @@ def pytest_addoption(parser: pytest.Parser): """ Adds command-line options to pytest. """ - pre_alloc_group = parser.getgroup("pre_alloc", "Arguments defining pre-allocation behavior.") + pre_alloc_group = parser.getgroup( + "pre_alloc", "Arguments defining pre-allocation behavior during test filling." + ) pre_alloc_group.addoption( "--strict-alloc", @@ -193,7 +195,7 @@ def deploy_contract( def fund_eoa( self, - amount: NumberConvertible = 10**21, + amount: NumberConvertible | None = None, label: str | None = None, storage: Storage | None = None, delegation: Address | Literal["Self"] | None = None, @@ -205,6 +207,8 @@ def fund_eoa( returned. """ 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 storage is None and delegation is None: account = Account( From 87a297f9c250787681883bcfc4f1ccf47747c948 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Fri, 20 Sep 2024 19:27:30 +0000 Subject: [PATCH 7/7] fix(vm): add missing `SupportsBytes` parameter type --- src/ethereum_test_vm/opcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ethereum_test_vm/opcode.py b/src/ethereum_test_vm/opcode.py index 398a7ae62c..90e10e0394 100644 --- a/src/ethereum_test_vm/opcode.py +++ b/src/ethereum_test_vm/opcode.py @@ -33,7 +33,7 @@ def _get_int_size(n: int) -> int: def _stack_argument_to_bytecode( - arg: "int | bytes | str | Opcode | Bytecode | Iterable[int]", + arg: "int | bytes | SupportsBytes | str | Opcode | Bytecode | Iterable[int]", ) -> Bytecode: """ Converts a stack argument in an opcode or macro to bytecode.