This EIP introduces Block-Level Access Lists (BALs) that record all accounts and storage locations accessed during block execution, along with their post-execution values. BALs enable parallel disk reads, parallel transaction validation, and executionless state updates.
Transaction execution cannot be parallelized without knowing in advance which addresses and storage slots will be accessed. While EIP-2930 introduced optional transaction access lists, they are not enforced.
This proposal enforces access lists at the block level, enabling:
parallel IO + parallel EVMWe introduce a new field to the block header, block_access_list_hash, which contains the Keccak-256 hash of the RLP-encoded block access list. When no state changes are present, this field is the hash of an empty rlp list 0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347, i.e. keccak256(rlp.encode([])).
The block body includes a BlockAccessList containing all account accesses and state changes. This field is RLP-encoded as a list of AccountChanges. When no state changes are present, this field is the empty RLP list 0xc0, i.e. rlp.encode([]).
BALs use RLP encoding following the pattern: address -> field -> block_access_index -> change.
BlockAccessList is the set of all addresses accessed during block execution.
It MUST include:
Addresses with state changes (storage, balance, nonce, or code).
Addresses accessed without state changes, including:
BALANCE, EXTCODESIZE, EXTCODECOPY, EXTCODEHASH opcodesCALL, CALLCODE, DELEGATECALL, STATICCALL (even if they revert)CALL, CALLCODE, DELEGATECALL, STATICCALL), the target address is only included in the BAL if gas checks (memory expansion, access cost, transfer cost) pass; if gas shortage causes the operation to fail before state access, the target address MUST NOT be included.CREATE/CREATE2 (even when creation fails)0x0 with initcode)SELFDESTRUCTSYSTEM_ADDRESS (0xfffffffffffffffffffffffffffffffffffffffe), MUST NOT be included unless it experiences state access itselfAddresses with no state changes MUST still be present with empty change lists.
Entries from an EIP-2930 access list MUST NOT be included automatically. Only addresses and storage slots that are actually touched or changed during execution are recorded.
The following ordering rules MUST apply:
BlockAccessIndex values MUST be assigned as follows:
0 for pre‑execution system contract calls.1 … n for transactions (in block order).n + 1 for post‑execution system contract calls.Writes include:
Reads include:
SLOAD that are not written.SSTORE where post-value equals pre-value, also known as "no-op writes").Note: Implementations MUST check the pre-transaction value to correctly distinguish between actual writes and no-op writes.
balance_changes)Record post‑transaction balances (uint256) for:
value > 0).value > 0).value > 0).For unaltered account balances:
If an account’s balance changes during a transaction, but its post-transaction balance is equal to its pre-transaction balance, then the change MUST NOT be recorded in balance_changes. The sender and recipient address MUST be included in AccountChanges.
This includes the following special cases where addresses MUST be included with empty changes if no other state changes occur. This includes:
Zero-value block reward recipients MUST NOT trigger a balance change in the block access list and MUST NOT cause the recipient address to be included as a read (e.g. without changes). Zero-value block reward recipients MUST only be included with a balance change in blocks where the reward is greater than zero.
Track post‑transaction runtime bytecode for deployed or modified contracts, and delegation indicators for successful delegations as defined in EIP-7702.
Record post‑transaction nonces for:
CREATE or CREATE2.AccountChanges without nonce or code changes. However, if the account had a positive balance pre-transaction, the balance change to zero MUST be recorded. Storage keys within the self-destructed contracts that were modified or read MUST be included as a storage_read.EXTCODEHASH, EXTCODESIZE, BALANCE, STATICCALL, etc.).balance_changes.storage_reads.block_access_index = 0.block_access_index = len(transactions) + 1.accessed_addresses (as defined in EIP-2929). The delegation target MUST NOT be included during delegation creation and MUST be included when loaded as a call target under authority execution.The Engine API is extended with new structures and methods to support block-level access lists:
ExecutionPayloadV4 extends ExecutionPayloadV3 with:
blockAccessList: RLP-encoded block access listengine_newPayloadV5 validates execution payloads:
blockAccessListINVALID if access list is malformed or doesn't matchengine_getPayloadV6 builds execution payloads:
blockAccessList field with RLP-encoded access listThe execution layer provides the RLP-encoded blockAccessList to the consensus layer via the Engine API. The consensus layer then computes the SSZ hash_tree_root for storage in the ExecutionPayloadHeader.
The state transition function must validate that the provided BAL matches the actual state accesses:
The BAL MUST be complete and accurate. Missing or spurious entries invalidate the block.
Clients MAY validate by comparing execution-gathered accesses with the BAL.
Clients MAY invalidate immediately if any transaction exceeds declared state.
Example block:
Pre-execution:
Transactions:
Post-execution:
Note: Pre-execution system contract uses block_access_index = 0. Post-execution withdrawal uses block_access_index = 3 (len(transactions) + 1)
Resulting BAL (RLP structure):
RLP-encoded and compressed: ~400-500 bytes.
This design variant was chosen for several key reasons:
Size vs parallelization: BALs include all accessed addresses (even unchanged) for complete parallel IO and execution.
Storage values for writes: Post-execution values enable state reconstruction during sync without individual proofs against state root.
Overhead analysis: Historical data shows ~45 KiB average BAL size.
Transaction independence: 60-80% of transactions access disjoint storage slots, enabling effective parallelization. The remaining 20-40% can be parallelized by having post-transaction state diffs.
RLP encoding: Native Ethereum encoding format, maintains compatibility with existing infrastructure.
Block size impact (historical analysis):
Smaller than current worst-case calldata blocks.
An empirical analysis has been done here. An updated analysis for a 60 million block gas limit can be found here.
BAL verification occurs alongside parallel IO and EVM operations without delaying block processing.
This proposal requires changes to the block structure that are not backwards compatible and require a hard fork.
Validating access lists and balance diffs adds validation overhead but is essential to prevent acceptance of invalid blocks.
Increased block size impacts propagation but overhead (~40 KiB average) is reasonable for performance gains.
Copyright and related rights waived via CC0.