Note: this document is a result of a collaborative effort. It would not had been possible without the contribution and ideas of @MarcoPolo , @leobago, @kamilsa , @fradamt , and the whole EF networking team.
TL;DR
-
RowDAS (EIP-8371) fixes the part of PeerDAS that does not scale: reconstruction is not distributed — it falls on high-custody nodes (any node holding at least 64 of the 128 columns), and each of them repeats the full work alone. The ideas to overcome this are rooted in FullDAS and it is now Proposed for Inclusion (PFI) in Hegotá, the fork after Glamsterdam.
-
RowDAS cuts reconstruction work for high-custody nodes by up to a factor of 128: at 128 blobs, 162 ms of CPU core time per slot instead of up to 20 s. These are CPU-load figures, not latency; the recoveries parallelize.
-
Under RowDAS, reconstruction no longer needs supernodes, not even nodes that hold 64 columns: ordinary nodes can pool their custody and reconstruct collectively, wherever their coverage clears the erasure-coding threshold.
-
The CPU relief does not depend on the new topics: a reduced variant (keep only the row assignment, with today’s PeerDAS behaviour as backstop) captures most of the reduction and can be used for incremental adoption; the row topics then buy what only they can: supernodeless recovery and per-blob access (details at the end).
-
The column path is not measurably affected: paired runs across six network shapes put column completion within noise of baseline (−30 to +6 ms).
-
Implemented and measured: a working Prysm prototype is published below, measured under synthetic load at up to 1000 nodes with correspondingly reduced row-subnet counts (not yet mainnet-scale).
The problem
Under PeerDAS (EIP-7594), reconstruction duty attaches to custody: any node holding at least half the columns (in practice supernodes backed by high stake and L2 nodes requiring blobs) can reconstruct, and is required to do so. Every such node reconstructs every missing blob: the same work, repeated per node. At a measured 162 ms per blob recovery (real KZG, Ryzen 9 8945HS) and 32 blobs, that is 5.2 s of CPU core time per reconstructor per slot when every blob needs recovery, and it scales linearly with the blob count. We have an estimated 2000+ such high-custody nodes in the network at the time of writing.
Suppression by waiting. As the PeerDAS consensus spec suggests, clients can (and some do) blunt the repetition with a randomised delay — wake within 2 s of the slot start (Prysm’s window), re-check custody, stand down if it completed meanwhile — but the saving is capped by how far propagation gets inside that window: measured at 1000 nodes it shaves 19–43% off the naive bound (Figure 3), and waiting longer buys more suppression only by delaying recovery.
Load, not latency. Recoveries are independent per blob and Prysm runs them in parallel, so 5.2 s is about 0.43 cores occupied for the whole 12 s slot, or a ~650 ms burst on eight cores. It matters because it arrives as a burst competing with everything else in the slot, and because it sets a floor under what hardware can follow the chain in these roles (high-custody or L2). At 128 blobs, without blobs available locally from the execution layer via getBlobs, it would be 1.7 cores sustained.
RowDAS (EIP-8371) removes most of this load by adding a second axis: 128 data_row_{subnet_id} topics, each carrying the cells of one blob across columns. A node’s row subnet comes from its node ID, blobs map to subnets by a per-slot permutation, and a subnet’s members carry the reconstruction duty for the rows it carries — high-custody members first, ordinary members as a delayed, pooled fallback. Duty moves from “everything you can” to “your own subnet’s rows”, and reconstructed cells are announced back into the column subnets that lost them, to be served on request.
Figure 1: the cell matrix — column subnets vertical (custody, the authoritative path), one row subnet horizontal, cross-fill where they meet.
What it achieves
Reconstruction load falls by two orders of magnitude.
| blobs | PeerDAS, per reconstructor per slot | RowDAS, per reconstructor per slot | network-wide, 2000 reconstructors |
|---|---|---|---|
| 32 | 5.18 s of CPU core time | 40 ms | 10 400 s → 81 s |
| 128 | 20.7 s (1.7 cores sustained) | 162 ms — one blob | 41 400 s → 324 s |
Reading the table. At 128 blobs the statement needs no arithmetic: a reconstructor recovers exactly one blob per slot instead of every missing one. At 32 blobs the 40 ms is an average across reconstructors — only a quarter of the 128 subnets carry a blob — and a node that does have duty spends the full 162 ms on it. The network-wide column projects the same arithmetic over the estimated 2000 high-custody nodes, in the everything-missing case; the RowDAS figures are the mapped first-phase duty, with the fallback phases adding work only when rows actually go missing. The PeerDAS columns are the arithmetic bound; the randomised-delay suppression above would shave a measured 19–43% off them without changing their shape — the residual still scales with the number of reconstructors.
Rows do not delay the column path. This is the EIP’s safety condition: row dissemination is an optimisation, and the column path stays authoritative. It is also the natural failure mode: rows and columns carry the same cells over the same links, so a naive row axis would compete with the column path. The design answer is priority and timing: row announcements are batched, and row requests wait about as long as the column path’s median completion, a delay the EIP already permits. By the time the row axis asks for cells, most have already arrived by column, and rows only backfill what is missing (Figure 2). Measured across six network shapes (node count × custody × blob count, 24 to 1000 nodes) in paired runs — identical topology and randomness, row axis on against off — with up to 25 seed pairs per shape: median column completion moves by −30 to +6 ms, against completion times of seconds.
Figure 2: the deferral orders the two axes in time — announcements go early in batches, requests wait out the column path’s median, and rows backfill only what is missing.
Recovery works down to the erasure-coding threshold. Any 64 of the 128 columns can reconstruct a blob, but under PeerDAS that margin is only usable by a node that holds 64 columns. RowDAS lets nodes pool their custody over a row topic and use the margin collectively.
Recovery without supernodes is a powerful property, but some limits are worth stating:
-
RowDAS does not lower the coding threshold, it makes it usable;
-
at the threshold exactly, usability depends on subnet membership: at 50 members a subnet’s collective holdings usually miss a column or two; at 250 they reliably do not.
Recovery stays cheap and fast even under attack. The stress test behind Figure 3: the proposer withholds columns at the source — up to 64, the coding limit — and the network must recover them. The three panels answer the three questions that matter: how much CPU recovery burns, when the withheld data is actually back, and what the wire carries. The PeerDAS reference is deliberately not a strawman: it models what deployed clients do — every supernode arms once it holds 64 columns, wakes after a random delay, re-checks whether the rest arrived while it slept, and reconstructs only if not — so its randomised delays get full credit for the cost they do save.
The core difference the left panel shows is speculation versus responsibility. PeerDAS reconstruction is speculative: a waking supernode cannot know the slot is fine until propagation finishes, so even a slot with nothing withheld costs about three quarters of the tier’s full effort. Under RowDAS every live row has designated reconstructors, and everyone else can see who they are and stand down. With the supernode fallback at 1750 ms, a stressed slot costs the four designated reconstructions — one per blob — and nothing else: 0.65 s of CPU network-wide with up to a third of the columns withheld. The saving is not bought with time (middle panel): the withheld data is back 1–1.7 s into the slot.
Traffic is the right panel, and the row axis adds almost none. The rule: a node never requests row cells it cannot show it needs — the request fires only if its own custody is still incomplete, no designated reconstructor is on the job, and nobody has claimed the whole row; if a claimed reconstruction then fails to appear, the request re-arms and pooled recovery takes over. The result is that the RowDAS and PeerDAS wire totals nearly coincide: ~10 MB of signaling per slot on top of column traffic that matches PeerDAS’s own.
Figure 3: recovery under stress at 1000 nodes and 4 blobs — reconstruction CPU (left), when the withheld data is back (middle), and bytes on the wire (right), against the number of columns the proposer withholds. One blob recovery is priced at the measured 162 ms; 5 seeds per point, faint dots are individual runs, lines join medians. The PeerDAS lines are the deployed-client model (random delay, fire-time re-check).
Reading the figure. Two modelling choices matter. First, membership: these cells place every node on a blob-carrying row subnet (~250 members each), which isolates the timing race and the stand-downs from the luck of subnet assignment. At mainnet ratios — ~78 members and ~15 high-custody nodes per subnet — a live subnet is all but guaranteed a reconstructor, and this is measured, not assumed: re-running these cells under the EIP’s real hash-based allocation at that ratio reproduces the results (5 seeds). When a row’s subnet nevertheless lacks one, the supernode fallback picks the row up: extra reconstruction work, never lost data — a cost that matters only on small networks, and a derivable stand-in schedule removes most of it there. Second, blob count: 4 blobs is the getBlobs-era stress case. A blob already in every node’s mempool never enters recovery, so a few withheld or private blobs are the realistic hard core of a much larger slot.
RowDAS’s own failure mode is bounded. If a row subnet is not working, measured column completion stays on the no-row-axis baseline rather than getting worse: a suppressed row subnet degrades to the status quo, costing its benefit and nothing more.
The networking layer, upgraded
The results above did not come from RowDAS logic alone: RowDAS rides gossipsub’s partial-messages extension, in which a peer announces which cells it holds instead of eagerly sending payloads, and its peers request only what they lack — the announcements and requests of the table below. The extension and its Go implementation (as they stand today) are too chatty and too eager for a second axis to be safe. Getting to the presented performance took six mechanisms at the protocol substrate level — each small, none row-specific, but these are the details that make a p2p protocol performant and well-behaved. Most are also not new ideas: the links in the table below trace each back to the earlier post where it was proposed. What is new is that their details are now mostly worked out, implemented, and measured together.
| mechanism | what it does | what it buys |
|---|---|---|
| Announce policy (the aggregation idea: PPPT § IHAVE traffic overhead) |
batches per-peer announcements in a 200 ms window with an immediate leading edge; bounded retries; caps on tracked state | row-topic messages reduced more than 10x at the hardest shape measured (128 nodes / custody 8 / 8 blobs: 570k → 45k) |
| Request deferral | holds row requests for about the column path’s median completion; a request is dispatched the moment it is selected | the safety headline: column completion within noise (−30 to +6 ms); rows ship almost nothing where columns win the race |
| Phase-aware forwarding (roots: PPPT; the peer-knowledge form: partial messages § Publishing strategy; the suppression precedent: gossipsub v1.2 IDONTWANT) |
pushes cell payloads only to peers not known to hold them; announces to the rest (“phase” here is a message’s push/pull lifecycle, unrelated to the reconstruction phases below) | duplicate payload delivery bounded by design: each peer receives the bytes once, metadata covers the rest |
| Request discipline (IWANT) (roots: common sense, but surprisingly not present in some implementations) |
one request per missing cell at a time, held as a claim with a deadline: a lapsed claim re-fires to another announcer with backoff, and a repeatedly silent peer is not asked again | no redundant pulls — a cell announced by k peers is fetched once, not k times — and no stall either: a held-back request always owns a wake-up, with the column path as the authoritative fallback |
| Partial interest without subscribing (roots: FullDAS § Interest: Custody vs. Sample; the extension: partial messages) |
signals partial-message interest on a joined but unsubscribed topic | makes the EIP’s optional pull implementable at all: a reconstructor can collect cells from column subnets it does not custody |
| Advertisement-only cross-forwarding (roots: FullDAS § in-network repair, as detailed in FullDASv2 § getBlobs-based column reconstruction) |
announces recovered cells into the column subnets that lost them; serves them on request | the same recovery boundary as eager pushing — everything rescued while 64 column subnets survive — at a fraction of the traffic |
Note that little here is specific to partial messages: an announce/request split with batching, phase-aware forwarding and disciplined pulls apply to any DAS or diffusion protocol that separates metadata from payload — which is why most techniques trace back to FullDAS-era proposals rather than to RowDAS itself. The lineage is of course older, with many of these techniques present in p2p protocols well before Bitcoin was invented.
The prototype
Everything above is published:
-
Prysm:
cskiraly/prysm, branchrowdas— an 11-commit series on currentdevelop, behind a--row-dasflag (the numbers in this post: commit2cec8fe20a). -
gossipsub:
cskiraly/go-libp2p-pubsub, branchrowdas-partial-messages— the mechanisms of the table above, as ten commits on top of v0.17.0 (commit115d7f6949). -
Measurement framework:
cskiraly/eth-networking-lab— N in-process nodes over real gossipsub with real KZG, every byte on the wire accounted for, and a deterministic scheduler for the count-based results (commit2167ef4).
Most of the substrate used by RowDAS already existed — partial columns (EIP-8136) ship in Prysm today, and the gossipsub partial-messages extension is upstream — and RowDAS is the transpose of the partial-column machinery, so KZG verification, the header container and the per-peer publish state machine are shared rather than duplicated. What is new:
-
One broadcaster serving both axes, with a cross-fill bridge. A cell verified on either axis is offered to the other, so a cell learned on a row topic fills a custodied column with no column-topic traffic at all.
-
Three-phase reconstruction duties — own row, any row, opportunistic — bounded, timed from block-root acquisition, and cancelled when the row is served elsewhere.
-
Cross-forwarding in both directions — the gated advertisements of the table above, plus the EIP’s optional pull.
-
ROW_SUBNET_COUNTas a config value, because with 128 subnets and one per node nothing below ~1000 nodes exercises the feature to its full extent. Any devnet needs this.
The fallback plan: duty-sharded PeerDAS
We’ve discussed and implemented distributed reconstruction, more precisely two dimensions of it:
-
distributing the reconstruction load between the high-custody nodes, so no node repeats every recovery;
-
allowing distributed reconstruction even when there are no high-custody nodes, by pooling custody along the row axis.
The EIP has both goals, but there is a reduced variant that keeps only the first — and it needs almost none of the machinery. We still assign rows and we still phase the duties, but we do not do row topics: a high-custody node holds at least 64 columns, hence at least 64 cells of every row, so it can reconstruct its assigned row from its own custody with no row channel at all. Recovered cells go back into the column subnets at cell granularity over the same partial-message machinery — multi-source consolidation, which is exactly what the request discipline of the table above was built for. Behind the assigned duty, today’s PeerDAS behaviour remains as the safety floor: any high-custody node reconstructs whatever is still missing after a backstop delay. The variant therefore degrades to exactly the status quo and never below it — as a spec change it is a paragraph on top of PeerDAS, not a new subsystem.
Measured in the same stress harness as Figure 3 (1000 nodes, the EIP’s hash assignment at mainnet supernode-to-row ratios, the deployed-client model on both sides):
-
network-wide reconstruction CPU falls from 48.6 s to 2.75 s at zero loss with 10% supernodes (91 s → 6.6 s at 20%) — a factor of 11–18× across the grid, more than the ÷13 duty-division arithmetic alone because stand-downs still help — and stays in the 2.8–3.4 s band (10%; 4.7–6.6 s at 20%) with up to half the columns withheld, where the deployed-client model pays 34–99 s;
-
the withheld data is back 1.1–1.8 s into the slot, riding the same client delay window as deployed PeerDAS — and the tier backstop behind the assigned duty never fired at all;
-
the row axis’s ~10 MB of signaling drops to zero — there are no row topics — and column-axis bytes stay at PeerDAS parity (381–394 MB against 372–414 MB per slot at 10%).
What the fallback gives up is everything only the row topics provide. Reconstruction still needs high-custody nodes — goal 2 dies: with no row topic to pool custody over, nodes that individually hold fewer than 64 columns can no longer reconstruct collectively, and recovery is anchored to the supernode tier exactly as it is today. A column subnet that lost its data can no longer be rescued through the row axis. There is no per-blob channel for an L2 node to follow. And suppression among the ~8–16 nodes sharing an assigned row (at 10–20% supernode share) is statistical rather than ordered, so a stressed row costs several recoveries where RowDAS pays its single designated one — 3.4 s network-wide against RowDAS’s 0.65 s at the same depth. That is the honest role of this variant: the CPU claim survives even if the row layer never ships, and the row layer justifies itself on what only it provides.
Status
-
Implementing the EIP produced several concrete spec improvements, among them: the eager cross-forwarding push made advertisement-only; “MAY delay row requests” strengthened to a SHOULD; an explicit activation condition for opportunistic reconstruction that a custody-minimum node can evaluate; etc.
-
Everything above is a simulated in-process network of 16–1000 nodes with real cryptography. No devnet yet, and
ROW_SUBNET_COUNT = 128itself is an extrapolation from smaller counts. Larger simulations are in the works, then Kurtosis and devnet runs. -
Compatibility with a sparse blobpool is a design goal, not yet a measured result: cell-level messages are exactly the regime FullDASv2’s getBlobs-based reconstruction and its private-blobs discussion assume, but a dedicated analysis, and a section in the EIP, are still owed.
-
Introducing the row channel enables nodes to collect specific blobs, an interesting opportunity for L2 nodes. The exploration of this aspect is still to do
-
The duty-sharded fallback gives the proposal a staged adoption path: the reconstruction-load fix could ship first as a small amendment to PeerDAS’s reconstruction rules — no new topics, no new containers — with the row layer as a second stage justified by what only it provides. Folding this split explicitly into the EIP text is now on the list.
-
The next steps are evaluating under mainnet scale and in real networks. A second implementation has some head start: several of the construct existed in my Nim FullDAS prototype from two years ago.
Discussion of the EIP text itself lives on ethereum-magicians.


