From 168e06e6074e101ca45036b46fbedf5f135fc199 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Sun, 6 Feb 2022 10:00:47 -0800 Subject: [PATCH] Save sync tips to DB (#10171) * Save sync tips to DB * Fix build * Update process_block_test.go * Copy map * Revert back to nil Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com> --- beacon-chain/blockchain/process_block.go | 17 +++++++ beacon-chain/blockchain/process_block_test.go | 46 +++++++++++++++++++ beacon-chain/db/iface/interface.go | 2 + beacon-chain/forkchoice/interfaces.go | 8 ++++ beacon-chain/forkchoice/protoarray/store.go | 12 +++++ 5 files changed, 85 insertions(+) diff --git a/beacon-chain/blockchain/process_block.go b/beacon-chain/blockchain/process_block.go index db1556e4b3..553f99ff9d 100644 --- a/beacon-chain/blockchain/process_block.go +++ b/beacon-chain/blockchain/process_block.go @@ -175,6 +175,10 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b log.WithError(err).Warn("Could not update head") } + if err := s.saveSyncedTipsDB(ctx); err != nil { + return errors.Wrap(err, "could not save synced tips") + } + if err := s.pruneCanonicalAttsFromPool(ctx, blockRoot, signed); err != nil { return err } @@ -330,6 +334,10 @@ func (s *Service) handleBlockAfterBatchVerify(ctx context.Context, signed block. if err := s.insertBlockToForkChoiceStore(ctx, b, blockRoot, fCheckpoint, jCheckpoint); err != nil { return err } + if err := s.saveSyncedTipsDB(ctx); err != nil { + return errors.Wrap(err, "could not save synced tips") + } + if err := s.cfg.BeaconDB.SaveStateSummary(ctx, ðpb.StateSummary{ Slot: signed.Block().Slot(), Root: blockRoot[:], @@ -501,3 +509,12 @@ func (s *Service) pruneCanonicalAttsFromPool(ctx context.Context, r [32]byte, b } return nil } + +// Saves synced and validated tips to DB. +func (s *Service) saveSyncedTipsDB(ctx context.Context) error { + tips := s.cfg.ForkChoiceStore.SyncedTips() + if len(tips) == 0 { + return nil + } + return s.cfg.BeaconDB.UpdateValidatedTips(ctx, tips) +} diff --git a/beacon-chain/blockchain/process_block_test.go b/beacon-chain/blockchain/process_block_test.go index 6cae2a22b2..d24ee3e85f 100644 --- a/beacon-chain/blockchain/process_block_test.go +++ b/beacon-chain/blockchain/process_block_test.go @@ -996,3 +996,49 @@ func TestRemoveBlockAttestationsInPool_NonCanonical(t *testing.T) { require.NoError(t, service.pruneCanonicalAttsFromPool(ctx, r, wrapper.WrappedPhase0SignedBeaconBlock(b))) require.Equal(t, 1, service.cfg.AttPool.AggregatedAttestationCount()) } + +func TestService_saveSyncedTipsDB(t *testing.T) { + ctx := context.Background() + beaconDB := testDB.SetupDB(t) + service := setupBeaconChain(t, beaconDB) + + b1 := util.NewBeaconBlock() + b1.Block.Slot = 1 + b1.Block.ParentRoot = bytesutil.PadTo([]byte{'a'}, 32) + r1, err := b1.Block.HashTreeRoot() + require.NoError(t, err) + b100 := util.NewBeaconBlock() + b100.Block.Slot = 100 + b100.Block.ParentRoot = r1[:] + r100, err := b100.Block.HashTreeRoot() + require.NoError(t, err) + b200 := util.NewBeaconBlock() + b200.Block.Slot = 200 + b200.Block.ParentRoot = r1[:] + r200, err := b200.Block.HashTreeRoot() + require.NoError(t, err) + for _, b := range []*ethpb.SignedBeaconBlock{b1, b100, b200} { + beaconBlock := util.NewBeaconBlock() + beaconBlock.Block.Slot = b.Block.Slot + beaconBlock.Block.ParentRoot = bytesutil.PadTo(b.Block.ParentRoot, 32) + r, err := b.Block.HashTreeRoot() + require.NoError(t, err) + require.NoError(t, service.cfg.ForkChoiceStore.ProcessBlock(context.Background(), b.Block.Slot, r, bytesutil.ToBytes32(b.Block.ParentRoot), [32]byte{}, 0, 0)) + } + + require.NoError(t, service.cfg.ForkChoiceStore.UpdateSyncedTipsWithValidRoot(ctx, r100)) + require.NoError(t, service.saveSyncedTipsDB(ctx)) + savedTips, err := service.cfg.BeaconDB.ValidatedTips(ctx) + require.NoError(t, err) + require.Equal(t, 2, len(savedTips)) + require.Equal(t, types.Slot(1), savedTips[r1]) + require.Equal(t, types.Slot(100), savedTips[r100]) + + // Delete invalid root + require.NoError(t, service.cfg.ForkChoiceStore.UpdateSyncedTipsWithInvalidRoot(ctx, r200)) + require.NoError(t, service.saveSyncedTipsDB(ctx)) + savedTips, err = service.cfg.BeaconDB.ValidatedTips(ctx) + require.NoError(t, err) + require.Equal(t, 1, len(savedTips)) + require.Equal(t, types.Slot(100), savedTips[r100]) +} diff --git a/beacon-chain/db/iface/interface.go b/beacon-chain/db/iface/interface.go index ad900097f7..febc80e00f 100644 --- a/beacon-chain/db/iface/interface.go +++ b/beacon-chain/db/iface/interface.go @@ -30,6 +30,7 @@ type ReadOnlyDatabase interface { IsFinalizedBlock(ctx context.Context, blockRoot [32]byte) bool FinalizedChildBlock(ctx context.Context, blockRoot [32]byte) (block.SignedBeaconBlock, error) HighestSlotBlocksBelow(ctx context.Context, slot types.Slot) ([]block.SignedBeaconBlock, error) + ValidatedTips(ctx context.Context) (map[[32]byte]types.Slot, error) // State related methods. State(ctx context.Context, blockRoot [32]byte) (state.BeaconState, error) GenesisState(ctx context.Context) (state.BeaconState, error) @@ -61,6 +62,7 @@ type NoHeadAccessDatabase interface { SaveBlock(ctx context.Context, block block.SignedBeaconBlock) error SaveBlocks(ctx context.Context, blocks []block.SignedBeaconBlock) error SaveGenesisBlockRoot(ctx context.Context, blockRoot [32]byte) error + UpdateValidatedTips(ctx context.Context, newVals map[[32]byte]types.Slot) error // State related methods. SaveState(ctx context.Context, state state.ReadOnlyBeaconState, blockRoot [32]byte) error SaveStates(ctx context.Context, states []state.ReadOnlyBeaconState, blockRoots [][32]byte) error diff --git a/beacon-chain/forkchoice/interfaces.go b/beacon-chain/forkchoice/interfaces.go index a1729e7436..854bea762b 100644 --- a/beacon-chain/forkchoice/interfaces.go +++ b/beacon-chain/forkchoice/interfaces.go @@ -16,6 +16,7 @@ type ForkChoicer interface { Pruner // to clean old data for fork choice. Getter // to retrieve fork choice information. ProposerBooster // ability to boost timely-proposed block roots. + SyncTipper // to update and retrieve validated sync tips. } // HeadRetriever retrieves head root and optimistic info of the current chain. @@ -55,3 +56,10 @@ type Getter interface { AncestorRoot(ctx context.Context, root [32]byte, slot types.Slot) ([]byte, error) IsCanonical(root [32]byte) bool } + +// SyncTipper returns sync tips related information. +type SyncTipper interface { + SyncedTips() map[[32]byte]types.Slot + UpdateSyncedTipsWithValidRoot(ctx context.Context, root [32]byte) error + UpdateSyncedTipsWithInvalidRoot(ctx context.Context, root [32]byte) error +} diff --git a/beacon-chain/forkchoice/protoarray/store.go b/beacon-chain/forkchoice/protoarray/store.go index e8f414568d..eb6225d7cc 100644 --- a/beacon-chain/forkchoice/protoarray/store.go +++ b/beacon-chain/forkchoice/protoarray/store.go @@ -40,6 +40,18 @@ func New(justifiedEpoch, finalizedEpoch types.Epoch, finalizedRoot [32]byte) *Fo return &ForkChoice{store: s, balances: b, votes: v, syncedTips: st} } +// SyncedTips returns the synced and validated tips from the fork choice store. +func (f *ForkChoice) SyncedTips() map[[32]byte]types.Slot { + f.syncedTips.RLock() + defer f.syncedTips.RUnlock() + + m := make(map[[32]byte]types.Slot) + for k, v := range f.syncedTips.validatedTips { + m[k] = v + } + return m +} + // Head returns the head root from fork choice store. // It firsts computes validator's balance changes then recalculates block tree from leaves to root. func (f *ForkChoice) Head(