github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/rpc/eth/v1/node/node_test.go (about) 1 package node 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 "runtime" 8 "strconv" 9 "strings" 10 "testing" 11 12 "github.com/ethereum/go-ethereum/p2p/enode" 13 "github.com/ethereum/go-ethereum/p2p/enr" 14 grpcruntime "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" 15 "github.com/libp2p/go-libp2p-core/network" 16 "github.com/libp2p/go-libp2p-core/peer" 17 libp2ptest "github.com/libp2p/go-libp2p-peerstore/test" 18 ma "github.com/multiformats/go-multiaddr" 19 types "github.com/prysmaticlabs/eth2-types" 20 "github.com/prysmaticlabs/go-bitfield" 21 mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" 22 "github.com/prysmaticlabs/prysm/beacon-chain/p2p" 23 "github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers" 24 mockp2p "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing" 25 syncmock "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync/testing" 26 pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" 27 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1" 28 "github.com/prysmaticlabs/prysm/shared/grpcutils" 29 "github.com/prysmaticlabs/prysm/shared/interfaces" 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/version" 34 "google.golang.org/grpc" 35 "google.golang.org/protobuf/types/known/emptypb" 36 ) 37 38 type dummyIdentity enode.ID 39 40 func (id dummyIdentity) Verify(_ *enr.Record, _ []byte) error { return nil } 41 func (id dummyIdentity) NodeAddr(_ *enr.Record) []byte { return id[:] } 42 43 func TestGetVersion(t *testing.T) { 44 semVer := version.SemanticVersion() 45 os := runtime.GOOS 46 arch := runtime.GOARCH 47 res, err := (&Server{}).GetVersion(context.Background(), &emptypb.Empty{}) 48 require.NoError(t, err) 49 v := res.Data.Version 50 assert.Equal(t, true, strings.Contains(v, semVer)) 51 assert.Equal(t, true, strings.Contains(v, os)) 52 assert.Equal(t, true, strings.Contains(v, arch)) 53 } 54 55 func TestGetHealth(t *testing.T) { 56 ctx := grpc.NewContextWithServerTransportStream(context.Background(), &grpcruntime.ServerTransportStream{}) 57 checker := &syncmock.Sync{} 58 s := &Server{ 59 SyncChecker: checker, 60 } 61 62 _, err := s.GetHealth(ctx, &emptypb.Empty{}) 63 require.ErrorContains(t, "Node not initialized or having issues", err) 64 checker.IsInitialized = true 65 _, err = s.GetHealth(ctx, &emptypb.Empty{}) 66 require.NoError(t, err) 67 stream, ok := grpc.ServerTransportStreamFromContext(ctx).(*grpcruntime.ServerTransportStream) 68 require.Equal(t, true, ok, "type assertion failed") 69 assert.Equal(t, stream.Header()[strings.ToLower(grpcutils.HttpCodeMetadataKey)][0], strconv.Itoa(http.StatusPartialContent)) 70 checker.IsSynced = true 71 _, err = s.GetHealth(ctx, &emptypb.Empty{}) 72 require.NoError(t, err) 73 } 74 75 func TestGetIdentity(t *testing.T) { 76 ctx := context.Background() 77 p2pAddr, err := ma.NewMultiaddr("/ip4/7.7.7.7/udp/30303") 78 require.NoError(t, err) 79 discAddr1, err := ma.NewMultiaddr("/ip4/7.7.7.7/udp/30303/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N") 80 require.NoError(t, err) 81 discAddr2, err := ma.NewMultiaddr("/ip6/1:2:3:4:5:6:7:8/udp/20202/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N") 82 require.NoError(t, err) 83 enrRecord := &enr.Record{} 84 err = enrRecord.SetSig(dummyIdentity{1}, []byte{42}) 85 require.NoError(t, err) 86 enrRecord.Set(enr.IPv4{7, 7, 7, 7}) 87 err = enrRecord.SetSig(dummyIdentity{}, []byte{}) 88 require.NoError(t, err) 89 attnets := bitfield.NewBitvector64() 90 attnets.SetBitAt(1, true) 91 metadataProvider := &mockp2p.MockMetadataProvider{Data: interfaces.WrappedMetadataV0(&pb.MetaDataV0{SeqNumber: 1, Attnets: attnets})} 92 93 t.Run("OK", func(t *testing.T) { 94 peerManager := &mockp2p.MockPeerManager{ 95 Enr: enrRecord, 96 PID: "foo", 97 BHost: &mockp2p.MockHost{Addresses: []ma.Multiaddr{p2pAddr}}, 98 DiscoveryAddr: []ma.Multiaddr{discAddr1, discAddr2}, 99 } 100 s := &Server{ 101 PeerManager: peerManager, 102 MetadataProvider: metadataProvider, 103 } 104 105 resp, err := s.GetIdentity(ctx, &emptypb.Empty{}) 106 require.NoError(t, err) 107 expectedID := peer.ID("foo").Pretty() 108 assert.Equal(t, expectedID, resp.Data.PeerId) 109 expectedEnr, err := p2p.SerializeENR(enrRecord) 110 require.NoError(t, err) 111 assert.Equal(t, fmt.Sprint("enr:", expectedEnr), resp.Data.Enr) 112 require.Equal(t, 1, len(resp.Data.P2PAddresses)) 113 assert.Equal(t, p2pAddr.String()+"/p2p/"+expectedID, resp.Data.P2PAddresses[0]) 114 require.Equal(t, 2, len(resp.Data.DiscoveryAddresses)) 115 ipv4Found, ipv6Found := false, false 116 for _, address := range resp.Data.DiscoveryAddresses { 117 if address == discAddr1.String() { 118 ipv4Found = true 119 } else if address == discAddr2.String() { 120 ipv6Found = true 121 } 122 } 123 assert.Equal(t, true, ipv4Found, "IPv4 discovery address not found") 124 assert.Equal(t, true, ipv6Found, "IPv6 discovery address not found") 125 assert.Equal(t, discAddr1.String(), resp.Data.DiscoveryAddresses[0]) 126 assert.Equal(t, discAddr2.String(), resp.Data.DiscoveryAddresses[1]) 127 }) 128 129 t.Run("ENR failure", func(t *testing.T) { 130 peerManager := &mockp2p.MockPeerManager{ 131 Enr: &enr.Record{}, 132 PID: "foo", 133 BHost: &mockp2p.MockHost{Addresses: []ma.Multiaddr{p2pAddr}}, 134 DiscoveryAddr: []ma.Multiaddr{discAddr1, discAddr2}, 135 } 136 s := &Server{ 137 PeerManager: peerManager, 138 MetadataProvider: metadataProvider, 139 } 140 141 _, err = s.GetIdentity(ctx, &emptypb.Empty{}) 142 assert.ErrorContains(t, "Could not obtain enr", err) 143 }) 144 145 t.Run("Discovery addresses failure", func(t *testing.T) { 146 peerManager := &mockp2p.MockPeerManager{ 147 Enr: enrRecord, 148 PID: "foo", 149 BHost: &mockp2p.MockHost{Addresses: []ma.Multiaddr{p2pAddr}}, 150 DiscoveryAddr: []ma.Multiaddr{discAddr1, discAddr2}, 151 FailDiscoveryAddr: true, 152 } 153 s := &Server{ 154 PeerManager: peerManager, 155 MetadataProvider: metadataProvider, 156 } 157 158 _, err = s.GetIdentity(ctx, &emptypb.Empty{}) 159 assert.ErrorContains(t, "Could not obtain discovery address", err) 160 }) 161 } 162 163 func TestSyncStatus(t *testing.T) { 164 currentSlot := new(types.Slot) 165 *currentSlot = 110 166 state, err := testutil.NewBeaconState() 167 require.NoError(t, err) 168 err = state.SetSlot(100) 169 require.NoError(t, err) 170 chainService := &mock.ChainService{Slot: currentSlot, State: state} 171 syncChecker := &syncmock.Sync{} 172 syncChecker.IsSyncing = true 173 174 s := &Server{ 175 HeadFetcher: chainService, 176 GenesisTimeFetcher: chainService, 177 SyncChecker: syncChecker, 178 } 179 resp, err := s.GetSyncStatus(context.Background(), &emptypb.Empty{}) 180 require.NoError(t, err) 181 assert.Equal(t, types.Slot(100), resp.Data.HeadSlot) 182 assert.Equal(t, types.Slot(10), resp.Data.SyncDistance) 183 assert.Equal(t, true, resp.Data.IsSyncing) 184 } 185 186 func TestGetPeer(t *testing.T) { 187 const rawId = "16Uiu2HAkvyYtoQXZNTsthjgLHjEnv7kvwzEmjvsJjWXpbhtqpSUN" 188 ctx := context.Background() 189 decodedId, err := peer.Decode(rawId) 190 require.NoError(t, err) 191 enrRecord := &enr.Record{} 192 err = enrRecord.SetSig(dummyIdentity{1}, []byte{42}) 193 require.NoError(t, err) 194 enrRecord.Set(enr.IPv4{7, 7, 7, 7}) 195 err = enrRecord.SetSig(dummyIdentity{}, []byte{}) 196 require.NoError(t, err) 197 const p2pAddr = "/ip4/7.7.7.7/udp/30303/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N" 198 p2pMultiAddr, err := ma.NewMultiaddr(p2pAddr) 199 require.NoError(t, err) 200 peerFetcher := &mockp2p.MockPeersProvider{} 201 s := Server{PeersFetcher: peerFetcher} 202 peerFetcher.Peers().Add(enrRecord, decodedId, p2pMultiAddr, network.DirInbound) 203 204 t.Run("OK", func(t *testing.T) { 205 resp, err := s.GetPeer(ctx, ðpb.PeerRequest{PeerId: rawId}) 206 require.NoError(t, err) 207 assert.Equal(t, rawId, resp.Data.PeerId) 208 assert.Equal(t, p2pAddr, resp.Data.LastSeenP2PAddress) 209 assert.Equal(t, "enr:yoABgmlwhAcHBwc=", resp.Data.Enr) 210 assert.Equal(t, ethpb.ConnectionState_DISCONNECTED, resp.Data.State) 211 assert.Equal(t, ethpb.PeerDirection_INBOUND, resp.Data.Direction) 212 }) 213 214 t.Run("Invalid ID", func(t *testing.T) { 215 _, err = s.GetPeer(ctx, ðpb.PeerRequest{PeerId: "foo"}) 216 assert.ErrorContains(t, "Invalid peer ID", err) 217 }) 218 219 t.Run("Peer not found", func(t *testing.T) { 220 generatedId := "16Uiu2HAmQqFdEcHbSmQTQuLoAhnMUrgoWoraKK4cUJT6FuuqHqTU" 221 _, err = s.GetPeer(ctx, ðpb.PeerRequest{PeerId: generatedId}) 222 assert.ErrorContains(t, "Peer not found", err) 223 }) 224 } 225 226 func TestListPeers(t *testing.T) { 227 ids := libp2ptest.GeneratePeerIDs(9) 228 peerFetcher := &mockp2p.MockPeersProvider{} 229 peerFetcher.ClearPeers() 230 peerStatus := peerFetcher.Peers() 231 232 for i, id := range ids { 233 // Make last peer undiscovered 234 if i == len(ids)-1 { 235 peerStatus.Add(nil, id, nil, network.DirUnknown) 236 } else { 237 enrRecord := &enr.Record{} 238 err := enrRecord.SetSig(dummyIdentity{1}, []byte{42}) 239 require.NoError(t, err) 240 enrRecord.Set(enr.IPv4{127, 0, 0, byte(i)}) 241 err = enrRecord.SetSig(dummyIdentity{}, []byte{}) 242 require.NoError(t, err) 243 var p2pAddr = "/ip4/127.0.0." + strconv.Itoa(i) + "/udp/30303/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N" 244 p2pMultiAddr, err := ma.NewMultiaddr(p2pAddr) 245 require.NoError(t, err) 246 247 var direction network.Direction 248 if i%2 == 0 { 249 direction = network.DirInbound 250 } else { 251 direction = network.DirOutbound 252 } 253 peerStatus.Add(enrRecord, id, p2pMultiAddr, direction) 254 255 switch i { 256 case 0, 1: 257 peerStatus.SetConnectionState(id, peers.PeerConnecting) 258 case 2, 3: 259 peerStatus.SetConnectionState(id, peers.PeerConnected) 260 case 4, 5: 261 peerStatus.SetConnectionState(id, peers.PeerDisconnecting) 262 case 6, 7: 263 peerStatus.SetConnectionState(id, peers.PeerDisconnected) 264 default: 265 t.Fatalf("Failed to set connection state for peer") 266 } 267 } 268 } 269 270 s := Server{PeersFetcher: peerFetcher} 271 272 t.Run("Peer data OK", func(t *testing.T) { 273 // We will check the first peer from the list. 274 expectedId := ids[0] 275 276 resp, err := s.ListPeers(context.Background(), ðpb.PeersRequest{ 277 State: []ethpb.ConnectionState{ethpb.ConnectionState_CONNECTING}, 278 Direction: []ethpb.PeerDirection{ethpb.PeerDirection_INBOUND}, 279 }) 280 require.NoError(t, err) 281 require.Equal(t, 1, len(resp.Data)) 282 returnedPeer := resp.Data[0] 283 assert.Equal(t, expectedId.Pretty(), returnedPeer.PeerId) 284 expectedEnr, err := peerStatus.ENR(expectedId) 285 require.NoError(t, err) 286 serializedEnr, err := p2p.SerializeENR(expectedEnr) 287 require.NoError(t, err) 288 assert.Equal(t, "enr:"+serializedEnr, returnedPeer.Enr) 289 expectedP2PAddr, err := peerStatus.Address(expectedId) 290 require.NoError(t, err) 291 assert.Equal(t, expectedP2PAddr.String(), returnedPeer.LastSeenP2PAddress) 292 assert.Equal(t, ethpb.ConnectionState_CONNECTING, returnedPeer.State) 293 assert.Equal(t, ethpb.PeerDirection_INBOUND, returnedPeer.Direction) 294 }) 295 296 filterTests := []struct { 297 name string 298 states []ethpb.ConnectionState 299 directions []ethpb.PeerDirection 300 wantIds []peer.ID 301 }{ 302 { 303 name: "No filters - return all peers", 304 states: []ethpb.ConnectionState{}, 305 directions: []ethpb.PeerDirection{}, 306 wantIds: ids[:len(ids)-1], // Excluding last peer as it is not connected. 307 }, 308 { 309 name: "State filter empty - return peers for all states", 310 states: []ethpb.ConnectionState{}, 311 directions: []ethpb.PeerDirection{ethpb.PeerDirection_INBOUND}, 312 wantIds: []peer.ID{ids[0], ids[2], ids[4], ids[6]}, 313 }, 314 { 315 name: "Direction filter empty - return peers for all directions", 316 states: []ethpb.ConnectionState{ethpb.ConnectionState_CONNECTED}, 317 directions: []ethpb.PeerDirection{}, 318 wantIds: []peer.ID{ids[2], ids[3]}, 319 }, 320 { 321 name: "One state and direction", 322 states: []ethpb.ConnectionState{ethpb.ConnectionState_DISCONNECTED}, 323 directions: []ethpb.PeerDirection{ethpb.PeerDirection_INBOUND}, 324 wantIds: []peer.ID{ids[6]}, 325 }, 326 { 327 name: "Multiple states and directions", 328 states: []ethpb.ConnectionState{ethpb.ConnectionState_CONNECTING, ethpb.ConnectionState_DISCONNECTING}, 329 directions: []ethpb.PeerDirection{ethpb.PeerDirection_INBOUND, ethpb.PeerDirection_OUTBOUND}, 330 wantIds: []peer.ID{ids[0], ids[1], ids[4], ids[5]}, 331 }, 332 { 333 name: "Unknown filter is ignored", 334 states: []ethpb.ConnectionState{ethpb.ConnectionState_CONNECTED, 99}, 335 directions: []ethpb.PeerDirection{ethpb.PeerDirection_OUTBOUND, 99}, 336 wantIds: []peer.ID{ids[3]}, 337 }, 338 { 339 name: "Only unknown filters - return all peers", 340 states: []ethpb.ConnectionState{99}, 341 directions: []ethpb.PeerDirection{99}, 342 wantIds: ids[:len(ids)-1], // Excluding last peer as it is not connected. 343 }, 344 } 345 for _, tt := range filterTests { 346 t.Run(tt.name, func(t *testing.T) { 347 resp, err := s.ListPeers(context.Background(), ðpb.PeersRequest{ 348 State: tt.states, 349 Direction: tt.directions, 350 }) 351 require.NoError(t, err) 352 assert.Equal(t, len(tt.wantIds), len(resp.Data), "Wrong number of peers returned") 353 for _, id := range tt.wantIds { 354 expectedId := id.Pretty() 355 found := false 356 for _, returnedPeer := range resp.Data { 357 if returnedPeer.PeerId == expectedId { 358 found = true 359 break 360 } 361 } 362 if !found { 363 t.Errorf("Expected ID '" + expectedId + "' not found") 364 } 365 } 366 }) 367 } 368 } 369 370 func TestListPeers_NoPeersReturnsEmptyArray(t *testing.T) { 371 peerFetcher := &mockp2p.MockPeersProvider{} 372 peerFetcher.ClearPeers() 373 s := Server{PeersFetcher: peerFetcher} 374 375 resp, err := s.ListPeers(context.Background(), ðpb.PeersRequest{ 376 State: []ethpb.ConnectionState{ethpb.ConnectionState_CONNECTED}, 377 }) 378 require.NoError(t, err) 379 require.NotNil(t, resp.Data) 380 assert.Equal(t, 0, len(resp.Data)) 381 } 382 383 func TestPeerCount(t *testing.T) { 384 ids := libp2ptest.GeneratePeerIDs(10) 385 peerFetcher := &mockp2p.MockPeersProvider{} 386 peerFetcher.ClearPeers() 387 peerStatus := peerFetcher.Peers() 388 389 for i, id := range ids { 390 enrRecord := &enr.Record{} 391 err := enrRecord.SetSig(dummyIdentity{1}, []byte{42}) 392 require.NoError(t, err) 393 enrRecord.Set(enr.IPv4{127, 0, 0, byte(i)}) 394 err = enrRecord.SetSig(dummyIdentity{}, []byte{}) 395 require.NoError(t, err) 396 var p2pAddr = "/ip4/127.0.0." + strconv.Itoa(i) + "/udp/30303/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N" 397 p2pMultiAddr, err := ma.NewMultiaddr(p2pAddr) 398 require.NoError(t, err) 399 400 var direction network.Direction 401 if i%2 == 0 { 402 direction = network.DirInbound 403 } else { 404 direction = network.DirOutbound 405 } 406 peerStatus.Add(enrRecord, id, p2pMultiAddr, direction) 407 408 switch i { 409 case 0: 410 peerStatus.SetConnectionState(id, peers.PeerConnecting) 411 case 1, 2: 412 peerStatus.SetConnectionState(id, peers.PeerConnected) 413 case 3, 4, 5: 414 peerStatus.SetConnectionState(id, peers.PeerDisconnecting) 415 case 6, 7, 8, 9: 416 peerStatus.SetConnectionState(id, peers.PeerDisconnected) 417 default: 418 t.Fatalf("Failed to set connection state for peer") 419 } 420 } 421 422 s := Server{PeersFetcher: peerFetcher} 423 resp, err := s.PeerCount(context.Background(), &emptypb.Empty{}) 424 require.NoError(t, err) 425 assert.Equal(t, uint64(1), resp.Data.Connecting, "Wrong number of connecting peers") 426 assert.Equal(t, uint64(2), resp.Data.Connected, "Wrong number of connected peers") 427 assert.Equal(t, uint64(3), resp.Data.Disconnecting, "Wrong number of disconnecting peers") 428 assert.Equal(t, uint64(4), resp.Data.Disconnected, "Wrong number of disconnected peers") 429 } 430 431 func BenchmarkListPeers(b *testing.B) { 432 // We simulate having a lot of peers. 433 ids := libp2ptest.GeneratePeerIDs(2000) 434 peerFetcher := &mockp2p.MockPeersProvider{} 435 436 for _, id := range ids { 437 enrRecord := &enr.Record{} 438 err := enrRecord.SetSig(dummyIdentity{1}, []byte{42}) 439 require.NoError(b, err) 440 enrRecord.Set(enr.IPv4{7, 7, 7, 7}) 441 err = enrRecord.SetSig(dummyIdentity{}, []byte{}) 442 require.NoError(b, err) 443 const p2pAddr = "/ip4/7.7.7.7/udp/30303/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N" 444 p2pMultiAddr, err := ma.NewMultiaddr(p2pAddr) 445 require.NoError(b, err) 446 peerFetcher.Peers().Add(enrRecord, id, p2pMultiAddr, network.DirInbound) 447 } 448 449 s := Server{PeersFetcher: peerFetcher} 450 451 b.ResetTimer() 452 for i := 0; i < b.N; i++ { 453 _, err := s.ListPeers(context.Background(), ðpb.PeersRequest{ 454 State: []ethpb.ConnectionState{}, 455 Direction: []ethpb.PeerDirection{}, 456 }) 457 require.NoError(b, err) 458 } 459 }