aboutsummaryrefslogtreecommitdiffstats
path: root/p2p
diff options
context:
space:
mode:
Diffstat (limited to 'p2p')
-rw-r--r--p2p/peer.go46
-rw-r--r--p2p/protocol.go15
-rw-r--r--p2p/server.go63
3 files changed, 123 insertions, 1 deletions
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
+}