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

     1  package stategen
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/pkg/errors"
     8  	types "github.com/prysmaticlabs/eth2-types"
     9  	transition "github.com/prysmaticlabs/prysm/beacon-chain/core/state"
    10  	"github.com/prysmaticlabs/prysm/beacon-chain/db/filters"
    11  	iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface"
    12  	"github.com/prysmaticlabs/prysm/proto/interfaces"
    13  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    14  	"go.opencensus.io/trace"
    15  )
    16  
    17  // ReplayBlocks replays the input blocks on the input state until the target slot is reached.
    18  func (s *State) ReplayBlocks(
    19  	ctx context.Context,
    20  	state iface.BeaconState,
    21  	signed []interfaces.SignedBeaconBlock,
    22  	targetSlot types.Slot,
    23  ) (iface.BeaconState, error) {
    24  	ctx, span := trace.StartSpan(ctx, "stateGen.ReplayBlocks")
    25  	defer span.End()
    26  
    27  	var err error
    28  	log.Debugf("Replaying state from slot %d till slot %d", state.Slot(), targetSlot)
    29  	// The input block list is sorted in decreasing slots order.
    30  	if len(signed) > 0 {
    31  		for i := len(signed) - 1; i >= 0; i-- {
    32  			if ctx.Err() != nil {
    33  				return nil, ctx.Err()
    34  			}
    35  			if state.Slot() >= targetSlot {
    36  				break
    37  			}
    38  			// A node shouldn't process the block if the block slot is lower than the state slot.
    39  			if state.Slot() >= signed[i].Block().Slot() {
    40  				continue
    41  			}
    42  			state, err = executeStateTransitionStateGen(ctx, state, signed[i])
    43  			if err != nil {
    44  				return nil, err
    45  			}
    46  		}
    47  	}
    48  
    49  	// If there is skip slots at the end.
    50  	if targetSlot > state.Slot() {
    51  		state, err = processSlotsStateGen(ctx, state, targetSlot)
    52  		if err != nil {
    53  			return nil, err
    54  		}
    55  	}
    56  
    57  	return state, nil
    58  }
    59  
    60  // LoadBlocks loads the blocks between start slot and end slot by recursively fetching from end block root.
    61  // The Blocks are returned in slot-descending order.
    62  func (s *State) LoadBlocks(ctx context.Context, startSlot, endSlot types.Slot, endBlockRoot [32]byte) ([]interfaces.SignedBeaconBlock, error) {
    63  	// Nothing to load for invalid range.
    64  	if endSlot < startSlot {
    65  		return nil, fmt.Errorf("start slot %d >= end slot %d", startSlot, endSlot)
    66  	}
    67  	filter := filters.NewFilter().SetStartSlot(startSlot).SetEndSlot(endSlot)
    68  	blocks, blockRoots, err := s.beaconDB.Blocks(ctx, filter)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  	// The retrieved blocks and block roots have to be in the same length given same filter.
    73  	if len(blocks) != len(blockRoots) {
    74  		return nil, errors.New("length of blocks and roots don't match")
    75  	}
    76  	// Return early if there's no block given the input.
    77  	length := len(blocks)
    78  	if length == 0 {
    79  		return nil, nil
    80  	}
    81  
    82  	// The last retrieved block root has to match input end block root.
    83  	// Covers the edge case if there's multiple blocks on the same end slot,
    84  	// the end root may not be the last index in `blockRoots`.
    85  	for length >= 3 && blocks[length-1].Block().Slot() == blocks[length-2].Block().Slot() && blockRoots[length-1] != endBlockRoot {
    86  		if ctx.Err() != nil {
    87  			return nil, ctx.Err()
    88  		}
    89  		length--
    90  		if blockRoots[length-2] == endBlockRoot {
    91  			length--
    92  			break
    93  		}
    94  	}
    95  
    96  	if blockRoots[length-1] != endBlockRoot {
    97  		return nil, errors.New("end block roots don't match")
    98  	}
    99  
   100  	filteredBlocks := []interfaces.SignedBeaconBlock{blocks[length-1]}
   101  	// Starting from second to last index because the last block is already in the filtered block list.
   102  	for i := length - 2; i >= 0; i-- {
   103  		if ctx.Err() != nil {
   104  			return nil, ctx.Err()
   105  		}
   106  		b := filteredBlocks[len(filteredBlocks)-1]
   107  		if bytesutil.ToBytes32(b.Block().ParentRoot()) != blockRoots[i] {
   108  			continue
   109  		}
   110  		filteredBlocks = append(filteredBlocks, blocks[i])
   111  	}
   112  
   113  	return filteredBlocks, nil
   114  }
   115  
   116  // executeStateTransitionStateGen applies state transition on input historical state and block for state gen usages.
   117  // There's no signature verification involved given state gen only works with stored block and state in DB.
   118  // If the objects are already in stored in DB, one can omit redundant signature checks and ssz hashing calculations.
   119  // WARNING: This method should not be used on an unverified new block.
   120  func executeStateTransitionStateGen(
   121  	ctx context.Context,
   122  	state iface.BeaconState,
   123  	signed interfaces.SignedBeaconBlock,
   124  ) (iface.BeaconState, error) {
   125  	if ctx.Err() != nil {
   126  		return nil, ctx.Err()
   127  	}
   128  	if signed == nil || signed.IsNil() || signed.Block().IsNil() {
   129  		return nil, errUnknownBlock
   130  	}
   131  
   132  	ctx, span := trace.StartSpan(ctx, "stategen.ExecuteStateTransitionStateGen")
   133  	defer span.End()
   134  	var err error
   135  
   136  	// Execute per slots transition.
   137  	// Given this is for state gen, a node uses the version process slots without skip slots cache.
   138  	state, err = processSlotsStateGen(ctx, state, signed.Block().Slot())
   139  	if err != nil {
   140  		return nil, errors.Wrap(err, "could not process slot")
   141  	}
   142  
   143  	// Execute per block transition.
   144  	// Given this is for state gen, a node only cares about the post state without proposer
   145  	// and randao signature verifications.
   146  	state, err = transition.ProcessBlockForStateRoot(ctx, state, signed)
   147  	if err != nil {
   148  		return nil, errors.Wrap(err, "could not process block")
   149  	}
   150  
   151  	return state, nil
   152  }
   153  
   154  // processSlotsStateGen to process old slots for state gen usages.
   155  // There's no skip slot cache involved given state gen only works with already stored block and state in DB.
   156  // WARNING: This method should not be used for future slot.
   157  func processSlotsStateGen(ctx context.Context, state iface.BeaconState, slot types.Slot) (iface.BeaconState, error) {
   158  	ctx, span := trace.StartSpan(ctx, "stategen.ProcessSlotsStateGen")
   159  	defer span.End()
   160  	if state == nil || state.IsNil() {
   161  		return nil, errUnknownState
   162  	}
   163  
   164  	if state.Slot() > slot {
   165  		err := fmt.Errorf("expected state.slot %d < slot %d", state.Slot(), slot)
   166  		return nil, err
   167  	}
   168  
   169  	if state.Slot() == slot {
   170  		return state, nil
   171  	}
   172  
   173  	var err error
   174  	for state.Slot() < slot {
   175  		state, err = transition.ProcessSlot(ctx, state)
   176  		if err != nil {
   177  			return nil, errors.Wrap(err, "could not process slot")
   178  		}
   179  		if transition.CanProcessEpoch(state) {
   180  			state, err = transition.ProcessEpochPrecompute(ctx, state)
   181  			if err != nil {
   182  				return nil, errors.Wrap(err, "could not process epoch with optimizations")
   183  			}
   184  		}
   185  		if err := state.SetSlot(state.Slot() + 1); err != nil {
   186  			return nil, err
   187  		}
   188  	}
   189  
   190  	return state, nil
   191  }
   192  
   193  // This finds the last saved block in DB from searching backwards from input slot,
   194  // it returns the block root and the slot of the block.
   195  // This is used by both hot and cold state management.
   196  func (s *State) lastSavedBlock(ctx context.Context, slot types.Slot) ([32]byte, types.Slot, error) {
   197  	ctx, span := trace.StartSpan(ctx, "stateGen.lastSavedBlock")
   198  	defer span.End()
   199  
   200  	// Handle the genesis case where the input slot is 0.
   201  	if slot == 0 {
   202  		gRoot, err := s.genesisRoot(ctx)
   203  		if err != nil {
   204  			return [32]byte{}, 0, err
   205  		}
   206  		return gRoot, 0, nil
   207  	}
   208  
   209  	lastSaved, err := s.beaconDB.HighestSlotBlocksBelow(ctx, slot+1)
   210  	if err != nil {
   211  		return [32]byte{}, 0, err
   212  	}
   213  
   214  	// Given this is used to query canonical block. There should only be one saved canonical block of a given slot.
   215  	if len(lastSaved) != 1 {
   216  		return [32]byte{}, 0, fmt.Errorf("highest saved block does not equal to 1, it equals to %d", len(lastSaved))
   217  	}
   218  	if lastSaved[0] == nil || lastSaved[0].IsNil() || lastSaved[0].Block().IsNil() {
   219  		return [32]byte{}, 0, nil
   220  	}
   221  	r, err := lastSaved[0].Block().HashTreeRoot()
   222  	if err != nil {
   223  		return [32]byte{}, 0, err
   224  	}
   225  
   226  	return r, lastSaved[0].Block().Slot(), nil
   227  }
   228  
   229  // This finds the last saved state in DB from searching backwards from input slot,
   230  // it returns the block root of the block which was used to produce the state.
   231  // This is used by both hot and cold state management.
   232  func (s *State) lastSavedState(ctx context.Context, slot types.Slot) (iface.ReadOnlyBeaconState, error) {
   233  	ctx, span := trace.StartSpan(ctx, "stateGen.lastSavedState")
   234  	defer span.End()
   235  
   236  	// Handle the genesis case where the input slot is 0.
   237  	if slot == 0 {
   238  		return s.beaconDB.GenesisState(ctx)
   239  	}
   240  
   241  	lastSaved, err := s.beaconDB.HighestSlotStatesBelow(ctx, slot+1)
   242  	if err != nil {
   243  		return nil, err
   244  	}
   245  
   246  	// Given this is used to query canonical state. There should only be one saved canonical block of a given slot.
   247  	if len(lastSaved) != 1 {
   248  		return nil, fmt.Errorf("highest saved state does not equal to 1, it equals to %d", len(lastSaved))
   249  	}
   250  	if lastSaved[0] == nil {
   251  		return nil, errUnknownState
   252  	}
   253  
   254  	return lastSaved[0], nil
   255  }
   256  
   257  // This returns the genesis root.
   258  func (s *State) genesisRoot(ctx context.Context) ([32]byte, error) {
   259  	b, err := s.beaconDB.GenesisBlock(ctx)
   260  	if err != nil {
   261  		return [32]byte{}, err
   262  	}
   263  	return b.Block().HashTreeRoot()
   264  }
   265  
   266  // Given the start slot and the end slot, this returns the finalized beacon blocks in between.
   267  // Since hot states don't have finalized blocks, this should ONLY be used for replaying cold state.
   268  func (s *State) loadFinalizedBlocks(ctx context.Context, startSlot, endSlot types.Slot) ([]interfaces.SignedBeaconBlock, error) {
   269  	f := filters.NewFilter().SetStartSlot(startSlot).SetEndSlot(endSlot)
   270  	bs, bRoots, err := s.beaconDB.Blocks(ctx, f)
   271  	if err != nil {
   272  		return nil, err
   273  	}
   274  	if len(bs) != len(bRoots) {
   275  		return nil, errors.New("length of blocks and roots don't match")
   276  	}
   277  	fbs := make([]interfaces.SignedBeaconBlock, 0, len(bs))
   278  	for i := len(bs) - 1; i >= 0; i-- {
   279  		if s.beaconDB.IsFinalizedBlock(ctx, bRoots[i]) {
   280  			fbs = append(fbs, bs[i])
   281  		}
   282  	}
   283  	return fbs, nil
   284  }