From 263f36d723f85249f6317c4bf2b8cd8aad791761 Mon Sep 17 00:00:00 2001 From: spencer-tb Date: Wed, 1 Jul 2026 15:51:31 +0100 Subject: [PATCH 1/5] feat(tests): EIP-7928 reject newPayload with malformed or missing block access list --- .../src/execution_testing/specs/blockchain.py | 52 +++++++++++-- .../specs/tests/test_types.py | 27 +++++++ .../test_block_access_lists_invalid.py | 75 +++++++++++++++++++ 3 files changed, 148 insertions(+), 6 deletions(-) diff --git a/packages/testing/src/execution_testing/specs/blockchain.py b/packages/testing/src/execution_testing/specs/blockchain.py index 39fc641c3fb..bd722ce3eed 100644 --- a/packages/testing/src/execution_testing/specs/blockchain.py +++ b/packages/testing/src/execution_testing/specs/blockchain.py @@ -306,6 +306,8 @@ class Block(Header): """Post state for verification after block execution in BlockchainTest""" block_access_list: Bytes | None = Field(None) """EIP-7928: Block-level access lists (serialized).""" + engine_new_payload_block_access_list: Removable | Bytes | None = None + """EIP-7928: override only the engine newPayload blockAccessList field.""" expected_gas_used: int | None = None """Expected gas used for the block.""" @@ -385,6 +387,11 @@ def set_environment(self, env: Environment) -> Environment: class BuiltBlock(CamelModel): """Model that contains all properties to build a full block or payload.""" + model_config = ConfigDict( + **CamelModel.model_config, + arbitrary_types_allowed=True, + ) + header: FixtureHeader env: Environment alloc: LazyAlloc @@ -399,6 +406,7 @@ class BuiltBlock(CamelModel): rlp_modifier: Header | None = None fork: Fork block_access_list: BlockAccessList | None + engine_new_payload_block_access_list: Removable | Bytes | None = None def get_fixture_block( self, *, include_receipts: bool = True @@ -483,8 +491,36 @@ def derive_engine_payload_modifier( ) return None + @staticmethod + def engine_payload_bal_override_modifier( + override: Removable | Bytes | None, + ) -> "FixtureExecutionPayloadModifier | None": + """ + Map a block access list override to an engine payload modifier. + + Raw `Bytes` are sent verbatim as the payload body (e.g. `0x` for the + invalid empty-byte-string encoding); a `Removable` omits the field. + `None` means no override. + """ + if override is None: + return None + return FixtureExecutionPayloadModifier( + block_access_list=( + FixtureExecutionPayloadModifier.REMOVE_FIELD + if isinstance(override, Removable) + else override + ), + ) + def get_fixture_engine_new_payload(self) -> FixtureEngineNewPayload: """Get a FixtureEngineNewPayload from the built block.""" + # An explicit block access list override targets only the engine + # payload body and takes precedence over the header-derived modifier. + execution_payload_modifier = self.engine_payload_bal_override_modifier( + self.engine_new_payload_block_access_list + ) or self.derive_engine_payload_modifier( + self.rlp_modifier, self.block_access_list + ) return FixtureEngineNewPayload.from_fixture_header( fork=self.fork, header=self.header, @@ -494,9 +530,7 @@ def get_fixture_engine_new_payload(self) -> FixtureEngineNewPayload: block_access_list=self.block_access_list.rlp if self.block_access_list else None, - execution_payload_modifier=self.derive_engine_payload_modifier( - self.rlp_modifier, self.block_access_list - ), + execution_payload_modifier=execution_payload_modifier, validation_error=self.expected_exception, error_code=self.engine_api_error_code, ) @@ -850,6 +884,9 @@ def generate_block_data( rlp_modifier=block.rlp_modifier, fork=fork, block_access_list=bal, + engine_new_payload_block_access_list=( + block.engine_new_payload_block_access_list + ), ) try: @@ -861,6 +898,7 @@ def generate_block_data( and block.rlp_modifier is None and block.requests is None and not block.skip_exception_verification + and block.engine_new_payload_block_access_list is None and not ( block.expected_block_access_list is not None and block.expected_block_access_list._modifier is not None @@ -871,9 +909,11 @@ def generate_block_data( # exceptions. - No RLP modifier was specified, because the # modifier is what normally produces the block exception. - No # requests were specified, because modified requests are also - # what normally produces the block exception. - No BAL modifier - # was specified, because modified BAL also produces block - # exceptions. + # what normally produces the block exception. - No engine + # payload BAL override was specified, because it corrupts only + # the engine payload after the transition tool has run. - No + # BAL modifier was specified, because modified BAL also + # produces block exceptions. built_block.verify_block_exception( transition_tool_exceptions_reliable=t8n.exception_mapper.reliable, ) diff --git a/packages/testing/src/execution_testing/specs/tests/test_types.py b/packages/testing/src/execution_testing/specs/tests/test_types.py index f5c43cb5fe9..088ff4adc52 100644 --- a/packages/testing/src/execution_testing/specs/tests/test_types.py +++ b/packages/testing/src/execution_testing/specs/tests/test_types.py @@ -210,3 +210,30 @@ def test_inject_bal_hash_on_post_fork_leaves_body_alone(self) -> None: ) is None ) + + +class TestEnginePayloadBalOverrideModifier: + """ + Verify the explicit engine-payload `block_access_list` override that + negative tests use to corrupt only the `engine_newPayload` body. + """ + + def test_no_override_returns_none(self) -> None: + """No override → no engine payload modifier.""" + assert BuiltBlock.engine_payload_bal_override_modifier(None) is None + + def test_empty_bytes_override_sends_raw_body(self) -> None: + """Raw `Bytes` (e.g. the invalid `0x`) are sent verbatim.""" + modifier = BuiltBlock.engine_payload_bal_override_modifier(Bytes(b"")) + assert isinstance(modifier, FixtureExecutionPayloadModifier) + assert modifier.block_access_list == Bytes(b"") + + def test_remove_field_override_omits_body(self) -> None: + """A `Removable` omits the payload field entirely.""" + modifier = BuiltBlock.engine_payload_bal_override_modifier( + Header.REMOVE_FIELD + ) + assert isinstance(modifier, FixtureExecutionPayloadModifier) + assert modifier.block_access_list is ( + FixtureExecutionPayloadModifier.REMOVE_FIELD + ) diff --git a/tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_invalid.py b/tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_invalid.py index 52d366019d8..4d1e4a441be 100644 --- a/tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_invalid.py +++ b/tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_invalid.py @@ -21,7 +21,9 @@ BlockAccessListExpectation, BlockchainTestFiller, BlockException, + Bytes, EIPChecklist, + EngineAPIError, Environment, Fork, Hash, @@ -1627,3 +1629,76 @@ def test_bal_invalid_extraneous_coinbase( ) ], ) + + +@pytest.mark.valid_from("Amsterdam") +@pytest.mark.blockchain_test_engine_only +@pytest.mark.exception_test +def test_bal_invalid_engine_payload_empty_bytes_encoding( + blockchain_test: BlockchainTestFiller, + pre: Alloc, +) -> None: + """ + Reject a `newPayload` whose `blockAccessList` is the empty byte string + `0x` rather than a valid RLP list (an empty BAL is `0xc0`). + """ + sender = pre.fund_eoa(amount=10**18) + receiver = pre.fund_eoa(amount=0) + + tx = Transaction( + sender=sender, + to=receiver, + value=10**15, + gas_limit=21_000, + ) + + blockchain_test( + pre=pre, + post={ + sender: Account(balance=10**18, nonce=0), + receiver: None, + }, + blocks=[ + Block( + txs=[tx], + engine_new_payload_block_access_list=Bytes(b""), + exception=BlockException.INVALID_BLOCK_ACCESS_LIST, + engine_api_error_code=EngineAPIError.InvalidParams, + ) + ], + ) + + +@pytest.mark.valid_from("Amsterdam") +@pytest.mark.blockchain_test_engine_only +@pytest.mark.exception_test +def test_bal_invalid_engine_payload_missing_field( + blockchain_test: BlockchainTestFiller, + pre: Alloc, +) -> None: + """Reject a `newPayload` whose `blockAccessList` field is omitted.""" + sender = pre.fund_eoa(amount=10**18) + receiver = pre.fund_eoa(amount=0) + + tx = Transaction( + sender=sender, + to=receiver, + value=10**15, + gas_limit=21_000, + ) + + blockchain_test( + pre=pre, + post={ + sender: Account(balance=10**18, nonce=0), + receiver: None, + }, + blocks=[ + Block( + txs=[tx], + engine_new_payload_block_access_list=Header.REMOVE_FIELD, + exception=BlockException.INCORRECT_BLOCK_FORMAT, + engine_api_error_code=EngineAPIError.InvalidParams, + ) + ], + ) From 07defdaca8b1d6fd98326dda8eda198610f4b5ad Mon Sep 17 00:00:00 2001 From: spencer-tb Date: Thu, 2 Jul 2026 19:30:03 +0100 Subject: [PATCH 2/5] feat(tests): parametrize invalid BAL engine payload encodings --- .../test_block_access_lists_invalid.py | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_invalid.py b/tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_invalid.py index 4d1e4a441be..f8de53c785b 100644 --- a/tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_invalid.py +++ b/tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_invalid.py @@ -1634,13 +1634,24 @@ def test_bal_invalid_extraneous_coinbase( @pytest.mark.valid_from("Amsterdam") @pytest.mark.blockchain_test_engine_only @pytest.mark.exception_test -def test_bal_invalid_engine_payload_empty_bytes_encoding( +@pytest.mark.parametrize( + "invalid_bal_payload", + [ + pytest.param(b"", id="empty_byte_string"), + pytest.param(b"\x80", id="rlp_non_list"), + pytest.param(b"\xc1", id="rlp_truncated_list"), + ], +) +def test_bal_invalid_engine_payload_encoding( blockchain_test: BlockchainTestFiller, pre: Alloc, + invalid_bal_payload: bytes, ) -> None: """ - Reject a `newPayload` whose `blockAccessList` is the empty byte string - `0x` rather than a valid RLP list (an empty BAL is `0xc0`). + Reject a `newPayload` whose `blockAccessList` does not decode as an RLP + list: the empty byte string `0x` (an empty BAL is `0xc0`), the RLP + empty byte string `0x80` (valid RLP but not a list), or a truncated + list header `0xc1`. """ sender = pre.fund_eoa(amount=10**18) receiver = pre.fund_eoa(amount=0) @@ -1661,7 +1672,9 @@ def test_bal_invalid_engine_payload_empty_bytes_encoding( blocks=[ Block( txs=[tx], - engine_new_payload_block_access_list=Bytes(b""), + engine_new_payload_block_access_list=Bytes( + invalid_bal_payload + ), exception=BlockException.INVALID_BLOCK_ACCESS_LIST, engine_api_error_code=EngineAPIError.InvalidParams, ) From 541b0a47114508a9c112d37145c35a00f3df446c Mon Sep 17 00:00:00 2001 From: spencer-tb Date: Thu, 2 Jul 2026 19:43:35 +0100 Subject: [PATCH 3/5] feat(tests): reject pre-fork engine payload carrying a block access list --- .../test_fork_transition.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/amsterdam/eip7928_block_level_access_lists/test_fork_transition.py b/tests/amsterdam/eip7928_block_level_access_lists/test_fork_transition.py index 8e524c8e68e..b4887edc1ca 100644 --- a/tests/amsterdam/eip7928_block_level_access_lists/test_fork_transition.py +++ b/tests/amsterdam/eip7928_block_level_access_lists/test_fork_transition.py @@ -11,6 +11,7 @@ BlockAccessListExpectation, BlockchainTestFiller, BlockException, + Bytes, EIPChecklist, EngineAPIError, Environment, @@ -118,6 +119,42 @@ def test_invalid_pre_fork_block_with_bal_hash_field( ) +@pytest.mark.valid_at_transition_to("Amsterdam") +@pytest.mark.blockchain_test_engine_only +@pytest.mark.exception_test +def test_bal_invalid_engine_payload_field_before_fork( + blockchain_test: BlockchainTestFiller, + pre: Alloc, +) -> None: + """ + Reject a pre-Amsterdam `newPayload` that carries a `blockAccessList`. + + The block and its header are otherwise valid, so the spurious payload + field is the only defect: clients that silently drop unknown + `newPayloadV4` fields would answer VALID and must fail this test. + """ + sender = pre.fund_eoa() + receiver = pre.fund_eoa(amount=0) + + tx = Transaction(sender=sender, to=receiver, value=100, gas_price=10) + + blockchain_test( + pre=pre, + post={}, + blocks=[ + Block( + timestamp=FORK_TIMESTAMP - 1, + txs=[tx], + # A valid empty-BAL encoding: field presence alone, not + # decodability, must trigger the rejection. + engine_new_payload_block_access_list=Bytes(b"\xc0"), + exception=BlockException.INCORRECT_BLOCK_FORMAT, + engine_api_error_code=EngineAPIError.InvalidParams, + ), + ], + ) + + @EIPChecklist.BlockHeaderField.Test.ForkTransition.After() @pytest.mark.valid_at_transition_to("Amsterdam") @pytest.mark.exception_test From 745d50aadbc3d6b0e06476086f0ce1cf4620ce1b Mon Sep 17 00:00:00 2001 From: marioevz Date: Thu, 2 Jul 2026 16:32:11 -0600 Subject: [PATCH 4/5] fix(test-specs): Review suggestions --- .../src/execution_testing/specs/blockchain.py | 50 +++-------- .../specs/tests/test_types.py | 83 ++++++++++++------- .../test_block_access_lists_invalid.py | 48 +---------- .../test_fork_transition.py | 4 +- 4 files changed, 69 insertions(+), 116 deletions(-) diff --git a/packages/testing/src/execution_testing/specs/blockchain.py b/packages/testing/src/execution_testing/specs/blockchain.py index bd722ce3eed..c2f9d47aebc 100644 --- a/packages/testing/src/execution_testing/specs/blockchain.py +++ b/packages/testing/src/execution_testing/specs/blockchain.py @@ -306,7 +306,7 @@ class Block(Header): """Post state for verification after block execution in BlockchainTest""" block_access_list: Bytes | None = Field(None) """EIP-7928: Block-level access lists (serialized).""" - engine_new_payload_block_access_list: Removable | Bytes | None = None + engine_new_payload_block_access_list: Bytes | None = None """EIP-7928: override only the engine newPayload blockAccessList field.""" expected_gas_used: int | None = None """Expected gas used for the block.""" @@ -406,7 +406,7 @@ class BuiltBlock(CamelModel): rlp_modifier: Header | None = None fork: Fork block_access_list: BlockAccessList | None - engine_new_payload_block_access_list: Removable | Bytes | None = None + engine_new_payload_block_access_list: Bytes | None = None def get_fixture_block( self, *, include_receipts: bool = True @@ -457,10 +457,8 @@ def get_block_rlp(self) -> Bytes: """Get the RLP of the block.""" return self.get_fixture_block().rlp - @staticmethod - def derive_engine_payload_modifier( - rlp_modifier: Header | None, - block_access_list: BlockAccessList | None, + def engine_payload_modifier( + self, ) -> "FixtureExecutionPayloadModifier | None": """ Propagate ``rlp_modifier``'s header changes to the engine payload. @@ -470,9 +468,13 @@ def derive_engine_payload_modifier( the ``block_access_list`` body. So a header modifier that touches the BAL hash needs to drive a matching change on the payload body. """ - if rlp_modifier is None: + if self.engine_new_payload_block_access_list is not None: + return FixtureExecutionPayloadModifier( + block_access_list=self.engine_new_payload_block_access_list, + ) + if self.rlp_modifier is None: return None - bal_hash_override = rlp_modifier.block_access_list_hash + bal_hash_override = self.rlp_modifier.block_access_list_hash if bal_hash_override is None: return None if bal_hash_override is Header.REMOVE_FIELD: @@ -485,42 +487,14 @@ def derive_engine_payload_modifier( # payload by forcing a body to be present. Its exact value is # irrelevant for negative tests — a non-``None`` value is enough to # make a payload-version mismatch detectable. - if block_access_list is None: + if self.block_access_list is None: return FixtureExecutionPayloadModifier( block_access_list=Bytes(b""), ) return None - @staticmethod - def engine_payload_bal_override_modifier( - override: Removable | Bytes | None, - ) -> "FixtureExecutionPayloadModifier | None": - """ - Map a block access list override to an engine payload modifier. - - Raw `Bytes` are sent verbatim as the payload body (e.g. `0x` for the - invalid empty-byte-string encoding); a `Removable` omits the field. - `None` means no override. - """ - if override is None: - return None - return FixtureExecutionPayloadModifier( - block_access_list=( - FixtureExecutionPayloadModifier.REMOVE_FIELD - if isinstance(override, Removable) - else override - ), - ) - def get_fixture_engine_new_payload(self) -> FixtureEngineNewPayload: """Get a FixtureEngineNewPayload from the built block.""" - # An explicit block access list override targets only the engine - # payload body and takes precedence over the header-derived modifier. - execution_payload_modifier = self.engine_payload_bal_override_modifier( - self.engine_new_payload_block_access_list - ) or self.derive_engine_payload_modifier( - self.rlp_modifier, self.block_access_list - ) return FixtureEngineNewPayload.from_fixture_header( fork=self.fork, header=self.header, @@ -530,7 +504,7 @@ def get_fixture_engine_new_payload(self) -> FixtureEngineNewPayload: block_access_list=self.block_access_list.rlp if self.block_access_list else None, - execution_payload_modifier=execution_payload_modifier, + execution_payload_modifier=self.engine_payload_modifier(), validation_error=self.expected_exception, error_code=self.engine_api_error_code, ) diff --git a/packages/testing/src/execution_testing/specs/tests/test_types.py b/packages/testing/src/execution_testing/specs/tests/test_types.py index 088ff4adc52..838f98f68b5 100644 --- a/packages/testing/src/execution_testing/specs/tests/test_types.py +++ b/packages/testing/src/execution_testing/specs/tests/test_types.py @@ -9,10 +9,14 @@ Hash, HeaderNonce, ) +from execution_testing.client_clis import Result +from execution_testing.client_clis.cli_types import LazyAllocStr from execution_testing.fixtures.blockchain import ( FixtureExecutionPayloadModifier, FixtureHeader, ) +from execution_testing.forks import Amsterdam +from execution_testing.test_types import Environment from execution_testing.test_types.block_access_list import BlockAccessList from ..blockchain import BuiltBlock, Header @@ -39,6 +43,15 @@ excess_blob_gas=1, # hash=Hash(1), ) +result_empty = Result( + state_root=0, + transactions_trie=0, + receipts_root=0, + logs_hash=0, + logs_bloom=0, + receipts=[], + gas_used=0, +) @pytest.mark.parametrize( @@ -145,6 +158,30 @@ def test_fixture_header_join( assert modifier.apply(fixture_header) == fixture_header_expected +def built_block( + *, + rlp_modifier: Header | None = None, + block_access_list: BlockAccessList | None = None, + engine_new_payload_block_access_list: Bytes | None = None, +) -> BuiltBlock: + """Generate a dummy built block with all default values.""" + return BuiltBlock( + header=fixture_header_ones, + env=Environment(), + alloc=LazyAllocStr(raw="", _state_root=Hash(0)), + state_root=Hash(0), + txs=[], + ommers=[], + withdrawals=None, + requests=None, + result=result_empty, + fork=Amsterdam, + rlp_modifier=rlp_modifier, + block_access_list=block_access_list, + engine_new_payload_block_access_list=engine_new_payload_block_access_list, + ) + + class TestDeriveEnginePayloadModifier: """ Verify the auto-propagation from ``rlp_modifier``'s header-only changes @@ -156,29 +193,30 @@ class TestDeriveEnginePayloadModifier: def test_no_rlp_modifier_returns_none(self) -> None: """No modifier → no engine payload override.""" assert ( - BuiltBlock.derive_engine_payload_modifier( + built_block( rlp_modifier=None, block_access_list=None, - ) + engine_new_payload_block_access_list=None, + ).engine_payload_modifier() is None ) def test_rlp_modifier_unrelated_field_returns_none(self) -> None: """A modifier that doesn't touch BAL hash leaves the payload alone.""" assert ( - BuiltBlock.derive_engine_payload_modifier( + built_block( rlp_modifier=Header(state_root=Hash(100)), block_access_list=None, - ) + ).engine_payload_modifier() is None ) def test_remove_bal_hash_removes_body_from_payload(self) -> None: """Removing the header's BAL hash also removes the payload body.""" - modifier = BuiltBlock.derive_engine_payload_modifier( + modifier = built_block( rlp_modifier=Header(block_access_list_hash=Header.REMOVE_FIELD), block_access_list=BlockAccessList(), - ) + ).engine_payload_modifier() assert isinstance(modifier, FixtureExecutionPayloadModifier) assert modifier.block_access_list is ( FixtureExecutionPayloadModifier.REMOVE_FIELD @@ -190,10 +228,10 @@ def test_inject_bal_hash_on_pre_fork_adds_body(self) -> None: triggers a body to be added to the engine payload, so a payload- version mismatch is detectable. """ - modifier = BuiltBlock.derive_engine_payload_modifier( + modifier = built_block( rlp_modifier=Header(block_access_list_hash=Hash(0)), block_access_list=None, - ) + ).engine_payload_modifier() assert isinstance(modifier, FixtureExecutionPayloadModifier) assert modifier.block_access_list == Bytes(b"") @@ -204,36 +242,17 @@ def test_inject_bal_hash_on_post_fork_leaves_body_alone(self) -> None: what triggers the client rejection in that scenario. """ assert ( - BuiltBlock.derive_engine_payload_modifier( + built_block( rlp_modifier=Header(block_access_list_hash=Hash(0)), block_access_list=BlockAccessList(), - ) + ).engine_payload_modifier() is None ) - -class TestEnginePayloadBalOverrideModifier: - """ - Verify the explicit engine-payload `block_access_list` override that - negative tests use to corrupt only the `engine_newPayload` body. - """ - - def test_no_override_returns_none(self) -> None: - """No override → no engine payload modifier.""" - assert BuiltBlock.engine_payload_bal_override_modifier(None) is None - def test_empty_bytes_override_sends_raw_body(self) -> None: """Raw `Bytes` (e.g. the invalid `0x`) are sent verbatim.""" - modifier = BuiltBlock.engine_payload_bal_override_modifier(Bytes(b"")) + modifier = built_block( + engine_new_payload_block_access_list=Bytes(b"") + ).engine_payload_modifier() assert isinstance(modifier, FixtureExecutionPayloadModifier) assert modifier.block_access_list == Bytes(b"") - - def test_remove_field_override_omits_body(self) -> None: - """A `Removable` omits the payload field entirely.""" - modifier = BuiltBlock.engine_payload_bal_override_modifier( - Header.REMOVE_FIELD - ) - assert isinstance(modifier, FixtureExecutionPayloadModifier) - assert modifier.block_access_list is ( - FixtureExecutionPayloadModifier.REMOVE_FIELD - ) diff --git a/tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_invalid.py b/tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_invalid.py index f8de53c785b..05a3ea10001 100644 --- a/tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_invalid.py +++ b/tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_invalid.py @@ -1653,20 +1653,15 @@ def test_bal_invalid_engine_payload_encoding( empty byte string `0x80` (valid RLP but not a list), or a truncated list header `0xc1`. """ - sender = pre.fund_eoa(amount=10**18) - receiver = pre.fund_eoa(amount=0) + sender = pre.fund_eoa() + receiver = pre.nonexistent_account() - tx = Transaction( - sender=sender, - to=receiver, - value=10**15, - gas_limit=21_000, - ) + tx = Transaction(sender=sender, to=receiver) blockchain_test( pre=pre, post={ - sender: Account(balance=10**18, nonce=0), + sender: Account(nonce=0), receiver: None, }, blocks=[ @@ -1680,38 +1675,3 @@ def test_bal_invalid_engine_payload_encoding( ) ], ) - - -@pytest.mark.valid_from("Amsterdam") -@pytest.mark.blockchain_test_engine_only -@pytest.mark.exception_test -def test_bal_invalid_engine_payload_missing_field( - blockchain_test: BlockchainTestFiller, - pre: Alloc, -) -> None: - """Reject a `newPayload` whose `blockAccessList` field is omitted.""" - sender = pre.fund_eoa(amount=10**18) - receiver = pre.fund_eoa(amount=0) - - tx = Transaction( - sender=sender, - to=receiver, - value=10**15, - gas_limit=21_000, - ) - - blockchain_test( - pre=pre, - post={ - sender: Account(balance=10**18, nonce=0), - receiver: None, - }, - blocks=[ - Block( - txs=[tx], - engine_new_payload_block_access_list=Header.REMOVE_FIELD, - exception=BlockException.INCORRECT_BLOCK_FORMAT, - engine_api_error_code=EngineAPIError.InvalidParams, - ) - ], - ) diff --git a/tests/amsterdam/eip7928_block_level_access_lists/test_fork_transition.py b/tests/amsterdam/eip7928_block_level_access_lists/test_fork_transition.py index b4887edc1ca..a9df078ea58 100644 --- a/tests/amsterdam/eip7928_block_level_access_lists/test_fork_transition.py +++ b/tests/amsterdam/eip7928_block_level_access_lists/test_fork_transition.py @@ -134,9 +134,9 @@ def test_bal_invalid_engine_payload_field_before_fork( `newPayloadV4` fields would answer VALID and must fail this test. """ sender = pre.fund_eoa() - receiver = pre.fund_eoa(amount=0) + receiver = pre.nonexistent_account() - tx = Transaction(sender=sender, to=receiver, value=100, gas_price=10) + tx = Transaction(sender=sender, to=receiver, value=100) blockchain_test( pre=pre, From 9ce11b8f4fc71f0ba07abf0ca93a5f5a205430ca Mon Sep 17 00:00:00 2001 From: marioevz Date: Thu, 2 Jul 2026 16:34:07 -0600 Subject: [PATCH 5/5] fix(test-specs): Review suggestions --- packages/testing/src/execution_testing/specs/blockchain.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/testing/src/execution_testing/specs/blockchain.py b/packages/testing/src/execution_testing/specs/blockchain.py index c2f9d47aebc..c22cbd945cc 100644 --- a/packages/testing/src/execution_testing/specs/blockchain.py +++ b/packages/testing/src/execution_testing/specs/blockchain.py @@ -387,11 +387,6 @@ def set_environment(self, env: Environment) -> Environment: class BuiltBlock(CamelModel): """Model that contains all properties to build a full block or payload.""" - model_config = ConfigDict( - **CamelModel.model_config, - arbitrary_types_allowed=True, - ) - header: FixtureHeader env: Environment alloc: LazyAlloc