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

     1  package p2p
     2  
     3  import (
     4  	"context"
     5  	"crypto/ecdsa"
     6  	"fmt"
     7  	"math/rand"
     8  	"net"
     9  	"os"
    10  	"path"
    11  	"strconv"
    12  	"strings"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/ethereum/go-ethereum/p2p/discover"
    17  	"github.com/ethereum/go-ethereum/p2p/enode"
    18  	"github.com/ethereum/go-ethereum/p2p/enr"
    19  	"github.com/kevinms/leakybucket-go"
    20  	"github.com/libp2p/go-libp2p-core/host"
    21  	"github.com/libp2p/go-libp2p-core/network"
    22  	"github.com/libp2p/go-libp2p-core/peer"
    23  	"github.com/prysmaticlabs/go-bitfield"
    24  	mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
    25  	"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
    26  	statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state"
    27  	"github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers"
    28  	"github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers/peerdata"
    29  	"github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers/scorers"
    30  	testp2p "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing"
    31  	pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
    32  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    33  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    34  	"github.com/prysmaticlabs/prysm/shared/interfaces"
    35  	"github.com/prysmaticlabs/prysm/shared/iputils"
    36  	"github.com/prysmaticlabs/prysm/shared/testutil/assert"
    37  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    38  	logTest "github.com/sirupsen/logrus/hooks/test"
    39  )
    40  
    41  var discoveryWaitTime = 1 * time.Second
    42  
    43  func init() {
    44  	rand.Seed(time.Now().Unix())
    45  }
    46  
    47  func createAddrAndPrivKey(t *testing.T) (net.IP, *ecdsa.PrivateKey) {
    48  	ip, err := iputils.ExternalIPv4()
    49  	require.NoError(t, err, "Could not get ip")
    50  	ipAddr := net.ParseIP(ip)
    51  	temp := t.TempDir()
    52  	randNum := rand.Int()
    53  	tempPath := path.Join(temp, strconv.Itoa(randNum))
    54  	require.NoError(t, os.Mkdir(tempPath, 0700))
    55  	pkey, err := privKey(&Config{DataDir: tempPath})
    56  	require.NoError(t, err, "Could not get private key")
    57  	return ipAddr, pkey
    58  }
    59  
    60  func TestCreateListener(t *testing.T) {
    61  	port := 1024
    62  	ipAddr, pkey := createAddrAndPrivKey(t)
    63  	s := &Service{
    64  		genesisTime:           time.Now(),
    65  		genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32),
    66  		cfg:                   &Config{UDPPort: uint(port)},
    67  	}
    68  	listener, err := s.createListener(ipAddr, pkey)
    69  	require.NoError(t, err)
    70  	defer listener.Close()
    71  
    72  	assert.Equal(t, true, listener.Self().IP().Equal(ipAddr), "IP address is not the expected type")
    73  	assert.Equal(t, port, listener.Self().UDP(), "Incorrect port number")
    74  
    75  	pubkey := listener.Self().Pubkey()
    76  	XisSame := pkey.PublicKey.X.Cmp(pubkey.X) == 0
    77  	YisSame := pkey.PublicKey.Y.Cmp(pubkey.Y) == 0
    78  
    79  	if !(XisSame && YisSame) {
    80  		t.Error("Pubkey is different from what was used to create the listener")
    81  	}
    82  }
    83  
    84  func TestStartDiscV5_DiscoverAllPeers(t *testing.T) {
    85  	port := 2000
    86  	ipAddr, pkey := createAddrAndPrivKey(t)
    87  	genesisTime := time.Now()
    88  	genesisValidatorsRoot := make([]byte, 32)
    89  	s := &Service{
    90  		cfg:                   &Config{UDPPort: uint(port)},
    91  		genesisTime:           genesisTime,
    92  		genesisValidatorsRoot: genesisValidatorsRoot,
    93  	}
    94  	bootListener, err := s.createListener(ipAddr, pkey)
    95  	require.NoError(t, err)
    96  	defer bootListener.Close()
    97  
    98  	bootNode := bootListener.Self()
    99  
   100  	var listeners []*discover.UDPv5
   101  	for i := 1; i <= 5; i++ {
   102  		port = 3000 + i
   103  		cfg := &Config{
   104  			Discv5BootStrapAddr: []string{bootNode.String()},
   105  			UDPPort:             uint(port),
   106  		}
   107  		ipAddr, pkey := createAddrAndPrivKey(t)
   108  		s = &Service{
   109  			cfg:                   cfg,
   110  			genesisTime:           genesisTime,
   111  			genesisValidatorsRoot: genesisValidatorsRoot,
   112  		}
   113  		listener, err := s.startDiscoveryV5(ipAddr, pkey)
   114  		assert.NoError(t, err, "Could not start discovery for node")
   115  		listeners = append(listeners, listener)
   116  	}
   117  	defer func() {
   118  		// Close down all peers.
   119  		for _, listener := range listeners {
   120  			listener.Close()
   121  		}
   122  	}()
   123  
   124  	// Wait for the nodes to have their local routing tables to be populated with the other nodes
   125  	time.Sleep(discoveryWaitTime)
   126  
   127  	lastListener := listeners[len(listeners)-1]
   128  	nodes := lastListener.Lookup(bootNode.ID())
   129  	if len(nodes) < 4 {
   130  		t.Errorf("The node's local table doesn't have the expected number of nodes. "+
   131  			"Expected more than or equal to %d but got %d", 4, len(nodes))
   132  	}
   133  }
   134  
   135  func TestMultiAddrsConversion_InvalidIPAddr(t *testing.T) {
   136  	addr := net.ParseIP("invalidIP")
   137  	_, pkey := createAddrAndPrivKey(t)
   138  	s := &Service{
   139  		genesisTime:           time.Now(),
   140  		genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32),
   141  	}
   142  	node, err := s.createLocalNode(pkey, addr, 0, 0)
   143  	require.NoError(t, err)
   144  	multiAddr := convertToMultiAddr([]*enode.Node{node.Node()})
   145  	assert.Equal(t, 0, len(multiAddr), "Invalid ip address converted successfully")
   146  }
   147  
   148  func TestMultiAddrConversion_OK(t *testing.T) {
   149  	hook := logTest.NewGlobal()
   150  	ipAddr, pkey := createAddrAndPrivKey(t)
   151  	s := &Service{
   152  		cfg: &Config{
   153  			TCPPort: 0,
   154  			UDPPort: 0,
   155  		},
   156  		genesisTime:           time.Now(),
   157  		genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32),
   158  	}
   159  	listener, err := s.createListener(ipAddr, pkey)
   160  	require.NoError(t, err)
   161  	defer listener.Close()
   162  
   163  	_ = convertToMultiAddr([]*enode.Node{listener.Self()})
   164  	require.LogsDoNotContain(t, hook, "Node doesn't have an ip4 address")
   165  	require.LogsDoNotContain(t, hook, "Invalid port, the tcp port of the node is a reserved port")
   166  	require.LogsDoNotContain(t, hook, "Could not get multiaddr")
   167  }
   168  
   169  func TestStaticPeering_PeersAreAdded(t *testing.T) {
   170  	cfg := &Config{
   171  		MaxPeers: 30,
   172  	}
   173  	port := 6000
   174  	var staticPeers []string
   175  	var hosts []host.Host
   176  	// setup other nodes
   177  	for i := 1; i <= 5; i++ {
   178  		h, _, ipaddr := createHost(t, port+i)
   179  		staticPeers = append(staticPeers, fmt.Sprintf("/ip4/%s/tcp/%d/p2p/%s", ipaddr, port+i, h.ID()))
   180  		hosts = append(hosts, h)
   181  	}
   182  
   183  	defer func() {
   184  		for _, h := range hosts {
   185  			if err := h.Close(); err != nil {
   186  				t.Log(err)
   187  			}
   188  		}
   189  	}()
   190  
   191  	cfg.TCPPort = 14500
   192  	cfg.UDPPort = 14501
   193  	cfg.StaticPeers = staticPeers
   194  	cfg.StateNotifier = &mock.MockStateNotifier{}
   195  	cfg.NoDiscovery = true
   196  	s, err := NewService(context.Background(), cfg)
   197  	require.NoError(t, err)
   198  
   199  	exitRoutine := make(chan bool)
   200  	go func() {
   201  		s.Start()
   202  		<-exitRoutine
   203  	}()
   204  	time.Sleep(50 * time.Millisecond)
   205  	// Send in a loop to ensure it is delivered (busy wait for the service to subscribe to the state feed).
   206  	for sent := 0; sent == 0; {
   207  		sent = s.stateNotifier.StateFeed().Send(&feed.Event{
   208  			Type: statefeed.Initialized,
   209  			Data: &statefeed.InitializedData{
   210  				StartTime:             time.Now(),
   211  				GenesisValidatorsRoot: make([]byte, 32),
   212  			},
   213  		})
   214  	}
   215  	time.Sleep(4 * time.Second)
   216  	peers := s.host.Network().Peers()
   217  	assert.Equal(t, 5, len(peers), "Not all peers added to peerstore")
   218  	require.NoError(t, s.Stop())
   219  	exitRoutine <- true
   220  }
   221  
   222  func TestHostIsResolved(t *testing.T) {
   223  	// As defined in RFC 2606 , example.org is a
   224  	// reserved example domain name.
   225  	exampleHost := "example.org"
   226  	exampleIP := "93.184.216.34"
   227  
   228  	s := &Service{
   229  		cfg: &Config{
   230  			HostDNS: exampleHost,
   231  		},
   232  		genesisTime:           time.Now(),
   233  		genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32),
   234  	}
   235  	ip, key := createAddrAndPrivKey(t)
   236  	list, err := s.createListener(ip, key)
   237  	require.NoError(t, err)
   238  
   239  	newIP := list.Self().IP()
   240  	assert.Equal(t, exampleIP, newIP.String(), "Did not resolve to expected IP")
   241  }
   242  
   243  func TestInboundPeerLimit(t *testing.T) {
   244  	fakePeer := testp2p.NewTestP2P(t)
   245  	s := &Service{
   246  		cfg:       &Config{MaxPeers: 30},
   247  		ipLimiter: leakybucket.NewCollector(ipLimit, ipBurst, false),
   248  		peers: peers.NewStatus(context.Background(), &peers.StatusConfig{
   249  			PeerLimit:    30,
   250  			ScorerParams: &scorers.Config{},
   251  		}),
   252  		host: fakePeer.BHost,
   253  	}
   254  
   255  	for i := 0; i < 30; i++ {
   256  		_ = addPeer(t, s.peers, peerdata.PeerConnectionState(ethpb.ConnectionState_CONNECTED))
   257  	}
   258  
   259  	require.Equal(t, true, s.isPeerAtLimit(false), "not at limit for outbound peers")
   260  	require.Equal(t, false, s.isPeerAtLimit(true), "at limit for inbound peers")
   261  
   262  	for i := 0; i < highWatermarkBuffer; i++ {
   263  		_ = addPeer(t, s.peers, peerdata.PeerConnectionState(ethpb.ConnectionState_CONNECTED))
   264  	}
   265  
   266  	require.Equal(t, true, s.isPeerAtLimit(true), "not at limit for inbound peers")
   267  }
   268  
   269  func TestUDPMultiAddress(t *testing.T) {
   270  	port := 6500
   271  	ipAddr, pkey := createAddrAndPrivKey(t)
   272  	genesisTime := time.Now()
   273  	genesisValidatorsRoot := make([]byte, 32)
   274  	s := &Service{
   275  		cfg:                   &Config{UDPPort: uint(port)},
   276  		genesisTime:           genesisTime,
   277  		genesisValidatorsRoot: genesisValidatorsRoot,
   278  	}
   279  	listener, err := s.createListener(ipAddr, pkey)
   280  	require.NoError(t, err)
   281  	defer listener.Close()
   282  	s.dv5Listener = listener
   283  
   284  	multiAddresses, err := s.DiscoveryAddresses()
   285  	require.NoError(t, err)
   286  	require.Equal(t, true, len(multiAddresses) > 0)
   287  	assert.Equal(t, true, strings.Contains(multiAddresses[0].String(), fmt.Sprintf("%d", port)))
   288  	assert.Equal(t, true, strings.Contains(multiAddresses[0].String(), "udp"))
   289  }
   290  
   291  func TestMultipleDiscoveryAddresses(t *testing.T) {
   292  	db, err := enode.OpenDB(t.TempDir())
   293  	require.NoError(t, err)
   294  	_, key := createAddrAndPrivKey(t)
   295  	node := enode.NewLocalNode(db, key)
   296  	node.Set(enr.IPv4{127, 0, 0, 1})
   297  	node.Set(enr.IPv6{0x20, 0x01, 0x48, 0x60, 0, 0, 0x20, 0x01, 0, 0, 0, 0, 0, 0, 0x00, 0x68})
   298  	s := &Service{dv5Listener: mockListener{localNode: node}}
   299  
   300  	multiAddresses, err := s.DiscoveryAddresses()
   301  	require.NoError(t, err)
   302  	require.Equal(t, 2, len(multiAddresses))
   303  	ipv4Found, ipv6Found := false, false
   304  	for _, address := range multiAddresses {
   305  		s := address.String()
   306  		if strings.Contains(s, "ip4") {
   307  			ipv4Found = true
   308  		} else if strings.Contains(s, "ip6") {
   309  			ipv6Found = true
   310  		}
   311  	}
   312  	assert.Equal(t, true, ipv4Found, "IPv4 discovery address not found")
   313  	assert.Equal(t, true, ipv6Found, "IPv6 discovery address not found")
   314  }
   315  
   316  func TestCorrectUDPVersion(t *testing.T) {
   317  	assert.Equal(t, "udp4", udpVersionFromIP(net.IPv4zero), "incorrect network version")
   318  	assert.Equal(t, "udp6", udpVersionFromIP(net.IPv6zero), "incorrect network version")
   319  	assert.Equal(t, "udp4", udpVersionFromIP(net.IP{200, 20, 12, 255}), "incorrect network version")
   320  	assert.Equal(t, "udp6", udpVersionFromIP(net.IP{22, 23, 24, 251, 17, 18, 0, 0, 0, 0, 12, 14, 212, 213, 16, 22}), "incorrect network version")
   321  	// v4 in v6
   322  	assert.Equal(t, "udp4", udpVersionFromIP(net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 212, 213, 16, 22}), "incorrect network version")
   323  }
   324  
   325  // addPeer is a helper to add a peer with a given connection state)
   326  func addPeer(t *testing.T, p *peers.Status, state peerdata.PeerConnectionState) peer.ID {
   327  	// Set up some peers with different states
   328  	mhBytes := []byte{0x11, 0x04}
   329  	idBytes := make([]byte, 4)
   330  	_, err := rand.Read(idBytes)
   331  	require.NoError(t, err)
   332  	mhBytes = append(mhBytes, idBytes...)
   333  	id, err := peer.IDFromBytes(mhBytes)
   334  	require.NoError(t, err)
   335  	p.Add(new(enr.Record), id, nil, network.DirInbound)
   336  	p.SetConnectionState(id, state)
   337  	p.SetMetadata(id, interfaces.WrappedMetadataV0(&pb.MetaDataV0{
   338  		SeqNumber: 0,
   339  		Attnets:   bitfield.NewBitvector64(),
   340  	}))
   341  	return id
   342  }