diff --git a/beacon-chain/blockchain/process_block_test.go b/beacon-chain/blockchain/process_block_test.go index 361fed66b3..2df46751c7 100644 --- a/beacon-chain/blockchain/process_block_test.go +++ b/beacon-chain/blockchain/process_block_test.go @@ -3006,7 +3006,6 @@ func TestStore_NoViableHead_Reboot_DoublyLinkedTree(t *testing.T) { headRoot, err := service.HeadRoot(ctx) require.NoError(t, err) require.Equal(t, genesisRoot, bytesutil.ToBytes32(headRoot)) - // The node is optimistic now. optimistic, err := service.IsOptimistic(ctx) require.NoError(t, err) require.Equal(t, false, optimistic) @@ -3230,7 +3229,6 @@ func TestStore_NoViableHead_Reboot_Protoarray(t *testing.T) { headRoot, err := service.HeadRoot(ctx) require.NoError(t, err) require.Equal(t, genesisRoot, bytesutil.ToBytes32(headRoot)) - // The node is optimistic now optimistic, err := service.IsOptimistic(ctx) require.NoError(t, err) require.Equal(t, false, optimistic) diff --git a/beacon-chain/blockchain/service.go b/beacon-chain/blockchain/service.go index 5d9d9b6d67..c6ed822d38 100644 --- a/beacon-chain/blockchain/service.go +++ b/beacon-chain/blockchain/service.go @@ -231,14 +231,15 @@ func (s *Service) StartFromSavedState(saved state.BeaconState) error { if err := forkChoicer.InsertNode(s.ctx, st, fRoot); err != nil { return errors.Wrap(err, "could not insert finalized block to forkchoice") } - - 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 := forkChoicer.SetOptimisticToValid(s.ctx, fRoot); err != nil { - return errors.Wrap(err, "could not set finalized block as validated") + 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 := forkChoicer.SetOptimisticToValid(s.ctx, fRoot); err != nil { + return errors.Wrap(err, "could not set finalized block as validated") + } } } // not attempting to save initial sync blocks here, because there shouldn't be any until diff --git a/beacon-chain/blockchain/service_test.go b/beacon-chain/blockchain/service_test.go index e1e896c472..e972908579 100644 --- a/beacon-chain/blockchain/service_test.go +++ b/beacon-chain/blockchain/service_test.go @@ -26,6 +26,7 @@ import ( "github.com/prysmaticlabs/prysm/v3/beacon-chain/p2p" "github.com/prysmaticlabs/prysm/v3/beacon-chain/state/stategen" v1 "github.com/prysmaticlabs/prysm/v3/beacon-chain/state/v1" + "github.com/prysmaticlabs/prysm/v3/config/features" "github.com/prysmaticlabs/prysm/v3/config/params" consensusblocks "github.com/prysmaticlabs/prysm/v3/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces" @@ -528,3 +529,45 @@ func BenchmarkHasBlockForkChoiceStore_DoublyLinkedTree(b *testing.B) { require.Equal(b, true, s.cfg.ForkChoiceStore.HasNode(r), "Block is not in fork choice store") } } + +func TestChainService_EverythingOptimistic(t *testing.T) { + resetFn := features.InitWithReset(&features.Flags{ + EnableStartOptimistic: true, + }) + defer resetFn() + beaconDB := testDB.SetupDB(t) + ctx := context.Background() + + genesis := util.NewBeaconBlock() + genesisRoot, err := genesis.Block.HashTreeRoot() + require.NoError(t, err) + require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, genesisRoot)) + util.SaveBlock(t, ctx, beaconDB, genesis) + + finalizedSlot := params.BeaconConfig().SlotsPerEpoch*2 + 1 + headBlock := util.NewBeaconBlock() + headBlock.Block.Slot = finalizedSlot + headBlock.Block.ParentRoot = bytesutil.PadTo(genesisRoot[:], 32) + headState, err := util.NewBeaconState() + require.NoError(t, err) + require.NoError(t, headState.SetSlot(finalizedSlot)) + require.NoError(t, headState.SetGenesisValidatorsRoot(params.BeaconConfig().ZeroHash[:])) + headRoot, err := headBlock.Block.HashTreeRoot() + require.NoError(t, err) + require.NoError(t, beaconDB.SaveState(ctx, headState, headRoot)) + require.NoError(t, beaconDB.SaveState(ctx, headState, genesisRoot)) + util.SaveBlock(t, ctx, beaconDB, headBlock) + require.NoError(t, beaconDB.SaveFinalizedCheckpoint(ctx, ðpb.Checkpoint{Epoch: slots.ToEpoch(finalizedSlot), Root: headRoot[:]})) + attSrv, err := attestations.NewService(ctx, &attestations.Config{}) + require.NoError(t, err) + stateGen := stategen.New(beaconDB) + c, err := NewService(ctx, WithDatabase(beaconDB), WithStateGen(stateGen), WithAttestationService(attSrv), WithStateNotifier(&mock.MockStateNotifier{}), WithFinalizedStateAtStartUp(headState)) + require.NoError(t, err) + require.NoError(t, stateGen.SaveState(ctx, headRoot, headState)) + require.NoError(t, beaconDB.SaveLastValidatedCheckpoint(ctx, ðpb.Checkpoint{Epoch: slots.ToEpoch(finalizedSlot), Root: headRoot[:]})) + require.NoError(t, c.StartFromSavedState(headState)) + require.Equal(t, true, c.cfg.ForkChoiceStore.HasNode(headRoot)) + op, err := c.cfg.ForkChoiceStore.IsOptimistic(headRoot) + require.NoError(t, err) + require.Equal(t, true, op) +} diff --git a/config/features/config.go b/config/features/config.go index 713083166b..0a9ffcf426 100644 --- a/config/features/config.go +++ b/config/features/config.go @@ -66,6 +66,7 @@ type Flags struct { DisableForkchoiceDoublyLinkedTree bool // DisableForkChoiceDoublyLinkedTree specifies whether fork choice store will use a doubly linked tree. EnableBatchGossipAggregation bool // EnableBatchGossipAggregation specifies whether to further aggregate our gossip batches before verifying them. EnableOnlyBlindedBeaconBlocks bool // EnableOnlyBlindedBeaconBlocks enables only storing blinded beacon blocks in the DB post-Bellatrix fork. + EnableStartOptimistic bool // EnableStartOptimistic treats every block as optimistic at startup. // KeystoreImportDebounceInterval specifies the time duration the validator waits to reload new keys if they have // changed on disk. This feature is for advanced use cases only. @@ -241,6 +242,10 @@ func ConfigureBeaconChain(ctx *cli.Context) error { logEnabled(EnableOnlyBlindedBeaconBlocks) cfg.EnableOnlyBlindedBeaconBlocks = true } + if ctx.Bool(enableStartupOptimistic.Name) { + logEnabled(enableStartupOptimistic) + cfg.EnableStartOptimistic = true + } Init(cfg) return nil } diff --git a/config/features/flags.go b/config/features/flags.go index f0879b187d..ac4e63b30c 100644 --- a/config/features/flags.go +++ b/config/features/flags.go @@ -113,6 +113,12 @@ var ( Name: "enable-only-blinded-beacon-blocks", Usage: "Enables storing only blinded beacon blocks in the database without full execution layer transactions", } + enableStartupOptimistic = &cli.BoolFlag{ + Name: "startup-optimistic", + Usage: "Treats every block as optimistically synced at launch. Use with caution", + Value: false, + Hidden: true, + } ) // devModeFlags holds list of flags that are set when development mode is on. @@ -156,6 +162,7 @@ var BeaconChainFlags = append(deprecatedBeaconFlags, append(deprecatedFlags, []c disableForkChoiceDoublyLinkedTree, disableGossipBatchAggregation, EnableOnlyBlindedBeaconBlocks, + enableStartupOptimistic, }...)...) // E2EBeaconChainFlags contains a list of the beacon chain feature flags to be tested in E2E.