@@ -3,30 +3,71 @@ package checkpoint_sync
33import (
44 "context"
55 "fmt"
6+ "path/filepath"
67
78 "github.com/spf13/afero"
89
910 "github.com/erigontech/erigon/cl/clparams"
1011 "github.com/erigontech/erigon/cl/persistence/genesisdb"
1112 "github.com/erigontech/erigon/cl/phase1/core/state"
13+ "github.com/erigontech/erigon/cl/utils"
14+ "github.com/erigontech/erigon/common/log/v3"
1215 "github.com/erigontech/erigon/db/datadir"
1316)
1417
1518// ReadOrFetchLatestBeaconState reads the latest beacon state from disk or fetches it from the network.
19+ // If remote checkpoint sync fails, it falls back to the local head state on disk.
20+ // If no local head state is available, it returns an error.
1621func ReadOrFetchLatestBeaconState (ctx context.Context , dirs datadir.Dirs , beaconCfg * clparams.BeaconChainConfig , caplinConfig clparams.CaplinConfig , genesisDB genesisdb.GenesisDB ) (* state.CachingBeaconState , error ) {
17- var syncer CheckpointSyncer
1822 remoteSync := ! caplinConfig .DisabledCheckpointSync && ! caplinConfig .IsDevnet ()
1923
2024 if remoteSync {
21- syncer = NewRemoteCheckpointSync (beaconCfg , caplinConfig .NetworkId )
22- } else {
23- aferoFs := afero .NewOsFs ()
25+ syncer := NewRemoteCheckpointSync (beaconCfg , caplinConfig .NetworkId )
26+ st , err := syncer .GetLatestBeaconState (ctx )
27+ if err == nil {
28+ return st , nil
29+ }
30+ log .Warn ("[Checkpoint Sync] Remote checkpoint sync failed, attempting to read local head state" , "err" , err )
2431
25- genesisState , err := genesisDB .ReadGenesisState ()
26- if err != nil {
27- return nil , fmt .Errorf ("could not read genesis state: %w" , err )
32+ // Fallback: try to read the local head state from disk
33+ localState , localErr := ReadLocalHeadState (dirs , beaconCfg )
34+ if localErr == nil {
35+ log .Info ("[Checkpoint Sync] Successfully loaded local head state" , "slot" , localState .Slot ())
36+ return localState , nil
2837 }
29- syncer = NewLocalCheckpointSyncer (genesisState , afero .NewBasePathFs (aferoFs , dirs .CaplinLatest ))
38+ log .Error ("[Checkpoint Sync] No local head state available either" , "err" , localErr )
39+ return nil , fmt .Errorf ("remote checkpoint sync failed: %w, and no local head state: %w" , err , localErr )
40+ }
41+
42+ // Non-remote sync path (disabled checkpoint sync or devnet)
43+ aferoFs := afero .NewOsFs ()
44+ genesisState , err := genesisDB .ReadGenesisState ()
45+ if err != nil {
46+ return nil , fmt .Errorf ("could not read genesis state: %w" , err )
3047 }
48+ syncer := NewLocalCheckpointSyncer (genesisState , afero .NewBasePathFs (aferoFs , dirs .CaplinLatest ))
3149 return syncer .GetLatestBeaconState (ctx )
3250}
51+
52+ // ReadLocalHeadState reads the head state directly from disk without falling back to genesis.
53+ func ReadLocalHeadState (dirs datadir.Dirs , beaconCfg * clparams.BeaconChainConfig ) (* state.CachingBeaconState , error ) {
54+ statePath := filepath .Join (dirs .CaplinLatest , clparams .LatestStateFileName )
55+ snappyEncoded , err := afero .ReadFile (afero .NewOsFs (), statePath )
56+ if err != nil {
57+ return nil , fmt .Errorf ("could not read local head state file: %w" , err )
58+ }
59+ decompressed , err := utils .DecompressSnappy (snappyEncoded , false )
60+ if err != nil {
61+ return nil , fmt .Errorf ("local head state is corrupt: %w" , err )
62+ }
63+ slot , err := utils .ExtractSlotFromSerializedBeaconState (decompressed )
64+ if err != nil {
65+ return nil , fmt .Errorf ("could not extract slot from local head state: %w" , err )
66+ }
67+ bs := state .New (beaconCfg )
68+ epoch := slot / beaconCfg .SlotsPerEpoch
69+ if err := bs .DecodeSSZ (decompressed , int (beaconCfg .GetCurrentStateVersion (epoch ))); err != nil {
70+ return nil , fmt .Errorf ("could not decode local head state: %w" , err )
71+ }
72+ return bs , nil
73+ }
0 commit comments