eth/syncer: only synthesize finalized/safe markers with an explicit sync target (#35433)

Fixes #35418.

After a restart, the syncer sets the finalized marker to head−64 and the
safe marker to head−32 on every downloader sync event, ignoring epoch
boundaries. Since this service is registered unconditionally and
`api_backend` serves `CurrentFinalBlock()` verbatim,
`eth_getBlockByNumber("finalized")` can return a mid-epoch block that
was never finalized and can still be reorged.

The synthesized markers exist to make `finalized`/`safe` usable when no
consensus client is attached. This change scopes them to exactly that
case: they are only set when an explicit sync target has been specified
(`--synctarget`), which is the only mode where no CL supplies real
checkpoints. Normal nodes now never override consensus-client
checkpoints.

`TestSyncerDoesNotInventFinalityMarkers` syncs a node to head with no CL
finality input and asserts the chain's finalized/safe markers stay
unset; it fails on master (finalized invented at head−64) and passes
with this change.

---------

Co-authored-by: SillyZir <269283839+SillyZir@users.noreply.github.com>
Co-authored-by: Jonny Rhea <5555162+jrhea@users.noreply.github.com>
Co-authored-by: rjl493456442 <garyrong0905@gmail.com>
This commit is contained in:
SillyZir 2026-08-12 11:57:13 -04:00 committed by GitHub
parent ee0607cdcc
commit 4307ee86f8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -159,10 +159,14 @@ func (s *Syncer) run() {
}
head := s.backend.BlockChain().CurrentHeader()
if head != nil {
if target != nil && head != nil {
// Set the finalized and safe markers relative to the current head.
// The finalized marker is set two epochs behind the target,
// and the safe marker is set one epoch behind the target.
//
// Note, the markers are only synthesized if a sync target has
// been explicitly specified, in which case no consensus client
// is attached to supply the real ones.
if header := s.backend.BlockChain().GetHeaderByNumber(head.Number.Uint64() - params.EpochLength*2); header != nil {
if final := s.backend.BlockChain().CurrentFinalBlock(); final == nil || final.Number.Cmp(header.Number) < 0 {
s.backend.BlockChain().SetFinalized(header)