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

     1  package stategen
     2  
     3  import (
     4  	"context"
     5  	"encoding/hex"
     6  	"fmt"
     7  
     8  	iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface"
     9  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    10  	"github.com/sirupsen/logrus"
    11  	"go.opencensus.io/trace"
    12  )
    13  
    14  // MigrateToCold advances the finalized info in between the cold and hot state sections.
    15  // It moves the recent finalized states from the hot section to the cold section and
    16  // only preserve the ones that's on archived point.
    17  func (s *State) MigrateToCold(ctx context.Context, fRoot [32]byte) error {
    18  	ctx, span := trace.StartSpan(ctx, "stateGen.MigrateToCold")
    19  	defer span.End()
    20  
    21  	s.finalizedInfo.lock.RLock()
    22  	oldFSlot := s.finalizedInfo.slot
    23  	s.finalizedInfo.lock.RUnlock()
    24  
    25  	fBlock, err := s.beaconDB.Block(ctx, fRoot)
    26  	if err != nil {
    27  		return err
    28  	}
    29  	fSlot := fBlock.Block().Slot()
    30  	if oldFSlot > fSlot {
    31  		return nil
    32  	}
    33  
    34  	// Start at previous finalized slot, stop at current finalized slot.
    35  	// If the slot is on archived point, save the state of that slot to the DB.
    36  	for slot := oldFSlot; slot < fSlot; slot++ {
    37  		if ctx.Err() != nil {
    38  			return ctx.Err()
    39  		}
    40  
    41  		if slot%s.slotsPerArchivedPoint == 0 && slot != 0 {
    42  			cached, exists, err := s.epochBoundaryStateCache.getBySlot(slot)
    43  			if err != nil {
    44  				return fmt.Errorf("could not get epoch boundary state for slot %d", slot)
    45  			}
    46  
    47  			var aRoot [32]byte
    48  			var aState iface.BeaconState
    49  
    50  			// When the epoch boundary state is not in cache due to skip slot scenario,
    51  			// we have to regenerate the state which will represent epoch boundary.
    52  			// By finding the highest available block below epoch boundary slot, we
    53  			// generate the state for that block root.
    54  			if exists {
    55  				aRoot = cached.root
    56  				aState = cached.state
    57  			} else {
    58  				blks, err := s.beaconDB.HighestSlotBlocksBelow(ctx, slot)
    59  				if err != nil {
    60  					return err
    61  				}
    62  				// Given the block has been finalized, the db should not have more than one block in a given slot.
    63  				// We should error out when this happens.
    64  				if len(blks) != 1 {
    65  					return errUnknownBlock
    66  				}
    67  				missingRoot, err := blks[0].Block().HashTreeRoot()
    68  				if err != nil {
    69  					return err
    70  				}
    71  				aRoot = missingRoot
    72  				// There's no need to generate the state if the state already exists on the DB.
    73  				// We can skip saving the state.
    74  				if !s.beaconDB.HasState(ctx, aRoot) {
    75  					aState, err = s.StateByRoot(ctx, missingRoot)
    76  					if err != nil {
    77  						return err
    78  					}
    79  				}
    80  			}
    81  
    82  			if s.beaconDB.HasState(ctx, aRoot) {
    83  				// Remove hot state DB root to prevent it gets deleted later when we turn hot state save DB mode off.
    84  				s.saveHotStateDB.lock.Lock()
    85  				roots := s.saveHotStateDB.savedStateRoots
    86  				for i := 0; i < len(roots); i++ {
    87  					if aRoot == roots[i] {
    88  						s.saveHotStateDB.savedStateRoots = append(roots[:i], roots[i+1:]...)
    89  						// There shouldn't be duplicated roots in `savedStateRoots`.
    90  						// Break here is ok.
    91  						break
    92  					}
    93  				}
    94  				s.saveHotStateDB.lock.Unlock()
    95  				continue
    96  			}
    97  
    98  			if err := s.beaconDB.SaveState(ctx, aState, aRoot); err != nil {
    99  				return err
   100  			}
   101  			log.WithFields(
   102  				logrus.Fields{
   103  					"slot": aState.Slot(),
   104  					"root": hex.EncodeToString(bytesutil.Trunc(aRoot[:])),
   105  				}).Info("Saved state in DB")
   106  		}
   107  	}
   108  
   109  	// Update finalized info in memory.
   110  	fInfo, ok, err := s.epochBoundaryStateCache.getByRoot(fRoot)
   111  	if err != nil {
   112  		return err
   113  	}
   114  	if ok {
   115  		s.SaveFinalizedState(fSlot, fRoot, fInfo.state)
   116  	}
   117  
   118  	return nil
   119  }