diff --git a/src/ethereum_test_forks/base_fork.py b/src/ethereum_test_forks/base_fork.py index 1bc51683fc..2ddfbe13c2 100644 --- a/src/ethereum_test_forks/base_fork.py +++ b/src/ethereum_test_forks/base_fork.py @@ -174,6 +174,16 @@ def engine_new_payload_beacon_root(cls, block_number: int = 0, timestamp: int = """ pass + @classmethod + @abstractmethod + def engine_forkchoice_updated_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 + # Meta information about the fork @classmethod def name(cls) -> str: diff --git a/src/ethereum_test_forks/forks/forks.py b/src/ethereum_test_forks/forks/forks.py index b943677536..d2f32250e9 100644 --- a/src/ethereum_test_forks/forks/forks.py +++ b/src/ethereum_test_forks/forks/forks.py @@ -91,6 +91,15 @@ def engine_new_payload_beacon_root(cls, block_number: int = 0, timestamp: int = """ return False + @classmethod + def engine_forkchoice_updated_version( + cls, block_number: int = 0, timestamp: int = 0 + ) -> Optional[int]: + """ + At genesis, forkchoice updates cannot be sent 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: """ diff --git a/src/ethereum_test_tools/common/types.py b/src/ethereum_test_tools/common/types.py index d152f9b19f..254a24add4 100644 --- a/src/ethereum_test_tools/common/types.py +++ b/src/ethereum_test_tools/common/types.py @@ -2707,6 +2707,11 @@ class Fixture: to_json=True, ), ) + fcu_version: Optional[int] = field( + json_encoder=JSONEncoder.Field( + name="engineFcuVersion", + ), + ) genesis: FixtureHeader = field( json_encoder=JSONEncoder.Field( name="genesisBlockHeader", diff --git a/src/ethereum_test_tools/filling/fill.py b/src/ethereum_test_tools/filling/fill.py index 9a4d5e9435..b675d701a2 100644 --- a/src/ethereum_test_tools/filling/fill.py +++ b/src/ethereum_test_tools/filling/fill.py @@ -26,7 +26,7 @@ def fill_test( pre, genesis_rlp, genesis = test_spec.make_genesis(t8n, fork) - (blocks, head, alloc) = test_spec.make_blocks( + (blocks, head, alloc, fcu_version) = test_spec.make_blocks( t8n, genesis, pre, @@ -45,6 +45,7 @@ def fill_test( post_state=alloc_to_accounts(alloc), seal_engine=engine, name=test_spec.tag, + fcu_version=fcu_version, ) fixture.fill_info(t8n, spec) diff --git a/src/ethereum_test_tools/spec/base_test.py b/src/ethereum_test_tools/spec/base_test.py index 76c7b95c3e..bbad9fa7ce 100644 --- a/src/ethereum_test_tools/spec/base_test.py +++ b/src/ethereum_test_tools/spec/base_test.py @@ -127,7 +127,7 @@ def make_blocks( fork: Fork, chain_id: int = 1, eips: Optional[List[int]] = None, - ) -> Tuple[List[FixtureBlock], Hash, Dict[str, Any]]: + ) -> Tuple[List[FixtureBlock], Hash, Dict[str, Any], Optional[int]]: """ Generate the blockchain that must be executed sequentially during test. """ diff --git a/src/ethereum_test_tools/spec/blockchain_test.py b/src/ethereum_test_tools/spec/blockchain_test.py index a8a63a700f..0ec70fceba 100644 --- a/src/ethereum_test_tools/spec/blockchain_test.py +++ b/src/ethereum_test_tools/spec/blockchain_test.py @@ -264,7 +264,7 @@ def make_blocks( fork: Fork, chain_id=1, eips: Optional[List[int]] = None, - ) -> Tuple[List[FixtureBlock], Hash, Dict[str, Any]]: + ) -> Tuple[List[FixtureBlock], Hash, Dict[str, Any], Optional[int]]: """ Create a block list from the blockchain test definition. Performs checks against the expected behavior of the test. @@ -273,6 +273,9 @@ def make_blocks( alloc = to_json(pre) env = Environment.from_parent_header(genesis) blocks: List[FixtureBlock] = [] + fcu_version: Optional[int] = None + last_valid: Optional[FixtureHeader] = None + head = genesis.hash if genesis.hash is not None else Hash(0) for block in self.blocks: fixture_block, env, alloc, head = self.make_block( @@ -286,6 +289,14 @@ def make_blocks( eips=eips, ) blocks.append(fixture_block) + if block.exception is None: + last_valid = fixture_block.block_header + + if not self.base_test_config.disable_hive and last_valid is not None: + fcu_version = fork.engine_forkchoice_updated_version( + block_number=last_valid.number, + timestamp=last_valid.timestamp, + ) try: verify_post_alloc(self.post, alloc) @@ -293,7 +304,7 @@ def make_blocks( print_traces(t8n.get_traces()) raise e - return (blocks, head, alloc) + return (blocks, head, alloc, fcu_version) BlockchainTestSpec = Callable[[str], Generator[BlockchainTest, None, None]] diff --git a/src/ethereum_test_tools/spec/state_test.py b/src/ethereum_test_tools/spec/state_test.py index 64476e6b10..9e7145ccc1 100644 --- a/src/ethereum_test_tools/spec/state_test.py +++ b/src/ethereum_test_tools/spec/state_test.py @@ -127,7 +127,7 @@ def make_blocks( fork: Fork, chain_id=1, eips: Optional[List[int]] = None, - ) -> Tuple[List[FixtureBlock], Hash, Dict[str, Any]]: + ) -> Tuple[List[FixtureBlock], Hash, Dict[str, Any], Optional[int]]: """ Create a block from the state test definition. Performs checks against the expected behavior of the test. @@ -178,15 +178,18 @@ def make_blocks( withdrawals=env.withdrawals, ) + # Hive specific fields new_payload: FixtureEngineNewPayload | None = None + fcu_version: int | None = None if not self.base_test_config.disable_hive: new_payload = FixtureEngineNewPayload.from_fixture_header( fork=fork, header=header, transactions=txs, withdrawals=env.withdrawals, - error_code=self.engine_api_error_code, + error_code=None, ) + fcu_version = fork.engine_forkchoice_updated_version(header.number, header.timestamp) return ( [ @@ -201,6 +204,7 @@ def make_blocks( ], header.hash, alloc, + fcu_version, ) diff --git a/whitelist.txt b/whitelist.txt index 637cc77350..287b658776 100644 --- a/whitelist.txt +++ b/whitelist.txt @@ -84,6 +84,7 @@ extcodesize fn fname forkchoice +fcu formatOnSave formatter fromhex