Skip to content

feat: Gloas fork choice with payload status - #90

Merged
Savid merged 4 commits into
masterfrom
feat/gloas-fork-choice
Oct 8, 2026
Merged

Savid merged 4 commits into
masterfrom
feat/gloas-fork-choice

Conversation

@Savid

@Savid Savid commented Oct 8, 2026

Copy link
Copy Markdown
Member

Gloas fork choice in forky: payload-status-aware nodes from beacon nodes and Xatu, shown in the graph.

Changes

  • Fork choice with payload status. Forky's own fork choice type (internal/types) replaces go-eth2-client's v1 type in frames. Post-Gloas a block can have a pending, an empty and a full node, each with its parent payload status, checkpoint epochs and PTC counts. The store's extra_data is kept.
  • Beacon node source fetches GET /eth/v2/debug/fork_choice via go-eth2-client v0.1.10, which decodes it strictly to ethereum/beacon-APIs#615 as merged. Nodes without the endpoint, or with a non-spec response (ErrInvalidResponse, i.e. every client today), use v1 for an hour before v2 is retried; other failures fall back for that fetch only.
  • Xatu source decodes events with DiscardUnknown, so new Xatu fields don't drop batches. Events with payload statuses (from sentries running feat(sentry): fetch Gloas-aware v2 debug fork choice xatu#900) keep their empty and full nodes; events without (every sentry today) are normalised as v1.
  • Normalisation. v1 dumps that return several nodes per Gloas block are collapsed to one per block. v2 nodes have empty/full parents mapped back to the parent block's root, and any values a node lacks are filled in from its extra_data or its block's other nodes. Lighthouse's not_yet_revealed validity is shown as optimistic.
  • Web: graph nodes show empty and full payloads, a legend, rich hover cards and a payload "tide" view.
  • go-eth2-client moves from attestantio to ethpandaops (v0.1.10); xatu bumped to the feat(sentry): fetch Gloas-aware v2 debug fork choice xatu#900 commit.

Compatibility

  • Works with the Xatu currently in production: events from today's sentries (no payload status) were captured live from Sepolia (Teku, Prysm, Nimbus, Lighthouse) and mainnet (Prysm, Lighthouse) and all decode through this branch's path.
  • Frames stored by 1.0.2 still load (TestFrameLegacyJSON). No database migrations.
  • Deploy this before feat(sentry): fetch Gloas-aware v2 debug fork choice xatu#900. Forky 1.0.2 decodes strictly and would drop batches containing #900's new fields.

Verified

  • go test ./internal/... and golangci-lint pass.
  • Live against Sepolia: a Teku beacon node source backs off to v1 (71 nodes, one per block); the same node behind a proxy that reshapes its v2 response to the merged spec goes through v2 (213 nodes: pending/empty/full for 71 blocks, all with payload status, PTC counts and epochs, parent links correct).

Follow-up

Once ethpandaops/xatu#900 is merged and released, bump xatu from the PR commit to the release tag.

🤖 Generated with Claude Code

Savid and others added 4 commits October 7, 2026 13:42
- Fetch GET /eth/v2/debug/fork_choice, falling back to v1 for clients
  without it (retried hourly), via ethpandaops/go-eth2-client#60.
- Normalize v1 and v2 dumps across client differences: collapse
  duplicate v1 nodes per block, fill epochs and PTC counts from extra
  data or sibling nodes, and infer parent payload statuses.
- Accept Xatu fork choice frames through the same normalization.
- Expose payload status, parent payload status, PTC counts and the
  fork choice store's extra data (e.g. unrealized justification) in the
  API.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Group a block's pending, empty and full fork choice nodes into one
  graph node, with the payload's stage and PTC progress drawn inside it
  and edges marking blocks built on an empty parent.
- Animate payload levels between snapshots and on canvas pans for the
  newest nodes.
- Payload stages in the legend, node cards and block summary, and the
  fork choice store's values in the frame summary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Bump xatu to the glamsterdam-sepolia release, whose fork choice proto
  carries the v2 payload fields, dropping the old attestantio-based
  xatu and its dependencies.
- Normalize Xatu fork choices whose nodes all have a payload status as
  v2 dumps rather than collapsing them per block, so frames from Xatu
  match those fetched from the beacon node directly.
- Ignore Xatu event fields forky does not know rather than rejecting
  the event, so a newer Xatu server cannot break ingestion.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
go-eth2-client v0.1.10 decodes GET /eth/v2/debug/fork_choice strictly to
ethereum/beacon-APIs#615 as merged, so every node carries its checkpoints,
PTC counts and parent payload status.

- ForkChoiceFromV2 maps the spec's node checkpoints, Gwei weight and plain
  counts onto forky's nodes, then runs the shared NormalizeV2.
- Xatu events convert straight to forky's nodes, keeping values a sentry
  did not send unset (rather than zero) for NormalizeV2 to fill in. Node
  checkpoints from ethpandaops/xatu#900 are read, falling back to the epoch
  fields older sentries send.
- v1: Lighthouse's not_yet_revealed now decodes as its own validity; it is
  still shown as optimistic, as is the older unknown + extra_data form.
- The beacon node source backs off v2 for an hour only for nodes without the
  endpoint or with a non-spec response (ErrInvalidResponse); other failures
  fall back to v1 for that fetch.
- Empty extra data is normalised to nil, as forky stores it.
- Tests: a spec-shaped fixture derived from the Teku capture; the pre-spec
  client captures now exercise NormalizeV2 on partial nodes.

Bumps go-eth2-client to v0.1.10 and xatu to ethpandaops/xatu#900.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Savid
Savid merged commit 8f5a5be into master Oct 8, 2026
7 checks passed

@redpandabot redpandabot 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.

This PR adds Gloas payload-status-aware fork choice: a forky-owned ForkChoice/ForkChoiceNode type replacing go-eth2-client's v1 type, a v2 beacon-node source with an hour-long v1 back-off for unsupported/non-spec responses, Xatu decoding that preserves payload nodes, normalization across v1/v2/partial clients, and graph/tide UI. I traced the normalization, enum mappings (including xatu's 0=EMPTY/1=FULL/2=PENDING against the pinned commit), API schema, and frame round-trip and found no confirmed blocker or concern.


Reviewed 49 changed file(s) @ 8ec8ec90 — no blocking issues found.
"The bug is always in the last place you look — because you stop looking after you find it."

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