Skip to content

Commit e165c2d

Browse files
committed
Merge pull request #1934 from karalabe/polish-protocol-infos
eth, p2p, rpc/api: polish protocol info gathering
2 parents dda3bf3 + e46ab3b commit e165c2d

File tree

11 files changed

+208
-85
lines changed

11 files changed

+208
-85
lines changed

eth/backend.go

Lines changed: 4 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -467,62 +467,10 @@ func New(config *Config) (*Ethereum, error) {
467467
return eth, nil
468468
}
469469

470-
type NodeInfo struct {
471-
Name string
472-
NodeUrl string
473-
NodeID string
474-
IP string
475-
DiscPort int // UDP listening port for discovery protocol
476-
TCPPort int // TCP listening port for RLPx
477-
Td string
478-
ListenAddr string
479-
}
480-
481-
func (s *Ethereum) NodeInfo() *NodeInfo {
482-
node := s.net.Self()
483-
484-
return &NodeInfo{
485-
Name: s.Name(),
486-
NodeUrl: node.String(),
487-
NodeID: node.ID.String(),
488-
IP: node.IP.String(),
489-
DiscPort: int(node.UDP),
490-
TCPPort: int(node.TCP),
491-
ListenAddr: s.net.ListenAddr,
492-
Td: s.BlockChain().GetTd(s.BlockChain().CurrentBlock().Hash()).String(),
493-
}
494-
}
495-
496-
type PeerInfo struct {
497-
ID string
498-
Name string
499-
Caps string
500-
RemoteAddress string
501-
LocalAddress string
502-
}
503-
504-
func newPeerInfo(peer *p2p.Peer) *PeerInfo {
505-
var caps []string
506-
for _, cap := range peer.Caps() {
507-
caps = append(caps, cap.String())
508-
}
509-
return &PeerInfo{
510-
ID: peer.ID().String(),
511-
Name: peer.Name(),
512-
Caps: strings.Join(caps, ", "),
513-
RemoteAddress: peer.RemoteAddr().String(),
514-
LocalAddress: peer.LocalAddr().String(),
515-
}
516-
}
517-
518-
// PeersInfo returns an array of PeerInfo objects describing connected peers
519-
func (s *Ethereum) PeersInfo() (peersinfo []*PeerInfo) {
520-
for _, peer := range s.net.Peers() {
521-
if peer != nil {
522-
peersinfo = append(peersinfo, newPeerInfo(peer))
523-
}
524-
}
525-
return
470+
// Network retrieves the underlying P2P network server. This should eventually
471+
// be moved out into a protocol independent package, but for now use an accessor.
472+
func (s *Ethereum) Network() *p2p.Server {
473+
return s.net
526474
}
527475

528476
func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) {

eth/handler.go

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
"github.com/ethereum/go-ethereum/logger"
3535
"github.com/ethereum/go-ethereum/logger/glog"
3636
"github.com/ethereum/go-ethereum/p2p"
37+
"github.com/ethereum/go-ethereum/p2p/discover"
3738
"github.com/ethereum/go-ethereum/pow"
3839
"github.com/ethereum/go-ethereum/rlp"
3940
)
@@ -55,6 +56,8 @@ type hashFetcherFn func(common.Hash) error
5556
type blockFetcherFn func([]common.Hash) error
5657

5758
type ProtocolManager struct {
59+
networkId int
60+
5861
fastSync bool
5962
txpool txPool
6063
blockchain *core.BlockChain
@@ -91,6 +94,7 @@ func NewProtocolManager(fastSync bool, networkId int, mux *event.TypeMux, txpool
9194
}
9295
// Create the protocol manager with the base fields
9396
manager := &ProtocolManager{
97+
networkId: networkId,
9498
fastSync: fastSync,
9599
eventMux: mux,
96100
txpool: txpool,
@@ -111,14 +115,23 @@ func NewProtocolManager(fastSync bool, networkId int, mux *event.TypeMux, txpool
111115
// Compatible; initialise the sub-protocol
112116
version := version // Closure for the run
113117
manager.SubProtocols = append(manager.SubProtocols, p2p.Protocol{
114-
Name: "eth",
118+
Name: ProtocolName,
115119
Version: version,
116120
Length: ProtocolLengths[i],
117121
Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
118-
peer := manager.newPeer(int(version), networkId, p, rw)
122+
peer := manager.newPeer(int(version), p, rw)
119123
manager.newPeerCh <- peer
120124
return manager.handle(peer)
121125
},
126+
NodeInfo: func() interface{} {
127+
return manager.NodeInfo()
128+
},
129+
PeerInfo: func(id discover.NodeID) interface{} {
130+
if p := manager.peers.Peer(fmt.Sprintf("%x", id[:8])); p != nil {
131+
return p.Info()
132+
}
133+
return nil
134+
},
122135
})
123136
}
124137
if len(manager.SubProtocols) == 0 {
@@ -188,8 +201,8 @@ func (pm *ProtocolManager) Stop() {
188201
glog.V(logger.Info).Infoln("Ethereum protocol handler stopped")
189202
}
190203

191-
func (pm *ProtocolManager) newPeer(pv, nv int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
192-
return newPeer(pv, nv, p, newMeteredMsgWriter(rw))
204+
func (pm *ProtocolManager) newPeer(pv int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
205+
return newPeer(pv, p, newMeteredMsgWriter(rw))
193206
}
194207

195208
// handle is the callback invoked to manage the life cycle of an eth peer. When
@@ -199,7 +212,7 @@ func (pm *ProtocolManager) handle(p *peer) error {
199212

200213
// Execute the Ethereum handshake
201214
td, head, genesis := pm.blockchain.Status()
202-
if err := p.Handshake(td, head, genesis); err != nil {
215+
if err := p.Handshake(pm.networkId, td, head, genesis); err != nil {
203216
glog.V(logger.Debug).Infof("%v: handshake failed: %v", p, err)
204217
return err
205218
}
@@ -730,3 +743,22 @@ func (self *ProtocolManager) txBroadcastLoop() {
730743
self.BroadcastTx(event.Tx.Hash(), event.Tx)
731744
}
732745
}
746+
747+
// EthNodeInfo represents a short summary of the Ethereum sub-protocol metadata known
748+
// about the host peer.
749+
type EthNodeInfo struct {
750+
Network int `json:"network"` // Ethereum network ID (0=Olympic, 1=Frontier, 2=Morden)
751+
Difficulty *big.Int `json:"difficulty"` // Total difficulty of the host's blockchain
752+
Genesis string `json:"genesis"` // SHA3 hash of the host's genesis block
753+
Head string `json:"head"` // SHA3 hash of the host's best owned block
754+
}
755+
756+
// NodeInfo retrieves some protocol metadata about the running host node.
757+
func (self *ProtocolManager) NodeInfo() *EthNodeInfo {
758+
return &EthNodeInfo{
759+
Network: self.networkId,
760+
Difficulty: self.blockchain.GetTd(self.blockchain.CurrentBlock().Hash()),
761+
Genesis: fmt.Sprintf("%x", self.blockchain.Genesis().Hash()),
762+
Head: fmt.Sprintf("%x", self.blockchain.CurrentBlock().Hash()),
763+
}
764+
}

eth/helper_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ func newTestPeer(name string, version int, pm *ProtocolManager, shake bool) (*te
117117
var id discover.NodeID
118118
rand.Read(id[:])
119119

120-
peer := pm.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net)
120+
peer := pm.newPeer(version, p2p.NewPeer(id, name, nil), net)
121121

122122
// Start the peer on a new thread
123123
errc := make(chan error, 1)

eth/peer.go

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -44,38 +44,51 @@ const (
4444
handshakeTimeout = 5 * time.Second
4545
)
4646

47+
// PeerInfo represents a short summary of the Ethereum sub-protocol metadata known
48+
// about a connected peer.
49+
type PeerInfo struct {
50+
Version int `json:"version"` // Ethereum protocol version negotiated
51+
Difficulty *big.Int `json:"difficulty"` // Total difficulty of the peer's blockchain
52+
Head string `json:"head"` // SHA3 hash of the peer's best owned block
53+
}
54+
4755
type peer struct {
48-
*p2p.Peer
56+
id string
4957

58+
*p2p.Peer
5059
rw p2p.MsgReadWriter
5160

5261
version int // Protocol version negotiated
53-
network int // Network ID being on
54-
55-
id string
56-
57-
head common.Hash
58-
td *big.Int
59-
lock sync.RWMutex
62+
head common.Hash
63+
td *big.Int
64+
lock sync.RWMutex
6065

6166
knownTxs *set.Set // Set of transaction hashes known to be known by this peer
6267
knownBlocks *set.Set // Set of block hashes known to be known by this peer
6368
}
6469

65-
func newPeer(version, network int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
70+
func newPeer(version int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
6671
id := p.ID()
6772

6873
return &peer{
6974
Peer: p,
7075
rw: rw,
7176
version: version,
72-
network: network,
7377
id: fmt.Sprintf("%x", id[:8]),
7478
knownTxs: set.New(),
7579
knownBlocks: set.New(),
7680
}
7781
}
7882

83+
// Info gathers and returns a collection of metadata known about a peer.
84+
func (p *peer) Info() *PeerInfo {
85+
return &PeerInfo{
86+
Version: p.version,
87+
Difficulty: p.Td(),
88+
Head: fmt.Sprintf("%x", p.Head()),
89+
}
90+
}
91+
7992
// Head retrieves a copy of the current head (most recent) hash of the peer.
8093
func (p *peer) Head() (hash common.Hash) {
8194
p.lock.RLock()
@@ -268,20 +281,22 @@ func (p *peer) RequestReceipts(hashes []common.Hash) error {
268281

269282
// Handshake executes the eth protocol handshake, negotiating version number,
270283
// network IDs, difficulties, head and genesis blocks.
271-
func (p *peer) Handshake(td *big.Int, head common.Hash, genesis common.Hash) error {
284+
func (p *peer) Handshake(network int, td *big.Int, head common.Hash, genesis common.Hash) error {
285+
// Send out own handshake in a new thread
272286
errc := make(chan error, 2)
273287
var status statusData // safe to read after two values have been received from errc
288+
274289
go func() {
275290
errc <- p2p.Send(p.rw, StatusMsg, &statusData{
276291
ProtocolVersion: uint32(p.version),
277-
NetworkId: uint32(p.network),
292+
NetworkId: uint32(network),
278293
TD: td,
279294
CurrentBlock: head,
280295
GenesisBlock: genesis,
281296
})
282297
}()
283298
go func() {
284-
errc <- p.readStatus(&status, genesis)
299+
errc <- p.readStatus(network, &status, genesis)
285300
}()
286301
timeout := time.NewTimer(handshakeTimeout)
287302
defer timeout.Stop()
@@ -299,7 +314,7 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, genesis common.Hash) err
299314
return nil
300315
}
301316

302-
func (p *peer) readStatus(status *statusData, genesis common.Hash) (err error) {
317+
func (p *peer) readStatus(network int, status *statusData, genesis common.Hash) (err error) {
303318
msg, err := p.rw.ReadMsg()
304319
if err != nil {
305320
return err
@@ -317,8 +332,8 @@ func (p *peer) readStatus(status *statusData, genesis common.Hash) (err error) {
317332
if status.GenesisBlock != genesis {
318333
return errResp(ErrGenesisBlockMismatch, "%x (!= %x)", status.GenesisBlock, genesis)
319334
}
320-
if int(status.NetworkId) != p.network {
321-
return errResp(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, p.network)
335+
if int(status.NetworkId) != network {
336+
return errResp(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, network)
322337
}
323338
if int(status.ProtocolVersion) != p.version {
324339
return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, p.version)

eth/protocol.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ const (
3333
eth63 = 63
3434
)
3535

36+
// Official short name of the protocol used during capability negotiation.
37+
var ProtocolName = "eth"
38+
3639
// Supported versions of the eth protocol (first is primary).
3740
var ProtocolVersions = []uint{eth63, eth62, eth61}
3841

eth/sync_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ func TestFastSyncDisabling(t *testing.T) {
4040
// Sync up the two peers
4141
io1, io2 := p2p.MsgPipe()
4242

43-
go pmFull.handle(pmFull.newPeer(63, NetworkId, p2p.NewPeer(discover.NodeID{}, "empty", nil), io2))
44-
go pmEmpty.handle(pmEmpty.newPeer(63, NetworkId, p2p.NewPeer(discover.NodeID{}, "full", nil), io1))
43+
go pmFull.handle(pmFull.newPeer(63, p2p.NewPeer(discover.NodeID{}, "empty", nil), io2))
44+
go pmEmpty.handle(pmEmpty.newPeer(63, p2p.NewPeer(discover.NodeID{}, "full", nil), io1))
4545

4646
time.Sleep(250 * time.Millisecond)
4747
pmEmpty.synchronise(pmEmpty.peers.BestPeer())

p2p/peer.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,3 +359,49 @@ func (rw *protoRW) ReadMsg() (Msg, error) {
359359
return Msg{}, io.EOF
360360
}
361361
}
362+
363+
// PeerInfo represents a short summary of the information known about a connected
364+
// peer. Sub-protocol independent fields are contained and initialized here, with
365+
// protocol specifics delegated to all connected sub-protocols.
366+
type PeerInfo struct {
367+
ID string `json:"id"` // Unique node identifier (also the encryption key)
368+
Name string `json:"name"` // Name of the node, including client type, version, OS, custom data
369+
Caps []string `json:"caps"` // Sum-protocols advertised by this particular peer
370+
Network struct {
371+
LocalAddress string `json:"localAddress"` // Local endpoint of the TCP data connection
372+
RemoteAddress string `json:"remoteAddress"` // Remote endpoint of the TCP data connection
373+
} `json:"network"`
374+
Protocols map[string]interface{} `json:"protocols"` // Sub-protocol specific metadata fields
375+
}
376+
377+
// Info gathers and returns a collection of metadata known about a peer.
378+
func (p *Peer) Info() *PeerInfo {
379+
// Gather the protocol capabilities
380+
var caps []string
381+
for _, cap := range p.Caps() {
382+
caps = append(caps, cap.String())
383+
}
384+
// Assemble the generic peer metadata
385+
info := &PeerInfo{
386+
ID: p.ID().String(),
387+
Name: p.Name(),
388+
Caps: caps,
389+
Protocols: make(map[string]interface{}),
390+
}
391+
info.Network.LocalAddress = p.LocalAddr().String()
392+
info.Network.RemoteAddress = p.RemoteAddr().String()
393+
394+
// Gather all the running protocol infos
395+
for _, proto := range p.running {
396+
protoInfo := interface{}("unknown")
397+
if query := proto.Protocol.PeerInfo; query != nil {
398+
if metadata := query(p.ID()); metadata != nil {
399+
protoInfo = metadata
400+
} else {
401+
protoInfo = "handshake"
402+
}
403+
}
404+
info.Protocols[proto.Name] = protoInfo
405+
}
406+
return info
407+
}

p2p/protocol.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616

1717
package p2p
1818

19-
import "fmt"
19+
import (
20+
"fmt"
21+
22+
"github.com/ethereum/go-ethereum/p2p/discover"
23+
)
2024

2125
// Protocol represents a P2P subprotocol implementation.
2226
type Protocol struct {
@@ -39,6 +43,15 @@ type Protocol struct {
3943
// any protocol-level error (such as an I/O error) that is
4044
// encountered.
4145
Run func(peer *Peer, rw MsgReadWriter) error
46+
47+
// NodeInfo is an optional helper method to retrieve protocol specific metadata
48+
// about the host node.
49+
NodeInfo func() interface{}
50+
51+
// PeerInfo is an optional helper method to retrieve protocol specific metadata
52+
// about a certain peer in the network. If an info retrieval function is set,
53+
// but returns nil, it is assumed that the protocol handshake is still running.
54+
PeerInfo func(id discover.NodeID) interface{}
4255
}
4356

4457
func (p Protocol) cap() Cap {

0 commit comments

Comments
 (0)