From 3e32b2653d604258242bac3093891973358b2b25 Mon Sep 17 00:00:00 2001 From: Alexey Samosadov Date: Sun, 8 Feb 2026 04:29:36 +0300 Subject: [PATCH] eth/api: report syncing on startup until CL sends first update This commit fixes an issue where geth reports "synced" immediately on startup even before receiving any blocks from the consensus layer (CL). This causes problems for load balancers like HAProxy that route requests to out-of-sync nodes. The fix adds a `SyncedWithCL` field to `SyncProgress` that tracks whether the node has received at least one successful ForkchoiceUpdated from the CL. The `SyncProgress.Done()` method now requires this flag to be true before reporting the node as synced. This follows the suggestion from @jwasinger and @MariusVanDerWijden: "hardcode the node to report unsynced on startup and only change to synced once we learn about a new block" Fixes #33687 Co-Authored-By: Claude Opus 4.5 --- eth/api_backend.go | 3 +++ interfaces.go | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/eth/api_backend.go b/eth/api_backend.go index 3f826b7861..1aa6052067 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -418,6 +418,9 @@ func (b *EthAPIBackend) SyncProgress(ctx context.Context) ethereum.SyncProgress if err == nil { prog.StateIndexRemaining = remain } + // SyncedWithCL is true when the node has received at least one successful + // ForkchoiceUpdated from the consensus layer with a known block. + prog.SyncedWithCL = b.eth.Synced() return prog } diff --git a/interfaces.go b/interfaces.go index 21d42c6d34..1be1a89a0a 100644 --- a/interfaces.go +++ b/interfaces.go @@ -141,10 +141,23 @@ type SyncProgress struct { // "historical state indexing" fields StateIndexRemaining uint64 // Number of states remain unindexed + + // SyncedWithCL indicates whether the node has received at least one successful + // consensus layer update (ForkchoiceUpdated with a known block). On startup, + // this is false until the CL sends the first valid update. + SyncedWithCL bool } // Done returns the indicator if the initial sync is finished or not. +// A node is considered synced only when: +// 1. It has received at least one successful CL update (SyncedWithCL is true) +// 2. CurrentBlock >= HighestBlock +// 3. Transaction and state indexing are complete func (prog SyncProgress) Done() bool { + // Node must have received at least one CL update to be considered synced + if !prog.SyncedWithCL { + return false + } if prog.CurrentBlock < prog.HighestBlock { return false }