diff --git a/gossipd/gossip.c b/gossipd/gossip.c index a5ce8d6f6..bf3793c77 100644 --- a/gossipd/gossip.c +++ b/gossipd/gossip.c @@ -1095,23 +1095,40 @@ static struct io_plan *getchannels_req(struct io_conn *conn, struct daemon *daem return daemon_conn_read_next(conn, &daemon->master); } -static struct io_plan *getnodes(struct io_conn *conn, struct daemon *daemon) +static void append_node(struct gossip_getnodes_entry **nodes, + const struct node *n) +{ + size_t num_nodes = tal_count(*nodes); + tal_resize(nodes, num_nodes + 1); + (*nodes)[num_nodes].nodeid = n->id; + (*nodes)[num_nodes].addresses = n->addresses; +} + +static struct io_plan *getnodes(struct io_conn *conn, struct daemon *daemon, + const u8 *msg) { tal_t *tmpctx = tal_tmpctx(daemon); u8 *out; struct node *n; - struct node_map_iter i; struct gossip_getnodes_entry *nodes; - size_t node_count = 0; + struct pubkey *ids; - nodes = tal_arr(tmpctx, struct gossip_getnodes_entry, node_count); - n = node_map_first(daemon->rstate->nodes, &i); - while (n != NULL) { - tal_resize(&nodes, node_count + 1); - nodes[node_count].nodeid = n->id; - nodes[node_count].addresses = n->addresses; - node_count++; - n = node_map_next(daemon->rstate->nodes, &i); + fromwire_gossip_getnodes_request(tmpctx, msg, NULL, &ids); + + nodes = tal_arr(tmpctx, struct gossip_getnodes_entry, 0); + if (ids) { + for (size_t i = 0; i < tal_count(ids); i++) { + n = node_map_get(daemon->rstate->nodes, &ids[i].pubkey); + if (n) + append_node(&nodes, n); + } + } else { + struct node_map_iter i; + n = node_map_first(daemon->rstate->nodes, &i); + while (n != NULL) { + append_node(&nodes, n); + n = node_map_next(daemon->rstate->nodes, &i); + } } out = towire_gossip_getnodes_reply(daemon, nodes); daemon_conn_send(&daemon->master, take(out)); @@ -1749,7 +1766,7 @@ static struct io_plan *recv_req(struct io_conn *conn, struct daemon_conn *master return release_peer(conn, daemon, master->msg_in); case WIRE_GOSSIP_GETNODES_REQUEST: - return getnodes(conn, daemon); + return getnodes(conn, daemon, daemon->master.msg_in); case WIRE_GOSSIP_GETROUTE_REQUEST: return getroute_req(conn, daemon, daemon->master.msg_in); diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index 287889468..d7dd0bce5 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -83,6 +83,9 @@ gossipctl_hand_back_peer,,msg,len*u8 # Pass JSON-RPC getnodes call through gossip_getnodes_request,3005 +# Can be 0 or 1 currently +gossip_getnodes_request,,num,u16 +gossip_getnodes_request,,id,num*struct pubkey #include gossip_getnodes_reply,3105 diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index cdd28149d..53dfb3003 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -211,18 +211,38 @@ static void json_getnodes_reply(struct subd *gossip, const u8 *reply, command_success(cmd, response); } -static void json_getnodes(struct command *cmd, const char *buffer, +static void json_listnodes(struct command *cmd, const char *buffer, const jsmntok_t *params) { - u8 *req = towire_gossip_getnodes_request(cmd); + u8 *req; + jsmntok_t *idtok = NULL; + struct pubkey *id = NULL; + + if (!json_get_params(buffer, params, + "?id", &idtok, + NULL)) { + command_fail(cmd, "Invalid arguments"); + return; + } + + if (idtok) { + id = tal_arr(cmd, struct pubkey, 1); + if (!json_tok_pubkey(buffer, idtok, id)) { + command_fail(cmd, "Invalid id"); + return; + } + } + + req = towire_gossip_getnodes_request(cmd, id); subd_req(cmd, cmd->ld->gossip, req, -1, 0, json_getnodes_reply, cmd); command_still_pending(cmd); } -static const struct json_command getnodes_command = { - "getnodes", json_getnodes, "Retrieve all nodes in our local network view", +static const struct json_command listnodes_command = { + "listnodes", json_listnodes, + "List a nodes in our local network view (or all, if no {id})", "Returns a list of all nodes that we know about"}; -AUTODATA(json_command, &getnodes_command); +AUTODATA(json_command, &listnodes_command); static void json_getroute_reply(struct subd *gossip, const u8 *reply, const int *fds, struct command *cmd) diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index 3d8639a14..7a65f121e 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -1412,9 +1412,15 @@ class LightningDTests(BaseLightningDTests): 'Channel {}\\(1\\) was updated.' .format(channel_id)]) - nodes = l1.rpc.getnodes()['nodes'] + nodes = l1.rpc.listnodes()['nodes'] assert set([n['nodeid'] for n in nodes]) == set([l1.info['id'], l2.info['id']]) + # Test listnodes with an arg, while we're here. + n1 = l1.rpc.listnodes(l1.info['id'])['nodes'][0] + n2 = l1.rpc.listnodes(l2.info['id'])['nodes'][0] + assert n1['nodeid'] == l1.info['id'] + assert n2['nodeid'] == l2.info['id'] + assert [c['active'] for c in l1.rpc.getchannels()['channels']] == [True, True] assert [c['public'] for c in l1.rpc.getchannels()['channels']] == [True, True] assert [c['active'] for c in l2.rpc.getchannels()['channels']] == [True, True] @@ -1470,8 +1476,8 @@ class LightningDTests(BaseLightningDTests): assert scid2 not in [c['short_channel_id'] for c in l1.rpc.getchannels()['channels']] assert scid2 not in [c['short_channel_id'] for c in l2.rpc.getchannels()['channels']] - assert l3.info['id'] not in [n['nodeid'] for n in l1.rpc.getnodes()['nodes']] - assert l3.info['id'] not in [n['nodeid'] for n in l2.rpc.getnodes()['nodes']] + assert l3.info['id'] not in [n['nodeid'] for n in l1.rpc.listnodes()['nodes']] + assert l3.info['id'] not in [n['nodeid'] for n in l2.rpc.listnodes()['nodes']] def ping_tests(self, l1, l2): # 0-byte pong gives just type + length field.