| name | data-availability-enforcement |
| description | L1 supplement - audits storage / data-availability chains (Arweave, Filecoin, Irys, Crust, Celestia, EigenDA) for unenforced data commitments: producer commits to data inclusion but the validator never verifies the data was actually published / gossiped / sampled. |
Injectable Skill: Data Availability Enforcement
L1 trigger: L1_PATTERN=true AND protocol-type is one of: storage, data_availability, da_chain, da_layer, blob_storage. Detection: recon finds a "publish ledger", "data root", "blob commitment", "DA root", or "ingress proof" structure in the block header AND the protocol's economic model assumes miners store data.
Inject Into: depth-consensus-invariant, depth-state-trace
Language: Go and Rust
Finding prefix: [DA-N]
Status: v0.1 — derived from Run 7 Irys post-mortem
When This Skill Activates
Data availability chains are L1s whose primary function is to commit to and serve user data, not just to settle transactions. Examples: Arweave (permanent storage), Filecoin (negotiated storage), Irys (DA + L2 settlement), Celestia (rollup blob DA), EigenDA. The trust model is fundamentally different from settlement L1s: validators must enforce that committed data is actually available, not just that the commitment is well-formed.
The single most common DA-chain bug class is: the block header carries a commitment to data X, the producer is paid for committing to X, but nothing in the protocol forces the producer to actually publish X. Honest miners assigned to store X cannot, get penalized, and the chain becomes unreliable.
1. Commitment Inventory
Enumerate every block-header field that commits to data the protocol expects to be stored elsewhere:
- "Publish ledger" / "Submit ledger" / "DA root" / "blob commitment" / "data root"
- "Ingress proof root" / "chunk merkle root"
- "Replication committee assignments" / "partition assignments"
For each, identify:
- The exact field path in the block header struct
- The economic action attached to inclusion (reward, partition assignment, slashing trigger)
- The set of actors expected to store the committed data after inclusion
Write the inventory to scratchpad/da_commitment_inventory.md.
2. Availability Check Audit
For EACH commitment in the inventory, the validator must enforce that the data is actually available. Three valid mechanisms:
2a. Sample-and-prove (Celestia / DAS)
- Validator picks K random chunks of the committed data
- Verifies each chunk's Merkle proof against the committed root
- Accepts only if all K chunks return valid proofs within timeout
- Check: random chunk selection (not attacker-influenceable), K large enough for security target, timeout bounded
2b. Full retrieval (Arweave-style)
- Validator downloads the entire committed data from a peer claiming to host it
- Hashes and compares against the commitment
- Check: peer selection is permissionless (not just from producer's peer list), retrieval timeout bounded, fallback to multiple peers
2c. Attestation from N peers (Filecoin-style)
- N independent peers attest they have the data (signed messages)
- Validator collects ≥ threshold attestations before accepting the commitment
- Check: attestation threshold ≥ Byzantine fraction + 1, attesters are sybil-resistant, no producer-attesting
If NONE of these mechanisms exists for a commitment → CRITICAL finding. The producer is paid for a commitment with no enforcement. Honest miners assigned to store the data can be poisoned.
Tag: [DA-COMMITMENT-NO-AVAILABILITY:{ledger-name}]
2d. Merkle Proof Primitive Audit
Whenever the DA flow relies on validate_path, verify_proof, validate_chunk,
or any Merkle-style helper, audit the verifier itself rather than trusting that
the caller used it correctly.
Questions:
- Leaf binding: does the proof chain the claimed leaf all the way to the
committed root, or does it only compare a caller-supplied hash at the end?
- Length bounds: before slicing / advancing the proof buffer, does the
verifier assert enough bytes remain?
- Range binding: if an offset chooses left vs right branch, does the same
offset also prove the queried chunk is in-range for that branch?
- Operator direction: where the proof combines offset and hash checks, are
&& and || used with the intended meaning?
Tag: [DA-MERKLE-PROOF:{function}]
3. Gossip-Path Trace
For each committed data root, trace the gossip handler that is expected to deliver the actual chunks to other peers. The validator side does NOT replace gossip — both must exist:
- Find the producer-side function that broadcasts the data after inclusion (e.g.,
gossip_chunks_for_data_root)
- Verify the function is called UNCONDITIONALLY after the producer's block is accepted, not just on best-effort
- Verify the receive-side handler stores the chunks AND signals success to the validator's availability check
- Common bug: producer broadcasts on its own initiative (no protocol enforcement), but if it doesn't, no consequence — validators never check
Tag: [DA-GOSSIP-NOT-ENFORCED:{handler-path}]
3a. Publish-Ledger Gossip Duty Matrix
For each ledger / blob / chunk class whose availability depends on peer-side
replication, write a row:
| Data class | Publisher | Required recipients | Gossip trigger | Validator check | Penalty on missing gossip | Evidence |
|---|
Required enumeration:
- Identify every publish-ledger or data-ledger field that creates an
obligation for a producer or relay to gossip bytes after block acceptance.
- Locate the exact trigger that sends the bytes. A best-effort background
task is not enforcement unless block validity, reward, or reputation depends
on success.
- Locate the verifier or challenge that detects missing gossip before honest
assigned peers are penalized for not storing unavailable bytes.
- Locate the penalty path for producer/relay non-delivery: rejection, delayed
reward, score decrement, challenge failure, or slashing.
- If any obligation has no verifier or no penalty, emit
[DA-GOSSIP-DUTY-UNENFORCED:{data-class}].
3b. Relay Obligation Enforcement
For each peer that RECEIVES committed data (chunks, blobs, DA attestations) via gossip:
- Trace the receive-side handler: after storing a received chunk, does it re-broadcast to peers whose partition assignment covers that chunk's ledger offset?
- Is the re-broadcast mandatory (protocol-enforced) or best-effort (no penalty for dropping)?
- If a relay node silently drops received chunks, is there any detection mechanism (challenge, attestation timeout, partition-holder complaint)?
- Common bug class (Arweave Wildfire): relay is best-effort; a miner stores data for itself but does not forward to assigned partition holders → those holders have empty partitions and cannot produce valid storage proofs → the network's DA guarantee degrades silently.
Tag: [DA-RELAY-NOT-ENFORCED:{handler-path}]
4. Recall Range / Proof-of-Access
Many DA chains use a "challenge" mechanism: every block, the protocol requires the miner to prove they have a randomly-selected chunk of their assigned partition. This is the storage-proof economic primitive.
Check:
- Recall range RNG: is the seed derived from a high-entropy source the miner cannot grind? (See
consensus-safety-invariants Section 1 + efficient-sampling patterns)
- The chunk hash chain: is each chunk in the recall range bound to the previous via a hash function the miner cannot pre-compute?
- The proof-of-access verifier: does it actually check the chunk content, or just the chunk index?
- Selective storage attack: if the recall range is predictable, a miner storing only N% of the partition can answer N% of challenges. Storage cost / reward economic model breaks.
Tag: [DA-PoA-PREDICTABLE:{rng-source}], [DA-PoA-VERIFIER-WEAK:{check}]
5. Slashing for Non-Storage
Once availability is enforced, the next question is: what happens when a miner fails the proof? Most DA chains plan slashing but ship without it.
Check:
- Is there a
Slash / RemoveStake / Penalize code path in the validator?
- Is it called from the proof-of-access verifier on failure?
- If slashing is "planned but not implemented", document as a Critical pre-mainnet finding — the economic model assumes slashing but the code does not enforce it
Tag: [DA-SLASHING-MISSING]
6. Output schema
- Layer: consensus / storage
- Bug class: da-availability / da-gossip-enforcement / da-poa-weakness / da-slashing-missing
- Preferred evidence tags:
[CONFORMANCE-PASS] (run a synthetic non-publishing producer and verify validators reject) > [LSP-TRACE] > [CODE-TRACE]
- Severity baseline: Critical (DA chain whose entire economic model depends on enforcement)
7. Known bug classes (research basis)
-
Arweave early Wildfire reputation gaming — miners selectively responded to recall challenges they had pre-stored, refusing others. Reputation system was patched repeatedly. The lesson: any storage-proof verifier whose challenge can be predicted by the prover is exploitable.
-
Filecoin Window PoSt incentive misalignment — miners can defer submission of storage proofs to earn revenue without losing stake until the window closes. Filecoin patched the timing model multiple times.
-
Celestia / EigenDA DAS sample size selection — academic papers show that DA sampling parameters (K samples, M-of-N threshold) must be calibrated against the target Byzantine fraction; under-sampled DAS gives a false sense of availability.
-
Irys Run 7 post-mortem (this skill's origin) — Publish ledger commitments had NO validator-side enforcement that gossiped chunks reached the assigned partition holders. A producer could include a data_root and never broadcast; assigned miners received partitions they could not reconstruct.
Cross-references
- Related:
consensus-safety-invariants (boundary conditions on recall range RNG), p2p-dos-and-eclipse (gossip-path enforcement)
- Consumed by:
depth-consensus-invariant, depth-state-trace
- Severity guide:
docs/l1-mode/severity-matrix.md