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 <noreply@anthropic.com>
This commit is contained in:
Alexey Samosadov 2026-02-08 04:29:36 +03:00
parent 777265620d
commit 3e32b2653d
2 changed files with 16 additions and 0 deletions

View file

@ -418,6 +418,9 @@ func (b *EthAPIBackend) SyncProgress(ctx context.Context) ethereum.SyncProgress
if err == nil { if err == nil {
prog.StateIndexRemaining = remain 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 return prog
} }

View file

@ -141,10 +141,23 @@ type SyncProgress struct {
// "historical state indexing" fields // "historical state indexing" fields
StateIndexRemaining uint64 // Number of states remain unindexed 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. // 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 { 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 { if prog.CurrentBlock < prog.HighestBlock {
return false return false
} }