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 := &ethpb.Metadata{
    67  		SeqNumber: ns.MetadataProvider.MetadataSeq(),
    68  		Attnets:   ns.MetadataProvider.Metadata().AttnetsBitfield(),
    69  	}
    70  
    71  	return &ethpb.IdentityResponse{
    72  		Data: &ethpb.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 &ethpb.PeerResponse{
   125  		Data: &ethpb.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 &ethpb.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 &ethpb.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 &ethpb.PeerCountResponse{
   239  		Data: &ethpb.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 &ethpb.VersionResponse{
   256  		Data: &ethpb.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 &ethpb.SyncingResponse{
   270  		Data: &ethpb.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  }