Skip to content

Conversation

@qu0b
Copy link
Contributor

@qu0b qu0b commented Nov 26, 2025

🗒️ Description

Implement new BAL (Block Access List) tests with full test implementations:

  • test_bal_create2_collision - CREATE2 address collision handling
  • test_bal_create_selfdestruct_to_self_with_call - Init code with external call then selfdestruct to self
  • test_bal_selfdestruct_to_7702_delegation - SELFDESTRUCT to EIP-7702 delegated account
  • test_bal_revert_insufficient_funds - CALL failure due to insufficient balance
  • test_bal_lexicographic_address_ordering - Strict byte-wise address ordering validation with endian-trap addresses
  • test_bal_transient_storage_not_tracked - EIP-1153 transient storage exclusion
  • test_bal_selfdestruct_to_precompile - SELFDESTRUCT with precompile beneficiary
  • test_bal_all_transaction_types - All 5 tx types (Legacy, EIP-2930, EIP-1559, Blob, EIP-7702) in single block
  • test_bal_create_early_failure - CREATE failure before track_address
  • test_bal_withdrawal_to_7702_delegation - Withdrawal to EIP-7702 delegated account
image

@qu0b qu0b closed this Nov 26, 2025
@qu0b qu0b reopened this Nov 26, 2025
@qu0b qu0b changed the base branch from forks/amsterdam to eips/amsterdam/eip-7928 November 26, 2025 12:19
@marioevz marioevz requested a review from fselmo November 26, 2025 13:04
| `test_bal_create2_collision` | Ensure BAL handles CREATE2 address collision correctly | Factory contract (nonce=1, storage slot 0=0xDEAD) executes `CREATE2(salt=0, initcode)` targeting address that already has `code=STOP, nonce=1`. Pre-deploy contract at calculated CREATE2 target address before factory deployment. | BAL **MUST** include: (1) Factory with `nonce_changes` (1→2, incremented even on failed CREATE2), `storage_changes` for slot 0 (0xDEAD→0, stores failure). (2) Collision address with empty changes (accessed during collision check, no state changes). CREATE2 returns 0. Collision address **MUST NOT** have `nonce_changes` or `code_changes`. | 🟡 Planned |
| `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: (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. Note: Uses EXTCODECOPY from template contract for init code (too long for PUSH32). | BAL **MUST** include: (1) Factory with `nonce_changes`, `balance_changes` (loses 100). (2) Oracle with `storage_changes` for slot 0x01 (external call succeeded). (3) Template contract (EXTCODECOPY source). (4) 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, SELFDESTRUCT to self = net zero). | 🟡 Planned |
| `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 tx_index=1 with `code_changes` (delegation), `nonce_changes`. (2) Alice at tx_index=2 with `balance_changes` (receives selfdestruct). (3) Victim at tx_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. | 🟡 Planned |
| `test_bal_revert_insufficient_funds` | Ensure BAL handles CALL failure due to insufficient balance (not OOG) | Contract (balance=100, storage slot 0x02=0xDEAD) executes: `SLOAD(0x01), CALL(target, value=1000), SSTORE(0x02, result)`. CALL fails because 1000 > 100. Target address 0xDEAD (pre-existing with non-zero balance to avoid pruning). Note: slot 0x02 must start non-zero so SSTORE(0) is a change. | BAL **MUST** include: (1) Contract with `storage_reads` for slot 0x01, `storage_changes` for slot 0x02 (value=0, CALL returned failure). (2) Target (0xDEAD) with empty changes. Per EVM, CALL loads target address before balance check, so target **MUST** appear in BAL even though transfer failed. No `balance_changes` for target. | 🟡 Planned |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re test_bal_revert_insufficient_funds , we need to double check if the target address should be in the BAL or not. My intuition is that the BAL MUST NOT include the target if the balance of the caller is insufficient for the CALL.
Also, the sentence in the expectation ends abruptly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the specs are correct.
But the test definition is potentially wrong.

In L412, we're only tracing the 2929 access list, nothing re BALs.
Then at L455ff we never call generic_call such that the target would never be tracked.

Based on the specs, the target of a call with value, but with insufficient balance, is only put in the BAL in case generic_call executes and calls process_message.

Copy link
Member

@jochem-brouwer jochem-brouwer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey Stefan 😄 👍 We met at Devconnect!! 🥳

Very impressive edge cases! I left a few comments on them, also some clarifications or just some notes. Thanks a lot 😄 👍

@qu0b
Copy link
Contributor Author

qu0b commented Nov 28, 2025

Recent run with the new test cases against besu and nethermind.

image

@fselmo
Copy link
Contributor

fselmo commented Dec 1, 2025

This needs a rebase with the recent additions from #1825 and #1829

@qu0b qu0b force-pushed the qu0b/add-bal-test-cases branch from e4c7fe1 to 98c2154 Compare December 2, 2025 10:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants