From c6c380acffa586cfd683384fb2e09348e9a97c03 Mon Sep 17 00:00:00 2001 From: Guruprasad Kamath <48196632+gurukamath@users.noreply.github.com> Date: Mon, 26 Jan 2026 22:22:23 +0100 Subject: [PATCH] feat(tests): add BAL tests that dequeue consolidations (#2076) --- docs/CHANGELOG.md | 1 + .../test_block_access_lists_eip7251.py | 185 ++++++++++++++++++ .../test_cases.md | 2 +- 3 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_eip7251.py diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 3f1e21902e..0f2bab30b2 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -39,6 +39,7 @@ Test fixtures for use by clients are available for each release on the [Github r - ✨ New EIP-7702 test cases added ([#1974](https://github.com/ethereum/execution-specs/pull/1974)). - ✨ Add missing benchmark configurations / opcode to benchmark tests for repricing analysis([#2006](https://github.com/ethereum/execution-specs/pull/2006)). - ✨ Port STATICCALL to CALL tests with zero and non-zero value transfer from `tests/static`, extending coverage with `pytest.mark.with_all_precompiles` ([#1960](https://github.com/ethereum/execution-specs/pull/1960)). +- ✨ Add BAL tests that dequeue EIP-7251 consolidation requests. ([#2076](https://github.com/ethereum/execution-specs/pull/2076)). ## [v5.4.0](https://github.com/ethereum/execution-spec-tests/releases/tag/v5.4.0) - 2025-12-07 diff --git a/tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_eip7251.py b/tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_eip7251.py new file mode 100644 index 0000000000..b2cec1d28a --- /dev/null +++ b/tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_eip7251.py @@ -0,0 +1,185 @@ +"""Tests for the effects of EIP-7251 beacon roots on EIP-7928.""" + +from typing import List + +import pytest +from execution_testing import ( + Address, + Alloc, + BalAccountExpectation, + BalStorageChange, + BalStorageSlot, + Block, + BlockAccessListExpectation, + BlockchainTestFiller, + Environment, +) + +from tests.prague.eip7251_consolidations.helpers import ( + ConsolidationRequest, + ConsolidationRequestTransaction, +) +from tests.prague.eip7251_consolidations.spec import Spec, ref_spec_7251 + +REFERENCE_SPEC_GIT_PATH = ref_spec_7251.git_path +REFERENCE_SPEC_VERSION = ref_spec_7251.version + +pytestmark = pytest.mark.valid_from("Amsterdam") + +CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS = ( + Spec.CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS +) +CONSOLIDATION_REQUEST_COUNT_STORAGE_SLOT = ( + Spec.CONSOLIDATION_REQUEST_COUNT_STORAGE_SLOT +) +CONSOLIDATION_REQUEST_QUEUE_HEAD_STORAGE_SLOT = ( + Spec.CONSOLIDATION_REQUEST_QUEUE_HEAD_STORAGE_SLOT +) +CONSOLIDATION_REQUEST_QUEUE_TAIL_STORAGE_SLOT = ( + Spec.CONSOLIDATION_REQUEST_QUEUE_TAIL_STORAGE_SLOT +) +MAX_CONSOLIDATION_REQUESTS_PER_BLOCK = ( + Spec.MAX_CONSOLIDATION_REQUESTS_PER_BLOCK +) +SYSTEM_ADDRESS = Address(Spec.SYSTEM_ADDRESS) + + +@pytest.mark.parametrize( + "blocks_consolidation_requests", + [ + pytest.param( + [ + ConsolidationRequestTransaction( + requests=[ + ConsolidationRequest( + source_pubkey=0x01, + target_pubkey=0x02, + fee=Spec.get_fee(0), + ) + ], + ) + ], + id="single_block_single_consolidation_request_from_eoa", + ), + pytest.param( + [ + ConsolidationRequestTransaction( + requests=[ + ConsolidationRequest( + source_pubkey=i * 2 + 1, + target_pubkey=i * 2 + 2, + fee=Spec.get_fee(0), + ) + ], + ) + for i in range(MAX_CONSOLIDATION_REQUESTS_PER_BLOCK) + ], + id="single_block_max_consolidation_per_block", + ), + pytest.param( + [ + ConsolidationRequestTransaction( + requests=[ + ConsolidationRequest( + source_pubkey=i * 2 + 1, + target_pubkey=i * 2 + 2, + fee=Spec.get_fee(0), + ) + ], + ) + for i in range(MAX_CONSOLIDATION_REQUESTS_PER_BLOCK + 1) + ], + id="single_block_max_consolidation_per_block_plus1", + ), + ], +) +@pytest.mark.pre_alloc_group( + "consolidation_requests", + reason="Tests standard consolidation request functionality", +) +def test_bal_system_dequeue_consolidations_eip7251( + blockchain_test: BlockchainTestFiller, + pre: Alloc, + blocks_consolidation_requests: List[ConsolidationRequestTransaction], +) -> None: + """Test making a consolidation request to the beacon chain.""" + txs = [] + + for request in blocks_consolidation_requests: + request.update_pre(pre=pre) + txs += request.transactions() + + num = len(txs) + + count_slot_changes = [] + head_slot_changes = [] + tail_slot_changes = [] + + for idx, _ in enumerate(txs): + count_slot_changes.append( + BalStorageChange(block_access_index=idx + 1, post_value=idx + 1) + ) + + tail_slot_changes.append( + BalStorageChange(block_access_index=idx + 1, post_value=idx + 1) + ) + + # Count slot is always reset to zero after request processing + count_slot_changes.append( + BalStorageChange(block_access_index=num + 1, post_value=0) + ) + + if num > MAX_CONSOLIDATION_REQUESTS_PER_BLOCK: + head_slot_changes.append( + BalStorageChange( + block_access_index=num + 1, + post_value=MAX_CONSOLIDATION_REQUESTS_PER_BLOCK, + ) + ) + else: + tail_slot_changes.append( + BalStorageChange(block_access_index=num + 1, post_value=0) + ) + + storage_changes = [] + if any(count_slot_changes): + storage_changes.append( + BalStorageSlot( + slot=CONSOLIDATION_REQUEST_COUNT_STORAGE_SLOT, + slot_changes=count_slot_changes, + ) + ) + + if any(head_slot_changes): + storage_changes.append( + BalStorageSlot( + slot=CONSOLIDATION_REQUEST_QUEUE_HEAD_STORAGE_SLOT, + slot_changes=head_slot_changes, + ) + ) + + if any(tail_slot_changes): + storage_changes.append( + BalStorageSlot( + slot=CONSOLIDATION_REQUEST_QUEUE_TAIL_STORAGE_SLOT, + slot_changes=tail_slot_changes, + ) + ) + + block = Block( + txs=txs, + expected_block_access_list=BlockAccessListExpectation( + account_expectations={ + CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS: ( + BalAccountExpectation(storage_changes=storage_changes) + ) + } + ), + ) + + blockchain_test( + genesis_environment=Environment(), + pre=pre, + post={}, + blocks=[block], + ) 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 54d53064fe..3aa38e0eae 100644 --- a/tests/amsterdam/eip7928_block_level_access_lists/test_cases.md +++ b/tests/amsterdam/eip7928_block_level_access_lists/test_cases.md @@ -29,7 +29,7 @@ | `test_bal_noop_storage_write` | Ensure BAL includes storage read but not write for no-op writes where pre-state equals post-state | Contract with pre-existing storage value `0x42` in slot `0x01`; transaction executes `SSTORE(0x01, 0x42)` (writing same value) | BAL **MUST** include the contract address with `storage_reads` for slot `0x01` since it was accessed, but **MUST NOT** include it in `storage_changes` (no actual state change). | ✅ Completed | | `test_bal_fully_unmutated_account` | Ensure BAL captures account that has zero net mutations | Alice sends 0 wei to `Oracle` which writes same pre-existing value to storage | BAL MUST include Alice with `nonce_changes` and balance changes (gas), `Oracle` with `storage_reads` for accessed slot but empty `storage_changes`. | ✅ Completed | | `test_bal_net_zero_balance_transfer` | BAL includes accounts with net-zero balance change but excludes them from balance changes | Contract receives and sends same amount to recipient using CALL or SELFDESTRUCT | BAL **MUST** include contract in `account_changes` without `balance_changes` (net zero). BAL **MUST** record non-zero `balance_changes` for recipient. | ✅ Completed | -| `test_bal_system_dequeue_consolidations_eip7251` | BAL tracks post-exec system dequeues for consolidations | Pre-populate EIP-7251 consolidation requests; produce a block where dequeues occur | BAL MUST include the 7251 system contract with `storage_changes` (queue slots 0–3) using `block_access_index = len(txs)`. | 🟡 Planned | +| `test_bal_system_dequeue_consolidations_eip7251` | BAL tracks post-exec system dequeues for consolidations | Pre-populate EIP-7251 consolidation requests; produce a block where dequeues occur | BAL MUST include the 7251 system contract with `storage_changes` (queue slots 0–3) using `block_access_index = len(txs)`. | ✅ Completed | | `test_bal_aborted_storage_access` | Ensure BAL captures storage access in aborted transactions correctly | Alice calls contract that reads storage slot `0x01`, writes to slot `0x02`, then aborts with `REVERT`/`INVALID` | BAL MUST include storage_reads for slots `0x01` and `0x02` (aborted writes become reads), empty storage_changes. Only nonce changes for Alice. | ✅ Completed | | `test_bal_aborted_account_access` | Ensure BAL captures account access in aborted transactions for all account accessing opcodes | Alice calls `AbortContract` that performs account access operations (`BALANCE`, `EXTCODESIZE`, `EXTCODECOPY`, `EXTCODEHASH`, `CALL`, `CALLCODE`, `DELEGATECALL`, `STATICCALL`) on `TargetContract` and aborts via `REVERT`/`INVALID` | BAL MUST include Alice, `TargetContract`, and `AbortContract` in account_changes and nonce changes for Alice. | ✅ Completed | | `test_bal_pure_contract_call` | Ensure BAL captures contract access for pure computation calls | Alice calls `PureContract` that performs pure arithmetic (ADD operation) without storage or balance changes | BAL MUST include Alice and `PureContract` in `account_changes`, and `nonce_changes` for Alice. | ✅ Completed |