mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-27 05:38:55 +00:00
a069738c20
* update shared/params * update eth2-types deps * update protobufs * update shared/* * fix testutil/state * update beacon-chain/state * update beacon-chain/db * update tests * fix test * update beacon-chain/core * update beacon-chain/blockchain * update beacon-chain/cache * beacon-chain/forkchoice * update beacon-chain/operations * update beacon-chain/p2p * update beacon-chain/rpc * update sync/initial-sync * update deps * update deps * go fmt * update beacon-chain/sync * update endtoend/ * bazel build //beacon-chain - works w/o issues * update slasher code * udpate tools/ * update validator/ * update fastssz * fix build * fix test building * update tests * update ethereumapis deps * fix tests * update state/stategen * fix build * fix test * add FarFutureSlot * go imports * Radek's suggestions * Ivan's suggestions * type conversions * Nishant's suggestions * add more tests to rpc_send_request * fix test * clean up * fix conflicts Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com> Co-authored-by: nisdas <nishdas93@gmail.com>
297 lines
8.9 KiB
Go
297 lines
8.9 KiB
Go
package stategen
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/pkg/errors"
|
|
types "github.com/prysmaticlabs/eth2-types"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
|
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
|
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
|
"github.com/prysmaticlabs/prysm/shared/params"
|
|
"go.opencensus.io/trace"
|
|
)
|
|
|
|
// HasState returns true if the state exists in cache or in DB.
|
|
func (s *State) HasState(ctx context.Context, blockRoot [32]byte) (bool, error) {
|
|
has, err := s.HasStateInCache(ctx, blockRoot)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if has {
|
|
return true, nil
|
|
}
|
|
return s.beaconDB.HasState(ctx, blockRoot), nil
|
|
}
|
|
|
|
// HasStateInCache returns true if the state exists in cache.
|
|
func (s *State) HasStateInCache(ctx context.Context, blockRoot [32]byte) (bool, error) {
|
|
if s.hotStateCache.has(blockRoot) {
|
|
return true, nil
|
|
}
|
|
_, has, err := s.epochBoundaryStateCache.getByRoot(blockRoot)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return has, nil
|
|
}
|
|
|
|
// StateByRoot retrieves the state using input block root.
|
|
func (s *State) StateByRoot(ctx context.Context, blockRoot [32]byte) (*state.BeaconState, error) {
|
|
ctx, span := trace.StartSpan(ctx, "stateGen.StateByRoot")
|
|
defer span.End()
|
|
|
|
// Genesis case. If block root is zero hash, short circuit to use genesis cachedState stored in DB.
|
|
if blockRoot == params.BeaconConfig().ZeroHash {
|
|
return s.beaconDB.State(ctx, blockRoot)
|
|
}
|
|
return s.loadStateByRoot(ctx, blockRoot)
|
|
}
|
|
|
|
// StateByRootInitialSync retrieves the state from the DB for the initial syncing phase.
|
|
// It assumes initial syncing using a block list rather than a block tree hence the returned
|
|
// state is not copied.
|
|
// It invalidates cache for parent root because pre state will get mutated.
|
|
// Do not use this method for anything other than initial syncing purpose or block tree is applied.
|
|
func (s *State) StateByRootInitialSync(ctx context.Context, blockRoot [32]byte) (*state.BeaconState, error) {
|
|
// Genesis case. If block root is zero hash, short circuit to use genesis state stored in DB.
|
|
if blockRoot == params.BeaconConfig().ZeroHash {
|
|
return s.beaconDB.State(ctx, blockRoot)
|
|
}
|
|
|
|
// To invalidate cache for parent root because pre state will get mutated.
|
|
defer s.hotStateCache.delete(blockRoot)
|
|
|
|
if s.hotStateCache.has(blockRoot) {
|
|
return s.hotStateCache.getWithoutCopy(blockRoot), nil
|
|
}
|
|
|
|
cachedInfo, ok, err := s.epochBoundaryStateCache.getByRoot(blockRoot)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ok {
|
|
return cachedInfo.state, nil
|
|
}
|
|
|
|
startState, err := s.lastAncestorState(ctx, blockRoot)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not get ancestor state")
|
|
}
|
|
if startState == nil {
|
|
return nil, errUnknownState
|
|
}
|
|
summary, err := s.stateSummary(ctx, blockRoot)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not get state summary")
|
|
}
|
|
if startState.Slot() == summary.Slot {
|
|
return startState, nil
|
|
}
|
|
|
|
blks, err := s.LoadBlocks(ctx, startState.Slot()+1, summary.Slot, bytesutil.ToBytes32(summary.Root))
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not load blocks")
|
|
}
|
|
startState, err = s.ReplayBlocks(ctx, startState, blks, summary.Slot)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not replay blocks")
|
|
}
|
|
|
|
return startState, nil
|
|
}
|
|
|
|
// StateBySlot retrieves the state using input slot.
|
|
func (s *State) StateBySlot(ctx context.Context, slot types.Slot) (*state.BeaconState, error) {
|
|
ctx, span := trace.StartSpan(ctx, "stateGen.StateBySlot")
|
|
defer span.End()
|
|
|
|
return s.loadStateBySlot(ctx, slot)
|
|
}
|
|
|
|
// This returns the state summary object of a given block root, it first checks the cache
|
|
// then checks the DB. An error is returned if state summary object is nil.
|
|
func (s *State) stateSummary(ctx context.Context, blockRoot [32]byte) (*pb.StateSummary, error) {
|
|
var summary *pb.StateSummary
|
|
var err error
|
|
|
|
summary, err = s.beaconDB.StateSummary(ctx, blockRoot)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if summary == nil {
|
|
return s.RecoverStateSummary(ctx, blockRoot)
|
|
}
|
|
return summary, nil
|
|
}
|
|
|
|
// RecoverStateSummary recovers state summary object of a given block root by using the saved block in DB.
|
|
func (s *State) RecoverStateSummary(ctx context.Context, blockRoot [32]byte) (*pb.StateSummary, error) {
|
|
if s.beaconDB.HasBlock(ctx, blockRoot) {
|
|
b, err := s.beaconDB.Block(ctx, blockRoot)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
summary := &pb.StateSummary{Slot: b.Block.Slot, Root: blockRoot[:]}
|
|
if err := s.beaconDB.SaveStateSummary(ctx, summary); err != nil {
|
|
return nil, err
|
|
}
|
|
return summary, nil
|
|
}
|
|
return nil, errors.New("could not find block in DB")
|
|
}
|
|
|
|
// This loads a beacon state from either the cache or DB then replay blocks up the requested block root.
|
|
func (s *State) loadStateByRoot(ctx context.Context, blockRoot [32]byte) (*state.BeaconState, error) {
|
|
ctx, span := trace.StartSpan(ctx, "stateGen.loadStateByRoot")
|
|
defer span.End()
|
|
|
|
// First, it checks if the state exists in hot state cache.
|
|
cachedState := s.hotStateCache.get(blockRoot)
|
|
if cachedState != nil {
|
|
return cachedState, nil
|
|
}
|
|
|
|
// Second, it checks if the state exits in epoch boundary state cache.
|
|
cachedInfo, ok, err := s.epochBoundaryStateCache.getByRoot(blockRoot)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ok {
|
|
return cachedInfo.state, nil
|
|
}
|
|
|
|
// Short cut if the cachedState is already in the DB.
|
|
if s.beaconDB.HasState(ctx, blockRoot) {
|
|
return s.beaconDB.State(ctx, blockRoot)
|
|
}
|
|
|
|
summary, err := s.stateSummary(ctx, blockRoot)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not get state summary")
|
|
}
|
|
targetSlot := summary.Slot
|
|
|
|
// Since the requested state is not in caches, start replaying using the last available ancestor state which is
|
|
// retrieved using input block's parent root.
|
|
startState, err := s.lastAncestorState(ctx, blockRoot)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not get ancestor state")
|
|
}
|
|
if startState == nil {
|
|
return nil, errUnknownBoundaryState
|
|
}
|
|
|
|
// Return state early if we are retrieving it from our finalized state cache.
|
|
if startState.Slot() == targetSlot {
|
|
return startState, nil
|
|
}
|
|
|
|
blks, err := s.LoadBlocks(ctx, startState.Slot()+1, targetSlot, bytesutil.ToBytes32(summary.Root))
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not load blocks for hot state using root")
|
|
}
|
|
|
|
replayBlockCount.Observe(float64(len(blks)))
|
|
|
|
return s.ReplayBlocks(ctx, startState, blks, targetSlot)
|
|
}
|
|
|
|
// This loads a state by slot.
|
|
func (s *State) loadStateBySlot(ctx context.Context, slot types.Slot) (*state.BeaconState, error) {
|
|
ctx, span := trace.StartSpan(ctx, "stateGen.loadStateBySlot")
|
|
defer span.End()
|
|
|
|
// Return genesis state if slot is 0.
|
|
if slot == 0 {
|
|
return s.beaconDB.GenesisState(ctx)
|
|
}
|
|
|
|
// Gather the last saved block root and the slot number.
|
|
lastValidRoot, lastValidSlot, err := s.lastSavedBlock(ctx, slot)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not get last valid block for hot state using slot")
|
|
}
|
|
|
|
replayStartState, err := s.loadStateByRoot(ctx, lastValidRoot)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if lastValidSlot < slot {
|
|
replayStartState, err = processSlotsStateGen(ctx, replayStartState, slot)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return replayStartState, nil
|
|
}
|
|
|
|
// This returns the highest available ancestor state of the input block root.
|
|
// It recursively look up block's parent until a corresponding state of the block root
|
|
// is found in the caches or DB.
|
|
//
|
|
// There's three ways to derive block parent state:
|
|
// 1.) block parent state is the last finalized state
|
|
// 2.) block parent state is the epoch boundary state and exists in epoch boundary cache.
|
|
// 3.) block parent state is in DB.
|
|
func (s *State) lastAncestorState(ctx context.Context, root [32]byte) (*state.BeaconState, error) {
|
|
ctx, span := trace.StartSpan(ctx, "stateGen.lastAncestorState")
|
|
defer span.End()
|
|
|
|
if s.isFinalizedRoot(root) && s.finalizedState() != nil {
|
|
return s.finalizedState(), nil
|
|
}
|
|
|
|
b, err := s.beaconDB.Block(ctx, root)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if b == nil {
|
|
return nil, errUnknownBlock
|
|
}
|
|
|
|
for {
|
|
if ctx.Err() != nil {
|
|
return nil, ctx.Err()
|
|
}
|
|
// Is the state a genesis state.
|
|
parentRoot := bytesutil.ToBytes32(b.Block.ParentRoot)
|
|
if parentRoot == params.BeaconConfig().ZeroHash {
|
|
return s.beaconDB.GenesisState(ctx)
|
|
}
|
|
|
|
// Does the state exist in the hot state cache.
|
|
if s.hotStateCache.has(parentRoot) {
|
|
return s.hotStateCache.get(parentRoot), nil
|
|
}
|
|
|
|
// Does the state exist in finalized info cache.
|
|
if s.isFinalizedRoot(parentRoot) {
|
|
return s.finalizedState(), nil
|
|
}
|
|
|
|
// Does the state exist in epoch boundary cache.
|
|
cachedInfo, ok, err := s.epochBoundaryStateCache.getByRoot(parentRoot)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ok {
|
|
return cachedInfo.state, nil
|
|
}
|
|
|
|
// Does the state exists in DB.
|
|
if s.beaconDB.HasState(ctx, parentRoot) {
|
|
return s.beaconDB.State(ctx, parentRoot)
|
|
}
|
|
b, err = s.beaconDB.Block(ctx, parentRoot)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if b == nil {
|
|
return nil, errUnknownBlock
|
|
}
|
|
}
|
|
}
|