Skip to content

fix(sync): judge the sync gate against peers' Status heads - #791

Draft
MegaRedHand wants to merge 1 commit into
mainfrom
fix/sync-gate-peer-heads
Draft

MegaRedHand wants to merge 1 commit into
mainfrom
fix/sync-gate-peer-heads

Conversation

@MegaRedHand

Copy link
Copy Markdown
Collaborator

🗒️ Description / Motivation

The sync gate reported Synced while a node was far behind (#559), so it attested and proposed on a stale head. It took the freshest block the network knows from the node's own imported chain, which trails the head during a catch-up, so the network-stall override fired. The beacon follower had the same bug: docs/beacon_wire.md records it reporting synced through a 10-minute catch-up up to 98 slots behind.

This follows Lighthouse and Prysm: a node is syncing only when its head trails the wall clock and the head its peers report in Status is ahead of its own. Peers decide "behind vs. stalled"; the clock keeps any peer claim from idling a node that is current.

What Changed

  • crates/blockchain/src/sync_status.rs: new rule with per-chain SyncThresholds. Lean keeps leanSpec ci: add a Commit-Boost check to the kurtosis battery #708's 4/2. Beacon uses Lighthouse's numbers: 2 epochs of clock lag (DEFAULT_SYNC_TOLERANCE_EPOCHS, also Prysm's shouldReSync) and 1 epoch of peer lead (SLOT_IMPORT_TOLERANCE). There is also a bounded startup hold before the first Status (lean 8 slots, beacon 1 epoch).
  • crates/net/p2p/src/peer_heads.rs (new): each connected peer's latest Status head, dropped on disconnect.
    • A head more than 1 slot past the wall clock is ignored (Lighthouse/Prysm FUTURE_SLOT_TOLERANCE).
    • The published value is the second-highest report once 3+ peers have reported, the highest below that.
    • It is published on Store::network_head.
  • p2p: Status is re-requested every 4 slots (lean) or every half epoch (beacon, Prysm's cadence). A refresh answer starts range sync only beyond the threshold lead, so it never races gossip.
  • crates/blockchain/src/lib.rs: the tracker reads Store::network_head, and the beacon fork guard is kept.
  • Docs: a new spec_deviations.md entry (lean departs from leanSpec ci: add a Commit-Boost check to the kurtosis battery #708), plus updates to architecture.md, rpc.md and beacon_wire.md.

Correctness / Behavior Guarantees

  • Stall: when no peer is ahead, the node stays synced at any clock lag (leanSpec's stall tests still hold). A run of missed proposals (leanSpec #1194) no longer closes the gate.
  • Unauthenticated input is bounded: pending or unverified blocks are not counted, and a peer's claim alone cannot stop a node whose head is within the clock threshold.
  • Losing every peer: reads as a stall (synced), as on Lighthouse and Prysm.
  • Lean deviation: a node behind its peers with nothing newer imported is now syncing, where leanSpec lets it do duties.

Tests Added / Run

  • Unit tests for the tracker rule, peer aggregation, Store::network_head sharing, and the beacon follower cases.
  • make fmt, make lint and make test all pass (2,934 tests).
  • Kurtosis (lighthouse×2 + ethlambda beacon/VC, mainnet preset):
    • Synced, verified: is_syncing=false, 2 peers, duty endpoints 200, and the VC attests, aggregates and does sync-committee duties every slot.
    • Not synced (node stopped for more than 64 slots, then restarted): still pending.
    • Cut-off (p2p ports dropped with iptables, then restored): still pending.

Related Issues / PRs

✅ Verification Checklist

  • Ran make fmt — clean
  • Ran make lint (clippy with -D warnings) — clean
  • Ran make test (test-consensus plus test-node, at release-fast) — all passing

A node reported itself synced while far behind (#559): the tracker took the
freshest block the network knows from the node's own imported chain, which
trails the head during a catch-up, so its network-stall override fired and the
node attested and proposed on a stale head. The beacon follower had the same
bug and reported synced through whole catch-ups.

Follow Lighthouse and Prysm instead: a node is syncing only when its head
trails the wall clock and the head its peers report in Status is ahead of its
own. P2P keeps each connected peer's latest Status head (re-requested every
few slots, dropped on disconnect, a head past the clock ignored) and publishes
the second-highest of three or more on the Store. Thresholds are per chain:
leanSpec's 4/2 on lean, Lighthouse's two epochs of clock lag and one epoch of
peer lead on beacon.

This branch has not been deployed

No deployments
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.

Duty sync gate reports Synced while the node is far behind, so it attests and proposes on a stale head

1 participant