diff --git a/beacon-chain/blockchain/BUILD.bazel b/beacon-chain/blockchain/BUILD.bazel index 78ca6aaf7f..317a731ec4 100644 --- a/beacon-chain/blockchain/BUILD.bazel +++ b/beacon-chain/blockchain/BUILD.bazel @@ -26,6 +26,7 @@ go_library( "receive_blob.go", "receive_block.go", "service.go", + "setup_forchoice.go", "tracked_proposer.go", "weak_subjectivity_checks.go", ], diff --git a/beacon-chain/blockchain/process_block_test.go b/beacon-chain/blockchain/process_block_test.go index a41e5a178d..b7f4a39e21 100644 --- a/beacon-chain/blockchain/process_block_test.go +++ b/beacon-chain/blockchain/process_block_test.go @@ -2039,7 +2039,7 @@ func TestNoViableHead_Reboot(t *testing.T) { require.Equal(t, genesisRoot, bytesutil.ToBytes32(headRoot)) optimistic, err := service.IsOptimistic(ctx) require.NoError(t, err) - require.Equal(t, false, optimistic) + require.Equal(t, true, optimistic) // Check that the node's justified checkpoint does not agree with the // last valid state's justified checkpoint diff --git a/beacon-chain/blockchain/service.go b/beacon-chain/blockchain/service.go index dbd7685714..98cb0beb28 100644 --- a/beacon-chain/blockchain/service.go +++ b/beacon-chain/blockchain/service.go @@ -3,7 +3,6 @@ package blockchain import ( - "bytes" "context" "fmt" "runtime" @@ -23,7 +22,6 @@ import ( "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/filesystem" "github.com/prysmaticlabs/prysm/v5/beacon-chain/execution" f "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice" - forkchoicetypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/types" "github.com/prysmaticlabs/prysm/v5/beacon-chain/operations/attestations" "github.com/prysmaticlabs/prysm/v5/beacon-chain/operations/blstoexec" "github.com/prysmaticlabs/prysm/v5/beacon-chain/operations/slashings" @@ -32,7 +30,6 @@ import ( "github.com/prysmaticlabs/prysm/v5/beacon-chain/startup" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stategen" - "github.com/prysmaticlabs/prysm/v5/config/features" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces" @@ -269,69 +266,18 @@ func (s *Service) StartFromSavedState(saved state.BeaconState) error { return err } s.originBlockRoot = originRoot - - if err := s.initializeHeadFromDB(s.ctx); err != nil { - return errors.Wrap(err, "could not set up chain info") + st, err := s.cfg.StateGen.Resume(s.ctx, s.cfg.FinalizedStateAtStartUp) + if err != nil { + return errors.Wrap(err, "could not get finalized state from db") } spawnCountdownIfPreGenesis(s.ctx, s.genesisTime, s.cfg.BeaconDB) - - justified, err := s.cfg.BeaconDB.JustifiedCheckpoint(s.ctx) - if err != nil { - return errors.Wrap(err, "could not get justified checkpoint") - } - if justified == nil { - return errNilJustifiedCheckpoint - } - finalized, err := s.cfg.BeaconDB.FinalizedCheckpoint(s.ctx) - if err != nil { - return errors.Wrap(err, "could not get finalized checkpoint") - } - if finalized == nil { - return errNilFinalizedCheckpoint - } - - fRoot := s.ensureRootNotZeros(bytesutil.ToBytes32(finalized.Root)) - s.cfg.ForkChoiceStore.Lock() - defer s.cfg.ForkChoiceStore.Unlock() - if err := s.cfg.ForkChoiceStore.UpdateJustifiedCheckpoint(s.ctx, &forkchoicetypes.Checkpoint{Epoch: justified.Epoch, - Root: bytesutil.ToBytes32(justified.Root)}); err != nil { - return errors.Wrap(err, "could not update forkchoice's justified checkpoint") - } - if err := s.cfg.ForkChoiceStore.UpdateFinalizedCheckpoint(&forkchoicetypes.Checkpoint{Epoch: finalized.Epoch, - Root: bytesutil.ToBytes32(finalized.Root)}); err != nil { - return errors.Wrap(err, "could not update forkchoice's finalized checkpoint") - } - s.cfg.ForkChoiceStore.SetGenesisTime(uint64(s.genesisTime.Unix())) - - st, err := s.cfg.StateGen.StateByRoot(s.ctx, fRoot) - if err != nil { - return errors.Wrap(err, "could not get finalized checkpoint state") - } - finalizedBlock, err := s.cfg.BeaconDB.Block(s.ctx, fRoot) - if err != nil { - return errors.Wrap(err, "could not get finalized checkpoint block") - } - roblock, err := blocks.NewROBlockWithRoot(finalizedBlock, fRoot) - if err != nil { - return err - } - if err := s.cfg.ForkChoiceStore.InsertNode(s.ctx, st, roblock); err != nil { - return errors.Wrap(err, "could not insert finalized block to forkchoice") - } - if !features.Get().EnableStartOptimistic { - lastValidatedCheckpoint, err := s.cfg.BeaconDB.LastValidatedCheckpoint(s.ctx) - if err != nil { - return errors.Wrap(err, "could not get last validated checkpoint") - } - if bytes.Equal(finalized.Root, lastValidatedCheckpoint.Root) { - if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(s.ctx, fRoot); err != nil { - return errors.Wrap(err, "could not set finalized block as validated") - } - } + if err := s.setupForkchoice(st); err != nil { + return errors.Wrap(err, "could not set up forkchoice") } // not attempting to save initial sync blocks here, because there shouldn't be any until // after the statefeed.Initialized event is fired (below) - if err := s.wsVerifier.VerifyWeakSubjectivity(s.ctx, finalized.Epoch); err != nil { + cp := s.FinalizedCheckpt() + if err := s.wsVerifier.VerifyWeakSubjectivity(s.ctx, cp.Epoch); err != nil { // Exit run time if the node failed to verify weak subjectivity checkpoint. return errors.Wrap(err, "could not verify initial checkpoint provided for chain sync") } @@ -340,7 +286,6 @@ func (s *Service) StartFromSavedState(saved state.BeaconState) error { if err := s.clockSetter.SetClock(startup.NewClock(s.genesisTime, vr)); err != nil { return errors.Wrap(err, "failed to initialize blockchain service") } - return nil } @@ -373,23 +318,10 @@ func (s *Service) originRootFromSavedState(ctx context.Context) ([32]byte, error // initializeHeadFromDB uses the finalized checkpoint and head block found in the database to set the current head. // Note that this may block until stategen replays blocks between the finalized and head blocks // if the head sync flag was specified and the gap between the finalized and head blocks is at least 128 epochs long. -func (s *Service) initializeHeadFromDB(ctx context.Context) error { - finalized, err := s.cfg.BeaconDB.FinalizedCheckpoint(ctx) - if err != nil { - return errors.Wrap(err, "could not get finalized checkpoint from db") - } - if finalized == nil { - // This should never happen. At chain start, the finalized checkpoint - // would be the genesis state and block. - return errors.New("no finalized epoch in the database") - } - finalizedRoot := s.ensureRootNotZeros(bytesutil.ToBytes32(finalized.Root)) - var finalizedState state.BeaconState - - finalizedState, err = s.cfg.StateGen.Resume(ctx, s.cfg.FinalizedStateAtStartUp) - if err != nil { - return errors.Wrap(err, "could not get finalized state from db") - } +func (s *Service) initializeHeadFromDB(ctx context.Context, finalizedState state.BeaconState) error { + cp := s.FinalizedCheckpt() + fRoot := [32]byte(cp.Root) + finalizedRoot := s.ensureRootNotZeros(fRoot) if finalizedState == nil || finalizedState.IsNil() { return errors.New("finalized state can't be nil") diff --git a/beacon-chain/blockchain/setup_forchoice.go b/beacon-chain/blockchain/setup_forchoice.go new file mode 100644 index 0000000000..3ba8d1592f --- /dev/null +++ b/beacon-chain/blockchain/setup_forchoice.go @@ -0,0 +1,84 @@ +package blockchain + +import ( + "bytes" + + "github.com/pkg/errors" + forkchoicetypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/types" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" + "github.com/prysmaticlabs/prysm/v5/config/features" + "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" + "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" +) + +func (s *Service) setupForkchoice(st state.BeaconState) error { + if err := s.setupForkchoiceCheckpoints(); err != nil { + return errors.Wrap(err, "could not set up forkchoice checkpoints") + } + if err := s.setupForkchoiceRoot(st); err != nil { + return errors.Wrap(err, "could not set up forkchoice root") + } + if err := s.initializeHeadFromDB(s.ctx, st); err != nil { + return errors.Wrap(err, "could not initialize head from db") + } + return nil +} + +func (s *Service) setupForkchoiceRoot(st state.BeaconState) error { + cp := s.FinalizedCheckpt() + fRoot := s.ensureRootNotZeros([32]byte(cp.Root)) + finalizedBlock, err := s.cfg.BeaconDB.Block(s.ctx, fRoot) + if err != nil { + return errors.Wrap(err, "could not get finalized checkpoint block") + } + roblock, err := blocks.NewROBlockWithRoot(finalizedBlock, fRoot) + if err != nil { + return err + } + if err := s.cfg.ForkChoiceStore.InsertNode(s.ctx, st, roblock); err != nil { + return errors.Wrap(err, "could not insert finalized block to forkchoice") + } + if !features.Get().EnableStartOptimistic { + lastValidatedCheckpoint, err := s.cfg.BeaconDB.LastValidatedCheckpoint(s.ctx) + if err != nil { + return errors.Wrap(err, "could not get last validated checkpoint") + } + if bytes.Equal(fRoot[:], lastValidatedCheckpoint.Root) { + if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(s.ctx, fRoot); err != nil { + return errors.Wrap(err, "could not set finalized block as validated") + } + } + } + return nil +} + +func (s *Service) setupForkchoiceCheckpoints() error { + justified, err := s.cfg.BeaconDB.JustifiedCheckpoint(s.ctx) + if err != nil { + return errors.Wrap(err, "could not get justified checkpoint") + } + if justified == nil { + return errNilJustifiedCheckpoint + } + finalized, err := s.cfg.BeaconDB.FinalizedCheckpoint(s.ctx) + if err != nil { + return errors.Wrap(err, "could not get finalized checkpoint") + } + if finalized == nil { + return errNilFinalizedCheckpoint + } + + fRoot := s.ensureRootNotZeros(bytesutil.ToBytes32(finalized.Root)) + s.cfg.ForkChoiceStore.Lock() + defer s.cfg.ForkChoiceStore.Unlock() + if err := s.cfg.ForkChoiceStore.UpdateJustifiedCheckpoint(s.ctx, &forkchoicetypes.Checkpoint{Epoch: justified.Epoch, + Root: bytesutil.ToBytes32(justified.Root)}); err != nil { + return errors.Wrap(err, "could not update forkchoice's justified checkpoint") + } + if err := s.cfg.ForkChoiceStore.UpdateFinalizedCheckpoint(&forkchoicetypes.Checkpoint{Epoch: finalized.Epoch, + Root: fRoot}); err != nil { + return errors.Wrap(err, "could not update forkchoice's finalized checkpoint") + } + s.cfg.ForkChoiceStore.SetGenesisTime(uint64(s.genesisTime.Unix())) + return nil +} diff --git a/changelog/potuz_forkchoice_startup.md b/changelog/potuz_forkchoice_startup.md new file mode 100644 index 0000000000..a217c38ee1 --- /dev/null +++ b/changelog/potuz_forkchoice_startup.md @@ -0,0 +1,3 @@ +### Ignored + +- Split out forkchoice startup from the main service startup.