plugins/topology: make listchannels mark disconnected local channels active=false.

This is blurring the lines a bit, but it's closer to what gossipd did.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2021-06-15 06:37:38 +09:30
parent 63d97d2467
commit 1a7731cfbf

View File

@@ -1,5 +1,6 @@
#include <ccan/array_size/array_size.h> #include <ccan/array_size/array_size.h>
#include <ccan/crypto/siphash24/siphash24.h> #include <ccan/crypto/siphash24/siphash24.h>
#include <ccan/htable/htable_type.h>
#include <ccan/json_out/json_out.h> #include <ccan/json_out/json_out.h>
#include <ccan/tal/str/str.h> #include <ccan/tal/str/str.h>
#include <ccan/time/time.h> #include <ccan/time/time.h>
@@ -266,10 +267,25 @@ static struct command_result *json_getroute(struct command *cmd,
return command_finished(cmd, js); return command_finished(cmd, js);
} }
static const struct node_id *node_id_keyof(const struct node_id *id)
{
return id;
}
static size_t node_id_hash(const struct node_id *id)
{
return siphash24(siphash_seed(), id->k, sizeof(id->k));
}
HTABLE_DEFINE_TYPE(struct node_id, node_id_keyof, node_id_hash, node_id_eq,
node_map);
/* To avoid multiple fetches, we represent directions as a bitmap /* To avoid multiple fetches, we represent directions as a bitmap
* so we can do two at once. */ * so we can do two at once. */
static void json_add_halfchan(struct json_stream *response, static void json_add_halfchan(struct json_stream *response,
struct gossmap *gossmap, struct gossmap *gossmap,
const struct node_map *connected,
const struct gossmap_chan *c, const struct gossmap_chan *c,
int dirbits) int dirbits)
{ {
@@ -277,6 +293,7 @@ static void json_add_halfchan(struct json_stream *response,
struct node_id node_id[2]; struct node_id node_id[2];
const u8 *chanfeatures; const u8 *chanfeatures;
struct amount_sat capacity; struct amount_sat capacity;
bool local_disable;
/* These are channel (not per-direction) properties */ /* These are channel (not per-direction) properties */
chanfeatures = gossmap_chan_get_features(tmpctx, gossmap, c); chanfeatures = gossmap_chan_get_features(tmpctx, gossmap, c);
@@ -289,6 +306,14 @@ static void json_add_halfchan(struct json_stream *response,
if (!gossmap_chan_get_capacity(gossmap, c, &capacity)) if (!gossmap_chan_get_capacity(gossmap, c, &capacity))
capacity = AMOUNT_SAT(0); capacity = AMOUNT_SAT(0);
/* Local channels are not "active" unless peer is connected. */
if (node_id_eq(&node_id[0], &local_id))
local_disable = !node_map_get(connected, &node_id[1]);
else if (node_id_eq(&node_id[1], &local_id))
local_disable = !node_map_get(connected, &node_id[0]);
else
local_disable = false;
for (int dir = 0; dir < 2; dir++) { for (int dir = 0; dir < 2; dir++) {
u32 timestamp; u32 timestamp;
u8 message_flags, channel_flags; u8 message_flags, channel_flags;
@@ -320,7 +345,9 @@ static void json_add_halfchan(struct json_stream *response,
"satoshis", "amount_msat"); "satoshis", "amount_msat");
json_add_num(response, "message_flags", message_flags); json_add_num(response, "message_flags", message_flags);
json_add_num(response, "channel_flags", channel_flags); json_add_num(response, "channel_flags", channel_flags);
json_add_bool(response, "active", c->half[dir].enabled);
json_add_bool(response, "active",
c->half[dir].enabled && !local_disable);
json_add_num(response, "last_update", timestamp); json_add_num(response, "last_update", timestamp);
json_add_num(response, "base_fee_millisatoshi", fee_base_msat); json_add_num(response, "base_fee_millisatoshi", fee_base_msat);
json_add_num(response, "fee_per_millionth", json_add_num(response, "fee_per_millionth",
@@ -348,49 +375,98 @@ static void json_add_halfchan(struct json_stream *response,
} }
} }
static struct command_result *json_listchannels(struct command *cmd, struct listchannels_opts {
const char *buffer,
const jsmntok_t *params)
{
struct node_id *source; struct node_id *source;
struct short_channel_id *scid; struct short_channel_id *scid;
struct json_stream *js; };
/* We record which local channels are valid; we could record which are
* invalid, but our testsuite has some weirdness where it has local
* channels in the store it knows nothing about. */
static struct node_map *local_connected(const tal_t *ctx,
const char *buf,
const jsmntok_t *result)
{
size_t i;
const jsmntok_t *t, *peers = json_get_member(buf, result, "peers");
struct node_map *connected = tal(ctx, struct node_map);
node_map_init(connected);
json_for_each_arr(i, t, peers) {
const jsmntok_t *chans, *c;
struct node_id id;
bool is_connected, normal_chan;
const char *err;
size_t j;
err = json_scan(tmpctx, buf, t,
"{id:%,connected:%}",
JSON_SCAN(json_to_node_id, &id),
JSON_SCAN(json_to_bool, &is_connected));
if (err)
plugin_err(plugin, "Bad listpeers response (%s): %.*s",
err,
json_tok_full_len(result),
json_tok_full(buf, result));
if (!is_connected)
continue;
/* Must also have a channel in CHANNELD_NORMAL */
normal_chan = false;
chans = json_get_member(buf, t, "channels");
json_for_each_arr(j, c, chans) {
if (json_tok_streq(buf,
json_get_member(buf, c, "state"),
"CHANNELD_NORMAL"))
normal_chan = true;
}
if (normal_chan)
node_map_add(connected,
tal_dup(connected, struct node_id, &id));
}
return connected;
}
/* We want to combine local knowledge to we know which are actually inactive! */
static struct command_result *listpeers_done(struct command *cmd,
const char *buf,
const jsmntok_t *result,
struct listchannels_opts *opts)
{
struct node_map *connected;
struct gossmap_chan *c; struct gossmap_chan *c;
struct gossmap *gossmap; struct json_stream *js;
struct gossmap *gossmap = get_gossmap();
if (!param(cmd, buffer, params, connected = local_connected(opts, buf, result);
p_opt("short_channel_id", param_short_channel_id, &scid),
p_opt("source", param_node_id, &source),
NULL))
return command_param_failed();
if (scid && source)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Cannot specify both source and short_channel_id");
gossmap = get_gossmap();
js = jsonrpc_stream_success(cmd); js = jsonrpc_stream_success(cmd);
json_array_start(js, "channels"); json_array_start(js, "channels");
if (scid) { if (opts->scid) {
c = gossmap_find_chan(gossmap, scid); c = gossmap_find_chan(gossmap, opts->scid);
if (c) if (c)
json_add_halfchan(js, gossmap, c, 3); json_add_halfchan(js, gossmap, connected, c, 3);
} else if (source) { } else if (opts->source) {
struct gossmap_node *src; struct gossmap_node *src;
src = gossmap_find_node(gossmap, source); src = gossmap_find_node(gossmap, opts->source);
if (src) { if (src) {
for (size_t i = 0; i < src->num_chans; i++) { for (size_t i = 0; i < src->num_chans; i++) {
int dir; int dir;
c = gossmap_nth_chan(gossmap, src, i, &dir); c = gossmap_nth_chan(gossmap, src, i, &dir);
json_add_halfchan(js, gossmap, c, 1 << dir); json_add_halfchan(js, gossmap, connected,
c, 1 << dir);
} }
} }
} else { } else {
for (c = gossmap_first_chan(gossmap); for (c = gossmap_first_chan(gossmap);
c; c;
c = gossmap_next_chan(gossmap, c)) { c = gossmap_next_chan(gossmap, c)) {
json_add_halfchan(js, gossmap, c, 3); json_add_halfchan(js, gossmap, connected, c, 3);
} }
} }
@@ -399,6 +475,28 @@ static struct command_result *json_listchannels(struct command *cmd,
return command_finished(cmd, js); return command_finished(cmd, js);
} }
static struct command_result *json_listchannels(struct command *cmd,
const char *buffer,
const jsmntok_t *params)
{
struct listchannels_opts *opts = tal(cmd, struct listchannels_opts);
struct out_req *req;
if (!param(cmd, buffer, params,
p_opt("short_channel_id", param_short_channel_id,
&opts->scid),
p_opt("source", param_node_id, &opts->source),
NULL))
return command_param_failed();
if (opts->scid && opts->source)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Cannot specify both source and short_channel_id");
req = jsonrpc_request_start(cmd->plugin, cmd, "listpeers",
listpeers_done, forward_error, opts);
return send_outreq(cmd->plugin, req);
}
static const char *init(struct plugin *p, static const char *init(struct plugin *p,
const char *buf UNUSED, const jsmntok_t *config UNUSED) const char *buf UNUSED, const jsmntok_t *config UNUSED)
{ {