diff --git a/gossipd/gossip.c b/gossipd/gossip.c index b48edd43b..dd3f3758d 100644 --- a/gossipd/gossip.c +++ b/gossipd/gossip.c @@ -1299,6 +1299,31 @@ static struct io_plan *addr_hint(struct io_conn *conn, return daemon_conn_read_next(conn, &daemon->master); } +static struct io_plan *get_peers(struct io_conn *conn, + struct daemon *daemon, const u8 *msg) +{ + struct peer *peer; + size_t n = 0; + struct pubkey *id = tal_arr(conn, struct pubkey, n); + struct wireaddr *wireaddr = tal_arr(conn, struct wireaddr, n); + + if (!fromwire_gossip_getpeers_request(msg, NULL)) + master_badmsg(WIRE_GOSSIPCTL_PEER_ADDRHINT, msg); + + list_for_each(&daemon->peers, peer, list) { + tal_resize(&id, n+1); + tal_resize(&wireaddr, n+1); + + id[n] = peer->id; + wireaddr[n] = peer->addr; + n++; + } + + daemon_conn_send(&daemon->master, + take(towire_gossip_getpeers_reply(conn, id, wireaddr))); + return daemon_conn_read_next(conn, &daemon->master); +} + static struct io_plan *recv_req(struct io_conn *conn, struct daemon_conn *master) { struct daemon *daemon = container_of(master, struct daemon, master); @@ -1342,11 +1367,15 @@ static struct io_plan *recv_req(struct io_conn *conn, struct daemon_conn *master case WIRE_GOSSIPCTL_PEER_ADDRHINT: return addr_hint(conn, daemon, master->msg_in); + case WIRE_GOSSIP_GETPEERS_REQUEST: + return get_peers(conn, daemon, master->msg_in); + case WIRE_GOSSIPCTL_RELEASE_PEER_REPLY: case WIRE_GOSSIPCTL_RELEASE_PEER_REPLYFAIL: case WIRE_GOSSIP_GETNODES_REPLY: case WIRE_GOSSIP_GETROUTE_REPLY: case WIRE_GOSSIP_GETCHANNELS_REPLY: + case WIRE_GOSSIP_GETPEERS_REPLY: case WIRE_GOSSIP_PING_REPLY: case WIRE_GOSSIP_RESOLVE_CHANNEL_REPLY: case WIRE_GOSSIP_PEER_CONNECTED: diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index 020d1cdc6..3ded7872c 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -123,3 +123,10 @@ gossip_forwarded_msg,3010 gossip_forwarded_msg,,msglen,u16 gossip_forwarded_msg,,msg,msglen*u8 +# The main daemon asks for peers +gossip_getpeers_request,3011 + +gossip_getpeers_reply,3111 +gossip_getpeers_reply,,num,u16 +gossip_getpeers_reply,,id,num*struct pubkey +gossip_getpeers_reply,,addr,num*struct wireaddr diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 6de52a94b..b41137e42 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -60,6 +60,7 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIP_GETNODES_REQUEST: case WIRE_GOSSIP_GETROUTE_REQUEST: case WIRE_GOSSIP_GETCHANNELS_REQUEST: + case WIRE_GOSSIP_GETPEERS_REQUEST: case WIRE_GOSSIP_PING: case WIRE_GOSSIP_RESOLVE_CHANNEL_REQUEST: case WIRE_GOSSIP_FORWARDED_MSG: @@ -71,6 +72,7 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIP_GETNODES_REPLY: case WIRE_GOSSIP_GETROUTE_REPLY: case WIRE_GOSSIP_GETCHANNELS_REPLY: + case WIRE_GOSSIP_GETPEERS_REPLY: case WIRE_GOSSIP_PING_REPLY: case WIRE_GOSSIP_RESOLVE_CHANNEL_REPLY: case WIRE_GOSSIPCTL_RELEASE_PEER_REPLY: diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index cc6796a8c..fb00699fc 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -786,34 +786,31 @@ static void log_to_json(unsigned int skipped, json_add_string(info->response, NULL, log); } -static void json_getpeers(struct command *cmd, - const char *buffer, const jsmntok_t *params) +struct getpeers_args { + struct command *cmd; + /* If non-NULL, they want logs too */ + enum log_level *ll; +}; + +static void gossipd_getpeers_complete(struct subd *gossip, const u8 *msg, + const int *fds, + struct getpeers_args *gpa) { + /* This is a little sneaky... */ + struct pubkey *ids; + struct wireaddr *addrs; + struct json_result *response = new_json_result(gpa->cmd); struct peer *p; - struct json_result *response = new_json_result(cmd); - jsmntok_t *leveltok; - struct log_info info; - json_get_params(buffer, params, "?level", &leveltok, NULL); - - if (!leveltok) - ; - else if (json_tok_streq(buffer, leveltok, "debug")) - info.level = LOG_DBG; - else if (json_tok_streq(buffer, leveltok, "info")) - info.level = LOG_INFORM; - else if (json_tok_streq(buffer, leveltok, "unusual")) - info.level = LOG_UNUSUAL; - else if (json_tok_streq(buffer, leveltok, "broken")) - info.level = LOG_BROKEN; - else { - command_fail(cmd, "Invalid level param"); + if (!fromwire_gossip_getpeers_reply(msg, msg, NULL, &ids, &addrs)) { + command_fail(gpa->cmd, "Bad response from gossipd"); return; } + /* First the peers not just gossiping. */ json_object_start(response, NULL); json_array_start(response, "peers"); - list_for_each(&cmd->ld->peers, p, list) { + list_for_each(&gpa->cmd->ld->peers, p, list) { json_object_start(response, NULL); json_add_string(response, "state", peer_state_name(p->state)); json_add_string(response, "netaddr", @@ -832,7 +829,9 @@ static void json_getpeers(struct command *cmd, p->funding_satoshi * 1000); } - if (leveltok) { + if (gpa->ll) { + struct log_info info; + info.level = *gpa->ll; info.response = response; json_array_start(response, "log"); log_each_line(p->log_book, log_to_json, &info); @@ -840,9 +839,60 @@ static void json_getpeers(struct command *cmd, } json_object_end(response); } + + for (size_t i = 0; i < tal_count(ids); i++) { + /* Don't report peers in both, which can happen if they're + * reconnecting */ + if (peer_by_id(gpa->cmd->ld, ids + i)) + continue; + + json_object_start(response, NULL); + /* Fake state. */ + json_add_string(response, "state", "GOSSIPING"); + json_add_pubkey(response, "peerid", ids+i); + json_add_string(response, "netaddr", + type_to_string(response, struct wireaddr, + addrs + i)); + json_add_bool(response, "connected", "true"); + json_add_string(response, "owner", gossip->name); + json_object_end(response); + } + json_array_end(response); json_object_end(response); - command_success(cmd, response); + command_success(gpa->cmd, response); +} + +static void json_getpeers(struct command *cmd, + const char *buffer, const jsmntok_t *params) +{ + jsmntok_t *leveltok; + struct getpeers_args *gpa = tal(cmd, struct getpeers_args); + + gpa->cmd = cmd; + json_get_params(buffer, params, "?level", &leveltok, NULL); + + if (leveltok) { + gpa->ll = tal(gpa, enum log_level); + if (json_tok_streq(buffer, leveltok, "debug")) + *gpa->ll = LOG_DBG; + else if (json_tok_streq(buffer, leveltok, "info")) + *gpa->ll = LOG_INFORM; + else if (json_tok_streq(buffer, leveltok, "unusual")) + *gpa->ll = LOG_UNUSUAL; + else if (json_tok_streq(buffer, leveltok, "broken")) + *gpa->ll = LOG_BROKEN; + else { + command_fail(cmd, "Invalid level param"); + return; + } + } else + gpa->ll = NULL; + + /* Get peers from gossipd. */ + subd_req(cmd, cmd->ld->gossip, + take(towire_gossip_getpeers_request(cmd)), + -1, 0, gossipd_getpeers_complete, gpa); } static const struct json_command getpeers_command = { diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index 15fef25de..c4f22500e 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -279,9 +279,9 @@ class LightningDTests(BaseLightningDTests): def test_connect(self): l1,l2 = self.connect() - # Main daemon has no idea about these peers; they're in gossipd. - assert l1.rpc.getpeer(l2.info['id'], 'info') == None - assert l2.rpc.getpeer(l1.info['id'], 'info') == None + # These should be in gossipd. + assert l1.rpc.getpeer(l2.info['id'])['state'] == 'GOSSIPING' + assert l2.rpc.getpeer(l1.info['id'])['state'] == 'GOSSIPING' # Both gossipds will have them as new peers once handed back. l1.daemon.wait_for_log('handle_peer {}: new peer'.format(l2.info['id']))