Alert Source Discuss
⚠️ Draft Standards Track: Networking

EIP-8411: Fast Execution Payload Broadcast

Faster execution payload propagation via chunked gossip

Authors Kamil Salakhiev (@kamilsa), Csaba Kiraly (@cskiraly), Potuz (@potuz), Raúl Kripalani (@raulk), Satyajit Das (@satushh)
Created 2026-09-05
Discussion Link https://ethereum-magicians.org/t/eip-8411-fast-execution-payload-broadcast/29613
Requires EIP-7732

Abstract

EIP-7732 propagates the execution payload as a single message on the execution_payload gossipsub topic. This EIP replaces that topic with execution_payload_chunks, which carries the payload envelope split into TOTAL_EXECUTION_PAYLOAD_CHUNKS chunks. The builder commits to the chunk set with a Merkle root carried in the execution bid, so each chunk is independently verifiable from a single bid signature check plus an inclusion proof.

Motivation

The gas-limit growth that EIP-7732 enables implies larger payloads leading to increased latency. If Payload Timeliness Committee (PTC) members do not receive the payload within the attestation deadline, an otherwise valid payload is considered missed.

The existing execution_payload gossipsub topic scales poorly as payloads grow. Two effects dominate:

  1. Once a peer has received a large message, it is hard to cancel in-flight duplicates, so the same bytes arrive several times and consume bandwidth that could carry new data.
  2. A peer must download the full message before it can forward any of it, so each hop adds a full transfer, leading to increased latency proportional to message size multiplied by hop count.

Both effects are mitigated when the message is chunked. Large duplicates are impossible with chunk granularity, and a peer can forward each chunk as soon as it arrives, so hops overlap instead of serializing.

Specification

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119 and RFC 8174.

Parameters

Constant Value
TOTAL_EXECUTION_PAYLOAD_CHUNKS 64
CHUNK_INCLUSION_PROOF_DEPTH 6
MAX_PAYLOAD_CHUNK_SIZE MAX_PAYLOAD_SIZE // TOTAL_EXECUTION_PAYLOAD_CHUNKS (163,840 bytes)

Custom types

Name SSZ equivalent Description
ChunkIndex uint64 Index of a chunk, 0 through TOTAL_EXECUTION_PAYLOAD_CHUNKS - 1
Chunk ByteList[MAX_PAYLOAD_CHUNK_SIZE] Contiguous slice of the serialized ExecutionPayloadEnvelope

Commitment

The execution bid commits to the chunked envelope:

class ExecutionPayloadBid(ProgressiveContainer):
    ...
    payload_chunks_root: Root  # [New in this EIP]
    ...

payload_chunks_root is the SSZ hash_tree_root of the Vector[Bytes32, TOTAL_EXECUTION_PAYLOAD_CHUNKS] of SHA-256 hashes of the chunks of the serialized ExecutionPayloadEnvelope, as computed by compute_payload_chunks_root in the Chunking section. A receiver therefore verifies one bid signature and, per chunk, one Merkle inclusion proof. Once the envelope is reconstructed, it MUST be checked against the payload hash committed in the beacon block.

Envelope

Chunking is applied to the envelope:

class ExecutionPayloadEnvelope():
    payload: ExecutionPayload
    execution_requests: ExecutionRequests
    parent_beacon_block_root: Root
    builder_index: BuilderIndex
    # [Modified in this EIP] removed `beacon_block_root`

beacon_block_root is removed because it is not known at the time of envelope construction.

Under EIP-7732 the builder constructs the envelope after observing that its bid was included, and can therefore embed the beacon block root. Under this EIP the builder must commit to the chunks when it produces the bid — before the block exists — so the root cannot be part of the committed bytes. It is instead carried per chunk in the PayloadChunk message, together with the payload_chunks_root committed in the bid:

class PayloadChunk():
    payload_chunks_root: Root
    index: ChunkIndex
    chunk: Chunk
    chunk_inclusion_proof: Vector[Bytes32, CHUNK_INCLUSION_PROOF_DEPTH]
    slot: Slot
    beacon_block_root: Root

payload_chunks_root matches the commitment in the bid and identifies the chunk set. Because bids propagate on their own gossip topic before the beacon block, a receiver that has observed a valid bid committing to this root can verify the chunk as soon as it arrives, without waiting for the block.

slot and beacon_block_root sit outside the inclusion proof and are unauthenticated hints: slot allows windowing a chunk cheaply and MUST equal the slot of a bid committing to payload_chunks_root, while beacon_block_root lets a receiver locate or request the corresponding beacon block and MUST equal the root of the block containing that bid. Receivers MUST NOT treat either field as authenticated and MUST use them only to route lookups; a chunk is bound to a bid solely through payload_chunks_root.

Chunking

The builder MUST derive the chunks, and therefore payload_chunks_root, as follows. No padding is added: the last non-empty chunk can be shorter, and trailing chunks can be empty.

def compute_payload_chunks(
    envelope: ExecutionPayloadEnvelope,
) -> Vector[Chunk, TOTAL_EXECUTION_PAYLOAD_CHUNKS]:
    data = serialize(envelope)
    chunk_size = (len(data) + TOTAL_EXECUTION_PAYLOAD_CHUNKS - 1) // TOTAL_EXECUTION_PAYLOAD_CHUNKS
    chunks = []
    for i in range(TOTAL_EXECUTION_PAYLOAD_CHUNKS):
        start = i * chunk_size
        chunks.append(Chunk(data[start:start + chunk_size]))
    return chunks


def compute_payload_chunks_root(envelope: ExecutionPayloadEnvelope) -> Root:
    leaves = Vector[Bytes32, TOTAL_EXECUTION_PAYLOAD_CHUNKS](
        [hash(chunk) for chunk in compute_payload_chunks(envelope)]
    )
    return hash_tree_root(leaves)

Each leaf is hash(chunk), the SHA-256 hash of the chunk’s bytes; chunk_inclusion_proof is the corresponding Merkle branch:

def verify_payload_chunk_inclusion_proof(payload_chunk: PayloadChunk) -> bool:
    return is_valid_merkle_branch(
        leaf=hash(payload_chunk.chunk),
        branch=payload_chunk.chunk_inclusion_proof,
        depth=CHUNK_INCLUSION_PROOF_DEPTH,
        index=payload_chunk.index,
        root=payload_chunk.payload_chunks_root,
    )

Receivers concatenate chunks 0 through TOTAL_EXECUTION_PAYLOAD_CHUNKS - 1 and deserialize the result; nothing is stripped, since each leaf hashes its chunk’s exact bytes.

Networking

This EIP introduces the execution_payload_chunks gossip topic and deprecates execution_payload. The topic carries PayloadChunk messages.

Once the builder observes a beacon block containing its bid, it is able to construct the TOTAL_EXECUTION_PAYLOAD_CHUNKS PayloadChunk messages and publishes them to the topic.

Seen cache

The Seen cache used by the gossip validation functions is extended to retain the chunk commitments of observed bids and to track received chunks:

@dataclass
class Seen:
    ...
    payload_chunks_roots: Set[Tuple[Slot, Root]]  # [New in this EIP]
    payload_chunk_tuples: Set[Tuple[Root, ChunkIndex]]  # [New in this EIP]

The tuple (bid.slot, bid.payload_chunks_root) is added to payload_chunks_roots for every bid whose SignedExecutionPayloadBid is accepted on the bid gossip topic, and for the bid contained in every valid beacon block. Entries MUST be retained at least until the end of their slot (within MAXIMUM_GOSSIP_CLOCK_DISPARITY) and MAY be pruned afterwards. The set stays small: the bid topic’s own validation rules bound how many bids enter it per slot.

payload_chunk_tuples records the (payload_chunks_root, index) pair of every accepted chunk, analogous to data_column_sidecar_tuples for column sidecars, and backs the first-seen check below.

Gossip validation

The following validations MUST pass before a node forwards a PayloadChunk chunk on the topic.

  • [IGNORE] chunk.slot equals the current slot, within MAXIMUM_GOSSIP_CLOCK_DISPARITY allowance.
  • [REJECT] chunk.index is less than TOTAL_EXECUTION_PAYLOAD_CHUNKS.
  • [IGNORE] (chunk.slot, chunk.payload_chunks_root) is present in seen.payload_chunks_roots. Clients MAY queue chunks referencing an unknown root for a short period and revalidate once the bid arrives.
  • [REJECT] The inclusion proof is valid as verified by verify_payload_chunk_inclusion_proof(chunk).
  • [REJECT] If the node has seen a valid beacon block whose bid commits to chunk.payload_chunks_root: chunk.beacon_block_root equals the hash tree root of that block.
  • [IGNORE] (chunk.payload_chunks_root, chunk.index) is not present in seen.payload_chunk_tuples (first-seen per commitment and index); the tuple is added upon acceptance.

A chunk that passes these validations MUST be forwarded immediately, without waiting for the remaining chunks or for the beacon block, and buffered until the envelope can be reconstructed.

Open questions and future considerations

  • The networking scheme could be extended by erasure coding the chunks, so that a receiver can reconstruct the envelope from a subset of chunks. This introduces additional complexity and may lead to higher bandwidth usage. However, it may mitigate “coupon collector” problems, where a receiver missing a small number of chunks may wait a long time to receive them.
  • In future it might be considered to introduce a bal_chunks topic following the same broadcasting scheme for Block Access List (BAL, EIP-7928) messages, which are currently part of the execution payload. This would allow BAL message propagation to start sooner than the rest of the payload.

Rationale

Why a Merkle commitment. Alternatives such as KZG place proof generation on the builder’s critical path. A Merkle tree over chunks is cheap enough to build at bid time, and a single root in the bid makes each chunk self-verifying against one already-verified signature.

Why TOTAL_EXECUTION_PAYLOAD_CHUNKS = 64. The value is not derived from measurement; smaller chunk counts already show benefit compared to single large messages. 64 is chosen for forward compatibility in case we decide to extend protocol erasure coding via c-kzg, which segments data into 64 chunks and extends them to 128.

Why unpadded ByteList chunks. SSZ has no outer length prefix, so padding bytes would deserialize as envelope data; hashing each chunk’s exact bytes commits its true length instead. Pinning the split and merkleization to SSZ lets two clients derive identical roots with existing code.

Backwards Compatibility

The execution_payload topic is removed, so this change is not backwards compatible and requires a fork.

Reference Implementation

TBD.

Security Considerations

Chunks are not individually signed; their authenticity derives entirely from the bid signature via payload_chunks_root. The root carried in a chunk is unauthenticated on its own, but a chunk is only accepted if a bid that already passed signature validation committed to that root: a chunk referencing an unknown root is not forwarded, so unsigned chunk spam is filtered at the first hop.

slot and beacon_block_root are covered by neither the bid signature nor the inclusion proof, which is why the specification restricts them to routing lookups. The consistency checks in gossip validation bound their misuse: a chunk replayed with a divergent slot fails the (slot, payload_chunks_root) membership check, and a divergent beacon_block_root is rejected once the block containing the bid is known. Because the chunk bytes are proof-bound, a message that differs only in these hint fields cannot alter the reconstructed envelope.

Per-index equivocation is not possible: two distinct chunks that both verify at the same (payload_chunks_root, index) would imply a collision in the Merkle tree, so at most one chunk per index can propagate for a given commitment.

The chunk.index bound is load-bearing: is_valid_merkle_branch reads only the low CHUNK_INCLUSION_PROOF_DEPTH bits, so an unbounded index would let the same chunk propagate again at index + k * TOTAL_EXECUTION_PAYLOAD_CHUNKS.

Pre-block buffering is bounded: a node stores at most TOTAL_EXECUTION_PAYLOAD_CHUNKS chunks per entry in payload_chunks_roots, and the size of that set is itself limited by bid gossip validation (builder signature and slot window).

Gossip validity of a chunk does not imply validity of the reconstructed envelope; full envelope validation still happens after reconstruction, as under EIP-7732.

Copyright and related rights waived via CC0.

Citation

Please cite this document as:

Kamil Salakhiev (@kamilsa), Csaba Kiraly (@cskiraly), Potuz (@potuz), Raúl Kripalani (@raulk), Satyajit Das (@satushh), "EIP-8411: Fast Execution Payload Broadcast [DRAFT]," Ethereum Improvement Proposals, no. 8411, September 2026. Available: https://eips.ethereum.org/EIPS/eip-8411.