diff --git a/packages/testing/src/execution_testing/specs/blockchain.py b/packages/testing/src/execution_testing/specs/blockchain.py index 3ba8cf0cc5..0f71672387 100644 --- a/packages/testing/src/execution_testing/specs/blockchain.py +++ b/packages/testing/src/execution_testing/specs/blockchain.py @@ -549,11 +549,16 @@ def make_genesis( state_root = pre_alloc.state_root() genesis = FixtureHeader.genesis(self.fork, env, state_root) + genesis_bal = None + if self.fork.header_bal_hash_required(block_number=0, timestamp=0): + genesis_bal = BlockAccessList() + return ( pre_alloc, FixtureBlockBase( header=genesis, withdrawals=None if env.withdrawals is None else [], + block_access_list=genesis_bal, ).with_rlp(txs=[]), ) diff --git a/src/ethereum/forks/amsterdam/block_access_lists/builder.py b/src/ethereum/forks/amsterdam/block_access_lists/builder.py index c1dbf98222..e860c84068 100644 --- a/src/ethereum/forks/amsterdam/block_access_lists/builder.py +++ b/src/ethereum/forks/amsterdam/block_access_lists/builder.py @@ -405,7 +405,7 @@ def _build_from_builder( [`BlockAccessList`]: ref:ethereum.forks.amsterdam.block_access_lists.rlp_types.BlockAccessList # noqa: E501 """ - account_changes_list = [] + block_access_list: BlockAccessList = [] for address, changes in builder.accounts.items(): storage_changes = [] @@ -444,11 +444,11 @@ def _build_from_builder( code_changes=code_changes, ) - account_changes_list.append(account_change) + block_access_list.append(account_change) - account_changes_list.sort(key=lambda x: x.address) + block_access_list.sort(key=lambda x: x.address) - return BlockAccessList(account_changes=tuple(account_changes_list)) + return block_access_list def build_block_access_list( diff --git a/src/ethereum/forks/amsterdam/block_access_lists/rlp_types.py b/src/ethereum/forks/amsterdam/block_access_lists/rlp_types.py index e4d37d6a74..c4f49ff4aa 100644 --- a/src/ethereum/forks/amsterdam/block_access_lists/rlp_types.py +++ b/src/ethereum/forks/amsterdam/block_access_lists/rlp_types.py @@ -8,7 +8,7 @@ """ from dataclasses import dataclass -from typing import Tuple +from typing import List, Tuple from ethereum_types.bytes import Bytes, Bytes20, Bytes32 from ethereum_types.frozen import slotted_freezable @@ -118,13 +118,4 @@ class AccountChanges: code_changes: Tuple[CodeChange, ...] -@slotted_freezable -@dataclass -class BlockAccessList: - """ - Block-Level Access List for EIP-7928. - Contains all addresses accessed during block execution. - RLP encoded as a list of AccountChanges. - """ - - account_changes: Tuple[AccountChanges, ...] +BlockAccessList = List[AccountChanges] diff --git a/src/ethereum/forks/amsterdam/block_access_lists/rlp_utils.py b/src/ethereum/forks/amsterdam/block_access_lists/rlp_utils.py index 738abce181..2cd5b827f3 100644 --- a/src/ethereum/forks/amsterdam/block_access_lists/rlp_utils.py +++ b/src/ethereum/forks/amsterdam/block_access_lists/rlp_utils.py @@ -71,7 +71,7 @@ def rlp_encode_block_access_list(block_access_list: BlockAccessList) -> Bytes: """ # Encode as a list of AccountChanges directly (not wrapped) account_changes_list = [] - for account in block_access_list.account_changes: + for account in block_access_list: # Each account is encoded as: # [address, storage_changes, storage_reads, # balance_changes, nonce_changes, code_changes] @@ -146,7 +146,7 @@ def validate_block_access_list_against_execution( # 1. Validate structural constraints # Check that storage changes and reads don't overlap for the same slot - for account in block_access_list.account_changes: + for account in block_access_list: changed_slots = {sc.slot for sc in account.storage_changes} read_slots = set(account.storage_reads) @@ -155,9 +155,7 @@ def validate_block_access_list_against_execution( return False # 2. Validate ordering (addresses should be sorted lexicographically) - addresses = [ - account.address for account in block_access_list.account_changes - ] + addresses = [account.address for account in block_access_list] if addresses != sorted(addresses): return False @@ -165,7 +163,7 @@ def validate_block_access_list_against_execution( max_block_access_index = ( MAX_TXS + 1 ) # 0 for pre-exec, 1..MAX_TXS for txs, MAX_TXS+1 for post-exec - for account in block_access_list.account_changes: + for account in block_access_list: # Validate storage slots are sorted within each account storage_slots = [sc.slot for sc in account.storage_changes] if storage_slots != sorted(storage_slots): diff --git a/src/ethereum/forks/amsterdam/vm/__init__.py b/src/ethereum/forks/amsterdam/vm/__init__.py index d414aa50f9..b675143c10 100644 --- a/src/ethereum/forks/amsterdam/vm/__init__.py +++ b/src/ethereum/forks/amsterdam/vm/__init__.py @@ -96,9 +96,7 @@ class BlockOutput: ) blob_gas_used: U64 = U64(0) requests: List[Bytes] = field(default_factory=list) - block_access_list: BlockAccessList = field( - default_factory=lambda: BlockAccessList(account_changes=()) - ) + block_access_list: BlockAccessList = field(default_factory=list) @dataclass diff --git a/src/ethereum_spec_tools/evm_tools/loaders/fixture_loader.py b/src/ethereum_spec_tools/evm_tools/loaders/fixture_loader.py index 0070152c89..f26578c1d2 100644 --- a/src/ethereum_spec_tools/evm_tools/loaders/fixture_loader.py +++ b/src/ethereum_spec_tools/evm_tools/loaders/fixture_loader.py @@ -187,4 +187,8 @@ def json_to_header(self, raw: Any) -> Any: requests_hash = hex_to_bytes32(raw.get("requestsHash")) parameters.append(requests_hash) + if "blockAccessListHash" in raw: + bal_hash = hex_to_bytes32(raw.get("blockAccessListHash")) + parameters.append(bal_hash) + return self.fork.Header(*parameters) diff --git a/src/ethereum_spec_tools/evm_tools/t8n/t8n_types.py b/src/ethereum_spec_tools/evm_tools/t8n/t8n_types.py index 0384cd983b..43aeabdaf9 100644 --- a/src/ethereum_spec_tools/evm_tools/t8n/t8n_types.py +++ b/src/ethereum_spec_tools/evm_tools/t8n/t8n_types.py @@ -333,18 +333,17 @@ def update(self, t8n: "T8N", block_env: Any, block_output: Any) -> None: ) ) - def _block_access_list_to_json(self, bal: Any) -> Any: + @staticmethod + def _block_access_list_to_json(account_changes: Any) -> Any: """ Convert BlockAccessList to JSON format matching the Pydantic models. """ - account_changes = [] - - for account in bal.account_changes: + json_account_changes = [] + for account in account_changes: account_data: Dict[str, Any] = { "address": "0x" + account.address.hex() } - # Add storage changes if present if account.storage_changes: storage_changes = [] for slot_change in account.storage_changes: @@ -364,14 +363,12 @@ def _block_access_list_to_json(self, bal: Any) -> Any: storage_changes.append(slot_data) account_data["storageChanges"] = storage_changes - # Add storage reads if present if account.storage_reads: account_data["storageReads"] = [ int.from_bytes(slot, "big") for slot in account.storage_reads ] - # Add balance changes if present if account.balance_changes: account_data["balanceChanges"] = [ { @@ -381,7 +378,6 @@ def _block_access_list_to_json(self, bal: Any) -> Any: for change in account.balance_changes ] - # Add nonce changes if present if account.nonce_changes: account_data["nonceChanges"] = [ { @@ -391,7 +387,6 @@ def _block_access_list_to_json(self, bal: Any) -> Any: for change in account.nonce_changes ] - # Add code changes if present if account.code_changes: account_data["codeChanges"] = [ { @@ -401,10 +396,9 @@ def _block_access_list_to_json(self, bal: Any) -> Any: for change in account.code_changes ] - account_changes.append(account_data) + json_account_changes.append(account_data) - # return as list directly - return account_changes + return json_account_changes def json_encode_receipts(self) -> Any: """ diff --git a/tests/json_infra/__init__.py b/tests/json_infra/__init__.py index 0b43585434..455665cefd 100644 --- a/tests/json_infra/__init__.py +++ b/tests/json_infra/__init__.py @@ -25,9 +25,13 @@ class _FixtureSource(TypedDict): "fixture_path": "tests/json_infra/fixtures/ethereum_tests", }, "latest_fork_tests": { - "url": "https://github.com/ethereum/execution-spec-tests/releases/download/v5.0.0/fixtures_develop.tar.gz", + "url": "https://github.com/ethereum/execution-spec-tests/releases/download/v5.3.0/fixtures_develop.tar.gz", "fixture_path": "tests/json_infra/fixtures/latest_fork_tests", }, + "amsterdam_tests": { + "url": "https://github.com/ethereum/execution-spec-tests/releases/download/bal%40v1.8.0/fixtures_bal.tar.gz", + "fixture_path": "tests/json_infra/fixtures/amsterdam_tests", + }, } @@ -58,6 +62,7 @@ def _build_eest_test_paths(base_path: str) -> tuple: # Base paths ETHEREUM_TESTS_BASE = _get_fixture_path("ethereum_tests") EEST_TESTS_BASE = _get_fixture_path("latest_fork_tests") +AMSTERDAM_TESTS_BASE = _get_fixture_path("amsterdam_tests") # Ethereum test paths ( @@ -74,6 +79,9 @@ def _build_eest_test_paths(base_path: str) -> tuple: # EEST test paths EEST_BC_TESTS, EEST_STATE_TESTS = _build_eest_test_paths(EEST_TESTS_BASE) +AMSTERDAM_BC_TESTS, AMSTERDAM_STATE_TESTS = _build_eest_test_paths( + AMSTERDAM_TESTS_BASE +) ForkConfig = TypedDict( "ForkConfig", @@ -110,6 +118,11 @@ def _create_fork_config( [STATE_ETHEREUM_TESTS, EEST_STATE_TESTS], ) +AMSTERDAM_DIRS = ( + [AMSTERDAM_BC_TESTS], + [AMSTERDAM_STATE_TESTS], +) + FORKS: Dict[str, ForkConfig] = { **{ json_fork: _create_fork_config(eels_fork, *PRE_CONSTANTINOPLE_DIRS) @@ -140,4 +153,10 @@ def _create_fork_config( ("Osaka", "osaka"), ] }, + **{ + json_fork: _create_fork_config(eels_fork, *AMSTERDAM_DIRS) + for json_fork, eels_fork in [ + ("Amsterdam", "amsterdam"), + ] + }, } diff --git a/tests/json_infra/helpers/load_blockchain_tests.py b/tests/json_infra/helpers/load_blockchain_tests.py index 990e941a35..32a249d103 100644 --- a/tests/json_infra/helpers/load_blockchain_tests.py +++ b/tests/json_infra/helpers/load_blockchain_tests.py @@ -51,8 +51,8 @@ def run_blockchain_st_test(test_case: Dict, load: Load) -> None: if hasattr(genesis_header, "withdrawals_root"): parameters.append(()) - if hasattr(genesis_header, "requests_root"): - parameters.append(()) + if hasattr(genesis_header, "block_access_list_hash"): + parameters.append([]) genesis_block = load.fork.Block(*parameters)