From e46ab3bdcde7236c8fe54d6c83655e50bd19fe31 Mon Sep 17 00:00:00 2001 From: Péter Szilágyi Date: Tue, 27 Oct 2015 15:10:30 +0200 Subject: eth, p2p, rpc/api: polish protocol info gathering --- p2p/peer.go | 46 +++++++++++++++++++++++++++++++++++++++++ p2p/protocol.go | 15 +++++++++++++- p2p/server.go | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 1 deletion(-) (limited to 'p2p') diff --git a/p2p/peer.go b/p2p/peer.go index 1b3b19c79..72ed4069c 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -359,3 +359,49 @@ func (rw *protoRW) ReadMsg() (Msg, error) { return Msg{}, io.EOF } } + +// PeerInfo represents a short summary of the information known about a connected +// peer. Sub-protocol independent fields are contained and initialized here, with +// protocol specifics delegated to all connected sub-protocols. +type PeerInfo struct { + ID string `json:"id"` // Unique node identifier (also the encryption key) + Name string `json:"name"` // Name of the node, including client type, version, OS, custom data + Caps []string `json:"caps"` // Sum-protocols advertised by this particular peer + Network struct { + LocalAddress string `json:"localAddress"` // Local endpoint of the TCP data connection + RemoteAddress string `json:"remoteAddress"` // Remote endpoint of the TCP data connection + } `json:"network"` + Protocols map[string]interface{} `json:"protocols"` // Sub-protocol specific metadata fields +} + +// Info gathers and returns a collection of metadata known about a peer. +func (p *Peer) Info() *PeerInfo { + // Gather the protocol capabilities + var caps []string + for _, cap := range p.Caps() { + caps = append(caps, cap.String()) + } + // Assemble the generic peer metadata + info := &PeerInfo{ + ID: p.ID().String(), + Name: p.Name(), + Caps: caps, + Protocols: make(map[string]interface{}), + } + info.Network.LocalAddress = p.LocalAddr().String() + info.Network.RemoteAddress = p.RemoteAddr().String() + + // Gather all the running protocol infos + for _, proto := range p.running { + protoInfo := interface{}("unknown") + if query := proto.Protocol.PeerInfo; query != nil { + if metadata := query(p.ID()); metadata != nil { + protoInfo = metadata + } else { + protoInfo = "handshake" + } + } + info.Protocols[proto.Name] = protoInfo + } + return info +} diff --git a/p2p/protocol.go b/p2p/protocol.go index ac0c3d942..ee747ba23 100644 --- a/p2p/protocol.go +++ b/p2p/protocol.go @@ -16,7 +16,11 @@ package p2p -import "fmt" +import ( + "fmt" + + "github.com/ethereum/go-ethereum/p2p/discover" +) // Protocol represents a P2P subprotocol implementation. type Protocol struct { @@ -39,6 +43,15 @@ type Protocol struct { // any protocol-level error (such as an I/O error) that is // encountered. Run func(peer *Peer, rw MsgReadWriter) error + + // NodeInfo is an optional helper method to retrieve protocol specific metadata + // about the host node. + NodeInfo func() interface{} + + // PeerInfo is an optional helper method to retrieve protocol specific metadata + // about a certain peer in the network. If an info retrieval function is set, + // but returns nil, it is assumed that the protocol handshake is still running. + PeerInfo func(id discover.NodeID) interface{} } func (p Protocol) cap() Cap { diff --git a/p2p/server.go b/p2p/server.go index 6060adc71..ee670b10e 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -689,3 +689,66 @@ func (srv *Server) runPeer(p *Peer) { NumConnections: srv.PeerCount(), }) } + +// NodeInfo represents a short summary of the information known about the host. +type NodeInfo struct { + ID string `json:"id"` // Unique node identifier (also the encryption key) + Name string `json:"name"` // Name of the node, including client type, version, OS, custom data + Enode string `json:"enode"` // Enode URL for adding this peer from remote peers + IP string `json:"ip"` // IP address of the node + Ports struct { + Discovery int `json:"discovery"` // UDP listening port for discovery protocol + Listener int `json:"listener"` // TCP listening port for RLPx + } `json:"ports"` + ListenAddr string `json:"listenAddr"` + Protocols map[string]interface{} `json:"protocols"` +} + +// Info gathers and returns a collection of metadata known about the host. +func (srv *Server) NodeInfo() *NodeInfo { + node := srv.Self() + + // Gather and assemble the generic node infos + info := &NodeInfo{ + Name: srv.Name, + Enode: node.String(), + ID: node.ID.String(), + IP: node.IP.String(), + ListenAddr: srv.ListenAddr, + Protocols: make(map[string]interface{}), + } + info.Ports.Discovery = int(node.UDP) + info.Ports.Listener = int(node.TCP) + + // Gather all the running protocol infos (only once per protocol type) + for _, proto := range srv.Protocols { + if _, ok := info.Protocols[proto.Name]; !ok { + nodeInfo := interface{}("unknown") + if query := proto.NodeInfo; query != nil { + nodeInfo = proto.NodeInfo() + } + info.Protocols[proto.Name] = nodeInfo + } + } + return info +} + +// PeersInfo returns an array of metadata objects describing connected peers. +func (srv *Server) PeersInfo() []*PeerInfo { + // Gather all the generic and sub-protocol specific infos + infos := make([]*PeerInfo, 0, srv.PeerCount()) + for _, peer := range srv.Peers() { + if peer != nil { + infos = append(infos, peer.Info()) + } + } + // Sort the result array alphabetically by node identifier + for i := 0; i < len(infos); i++ { + for j := i + 1; j < len(infos); j++ { + if infos[i].ID > infos[j].ID { + infos[i], infos[j] = infos[j], infos[i] + } + } + } + return infos +} -- cgit