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 }