mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-08 07:58:40 +00:00
eth/catalyst: fix sync restart loop during partial state snap sync
The stateless block check in forkchoiceUpdated was calling BeaconSync() on every FCU (~12 seconds) during active snap sync, restarting the entire sync cycle each time. This prevented state download from ever completing. Guard the check with ConfigSyncMode: during active snap sync, the downloader is already working, so just return STATUS_SYNCING without restarting. Only trigger BeaconSync for stateless blocks after snap sync has completed (FullSync mode). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b5d9b12e70
commit
da476a8eca
1 changed files with 22 additions and 20 deletions
|
|
@ -295,28 +295,30 @@ func (api *ConsensusAPI) forkchoiceUpdated(ctx context.Context, update engine.Fo
|
|||
}
|
||||
return engine.STATUS_SYNCING, nil
|
||||
}
|
||||
// In partial state mode during snap sync, the block may have been persisted
|
||||
// (by WriteBlockWithoutState in newPayload) but we have no state for it yet.
|
||||
// If we try to SetCanonical, it will fail because HasState returns false and
|
||||
// partial state can't recoverAncestors. Instead, treat it like an unknown
|
||||
// block and trigger BeaconSync so the skeleton can start the sync cycle.
|
||||
//
|
||||
// After sync, the computed root may differ from the header root (unresolved
|
||||
// untracked storage roots), so we also check partialState's tracked root.
|
||||
partialRoot := common.Hash{}
|
||||
if api.eth.BlockChain().SupportsPartialState() {
|
||||
partialRoot = api.eth.BlockChain().PartialState().Root()
|
||||
}
|
||||
// In partial state mode, a block may exist in DB (from WriteBlockWithoutState
|
||||
// in newPayload) but have no state yet. During active snap sync, this is
|
||||
// expected — the downloader is already syncing state. Just return SYNCING
|
||||
// without triggering a restart. After snap sync completes, if we still see
|
||||
// a stateless block, trigger BeaconSync to re-sync for it.
|
||||
if api.eth.BlockChain().SupportsPartialState() &&
|
||||
!api.eth.BlockChain().HasState(block.Root()) &&
|
||||
(partialRoot == common.Hash{} || !api.eth.BlockChain().HasState(partialRoot)) {
|
||||
log.Info("Forkchoice: block known but stateless (partial state sync in progress), triggering BeaconSync",
|
||||
"number", block.NumberU64(), "hash", update.HeadBlockHash, "root", block.Root())
|
||||
finalized := api.remoteBlocks.get(update.FinalizedBlockHash)
|
||||
if err := api.eth.Downloader().BeaconSync(block.Header(), finalized); err != nil {
|
||||
return engine.STATUS_SYNCING, err
|
||||
!api.eth.BlockChain().HasState(block.Root()) {
|
||||
partialRoot := api.eth.BlockChain().PartialState().Root()
|
||||
if partialRoot == (common.Hash{}) || !api.eth.BlockChain().HasState(partialRoot) {
|
||||
if api.eth.Downloader().ConfigSyncMode() == ethconfig.SnapSync {
|
||||
// Snap sync active — downloader is already working. Don't restart.
|
||||
log.Debug("Forkchoice: stateless block during snap sync, not restarting",
|
||||
"number", block.NumberU64(), "hash", update.HeadBlockHash)
|
||||
return engine.STATUS_SYNCING, nil
|
||||
}
|
||||
// Snap sync done but block has no state — trigger BeaconSync.
|
||||
log.Info("Forkchoice: block known but stateless, triggering BeaconSync",
|
||||
"number", block.NumberU64(), "hash", update.HeadBlockHash, "root", block.Root())
|
||||
finalized := api.remoteBlocks.get(update.FinalizedBlockHash)
|
||||
if err := api.eth.Downloader().BeaconSync(block.Header(), finalized); err != nil {
|
||||
return engine.STATUS_SYNCING, err
|
||||
}
|
||||
return engine.STATUS_SYNCING, nil
|
||||
}
|
||||
return engine.STATUS_SYNCING, nil
|
||||
}
|
||||
// Block is known locally, just sanity check that the beacon client does not
|
||||
// attempt to push us back to before the merge.
|
||||
|
|
|
|||
Loading…
Reference in a new issue