github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/state/stategen/getter.go (about)

     1  package stategen
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/pkg/errors"
     7  	types "github.com/prysmaticlabs/eth2-types"
     8  	iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface"
     9  	pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
    10  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    11  	"github.com/prysmaticlabs/prysm/shared/params"
    12  	"go.opencensus.io/trace"
    13  )
    14  
    15  // HasState returns true if the state exists in cache or in DB.
    16  func (s *State) HasState(ctx context.Context, blockRoot [32]byte) (bool, error) {
    17  	has, err := s.HasStateInCache(ctx, blockRoot)
    18  	if err != nil {
    19  		return false, err
    20  	}
    21  	if has {
    22  		return true, nil
    23  	}
    24  	return s.beaconDB.HasState(ctx, blockRoot), nil
    25  }
    26  
    27  // HasStateInCache returns true if the state exists in cache.
    28  func (s *State) HasStateInCache(ctx context.Context, blockRoot [32]byte) (bool, error) {
    29  	if s.hotStateCache.has(blockRoot) {
    30  		return true, nil
    31  	}
    32  	_, has, err := s.epochBoundaryStateCache.getByRoot(blockRoot)
    33  	if err != nil {
    34  		return false, err
    35  	}
    36  	return has, nil
    37  }
    38  
    39  // StateByRoot retrieves the state using input block root.
    40  func (s *State) StateByRoot(ctx context.Context, blockRoot [32]byte) (iface.BeaconState, error) {
    41  	ctx, span := trace.StartSpan(ctx, "stateGen.StateByRoot")
    42  	defer span.End()
    43  
    44  	// Genesis case. If block root is zero hash, short circuit to use genesis cachedState stored in DB.
    45  	if blockRoot == params.BeaconConfig().ZeroHash {
    46  		return s.beaconDB.State(ctx, blockRoot)
    47  	}
    48  	return s.loadStateByRoot(ctx, blockRoot)
    49  }
    50  
    51  // StateByRootInitialSync retrieves the state from the DB for the initial syncing phase.
    52  // It assumes initial syncing using a block list rather than a block tree hence the returned
    53  // state is not copied.
    54  // It invalidates cache for parent root because pre state will get mutated.
    55  // Do not use this method for anything other than initial syncing purpose or block tree is applied.
    56  func (s *State) StateByRootInitialSync(ctx context.Context, blockRoot [32]byte) (iface.BeaconState, error) {
    57  	// Genesis case. If block root is zero hash, short circuit to use genesis state stored in DB.
    58  	if blockRoot == params.BeaconConfig().ZeroHash {
    59  		return s.beaconDB.State(ctx, blockRoot)
    60  	}
    61  
    62  	// To invalidate cache for parent root because pre state will get mutated.
    63  	defer s.hotStateCache.delete(blockRoot)
    64  
    65  	if s.hotStateCache.has(blockRoot) {
    66  		return s.hotStateCache.getWithoutCopy(blockRoot), nil
    67  	}
    68  
    69  	cachedInfo, ok, err := s.epochBoundaryStateCache.getByRoot(blockRoot)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	if ok {
    74  		return cachedInfo.state, nil
    75  	}
    76  
    77  	startState, err := s.lastAncestorState(ctx, blockRoot)
    78  	if err != nil {
    79  		return nil, errors.Wrap(err, "could not get ancestor state")
    80  	}
    81  	if startState == nil || startState.IsNil() {
    82  		return nil, errUnknownState
    83  	}
    84  	summary, err := s.stateSummary(ctx, blockRoot)
    85  	if err != nil {
    86  		return nil, errors.Wrap(err, "could not get state summary")
    87  	}
    88  	if startState.Slot() == summary.Slot {
    89  		return startState, nil
    90  	}
    91  
    92  	blks, err := s.LoadBlocks(ctx, startState.Slot()+1, summary.Slot, bytesutil.ToBytes32(summary.Root))
    93  	if err != nil {
    94  		return nil, errors.Wrap(err, "could not load blocks")
    95  	}
    96  	startState, err = s.ReplayBlocks(ctx, startState, blks, summary.Slot)
    97  	if err != nil {
    98  		return nil, errors.Wrap(err, "could not replay blocks")
    99  	}
   100  
   101  	return startState, nil
   102  }
   103  
   104  // StateBySlot retrieves the state using input slot.
   105  func (s *State) StateBySlot(ctx context.Context, slot types.Slot) (iface.BeaconState, error) {
   106  	ctx, span := trace.StartSpan(ctx, "stateGen.StateBySlot")
   107  	defer span.End()
   108  
   109  	return s.loadStateBySlot(ctx, slot)
   110  }
   111  
   112  // This returns the state summary object of a given block root, it first checks the cache
   113  // then checks the DB. An error is returned if state summary object is nil.
   114  func (s *State) stateSummary(ctx context.Context, blockRoot [32]byte) (*pb.StateSummary, error) {
   115  	var summary *pb.StateSummary
   116  	var err error
   117  
   118  	summary, err = s.beaconDB.StateSummary(ctx, blockRoot)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	if summary == nil {
   124  		return s.RecoverStateSummary(ctx, blockRoot)
   125  	}
   126  	return summary, nil
   127  }
   128  
   129  // RecoverStateSummary recovers state summary object of a given block root by using the saved block in DB.
   130  func (s *State) RecoverStateSummary(ctx context.Context, blockRoot [32]byte) (*pb.StateSummary, error) {
   131  	if s.beaconDB.HasBlock(ctx, blockRoot) {
   132  		b, err := s.beaconDB.Block(ctx, blockRoot)
   133  		if err != nil {
   134  			return nil, err
   135  		}
   136  		summary := &pb.StateSummary{Slot: b.Block().Slot(), Root: blockRoot[:]}
   137  		if err := s.beaconDB.SaveStateSummary(ctx, summary); err != nil {
   138  			return nil, err
   139  		}
   140  		return summary, nil
   141  	}
   142  	return nil, errors.New("could not find block in DB")
   143  }
   144  
   145  // This loads a beacon state from either the cache or DB then replay blocks up the requested block root.
   146  func (s *State) loadStateByRoot(ctx context.Context, blockRoot [32]byte) (iface.BeaconState, error) {
   147  	ctx, span := trace.StartSpan(ctx, "stateGen.loadStateByRoot")
   148  	defer span.End()
   149  
   150  	// First, it checks if the state exists in hot state cache.
   151  	cachedState := s.hotStateCache.get(blockRoot)
   152  	if cachedState != nil && !cachedState.IsNil() {
   153  		return cachedState, nil
   154  	}
   155  
   156  	// Second, it checks if the state exits in epoch boundary state cache.
   157  	cachedInfo, ok, err := s.epochBoundaryStateCache.getByRoot(blockRoot)
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  	if ok {
   162  		return cachedInfo.state, nil
   163  	}
   164  
   165  	// Short cut if the cachedState is already in the DB.
   166  	if s.beaconDB.HasState(ctx, blockRoot) {
   167  		return s.beaconDB.State(ctx, blockRoot)
   168  	}
   169  
   170  	summary, err := s.stateSummary(ctx, blockRoot)
   171  	if err != nil {
   172  		return nil, errors.Wrap(err, "could not get state summary")
   173  	}
   174  	targetSlot := summary.Slot
   175  
   176  	// Since the requested state is not in caches, start replaying using the last available ancestor state which is
   177  	// retrieved using input block's parent root.
   178  	startState, err := s.lastAncestorState(ctx, blockRoot)
   179  	if err != nil {
   180  		return nil, errors.Wrap(err, "could not get ancestor state")
   181  	}
   182  	if startState == nil || startState.IsNil() {
   183  		return nil, errUnknownBoundaryState
   184  	}
   185  
   186  	// Return state early if we are retrieving it from our finalized state cache.
   187  	if startState.Slot() == targetSlot {
   188  		return startState, nil
   189  	}
   190  
   191  	blks, err := s.LoadBlocks(ctx, startState.Slot()+1, targetSlot, bytesutil.ToBytes32(summary.Root))
   192  	if err != nil {
   193  		return nil, errors.Wrap(err, "could not load blocks for hot state using root")
   194  	}
   195  
   196  	replayBlockCount.Observe(float64(len(blks)))
   197  
   198  	return s.ReplayBlocks(ctx, startState, blks, targetSlot)
   199  }
   200  
   201  // This loads a state by slot.
   202  func (s *State) loadStateBySlot(ctx context.Context, slot types.Slot) (iface.BeaconState, error) {
   203  	ctx, span := trace.StartSpan(ctx, "stateGen.loadStateBySlot")
   204  	defer span.End()
   205  
   206  	// Return genesis state if slot is 0.
   207  	if slot == 0 {
   208  		return s.beaconDB.GenesisState(ctx)
   209  	}
   210  
   211  	// Gather the last saved block root and the slot number.
   212  	lastValidRoot, lastValidSlot, err := s.lastSavedBlock(ctx, slot)
   213  	if err != nil {
   214  		return nil, errors.Wrap(err, "could not get last valid block for hot state using slot")
   215  	}
   216  
   217  	replayStartState, err := s.loadStateByRoot(ctx, lastValidRoot)
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  
   222  	if lastValidSlot < slot {
   223  		replayStartState, err = processSlotsStateGen(ctx, replayStartState, slot)
   224  		if err != nil {
   225  			return nil, err
   226  		}
   227  	}
   228  	return replayStartState, nil
   229  }
   230  
   231  // This returns the highest available ancestor state of the input block root.
   232  // It recursively look up block's parent until a corresponding state of the block root
   233  // is found in the caches or DB.
   234  //
   235  // There's three ways to derive block parent state:
   236  // 1.) block parent state is the last finalized state
   237  // 2.) block parent state is the epoch boundary state and exists in epoch boundary cache.
   238  // 3.) block parent state is in DB.
   239  func (s *State) lastAncestorState(ctx context.Context, root [32]byte) (iface.BeaconState, error) {
   240  	ctx, span := trace.StartSpan(ctx, "stateGen.lastAncestorState")
   241  	defer span.End()
   242  
   243  	if s.isFinalizedRoot(root) && s.finalizedState() != nil {
   244  		return s.finalizedState(), nil
   245  	}
   246  
   247  	b, err := s.beaconDB.Block(ctx, root)
   248  	if err != nil {
   249  		return nil, err
   250  	}
   251  	if b == nil || b.IsNil() {
   252  		return nil, errUnknownBlock
   253  	}
   254  
   255  	for {
   256  		if ctx.Err() != nil {
   257  			return nil, ctx.Err()
   258  		}
   259  		// Is the state a genesis state.
   260  		parentRoot := bytesutil.ToBytes32(b.Block().ParentRoot())
   261  		if parentRoot == params.BeaconConfig().ZeroHash {
   262  			return s.beaconDB.GenesisState(ctx)
   263  		}
   264  
   265  		// Does the state exist in the hot state cache.
   266  		if s.hotStateCache.has(parentRoot) {
   267  			return s.hotStateCache.get(parentRoot), nil
   268  		}
   269  
   270  		// Does the state exist in finalized info cache.
   271  		if s.isFinalizedRoot(parentRoot) {
   272  			return s.finalizedState(), nil
   273  		}
   274  
   275  		// Does the state exist in epoch boundary cache.
   276  		cachedInfo, ok, err := s.epochBoundaryStateCache.getByRoot(parentRoot)
   277  		if err != nil {
   278  			return nil, err
   279  		}
   280  		if ok {
   281  			return cachedInfo.state, nil
   282  		}
   283  
   284  		// Does the state exists in DB.
   285  		if s.beaconDB.HasState(ctx, parentRoot) {
   286  			return s.beaconDB.State(ctx, parentRoot)
   287  		}
   288  		b, err = s.beaconDB.Block(ctx, parentRoot)
   289  		if err != nil {
   290  			return nil, err
   291  		}
   292  		if b == nil || b.IsNil() {
   293  			return nil, errUnknownBlock
   294  		}
   295  	}
   296  }