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

     1  package sync
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/ethereum/go-ethereum/p2p/enr"
    10  	"github.com/kevinms/leakybucket-go"
    11  	"github.com/libp2p/go-libp2p-core/network"
    12  	"github.com/libp2p/go-libp2p-core/protocol"
    13  	types "github.com/prysmaticlabs/eth2-types"
    14  	mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
    15  	"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
    16  	"github.com/prysmaticlabs/prysm/beacon-chain/db/kv"
    17  	testingDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
    18  	"github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers"
    19  	p2ptest "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing"
    20  	p2ptypes "github.com/prysmaticlabs/prysm/beacon-chain/p2p/types"
    21  	"github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
    22  	mockSync "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync/testing"
    23  	pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
    24  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    25  	"github.com/prysmaticlabs/prysm/proto/eth/v1alpha1/wrapper"
    26  	interfaces2 "github.com/prysmaticlabs/prysm/proto/interfaces"
    27  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    28  	"github.com/prysmaticlabs/prysm/shared/interfaces"
    29  	"github.com/prysmaticlabs/prysm/shared/params"
    30  	"github.com/prysmaticlabs/prysm/shared/testutil"
    31  	"github.com/prysmaticlabs/prysm/shared/testutil/assert"
    32  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    33  	"github.com/prysmaticlabs/prysm/shared/timeutils"
    34  	"google.golang.org/protobuf/proto"
    35  )
    36  
    37  func TestStatusRPCHandler_Disconnects_OnForkVersionMismatch(t *testing.T) {
    38  	p1 := p2ptest.NewTestP2P(t)
    39  	p2 := p2ptest.NewTestP2P(t)
    40  	p1.Connect(p2)
    41  	assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected")
    42  	root := [32]byte{'C'}
    43  
    44  	r := &Service{
    45  		cfg: &Config{
    46  			P2P: p1,
    47  			Chain: &mock.ChainService{
    48  				Fork: &pb.Fork{
    49  					PreviousVersion: params.BeaconConfig().GenesisForkVersion,
    50  					CurrentVersion:  params.BeaconConfig().GenesisForkVersion,
    51  				},
    52  				FinalizedCheckPoint: &ethpb.Checkpoint{
    53  					Epoch: 0,
    54  					Root:  root[:],
    55  				},
    56  				Genesis:        time.Now(),
    57  				ValidatorsRoot: [32]byte{'A'},
    58  				Root:           make([]byte, 32),
    59  			},
    60  		},
    61  		rateLimiter: newRateLimiter(p1),
    62  	}
    63  	pcl := protocol.ID("/testing")
    64  	topic := string(pcl)
    65  	r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(1, 1, false)
    66  
    67  	var wg sync.WaitGroup
    68  	wg.Add(1)
    69  	p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) {
    70  		defer wg.Done()
    71  		expectSuccess(t, stream)
    72  		out := &pb.Status{}
    73  		assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, out))
    74  		assert.DeepEqual(t, root[:], out.FinalizedRoot)
    75  		assert.NoError(t, stream.Close())
    76  	})
    77  
    78  	pcl2 := protocol.ID("/eth2/beacon_chain/req/goodbye/1/ssz_snappy")
    79  	topic = string(pcl2)
    80  	r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(1, 1, false)
    81  	var wg2 sync.WaitGroup
    82  	wg2.Add(1)
    83  	p2.BHost.SetStreamHandler(pcl2, func(stream network.Stream) {
    84  		defer wg2.Done()
    85  		msg := new(types.SSZUint64)
    86  		assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, msg))
    87  		assert.Equal(t, p2ptypes.GoodbyeCodeWrongNetwork, *msg)
    88  		assert.NoError(t, stream.Close())
    89  	})
    90  
    91  	stream1, err := p1.BHost.NewStream(context.Background(), p2.BHost.ID(), pcl)
    92  	require.NoError(t, err)
    93  	assert.NoError(t, r.statusRPCHandler(context.Background(), &pb.Status{ForkDigest: bytesutil.PadTo([]byte("f"), 4), HeadRoot: make([]byte, 32), FinalizedRoot: make([]byte, 32)}, stream1))
    94  
    95  	if testutil.WaitTimeout(&wg, 1*time.Second) {
    96  		t.Fatal("Did not receive stream within 1 sec")
    97  	}
    98  	if testutil.WaitTimeout(&wg2, 1*time.Second) {
    99  		t.Fatal("Did not receive stream within 1 sec")
   100  	}
   101  
   102  	assert.Equal(t, 0, len(p1.BHost.Network().Peers()), "handler did not disconnect peer")
   103  }
   104  
   105  func TestStatusRPCHandler_ConnectsOnGenesis(t *testing.T) {
   106  	p1 := p2ptest.NewTestP2P(t)
   107  	p2 := p2ptest.NewTestP2P(t)
   108  	p1.Connect(p2)
   109  	assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected")
   110  	root := [32]byte{}
   111  
   112  	r := &Service{
   113  		cfg: &Config{
   114  			P2P: p1,
   115  			Chain: &mock.ChainService{
   116  				Fork: &pb.Fork{
   117  					PreviousVersion: params.BeaconConfig().GenesisForkVersion,
   118  					CurrentVersion:  params.BeaconConfig().GenesisForkVersion,
   119  				},
   120  				FinalizedCheckPoint: &ethpb.Checkpoint{
   121  					Epoch: 0,
   122  					Root:  params.BeaconConfig().ZeroHash[:],
   123  				},
   124  				Genesis:        time.Now(),
   125  				ValidatorsRoot: [32]byte{'A'},
   126  				Root:           make([]byte, 32),
   127  			},
   128  		},
   129  		rateLimiter: newRateLimiter(p1),
   130  	}
   131  	pcl := protocol.ID("/testing")
   132  	topic := string(pcl)
   133  	r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(1, 1, false)
   134  
   135  	var wg sync.WaitGroup
   136  	wg.Add(1)
   137  	p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) {
   138  		defer wg.Done()
   139  		expectSuccess(t, stream)
   140  		out := &pb.Status{}
   141  		assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, out))
   142  		assert.DeepEqual(t, root[:], out.FinalizedRoot)
   143  	})
   144  
   145  	stream1, err := p1.BHost.NewStream(context.Background(), p2.BHost.ID(), pcl)
   146  	require.NoError(t, err)
   147  	digest, err := r.forkDigest()
   148  	require.NoError(t, err)
   149  
   150  	err = r.statusRPCHandler(context.Background(), &pb.Status{ForkDigest: digest[:], FinalizedRoot: params.BeaconConfig().ZeroHash[:]}, stream1)
   151  	assert.NoError(t, err)
   152  
   153  	if testutil.WaitTimeout(&wg, 1*time.Second) {
   154  		t.Fatal("Did not receive stream within 1 sec")
   155  	}
   156  
   157  	assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Handler disconnected with peer")
   158  }
   159  
   160  func TestStatusRPCHandler_ReturnsHelloMessage(t *testing.T) {
   161  	p1 := p2ptest.NewTestP2P(t)
   162  	p2 := p2ptest.NewTestP2P(t)
   163  	p1.Connect(p2)
   164  	assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected")
   165  	db := testingDB.SetupDB(t)
   166  
   167  	// Set up a head state with data we expect.
   168  	head := testutil.NewBeaconBlock()
   169  	head.Block.Slot = 111
   170  	headRoot, err := head.Block.HashTreeRoot()
   171  	require.NoError(t, err)
   172  	blkSlot := 3 * params.BeaconConfig().SlotsPerEpoch
   173  	finalized := testutil.NewBeaconBlock()
   174  	finalized.Block.Slot = blkSlot
   175  	finalizedRoot, err := finalized.Block.HashTreeRoot()
   176  	require.NoError(t, err)
   177  	genesisState, err := state.GenesisBeaconState(context.Background(), nil, 0, &ethpb.Eth1Data{})
   178  	require.NoError(t, err)
   179  	require.NoError(t, genesisState.SetSlot(111))
   180  	require.NoError(t, genesisState.UpdateBlockRootAtIndex(111%uint64(params.BeaconConfig().SlotsPerHistoricalRoot), headRoot))
   181  	require.NoError(t, db.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(finalized)))
   182  	require.NoError(t, db.SaveGenesisBlockRoot(context.Background(), finalizedRoot))
   183  	finalizedCheckpt := &ethpb.Checkpoint{
   184  		Epoch: 3,
   185  		Root:  finalizedRoot[:],
   186  	}
   187  	totalSec := int64(params.BeaconConfig().SlotsPerEpoch.Mul(5 * params.BeaconConfig().SecondsPerSlot))
   188  	genTime := time.Now().Unix() - totalSec
   189  
   190  	r := &Service{
   191  		cfg: &Config{
   192  			P2P: p1,
   193  			Chain: &mock.ChainService{
   194  				State:               genesisState,
   195  				FinalizedCheckPoint: finalizedCheckpt,
   196  				Root:                headRoot[:],
   197  				Fork: &pb.Fork{
   198  					PreviousVersion: params.BeaconConfig().GenesisForkVersion,
   199  					CurrentVersion:  params.BeaconConfig().GenesisForkVersion,
   200  				},
   201  				ValidatorsRoot: [32]byte{'A'},
   202  				Genesis:        time.Unix(genTime, 0),
   203  			},
   204  			DB: db,
   205  		},
   206  		rateLimiter: newRateLimiter(p1),
   207  	}
   208  	digest, err := r.forkDigest()
   209  	require.NoError(t, err)
   210  
   211  	// Setup streams
   212  	pcl := protocol.ID("/testing")
   213  	topic := string(pcl)
   214  	r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(1, 1, false)
   215  	var wg sync.WaitGroup
   216  	wg.Add(1)
   217  	p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) {
   218  		defer wg.Done()
   219  		expectSuccess(t, stream)
   220  		out := &pb.Status{}
   221  		assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, out))
   222  		expected := &pb.Status{
   223  			ForkDigest:     digest[:],
   224  			HeadSlot:       genesisState.Slot(),
   225  			HeadRoot:       headRoot[:],
   226  			FinalizedEpoch: 3,
   227  			FinalizedRoot:  finalizedRoot[:],
   228  		}
   229  		if !proto.Equal(out, expected) {
   230  			t.Errorf("Did not receive expected message. Got %+v wanted %+v", out, expected)
   231  		}
   232  	})
   233  	stream1, err := p1.BHost.NewStream(context.Background(), p2.BHost.ID(), pcl)
   234  	require.NoError(t, err)
   235  
   236  	err = r.statusRPCHandler(context.Background(), &pb.Status{
   237  		ForkDigest:     digest[:],
   238  		FinalizedRoot:  finalizedRoot[:],
   239  		FinalizedEpoch: 3,
   240  	}, stream1)
   241  	assert.NoError(t, err)
   242  
   243  	if testutil.WaitTimeout(&wg, 1*time.Second) {
   244  		t.Fatal("Did not receive stream within 1 sec")
   245  	}
   246  }
   247  
   248  func TestHandshakeHandlers_Roundtrip(t *testing.T) {
   249  	// Scenario is that p1 and p2 connect, exchange handshakes.
   250  	// p2 disconnects and p1 should forget the handshake status.
   251  	p1 := p2ptest.NewTestP2P(t)
   252  	p2 := p2ptest.NewTestP2P(t)
   253  	db := testingDB.SetupDB(t)
   254  
   255  	p1.LocalMetadata = interfaces.WrappedMetadataV0(&pb.MetaDataV0{
   256  		SeqNumber: 2,
   257  		Attnets:   bytesutil.PadTo([]byte{'A', 'B'}, 8),
   258  	})
   259  
   260  	p2.LocalMetadata = interfaces.WrappedMetadataV0(&pb.MetaDataV0{
   261  		SeqNumber: 2,
   262  		Attnets:   bytesutil.PadTo([]byte{'C', 'D'}, 8),
   263  	})
   264  
   265  	st, err := v1.InitializeFromProto(&pb.BeaconState{
   266  		Slot: 5,
   267  	})
   268  	require.NoError(t, err)
   269  	blk := testutil.NewBeaconBlock()
   270  	blk.Block.Slot = 0
   271  	require.NoError(t, db.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(blk)))
   272  	finalizedRoot, err := blk.Block.HashTreeRoot()
   273  	require.NoError(t, err)
   274  	require.NoError(t, db.SaveGenesisBlockRoot(context.Background(), finalizedRoot))
   275  	r := &Service{
   276  		cfg: &Config{
   277  			P2P: p1,
   278  			Chain: &mock.ChainService{
   279  				State:               st,
   280  				FinalizedCheckPoint: &ethpb.Checkpoint{Epoch: 0, Root: finalizedRoot[:]},
   281  				Fork: &pb.Fork{
   282  					PreviousVersion: params.BeaconConfig().GenesisForkVersion,
   283  					CurrentVersion:  params.BeaconConfig().GenesisForkVersion,
   284  				},
   285  				Genesis:        time.Now(),
   286  				ValidatorsRoot: [32]byte{'A'},
   287  				Root:           make([]byte, 32),
   288  			},
   289  			DB: db,
   290  		},
   291  		ctx:         context.Background(),
   292  		rateLimiter: newRateLimiter(p1),
   293  	}
   294  	p1.Digest, err = r.forkDigest()
   295  	require.NoError(t, err)
   296  
   297  	r2 := &Service{
   298  		cfg: &Config{
   299  			Chain: &mock.ChainService{
   300  				FinalizedCheckPoint: &ethpb.Checkpoint{Epoch: 0, Root: finalizedRoot[:]},
   301  			},
   302  			P2P: p2,
   303  		},
   304  		rateLimiter: newRateLimiter(p2),
   305  	}
   306  	p2.Digest, err = r.forkDigest()
   307  	require.NoError(t, err)
   308  
   309  	r.Start()
   310  
   311  	// Setup streams
   312  	pcl := protocol.ID("/eth2/beacon_chain/req/status/1/ssz_snappy")
   313  	topic := string(pcl)
   314  	r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(1, 1, false)
   315  	var wg sync.WaitGroup
   316  	wg.Add(1)
   317  	p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) {
   318  		defer wg.Done()
   319  		out := &pb.Status{}
   320  		assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, out))
   321  		log.WithField("status", out).Warn("received status")
   322  		resp := &pb.Status{HeadSlot: 100, HeadRoot: make([]byte, 32), ForkDigest: p2.Digest[:],
   323  			FinalizedRoot: finalizedRoot[:], FinalizedEpoch: 0}
   324  		_, err := stream.Write([]byte{responseCodeSuccess})
   325  		assert.NoError(t, err)
   326  		_, err = r.cfg.P2P.Encoding().EncodeWithMaxLength(stream, resp)
   327  		assert.NoError(t, err)
   328  		log.WithField("status", out).Warn("sending status")
   329  		if err := stream.Close(); err != nil {
   330  			t.Log(err)
   331  		}
   332  	})
   333  
   334  	pcl = "/eth2/beacon_chain/req/ping/1/ssz_snappy"
   335  	topic = string(pcl)
   336  	r2.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(1, 1, false)
   337  	var wg2 sync.WaitGroup
   338  	wg2.Add(1)
   339  	p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) {
   340  		defer wg2.Done()
   341  		out := new(types.SSZUint64)
   342  		assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, out))
   343  		assert.Equal(t, uint64(2), uint64(*out))
   344  		assert.NoError(t, r2.pingHandler(context.Background(), out, stream))
   345  		assert.NoError(t, stream.Close())
   346  	})
   347  
   348  	numInactive1 := len(p1.Peers().Inactive())
   349  	numActive1 := len(p1.Peers().Active())
   350  
   351  	p1.Connect(p2)
   352  
   353  	p1.Peers().Add(new(enr.Record), p2.BHost.ID(), p2.BHost.Addrs()[0], network.DirUnknown)
   354  	p1.Peers().SetMetadata(p2.BHost.ID(), p2.LocalMetadata)
   355  
   356  	p2.Peers().Add(new(enr.Record), p1.BHost.ID(), p1.BHost.Addrs()[0], network.DirUnknown)
   357  	p2.Peers().SetMetadata(p1.BHost.ID(), p1.LocalMetadata)
   358  
   359  	if testutil.WaitTimeout(&wg, 1*time.Second) {
   360  		t.Fatal("Did not receive stream within 1 sec")
   361  	}
   362  	if testutil.WaitTimeout(&wg2, 1*time.Second) {
   363  		t.Fatal("Did not receive stream within 1 sec")
   364  	}
   365  
   366  	// Wait for stream buffer to be read.
   367  	time.Sleep(200 * time.Millisecond)
   368  
   369  	numInactive2 := len(p1.Peers().Inactive())
   370  	numActive2 := len(p1.Peers().Active())
   371  
   372  	assert.Equal(t, numInactive1, numInactive1, "Number of inactive peers changed unexpectedly")
   373  	assert.Equal(t, numActive1+1, numActive2, "Number of active peers unexpected")
   374  
   375  	require.NoError(t, p2.Disconnect(p1.PeerID()))
   376  	p1.Peers().SetConnectionState(p2.PeerID(), peers.PeerDisconnected)
   377  
   378  	// Wait for disconnect event to trigger.
   379  	time.Sleep(200 * time.Millisecond)
   380  
   381  	numInactive3 := len(p1.Peers().Inactive())
   382  	numActive3 := len(p1.Peers().Active())
   383  	assert.Equal(t, numInactive2+1, numInactive3, "Number of inactive peers unexpected")
   384  	assert.Equal(t, numActive2-1, numActive3, "Number of active peers unexpected")
   385  }
   386  
   387  func TestStatusRPCRequest_RequestSent(t *testing.T) {
   388  	p1 := p2ptest.NewTestP2P(t)
   389  	p2 := p2ptest.NewTestP2P(t)
   390  
   391  	// Set up a head state with data we expect.
   392  	head := testutil.NewBeaconBlock()
   393  	head.Block.Slot = 111
   394  	headRoot, err := head.Block.HashTreeRoot()
   395  	require.NoError(t, err)
   396  	finalized := testutil.NewBeaconBlock()
   397  	finalized.Block.Slot = 40
   398  	finalizedRoot, err := finalized.Block.HashTreeRoot()
   399  	require.NoError(t, err)
   400  	genesisState, err := state.GenesisBeaconState(context.Background(), nil, 0, &ethpb.Eth1Data{})
   401  	require.NoError(t, err)
   402  	require.NoError(t, genesisState.SetSlot(111))
   403  	require.NoError(t, genesisState.UpdateBlockRootAtIndex(111%uint64(params.BeaconConfig().SlotsPerHistoricalRoot), headRoot))
   404  	finalizedCheckpt := &ethpb.Checkpoint{
   405  		Epoch: 5,
   406  		Root:  finalizedRoot[:],
   407  	}
   408  
   409  	r := &Service{
   410  		cfg: &Config{
   411  			P2P: p1,
   412  			Chain: &mock.ChainService{
   413  				State:               genesisState,
   414  				FinalizedCheckPoint: finalizedCheckpt,
   415  				Root:                headRoot[:],
   416  				Fork: &pb.Fork{
   417  					PreviousVersion: params.BeaconConfig().GenesisForkVersion,
   418  					CurrentVersion:  params.BeaconConfig().GenesisForkVersion,
   419  				},
   420  				Genesis:        time.Now(),
   421  				ValidatorsRoot: [32]byte{'A'},
   422  			},
   423  		},
   424  		ctx:         context.Background(),
   425  		rateLimiter: newRateLimiter(p1),
   426  	}
   427  
   428  	// Setup streams
   429  	pcl := protocol.ID("/eth2/beacon_chain/req/status/1/ssz_snappy")
   430  	topic := string(pcl)
   431  	r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(1, 1, false)
   432  	var wg sync.WaitGroup
   433  	wg.Add(1)
   434  	p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) {
   435  		defer wg.Done()
   436  		out := &pb.Status{}
   437  		assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, out))
   438  		digest, err := r.forkDigest()
   439  		assert.NoError(t, err)
   440  		expected := &pb.Status{
   441  			ForkDigest:     digest[:],
   442  			HeadSlot:       genesisState.Slot(),
   443  			HeadRoot:       headRoot[:],
   444  			FinalizedEpoch: 5,
   445  			FinalizedRoot:  finalizedRoot[:],
   446  		}
   447  		if !proto.Equal(out, expected) {
   448  			t.Errorf("Did not receive expected message. Got %+v wanted %+v", out, expected)
   449  		}
   450  	})
   451  
   452  	p1.AddConnectionHandler(r.sendRPCStatusRequest, nil)
   453  	p1.Connect(p2)
   454  
   455  	if testutil.WaitTimeout(&wg, 1*time.Second) {
   456  		t.Fatal("Did not receive stream within 1 sec")
   457  	}
   458  
   459  	assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to continue being connected")
   460  }
   461  
   462  func TestStatusRPCRequest_FinalizedBlockExists(t *testing.T) {
   463  	p1 := p2ptest.NewTestP2P(t)
   464  	p2 := p2ptest.NewTestP2P(t)
   465  	db := testingDB.SetupDB(t)
   466  
   467  	// Set up a head state with data we expect.
   468  	head := testutil.NewBeaconBlock()
   469  	head.Block.Slot = 111
   470  	headRoot, err := head.Block.HashTreeRoot()
   471  	require.NoError(t, err)
   472  	blkSlot := 3 * params.BeaconConfig().SlotsPerEpoch
   473  	finalized := testutil.NewBeaconBlock()
   474  	finalized.Block.Slot = blkSlot
   475  	finalizedRoot, err := finalized.Block.HashTreeRoot()
   476  	require.NoError(t, err)
   477  	genesisState, err := state.GenesisBeaconState(context.Background(), nil, 0, &ethpb.Eth1Data{DepositRoot: make([]byte, 32), BlockHash: make([]byte, 32)})
   478  	require.NoError(t, err)
   479  	require.NoError(t, genesisState.SetSlot(111))
   480  	require.NoError(t, genesisState.UpdateBlockRootAtIndex(111%uint64(params.BeaconConfig().SlotsPerHistoricalRoot), headRoot))
   481  	blk := testutil.NewBeaconBlock()
   482  	blk.Block.Slot = blkSlot
   483  	require.NoError(t, db.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(blk)))
   484  	require.NoError(t, db.SaveGenesisBlockRoot(context.Background(), finalizedRoot))
   485  	finalizedCheckpt := &ethpb.Checkpoint{
   486  		Epoch: 3,
   487  		Root:  finalizedRoot[:],
   488  	}
   489  	totalSec := int64(params.BeaconConfig().SlotsPerEpoch.Mul(5 * params.BeaconConfig().SecondsPerSlot))
   490  	genTime := time.Now().Unix() - totalSec
   491  	r := &Service{
   492  		cfg: &Config{
   493  			P2P: p1,
   494  			Chain: &mock.ChainService{
   495  				State:               genesisState,
   496  				FinalizedCheckPoint: finalizedCheckpt,
   497  				Root:                headRoot[:],
   498  				Fork: &pb.Fork{
   499  					PreviousVersion: params.BeaconConfig().GenesisForkVersion,
   500  					CurrentVersion:  params.BeaconConfig().GenesisForkVersion,
   501  				},
   502  				Genesis:        time.Unix(genTime, 0),
   503  				ValidatorsRoot: [32]byte{'A'},
   504  			},
   505  		},
   506  		ctx:         context.Background(),
   507  		rateLimiter: newRateLimiter(p1),
   508  	}
   509  
   510  	r2 := &Service{
   511  		cfg: &Config{
   512  			P2P: p1,
   513  			Chain: &mock.ChainService{
   514  				State:               genesisState,
   515  				FinalizedCheckPoint: finalizedCheckpt,
   516  				Root:                headRoot[:],
   517  				Fork: &pb.Fork{
   518  					PreviousVersion: params.BeaconConfig().GenesisForkVersion,
   519  					CurrentVersion:  params.BeaconConfig().GenesisForkVersion,
   520  				},
   521  				Genesis:        time.Unix(genTime, 0),
   522  				ValidatorsRoot: [32]byte{'A'},
   523  			},
   524  			DB: db,
   525  		},
   526  		ctx:         context.Background(),
   527  		rateLimiter: newRateLimiter(p1),
   528  	}
   529  
   530  	// Setup streams
   531  	pcl := protocol.ID("/eth2/beacon_chain/req/status/1/ssz_snappy")
   532  	topic := string(pcl)
   533  	r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(1, 1, false)
   534  	var wg sync.WaitGroup
   535  	wg.Add(1)
   536  	p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) {
   537  		defer wg.Done()
   538  		out := &pb.Status{}
   539  		assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, out))
   540  		assert.NoError(t, r2.validateStatusMessage(context.Background(), out))
   541  	})
   542  
   543  	p1.AddConnectionHandler(r.sendRPCStatusRequest, nil)
   544  	p1.Connect(p2)
   545  
   546  	if testutil.WaitTimeout(&wg, 1*time.Second) {
   547  		t.Fatal("Did not receive stream within 1 sec")
   548  	}
   549  
   550  	assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to continue being connected")
   551  }
   552  
   553  func TestStatusRPCRequest_FinalizedBlockSkippedSlots(t *testing.T) {
   554  	db, err := kv.NewKVStore(context.Background(), t.TempDir(), &kv.Config{})
   555  	require.NoError(t, err)
   556  	bState, err := state.GenesisBeaconState(context.Background(), nil, 0, &ethpb.Eth1Data{DepositRoot: make([]byte, 32), BlockHash: make([]byte, 32)})
   557  	require.NoError(t, err)
   558  
   559  	blk := testutil.NewBeaconBlock()
   560  	blk.Block.Slot = 0
   561  	genRoot, err := blk.Block.HashTreeRoot()
   562  	require.NoError(t, err)
   563  
   564  	require.NoError(t, db.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(blk)))
   565  	require.NoError(t, db.SaveGenesisBlockRoot(context.Background(), genRoot))
   566  	blocksTillHead := makeBlocks(t, 1, 1000, genRoot)
   567  	require.NoError(t, db.SaveBlocks(context.Background(), blocksTillHead))
   568  
   569  	stateSummaries := make([]*pb.StateSummary, len(blocksTillHead))
   570  	for i, b := range blocksTillHead {
   571  		bRoot, err := b.Block().HashTreeRoot()
   572  		require.NoError(t, err)
   573  		stateSummaries[i] = &pb.StateSummary{
   574  			Slot: b.Block().Slot(),
   575  			Root: bRoot[:],
   576  		}
   577  	}
   578  	require.NoError(t, db.SaveStateSummaries(context.Background(), stateSummaries))
   579  
   580  	rootFetcher := func(slot types.Slot) [32]byte {
   581  		rt, err := blocksTillHead[slot-1].Block().HashTreeRoot()
   582  		require.NoError(t, err)
   583  		return rt
   584  	}
   585  	tests := []struct {
   586  		name                   string
   587  		expectedFinalizedEpoch types.Epoch
   588  		expectedFinalizedRoot  [32]byte
   589  		headSlot               types.Slot
   590  		remoteFinalizedEpoch   types.Epoch
   591  		remoteFinalizedRoot    [32]byte
   592  		remoteHeadSlot         types.Slot
   593  		expectError            bool
   594  	}{
   595  		{
   596  			name:                   "valid finalized epoch",
   597  			expectedFinalizedEpoch: 3,
   598  			expectedFinalizedRoot:  rootFetcher(3 * params.BeaconConfig().SlotsPerEpoch),
   599  			headSlot:               111,
   600  			remoteFinalizedEpoch:   3,
   601  			remoteFinalizedRoot:    rootFetcher(3 * params.BeaconConfig().SlotsPerEpoch),
   602  			remoteHeadSlot:         100,
   603  			expectError:            false,
   604  		},
   605  		{
   606  			name:                   "invalid finalized epoch",
   607  			expectedFinalizedEpoch: 3,
   608  			expectedFinalizedRoot:  rootFetcher(3 * params.BeaconConfig().SlotsPerEpoch),
   609  			headSlot:               111,
   610  			remoteFinalizedEpoch:   3,
   611  			// give an incorrect root relative to the finalized epoch.
   612  			remoteFinalizedRoot: rootFetcher(2 * params.BeaconConfig().SlotsPerEpoch),
   613  			remoteHeadSlot:      120,
   614  			expectError:         true,
   615  		},
   616  		{
   617  			name:                   "invalid finalized root",
   618  			expectedFinalizedEpoch: 3,
   619  			expectedFinalizedRoot:  rootFetcher(3 * params.BeaconConfig().SlotsPerEpoch),
   620  			headSlot:               111,
   621  			remoteFinalizedEpoch:   3,
   622  			// give a bad finalized root, and the beacon node verifies that
   623  			// it is indeed incorrect.
   624  			remoteFinalizedRoot: [32]byte{'a', 'b', 'c'},
   625  			remoteHeadSlot:      120,
   626  			expectError:         true,
   627  		},
   628  	}
   629  
   630  	for _, tt := range tests {
   631  		p1 := p2ptest.NewTestP2P(t)
   632  		p2 := p2ptest.NewTestP2P(t)
   633  
   634  		expectedFinalizedEpoch := tt.expectedFinalizedEpoch
   635  		headSlot := tt.headSlot
   636  
   637  		nState := bState.Copy()
   638  		// Set up a head state with data we expect.
   639  		head := blocksTillHead[len(blocksTillHead)-1]
   640  		headRoot, err := head.Block().HashTreeRoot()
   641  		require.NoError(t, err)
   642  
   643  		rHead := blocksTillHead[tt.remoteHeadSlot-1]
   644  		rHeadRoot, err := rHead.Block().HashTreeRoot()
   645  		require.NoError(t, err)
   646  
   647  		require.NoError(t, nState.SetSlot(headSlot))
   648  		require.NoError(t, nState.UpdateBlockRootAtIndex(uint64(headSlot.ModSlot(params.BeaconConfig().SlotsPerHistoricalRoot)), headRoot))
   649  
   650  		finalizedCheckpt := &ethpb.Checkpoint{
   651  			Epoch: expectedFinalizedEpoch,
   652  			Root:  tt.expectedFinalizedRoot[:],
   653  		}
   654  
   655  		remoteFinalizedChkpt := &ethpb.Checkpoint{
   656  			Epoch: tt.remoteFinalizedEpoch,
   657  			Root:  tt.remoteFinalizedRoot[:],
   658  		}
   659  		require.NoError(t, db.SaveFinalizedCheckpoint(context.Background(), finalizedCheckpt))
   660  
   661  		epoch := expectedFinalizedEpoch.Add(2)
   662  		totalSec := uint64(params.BeaconConfig().SlotsPerEpoch.Mul(uint64(epoch) * params.BeaconConfig().SecondsPerSlot))
   663  		genTime := time.Now().Unix() - int64(totalSec)
   664  		r := &Service{
   665  			cfg: &Config{
   666  				P2P: p1,
   667  				Chain: &mock.ChainService{
   668  					State:               nState,
   669  					FinalizedCheckPoint: remoteFinalizedChkpt,
   670  					Root:                rHeadRoot[:],
   671  					Fork: &pb.Fork{
   672  						PreviousVersion: params.BeaconConfig().GenesisForkVersion,
   673  						CurrentVersion:  params.BeaconConfig().GenesisForkVersion,
   674  					},
   675  					Genesis:        time.Unix(genTime, 0),
   676  					ValidatorsRoot: [32]byte{'A'},
   677  				},
   678  			},
   679  			ctx:         context.Background(),
   680  			rateLimiter: newRateLimiter(p1),
   681  		}
   682  
   683  		r2 := &Service{
   684  			cfg: &Config{
   685  				P2P: p2,
   686  				Chain: &mock.ChainService{
   687  					State:               nState,
   688  					FinalizedCheckPoint: finalizedCheckpt,
   689  					Root:                headRoot[:],
   690  					Fork: &pb.Fork{
   691  						PreviousVersion: params.BeaconConfig().GenesisForkVersion,
   692  						CurrentVersion:  params.BeaconConfig().GenesisForkVersion,
   693  					},
   694  					Genesis:        time.Unix(genTime, 0),
   695  					ValidatorsRoot: [32]byte{'A'},
   696  				},
   697  				DB: db,
   698  			},
   699  
   700  			ctx:         context.Background(),
   701  			rateLimiter: newRateLimiter(p1),
   702  		}
   703  
   704  		// Setup streams
   705  		pcl := protocol.ID("/eth2/beacon_chain/req/status/1/ssz_snappy")
   706  		topic := string(pcl)
   707  		r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(1, 1, false)
   708  		var wg sync.WaitGroup
   709  		wg.Add(1)
   710  		p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) {
   711  			defer wg.Done()
   712  			out := &pb.Status{}
   713  			assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, out))
   714  			assert.Equal(t, tt.expectError, r2.validateStatusMessage(context.Background(), out) != nil)
   715  		})
   716  
   717  		p1.AddConnectionHandler(r.sendRPCStatusRequest, nil)
   718  		p1.Connect(p2)
   719  
   720  		if testutil.WaitTimeout(&wg, 1*time.Second) {
   721  			t.Fatal("Did not receive stream within 1 sec")
   722  		}
   723  
   724  		assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to continue being connected")
   725  		assert.NoError(t, p1.Disconnect(p2.PeerID()))
   726  	}
   727  	assert.NoError(t, db.Close())
   728  }
   729  
   730  func TestStatusRPCRequest_BadPeerHandshake(t *testing.T) {
   731  	p1 := p2ptest.NewTestP2P(t)
   732  	p2 := p2ptest.NewTestP2P(t)
   733  
   734  	// Set up a head state with data we expect.
   735  	head := testutil.NewBeaconBlock()
   736  	head.Block.Slot = 111
   737  	headRoot, err := head.Block.HashTreeRoot()
   738  	require.NoError(t, err)
   739  	finalized := testutil.NewBeaconBlock()
   740  	finalizedRoot, err := finalized.Block.HashTreeRoot()
   741  	require.NoError(t, err)
   742  	genesisState, err := state.GenesisBeaconState(context.Background(), nil, 0, &ethpb.Eth1Data{})
   743  	require.NoError(t, err)
   744  	require.NoError(t, genesisState.SetSlot(111))
   745  	require.NoError(t, genesisState.UpdateBlockRootAtIndex(111%uint64(params.BeaconConfig().SlotsPerHistoricalRoot), headRoot))
   746  	finalizedCheckpt := &ethpb.Checkpoint{
   747  		Epoch: 5,
   748  		Root:  finalizedRoot[:],
   749  	}
   750  
   751  	r := &Service{
   752  		cfg: &Config{
   753  			P2P: p1,
   754  			Chain: &mock.ChainService{
   755  				State:               genesisState,
   756  				FinalizedCheckPoint: finalizedCheckpt,
   757  				Root:                headRoot[:],
   758  				Fork: &pb.Fork{
   759  					PreviousVersion: params.BeaconConfig().GenesisForkVersion,
   760  					CurrentVersion:  params.BeaconConfig().GenesisForkVersion,
   761  				},
   762  				Genesis:        time.Now(),
   763  				ValidatorsRoot: [32]byte{'A'},
   764  			},
   765  		},
   766  
   767  		ctx:         context.Background(),
   768  		rateLimiter: newRateLimiter(p1),
   769  	}
   770  
   771  	r.Start()
   772  
   773  	// Setup streams
   774  	pcl := protocol.ID("/eth2/beacon_chain/req/status/1/ssz_snappy")
   775  	topic := string(pcl)
   776  	r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(1, 1, false)
   777  	var wg sync.WaitGroup
   778  	wg.Add(1)
   779  	p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) {
   780  		defer wg.Done()
   781  		out := &pb.Status{}
   782  		assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, out))
   783  		expected := &pb.Status{
   784  			ForkDigest:     []byte{1, 1, 1, 1},
   785  			HeadSlot:       genesisState.Slot(),
   786  			HeadRoot:       headRoot[:],
   787  			FinalizedEpoch: 5,
   788  			FinalizedRoot:  finalizedRoot[:],
   789  		}
   790  		if _, err := stream.Write([]byte{responseCodeSuccess}); err != nil {
   791  			log.WithError(err).Debug("Could not write to stream")
   792  		}
   793  		_, err := r.cfg.P2P.Encoding().EncodeWithMaxLength(stream, expected)
   794  		assert.NoError(t, err)
   795  	})
   796  
   797  	assert.Equal(t, false, p1.Peers().Scorers().IsBadPeer(p2.PeerID()), "Peer is marked as bad")
   798  	p1.Connect(p2)
   799  
   800  	if testutil.WaitTimeout(&wg, time.Second) {
   801  		t.Fatal("Did not receive stream within 1 sec")
   802  	}
   803  	time.Sleep(100 * time.Millisecond)
   804  
   805  	connectionState, err := p1.Peers().ConnectionState(p2.PeerID())
   806  	require.NoError(t, err, "Could not obtain peer connection state")
   807  	assert.Equal(t, peers.PeerDisconnected, connectionState, "Expected peer to be disconnected")
   808  
   809  	assert.Equal(t, true, p1.Peers().Scorers().IsBadPeer(p2.PeerID()), "Peer is not marked as bad")
   810  }
   811  
   812  func TestStatusRPC_ValidGenesisMessage(t *testing.T) {
   813  	// Set up a head state with data we expect.
   814  	head := testutil.NewBeaconBlock()
   815  	head.Block.Slot = 111
   816  	headRoot, err := head.Block.HashTreeRoot()
   817  	require.NoError(t, err)
   818  	blkSlot := 3 * params.BeaconConfig().SlotsPerEpoch
   819  	finalized := testutil.NewBeaconBlock()
   820  	finalized.Block.Slot = blkSlot
   821  	finalizedRoot, err := finalized.Block.HashTreeRoot()
   822  	require.NoError(t, err)
   823  	genesisState, err := state.GenesisBeaconState(context.Background(), nil, 0, &ethpb.Eth1Data{})
   824  	require.NoError(t, err)
   825  	require.NoError(t, genesisState.SetSlot(111))
   826  	require.NoError(t, genesisState.UpdateBlockRootAtIndex(111%uint64(params.BeaconConfig().SlotsPerHistoricalRoot), headRoot))
   827  	finalizedCheckpt := &ethpb.Checkpoint{
   828  		Epoch: 5,
   829  		Root:  finalizedRoot[:],
   830  	}
   831  	r := &Service{
   832  		cfg: &Config{
   833  			Chain: &mock.ChainService{
   834  				State:               genesisState,
   835  				FinalizedCheckPoint: finalizedCheckpt,
   836  				Root:                headRoot[:],
   837  				Fork: &pb.Fork{
   838  					PreviousVersion: params.BeaconConfig().GenesisForkVersion,
   839  					CurrentVersion:  params.BeaconConfig().GenesisForkVersion,
   840  				},
   841  				Genesis:        time.Now(),
   842  				ValidatorsRoot: [32]byte{'A'},
   843  			},
   844  		},
   845  		ctx: context.Background(),
   846  	}
   847  	digest, err := r.forkDigest()
   848  	require.NoError(t, err)
   849  	// There should be no error for a status message
   850  	// with a genesis checkpoint.
   851  	err = r.validateStatusMessage(r.ctx, &pb.Status{
   852  		ForkDigest:     digest[:],
   853  		FinalizedRoot:  params.BeaconConfig().ZeroHash[:],
   854  		FinalizedEpoch: 0,
   855  		HeadRoot:       headRoot[:],
   856  		HeadSlot:       111,
   857  	})
   858  	require.NoError(t, err)
   859  }
   860  
   861  func TestShouldResync(t *testing.T) {
   862  	type args struct {
   863  		genesis  time.Time
   864  		syncing  bool
   865  		headSlot types.Slot
   866  	}
   867  	tests := []struct {
   868  		name string
   869  		args args
   870  		want bool
   871  	}{
   872  		{
   873  			name: "genesis epoch should not resync when syncing is true",
   874  			args: args{
   875  				headSlot: 31,
   876  				genesis:  timeutils.Now(),
   877  				syncing:  true,
   878  			},
   879  			want: false,
   880  		},
   881  		{
   882  			name: "genesis epoch should not resync when syncing is false",
   883  			args: args{
   884  				headSlot: 31,
   885  				genesis:  timeutils.Now(),
   886  				syncing:  false,
   887  			},
   888  			want: false,
   889  		},
   890  		{
   891  			name: "two epochs behind, resync ok",
   892  			args: args{
   893  				headSlot: 31,
   894  				genesis:  timeutils.Now().Add(-1 * 96 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second),
   895  				syncing:  false,
   896  			},
   897  			want: true,
   898  		},
   899  		{
   900  			name: "two epochs behind, already syncing",
   901  			args: args{
   902  				headSlot: 31,
   903  				genesis:  timeutils.Now().Add(-1 * 96 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second),
   904  				syncing:  true,
   905  			},
   906  			want: false,
   907  		},
   908  	}
   909  	for _, tt := range tests {
   910  		headState, err := state.GenesisBeaconState(context.Background(), nil, 0, &ethpb.Eth1Data{})
   911  		require.NoError(t, err)
   912  		require.NoError(t, headState.SetSlot(tt.args.headSlot))
   913  		r := &Service{
   914  			cfg: &Config{
   915  				Chain: &mock.ChainService{
   916  					State:   headState,
   917  					Genesis: tt.args.genesis,
   918  				},
   919  				InitialSync: &mockSync.Sync{IsSyncing: tt.args.syncing},
   920  			},
   921  			ctx: context.Background(),
   922  		}
   923  		t.Run(tt.name, func(t *testing.T) {
   924  			if got := r.shouldReSync(); got != tt.want {
   925  				t.Errorf("shouldReSync() = %v, want %v", got, tt.want)
   926  			}
   927  		})
   928  	}
   929  }
   930  
   931  func makeBlocks(t *testing.T, i, n uint64, previousRoot [32]byte) []interfaces2.SignedBeaconBlock {
   932  	blocks := make([]*ethpb.SignedBeaconBlock, n)
   933  	ifaceBlocks := make([]interfaces2.SignedBeaconBlock, n)
   934  	for j := i; j < n+i; j++ {
   935  		parentRoot := make([]byte, 32)
   936  		copy(parentRoot, previousRoot[:])
   937  		blocks[j-i] = testutil.NewBeaconBlock()
   938  		blocks[j-i].Block.Slot = types.Slot(j + 1)
   939  		blocks[j-i].Block.ParentRoot = parentRoot
   940  		var err error
   941  		previousRoot, err = blocks[j-i].Block.HashTreeRoot()
   942  		require.NoError(t, err)
   943  		ifaceBlocks[j-i] = wrapper.WrappedPhase0SignedBeaconBlock(blocks[j-i])
   944  	}
   945  	return ifaceBlocks
   946  }