Partial columns let two peers exchange only the cells that the receiver lacks
instead of sending a full DataColumnSidecar. They reuse the existing data
column topic, mesh, fanout set, and peer score.
This surface is not part of the Fusaka activation claim. The pinned consensus spec defines it on top of Fulu types, but it was added after the Fulu specification and the related EIP remains Draft. The pinned libp2p extension is a Working Draft. The checked-in sources do not prove a mainnet deployment.
Wire containers
The pinned consensus source has separate Fulu and Gloas application profiles. They use the same partial-message extension but not the same SSZ containers or validation context.
Fulu profile
The Fulu PartialDataColumnSidecar contains:
cells_present_bitmap: BitList[MAX_BLOB_COMMITMENTS_PER_BLOCK]
partial_column: List[Cell, MAX_BLOB_COMMITMENTS_PER_BLOCK]
kzg_proofs: List[KZGProof, MAX_BLOB_COMMITMENTS_PER_BLOCK]
header: List[PartialDataColumnHeader, 1]
The set bits identify which blob rows are present. partial_column and
kzg_proofs contain items only for those set bits, in increasing blob-index
order. The topic subnet supplies the column index. header is optional and has
zero or one item.
PartialDataColumnHeader is the header common to all columns for a block. It
contains:
kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK]
signed_block_header: SignedBeaconBlockHeader
kzg_commitments_inclusion_proof: Vector[Bytes32, KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH]
It lets a receiver identify the blob rows and verify later cells. A peer sends it only on an eager push, because a receiver can only make a cell request after it has this header. Once validated for a block, it can be reused across every column subnet.
The Fulu PartialDataColumnGroupID contains the beacon block root.
Gloas profile
The Gloas PartialDataColumnSidecar removes header and uses progressive
collections:
cells_present_bitmap: ProgressiveBitList
partial_column: ProgressiveList[Cell]
kzg_proofs: ProgressiveList[KZGProof]
MAX_PARTIAL_DATA_COLUMN_SIDECAR_SIZE is 8,585,741 bytes. The Gloas
PartialDataColumnGroupID contains both beacon_block_root and slot. The
selected execution payload bid in that block supplies the KZG commitments.
For both profiles, the group ID wire form is version byte 0x00 followed by
the SSZ-encoded group ID. A receiver ignores an unknown version.
Shared parts metadata
Peers describe their local state with:
available: BitList[MAX_BLOB_COMMITMENTS_PER_BLOCK]
requests: BitList[MAX_BLOB_COMMITMENTS_PER_BLOCK]
For each blob index:
| Available | Requested | Meaning |
|---|---|---|
| 0 | 0 | The peer has no cell and does not request it. |
| 0 | 1 | The peer lacks the cell and requests it. |
| 1 | 0 or 1 | The peer has the cell and can provide it. |
Having a cell without its proof does not count as available. New metadata replaces the prior metadata for the same peer and message group.
Two-phase validation
Fulu checks
Header validation and cell validation can happen in different Fulu messages and on different subnets. A receiver rejects the message when:
- both the header and cells are absent;
- the count of cells differs from the number of set bitmap bits; or
- the proof count differs from the number of set bitmap bits.
For a message with a header, the receiver rejects:
- a header that differs from an earlier valid header for the same block root;
- a signed header whose hash does not equal the group block root;
- an empty commitment list;
- an out-of-range proposer index or invalid proposer signature;
- a parent that failed validation;
- a slot not later than the parent slot;
- a branch that does not descend from the finalized checkpoint;
- an invalid commitment-list inclusion proof; or
- a proposer index that differs from the expected proposer after the parent state is advanced to the header slot.
The receiver ignores a future header, a header at or before the latest finalized slot, or a header whose parent has not arrived. It may queue the future and missing-parent cases.
The receiver first needs a valid common header for the block. If it has none, it ignores and can queue the cells.
It rejects cells when the bitmap length differs from the header's commitment count. It derives each blob index from a set bit, repeats the topic's column index for each cell, and batch-verifies the cells and proofs against the corresponding commitments.
Gloas checks
A Gloas partial sidecar has no header and must contain at least one cell. The receiver rejects a cell or proof count that differs from the number of set bitmap bits.
It ignores the message while the group ID's block is unknown and may queue at least one sidecar per peer and subnet. After the block arrives, it rejects the message when:
- the block failed consensus validation;
- the group ID slot differs from the block slot;
- the bitmap length differs from the commitment count in the block's selected execution payload bid; or
- batch KZG verification fails for the selected bitmap rows and topic column.
A sender must send correct data even for a cell that the receiver already has. The receiver may compare that data with its local copy as an extra check.
Transfer behavior
The receiver can request cells before a peer announces which cells it has. This registers interest for data that the peer may learn later.
The default is pull-based cell transfer:
- a peer learns and validates the Fulu common header or the Gloas block and selected bid;
- it sends metadata that marks its available and missing rows;
- the other peer sends only requested cells that it has; and
- both peers update their metadata as their local matrix changes.
The application encodes and decodes the metadata and partial sidecar. The gossipsub library must pass these values to the application, including values from non-mesh peers.
Eager push
Pulling can add one round trip on the first dissemination hop. A client may eagerly send cells when it has good reason to think the peer lacks them.
- A proposer should eagerly send the common header.
- The preceding header rule applies only to the Fulu profile. Gloas receivers use the beacon block and selected bid instead.
- A proposer with private blobs should eagerly send cells for those blobs.
- The default should not eagerly send every cell.
- A client should offer an option to push all cells at proposal time.
- Do not send a header to a peer that has already sent data for the same group.
- After it validates the required context, a client should request missing cells. A Fulu client can do this before the full beacon block arrives.
Interaction with normal gossipsub
Partial and full messages share the same topic mesh and fanout peers. A client
that reconstructs a full DataColumnSidecar forwards it to peers that do not
use partial messages. It avoids sending that redundant full sidecar to peers
that requested partial transfer.
Useful novel data earns a first-delivery signal. The client rate-limits that reward so a peer cannot gain more score by splitting one column into many small deliveries. Invalid cell or proof data produces an invalid-delivery signal.
Clients must not prefer extension-capable peers so strongly that they split the mesh. A peer without the extension stays on the normal full-column path.
Unresolved negotiation field
The pinned sources do not agree on the subscription field names:
- the Fulu partial-column profile says a peer sets
SubOpts.partial = true; - the pinned generic libp2p document defines
requestsPartialandsupportsSendingPartial.
The EIP still points to earlier unmerged text. Until the sources converge, this corpus cannot declare one final protobuf contract. Implementers must use the exact revision selected by their deployment and must not infer compatibility from similar field meaning.
Resource limits
- Bound the number of message groups and peer-specific bitmaps kept in memory.
- Reserve processing and storage for mesh peers when non-mesh peers can send partial data.
- Expire state with the block and sidecar acceptance window.
- Ignore unknown group-ID versions.
- Keep the full-column fallback available throughout a mixed deployment.