github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/sync/initial-sync/round_robin_test.go (about)

     1  package initialsync
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/paulbellamy/ratecounter"
     9  	types "github.com/prysmaticlabs/eth2-types"
    10  	mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
    11  	dbtest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
    12  	p2pt "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing"
    13  	eth "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    14  	"github.com/prysmaticlabs/prysm/proto/eth/v1alpha1/wrapper"
    15  	"github.com/prysmaticlabs/prysm/proto/interfaces"
    16  	"github.com/prysmaticlabs/prysm/shared/abool"
    17  	"github.com/prysmaticlabs/prysm/shared/sliceutil"
    18  	"github.com/prysmaticlabs/prysm/shared/testutil"
    19  	"github.com/prysmaticlabs/prysm/shared/testutil/assert"
    20  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    21  	logTest "github.com/sirupsen/logrus/hooks/test"
    22  )
    23  
    24  func TestService_roundRobinSync(t *testing.T) {
    25  	tests := []struct {
    26  		name                string
    27  		currentSlot         types.Slot
    28  		availableBlockSlots []types.Slot
    29  		expectedBlockSlots  []types.Slot
    30  		peers               []*peerData
    31  	}{
    32  		{
    33  			name:                "Single peer with no finalized blocks",
    34  			currentSlot:         2,
    35  			availableBlockSlots: makeSequence(1, 32),
    36  			expectedBlockSlots:  makeSequence(1, 2),
    37  			peers: []*peerData{
    38  				{
    39  					blocks:         makeSequence(1, 2),
    40  					finalizedEpoch: 0,
    41  					headSlot:       2,
    42  				},
    43  			},
    44  		},
    45  		{
    46  			name:                "Multiple peers with no finalized blocks",
    47  			currentSlot:         2,
    48  			availableBlockSlots: makeSequence(1, 32),
    49  			expectedBlockSlots:  makeSequence(1, 2),
    50  			peers: []*peerData{
    51  				{
    52  					blocks:         makeSequence(1, 2),
    53  					finalizedEpoch: 0,
    54  					headSlot:       2,
    55  				},
    56  				{
    57  					blocks:         makeSequence(1, 2),
    58  					finalizedEpoch: 0,
    59  					headSlot:       2,
    60  				},
    61  				{
    62  					blocks:         makeSequence(1, 2),
    63  					finalizedEpoch: 0,
    64  					headSlot:       2,
    65  				},
    66  			},
    67  		},
    68  		{
    69  			name:                "Single peer with all blocks",
    70  			currentSlot:         131,
    71  			availableBlockSlots: makeSequence(1, 192),
    72  			expectedBlockSlots:  makeSequence(1, 131),
    73  			peers: []*peerData{
    74  				{
    75  					blocks:         makeSequence(1, 192),
    76  					finalizedEpoch: 1,
    77  					headSlot:       131,
    78  				},
    79  			},
    80  		},
    81  		{
    82  			name:                "Multiple peers with all blocks",
    83  			currentSlot:         131,
    84  			availableBlockSlots: makeSequence(1, 192),
    85  			expectedBlockSlots:  makeSequence(1, 131),
    86  			peers: []*peerData{
    87  				{
    88  					blocks:         makeSequence(1, 192),
    89  					finalizedEpoch: 1,
    90  					headSlot:       131,
    91  				},
    92  				{
    93  					blocks:         makeSequence(1, 192),
    94  					finalizedEpoch: 1,
    95  					headSlot:       131,
    96  				},
    97  				{
    98  					blocks:         makeSequence(1, 192),
    99  					finalizedEpoch: 1,
   100  					headSlot:       131,
   101  				},
   102  				{
   103  					blocks:         makeSequence(1, 192),
   104  					finalizedEpoch: 1,
   105  					headSlot:       131,
   106  				},
   107  			},
   108  		},
   109  		{
   110  			name:                "Multiple peers with failures",
   111  			currentSlot:         320, // 10 epochs
   112  			availableBlockSlots: makeSequence(1, 384),
   113  			expectedBlockSlots:  makeSequence(1, 320),
   114  			peers: []*peerData{
   115  				{
   116  					blocks:         makeSequence(1, 384),
   117  					finalizedEpoch: 8,
   118  					headSlot:       320,
   119  				},
   120  				{
   121  					blocks:         makeSequence(1, 384),
   122  					finalizedEpoch: 8,
   123  					headSlot:       320,
   124  					failureSlots:   makeSequence(1, 32), // first epoch
   125  				},
   126  				{
   127  					blocks:         makeSequence(1, 384),
   128  					finalizedEpoch: 8,
   129  					headSlot:       320,
   130  				},
   131  				{
   132  					blocks:         makeSequence(1, 384),
   133  					finalizedEpoch: 8,
   134  					headSlot:       320,
   135  				},
   136  			},
   137  		},
   138  		{
   139  			name:                "Multiple peers with many skipped slots",
   140  			currentSlot:         1280,
   141  			availableBlockSlots: append(makeSequence(1, 64), makeSequence(1000, 1300)...),
   142  			expectedBlockSlots:  append(makeSequence(1, 64), makeSequence(1000, 1280)...),
   143  			peers: []*peerData{
   144  				{
   145  					blocks:         append(makeSequence(1, 64), makeSequence(1000, 1300)...),
   146  					finalizedEpoch: 36,
   147  					headSlot:       1280,
   148  				},
   149  				{
   150  					blocks:         append(makeSequence(1, 64), makeSequence(1000, 1300)...),
   151  					finalizedEpoch: 36,
   152  					headSlot:       1280,
   153  				},
   154  				{
   155  					blocks:         append(makeSequence(1, 64), makeSequence(1000, 1300)...),
   156  					finalizedEpoch: 36,
   157  					headSlot:       1280,
   158  				},
   159  			},
   160  		},
   161  		{
   162  			name:                "Multiple peers with multiple failures",
   163  			currentSlot:         320, // 10 epochs
   164  			availableBlockSlots: makeSequence(1, 384),
   165  			expectedBlockSlots:  makeSequence(1, 320),
   166  			peers: []*peerData{
   167  				{
   168  					blocks:         makeSequence(1, 384),
   169  					finalizedEpoch: 9,
   170  					headSlot:       384,
   171  				},
   172  				{
   173  					blocks:         makeSequence(1, 320),
   174  					finalizedEpoch: 9,
   175  					headSlot:       384,
   176  					failureSlots:   makeSequence(1, 320),
   177  				},
   178  				{
   179  					blocks:         makeSequence(1, 320),
   180  					finalizedEpoch: 9,
   181  					headSlot:       384,
   182  					failureSlots:   makeSequence(1, 320),
   183  				},
   184  				{
   185  					blocks:         makeSequence(1, 320),
   186  					finalizedEpoch: 9,
   187  					headSlot:       384,
   188  					failureSlots:   makeSequence(1, 320),
   189  				},
   190  			},
   191  		},
   192  		{
   193  			name:                "Multiple peers with different finalized epoch",
   194  			currentSlot:         320, // 10 epochs
   195  			availableBlockSlots: makeSequence(1, 384),
   196  			expectedBlockSlots:  makeSequence(1, 320),
   197  			peers: []*peerData{
   198  				{
   199  					blocks:         makeSequence(1, 384),
   200  					finalizedEpoch: 10,
   201  					headSlot:       384,
   202  				},
   203  				{
   204  					blocks:         makeSequence(1, 384),
   205  					finalizedEpoch: 10,
   206  					headSlot:       384,
   207  				},
   208  				{
   209  					blocks:         makeSequence(1, 256),
   210  					finalizedEpoch: 5,
   211  					headSlot:       256,
   212  				},
   213  				{
   214  					blocks:         makeSequence(1, 192),
   215  					finalizedEpoch: 2,
   216  					headSlot:       192,
   217  				},
   218  			},
   219  		},
   220  		{
   221  			name:                "Multiple peers with missing parent blocks",
   222  			currentSlot:         160, // 5 epochs
   223  			availableBlockSlots: makeSequence(1, 192),
   224  			expectedBlockSlots:  makeSequence(1, 160),
   225  			peers: []*peerData{
   226  				{
   227  					blocks:         makeSequence(1, 192),
   228  					finalizedEpoch: 4,
   229  					headSlot:       160,
   230  				},
   231  				{
   232  					blocks:         append(makeSequence(1, 6), makeSequence(161, 165)...),
   233  					finalizedEpoch: 4,
   234  					headSlot:       160,
   235  					forkedPeer:     true,
   236  				},
   237  				{
   238  					blocks:         makeSequence(1, 192),
   239  					finalizedEpoch: 4,
   240  					headSlot:       160,
   241  				},
   242  				{
   243  					blocks:         makeSequence(1, 192),
   244  					finalizedEpoch: 4,
   245  					headSlot:       160,
   246  				},
   247  				{
   248  					blocks:         makeSequence(1, 192),
   249  					finalizedEpoch: 4,
   250  					headSlot:       160,
   251  				},
   252  				{
   253  					blocks:         makeSequence(1, 192),
   254  					finalizedEpoch: 4,
   255  					headSlot:       160,
   256  				},
   257  				{
   258  					blocks:         makeSequence(1, 192),
   259  					finalizedEpoch: 4,
   260  					headSlot:       160,
   261  				},
   262  				{
   263  					blocks:         makeSequence(1, 192),
   264  					finalizedEpoch: 4,
   265  					headSlot:       160,
   266  				},
   267  			},
   268  		},
   269  	}
   270  
   271  	for _, tt := range tests {
   272  		t.Run(tt.name, func(t *testing.T) {
   273  			if tt.availableBlockSlots == nil {
   274  				tt.availableBlockSlots = tt.expectedBlockSlots
   275  			}
   276  			cache.initializeRootCache(tt.availableBlockSlots, t)
   277  
   278  			p := p2pt.NewTestP2P(t)
   279  			beaconDB := dbtest.SetupDB(t)
   280  
   281  			connectPeers(t, p, tt.peers, p.Peers())
   282  			cache.RLock()
   283  			genesisRoot := cache.rootCache[0]
   284  			cache.RUnlock()
   285  
   286  			err := beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(testutil.NewBeaconBlock()))
   287  			require.NoError(t, err)
   288  
   289  			st, err := testutil.NewBeaconState()
   290  			require.NoError(t, err)
   291  			mc := &mock.ChainService{
   292  				State: st,
   293  				Root:  genesisRoot[:],
   294  				DB:    beaconDB,
   295  				FinalizedCheckPoint: &eth.Checkpoint{
   296  					Epoch: 0,
   297  				},
   298  			} // no-op mock
   299  			s := &Service{
   300  				ctx:          context.Background(),
   301  				cfg:          &Config{Chain: mc, P2P: p, DB: beaconDB},
   302  				synced:       abool.New(),
   303  				chainStarted: abool.NewBool(true),
   304  			}
   305  			assert.NoError(t, s.roundRobinSync(makeGenesisTime(tt.currentSlot)))
   306  			if s.cfg.Chain.HeadSlot() < tt.currentSlot {
   307  				t.Errorf("Head slot (%d) is less than expected currentSlot (%d)", s.cfg.Chain.HeadSlot(), tt.currentSlot)
   308  			}
   309  			assert.Equal(t, true, len(tt.expectedBlockSlots) <= len(mc.BlocksReceived), "Processes wrong number of blocks")
   310  			var receivedBlockSlots []types.Slot
   311  			for _, blk := range mc.BlocksReceived {
   312  				receivedBlockSlots = append(receivedBlockSlots, blk.Block().Slot())
   313  			}
   314  			missing := sliceutil.NotSlot(sliceutil.IntersectionSlot(tt.expectedBlockSlots, receivedBlockSlots), tt.expectedBlockSlots)
   315  			if len(missing) > 0 {
   316  				t.Errorf("Missing blocks at slots %v", missing)
   317  			}
   318  		})
   319  	}
   320  }
   321  
   322  func TestService_processBlock(t *testing.T) {
   323  	beaconDB := dbtest.SetupDB(t)
   324  	genesisBlk := testutil.NewBeaconBlock()
   325  	genesisBlkRoot, err := genesisBlk.Block.HashTreeRoot()
   326  	require.NoError(t, err)
   327  	err = beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(genesisBlk))
   328  	require.NoError(t, err)
   329  	st, err := testutil.NewBeaconState()
   330  	require.NoError(t, err)
   331  	s := NewService(context.Background(), &Config{
   332  		P2P: p2pt.NewTestP2P(t),
   333  		DB:  beaconDB,
   334  		Chain: &mock.ChainService{
   335  			State: st,
   336  			Root:  genesisBlkRoot[:],
   337  			DB:    beaconDB,
   338  			FinalizedCheckPoint: &eth.Checkpoint{
   339  				Epoch: 0,
   340  			},
   341  		},
   342  		StateNotifier: &mock.MockStateNotifier{},
   343  	})
   344  	ctx := context.Background()
   345  	genesis := makeGenesisTime(32)
   346  
   347  	t.Run("process duplicate block", func(t *testing.T) {
   348  		blk1 := testutil.NewBeaconBlock()
   349  		blk1.Block.Slot = 1
   350  		blk1.Block.ParentRoot = genesisBlkRoot[:]
   351  		blk1Root, err := blk1.Block.HashTreeRoot()
   352  		require.NoError(t, err)
   353  		blk2 := testutil.NewBeaconBlock()
   354  		blk2.Block.Slot = 2
   355  		blk2.Block.ParentRoot = blk1Root[:]
   356  
   357  		// Process block normally.
   358  		err = s.processBlock(ctx, genesis, wrapper.WrappedPhase0SignedBeaconBlock(blk1), func(
   359  			ctx context.Context, block interfaces.SignedBeaconBlock, blockRoot [32]byte) error {
   360  			assert.NoError(t, s.cfg.Chain.ReceiveBlock(ctx, block, blockRoot))
   361  			return nil
   362  		})
   363  		assert.NoError(t, err)
   364  
   365  		// Duplicate processing should trigger error.
   366  		err = s.processBlock(ctx, genesis, wrapper.WrappedPhase0SignedBeaconBlock(blk1), func(
   367  			ctx context.Context, block interfaces.SignedBeaconBlock, blockRoot [32]byte) error {
   368  			return nil
   369  		})
   370  		assert.ErrorContains(t, errBlockAlreadyProcessed.Error(), err)
   371  
   372  		// Continue normal processing, should proceed w/o errors.
   373  		err = s.processBlock(ctx, genesis, wrapper.WrappedPhase0SignedBeaconBlock(blk2), func(
   374  			ctx context.Context, block interfaces.SignedBeaconBlock, blockRoot [32]byte) error {
   375  			assert.NoError(t, s.cfg.Chain.ReceiveBlock(ctx, block, blockRoot))
   376  			return nil
   377  		})
   378  		assert.NoError(t, err)
   379  		assert.Equal(t, types.Slot(2), s.cfg.Chain.HeadSlot(), "Unexpected head slot")
   380  	})
   381  }
   382  
   383  func TestService_processBlockBatch(t *testing.T) {
   384  	beaconDB := dbtest.SetupDB(t)
   385  	genesisBlk := testutil.NewBeaconBlock()
   386  	genesisBlkRoot, err := genesisBlk.Block.HashTreeRoot()
   387  	require.NoError(t, err)
   388  	err = beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(genesisBlk))
   389  	require.NoError(t, err)
   390  	st, err := testutil.NewBeaconState()
   391  	require.NoError(t, err)
   392  	s := NewService(context.Background(), &Config{
   393  		P2P: p2pt.NewTestP2P(t),
   394  		DB:  beaconDB,
   395  		Chain: &mock.ChainService{
   396  			State: st,
   397  			Root:  genesisBlkRoot[:],
   398  			DB:    beaconDB,
   399  			FinalizedCheckPoint: &eth.Checkpoint{
   400  				Epoch: 0,
   401  			},
   402  		},
   403  		StateNotifier: &mock.MockStateNotifier{},
   404  	})
   405  	ctx := context.Background()
   406  	genesis := makeGenesisTime(32)
   407  
   408  	t.Run("process non-linear batch", func(t *testing.T) {
   409  		var batch []interfaces.SignedBeaconBlock
   410  		currBlockRoot := genesisBlkRoot
   411  		for i := types.Slot(1); i < 10; i++ {
   412  			parentRoot := currBlockRoot
   413  			blk1 := testutil.NewBeaconBlock()
   414  			blk1.Block.Slot = i
   415  			blk1.Block.ParentRoot = parentRoot[:]
   416  			blk1Root, err := blk1.Block.HashTreeRoot()
   417  			require.NoError(t, err)
   418  			err = beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(blk1))
   419  			require.NoError(t, err)
   420  			batch = append(batch, wrapper.WrappedPhase0SignedBeaconBlock(blk1))
   421  			currBlockRoot = blk1Root
   422  		}
   423  
   424  		var batch2 []interfaces.SignedBeaconBlock
   425  		for i := types.Slot(10); i < 20; i++ {
   426  			parentRoot := currBlockRoot
   427  			blk1 := testutil.NewBeaconBlock()
   428  			blk1.Block.Slot = i
   429  			blk1.Block.ParentRoot = parentRoot[:]
   430  			blk1Root, err := blk1.Block.HashTreeRoot()
   431  			require.NoError(t, err)
   432  			err = beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(blk1))
   433  			require.NoError(t, err)
   434  			batch2 = append(batch2, wrapper.WrappedPhase0SignedBeaconBlock(blk1))
   435  			currBlockRoot = blk1Root
   436  		}
   437  
   438  		// Process block normally.
   439  		err = s.processBatchedBlocks(ctx, genesis, batch, func(
   440  			ctx context.Context, blks []interfaces.SignedBeaconBlock, blockRoots [][32]byte) error {
   441  			assert.NoError(t, s.cfg.Chain.ReceiveBlockBatch(ctx, blks, blockRoots))
   442  			return nil
   443  		})
   444  		assert.NoError(t, err)
   445  
   446  		// Duplicate processing should trigger error.
   447  		err = s.processBatchedBlocks(ctx, genesis, batch, func(
   448  			ctx context.Context, blocks []interfaces.SignedBeaconBlock, blockRoots [][32]byte) error {
   449  			return nil
   450  		})
   451  		assert.ErrorContains(t, "no good blocks in batch", err)
   452  
   453  		var badBatch2 []interfaces.SignedBeaconBlock
   454  		for i, b := range batch2 {
   455  			// create a non-linear batch
   456  			if i%3 == 0 && i != 0 {
   457  				continue
   458  			}
   459  			badBatch2 = append(badBatch2, b)
   460  		}
   461  
   462  		// Bad batch should fail because it is non linear
   463  		err = s.processBatchedBlocks(ctx, genesis, badBatch2, func(
   464  			ctx context.Context, blks []interfaces.SignedBeaconBlock, blockRoots [][32]byte) error {
   465  			return nil
   466  		})
   467  		expectedSubErr := "expected linear block list"
   468  		assert.ErrorContains(t, expectedSubErr, err)
   469  
   470  		// Continue normal processing, should proceed w/o errors.
   471  		err = s.processBatchedBlocks(ctx, genesis, batch2, func(
   472  			ctx context.Context, blks []interfaces.SignedBeaconBlock, blockRoots [][32]byte) error {
   473  			assert.NoError(t, s.cfg.Chain.ReceiveBlockBatch(ctx, blks, blockRoots))
   474  			return nil
   475  		})
   476  		assert.NoError(t, err)
   477  		assert.Equal(t, types.Slot(19), s.cfg.Chain.HeadSlot(), "Unexpected head slot")
   478  	})
   479  }
   480  
   481  func TestService_blockProviderScoring(t *testing.T) {
   482  	cache.initializeRootCache(makeSequence(1, 640), t)
   483  
   484  	p := p2pt.NewTestP2P(t)
   485  	beaconDB := dbtest.SetupDB(t)
   486  
   487  	peerData := []*peerData{
   488  		{
   489  			// The slowest peer, only a single block in couple of epochs.
   490  			blocks:         []types.Slot{1, 65, 129},
   491  			finalizedEpoch: 5,
   492  			headSlot:       160,
   493  		},
   494  		{
   495  			// A relatively slow peer, still should perform better than the slowest peer.
   496  			blocks:         append([]types.Slot{1, 2, 3, 4, 65, 66, 67, 68, 129, 130}, makeSequence(131, 160)...),
   497  			finalizedEpoch: 5,
   498  			headSlot:       160,
   499  		},
   500  		{
   501  			// This peer has all blocks - should be a preferred one.
   502  			blocks:         makeSequence(1, 320),
   503  			finalizedEpoch: 5,
   504  			headSlot:       160,
   505  		},
   506  	}
   507  
   508  	peer1 := connectPeer(t, p, peerData[0], p.Peers())
   509  	peer2 := connectPeer(t, p, peerData[1], p.Peers())
   510  	peer3 := connectPeer(t, p, peerData[2], p.Peers())
   511  
   512  	cache.RLock()
   513  	genesisRoot := cache.rootCache[0]
   514  	cache.RUnlock()
   515  
   516  	err := beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(testutil.NewBeaconBlock()))
   517  	require.NoError(t, err)
   518  
   519  	st, err := testutil.NewBeaconState()
   520  	require.NoError(t, err)
   521  	require.NoError(t, err)
   522  	mc := &mock.ChainService{
   523  		State: st,
   524  		Root:  genesisRoot[:],
   525  		DB:    beaconDB,
   526  		FinalizedCheckPoint: &eth.Checkpoint{
   527  			Epoch: 0,
   528  			Root:  make([]byte, 32),
   529  		},
   530  	} // no-op mock
   531  	s := &Service{
   532  		ctx:          context.Background(),
   533  		cfg:          &Config{Chain: mc, P2P: p, DB: beaconDB},
   534  		synced:       abool.New(),
   535  		chainStarted: abool.NewBool(true),
   536  	}
   537  	scorer := s.cfg.P2P.Peers().Scorers().BlockProviderScorer()
   538  	expectedBlockSlots := makeSequence(1, 160)
   539  	currentSlot := types.Slot(160)
   540  
   541  	assert.Equal(t, scorer.MaxScore(), scorer.Score(peer1))
   542  	assert.Equal(t, scorer.MaxScore(), scorer.Score(peer2))
   543  	assert.Equal(t, scorer.MaxScore(), scorer.Score(peer3))
   544  
   545  	assert.NoError(t, s.roundRobinSync(makeGenesisTime(currentSlot)))
   546  	if s.cfg.Chain.HeadSlot() < currentSlot {
   547  		t.Errorf("Head slot (%d) is less than expected currentSlot (%d)", s.cfg.Chain.HeadSlot(), currentSlot)
   548  	}
   549  	assert.Equal(t, true, len(expectedBlockSlots) <= len(mc.BlocksReceived), "Processes wrong number of blocks")
   550  	var receivedBlockSlots []types.Slot
   551  	for _, blk := range mc.BlocksReceived {
   552  		receivedBlockSlots = append(receivedBlockSlots, blk.Block().Slot())
   553  	}
   554  	missing := sliceutil.NotSlot(sliceutil.IntersectionSlot(expectedBlockSlots, receivedBlockSlots), expectedBlockSlots)
   555  	if len(missing) > 0 {
   556  		t.Errorf("Missing blocks at slots %v", missing)
   557  	}
   558  
   559  	// Increment all peers' stats, so that nobody is boosted (as new, not yet used peer).
   560  	scorer.IncrementProcessedBlocks(peer1, 1)
   561  	scorer.IncrementProcessedBlocks(peer2, 1)
   562  	scorer.IncrementProcessedBlocks(peer3, 1)
   563  	score1 := scorer.Score(peer1)
   564  	score2 := scorer.Score(peer2)
   565  	score3 := scorer.Score(peer3)
   566  	assert.Equal(t, true, score1 < score3, "Incorrect score (%v) for peer: %v (must be lower than %v)", score1, peer1, score3)
   567  	assert.Equal(t, true, score2 < score3, "Incorrect score (%v) for peer: %v (must be lower than %v)", score2, peer2, score3)
   568  	assert.Equal(t, true, scorer.ProcessedBlocks(peer3) > 100, "Not enough blocks returned by healthy peer: %d", scorer.ProcessedBlocks(peer3))
   569  }
   570  
   571  func TestService_syncToFinalizedEpoch(t *testing.T) {
   572  	cache.initializeRootCache(makeSequence(1, 640), t)
   573  
   574  	p := p2pt.NewTestP2P(t)
   575  	beaconDB := dbtest.SetupDB(t)
   576  	cache.RLock()
   577  	genesisRoot := cache.rootCache[0]
   578  	cache.RUnlock()
   579  
   580  	err := beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(testutil.NewBeaconBlock()))
   581  	require.NoError(t, err)
   582  
   583  	st, err := testutil.NewBeaconState()
   584  	require.NoError(t, err)
   585  	mc := &mock.ChainService{
   586  		State: st,
   587  		Root:  genesisRoot[:],
   588  		DB:    beaconDB,
   589  		FinalizedCheckPoint: &eth.Checkpoint{
   590  			Epoch: 0,
   591  			Root:  make([]byte, 32),
   592  		},
   593  	}
   594  	s := &Service{
   595  		ctx:          context.Background(),
   596  		cfg:          &Config{Chain: mc, P2P: p, DB: beaconDB},
   597  		synced:       abool.New(),
   598  		chainStarted: abool.NewBool(true),
   599  		counter:      ratecounter.NewRateCounter(counterSeconds * time.Second),
   600  	}
   601  	expectedBlockSlots := makeSequence(1, 191)
   602  	currentSlot := types.Slot(191)
   603  
   604  	// Sync to finalized epoch.
   605  	hook := logTest.NewGlobal()
   606  	connectPeer(t, p, &peerData{
   607  		blocks:         makeSequence(1, 240),
   608  		finalizedEpoch: 5,
   609  		headSlot:       195,
   610  	}, p.Peers())
   611  	genesis := makeGenesisTime(currentSlot)
   612  	assert.NoError(t, s.syncToFinalizedEpoch(context.Background(), genesis))
   613  	if s.cfg.Chain.HeadSlot() < currentSlot {
   614  		t.Errorf("Head slot (%d) is less than expected currentSlot (%d)", s.cfg.Chain.HeadSlot(), currentSlot)
   615  	}
   616  	assert.Equal(t, true, len(expectedBlockSlots) <= len(mc.BlocksReceived), "Processes wrong number of blocks")
   617  	var receivedBlockSlots []types.Slot
   618  	for _, blk := range mc.BlocksReceived {
   619  		receivedBlockSlots = append(receivedBlockSlots, blk.Block().Slot())
   620  	}
   621  	missing := sliceutil.NotSlot(sliceutil.IntersectionSlot(expectedBlockSlots, receivedBlockSlots), expectedBlockSlots)
   622  	if len(missing) > 0 {
   623  		t.Errorf("Missing blocks at slots %v", missing)
   624  	}
   625  	assert.LogsDoNotContain(t, hook, "Already synced to finalized epoch")
   626  
   627  	// Try to re-sync, should be exited immediately (node is already synced to finalized epoch).
   628  	hook.Reset()
   629  	assert.NoError(t, s.syncToFinalizedEpoch(context.Background(), genesis))
   630  	assert.LogsContain(t, hook, "Already synced to finalized epoch")
   631  }