Skip to content

feat(app): fail fast on lock vs beacon-node fork-schedule mismatch#558

Merged
emlautarom1 merged 3 commits into
mainfrom
worktree-issue-532-fork-schedule-mismatch
Jul 23, 2026
Merged

feat(app): fail fast on lock vs beacon-node fork-schedule mismatch#558
emlautarom1 merged 3 commits into
mainfrom
worktree-issue-532-fork-schedule-mismatch

Conversation

@emlautarom1-agent

@emlautarom1-agent emlautarom1-agent Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Closes #532.

Summary

Fail fast at startup when the beacon node is on a different network than the cluster lock.

Follow-up to #536, which introduced run and explicitly deferred this misconfiguration guard.

Differences from Charon

  1. Single check instead of two. Charon runs the guard inside configureEth2Client, once per independently constructed client (eth2Cl and submissionEth2Cl). Pluto's two clients target the same endpoint, so one check on the primary client covers both.
  2. Unknown fork version falls back to hex rather than erroring. Charon returns a distinct "cannot parse ... fork version" error when a version matches no known network. Pluto instead names the raw 0x… fork version in the mismatch error — more actionable for an operator, and it still fails on a genuine network mismatch.
  3. Empty fork schedule is handled gracefully. Charon indexes schedule[0] unconditionally (a panic risk on an empty schedule); Pluto reports the beacon node network as unknown and still returns the structured mismatch error.

emlautarom1-agent[bot]

This comment was marked as outdated.

@emlautarom1
emlautarom1 marked this pull request as ready for review July 21, 2026 14:15
Copilot AI review requested due to automatic review settings July 21, 2026 14:15

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

This PR adds a startup-time misconfiguration guard to ensure the configured beacon node is on the same network as the cluster lock, failing fast with an operator-facing error when the lock fork version is not present in the beacon node’s fork schedule.

Changes:

  • Add an EthBeaconNodeApiClient helper to fetch and decode fork-schedule current_version entries.
  • Add a startup guard in app to compare the lock’s fork version against the beacon node’s fork schedule and error on mismatch (with best-effort network naming).
  • Add beaconmock-backed tests for the matching and mismatching network paths.

Reviewed changes

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

File Description
crates/eth2api/src/extensions.rs Adds a helper to fetch/decode fork-schedule versions for reuse by higher-level guards.
crates/app/src/node/mod.rs Adds the fail-fast fork-schedule verification at startup, a structured error, and tests.
crates/app/Cargo.toml Adds pluto-eth2util dependency needed for fork-version → network naming.
Cargo.lock Records the updated dependency graph.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/app/src/node/mod.rs
Comment thread crates/eth2api/src/extensions.rs Outdated
@emlautarom1-agent
emlautarom1-agent Bot force-pushed the worktree-issue-532-fork-schedule-mismatch branch from f6a2e44 to 20f0492 Compare July 21, 2026 14:34

@emlautarom1-agent emlautarom1-agent Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Inline notes for reviewers (re-posted at current lines after the rebase onto main).

/// schedule (`/eth/v1/config/fork_schedule`), decoded and ordered
/// oldest-to-newest. The first entry is the genesis fork version, which
/// identifies the beacon node's network.
pub async fn fetch_fork_schedule_versions(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

New API (why not the existing one). This reads the real /eth/v1/config/fork_schedule endpoint (matching Charon's eth2Cl.ForkSchedule()). The sibling fetch_fork_config can't be reused here: it is spec-derived from <FORK>_FORK_VERSION keys over Altair..Fulu only, so it omits the genesis/phase0 entry — the lock's fork version is the genesis fork version, so checking against fetch_fork_config would falsely reject every correct network.

Comment on lines +942 to +948
Err(AppError::ForkScheduleMismatch {
lock_network: network_name_or_hex(lock_fork_version),
lock_fork_version: format!("0x{}", hex::encode(lock_fork_version)),
beacon_node_network: versions
.first()
.map(|v| network_name_or_hex(&v[..]))
.unwrap_or_else(|| "unknown".to_string()),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Differs from Charon. Two deliberate deviations from configureEth2Client: (1) an unknown fork version falls back to its 0x… hex here (via network_name_or_hex) instead of Charon's hard "cannot parse" error — more actionable and still fails on genuine mismatch; (2) an empty schedule yields unknown rather than panicking on schedule[0].

Port Charon's `configureEth2Client` guard (app/app.go:1022-1053): after
building the eth2 client, fetch the beacon node's fork schedule and error
at startup if it does not contain the cluster lock's fork version. This
rejects a wrong-network beacon node up front instead of letting the node
schedule duties and silently fail signatures later.

- eth2api: add `fetch_fork_schedule_versions`, exposing the decoded
  `current_version`s from `/eth/v1/config/fork_schedule`.
- app: `verify_fork_schedule` checks lock fork version membership and, on
  mismatch, returns `AppError::ForkScheduleMismatch` naming the lock fork
  version + network and the beacon node network (best-effort, hex fallback
  for unknown networks).
- beaconmock-backed tests for both the matching and mismatched paths.
@emlautarom1
emlautarom1 force-pushed the worktree-issue-532-fork-schedule-mismatch branch from 20f0492 to 40125ee Compare July 21, 2026 14:47
- Explain expected endpoint ordering
@emlautarom1
emlautarom1 requested a review from iamquang95 July 21, 2026 14:52

@emlautarom1 emlautarom1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Additional wiring check. Minor stuff, LGTM.

Comment thread crates/app/src/node/mod.rs Outdated
Comment thread crates/app/src/node/mod.rs Outdated
@emlautarom1
emlautarom1 merged commit 84f346a into main Jul 23, 2026
11 checks passed
@emlautarom1
emlautarom1 deleted the worktree-issue-532-fork-schedule-mismatch branch July 23, 2026 14:39
@emlautarom1
emlautarom1 restored the worktree-issue-532-fork-schedule-mismatch branch July 23, 2026 15:00
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.

feat(app): fail fast on lock vs beacon-node fork-schedule mismatch

3 participants