// Package node defines a gRPC node service implementation, providing // useful endpoints for checking a node's sync status, peer info, // genesis data, and version information. package node import ( "context" "fmt" "sort" "time" ptypes "github.com/gogo/protobuf/types" "github.com/libp2p/go-libp2p-core/network" "github.com/libp2p/go-libp2p-core/peer" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/prysm/beacon-chain/blockchain" "github.com/prysmaticlabs/prysm/beacon-chain/db" "github.com/prysmaticlabs/prysm/beacon-chain/p2p" "github.com/prysmaticlabs/prysm/beacon-chain/sync" pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" "github.com/prysmaticlabs/prysm/shared/logutil" "github.com/prysmaticlabs/prysm/shared/version" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) // Server defines a server implementation of the gRPC Node service, // providing RPC endpoints for verifying a beacon node's sync status, genesis and // version information, and services the node implements and runs. type Server struct { LogsStreamer logutil.Streamer StreamLogsBufferSize int SyncChecker sync.Checker Server *grpc.Server BeaconDB db.ReadOnlyDatabase PeersFetcher p2p.PeersProvider PeerManager p2p.PeerManager GenesisTimeFetcher blockchain.TimeFetcher GenesisFetcher blockchain.GenesisFetcher BeaconMonitoringHost string BeaconMonitoringPort int } // GetSyncStatus checks the current network sync status of the node. func (ns *Server) GetSyncStatus(_ context.Context, _ *ptypes.Empty) (*ethpb.SyncStatus, error) { return ðpb.SyncStatus{ Syncing: ns.SyncChecker.Syncing(), }, nil } // GetGenesis fetches genesis chain information of Ethereum 2.0. Returns unix timestamp 0 // if a genesis time has yet to be determined. func (ns *Server) GetGenesis(ctx context.Context, _ *ptypes.Empty) (*ethpb.Genesis, error) { contractAddr, err := ns.BeaconDB.DepositContractAddress(ctx) if err != nil { return nil, status.Errorf(codes.Internal, "Could not retrieve contract address from db: %v", err) } genesisTime := ns.GenesisTimeFetcher.GenesisTime() var defaultGenesisTime time.Time var gt *ptypes.Timestamp if genesisTime == defaultGenesisTime { gt, err = ptypes.TimestampProto(time.Unix(0, 0)) } else { gt, err = ptypes.TimestampProto(genesisTime) } if err != nil { return nil, status.Errorf(codes.Internal, "Could not convert genesis time to proto: %v", err) } genValRoot := ns.GenesisFetcher.GenesisValidatorRoot() return ðpb.Genesis{ GenesisTime: gt, DepositContractAddress: contractAddr, GenesisValidatorsRoot: genValRoot[:], }, nil } // GetVersion checks the version information of the beacon node. func (ns *Server) GetVersion(_ context.Context, _ *ptypes.Empty) (*ethpb.Version, error) { return ðpb.Version{ Version: version.GetVersion(), }, nil } // ListImplementedServices lists the services implemented and enabled by this node. // // Any service not present in this list may return UNIMPLEMENTED or // PERMISSION_DENIED. The server may also support fetching services by grpc // reflection. func (ns *Server) ListImplementedServices(_ context.Context, _ *ptypes.Empty) (*ethpb.ImplementedServices, error) { serviceInfo := ns.Server.GetServiceInfo() serviceNames := make([]string, 0, len(serviceInfo)) for svc := range serviceInfo { serviceNames = append(serviceNames, svc) } sort.Strings(serviceNames) return ðpb.ImplementedServices{ Services: serviceNames, }, nil } // GetHost returns the p2p data on the current local and host peer. func (ns *Server) GetHost(_ context.Context, _ *ptypes.Empty) (*ethpb.HostData, error) { var stringAddr []string for _, addr := range ns.PeerManager.Host().Addrs() { stringAddr = append(stringAddr, addr.String()) } record := ns.PeerManager.ENR() enr := "" var err error if record != nil { enr, err = p2p.SerializeENR(record) if err != nil { return nil, status.Errorf(codes.Internal, "Unable to serialize enr: %v", err) } } return ðpb.HostData{ Addresses: stringAddr, PeerId: ns.PeerManager.PeerID().String(), Enr: enr, }, nil } // GetPeer returns the data known about the peer defined by the provided peer id. func (ns *Server) GetPeer(_ context.Context, peerReq *ethpb.PeerRequest) (*ethpb.Peer, error) { pid, err := peer.Decode(peerReq.PeerId) if err != nil { return nil, status.Errorf(codes.InvalidArgument, "Unable to parse provided peer id: %v", err) } addr, err := ns.PeersFetcher.Peers().Address(pid) if err != nil { return nil, status.Errorf(codes.NotFound, "Requested peer does not exist: %v", err) } dir, err := ns.PeersFetcher.Peers().Direction(pid) if err != nil { return nil, status.Errorf(codes.NotFound, "Requested peer does not exist: %v", err) } pbDirection := ethpb.PeerDirection_UNKNOWN switch dir { case network.DirInbound: pbDirection = ethpb.PeerDirection_INBOUND case network.DirOutbound: pbDirection = ethpb.PeerDirection_OUTBOUND } connState, err := ns.PeersFetcher.Peers().ConnectionState(pid) if err != nil { return nil, status.Errorf(codes.NotFound, "Requested peer does not exist: %v", err) } record, err := ns.PeersFetcher.Peers().ENR(pid) if err != nil { return nil, status.Errorf(codes.NotFound, "Requested peer does not exist: %v", err) } enr := "" if record != nil { enr, err = p2p.SerializeENR(record) if err != nil { return nil, status.Errorf(codes.Internal, "Unable to serialize enr: %v", err) } } return ðpb.Peer{ Address: addr.String(), Direction: pbDirection, ConnectionState: ethpb.ConnectionState(connState), PeerId: peerReq.PeerId, Enr: enr, }, nil } // ListPeers lists the peers connected to this node. func (ns *Server) ListPeers(ctx context.Context, _ *ptypes.Empty) (*ethpb.Peers, error) { peers := ns.PeersFetcher.Peers().Connected() res := make([]*ethpb.Peer, 0, len(peers)) for _, pid := range peers { if ctx.Err() != nil { return nil, ctx.Err() } multiaddr, err := ns.PeersFetcher.Peers().Address(pid) if err != nil { continue } direction, err := ns.PeersFetcher.Peers().Direction(pid) if err != nil { continue } record, err := ns.PeersFetcher.Peers().ENR(pid) if err != nil { continue } enr := "" if record != nil { enr, err = p2p.SerializeENR(record) if err != nil { continue } } multiAddrStr := "unknown" if multiaddr != nil { multiAddrStr = multiaddr.String() } address := fmt.Sprintf("%s/p2p/%s", multiAddrStr, pid.Pretty()) pbDirection := ethpb.PeerDirection_UNKNOWN switch direction { case network.DirInbound: pbDirection = ethpb.PeerDirection_INBOUND case network.DirOutbound: pbDirection = ethpb.PeerDirection_OUTBOUND } res = append(res, ðpb.Peer{ Address: address, Direction: pbDirection, ConnectionState: ethpb.ConnectionState_CONNECTED, PeerId: pid.String(), Enr: enr, }) } return ðpb.Peers{ Peers: res, }, nil } // StreamBeaconLogs from the beacon node via a gRPC server-side stream. func (ns *Server) StreamBeaconLogs(_ *ptypes.Empty, stream pb.Health_StreamBeaconLogsServer) error { ch := make(chan []byte, ns.StreamLogsBufferSize) defer close(ch) sub := ns.LogsStreamer.LogsFeed().Subscribe(ch) defer sub.Unsubscribe() recentLogs := ns.LogsStreamer.GetLastFewLogs() logStrings := make([]string, len(recentLogs)) for i, log := range recentLogs { logStrings[i] = string(log) } if err := stream.Send(&pb.LogsResponse{ Logs: logStrings, }); err != nil { return status.Errorf(codes.Unavailable, "Could not send over stream: %v", err) } for { select { case log := <-ch: resp := &pb.LogsResponse{ Logs: []string{string(log)}, } if err := stream.Send(resp); err != nil { return status.Errorf(codes.Unavailable, "Could not send over stream: %v", err) } case <-stream.Context().Done(): return status.Error(codes.Canceled, "Context canceled") } } }