github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/rpc/eth/v1/node/node.go (about) 1 package node 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 "runtime" 8 "strconv" 9 "strings" 10 11 "github.com/libp2p/go-libp2p-core/peer" 12 "github.com/pkg/errors" 13 "github.com/prysmaticlabs/prysm/beacon-chain/p2p" 14 "github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers" 15 "github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers/peerdata" 16 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1" 17 ethpb_alpha "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 18 "github.com/prysmaticlabs/prysm/proto/migration" 19 "github.com/prysmaticlabs/prysm/shared/grpcutils" 20 "github.com/prysmaticlabs/prysm/shared/version" 21 "go.opencensus.io/trace" 22 "google.golang.org/grpc" 23 "google.golang.org/grpc/codes" 24 "google.golang.org/grpc/metadata" 25 "google.golang.org/grpc/status" 26 "google.golang.org/protobuf/types/known/emptypb" 27 ) 28 29 var ( 30 stateConnecting = ethpb.ConnectionState_CONNECTING.String() 31 stateConnected = ethpb.ConnectionState_CONNECTED.String() 32 stateDisconnecting = ethpb.ConnectionState_DISCONNECTING.String() 33 stateDisconnected = ethpb.ConnectionState_DISCONNECTED.String() 34 directionInbound = ethpb.PeerDirection_INBOUND.String() 35 directionOutbound = ethpb.PeerDirection_OUTBOUND.String() 36 ) 37 38 // GetIdentity retrieves data about the node's network presence. 39 func (ns *Server) GetIdentity(ctx context.Context, _ *emptypb.Empty) (*ethpb.IdentityResponse, error) { 40 ctx, span := trace.StartSpan(ctx, "nodeV1.GetIdentity") 41 defer span.End() 42 43 peerId := ns.PeerManager.PeerID().Pretty() 44 45 serializedEnr, err := p2p.SerializeENR(ns.PeerManager.ENR()) 46 if err != nil { 47 return nil, status.Errorf(codes.Internal, "Could not obtain enr: %v", err) 48 } 49 enr := "enr:" + serializedEnr 50 51 sourcep2p := ns.PeerManager.Host().Addrs() 52 p2pAddresses := make([]string, len(sourcep2p)) 53 for i := range sourcep2p { 54 p2pAddresses[i] = sourcep2p[i].String() + "/p2p/" + peerId 55 } 56 57 sourceDisc, err := ns.PeerManager.DiscoveryAddresses() 58 if err != nil { 59 return nil, status.Errorf(codes.Internal, "Could not obtain discovery address: %v", err) 60 } 61 discoveryAddresses := make([]string, len(sourceDisc)) 62 for i := range sourceDisc { 63 discoveryAddresses[i] = sourceDisc[i].String() 64 } 65 66 metadata := ðpb.Metadata{ 67 SeqNumber: ns.MetadataProvider.MetadataSeq(), 68 Attnets: ns.MetadataProvider.Metadata().AttnetsBitfield(), 69 } 70 71 return ðpb.IdentityResponse{ 72 Data: ðpb.Identity{ 73 PeerId: peerId, 74 Enr: enr, 75 P2PAddresses: p2pAddresses, 76 DiscoveryAddresses: discoveryAddresses, 77 Metadata: metadata, 78 }, 79 }, nil 80 } 81 82 // GetPeer retrieves data about the given peer. 83 func (ns *Server) GetPeer(ctx context.Context, req *ethpb.PeerRequest) (*ethpb.PeerResponse, error) { 84 ctx, span := trace.StartSpan(ctx, "nodev1.GetPeer") 85 defer span.End() 86 87 peerStatus := ns.PeersFetcher.Peers() 88 id, err := peer.Decode(req.PeerId) 89 if err != nil { 90 return nil, status.Errorf(codes.InvalidArgument, "Invalid peer ID: %v", err) 91 } 92 enr, err := peerStatus.ENR(id) 93 if err != nil { 94 if errors.Is(err, peerdata.ErrPeerUnknown) { 95 return nil, status.Error(codes.NotFound, "Peer not found") 96 } 97 return nil, status.Errorf(codes.Internal, "Could not obtain ENR: %v", err) 98 } 99 serializedEnr, err := p2p.SerializeENR(enr) 100 if err != nil { 101 return nil, status.Errorf(codes.Internal, "Could not obtain ENR: %v", err) 102 } 103 p2pAddress, err := peerStatus.Address(id) 104 if err != nil { 105 return nil, status.Errorf(codes.Internal, "Could not obtain address: %v", err) 106 } 107 state, err := peerStatus.ConnectionState(id) 108 if err != nil { 109 return nil, status.Errorf(codes.Internal, "Could not obtain connection state: %v", err) 110 } 111 direction, err := peerStatus.Direction(id) 112 if err != nil { 113 return nil, status.Errorf(codes.Internal, "Could not obtain direction: %v", err) 114 } 115 if ethpb_alpha.PeerDirection(direction) == ethpb_alpha.PeerDirection_UNKNOWN { 116 return nil, status.Error(codes.NotFound, "Peer not found") 117 } 118 119 v1ConnState := migration.V1Alpha1ConnectionStateToV1(ethpb_alpha.ConnectionState(state)) 120 v1PeerDirection, err := migration.V1Alpha1PeerDirectionToV1(ethpb_alpha.PeerDirection(direction)) 121 if err != nil { 122 return nil, status.Errorf(codes.Internal, "Could not handle peer direction: %v", err) 123 } 124 return ðpb.PeerResponse{ 125 Data: ðpb.Peer{ 126 PeerId: req.PeerId, 127 Enr: "enr:" + serializedEnr, 128 LastSeenP2PAddress: p2pAddress.String(), 129 State: v1ConnState, 130 Direction: v1PeerDirection, 131 }, 132 }, nil 133 } 134 135 // ListPeers retrieves data about the node's network peers. 136 func (ns *Server) ListPeers(ctx context.Context, req *ethpb.PeersRequest) (*ethpb.PeersResponse, error) { 137 ctx, span := trace.StartSpan(ctx, "nodev1.ListPeers") 138 defer span.End() 139 140 peerStatus := ns.PeersFetcher.Peers() 141 emptyStateFilter, emptyDirectionFilter := ns.handleEmptyFilters(req) 142 143 if emptyStateFilter && emptyDirectionFilter { 144 allIds := peerStatus.All() 145 allPeers := make([]*ethpb.Peer, 0, len(allIds)) 146 for _, id := range allIds { 147 p, err := peerInfo(peerStatus, id) 148 if err != nil { 149 return nil, status.Errorf(codes.Internal, "Could not get peer info: %v", err) 150 } 151 if p == nil { 152 continue 153 } 154 allPeers = append(allPeers, p) 155 } 156 return ðpb.PeersResponse{Data: allPeers}, nil 157 } 158 159 var stateIds []peer.ID 160 if emptyStateFilter { 161 stateIds = peerStatus.All() 162 } else { 163 for _, stateFilter := range req.State { 164 normalized := strings.ToUpper(stateFilter.String()) 165 if normalized == stateConnecting { 166 ids := peerStatus.Connecting() 167 stateIds = append(stateIds, ids...) 168 continue 169 } 170 if normalized == stateConnected { 171 ids := peerStatus.Connected() 172 stateIds = append(stateIds, ids...) 173 continue 174 } 175 if normalized == stateDisconnecting { 176 ids := peerStatus.Disconnecting() 177 stateIds = append(stateIds, ids...) 178 continue 179 } 180 if normalized == stateDisconnected { 181 ids := peerStatus.Disconnected() 182 stateIds = append(stateIds, ids...) 183 continue 184 } 185 } 186 } 187 188 var directionIds []peer.ID 189 if emptyDirectionFilter { 190 directionIds = peerStatus.All() 191 } else { 192 for _, directionFilter := range req.Direction { 193 normalized := strings.ToUpper(directionFilter.String()) 194 if normalized == directionInbound { 195 ids := peerStatus.Inbound() 196 directionIds = append(directionIds, ids...) 197 continue 198 } 199 if normalized == directionOutbound { 200 ids := peerStatus.Outbound() 201 directionIds = append(directionIds, ids...) 202 continue 203 } 204 } 205 } 206 207 var filteredIds []peer.ID 208 for _, stateId := range stateIds { 209 for _, directionId := range directionIds { 210 if stateId.Pretty() == directionId.Pretty() { 211 filteredIds = append(filteredIds, stateId) 212 break 213 } 214 } 215 } 216 filteredPeers := make([]*ethpb.Peer, 0, len(filteredIds)) 217 for _, id := range filteredIds { 218 p, err := peerInfo(peerStatus, id) 219 if err != nil { 220 return nil, status.Errorf(codes.Internal, "Could not get peer info: %v", err) 221 } 222 if p == nil { 223 continue 224 } 225 filteredPeers = append(filteredPeers, p) 226 } 227 228 return ðpb.PeersResponse{Data: filteredPeers}, nil 229 } 230 231 // PeerCount retrieves retrieves number of known peers. 232 func (ns *Server) PeerCount(ctx context.Context, _ *emptypb.Empty) (*ethpb.PeerCountResponse, error) { 233 ctx, span := trace.StartSpan(ctx, "nodev1.PeerCount") 234 defer span.End() 235 236 peerStatus := ns.PeersFetcher.Peers() 237 238 return ðpb.PeerCountResponse{ 239 Data: ðpb.PeerCountResponse_PeerCount{ 240 Disconnected: uint64(len(peerStatus.Disconnected())), 241 Connecting: uint64(len(peerStatus.Connecting())), 242 Connected: uint64(len(peerStatus.Connected())), 243 Disconnecting: uint64(len(peerStatus.Disconnecting())), 244 }, 245 }, nil 246 } 247 248 // GetVersion requests that the beacon node identify information about its implementation in a 249 // format similar to a HTTP User-Agent field. 250 func (ns *Server) GetVersion(ctx context.Context, _ *emptypb.Empty) (*ethpb.VersionResponse, error) { 251 ctx, span := trace.StartSpan(ctx, "nodev1.Version") 252 defer span.End() 253 254 v := fmt.Sprintf("Prysm/%s (%s %s)", version.SemanticVersion(), runtime.GOOS, runtime.GOARCH) 255 return ðpb.VersionResponse{ 256 Data: ðpb.Version{ 257 Version: v, 258 }, 259 }, nil 260 } 261 262 // GetSyncStatus requests the beacon node to describe if it's currently syncing or not, and 263 // if it is, what block it is up to. 264 func (ns *Server) GetSyncStatus(ctx context.Context, _ *emptypb.Empty) (*ethpb.SyncingResponse, error) { 265 ctx, span := trace.StartSpan(ctx, "nodev1.GetSyncStatus") 266 defer span.End() 267 268 headSlot := ns.HeadFetcher.HeadSlot() 269 return ðpb.SyncingResponse{ 270 Data: ðpb.SyncInfo{ 271 HeadSlot: headSlot, 272 SyncDistance: ns.GenesisTimeFetcher.CurrentSlot() - headSlot, 273 IsSyncing: ns.SyncChecker.Syncing(), 274 }, 275 }, nil 276 } 277 278 // GetHealth returns node health status in http status codes. Useful for load balancers. 279 // Response Usage: 280 // "200": 281 // description: Node is ready 282 // "206": 283 // description: Node is syncing but can serve incomplete data 284 // "503": 285 // description: Node not initialized or having issues 286 func (ns *Server) GetHealth(ctx context.Context, _ *emptypb.Empty) (*emptypb.Empty, error) { 287 ctx, span := trace.StartSpan(ctx, "nodev1.GetHealth") 288 defer span.End() 289 290 if ns.SyncChecker.Synced() { 291 return &emptypb.Empty{}, nil 292 } 293 if ns.SyncChecker.Syncing() || ns.SyncChecker.Initialized() { 294 if err := grpc.SetHeader(ctx, metadata.Pairs(grpcutils.HttpCodeMetadataKey, strconv.Itoa(http.StatusPartialContent))); err != nil { 295 // We return a positive result because failing to set a non-gRPC related header should not cause the gRPC call to fail. 296 return &emptypb.Empty{}, nil 297 } 298 return &emptypb.Empty{}, nil 299 } 300 return &emptypb.Empty{}, status.Error(codes.Internal, "Node not initialized or having issues") 301 } 302 303 func (ns *Server) handleEmptyFilters(req *ethpb.PeersRequest) (emptyState, emptyDirection bool) { 304 emptyState = true 305 for _, stateFilter := range req.State { 306 normalized := strings.ToUpper(stateFilter.String()) 307 filterValid := normalized == stateConnecting || normalized == stateConnected || 308 normalized == stateDisconnecting || normalized == stateDisconnected 309 if filterValid { 310 emptyState = false 311 break 312 } 313 } 314 315 emptyDirection = true 316 for _, directionFilter := range req.Direction { 317 normalized := strings.ToUpper(directionFilter.String()) 318 filterValid := normalized == directionInbound || normalized == directionOutbound 319 if filterValid { 320 emptyDirection = false 321 break 322 } 323 } 324 325 return emptyState, emptyDirection 326 } 327 328 func peerInfo(peerStatus *peers.Status, id peer.ID) (*ethpb.Peer, error) { 329 enr, err := peerStatus.ENR(id) 330 if err != nil { 331 return nil, errors.Wrap(err, "could not obtain ENR") 332 } 333 var serializedEnr string 334 if enr != nil { 335 serializedEnr, err = p2p.SerializeENR(enr) 336 if err != nil { 337 return nil, errors.Wrap(err, "could not serialize ENR") 338 } 339 } 340 address, err := peerStatus.Address(id) 341 if err != nil { 342 return nil, errors.Wrap(err, "could not obtain address") 343 } 344 connectionState, err := peerStatus.ConnectionState(id) 345 if err != nil { 346 return nil, errors.Wrap(err, "could not obtain connection state") 347 } 348 direction, err := peerStatus.Direction(id) 349 if err != nil { 350 return nil, errors.Wrap(err, "could not obtain direction") 351 } 352 if ethpb_alpha.PeerDirection(direction) == ethpb_alpha.PeerDirection_UNKNOWN { 353 return nil, nil 354 } 355 v1ConnState := migration.V1Alpha1ConnectionStateToV1(ethpb_alpha.ConnectionState(connectionState)) 356 v1PeerDirection, err := migration.V1Alpha1PeerDirectionToV1(ethpb_alpha.PeerDirection(direction)) 357 if err != nil { 358 return nil, errors.Wrapf(err, "could not handle peer direction") 359 } 360 p := ethpb.Peer{ 361 PeerId: id.Pretty(), 362 State: v1ConnState, 363 Direction: v1PeerDirection, 364 } 365 if address != nil { 366 p.LastSeenP2PAddress = address.String() 367 } 368 if serializedEnr != "" { 369 p.Enr = "enr:" + serializedEnr 370 } 371 372 return &p, nil 373 }