aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeffrey Wilcke <jeffrey@ethereum.org>2015-11-04 18:59:31 +0800
committerJeffrey Wilcke <jeffrey@ethereum.org>2015-11-04 18:59:31 +0800
commite165c2d23cf29fdec51c59f42f74735be2a8e1ff (patch)
tree375a6b3870c6b4f791c79128a4d7b29015b64851
parentdda3bf3ce7ef109541adb6f0020f8d2dd28e5e51 (diff)
parente46ab3bdcde7236c8fe54d6c83655e50bd19fe31 (diff)
downloaddexon-e165c2d23cf29fdec51c59f42f74735be2a8e1ff.tar
dexon-e165c2d23cf29fdec51c59f42f74735be2a8e1ff.tar.gz
dexon-e165c2d23cf29fdec51c59f42f74735be2a8e1ff.tar.bz2
dexon-e165c2d23cf29fdec51c59f42f74735be2a8e1ff.tar.lz
dexon-e165c2d23cf29fdec51c59f42f74735be2a8e1ff.tar.xz
dexon-e165c2d23cf29fdec51c59f42f74735be2a8e1ff.tar.zst
dexon-e165c2d23cf29fdec51c59f42f74735be2a8e1ff.zip
Merge pull request #1934 from karalabe/polish-protocol-infos
eth, p2p, rpc/api: polish protocol info gathering
-rw-r--r--eth/backend.go60
-rw-r--r--eth/handler.go42
-rw-r--r--eth/helper_test.go2
-rw-r--r--eth/peer.go47
-rw-r--r--eth/protocol.go3
-rw-r--r--eth/sync_test.go4
-rw-r--r--p2p/peer.go46
-rw-r--r--p2p/protocol.go15
-rw-r--r--p2p/server.go63
-rw-r--r--rpc/api/admin.go9
-rw-r--r--rpc/api/utils.go2
11 files changed, 208 insertions, 85 deletions
diff --git a/eth/backend.go b/eth/backend.go
index 0683705df..9eb211e31 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -467,62 +467,10 @@ func New(config *Config) (*Ethereum, error) {
return eth, nil
}
-type NodeInfo struct {
- Name string
- NodeUrl string
- NodeID string
- IP string
- DiscPort int // UDP listening port for discovery protocol
- TCPPort int // TCP listening port for RLPx
- Td string
- ListenAddr string
-}
-
-func (s *Ethereum) NodeInfo() *NodeInfo {
- node := s.net.Self()
-
- return &NodeInfo{
- Name: s.Name(),
- NodeUrl: node.String(),
- NodeID: node.ID.String(),
- IP: node.IP.String(),
- DiscPort: int(node.UDP),
- TCPPort: int(node.TCP),
- ListenAddr: s.net.ListenAddr,
- Td: s.BlockChain().GetTd(s.BlockChain().CurrentBlock().Hash()).String(),
- }
-}
-
-type PeerInfo struct {
- ID string
- Name string
- Caps string
- RemoteAddress string
- LocalAddress string
-}
-
-func newPeerInfo(peer *p2p.Peer) *PeerInfo {
- var caps []string
- for _, cap := range peer.Caps() {
- caps = append(caps, cap.String())
- }
- return &PeerInfo{
- ID: peer.ID().String(),
- Name: peer.Name(),
- Caps: strings.Join(caps, ", "),
- RemoteAddress: peer.RemoteAddr().String(),
- LocalAddress: peer.LocalAddr().String(),
- }
-}
-
-// PeersInfo returns an array of PeerInfo objects describing connected peers
-func (s *Ethereum) PeersInfo() (peersinfo []*PeerInfo) {
- for _, peer := range s.net.Peers() {
- if peer != nil {
- peersinfo = append(peersinfo, newPeerInfo(peer))
- }
- }
- return
+// Network retrieves the underlying P2P network server. This should eventually
+// be moved out into a protocol independent package, but for now use an accessor.
+func (s *Ethereum) Network() *p2p.Server {
+ return s.net
}
func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) {
diff --git a/eth/handler.go b/eth/handler.go
index 7dc7de80e..d8c5b4b64 100644
--- a/eth/handler.go
+++ b/eth/handler.go
@@ -34,6 +34,7 @@ import (
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/p2p"
+ "github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/pow"
"github.com/ethereum/go-ethereum/rlp"
)
@@ -55,6 +56,8 @@ type hashFetcherFn func(common.Hash) error
type blockFetcherFn func([]common.Hash) error
type ProtocolManager struct {
+ networkId int
+
fastSync bool
txpool txPool
blockchain *core.BlockChain
@@ -91,6 +94,7 @@ func NewProtocolManager(fastSync bool, networkId int, mux *event.TypeMux, txpool
}
// Create the protocol manager with the base fields
manager := &ProtocolManager{
+ networkId: networkId,
fastSync: fastSync,
eventMux: mux,
txpool: txpool,
@@ -111,14 +115,23 @@ func NewProtocolManager(fastSync bool, networkId int, mux *event.TypeMux, txpool
// Compatible; initialise the sub-protocol
version := version // Closure for the run
manager.SubProtocols = append(manager.SubProtocols, p2p.Protocol{
- Name: "eth",
+ Name: ProtocolName,
Version: version,
Length: ProtocolLengths[i],
Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
- peer := manager.newPeer(int(version), networkId, p, rw)
+ peer := manager.newPeer(int(version), p, rw)
manager.newPeerCh <- peer
return manager.handle(peer)
},
+ NodeInfo: func() interface{} {
+ return manager.NodeInfo()
+ },
+ PeerInfo: func(id discover.NodeID) interface{} {
+ if p := manager.peers.Peer(fmt.Sprintf("%x", id[:8])); p != nil {
+ return p.Info()
+ }
+ return nil
+ },
})
}
if len(manager.SubProtocols) == 0 {
@@ -188,8 +201,8 @@ func (pm *ProtocolManager) Stop() {
glog.V(logger.Info).Infoln("Ethereum protocol handler stopped")
}
-func (pm *ProtocolManager) newPeer(pv, nv int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
- return newPeer(pv, nv, p, newMeteredMsgWriter(rw))
+func (pm *ProtocolManager) newPeer(pv int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
+ return newPeer(pv, p, newMeteredMsgWriter(rw))
}
// 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 {
// Execute the Ethereum handshake
td, head, genesis := pm.blockchain.Status()
- if err := p.Handshake(td, head, genesis); err != nil {
+ if err := p.Handshake(pm.networkId, td, head, genesis); err != nil {
glog.V(logger.Debug).Infof("%v: handshake failed: %v", p, err)
return err
}
@@ -730,3 +743,22 @@ func (self *ProtocolManager) txBroadcastLoop() {
self.BroadcastTx(event.Tx.Hash(), event.Tx)
}
}
+
+// EthNodeInfo represents a short summary of the Ethereum sub-protocol metadata known
+// about the host peer.
+type EthNodeInfo struct {
+ Network int `json:"network"` // Ethereum network ID (0=Olympic, 1=Frontier, 2=Morden)
+ Difficulty *big.Int `json:"difficulty"` // Total difficulty of the host's blockchain
+ Genesis string `json:"genesis"` // SHA3 hash of the host's genesis block
+ Head string `json:"head"` // SHA3 hash of the host's best owned block
+}
+
+// NodeInfo retrieves some protocol metadata about the running host node.
+func (self *ProtocolManager) NodeInfo() *EthNodeInfo {
+ return &EthNodeInfo{
+ Network: self.networkId,
+ Difficulty: self.blockchain.GetTd(self.blockchain.CurrentBlock().Hash()),
+ Genesis: fmt.Sprintf("%x", self.blockchain.Genesis().Hash()),
+ Head: fmt.Sprintf("%x", self.blockchain.CurrentBlock().Hash()),
+ }
+}
diff --git a/eth/helper_test.go b/eth/helper_test.go
index 16907be8b..65fccf7b4 100644
--- a/eth/helper_test.go
+++ b/eth/helper_test.go
@@ -117,7 +117,7 @@ func newTestPeer(name string, version int, pm *ProtocolManager, shake bool) (*te
var id discover.NodeID
rand.Read(id[:])
- peer := pm.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net)
+ peer := pm.newPeer(version, p2p.NewPeer(id, name, nil), net)
// Start the peer on a new thread
errc := make(chan error, 1)
diff --git a/eth/peer.go b/eth/peer.go
index 695e910f6..15ba22ff5 100644
--- a/eth/peer.go
+++ b/eth/peer.go
@@ -44,38 +44,51 @@ const (
handshakeTimeout = 5 * time.Second
)
+// PeerInfo represents a short summary of the Ethereum sub-protocol metadata known
+// about a connected peer.
+type PeerInfo struct {
+ Version int `json:"version"` // Ethereum protocol version negotiated
+ Difficulty *big.Int `json:"difficulty"` // Total difficulty of the peer's blockchain
+ Head string `json:"head"` // SHA3 hash of the peer's best owned block
+}
+
type peer struct {
- *p2p.Peer
+ id string
+ *p2p.Peer
rw p2p.MsgReadWriter
version int // Protocol version negotiated
- network int // Network ID being on
-
- id string
-
- head common.Hash
- td *big.Int
- lock sync.RWMutex
+ head common.Hash
+ td *big.Int
+ lock sync.RWMutex
knownTxs *set.Set // Set of transaction hashes known to be known by this peer
knownBlocks *set.Set // Set of block hashes known to be known by this peer
}
-func newPeer(version, network int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
+func newPeer(version int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
id := p.ID()
return &peer{
Peer: p,
rw: rw,
version: version,
- network: network,
id: fmt.Sprintf("%x", id[:8]),
knownTxs: set.New(),
knownBlocks: set.New(),
}
}
+// Info gathers and returns a collection of metadata known about a peer.
+func (p *peer) Info() *PeerInfo {
+ return &PeerInfo{
+ Version: p.version,
+ Difficulty: p.Td(),
+ Head: fmt.Sprintf("%x", p.Head()),
+ }
+}
+
// Head retrieves a copy of the current head (most recent) hash of the peer.
func (p *peer) Head() (hash common.Hash) {
p.lock.RLock()
@@ -268,20 +281,22 @@ func (p *peer) RequestReceipts(hashes []common.Hash) error {
// Handshake executes the eth protocol handshake, negotiating version number,
// network IDs, difficulties, head and genesis blocks.
-func (p *peer) Handshake(td *big.Int, head common.Hash, genesis common.Hash) error {
+func (p *peer) Handshake(network int, td *big.Int, head common.Hash, genesis common.Hash) error {
+ // Send out own handshake in a new thread
errc := make(chan error, 2)
var status statusData // safe to read after two values have been received from errc
+
go func() {
errc <- p2p.Send(p.rw, StatusMsg, &statusData{
ProtocolVersion: uint32(p.version),
- NetworkId: uint32(p.network),
+ NetworkId: uint32(network),
TD: td,
CurrentBlock: head,
GenesisBlock: genesis,
})
}()
go func() {
- errc <- p.readStatus(&status, genesis)
+ errc <- p.readStatus(network, &status, genesis)
}()
timeout := time.NewTimer(handshakeTimeout)
defer timeout.Stop()
@@ -299,7 +314,7 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, genesis common.Hash) err
return nil
}
-func (p *peer) readStatus(status *statusData, genesis common.Hash) (err error) {
+func (p *peer) readStatus(network int, status *statusData, genesis common.Hash) (err error) {
msg, err := p.rw.ReadMsg()
if err != nil {
return err
@@ -317,8 +332,8 @@ func (p *peer) readStatus(status *statusData, genesis common.Hash) (err error) {
if status.GenesisBlock != genesis {
return errResp(ErrGenesisBlockMismatch, "%x (!= %x)", status.GenesisBlock, genesis)
}
- if int(status.NetworkId) != p.network {
- return errResp(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, p.network)
+ if int(status.NetworkId) != network {
+ return errResp(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, network)
}
if int(status.ProtocolVersion) != p.version {
return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, p.version)
diff --git a/eth/protocol.go b/eth/protocol.go
index 410347ed3..808ac0601 100644
--- a/eth/protocol.go
+++ b/eth/protocol.go
@@ -33,6 +33,9 @@ const (
eth63 = 63
)
+// Official short name of the protocol used during capability negotiation.
+var ProtocolName = "eth"
+
// Supported versions of the eth protocol (first is primary).
var ProtocolVersions = []uint{eth63, eth62, eth61}
diff --git a/eth/sync_test.go b/eth/sync_test.go
index f3a6718ab..afd90c9b6 100644
--- a/eth/sync_test.go
+++ b/eth/sync_test.go
@@ -40,8 +40,8 @@ func TestFastSyncDisabling(t *testing.T) {
// Sync up the two peers
io1, io2 := p2p.MsgPipe()
- go pmFull.handle(pmFull.newPeer(63, NetworkId, p2p.NewPeer(discover.NodeID{}, "empty", nil), io2))
- go pmEmpty.handle(pmEmpty.newPeer(63, NetworkId, p2p.NewPeer(discover.NodeID{}, "full", nil), io1))
+ go pmFull.handle(pmFull.newPeer(63, p2p.NewPeer(discover.NodeID{}, "empty", nil), io2))
+ go pmEmpty.handle(pmEmpty.newPeer(63, p2p.NewPeer(discover.NodeID{}, "full", nil), io1))
time.Sleep(250 * time.Millisecond)
pmEmpty.synchronise(pmEmpty.peers.BestPeer())
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
+}
diff --git a/rpc/api/admin.go b/rpc/api/admin.go
index eb08fbc5d..b359d52a1 100644
--- a/rpc/api/admin.go
+++ b/rpc/api/admin.go
@@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/logger/glog"
+ "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/comms"
@@ -80,15 +81,17 @@ type adminhandler func(*adminApi, *shared.Request) (interface{}, error)
// admin api provider
type adminApi struct {
xeth *xeth.XEth
+ network *p2p.Server
ethereum *eth.Ethereum
codec codec.Codec
coder codec.ApiCoder
}
// create a new admin api instance
-func NewAdminApi(xeth *xeth.XEth, ethereum *eth.Ethereum, codec codec.Codec) *adminApi {
+func NewAdminApi(xeth *xeth.XEth, network *p2p.Server, ethereum *eth.Ethereum, codec codec.Codec) *adminApi {
return &adminApi{
xeth: xeth,
+ network: network,
ethereum: ethereum,
codec: codec,
coder: codec.New(nil),
@@ -137,11 +140,11 @@ func (self *adminApi) AddPeer(req *shared.Request) (interface{}, error) {
}
func (self *adminApi) Peers(req *shared.Request) (interface{}, error) {
- return self.ethereum.PeersInfo(), nil
+ return self.network.PeersInfo(), nil
}
func (self *adminApi) NodeInfo(req *shared.Request) (interface{}, error) {
- return self.ethereum.NodeInfo(), nil
+ return self.network.NodeInfo(), nil
}
func (self *adminApi) DataDir(req *shared.Request) (interface{}, error) {
diff --git a/rpc/api/utils.go b/rpc/api/utils.go
index 5a3ade46b..6dde4022b 100644
--- a/rpc/api/utils.go
+++ b/rpc/api/utils.go
@@ -165,7 +165,7 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth.
for i, name := range names {
switch strings.ToLower(strings.TrimSpace(name)) {
case shared.AdminApiName:
- apis[i] = NewAdminApi(xeth, eth, codec)
+ apis[i] = NewAdminApi(xeth, eth.Network(), eth, codec)
case shared.DebugApiName:
apis[i] = NewDebugApi(xeth, eth, codec)
case shared.DbApiName: