--- eip: 8146 title: Block Access List Sidecars description: Decouple block access list propagation from execution payload envelopes author: Toni Wahrstätter (@nerolation), Raúl Kripalani (@raulk) discussions-to: https://ethereum-magicians.org/t/eip-8146-block-access-list-sidecars/27757 status: Draft type: Standards Track category: Core created: 2026-02-03 requires: 7732, 7928 --- ## Abstract This EIP removes the block access list (BAL) from the `ExecutionPayloadEnvelope` and propagates it as an independent sidecar on a dedicated gossip topic. Builders commit to the BAL root in their `ExecutionPayloadBid`. Sidecar verification uses this commitment; no separate signature is required. The Payload Timeliness Committee (PTC) enforces BAL availability at the attestation deadline. ## Motivation [EIP-7928](./eip-7928.md) adds block access lists to the `ExecutionPayload`. Under [EIP-7732](./eip-7732.md), the execution payload travels inside a `SignedExecutionPayloadEnvelope` that the builder broadcasts after the beacon block. Including the BAL (~70 KiB average, up to 1 MiB) in the envelope increases its size and propagation latency on the critical path. Separating the BAL into a sidecar reduces envelope size, improving propagation. The BAL remains required for execution validation; the PTC enforces availability so that the BAL is present before the payload processing deadline. ## Specification ### Consensus Layer #### Presets | Name | Value | | - | - | | `MAX_BLOCK_ACCESS_LIST_SIZE` | `uint64(2**23)` (= 8 MiB) | #### Types The `BlockAccessList` type from [EIP-7928](./eip-7928.md): | Name | SSZ equivalent | Description | | - | - | - | | `BlockAccessList` | `ByteList[MAX_BLOCK_ACCESS_LIST_SIZE]` | RLP-encoded block access list | #### Modified Containers ##### `ExecutionPayload` The `block_access_list` field introduced by [EIP-7928](./eip-7928.md) is removed: ```python class ExecutionPayload(Container): parent_hash: Hash32 fee_recipient: ExecutionAddress state_root: Bytes32 receipts_root: Bytes32 logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] prev_randao: Bytes32 block_number: uint64 gas_limit: uint64 gas_used: uint64 timestamp: uint64 extra_data: ByteList[MAX_EXTRA_DATA_BYTES] base_fee_per_gas: uint256 block_hash: Hash32 transactions: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD] withdrawals: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD] blob_gas_used: uint64 excess_blob_gas: uint64 # [Removed in EIP-8143] # block_access_list: BlockAccessList ``` ##### `ExecutionPayloadBid` A `block_access_list_root` field is added: ```python class ExecutionPayloadBid(Container): parent_block_hash: Hash32 parent_block_root: Root block_hash: Hash32 prev_randao: Bytes32 fee_recipient: ExecutionAddress gas_limit: uint64 builder_index: BuilderIndex slot: Slot value: Gwei execution_payment: Gwei blob_kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK] # [New in EIP-8143] block_access_list_root: Root ``` ##### `PayloadAttestationData` A `block_access_list_present` field is added: ```python class PayloadAttestationData(Container): beacon_block_root: Root slot: Slot payload_present: boolean blob_data_available: boolean # [New in EIP-8143] block_access_list_present: boolean ``` #### New Containers ##### `BlockAccessListSidecar` ```python class BlockAccessListSidecar(Container): beacon_block_root: Root slot: Slot block_access_list: BlockAccessList ``` ### P2P Networking #### Gossip ##### Global Topic: `block_access_list_sidecar` This topic propagates `BlockAccessListSidecar` objects. The following validations MUST pass before forwarding a `sidecar` on the network: - _[IGNORE]_ `sidecar.beacon_block_root` has been seen (via gossip or non-gossip sources). A client MAY queue the sidecar for processing once the block is retrieved. - _[IGNORE]_ No valid `BlockAccessListSidecar` for this `beacon_block_root` has been seen. - _[IGNORE]_ `sidecar.slot >= compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)`. Let `block` be the beacon block with root `sidecar.beacon_block_root`. Let `bid` alias `block.body.signed_execution_payload_bid.message`: - _[REJECT]_ `block` passes validation. - _[REJECT]_ `sidecar.slot == block.slot`. - _[REJECT]_ `hash_tree_root(sidecar.block_access_list) == bid.block_access_list_root`. #### Req/Resp ##### `BlockAccessListSidecarsByRange` v1 **Protocol ID:** `/eth2/beacon_chain/req/block_access_list_sidecars_by_range/1/` Request Content: ```python ( start_slot: Slot count: uint64 ) ``` Response Content: ```python List[BlockAccessListSidecar, MAX_REQUEST_PAYLOADS] ``` Returns sidecars in slot range `[start_slot, start_slot + count)`, ordered by slot. The response MUST contain no more than `MAX_REQUEST_PAYLOADS` sidecars. Clients SHOULD respond with at least one sidecar if available. ##### `BlockAccessListSidecarsByRoot` v1 **Protocol ID:** `/eth2/beacon_chain/req/block_access_list_sidecars_by_root/1/` Request Content: ```python List[Root, MAX_REQUEST_PAYLOADS] ``` Response Content: ```python List[BlockAccessListSidecar, MAX_REQUEST_PAYLOADS] ``` Returns sidecars matching the requested beacon block roots. ### Fork Choice #### Modified `Store` ```python @dataclass class Store(object): # ... existing fields ... # [New in EIP-8143] block_access_lists: Dict[Root, BlockAccessList] = field(default_factory=dict) # [New in EIP-8143] ptc_block_access_list_vote: Dict[Root, Vector[boolean, PTC_SIZE]] = field(default_factory=dict) ``` #### Modified `on_block` When a new block is added to the store, initialize the BAL PTC vote tracker (alongside the existing `ptc_vote` initialization): ```python # [New in EIP-8143] Add a new PTC BAL voting for this block to the store store.ptc_block_access_list_vote[block_root] = [False] * PTC_SIZE ``` #### Modified `on_payload_attestation_message` The handler records the BAL availability vote alongside the existing payload presence vote: ```python def on_payload_attestation_message( store: Store, ptc_message: PayloadAttestationMessage, is_from_block: bool = False ) -> None: # ... existing validation and payload_present recording ... # [New in EIP-8143] Update the BAL vote for the block ptc_block_access_list_vote = store.ptc_block_access_list_vote[data.beacon_block_root] ptc_block_access_list_vote[ptc_index] = data.block_access_list_present ``` #### Modified `notify_ptc_messages` When extracting `PayloadAttestationMessage` objects from `PayloadAttestation` aggregates in a beacon block, the `block_access_list_present` field is propagated from the attestation data. #### New `on_block_access_list_sidecar` ```python def on_block_access_list_sidecar(store: Store, sidecar: BlockAccessListSidecar) -> None: # The beacon block must be known assert sidecar.beacon_block_root in store.blocks block = store.blocks[sidecar.beacon_block_root] # Verify slot consistency assert sidecar.slot == block.slot # Verify BAL matches commitment in bid bid = block.body.signed_execution_payload_bid.message assert hash_tree_root(sidecar.block_access_list) == bid.block_access_list_root # Store BAL store.block_access_lists[sidecar.beacon_block_root] = sidecar.block_access_list # Notify the EL for early prefetching or post-state root calculation EXECUTION_ENGINE.notify_block_access_list(sidecar.block_access_list, bid.block_hash) ``` #### Modified `on_execution_payload` BAL availability is required before processing the execution payload. The BAL has already been delivered to the EL via `engine_notifyBlockAccessListV1` when the sidecar was received; `process_execution_payload` itself is unchanged. ```python def on_execution_payload(store: Store, signed_envelope: SignedExecutionPayloadEnvelope) -> None: envelope = signed_envelope.message # The corresponding beacon block root needs to be known assert envelope.beacon_block_root in store.block_states # Check if blob data is available assert is_data_available(envelope.beacon_block_root) # Make a copy of the state to avoid mutability issues state = copy(store.block_states[envelope.beacon_block_root]) # Process the execution payload (unchanged) process_execution_payload(state, signed_envelope, EXECUTION_ENGINE) # Add new state for this payload to the store store.execution_payload_states[envelope.beacon_block_root] = state ``` ### Engine API `engine_getPayloadV6` returns the BAL as a separate `blockAccessList` field (RLP-encoded bytes) outside the `ExecutionPayload`. The builder computes `block_access_list_root = hash_tree_root(ByteList(blockAccessList))` for inclusion in the bid. `engine_notifyBlockAccessListV1` is a new method that delivers the BAL to the EL independently of the execution payload. It accepts `blockAccessList` (RLP-encoded bytes) and `blockHash` (to associate the BAL with the corresponding payload). The EL stores the BAL and begins prefetching the referenced state. The CL calls this method when the BAL sidecar is received (in `on_block_access_list_sidecar`). `engine_newPayloadV5` is unchanged from [EIP-7732](./eip-7732.md). It does not include the BAL. The EL uses the BAL previously delivered via `engine_notifyBlockAccessListV1`, matching by `blockHash`. ### Execution Layer The execution block header field `block_access_list_hash` (keccak256 of RLP-encoded BAL) is unchanged from [EIP-7928](./eip-7928.md). BAL construction and validation rules are as specified in [EIP-7928](./eip-7928.md). ### Validator Guide #### Builders When constructing a bid, the builder: 1. Obtains the BAL from `engine_getPayloadV6`. 2. Computes `block_access_list_root = hash_tree_root(ByteList(blockAccessList))` and includes it in the `ExecutionPayloadBid`. 3. After the beacon block is published, broadcasts the `BlockAccessListSidecar` on the `block_access_list_sidecar` gossip topic. 4. Broadcasts the `SignedExecutionPayloadEnvelope` (which no longer contains the BAL). Builders SHOULD broadcast the BAL sidecar as early as possible to enable prefetching by the next block's builder. #### PTC Members PTC members set `block_access_list_present = True` in their `PayloadAttestationData` if they have received a valid `BlockAccessListSidecar` for the block (i.e., the sidecar passes gossip validation including the `hash_tree_root` commitment check). PTC members set `payload_present` and `blob_data_available` per [EIP-7732](./eip-7732.md) rules, independently of `block_access_list_present`. ## Rationale ### No Separate Signature The BAL sidecar requires no BLS signature. The builder commits to `block_access_list_root` in the signed `ExecutionPayloadBid`; verification is `hash_tree_root(sidecar.block_access_list) == bid.block_access_list_root`. This mirrors `DataColumnSidecar` verification via KZG proofs against commitments in the bid. ### Separate PTC Field BAL availability is tracked as a dedicated `block_access_list_present` boolean in `PayloadAttestationData`, following the same pattern as `blob_data_available`. This keeps concerns separated: `payload_present` signals envelope timeliness, `blob_data_available` signals blob availability, and `block_access_list_present` signals BAL availability. The fork choice `on_execution_payload` handler independently gates on local BAL availability (`assert root in store.block_access_lists`), ensuring execution validation cannot proceed without the BAL regardless of PTC votes. ### PTC Deadline Independence [EIP-7732](./eip-7732.md) applies a variable PTC deadline to the execution payload based on payload size. Since the BAL travels as an independent sidecar, and its size scales inversely with the payload size, this variable deadline does not apply to it. ### Dedicated Engine API Method The BAL is delivered to the EL exclusively via `engine_notifyBlockAccessListV1`, separate from `engine_newPayloadV5`. This enables early delivery: the BAL sidecar typically arrives before the execution payload envelope, so the EL can begin prefetching storage slots immediately, or start calculating the post-state root. When the payload arrives later, `engine_newPayloadV5` proceeds without the BAL -- the EL already has it, matched by `blockHash`. ### Data Retention Clients MUST retain BAL sidecars for at least `MIN_EPOCHS_FOR_BAL_SIDECARS_REQUESTS` epochs to support syncing nodes. After this period, clients MAY prune sidecars. ## Backwards Compatibility This EIP requires a hard fork. It modifies the `ExecutionPayload` and `ExecutionPayloadBid` containers from [EIP-7732](./eip-7732.md) and changes how [EIP-7928](./eip-7928.md) BALs are propagated. ## Security Considerations **Withholding**: A builder can withhold the BAL sidecar. PTC members will vote `block_access_list_present = False`, providing network-wide visibility. The fork choice `on_execution_payload` handler gates on local BAL availability, so the payload cannot be validated without the BAL. The builder withhold safety properties from [EIP-7732](./eip-7732.md) apply: if the beacon block was not timely, the builder is not charged. **Network overhead**: The BAL sidecar adds ~70 KiB average per slot to gossip traffic, equal to the current overhead when BAL is inside the envelope. Total network load is unchanged; it is redistributed across topics. **Verification cost**: Sidecar verification requires one `hash_tree_root` computation on up to 1 MiB. This is negligible compared to execution validation. ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md).