Skip to content

fix(node): verify blob content in beacon fallback before accepting a response - #1031

Merged
curryxbo merged 13 commits into
mainfrom
fix/beacon-fallback-verify-blob-content
Aug 7, 2026
Merged

fix(node): verify blob content in beacon fallback before accepting a response#1031
curryxbo merged 13 commits into
mainfrom
fix/beacon-fallback-verify-blob-content

Conversation

@curryxbo

@curryxbo curryxbo commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Liveness fix for the beacon fallback path: if a beacon returns the correct number of sidecars but the blob content is wrong, the fallback never switched to the next endpoint.

The fallback client accepted any 200 response with enough sidecars. Content verification only happened downstream in fetchRollupDataByTxHash, whose error aborts the poll round — the next round starts from the same primary beacon, so derivation stalls on a bad endpoint forever. Safety was never at risk (downstream verification always rejected bad bytes); the bug is that the node gets stuck instead of rotating, with no metric to alert on.

Changes

  • beacon.go: the fallback client now owns blob verification, in one place. GetVerifiedBlobSidecar(ctx, ref, wantHashes, indexHints) tries each beacon in order and returns the assembled BlobTxSidecar (blobs + commitments, in tx order). Verification is purely hash-based: every hash in wantHashes (the L1 tx's versioned blob hashes) must be matched via the commitment-derived hash and pass a local KZG commitment round-trip (verifyBlob). Any failure — error, missing hash, bad bytes, nil/null sidecar entries — counts as an endpoint failure (beacon_request_failure_total) and rotates to the next beacon.
  • beacon.go scope note: fallback only covers per-endpoint data faults; safety comes from hash verification itself, and EL/CL fork mismatches near the head are addressed by confirmations=finalized, not by trying more beacons.
  • derivation.go: fetchRollupDataByTxHash drops its own match/decode/verify loop and uses the verified result, so verification runs exactly once. wantHashes (security input, from tx.BlobHashes()) is deliberately separated from indexHints (query optimization, needs the full block body): a BlockByNumber failure just drops the hint and fetches every sidecar at the slot, keeping the Get all blobs corresponding to this timestamp when filter failed #745 self-heal — verification strength is identical with or without hints.
  • base_client_test.go: stubs serve real KZG data (zero blob + its actual commitment); added the failure-mode tests below.

New test coverage

  • TestFallbackBeacon_FallsBackOnCorruptBlobContent: 200, right count, right commitment, corrupted blob bytes → must switch (the original scenario).
  • TestFallbackBeacon_FallsBackOnMissingRequestedHash: 200, right count, but none of the sidecars carries the requested hash (e.g. another fork's sidecars at the same slot) → must switch.
  • TestFallbackBeacon_FallsBackOnNullSidecar: JSON null in the sidecar list → verification failure and rotation, not a panic.
  • TestFallbackBeacon_VerifiesWithoutIndexHints / TestFallbackBeacon_FallsBackWithoutIndexHintsOnCorruptContent: the no-hint fetch-all path stays fully verified and still rotates on bad content.

Test plan

  • go test ./derivation/ -count=1 passes (all existing + new tests)
  • go build ./... in node/
  • Devnet/testnet sanity: point a node at a bad primary beacon + healthy fallback and confirm it derives through

Summary by CodeRabbit

  • Bug Fixes
    • Improved blob sidecar retrieval by validating content and matching requested transaction hashes.
    • Added reliable fallback behavior for corrupt, missing, or null sidecar data.
    • Blob retrieval now continues when block index hints or block lookups are unavailable.
    • Verified sidecars are assembled in the requested order before processing.
    • Added clearer failure handling when no retrieval endpoints are available or all configured endpoints fail.
    • Improved cancellation handling during sidecar retrieval.

…response

The fallback beacon client accepted any 200 response carrying enough
sidecars, so a beacon serving the right count with corrupted blob bytes
(or sidecars missing the requested versioned hash) was accepted, failed
verification downstream, and derivation retried the same bad endpoint
forever without ever switching.

Move content authentication into the fallback loop: each candidate
response must contain every requested versioned hash and each matched
blob must pass a local KZG commitment round-trip, otherwise the endpoint
is recorded as failed and the next one is tried. BlockByNumber failures
now abort the attempt instead of degrading to an unfiltered, unverifiable
all-blobs query, so the fallback always has hashes to authenticate
against.

Co-authored-by: Cursor <[email protected]>
@curryxbo
curryxbo requested a review from a team as a code owner August 5, 2026 04:07
@curryxbo
curryxbo requested review from dylanCai9 and removed request for a team August 5, 2026 04:07
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Blob retrieval now passes optional indexed hash hints to verified beacon retrieval. The fallback client validates sidecar content, commitments, sizes, and requested hashes. Invalid responses trigger endpoint fallback. L1 block lookup failures no longer stop sidecar retrieval.

Changes

Blob retrieval validation

Layer / File(s) Summary
Derivation blob fetch integration
node/derivation/derivation.go
Rollup data retrieval builds optional blob hash hints. L1 block lookup failures are non-fatal. Verified sidecars are passed directly to the batch.
Sidecar verification and fallback
node/derivation/beacon.go
GetVerifiedBlobSidecar validates sidecar completeness, encoding, size, commitments, and KZG-derived hashes. It retries configured endpoints and handles missing endpoints and cancellation.
Verification and fallback test coverage
node/derivation/base_client_test.go
Tests cover corrupt content, incorrect commitments, missing hashes, null sidecars, no index hints, cancellation, empty configuration, and all-endpoint failure.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant fetchRollupDataByTxHash
  participant FallbackBeaconClient
  participant blobsFromSidecars
  fetchRollupDataByTxHash->>FallbackBeaconClient: request sidecars with optional hash hints
  FallbackBeaconClient->>blobsFromSidecars: validate beacon sidecars
  blobsFromSidecars-->>FallbackBeaconClient: verified blobs and commitments
  FallbackBeaconClient-->>fetchRollupDataByTxHash: return verified sidecars
Loading

Possibly related PRs

Suggested labels: validator

Suggested reviewers: dylancai9, twcctop

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: verifying blob content before accepting beacon fallback responses.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/beacon-fallback-verify-blob-content

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
node/derivation/base_client_test.go (1)

215-230: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the per-endpoint failure metric.

This test creates *Metrics, but it only checks endpoint hits. Assert one BeaconRequestFailure increment for both endpoints. This protects the requirement that corrupt-content failures count as beacon failures.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@node/derivation/base_client_test.go` around lines 215 - 230, Update
TestFallbackBeacon_AllFailReturnsError to assert that the real Metrics instance
records one BeaconRequestFailure increment for each endpoint after both beacon
attempts fail. Keep the existing error, nil sidecars, and endpoint-hit
assertions, and verify the corrupt-content fallback contributes equally to the
failure metric.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@node/derivation/beacon.go`:
- Around line 293-297: Update verifySidecars to detect and reject nil entries in
the sidecars slice before accessing sidecar.KZGCommitment, returning the
existing verification error path so fallback endpoint handling can continue.
Keep normal non-nil sidecar hash mapping unchanged.

---

Nitpick comments:
In `@node/derivation/base_client_test.go`:
- Around line 215-230: Update TestFallbackBeacon_AllFailReturnsError to assert
that the real Metrics instance records one BeaconRequestFailure increment for
each endpoint after both beacon attempts fail. Keep the existing error, nil
sidecars, and endpoint-hit assertions, and verify the corrupt-content fallback
contributes equally to the failure metric.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a0871d63-e9a9-44b0-ab9c-2e7f3c39bade

📥 Commits

Reviewing files that changed from the base of the PR and between fc629c0 and 1f318c1.

📒 Files selected for processing (3)
  • node/derivation/base_client_test.go
  • node/derivation/beacon.go
  • node/derivation/derivation.go

Comment thread node/derivation/beacon.go

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

Claude Code Review is paused for this repository. To reconnect it, an admin of this repository's GitHub organization (or the account owner, for personal repositories) who can also manage your Claude organization's Code Review settings needs to re-link GitHub in Code Review settings. This is a one-time step.

Tip: disable this comment in your organization's Code Review settings.

corey and others added 4 commits August 5, 2026 15:52
Fold the duplicated match/decode/verify logic into the fallback client:
GetVerifiedBlobs now returns the assembled, content-verified sidecar in
tx order, and fetchRollupDataByTxHash just uses it.

Co-authored-by: Cursor <[email protected]>
…etch-all self-heal

Verification is always against the tx's versioned blob hashes, which need
no block body; blob indices are only a beacon query filter. BlockByNumber
failure now just drops the hint and fetches the whole slot instead of
wedging derivation on a block whose body never loads (#745).

Co-authored-by: Cursor <[email protected]>

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
node/derivation/base_client_test.go (1)

100-118: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for multi-blob ordering.

Every test requests a single hash, so no test proves that blobsFromSidecars returns blobs in wantHashes order. Order is a documented correctness requirement, because batches are decoded by concatenating blob bodies. Add a case with two distinct blobs served in reverse order and assert that the returned Blobs follow the requested order.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@node/derivation/base_client_test.go` around lines 100 - 118, Add a multi-blob
test covering blobsFromSidecars through the existing FallbackBeaconClient test
helpers: serve two distinct sidecars in reverse order, request their hashes in
the intended order, and assert the returned Blobs match that requested order.
Keep the existing single-blob tests unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@node/derivation/beacon.go`:
- Around line 261-266: Update FallbackBeaconClient.GetVerifiedBlobSidecar to
detect an empty c.clients configuration when wantHashes is non-empty and return
an explicit error instead of falling through with a nil error. Preserve the
existing immediate empty-result behavior when wantHashes is empty and the normal
client iteration for configured clients.

---

Nitpick comments:
In `@node/derivation/base_client_test.go`:
- Around line 100-118: Add a multi-blob test covering blobsFromSidecars through
the existing FallbackBeaconClient test helpers: serve two distinct sidecars in
reverse order, request their hashes in the intended order, and assert the
returned Blobs match that requested order. Keep the existing single-blob tests
unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fea5d42e-db53-4fb0-9976-51df6e02bef7

📥 Commits

Reviewing files that changed from the base of the PR and between 6cc8f90 and 019ee64.

📒 Files selected for processing (3)
  • node/derivation/base_client_test.go
  • node/derivation/beacon.go
  • node/derivation/derivation.go

Comment thread node/derivation/beacon.go

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

Claude Code Review is paused for this repository. To reconnect it, an admin of this repository's GitHub organization (or the account owner, for personal repositories) who can also manage your Claude organization's Code Review settings needs to re-link GitHub in Code Review settings. This is a one-time step.

Tip: disable this comment in your organization's Code Review settings.

@curryxbo
curryxbo merged commit d79f4fd into main Aug 7, 2026
13 checks passed
@curryxbo
curryxbo deleted the fix/beacon-fallback-verify-blob-content branch August 7, 2026 06:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants