github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/state/stategen/service.go (about) 1 // Package stategen defines functions to regenerate beacon chain states 2 // by replaying blocks from a stored state checkpoint, useful for 3 // optimization and reducing a beacon node's resource consumption. 4 package stategen 5 6 import ( 7 "context" 8 "errors" 9 "sync" 10 11 types "github.com/prysmaticlabs/eth2-types" 12 "github.com/prysmaticlabs/prysm/beacon-chain/db" 13 iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface" 14 ethereum_beacon_p2p_v1 "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" 15 "github.com/prysmaticlabs/prysm/proto/interfaces" 16 "github.com/prysmaticlabs/prysm/shared/bytesutil" 17 "github.com/prysmaticlabs/prysm/shared/params" 18 "go.opencensus.io/trace" 19 ) 20 21 var defaultHotStateDBInterval types.Slot = 128 22 23 // StateManager represents a management object that handles the internal 24 // logic of maintaining both hot and cold states in DB. 25 type StateManager interface { 26 Resume(ctx context.Context) (iface.BeaconState, error) 27 SaveFinalizedState(fSlot types.Slot, fRoot [32]byte, fState iface.BeaconState) 28 MigrateToCold(ctx context.Context, fRoot [32]byte) error 29 ReplayBlocks(ctx context.Context, state iface.BeaconState, signed []interfaces.SignedBeaconBlock, targetSlot types.Slot) (iface.BeaconState, error) 30 LoadBlocks(ctx context.Context, startSlot, endSlot types.Slot, endBlockRoot [32]byte) ([]interfaces.SignedBeaconBlock, error) 31 HasState(ctx context.Context, blockRoot [32]byte) (bool, error) 32 HasStateInCache(ctx context.Context, blockRoot [32]byte) (bool, error) 33 StateByRoot(ctx context.Context, blockRoot [32]byte) (iface.BeaconState, error) 34 StateByRootInitialSync(ctx context.Context, blockRoot [32]byte) (iface.BeaconState, error) 35 StateBySlot(ctx context.Context, slot types.Slot) (iface.BeaconState, error) 36 RecoverStateSummary(ctx context.Context, blockRoot [32]byte) (*ethereum_beacon_p2p_v1.StateSummary, error) 37 SaveState(ctx context.Context, root [32]byte, st iface.BeaconState) error 38 ForceCheckpoint(ctx context.Context, root []byte) error 39 EnableSaveHotStateToDB(_ context.Context) 40 DisableSaveHotStateToDB(ctx context.Context) error 41 } 42 43 // State is a concrete implementation of StateManager. 44 type State struct { 45 beaconDB db.NoHeadAccessDatabase 46 slotsPerArchivedPoint types.Slot 47 hotStateCache *hotStateCache 48 finalizedInfo *finalizedInfo 49 epochBoundaryStateCache *epochBoundaryState 50 saveHotStateDB *saveHotStateDbConfig 51 } 52 53 // This tracks the config in the event of long non-finality, 54 // how often does the node save hot states to db? what are 55 // the saved hot states in db?... etc 56 type saveHotStateDbConfig struct { 57 enabled bool 58 lock sync.Mutex 59 duration types.Slot 60 savedStateRoots [][32]byte 61 } 62 63 // This tracks the finalized point. It's also the point where slot and the block root of 64 // cold and hot sections of the DB splits. 65 type finalizedInfo struct { 66 slot types.Slot 67 root [32]byte 68 state iface.BeaconState 69 lock sync.RWMutex 70 } 71 72 // New returns a new state management object. 73 func New(beaconDB db.NoHeadAccessDatabase) *State { 74 return &State{ 75 beaconDB: beaconDB, 76 hotStateCache: newHotStateCache(), 77 finalizedInfo: &finalizedInfo{slot: 0, root: params.BeaconConfig().ZeroHash}, 78 slotsPerArchivedPoint: params.BeaconConfig().SlotsPerArchivedPoint, 79 epochBoundaryStateCache: newBoundaryStateCache(), 80 saveHotStateDB: &saveHotStateDbConfig{ 81 duration: defaultHotStateDBInterval, 82 }, 83 } 84 } 85 86 // Resume resumes a new state management object from previously saved finalized check point in DB. 87 func (s *State) Resume(ctx context.Context) (iface.BeaconState, error) { 88 ctx, span := trace.StartSpan(ctx, "stateGen.Resume") 89 defer span.End() 90 91 c, err := s.beaconDB.FinalizedCheckpoint(ctx) 92 if err != nil { 93 return nil, err 94 } 95 fRoot := bytesutil.ToBytes32(c.Root) 96 // Resume as genesis state if last finalized root is zero hashes. 97 if fRoot == params.BeaconConfig().ZeroHash { 98 return s.beaconDB.GenesisState(ctx) 99 } 100 fState, err := s.StateByRoot(ctx, fRoot) 101 if err != nil { 102 return nil, err 103 } 104 if fState == nil || fState.IsNil() { 105 return nil, errors.New("finalized state not found in disk") 106 } 107 108 go func() { 109 if err := s.beaconDB.CleanUpDirtyStates(ctx, s.slotsPerArchivedPoint); err != nil { 110 log.WithError(err).Error("Could not clean up dirty states") 111 } 112 }() 113 114 s.finalizedInfo = &finalizedInfo{slot: fState.Slot(), root: fRoot, state: fState.Copy()} 115 116 return fState, nil 117 } 118 119 // SaveFinalizedState saves the finalized slot, root and state into memory to be used by state gen service. 120 // This used for migration at the correct start slot and used for hot state play back to ensure 121 // lower bound to start is always at the last finalized state. 122 func (s *State) SaveFinalizedState(fSlot types.Slot, fRoot [32]byte, fState iface.BeaconState) { 123 s.finalizedInfo.lock.Lock() 124 defer s.finalizedInfo.lock.Unlock() 125 s.finalizedInfo.root = fRoot 126 s.finalizedInfo.state = fState.Copy() 127 s.finalizedInfo.slot = fSlot 128 } 129 130 // Returns true if input root equals to cached finalized root. 131 func (s *State) isFinalizedRoot(r [32]byte) bool { 132 s.finalizedInfo.lock.RLock() 133 defer s.finalizedInfo.lock.RUnlock() 134 return r == s.finalizedInfo.root 135 } 136 137 // Returns the cached and copied finalized state. 138 func (s *State) finalizedState() iface.BeaconState { 139 s.finalizedInfo.lock.RLock() 140 defer s.finalizedInfo.lock.RUnlock() 141 return s.finalizedInfo.state.Copy() 142 }