EIP-7928: Block-Level Access Lists
Enforced block access lists with storage locations and post-transaction state diffs
Abstract
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.
Motivation
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 disk reads and transaction execution
- State reconstruction without executing transactions
- Reduced execution time to
parallel IO + parallel EVM
Specification
Block Structure Modification
We introduce a new field to the block header:
The block body includes a BlockAccessList
containing all account accesses and state changes.
RLP Data Structures
BALs use RLP encoding following the pattern: address -> field -> block_access_index -> change
.
Scope and Inclusion
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 (e.g.,
STATICCALL
targets,BALANCE
opcode targets).
Addresses 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.
Ordering and Determinism
The following ordering rules MUST apply:
- Addresses: lexicographic (bytewise).
- Storage keys: lexicographic within each account.
- Block access indices: ascending within each change list.
BlockAccessIndex Assignment
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.
Recording Semantics by Change Type
Storage
-
Writes include:
- Any value change (post‑value ≠ pre‑value).
- Zeroing a slot (pre‑value exists, post‑value is zero).
-
Reads include:
- Slots accessed via
SLOAD
that are not written. - Slots written with unchanged values (i.e.,
SSTORE
that re‑stores the same value).
- Slots accessed via
Balance (balance_changes
)
Record post‑transaction balances (uint128
) for:
- Transaction senders (gas + value).
- Transaction recipients (only if
value > 0
). - Coinbase (rewards + fees).
- SELFDESTRUCT/SENDALL beneficiaries.
- Withdrawal recipients (system withdrawals, EIP-4895).
Zero‑value transfers: MUST NOT be recorded in balance_changes
, but the corresponding addresses MUST still be included with empty AccountChanges
.
Code
Track post‑transaction runtime bytecode for deployed or modified contracts, and delegation indicators as defined in EIP-7702.
Nonce
Record post‑transaction nonces for:
- EOA senders.
- Contracts that performed a successful
CREATE
orCREATE2
. - Deployed contracts.
- EIP-7702 authorities.
Edge Cases (Normative)
- SELFDESTRUCT/SENDALL: Beneficiary is recorded as a balance change.
- Accessed but unchanged: Include the address with empty changes (e.g., targets of
EXTCODEHASH
,EXTCODESIZE
,BALANCE
,STATICCALL
, etc.). - Zero‑value transfers: Include the address; omit from
balance_changes
. - Gas refunds: Record the final balance of the sender after each transaction.
- Block rewards: Record the final balance of the fee recipient after each transaction.
- Exceptional halts: Record the final nonce and balance of the sender, and the final balance of the fee recipient after each transaction.
- Pre‑execution system contract calls: All state changes MUST use
block_access_index = 0
. - Post‑execution system contract calls: All state changes MUST use
block_access_index = len(transactions) + 1
. - EIP‑4895 (Consensus layer withdrawals): Recipients are recorded with their final balance after the withdrawal.
- EIP‑2935 (block hash): Record system contract storage diffs of the single updated storage slot in the ring buffer.
- EIP‑4788 (beacon root): Record system contract storage diffs of the two updated storage slots in the ring buffer.
- EIP‑7002 (withdrawals): Record system contract storage diffs of storage slots 0–3 (4 slots) after the dequeuing call.
- EIP‑7251 (consolidations): Record system contract storage diffs of storage slots 0–3 (4 slots) after the dequeuing call.
Engine API
The execution layer computes:
and provides both block_access_list
and block_access_list_hash
in the ExecutionPayload
to the consensus layer, which stores them without modification.
State Transition Function
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 MUST validate by comparing execution-gathered accesses (per EIP-2929) with the BAL.
Clients MAY invalidate immediately if any transaction exceeds declared state.
Concrete Example
Example block:
Pre-execution:
- EIP-2935: Store parent hash at block hash contract (0x0000F90827F1C53a10cb7A02335B175320002935)
- EIP-7002: Omitted for simplicity.
Transactions:
- Alice (0xaaaa...) sends 1 ETH to Bob (0xbbbb...), checks balance of 0x2222...
- Charlie (0xcccc...) calls factory (0xffff...) deploying contract at 0xdddd...
Post-execution:
- Withdrawal of 100 ETH to Eve (0xabcd...)
- EIP-7002 and EIP-7251 omitted for simplicity.
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.
Rationale
BAL Design Choice
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 Considerations
Block size impact (historical analysis):
- Average: ~40 KiB (compressed)
- Balance diffs: ~4.5 KiB (compressed)
- Storage diffs: ~33.88 KiB (compressed)
- Nonce diffs: ~0.02 KiB (compressed)
- Code diffs: ~0.6 KiB (compressed)
- Worst-case (36m gas): ~0.93 MiB
- Worst-case balance diffs: ~0.12 MiB
Smaller than current worst-case calldata blocks.
An empirical analysis has been done here.
Asynchronous Validation
BAL verification occurs alongside parallel IO and EVM operations without delaying block processing.
Backwards Compatibility
This proposal requires changes to the block structure that are not backwards compatible and require a hard fork.
Security Considerations
Validation Overhead
Validating access lists and balance diffs adds validation overhead but is essential to prevent acceptance of invalid blocks.
Block Size
Increased block size impacts propagation but overhead (~40 KiB average) is reasonable for performance gains.
Copyright
Copyright and related rights waived via CC0.