From ccf25a322bf37a1c0fd9d6498c5e9edfaeb0d098 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Tue, 6 Feb 2024 03:20:02 -0600 Subject: [PATCH] bug(fw): fix account empty code check (#425) * fix(fw): fix account empty code check * changelog --- docs/CHANGELOG.md | 2 ++ src/ethereum_test_tools/common/types.py | 25 +++++++++++++++---- .../spec/blockchain/blockchain_test.py | 13 +++++++--- .../spec/state/state_test.py | 3 +++ 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 65c4d1ce38..c309c88ea1 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -8,6 +8,8 @@ Test fixtures for use by clients are available for each release on the [Github r ### ๐Ÿงช Test Cases +- ๐Ÿž Fix beacon root contract deployment tests so the account in the pre-alloc is not empty ([#425](https://github.com/ethereum/execution-spec-tests/pull/425)). + ### ๐Ÿ› ๏ธ Framework - โœจ Add Prague to forks ([#419](https://github.com/ethereum/execution-spec-tests/pull/419)). diff --git a/src/ethereum_test_tools/common/types.py b/src/ethereum_test_tools/common/types.py index 113ed8fe22..49523a1c7b 100644 --- a/src/ethereum_test_tools/common/types.py +++ b/src/ethereum_test_tools/common/types.py @@ -519,6 +519,12 @@ def check_alloc(self: "Account", address: Address, alloc: dict): actual_storage = Storage(alloc["storage"]) if "storage" in alloc else Storage({}) expected_storage.must_be_equal(address=address, other=actual_storage) + def has_empty_code(self: "Account") -> bool: + """ + Returns true if an account has no bytecode. + """ + return not self.code or Bytes(self.code) == b"" + def is_empty(self: "Account") -> bool: """ Returns true if an account deemed empty. @@ -526,7 +532,7 @@ def is_empty(self: "Account") -> bool: return ( (self.nonce == 0 or self.nonce is None) and (self.balance == 0 or self.balance is None) - and (not self.code and self.code is None) + and self.has_empty_code() and (not self.storage or self.storage == {} or self.storage is None) ) @@ -580,9 +586,7 @@ def __init__(self, d: Mapping[FixedSizeBytesConvertible, Account | Dict] = {}): for address, account in d.items(): address = Address(address) assert address not in self, f"Duplicate address in alloc: {address}" - account = Account.from_dict(account) - assert not account.is_empty(), f"Empty account: {account} for address: {address}" - self[address] = account + self[address] = Account.from_dict(account) @classmethod def merge(cls, alloc_1: "Alloc", alloc_2: "Alloc") -> "Alloc": @@ -592,10 +596,21 @@ def merge(cls, alloc_1: "Alloc", alloc_2: "Alloc") -> "Alloc": merged = alloc_1.copy() for address, other_account in alloc_2.items(): - merged[address] = Account.merge(merged.get(address, None), other_account) + merged_account = Account.merge(merged.get(address, None), other_account) + if merged_account.is_empty(): + if address in merged: + merged.pop(address, None) + else: + merged[address] = merged_account return Alloc(merged) + def empty_accounts(self) -> List[Address]: + """ + Returns a list of addresses of empty accounts. + """ + return [address for address, account in self.items() if account.is_empty()] + def __json__(self, encoder: JSONEncoder) -> Mapping[str, Any]: """ Returns the JSON representation of the allocation. diff --git a/src/ethereum_test_tools/spec/blockchain/blockchain_test.py b/src/ethereum_test_tools/spec/blockchain/blockchain_test.py index e5e1b5d20e..cef192ad7f 100644 --- a/src/ethereum_test_tools/spec/blockchain/blockchain_test.py +++ b/src/ethereum_test_tools/spec/blockchain/blockchain_test.py @@ -1,6 +1,7 @@ """ Ethereum blockchain test spec definition and filler. """ + from copy import copy from dataclasses import dataclass, field, replace from pprint import pprint @@ -133,12 +134,16 @@ def make_genesis( if env.beacon_root is not None: assert Hash(env.beacon_root) == Hash(0), "beacon_root must be empty at genesis" - pre_alloc = Alloc( - fork.pre_allocation(block_number=0, timestamp=Number(env.timestamp)), + pre_alloc = Alloc.merge( + Alloc( + fork.pre_allocation(block_number=0, timestamp=Number(env.timestamp)), + ), + Alloc(self.pre), ) - + if empty_accounts := pre_alloc.empty_accounts(): + raise Exception(f"Empty accounts in pre state: {empty_accounts}") new_alloc, state_root = t8n.calc_state_root( - alloc=to_json(Alloc.merge(pre_alloc, Alloc(self.pre))), + alloc=to_json(pre_alloc), fork=fork, debug_output_path=self.get_next_transition_tool_output_path(), ) diff --git a/src/ethereum_test_tools/spec/state/state_test.py b/src/ethereum_test_tools/spec/state/state_test.py index f08814fff5..fcc0dc22eb 100644 --- a/src/ethereum_test_tools/spec/state/state_test.py +++ b/src/ethereum_test_tools/spec/state/state_test.py @@ -1,6 +1,7 @@ """ Ethereum state test spec definition and filler. """ + from copy import copy from dataclasses import dataclass from typing import Callable, Generator, List, Mapping, Optional, Type @@ -133,6 +134,8 @@ def make_state_test_fixture( ), Alloc(self.pre), ) + if empty_accounts := pre_alloc.empty_accounts(): + raise Exception(f"Empty accounts in pre state: {empty_accounts}") transition_tool_name = fork.transition_tool_name( block_number=Number(self.env.number), timestamp=Number(self.env.timestamp),