prysm-pulse/beacon-chain/rpc/eth/node/handlers_peers.go
terence 5a66807989
Update to V5 (#13622)
* First take at updating everything to v5

* Patch gRPC gateway to use prysm v5

Fix patch

* Update go ssz

---------

Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>
2024-02-15 05:46:47 +00:00

281 lines
8.1 KiB
Go

package node
import (
"net/http"
"strconv"
"strings"
"github.com/gorilla/mux"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/api/server/structs"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/p2p"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/p2p/peers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/p2p/peers/peerdata"
"github.com/prysmaticlabs/prysm/v5/network/httputil"
"github.com/prysmaticlabs/prysm/v5/proto/migration"
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"go.opencensus.io/trace"
)
// GetPeer retrieves data about the given peer.
func (s *Server) GetPeer(w http.ResponseWriter, r *http.Request) {
_, span := trace.StartSpan(r.Context(), "node.GetPeer")
defer span.End()
rawId := mux.Vars(r)["peer_id"]
if rawId == "" {
httputil.HandleError(w, "peer_id is required in URL params", http.StatusBadRequest)
return
}
peerStatus := s.PeersFetcher.Peers()
id, err := peer.Decode(rawId)
if err != nil {
httputil.HandleError(w, "Invalid peer ID: "+err.Error(), http.StatusBadRequest)
return
}
enr, err := peerStatus.ENR(id)
if err != nil {
if errors.Is(err, peerdata.ErrPeerUnknown) {
httputil.HandleError(w, "Peer not found: "+err.Error(), http.StatusNotFound)
return
}
httputil.HandleError(w, "Could not obtain ENR: "+err.Error(), http.StatusInternalServerError)
return
}
serializedEnr, err := p2p.SerializeENR(enr)
if err != nil {
httputil.HandleError(w, "Could not obtain ENR: "+err.Error(), http.StatusInternalServerError)
return
}
p2pAddress, err := peerStatus.Address(id)
if err != nil {
httputil.HandleError(w, "Could not obtain address: "+err.Error(), http.StatusInternalServerError)
return
}
state, err := peerStatus.ConnectionState(id)
if err != nil {
httputil.HandleError(w, "Could not obtain connection state: "+err.Error(), http.StatusInternalServerError)
return
}
direction, err := peerStatus.Direction(id)
if err != nil {
httputil.HandleError(w, "Could not obtain direction: "+err.Error(), http.StatusInternalServerError)
return
}
if eth.PeerDirection(direction) == eth.PeerDirection_UNKNOWN {
httputil.HandleError(w, "Peer not found", http.StatusNotFound)
return
}
v1ConnState := migration.V1Alpha1ConnectionStateToV1(eth.ConnectionState(state))
v1PeerDirection, err := migration.V1Alpha1PeerDirectionToV1(eth.PeerDirection(direction))
if err != nil {
httputil.HandleError(w, "Could not handle peer direction: "+err.Error(), http.StatusInternalServerError)
return
}
resp := &structs.GetPeerResponse{
Data: &structs.Peer{
PeerId: rawId,
Enr: "enr:" + serializedEnr,
LastSeenP2PAddress: p2pAddress.String(),
State: strings.ToLower(v1ConnState.String()),
Direction: strings.ToLower(v1PeerDirection.String()),
},
}
httputil.WriteJson(w, resp)
}
// GetPeers retrieves data about the node's network peers.
func (s *Server) GetPeers(w http.ResponseWriter, r *http.Request) {
_, span := trace.StartSpan(r.Context(), "node.GetPeers")
defer span.End()
states := r.URL.Query()["state"]
directions := r.URL.Query()["direction"]
peerStatus := s.PeersFetcher.Peers()
emptyStateFilter, emptyDirectionFilter := handleEmptyFilters(states, directions)
if emptyStateFilter && emptyDirectionFilter {
allIds := peerStatus.All()
allPeers := make([]*structs.Peer, 0, len(allIds))
for _, id := range allIds {
p, err := peerInfo(peerStatus, id)
if err != nil {
httputil.HandleError(w, "Could not get peer info: "+err.Error(), http.StatusInternalServerError)
return
}
if p == nil {
continue
}
allPeers = append(allPeers, p)
}
resp := &structs.GetPeersResponse{Data: allPeers}
httputil.WriteJson(w, resp)
return
}
var stateIds []peer.ID
if emptyStateFilter {
stateIds = peerStatus.All()
} else {
for _, stateFilter := range states {
switch strings.ToUpper(stateFilter) {
case stateConnecting:
ids := peerStatus.Connecting()
stateIds = append(stateIds, ids...)
case stateConnected:
ids := peerStatus.Connected()
stateIds = append(stateIds, ids...)
case stateDisconnecting:
ids := peerStatus.Disconnecting()
stateIds = append(stateIds, ids...)
case stateDisconnected:
ids := peerStatus.Disconnected()
stateIds = append(stateIds, ids...)
}
}
}
var directionIds []peer.ID
if emptyDirectionFilter {
directionIds = peerStatus.All()
} else {
for _, directionFilter := range directions {
switch strings.ToUpper(directionFilter) {
case directionInbound:
ids := peerStatus.Inbound()
directionIds = append(directionIds, ids...)
case directionOutbound:
ids := peerStatus.Outbound()
directionIds = append(directionIds, ids...)
}
}
}
var filteredIds []peer.ID
for _, stateId := range stateIds {
for _, directionId := range directionIds {
if stateId.String() == directionId.String() {
filteredIds = append(filteredIds, stateId)
break
}
}
}
filteredPeers := make([]*structs.Peer, 0, len(filteredIds))
for _, id := range filteredIds {
p, err := peerInfo(peerStatus, id)
if err != nil {
httputil.HandleError(w, "Could not get peer info: "+err.Error(), http.StatusInternalServerError)
return
}
if p == nil {
continue
}
filteredPeers = append(filteredPeers, p)
}
resp := &structs.GetPeersResponse{Data: filteredPeers}
httputil.WriteJson(w, resp)
}
// GetPeerCount retrieves number of known peers.
func (s *Server) GetPeerCount(w http.ResponseWriter, r *http.Request) {
_, span := trace.StartSpan(r.Context(), "node.PeerCount")
defer span.End()
peerStatus := s.PeersFetcher.Peers()
resp := &structs.GetPeerCountResponse{
Data: &structs.PeerCount{
Disconnected: strconv.FormatInt(int64(len(peerStatus.Disconnected())), 10),
Connecting: strconv.FormatInt(int64(len(peerStatus.Connecting())), 10),
Connected: strconv.FormatInt(int64(len(peerStatus.Connected())), 10),
Disconnecting: strconv.FormatInt(int64(len(peerStatus.Disconnecting())), 10),
},
}
httputil.WriteJson(w, resp)
}
func handleEmptyFilters(states []string, directions []string) (emptyState, emptyDirection bool) {
emptyState = true
for _, stateFilter := range states {
normalized := strings.ToUpper(stateFilter)
filterValid := normalized == stateConnecting || normalized == stateConnected ||
normalized == stateDisconnecting || normalized == stateDisconnected
if filterValid {
emptyState = false
break
}
}
emptyDirection = true
for _, directionFilter := range directions {
normalized := strings.ToUpper(directionFilter)
filterValid := normalized == directionInbound || normalized == directionOutbound
if filterValid {
emptyDirection = false
break
}
}
return emptyState, emptyDirection
}
func peerInfo(peerStatus *peers.Status, id peer.ID) (*structs.Peer, error) {
enr, err := peerStatus.ENR(id)
if err != nil {
if errors.Is(err, peerdata.ErrPeerUnknown) {
return nil, nil
}
return nil, errors.Wrap(err, "could not obtain ENR")
}
var serializedEnr string
if enr != nil {
serializedEnr, err = p2p.SerializeENR(enr)
if err != nil {
return nil, errors.Wrap(err, "could not serialize ENR")
}
}
address, err := peerStatus.Address(id)
if err != nil {
if errors.Is(err, peerdata.ErrPeerUnknown) {
return nil, nil
}
return nil, errors.Wrap(err, "could not obtain address")
}
connectionState, err := peerStatus.ConnectionState(id)
if err != nil {
if errors.Is(err, peerdata.ErrPeerUnknown) {
return nil, nil
}
return nil, errors.Wrap(err, "could not obtain connection state")
}
direction, err := peerStatus.Direction(id)
if err != nil {
if errors.Is(err, peerdata.ErrPeerUnknown) {
return nil, nil
}
return nil, errors.Wrap(err, "could not obtain direction")
}
if eth.PeerDirection(direction) == eth.PeerDirection_UNKNOWN {
return nil, nil
}
p := &structs.Peer{
PeerId: id.String(),
State: strings.ToLower(eth.ConnectionState(connectionState).String()),
Direction: strings.ToLower(eth.PeerDirection(direction).String()),
}
if address != nil {
p.LastSeenP2PAddress = address.String()
}
if serializedEnr != "" {
p.Enr = "enr:" + serializedEnr
}
return p, nil
}