---
eip: 8101
title: Payload Chunking with Chunk Access Lists
description: Semantic payload chunking with separated state diffs for streaming validation and reduced latency
author: Toni Wahrstätter (@nerolation), Milos Stankovic (@morph-dev), Jihoon Song (@jihoonsong), Bharath Vedartham (@bharath-123), Raúl Kripalani (@raulk)
discussions-to: https://ethereum-magicians.org/t/eip-8101-payload-chunking/27085
status: Draft
type: Standards Track
category: Core
created: 2025-12-01
requires: 4844, 4895, 7732, 7825, 7928
---

## Abstract

This EIP introduces Payload Chunking, a protocol upgrade that restructures Ethereum block propagation into self-contained execution chunks with separated Chunk Access Lists (CALs). The proposer propagates beacon and execution payload headers, followed by CALs and chunks as independent network messages. CALs contain state diffs from chunk execution, enabling parallel chunk validation: chunk N can be validated once CALs 0..N are available. After all chunks validate successfully, the consensus layer finalizes the block. This architecture transforms block validation from a monolithic operation into a streaming pipeline while preserving block atomicity. Chunks exist only as a propagation and validation construct; the canonical chain stores complete execution payloads.

## Motivation

As Ethereum's gas limit increases, block sizes and execution complexity grow correspondingly. The current monolithic block structure requires validators to download and decompress the entire execution payload before beginning execution, creating a bottleneck in the consensus timeline. This sequential dependency—download, decompress, then execute—becomes increasingly problematic as blocks grow larger.

This proposal addresses these constraints by:

- **Parallel propagation**: Nodes propagate parts of the block separately, improving propagation speed through the network
- **Streaming validation**: Execution begins as chunks arrive rather than after full payload receipt
- **Parallel chunk execution**: CALs provide state diffs enabling independent validation of chunks
- **Bounded resources**: Per-chunk gas limits (`CHUNK_GAS_LIMIT = 2**24`) bound memory and CPU per validation unit
- **Early rejection**: Invalid blocks can be rejected at the first invalid chunk without processing remaining chunks
- **Proving compatibility**: Chunk boundaries create natural proving units for future ZK proving systems

## Specification

### Constants

| Name | Value | Description |
|------|-------|-------------|
| `CHUNK_GAS_LIMIT` | `2**24` (16,777,216) | Maximum gas per chunk, from [EIP-7825](./eip-7825.md) |
| `MAX_CHUNKS_PER_BLOCK` | `2**8` (256) | Maximum chunks per block |
| `MAX_TRANSACTIONS_PER_CHUNK` | `2**16` (65,536) | Maximum transactions per chunk |
| `CHUNK_INCLUSION_PROOF_DEPTH` | `floorlog2(get_generalized_index(BeaconBlockBody, 'chunk_headers_root')) + 1 + ceillog2(MAX_CHUNKS_PER_BLOCK)` (13) | SSZ Merkle proof depth for message inclusion |
| `MAX_CAL_SIZE` | `2**24` (16,777,216) | Maximum size in bytes of RLP-encoded CAL |

### Type Aliases

```python
ChunkIndex = uint8                  # Chunk position within block (0 to MAX_CHUNKS_PER_BLOCK-1)
Root = Bytes32                      # 32-byte hash (Merkle root or block root)
```

### Data Structures

#### Execution Chunk

```python
# Chunk header containing execution commitments
class ExecutionChunkHeader(Container):
    index: ChunkIndex                # Position in block [0, MAX_CHUNKS_PER_BLOCK)
    chunk_access_list_hash: Root     # keccak256(RLP(ChunkAccessList)) per EIP-7928
    pre_chunk_tx_count: uint32       # Cumulative transaction count before this chunk
    pre_chunk_gas_used: uint64       # Cumulative gas used before this chunk
    pre_chunk_blob_gas_used: uint64  # Cumulative blob gas used before this chunk
    txs_root: Root                   # Merkle root of chunk transactions
    gas_used: uint64                 # Gas consumed by this chunk
    blob_gas_used: uint64            # Blob gas consumed by this chunk
    withdrawals_root: Root           # Merkle root of withdrawals (non-empty only in last chunk)

# Self-contained execution chunk
class ExecutionChunk(Container):
    chunk_header: ExecutionChunkHeader
    transactions: List[Transaction, MAX_TRANSACTIONS_PER_CHUNK]
    withdrawals: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD]  # Only in last chunk
```

#### Chunk Access Lists (CALs)

CALs are RLP-encoded structures following the Block Access List format from [EIP-7928](./eip-7928.md), scoped to individual chunks. Each CAL records state diffs produced by executing its corresponding chunk.

**Type Definitions:**

```python
Address = Bytes20              # Ethereum address
StorageKey = Bytes32           # Storage slot key
StorageValue = Bytes32         # Storage slot value
Balance = uint256              # Account balance
Nonce = uint64                 # Account nonce
CodeData = bytes               # Contract bytecode
TxIndex = uint16               # Transaction index within the block
```

**Change Structures:**

```python
StorageChange = [TxIndex, StorageValue]     # [tx_index, new_value]
BalanceChange = [TxIndex, Balance]          # [tx_index, post_balance]
NonceChange = [TxIndex, Nonce]              # [tx_index, new_nonce]
CodeChange = [TxIndex, CodeData]            # [tx_index, new_code]

SlotChanges = [StorageKey, List[StorageChange]]  # All changes to a single storage slot

AccountChanges = [
    Address,                   # Account address
    List[SlotChanges],         # Storage writes
    List[StorageKey],          # Storage reads (slots read but not written)
    List[BalanceChange],       # Balance changes
    List[NonceChange],         # Nonce changes
    List[CodeChange]           # Code deployments
]

ChunkAccessList = List[AccountChanges]
```

**CAL Properties:**

1. **Incremental**: Each CAL contains only the state diffs from its chunk. Changes from earlier chunks are not repeated.
2. **Independent**: CALs can be validated independently given the pre-state.

**System Contract Entries:**

- **First CAL (chunk 0)**: Includes pre-execution system writes ([EIP-2935](./eip-2935.md) block hash history, [EIP-4788](./eip-4788.md) beacon root)
- **Last CAL (chunk N-1)**: Includes post-execution system operations ([EIP-4895](./eip-4895.md) withdrawals, [EIP-7002](./eip-7002.md) and [EIP-7251](./eip-7251.md) validator operations)

#### ExecutionPayload and ExecutionPayloadHeader

The `ExecutionPayload` is no longer used and is replaced by `ExecutionPayloadHeader`. 

The `ExecutionPayloadHeader` is modified to include number of chunks in a block:

```python
class ExecutionPayloadHeader(Container):
    # ... existing fields ...
	chunk_count: uint8      # Number of chunks in block
```

#### Beacon Block Commitments

The beacon block body replaces `execution_payload` with `execution_payload_header` and includes two Merkle roots committing to chunks and CALs:

```python
class BeaconBlockBody(Container):
    # ... other existing fields ...
    # Removed `execution_payload`
    execution_payload_header: ExecutionPayloadHeader    # Execution payload header

    chunk_headers_root: Root                            # Commitment to all chunk headers
    chunk_access_lists_root: Root                       # Commitment to all CAL hashes
```

Note: If [EIP-7732](./eip-7732.md) (ePBS) is enabled, then `execution_payload` field of `ExecutionPayloadEnvelope` is replaced by `execution_payload_header` in the same way.

**Commitment construction:**

```python
# Chunk headers commitment (SSZ)
chunk_headers: List[ExecutionChunkHeader, MAX_CHUNKS_PER_BLOCK]
chunk_headers_root = hash_tree_root(chunk_headers)

# CAL commitment (SSZ hash of RLP-encoded CALs for CL inclusion proofs)
cal_hashes: List[Root, MAX_CHUNKS_PER_BLOCK]  # Each Root = hash_tree_root(ByteList(RLP(CAL)))
chunk_access_lists_root = hash_tree_root(cal_hashes)
```

Note: The EL uses `keccak256(RLP(CAL))` in chunk headers per [EIP-7928](./eip-7928.md). The CL uses SSZ `hash_tree_root` for inclusion proofs. Both reference the same RLP-encoded CAL data.

#### Network Topis

Chunks and CALs propagate as separate network message with SSZ Merkle inclusion proofs against the beacon block header.

```python
class ExecutionChunkMessage(Container):
    chunk: ExecutionChunk                                               # The execution chunk
    inclusion_proof: Vector[Bytes32, CHUNK_INCLUSION_PROOF_DEPTH]       # SSZ Merkle proof against signed_block_header.message.body_root
    signed_block_header: SignedBeaconBlockHeader                        # Signed beacon block header

class ChunkAccessListMessage(Container):
    chunk_index: ChunkIndex                                             # Chunk index this CAL corresponds to
    chunk_access_list: ByteList[MAX_CAL_SIZE]                           # RLP-encoded chunk access list
    inclusion_proof: Vector[Bytes32, CHUNK_INCLUSION_PROOF_DEPTH]       # SSZ Merkle proof against signed_block_header.message.body_root
    signed_block_header: SignedBeaconBlockHeader                        # Signed beacon block header
```

The `signed_block_header` provides the proposer signature for authentication and the beacon block root for proof verification.

### Chunk Construction Rules

Block producers MUST follow these rules when constructing chunks:

1. **Gas Bound**: Each chunk MUST satisfy `gas_used <= CHUNK_GAS_LIMIT`
2. **Minimal Chunking**: For any two consecutive chunks `i` and `i+1`, their combined gas MUST exceed `CHUNK_GAS_LIMIT`. This ensures chunks cannot be trivially merged, preventing unnecessary fragmentation.
3. **Transaction Atomicity**: Transactions MUST NOT be split across chunks. Each transaction belongs entirely to one chunk.
4. **Withdrawal Placement**: Withdrawals MUST appear only in the final chunk. All other chunks have empty withdrawal lists.
5. **Chunk Count Bound**: A block MUST contain at most `MAX_CHUNKS_PER_BLOCK` chunks
6. **Sequential Indexing**: Chunks MUST be indexed sequentially from `0` to `N-1` where `N` is the total chunk count

### Network Protocol

#### Gossip Topics

Chunks and CALs propagate on dedicated gossip topics:

| Topic | Payload |
|-------|---------|
| `execution_chunk` | `ExecutionChunkMessage` |
| `chunk_access_list` | `ChunkAccessListMessage` |

#### Message Validation

Nodes MUST validate messages before forwarding:

**ExecutionChunkMessage validation:**

1. Verify `signed_block_header.signature` is valid for the proposer at the given slot
2. Verify `chunk.chunk_header.index < MAX_CHUNKS_PER_BLOCK`
3. Verify `chunk.chunk_header.gas_used <= CHUNK_GAS_LIMIT`
4. Verify `hash_tree_root(chunk.transactions) == chunk.chunk_header.txs_root`
5. Verify inclusion proof:

    ```python
    gindex = get_generalized_index(BeaconBlockBody, 'chunk_headers_root', chunk.chunk_header.index)
    is_valid_merkle_branch(
        leaf=hash_tree_root(chunk.chunk_header),
        branch=inclusion_proof,
        depth=CHUNK_INCLUSION_PROOF_DEPTH,
        index=get_subtree_index(gindex),
        root=signed_block_header.message.body_root,
    )
    ```

**ChunkAccessListMessage validation:**

1. Verify `signed_block_header.signature` is valid for the proposer at the given slot
2. Verify `chunk_index < MAX_CHUNKS_PER_BLOCK`
3. Verify `len(chunk_access_list) <= MAX_CAL_SIZE`
4. Verify inclusion proof:

    ```python
    gindex = get_generalized_index(BeaconBlockBody, 'chunk_access_lists_root', chunk_index)
    is_valid_merkle_branch(
        leaf=hash_tree_root(chunk_access_list),
        branch=inclusion_proof,
        depth=CHUNK_INCLUSION_PROOF_DEPTH,
        index=get_subtree_index(gindex),
        root=signed_block_header.message.body_root,
    )
    ```

Nodes MUST subscribe to chunk and CAL topics. Messages may be validated immediately using the signed block header without waiting for the full beacon block.

### Consensus Layer Changes

The consensus layer orchestrates chunk validation as beacon blocks, chunks, and CALs arrive from the network.

#### Types

```python
class ChunkExecutionStatus(Container):
    valid: bool
    error: Optional[str]
```

#### Store Extensions

```python
class Store:
    # ... existing fields ...

    # Tracks received CALs per block
    chunk_access_lists: Dict[Root, Set[ChunkIndex]]
    # Tracks received chunk headers per block
    chunk_headers: Dict[Root, Dict[ChunkIndex, ExecutionChunkHeader]]
    # Tracks chunk execution results per block
    chunk_execution_statuses: Dict[Root, Dict[ChunkIndex, ChunkExecutionStatus]]
    # Tracks final block validity
    block_valid: Dict[Root, bool]
```

#### Three-Phase Validation

##### Phase 1: CAL Reception

CALs are forwarded to the execution layer upon receipt. The EL caches CALs for use in subsequent chunk validation.

```python
def on_chunk_access_list_received(store: Store, cal_message: ChunkAccessListMessage) -> None:
    # Derive beacon block root from signed header
    root = hash_tree_root(cal_message.signed_block_header.message)
    chunk_index = cal_message.chunk_index

    # Validate cal message (proposer signature, inclusion proof, index bounds)
    assert verify_chunk_access_list_message(cal_message)

    # Wait for beacon block to get execution payload header
    block = wait_for_beacon_block(store, root)

    # Forward CAL to execution layer for caching
    execution_engine.new_chunk_access_list(
        block.body.execution_payload_header.block_hash,
        chunk_index,
        cal_message.chunk_access_list
    )

    # Record CAL receipt and notify waiting chunk validations
    store.chunk_access_lists[root].add(chunk_index)
    notify_cal_available(root, chunk_index)
```

##### Phase 2: Chunk Validation

Chunks are validated as they arrive, provided all prerequisite CALs (indices `0` through `N` for chunk `N`) are available.

```python
def on_chunk_received(store: Store, chunk_message: ExecutionChunkMessage) -> None:
    # Derive beacon block root from signed header
    root = hash_tree_root(chunk_message.signed_block_header.message)
    chunk = chunk_message.chunk
    chunk_index = chunk.chunk_header.index

    # Validate chunk message (proposer signature, inclusion proof, index bounds, gas bounds, tx root)
    assert verify_chunk_message(chunk_message)

    # Store chunk header for later finalization
    store.chunk_headers[root][chunk_index] = chunk.chunk_header

    # Wait for all prerequisite CALs [0, chunk_index]
    for i in range(chunk_index + 1):
        wait_for_cal(root, i)

    # Wait for beacon block to get execution payload header
    block = wait_for_beacon_block(store, root)

    # Execute chunk via EL (EL has cached all required CALs)
    execution_status = execution_engine.execute_chunk(
        block.body.execution_payload_header.block_hash,
        chunk
    )
    store.chunk_execution_statuses[root][chunk_index] = execution_status

    # Attempt block finalization if all chunks validated
    maybe_finalize_block(store, root)
```

##### Phase 3: Block Finalization

Once all chunks have been validated, the block is finalized by verifying chunk chaining and aggregate consistency.

```python
def maybe_finalize_block(store: Store, root: Root) -> None:
    if root in store.block_valid:
        return

    block = store.blocks[root]
    payload_header = block.body.execution_payload_header

    chunk_headers = store.chunk_headers.get(root, {})
    chunk_statuses = store.chunk_execution_statuses.get(root, {})

    # Verify sequential chunk coverage and validity
    for i in range(payload_header.chunk_count):
        if i not in chunk_statuses:
            return  # Gap in sequence
        if not chunk_statuses[i].valid:
            store.block_valid[root] = False
            return

    # EL verifies chunk header chaining and final state root
    finalize_result = execution_engine.finalize_block(payload_header.block_hash)
    store.block_valid[root] = finalize_result.valid
```

#### Attestation Rules

Validators MUST NOT attest to a block until:

1. All chunks have been received and individually validated (Phase 2 complete)
2. Block finalization has succeeded (Phase 3 complete)

**ePBS Integration ([EIP-7732](./eip-7732.md))**: PTC (Payload Timeliness Committee) members attest to chunk and CAL data availability. PTC attestations are independent of execution validity—they confirm only that all messages were received within the timeliness window.

### Execution Layer Changes

#### Chunk Execution

The execution layer validates chunks independently. For chunk `N`, the EL applies CALs `0..N-1` to the parent block state to reconstruct the chunk's pre-state, then executes the chunk transactions. It uses CAL `N` to execute transactions in parallel (the same as in [EIP-7928](./eip-7928.md)).

```python
def el_execute_chunk(
    payload_header: ExecutionPayloadHeader,
    chunk: ExecutionChunk,
    cached_cals: Dict[ChunkIndex, ChunkAccessList]
) -> PayloadStatusV1:
    """Execute chunk using cached CALs to reconstruct pre-state."""
    chunk_index = chunk.chunk_header.index

    # Verify all prerequisite CALs are cached
    for i in range(chunk_index):
        if i not in cached_cals:
            return PayloadStatusV1(
                status="INSUFFICIENT_INFORMATION",
                error=f"Missing CAL {i}, have {list(cached_cals.keys())}"
            )

    # Verify chunk's own CAL is cached
    if chunk_index not in cached_cals:
        return PayloadStatusV1(
            status="INSUFFICIENT_INFORMATION",
            error=f"Missing CAL {chunk_index}"
        )
    cal = cached_cals[chunk_index]

    # Get parent block state
    parent_state = get_state(payload_header.parent_hash)
    if parent_state is None:
        return PayloadStatusV1(
            status="SYNCING"
        )

    # Apply CALs 0..N-1 to reconstruct chunk pre-state
    pre_state = parent_state
    for i in range(chunk_index):
        pre_state = apply_state_diffs(pre_state, cached_cals[i])

    # Verify CAL hash matches chunk header commitment (EIP-7928 format)
    cal_hash = keccak256(cal)  # CAL is RLP-encoded
    if cal_hash != chunk.chunk_header.chunk_access_list_hash:
        return PayloadStatusV1(
            status="INVALID",
            error="CAL hash mismatch"
        )

    # Execute chunk transactions against pre-state
    result = execute_transactions(
        payload_header,
        pre_state,
        chunk.transactions,
        cal
    )

    if result.error:
        return PayloadStatusV1(
            status="INVALID",
            error=result.error
        )

    return PayloadStatusV1(
        status="VALID"
    )
```

### Engine API

Four new Engine API methods support chunked validation:

#### `engine_newBlockHeaderV1`

Replaces `engine_newPayloadV5`. Informes EL that new block is available and passes block data, except CALs and chunks. Must be called first, as other calls depend on this data.

**Parameters:**

- `payload_header`: `ExecutionPayloadHeader`
- `beaconRoot`: `Hash32`
- `blobHashes`: `List[Hash32]`
- `executionRequests`: `List[Bytes]`

**Returns:** PayloadStatusV1

#### `engine_newChunkAccessListV1`

Caches a CAL for subsequent chunk execution. Requires block data to have been sent via `engine_newBlockHeaderV1`.

**Parameters:**

- `blockHash`: `Hash32` - The block hash
- `chunkIndex`: `ChunkIndex` - Index of the chunk this CAL corresponds to
- `chunkAccessList`: `ChunkAccessList` - RLP-encoded chunk access list

**Returns:** `PayloadStatusV1`

#### `engine_executeChunkV1`

Executes and validates a chunk. Requires block data to have been sent via `engine_newBlockHeaderV1`, and all prerequisite CALs (indices `0` through `N`) to have been sent via `engine_newChunkAccessListV1`.

**Parameters:**

- `blockHash`: `Hash32` - The block hash
- `chunk`: `ExecutionChunk` - The chunk to execute

**Returns:** `PayloadStatusV1`

#### `engine_finalizeBlockV1`

Finalizes block validation after all chunks have been executed. Verifies:

- Chunk headers chain correctly: chunk N's `pre_chunk_*` fields equal chunk N-1's cumulative values
- Final state root matches `payload_header.state_root`
- All block header fields are valid

**Parameters:**

- `blockHash`: `Hash32` - Hash of the execution payload

**Returns:** `PayloadStatusV1`

#### Response Types

All new Engine API methods return `PayloadStatusV1` type, but `status` field has different meaning and values depending on the method. Following table defines all possible values and when they can be used.

| Status | Semantics | `newBlockHeader` | `newChunkAccessList` | `executeChunk` | `finalizeBlock` |
|-|-|-|-|-|-|
| `ACCEPTED` | BlockHeader/CAL accepted successfully | ✅ | ✅ | | |
| `VALID` | Chunk/block executed correctly | | | ✅ | ✅ |
| `INSUFFICIENT_INFORMATION` | Missing prerequisite CALs | | | ✅ | |
| `INVALID` | Execution or validation failed | ✅ | ✅ | ✅ | ✅ |
| `SYNCING` | Parent state not available (node syncing) | ✅ | ✅ | ✅ | ✅ |

The `error` field is present if status is `INSUFFICIENT_INFORMATION` or `INVALID`.

## Rationale

### Incremental CALs

Chunk N applies CALs 0..N-1 to the parent state to reconstruct its pre-state. This incremental approach provides:

1. **Parallel Execution**: Multiple chunks can execute concurrently once their prerequisite CALs arrive. Alternative designs where each CAL contains cumulative state diffs would cause CAL sizes to grow linearly with chunk count, creating worst-case bandwidth issues.
2. **Early Rejection**: Invalid chunks cause immediate block rejection without processing subsequent chunks.
3. **Bounded Resources**: Each chunk validation is independently bounded by `CHUNK_GAS_LIMIT`.

### CL-Driven Architecture

The consensus layer orchestrates chunk execution because:

1. **Consistency**: Existing CL-EL interaction follows CL-driven patterns (e.g., `engine_newPayload`).
2. **Global View**: The CL tracks which CALs have arrived from gossip and can optimally schedule chunk execution based on availability.
3. **Dependency Enforcement**: The CL ensures chunks execute only when prerequisites are satisfied.
4. **Timeliness Tracking**: The CL determines whether messages arrived within attestation deadlines.

### Separate CAL Propagation

Decoupling CALs from chunks provides:

1. **Faster Propagation**: CALs (kilobytes of state diffs) propagate faster than chunks (megabytes of transactions), enabling earlier execution starts.
2. **Parallelization**: Chunks can execute independently by applying prior CALs to reconstruct pre-states.
3. **State reads**: CALs allow us to start reading required state from db before transactions arrive.
4. **StateRoot calculation**: EL can start calculating state root as soon as all CALs are received.
5. **Extensibility**: CALs can be extended with proving metadata without modifying chunk structure.

### Semantic Chunking

Semantic chunking (transaction-aligned boundaries with gas limits) differs from byte-level fragmentation:

1. **Streaming Validation**: Each chunk is a complete execution unit validatable upon receipt.
2. **Resource Bounds**: Gas limits bound per-chunk memory, CPU, and proving costs.
3. **Transaction Integrity**: No transaction splitting simplifies execution semantics.

### Parameter Selection

| Parameter | Value | Rationale |
|-----------|-------|-----------|
| `CHUNK_GAS_LIMIT` | `2^24` (~16.7M) | Balances parallelization granularity with per-chunk overhead. Large enough for complex transactions, small enough for bounded ZK proving circuits. |
| `MAX_CHUNKS_PER_BLOCK` | `256` | Supports ~4 Ggas blocks (256 × 16M). Conservative upper bound accommodating future gas limit increases. |
| `MAX_TRANSACTIONS_PER_CHUNK` | `2^16` (65,536) | Accommodates chunks filled with minimal-gas transactions (ETH transfer - 21,000 gas) with margin for future gas cost reductions. |

## Backwards Compatibility

This EIP introduces breaking changes requiring a coordinated hard fork:

| Component | Change |
|-----------|--------|
| **Block Propagation** | Execution payloads propagate as chunk and CAL messages instead of monolithic payloads |
| **Network Protocol** | New gossip topics for `ExecutionChunkMessage` and `ChunkAccessListMessage` |
| **Engine API** | Four new methods: `engine_newBlockHeaderV1`, `engine_newChunkAccessListV1`, `engine_executeChunkV1`, `engine_finalizeBlockV1` |
| **Validation** | Three-phase validation (CAL reception, chunk validation, block finalization) |

Post-fork, nodes must implement chunked validation to participate in consensus. Historical blocks remain unaffected.

## Security Considerations

### Data Availability Attacks

Chunk separation introduces new attack surfaces:

| Attack | Description | Impact | Mitigation |
|--------|-------------|--------|------------|
| **Chunk Withholding** | Builder publishes some chunks but withholds others | Block unvalidatable; attestation deadline missed | Attestation rules require all chunks; PTC votes on availability |
| **CAL Withholding** | Builder publishes chunks but withholds CALs | Streaming execution blocked; falls back to sequential | CALs reconstructible by sequential chunk execution |
| **Reverse Propagation** | Builder sends chunks/CALs in reverse order (N→0) | No parallel execution until chunk 0 and CAL 0 arrive | Rational builders propagate in order for faster attestations |

## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).
