EIP-8411 Fast propagation of execution payloads (Fast Execution Payload Broadcast)
Hegota Considered for Inclusion · Networking & nodes · EIP status Draft · created 2026-09-05
This changes the way EIP-7732 sends the execution payload as a single message into sending it split into 64 chunks. The builder puts the Merkle root of the chunk set into the bid so that each chunk can be verified separately.
Why it is needed
The gas limit increase that EIP-7732 makes possible means larger execution payloads. Larger payloads take longer to spread across the network. If a Payload Timeliness Committee (PTC) member does not receive the payload within the attestation deadline, even a valid payload is treated as missing. With the current execution_payload gossip topic, two problems become more pronounced as payloads grow. First, a node that has already received a large message has difficulty cutting off duplicate transmissions arriving by other paths midway, so the same bytes arrive multiple times and consume bandwidth that could carry new data. Second, a node can pass a message on only after receiving all of it, so each hop (one step of forwarding between nodes) adds one full transmission and the delay grows in proportion to message size times hop count. Splitting the message into chunks reduces both problems. At the chunk level no large duplicates can occur, and a node can forward a chunk as soon as it receives it, so hops proceed overlapped instead of queuing one after another.
What changes
Today
- The builder sends the execution payload envelope on the execution_payload topic as a single message.
- A node can pass the message on only after receiving all of it, so the full transmission time is added at every hop.
- Even when a large message arrives overlapping over several paths, it is hard to cut off midway, so the same bytes are received multiple times.
- The beacon block root (beacon_block_root) is contained in the envelope.
After EIP-8411
- The envelope is split into 64 chunks and sent on the execution_payload_chunks topic, and the old topic is removed.
- When a chunk arrives, it is verified immediately against the Merkle root in the bid and forwarded right away without waiting for the rest.
- Even when duplicates occur they are only the size of a chunk, so little bandwidth is wasted.
- The beacon block root is removed from the envelope and carried in each chunk message as a hint.
Who is affected
- Consensus client teams: A new gossip topic, an extended Seen cache, chunk verification rules and envelope reassembly must be implemented. Because the old topic is removed, a fork is required.
- Builder: When creating a bid, the builder splits the envelope into chunks in advance, computes payload_chunks_root and puts it in, and after seeing the block containing its own bid, publishes the 64 chunk messages.
- Validators and the PTC: Because chunks are forwarded as they arrive, the payload is more likely to be received within the attestation deadline.
- Regular users and app developers: How wallets and apps are used does not change. Only the way messages propagate between nodes changes, and it lays the groundwork for supporting larger payloads.
In depth
Constants, types, and the commitment in the bid
The number of chunks, TOTAL_EXECUTION_PAYLOAD_CHUNKS, is 64, and the inclusion proof depth, CHUNK_INCLUSION_PROOF_DEPTH, is 6. The maximum size of one chunk, MAX_PAYLOAD_CHUNK_SIZE, is MAX_PAYLOAD_SIZE // TOTAL_EXECUTION_PAYLOAD_CHUNKS, which is 163,840 bytes. The new type ChunkIndex is a uint64 holding the chunk number from 0 to TOTAL_EXECUTION_PAYLOAD_CHUNKS - 1, and Chunk is ByteList[MAX_PAYLOAD_CHUNK_SIZE], a contiguous segment of the serialized ExecutionPayloadEnvelope. The commitment is carried in the bid. A payload_chunks_root: Root field is added to ExecutionPayloadBid, which is the SSZ hash_tree_root of a Vector[Bytes32, TOTAL_EXECUTION_PAYLOAD_CHUNKS] holding the SHA-256 hash of each chunk of the serialized envelope. The receiver only needs to check the bid signature once and the Merkle inclusion proof once per chunk. After reassembling the envelope, it MUST compare it against the payload hash committed in the beacon block.
Block root removed from the envelope, and the PayloadChunk message
ExecutionPayloadEnvelope carries payload, execution_requests, parent_beacon_block_root and builder_index, and drops the old beacon_block_root. When a builder creates a bid, the block does not exist yet, so the block root cannot be known. In EIP-7732, the builder could see the bid included in a block and then build the envelope with the root. The block root is instead carried in the chunk message. PayloadChunk consists of payload_chunks_root, index, chunk, chunk_inclusion_proof (Vector[Bytes32, CHUNK_INCLUSION_PROOF_DEPTH]), slot, and beacon_block_root. payload_chunks_root is the same value the bid commits to and identifies the set of chunks. The bid spreads on its own gossip topic ahead of the block, so a recipient that has seen a valid bid committing to that root can verify chunks immediately without waiting for the block. slot and beacon_block_root are unauthenticated hints that sit outside the inclusion proof. slot is used to cheaply filter chunks by slot window and MUST equal the slot of the bid that committed to that root. beacon_block_root is used to find or request the corresponding block and MUST equal the root of the block that contains that bid. Recipients MUST NOT treat the two fields as authenticated values and use them only to decide the lookup path. The only thing that binds a chunk to a bid is payload_chunks_root.
Chunking rules and the inclusion proof
A builder MUST build the chunks and payload_chunks_root as follows. For data, the serialized bytes of the envelope, compute_payload_chunks computes chunk_size = (len(data) + TOTAL_EXECUTION_PAYLOAD_CHUNKS - 1) // TOTAL_EXECUTION_PAYLOAD_CHUNKS, then cuts the i-th chunk as data[start:start + chunk_size] with start = i * chunk_size, producing TOTAL_EXECUTION_PAYLOAD_CHUNKS chunks. No padding is added. The last chunk with content may be shorter, and the chunks after it may be empty. compute_payload_chunks_root takes the SHA-256 hash of each chunk, hash(chunk), as a leaf of a Vector[Bytes32, TOTAL_EXECUTION_PAYLOAD_CHUNKS] and computes hash_tree_root. chunk_inclusion_proof is the Merkle branch for that leaf. verify_payload_chunk_inclusion_proof checks by passing leaf=hash(chunk), branch=chunk_inclusion_proof, depth=CHUNK_INCLUSION_PROOF_DEPTH, index=index, root=payload_chunks_root to is_valid_merkle_branch. The recipient concatenates chunks 0 through TOTAL_EXECUTION_PAYLOAD_CHUNKS - 1 and deserializes the result. Each leaf hashes the exact bytes of the chunk, so there is nothing to strip off.
New gossip topic and Seen cache extension
A new execution_payload_chunks topic is added and the execution_payload topic is deprecated. The new topic carries PayloadChunk messages. After observing the beacon block that contains its bid, the builder creates TOTAL_EXECUTION_PAYLOAD_CHUNKS PayloadChunk messages and publishes them to the topic. Two sets are added to the Seen cache used by the gossip validation functions. payload_chunks_roots is a Set[Tuple[Slot, Root]]. For each SignedExecutionPayloadBid accepted on the bid topic, and for each bid contained in a valid beacon block, (bid.slot, bid.payload_chunks_root) is inserted. Entries MUST be kept until the end of the corresponding slot (within MAXIMUM_GOSSIP_CLOCK_DISPARITY) and MAY be pruned after that. The validation rules on the bid topic limit the number of bids per slot, so this set stays small. payload_chunk_tuples is a Set[Tuple[Root, ChunkIndex]] that records (payload_chunks_root, index) for each accepted chunk. It plays the same role as data_column_sidecar_tuples for column sidecars and is the basis for the check on whether a chunk is seen for the first time.
Gossip validation before forwarding a chunk
A node MUST forward a PayloadChunk on the topic only if it passes all of the following checks. [IGNORE] chunk.slot must equal the current slot, allowing a tolerance of MAXIMUM_GOSSIP_CLOCK_DISPARITY. [REJECT] chunk.index must be less than TOTAL_EXECUTION_PAYLOAD_CHUNKS. [IGNORE] (chunk.slot, chunk.payload_chunks_root) must be in seen.payload_chunks_roots. A client MAY queue a chunk that points to an unknown root for a short time and validate it again when the bid arrives. [REJECT] verify_payload_chunk_inclusion_proof(chunk) must be true. [REJECT] If the node has already seen a valid beacon block that contains a bid committing to chunk.payload_chunks_root, chunk.beacon_block_root must equal that block's hash tree root. [IGNORE] (chunk.payload_chunks_root, chunk.index) must not be in seen.payload_chunk_tuples, and on acceptance the node adds that pair. A chunk that passes validation MUST be forwarded immediately, without waiting for the other chunks or the beacon block, and is kept in a buffer until the envelope can be reconstructed.
Design rationale and open questions
A Merkle commitment was chosen because alternatives such as KZG put proof generation on the builder's critical path. Building a Merkle tree over the chunks is cheap enough to do at bid time, and putting a single root in the bid lets each chunk verify itself by relying on one already verified signature. TOTAL_EXECUTION_PAYLOAD_CHUNKS = 64 is not a value that came from measurement. Even a smaller number of chunks shows an advantage over one large message. 64 was chosen for compatibility in case erasure coding with c-kzg is added to the protocol later. c-kzg splits data into 64 pieces and extends it to 128 pieces. Chunks are kept as ByteList without padding because SSZ has no outer length prefix. If padding bytes were added, the result would still deserialize to the envelope data, but hashing the exact bytes of the chunk also commits to the real length. Fixing the split and the Merkleization to SSZ lets two clients arrive at the same root with existing code. There are two open questions. If erasure coding is applied to the chunks, the envelope can be recovered from only some of them. This may increase complexity and bandwidth, but it can mitigate the coupon collector problem, where a recipient that failed to receive a few chunks waits for a long time. Another option mentioned is to send the block access list (BAL, EIP-7928), which is currently inside the execution payload, separately on a bal_chunks topic using the same method, so that its propagation starts earlier than the rest of the payload.
Security considerations
Chunks have no individual signatures. Authenticity comes entirely from the bid signature through payload_chunks_root. The root carried in a chunk is not authenticated by itself, but a chunk is accepted only if a bid that passed signature verification committed to that root. A chunk that points to an unknown root is not forwarded, so unsigned chunk spam is filtered out at the first hop. slot and beacon_block_root are included in neither the bid signature nor the inclusion proof, so they are restricted to lookup use only. A chunk resent with a changed slot fails the (slot, payload_chunks_root) membership check, and a chunk with a changed beacon_block_root is rejected once the block containing the bid is known. Chunk bytes are bound to the proof, so a message that differs only in the hint fields cannot alter the reassembled envelope. For two different chunks at the same (payload_chunks_root, index) to both verify, a Merkle tree collision would be required, so per-index chunk equivocation is impossible. The upper bound on chunk.index is a key safeguard. is_valid_merkle_branch reads only the low CHUNK_INCLUSION_PROOF_DEPTH bits, so without the bound the same chunk could be spread again as index + k * TOTAL_EXECUTION_PAYLOAD_CHUNKS. Buffering before the block is also bounded. A node stores at most TOTAL_EXECUTION_PAYLOAD_CHUNKS chunks per payload_chunks_roots entry, and the size of that set is limited by bid gossip validation (the builder signature and the slot window). A chunk passing gossip validation does not make the reassembled envelope valid, and full envelope validation takes place after reassembly, as in EIP-7732.
Compatibility, status, and fork decisions
Because the execution_payload topic is removed, this change is not backwards compatible and requires a fork. The reference implementation is not yet determined (TBD). The status in the source is Draft, so the constants, message structure, and validation rules may still change. The progress on core developer calls is as follows. It was discussed on 09.10 at ACDE #245, with no decision. On 09.17 at ACDC #187 it was accepted as a Hegota candidate (PFI), and the decision on Considered for Inclusion (CFI) was deferred until the scope decisions for other EIPs were done. On 09.21 at ACDT #97 there was an announcement that the champion withdrew EIP-8142 (BiB) and proposed this EIP as its replacement. No decision was made on that call, and the CL teams were asked to give ratings before the next ACDC. On 10.29 at ACDC #190 it was decided as CFI for Hegota.