Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add getnodeaddresses JSON-RPC support (rpc client & server) #1590

Merged
merged 1 commit into from
Sep 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions btcjson/chainsvrcmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,22 @@ func NewGetNetworkHashPSCmd(numBlocks, height *int) *GetNetworkHashPSCmd {
}
}

// GetNodeAddressesCmd defines the getnodeaddresses JSON-RPC command.
type GetNodeAddressesCmd struct {
Count *int32 `jsonrpcdefault:"1"`
}

// NewGetNodeAddressesCmd returns a new instance which can be used to issue a
// getnodeaddresses JSON-RPC command.
//
// The parameters which are pointers indicate they are optional. Passing nil
// for optional parameters will use the default value.
func NewGetNodeAddressesCmd(count *int32) *GetNodeAddressesCmd {
return &GetNodeAddressesCmd{
Count: count,
}
}

// GetPeerInfoCmd defines the getpeerinfo JSON-RPC command.
type GetPeerInfoCmd struct{}

Expand Down Expand Up @@ -974,6 +990,7 @@ func init() {
MustRegisterCmd("getnetworkinfo", (*GetNetworkInfoCmd)(nil), flags)
MustRegisterCmd("getnettotals", (*GetNetTotalsCmd)(nil), flags)
MustRegisterCmd("getnetworkhashps", (*GetNetworkHashPSCmd)(nil), flags)
MustRegisterCmd("getnodeaddresses", (*GetNodeAddressesCmd)(nil), flags)
MustRegisterCmd("getpeerinfo", (*GetPeerInfoCmd)(nil), flags)
MustRegisterCmd("getrawmempool", (*GetRawMempoolCmd)(nil), flags)
MustRegisterCmd("getrawtransaction", (*GetRawTransactionCmd)(nil), flags)
Expand Down
26 changes: 26 additions & 0 deletions btcjson/chainsvrcmds_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,32 @@ func TestChainSvrCmds(t *testing.T) {
Height: btcjson.Int(123),
},
},
{
name: "getnodeaddresses",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("getnodeaddresses")
},
staticCmd: func() interface{} {
return btcjson.NewGetNodeAddressesCmd(nil)
},
marshalled: `{"jsonrpc":"1.0","method":"getnodeaddresses","params":[],"id":1}`,
unmarshalled: &btcjson.GetNodeAddressesCmd{
Count: btcjson.Int32(1),
},
},
{
name: "getnodeaddresses optional",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("getnodeaddresses", 10)
},
staticCmd: func() interface{} {
return btcjson.NewGetNodeAddressesCmd(btcjson.Int32(10))
},
marshalled: `{"jsonrpc":"1.0","method":"getnodeaddresses","params":[10],"id":1}`,
unmarshalled: &btcjson.GetNodeAddressesCmd{
Count: btcjson.Int32(10),
},
},
{
name: "getpeerinfo",
newCmd: func() (interface{}, error) {
Expand Down
10 changes: 10 additions & 0 deletions btcjson/chainsvrresults.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,16 @@ type GetNetworkInfoResult struct {
Warnings string `json:"warnings"`
}

// GetNodeAddressesResult models the data returned from the getnodeaddresses
// command.
type GetNodeAddressesResult struct {
// Timestamp in seconds since epoch (Jan 1 1970 GMT) keeping track of when the node was last seen
Time int64 `json:"time"`
Services uint64 `json:"services"` // The services offered
Address string `json:"address"` // The address of the node
Port uint16 `json:"port"` // The port of the node
}

// GetPeerInfoResult models the data returned from the getpeerinfo command.
type GetPeerInfoResult struct {
ID int32 `json:"id"`
Expand Down
9 changes: 9 additions & 0 deletions rpcadapters.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,15 @@ func (cm *rpcConnManager) RelayTransactions(txns []*mempool.TxDesc) {
cm.server.relayTransactions(txns)
}

// NodeAddresses returns an array consisting node addresses which can
// potentially be used to find new nodes in the network.
//
// This function is safe for concurrent access and is part of the
// rpcserverConnManager interface implementation.
func (cm *rpcConnManager) NodeAddresses() []*wire.NetAddress {
return cm.server.addrManager.AddressCache()
}

// rpcSyncMgr provides a block manager for use with the RPC server and
// implements the rpcserverSyncManager interface.
type rpcSyncMgr struct {
Expand Down
37 changes: 37 additions & 0 deletions rpcclient/net.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,43 @@ func (c *Client) GetNetworkInfo() (*btcjson.GetNetworkInfoResult, error) {
return c.GetNetworkInfoAsync().Receive()
}

// FutureGetNodeAddressesResult is a future promise to deliver the result of a
// GetNodeAddressesAsync RPC invocation (or an applicable error).
type FutureGetNodeAddressesResult chan *response

// Receive waits for the response promised by the future and returns data about
// known node addresses.
func (r FutureGetNodeAddressesResult) Receive() ([]btcjson.GetNodeAddressesResult, error) {
res, err := receiveFuture(r)
if err != nil {
return nil, err
}

// Unmarshal result as an array of getnodeaddresses result objects.
var nodeAddresses []btcjson.GetNodeAddressesResult
err = json.Unmarshal(res, &nodeAddresses)
if err != nil {
return nil, err
}

return nodeAddresses, nil
}

// GetNodeAddressesAsync returns an instance of a type that can be used to get the
// result of the RPC at some future time by invoking the Receive function on the
// returned instance.
//
// See GetNodeAddresses for the blocking version and more details.
func (c *Client) GetNodeAddressesAsync(count *int32) FutureGetNodeAddressesResult {
cmd := btcjson.NewGetNodeAddressesCmd(count)
return c.sendCmd(cmd)
}

// GetNodeAddresses returns data about known node addresses.
func (c *Client) GetNodeAddresses(count *int32) ([]btcjson.GetNodeAddressesResult, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are plenty of examples in rpcclient where each optional parameter has its own dedicated method. For example, instead of GetNodeAddresses(count *int32), we can have the following:

  • GetNodeAddresses()
  • GetNodeAddressesCount(count int32)

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for bringing this up. After getting similar feedback in another PR I was looking around rpcclient. In RPC calls that take a single optional variable I can see many examples where a single function which takes a pointer is provided - similarly to GetNodeAddresses(count *int32)

Trying to be consistent is the best solution I think. I can change it to the non-pointer style that you're suggesting if you think that's better.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine with me as long as we are consistent. 👍

return c.GetNodeAddressesAsync(count).Receive()
}

// FutureGetPeerInfoResult is a future promise to deliver the result of a
// GetPeerInfoAsync RPC invocation (or an applicable error).
type FutureGetPeerInfoResult chan *response
Expand Down
39 changes: 39 additions & 0 deletions rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ var rpcHandlersBeforeInit = map[string]commandHandler{
"getmininginfo": handleGetMiningInfo,
"getnettotals": handleGetNetTotals,
"getnetworkhashps": handleGetNetworkHashPS,
"getnodeaddresses": handleGetNodeAddresses,
"getpeerinfo": handleGetPeerInfo,
"getrawmempool": handleGetRawMempool,
"getrawtransaction": handleGetRawTransaction,
Expand Down Expand Up @@ -2477,6 +2478,40 @@ func handleGetNetworkHashPS(s *rpcServer, cmd interface{}, closeChan <-chan stru
return hashesPerSec.Int64(), nil
}

// handleGetNodeAddresses implements the getnodeaddresses command.
func handleGetNodeAddresses(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
c := cmd.(*btcjson.GetNodeAddressesCmd)

count := int32(1)
if c.Count != nil {
count = *c.Count
if count <= 0 {
return nil, &btcjson.RPCError{
Code: btcjson.ErrRPCInvalidParameter,
Message: "Address count out of range",
}
}
}

nodes := s.cfg.ConnMgr.NodeAddresses()
if n := int32(len(nodes)); n < count {
count = n
}

addresses := make([]*btcjson.GetNodeAddressesResult, 0, count)
for _, node := range nodes[:count] {
address := &btcjson.GetNodeAddressesResult{
Time: node.Timestamp.Unix(),
Services: uint64(node.Services),
Address: node.IP.String(),
Port: node.Port,
}
addresses = append(addresses, address)
}

return addresses, nil
}

// handleGetPeerInfo implements the getpeerinfo command.
func handleGetPeerInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
peers := s.cfg.ConnMgr.ConnectedPeers()
Expand Down Expand Up @@ -4298,6 +4333,10 @@ type rpcserverConnManager interface {
// RelayTransactions generates and relays inventory vectors for all of
// the passed transactions to all connected peers.
RelayTransactions(txns []*mempool.TxDesc)

// NodeAddresses returns an array consisting node addresses which can
// potentially be used to find new nodes in the network.
NodeAddresses() []*wire.NetAddress
}

// rpcserverSyncManager represents a sync manager for use with the RPC server.
Expand Down
12 changes: 12 additions & 0 deletions rpcserverhelp.go
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,17 @@ var helpDescsEnUS = map[string]string{
"getnettotalsresult-totalbytessent": "Total bytes sent",
"getnettotalsresult-timemillis": "Number of milliseconds since 1 Jan 1970 GMT",

// GetNodeAddressesResult help.
"getnodeaddressesresult-time": "Timestamp in seconds since epoch (Jan 1 1970 GMT) keeping track of when the node was last seen",
"getnodeaddressesresult-services": "The services offered",
"getnodeaddressesresult-address": "The address of the node",
"getnodeaddressesresult-port": "The port of the node",

// GetNodeAddressesCmd help.
"getnodeaddresses--synopsis": "Return known addresses which can potentially be used to find new nodes in the network",
"getnodeaddresses-count": "How many addresses to return. Limited to the smaller of 2500 or 23% of all known addresses",
"getnodeaddresses--result0": "List of node addresses",

// GetPeerInfoResult help.
"getpeerinforesult-id": "A unique node ID",
"getpeerinforesult-addr": "The ip address and port of the peer",
Expand Down Expand Up @@ -726,6 +737,7 @@ var rpcResultTypes = map[string][]interface{}{
"getmininginfo": {(*btcjson.GetMiningInfoResult)(nil)},
"getnettotals": {(*btcjson.GetNetTotalsResult)(nil)},
"getnetworkhashps": {(*int64)(nil)},
"getnodeaddresses": {(*[]btcjson.GetNodeAddressesResult)(nil)},
"getpeerinfo": {(*[]btcjson.GetPeerInfoResult)(nil)},
"getrawmempool": {(*[]string)(nil), (*btcjson.GetRawMempoolVerboseResult)(nil)},
"getrawtransaction": {(*string)(nil), (*btcjson.TxRawResult)(nil)},
Expand Down