From dc2ee9639b481e4c6ea73b04c7d2f920d48afbee Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 15 Jan 2019 14:39:27 +1030 Subject: [PATCH] listchannels: allow source arg to list channels by their source node. Signed-off-by: Rusty Russell --- CHANGELOG.md | 1 + contrib/pylightning/lightning/lightning.py | 7 ++++--- doc/lightning-listchannels.7 | 18 ++++++++++------- doc/lightning-listchannels.7.txt | 23 +++++++++++++--------- gossipd/gossip_wire.csv | 1 + gossipd/gossipd.c | 12 ++++++++++- lightningd/gossip_control.c | 10 ++++++++-- tests/test_gossip.py | 9 +++++++++ 8 files changed, 59 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4062798b1..b1ee55b88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - lightning-cli: `help ` finds man pages even if `make install` not run. - JSON API: `waitsendpay` now has an `erring_channel_direction` field. - JSON API: `listpeers` now has a `channel_direction` field in `channels`. +- JSON API: `listchannels` now takes a `source` option to filter by node id. ### Changed diff --git a/contrib/pylightning/lightning/lightning.py b/contrib/pylightning/lightning/lightning.py index 6ac9a3816..a4392cb7f 100644 --- a/contrib/pylightning/lightning/lightning.py +++ b/contrib/pylightning/lightning/lightning.py @@ -167,12 +167,13 @@ class LightningRpc(UnixDomainSocketRpc): } return self.call("getroute", payload) - def listchannels(self, short_channel_id=None): + def listchannels(self, short_channel_id=None, source=None): """ - Show all known channels, accept optional {short_channel_id} + Show all known channels, accept optional {short_channel_id} or {source} """ payload = { - "short_channel_id": short_channel_id + "short_channel_id": short_channel_id, + "source": source } return self.call("listchannels", payload) diff --git a/doc/lightning-listchannels.7 b/doc/lightning-listchannels.7 index 976dad333..77faf33f3 100644 --- a/doc/lightning-listchannels.7 +++ b/doc/lightning-listchannels.7 @@ -2,12 +2,12 @@ .\" Title: lightning-listchannels .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 -.\" Date: 01/08/2019 +.\" Date: 01/09/2019 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "LIGHTNING\-LISTCHANN" "7" "01/08/2019" "\ \&" "\ \&" +.TH "LIGHTNING\-LISTCHANN" "7" "01/09/2019" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -31,14 +31,16 @@ lightning-listchannels \- Command to query active lightning channels in the entire network\&. .SH "SYNOPSIS" .sp -\fBlistchannels\fR [\fIshort_channel_id\fR] +\fBlistchannels\fR [\fIshort_channel_id\fR] [\fIsource\fR] .SH "DESCRIPTION" .sp The \fBlistchannels\fR RPC command returns data on channels that are known to the node\&. Because channels may be bidirectional, up to 2 objects will be returned for each channel (one for each direction)\&. .sp -If no \fIshort_channel_id\fR is supplied, then data on all lightning channels known to this node, are returned\&. These can be local channels or public channels broadcast on the gossip network\&. +If \fIshort_channel_id\fR is supplied, then only known channels with a matching \fIshort_channel_id\fR are returned\&. .sp -Supplying \fIshort_channel_id\fR will filter the results to only return data for known channels with a matching \fIshort_channel_id\fR\&. +If \fIsource\fR is supplied, then only channels leading from that node id are returned\&. +.sp +If neither is supplied, data on all lightning channels known to this node, are returned\&. These can be local channels or public channels broadcast on the gossip network\&. .SH "RETURN VALUE" .sp On success, an object with a "channels" key is returned containing a list of 0 or more objects\&. @@ -191,7 +193,7 @@ message (BOLT #7)\&. : The number of blocks delay required to wait for on\-chain settlement when unilaterally closing the channel (BOLT #2)\&. .RE .sp -If \fIshort_channel_id\fR is supplied and no matching channels are found, a "channels" object with an empty list is returned\&. +If \fIshort_channel_id\fR or \fIsource\fR is supplied and no matching channels are found, a "channels" object with an empty list is returned\&. .SH "ERRORS" .sp If \fIshort_channel_id\fR is not a valid short_channel_id, an error message will be returned: @@ -206,12 +208,14 @@ If \fIshort_channel_id\fR is not a valid short_channel_id, an error message will .if n \{\ .RE .\} +.sp +Similarly if \fIsource\fR is not a valid pubkey\&. .SH "AUTHOR" .sp Michael Hawkins \&. .SH "SEE ALSO" .sp -lightning\-fundchannel(7) +lightning\-fundchannel(7), lightning\-listnodes(7) .SH "RESOURCES" .sp Main web site: https://github\&.com/ElementsProject/lightning diff --git a/doc/lightning-listchannels.7.txt b/doc/lightning-listchannels.7.txt index 95cf6eea0..f2ada1d52 100644 --- a/doc/lightning-listchannels.7.txt +++ b/doc/lightning-listchannels.7.txt @@ -8,7 +8,7 @@ lightning-listchannels - Command to query active lightning channels in the entir SYNOPSIS -------- -*listchannels* ['short_channel_id'] +*listchannels* ['short_channel_id'] ['source'] DESCRIPTION ----------- @@ -16,12 +16,15 @@ The *listchannels* RPC command returns data on channels that are known to the node. Because channels may be bidirectional, up to 2 objects will be returned for each channel (one for each direction). -If no 'short_channel_id' is supplied, then data on all lightning channels known -to this node, are returned. These can be local channels or public channels -broadcast on the gossip network. +If 'short_channel_id' is supplied, then only known channels with a +matching 'short_channel_id' are returned. -Supplying 'short_channel_id' will filter the results to only return data for -known channels with a matching 'short_channel_id'. +If 'source' is supplied, then only channels leading from that node id +are returned. + +If neither is supplied, data on all lightning channels known to this +node, are returned. These can be local channels or public channels +broadcast on the gossip network. RETURN VALUE ------------ @@ -56,8 +59,8 @@ transferred satoshi (BOLT #2). - 'delay' : The number of blocks delay required to wait for on-chain settlement when unilaterally closing the channel (BOLT #2). -If 'short_channel_id' is supplied and no matching channels are found, a -"channels" object with an empty list is returned. +If 'short_channel_id' or 'source' is supplied and no matching channels +are found, a "channels" object with an empty list is returned. ERRORS ------ @@ -69,13 +72,15 @@ returned: "message" : "'short_channel_id' should be a short channel id, not '...'" } ---- +Similarly if 'source' is not a valid pubkey. + AUTHOR ------ Michael Hawkins . SEE ALSO -------- -lightning-fundchannel(7) +lightning-fundchannel(7), lightning-listnodes(7) RESOURCES --------- diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index d15607d94..e2e02fed3 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -43,6 +43,7 @@ gossip_getroute_reply,,hops,num_hops*struct route_hop gossip_getchannels_request,3007 gossip_getchannels_request,,short_channel_id,?struct short_channel_id +gossip_getchannels_request,,source,?struct pubkey gossip_getchannels_reply,3107 gossip_getchannels_reply,,num_channels,u16 diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 0d4ea1f34..ed9519422 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -1971,9 +1971,10 @@ static struct io_plan *getchannels_req(struct io_conn *conn, struct gossip_getchannels_entry *entries; struct chan *chan; struct short_channel_id *scid; + struct pubkey *source; /* Note: scid is marked optional in gossip_wire.csv */ - if (!fromwire_gossip_getchannels_request(msg, msg, &scid)) + if (!fromwire_gossip_getchannels_request(msg, msg, &scid, &source)) master_badmsg(WIRE_GOSSIP_GETCHANNELS_REQUEST, msg); entries = tal_arr(tmpctx, struct gossip_getchannels_entry, 0); @@ -1982,6 +1983,15 @@ static struct io_plan *getchannels_req(struct io_conn *conn, chan = get_channel(daemon->rstate, scid); if (chan) append_channel(&entries, chan); + } else if (source) { + struct node *s = get_node(daemon->rstate, source); + if (s) { + for (size_t i = 0; i < tal_count(s->chans); i++) + append_half_channel(&entries, + s->chans[i], + !half_chan_to(s, + s->chans[i])); + } } else { u64 idx; diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index d28ce2b0f..479251d82 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -453,12 +453,18 @@ static struct command_result *json_listchannels(struct command *cmd, { u8 *req; struct short_channel_id *id; + struct pubkey *source; + if (!param(cmd, buffer, params, p_opt("short_channel_id", param_short_channel_id, &id), + p_opt("source", param_pubkey, &source), NULL)) return command_param_failed(); - req = towire_gossip_getchannels_request(cmd, id); + if (id && source) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Cannot specify both source and short_channel_id"); + req = towire_gossip_getchannels_request(cmd, id, source); subd_req(cmd->ld->gossip, cmd->ld->gossip, req, -1, 0, json_listchannels_reply, cmd); return command_still_pending(cmd); @@ -467,7 +473,7 @@ static struct command_result *json_listchannels(struct command *cmd, static const struct json_command listchannels_command = { "listchannels", json_listchannels, - "Show channel {short_channel_id} (or all known channels, if no {short_channel_id})" + "Show channel {short_channel_id} or {source} (or all known channels, if not specified)" }; AUTODATA(json_command, &listchannels_command); diff --git a/tests/test_gossip.py b/tests/test_gossip.py index b69f20a14..5bbe74aa7 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -277,6 +277,15 @@ def test_gossip_jsonrpc(node_factory): assert len([c for c in channels1 if not c['public']]) == 2 assert len([c for c in channels2 if not c['public']]) == 2 + # Test listchannels-by-source + channels1 = l1.rpc.listchannels(source=l1.info['id'])['channels'] + channels2 = l2.rpc.listchannels(source=l1.info['id'])['channels'] + assert only_one(channels1)['source'] == l1.info['id'] + assert only_one(channels1)['destination'] == l2.info['id'] + assert channels1 == channels2 + + channels2 = l2.rpc.listchannels()['channels'] + # Now proceed to funding-depth and do a full gossip round l1.bitcoin.generate_block(5) # Could happen in either order.