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

     1  package p2p
     2  
     3  import (
     4  	"context"
     5  	"math/rand"
     6  	"os"
     7  	"path"
     8  	"strconv"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/ethereum/go-ethereum/p2p/discover"
    13  	"github.com/ethereum/go-ethereum/p2p/enode"
    14  	"github.com/ethereum/go-ethereum/p2p/enr"
    15  	ma "github.com/multiformats/go-multiaddr"
    16  	types "github.com/prysmaticlabs/eth2-types"
    17  	mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
    18  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    19  	pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
    20  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    21  	"github.com/prysmaticlabs/prysm/shared/p2putils"
    22  	"github.com/prysmaticlabs/prysm/shared/params"
    23  	"github.com/prysmaticlabs/prysm/shared/testutil/assert"
    24  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    25  	"github.com/sirupsen/logrus"
    26  	logTest "github.com/sirupsen/logrus/hooks/test"
    27  )
    28  
    29  func TestStartDiscv5_DifferentForkDigests(t *testing.T) {
    30  	port := 2000
    31  	ipAddr, pkey := createAddrAndPrivKey(t)
    32  	genesisTime := time.Now()
    33  	genesisValidatorsRoot := make([]byte, 32)
    34  	s := &Service{
    35  		cfg: &Config{
    36  			UDPPort:       uint(port),
    37  			StateNotifier: &mock.MockStateNotifier{},
    38  		},
    39  		genesisTime:           genesisTime,
    40  		genesisValidatorsRoot: genesisValidatorsRoot,
    41  	}
    42  	bootListener, err := s.createListener(ipAddr, pkey)
    43  	require.NoError(t, err)
    44  	defer bootListener.Close()
    45  
    46  	bootNode := bootListener.Self()
    47  	cfg := &Config{
    48  		Discv5BootStrapAddr: []string{bootNode.String()},
    49  		UDPPort:             uint(port),
    50  		StateNotifier:       &mock.MockStateNotifier{},
    51  	}
    52  
    53  	var listeners []*discover.UDPv5
    54  	for i := 1; i <= 5; i++ {
    55  		port = 3000 + i
    56  		cfg.UDPPort = uint(port)
    57  		ipAddr, pkey := createAddrAndPrivKey(t)
    58  
    59  		// We give every peer a different genesis validators root, which
    60  		// will cause each peer to have a different ForkDigest, preventing
    61  		// them from connecting according to our discovery rules for Ethereum consensus.
    62  		root := make([]byte, 32)
    63  		copy(root, strconv.Itoa(port))
    64  		s = &Service{
    65  			cfg:                   cfg,
    66  			genesisTime:           genesisTime,
    67  			genesisValidatorsRoot: root,
    68  		}
    69  		listener, err := s.startDiscoveryV5(ipAddr, pkey)
    70  		assert.NoError(t, err, "Could not start discovery for node")
    71  		listeners = append(listeners, listener)
    72  	}
    73  	defer func() {
    74  		// Close down all peers.
    75  		for _, listener := range listeners {
    76  			listener.Close()
    77  		}
    78  	}()
    79  
    80  	// Wait for the nodes to have their local routing tables to be populated with the other nodes
    81  	time.Sleep(discoveryWaitTime)
    82  
    83  	lastListener := listeners[len(listeners)-1]
    84  	nodes := lastListener.Lookup(bootNode.ID())
    85  	if len(nodes) < 4 {
    86  		t.Errorf("The node's local table doesn't have the expected number of nodes. "+
    87  			"Expected more than or equal to %d but got %d", 4, len(nodes))
    88  	}
    89  
    90  	// Now, we start a new p2p service. It should have no peers aside from the
    91  	// bootnode given all nodes provided by discv5 will have different fork digests.
    92  	cfg.UDPPort = 14000
    93  	cfg.TCPPort = 14001
    94  	cfg.MaxPeers = 30
    95  	s, err = NewService(context.Background(), cfg)
    96  	require.NoError(t, err)
    97  	s.genesisTime = genesisTime
    98  	s.genesisValidatorsRoot = make([]byte, 32)
    99  	s.dv5Listener = lastListener
   100  	var addrs []ma.Multiaddr
   101  
   102  	for _, n := range nodes {
   103  		if s.filterPeer(n) {
   104  			addr, err := convertToSingleMultiAddr(n)
   105  			require.NoError(t, err)
   106  			addrs = append(addrs, addr)
   107  		}
   108  	}
   109  
   110  	// We should not have valid peers if the fork digest mismatched.
   111  	assert.Equal(t, 0, len(addrs), "Expected 0 valid peers")
   112  	require.NoError(t, s.Stop())
   113  }
   114  
   115  func TestStartDiscv5_SameForkDigests_DifferentNextForkData(t *testing.T) {
   116  	hook := logTest.NewGlobal()
   117  	logrus.SetLevel(logrus.TraceLevel)
   118  	port := 2000
   119  	ipAddr, pkey := createAddrAndPrivKey(t)
   120  	genesisTime := time.Now()
   121  	genesisValidatorsRoot := make([]byte, 32)
   122  	s := &Service{
   123  		cfg:                   &Config{UDPPort: uint(port)},
   124  		genesisTime:           genesisTime,
   125  		genesisValidatorsRoot: genesisValidatorsRoot,
   126  		stateNotifier:         &mock.MockStateNotifier{},
   127  	}
   128  	bootListener, err := s.createListener(ipAddr, pkey)
   129  	require.NoError(t, err)
   130  	defer bootListener.Close()
   131  
   132  	bootNode := bootListener.Self()
   133  	cfg := &Config{
   134  		Discv5BootStrapAddr: []string{bootNode.String()},
   135  		UDPPort:             uint(port),
   136  	}
   137  
   138  	params.SetupTestConfigCleanup(t)
   139  	var listeners []*discover.UDPv5
   140  	for i := 1; i <= 5; i++ {
   141  		port = 3000 + i
   142  		cfg.UDPPort = uint(port)
   143  		ipAddr, pkey := createAddrAndPrivKey(t)
   144  
   145  		c := params.BeaconConfig()
   146  		nextForkEpoch := types.Epoch(i)
   147  		c.NextForkEpoch = nextForkEpoch
   148  		params.OverrideBeaconConfig(c)
   149  
   150  		// We give every peer a different genesis validators root, which
   151  		// will cause each peer to have a different ForkDigest, preventing
   152  		// them from connecting according to our discovery rules for Ethereum consensus.
   153  		s = &Service{
   154  			cfg:                   cfg,
   155  			genesisTime:           genesisTime,
   156  			genesisValidatorsRoot: genesisValidatorsRoot,
   157  			stateNotifier:         &mock.MockStateNotifier{},
   158  		}
   159  		listener, err := s.startDiscoveryV5(ipAddr, pkey)
   160  		assert.NoError(t, err, "Could not start discovery for node")
   161  		listeners = append(listeners, listener)
   162  	}
   163  	defer func() {
   164  		// Close down all peers.
   165  		for _, listener := range listeners {
   166  			listener.Close()
   167  		}
   168  	}()
   169  
   170  	// Wait for the nodes to have their local routing tables to be populated with the other nodes
   171  	time.Sleep(discoveryWaitTime)
   172  
   173  	lastListener := listeners[len(listeners)-1]
   174  	nodes := lastListener.Lookup(bootNode.ID())
   175  	if len(nodes) < 4 {
   176  		t.Errorf("The node's local table doesn't have the expected number of nodes. "+
   177  			"Expected more than or equal to %d but got %d", 4, len(nodes))
   178  	}
   179  
   180  	// Now, we start a new p2p service. It should have no peers aside from the
   181  	// bootnode given all nodes provided by discv5 will have different fork digests.
   182  	cfg.UDPPort = 14000
   183  	cfg.TCPPort = 14001
   184  	cfg.MaxPeers = 30
   185  	cfg.StateNotifier = &mock.MockStateNotifier{}
   186  	s, err = NewService(context.Background(), cfg)
   187  	require.NoError(t, err)
   188  
   189  	s.genesisTime = genesisTime
   190  	s.genesisValidatorsRoot = make([]byte, 32)
   191  	s.dv5Listener = lastListener
   192  	var addrs []ma.Multiaddr
   193  
   194  	for _, n := range nodes {
   195  		if s.filterPeer(n) {
   196  			addr, err := convertToSingleMultiAddr(n)
   197  			require.NoError(t, err)
   198  			addrs = append(addrs, addr)
   199  		}
   200  	}
   201  	if len(addrs) == 0 {
   202  		t.Error("Expected to have valid peers, got 0")
   203  	}
   204  
   205  	require.LogsContain(t, hook, "Peer matches fork digest but has different next fork epoch")
   206  	require.NoError(t, s.Stop())
   207  }
   208  
   209  func TestDiscv5_AddRetrieveForkEntryENR(t *testing.T) {
   210  	params.SetupTestConfigCleanup(t)
   211  	c := params.BeaconConfig()
   212  	c.ForkVersionSchedule = map[types.Epoch][]byte{
   213  		0: params.BeaconConfig().GenesisForkVersion,
   214  		1: {0, 0, 0, 1},
   215  	}
   216  	nextForkEpoch := types.Epoch(1)
   217  	nextForkVersion := []byte{0, 0, 0, 1}
   218  	c.NextForkEpoch = nextForkEpoch
   219  	c.NextForkVersion = nextForkVersion
   220  	params.OverrideBeaconConfig(c)
   221  
   222  	genesisTime := time.Now()
   223  	genesisValidatorsRoot := make([]byte, 32)
   224  	digest, err := p2putils.CreateForkDigest(genesisTime, make([]byte, 32))
   225  	require.NoError(t, err)
   226  	enrForkID := &pb.ENRForkID{
   227  		CurrentForkDigest: digest[:],
   228  		NextForkVersion:   nextForkVersion,
   229  		NextForkEpoch:     nextForkEpoch,
   230  	}
   231  	enc, err := enrForkID.MarshalSSZ()
   232  	require.NoError(t, err)
   233  	entry := enr.WithEntry(eth2ENRKey, enc)
   234  	// In epoch 1 of current time, the fork version should be
   235  	// {0, 0, 0, 1} according to the configuration override above.
   236  	temp := t.TempDir()
   237  	randNum := rand.Int()
   238  	tempPath := path.Join(temp, strconv.Itoa(randNum))
   239  	require.NoError(t, os.Mkdir(tempPath, 0700))
   240  	pkey, err := privKey(&Config{DataDir: tempPath})
   241  	require.NoError(t, err, "Could not get private key")
   242  	db, err := enode.OpenDB("")
   243  	require.NoError(t, err)
   244  	localNode := enode.NewLocalNode(db, pkey)
   245  	localNode.Set(entry)
   246  
   247  	want, err := helpers.ComputeForkDigest([]byte{0, 0, 0, 0}, genesisValidatorsRoot)
   248  	require.NoError(t, err)
   249  
   250  	resp, err := forkEntry(localNode.Node().Record())
   251  	require.NoError(t, err)
   252  	assert.DeepEqual(t, want[:], resp.CurrentForkDigest)
   253  	assert.DeepEqual(t, nextForkVersion, resp.NextForkVersion)
   254  	assert.Equal(t, nextForkEpoch, resp.NextForkEpoch, "Unexpected next fork epoch")
   255  }
   256  
   257  func TestAddForkEntry_Genesis(t *testing.T) {
   258  	temp := t.TempDir()
   259  	randNum := rand.Int()
   260  	tempPath := path.Join(temp, strconv.Itoa(randNum))
   261  	require.NoError(t, os.Mkdir(tempPath, 0700))
   262  	pkey, err := privKey(&Config{DataDir: tempPath})
   263  	require.NoError(t, err, "Could not get private key")
   264  	db, err := enode.OpenDB("")
   265  	require.NoError(t, err)
   266  
   267  	localNode := enode.NewLocalNode(db, pkey)
   268  	localNode, err = addForkEntry(localNode, time.Now().Add(10*time.Second), bytesutil.PadTo([]byte{'A', 'B', 'C', 'D'}, 32))
   269  	require.NoError(t, err)
   270  	forkEntry, err := forkEntry(localNode.Node().Record())
   271  	require.NoError(t, err)
   272  	assert.DeepEqual(t,
   273  		params.BeaconConfig().GenesisForkVersion, forkEntry.NextForkVersion,
   274  		"Wanted Next Fork Version to be equal to genesis fork version")
   275  }