Markdown Content
Below is the verbatim markdown content from tests/amsterdam/eip7928_block_level_access_lists/test_cases.md@b314d18e.
EIP-7928 Block Access Lists (BAL) Test Cases¶
| Function Name | Goal | Setup | Expectation | Status |
|---|---|---|---|---|
test_bal_nonce_changes |
Ensure BAL captures changes to nonce | Alice sends 100 wei to Bob | BAL MUST include changes to Alice's nonce. | ✅ Completed |
test_bal_balance_changes |
Ensure BAL captures changes to balance | Alice sends 100 wei to Bob | BAL MUST include balance change for Alice, Bob, and Coinbase | ✅ Completed |
test_bal_code_changes |
Ensure BAL captures changes to account code | Alice deploys factory contract that creates new contract | BAL MUST include code changes for newly deployed contract | ✅ Completed |
test_selfdestruct_to_account (TangerineWhistle) |
Ensure BAL captures SELFDESTRUCT success boundary for account beneficiaries | Victim executes SELFDESTRUCT(beneficiary) at exact gas boundary. Tests final gas boundary where operation completes. Parametrized: is_success (exact_gas/exact_gas_minus_1), beneficiary (EOA/contract), warm (cold/warm where warm=Berlin+), same_tx (pre_deploy/same_tx), originator_balance (0/1), beneficiary_balance (0/1). File: tests/tangerine_whistle/eip150_operation_gas_costs/test_eip150_selfdestruct.py. |
exact_gas: Beneficiary in BAL with balance_changes, victim destroyed (pre-Cancun/same_tx) or preserved (>=Cancun). exact_gas_minus_1: OOG, beneficiary in BAL only if G_NEW_ACCOUNT was part of gas calculation. |
✅ Completed |
test_selfdestruct_state_access_boundary (TangerineWhistle) |
Ensure BAL correctly tracks beneficiary access at state access boundary (consensus check) | Victim executes SELFDESTRUCT(beneficiary) at state access boundary (base + cold). Verifies beneficiary is accessed before G_NEW_ACCOUNT check. Parametrized: is_success (exact_gas/exact_gas_minus_1), beneficiary (EOA/contract), warm (cold/warm), same_tx, originator_balance (0/1), beneficiary_balance (0/1). File: tests/tangerine_whistle/eip150_operation_gas_costs/test_eip150_selfdestruct.py. |
exact_gas: Beneficiary IN BAL (state accessed). exact_gas_minus_1: Beneficiary NOT in BAL (OOG before state access). Operation may succeed at exact_gas if no G_NEW_ACCOUNT needed. | ✅ Completed |
test_selfdestruct_to_self (TangerineWhistle) |
Ensure BAL captures SELFDESTRUCT where beneficiary is self at gas boundary | Victim executes SELFDESTRUCT(ADDRESS) - selfdestructs to itself. Always warm, always alive (no G_NEW_ACCOUNT, no cold access). Gas = G_BASE + G_SELF_DESTRUCT. Parametrized: is_success (exact_gas/exact_gas_minus_1), originator_balance (0/1), same_tx (pre_deploy/same_tx). File: tests/tangerine_whistle/eip150_operation_gas_costs/test_eip150_selfdestruct.py. |
exact_gas_minus_1: Victim in BAL with unchanged state. exact_gas: Pre-Cancun/same_tx: destroyed, balance=0. >=Cancun pre-existing: preserved with original balance. | ✅ Completed |
test_selfdestruct_to_precompile (TangerineWhistle) |
Ensure BAL captures SELFDESTRUCT success boundary for precompile beneficiaries | Victim executes SELFDESTRUCT(precompile) at exact gas boundary. Precompiles are always warm (no cold access charge). Parametrized: is_success (exact_gas/exact_gas_minus_1), all precompiles via @pytest.mark.with_all_precompiles, same_tx (pre_deploy/same_tx), originator_balance (0/1), beneficiary_balance (0/1). File: tests/tangerine_whistle/eip150_operation_gas_costs/test_eip150_selfdestruct.py. |
exact_gas: Precompile in BAL with balance_changes, victim destroyed (pre-Cancun/same_tx) or preserved (>=Cancun). exact_gas_minus_1: OOG, precompile in BAL only if G_NEW_ACCOUNT was part of gas calculation. |
✅ Completed |
test_selfdestruct_to_precompile_state_access_boundary (TangerineWhistle) |
Ensure BAL correctly tracks precompile access at state access boundary (consensus check) | Victim executes SELFDESTRUCT(precompile) at state access boundary (base only, precompiles always warm). Verifies precompile is accessed before G_NEW_ACCOUNT check. Parametrized: is_success (exact_gas/exact_gas_minus_1), all precompiles, same_tx, originator_balance (0/1), beneficiary_balance (0/1). File: tests/tangerine_whistle/eip150_operation_gas_costs/test_eip150_selfdestruct.py. |
exact_gas: Precompile IN BAL (state accessed). exact_gas_minus_1: Precompile NOT in BAL (OOG before state access). Operation may succeed at exact_gas if no G_NEW_ACCOUNT needed. | ✅ Completed |
test_selfdestruct_to_system_contract (Cancun) |
Ensure BAL captures SELFDESTRUCT success boundary for system contract beneficiaries | Victim executes SELFDESTRUCT(system_contract) at exact gas boundary. System contracts are always warm (no cold access charge) and always have code (no G_NEW_ACCOUNT charge). Gas = G_VERY_LOW + G_SELF_DESTRUCT. Parametrized: is_success (exact_gas/exact_gas_minus_1), all system contracts via @pytest.mark.with_all_system_contracts, same_tx (pre_deploy/same_tx), originator_balance (0/1). File: tests/tangerine_whistle/eip150_operation_gas_costs/test_eip150_selfdestruct.py. |
exact_gas: System contract in BAL with balance_changes if originator had balance, victim destroyed (same_tx) or balance=0 (pre-existing). exact_gas_minus_1: OOG, system contract not in BAL (no state access). |
✅ Completed |
test_initcode_selfdestruct_to_self (TangerineWhistle) |
Ensure BAL captures SELFDESTRUCT during initcode where beneficiary is self | Initcode executes SELFDESTRUCT(ADDRESS) during CREATE, before any code is deployed. Contract has nonce=1 (post-EIP-161), making it non-empty. Always warm (executing contract), no G_NEW_ACCOUNT (nonce > 0). Gas boundary testing not possible (CREATE uses all available gas). Parametrized: originator_balance (0/1). File: tests/tangerine_whistle/eip150_operation_gas_costs/test_eip150_selfdestruct.py. |
Contract created and destroyed in same tx - victim has empty BAL changes. Caller has nonce_changes (incremented by CREATE) and balance_changes if originator had balance. Victim is NONEXISTENT in post state. |
✅ Completed |
test_bal_account_access_target |
Ensure BAL captures target addresses of account access opcodes | Alice calls Oracle contract which uses account access opcodes (BALANCE, EXTCODESIZE, EXTCODECOPY, EXTCODEHASH, CALL, CALLCODE, DELEGATECALL, STATICCALL) on TargetContract. |
BAL MUST include Alice, Oracle, and TargetContract with empty changes for TargetContract and nonce changes for Alice. |
✅ Completed |
test_bal_call_no_delegation_and_oog_before_target_access |
Ensure BAL handles OOG before target access and success for non-delegated CALL | Parametrized: target warm/cold, target empty/existing, value 0/1, (args_size, ret_size) pair (covers in-only and out-only expansion per #1910), OOG boundary (before_target_access/success). |
OOG: target in BAL ONLY if pre-warmed. Success: target always in BAL with balance changes when value > 0. | ✅ Completed |
test_bal_call_no_delegation_oog_after_target_access |
Ensure BAL includes target but excludes value transfer when OOG after target access | Hardcoded: empty target, value=1 (required for create_cost gap). Parametrized: warm/cold, (args_size, ret_size) pair (covers in-only and out-only expansion per #1910). |
Target always in BAL. No balance changes (value transfer fails after G_NEW_ACCOUNT check). | ✅ Completed |
test_bal_call_7702_delegation_and_oog |
Ensure BAL handles OOG at all 4 boundaries for CALL to 7702 delegated accounts | Parametrized: target warm/cold, delegation warm/cold, value 0/1, (args_size, ret_size) pair (covers in-only and out-only expansion per #1910), OOG boundary (before_target_access/after_target_access/success_minus_1/success). |
OOG before: neither in BAL. OOG after & success_minus_1: target in BAL, delegation NOT in BAL (static check optimization). Success: all in BAL. | ✅ Completed |
test_bal_callcode_nested_value_transfer |
Ensure BAL captures balance changes from nested value transfers when CALLCODE executes target code that itself makes CALL with value | Alice calls Oracle contract (200 wei balance) which uses CALLCODE to execute TargetContract's code; that code makes a nested CALL transferring 100 wei to Bob. |
BAL MUST include Alice (nonce changes), Oracle (balance change to 100 wei), Bob (balance change to 100 wei), and TargetContract (empty changes). |
✅ Completed |
test_bal_delegated_storage_writes |
Ensure BAL captures delegated storage writes via DELEGATECALL and CALLCODE |
Alice calls Oracle contract which uses DELEGATECALL/CALLCODE to TargetContract that writes 0x42 to slot 0x01. |
BAL MUST include Alice (nonce changes), Oracle (storage changes for slot 0x01 = 0x42), and TargetContract (empty changes). |
✅ Completed |
test_bal_delegated_storage_reads |
Ensure BAL captures delegated storage reads via DELEGATECALL and CALLCODE |
Alice calls Oracle contract (with slot 0x01 = 0x42) which uses DELEGATECALL/CALLCODE to TargetContract that reads from slot 0x01. |
BAL MUST include Alice (nonce changes), Oracle (storage reads for slot 0x01), and TargetContract (empty changes). |
✅ Completed |
test_bal_block_rewards |
BAL tracks fee recipient balance changes from block rewards | Alice sends 100 wei to Bob with Charlie as fee recipient | BAL MUST include fee recipient Charlie with balance_changes reflecting transaction fees collected from the block. |
✅ Completed |
test_bal_selfdestruct_to_coinbase |
Ensure BAL records SELFDESTRUCT when the beneficiary is the coinbase address. Parametrized over same_tx ∈ [False, True]. Coinbase pre-funded with amount=0 (empty per EIP-161). gas_price = base_fee_per_gas so the priority-fee tip is zero and coinbase's entry carries only the SELFDESTRUCT transfer. |
pre_deploy: pre-existing victim contract (balance 100) executes SELFDESTRUCT(Op.COINBASE); post-Cancun (EIP-6780) the contract is preserved with balance 0. same_tx: factory creates victim with CREATE(value=100, ...) and CALLs it; victim's runtime SELFDESTRUCT runs in the same tx so the contract is actually destroyed. |
pre_deploy: BAL MUST include victim with balance_changes 100→0 and coinbase with balance_changes 0→100. same_tx: BAL MUST include factory with nonce_changes (CREATE bumped nonce) + balance_changes 100→0, victim with empty changes (created+destroyed same-tx), and coinbase with balance_changes 0→100. |
✅ Completed |
test_bal_2930_account_listed_but_untouched |
Ensure BAL excludes listed but untouched account | Alice sends a simple eth transfer tx to Bob with EIP-2930 access list including Oracle |
BAL MUST NOT include any entry for Oracle because it wasn't accessed. |
✅ Completed |
test_bal_2930_slot_listed_but_untouched |
Ensure BAL excludes listed but untouched storage slots | Alice sends tx with EIP-2930 access list including (PureCalculator, slot=0x01); PureCalculator executes pure arithmetic (adding two numbers) without touching slot 0x01 |
BAL MUST NOT include any entry for PureCalculator's slot 0x01 because it doesn't access state |
✅ Completed |
test_bal_2930_slot_listed_and_unlisted_writes |
Ensure BAL includes storage writes regardless of access list presence | Alice sends tx with EIP-2930 access list including (StorageWriter, slot=0x01); StorageWriter executes SSTORE to slots 0x01 and 0x02 |
BAL MUST include storage_changes for StorageWriter's slots 0x01 and 0x02 |
✅ Completed |
test_bal_2930_slot_listed_and_unlisted_reads |
Ensure BAL includes storage reads regardless of access list presence | Alice sends tx with EIP-2930 access list including (StorageReader, slot=0x01); StorageReader executes SLOAD from slots 0x01 and 0x02 |
BAL MUST include storage_reads for StorageReader's slots 0x01 and 0x02 |
✅ Completed |
test_bal_self_transfer |
BAL handles self-transfers correctly | Alice sends 100 wei to Alice |
BAL MUST include one entry for Alice with balance_changes reflecting gas cost only (value cancels out) and nonce change. |
✅ Completed |
test_bal_zero_value_transfer |
BAL handles zero-value transfers correctly | Alice sends 0 wei to Bob |
BAL MUST include Alice with balance_changes (gas cost only) and nonce change, and Bob in account_changes with empty balance_changes. |
✅ Completed |
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). |
✅ Completed |
test_bal_withdrawal_contract_cross_index |
Ensure withdrawal system contract shows storage changes at both tx and post-execution indices | Alice sends withdrawal request to EIP-7002 system contract. Slots 0x01 (count) and 0x03 (tail) are incremented during tx (index 1) and reset during post-execution dequeue (index 2). | BAL MUST include withdrawal request contract with storage_changes for slots 0x01 and 0x03, each with two slot_changes: one at block_access_index=1 (tx) and one at block_access_index=2 (post-exec). |
✅ Completed |
test_bal_consolidation_contract_cross_index |
Ensure consolidation system contract shows storage changes at both tx and post-execution indices | Alice sends consolidation request to EIP-7251 system contract. Slots 0x01 (count) and 0x03 (tail) are incremented during tx (index 1) and reset during post-execution dequeue (index 2). | BAL MUST include consolidation request contract with storage_changes for slots 0x01 and 0x03, each with two slot_changes: one at block_access_index=1 (tx) and one at block_access_index=2 (post-exec). |
✅ Completed |
test_bal_noop_write_filtering |
Ensure BAL filters NOOP storage writes (writing same value or 0 to empty slot) | Contract writes 0 to uninitialized slot 1 (noop), 42 to slot 2 (real change), 100 to slot 3 (same as pre-state, noop), 200 to slot 4 (different from pre-state 150, real change). | BAL MUST include storage_changes only for slots 2 and 4 (actual changes). Slots 1 and 3 MUST NOT appear in storage_changes (no-op writes filtered). |
✅ Completed |
test_bal_system_contract_noop_filtering |
Ensure system contract post-execution calls filter net-zero storage writes | Simple transfer that doesn't interact with system contracts. Post-execution system calls read withdrawal/consolidation contract slots 0-3 but don't modify them. | Withdrawal and consolidation system contracts MUST have storage_reads for slots 0x00-0x03 but MUST NOT have storage_changes (no actual modifications occurred). |
✅ 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_parent_revert_state_access |
Ensure BAL captures child-frame state access when the parent frame reverts (write-demoted-to-read across frame boundary). Parametrized: inner_action ∈ [sstore, sload, balance, extcodesize], outer_abort ∈ [REVERT, INVALID]. |
Outer contract CALLs an inner contract that performs one state-access op (SSTORE/SLOAD against its own storage, or BALANCE/EXTCODESIZE against a separate target). Inner's slot 1 is pre-set to 0xDEAD so the post-state confirms the demoted SSTORE didn't overwrite it. Inner returns successfully; outer then aborts. |
BAL MUST include inner with storage_reads=[1] (SSTORE/SLOAD cases — write demoted to read), or empty changes plus the separate target in BAL with empty changes (BALANCE/EXTCODESIZE cases). Post-state: inner.storage[1] == 0xDEAD for SSTORE/SLOAD cases. |
✅ Completed |
test_selfdestruct_balance_transfer_reverted (Cancun) |
Ensure BAL records SELFDESTRUCT touches even when the containing sub-call reverts. Multi-fork via state_test; BAL expectations gated on fork.is_eip_enabled(7928). File: tests/cancun/eip6780_selfdestruct/test_journal_revert.py. |
Outer → controller (CALL) → victim (CALL); victim executes SELFDESTRUCT(beneficiary), controller REVERTs (rolling back the balance transfer), outer continues and observes balances. |
When EIP-7928 is enabled, BAL MUST include victim and beneficiary with empty changes (touched in the reverted sub-call's SELFDESTRUCT plus outer's BALANCE reads; net balances unchanged). | ✅ Completed |
test_bal_outer_revert_with_inner_insufficient_funds |
Combine outer-frame REVERT with inner-frame insufficient-funds CALL or CREATE. Parametrized inner_op ∈ [call, create]. |
Outer → inner (CALL). Inner does SSTORE(slot_a, 0x42), then attempts CALL/CREATE with value > balance=0. Inner returns successfully (insufficient-balance check happens after the opcode's state-touching costs are charged, not via a REVERT). Outer then REVERTs. |
Inner's two SSTOREs demote to storage_reads. For call: the failed call's target appears in BAL with empty changes (cold access charged before balance check). For create: the would-be address is NOT in BAL (early failure precedes track_address). |
✅ Completed |
test_create_insufficient_balance (Berlin) |
Ensure BAL records that a failed CREATE does NOT itself add the would-be address; only a subsequent BALANCE call does. Multi-fork via state_test; BAL gated on fork.is_eip_enabled(7928). File: tests/berlin/eip2929_gas_cost_increases/test_create.py. |
Entry → creator (CREATE with value=1, creator balance 0 → fails) → checker (cold BALANCE on the would-be address, measures gas). |
Creator has real storage_changes (slot 0 = 1 → 0). Checker has storage_changes (slot 1 = cold BALANCE gas cost). Would-be address appears in BAL with empty changes — accessed only via the BALANCE in the checker contract, NOT via the failed CREATE. |
✅ 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 |
test_bal_create_storage_op_then_selfdestruct_same_tx |
BAL correctly demotes ephemeral storage operations to storage_reads when a contract is created and destroyed in the same tx (combined coverage for read and write storage_op cases — replaces test_bal_create2_to_A_read_then_selfdestruct and test_bal_create2_to_A_write_then_selfdestruct). Parametrized: @pytest.mark.with_all_create_opcodes + storage_op ∈ ["read", "write"]. |
Address A is pre-funded via pre.fund_address. Alice sends a single tx calling a factory that deploys a contract at A via the parametrized create opcode; init code is either SLOAD(B)+SELFDESTRUCT or SSTORE(B, v)+SELFDESTRUCT (beneficiary = separate EOA). |
BAL MUST include A with balance_changes [(1, 0)] (outflow to beneficiary on destruction). Slot B MUST appear under storage_reads, NOT storage_changes (write demoted to read because the contract is destroyed same-tx). A MUST NOT have nonce_changes or code_changes. Beneficiary has balance_changes reflecting receipt of fund at index 1. |
✅ Completed |
test_bal_precompile_funded |
BAL records precompile value transfer with or without balance change | Alice sends value to precompile (all precompiles) via direct transaction. Parameterized: (1) with value (1 ETH), (2) without value (0 ETH). | For with_value: BAL MUST include precompile with balance_changes. For no_value: BAL MUST include precompile with empty balance_changes. No storage_changes or code_changes in either case. |
✅ Completed |
test_bal_precompile_call_opcode |
BAL records the precompile address regardless of call opcode. | Parametrized: @pytest.mark.with_all_precompiles × @pytest.mark.with_all_call_opcodes. Alice calls Oracle which invokes the precompile via the parametrized call opcode. For DELEGATECALL/CALLCODE the precompile provides the code but is not the call target, so its access has to be recorded explicitly rather than incidentally. |
BAL MUST include the precompile address with empty changes for all four call opcodes. Oracle has empty changes (called but no state mutation), Alice has nonce_changes. |
✅ Completed |
test_bal_7702_delegated_create |
BAL tracks EIP-7702 delegation indicator write and contract creation | Alice sends a type-4 (7702) tx authorizing herself to delegate to Deployer code which executes CREATE |
BAL MUST include for Alice: code_changes (delegation indicator), nonce_changes (increment from 7702 processing), and balance_changes (post-gas). For Child: code_changes (runtime bytecode) and nonce_changes = 1. |
✅ Completed |
test_bal_7702_delegation_create |
Ensure BAL captures creation of EOA delegation | Alice authorizes delegation to contract Oracle. Transaction sends 10 wei to Bob. Two variants: (1) Self-funded: Alice sends 7702 tx herself. (2) Sponsored: Relayer sends 7702 tx on Alice's behalf. |
BAL MUST include Alice: code_changes (delegation designation 0xef0100\|\|address(Oracle)),nonce_changes (increment). Bob: balance_changes (receives 10 wei). For sponsored variant, BAL MUST also include Relayer:nonce_changes.Oracle MUST NOT be present in BAL - the account is never accessed. |
✅ Completed |
test_bal_7702_delegation_update |
Ensure BAL captures update of existing EOA delegation. Three variants: (1) Self-funded: Alice sends both 7702 txs herself. (2) Sponsored: a single Relayer sends both txs on Alice's behalf. (3) sponsored_cross_sender: a distinct relayer per tx — exercises the cross-tx auth-nonce-chain dependency since sender-nonce serialization no longer trivializes the test. A client that parallel-verifies auth signatures must consult Alice's BAL nonce_changes to validate the second auth against her post-tx-1 nonce. |
Alice first delegates to Oracle1, then in second tx updates delegation to Oracle2. Each transaction sends 10 wei to Bob. |
BAL MUST include Alice: first tx has code_changes (delegation designation 0xef0100\|\|address(Oracle1)),nonce_changes. Second tx hascode_changes (delegation designation 0xef0100\|\|address(Oracle2)),nonce_changes. Bob:balance_changes (receives 10 wei on each tx). For sponsored variant, BAL MUST also include Relayer:nonce_changes for both transactions; for sponsored_cross_sender, each relayer has one nonce_changes at its tx index. Oracle1 and Oracle2 MUST NOT be present in BAL - accounts are never accessed. |
✅ Completed |
test_bal_7702_delegation_clear |
Ensure BAL captures clearing of EOA delegation | Alice first delegates to Oracle, then in second tx clears delegation by authorizing to 0x0 address. Each transaction sends 10 wei to Bob. Two variants: (1) Self-funded: Alice sends both 7702 txs herself. (2) Sponsored: Relayer sends both 7702 txs on Alice's behalf. |
BAL MUST include Alice: first tx has code_changes (delegation designation 0xef0100\|\|address(Oracle)), nonce_changes. Second tx has code_changes (empty code - delegation cleared), nonce_changes. Bob: balance_changes (receives 10 wei on each tx). For sponsored variant, BAL MUST also include Relayer: nonce_changes for both transactions. Oracle and 0x0 address MUST NOT be present in BAL - accounts are never accessed. |
✅ Completed |
test_bal_7702_delegated_storage_access |
Ensure BAL captures storage operations when calling a delegated EIP-7702 account | Alice has delegated her account to Oracle. Oracle contract contains code that reads from storage slot 0x01 and writes to storage slot 0x02. Bob sends 10 wei to Alice (the delegated account), which executes Oracle's code. |
BAL MUST include Alice: balance_changes (receives 10 wei), storage_changes for slot 0x02 (write operation performed in Alice's storage), storage_reads for slot 0x01 (read operation from Alice's storage). Bob: nonce_changes (sender), balance_changes (loses 10 wei plus gas costs). Oracle (account access). |
✅ Completed |
test_bal_7702_invalid_nonce_authorization |
Ensure BAL handles failed authorization due to wrong nonce | Relayer sends sponsored transaction to Bob (10 wei transfer succeeds) but Alice's authorization to delegate to Oracle uses incorrect nonce, causing silent authorization failure |
BAL MUST include Alice with empty changes (account access), Bob with balance_changes (receives 10 wei), Relayer with nonce_changes. MUST NOT include Oracle (authorization failed, no delegation) |
✅ Completed |
test_bal_7702_invalid_chain_id_authorization |
Ensure BAL handles failed authorization due to wrong chain id | Relayer sends sponsored transaction to Bob (10 wei transfer succeeds) but Alice's authorization to delegate to Oracle uses incorrect chain id, causing authorization failure before account access |
BAL MUST include Bob with balance_changes (receives 10 wei), Relayer with nonce_changes. MUST NOT include Alice (authorization fails before loading account) or Oracle (authorization failed, no delegation) |
✅ Completed |
test_call_into_self_delegating_set_code |
Self-delegation degenerate one-hop case (companion to test_call_into_chain_delegating_set_code). File: tests/prague/eip7702_set_code_tx/test_set_code_txs.py. Parametrized over @pytest.mark.with_all_call_opcodes. |
auth_signer auths itself as its own delegation target. entry_address issues call_opcode(auth_signer). EVM resolves once: auth_signer's code is the designator pointing back to auth_signer; the second hop is not followed, so the 0xef0100... bytecode runs as legacy code → INVALID → returns 0. |
auth_signer MUST appear with nonce_changes and code_changes (delegation designator to itself). entry_address MUST have storage_reads=[0] (no-op SSTORE demoted). No additional delegation-target entry is created because the target coincides with the authority. |
✅ Completed |
test_bal_7702_invalid_authority_has_code_authorization |
Ensure BAL handles failed authorization where the authority already has non-empty, non-delegation code (EIP-7702 step-5 rejection, post-load). | Alice is pre-allocated with code=Op.STOP and nonce=1. Relayer sends sponsored tx to Bob (10 wei transfer). Authorization tuple delegates Alice to Oracle with matching nonce. EIP-7702 step 5 rejects because Alice's code is non-empty and not a delegation designator. |
BAL MUST include Bob with balance_changes (10 wei), Relayer with nonce_changes, and Alice with empty changes (loaded at step 4 then rejected at step 5). MUST NOT include Oracle (delegation target never loaded). |
✅ Completed |
test_signature_s_out_of_range |
Ensure BAL excludes the authority when the EIP-7702 auth signature has an out-of-range s value (EIP-2 low-s rule, ecrecover fails before authority is loaded). File: tests/prague/eip7702_set_code_tx/test_set_code_txs.py. |
auth_signer signs a normal auth tuple delegating to set_code_to_address; the tuple is then mutated so s = SECP256K1N - s and v is flipped, producing a high-s signature. Sender (a separate EOA) sends the tx to entry_address which executes SSTORE(1, 1). |
BAL MUST include sender with nonce_changes at block_access_index=1 and entry_address with storage_changes for slot 1 (post=1). BAL MUST NOT include auth_signer (auth fails at ecrecover, before the authority is added to accessed_addresses) or set_code_to_address (target is never loaded as an execution target). |
✅ Completed |
test_nonce_validity |
Ensure BAL correctly records EIP-7702 auth-tuple nonce-field validity across pre-load (nonce >= 2**64-1), success, and post-load (account.nonce != auth.nonce) branches. File: tests/prague/eip7702_set_code_tx/test_set_code_txs.py. |
Parametrized over (account_nonce, authorization_nonce): (1) (MAX, MAX) pre-load rejection at step 2, (2) (MAX-1, MAX-1) successful auth, (3) (0, 1) post-load nonce mismatch, (4) (1, 0) post-load nonce mismatch. entry_address always does SSTORE(success_slot, 1) + CALL(auth_signer) + SSTORE(return_slot, RETURNDATASIZE). |
Valid auth (case 2): auth_signer MUST have nonce_changes (post=account_nonce+1) and code_changes (delegation designation); set_code_to_address MUST appear with empty changes (loaded as execution target via delegation dispatch); entry_address has storage_changes for both slot 1 and slot 2 (post=1). Invalid auth (cases 1, 3, 4): auth_signer MUST appear with empty changes (the subsequent CALL touches it, even though the auth processing did or did not add it); set_code_to_address MUST NOT be in BAL; entry_address has storage_changes for slot 1 and storage_reads for slot 2 (the SSTORE(0, 0) no-op is demoted per EIP-7928). Sender always has nonce_changes. |
✅ Completed |
test_call_into_chain_delegating_set_code |
Ensure BAL respects EIP-7702 one-hop delegation resolution when A delegates to B and B delegates back to A. File: tests/prague/eip7702_set_code_tx/test_set_code_txs.py. Parameterized over @pytest.mark.with_all_call_opcodes. |
Two auths: auth_signer_1 -> auth_signer_2, then auth_signer_2 -> auth_signer_1. entry_address issues call_opcode(auth_signer_1) and stores the return code in slot 0. The EVM resolves A -> B once, then runs B's 0xef0100... bytecode as legacy code; 0xef is INVALID, so the call frame aborts and returns 0. |
BAL MUST include only auth_signer_1 and auth_signer_2 from the delegation chain (each with nonce_changes and code_changes for its delegation designator). No third address is ever loaded because the second hop is never followed. entry_address MUST have storage_reads for slot 0 (the SSTORE(slot, 0) is a no-op write and is demoted per EIP-7928) and no storage_changes. Sender has nonce_changes. |
✅ Completed |
test_bal_7702_multi_hop_delegation_chain |
Multi-hop EIP-7702 delegation: chain resolves A→B→C and loop resolves A→B→A. In both cases the EVM follows the delegation once and runs B's 0xef0100<dest> bytecode, which aborts on the INVALID 0xef opcode. Parametrized over destination_is_loop ∈ [False, True]. |
auth_a delegates to auth_b; auth_b delegates to either target_c (chain) or back to auth_a (loop). Entry contract has storage[0] = 0xDEAD (witness) and issues CALL(auth_a) then stores the return code in slot 0. |
auth_a and auth_b MUST appear with nonce_changes and code_changes (each successful auth). In chain, target_c MUST NOT appear in BAL. entry_address has storage_changes for slot 0 with post_value=0 (0xDEAD overwritten by the failed CALL's return code 0). Sender has nonce_changes. |
✅ Completed |
test_set_code_to_precompile |
Covers the BAL recording of a precompile address when reached via EIP-7702 delegation dispatch — across all 4 call opcodes and all precompiles. Companion to test_bal_precompile_call_opcode for the direct-call path. File: tests/prague/eip7702_set_code_tx/test_set_code_txs.py. |
@pytest.mark.with_all_call_opcodes × @pytest.mark.with_all_precompiles. EOA auth_signer delegates to a precompile address. A caller contract invokes call_opcode(auth_signer); EIP-7702 resolves the delegation to the precompile (whose code is treated as empty per spec). |
When EIP-7928 is enabled, BAL MUST include auth_signer with nonce_changes and code_changes (delegation designator), and MUST include the precompile address with empty changes (loaded as execution target via delegation dispatch). |
✅ Completed |
test_bal_7702_delegated_via_call_opcode |
Ensure BAL captures delegation target when a contract uses *CALL opcodes to call a delegated account | Pre-deployed contract Alice delegated to Oracle. Caller contract uses CALL/CALLCODE/DELEGATECALL/STATICCALL to call Alice. Bob sends transaction to Caller. |
BAL MUST include Bob: nonce_changes. Caller: empty changes (account access). Alice: empty changes (account access - delegated account being called). Oracle: empty changes (delegation target access). |
✅ Completed |
test_bal_7702_null_address_delegation_no_code_change |
Ensure BAL does not record spurious code changes for net-zero code operations | Alice sends transaction with authorization delegating to NULL_ADDRESS (0x0), which sets code to b"" on an account that already has b"" code. Transaction sends 10 wei to Bob. |
BAL MUST include Alice with nonce_changes (tx nonce + auth nonce increment) but MUST NOT include code_changes (setting b"" -> b"" is net-zero and filtered out). Bob: balance_changes (receives 10 wei). This ensures net-zero code change is not recorded. |
✅ Completed |
test_bal_7702_double_auth_reset |
Ensure BAL tracks multiple 7702 nonce increments but filters net-zero code change | Single transaction contains two EIP-7702 authorizations for Alice: (1) first auth sets delegation 0xef0100\|\|Oracle, (2) second auth clears delegation back to empty. Transaction sends 10 wei to Bob. Two variants: (a) Self-funded: Alice is tx sender (one tx nonce bump + two auth bumps → nonce 0→3). (b) Sponsored: Relayer is tx sender (Alice only in auths → nonce 0→2 for Alice, plus one nonce bump for Relayer). |
Variant (a): BAL MUST include Alice with nonce_changes 0→3. Variant (b): BAL MUST include Alice with nonce_changes 0→2 and Relayer with its own nonce_changes. For both variants, BAL MUST NOT include code_changes for Alice (net code is empty), MUST include Bob with balance_changes (receives 10 wei), and Oracle MUST NOT appear in BAL. |
✅ Completed |
test_bal_7702_double_auth_swap |
Ensure BAL captures final code when double auth swaps delegation targets | Relayer sends transaction with two authorizations for Alice: (1) First auth sets delegation to CONTRACT_A at nonce=0, (2) Second auth changes delegation to CONTRACT_B at nonce=1. Transaction sends 10 wei to Bob. Per EIP-7702, only the last authorization takes effect. |
BAL MUST include Alice with nonce_changes (both auths increment nonce to 2) and code_changes (final code is delegation designation for CONTRACT_B, not CONTRACT_A). Bob: balance_changes (receives 10 wei). Relayer: nonce_changes. Neither CONTRACT_A nor CONTRACT_B appear in BAL during delegation setup (never accessed). This ensures BAL shows final state, not intermediate changes. |
✅ Completed |
test_bal_sstore_and_oog |
Ensure BAL handles OOG during SSTORE execution at various gas boundaries (EIP-2200 stipend and implicit SLOAD) | Alice calls contract that attempts SSTORE to cold slot 0x01. Parameterized: (1) OOG at EIP-2200 stipend check (2300 gas after PUSH opcodes) - fails before implicit SLOAD, (2) OOG at stipend + 1 (2301 gas) - passes stipend check but fails after implicit SLOAD, (3) OOG at exact gas - 1, (4) Successful SSTORE with exact gas. |
For case (1): BAL MUST NOT include slot 0x01 in storage_reads or storage_changes (fails before implicit SLOAD). For cases (2) and (3): BAL MUST include slot 0x01 in storage_reads (implicit SLOAD occurred) but MUST NOT include in storage_changes (write didn't complete). For case (4): BAL MUST include slot 0x01 in storage_changes only (successful write; read is filtered by builder). |
✅ Completed |
test_bal_sstore_static_context |
SSTORE in static context must not leak storage reads into BAL | Contract A STATICCALLs Contract B which attempts SSTORE. Parametrized: original_value (0, nonzero) to catch clients that perform the implicit SLOAD before the static check. |
Contract B IS in BAL (accessed via STATICCALL) but MUST NOT have storage_reads. |
✅ Completed |
test_bal_sload_and_oog |
Ensure BAL handles OOG during SLOAD execution correctly | Alice calls contract that attempts SLOAD from cold slot 0x01. Parameterized: (1) OOG at SLOAD opcode (insufficient gas), (2) Successful SLOAD execution. |
For OOG case: BAL MUST NOT contain slot 0x01 in storage_reads since storage wasn't accessed. For success case: BAL MUST contain slot 0x01 in storage_reads. |
✅ Completed |
test_bal_balance_and_oog |
Ensure BAL handles OOG during BALANCE opcode execution correctly | Alice calls contract that attempts BALANCE opcode on cold target account. Parameterized: (1) OOG at BALANCE opcode (insufficient gas), (2) Successful BALANCE execution. |
For OOG case: BAL MUST NOT include target account (wasn't accessed). For success case: BAL MUST include target account in account_changes. |
✅ Completed |
test_bal_account_touch_system_address |
Ensure BAL includes SYSTEM_ADDRESS when a regular transaction touches it via any account-accessing opcode |
Alice calls a contract that executes one of BALANCE, EXTCODESIZE, EXTCODEHASH, EXTCODECOPY, CALL, or STATICCALL against SYSTEM_ADDRESS. Parametrized over the six opcodes. |
BAL MUST include SYSTEM_ADDRESS as an account-only entry for every opcode because the address experienced a real EVM state access. This is distinct from excluding the synthetic system-operation caller. |
✅ Completed |
test_bal_selfdestruct_to_system_address_zero_balance |
Ensure BAL includes SYSTEM_ADDRESS as the SELFDESTRUCT beneficiary even when no value is transferred. Companion to test_bal_account_touch_system_address (which covers BALANCE/EXTCODE*/CALL/STATICCALL); SELFDESTRUCT is the missing opcode in that parametrize list. File: tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_opcodes.py. |
CREATE transaction whose init code is SELFDESTRUCT(SYSTEM_ADDRESS); the new contract carries no value, so the beneficiary receives 0 wei. Per EIP-6780 the contract is deleted (creation and destruction in the same tx). |
BAL MUST include SYSTEM_ADDRESS with every change-set empty — the SELFDESTRUCT is the only access on SYSTEM_ADDRESS in this block. Post-state: created contract is NONEXISTENT (proves SELFDESTRUCT fired); Alice has nonce=1. |
✅ Completed |
test_bal_extcodesize_and_oog |
Ensure BAL handles OOG during EXTCODESIZE opcode execution correctly | Alice calls contract that attempts EXTCODESIZE opcode on cold target contract. Parameterized: (1) OOG at EXTCODESIZE opcode (insufficient gas), (2) Successful EXTCODESIZE execution. |
For OOG case: BAL MUST NOT include target contract (wasn't accessed). For success case: BAL MUST include target contract in account_changes. |
✅ Completed |
test_bal_delegatecall_no_delegation_and_oog_before_target_access |
Ensure BAL handles OOG before target access and success for non-delegated DELEGATECALL | Parametrized: target warm/cold, (args_size, ret_size) pair (covers in-only and out-only expansion per #1910), OOG boundary (before_target_access/success). |
OOG: target in BAL ONLY if pre-warmed. Success: target always in BAL. | ✅ Completed |
test_bal_delegatecall_7702_delegation_and_oog |
Ensure BAL handles OOG at all 4 boundaries for DELEGATECALL to 7702 delegated accounts | Parametrized: target warm/cold, delegation warm/cold, (args_size, ret_size) pair (covers in-only and out-only expansion per #1910), OOG boundary (before_target_access/after_target_access/success_minus_1/success). |
OOG before: neither in BAL. OOG after & success_minus_1: target in BAL, delegation NOT in BAL (static check optimization). Success: all in BAL. | ✅ Completed |
test_bal_callcode_no_delegation_and_oog_before_target_access |
Ensure BAL handles OOG before target access and success for non-delegated CALLCODE | Parametrized: target warm/cold, value 0/1, (args_size, ret_size) pair (covers in-only and out-only expansion per #1910), OOG boundary (before_target_access/success). |
OOG: target in BAL ONLY if pre-warmed. Success: target always in BAL. | ✅ Completed |
test_bal_callcode_7702_delegation_and_oog |
Ensure BAL handles OOG at all 4 boundaries for CALLCODE to 7702 delegated accounts | Parametrized: target warm/cold, delegation warm/cold, value 0/1, (args_size, ret_size) pair (covers in-only and out-only expansion per #1910), OOG boundary (before_target_access/after_target_access/success_minus_1/success). |
OOG before: neither in BAL. OOG after & success_minus_1: target in BAL, delegation NOT in BAL (static check optimization). Success: all in BAL. | ✅ Completed |
test_bal_staticcall_no_delegation_and_oog_before_target_access |
Ensure BAL handles OOG before target access and success for non-delegated STATICCALL | Parametrized: target warm/cold, (args_size, ret_size) pair (covers in-only and out-only expansion per #1910), OOG boundary (before_target_access/success). |
OOG: target in BAL ONLY if pre-warmed. Success: target always in BAL. | ✅ Completed |
test_bal_staticcall_7702_delegation_and_oog |
Ensure BAL handles OOG at all 4 boundaries for STATICCALL to 7702 delegated accounts | Parametrized: target warm/cold, delegation warm/cold, (args_size, ret_size) pair (covers in-only and out-only expansion per #1910), OOG boundary (before_target_access/after_target_access/success_minus_1/success). |
OOG before: neither in BAL. OOG after & success_minus_1: target in BAL, delegation NOT in BAL (static check optimization). Success: all in BAL. | ✅ Completed |
test_bal_extcodecopy_and_oog |
Ensure BAL handles OOG during EXTCODECOPY at various failure points | Alice calls contract that attempts EXTCODECOPY from cold target contract. Parameterized: (1) Successful EXTCODECOPY, (2) OOG at cold access (insufficient gas for account access), (3) OOG at memory expansion with large offset (64KB offset, gas covers cold access + copy but NOT memory expansion), (4) OOG at memory expansion boundary (256 byte offset, gas is exactly 1 less than needed). |
For success case: BAL MUST include target contract. For all OOG cases: BAL MUST NOT include target contract. Gas for ALL components (cold access + copy + memory expansion) must be checked BEFORE recording account access. | ✅ Completed |
test_bal_multiple_balance_changes_same_account |
Ensure BAL tracks multiple balance changes to same account across transactions | Alice funds Bob (starts at 0) in tx0 with exact amount needed. Bob spends everything in tx1 to Charlie. Bob's balance: 0 → funding_amount → 0 | BAL MUST include Bob with two balance_changes: one at txIndex=1 (receives funds) and one at txIndex=2 (balance returns to 0). This tests balance tracking across two transactions. |
✅ Completed |
test_bal_multiple_storage_writes_same_slot |
Ensure BAL tracks multiple writes to same storage slot across transactions | Alice calls contract 3 times in same block. Contract increments slot 1 on each call: 0 → 1 → 2 → 3 | BAL MUST include contract with slot 1 having three slot_changes: txIndex=1 (value 1), txIndex=2 (value 2), txIndex=3 (value 3). Each transaction's write must be recorded separately. |
✅ Completed |
test_bal_nested_delegatecall_storage_writes_net_zero |
Ensure BAL correctly filters net-zero storage changes across nested DELEGATECALL frames | Parametrized by nesting depth (1-3). Root contract has slot 0 = 1. Each frame writes a different intermediate value via DELEGATECALL chain, deepest frame writes back to original value (1). Example depth=2: 1 → 2 → 3 → 1 | BAL MUST include root contract with storage_reads for slot 0 but MUST NOT include storage_changes (net-zero). All delegate contracts MUST have empty changes. Tests that frame merging correctly removes parent's intermediate writes when child reverts to pre-tx value. |
✅ Completed |
test_bal_cross_tx_storage_write |
Ensure storage changes behave as expected across transaction boundaries | Tx1 writes a non-zero value to an empty slot; tx2 either writes zero (back to pre-block) or rewrites the same value (no-op vs post-tx1). | Tx1's change always appears at index 1. The revert case adds tx2's change at index 2 (must not be filtered as net-zero). The same-value case adds nothing and the slot MUST NOT appear in storage_reads (uniqueness rule). | ✅ Completed |
test_bal_cross_tx_storage_chain |
Verify clients apply BAL state changes from prior transactions before executing later transactions in the same block. Each later Tx depends on the two preceding writes (Fibonacci-style), so any tx skipped or run against pre-block state cascades into a wrong slot value and a different state root. | Fixed chain_length=8. Single branching contract: Tx i with i<2 seeds slot i with 1; Tx i with i>=2 writes slot[i] = SLOAD(i-1) + SLOAD(i-2). |
BAL MUST include the contract with storage_changes for each slot i (post=fib(i) at block_access_index=i+1). Post-state: slots 0-7 equal [1, 1, 2, 3, 5, 8, 13, 21]. |
✅ Completed |
test_bal_cross_tx_deploy_then_call |
Verify clients apply Tx1's CREATE to their state view before executing Tx2's CALL in the same block. A client that parallelizes Tx2 without applying Tx1's code_changes would hit an empty account, the CALL would no-op, and slot 0 would remain 0. Parametrized over @pytest.mark.with_all_create_opcodes (CREATE and CREATE2). |
Tx1 (Alice) calls a factory which CREATE/CREATE2s a contract whose runtime is SSTORE(0, 0x42) + STOP at a deterministic address. Tx2 (Bob) CALLs that address directly. |
BAL MUST include the target contract with nonce_changes and code_changes at block_access_index=1 (the deployment) and storage_changes for slot 0 (post=0x42) at block_access_index=2 (Tx2's CALL through the deployed runtime). Post-state: target contract has runtime code and slot[0] == 0x42. |
✅ Completed |
test_bal_cross_tx_factory_nonce_create_chain |
Verify clients propagate factory.nonce_changes across txs when later CREATE addresses derive from the factory's current nonce. The cross-tx dependency signal is solely nonce_changes — no storage or balance mutations exist anywhere. A scheduler that deprioritizes nonce_changes (or schedules CREATE-family txs by their resulting distinct addresses) would speculatively derive addr(factory, N+1) for every tx and produce only one successful deployment. Parametrized: failure_mode ∈ ["none", "collision", "oog"] — collision pre-populates the mid-chain target (factory.nonce still bumps; chain slides forward); oog gives the mid-chain tx intrinsic+1 gas so CREATE never fires (factory.nonce does not bump; chain slides backward, reusing the slot). |
8 txs from 8 distinct senders each call a shared factory that does CREATE with identical minimal initcode (deploys Op.STOP as runtime). |
none: factory has 8 sequential nonce_changes (post=N+1..N+8); each address has nonce_changes/code_changes at its block_access_index. collision: factory still has 8 sequential nonce_changes; the colliding target appears with BalAccountExpectation.empty() (accessed under EIP-684, no state change) and its pre-state code is preserved. oog: factory has only 7 nonce_changes (the OOG tx contributes none); subsequent post_nonce values are shifted -1; the OOG'd tx's would-be address slot is filled by the next tx; the final chain address is never touched (NONEXISTENT). Post-state: senders all nonce=1; factory nonce=N+(7 or 8) depending on mode. |
✅ Completed |
test_bal_cross_tx_balance_dependency |
Verify clients apply Tx1's balance change before executing Tx2 in the same block. A client that parallelizes Tx2 without applying Tx1's balance_changes would record the pre-block balance via SELFBALANCE, yielding a different state root. Parametrized over direct_call and selfdestruct funding paths so a client can't tie balance tracking to recipient-code execution. |
Tx1 (Alice) routes 1 wei into a branching contract (empty calldata → STOP). In direct_call, alice sends value directly; in selfdestruct, alice calls a pre-funded killer contract that SELFDESTRUCTs to the recipient (recipient bytecode never runs in Tx1). Tx2 (Bob) calls the contract with non-empty calldata, taking the SSTORE(0, SELFBALANCE) path. |
BAL MUST include the contract with balance_changes at block_access_index=1 (post=1) and storage_changes for slot 0 (post=1) at block_access_index=2. In selfdestruct, BAL also includes the killer with balance_changes (post=0) at index 1. Post-state: contract.balance == contract.storage[0] == 1 proves Tx2 observed Tx1's balance change. |
✅ Completed |
test_bal_7702_cross_tx_delegation_then_call |
Verify clients apply Tx1's EIP-7702 delegation before later txs CALL the now-delegated EOA. Three-tx chain forces clients to apply both the code-install and each intermediate storage increment for the final value to be correct. | Tx1 (Relayer): sponsors an EIP-7702 auth that delegates Alice to a SSTORE(0, SLOAD(0) + 1) counter contract. Tx2 (Bob) and Tx3 (Charlie): both CALL Alice, which dispatches to the counter and increments her slot 0. |
BAL MUST include Alice with code_changes at block_access_index=1 (delegation designator), nonce_changes at index 1, and two storage_changes for slot 0 (post=1 at index 2, post=2 at index 3). Post-state: Alice has the delegation code and slot[0] == 2. |
✅ Completed |
test_bal_cross_tx_funding_chain |
Verify clients apply each tx's BAL balance_changes to the next sender's funds check. Five-tx chain across distinct senders, each intermediate starts empty and depends on the prior tx to be solvent. A parallelizing client validating any later tx against pre-block state would see zero balance on its sender and wrongly reject the block. Parametrized over success, oog_minus_1 (eunice's tx OOGs at exact gas minus one), and insufficient_funds (dan forwards one wei short so eunice's upfront balance check fails, block rejected with INSUFFICIENT_ACCOUNT_FUNDS). |
Tx1 alice→bob, Tx2 bob→charlie, Tx3 charlie→dan, Tx4 dan→eunice. Each forwards exactly the next sender's upfront cost. Tx5 eunice→target with runtime SSTORE(0, 0xC0FFEE). |
For success/oog_minus_1: BAL MUST include nonce/balance changes for each EOA at its tx's block_access_index, plus target's storage_changes at index 5 (success) or storage_reads=[0] (oog_minus_1). Post-state: all five EOAs end with balance=0; target has slot[0]==0xC0FFEE in success and empty storage in oog_minus_1. For insufficient_funds: block MUST be rejected with TransactionException.INSUFFICIENT_ACCOUNT_FUNDS. |
✅ Completed |
test_bal_withdrawal_predeploy_balance_observed_cross_tx |
Verify Tx2 observes Tx1's balance change on the EIP-7002 withdrawal predeploy via the BALANCE opcode. Exercises cross-tx visibility through a system address that is also touched by the prepare-block system call, so a client snapshotting the predeploy ahead of the BAL prefix would mask the overlay. Companion to test_bal_cross_tx_balance_dependency, which uses SELFBALANCE on a regular contract. File: tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_cross_index.py. |
Tx1 (sender_0) sends 1 wei (the fee at excess=0) to WITHDRAWAL_REQUEST_PREDEPLOY with a valid 56-byte withdrawal-request calldata; the predeploy retains the fee. Tx2 (sender_1) calls a reader contract whose runtime is SSTORE(0, BALANCE(WITHDRAWAL_REQUEST_PREDEPLOY)). |
BAL MUST include WITHDRAWAL_REQUEST_PREDEPLOY with balance_changes at block_access_index=1 (post=1) and the reader with storage_changes for slot 0 at block_access_index=2 (post=1). Post-state: reader.storage[0] == 1 proves Tx2's BALANCE opcode returned the post-Tx1 balance. |
✅ Completed |
test_bal_intra_tx_multiple_sstores_same_slot |
Ensure BAL coalesces consecutive SSTOREs to the same slot within one tx into a single storage change with the final post-value | Contract executes SSTORE(0x01, 0xAA) + SSTORE(0x01, 0xBB) + SSTORE(0x01, 0xCC) in one tx. Parametrized by pre_value: slot_starts_empty (0x00), slot_starts_nonzero (0x11), intermediate_equals_pre (0xBB, where the second write transiently matches the pre-state). |
BAL MUST include contract with slot 0x01 having exactly one slot_changes entry: txIndex=1, post_value=0xCC. Intermediate values 0xAA and 0xBB MUST NOT appear as separate entries (enforced via absent_values). |
✅ Completed |
test_bal_intra_tx_sstores_same_slot_net_zero |
Ensure BAL filters net-zero result when multiple SSTOREs to the same slot occur within one tx | Parametrized: nonzero_pre_returns_to_pre (pre=0xCC, writes 0xAA→0xBB→0xCC) and empty_pre_ephemeral_writes (pre=0x00, writes 0xAA→0xBB→0x00). Final value equals pre-state in both cases. |
BAL MUST include contract with slot 0x01 in storage_reads (slot was accessed) and MUST NOT include slot 0x01 in storage_changes (net-zero). |
✅ Completed |
test_bal_create_contract_init_revert |
Ensure BAL correctly handles CREATE when parent call reverts | Caller calls factory, factory executes CREATE (succeeds), then factory REVERTs rolling back the CREATE | BAL MUST include Alice with nonce_changes. Caller and factory with no changes (reverted). Created contract address appears in BAL but MUST NOT have nonce_changes or code_changes (CREATE was rolled back). Contract address MUST NOT exist in post-state. |
✅ Completed |
test_bal_create_oog_code_deposit |
Ensure BAL correctly handles CREATE OOG during code deposit | Alice calls factory contract that executes CREATE with init code returning 10,000 bytes. Transaction has insufficient gas for code deposit. Factory nonce increments, CREATE returns 0 and stores in slot 1. | BAL MUST include Alice with nonce_changes. Factory with nonce_changes (incremented by CREATE) and storage_changes (slot 1 = 0). Contract address with empty changes (read during collision check). MUST NOT include nonce or code changes for contract address (rolled back on OOG). Contract address MUST NOT exist in post-state. |
✅ Completed |
test_bal_create_early_failure |
Ensure BAL omits would-be address when CREATE/CREATE2 fails because of insufficient balance. | Factory with balance below the endowment attempts CREATE/CREATE2; the call fails before the address is accessed. | Alice: nonce_changes. Factory: storage_changes (CREATE returned 0), no nonce_changes. Would-be address MUST NOT appear in BAL. |
✅ Completed |
test_bal_invalid_missing_nonce |
Verify clients reject blocks with BAL missing required nonce changes | Alice sends transaction to Bob; BAL modifier removes Alice's nonce change entry | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST validate that all sender accounts have nonce changes recorded. |
✅ Completed |
test_bal_invalid_nonce_value |
Verify clients reject blocks with incorrect nonce values in BAL | Alice sends transaction to Bob; BAL modifier changes Alice's nonce to incorrect value | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST validate nonce values match actual state transitions. |
✅ Completed |
test_bal_invalid_storage_value |
Verify clients reject blocks with incorrect storage values in BAL | Alice calls contract that writes to storage; BAL modifier changes storage value to incorrect value | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST validate storage change values match actual state transitions. |
✅ Completed |
test_bal_invalid_tx_order |
Verify clients reject blocks with incorrect transaction indices in BAL | Alice sends transaction; BAL modifier swaps transaction indices incorrectly | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST validate transaction ordering matches actual block execution order. |
✅ Completed |
test_bal_invalid_account |
Verify clients reject blocks with incorrect account addresses in BAL | Alice sends transaction; BAL modifier includes wrong account address | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST validate all account addresses in BAL were actually accessed. |
✅ Completed |
test_bal_invalid_duplicate_account |
Verify clients reject blocks with duplicate account entries in BAL | Alice sends transaction; BAL modifier duplicates Alice's account entry | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST ensure each account appears at most once in BAL. |
✅ Completed |
test_bal_invalid_account_order |
Verify clients reject blocks with incorrect account ordering in BAL | Alice sends transaction to Bob; BAL modifier reverses account order (BAL requires sorted order) | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST validate accounts are in canonical sorted order. |
✅ Completed |
test_bal_invalid_complex_corruption |
Verify clients reject blocks with multiple BAL corruptions | Alice calls contract with storage writes; BAL has multiple issues: wrong account, missing nonce, wrong storage value | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST detect any corruption regardless of other issues. |
✅ Completed |
test_bal_invalid_missing_account |
Verify clients reject blocks whose BAL omits an account that was touched during block execution | Parameterized test: balance_change — Alice sends a value transfer to Bob; BAL modifier removes Bob's entry (recipient had a balance change). access_only — Alice calls a contract that executes BALANCE against a target account without changing it; BAL modifier removes the target account's entry (accessed-but-unchanged). |
Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST validate all accessed accounts are present, including accessed-but-unchanged accounts with empty change lists. |
✅ Completed |
test_bal_invalid_balance_value |
Verify clients reject blocks with incorrect balance values in BAL | Alice sends value to Bob; BAL modifier changes balance to incorrect value | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST validate balance change values match actual state transitions. |
✅ Completed |
test_bal_empty_block_no_coinbase |
Ensure BAL correctly handles empty blocks without including coinbase | Block with 0 transactions, no withdrawals. System contracts may perform operations (EIP-2935 parent hash, EIP-4788 beacon root if active). | BAL MUST NOT include the coinbase/fee recipient (receives no fees). BAL MAY include system contract addresses (EIP-2935 HISTORY_STORAGE_ADDRESS, EIP-4788 BEACON_ROOTS_ADDRESS) with storage_changes at block_access_index=0 (pre-execution system operations). |
✅ Completed |
test_bal_coinbase_zero_tip |
Ensure BAL includes coinbase even when priority fee is zero | Block with 1 transaction: Alice sends 5 wei to Bob with priority fee = 0 (base fee burned post-EIP-1559) | BAL MUST include Alice with balance_changes (gas cost) and nonce_changes. BAL MUST include Bob with balance_changes. BAL MUST include coinbase with empty changes. |
✅ Completed |
test_bal_withdrawal_empty_block |
Ensure BAL captures withdrawal balance changes in empty block | Charlie starts with 1 gwei. Block with 0 transactions and 1 withdrawal of 10 gwei to Charlie | BAL MUST include Charlie with balance_changes at block_access_index = 1. Charlie's balance_changes MUST show final balance of 11 gwei. All other fields (storage_reads, storage_changes, nonce_changes, code_changes) MUST be empty. |
✅ Completed |
test_bal_withdrawal_and_transaction |
Ensure BAL captures both transaction and withdrawal balance changes | Block with 1 transaction: Alice sends 5 wei to Bob. 1 withdrawal of 10 gwei to Charlie | BAL MUST include Alice with nonce_changes and balance_changes at block_access_index = 1. BAL MUST include Bob with balance_changes at block_access_index = 1. BAL MUST include Charlie with balance_changes at block_access_index = 2 showing final balance after receiving 10 gwei. All other fields for Charlie MUST be empty. |
✅ Completed |
test_bal_withdrawal_to_nonexistent_account |
Ensure BAL captures withdrawal to non-existent account | Block with 1 withdrawal of 10 gwei to non-existent account Charlie | BAL MUST include Charlie with balance_changes at block_access_index = 1 showing final balance of 10 gwei. All other fields (storage_reads, storage_changes, nonce_changes, code_changes) MUST be empty. |
✅ Completed |
test_bal_withdrawal_no_evm_execution |
Ensure BAL captures withdrawal without triggering EVM execution | Contract Oracle with storage slot 0x01 = 0x42. Oracle code writes to slot 0x01 when called. Block with 1 withdrawal of 10 gwei to Oracle |
BAL MUST include Oracle with balance_changes at block_access_index = 1 showing final balance after receiving 10 gwei. Storage slot 0x01 MUST remain 0x42 and all other fields (storage_reads, storage_changes, nonce_changes, code_changes) MUST be empty. |
✅ Completed |
test_bal_withdrawal_and_state_access_same_account |
Ensure BAL captures both state access and withdrawal to same address | Contract Oracle with storage slot 0x01 = 0x42. Block with 1 transaction: Alice calls Oracle (reads from slot 0x01, writes to slot 0x02). 1 withdrawal of 10 gwei to Oracle |
BAL MUST include Oracle with storage_reads for slot 0x01 and storage_changes for slot 0x02 at block_access_index = 1. Oracle MUST also have balance_changes at block_access_index = 2 showing final balance after receiving 10 gwei. Both state access and withdrawal MUST be captured. |
✅ Completed |
test_bal_withdrawal_and_value_transfer_same_address |
Ensure BAL captures both transaction value transfer and withdrawal to same address | Block with 1 transaction: Alice sends 5 gwei to Bob. 1 withdrawal of 10 gwei to Bob | BAL MUST include Alice with nonce_changes and balance_changes at block_access_index = 1. BAL MUST include Bob with balance_changes at block_access_index = 1 showing balance after receiving 5 gwei. Bob MUST also have balance_changes at block_access_index = 2 showing balance after receiving 10 gwei withdrawal. Bob's final post-state balance MUST be 15 gwei (cumulative). |
✅ Completed |
test_bal_multiple_withdrawals_same_address |
Ensure BAL accumulates multiple withdrawals to same address | Block with 3 withdrawals to Charlie: 5 gwei, 10 gwei, 15 gwei | BAL MUST include Charlie with balance_changes at block_access_index = 1 showing final balance of 30 gwei. All other fields (storage_reads, storage_changes, nonce_changes, code_changes) MUST be empty. |
✅ Completed |
test_bal_withdrawal_and_selfdestruct |
Ensure BAL captures withdrawal to self-destructed contract address | Contract Oracle with 100 gwei balance. Block with 1 transaction: Oracle self-destructs sending balance to Bob. 1 withdrawal of 50 gwei to Oracle's address |
BAL MUST include Oracle with balance_changes showing 0 balance at block_access_index = 1 (after self-destruct). BAL MUST include Bob with balance_changes showing 100 gwei received from self-destruct at block_access_index = 1. Oracle MUST also have balance_changes at block_access_index = 2 showing 50 gwei after withdrawal. Both self-destruct and withdrawal MUST be captured. |
✅ Completed |
test_bal_withdrawal_and_new_contract |
Ensure BAL captures withdrawal to newly created contract | Block with 1 transaction: Alice deploys contract Oracle with 5 gwei initial balance. 1 withdrawal of 10 gwei to Oracle |
BAL MUST include Oracle with code_changes and balance_changes showing 5 gwei at block_access_index = 1. Oracle MUST also have balance_changes at block_access_index = 2 showing balance after receiving 10 gwei withdrawal. Oracle's final post-state balance MUST be 15 gwei (cumulative). |
✅ Completed |
test_bal_zero_withdrawal |
Ensure BAL handles zero-amount withdrawal correctly | Block with 0 transactions and 1 zero-amount withdrawal (0 gwei) to Charlie. Two variations: Charlie has existing balance (5 gwei) or Charlie is non-existent. | BAL MUST include Charlie at block_access_index = 1 with empty changes. Balance remains unchanged. |
✅ Completed |
test_bal_withdrawal_to_precompiles |
Ensure BAL captures withdrawal to precompile addresses | Block with 1 withdrawal of 10 gwei to precompile address (all precompiles) | BAL MUST include precompile address with balance_changes at block_access_index = 1 showing final balance of 10 gwei. All other fields (storage_reads, storage_changes, nonce_changes, code_changes) MUST be empty. |
✅ Completed |
test_bal_withdrawal_largest_amount |
Ensure BAL captures withdrawal with largest amount | Block with 1 withdrawal of maximum uint64 value (2^64-1 gwei) to Charlie | BAL MUST include Charlie with balance_changes at block_access_index = 1 showing final balance of (2^64-1) * 10^9 wei. All other fields (storage_reads, storage_changes, nonce_changes, code_changes) MUST be empty. |
✅ Completed |
test_bal_withdrawal_to_coinbase |
Ensure BAL captures withdrawal to coinbase address | Block with 1 transaction: Alice sends 5 wei to Bob. 1 withdrawal of 10 gwei to coinbase/fee recipient | BAL MUST include coinbase with balance_changes at block_access_index = 1 showing balance after transaction fees. Coinbase MUST also have balance_changes at block_access_index = 2 showing balance after receiving 10 gwei withdrawal. Coinbase's final post-state balance MUST include both transaction fees and withdrawal. |
✅ Completed |
test_bal_withdrawal_to_coinbase_empty_block |
Ensure BAL captures withdrawal to coinbase even when there are no transactions (no fees) | Block with 0 transactions and 1 withdrawal of 10 gwei to coinbase/fee recipient | BAL MUST include coinbase with balance_changes at block_access_index = 1 showing final balance of 10 gwei. All other fields (storage_reads, storage_changes, nonce_changes, code_changes) MUST be empty. |
✅ Completed |
test_bal_nonexistent_value_transfer |
Ensure BAL captures non-existent account on value transfer | Alice sends value (0 wei or 1 ETH) to non-existent account Bob (address never funded or accessed before) via direct transfer | For zero value: BAL MUST include Alice with nonce_changes and Bob (non-existent) with empty changes. For positive value: BAL MUST include Bob with balance_changes showing received amount. |
✅ Completed |
test_bal_nonexistent_account_access_read_only |
Ensure BAL captures non-existent account accessed via read-only account-reading opcodes | Alice calls Oracle contract which uses read-only account access opcodes (BALANCE, EXTCODESIZE, EXTCODECOPY, EXTCODEHASH, STATICCALL, DELEGATECALL) on non-existent account Bob. |
BAL MUST include Alice with nonce_changes, Oracle with empty changes, and Bob with empty changes (account accessed but no state modifications). |
✅ Completed |
test_bal_nonexistent_account_access_value_transfer |
Ensure BAL captures non-existent account accessed via CALL/CALLCODE with value transfers | Alice calls Oracle contract which uses CALL or CALLCODE on non-existent account Bob. Tests both zero and positive value transfers. |
BAL MUST include Alice with nonce_changes. For CALL with positive value: Oracle with balance_changes (loses value), Bob with balance_changes (receives value). For CALLCODE with value or zero value transfers: Oracle and Bob with empty changes (CALLCODE self-transfer = net zero). |
✅ Completed |
test_bal_storage_write_read_same_frame |
Ensure BAL captures write precedence over read in same call frame (writes shadow reads) | Alice calls Oracle which writes (SSTORE) value 0x42 to slot 0x01, then reads (SLOAD) from slot 0x01 in the same call frame |
BAL MUST include Oracle with slot 0x01 in storage_changes showing final value 0x42. Slot 0x01 MUST NOT appear in storage_reads (write shadows the subsequent read in same frame). |
✅ Completed |
test_bal_storage_write_read_cross_frame |
Ensure BAL captures write precedence over read across call frames (writes shadow reads cross-frame) | Alice calls Oracle. First call reads slot 0x01 (sees initial value), writes 0x42 to slot 0x01, then calls itself (via CALL, DELEGATECALL, or CALLCODE). Second call reads slot 0x01 (sees 0x42) and exits. |
BAL MUST include Oracle with slot 0x01 in storage_changes showing final value 0x42. Slot 0x01 MUST NOT appear in storage_reads (write shadows both the read before it in same frame and the read in the recursive call). |
✅ Completed |
test_bal_create_transaction_empty_code |
Ensure BAL does not record spurious code changes for CREATE transaction deploying empty code | Alice sends CREATE transaction with empty initcode (deploys code b""). Contract address gets nonce = 1 and code = b"". |
BAL MUST include Alice with nonce_changes and created contract with nonce_changes but MUST NOT include code_changes for contract (setting b"" -> b"" is net-zero). |
✅ Completed |
test_bal_cross_block_ripemd160_state_leak |
Ensure internal EVM state for precompile handling does not leak between blocks | Block 1: Alice calls RIPEMD-160 (0x03) with zero value (RIPEMD-160 must be pre-funded). Block 2: Bob's transaction triggers an exception (stack underflow). | BAL for Block 1 MUST include RIPEMD-160. BAL for Block 2 MUST NOT include RIPEMD-160 (never accessed in Block 2). Internal state from Parity Touch Bug (EIP-161) handling must be reset between blocks. | ✅ Completed |
test_bal_all_transaction_types |
Ensure BAL correctly captures state changes from all transaction types in a single block | Single block with 5 transactions: Type 0 (Legacy), Type 1 (EIP-2930 Access List), Type 2 (EIP-1559), Type 3 (EIP-4844 Blob), Type 4 (EIP-7702 Set Code). Each tx writes to contract storage. Note: Access list addresses are pre-warmed but NOT recorded in BAL (no state access). | BAL MUST include: (1) All 5 senders with nonce_changes. (2) Contracts 0-3 with storage_changes. (3) Alice (7702 target) with nonce_changes, code_changes (delegation), storage_changes. (4) Oracle (delegation source) with empty changes. |
✅ Completed |
test_bal_create_collision |
Ensure BAL handles CREATE/CREATE2 address collision correctly, with or without a subsequent tx that modifies the colliding address. Parametrized: @pytest.mark.with_all_create_opcodes, modification ∈ {"collision_only", "then_nonce_change", "then_storage_change"}. |
Factory (nonce=1, slot[0]=0xDEAD) executes CREATE/CREATE2 targeting a pre-populated address X. For collision_only, X is code=STOP, nonce=1. For then_nonce_change, X is a contract that runs its own inner CREATE when called. For then_storage_change, X is a contract that SSTOREs when called. If modification != "collision_only", a second tx (from Bob) calls X. |
BAL MUST include: (1) Factory with nonce_changes (1→2, bumped even on failed CREATE/CREATE2), storage_changes for slot 0 (0xDEAD→0). (2) X for the collision touch at index 1. For collision_only, X's BAL entry is empty(). For then_nonce_change, X has nonce_changes at index 2 (post=2) and the inner-created child has nonce_changes/code_changes at index 2; X MUST NOT have spurious code_changes/storage_changes/storage_reads/balance_changes. For then_storage_change, X has storage_changes for slot 0x01 (post=0xCAFE) at index 2; MUST NOT have spurious entries on other axes. |
✅ Completed |
test_bal_create2_deploy_then_collision |
Ensure BAL correctly preserves a colliding address's deployment entries when the same address is later touched again via a collision check, and that init code does NOT execute on the collision. | Tx1: factory CREATE2 deploys X; X's init code does SSTORE(0, SLOAD(0)+1) (increment) then deploys STOP. Tx2: same factory retries the identical CREATE2 — collision against X. |
BAL MUST include: (1) Factory with nonce_changes at indices 1 and 2 (1→2→3) and storage_changes at slot 0 with entries at indices 1 (post=X.address) and 2 (post=0 from the failed CREATE2 return). (2) X with nonce_changes=[(1, 1)], code_changes=[(1, STOP)], and storage_changes slot 0 [(1, 1)] from the deployment; no entries at index 2 and storage_reads MUST be empty — a client that runs init then reverts on collision would leak slot 0 as a demoted read. Post-state: X slot 0 == 1 proves init executed exactly once (tx2 collided, didn't re-run init). |
✅ Completed |
test_bal_create_selfdestruct_to_self_with_call |
Ensure BAL handles init code that calls external contract then selfdestructs to itself | Factory executes CREATE2 with endowment=100. Init code (embedded in factory via CODECOPY): (1) CALL(Oracle, 0) - Oracle writes to its storage slot 0x01. (2) SSTORE(0x01, 0x42) - write to own storage. (3) SELFDESTRUCT(SELF) - selfdestruct to own address. Contract created and destroyed in same tx. |
BAL MUST include: (1) Factory with nonce_changes, balance_changes (loses 100). (2) Oracle with storage_changes for slot 0x01 (external call succeeded). (3) Created address with storage_reads for slot 0x01 (aborted write becomes read) - MUST NOT have nonce_changes, code_changes, storage_changes, or balance_changes (ephemeral contract, balance burned via SELFDESTRUCT to self). |
✅ Completed |
test_bal_selfdestruct_to_7702_delegation |
Ensure BAL correctly handles SELFDESTRUCT to a 7702 delegated account (no code execution on recipient) | Tx1: Alice authorizes delegation to Oracle (sets code to 0xef0100\|\|Oracle). Tx2: Victim contract (balance=100) executes SELFDESTRUCT(Alice). Two separate transactions in same block. Note: Alice starts with initial balance which accumulates with selfdestruct. |
BAL MUST include: (1) Alice at block_access_index=1 with code_changes (delegation), nonce_changes. (2) Alice at block_access_index=2 with balance_changes (receives selfdestruct). (3) Victim at block_access_index=2 with balance_changes (100→0). Oracle MUST NOT appear in tx2 - per EVM spec, SELFDESTRUCT transfers balance without executing recipient code, so delegation target is never accessed. |
✅ Completed |
test_bal_call_revert_insufficient_funds |
Ensure BAL handles value-transferring call failure due to insufficient balance (not OOG), with and without 7702 delegation | Caller contract (balance=100, storage slot 0x02=0xDEAD) executes: SLOAD(0x01), call_opcode(target, value=1000), SSTORE(0x02, result). The call fails because 1000 > 100. Parametrized: (1) call_opcode over CALL and CALLCODE via with_all_call_opcodes(selector=...), (2) delegated (target is plain EOA vs. 7702-delegated EOA pointing to delegation_target=STOP), (3) target_is_warm (cold/warm via EIP-2930 access list), (4) delegation_is_warm (only when delegated). |
BAL MUST include: (1) Caller with storage_reads for slot 0x01, storage_changes for slot 0x02 (value=0, call returned failure). (2) Target with empty changes — accessed before the balance check fails. (3) When delegated: delegation_target MUST NOT appear in the BAL — the balance check fails before generic_call runs, so the delegation target's account is never read. Access-list warming does NOT add to BAL on its own, so the BAL is identical across warm/cold variants. |
✅ Completed |
test_bal_lexicographic_address_ordering |
Ensure BAL enforces strict lexicographic byte-wise ordering | Pre-fund three addresses with specific byte patterns: addr_low = 0x0000...0001, addr_mid = 0x0000...0100, addr_high = 0x0100...0000. Contract touches them in reverse order: BALANCE(addr_high), BALANCE(addr_low), BALANCE(addr_mid). Additionally, include two endian-trap addresses that are byte-reversals of each other: addr_endian_low = 0x0100000000000000000000000000000000000002, addr_endian_high = 0x0200000000000000000000000000000000000001. Note: reverse(addr_endian_low) = addr_endian_high. Correct lexicographic order: addr_endian_low < addr_endian_high (0x01 < 0x02 at byte 0). If implementation incorrectly reverses bytes before comparing, it would get addr_endian_low > addr_endian_high (wrong). |
BAL account list MUST be sorted lexicographically by address bytes: addr_low < addr_mid < addr_high < addr_endian_low < addr_endian_high, regardless of access order. The endian-trap addresses specifically catch byte-reversal bugs where addresses are compared with wrong byte order. Complements test_bal_invalid_account_order which tests rejection; this tests correct generation. |
✅ Completed |
test_bal_gas_limit_boundary |
Ensure the BAL max-items cap is enforced on the final BAL — including pre-tx system work, user txs, and post-tx system work. Parametrized on two orthogonal axes: with_tx ∈ {False, True} × with_cl_withdrawal ∈ {False, True} × boundary_offset ∈ {at, below}. The with_cl_withdrawal axis exercises the EIP-4895 withdrawals path (processed between txs and process_general_purpose_requests); combined with with_tx it catches clients that validate the cap before process_withdrawals runs. |
Baseline: 15 system items. with_tx: alice → bob value=1 adds 3 items (alice + bob + coinbase warmed via EIP-3651). with_cl_withdrawal: one EIP-4895 withdrawal to charlie adds 1 item at block_access_index = N+1. Gas limit set to total_items * BLOCK_ACCESS_LIST_ITEM + boundary_offset. |
At boundary: block MUST be accepted; BAL includes alice's nonce_change, bob's balance_change (when with_tx), charlie's balance_change at the post-tx index (when with_cl_withdrawal). Below boundary: block MUST be rejected with BLOCK_ACCESS_LIST_GAS_LIMIT_EXCEEDED. |
✅ Completed |
test_bal_transient_storage_not_tracked |
Ensure BAL excludes EIP-1153 transient storage operations | Contract executes: TSTORE(0x01, 0x42) (transient write), TLOAD(0x01) (transient read), SSTORE(0x02, result) (persistent write using transient value). |
BAL MUST include slot 0x02 in storage_changes (persistent storage was modified). BAL MUST NOT include slot 0x01 in storage_reads or storage_changes (transient storage is not persisted, not needed for stateless execution). This verifies TSTORE/TLOAD don't pollute BAL. |
✅ Completed |
test_bal_withdrawal_to_7702_delegation |
Ensure BAL correctly handles withdrawal to a 7702 delegated account (no code execution on recipient) | Tx1: Alice authorizes delegation to Oracle (sets code to 0xef0100\|\|Oracle). Withdrawal: 10 gwei sent to Alice. Single block with tx + withdrawal. |
BAL MUST include: (1) Alice at block_access_index=1 with code_changes (delegation), nonce_changes. (2) Alice at block_access_index=2 with balance_changes (receives withdrawal). Oracle MUST NOT appear - withdrawals credit balance without executing recipient code, so delegation target is never accessed. This complements test_bal_selfdestruct_to_7702_delegation (selfdestruct) and test_bal_withdrawal_no_evm_execution (withdrawal to contract). |
✅ Completed |
test_init_collision_create_tx |
Ensure BAL tracks CREATE collisions correctly (pre-Amsterdam test with BAL) | CREATE transaction targeting address with existing storage aborts | BAL MUST show empty expectations for collision address (no changes occur due to abort) | ✅ Completed |
test_call_to_pre_authorized_oog |
Ensure BAL handles OOG during EIP-7702 delegation access (pre-Amsterdam test with BAL) | Call to delegated account that OOGs before accessing delegation contract | BAL MUST include auth_signer (code read for delegation check) but MUST NOT include delegation contract (OOG before access) | ✅ Completed |
test_selfdestruct_created_in_same_tx_with_revert |
Ensure BAL tracks selfdestruct with revert correctly (pre-Amsterdam test with BAL) | Contract created and selfdestructed in same tx with nested revert | BAL MUST track storage reads and balance changes for selfdestruct even with reverts | ✅ Completed |
test_value_transfer_gas_calculation |
Ensure BAL correctly tracks OOG scenarios for CALL/CALLCODE/DELEGATECALL/STATICCALL (pre-Amsterdam test with BAL) | Nested calls with precise gas limits to test OOG behavior. For CALL with OOG: target account is read. For CALLCODE/DELEGATECALL/STATICCALL with OOG: target account NOT read (OOG before state access) | For CALL: target in BAL even with OOG. For CALLCODE/DELEGATECALL/STATICCALL: target NOT in BAL when OOG (state access deferred until after gas check) | ✅ Completed |
test_bal_call_with_value_in_static_context |
Ensure BAL does NOT include target when CALL with value fails in static context | static_caller uses STATICCALL to call caller. caller attempts CALL(target, value=1) which must fail due to static context. Target is an empty account. |
BAL MUST NOT include target because static context check (is_static && value > 0) must happen BEFORE any account access or BAL tracking. BAL MUST include static_caller with storage_changes (STATICCALL succeeded), caller with empty changes. |
✅ Completed |
test_staticcall_reentrant_call_to_precompile |
Ensure BAL captures STATICCALL reentry with CALL to precompile | Contract STATICCALLs itself. On reentry (CALLVALUE=0), attempts CALL to precompile with parametrized value. File: tests/byzantium/eip214_staticcall/test_staticcall.py. |
call_value=0: target with storage_changes (slot 0=1), precompile with empty changes. call_value>0: target with storage_reads (slot 0), precompile NOT in BAL (reverted before accessed). |
✅ Completed |
test_staticcall_call_to_precompile |
Ensure BAL captures STATICCALL → CALL to precompile chain | Contract A STATICCALLs contract B. B attempts CALL to precompile. File: tests/byzantium/eip214_staticcall/test_staticcall.py. |
call_value=0: contract_a with markers, contract_b empty (STATICCALLed), precompile empty. call_value>0: contract_a with storage_reads for slot 1, precompile NOT in BAL. |
✅ Completed |
test_staticcall_nested_call_to_precompile |
Ensure BAL captures nested CALL → STATICCALL → CALL to precompile | Contract B CALLs A. A STATICCALLs C. C attempts CALL to precompile. File: tests/byzantium/eip214_staticcall/test_staticcall.py. |
call_value=0: all contracts with markers/empty, precompile empty. call_value>0: contract_a with storage_reads for slot 1, precompile NOT in BAL. |
✅ Completed |
test_staticcall_call_to_precompile_from_contract_init |
Ensure BAL captures STATICCALL to precompile during CREATE init | Contract A CREATEs contract. Init code STATICCALLs B which CALLs precompile. File: tests/byzantium/eip214_staticcall/test_staticcall.py. |
call_value=0: contract_a with markers/nonce, created_contract with markers/nonce, contract_b empty, precompile empty. call_value>0: created_contract with storage_reads for slot 1, precompile NOT in BAL. |
✅ Completed |
test_bal_4788_simple |
Ensure BAL captures beacon root storage writes during pre-execution system call | Block with 2 normal user transactions: Alice sends 10 wei to Charlie, Bob sends 10 wei to Charlie. At block start (pre-execution), SYSTEM_ADDRESS calls BEACON_ROOTS_ADDRESS to store parent beacon root |
BAL MUST include at block_access_index=0: BEACON_ROOTS_ADDRESS with two storage_changes (timestamp slot and beacon root slot); SYSTEM_ADDRESS MUST NOT be included in BAL. At block_access_index=1: Alice with nonce_changes, Charlie with balance_changes (10 wei). At block_access_index=2: Bob with nonce_changes, Charlie with balance_changes (20 wei total). |
✅ Completed |
test_bal_4788_empty_block |
Ensure BAL captures beacon root storage writes in empty block | Block with no transactions. At block start (pre-execution), SYSTEM_ADDRESS calls BEACON_ROOTS_ADDRESS to store parent beacon root |
BAL MUST include at block_access_index=0: BEACON_ROOTS_ADDRESS with two storage_changes (timestamp slot and beacon root slot); SYSTEM_ADDRESS MUST NOT be included in BAL. No transaction-related BAL entries. |
✅ Completed |
test_bal_4788_query |
Ensure BAL captures storage reads when querying beacon root (valid and invalid queries) with optional value transfer | Parameterized test: Block 1 stores beacon root at timestamp 12. Block 2 queries with three timestamp scenarios (valid=12, invalid non-zero=42, invalid zero=0) and value (0 or 100 wei). Valid query (timestamp=12): reads both timestamp and root slots, writes returned value. If value > 0, beacon root contract receives balance. Invalid query with non-zero timestamp (timestamp=42): reads only timestamp slot before reverting, query contract has implicit SLOAD recorded (SSTORE reverts), no value transferred. Invalid query with zero timestamp (timestamp=0): reverts immediately without any storage access, query contract has implicit SLOAD recorded, no value transferred. | Block 1 BAL: System call writes. Block 2 BAL MUST include at block_access_index=0: System call writes for block 2. Valid case (timestamp=12) at block_access_index=1: BEACON_ROOTS_ADDRESS with storage_reads [timestamp_slot, root_slot] and balance_changes if value > 0, query contract with storage_changes. Invalid non-zero case (timestamp=42) at block_access_index=1: BEACON_ROOTS_ADDRESS with storage_reads [timestamp_slot only] and NO balance_changes (reverted), query contract with storage_reads [0] and NO storage_changes. Invalid zero case (timestamp=0) at block_access_index=1: BEACON_ROOTS_ADDRESS with NO storage_reads (reverts before access) and NO balance_changes, query contract with storage_reads [0] and NO storage_changes. |
✅ Completed |
test_bal_4788_invalid_calldata_size |
Ensure BAL correctly handles EIP-4788 queries with invalid calldata size | Parameterized test: Query contract calls BEACON_ROOTS_ADDRESS with variable-size calldata (0, 31, or 33 bytes) and value (0 or 100 wei). EIP-4788 requires exactly 32 bytes of calldata; any other size reverts before any storage access. |
BAL MUST include at block_access_index=0: BEACON_ROOTS_ADDRESS with storage_changes from system call. At block_access_index=1: BEACON_ROOTS_ADDRESS with NO storage_reads (reverts before access) and NO balance_changes (value not transferred), query contract with storage_reads [0] (implicit from no-op SSTORE) and NO storage_changes. If value > 0, query contract retains balance. Alice with nonce_changes. |
✅ Completed |
test_bal_4788_selfdestruct_to_beacon_root |
Ensure BAL captures SELFDESTRUCT to beacon root address alongside system call storage writes |
Single block: Pre-execution system call writes beacon root to storage. Transaction: Alice calls contract (pre-funded with 100 wei) that selfdestructs with BEACON_ROOTS_ADDRESS as beneficiary. |
BAL MUST include at block_access_index=0: BEACON_ROOTS_ADDRESS with storage_changes (timestamp and root slots from system call). At block_access_index=1: Alice with nonce_changes, contract with balance_changes (100→0), BEACON_ROOTS_ADDRESS with balance_changes (receives 100 wei). |
✅ Completed |
test_selfdestruct_send_to_sender |
Ensure BAL tracks SELFDESTRUCT sending all funds back to the tx sender (no burn). Multi-fork: valid_from TangerineWhistle, BAL expectations applied from Amsterdam via is_eip_enabled(7928). File: tests/tangerine_whistle/eip150_operation_gas_costs/test_eip150_selfdestruct.py. |
Parametrized over originator_balance: [0, 100]. Pre-deployed contract victim with code=SELFDESTRUCT(CALLER) and the given balance. Alice (tx.sender) sends tx directly to victim; no intermediate caller. Pre-Cancun: victim destroyed. >=Cancun (EIP-6780, non-same-tx): victim preserved with balance 0. |
When EIP-7928 is enabled, BAL MUST include Alice with nonce_changes coalesced with balance_changes (Alice is both sender and beneficiary). Victim has balance_changes 100→0 when funded, otherwise empty changes — and MUST NOT have code_changes or nonce_changes. |
✅ Completed |
test_bal_7002_clean_sweep |
Ensure BAL correctly tracks "clean sweep" where all withdrawal requests are dequeued in same block (requests ≤ MAX). Parameterized: (1) pubkey first 32 bytes zero / non-zero, (2) amount zero / non-zero | Alice sends transaction to WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS with 1 withdrawal request. Validator pubkey has either first 32 bytes zero or non-zero. Amount is either zero or non-zero. Since 1 ≤ MAX_WITHDRAWAL_REQUESTS_PER_BLOCK, post-execution system call dequeues all requests ("clean sweep"), resetting head and tail to 0. |
BAL MUST include Alice with nonce_changes at block_access_index=1. WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS MUST have: balance_changes at block_access_index=1 (receives fee), storage_reads for excess, head, and slot 5 (first 32 bytes of pubkey) if zero. At block_access_index=1 (tx enqueue): storage_changes for count (0→1), tail (0→1), slot 4 (source address), slot 5 (first 32 bytes, ONLY if non-zero), slot 6. At block_access_index=2 (post-exec dequeue): storage_changes for count (1→0), tail (1→0). Clean sweep invariant: when all requests dequeued, both head and tail reset to 0. |
✅ Completed |
test_bal_7002_partial_sweep |
Ensure BAL correctly tracks queue overflow when requests exceed MAX, demonstrating partial sweep in block 1 and cleanup in block 2 | Block 1: 20 different EOAs each send withdrawal request to WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS. Since 20 > MAX_WITHDRAWAL_REQUESTS_PER_BLOCK, only first MAX requests dequeued ("partial sweep"), leaving 4 in queue. Block 2: Empty block (no transactions), remaining 4 requests dequeued ("clean sweep"), queue becomes empty. |
Block 1 BAL MUST include all 20 senders with nonce_changes at respective block_access_index (1-20). WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS at each tx: storage_changes for count (increments to 20), tail (increments to 20). At block_access_index=21 (post-exec partial dequeue): storage_changes for count (20→0), head (0→MAX). Partial sweep: head advances by MAX, tail stays 20, queue has 4 remaining (tail - head = 4). Block 2 BAL MUST include WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS at block_access_index=1 (post-exec clean sweep): storage_changes for head (MAX→0), tail (20→0). Clean sweep: both head and tail reset to 0, queue empty. |
✅ Completed |
test_bal_7002_no_withdrawal_requests |
Ensure BAL captures EIP-7002 system contract dequeue operation even when block has no withdrawal requests | Block with 1 transaction: Alice sends 10 wei to Bob. No withdrawal requests submitted. | BAL MUST include Alice with nonce_changes at block_access_index=1. BAL MUST include Bob with balance_changes at block_access_index=1. BAL MUST include EIP-7002 system contract (WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS) with storage_reads for slots: excess (slot 0), count (slot 1), head (slot 2), tail (slot 3). System contract MUST NOT have storage_changes (no writes occur when queue is empty). This test demonstrates that the post-execution dequeue operation always runs and reads queue state, even when no requests are present. |
✅ Completed |
test_bal_7002_request_from_contract |
Ensure BAL captures withdrawal request from contract with correct source address | Alice calls RelayContract which internally calls EIP-7002 system contract with withdrawal request. Withdrawal request should have source_address = RelayContract (not Alice). |
BAL MUST include Alice with nonce_changes at block_access_index=1. BAL MUST include RelayContract with balance_changes (fee paid to system contract) at block_access_index=1. BAL MUST include system contract with balance_changes, storage_reads, and storage_changes (queue modified). Source address in withdrawal request MUST be RelayContract. Clean sweep: count and tail reset to 0 at block_access_index=2. |
✅ Completed |
test_bal_7002_request_invalid |
Ensure BAL correctly handles invalid withdrawal request scenarios | Parameterized test with 8 invalid scenarios: (1) insufficient_fee (fee=0), (2) calldata_too_short (55 bytes), (3) calldata_too_long (57 bytes), (4) oog (insufficient gas), (5-7) invalid_call_type (DELEGATECALL/STATICCALL/CALLCODE), (8) contract_reverts. Tests both EOA and contract-based withdrawal requests. | BAL MUST include sender with nonce_changes at block_access_index=1. BAL MUST include system contract with storage_reads for slots: excess (slot 0), count (slot 1), head (slot 2), tail (slot 3). System contract MUST NOT have storage_changes (transaction failed, no queue modification). |
✅ Completed |
test_bal_invalid_extraneous_entries |
Verify clients reject blocks with any type of extraneous BAL entries | Alice sends 100 wei to Oracle contract (which reads storage slot 0). Charlie is uninvolved in this transaction. A valid BAL is created containing nonce change for Alice, balance change and storage read for Oracle. The BAL is corrupted by adding various extraneous entries: (1) extra_nonce, (2) extra_balance, (3) extra_code, (4) extra_storage_write_touched (slot 0 - already read), (5) extra_storage_write_untouched (slot 1 - not accessed), (6) extra_storage_write_uninvolved_account (Charlie - uninvolved account), (7) extra_account_access (Charlie), (8) extra_storage_read (slot 999). Each tested at block_access_index 1 (same tx), 2 (system tx), 3 (out of bounds). | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST detect any extraneous entries in BAL. |
✅ Completed |
test_bal_invalid_duplicate_entries |
Verify clients reject blocks where BAL violates uniqueness constraints | Oracle writes storage, reads storage, and CREATEs a contract. BAL is corrupted with duplicate entries: (1) duplicate_nonce_change, (2) duplicate_balance_change, (3) duplicate_code_change, (4) duplicate_storage_slot, (5) duplicate_storage_read, (6) duplicate_slot_change, (7) storage_key_in_both_changes_and_reads. | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Each block_access_index must appear at most once per change list, each storage key at most once in storage_changes and storage_reads, and no key in both. |
✅ Completed |
test_bal_invalid_missing_withdrawal_account |
Verify clients reject blocks where BAL is missing an account modified only by a withdrawal | Alice sends 5 wei to Bob (1 transaction). Charlie receives 10 gwei withdrawal. BAL modifier removes Charlie's entry entirely. | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST detect that Charlie's balance was modified by the withdrawal but has no corresponding BAL entry. |
✅ Completed |
test_bal_invalid_missing_withdrawal_account_empty_block |
Verify clients reject blocks where BAL is missing a withdrawal-modified account in an empty block | Charlie receives 10 gwei withdrawal in block with no transactions. BAL modifier removes Charlie's entry entirely. | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST detect withdrawal-modified accounts even when no transactions are present. |
✅ Completed |
test_bal_invalid_hash_mismatch |
Verify clients reject blocks where the BAL hash in the header does not match the actual BAL content | Alice sends value to Bob. BAL content is valid but header hash is overridden to a wrong value via rlp_modifier. Unlike other invalid BAL tests (which corrupt BAL content with matching hash), this keeps the BAL valid but injects a wrong header hash. |
Block MUST be rejected with INVALID_BAL_HASH or INVALID_BLOCK_HASH exception. Clients MUST re-derive the BAL from block execution and compare its hash to the header, not just verify the BAL content is self-consistent. |
✅ Completed |
test_bal_invalid_field_entries |
Verify clients reject blocks with missing or incorrect field-level BAL entries | Oracle writes storage slot 1, reads storage slot 2, and CREATEs a small contract. A valid BAL is created, then corrupted by parameterized modifier: (1) missing_storage_change: Oracle's storage writes removed, (2) missing_storage_read: Oracle's storage reads removed, (3) missing_code_change: created contract's code change removed, (4) wrong_code_value: created contract's deployed bytecode changed to wrong value. | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST validate all field-level BAL entries: storage writes, storage reads, and code changes. |
✅ Completed |
test_bal_invalid_withdrawal_balance_value |
Verify clients reject blocks where BAL has an incorrect balance value for a withdrawal-modified account | Charlie receives 10 gwei withdrawal in an empty block. BAL modifier changes Charlie's post-balance from the correct 10_000_000_000 wei to 999. | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST validate withdrawal balance values match actual withdrawal amounts (including gwei-to-wei conversion). |
✅ Completed |
test_bal_invalid_missing_coinbase |
Verify clients reject blocks where BAL is missing the coinbase/fee recipient | Alice sends 100 wei to Bob with gas_price > base_fee so coinbase (charlie) receives a non-zero tip. BAL modifier removes charlie's entry entirely. | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST include fee recipients that received tips in the BAL. |
✅ Completed |
test_bal_invalid_coinbase_balance_value |
Verify clients reject blocks where BAL has an incorrect balance for the coinbase/fee recipient | Same setup as test_bal_invalid_missing_coinbase. BAL modifier changes charlie's post-balance from the actual tip to 999. | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST validate coinbase balance values match actual fee accounting (priority fee x gas used). |
✅ Completed |
test_bal_invalid_extraneous_coinbase |
Verify clients reject blocks with a spurious coinbase entry when coinbase received no fees | Parameterized: (1) empty_block: no txs, no withdrawals — only system contracts in valid BAL, (2) withdrawal_only: no txs, one withdrawal to a different address — withdrawals don't pay fees so coinbase is still untouched. BAL modifier appends spurious coinbase entry with empty changes. | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Coinbase MUST NOT appear in BAL when it receives no transaction tips, even if the block has other state-modifying activity (withdrawals). |
✅ Completed |
test_bal_invalid_surplus_system_address_from_system_call |
Verify clients reject a BAL containing SYSTEM_ADDRESS solely due to a system operation caller |
Empty block with an EIP-4788 pre-execution system call to BEACON_ROOTS_ADDRESS. Helper beacon_root_system_call_expectations builds the valid baseline BAL: BEACON_ROOTS_ADDRESS has timestamp/root storage changes at block_access_index=0, while SYSTEM_ADDRESS is marked absent (None). The fixture BAL is then corrupted by appending an empty SYSTEM_ADDRESS entry. |
Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST. The synthetic system caller address MUST NOT be accepted unless it experienced an actual state access. |
✅ Completed |
test_bal_2935_simple |
Ensure BAL captures EIP-2935 history storage writes during pre-execution system call alongside normal transactions | Block with 2 normal user transactions: Alice sends 10 wei to Charlie, Bob sends 10 wei to Charlie. At block start (pre-execution), SYSTEM_ADDRESS calls HISTORY_STORAGE_ADDRESS to store parent block hash. |
BAL MUST include HISTORY_STORAGE_ADDRESS with storage_changes (ring buffer slot 0, empty slot_changes since parent hash is framework-computed); SYSTEM_ADDRESS MUST NOT be included in BAL. At block_access_index=1: Alice with nonce_changes, Charlie with balance_changes (10 wei). At block_access_index=2: Bob with nonce_changes, Charlie with balance_changes (20 wei total). |
✅ Completed |
test_bal_2935_empty_block |
Ensure BAL captures EIP-2935 history storage writes in empty block | Block with no transactions. At block start (pre-execution), SYSTEM_ADDRESS calls HISTORY_STORAGE_ADDRESS to store parent block hash. |
BAL MUST include HISTORY_STORAGE_ADDRESS with storage_changes (ring buffer slot 0, empty slot_changes); SYSTEM_ADDRESS MUST NOT be included in BAL. No transaction-related BAL entries. |
✅ Completed |
test_bal_2935_query |
Ensure BAL captures storage reads when querying EIP-2935 historical block hashes (valid and invalid queries) with optional value transfer | Parameterized test: Block 1 (empty, stores genesis hash via system call). Block 2: Oracle contract queries HISTORY_STORAGE_ADDRESS with block number. Two block number scenarios (valid=0 genesis hash, invalid=1042 out of range) and value (0 or 100 wei). Valid query (block_number=0): reads genesis hash slot, oracle writes returned value. If value > 0, history storage contract receives balance. Invalid query (block_number=1042, out of range): reverts before storage access, oracle has implicit SLOAD recorded, value stays in oracle (not transferred to history storage). |
Block 2 BAL MUST include: Valid case at block_access_index=1: HISTORY_STORAGE_ADDRESS with storage_reads [slot 0] and balance_changes if value > 0, oracle with storage_changes (empty slot_changes). Invalid case at block_access_index=1: HISTORY_STORAGE_ADDRESS with NO storage_reads (reverts before access) and NO balance_changes, oracle with storage_reads [0], NO storage_changes, and balance_changes if value > 0 (value stays in oracle). Alice with nonce_changes at block_access_index=1. |
✅ Completed |
test_bal_2935_selfdestruct_to_history_storage |
Ensure BAL captures SELFDESTRUCT to EIP-2935 history storage address |
Single block: Transaction where Alice calls contract (pre-funded with 100 wei) that selfdestructs with HISTORY_STORAGE_ADDRESS as beneficiary. |
BAL MUST include at block_access_index=1: Alice with nonce_changes, contract with balance_changes (100→0), HISTORY_STORAGE_ADDRESS with balance_changes (receives 100 wei). |
✅ Completed |
test_bal_2935_invalid_calldata_size |
Ensure BAL correctly handles EIP-2935 queries with invalid calldata size (reverts before any storage access) | Parameterized test: Block 1 stores genesis hash via system call. Block 2: Oracle contract calls HISTORY_STORAGE_ADDRESS with invalid calldata sizes (0, 31, 33 bytes). EIP-2935 requires exactly 32 bytes calldata; any other size causes immediate revert before storage access. Optional value transfer (0 or 100 wei). |
Block 2 BAL MUST include: HISTORY_STORAGE_ADDRESS with NO storage_reads (calldata size check fails before any SLOAD) and NO balance_changes (call reverts). Oracle with storage_reads [0] (implicit SLOAD from no-op SSTORE), NO storage_changes, and balance_changes if value > 0 (value stays in oracle on revert). Alice with nonce_changes. |
✅ Completed |
test_bal_create2_selfdestruct_then_recreate_same_block |
Ensure BAL handles (tx1) CREATE2+SSTORE+SELFDESTRUCT then (tx2) CREATE2 "resurrection" of the same address in the same block. Parametrized over pre_balance: [0, 100]. |
Two identical txs invoke the same factory with the same initcode (same hash → same CREATE2 address A). The factory branches on its own storage[1]: on the first tx, slot 1 is 0 so the factory CREATE2's then CALLs A (runtime SSTOREs to a target slot then SELFDESTRUCTs to beneficiary) and records the CALL's return code in slot 1; on the second tx, slot 1 is non-zero so only CREATE2 runs and A persists with the runtime code (its runtime is never executed). When pre_balance > 0, A is pre-funded so Tx1's SELFDESTRUCT transfers a real balance. |
At block_access_index=1 (destructed A): MUST NOT include nonce_changes or code_changes for A (EIP-7928 SELFDESTRUCT-in-tx semantics); the SSTORE is demoted to storage_reads for the target slot (write demoted because A is destroyed same-tx). balance_changes for A appears only when pre-funded. Beneficiary appears with balance_changes if pre-funded, otherwise empty() (SELFDESTRUCT touches the beneficiary even with 0 value). At block_access_index=2 (resurrection): A has nonce_changes (post=1) and code_changes (post=runtime). Post-state: A has empty storage={} (the tx1 SSTORE was wiped; tx2 never executed the runtime). Factory's storage[1] = 1 confirms the Tx1 CALL went through. |
✅ Completed |
test_bal_call_with_value_in_static_context |
CALL with nonzero value in static context: target NOT in BAL | Parametrized: target_is_warm (cold/warm via access list), target_has_code (EOA/contract). Static check must fire before account access. |
target MUST NOT appear in BAL. Balances unchanged. |
✅ Completed |
test_bal_create_in_static_context |
CREATE/CREATE2 in static context: created address NOT in BAL | Parametrized: @pytest.mark.with_all_create_opcodes, value (0/1). Static check must fire before balance check, address computation, or nonce increment. |
Created address MUST NOT appear in BAL. Factory nonce unchanged. | ✅ Completed |
test_bal_selfdestruct_in_static_context |
SELFDESTRUCT in static context: beneficiary NOT in BAL | Parametrized: beneficiary_is_warm (cold/warm via access list), caller_balance (0/100). Static check must fire before beneficiary access or balance transfer. |
beneficiary MUST NOT appear in BAL. Balances unchanged. |
✅ Completed |
test_bal_call_opcode_succeeds_in_static_context |
All call opcodes (no value) succeed in static context | Parametrized: @pytest.mark.with_all_call_opcodes. Caller invokes call_opcode(target) inside STATICCALL. |
target MUST appear in BAL. Ensures clients don't over-restrict beyond EIP-214. |
✅ Completed |
test_bal_callcode_with_value_in_static_context |
CALLCODE with nonzero value succeeds in static context | EIP-214 explicitly excludes CALLCODE from write-protection. Caller invokes CALLCODE(value=1, target) inside STATICCALL. |
target MUST appear in BAL. Ensures clients don't apply CALL-with-value restriction to CALLCODE. |
✅ Completed |
test_bal_create_and_oog |
CREATE/CREATE2 OOG boundary test at three gas levels | Parametrized: @pytest.mark.with_all_create_opcodes, OutOfGasBoundary (OOG_BEFORE_TARGET_ACCESS, OOG_AFTER_TARGET_ACCESS, SUCCESS). BEFORE and AFTER differ by 1 gas, proving the static cost boundary. |
OOG_BEFORE: created address MUST NOT appear in BAL. OOG_AFTER: created address IS in BAL as empty() (accessed, state reverted). SUCCESS: created address in BAL with nonce_changes/code_changes. |
✅ Completed |
test_bal_fork_transition_happy_path |
Verify a BAL is produced at the Amsterdam activation block and absent before it. File: tests/amsterdam/eip7928_block_level_access_lists/test_fork_transition.py. Uses @pytest.mark.valid_at_transition_to("Amsterdam") to run across the BPO2 -> Amsterdam boundary. |
Two blocks: pre-fork (timestamp=14_999) with a simple Alice→Bob transfer, then activation block (timestamp=15_000) with the same kind of transfer. |
Pre-fork block header MUST NOT carry block_access_list_hash. Activation block MUST include block_access_list_hash correctly derived from the BAL body, and the BAL body MUST record Alice's nonce_changes and Bob's balance_changes at block_access_index=1. |
✅ Completed |
test_invalid_pre_fork_block_with_bal_hash_field |
Verify clients reject a pre-Amsterdam block whose header carries block_access_list_hash. File: tests/amsterdam/eip7928_block_level_access_lists/test_fork_transition.py. |
Single block at timestamp=14_999 with a regular transfer, mutated via rlp_modifier=Header(block_access_list_hash=Hash(0)) to inject the field into the pre-fork header schema. |
Block MUST be rejected with BlockException.INVALID_BLOCK_HASH: pre-fork clients compute the block hash without the injected field, mismatching the expected hash. |
✅ Completed |
test_invalid_post_fork_block_without_bal_hash_field |
Verify clients reject an Amsterdam activation block whose header is missing block_access_list_hash. File: tests/amsterdam/eip7928_block_level_access_lists/test_fork_transition.py. |
Single block at timestamp=15_000 with a regular transfer, mutated via rlp_modifier=Header(block_access_list_hash=Header.REMOVE_FIELD) so the field is dropped from the header. |
Block MUST be rejected with BlockException.INVALID_BAL_HASH: clients re-derive the BAL hash from execution and find no header hash to match. The engine fixture omits the blockAccessList param from newPayloadV5, which MUST return -32602: Invalid params. |
✅ Completed |
test_fork_transition_bal_size_constraint |
Verify the BAL size constraint (bal_items <= gas_limit // BLOCK_ACCESS_LIST_ITEM) applies only on/after Amsterdam. File: tests/amsterdam/eip7928_block_level_access_lists/test_fork_transition.py. Parametrized over exceeds_limit_at_fork: at_fork_within_budget (gas_limit == empty_block_bal_item_count() * BLOCK_ACCESS_LIST_ITEM) and at_fork_over_budget (gas_limit one wei below that). |
Two empty blocks: pre-fork (timestamp=14_999) and activation block (timestamp=15_000). The same low gas_limit is used for both via genesis_environment=Environment(gas_limit=...). |
Pre-fork block MUST be accepted under both budgets (constraint not yet enforced). Activation block MUST be accepted at the exact budget and MUST be rejected with BlockException.BLOCK_ACCESS_LIST_GAS_LIMIT_EXCEEDED one item over the budget. |
✅ Completed |
test_bal_dirty_account_selfdestruct |
Ensure BAL does not record dirty state on a same-tx ephemeral contract whose SELFDESTRUCT takes effect. |
A factory deploys an ephemeral whose initcode dirties balance, nonce, code, and storage; runtime SELFDESTRUCT routes through an intermediate oracle. |
success: ephemeral's BAL entry contains only storage_reads for the demoted slots. revert: BAL records all four dirty fields (destruction rolled back). |
✅ Completed |