diff --git a/api/admin/networking.go b/api/admin/networking.go deleted file mode 100644 index 3207e46..0000000 --- a/api/admin/networking.go +++ /dev/null @@ -1,27 +0,0 @@ -// (c) 2019-2020, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package admin - -import ( - "sort" - - "github.com/ava-labs/gecko/utils" -) - -// Peerable can return a group of peers -type Peerable interface{ IPs() []utils.IPDesc } - -// Networking provides helper methods for tracking the current network state -type Networking struct{ peers Peerable } - -// Peers returns the current peers -func (n *Networking) Peers() ([]string, error) { - ipDescs := n.peers.IPs() - ips := make([]string, len(ipDescs)) - for i, ipDesc := range ipDescs { - ips[i] = ipDesc.String() - } - sort.Strings(ips) - return ips, nil -} diff --git a/api/admin/service.go b/api/admin/service.go index abfbe1b..ba8cad2 100644 --- a/api/admin/service.go +++ b/api/admin/service.go @@ -11,6 +11,7 @@ import ( "github.com/ava-labs/gecko/api" "github.com/ava-labs/gecko/chains" "github.com/ava-labs/gecko/ids" + "github.com/ava-labs/gecko/network" "github.com/ava-labs/gecko/snow/engine/common" "github.com/ava-labs/gecko/utils/logging" @@ -22,14 +23,14 @@ type Admin struct { nodeID ids.ShortID networkID uint32 log logging.Logger - networking Networking + networking network.Network performance Performance chainManager chains.Manager httpServer *api.Server } // NewService returns a new admin API service -func NewService(nodeID ids.ShortID, networkID uint32, log logging.Logger, chainManager chains.Manager, peers Peerable, httpServer *api.Server) *common.HTTPHandler { +func NewService(nodeID ids.ShortID, networkID uint32, log logging.Logger, chainManager chains.Manager, peers network.Network, httpServer *api.Server) *common.HTTPHandler { newServer := rpc.NewServer() codec := cjson.NewCodec() newServer.RegisterCodec(codec, "application/json") @@ -39,10 +40,8 @@ func NewService(nodeID ids.ShortID, networkID uint32, log logging.Logger, chainM networkID: networkID, log: log, chainManager: chainManager, - networking: Networking{ - peers: peers, - }, - httpServer: httpServer, + networking: peers, + httpServer: httpServer, }, "admin") return &common.HTTPHandler{Handler: newServer} } @@ -103,16 +102,14 @@ type PeersArgs struct{} // PeersReply are the results from calling Peers type PeersReply struct { - Peers []string `json:"peers"` + Peers []network.PeerID `json:"peers"` } // Peers returns the list of current validators func (service *Admin) Peers(r *http.Request, args *PeersArgs, reply *PeersReply) error { service.log.Debug("Admin: Peers called") - - peers, err := service.networking.Peers() - reply.Peers = peers - return err + reply.Peers = service.networking.Peers() + return nil } // StartCPUProfilerArgs are the arguments for calling StartCPUProfiler diff --git a/network/network.go b/network/network.go index 8a61364..471de3a 100644 --- a/network/network.go +++ b/network/network.go @@ -70,9 +70,9 @@ type Network interface { // The handler will initially be called with this local node's ID. RegisterHandler(h Handler) - // Returns the IPs of nodes this network is currently connected to - // externally. Thread safety must be managed internally to the network. - IPs() []utils.IPDesc + // Returns the description of the nodes this network is currently connected + // to externally. Thread safety must be managed internally to the network. + Peers() []PeerID // Close this network and all existing connections it has. Thread safety // must be managed internally to the network. Calling close multiple times @@ -511,17 +511,24 @@ func (n *network) RegisterHandler(h Handler) { } // IPs implements the Network interface -func (n *network) IPs() []utils.IPDesc { +func (n *network) Peers() []PeerID { n.stateLock.Lock() defer n.stateLock.Unlock() - ips := []utils.IPDesc(nil) + peers := []PeerID{} for _, peer := range n.peers { if peer.connected { - ips = append(ips, peer.ip) + peers = append(peers, PeerID{ + IP: peer.conn.RemoteAddr().String(), + PublicIP: peer.ip.String(), + ID: peer.id, + Version: peer.versionStr, + LastSent: time.Unix(atomic.LoadInt64(&peer.lastSent), 0), + LastReceived: time.Unix(atomic.LoadInt64(&peer.lastReceived), 0), + }) } } - return ips + return peers } // Close implements the Network interface diff --git a/network/peer.go b/network/peer.go index dfbde63..5bb7601 100644 --- a/network/peer.go +++ b/network/peer.go @@ -8,6 +8,7 @@ import ( "math" "net" "sync" + "sync/atomic" "time" "github.com/ava-labs/gecko/ids" @@ -43,6 +44,12 @@ type peer struct { // the connection object that is used to read/write messages from conn net.Conn + + // version that the peer reported during the handshake + versionStr string + + // unix time of the last message sent and received respectively + lastSent, lastReceived int64 } // assume the stateLock is held @@ -159,6 +166,7 @@ func (p *peer) WriteMessages() { } msg = msg[written:] } + atomic.StoreInt64(&p.lastSent, p.net.clock.Time().Unix()) } } @@ -188,6 +196,7 @@ func (p *peer) send(msg Msg) bool { // assumes the stateLock is not held func (p *peer) handle(msg Msg) { p.net.heartbeat() + atomic.StoreInt64(&p.lastReceived, p.net.clock.Time().Unix()) op := msg.Op() msgMetrics := p.net.message(op) @@ -415,6 +424,8 @@ func (p *peer) version(msg Msg) { return } + p.versionStr = peerVersion.String() + p.connected = true p.net.connected(p) } diff --git a/network/peer_id.go b/network/peer_id.go new file mode 100644 index 0000000..333a923 --- /dev/null +++ b/network/peer_id.go @@ -0,0 +1,20 @@ +// (c) 2019-2020, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package network + +import ( + "time" + + "github.com/ava-labs/gecko/ids" +) + +// PeerID ... +type PeerID struct { + IP string `json:"ip"` + PublicIP string `json:"publicIP"` + ID ids.ShortID `json:"id"` + Version string `json:"version"` + LastSent time.Time `json:"lastSent"` + LastReceived time.Time `json:"lastReceived"` +}