Skip to content

Conversation

@nerolation
Copy link
Contributor

This PR proposes payload chunking as a feature to streamline block download and execution.

@nerolation nerolation requested a review from eth-bot as a code owner December 11, 2025 14:15
@github-actions github-actions bot added c-new Creates a brand new proposal s-draft This EIP is a Draft t-core labels Dec 11, 2025
@eth-bot
Copy link
Collaborator

eth-bot commented Dec 11, 2025

File EIPS/eip-8101.md

Requires 1 more reviewers from @g11tech, @lightclient, @SamWilsn

@eth-bot eth-bot added e-consensus Waiting on editor consensus e-review Waiting on editor to review labels Dec 11, 2025
@eth-bot eth-bot changed the title Add EIP: Payload chunking Add EIP: Payload Chunking with Chunk Access Lists Dec 11, 2025
EIPS/eip-9999.md Outdated
title: Payload Chunking with Chunk Access Lists
description: Semantic block 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-9999-payload-chunking/27085
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
discussions-to: https://ethereum-magicians.org/t/eip-9999-payload-chunking/27085
discussions-to: https://ethereum-magicians.org/t/eip-8101-payload-chunking/27085

Added assigned number.

EIPS/eip-9999.md Outdated
@@ -0,0 +1,447 @@
---
eip: 9999
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
eip: 9999
eip: 8101

Assigning next sequential EIP/ERC/RIP number.
Numbers are assigned by editors & associates.

Please also update the filename.

chunk_index = cal_sidecar.chunk_index

# Get or wait for beacon block
block = wait_beacon_block(store, root)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i believe we could probably queue up the chunks instead of blocking on the beacon block. and then when we receive the beacon block, we send the CAL to the EL

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But with ePBS, i would expect the beacon block to be available. since we first broadcast a beacon block with the execution payload bid and then broadcast the execution payload in chunks.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i believe we could probably queue up the chunks instead of blocking on the beacon block.

I think this is implementation detail. Clients can handle this any way they want

But with ePBS, i would expect the beacon block to be available.

In most of the cases, yes. But this isn't guaranteed because of the network propagation.


# Wait prior and chunk's CALs
for chunk_index in range(chunk_header.index + 1):
wait_cal(root, chunk_index)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again i think instead of an explicit wait, we could queue up the chunks but i think that could also be an impl detail.

EIPS/eip-8101.md Outdated
1. All chunks have been individually validated (Phase 2)
2. The complete state transition has been verified (Phase 3)

In the context of EIP-7732 (ePBS), the PTC (Payload Timeliness Committee) validator should attest whether all chunks and CALs are available in the timely manner (even if validation is not finished).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we definitely need ePBS for CALs i believe. With the current status quo, i fear that attestations will be sent out late because of one chunk potentially arriving late as it was on a slow network propagation path.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, ePBS and BAL are required EIPs (together with few others).

Copy link
Member

@jochem-brouwer jochem-brouwer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some initial comments after the first pass 😄 👍

This reads really well and it also looks complete. Have added some minor thoughts and questions.

Also:

I get the general idea that CALs are sent/received faster than the chunks, but I think for the complete picture we should take into account that sometimes there might be "gaps". So in order to build the prestate for some chunk N, we need a combination of either the previous chunks, and for the missing chunks we would need the CALs for the chunks missing. With that data we can start executing chunk N. If any of the previous data is invalid, this would invalidate the block. But this would be realized once we have the CAL + chunk and the relevant previous data so we can execute it and verify the data. If that is incorrect, we can invalidate it.

I'm interested to see how this would work in practice, for instance when some chunks/CALs are not received. Might also take a look if we can somehow compress the data of a chunk/CAL such that it fits in a single UDP/TCP packet.


## 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). Proposer propagates beacon and execution block headers, followed by CALs and Chunks. CALs propagate separately and contain state diffs that enable independent chunk execution. Chunks are executed and verified separately (as long as CALs `0..N` are available for chunk `N`). After all chunks execute, the CL validates and finalizes the block. This architecture transforms block validation from a monolithic operation into a streaming pipeline while preserving block atomicity. The concept of chunks is used only during block propagation.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need CAL N for chunk N? We can execute chunk N if we have either the previous chunks, or the CALs 0..N-1. However, to verify, we need also CAL N to check if it matches the access list we produced when executing chunk N.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strictly speaking, we don't need CAL_N for chunk N, but you can't execute txs from a chunk in parallel without it (which is something that we want).

Assuming you have everything else (chunk and all other CALs), it's a trade off between starting sequential execution immediately or waiting for CAL_N and doing it in parallel.

I don't think that we can leave clients to choose one or the other approach (or support both), as it affects both EL and CL clients, their interaction, and Engine API. So waiting for CAL_N makes the most sense to me.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would need the prestate for chunk N in order to execute it. If we also have the access list, we can prefetch the accessed storage/accounts also. However in order to thus start executing it, we only need the previous chunks in order to be sure that we know the state it runs on.
This might also be my perspective how I'm seeing it, because we would also need to verify chunk Ns CAL. So in that case, we would indeed need CAL N in order to fully validate it (but we can still start executing it if we have the previous chunks, and to fully validate it we would need to check if CAL N indeed has the access list we just generated)

### Constants

```python
CHUNK_GAS_LIMIT = 2**24 # From EIP-7825 (16,777,216 gas per chunk)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This thus requires EIP-7825 and should be added to required EIPs.

EIPS/eip-9999.md Outdated
```python
CHUNK_GAS_LIMIT = 2**24 # From EIP-7825 (16,777,216 gas per chunk)
MAX_CHUNKS_PER_BLOCK = 2**8 # Maximum chunks in a block (256 chunks, allowing 4G gas blocks)
MAX_TRANSACTIONS_PER_CHUNK = 2**10 # Maximum number of transactions in a chunk (1,024 tx per chunk)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine (for now) but if we lower intrinsic tx gas limit below CHUNK_GAS_LIMIT // MAX_TRANSACTIONS_PER_CHUNK then the requirement that the txs per chunk are equal to or lower than the maximum will not hold. E.g. if https://eips.ethereum.org/EIPS/eip-2780 ships.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. I'm just going to increase it to 2**16.

pre_chunk_gas_used: uint64 # Gas consumed in block before the chunk
pre_chunk_blob_gas_used: uint64 # Blob gas consumed in block before the chunk
txs_root: Root # Merkle root of transactions
gas_used: uint64 # Gas consumed in chunk
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If CHUNK_GAS_LIMIT is 2 ** 24 then uint64 is quite large. But for size it does not matter as we strip the topmost zeros. Is uint64 choosen to be forward compatible?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's done as uint64 mostly for consistency reasons with other gas fields. I can change it to uint32 if that is desired in this case. WDYT?

Copy link
Member

@jochem-brouwer jochem-brouwer Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm actually not sure why I raised this point, I think it was because some values were/are rather strict (uint16s) and this one seemed more relaxed. If we RLP encode it we will just strip away any higher 0s so for bandwidth it does not matter. For forward compatibility I think we should just encode it as uint64, so clients also implement it as is. (So no need to change, this is great! 😄 👍 )

class ExecutionChunk(Container):
chunk_header: ExecutionChunkHeader
transactions: List[Transaction, MAX_TRANSACTIONS_PER_CHUNK]
withdrawals: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD] # Only in last chunk
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs: withdrawals, so also requires https://eips.ethereum.org/EIPS/eip-4895

EIPS/eip-9999.md Outdated
if last_chunk_index not in chunk_headers:
# The chunk with last_chunk_index was not validated
return
if not chunk_headers[last_chunk_index].is_last:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need is_last? Can't we see from gas_used + previous gas used to see this is now the block gas used? So this should be the last chunk?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think you are correct and we don't need is_last. I will remove it.

Initial design assumed that we start broadcasting chunks before we finalize the block. In that design, chunks also needed to contain is_last in order to be validated separately.

EIPS/eip-9999.md Outdated
### Choice of Parameters

- **`CHUNK_GAS_LIMIT = 2**24`** (16.7M gas): Balances parallelization benefits with chunk overhead. Large enough for complex transactions, small enough for bounded proving
- **`MAX_CHUNKS_PER_BLOCK = 256`**: Supports up to 268 Mgas blocks
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- **`MAX_CHUNKS_PER_BLOCK = 256`**: Supports up to 268 Mgas blocks
- **`MAX_CHUNKS_PER_BLOCK = 256`**: Supports up to 4294 Mgas blocks

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

256 * 2 ** 24 = 4294967296 (4.294G)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I mentioned in the other comment, it's very unlikely that 4 Ggas block can be split into 256 chunks (chunks wouldn't be full).

Because of the rules for chunk creation, we know that on average, each chunk has to be at least 50% full. This means that we support blocks up to 2 Ggas.

I fixed the value.

EIPS/eip-9999.md Outdated

- **`CHUNK_GAS_LIMIT = 2**24`** (16.7M gas): Balances parallelization benefits with chunk overhead. Large enough for complex transactions, small enough for bounded proving
- **`MAX_CHUNKS_PER_BLOCK = 256`**: Supports up to 268 Mgas blocks
- **`MAX_TRANSACTIONS_PER_CHUNK = 2**10`** (1,024): Smallest power of two that is higher than the number of transactions in a chunk full of basic ETH transfers (21,000 gas per tx)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think to take out any tricks which could be played with refunds, then https://eips.ethereum.org/EIPS/eip-7778 should also be required by this EIP.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I increased the number of transactions per chunk. With higher value, I don't think EIP-7778 is needed.

- **Storage**: Orphan chunks stored temporarily with (slot, proposer, index) identification
- **Validation**: Confirmed against commitments when beacon block arrives
- **Limits**: Bounded orphan storage prevents DoS attacks

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is also the "attack" to stream chunks + CALs in the wrong order, i.e. start with the last one and then stream to the start. Then it is not possible to start executing before the final chunk/CAL has been received.

EIPS/eip-9999.md Outdated

This EIP introduces breaking changes to:

1. **Block Structure**: Replaces monolithic execution payload with chunked structure
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, it does change the execution payload but not the "block structure" (I'd call block header the block structure?)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the wording.

@morph-dev
Copy link

I get the general idea that CALs are sent/received faster than the chunks, but I think for the complete picture we should take into account that sometimes there might be "gaps". So in order to build the prestate for some chunk N, we need a combination of either the previous chunks, and for the missing chunks we would need the CALs for the chunks missing. With that data we can start executing chunk N. If any of the previous data is invalid, this would invalidate the block. But this would be realized once we have the CAL + chunk and the relevant previous data so we can execute it and verify the data. If that is incorrect, we can invalidate it.

I'm interested to see how this would work in practice, for instance when some chunks/CALs are not received. Might also take a look if we can somehow compress the data of a chunk/CAL such that it fits in a single UDP/TCP packet.

I mentioned in one of the comments, but I think it's important to discuss it here as well.

What you are suggesting is something that we can technically do, but I don't think that we should beacuse:

  • CL keeps track of received chunks and CALs, and tells EL when to validate and what:
    • This is aligned with current design where CL drives interaction between two layers
    • Supporting cases when some CALs are missing would significantly complicated the engine API and their interaction
  • If only CAL_N is not available (i.e. we have chunk N + CALs 1..N-1), we can't execute chunk N in parallel
    • It might be worth just waiting for CAL_N to arrive, as execution with it should be significantly faster
    • It's not just about parallel tx execution. With CAL, we can also fetch state upfront

To mitigate this, all chunks and CALs should be propagated and on time. Otherwise validators should vote that block is not timely.

Yes, we will need to do extensive testing and benchmarking to figure out if this is going to be an issue on the real network.

Ultimately, I would prefer to go with the simpler design now and improve on it in the future if there is need.

Copy link
Member

@jochem-brouwer jochem-brouwer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes look good! 😄 👍

I am still confused why we need CAL N in order to parallel execute chunk N, we would only need it to fully validate it. I think I'm missing a key point here 🤔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c-new Creates a brand new proposal e-consensus Waiting on editor consensus e-review Waiting on editor to review s-draft This EIP is a Draft t-core

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants