erigontech / erigontech/erigon
cl/das: Gloas blob recovery stores recovered columns under slot 0
- Dominant language
- Go
- Stars
- 3.6k
- Forks
- 1.5k
- Avg merge
- 1d 16h
- Merged PRs (30d)
- 455
Description
## Problem
On the Gloas path, blob recovery reconstructs the custody columns it is missing and writes them under **slot 0** instead of the block's real slot, making them invisible to every later lookup.
In `cl/das/peer_das.go` (the `!exist` branch of the recovery loop, around lines 528-543):
```go
sidecar := cltypes.NewDataColumnSidecar() // version stays at the zero value: phase0
sidecar.Index = columnIndex
sidecar.SignedBlockHeader = anyColumnSidecar.SignedBlockHeader // nil under the Gloas schema
if !isGloas {
sidecar.KzgCommitmentsInclusionProof = anyColumnSidecar.KzgCommitmentsInclusionProof
sidecar.KzgCommitments = anyColumnSidecar.KzgCommitments
}
```
`NewDataColumnSidecar()` leaves the internal `version` at its zero value, `phase0`, rather than the fork the sidecar actually belongs to. On the Gloas path `anyColumnSidecar.SignedBlockHeader` is nil, and `Slot` / `BeaconBlockRoot` are never assigned.
`WriteColumnSidecars` (`cl/persistence/blob_storage/data_column_db.go:69-89`) then derives the slot as: Gloas version → `columnData.Slot`; else non-nil header → header slot; else → `columnData.Slot`. With version `phase0` and a nil header it falls through to the last branch, so `slot = 0`.
## Effect
- The reconstructed columns land at `0/_` and are invisible to `GetSavedColumnIndex(realSlot, blockRoot)`, so the node believes it still lacks its custody columns.
- They are also encoded with the pre-Gloas schema, because `getSchemaForVersion` re-initializes an empty `SignedBlockHeader` and commitments for a sub-Gloas version.
- `Prune` is keyed on slot, so these files sit in the slot-0 subdirectory rather than with the block they belong to.
Confirmed with a throwaway test: `NewDataColumnSidecar().Version()` reports `phase0`, and the write lands at `0/0x0100…00_7`.
## Scope
Requires `GloasForkEpoch` to be scheduled, so devnets rather than mainnet today. It is a mis-filing rather than a crash, and it is not peer-controlled — the input is locally reconstructed data.
## Suggestion
Construct the sidecar with the fork it belongs to and populate the fields that schema requires, e.g. `cltypes.NewDataColumnSidecarWithVersion(anyColumnSidecar.Version())`, then set `Slot` and `BeaconBlockRoot` on the Gloas path. More generally, `NewDataColumnSidecar()` defaulting to `phase0` is an easy footgun for any caller that later relies on `Version()`.
Contributor guide
Assessment
This issue has not been assessed yet.