Skip to content

cl: enforce Fulu data availability across block imports - #23292

Draft
yperbasis wants to merge 15 commits into
mainfrom
yperbasis/fix-fulu-column-availability
Draft

cl: enforce Fulu data availability across block imports#23292
yperbasis wants to merge 15 commits into
mainfrom
yperbasis/fix-fulu-column-availability

Conversation

@yperbasis

@yperbasis yperbasis commented Aug 14, 2026

Copy link
Copy Markdown
Member

Problem

A recent Fulu block with blob commitments could enter fork choice while its required PeerDAS columns were unavailable. A Caplin validator could then attest to or build on a head whose data it could neither sample nor serve.

Summary

  • make PeerDAS availability an OnBlock invariant for Fulu blocks inside the protocol's data-column request window, regardless of the caller
  • acquire required columns before recent Fulu chain-tip and forward-sync imports, including archive-node recovery, while allowing historical sync outside the serving window
  • persist every column from a local Fulu proposal before importing the block
  • accept EL blob responses as availability evidence only for Deneb and Electra, after exact blob and proof shape checks; Fulu requires materialized PeerDAS columns
  • bound and back off unavailable-data retries and deferred column work

Design

For a Fulu block with blob commitments, OnBlock derives the availability requirement from the store slot and the epoch-based MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS window. A caller cannot disable this check for a block whose columns the network is still required to serve. Blocks older than that window remain importable during historical sync because peers are no longer required to retain their columns.

ForwardSync and ChainTipSync acquire columns before entering the locked fork-choice path. A normal node downloads its custody columns; an archive node downloads enough columns to recover the blobs. Both paths derive the fork from the block slot, recheck peerDas.IsDataAvailable, and import only after availability succeeds. Chain-tip availability failures remain retryable and do not enter the round's permanent seen set.

Locally produced Fulu blocks store all configured data columns before OnBlock, so the same invariant also applies to self-produced blocks.

EL blob responses are not sufficient evidence for Fulu because the returned blobs and cell proofs are not converted into stored columns. The Deneb and Electra shortcut requires one complete 131072-byte blob and one 48-byte proof for every commitment. Safe Fulu EL materialization is tracked in erigontech/security#80.

Resource bounds

  • gossip jobs preserve their retry budget across re-delivery and make at most four queued DA attempts, with one-second backoff measured from attempt completion
  • both EIP-4844 and EIP-7594 availability failures use that bound
  • chain-tip acquisition happens before block database writes and EL validation
  • deferred column work stores compact block metadata, is capped at 64 entries, and is pruned after 30 minutes independently of RPC or peer availability
  • column downloads have a one-slot timeout, and a missing PeerDAS RPC cannot panic the worker

Testing

  • affected CL package tests, including configured-fork and deferred-expiry regressions
  • short unit-test suite
  • full mainnet consensus-spec suite, including Fulu and Gloas fork-choice vectors
  • race detector for PeerDAS and block-service retry changes
  • make erigon integration
  • lint across every changed package

Fixes erigontech/security#79.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Prevents Fulu blocks with unavailable PeerDAS columns from entering fork choice.

Changes:

  • Return the data-unavailable error after scheduling deferred recovery.
  • Initialize spectests with the anchor head to exercise synced behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
cl/phase1/forkchoice/on_block.go Rejects blocks until column data is available.
cl/spectest/consensus_tests/fork_choice.go Initializes synced data from the anchor block and state.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@yperbasis yperbasis added this to the 3.7.0 milestone Aug 14, 2026
@yperbasis yperbasis added FUSAKA Caplin Caplin: Consensus Layer, Beacon API labels Aug 14, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

@yperbasis yperbasis changed the title cl/forkchoice: defer Fulu blocks with unavailable columns cl/forkchoice: enforce Fulu data availability before block import Aug 16, 2026
@yperbasis yperbasis changed the title cl/forkchoice: enforce Fulu data availability before block import cl: require Fulu data availability before fork-choice insertion Aug 17, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.

Suppressed comments (2)

cl/phase1/stages/chain_tip_sync.go:271

  • ErrEIP7594ColumnDataNotAvailable is transient, but this error path marks its root as seen on the following line. Every later range response then skips that block even after PeerDAS obtains the columns, and its descendants remain blocked for the rest of this chain-tip invocation. Leave unavailable roots retryable while continuing to suppress retries for non-transient processing errors.
				if err := processBlock(ctx, cfg, cfg.indiciesDB, block, true, true, true); err != nil {
					log.Debug("failed to process chain-tip block", "err", err, "blockSlot", block.Block.Slot)

cl/phase1/network/services/block_service.go:344

  • A newly scheduled job has a zero retryAfter, so this guard allows the first queued data-availability retry on the next 50 ms ticker tick. That means the initial attempt and first retry are not separated by dataAvailabilityRetryInterval, contrary to the one-second retry policy. Initialize the retry deadline when scheduling specifically after ErrEIP7594ColumnDataNotAvailable, while preserving immediate processing for jobs queued for other reasons.
		if now.Before(blockJob.retryAfter) {

@yperbasis yperbasis changed the title cl: require Fulu data availability before fork-choice insertion cl: enforce Fulu data availability on gossip and chain-tip imports Aug 17, 2026
@yperbasis
yperbasis requested a balanced review from Copilot August 17, 2026 10:17
@yperbasis
yperbasis marked this pull request as ready for review August 17, 2026 10:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Suppressed comments (1)

cl/phase1/network/services/block_service.go:345

  • Newly scheduled jobs leave retryAfter at its zero value, so this check permits the first queued retry on the next 50 ms block-service tick. The four queued retries therefore are not all spaced one second apart as intended: only retries after the first queued attempt are delayed. Initialize the DA job's first retry deadline from its scheduling time.
		if now.Before(blockJob.retryAfter) {
			return true

Comment thread cl/phase1/stages/chain_tip_sync.go Outdated
@yperbasis
yperbasis marked this pull request as draft August 17, 2026 12:25
@yperbasis yperbasis changed the title cl: enforce Fulu data availability on gossip and chain-tip imports cl: enforce Fulu data availability across block imports Aug 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Caplin Caplin: Consensus Layer, Beacon API FUSAKA

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants