github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/blockchain/service.go (about)

     1  // Package blockchain defines the life-cycle of the blockchain at the core of
     2  // Ethereum, including processing of new blocks and attestations using proof of stake.
     3  package blockchain
     4  
     5  import (
     6  	"context"
     7  	"fmt"
     8  	"runtime"
     9  	"sync"
    10  	"time"
    11  
    12  	"github.com/pkg/errors"
    13  	types "github.com/prysmaticlabs/eth2-types"
    14  	"github.com/prysmaticlabs/prysm/beacon-chain/cache"
    15  	"github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache"
    16  	"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
    17  	statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state"
    18  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    19  	"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
    20  	"github.com/prysmaticlabs/prysm/beacon-chain/db"
    21  	f "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice"
    22  	"github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/protoarray"
    23  	"github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations"
    24  	"github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings"
    25  	"github.com/prysmaticlabs/prysm/beacon-chain/operations/voluntaryexits"
    26  	"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
    27  	"github.com/prysmaticlabs/prysm/beacon-chain/powchain"
    28  	iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface"
    29  	"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
    30  	"github.com/prysmaticlabs/prysm/cmd/beacon-chain/flags"
    31  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    32  	"github.com/prysmaticlabs/prysm/proto/interfaces"
    33  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    34  	"github.com/prysmaticlabs/prysm/shared/copyutil"
    35  	"github.com/prysmaticlabs/prysm/shared/params"
    36  	"github.com/prysmaticlabs/prysm/shared/slotutil"
    37  	"github.com/sirupsen/logrus"
    38  	"go.opencensus.io/trace"
    39  )
    40  
    41  // headSyncMinEpochsAfterCheckpoint defines how many epochs should elapse after known finalization
    42  // checkpoint for head sync to be triggered.
    43  const headSyncMinEpochsAfterCheckpoint = 128
    44  
    45  // Service represents a service that handles the internal
    46  // logic of managing the full PoS beacon chain.
    47  type Service struct {
    48  	cfg                   *Config
    49  	ctx                   context.Context
    50  	cancel                context.CancelFunc
    51  	genesisTime           time.Time
    52  	head                  *head
    53  	headLock              sync.RWMutex
    54  	genesisRoot           [32]byte
    55  	justifiedCheckpt      *ethpb.Checkpoint
    56  	prevJustifiedCheckpt  *ethpb.Checkpoint
    57  	bestJustifiedCheckpt  *ethpb.Checkpoint
    58  	finalizedCheckpt      *ethpb.Checkpoint
    59  	prevFinalizedCheckpt  *ethpb.Checkpoint
    60  	nextEpochBoundarySlot types.Slot
    61  	boundaryRoots         [][32]byte
    62  	checkpointStateCache  *cache.CheckpointStateCache
    63  	initSyncBlocks        map[[32]byte]interfaces.SignedBeaconBlock
    64  	initSyncBlocksLock    sync.RWMutex
    65  	justifiedBalances     []uint64
    66  	justifiedBalancesLock sync.RWMutex
    67  	wsVerified            bool
    68  }
    69  
    70  // Config options for the service.
    71  type Config struct {
    72  	BeaconBlockBuf          int
    73  	ChainStartFetcher       powchain.ChainStartFetcher
    74  	BeaconDB                db.HeadAccessDatabase
    75  	DepositCache            *depositcache.DepositCache
    76  	AttPool                 attestations.Pool
    77  	ExitPool                voluntaryexits.PoolManager
    78  	SlashingPool            slashings.PoolManager
    79  	P2p                     p2p.Broadcaster
    80  	MaxRoutines             int
    81  	StateNotifier           statefeed.Notifier
    82  	ForkChoiceStore         f.ForkChoicer
    83  	AttService              *attestations.Service
    84  	StateGen                *stategen.State
    85  	WeakSubjectivityCheckpt *ethpb.Checkpoint
    86  }
    87  
    88  // NewService instantiates a new block service instance that will
    89  // be registered into a running beacon node.
    90  func NewService(ctx context.Context, cfg *Config) (*Service, error) {
    91  	ctx, cancel := context.WithCancel(ctx)
    92  	return &Service{
    93  		cfg:                  cfg,
    94  		ctx:                  ctx,
    95  		cancel:               cancel,
    96  		boundaryRoots:        [][32]byte{},
    97  		checkpointStateCache: cache.NewCheckpointStateCache(),
    98  		initSyncBlocks:       make(map[[32]byte]interfaces.SignedBeaconBlock),
    99  		justifiedBalances:    make([]uint64, 0),
   100  	}, nil
   101  }
   102  
   103  // Start a blockchain service's main event loop.
   104  func (s *Service) Start() {
   105  	// For running initial sync with state cache, in an event of restart, we use
   106  	// last finalized check point as start point to sync instead of head
   107  	// state. This is because we no longer save state every slot during sync.
   108  	cp, err := s.cfg.BeaconDB.FinalizedCheckpoint(s.ctx)
   109  	if err != nil {
   110  		log.Fatalf("Could not fetch finalized cp: %v", err)
   111  	}
   112  
   113  	r := bytesutil.ToBytes32(cp.Root)
   114  	// Before the first finalized epoch, in the current epoch,
   115  	// the finalized root is defined as zero hashes instead of genesis root hash.
   116  	// We want to use genesis root to retrieve for state.
   117  	if r == params.BeaconConfig().ZeroHash {
   118  		genesisBlock, err := s.cfg.BeaconDB.GenesisBlock(s.ctx)
   119  		if err != nil {
   120  			log.Fatalf("Could not fetch finalized cp: %v", err)
   121  		}
   122  		if genesisBlock != nil && !genesisBlock.IsNil() {
   123  			r, err = genesisBlock.Block().HashTreeRoot()
   124  			if err != nil {
   125  				log.Fatalf("Could not tree hash genesis block: %v", err)
   126  			}
   127  		}
   128  	}
   129  	beaconState, err := s.cfg.StateGen.StateByRoot(s.ctx, r)
   130  	if err != nil {
   131  		log.Fatalf("Could not fetch beacon state by root: %v", err)
   132  	}
   133  
   134  	// Make sure that attestation processor is subscribed and ready for state initializing event.
   135  	attestationProcessorSubscribed := make(chan struct{}, 1)
   136  
   137  	// If the chain has already been initialized, simply start the block processing routine.
   138  	if beaconState != nil && !beaconState.IsNil() {
   139  		log.Info("Blockchain data already exists in DB, initializing...")
   140  		s.genesisTime = time.Unix(int64(beaconState.GenesisTime()), 0)
   141  		s.cfg.AttService.SetGenesisTime(beaconState.GenesisTime())
   142  		if err := s.initializeChainInfo(s.ctx); err != nil {
   143  			log.Fatalf("Could not set up chain info: %v", err)
   144  		}
   145  
   146  		// We start a counter to genesis, if needed.
   147  		gState, err := s.cfg.BeaconDB.GenesisState(s.ctx)
   148  		if err != nil {
   149  			log.Fatalf("Could not retrieve genesis state: %v", err)
   150  		}
   151  		gRoot, err := gState.HashTreeRoot(s.ctx)
   152  		if err != nil {
   153  			log.Fatalf("Could not hash tree root genesis state: %v", err)
   154  		}
   155  		go slotutil.CountdownToGenesis(s.ctx, s.genesisTime, uint64(gState.NumValidators()), gRoot)
   156  
   157  		justifiedCheckpoint, err := s.cfg.BeaconDB.JustifiedCheckpoint(s.ctx)
   158  		if err != nil {
   159  			log.Fatalf("Could not get justified checkpoint: %v", err)
   160  		}
   161  		finalizedCheckpoint, err := s.cfg.BeaconDB.FinalizedCheckpoint(s.ctx)
   162  		if err != nil {
   163  			log.Fatalf("Could not get finalized checkpoint: %v", err)
   164  		}
   165  
   166  		// Resume fork choice.
   167  		s.justifiedCheckpt = copyutil.CopyCheckpoint(justifiedCheckpoint)
   168  		if err := s.cacheJustifiedStateBalances(s.ctx, s.ensureRootNotZeros(bytesutil.ToBytes32(s.justifiedCheckpt.Root))); err != nil {
   169  			log.Fatalf("Could not cache justified state balances: %v", err)
   170  		}
   171  		s.prevJustifiedCheckpt = copyutil.CopyCheckpoint(justifiedCheckpoint)
   172  		s.bestJustifiedCheckpt = copyutil.CopyCheckpoint(justifiedCheckpoint)
   173  		s.finalizedCheckpt = copyutil.CopyCheckpoint(finalizedCheckpoint)
   174  		s.prevFinalizedCheckpt = copyutil.CopyCheckpoint(finalizedCheckpoint)
   175  		s.resumeForkChoice(justifiedCheckpoint, finalizedCheckpoint)
   176  
   177  		ss, err := helpers.StartSlot(s.finalizedCheckpt.Epoch)
   178  		if err != nil {
   179  			log.Fatalf("Could not get start slot of finalized epoch: %v", err)
   180  		}
   181  		h := s.headBlock().Block()
   182  		if h.Slot() > ss {
   183  			log.WithFields(logrus.Fields{
   184  				"startSlot": ss,
   185  				"endSlot":   h.Slot(),
   186  			}).Info("Loading blocks to fork choice store, this may take a while.")
   187  			if err := s.fillInForkChoiceMissingBlocks(s.ctx, h, s.finalizedCheckpt, s.justifiedCheckpt); err != nil {
   188  				log.Fatalf("Could not fill in fork choice store missing blocks: %v", err)
   189  			}
   190  		}
   191  
   192  		if err := s.VerifyWeakSubjectivityRoot(s.ctx); err != nil {
   193  			// Exit run time if the node failed to verify weak subjectivity checkpoint.
   194  			log.Fatalf("Could not verify weak subjectivity checkpoint: %v", err)
   195  		}
   196  
   197  		s.cfg.StateNotifier.StateFeed().Send(&feed.Event{
   198  			Type: statefeed.Initialized,
   199  			Data: &statefeed.InitializedData{
   200  				StartTime:             s.genesisTime,
   201  				GenesisValidatorsRoot: beaconState.GenesisValidatorRoot(),
   202  			},
   203  		})
   204  	} else {
   205  		log.Info("Waiting to reach the validator deposit threshold to start the beacon chain...")
   206  		if s.cfg.ChainStartFetcher == nil {
   207  			log.Fatal("Not configured web3Service for POW chain")
   208  			return // return need for TestStartUninitializedChainWithoutConfigPOWChain.
   209  		}
   210  		go func() {
   211  			stateChannel := make(chan *feed.Event, 1)
   212  			stateSub := s.cfg.StateNotifier.StateFeed().Subscribe(stateChannel)
   213  			defer stateSub.Unsubscribe()
   214  			<-attestationProcessorSubscribed
   215  			for {
   216  				select {
   217  				case event := <-stateChannel:
   218  					if event.Type == statefeed.ChainStarted {
   219  						data, ok := event.Data.(*statefeed.ChainStartedData)
   220  						if !ok {
   221  							log.Error("event data is not type *statefeed.ChainStartedData")
   222  							return
   223  						}
   224  						log.WithField("starttime", data.StartTime).Debug("Received chain start event")
   225  						s.processChainStartTime(s.ctx, data.StartTime)
   226  						return
   227  					}
   228  				case <-s.ctx.Done():
   229  					log.Debug("Context closed, exiting goroutine")
   230  					return
   231  				case err := <-stateSub.Err():
   232  					log.WithError(err).Error("Subscription to state notifier failed")
   233  					return
   234  				}
   235  			}
   236  		}()
   237  	}
   238  
   239  	go s.processAttestationsRoutine(attestationProcessorSubscribed)
   240  }
   241  
   242  // processChainStartTime initializes a series of deposits from the ChainStart deposits in the eth1
   243  // deposit contract, initializes the beacon chain's state, and kicks off the beacon chain.
   244  func (s *Service) processChainStartTime(ctx context.Context, genesisTime time.Time) {
   245  	preGenesisState := s.cfg.ChainStartFetcher.PreGenesisState()
   246  	initializedState, err := s.initializeBeaconChain(ctx, genesisTime, preGenesisState, s.cfg.ChainStartFetcher.ChainStartEth1Data())
   247  	if err != nil {
   248  		log.Fatalf("Could not initialize beacon chain: %v", err)
   249  	}
   250  	// We start a counter to genesis, if needed.
   251  	gRoot, err := initializedState.HashTreeRoot(s.ctx)
   252  	if err != nil {
   253  		log.Fatalf("Could not hash tree root genesis state: %v", err)
   254  	}
   255  	go slotutil.CountdownToGenesis(ctx, genesisTime, uint64(initializedState.NumValidators()), gRoot)
   256  
   257  	// We send out a state initialized event to the rest of the services
   258  	// running in the beacon node.
   259  	s.cfg.StateNotifier.StateFeed().Send(&feed.Event{
   260  		Type: statefeed.Initialized,
   261  		Data: &statefeed.InitializedData{
   262  			StartTime:             genesisTime,
   263  			GenesisValidatorsRoot: initializedState.GenesisValidatorRoot(),
   264  		},
   265  	})
   266  }
   267  
   268  // initializes the state and genesis block of the beacon chain to persistent storage
   269  // based on a genesis timestamp value obtained from the ChainStart event emitted
   270  // by the ETH1.0 Deposit Contract and the POWChain service of the node.
   271  func (s *Service) initializeBeaconChain(
   272  	ctx context.Context,
   273  	genesisTime time.Time,
   274  	preGenesisState iface.BeaconState,
   275  	eth1data *ethpb.Eth1Data) (iface.BeaconState, error) {
   276  	ctx, span := trace.StartSpan(ctx, "beacon-chain.Service.initializeBeaconChain")
   277  	defer span.End()
   278  	s.genesisTime = genesisTime
   279  	unixTime := uint64(genesisTime.Unix())
   280  
   281  	genesisState, err := state.OptimizedGenesisBeaconState(unixTime, preGenesisState, eth1data)
   282  	if err != nil {
   283  		return nil, errors.Wrap(err, "could not initialize genesis state")
   284  	}
   285  
   286  	if err := s.saveGenesisData(ctx, genesisState); err != nil {
   287  		return nil, errors.Wrap(err, "could not save genesis data")
   288  	}
   289  
   290  	log.Info("Initialized beacon chain genesis state")
   291  
   292  	// Clear out all pre-genesis data now that the state is initialized.
   293  	s.cfg.ChainStartFetcher.ClearPreGenesisData()
   294  
   295  	// Update committee shuffled indices for genesis epoch.
   296  	if err := helpers.UpdateCommitteeCache(genesisState, 0 /* genesis epoch */); err != nil {
   297  		return nil, err
   298  	}
   299  	if err := helpers.UpdateProposerIndicesInCache(genesisState); err != nil {
   300  		return nil, err
   301  	}
   302  
   303  	s.cfg.AttService.SetGenesisTime(genesisState.GenesisTime())
   304  
   305  	return genesisState, nil
   306  }
   307  
   308  // Stop the blockchain service's main event loop and associated goroutines.
   309  func (s *Service) Stop() error {
   310  	defer s.cancel()
   311  
   312  	if s.cfg.StateGen != nil && s.head != nil && s.head.state != nil {
   313  		if err := s.cfg.StateGen.ForceCheckpoint(s.ctx, s.head.state.FinalizedCheckpoint().Root); err != nil {
   314  			return err
   315  		}
   316  	}
   317  
   318  	// Save initial sync cached blocks to the DB before stop.
   319  	return s.cfg.BeaconDB.SaveBlocks(s.ctx, s.getInitSyncBlocks())
   320  }
   321  
   322  // Status always returns nil unless there is an error condition that causes
   323  // this service to be unhealthy.
   324  func (s *Service) Status() error {
   325  	if s.genesisRoot == params.BeaconConfig().ZeroHash {
   326  		return errors.New("genesis state has not been created")
   327  	}
   328  	if runtime.NumGoroutine() > s.cfg.MaxRoutines {
   329  		return fmt.Errorf("too many goroutines %d", runtime.NumGoroutine())
   330  	}
   331  	return nil
   332  }
   333  
   334  // This gets called when beacon chain is first initialized to save genesis data (state, block, and more) in db.
   335  func (s *Service) saveGenesisData(ctx context.Context, genesisState iface.BeaconState) error {
   336  	if err := s.cfg.BeaconDB.SaveGenesisData(ctx, genesisState); err != nil {
   337  		return errors.Wrap(err, "could not save genesis data")
   338  	}
   339  	genesisBlk, err := s.cfg.BeaconDB.GenesisBlock(ctx)
   340  	if err != nil || genesisBlk == nil || genesisBlk.IsNil() {
   341  		return fmt.Errorf("could not load genesis block: %v", err)
   342  	}
   343  	genesisBlkRoot, err := genesisBlk.Block().HashTreeRoot()
   344  	if err != nil {
   345  		return errors.Wrap(err, "could not get genesis block root")
   346  	}
   347  
   348  	s.genesisRoot = genesisBlkRoot
   349  	s.cfg.StateGen.SaveFinalizedState(0 /*slot*/, genesisBlkRoot, genesisState)
   350  
   351  	// Finalized checkpoint at genesis is a zero hash.
   352  	genesisCheckpoint := genesisState.FinalizedCheckpoint()
   353  
   354  	s.justifiedCheckpt = copyutil.CopyCheckpoint(genesisCheckpoint)
   355  	if err := s.cacheJustifiedStateBalances(ctx, genesisBlkRoot); err != nil {
   356  		return err
   357  	}
   358  	s.prevJustifiedCheckpt = copyutil.CopyCheckpoint(genesisCheckpoint)
   359  	s.bestJustifiedCheckpt = copyutil.CopyCheckpoint(genesisCheckpoint)
   360  	s.finalizedCheckpt = copyutil.CopyCheckpoint(genesisCheckpoint)
   361  	s.prevFinalizedCheckpt = copyutil.CopyCheckpoint(genesisCheckpoint)
   362  
   363  	if err := s.cfg.ForkChoiceStore.ProcessBlock(ctx,
   364  		genesisBlk.Block().Slot(),
   365  		genesisBlkRoot,
   366  		params.BeaconConfig().ZeroHash,
   367  		[32]byte{},
   368  		genesisCheckpoint.Epoch,
   369  		genesisCheckpoint.Epoch); err != nil {
   370  		log.Fatalf("Could not process genesis block for fork choice: %v", err)
   371  	}
   372  
   373  	s.setHead(genesisBlkRoot, genesisBlk, genesisState)
   374  	return nil
   375  }
   376  
   377  // This gets called to initialize chain info variables using the finalized checkpoint stored in DB
   378  func (s *Service) initializeChainInfo(ctx context.Context) error {
   379  	genesisBlock, err := s.cfg.BeaconDB.GenesisBlock(ctx)
   380  	if err != nil {
   381  		return errors.Wrap(err, "could not get genesis block from db")
   382  	}
   383  	if genesisBlock == nil || genesisBlock.IsNil() {
   384  		return errors.New("no genesis block in db")
   385  	}
   386  	genesisBlkRoot, err := genesisBlock.Block().HashTreeRoot()
   387  	if err != nil {
   388  		return errors.Wrap(err, "could not get signing root of genesis block")
   389  	}
   390  	s.genesisRoot = genesisBlkRoot
   391  
   392  	finalized, err := s.cfg.BeaconDB.FinalizedCheckpoint(ctx)
   393  	if err != nil {
   394  		return errors.Wrap(err, "could not get finalized checkpoint from db")
   395  	}
   396  	if finalized == nil {
   397  		// This should never happen. At chain start, the finalized checkpoint
   398  		// would be the genesis state and block.
   399  		return errors.New("no finalized epoch in the database")
   400  	}
   401  	finalizedRoot := s.ensureRootNotZeros(bytesutil.ToBytes32(finalized.Root))
   402  	var finalizedState iface.BeaconState
   403  
   404  	finalizedState, err = s.cfg.StateGen.Resume(ctx)
   405  	if err != nil {
   406  		return errors.Wrap(err, "could not get finalized state from db")
   407  	}
   408  
   409  	if flags.Get().HeadSync {
   410  		headBlock, err := s.cfg.BeaconDB.HeadBlock(ctx)
   411  		if err != nil {
   412  			return errors.Wrap(err, "could not retrieve head block")
   413  		}
   414  		headEpoch := helpers.SlotToEpoch(headBlock.Block().Slot())
   415  		var epochsSinceFinality types.Epoch
   416  		if headEpoch > finalized.Epoch {
   417  			epochsSinceFinality = headEpoch - finalized.Epoch
   418  		}
   419  		// Head sync when node is far enough beyond known finalized epoch,
   420  		// this becomes really useful during long period of non-finality.
   421  		if epochsSinceFinality >= headSyncMinEpochsAfterCheckpoint {
   422  			headRoot, err := headBlock.Block().HashTreeRoot()
   423  			if err != nil {
   424  				return errors.Wrap(err, "could not hash head block")
   425  			}
   426  			finalizedState, err := s.cfg.StateGen.Resume(ctx)
   427  			if err != nil {
   428  				return errors.Wrap(err, "could not get finalized state from db")
   429  			}
   430  			log.Infof("Regenerating state from the last checkpoint at slot %d to current head slot of %d."+
   431  				"This process may take a while, please wait.", finalizedState.Slot(), headBlock.Block().Slot())
   432  			headState, err := s.cfg.StateGen.StateByRoot(ctx, headRoot)
   433  			if err != nil {
   434  				return errors.Wrap(err, "could not retrieve head state")
   435  			}
   436  			s.setHead(headRoot, headBlock, headState)
   437  			return nil
   438  		} else {
   439  			log.Warnf("Finalized checkpoint at slot %d is too close to the current head slot, "+
   440  				"resetting head from the checkpoint ('--%s' flag is ignored).",
   441  				finalizedState.Slot(), flags.HeadSync.Name)
   442  		}
   443  	}
   444  
   445  	finalizedBlock, err := s.cfg.BeaconDB.Block(ctx, finalizedRoot)
   446  	if err != nil {
   447  		return errors.Wrap(err, "could not get finalized block from db")
   448  	}
   449  
   450  	if finalizedState == nil || finalizedState.IsNil() || finalizedBlock == nil || finalizedBlock.IsNil() {
   451  		return errors.New("finalized state and block can't be nil")
   452  	}
   453  	s.setHead(finalizedRoot, finalizedBlock, finalizedState)
   454  
   455  	return nil
   456  }
   457  
   458  // This is called when a client starts from non-genesis slot. This passes last justified and finalized
   459  // information to fork choice service to initializes fork choice store.
   460  func (s *Service) resumeForkChoice(justifiedCheckpoint, finalizedCheckpoint *ethpb.Checkpoint) {
   461  	store := protoarray.New(justifiedCheckpoint.Epoch, finalizedCheckpoint.Epoch, bytesutil.ToBytes32(finalizedCheckpoint.Root))
   462  	s.cfg.ForkChoiceStore = store
   463  }
   464  
   465  // This returns true if block has been processed before. Two ways to verify the block has been processed:
   466  // 1.) Check fork choice store.
   467  // 2.) Check DB.
   468  // Checking 1.) is ten times faster than checking 2.)
   469  func (s *Service) hasBlock(ctx context.Context, root [32]byte) bool {
   470  	if s.cfg.ForkChoiceStore.HasNode(root) {
   471  		return true
   472  	}
   473  
   474  	return s.cfg.BeaconDB.HasBlock(ctx, root)
   475  }