diff --git a/packages/testing/src/execution_testing/client_clis/clis/reth.py b/packages/testing/src/execution_testing/client_clis/clis/reth.py index 5fca3242ba4..d26ec1c671c 100644 --- a/packages/testing/src/execution_testing/client_clis/clis/reth.py +++ b/packages/testing/src/execution_testing/client_clis/clis/reth.py @@ -118,6 +118,7 @@ class RethExceptionMapper(ExceptionMapper): # BAL Exceptions BlockException.INVALID_BAL_HASH: (r"block access list hash mismatch"), BlockException.INVALID_BLOCK_ACCESS_LIST: ( + r"failed to decode block access list|" r"block access list hash mismatch|" r"BAL rejection: FinalHashMismatch|" r"Bal error: Account .* not found in BAL|" diff --git a/packages/testing/src/execution_testing/specs/blockchain.py b/packages/testing/src/execution_testing/specs/blockchain.py index 6df509fd78b..651458b60d8 100644 --- a/packages/testing/src/execution_testing/specs/blockchain.py +++ b/packages/testing/src/execution_testing/specs/blockchain.py @@ -560,12 +560,13 @@ def engine_payload_modifier( self, ) -> "FixtureExecutionPayloadModifier | None": """ - Propagate ``rlp_modifier``'s header changes to the engine payload. + Propagate header changes and explicit overrides to the engine payload. The engine ``ExecutionPayload`` schema does not carry - ``block_access_list_hash`` directly; the equivalent payload field is - the ``block_access_list`` body. So a header modifier that touches the - BAL hash needs to drive a matching change on the payload body. + ``block_access_list_hash`` directly; the corresponding payload + field is the ``block_access_list`` body. Removing the header hash + therefore removes the payload field. Adding or changing the hash + is reflected in ``blockHash`` without synthesizing a BAL body. """ if self.engine_new_payload_slot_number is not None: return FixtureExecutionPayloadModifier( @@ -586,14 +587,9 @@ def engine_payload_modifier( FixtureExecutionPayloadModifier.REMOVE_FIELD ), ) - # The user injected a header BAL hash; mirror that on the engine - # 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 self.block_access_list is None: - return FixtureExecutionPayloadModifier( - block_access_list=Bytes(b""), - ) + # Do not introduce an unsupported pre-fork payload field when the + # intended defect is the header hash. API field-presence tests use + # an explicit engine_new_payload_block_access_list override. return None def get_fixture_engine_new_payload(self) -> FixtureEngineNewPayload: 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 1816716fae7..9544c9517c5 100644 --- a/packages/testing/src/execution_testing/specs/tests/test_types.py +++ b/packages/testing/src/execution_testing/specs/tests/test_types.py @@ -18,7 +18,7 @@ FixtureExecutionPayloadModifier, FixtureHeader, ) -from execution_testing.forks import Amsterdam +from execution_testing.forks import Amsterdam, Fork, Osaka from execution_testing.test_types import Alloc, Environment from execution_testing.test_types.block_access_list import ( BlockAccessList, @@ -166,6 +166,7 @@ def test_fixture_header_join( def built_block( *, + fork: Fork = Amsterdam, rlp_modifier: Header | None = None, block_access_list: BlockAccessList | None = None, engine_new_payload_block_access_list: Bytes | None = None, @@ -181,7 +182,7 @@ def built_block( withdrawals=None, requests=None, result=result_empty, - fork=Amsterdam, + fork=fork, rlp_modifier=rlp_modifier, block_access_list=block_access_list, engine_new_payload_block_access_list=engine_new_payload_block_access_list, @@ -228,18 +229,19 @@ def test_remove_bal_hash_removes_body_from_payload(self) -> None: FixtureExecutionPayloadModifier.REMOVE_FIELD ) - def test_inject_bal_hash_on_pre_fork_adds_body(self) -> None: + def test_inject_bal_hash_on_pre_fork_keeps_body_absent(self) -> None: """ - Injecting a header BAL hash on a block that has no body (pre-fork) - triggers a body to be added to the engine payload, so a payload- - version mismatch is detectable. + Keep pre-fork payload parameters valid when only the header hash + is corrupted, so rejection tests the block hash alone. """ - 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"") + assert ( + built_block( + fork=Osaka, + rlp_modifier=Header(block_access_list_hash=Hash(0)), + block_access_list=None, + ).engine_payload_modifier() + is None + ) def test_inject_bal_hash_on_post_fork_leaves_body_alone(self) -> None: """ @@ -255,10 +257,12 @@ def test_inject_bal_hash_on_post_fork_leaves_body_alone(self) -> None: is None ) - def test_empty_bytes_override_sends_raw_body(self) -> None: + @pytest.mark.parametrize("fork", [Osaka, Amsterdam]) + def test_empty_bytes_override_sends_raw_body(self, fork: Fork) -> None: """Raw `Bytes` (e.g. the invalid `0x`) are sent verbatim.""" modifier = built_block( - engine_new_payload_block_access_list=Bytes(b"") + fork=fork, + engine_new_payload_block_access_list=Bytes(b""), ).engine_payload_modifier() assert isinstance(modifier, FixtureExecutionPayloadModifier) assert modifier.block_access_list == Bytes(b"") diff --git a/tests/amsterdam/eip7928_block_level_access_lists/spec.py b/tests/amsterdam/eip7928_block_level_access_lists/spec.py index 8b5a257a098..4e1676f7d8e 100644 --- a/tests/amsterdam/eip7928_block_level_access_lists/spec.py +++ b/tests/amsterdam/eip7928_block_level_access_lists/spec.py @@ -13,5 +13,5 @@ class ReferenceSpec: ref_spec_7928 = ReferenceSpec( git_path="EIPS/eip-7928.md", - version="f834f0004aa5110a5f1ac0d6b80e3dc4b842d040", + version="d2a64c2d4cc44f2f507577d0ebfb110dcc21d358", ) diff --git a/tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists.py b/tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists.py index a714a8b1191..3c113b83724 100644 --- a/tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists.py +++ b/tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists.py @@ -782,7 +782,7 @@ def test_bal_2930_account_listed_but_untouched( def test_bal_2930_precompile_listed_but_untouched( pre: Alloc, state_test: StateTestFiller, - precompile: int, + precompile: Address, ) -> None: """ Ensure a precompile named in the access list but never called stays @@ -1068,7 +1068,7 @@ def test_bal_zero_value_transfer( ], ), # Include the address; omit from balance_changes. - bob: BalAccountExpectation(balance_changes=[]), + bob: BalAccountExpectation.empty(), } ), ) @@ -2103,7 +2103,7 @@ def test_bal_precompile_funded( def test_bal_precompile_call_opcode( pre: Alloc, blockchain_test: BlockchainTestFiller, - precompile: int, + precompile: Address, call_opcode: Op, ) -> None: """ @@ -2182,10 +2182,10 @@ def test_bal_nonexistent_value_transfer( BalBalanceChange( block_access_index=1, post_balance=value ) - ] - if value > 0 - else [], - ), + ], + ) + if value > 0 + else BalAccountExpectation.empty(), } ), ) @@ -2360,10 +2360,10 @@ def test_bal_nonexistent_account_access_value_transfer( block_access_index=1, post_balance=bob_final_balance, ) - ] - if bob_has_balance_change - else [], - ), + ], + ) + if bob_has_balance_change + else BalAccountExpectation.empty(), } ), ) @@ -4278,7 +4278,8 @@ def test_bal_gas_limit_boundary( # charge that would otherwise inflate the tx's gas needs past # the BAL-sized ``block_gas_limit``. bob = pre.fund_eoa(amount=1) - # alice (sender) + bob (recipient) + coinbase (EIP-3651 warm). + # alice (sender) + bob (recipient) + coinbase, touched by the + # zero priority-fee credit. extra_items += 3 txs.append( Transaction( @@ -4400,7 +4401,8 @@ def test_bal_gas_limit_boundary_storage_keys( for _ in range(2) ] - # alice + counter + coinbase (EIP-3651 warm), then one item per key. + # alice + counter + coinbase (touched by the zero priority-fee + # credit), then one item per key. storage_keys = [written_slot, read_slot] total_items = fork.empty_block_bal_item_count() + 3 + len(storage_keys) gas_limit = ( 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 1f387946943..0c6023df17c 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 @@ -1890,25 +1890,33 @@ def test_bal_invalid_extraneous_coinbase( pytest.param(b"\xc1", id="rlp_truncated_list"), ], ) +@pytest.mark.parametrize("header_commits_to", ["canonical_rlp", "payload_rlp"]) def test_bal_invalid_engine_payload_encoding( blockchain_test: BlockchainTestFiller, pre: Alloc, invalid_bal_payload: bytes, + header_commits_to: str, ) -> None: """ - 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`. - - The field is present but not a valid encoding, so the payload is - invalid rather than the request being malformed. + Reject malformed BAL RLP with both matching and mismatched header + commitments, so a block-hash check cannot hide missing RLP validation. """ sender = pre.fund_eoa() receiver = pre.nonexistent_account() - tx = Transaction(sender=sender, to=receiver) + expectation = BlockAccessListExpectation() + exceptions = [BlockException.INVALID_BLOCK_ACCESS_LIST] + if header_commits_to == "canonical_rlp": + expectation = expectation.modify_rlp( + lambda _: Bytes(invalid_bal_payload) + ) + exceptions.append(BlockException.INVALID_BLOCK_HASH) + else: + expectation = expectation.modify( + override_rlp(lambda _: Bytes(invalid_bal_payload)) + ) + blockchain_test( pre=pre, post={ @@ -1918,10 +1926,8 @@ def test_bal_invalid_engine_payload_encoding( blocks=[ Block( txs=[tx], - engine_new_payload_block_access_list=Bytes( - invalid_bal_payload - ), - exception=BlockException.INVALID_BLOCK_ACCESS_LIST, + expected_block_access_list=expectation, + exception=exceptions, ) ], ) @@ -1960,6 +1966,10 @@ def test_bal_invalid_non_minimal_scalar_encoding( a matching hash and accepts. With the header committing to the payload RLP, a client that decodes leniently and hashes the bytes as received accepts instead. + + A strict client may notice the bad scalar while decoding, or only + once the hash it derives from the payload disagrees with the header; + both verdicts reject the block, so both are accepted. """ alice = pre.fund_eoa() oracle = pre.deploy_contract(code=Op.SSTORE(1, 1) + Op.SLOAD(2)) @@ -2020,7 +2030,10 @@ def test_bal_invalid_non_minimal_scalar_encoding( blocks=[ Block( txs=[tx], - exception=BlockException.INVALID_BLOCK_ACCESS_LIST, + exception=[ + BlockException.INVALID_BLOCK_ACCESS_LIST, + BlockException.INVALID_BLOCK_HASH, + ], expected_block_access_list=expectation, ) ], diff --git a/tests/amsterdam/eip7928_block_level_access_lists/test_cases.md b/tests/amsterdam/eip7928_block_level_access_lists/test_cases.md index d6e259996ed..4ee9ddd3df0 100644 --- a/tests/amsterdam/eip7928_block_level_access_lists/test_cases.md +++ b/tests/amsterdam/eip7928_block_level_access_lists/test_cases.md @@ -187,7 +187,7 @@ | `test_bal_invalid_missing_created_code` | Verify clients reject a BAL that omits the deployed code of a contract created via a contract-creation transaction | Alice sends a top-level `CREATE` transaction (`to=None`) whose init code deploys a small runtime. BAL modifier removes the created contract's `code_changes` entirely. | Block **MUST** be rejected with `INVALID_BLOCK_ACCESS_LIST` exception. Clients **MUST** detect a newly created contract whose deployed code has no corresponding BAL entry. | ✅ Completed | | `test_bal_invalid_omitted_slot_change_at_index` | Verify clients reject a BAL that drops a slot's earlier change while keeping its later one, misattributing the slot's first recorded change to a later transaction | Two transactions each write storage slot 0 of the same contract via the transaction's call value (slot 0: 0→1 at tx1, 1→2 at tx2). BAL modifier removes only tx1's `slot_changes` entry for slot 0 (new `remove_slot_change` modifier), leaving tx2's entry as the slot's only recorded change. | Block **MUST** be rejected with `INVALID_BLOCK_ACCESS_LIST` exception. Clients **MUST** validate that a slot's first BAL-recorded change matches the transaction that actually performed it, not merely that some change with the correct final value exists. | ✅ Completed | | `test_bal_invalid_phantom_read_on_selfdestruct` | Verify clients reject a BAL with a phantom storage read for an account created and destroyed within the same transaction | A contract-creation transaction's init code immediately `SELFDESTRUCT`s, sending its endowment to beneficiary, without ever returning runtime code. Per EIP-6780 the created account has zero net BAL changes (`BalAccountExpectation.empty()`) but still legitimately appears in the BAL as an entry with empty changes. BAL modifier injects a phantom `storage_reads` entry for a slot the account never touched. | Block **MUST** be rejected with `INVALID_BLOCK_ACCESS_LIST` exception. Clients **MUST** reject a storage read recorded against an account that performed no storage access at all, even when the account otherwise legitimately appears in the BAL. | ✅ Completed | -| `test_bal_invalid_non_minimal_scalar_encoding` | Verify clients reject a BAL whose RLP encodes an integer scalar non-minimally | Alice calls a contract that does `SSTORE(1, 1)` and `SLOAD(2)`, sending value so the account also has a balance change. The BAL contents are left untouched; the BAL delivered in the engine payload is re-encoded with one scalar carrying a leading zero byte. Parametrized over each integer field: `storage_slot`, `storage_value`, `storage_read`, `balance`, `block_access_index` (from the contract's balance change), `nonce` (from Alice's nonce change); and over what the header commits to: the canonical RLP (`modify_rlp`, payload only) or the payload RLP (`override_rlp`, so the block hash also follows). | Block **MUST** be rejected with `INVALID_BLOCK_ACCESS_LIST` exception. RLP integers **MUST** be minimally encoded. With the header committing to the canonical RLP, catches clients that decode these fields as raw byte strings and hash a re-encoding of the decoded BAL. With the header committing to the payload RLP, catches clients that hash the bytes as received without a minimal-encoding check. The uint32 index and uint64 nonce take different decoder paths from the uint256 fields, so each width is covered. | ✅ Completed | +| `test_bal_invalid_non_minimal_scalar_encoding` | Verify clients reject a BAL whose RLP encodes an integer scalar non-minimally | Alice calls a contract that does `SSTORE(1, 1)` and `SLOAD(2)`, sending value so the account also has a balance change. The BAL contents are left untouched; the BAL delivered in the engine payload is re-encoded with one scalar carrying a leading zero byte. Parametrized over each integer field: `storage_slot`, `storage_value`, `storage_read`, `balance`, `block_access_index` (from the contract's balance change), `nonce` (from Alice's nonce change); and over what the header commits to: the canonical RLP (`modify_rlp`, payload only) or the payload RLP (`override_rlp`, so the block hash also follows). | Block **MUST** be rejected, with `INVALID_BLOCK_ACCESS_LIST`, or with `INVALID_BLOCK_HASH` where the client compares the hash it derives from the payload against the header before validating the encoding. RLP integers **MUST** be minimally encoded. With the header committing to the canonical RLP, catches clients that decode these fields as raw byte strings and hash a re-encoding of the decoded BAL. With the header committing to the payload RLP, catches clients that hash the bytes as received without a minimal-encoding check. The uint32 index and uint64 nonce take different decoder paths from the uint256 fields, so each width is covered. | ✅ Completed | | `test_bal_invalid_missing_request_predeploy_accesses` | Verify clients reject a BAL that hides the queue slots a post-execution system call read from a request predeploy | Empty block; the post-execution system call reads the predeploy's excess, count, queue head and queue tail slots and writes nothing, so the entry's `storage_changes` is pinned empty. Parametrized over the EIP-7002, EIP-7251 and both EIP-8282 predeploys, and over the corruption: (1) `missing_entry`: the predeploy's entry is removed entirely. (2) `missing_reads`: the entry is kept but its `storage_reads` cleared, leaving an entry that records no accesses at all. | Block **MUST** be rejected with `INVALID_BLOCK_ACCESS_LIST` exception. The omission moves neither `state_root` nor gas, so clients **MUST** validate BAL completeness against the accesses of the post-execution system calls, not only their writes; and **MUST** validate that every slot read by such a call appears in `storage_reads` (or `storage_changes`) even when the predeploy's entry is otherwise present. | ✅ Completed | | `test_bal_invalid_missing_pre_block_system_call_read` | Verify clients reject a BAL that drops a storage read made by a pre-execution system call | Empty block with `parent_beacon_block_root = 0` (the framework default). The EIP-4788 system call writes the timestamp slot and rewrites the root slot with its current value, which EIP-7928 records as a `storage_reads` entry for `BEACON_ROOTS_ADDRESS`. BAL modifier clears that entry's `storage_reads`. | Block **MUST** be rejected with `INVALID_BLOCK_ACCESS_LIST` exception. Clients **MUST** validate `storage_reads` completeness at `block_access_index = 0`, not only the writes. EIP-7928 records a same-value write as a read, so a client that instead follows the spec's "record the two updated storage slots" phrasing for EIP-4788 emits the root slot as a `storage_changes` entry with `post_value = 0` and is caught here. | ✅ Completed | | `test_bal_invalid_missing_system_contract_entry` | Verify clients reject a BAL that omits a system contract written by a pre-execution system call | Parametrized over `HISTORY_STORAGE_ADDRESS` (EIP-2935) and `BEACON_ROOTS_ADDRESS` (EIP-4788). Empty block with a non-zero parent beacon root. BAL modifier removes the system contract's entry entirely. | Block **MUST** be rejected with `INVALID_BLOCK_ACCESS_LIST` exception. Clients **MUST** validate that every account written at `block_access_index = 0` has a BAL entry. | ✅ Completed | @@ -227,3 +227,37 @@ | `test_bal_2930_precompile_listed_but_untouched` | Ensure a precompile named in a transaction's access list but never called stays out of the BAL, for every precompile. | A plain transfer whose access list declares the precompile, via `@pytest.mark.with_all_precompiles`. | The precompile **MUST NOT** appear. A client that tracks precompiles apart from other accounts would leak the declaration here and in no other fixture. | ✅ Completed | | `test_sstore_clear_then_reset_nets_zero` (EIP-8038) | Ensure a slot cleared and then reset within one transaction is a single BAL change holding the final value. File: `tests/amsterdam/eip8038_state_access_gas_cost_increase/test_sstore_refunds.py`. | `SSTORE` clears a non-zero slot and rewrites it to a different non-zero value in the same frame, which also reverses the clear refund; the receipt pins the gas. | The slot **MUST** carry exactly one change at index 1 with the final value and **MUST NOT** appear in `storage_reads`; one entry per write would be rejected as a duplicate index. | ✅ Completed | | `test_bal_6110_deposit` | Ensure a deposit reaches the BAL as the deposit contract's balance change at the transaction's own index. | A single 32 ETH deposit sent by an EOA through `SystemContractInteractionTransaction`; the request is pinned in the header with `requests_hash`. | The deposit contract **MUST** record one balance change at index 1 holding the deposited value and no nonce or code change; the request is read from its log, so nothing touches it at the post-execution index. | ✅ Completed | + +### Malformed BAL encoding + +`test_bal_invalid_engine_payload_encoding` covers empty bytes, an RLP string +instead of a list, and a truncated list. Each payload is tested with the header +committing to either the canonical BAL or the malformed bytes. Matching-header +cases require `INVALID_BLOCK_ACCESS_LIST`; mismatched-header cases also accept +`INVALID_BLOCK_HASH`. + +### Zero-value touches and empty accounts (#3341) + +`test_bal_zero_value_transfer`, `test_bal_nonexistent_value_transfer`, and +`test_bal_nonexistent_account_access_value_transfer` require empty change lists +for recipients whose state is unchanged. The nonexistent-recipient cases also +assert that no account is created. + +An existing empty account is a different case: EIP-7523 excludes such accounts +from post-Merge test states, and #1160 deliberately removed their legacy touch +and deletion tracking. These tests cover valid Amsterdam states; they do not +claim to test deletion of pre-existing empty accounts. + +### Pre-fork Engine API field policy + +`test_invalid_pre_fork_block_with_bal_hash_field` keeps the engine payload's +`blockAccessList` field absent. The supplied block hash still commits to the +illegal extra header field, so rejection checks the hash without an unrelated +API parameter error. The RLP fixture retains the extra header field. + +`test_bal_invalid_engine_payload_field_before_fork` requires `-32602` for the +extra field. [Prague newPayloadV4](https://github.com/ethereum/execution-apis/blob/main/src/engine/prague.md#specification) +inherits [Cancun newPayloadV3's strict parameter and field matching rule](https://github.com/ethereum/execution-apis/blob/main/src/engine/cancun.md#specification). +Its ExecutionPayloadV3 has no `blockAccessList` field; returning `VALID` is an +Engine API conformance failure. Amsterdam missing-BAL and malformed-BAL checks +remain active with their existing expectations. 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 71ba29aa8f4..e92eac800b3 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 @@ -107,9 +107,9 @@ def test_invalid_pre_fork_block_with_bal_hash_field( Reject a pre-Amsterdam block whose header carries `block_access_list_hash`. - The engine fixture sends a pre-Amsterdam `newPayload` carrying an - empty `blockAccessList` param; the client's reconstructed header - omits the hash, so the block hash check fails. + The engine fixture omits `blockAccessList`, keeping the pre-fork API + parameters valid. Its block hash still commits to the extra header + field, so the client's reconstructed header cannot match it. """ sender = pre.fund_eoa() receiver = pre.fund_eoa(amount=0) @@ -138,11 +138,11 @@ def test_bal_invalid_engine_payload_field_before_fork( pre: Alloc, ) -> None: """ - Reject a pre-Amsterdam `newPayload` that carries a `blockAccessList`. + Reject an extra BAL field under the inherited strict-field API rule. - 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. + Prague's newPayloadV4 inherits Cancun's newPayloadV3 parameter checks + and accepts ExecutionPayloadV3, which has no blockAccessList field. + The otherwise-valid block isolates the required Invalid params error. """ sender = pre.fund_eoa() receiver = pre.nonexistent_account() @@ -156,8 +156,7 @@ def test_bal_invalid_engine_payload_field_before_fork( Block( timestamp=FORK_TIMESTAMP - 1, txs=[tx], - # A valid empty-BAL encoding: field presence alone, not - # decodability, must trigger the rejection. + # Isolate field presence from BAL decoding validity. engine_new_payload_block_access_list=Bytes(b"\xc0"), exception=BlockException.INCORRECT_BLOCK_FORMAT, engine_api_error_code=EngineAPIError.InvalidParams, diff --git a/tests/benchmark/compute/eip7928_block_level_access_lists/test_block_access_list.py b/tests/benchmark/compute/eip7928_block_level_access_lists/test_block_access_list.py index 42953d01339..00a0461a866 100644 --- a/tests/benchmark/compute/eip7928_block_level_access_lists/test_block_access_list.py +++ b/tests/benchmark/compute/eip7928_block_level_access_lists/test_block_access_list.py @@ -35,7 +35,7 @@ from ethereum.crypto.hash import keccak256 REFERENCE_SPEC_GIT_PATH = "EIPS/eip-7928.md" -REFERENCE_SPEC_VERSION = "aca88aa0932580c29d0233f902cb4390e88b8c41" +REFERENCE_SPEC_VERSION = "d2a64c2d4cc44f2f507577d0ebfb110dcc21d358" pytestmark = pytest.mark.valid_from("Amsterdam") diff --git a/tests/benchmark/stateful/eip7928_block_level_access_lists/spec.py b/tests/benchmark/stateful/eip7928_block_level_access_lists/spec.py index d8541199475..010c4226fc1 100644 --- a/tests/benchmark/stateful/eip7928_block_level_access_lists/spec.py +++ b/tests/benchmark/stateful/eip7928_block_level_access_lists/spec.py @@ -17,5 +17,5 @@ class ReferenceSpec: ref_spec_7928 = ReferenceSpec( git_path="EIPS/eip-7928.md", - version="f834f0004aa5110a5f1ac0d6b80e3dc4b842d040", + version="d2a64c2d4cc44f2f507577d0ebfb110dcc21d358", ) diff --git a/tests/frontier/validation/test_transaction.py b/tests/frontier/validation/test_transaction.py index e417bdddbbe..bdbbc2dba38 100644 --- a/tests/frontier/validation/test_transaction.py +++ b/tests/frontier/validation/test_transaction.py @@ -37,12 +37,19 @@ def test_tx_gas_limit( """ Tests that if a tx gas limit is higher than the block gas limit, an exception is raised. + + The block gas limit is kept well above what an empty block's access + list needs under the EIP-7928 item cap (`gas_limit // 2000` items, + against the system-contract reads every Amsterdam block carries), so + the transaction's gas allowance is the only thing wrong with the + block and clients do not disagree on which check to report. """ sender = pre.fund_eoa() to = pre.fund_eoa() + block_gas_limit = 100_000 tx = Transaction( - gas_limit=21001, + gas_limit=block_gas_limit + 1, to=to, gas_price=0x10, # Must be >= base fee to isolate gas limit validation sender=sender, @@ -50,8 +57,8 @@ def test_tx_gas_limit( error=TransactionException.GAS_ALLOWANCE_EXCEEDED, ) - modified_fields = {"gas_limit": ZeroPaddedHexNumber(21000)} - env.gas_limit = ZeroPaddedHexNumber(21000) + modified_fields = {"gas_limit": ZeroPaddedHexNumber(block_gas_limit)} + env.gas_limit = ZeroPaddedHexNumber(block_gas_limit) block = Block( txs=[tx],