2020-04-29 17:40:33 +00:00
|
|
|
// Package stategen defines functions to regenerate beacon chain states
|
|
|
|
// by replaying blocks from a stored state checkpoint, useful for
|
|
|
|
// optimization and reducing a beacon node's resource consumption.
|
2020-02-16 23:28:20 +00:00
|
|
|
package stategen
|
|
|
|
|
|
|
|
import (
|
2020-03-15 16:47:49 +00:00
|
|
|
"context"
|
2020-10-29 16:14:57 +00:00
|
|
|
"errors"
|
2020-07-06 17:22:12 +00:00
|
|
|
"sync"
|
2020-03-03 19:07:34 +00:00
|
|
|
|
2021-02-16 07:45:34 +00:00
|
|
|
types "github.com/prysmaticlabs/eth2-types"
|
2020-02-16 23:28:20 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
2021-03-08 22:37:33 +00:00
|
|
|
iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface"
|
2021-02-11 21:08:36 +00:00
|
|
|
ethereum_beacon_p2p_v1 "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
2020-10-29 16:14:57 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
2021-05-26 16:19:54 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/interfaces"
|
2020-03-06 23:06:01 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/params"
|
2020-03-15 16:47:49 +00:00
|
|
|
"go.opencensus.io/trace"
|
2020-02-16 23:28:20 +00:00
|
|
|
)
|
|
|
|
|
2021-02-16 07:45:34 +00:00
|
|
|
var defaultHotStateDBInterval types.Slot = 128
|
2020-10-23 00:35:30 +00:00
|
|
|
|
2021-02-11 21:08:36 +00:00
|
|
|
// StateManager represents a management object that handles the internal
|
2020-02-16 23:28:20 +00:00
|
|
|
// logic of maintaining both hot and cold states in DB.
|
2021-02-11 21:08:36 +00:00
|
|
|
type StateManager interface {
|
2021-03-08 22:37:33 +00:00
|
|
|
Resume(ctx context.Context) (iface.BeaconState, error)
|
|
|
|
SaveFinalizedState(fSlot types.Slot, fRoot [32]byte, fState iface.BeaconState)
|
2021-02-11 21:08:36 +00:00
|
|
|
MigrateToCold(ctx context.Context, fRoot [32]byte) error
|
2021-05-26 16:19:54 +00:00
|
|
|
ReplayBlocks(ctx context.Context, state iface.BeaconState, signed []interfaces.SignedBeaconBlock, targetSlot types.Slot) (iface.BeaconState, error)
|
|
|
|
LoadBlocks(ctx context.Context, startSlot, endSlot types.Slot, endBlockRoot [32]byte) ([]interfaces.SignedBeaconBlock, error)
|
2021-02-11 21:08:36 +00:00
|
|
|
HasState(ctx context.Context, blockRoot [32]byte) (bool, error)
|
|
|
|
HasStateInCache(ctx context.Context, blockRoot [32]byte) (bool, error)
|
2021-03-08 22:37:33 +00:00
|
|
|
StateByRoot(ctx context.Context, blockRoot [32]byte) (iface.BeaconState, error)
|
|
|
|
StateByRootInitialSync(ctx context.Context, blockRoot [32]byte) (iface.BeaconState, error)
|
|
|
|
StateBySlot(ctx context.Context, slot types.Slot) (iface.BeaconState, error)
|
2021-02-11 21:08:36 +00:00
|
|
|
RecoverStateSummary(ctx context.Context, blockRoot [32]byte) (*ethereum_beacon_p2p_v1.StateSummary, error)
|
2021-03-08 22:37:33 +00:00
|
|
|
SaveState(ctx context.Context, root [32]byte, st iface.BeaconState) error
|
2021-02-11 21:08:36 +00:00
|
|
|
ForceCheckpoint(ctx context.Context, root []byte) error
|
|
|
|
EnableSaveHotStateToDB(_ context.Context)
|
|
|
|
DisableSaveHotStateToDB(ctx context.Context) error
|
|
|
|
}
|
|
|
|
|
|
|
|
// State is a concrete implementation of StateManager.
|
2020-02-16 23:28:20 +00:00
|
|
|
type State struct {
|
2020-07-06 17:22:12 +00:00
|
|
|
beaconDB db.NoHeadAccessDatabase
|
2021-02-16 07:45:34 +00:00
|
|
|
slotsPerArchivedPoint types.Slot
|
2020-12-17 20:40:47 +00:00
|
|
|
hotStateCache *hotStateCache
|
2020-07-06 17:22:12 +00:00
|
|
|
finalizedInfo *finalizedInfo
|
|
|
|
epochBoundaryStateCache *epochBoundaryState
|
2020-10-23 00:35:30 +00:00
|
|
|
saveHotStateDB *saveHotStateDbConfig
|
|
|
|
}
|
|
|
|
|
|
|
|
// This tracks the config in the event of long non-finality,
|
|
|
|
// how often does the node save hot states to db? what are
|
|
|
|
// the saved hot states in db?... etc
|
|
|
|
type saveHotStateDbConfig struct {
|
|
|
|
enabled bool
|
|
|
|
lock sync.Mutex
|
2021-02-16 07:45:34 +00:00
|
|
|
duration types.Slot
|
2020-10-23 00:35:30 +00:00
|
|
|
savedStateRoots [][32]byte
|
2020-03-12 02:27:16 +00:00
|
|
|
}
|
|
|
|
|
2020-07-03 06:33:58 +00:00
|
|
|
// This tracks the finalized point. It's also the point where slot and the block root of
|
2020-03-12 02:27:16 +00:00
|
|
|
// cold and hot sections of the DB splits.
|
2020-07-06 17:22:12 +00:00
|
|
|
type finalizedInfo struct {
|
2021-02-16 07:45:34 +00:00
|
|
|
slot types.Slot
|
2020-07-06 17:22:12 +00:00
|
|
|
root [32]byte
|
2021-03-08 22:37:33 +00:00
|
|
|
state iface.BeaconState
|
2020-07-06 17:22:12 +00:00
|
|
|
lock sync.RWMutex
|
2020-02-16 23:28:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// New returns a new state management object.
|
2020-12-18 19:12:30 +00:00
|
|
|
func New(beaconDB db.NoHeadAccessDatabase) *State {
|
2020-02-16 23:28:20 +00:00
|
|
|
return &State{
|
2020-12-18 19:12:30 +00:00
|
|
|
beaconDB: beaconDB,
|
2020-12-17 20:40:47 +00:00
|
|
|
hotStateCache: newHotStateCache(),
|
2020-07-06 17:22:12 +00:00
|
|
|
finalizedInfo: &finalizedInfo{slot: 0, root: params.BeaconConfig().ZeroHash},
|
|
|
|
slotsPerArchivedPoint: params.BeaconConfig().SlotsPerArchivedPoint,
|
|
|
|
epochBoundaryStateCache: newBoundaryStateCache(),
|
2020-10-23 00:35:30 +00:00
|
|
|
saveHotStateDB: &saveHotStateDbConfig{
|
|
|
|
duration: defaultHotStateDBInterval,
|
|
|
|
},
|
2020-02-16 23:28:20 +00:00
|
|
|
}
|
|
|
|
}
|
2020-03-06 23:06:01 +00:00
|
|
|
|
2020-03-15 16:47:49 +00:00
|
|
|
// Resume resumes a new state management object from previously saved finalized check point in DB.
|
2021-03-08 22:37:33 +00:00
|
|
|
func (s *State) Resume(ctx context.Context) (iface.BeaconState, error) {
|
2020-03-15 16:47:49 +00:00
|
|
|
ctx, span := trace.StartSpan(ctx, "stateGen.Resume")
|
|
|
|
defer span.End()
|
|
|
|
|
2020-10-29 16:14:57 +00:00
|
|
|
c, err := s.beaconDB.FinalizedCheckpoint(ctx)
|
2020-03-15 16:47:49 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-10-29 16:14:57 +00:00
|
|
|
fRoot := bytesutil.ToBytes32(c.Root)
|
|
|
|
// Resume as genesis state if last finalized root is zero hashes.
|
|
|
|
if fRoot == params.BeaconConfig().ZeroHash {
|
2020-03-16 19:07:07 +00:00
|
|
|
return s.beaconDB.GenesisState(ctx)
|
|
|
|
}
|
2020-10-29 19:04:06 +00:00
|
|
|
fState, err := s.StateByRoot(ctx, fRoot)
|
2020-10-29 16:14:57 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-05-24 04:55:42 +00:00
|
|
|
if fState == nil || fState.IsNil() {
|
2020-10-29 16:14:57 +00:00
|
|
|
return nil, errors.New("finalized state not found in disk")
|
|
|
|
}
|
2020-03-16 19:07:07 +00:00
|
|
|
|
2020-11-06 21:05:44 +00:00
|
|
|
go func() {
|
|
|
|
if err := s.beaconDB.CleanUpDirtyStates(ctx, s.slotsPerArchivedPoint); err != nil {
|
|
|
|
log.WithError(err).Error("Could not clean up dirty states")
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2020-10-29 16:14:57 +00:00
|
|
|
s.finalizedInfo = &finalizedInfo{slot: fState.Slot(), root: fRoot, state: fState.Copy()}
|
2020-03-16 19:07:07 +00:00
|
|
|
|
2020-10-29 16:14:57 +00:00
|
|
|
return fState, nil
|
2020-03-15 16:47:49 +00:00
|
|
|
}
|
2020-07-06 17:22:12 +00:00
|
|
|
|
|
|
|
// SaveFinalizedState saves the finalized slot, root and state into memory to be used by state gen service.
|
|
|
|
// This used for migration at the correct start slot and used for hot state play back to ensure
|
|
|
|
// lower bound to start is always at the last finalized state.
|
2021-03-08 22:37:33 +00:00
|
|
|
func (s *State) SaveFinalizedState(fSlot types.Slot, fRoot [32]byte, fState iface.BeaconState) {
|
2020-07-06 17:22:12 +00:00
|
|
|
s.finalizedInfo.lock.Lock()
|
|
|
|
defer s.finalizedInfo.lock.Unlock()
|
|
|
|
s.finalizedInfo.root = fRoot
|
|
|
|
s.finalizedInfo.state = fState.Copy()
|
|
|
|
s.finalizedInfo.slot = fSlot
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns true if input root equals to cached finalized root.
|
|
|
|
func (s *State) isFinalizedRoot(r [32]byte) bool {
|
|
|
|
s.finalizedInfo.lock.RLock()
|
|
|
|
defer s.finalizedInfo.lock.RUnlock()
|
|
|
|
return r == s.finalizedInfo.root
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the cached and copied finalized state.
|
2021-03-08 22:37:33 +00:00
|
|
|
func (s *State) finalizedState() iface.BeaconState {
|
2020-07-06 17:22:12 +00:00
|
|
|
s.finalizedInfo.lock.RLock()
|
|
|
|
defer s.finalizedInfo.lock.RUnlock()
|
|
|
|
return s.finalizedInfo.state.Copy()
|
|
|
|
}
|