add support for the ping rpc command.
And the pingtime and pingwait fields of getpeerinfo.
This commit is contained in:
parent
1487a352da
commit
9cb5190ac2
4 changed files with 100 additions and 37 deletions
40
peer.go
40
peer.go
|
@ -162,6 +162,10 @@ type peer struct {
|
||||||
blockProcessed chan bool
|
blockProcessed chan bool
|
||||||
quit chan bool
|
quit chan bool
|
||||||
userAgent string
|
userAgent string
|
||||||
|
pingStatsMtx sync.Mutex // protects lastPing*
|
||||||
|
lastPingNonce uint64 // Set to nonce if we have a pending ping.
|
||||||
|
lastPingTime time.Time // Time we sent last ping.
|
||||||
|
lastPingMicros int64 // Time for last ping to return.
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns the peer's address and directionality as a human-readable
|
// String returns the peer's address and directionality as a human-readable
|
||||||
|
@ -921,6 +925,30 @@ func (p *peer) handlePingMsg(msg *btcwire.MsgPing) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handlePongMsg is invoked when a peer recieved a pong bitcoin message.
|
||||||
|
// recent clients (protocol version > BIP0031Version), and if we had send a ping
|
||||||
|
// previosuly we update our ping time statistics. If the client is too old or
|
||||||
|
// we had not send a ping we ignore it.
|
||||||
|
func (p *peer) handlePongMsg(msg *btcwire.MsgPong) {
|
||||||
|
p.pingStatsMtx.Lock()
|
||||||
|
defer p.pingStatsMtx.Unlock()
|
||||||
|
|
||||||
|
// Arguably we could use a buffered channel here sending data
|
||||||
|
// in a fifo manner whenever we send a ping, or a list keeping track of
|
||||||
|
// the times of each ping. For now we just make a best effort and
|
||||||
|
// only record stats if it was for the last ping sent. Any preceding
|
||||||
|
// and overlapping pings will be ignored. It is unlikely to occur
|
||||||
|
// without large usage of the ping rpc call since we ping
|
||||||
|
// infrequently enough that if they overlap we would have timed out
|
||||||
|
// the peer.
|
||||||
|
if p.protocolVersion > btcwire.BIP0031Version &&
|
||||||
|
p.lastPingNonce != 0 && msg.Nonce == p.lastPingNonce {
|
||||||
|
p.lastPingMicros = time.Now().Sub(p.lastPingTime).Nanoseconds()
|
||||||
|
p.lastPingMicros /= 1000 // convert to usec.
|
||||||
|
p.lastPingNonce = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// readMessage reads the next bitcoin message from the peer with logging.
|
// readMessage reads the next bitcoin message from the peer with logging.
|
||||||
func (p *peer) readMessage() (msg btcwire.Message, buf []byte, err error) {
|
func (p *peer) readMessage() (msg btcwire.Message, buf []byte, err error) {
|
||||||
msg, buf, err = btcwire.ReadMessage(p.conn, p.protocolVersion, p.btcnet)
|
msg, buf, err = btcwire.ReadMessage(p.conn, p.protocolVersion, p.btcnet)
|
||||||
|
@ -1092,8 +1120,7 @@ out:
|
||||||
markConnected = true
|
markConnected = true
|
||||||
|
|
||||||
case *btcwire.MsgPong:
|
case *btcwire.MsgPong:
|
||||||
// Don't do anything, but could try to work out network
|
p.handlePongMsg(msg)
|
||||||
// timing or similar.
|
|
||||||
|
|
||||||
case *btcwire.MsgAlert:
|
case *btcwire.MsgAlert:
|
||||||
p.server.BroadcastMessage(msg, p)
|
p.server.BroadcastMessage(msg, p)
|
||||||
|
@ -1324,13 +1351,20 @@ out:
|
||||||
// specially.
|
// specially.
|
||||||
peerLog.Tracef("%s: recieved from queuehandler", p)
|
peerLog.Tracef("%s: recieved from queuehandler", p)
|
||||||
reset := true
|
reset := true
|
||||||
switch msg.msg.(type) {
|
switch m := msg.msg.(type) {
|
||||||
case *btcwire.MsgVersion:
|
case *btcwire.MsgVersion:
|
||||||
// should get an ack
|
// should get an ack
|
||||||
case *btcwire.MsgGetAddr:
|
case *btcwire.MsgGetAddr:
|
||||||
// should get addresses
|
// should get addresses
|
||||||
case *btcwire.MsgPing:
|
case *btcwire.MsgPing:
|
||||||
// expects pong
|
// expects pong
|
||||||
|
// Also set up statistics.
|
||||||
|
p.pingStatsMtx.Lock()
|
||||||
|
if p.protocolVersion > btcwire.BIP0031Version {
|
||||||
|
p.lastPingNonce = m.Nonce
|
||||||
|
p.lastPingTime = time.Now()
|
||||||
|
}
|
||||||
|
p.pingStatsMtx.Unlock()
|
||||||
case *btcwire.MsgMemPool:
|
case *btcwire.MsgMemPool:
|
||||||
// Should return an inv.
|
// Should return an inv.
|
||||||
case *btcwire.MsgGetData:
|
case *btcwire.MsgGetData:
|
||||||
|
|
79
rpcserver.go
79
rpcserver.go
|
@ -49,28 +49,29 @@ type commandHandler func(*rpcServer, btcjson.Cmd) (interface{}, error)
|
||||||
// a dependancy loop.
|
// a dependancy loop.
|
||||||
var rpcHandlers map[string]commandHandler
|
var rpcHandlers map[string]commandHandler
|
||||||
var rpcHandlersBeforeInit = map[string]commandHandler{
|
var rpcHandlersBeforeInit = map[string]commandHandler{
|
||||||
"addnode": handleAddNode,
|
"addnode": handleAddNode,
|
||||||
"createrawtransaction": handleCreateRawTransaction,
|
"createrawtransaction": handleCreateRawTransaction,
|
||||||
"debuglevel": handleDebugLevel,
|
"debuglevel": handleDebugLevel,
|
||||||
"decoderawtransaction": handleDecodeRawTransaction,
|
"decoderawtransaction": handleDecodeRawTransaction,
|
||||||
"decodescript": handleDecodeScript,
|
"decodescript": handleDecodeScript,
|
||||||
"getbestblockhash": handleGetBestBlockHash,
|
"getbestblockhash": handleGetBestBlockHash,
|
||||||
"getblock": handleGetBlock,
|
"getblock": handleGetBlock,
|
||||||
"getblockcount": handleGetBlockCount,
|
"getblockcount": handleGetBlockCount,
|
||||||
"getblockhash": handleGetBlockHash,
|
"getblockhash": handleGetBlockHash,
|
||||||
"getconnectioncount": handleGetConnectionCount,
|
"getconnectioncount": handleGetConnectionCount,
|
||||||
"getdifficulty": handleGetDifficulty,
|
"getdifficulty": handleGetDifficulty,
|
||||||
"getgenerate": handleGetGenerate,
|
"getgenerate": handleGetGenerate,
|
||||||
"gethashespersec": handleGetHashesPerSec,
|
"gethashespersec": handleGetHashesPerSec,
|
||||||
"getpeerinfo": handleGetPeerInfo,
|
"getpeerinfo": handleGetPeerInfo,
|
||||||
"getrawmempool": handleGetRawMempool,
|
"getrawmempool": handleGetRawMempool,
|
||||||
"getrawtransaction": handleGetRawTransaction,
|
"getrawtransaction": handleGetRawTransaction,
|
||||||
"help": handleHelp,
|
"help": handleHelp,
|
||||||
"sendrawtransaction": handleSendRawTransaction,
|
"ping": handlePing,
|
||||||
"setgenerate": handleSetGenerate,
|
"sendrawtransaction": handleSendRawTransaction,
|
||||||
"stop": handleStop,
|
"setgenerate": handleSetGenerate,
|
||||||
"submitblock": handleSubmitBlock,
|
"stop": handleStop,
|
||||||
"verifychain": handleVerifyChain,
|
"submitblock": handleSubmitBlock,
|
||||||
|
"verifychain": handleVerifyChain,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -126,15 +127,14 @@ var rpcAskWallet = map[string]bool{
|
||||||
|
|
||||||
// Commands that are temporarily unimplemented.
|
// Commands that are temporarily unimplemented.
|
||||||
var rpcUnimplemented = map[string]bool{
|
var rpcUnimplemented = map[string]bool{
|
||||||
"getaddednodeinfo": true,
|
"getaddednodeinfo": true,
|
||||||
"getblocktemplate": true,
|
"getblocktemplate": true,
|
||||||
"getinfo": true,
|
"getinfo": true,
|
||||||
"getmininginfo": true,
|
"getmininginfo": true,
|
||||||
"getnettotals": true,
|
"getnettotals": true,
|
||||||
"getnetworkhashps": true,
|
"getnetworkhashps": true,
|
||||||
"getnewaddress": true,
|
"getnewaddress": true,
|
||||||
"getwork": true,
|
"getwork": true,
|
||||||
"ping": true,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// rpcServer holds the items the rpc server may need to access (config,
|
// rpcServer holds the items the rpc server may need to access (config,
|
||||||
|
@ -1037,7 +1037,7 @@ NOTE: btcd does not mine so this will always return false. The call is provided
|
||||||
for compatibility only.`,
|
for compatibility only.`,
|
||||||
"getpeerinfo": `
|
"getpeerinfo": `
|
||||||
NOTE: btcd does not currently implement all fields. the "bytessent",
|
NOTE: btcd does not currently implement all fields. the "bytessent",
|
||||||
"bytesrecv", "pingtime", "pingwait" and "syncnode" fields are not yet
|
"bytesrecv" and "syncnode" fields are not yet
|
||||||
implemented.`,
|
implemented.`,
|
||||||
"sendrawtransaction": `
|
"sendrawtransaction": `
|
||||||
NOTE: btcd does not currently support the "allowhighfees" parameter.`,
|
NOTE: btcd does not currently support the "allowhighfees" parameter.`,
|
||||||
|
@ -1085,13 +1085,26 @@ func handleHelp(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||||
// search the main list of hanlders since we do not wish to provide help
|
// search the main list of hanlders since we do not wish to provide help
|
||||||
// for commands that are unimplemented or relate to wallet
|
// for commands that are unimplemented or relate to wallet
|
||||||
// functionality.
|
// functionality.
|
||||||
if _, ok := rpcHandlers[help.Command]; !ok {
|
if _, ok := rpcHandlers[help.Command]; !ok {
|
||||||
return "", fmt.Errorf("help: unknown command: %s", help.Command)
|
return "", fmt.Errorf("help: unknown command: %s", help.Command)
|
||||||
}
|
}
|
||||||
|
|
||||||
return getHelpText(help.Command)
|
return getHelpText(help.Command)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handlePing implements the ping command.
|
||||||
|
func handlePing(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||||
|
// Ask server to ping \o_
|
||||||
|
nonce, err := btcwire.RandomUint64()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Not sending ping - can not generate "+
|
||||||
|
"nonce: %v", err)
|
||||||
|
}
|
||||||
|
s.server.BroadcastMessage(btcwire.NewMsgPing(nonce))
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// handleSendRawTransaction implements the sendrawtransaction command.
|
// handleSendRawTransaction implements the sendrawtransaction command.
|
||||||
func handleSendRawTransaction(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
func handleSendRawTransaction(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||||
c := cmd.(*btcjson.SendRawTransactionCmd)
|
c := cmd.(*btcjson.SendRawTransactionCmd)
|
||||||
|
|
10
server.go
10
server.go
|
@ -266,6 +266,8 @@ type PeerInfo struct {
|
||||||
LastRecv int64 `json:"lastrecv"`
|
LastRecv int64 `json:"lastrecv"`
|
||||||
BytesSent int `json:"bytessent"`
|
BytesSent int `json:"bytessent"`
|
||||||
BytesRecv int `json:"bytesrecv"`
|
BytesRecv int `json:"bytesrecv"`
|
||||||
|
PingTime int64 `json:"pingtime"`
|
||||||
|
PingWait int64 `json:"pingwait,omitempty"`
|
||||||
ConnTime int64 `json:"conntime"`
|
ConnTime int64 `json:"conntime"`
|
||||||
Version uint32 `json:"version"`
|
Version uint32 `json:"version"`
|
||||||
SubVer string `json:"subver"`
|
SubVer string `json:"subver"`
|
||||||
|
@ -333,6 +335,14 @@ func (s *server) handleQuery(querymsg interface{}, state *peerState) {
|
||||||
BanScore: 0,
|
BanScore: 0,
|
||||||
SyncNode: false, // TODO(oga) for now. bm knows this.
|
SyncNode: false, // TODO(oga) for now. bm knows this.
|
||||||
}
|
}
|
||||||
|
p.pingStatsMtx.Lock()
|
||||||
|
info.PingTime = p.lastPingMicros
|
||||||
|
if p.lastPingNonce != 0 {
|
||||||
|
wait := time.Now().Sub(p.lastPingTime).Nanoseconds()
|
||||||
|
// We actually want microseconds.
|
||||||
|
info.PingWait = wait / 1000
|
||||||
|
}
|
||||||
|
p.pingStatsMtx.Unlock()
|
||||||
infos = append(infos, info)
|
infos = append(infos, info)
|
||||||
})
|
})
|
||||||
msg.reply <- infos
|
msg.reply <- infos
|
||||||
|
|
|
@ -64,9 +64,10 @@ var commandHandlers = map[string]*handlerData{
|
||||||
"getpeerinfo": &handlerData{0, 0, displayJSONDump, nil, makeGetPeerInfo, ""},
|
"getpeerinfo": &handlerData{0, 0, displayJSONDump, nil, makeGetPeerInfo, ""},
|
||||||
"getrawmempool": &handlerData{0, 1, displayJSONDump, []conversionHandler{toBool}, makeGetRawMempool, "[verbose=false]"},
|
"getrawmempool": &handlerData{0, 1, displayJSONDump, []conversionHandler{toBool}, makeGetRawMempool, "[verbose=false]"},
|
||||||
"getrawtransaction": &handlerData{1, 1, displayJSONDump, []conversionHandler{nil, toBool}, makeGetRawTransaction, "<txhash> [verbose=false]"},
|
"getrawtransaction": &handlerData{1, 1, displayJSONDump, []conversionHandler{nil, toBool}, makeGetRawTransaction, "<txhash> [verbose=false]"},
|
||||||
"help": &handlerData{0, 1, displayGeneric, nil, makeHelp, "[commandName]"},
|
"help": &handlerData{0, 1, displayGeneric, nil, makeHelp, "[commandName]"},
|
||||||
"importprivkey": &handlerData{1, 2, displayGeneric, []conversionHandler{nil, nil, toBool}, makeImportPrivKey, "<wifprivkey> [label] [rescan=true]"},
|
"importprivkey": &handlerData{1, 2, displayGeneric, []conversionHandler{nil, nil, toBool}, makeImportPrivKey, "<wifprivkey> [label] [rescan=true]"},
|
||||||
"listtransactions": &handlerData{0, 3, displayJSONDump, []conversionHandler{nil, toInt, toInt}, makeListTransactions, "[account] [count=10] [from=0]"},
|
"listtransactions": &handlerData{0, 3, displayJSONDump, []conversionHandler{nil, toInt, toInt}, makeListTransactions, "[account] [count=10] [from=0]"},
|
||||||
|
"ping": &handlerData{0, 0, displayGeneric, nil, makePing, ""},
|
||||||
"verifychain": &handlerData{0, 2, displayJSONDump, []conversionHandler{toInt, toInt}, makeVerifyChain, "[level] [numblocks]"},
|
"verifychain": &handlerData{0, 2, displayJSONDump, []conversionHandler{toInt, toInt}, makeVerifyChain, "[level] [numblocks]"},
|
||||||
"sendrawtransaction": &handlerData{1, 0, displayGeneric, nil, makeSendRawTransaction, "<hextx>"},
|
"sendrawtransaction": &handlerData{1, 0, displayGeneric, nil, makeSendRawTransaction, "<hextx>"},
|
||||||
"stop": &handlerData{0, 0, displayGeneric, nil, makeStop, ""},
|
"stop": &handlerData{0, 0, displayGeneric, nil, makeStop, ""},
|
||||||
|
@ -353,6 +354,11 @@ func makeListTransactions(args []interface{}) (btcjson.Cmd, error) {
|
||||||
return btcjson.NewListTransactionsCmd("btcctl", optargs...)
|
return btcjson.NewListTransactionsCmd("btcctl", optargs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// makePing generates the cmd structure for ping commands.
|
||||||
|
func makePing(args []interface{}) (btcjson.Cmd, error) {
|
||||||
|
return btcjson.NewPingCmd("btcctl")
|
||||||
|
}
|
||||||
|
|
||||||
// makeSendRawTransaction generates the cmd structure for sendrawtransaction
|
// makeSendRawTransaction generates the cmd structure for sendrawtransaction
|
||||||
// commands.
|
// commands.
|
||||||
func makeSendRawTransaction(args []interface{}) (btcjson.Cmd, error) {
|
func makeSendRawTransaction(args []interface{}) (btcjson.Cmd, error) {
|
||||||
|
|
Loading…
Reference in a new issue