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 }