From 370aee0c9e1845ae728058f151f4d63a3cfbe133 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 15 Jun 2021 06:37:36 +0930 Subject: [PATCH 01/17] ccan: update to get RETURNS_NONNULL macro. Signed-off-by: Rusty Russell --- ccan/README | 2 +- ccan/ccan/compiler/compiler.h | 13 +++++++++++++ ccan/tools/configurator/configurator.c | 3 +++ tools/mockup.sh | 2 +- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/ccan/README b/ccan/README index 3316cef10dfb..289e9cf259c9 100644 --- a/ccan/README +++ b/ccan/README @@ -1,3 +1,3 @@ CCAN imported from http://ccodearchive.net. -CCAN version: init-2504-g48b4ffc3 +CCAN version: init-2506-gec95c3c5 diff --git a/ccan/ccan/compiler/compiler.h b/ccan/ccan/compiler/compiler.h index 1bbb3b8b61c6..562b29ec71cc 100644 --- a/ccan/ccan/compiler/compiler.h +++ b/ccan/ccan/compiler/compiler.h @@ -271,6 +271,19 @@ #define NON_NULL_ARGS(...) #endif +#if HAVE_ATTRIBUTE_RETURNS_NONNULL +/** + * RETURNS_NONNULL - specify that this function cannot return NULL. + * + * Mainly an optimization opportunity, but can also suppress warnings. + * + * Example: + * RETURNS_NONNULL char *my_copy(char *buf); + */ +#define RETURNS_NONNULL __attribute__((__returns_nonnull__)) +#else +#define RETURNS_NONNULL +#endif #if HAVE_ATTRIBUTE_SENTINEL /** diff --git a/ccan/tools/configurator/configurator.c b/ccan/tools/configurator/configurator.c index 33651ef4df3b..9487e694ce23 100644 --- a/ccan/tools/configurator/configurator.c +++ b/ccan/tools/configurator/configurator.c @@ -142,6 +142,9 @@ static const struct test base_tests[] = { { "HAVE_ATTRIBUTE_NONNULL", "__attribute__((nonnull)) support", "DEFINES_FUNC", NULL, NULL, "static char *__attribute__((nonnull)) func(char *p) { return p; }" }, + { "HAVE_ATTRIBUTE_RETURNS_NONNULL", "__attribute__((returns_nonnull)) support", + "DEFINES_FUNC", NULL, NULL, + "static const char *__attribute__((returns_nonnull)) func(void) { return \"hi\"; }" }, { "HAVE_ATTRIBUTE_SENTINEL", "__attribute__((sentinel)) support", "DEFINES_FUNC", NULL, NULL, "static int __attribute__((sentinel)) func(int i, ...) { return i; }" }, diff --git a/tools/mockup.sh b/tools/mockup.sh index 9686b14cffdc..f1e8ac047125 100755 --- a/tools/mockup.sh +++ b/tools/mockup.sh @@ -61,5 +61,5 @@ for SYMBOL; do echo "/* Generated stub for $SYMBOL */" - tail -n "+${LINE}" < "$FILE" | head -n "$NUM" | sed 's/^extern *//' | sed 's/PRINTF_FMT([^)]*)//' | sed 's/NON_NULL_ARGS([^)]*)//' | sed 's/NO_NULL_ARGS//g' | sed 's/NORETURN//g' | sed 's/LAST_ARG_NULL//g' | sed 's/WARN_UNUSED_RESULT//g' | sed 's/,/ UNNEEDED,/g' | sed 's/\([a-z0-9A-Z*_]* [a-z0-9A-Z*_]*\));/\1 UNNEEDED);/' | sed "s/;\$/$STUB/" | sed 's/[[:space:]]*$//' + tail -n "+${LINE}" < "$FILE" | head -n "$NUM" | sed 's/^extern *//' | sed 's/PRINTF_FMT([^)]*)//' | sed 's/NON_NULL_ARGS([^)]*)//' | sed 's/NO_NULL_ARGS//g' | sed 's/NORETURN//g' | sed 's/RETURNS_NONNULL//g' | sed 's/LAST_ARG_NULL//g' | sed 's/WARN_UNUSED_RESULT//g' | sed 's/,/ UNNEEDED,/g' | sed 's/\([a-z0-9A-Z*_]* [a-z0-9A-Z*_]*\));/\1 UNNEEDED);/' | sed "s/;\$/$STUB/" | sed 's/[[:space:]]*$//' done From ab6106752498a7e9e8fd9569f451bb4c41ddd0ff Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 15 Jun 2021 06:37:38 +0930 Subject: [PATCH 02/17] common: note that command_fail doesn't return NULL. This suppresses some "may-be-uninitialized" warnings later. It makes gcc pickier about how we ignore the result though :( Signed-off-by: Rusty Russell --- common/json_command.h | 2 +- common/param.c | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/common/json_command.h b/common/json_command.h index d090327ae3da..95ae8a192357 100644 --- a/common/json_command.h +++ b/common/json_command.h @@ -15,7 +15,7 @@ struct command_result; /* Caller supplied this: param assumes it can call it. */ struct command_result *command_fail(struct command *cmd, errcode_t code, const char *fmt, ...) - PRINTF_FMT(3, 4) WARN_UNUSED_RESULT; + PRINTF_FMT(3, 4) WARN_UNUSED_RESULT RETURNS_NONNULL; /* Convenient wrapper for "paramname: msg: invalid token '.*%s'" */ static inline struct command_result * diff --git a/common/param.c b/common/param.c index 3babe665e0c3..7d860225ab3f 100644 --- a/common/param.c +++ b/common/param.c @@ -298,9 +298,10 @@ const char *param_subcommand(struct command *cmd, const char *buffer, return subcmd; /* We really do ignore this. */ - if (command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Unknown subcommand '%s'", subcmd)) - ; + struct command_result *ignore; + ignore = command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Unknown subcommand '%s'", subcmd); + assert(ignore); return NULL; } @@ -323,9 +324,10 @@ bool param(struct command *cmd, const char *buffer, } if (!param_add(¶ms, name, required, cbx, arg)) { /* We really do ignore this return! */ - if (command_fail(cmd, PARAM_DEV_ERROR, - "developer error: param_add %s", name)) - ; + struct command_result *ignore; + ignore = command_fail(cmd, PARAM_DEV_ERROR, + "developer error: param_add %s", name); + assert(ignore); va_end(ap); return false; } From ee94799a6e7ab1093e80ad06532ff651fc2becc8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 15 Jun 2021 06:37:38 +0930 Subject: [PATCH 03/17] topology: plugin to implement getroute command. Temporarily rename old getroute to getrouteold (we will remove this). Changelog-Changed: JSON-RPC: `getroute` is now implemented in a plugin. Signed-off-by: Rusty Russell --- lightningd/gossip_control.c | 2 +- plugins/Makefile | 7 + plugins/topology.c | 300 ++++++++++++++++++++++++++++++++++++ tests/test_closing.py | 3 + 4 files changed, 311 insertions(+), 1 deletion(-) create mode 100644 plugins/topology.c diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 38ceac648123..3dceb09b9f76 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -448,7 +448,7 @@ static struct command_result *json_getroute(struct command *cmd, } static const struct json_command getroute_command = { - "getroute", + "getrouteold", "channels", json_getroute, "Show route to {id} for {msatoshi}, using {riskfactor} and optional {cltv} (default 9). " diff --git a/plugins/Makefile b/plugins/Makefile index 410a2a7be9e6..5761f41dd41e 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -4,6 +4,9 @@ PLUGIN_PAY_OBJS := $(PLUGIN_PAY_SRC:.c=.o) PLUGIN_AUTOCLEAN_SRC := plugins/autoclean.c PLUGIN_AUTOCLEAN_OBJS := $(PLUGIN_AUTOCLEAN_SRC:.c=.o) +PLUGIN_TOPOLOGY_SRC := plugins/topology.c +PLUGIN_TOPOLOGY_OBJS := $(PLUGIN_TOPOLOGY_SRC:.c=.o) + PLUGIN_TXPREPARE_SRC := plugins/txprepare.c PLUGIN_TXPREPARE_OBJS := $(PLUGIN_TXPREPARE_SRC:.c=.o) @@ -55,6 +58,7 @@ PLUGIN_ALL_SRC := \ $(PLUGIN_BCLI_SRC) \ $(PLUGIN_FETCHINVOICE_SRC) \ $(PLUGIN_FUNDER_SRC) \ + $(PLUGIN_TOPOLOGY_SRC) \ $(PLUGIN_KEYSEND_SRC) \ $(PLUGIN_TXPREPARE_SRC) \ $(PLUGIN_LIB_SRC) \ @@ -76,6 +80,7 @@ PLUGINS := \ plugins/bcli \ plugins/fetchinvoice \ plugins/funder \ + plugins/topology \ plugins/keysend \ plugins/offers \ plugins/pay \ @@ -135,6 +140,8 @@ $(PLUGIN_PAY_OBJS): $(PLUGIN_PAY_LIB_HEADER) plugins/autoclean: bitcoin/chainparams.o $(PLUGIN_AUTOCLEAN_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) +plugins/topology: common/route.o common/dijkstra.o common/gossmap.o common/fp16.o bitcoin/chainparams.o $(PLUGIN_TOPOLOGY_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) + plugins/txprepare: bitcoin/chainparams.o $(PLUGIN_TXPREPARE_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) plugins/bcli: bitcoin/chainparams.o $(PLUGIN_BCLI_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) diff --git a/plugins/topology.c b/plugins/topology.c new file mode 100644 index 000000000000..ea202282032a --- /dev/null +++ b/plugins/topology.c @@ -0,0 +1,300 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Access via get_gossmap() */ +static struct node_id local_id; +static struct plugin *plugin; + +/* We load this on demand, since we can start before gossipd. */ +static struct gossmap *get_gossmap(void) +{ + static struct gossmap *gossmap; + + if (gossmap) + gossmap_refresh(gossmap); + else { + gossmap = notleak_with_children(gossmap_load(NULL, + GOSSIP_STORE_FILENAME)); + if (!gossmap) + plugin_err(plugin, "Could not load gossmap %s: %s", + GOSSIP_STORE_FILENAME, strerror(errno)); + } + return gossmap; +} + +/* Convenience global since route_score_fuzz doesn't take args. 0 to 1. */ +static double fuzz; + +enum exclude_entry_type { + EXCLUDE_CHANNEL = 1, + EXCLUDE_NODE = 2 +}; + +struct exclude_entry { + enum exclude_entry_type type; + union { + struct short_channel_id_dir chan_id; + struct node_id node_id; + } u; +}; + +/* Prioritize costs over distance, but with fuzz. Cost must be + * the same when the same channel queried, so we base it on that. */ +static u64 route_score_fuzz(u32 distance, + struct amount_msat cost, + struct amount_msat risk, + const struct gossmap_chan *c) +{ + u64 costs = cost.millisatoshis + risk.millisatoshis; /* Raw: score */ + /* Use the literal pointer, since it's stable. */ + u64 h = siphash24(siphash_seed(), &c, sizeof(c)); + + /* Use distance as the tiebreaker */ + costs += distance; + + /* h / (UINT64_MAX / 2.0) is between 0 and 2. */ + costs *= (h / (double)(UINT64_MAX / 2) - 1) * fuzz; + + return costs; +} + +static bool can_carry(const struct gossmap *map, + const struct gossmap_chan *c, + int dir, + struct amount_msat amount, + const struct exclude_entry **excludes) +{ + struct node_id dstid; + + /* First do generic check */ + if (!route_can_carry(map, c, dir, amount, NULL)) { + plugin_log(plugin, LOG_DBG, "cannot carry %s across %p", + type_to_string(tmpctx, struct amount_msat, &amount), + c); + return false; + } + + /* Now check exclusions. Premature optimization: */ + if (!tal_count(excludes)) { + plugin_log(plugin, LOG_DBG, "CAN carry %s across %p", + type_to_string(tmpctx, struct amount_msat, &amount), + c); + return true; + } + + gossmap_node_get_id(map, gossmap_nth_node(map, c, !dir), &dstid); + for (size_t i = 0; i < tal_count(excludes); i++) { + struct short_channel_id scid; + + switch (excludes[i]->type) { + case EXCLUDE_CHANNEL: + scid = gossmap_chan_scid(map, c); + if (short_channel_id_eq(&excludes[i]->u.chan_id.scid, &scid) + && dir == excludes[i]->u.chan_id.dir) + return false; + continue; + + case EXCLUDE_NODE: + if (node_id_eq(&dstid, &excludes[i]->u.node_id)) + return false; + continue; + } + /* No other cases should be possible! */ + plugin_err(plugin, "Invalid type %i in exclusion[%zu]", + excludes[i]->type, i); + } + return true; +} + +static void json_add_route_hop_style(struct json_stream *response, + const char *fieldname, + enum route_hop_style style) +{ + switch (style) { + case ROUTE_HOP_LEGACY: + json_add_string(response, fieldname, "legacy"); + return; + case ROUTE_HOP_TLV: + json_add_string(response, fieldname, "tlv"); + return; + } + abort(); +} + +/* Output a route hop */ +static void json_add_route_hop(struct json_stream *js, + const char *fieldname, + const struct route_hop *r) +{ + /* Imitate what getroute/sendpay use */ + json_object_start(js, fieldname); + json_add_node_id(js, "id", &r->node_id); + json_add_short_channel_id(js, "channel", &r->scid); + json_add_num(js, "direction", r->direction); + json_add_amount_msat_compat(js, r->amount, "msatoshi", "amount_msat"); + json_add_num(js, "delay", r->delay); + json_add_route_hop_style(js, "style", r->style); + json_object_end(js); +} + +static struct command_result *json_getroute(struct command *cmd, + const char *buffer, + const jsmntok_t *params) +{ + struct node_id *destination; + struct node_id *source; + const jsmntok_t *excludetok; + struct amount_msat *msat; + u32 *cltv; + /* risk factor 12.345% -> riskfactor_millionths = 12345000 */ + u64 *riskfactor_millionths, *fuzz_millionths; + const struct exclude_entry **excluded; + u32 *max_hops; + const struct dijkstra *dij; + struct route_hop *route; + struct gossmap_node *src, *dst; + struct json_stream *js; + struct gossmap *gossmap; + + if (!param(cmd, buffer, params, + p_req("id", param_node_id, &destination), + p_req("msatoshi", param_msat, &msat), + p_req("riskfactor", param_millionths, &riskfactor_millionths), + p_opt_def("cltv", param_number, &cltv, 9), + p_opt_def("fromid", param_node_id, &source, local_id), + p_opt_def("fuzzpercent", param_millionths, &fuzz_millionths, + 5000000), + p_opt("exclude", param_array, &excludetok), + p_opt_def("maxhops", param_number, &max_hops, ROUTING_MAX_HOPS), + NULL)) + return command_param_failed(); + + /* Convert from percentage */ + fuzz = *fuzz_millionths / 100.0 / 1000000.0; + if (fuzz > 1.0) + return command_fail_badparam(cmd, "fuzzpercent", + buffer, params, + "should be <= 100"); + + if (excludetok) { + const jsmntok_t *t; + size_t i; + + excluded = tal_arr(cmd, const struct exclude_entry *, 0); + + json_for_each_arr(i, t, excludetok) { + struct exclude_entry *entry = tal(excluded, struct exclude_entry); + struct short_channel_id_dir *chan_id = tal(tmpctx, struct short_channel_id_dir); + if (!short_channel_id_dir_from_str(buffer + t->start, + t->end - t->start, + chan_id)) { + struct node_id *node_id = tal(tmpctx, struct node_id); + + if (!json_to_node_id(buffer, t, node_id)) + return command_fail_badparam(cmd, "exclude", + buffer, t, + "should be short_channel_id or node_id"); + + entry->type = EXCLUDE_NODE; + entry->u.node_id = *node_id; + } else { + entry->type = EXCLUDE_CHANNEL; + entry->u.chan_id = *chan_id; + } + + tal_arr_expand(&excluded, entry); + } + } else { + excluded = NULL; + } + + gossmap = get_gossmap(); + src = gossmap_find_node(gossmap, source); + if (!src) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "%s: unknown source node_id (no public channels?)", + type_to_string(tmpctx, struct node_id, source)); + + dst = gossmap_find_node(gossmap, destination); + if (!dst) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "%s: unknown destination node_id (no public channels?)", + type_to_string(tmpctx, struct node_id, destination)); + + fuzz = 0; + dij = dijkstra(tmpctx, gossmap, dst, *msat, + *riskfactor_millionths / 1000000.0, + can_carry, route_score_fuzz, excluded); + route = route_from_dijkstra(dij, gossmap, dij, src, *msat, *cltv); + if (!route) + return command_fail(cmd, PAY_ROUTE_NOT_FOUND, "Could not find a route"); + + /* If it's too far, fall back to using shortest path. */ + if (tal_count(route) > *max_hops) { + plugin_notify_message(cmd, LOG_INFORM, "Cheapest route %zu hops: seeking shorter (no fuzz)", + tal_count(route)); + dij = dijkstra(tmpctx, gossmap, dst, *msat, + *riskfactor_millionths / 1000000.0, + can_carry, route_score_shorter, excluded); + route = route_from_dijkstra(dij, gossmap, dij, src, *msat, *cltv); + if (tal_count(route) > *max_hops) + return command_fail(cmd, PAY_ROUTE_NOT_FOUND, "Shortest route was %zu", + tal_count(route)); + } + + js = jsonrpc_stream_success(cmd); + json_array_start(js, "route"); + for (size_t i = 0; i < tal_count(route); i++) { + json_add_route_hop(js, NULL, &route[i]); + } + json_array_end(js); + + return command_finished(cmd, js); +} + +static const char *init(struct plugin *p, + const char *buf UNUSED, const jsmntok_t *config UNUSED) +{ + plugin = p; + rpc_scan(p, "getinfo", + take(json_out_obj(NULL, NULL, NULL)), + "{id:%}", JSON_SCAN(json_to_node_id, &local_id)); + + return NULL; +} + +static const struct plugin_command commands[] = { + { + "getroute", + "channels", + "Primitive route command", + "Show route to {id} for {msatoshi}, using {riskfactor} and optional {cltv} (default 9). " + "If specified search from {fromid} otherwise use this node as source. " + "Randomize the route with up to {fuzzpercent} (default 5.0). " + "{exclude} an array of short-channel-id/direction (e.g. [ '564334x877x1/0', '564195x1292x0/1' ]) " + "or node-id from consideration. " + "Set the {maxhops} the route can take (default 20).", + json_getroute, + }, +}; + +int main(int argc, char *argv[]) +{ + setup_locale(); + plugin_main(argv, init, PLUGIN_STATIC, true, NULL, commands, ARRAY_SIZE(commands), + NULL, 0, NULL, 0, NULL, 0, NULL); +} diff --git a/tests/test_closing.py b/tests/test_closing.py index 55d05d447dac..6702278ff9e8 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -777,6 +777,9 @@ def test_penalty_htlc_tx_fulfill(node_factory, bitcoind, chainparams): amt = 10**8 // 2 sticky_inv = l1.rpc.invoice(amt, '2', 'sticky') route = l4.rpc.getroute(l1.info['id'], amt, 1)['route'] + route2 = l4.rpc.getrouteold(l1.info['id'], amt, 1)['route'] + + assert(route == route2) l4.rpc.sendpay(route, sticky_inv['payment_hash']) l1.daemon.wait_for_log('dev_disconnect: -WIRE_UPDATE_FULFILL_HTLC') From d1f0c6bf74ce2bfe91b6b3f95e7c699f9811b872 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 15 Jun 2021 06:37:38 +0930 Subject: [PATCH 04/17] pyln-client: hack in test that getroute returns the same old/new. It sometimes fails because the two race, and sometimes because there's randomness, but it generally works (and doesn't fail systemically). We remove this before the final merge. Signed-off-by: Rusty Russell --- contrib/pyln-client/pyln/client/lightning.py | 26 +++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index f4e53f31935d..7c088c2495a1 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -816,7 +816,31 @@ def getroute(self, node_id, msatoshi, riskfactor, cltv=9, fromid=None, fuzzperce "exclude": exclude, "maxhops": maxhops } - return self.call("getroute", payload) + + # This is a hack to make sure old and new routines return the same result. + e1 = None + ret1 = None + err = None + try: + ret1 = self.call("getroute", payload) + except RpcError as e: + err = e + e1 = e.error + e2 = None + ret2 = None + try: + ret2 = self.call("getrouteold", payload) + except RpcError as e: + e2 = e.error + + print("new getroute: {} exception {}".format(ret1, e1)) + print("old getroute: {} exception {}".format(ret2, e2)) + assert ret1 == ret2 + assert e1 == e2 + + if err is not None: + raise err + return ret1 def help(self, command=None): """ From e1ba3e7decdb89ebbdc71aa57e4d77517bafac22 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 15 Jun 2021 06:37:38 +0930 Subject: [PATCH 05/17] plugins/topology: listchannels command. Signed-off-by: Rusty Russell --- lightningd/gossip_control.c | 2 +- plugins/topology.c | 140 ++++++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+), 1 deletion(-) diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 3dceb09b9f76..349d1c89fb06 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -569,7 +569,7 @@ static struct command_result *json_listchannels(struct command *cmd, } static const struct json_command listchannels_command = { - "listchannels", + "listchannelsold", "channels", json_listchannels, "Show channel {short_channel_id} or {source} (or all known channels, if not specified)" diff --git a/plugins/topology.c b/plugins/topology.c index ea202282032a..fb6f3ef52d51 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -266,6 +266,139 @@ static struct command_result *json_getroute(struct command *cmd, return command_finished(cmd, js); } +/* To avoid multiple fetches, we represent directions as a bitmap + * so we can do two at once. */ +static void json_add_halfchan(struct json_stream *response, + struct gossmap *gossmap, + const struct gossmap_chan *c, + int dirbits) +{ + struct short_channel_id scid; + struct node_id node_id[2]; + const u8 *chanfeatures; + struct amount_sat capacity; + + /* These are channel (not per-direction) properties */ + chanfeatures = gossmap_chan_get_features(tmpctx, gossmap, c); + scid = gossmap_chan_scid(gossmap, c); + for (size_t i = 0; i < 2; i++) + gossmap_node_get_id(gossmap, gossmap_nth_node(gossmap, c, i), + &node_id[i]); + + /* This can theoretically happen on partial write races. */ + if (!gossmap_chan_get_capacity(gossmap, c, &capacity)) + capacity = AMOUNT_SAT(0); + + for (int dir = 0; dir < 2; dir++) { + u32 timestamp; + u8 message_flags, channel_flags; + u32 fee_base_msat, fee_proportional_millionths; + struct amount_msat htlc_minimum_msat, htlc_maximum_msat; + + if (!((1 << dir) & dirbits)) + continue; + + if (!gossmap_chan_set(c, dir)) + continue; + + json_object_start(response, NULL); + json_add_node_id(response, "source", &node_id[dir]); + json_add_node_id(response, "destination", &node_id[!dir]); + json_add_short_channel_id(response, "short_channel_id", &scid); + json_add_bool(response, "public", !c->private); + + gossmap_chan_get_update_details(gossmap, c, dir, + ×tamp, + &message_flags, + &channel_flags, + &fee_base_msat, + &fee_proportional_millionths, + &htlc_minimum_msat, + &htlc_maximum_msat); + + json_add_amount_sat_compat(response, capacity, + "satoshis", "amount_msat"); + json_add_num(response, "message_flags", message_flags); + json_add_num(response, "channel_flags", channel_flags); + json_add_bool(response, "active", c->half[dir].enabled); + json_add_num(response, "last_update", timestamp); + json_add_num(response, "base_fee_millisatoshi", fee_base_msat); + json_add_num(response, "fee_per_millionth", + fee_proportional_millionths); + json_add_num(response, "delay", c->half[dir].delay); + json_add_amount_msat_only(response, "htlc_minimum_msat", + htlc_minimum_msat); + + /* We used to always print this, but that's weird */ + if (deprecated_apis && !(message_flags & 1)) { + if (!amount_sat_to_msat(&htlc_maximum_msat, capacity)) + plugin_err(plugin, + "Channel with impossible capacity %s", + type_to_string(tmpctx, + struct amount_sat, + &capacity)); + message_flags = 1; + } + + if (message_flags & 1) + json_add_amount_msat_only(response, "htlc_maximum_msat", + htlc_maximum_msat); + json_add_hex_talarr(response, "features", chanfeatures); + json_object_end(response); + } +} + +static struct command_result *json_listchannels(struct command *cmd, + const char *buffer, + const jsmntok_t *params) +{ + struct node_id *source; + struct short_channel_id *scid; + struct json_stream *js; + struct gossmap_chan *c; + struct gossmap *gossmap; + + if (!param(cmd, buffer, params, + 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); + json_array_start(js, "channels"); + if (scid) { + c = gossmap_find_chan(gossmap, scid); + if (c) + json_add_halfchan(js, gossmap, c, 3); + } else if (source) { + struct gossmap_node *src; + + src = gossmap_find_node(gossmap, source); + if (src) { + for (size_t i = 0; i < src->num_chans; i++) { + int dir; + c = gossmap_nth_chan(gossmap, src, i, &dir); + json_add_halfchan(js, gossmap, c, 1 << dir); + } + } + } else { + for (c = gossmap_first_chan(gossmap); + c; + c = gossmap_next_chan(gossmap, c)) { + json_add_halfchan(js, gossmap, c, 3); + } + } + + json_array_end(js); + + return command_finished(cmd, js); +} + static const char *init(struct plugin *p, const char *buf UNUSED, const jsmntok_t *config UNUSED) { @@ -290,6 +423,13 @@ static const struct plugin_command commands[] = { "Set the {maxhops} the route can take (default 20).", json_getroute, }, + { + "listchannels", + "channels", + "List all known channels in the network", + "Show channel {short_channel_id} or {source} (or all known channels, if not specified)", + json_listchannels, + }, }; int main(int argc, char *argv[]) From 2063c91ac4df40ce48cf244583f0341e6739ccf6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 15 Jun 2021 06:37:38 +0930 Subject: [PATCH 06/17] 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 --- plugins/topology.c | 146 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 122 insertions(+), 24 deletions(-) diff --git a/plugins/topology.c b/plugins/topology.c index fb6f3ef52d51..5398b1d60009 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -266,10 +267,25 @@ static struct command_result *json_getroute(struct command *cmd, 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 * so we can do two at once. */ static void json_add_halfchan(struct json_stream *response, struct gossmap *gossmap, + const struct node_map *connected, const struct gossmap_chan *c, int dirbits) { @@ -277,6 +293,7 @@ static void json_add_halfchan(struct json_stream *response, struct node_id node_id[2]; const u8 *chanfeatures; struct amount_sat capacity; + bool local_disable; /* These are channel (not per-direction) properties */ 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)) 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++) { u32 timestamp; u8 message_flags, channel_flags; @@ -320,7 +345,9 @@ static void json_add_halfchan(struct json_stream *response, "satoshis", "amount_msat"); json_add_num(response, "message_flags", message_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, "base_fee_millisatoshi", fee_base_msat); 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, - const char *buffer, - const jsmntok_t *params) -{ +struct listchannels_opts { struct node_id *source; struct short_channel_id *scid; - struct json_stream *js; - struct gossmap_chan *c; - struct gossmap *gossmap; +}; - if (!param(cmd, buffer, params, - p_opt("short_channel_id", param_short_channel_id, &scid), - p_opt("source", param_node_id, &source), - NULL)) - return command_param_failed(); +/* 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; - if (scid && source) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Cannot specify both source and short_channel_id"); + /* 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 json_stream *js; + struct gossmap *gossmap = get_gossmap(); + + connected = local_connected(opts, buf, result); - gossmap = get_gossmap(); js = jsonrpc_stream_success(cmd); json_array_start(js, "channels"); - if (scid) { - c = gossmap_find_chan(gossmap, scid); + if (opts->scid) { + c = gossmap_find_chan(gossmap, opts->scid); if (c) - json_add_halfchan(js, gossmap, c, 3); - } else if (source) { + json_add_halfchan(js, gossmap, connected, c, 3); + } else if (opts->source) { struct gossmap_node *src; - src = gossmap_find_node(gossmap, source); + src = gossmap_find_node(gossmap, opts->source); if (src) { for (size_t i = 0; i < src->num_chans; i++) { int 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 { for (c = gossmap_first_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); } +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, const char *buf UNUSED, const jsmntok_t *config UNUSED) { From 83cef65f56b4d7dd50964a8335e76a4984bd593f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 15 Jun 2021 06:37:38 +0930 Subject: [PATCH 07/17] pyln-client: hack in test that listchannels returns the same old/new. It sometimes fails because the two race, and sometimes because there's randomness, but it generally works (and doesn't fail systemically). We remove this before the final merge. Signed-off-by: Rusty Russell --- contrib/pyln-client/pyln/client/lightning.py | 45 +++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index 7c088c2495a1..363435543bdb 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -876,7 +876,50 @@ def listchannels(self, short_channel_id=None, source=None): "short_channel_id": short_channel_id, "source": source } - return self.call("listchannels", payload) + + + # This is a hack to make sure old and new routines return the same result. + e1 = None + ret1 = None + err = None + try: + ret1 = self.call("listchannels", payload) + except RpcError as e: + err = e + e1 = e.error + e2 = None + ret2 = None + try: + ret2 = self.call("listchannelsold", payload) + except RpcError as e: + e2 = e.error + + print("new listchannels: {} exception {}".format(ret1, e1)) + print("old listchannels: {} exception {}".format(ret2, e2)) + # gossipd only marks a channel enabled again when channeld says to; + # our new code just requires a reconnection. So check that + # separately + if ret1: + assert len(ret1['channels']) == len(ret2['channels']) + for i in range(len(ret1['channels'])): + if ret1['channels'][i]['active'] and not ret2['channels'][i]['active']: + ret2['channels'][i]['active'] = True + + def chan_key(c): + return c['source'] + c['destination'] + + # Order is arbitrary + if ret1: + ret1['channels'].sort(key=chan_key) + if ret2: + ret2['channels'].sort(key=chan_key) + + assert ret1 == ret2 + assert e1 == e2 + + if err is not None: + raise err + return ret1 def listconfigs(self, config=None): """List this node's config. From 56014eb37c53ab0ccaae8c20ab907b61c722368e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 15 Jun 2021 06:37:38 +0930 Subject: [PATCH 08/17] common/wireaddr: fromwire_wireaddr_array helper. This was previously internal to gossipd and was called "read_addresses". Signed-off-by: Rusty Russell --- common/wireaddr.c | 29 ++++++++++++++++ common/wireaddr.h | 3 ++ gossipd/gossipd.c | 2 +- gossipd/routing.c | 33 +------------------ gossipd/routing.h | 3 -- gossipd/test/run-bench-find_route.c | 6 ++-- gossipd/test/run-check_channel_announcement.c | 6 ++-- gossipd/test/run-find_route-specific.c | 6 ++-- gossipd/test/run-find_route.c | 6 ++-- gossipd/test/run-overlong.c | 6 ++-- gossipd/test/run-txout_failure.c | 6 ++-- 11 files changed, 52 insertions(+), 54 deletions(-) diff --git a/common/wireaddr.c b/common/wireaddr.c index b765157c385f..3a0a3784094f 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -691,6 +691,35 @@ struct addrinfo *wireaddr_to_addrinfo(const tal_t *ctx, abort(); } +struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx, const u8 *ser) +{ + const u8 *cursor = ser; + size_t len = tal_count(ser); + struct wireaddr *wireaddrs = tal_arr(ctx, struct wireaddr, 0); + + while (cursor && len) { + struct wireaddr wireaddr; + + /* BOLT #7: + * + * The receiving node: + *... + * - SHOULD ignore the first `address descriptor` that does + * NOT match the types defined above. + */ + if (!fromwire_wireaddr(&cursor, &len, &wireaddr)) { + if (!cursor) + /* Parsing address failed */ + return tal_free(wireaddrs); + /* Unknown type, stop there. */ + break; + } + + tal_arr_expand(&wireaddrs, wireaddr); + } + return wireaddrs; +} + bool all_tor_addresses(const struct wireaddr_internal *wireaddr) { for (int i = 0; i < tal_count(wireaddr); i++) { diff --git a/common/wireaddr.h b/common/wireaddr.h index 26612a16e2fd..f45c2cc76e62 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -165,4 +165,7 @@ struct addrinfo *wireaddr_internal_to_addrinfo(const tal_t *ctx, bool all_tor_addresses(const struct wireaddr_internal *wireaddr); +/* Decode an array of serialized addresses from node_announcement */ +struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx, const u8 *ser); + #endif /* LIGHTNING_COMMON_WIREADDR_H */ diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 5619b5ef7b3d..d495771e8008 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -193,7 +193,7 @@ static bool get_node_announcement(const tal_t *ctx, return false; } - *wireaddrs = read_addresses(ctx, addresses); + *wireaddrs = fromwire_wireaddr_array(ctx, addresses); tal_free(addresses); return true; } diff --git a/gossipd/routing.c b/gossipd/routing.c index 321ce14ba1cf..96e24d9ae257 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -2383,37 +2383,6 @@ u8 *handle_channel_update(struct routing_state *rstate, const u8 *update TAKES, return NULL; } -struct wireaddr *read_addresses(const tal_t *ctx, const u8 *ser) -{ - const u8 *cursor = ser; - size_t len = tal_count(ser); - struct wireaddr *wireaddrs = tal_arr(ctx, struct wireaddr, 0); - - while (cursor && len) { - struct wireaddr wireaddr; - - /* BOLT #7: - * - * The receiving node: - *... - * - SHOULD ignore the first `address descriptor` that does - * NOT match the types defined above. - */ - if (!fromwire_wireaddr(&cursor, &len, &wireaddr)) { - if (!cursor) - /* Parsing address failed */ - return tal_free(wireaddrs); - /* Unknown type, stop there. */ - status_debug("read_addresses: unknown address type %u", - cursor[0]); - break; - } - - tal_arr_expand(&wireaddrs, wireaddr); - } - return wireaddrs; -} - bool routing_add_node_announcement(struct routing_state *rstate, const u8 *msg TAKES, u32 index, @@ -2619,7 +2588,7 @@ u8 *handle_node_announcement(struct routing_state *rstate, const u8 *node_ann, return err; } - wireaddrs = read_addresses(tmpctx, addresses); + wireaddrs = fromwire_wireaddr_array(tmpctx, addresses); if (!wireaddrs) { /* BOLT #7: * diff --git a/gossipd/routing.h b/gossipd/routing.h index 367054108d51..5cc4d5e96f88 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -511,9 +511,6 @@ static inline void local_enable_chan(struct routing_state *rstate, local_chan->local_disabled = false; } -/* Helper to convert on-wire addresses format to wireaddrs array */ -struct wireaddr *read_addresses(const tal_t *ctx, const u8 *ser); - /* Remove channel from store: announcement and any updates. */ void remove_channel_from_store(struct routing_state *rstate, struct chan *chan); diff --git a/gossipd/test/run-bench-find_route.c b/gossipd/test/run-bench-find_route.c index 0772b7d2348f..80b1533c9c57 100644 --- a/gossipd/test/run-bench-find_route.c +++ b/gossipd/test/run-bench-find_route.c @@ -39,9 +39,9 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED, /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr_array */ +struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx UNNEEDED, const u8 *ser UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr_array called!\n"); abort(); } /* Generated stub for json_add_member */ void json_add_member(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED, diff --git a/gossipd/test/run-check_channel_announcement.c b/gossipd/test/run-check_channel_announcement.c index cc77a3414819..caaea2ca35d3 100644 --- a/gossipd/test/run-check_channel_announcement.c +++ b/gossipd/test/run-check_channel_announcement.c @@ -42,9 +42,9 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED, /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr_array */ +struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx UNNEEDED, const u8 *ser UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr_array called!\n"); abort(); } /* Generated stub for gossip_store_add */ u64 gossip_store_add(struct gossip_store *gs UNNEEDED, const u8 *gossip_msg UNNEEDED, u32 timestamp UNNEEDED, bool push UNNEEDED, const u8 *addendum UNNEEDED) diff --git a/gossipd/test/run-find_route-specific.c b/gossipd/test/run-find_route-specific.c index 64060092c2cb..8431e25a5590 100644 --- a/gossipd/test/run-find_route-specific.c +++ b/gossipd/test/run-find_route-specific.c @@ -25,9 +25,9 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED, /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr_array */ +struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx UNNEEDED, const u8 *ser UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr_array called!\n"); abort(); } /* Generated stub for json_add_member */ void json_add_member(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED, diff --git a/gossipd/test/run-find_route.c b/gossipd/test/run-find_route.c index 41400bf2cd05..1e63f6700adf 100644 --- a/gossipd/test/run-find_route.c +++ b/gossipd/test/run-find_route.c @@ -25,9 +25,9 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED, /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr_array */ +struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx UNNEEDED, const u8 *ser UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr_array called!\n"); abort(); } /* Generated stub for json_add_member */ void json_add_member(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED, diff --git a/gossipd/test/run-overlong.c b/gossipd/test/run-overlong.c index 2a16642f7026..b643cd755df7 100644 --- a/gossipd/test/run-overlong.c +++ b/gossipd/test/run-overlong.c @@ -25,9 +25,9 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED, /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr_array */ +struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx UNNEEDED, const u8 *ser UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr_array called!\n"); abort(); } /* Generated stub for json_add_member */ void json_add_member(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED, diff --git a/gossipd/test/run-txout_failure.c b/gossipd/test/run-txout_failure.c index 044e8fed16ff..38cde87d4459 100644 --- a/gossipd/test/run-txout_failure.c +++ b/gossipd/test/run-txout_failure.c @@ -13,9 +13,9 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED, /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr_array */ +struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx UNNEEDED, const u8 *ser UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr_array called!\n"); abort(); } /* Generated stub for gossip_store_add */ u64 gossip_store_add(struct gossip_store *gs UNNEEDED, const u8 *gossip_msg UNNEEDED, u32 timestamp UNNEEDED, bool push UNNEEDED, const u8 *addendum UNNEEDED) From 781d811c0ecf802112e1c942ddd37f569ef1ad22 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 15 Jun 2021 06:37:38 +0930 Subject: [PATCH 09/17] plugins/topology: add listnodes command. Signed-off-by: Rusty Russell --- lightningd/gossip_control.c | 2 +- plugins/Makefile | 4 +- plugins/topology.c | 100 ++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 2 deletions(-) diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 349d1c89fb06..6d1fb71d69b7 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -301,7 +301,7 @@ static struct command_result *json_listnodes(struct command *cmd, } static const struct json_command listnodes_command = { - "listnodes", + "listnodesold", "network", json_listnodes, "Show node {id} (or all, if no {id}), in our local network view" diff --git a/plugins/Makefile b/plugins/Makefile index 5761f41dd41e..c13808bbc7b8 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -140,7 +140,9 @@ $(PLUGIN_PAY_OBJS): $(PLUGIN_PAY_LIB_HEADER) plugins/autoclean: bitcoin/chainparams.o $(PLUGIN_AUTOCLEAN_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) -plugins/topology: common/route.o common/dijkstra.o common/gossmap.o common/fp16.o bitcoin/chainparams.o $(PLUGIN_TOPOLOGY_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) +# Topology wants to decode node_announcement, and peer_wiregen which +# pulls in some of bitcoin/. +plugins/topology: common/route.o common/dijkstra.o common/gossmap.o common/fp16.o bitcoin/chainparams.o wire/peer$(EXP)_wiregen.o bitcoin/block.o bitcoin/preimage.o $(PLUGIN_TOPOLOGY_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) plugins/txprepare: bitcoin/chainparams.o $(PLUGIN_TXPREPARE_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) diff --git a/plugins/topology.c b/plugins/topology.c index 5398b1d60009..c1a4d7746ea6 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -12,9 +13,11 @@ #include #include #include +#include #include #include #include +#include /* Access via get_gossmap() */ static struct node_id local_id; @@ -497,6 +500,96 @@ static struct command_result *json_listchannels(struct command *cmd, return send_outreq(cmd->plugin, req); } +static void json_add_node(struct json_stream *js, + const struct gossmap *gossmap, + const struct gossmap_node *n) +{ + struct node_id node_id; + u8 *nannounce; + + json_object_start(js, NULL); + gossmap_node_get_id(gossmap, n, &node_id); + json_add_node_id(js, "nodeid", &node_id); + nannounce = gossmap_node_get_announce(tmpctx, gossmap, n); + if (nannounce) { + secp256k1_ecdsa_signature signature; + u8 *features; + u32 timestamp; + u8 rgb_color[3], alias[32]; + u8 *addresses; + struct node_id nid; + struct wireaddr *addrs; + struct json_escape *esc; + + if (!fromwire_node_announcement(nannounce, nannounce, + &signature, + &features, + ×tamp, + &nid, + rgb_color, + alias, + &addresses)) { + plugin_log(plugin, LOG_BROKEN, + "Cannot parse stored node_announcement" + " for %s at %u: %s", + type_to_string(tmpctx, struct node_id, + &node_id), + n->nann_off, + tal_hex(tmpctx, nannounce)); + goto out; + } + + esc = json_escape(NULL, + take(tal_strndup(NULL, + (const char *)alias, + ARRAY_SIZE(alias)))); + json_add_escaped_string(js, "alias", take(esc)); + json_add_hex(js, "color", rgb_color, ARRAY_SIZE(rgb_color)); + json_add_u64(js, "last_timestamp", timestamp); + json_add_hex_talarr(js, "features", features); + + json_array_start(js, "addresses"); + addrs = fromwire_wireaddr_array(nannounce, addresses); + for (size_t i = 0; i < tal_count(addrs); i++) + json_add_address(js, NULL, &addrs[i]); + json_array_end(js); + } +out: + json_object_end(js); +} + +static struct command_result *json_listnodes(struct command *cmd, + const char *buffer, + const jsmntok_t *params) +{ + struct node_id *id; + struct json_stream *js; + struct gossmap *gossmap; + + if (!param(cmd, buffer, params, + p_opt("id", param_node_id, &id), + NULL)) + return command_param_failed(); + + gossmap = get_gossmap(); + js = jsonrpc_stream_success(cmd); + json_array_start(js, "nodes"); + if (id) { + struct gossmap_node *n = gossmap_find_node(gossmap, id); + if (n) + json_add_node(js, gossmap, n); + } else { + for (struct gossmap_node *n = gossmap_first_node(gossmap); + n; + n = gossmap_next_node(gossmap, n)) { + json_add_node(js, gossmap, n); + } + } + json_array_end(js); + + return command_finished(cmd, js); +} + static const char *init(struct plugin *p, const char *buf UNUSED, const jsmntok_t *config UNUSED) { @@ -528,6 +621,13 @@ static const struct plugin_command commands[] = { "Show channel {short_channel_id} or {source} (or all known channels, if not specified)", json_listchannels, }, + { + "listnodes", + "network", + "List all known nodes in the network", + "Show node {id} (or all known nods, if not specified)", + json_listnodes, + }, }; int main(int argc, char *argv[]) From 63b7e099b73b7e26d580ad79bac6665fc3289ace Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 15 Jun 2021 06:37:38 +0930 Subject: [PATCH 10/17] pyln-client: hack in test that listnodes returns the same old/new. It sometimes fails because the two race, and sometimes because there's randomness, but it generally works (and doesn't fail systemically). We remove this before the final merge. Signed-off-by: Rusty Russell --- contrib/pyln-client/pyln/client/lightning.py | 35 +++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index 363435543bdb..c287b91cbaf1 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -979,7 +979,40 @@ def listnodes(self, node_id=None): payload = { "id": node_id } - return self.call("listnodes", payload) + + # This is a hack to make sure old and new routines return the same result. + e1 = None + ret1 = None + err = None + try: + ret1 = self.call("listnodes", payload) + except RpcError as e: + err = e + e1 = e.error + e2 = None + ret2 = None + try: + ret2 = self.call("listnodesold", payload) + except RpcError as e: + e2 = e.error + + print("new listnodes: {} exception {}".format(ret1, e1)) + print("old listnodes: {} exception {}".format(ret2, e2)) + + # Order is arbitrary + def node_key(n): + return n['nodeid'] + + if ret1: + ret1['nodes'].sort(key=node_key) + if ret2: + ret2['nodes'].sort(key=node_key) + assert ret1 == ret2 + assert e1 == e2 + + if err is not None: + raise err + return ret1 def listpays(self, bolt11=None, payment_hash=None): """ From 8da24b887724259f41c2a4f2c3d652813f8d17fb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 15 Jun 2021 06:37:38 +0930 Subject: [PATCH 11/17] plugins/topology: listincoming function to help invoices. Invoices need to know what the peers' update contains, to create routehints. It also wants to know if a peer has no other public channels (so-called "dead end" peers) to eliminate them from routehint consideration. This was previously done by a special function to ask gossipd. Signed-off-by: Rusty Russell --- plugins/topology.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/plugins/topology.c b/plugins/topology.c index c1a4d7746ea6..47490f754a62 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -590,6 +590,85 @@ static struct command_result *json_listnodes(struct command *cmd, return command_finished(cmd, js); } +/* What is capacity of peer attached to chan #n? */ +static struct amount_sat peer_capacity(const struct gossmap *gossmap, + const struct gossmap_node *me, + const struct gossmap_node *peer, + const struct gossmap_chan *ourchan) +{ + struct amount_sat capacity = AMOUNT_SAT(0); + + for (size_t i = 0; i < peer->num_chans; i++) { + int dir; + struct gossmap_chan *c; + c = gossmap_nth_chan(gossmap, peer, i, &dir); + if (c == ourchan) + continue; + if (!c->half[!dir].enabled) + continue; + if (!amount_sat_add(&capacity, capacity, + amount_sat(fp16_to_u64(c->half[dir] + .htlc_max)))) + continue; + } + return capacity; +} + +static struct command_result *json_listincoming(struct command *cmd, + const char *buffer, + const jsmntok_t *params) +{ + struct json_stream *js; + struct gossmap_node *me; + struct gossmap *gossmap; + + if (!param(cmd, buffer, params, NULL)) + return command_param_failed(); + + gossmap = get_gossmap(); + + js = jsonrpc_stream_success(cmd); + json_array_start(js, "incoming"); + me = gossmap_find_node(gossmap, &local_id); + if (!me) + goto done; + + for (size_t i = 0; i < me->num_chans; i++) { + struct node_id peer_id; + int dir; + struct gossmap_chan *ourchan; + struct gossmap_node *peer; + struct short_channel_id scid; + + ourchan = gossmap_nth_chan(gossmap, me, i, &dir); + /* If its half is disabled, ignore. */ + if (!ourchan->half[!dir].enabled) + continue; + + peer = gossmap_nth_node(gossmap, ourchan, !dir); + scid = gossmap_chan_scid(gossmap, ourchan); + + json_object_start(js, NULL); + gossmap_node_get_id(gossmap, peer, &peer_id); + json_add_node_id(js, "id", &peer_id); + json_add_short_channel_id(js, "short_channel_id", &scid); + json_add_amount_msat_only(js, "fee_base_msat", + amount_msat(ourchan->half[!dir] + .base_fee)); + json_add_u32(js, "fee_proportional_millionths", + ourchan->half[!dir].proportional_fee); + json_add_u32(js, "cltv_expiry_delta", ourchan->half[!dir].delay); + json_add_amount_sat_only(js, "incoming_capacity_msat", + peer_capacity(gossmap, + me, peer, ourchan)); + json_object_end(js); + } +done: + json_array_end(js); + + return command_finished(cmd, js); +} + static const char *init(struct plugin *p, const char *buf UNUSED, const jsmntok_t *config UNUSED) { @@ -628,6 +707,13 @@ static const struct plugin_command commands[] = { "Show node {id} (or all known nods, if not specified)", json_listnodes, }, + { + "listincoming", + "network", + "List the channels incoming from our direct peers", + "Used by invoice code to select peers for routehints", + json_listincoming, + }, }; int main(int argc, char *argv[]) From f62b90f3a58e4fab3a8d264b3f9465cb3c38d40b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 15 Jun 2021 06:37:38 +0930 Subject: [PATCH 12/17] invoice: overhaul routehints to use topology.listincoming, cleanup. This turned into a more extensive cleanup than intended. The previous warnings were overlapping and confusing, especially now MPP is the norm. *warning_capacity* is now the "even under best circumstances, we don't have enough incoming capacity", which is really what warning_mpp_capacity was trying to say (no longer printed). *warning_offline* and *warning_deadends* are only given if adding such peers would have helped give capacity (i.e. not if *warning_capacity* is set). The new *warning_private_unused* tells you that we would have sufficient capacity, but we refused to expose private channels. The test cases have been enhanced to cover the new warnings. Signed-off-by: Rusty Russell Changelog-Added: JSON-RPC: `invoice` now gives `warning_private_unused` if unused unannounced channels could have provided sufficient capacity. Changelog-Changed: JSON-RPC: `invoice` warnings are now better defined, and `warning_mpp_capacity` is no longer included (since `warning_capacity` covers that). --- doc/lightning-invoice.7 | 15 +- doc/lightning-invoice.7.md | 11 +- lightningd/invoice.c | 319 +++++++++++++------- lightningd/routehint.c | 262 ++++++++++------ lightningd/routehint.h | 21 +- lightningd/test/run-invoice-select-inchan.c | 39 ++- tests/test_invoices.py | 35 ++- 7 files changed, 477 insertions(+), 225 deletions(-) diff --git a/doc/lightning-invoice.7 b/doc/lightning-invoice.7 index 9e07b6ac17b8..d640a134ffcd 100644 --- a/doc/lightning-invoice.7 +++ b/doc/lightning-invoice.7 @@ -106,19 +106,16 @@ One of the following warnings may occur (on success): .RS .IP \[bu] -\fIwarning_offline\fR if no channel with a currently connected peer has - the incoming capacity to pay this invoice +\fIwarning_capacity\fR: even using all possible channels, there's not enough incoming capacity to pay this invoice\. .IP \[bu] -\fIwarning_capacity\fR if there is no channel that has sufficient - incoming capacity +\fIwarning_offline\fR: there would be enough incoming capacity, but some channels are offline, so there isn't\. .IP \[bu] -\fIwarning_deadends\fR if there is no channel that is not a dead-end +\fIwarning_deadends\fR: there would be enough incoming capacity, but some channels are dead-ends (no other public channels from those peers), so there isn't\. +.IP \[bu] +\fIwarning_private_unused\fR: there would be enough incoming capacity, but some channels are unannounced and \fIexposeprivatechannels\fR is \fIfalse\fR, so there isn't\. .IP \[bu] \fIwarning_mpp\fR if there is sufficient capacity, but not in a single channel, so the payer will have to use multi-part payments\. -.IP \[bu] -\fIwarning_mpp_capacity\fR if there is not sufficient capacity, even with all - channels\. .RE .SH AUTHOR @@ -134,4 +131,4 @@ Rusty Russell \fI is mainly responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:dc375ee583188c669574a8ef203971df650ef221567dccb8df7a81ed5ee4990f +\" SHA256STAMP:d53ec67cd81a41c7218e282c3d7662933868b25190334e9322de8a90ab99d603 diff --git a/doc/lightning-invoice.7.md b/doc/lightning-invoice.7.md index 6f43a9b322eb..d48246c6855f 100644 --- a/doc/lightning-invoice.7.md +++ b/doc/lightning-invoice.7.md @@ -87,15 +87,12 @@ The following error codes may occur: - 902: None of the specified *exposeprivatechannels* were usable. One of the following warnings may occur (on success): -- *warning\_offline* if no channel with a currently connected peer has - the incoming capacity to pay this invoice -- *warning\_capacity* if there is no channel that has sufficient - incoming capacity -- *warning\_deadends* if there is no channel that is not a dead-end +- *warning_capacity*: even using all possible channels, there's not enough incoming capacity to pay this invoice. +- *warning_offline*: there would be enough incoming capacity, but some channels are offline, so there isn't. +- *warning_deadends*: there would be enough incoming capacity, but some channels are dead-ends (no other public channels from those peers), so there isn't. +- *warning_private_unused*: there would be enough incoming capacity, but some channels are unannounced and *exposeprivatechannels* is *false*, so there isn't. - *warning_mpp* if there is sufficient capacity, but not in a single channel, so the payer will have to use multi-part payments. -- *warning_mpp_capacity* if there is not sufficient capacity, even with all - channels. AUTHOR ------ diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 56b42a10e39c..bf7c2ec4ca13 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -603,8 +603,7 @@ static struct route_info **select_inchan_mpp(const tal_t *ctx, struct lightningd *ld, struct amount_msat amount_needed, struct routehint_candidate - *candidates, - bool *warning_mpp_capacity) + *candidates) { /* The total amount we have gathered for incoming channels. */ struct amount_msat gathered; @@ -637,9 +636,6 @@ static struct route_info **select_inchan_mpp(const tal_t *ctx, candidates[i].c->rr_number = ld->rr_counter++; } - /* Check if we gathered enough. */ - *warning_mpp_capacity = amount_msat_less(gathered, amount_needed); - return routehints; } @@ -657,87 +653,169 @@ struct invoice_info { struct chanhints *chanhints; }; -static void gossipd_incoming_channels_reply(struct subd *gossipd, - const u8 *msg, - const int *fs, - struct invoice_info *info) +/* Add routehints based on listincoming results: NULL means success. */ +static struct command_result * +add_routehints(struct invoice_info *info, + const char *buffer, + const jsmntok_t *toks, + bool *warning_mpp, + bool *warning_capacity, + bool *warning_deadends, + bool *warning_offline, + bool *warning_private_unused) { - struct json_stream *response; - struct invoice invoice; - char *b11enc; - const struct invoice_details *details; - struct wallet *wallet = info->cmd->ld->wallet; const struct chanhints *chanhints = info->chanhints; - - struct routehint_candidate *candidates; - struct amount_msat offline_amt; - bool warning_mpp = false; - bool warning_mpp_capacity = false; - bool deadends; bool node_unpublished; + struct amount_msat avail_capacity, deadend_capacity, offline_capacity, + private_capacity; + struct routehint_candidate *candidates; + struct amount_msat total, needed; - candidates = routehint_candidates(tmpctx, info->cmd->ld, msg, - chanhints ? chanhints->expose_all_private : false, + /* Dev code can force routes. */ + if (tal_count(info->b11->routes) != 0) { + *warning_mpp = *warning_capacity = *warning_deadends + = *warning_offline = *warning_private_unused + = false; + return NULL; + } + + candidates = routehint_candidates(tmpctx, info->cmd->ld, + buffer, toks, + chanhints ? &chanhints->expose_all_private : NULL, chanhints ? chanhints->hints : NULL, &node_unpublished, - &deadends, - &offline_amt); + &avail_capacity, + &private_capacity, + &deadend_capacity, + &offline_capacity); /* If they told us to use scids and we couldn't, fail. */ if (tal_count(candidates) == 0 && chanhints && tal_count(chanhints->hints) != 0) { - was_pending(command_fail(info->cmd, - INVOICE_HINTS_GAVE_NO_ROUTES, - "None of those hints were suitable local channels")); - return; + return command_fail(info->cmd, + INVOICE_HINTS_GAVE_NO_ROUTES, + "None of those hints were suitable local channels"); } + needed = info->b11->msat ? *info->b11->msat : AMOUNT_MSAT(1); + + /* If we are not completely unpublished, try with reservoir + * sampling first. + * + * Why do we not do this if we are completely unpublished? + * Because it is possible that multiple invoices will, by + * chance, select the same channel as routehint. + * This single channel might not be able to accept all the + * incoming payments on all the invoices generated. + * If we were published, that is fine because the payer can + * fall back to just attempting to route directly. + * But if we were unpublished, the only way for the payer to + * reach us would be via the routehints we provide, so we + * should make an effort to avoid overlapping incoming + * channels, which is done by select_inchan_mpp. + */ + if (!node_unpublished) + info->b11->routes = select_inchan(info->b11, + info->cmd->ld, + needed, + candidates); + + /* If we are completely unpublished, or if the above reservoir + * sampling fails, select channels by round-robin. */ if (tal_count(info->b11->routes) == 0) { - struct amount_msat needed; - needed = info->b11->msat ? *info->b11->msat : AMOUNT_MSAT(1); - - /* If we are not completely unpublished, try with reservoir - * sampling first. - * - * Why do we not do this if we are completely unpublished? - * Because it is possible that multiple invoices will, by - * chance, select the same channel as routehint. - * This single channel might not be able to accept all the - * incoming payments on all the invoices generated. - * If we were published, that is fine because the payer can - * fall back to just attempting to route directly. - * But if we were unpublished, the only way for the payer to - * reach us would be via the routehints we provide, so we - * should make an effort to avoid overlapping incoming - * channels, which is done by select_inchan_mpp. - */ - if (!node_unpublished) - info->b11->routes = select_inchan(info->b11, - info->cmd->ld, - needed, - candidates); - /* If we are completely unpublished, or if the above reservoir - * sampling fails, select channels by round-robin. */ - if (tal_count(info->b11->routes) == 0) { - info->b11->routes = select_inchan_mpp(info->b11, - info->cmd->ld, - needed, - candidates, - &warning_mpp_capacity); - warning_mpp = (tal_count(info->b11->routes) > 1); + info->b11->routes = select_inchan_mpp(info->b11, + info->cmd->ld, + needed, + candidates); + *warning_mpp = (tal_count(info->b11->routes) > 1); + } else { + *warning_mpp = false; + } + + log_debug(info->cmd->ld->log, "needed = %s, avail_capacity = %s, private_capacity = %s, offline_capacity = %s, deadend_capacity = %s", + type_to_string(tmpctx, struct amount_msat, &needed), + type_to_string(tmpctx, struct amount_msat, &avail_capacity), + type_to_string(tmpctx, struct amount_msat, &private_capacity), + type_to_string(tmpctx, struct amount_msat, &offline_capacity), + type_to_string(tmpctx, struct amount_msat, &deadend_capacity)); + + if (!amount_msat_add(&total, avail_capacity, offline_capacity) + || !amount_msat_add(&total, total, deadend_capacity) + || !amount_msat_add(&total, total, private_capacity)) + fatal("Cannot add %s + %s + %s + %s", + type_to_string(tmpctx, struct amount_msat, + &avail_capacity), + type_to_string(tmpctx, struct amount_msat, + &offline_capacity), + type_to_string(tmpctx, struct amount_msat, + &deadend_capacity), + type_to_string(tmpctx, struct amount_msat, + &private_capacity)); + + /* If we literally didn't have capacity at all, warn. */ + *warning_capacity = amount_msat_greater_eq(needed, total); + + /* We only warn about these if we didn't have capacity and + * they would have helped. */ + *warning_offline = false; + *warning_deadends = false; + *warning_private_unused = false; + if (amount_msat_greater(needed, avail_capacity)) { + struct amount_msat tot; + + /* We didn't get enough: would offline have helped? */ + if (!amount_msat_add(&tot, avail_capacity, offline_capacity)) + abort(); + if (amount_msat_greater_eq(tot, needed)) { + *warning_offline = true; + goto done; + } + + /* Hmm, what about deadends? */ + if (!amount_msat_add(&tot, tot, deadend_capacity)) + abort(); + if (amount_msat_greater_eq(tot, needed)) { + *warning_deadends = true; + goto done; + } + + /* What about private channels? */ + if (!amount_msat_add(&tot, tot, private_capacity)) + abort(); + if (amount_msat_greater_eq(tot, needed)) { + *warning_private_unused = true; + goto done; } } +done: + return NULL; +} + +static struct command_result * +invoice_complete(struct invoice_info *info, + bool warning_no_listincoming, + bool warning_mpp, + bool warning_capacity, + bool warning_deadends, + bool warning_offline, + bool warning_private_unused) +{ + struct json_stream *response; + struct invoice invoice; + char *b11enc; + const struct invoice_details *details; + struct wallet *wallet = info->cmd->ld->wallet; + b11enc = bolt11_encode(info, info->b11, false, hsm_sign_b11, info->cmd->ld); /* Check duplicate preimage (unlikely unless they specified it!) */ if (wallet_invoice_find_by_rhash(wallet, &invoice, &info->b11->payment_hash)) { - was_pending(command_fail(info->cmd, - INVOICE_PREIMAGE_ALREADY_EXISTS, - "preimage already used")); - return; + return command_fail(info->cmd, + INVOICE_PREIMAGE_ALREADY_EXISTS, + "preimage already used"); } if (!wallet_invoice_create(wallet, @@ -751,10 +829,9 @@ static void gossipd_incoming_channels_reply(struct subd *gossipd, &info->payment_preimage, &info->b11->payment_hash, NULL)) { - was_pending(command_fail(info->cmd, INVOICE_LABEL_ALREADY_EXISTS, - "Duplicate label '%s'", - info->label->s)); - return; + return command_fail(info->cmd, INVOICE_LABEL_ALREADY_EXISTS, + "Duplicate label '%s'", + info->label->s); } /* Get details */ @@ -768,41 +845,60 @@ static void gossipd_incoming_channels_reply(struct subd *gossipd, notify_invoice_creation(info->cmd->ld, info->b11->msat, info->payment_preimage, info->label); - /* Warn if there's not sufficient incoming capacity. */ - if (tal_count(info->b11->routes) == 0) { - log_unusual(info->cmd->ld->log, - "invoice: insufficient incoming capacity for %s%s", - info->b11->msat - ? type_to_string(tmpctx, struct amount_msat, - info->b11->msat) - : "0", - amount_msat_greater(offline_amt, AMOUNT_MSAT(0)) - ? " (among currently connected peers)" : ""); - - if (amount_msat_greater(offline_amt, AMOUNT_MSAT(0))) { - json_add_string(response, "warning_offline", - "No channel with a peer that is currently connected" - " has sufficient incoming capacity"); - } else if (deadends) { - json_add_string(response, "warning_deadends", - "No channel with a peer that is not a dead end"); - } else if (tal_count(candidates) == 0) { - json_add_string(response, "warning_capacity", - "No channels"); - } else { - json_add_string(response, "warning_capacity", - "No channel with a peer that has sufficient incoming capacity"); - } - } - + if (warning_no_listincoming) + json_add_string(response, "warning_listincoming", + "No listincoming command available, cannot add routehints to invoice"); if (warning_mpp) json_add_string(response, "warning_mpp", "The invoice might only be payable by MPP-capable payers."); - if (warning_mpp_capacity) - json_add_string(response, "warning_mpp_capacity", - "The total incoming capacity is still insufficient even if the payer had MPP capability."); + if (warning_capacity) + json_add_string(response, "warning_capacity", + "Insufficient incoming channel capacity to pay invoice"); + + if (warning_deadends) + json_add_string(response, "warning_deadends", + "Insufficient incoming capacity, once dead-end peers were excluded"); + + if (warning_offline) + json_add_string(response, "warning_offline", + "Insufficient incoming capacity, once offline peers were excluded"); - was_pending(command_success(info->cmd, response)); + if (warning_private_unused) + json_add_string(response, "warning_private_unused", + "Insufficient incoming capacity, once private channels were excluded (try exposeprivatechannels=true?)"); + + return command_success(info->cmd, response); +} + +/* Return from "listincoming". */ +static void listincoming_done(const char *buffer, + const jsmntok_t *toks, + const jsmntok_t *idtok UNUSED, + struct invoice_info *info) +{ + struct lightningd *ld = info->cmd->ld; + struct command_result *ret; + bool warning_mpp, warning_capacity, warning_deadends, warning_offline, warning_private_unused; + + ret = add_routehints(info, buffer, toks, + &warning_mpp, + &warning_capacity, + &warning_deadends, + &warning_offline, + &warning_private_unused); + if (ret) + return; + + /* We're actually outside a db transaction here: spooky! */ + db_begin_transaction(ld->wallet->db); + invoice_complete(info, + false, + warning_mpp, + warning_capacity, + warning_deadends, + warning_offline, + warning_private_unused); + db_commit_transaction(ld->wallet->db); } #if DEVELOPER @@ -940,12 +1036,11 @@ static struct command_result *param_chanhints(struct command *cmd, /* Could be simply "true" or "false" */ if (json_to_bool(buffer, tok, &boolhint)) { (*chanhints)->expose_all_private = boolhint; - (*chanhints)->hints - = tal_arr(*chanhints, struct short_channel_id, 0); + (*chanhints)->hints = NULL; return NULL; } - (*chanhints)->expose_all_private = false; + (*chanhints)->expose_all_private = true; /* Could be a single short_channel_id or an array */ if (tok->type == JSMN_ARRAY) { size_t i; @@ -999,6 +1094,8 @@ static struct command_result *json_invoice(struct command *cmd, struct secret payment_secret; struct preimage *preimage; u32 *cltv; + struct jsonrpc_request *req; + struct plugin *plugin; #if DEVELOPER const jsmntok_t *routes; #endif @@ -1094,11 +1191,21 @@ static struct command_result *json_invoice(struct command *cmd, if (fallback_scripts) info->b11->fallbacks = tal_steal(info->b11, fallback_scripts); - subd_req(cmd, cmd->ld->gossip, - take(towire_gossipd_get_incoming_channels(NULL)), - -1, 0, gossipd_incoming_channels_reply, info); + req = jsonrpc_request_start(info, "listincoming", + cmd->ld->log, + NULL, listincoming_done, + info); + jsonrpc_request_end(req); + + plugin = find_plugin_for_command(cmd->ld, "listincoming"); + if (plugin) { + plugin_request_send(plugin, req); + return command_still_pending(cmd); + } - return command_still_pending(cmd); + /* We can't generate routehints without listincoming. */ + return invoice_complete(info, true, + false, false, false, false, false); } static const struct json_command invoice_command = { diff --git a/lightningd/routehint.c b/lightningd/routehint.c index 284dedcae112..d87d0b73dd63 100644 --- a/lightningd/routehint.c +++ b/lightningd/routehint.c @@ -1,26 +1,12 @@ #include +#include #include #include +#include #include #include #include -static void append_routes(struct route_info **dst, const struct route_info *src) -{ - size_t n = tal_count(*dst); - - tal_resize(dst, n + tal_count(src)); - memcpy(*dst + n, src, tal_count(src) * sizeof(*src)); -} - -static void append_bools(bool **dst, const bool *src) -{ - size_t n = tal_count(*dst); - - tal_resize(dst, n + tal_count(src)); - memcpy(*dst + n, src, tal_count(src) * sizeof(*src)); -} - static bool scid_in_arr(const struct short_channel_id *scidarr, const struct short_channel_id *scid) { @@ -34,100 +20,204 @@ static bool scid_in_arr(const struct short_channel_id *scidarr, struct routehint_candidate * routehint_candidates(const tal_t *ctx, struct lightningd *ld, - const u8 *incoming_channels_reply, - bool expose_all_private, + const char *buf, + const jsmntok_t *toks, + const bool *expose_all_private, const struct short_channel_id *hints, bool *none_public, - bool *deadends, - struct amount_msat *amount_offline) + struct amount_msat *avail_capacity, + struct amount_msat *private_capacity, + struct amount_msat *deadend_capacity, + struct amount_msat *offline_capacity) { - struct routehint_candidate *candidates; - struct route_info *inchans, *private; - bool *inchan_deadends, *private_deadends; - - if (!fromwire_gossipd_get_incoming_channels_reply(tmpctx, - incoming_channels_reply, - &inchans, - &inchan_deadends, - &private, - &private_deadends)) - fatal("Gossip gave bad GOSSIPD_GET_INCOMING_CHANNELS_REPLY %s", - tal_hex(tmpctx, incoming_channels_reply)); - - *none_public = (tal_count(inchans) == 0) && (tal_count(private) > 0); - *deadends = false; - - /* fromwire explicitly makes empty arrays into NULL */ - if (!inchans) { - inchans = tal_arr(tmpctx, struct route_info, 0); - inchan_deadends = tal_arr(tmpctx, bool, 0); - } - - if (expose_all_private) { - append_routes(&inchans, private); - append_bools(&inchan_deadends, private_deadends); - } else if (hints) { - /* Start by considering all channels as candidates */ - append_routes(&inchans, private); - append_bools(&inchan_deadends, private_deadends); - - /* Consider only hints they gave */ - for (size_t i = 0; i < tal_count(inchans); i++) { - if (!scid_in_arr(hints, - &inchans[i].short_channel_id)) { - tal_arr_remove(&inchans, i); - tal_arr_remove(&inchan_deadends, i); - i--; - } else - /* If they specify directly, we don't - * care if it's a deadend */ - inchan_deadends[i] = false; - } - } else { - assert(!hints); - /* By default, only consider private channels if there are - * no public channels *at all* */ - if (tal_count(inchans) == 0) { - append_routes(&inchans, private); - append_bools(&inchan_deadends, private_deadends); - } - } + struct routehint_candidate *candidates, *privcandidates; + const jsmntok_t *t, *arr; + size_t i; + + log_debug(ld->log, "routehint: %.*s", + json_tok_full_len(toks), + json_tok_full(buf, toks)); + + /* We get the full JSON, including result. */ + t = json_get_member(buf, toks, "result"); + if (!t) + fatal("Missing result from listincoming: %.*s", + json_tok_full_len(toks), + json_tok_full(buf, toks)); + arr = json_get_member(buf, t, "incoming"); candidates = tal_arr(ctx, struct routehint_candidate, 0); - *amount_offline = AMOUNT_MSAT(0); - - for (size_t i = 0; i < tal_count(inchans); i++) { - struct peer *peer; + privcandidates = tal_arr(tmpctx, struct routehint_candidate, 0); + *none_public = true; + *deadend_capacity = AMOUNT_MSAT(0); + *offline_capacity = AMOUNT_MSAT(0); + *avail_capacity = AMOUNT_MSAT(0); + *private_capacity = AMOUNT_MSAT(0); + + /* We combine the JSON output which knows the peers' details, + * with our internal information */ + json_for_each_arr(i, t, arr) { + struct amount_msat capacity; + const char *err; struct routehint_candidate candidate; + struct amount_msat fee_base; + struct route_info *r; + struct peer *peer; + bool is_public; + + r = tal(tmpctx, struct route_info); + + err = json_scan(tmpctx, buf, t, + "{id:%" + ",short_channel_id:%" + ",fee_base_msat:%" + ",fee_proportional_millionths:%" + ",cltv_expiry_delta:%" + ",incoming_capacity_msat:%" + "}", + JSON_SCAN(json_to_node_id, &r->pubkey), + JSON_SCAN(json_to_short_channel_id, + &r->short_channel_id), + JSON_SCAN(json_to_msat, &fee_base), + JSON_SCAN(json_to_u32, + &r->fee_proportional_millionths), + JSON_SCAN(json_to_u16, &r->cltv_expiry_delta), + JSON_SCAN(json_to_msat, &capacity)); + + if (err) { + fatal("Invalid return from listincoming (%s): %.*s", + err, + json_tok_full_len(toks), + json_tok_full(buf, toks)); + } /* Do we know about this peer? */ - peer = peer_by_id(ld, &inchans[i].pubkey); - if (!peer) + peer = peer_by_id(ld, &r->pubkey); + if (!peer) { + log_debug(ld->log, "%s: unknown peer", + type_to_string(tmpctx, + struct short_channel_id, + &r->short_channel_id)); continue; + } /* Does it have a channel in state CHANNELD_NORMAL */ candidate.c = peer_normal_channel(peer); - if (!candidate.c) + if (!candidate.c) { + log_debug(ld->log, "%s: abnormal channel", + type_to_string(tmpctx, + struct short_channel_id, + &r->short_channel_id)); continue; + } + + candidate.capacity = channel_amount_receivable(candidate.c); - /* Is it a dead-end? */ - if (inchan_deadends[i]) { - *deadends = true; + /* Now we can tell if it's public. If so (even if it's otherwise + * unusable), we *don't* expose private channels! */ + is_public = (candidate.c->channel_flags + & CHANNEL_FLAGS_ANNOUNCE_CHANNEL); + + if (is_public) + *none_public = false; + + /* If they explicitly say to expose all private ones, consider + * it public. */ + if (expose_all_private != NULL && *expose_all_private) + is_public = true; + + r->fee_base_msat = fee_base.millisatoshis; /* Raw: route_info */ + /* Could wrap: if so ignore */ + if (!amount_msat_eq(amount_msat(r->fee_base_msat), fee_base)) { + log_debug(ld->log, + "Peer charging insane fee %.*s; ignoring", + json_tok_full_len(t), + json_tok_full(buf, t)); continue; } - candidate.capacity = channel_amount_receivable(candidate.c); + /* Consider only hints they gave */ + if (hints) { + log_debug(ld->log, "We have hints!"); + if (!scid_in_arr(hints, &r->short_channel_id)) { + log_debug(ld->log, "scid %s not in hints", + type_to_string(tmpctx, + struct short_channel_id, + &r->short_channel_id)); + continue; + } + /* If they give us a hint, we use even if capacity 0 */ + } else if (amount_msat_eq(capacity, AMOUNT_MSAT(0))) { + log_debug(ld->log, "%s: deadend", + type_to_string(tmpctx, + struct short_channel_id, + &r->short_channel_id)); + if (!amount_msat_add(deadend_capacity, + *deadend_capacity, + candidate.capacity)) + fatal("Overflow summing deadend capacity!"); + continue; + } /* Is it offline? */ if (candidate.c->owner == NULL) { - if (!amount_msat_add(amount_offline, - *amount_offline, + log_debug(ld->log, "%s: offline", + type_to_string(tmpctx, + struct short_channel_id, + &r->short_channel_id)); + if (!amount_msat_add(offline_capacity, + *offline_capacity, candidate.capacity)) fatal("Overflow summing offline capacity!"); continue; } - candidate.r = &inchans[i]; - tal_arr_expand(&candidates, candidate); + + /* OK, finish it and append to one of the arrays. */ + if (is_public) { + log_debug(ld->log, "%s: added to public", + type_to_string(tmpctx, + struct short_channel_id, + &r->short_channel_id)); + candidate.r = tal_steal(candidates, r); + tal_arr_expand(&candidates, candidate); + if (!amount_msat_add(avail_capacity, + *avail_capacity, + candidate.capacity)) { + fatal("Overflow summing pub capacities %s + %s", + type_to_string(tmpctx, struct amount_msat, + avail_capacity), + type_to_string(tmpctx, struct amount_msat, + &candidate.capacity)); + } + } else { + log_debug(ld->log, "%s: added to private", + type_to_string(tmpctx, + struct short_channel_id, + &r->short_channel_id)); + candidate.r = tal_steal(privcandidates, r); + tal_arr_expand(&privcandidates, candidate); + if (!amount_msat_add(private_capacity, + *private_capacity, + candidate.capacity)) { + fatal("Overflow summing priv capacities %s + %s", + type_to_string(tmpctx, struct amount_msat, + private_capacity), + type_to_string(tmpctx, struct amount_msat, + &candidate.capacity)); + } + } + } + + /* By default, only consider private channels if there are + * no public channels *at all* */ + if (expose_all_private == NULL + && tal_count(candidates) == 0 && *none_public) { + log_debug(ld->log, "No publics: using private channels"); + tal_free(candidates); + candidates = tal_steal(ctx, privcandidates); + *avail_capacity = *private_capacity; + /* This reflects *unused* private capacity. */ + *private_capacity = AMOUNT_MSAT(0); } return candidates; diff --git a/lightningd/routehint.h b/lightningd/routehint.h index 8eb8476ec99f..27eadca688e2 100644 --- a/lightningd/routehint.h +++ b/lightningd/routehint.h @@ -20,21 +20,26 @@ struct routehint_candidate { * routehint_candidates - get possible incoming channels for routehinting. * @ctx: tal context to allocate return off * @ld: lightningd - * @incoming_channels_reply: reply from gossipd get_incoming_channels - * @expose_all_private: consider private channels too (otherwise iff no public) + * @buf, @toks: output of listincoming command + * @expose_all_private: trinary. NULL=iff no public, true=always, false=never. * @hints: only consider these channels (if !expose_all_private). * @none_public: set to true if we used private channels because none were public. - * @deadends: set to true if we found a dead-end channel. - * @amount_offline: amount we didn't consider due to offline channels. + * @avail_capacity: total capacity of usable channels. + * @private_capacity: total capacity of unused private channels. + * @deadend_capacity: total capacity of "deadend" channels. + * @offline_capacity: total capacity of offline channels. */ struct routehint_candidate * routehint_candidates(const tal_t *ctx, struct lightningd *ld, - const u8 *incoming_channels_reply, - bool expose_all_private, + const char *buf, + const jsmntok_t *toks, + const bool *expose_all_private, const struct short_channel_id *hints, bool *none_public, - bool *deadends, - struct amount_msat *amount_offline); + struct amount_msat *avail_capacity, + struct amount_msat *private_capacity, + struct amount_msat *deadend_capacity, + struct amount_msat *offline_capacity); #endif /* LIGHTNING_LIGHTNINGD_ROUTEHINT_H */ diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 8a4fd854e092..34b7e7e56b7d 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -160,6 +160,12 @@ void connect_succeeded(struct lightningd *ld UNNEEDED, const struct peer *peer U bool incoming UNNEEDED, const struct wireaddr_internal *addr UNNEEDED) { fprintf(stderr, "connect_succeeded called!\n"); abort(); } +/* Generated stub for db_begin_transaction_ */ +void db_begin_transaction_(struct db *db UNNEEDED, const char *location UNNEEDED) +{ fprintf(stderr, "db_begin_transaction_ called!\n"); abort(); } +/* Generated stub for db_commit_transaction */ +void db_commit_transaction(struct db *db UNNEEDED) +{ fprintf(stderr, "db_commit_transaction called!\n"); abort(); } /* Generated stub for delay_then_reconnect */ void delay_then_reconnect(struct channel *channel UNNEEDED, u32 seconds_delay UNNEEDED, const struct wireaddr_internal *addrhint TAKES UNNEEDED) @@ -191,6 +197,10 @@ bool feature_negotiated(const struct feature_set *our_features UNNEEDED, /* Generated stub for feature_offered */ bool feature_offered(const u8 *features UNNEEDED, size_t f UNNEEDED) { fprintf(stderr, "feature_offered called!\n"); abort(); } +/* Generated stub for find_plugin_for_command */ +struct plugin *find_plugin_for_command(struct lightningd *ld UNNEEDED, + const char *cmd_name UNNEEDED) +{ fprintf(stderr, "find_plugin_for_command called!\n"); abort(); } /* Generated stub for fixup_htlcs_out */ void fixup_htlcs_out(struct lightningd *ld UNNEEDED) { fprintf(stderr, "fixup_htlcs_out called!\n"); abort(); } @@ -207,9 +217,6 @@ bool fromwire_channeld_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNE /* Generated stub for fromwire_connectd_peer_connected */ bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, bool *incoming UNNEEDED, struct per_peer_state **pps UNNEEDED, u8 **features UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_connected called!\n"); abort(); } -/* Generated stub for fromwire_gossipd_get_incoming_channels_reply */ -bool fromwire_gossipd_get_incoming_channels_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct route_info **public_route_info UNNEEDED, bool **public_deadends UNNEEDED, struct route_info **private_route_info UNNEEDED, bool **private_deadends UNNEEDED) -{ fprintf(stderr, "fromwire_gossipd_get_incoming_channels_reply called!\n"); abort(); } /* Generated stub for fromwire_hsmd_sign_bolt12_reply */ bool fromwire_hsmd_sign_bolt12_reply(const void *p UNNEEDED, struct bip340sig *sig UNNEEDED) { fprintf(stderr, "fromwire_hsmd_sign_bolt12_reply called!\n"); abort(); } @@ -389,6 +396,10 @@ enum address_parse_result json_to_address_scriptpubkey(const tal_t *ctx UNNEEDED const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, const u8 **scriptpubkey UNNEEDED) { fprintf(stderr, "json_to_address_scriptpubkey called!\n"); abort(); } +/* Generated stub for json_to_msat */ +bool json_to_msat(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct amount_msat *msat UNNEEDED) +{ fprintf(stderr, "json_to_msat called!\n"); abort(); } /* Generated stub for json_to_node_id */ bool json_to_node_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct node_id *id UNNEEDED) @@ -405,6 +416,21 @@ bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok bool json_tok_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct channel_id *cid UNNEEDED) { fprintf(stderr, "json_tok_channel_id called!\n"); abort(); } +/* Generated stub for jsonrpc_request_end */ +void jsonrpc_request_end(struct jsonrpc_request *request UNNEEDED) +{ fprintf(stderr, "jsonrpc_request_end called!\n"); abort(); } +/* Generated stub for jsonrpc_request_start_ */ +struct jsonrpc_request *jsonrpc_request_start_( + const tal_t *ctx UNNEEDED, const char *method UNNEEDED, struct log *log UNNEEDED, + void (*notify_cb)(const char *buffer UNNEEDED, + const jsmntok_t *idtok UNNEEDED, + const jsmntok_t *methodtok UNNEEDED, + const jsmntok_t *paramtoks UNNEEDED, + void *) UNNEEDED, + void (*response_cb)(const char *buffer UNNEEDED, const jsmntok_t *toks UNNEEDED, + const jsmntok_t *idtok UNNEEDED, void *) UNNEEDED, + void *response_cb_arg UNNEEDED) +{ fprintf(stderr, "jsonrpc_request_start_ called!\n"); abort(); } /* Generated stub for kill_uncommitted_channel */ void kill_uncommitted_channel(struct uncommitted_channel *uc UNNEEDED, const char *why UNNEEDED) @@ -601,6 +627,10 @@ void per_peer_state_set_fds(struct per_peer_state *pps UNNEEDED, bool plugin_hook_call_(struct lightningd *ld UNNEEDED, const struct plugin_hook *hook UNNEEDED, tal_t *cb_arg STEALS UNNEEDED) { fprintf(stderr, "plugin_hook_call_ called!\n"); abort(); } +/* Generated stub for plugin_request_send */ +void plugin_request_send(struct plugin *plugin UNNEEDED, + struct jsonrpc_request *req TAKES UNNEEDED) +{ fprintf(stderr, "plugin_request_send called!\n"); abort(); } /* Generated stub for subd_req_ */ void subd_req_(const tal_t *ctx UNNEEDED, struct subd *sd UNNEEDED, @@ -647,9 +677,6 @@ u8 *towire_errorfmt(const tal_t *ctx UNNEEDED, const struct channel_id *channel UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "towire_errorfmt called!\n"); abort(); } -/* Generated stub for towire_gossipd_get_incoming_channels */ -u8 *towire_gossipd_get_incoming_channels(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "towire_gossipd_get_incoming_channels called!\n"); abort(); } /* Generated stub for towire_hsmd_sign_bolt12 */ u8 *towire_hsmd_sign_bolt12(const tal_t *ctx UNNEEDED, const wirestring *messagename UNNEEDED, const wirestring *fieldname UNNEEDED, const struct sha256 *merkleroot UNNEEDED, const u8 *publictweak UNNEEDED) { fprintf(stderr, "towire_hsmd_sign_bolt12 called!\n"); abort(); } diff --git a/tests/test_invoices.py b/tests/test_invoices.py index 995f3c00f00b..33e91ae9bc76 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -160,9 +160,11 @@ def test_invoice_routeboost(node_factory, bitcoind): # Make invoice and pay it inv = l2.rpc.invoice(msatoshi=123456, label="inv1", description="?") # Check routeboost. + assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv assert 'warning_deadends' not in inv + assert 'warning_mpp' not in inv # Route array has single route with single element. r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes'])) assert r['pubkey'] == l1.info['id'] @@ -178,18 +180,22 @@ def test_invoice_routeboost(node_factory, bitcoind): # Due to reserve & fees, l1 doesn't have capacity to pay this. inv = l2.rpc.invoice(msatoshi=2 * (10**8) - 123456, label="inv2", description="?") # Check warning - assert 'warning_capacity' in inv or 'warning_mpp_capacity' in inv + assert 'warning_capacity' in inv + assert 'warning_private_unused' not in inv assert 'warning_offline' not in inv assert 'warning_deadends' not in inv + assert 'warning_mpp' not in inv l1.rpc.disconnect(l2.info['id'], True) wait_for(lambda: not only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['connected']) inv = l2.rpc.invoice(123456, label="inv3", description="?") # Check warning. + assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_deadends' not in inv assert 'warning_offline' in inv + assert 'warning_mpp' not in inv # Close l0, l2 will not use l1 at all. l0.rpc.close(l1.info['id']) @@ -201,8 +207,10 @@ def test_invoice_routeboost(node_factory, bitcoind): inv = l2.rpc.invoice(123456, label="inv4", description="?") # Check warning. assert 'warning_deadends' in inv + assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv + assert 'warning_mpp' not in inv @pytest.mark.developer("gossip without DEVELOPER=1 is slow") @@ -226,9 +234,11 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # Since there's only one route, it will reluctantly hint that even # though it's private inv = l2.rpc.invoice(msatoshi=123456, label="inv0", description="?") + assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv assert 'warning_deadends' not in inv + assert 'warning_mpp' not in inv # Route array has single route with single element. r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes'])) assert r['pubkey'] == l1.info['id'] @@ -239,16 +249,20 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # If we explicitly say not to, it won't expose. inv = l2.rpc.invoice(msatoshi=123456, label="inv1", description="?", exposeprivatechannels=False) - assert 'warning_capacity' in inv or 'warning_mpp_capacity' in inv + assert 'warning_private_unused' in inv + assert 'warning_capacity' not in inv assert 'warning_offline' not in inv assert 'warning_deadends' not in inv + assert 'warning_mpp' not in inv assert 'routes' not in l1.rpc.decodepay(inv['bolt11']) # If we ask for it, we get it. inv = l2.rpc.invoice(msatoshi=123456, label="inv1a", description="?", exposeprivatechannels=scid) + assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv assert 'warning_deadends' not in inv + assert 'warning_mpp' not in inv # Route array has single route with single element. r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes'])) assert r['pubkey'] == l1.info['id'] @@ -259,9 +273,11 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # Similarly if we ask for an array. inv = l2.rpc.invoice(msatoshi=123456, label="inv1b", description="?", exposeprivatechannels=[scid]) + assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv assert 'warning_deadends' not in inv + assert 'warning_mpp' not in inv # Route array has single route with single element. r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes'])) assert r['pubkey'] == l1.info['id'] @@ -280,15 +296,20 @@ def test_invoice_routeboost_private(node_factory, bitcoind): wait_for(lambda: [c['public'] for c in l2.rpc.listchannels(scid2)['channels']] == [True, True]) inv = l2.rpc.invoice(msatoshi=10**7, label="inv2", description="?") + print(inv) assert 'warning_deadends' in inv + assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv + assert 'warning_mpp' not in inv # Unless we tell it to include it. inv = l2.rpc.invoice(msatoshi=10**7, label="inv3", description="?", exposeprivatechannels=True) + assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv assert 'warning_deadends' not in inv + assert 'warning_mpp' not in inv # Route array has single route with single element. r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes'])) assert r['pubkey'] == l1.info['id'] @@ -298,9 +319,11 @@ def test_invoice_routeboost_private(node_factory, bitcoind): assert r['cltv_expiry_delta'] == 6 inv = l2.rpc.invoice(msatoshi=10**7, label="inv4", description="?", exposeprivatechannels=scid) + assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv assert 'warning_deadends' not in inv + assert 'warning_mpp' not in inv # Route array has single route with single element. r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes'])) assert r['pubkey'] == l1.info['id'] @@ -311,15 +334,19 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # Ask it explicitly to use a channel it can't (insufficient capacity) inv = l2.rpc.invoice(msatoshi=(10**5) * 1000 + 1, label="inv5", description="?", exposeprivatechannels=scid2) + assert 'warning_private_unused' not in inv assert 'warning_deadends' not in inv - assert 'warning_capacity' in inv or 'warning_mpp_capacity' in inv + assert 'warning_capacity' in inv assert 'warning_offline' not in inv + assert 'warning_mpp' not in inv # Give it two options and it will pick one with suff capacity. inv = l2.rpc.invoice(msatoshi=(10**5) * 1000 + 1, label="inv6", description="?", exposeprivatechannels=[scid2, scid]) + assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv assert 'warning_deadends' not in inv + assert 'warning_mpp' not in inv # Route array has single route with single element. r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes'])) assert r['pubkey'] == l1.info['id'] @@ -335,9 +362,11 @@ def test_invoice_routeboost_private(node_factory, bitcoind): wait_for(lambda: l2.rpc.listchannels(scid_dummy)['channels'] == []) inv = l2.rpc.invoice(msatoshi=123456, label="inv7", description="?", exposeprivatechannels=scid) + assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv assert 'warning_deadends' not in inv + assert 'warning_mpp' not in inv # Route array has single route with single element. r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes'])) assert r['pubkey'] == l1.info['id'] From a7fd01679f82fa1f35c0ef45a2e6c2e9ae7f164d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 15 Jun 2021 06:37:38 +0930 Subject: [PATCH 13/17] signmessage: use listnodes instead of gossipd_getnodes_request. Signed-off-by: Rusty Russell --- lightningd/signmessage.c | 44 ++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/lightningd/signmessage.c b/lightningd/signmessage.c index 92d3c91f9f5b..ae9047208ba0 100644 --- a/lightningd/signmessage.c +++ b/lightningd/signmessage.c @@ -129,24 +129,22 @@ struct command_and_node { struct node_id id; }; -/* Gossipd tells us if it's a known node by returning details. */ -static void getnode_reply(struct subd *gossip UNUSED, const u8 *reply, - const int *fds UNUSED, - struct command_and_node *can) +/* topology tells us if it's a known node by returning details. */ +static void listnodes_done(const char *buffer, + const jsmntok_t *toks, + const jsmntok_t *idtok UNUSED, + struct command_and_node *can) { - struct gossip_getnodes_entry **nodes; struct json_stream *response; + const jsmntok_t *t; - if (!fromwire_gossipd_getnodes_reply(reply, reply, &nodes)) { - log_broken(can->cmd->ld->log, - "Malformed gossip_getnodes response %s", - tal_hex(tmpctx, reply)); - nodes = NULL; - } + t = json_get_member(buffer, toks, "result"); + if (t) + t = json_get_member(buffer, t, "nodes"); response = json_stream_success(can->cmd); json_add_node_id(response, "pubkey", &can->id); - json_add_bool(response, "verified", tal_count(nodes) > 0); + json_add_bool(response, "verified", t && t->size == 1); was_pending(command_success(can->cmd, response)); } @@ -207,19 +205,31 @@ static struct command_result *json_checkmessage(struct command *cmd, * make two (different) signed messages with the same recovered key * unless you know the secret key */ if (!pubkey) { - u8 *req; + struct jsonrpc_request *req; + struct plugin *plugin; struct command_and_node *can = tal(cmd, struct command_and_node); node_id_from_pubkey(&can->id, &reckey); can->cmd = cmd; - req = towire_gossipd_getnodes_request(cmd, &can->id); - subd_req(cmd, cmd->ld->gossip, req, -1, 0, getnode_reply, can); - return command_still_pending(cmd); + req = jsonrpc_request_start(cmd, "listnodes", + cmd->ld->log, + NULL, listnodes_done, + can); + json_add_node_id(req->stream, "id", &can->id); + jsonrpc_request_end(req); + + /* Only works if we have listnodes! */ + plugin = find_plugin_for_command(cmd->ld, "listnodes"); + if (plugin) { + plugin_request_send(plugin, req); + return command_still_pending(cmd); + } } response = json_stream_success(cmd); json_add_pubkey(response, "pubkey", &reckey); - json_add_bool(response, "verified", pubkey_eq(pubkey, &reckey)); + json_add_bool(response, "verified", + pubkey && pubkey_eq(pubkey, &reckey)); return command_success(cmd, response); } From 4b8fbf2a67281f0ab28a53584d081cd093510d3c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 15 Jun 2021 06:37:39 +0930 Subject: [PATCH 14/17] pyln-client: revert old/new comparisons for getroute, listchannels, listnodes. Signed-off-by: Rusty Russell --- contrib/pyln-client/pyln/client/lightning.py | 106 +------------------ tests/test_closing.py | 3 - 2 files changed, 3 insertions(+), 106 deletions(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index c287b91cbaf1..f4e53f31935d 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -816,31 +816,7 @@ def getroute(self, node_id, msatoshi, riskfactor, cltv=9, fromid=None, fuzzperce "exclude": exclude, "maxhops": maxhops } - - # This is a hack to make sure old and new routines return the same result. - e1 = None - ret1 = None - err = None - try: - ret1 = self.call("getroute", payload) - except RpcError as e: - err = e - e1 = e.error - e2 = None - ret2 = None - try: - ret2 = self.call("getrouteold", payload) - except RpcError as e: - e2 = e.error - - print("new getroute: {} exception {}".format(ret1, e1)) - print("old getroute: {} exception {}".format(ret2, e2)) - assert ret1 == ret2 - assert e1 == e2 - - if err is not None: - raise err - return ret1 + return self.call("getroute", payload) def help(self, command=None): """ @@ -876,50 +852,7 @@ def listchannels(self, short_channel_id=None, source=None): "short_channel_id": short_channel_id, "source": source } - - - # This is a hack to make sure old and new routines return the same result. - e1 = None - ret1 = None - err = None - try: - ret1 = self.call("listchannels", payload) - except RpcError as e: - err = e - e1 = e.error - e2 = None - ret2 = None - try: - ret2 = self.call("listchannelsold", payload) - except RpcError as e: - e2 = e.error - - print("new listchannels: {} exception {}".format(ret1, e1)) - print("old listchannels: {} exception {}".format(ret2, e2)) - # gossipd only marks a channel enabled again when channeld says to; - # our new code just requires a reconnection. So check that - # separately - if ret1: - assert len(ret1['channels']) == len(ret2['channels']) - for i in range(len(ret1['channels'])): - if ret1['channels'][i]['active'] and not ret2['channels'][i]['active']: - ret2['channels'][i]['active'] = True - - def chan_key(c): - return c['source'] + c['destination'] - - # Order is arbitrary - if ret1: - ret1['channels'].sort(key=chan_key) - if ret2: - ret2['channels'].sort(key=chan_key) - - assert ret1 == ret2 - assert e1 == e2 - - if err is not None: - raise err - return ret1 + return self.call("listchannels", payload) def listconfigs(self, config=None): """List this node's config. @@ -979,40 +912,7 @@ def listnodes(self, node_id=None): payload = { "id": node_id } - - # This is a hack to make sure old and new routines return the same result. - e1 = None - ret1 = None - err = None - try: - ret1 = self.call("listnodes", payload) - except RpcError as e: - err = e - e1 = e.error - e2 = None - ret2 = None - try: - ret2 = self.call("listnodesold", payload) - except RpcError as e: - e2 = e.error - - print("new listnodes: {} exception {}".format(ret1, e1)) - print("old listnodes: {} exception {}".format(ret2, e2)) - - # Order is arbitrary - def node_key(n): - return n['nodeid'] - - if ret1: - ret1['nodes'].sort(key=node_key) - if ret2: - ret2['nodes'].sort(key=node_key) - assert ret1 == ret2 - assert e1 == e2 - - if err is not None: - raise err - return ret1 + return self.call("listnodes", payload) def listpays(self, bolt11=None, payment_hash=None): """ diff --git a/tests/test_closing.py b/tests/test_closing.py index 6702278ff9e8..55d05d447dac 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -777,9 +777,6 @@ def test_penalty_htlc_tx_fulfill(node_factory, bitcoind, chainparams): amt = 10**8 // 2 sticky_inv = l1.rpc.invoice(amt, '2', 'sticky') route = l4.rpc.getroute(l1.info['id'], amt, 1)['route'] - route2 = l4.rpc.getrouteold(l1.info['id'], amt, 1)['route'] - - assert(route == route2) l4.rpc.sendpay(route, sticky_inv['payment_hash']) l1.daemon.wait_for_log('dev_disconnect: -WIRE_UPDATE_FULFILL_HTLC') From ec8696387df9085750efa505a8a02be2d9c23b19 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 15 Jun 2021 06:37:39 +0930 Subject: [PATCH 15/17] gossipd/test: remove routing tests. We're about to remove routing from gossipd. Signed-off-by: Rusty Russell --- gossipd/test/run-bench-find_route.c | 279 ------------------------- gossipd/test/run-find_route-specific.c | 256 ----------------------- gossipd/test/run-find_route.c | 276 ------------------------ gossipd/test/run-overlong.c | 196 ----------------- 4 files changed, 1007 deletions(-) delete mode 100644 gossipd/test/run-bench-find_route.c delete mode 100644 gossipd/test/run-find_route-specific.c delete mode 100644 gossipd/test/run-find_route.c delete mode 100644 gossipd/test/run-overlong.c diff --git a/gossipd/test/run-bench-find_route.c b/gossipd/test/run-bench-find_route.c deleted file mode 100644 index 80b1533c9c57..000000000000 --- a/gossipd/test/run-bench-find_route.c +++ /dev/null @@ -1,279 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../routing.c" -#include "../gossip_store.c" - -void status_fmt(enum log_level level UNUSED, - const struct node_id *node_id, - const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vprintf(fmt, ap); - printf("\n"); - va_end(ap); -} - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for cupdate_different */ -bool cupdate_different(struct gossip_store *gs UNNEEDED, - const struct half_chan *hc UNNEEDED, - const u8 *cupdate UNNEEDED) -{ fprintf(stderr, "cupdate_different called!\n"); abort(); } -/* Generated stub for fmt_wireaddr_without_port */ -char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) -{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr_array */ -struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx UNNEEDED, const u8 *ser UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr_array called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } -/* Generated stub for json_object_end */ -void json_object_end(struct json_stream *js UNNEEDED) -{ fprintf(stderr, "json_object_end called!\n"); abort(); } -/* Generated stub for json_object_start */ -void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) -{ fprintf(stderr, "json_object_start called!\n"); abort(); } -/* Generated stub for memleak_add_helper_ */ -void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED, - const tal_t *)){ } -/* Generated stub for nannounce_different */ -bool nannounce_different(struct gossip_store *gs UNNEEDED, - const struct node *node UNNEEDED, - const u8 *nannounce UNNEEDED) -{ fprintf(stderr, "nannounce_different called!\n"); abort(); } -/* Generated stub for notleak_ */ -void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) -{ fprintf(stderr, "notleak_ called!\n"); abort(); } -/* Generated stub for peer_supplied_good_gossip */ -void peer_supplied_good_gossip(struct peer *peer UNNEEDED, size_t amount UNNEEDED) -{ fprintf(stderr, "peer_supplied_good_gossip called!\n"); abort(); } -/* Generated stub for private_channel_announcement */ -const u8 *private_channel_announcement(const tal_t *ctx UNNEEDED, - const struct short_channel_id *scid UNNEEDED, - const struct node_id *local_node_id UNNEEDED, - const struct node_id *remote_node_id UNNEEDED, - const u8 *features UNNEEDED) -{ fprintf(stderr, "private_channel_announcement called!\n"); abort(); } -/* Generated stub for sanitize_error */ -char *sanitize_error(const tal_t *ctx UNNEEDED, const u8 *errmsg UNNEEDED, - struct channel_id *channel_id UNNEEDED) -{ fprintf(stderr, "sanitize_error called!\n"); abort(); } -/* Generated stub for status_failed */ -void status_failed(enum status_failreason code UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "status_failed called!\n"); abort(); } -/* Generated stub for towire_warningfmt */ -u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, - const struct channel_id *channel UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "towire_warningfmt called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -#if DEVELOPER -/* Generated stub for memleak_remove_htable */ -void memleak_remove_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) -{ fprintf(stderr, "memleak_remove_htable called!\n"); abort(); } -/* Generated stub for memleak_remove_intmap_ */ -void memleak_remove_intmap_(struct htable *memtable UNNEEDED, const struct intmap *m UNNEEDED) -{ fprintf(stderr, "memleak_remove_intmap_ called!\n"); abort(); } -#endif - -/* NOOP for new_reltimer_ */ -struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, - const tal_t *ctx UNNEEDED, - struct timerel expire UNNEEDED, - void (*cb)(void *) UNNEEDED, void *arg UNNEEDED) -{ - return NULL; -} - -/* Updates existing route if required. */ -static void add_connection(struct routing_state *rstate, - const struct node_id *nodes, - u32 from, u32 to, - u32 base_fee, s32 proportional_fee, - u32 delay) -{ - struct short_channel_id scid; - struct half_chan *c; - struct chan *chan; - int idx = node_id_idx(&nodes[from], &nodes[to]); - - /* Encode src and dst in scid. */ - memcpy((char *)&scid + idx * sizeof(from), &from, sizeof(from)); - memcpy((char *)&scid + (!idx) * sizeof(to), &to, sizeof(to)); - - chan = get_channel(rstate, &scid); - if (!chan) { - chan = new_chan(rstate, &scid, &nodes[from], &nodes[to], - AMOUNT_SAT(1000000), NULL); - } - - c = &chan->half[idx]; - c->base_fee = base_fee; - c->proportional_fee = proportional_fee; - c->delay = delay; - c->channel_flags = node_id_idx(&nodes[from], &nodes[to]); - /* This must be non-zero, otherwise we consider it disabled! */ - c->bcast.index = 1; - c->htlc_maximum = AMOUNT_MSAT(-1ULL); - c->htlc_minimum = AMOUNT_MSAT(0); -} - -static struct node_id nodeid(size_t n) -{ - struct node_id id; - struct pubkey k; - struct secret s; - - memset(&s, 0xFF, sizeof(s)); - memcpy(&s, &n, sizeof(n)); - pubkey_from_secret(&s, &k); - node_id_from_pubkey(&id, &k); - return id; -} - -static void populate_random_node(struct routing_state *rstate, - const struct node_id *nodes, - u32 n) -{ - /* Create 2 random channels. */ - if (n < 1) - return; - - for (size_t i = 0; i < 2; i++) { - u32 randnode = pseudorand(n); - - add_connection(rstate, nodes, n, randnode, - pseudorand(1000), - pseudorand(1000), - pseudorand(144)); - add_connection(rstate, nodes, randnode, n, - pseudorand(1000), - pseudorand(1000), - pseudorand(144)); - } -} - -static void run(const char *name) -{ - int status; - - switch (fork()) { - case 0: - execlp(name, name, NULL); - exit(127); - case -1: - err(1, "forking %s", name); - default: - wait(&status); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - errx(1, "%s failed", name); - } -} - -int main(int argc, char *argv[]) -{ - struct routing_state *rstate; - size_t num_nodes = 100, num_runs = 1; - struct timemono start, end; - size_t route_lengths[ROUTING_MAX_HOPS+1]; - struct node_id me; - struct node_id *nodes; - bool perfme = false; - const double riskfactor = 0.01 / BLOCKS_PER_YEAR / 10000; - struct siphash_seed base_seed; - - common_setup(argv[0]); - - me = nodeid(0); - rstate = new_routing_state(tmpctx, &me, NULL, NULL, NULL, - false, false); - opt_register_noarg("--perfme", opt_set_bool, &perfme, - "Run perfme-start and perfme-stop around benchmark"); - - opt_parse(&argc, argv, opt_log_stderr_exit); - - if (argc > 1) - num_nodes = atoi(argv[1]); - if (argc > 2) - num_runs = atoi(argv[2]); - if (argc > 3) - opt_usage_and_exit("[num_nodes [num_runs]]"); - - printf("Creating nodes...\n"); - nodes = tal_arr(rstate, struct node_id, num_nodes); - for (size_t i = 0; i < num_nodes; i++) - nodes[i] = nodeid(i); - - printf("Populating nodes...\n"); - memset(&base_seed, 0, sizeof(base_seed)); - for (size_t i = 0; i < num_nodes; i++) - populate_random_node(rstate, nodes, i); - - if (perfme) - run("perfme-start"); - - printf("Starting...\n"); - memset(route_lengths, 0, sizeof(route_lengths)); - start = time_mono(); - for (size_t i = 0; i < num_runs; i++) { - const struct node_id *from = &nodes[pseudorand(num_nodes)]; - const struct node_id *to = &nodes[pseudorand(num_nodes)]; - struct amount_msat fee; - struct chan **route; - size_t num_hops; - - route = find_route(tmpctx, rstate, from, to, - (struct amount_msat){pseudorand(100000)}, - riskfactor, - 0.75, &base_seed, - ROUTING_MAX_HOPS, - &fee); - num_hops = tal_count(route); - assert(num_hops < ARRAY_SIZE(route_lengths)); - route_lengths[num_hops]++; - tal_free(route); - } - end = time_mono(); - - if (perfme) - run("perfme-stop"); - - printf("%zu (%zu succeeded) routes in %zu nodes in %"PRIu64" msec (%"PRIu64" nanoseconds per route)\n", - num_runs, num_runs - route_lengths[0], num_nodes, - time_to_msec(timemono_between(end, start)), - time_to_nsec(time_divide(timemono_between(end, start), num_runs))); - for (size_t i = 0; i < ARRAY_SIZE(route_lengths); i++) - if (route_lengths[i]) - printf(" Length %zu: %zu\n", i, route_lengths[i]); - - common_shutdown(); - opt_free_table(); - return 0; -} diff --git a/gossipd/test/run-find_route-specific.c b/gossipd/test/run-find_route-specific.c deleted file mode 100644 index 8431e25a5590..000000000000 --- a/gossipd/test/run-find_route-specific.c +++ /dev/null @@ -1,256 +0,0 @@ -/* We can't seem to route the following: - * - * Expect route 03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf -> 0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae -> 02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06 - * - * getchannels: - * {'channels': [{'active': True, 'short_id': '6990x2x1/1', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 1, 'destination': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'source': '02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06', 'last_update': 1504064344}, {'active': True, 'short_id': '6989x2x1/0', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 0, 'destination': '03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf', 'source': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'last_update': 1504064344}, {'active': True, 'short_id': '6990x2x1/0', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 0, 'destination': '02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06', 'source': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'last_update': 1504064344}, {'active': True, 'short_id': '6989x2x1/1', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 1, 'destination': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'source': '03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf', 'last_update': 1504064344}]} - */ -#include -#include -#include - -#include -#define status_fmt(level, node_id, fmt, ...) \ - do { (void)node_id; printf((fmt) ,##__VA_ARGS__); printf("\n"); } while(0) - -#include "../routing.c" -#include "../gossip_store.c" - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for cupdate_different */ -bool cupdate_different(struct gossip_store *gs UNNEEDED, - const struct half_chan *hc UNNEEDED, - const u8 *cupdate UNNEEDED) -{ fprintf(stderr, "cupdate_different called!\n"); abort(); } -/* Generated stub for fmt_wireaddr_without_port */ -char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) -{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr_array */ -struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx UNNEEDED, const u8 *ser UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr_array called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } -/* Generated stub for json_object_end */ -void json_object_end(struct json_stream *js UNNEEDED) -{ fprintf(stderr, "json_object_end called!\n"); abort(); } -/* Generated stub for json_object_start */ -void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) -{ fprintf(stderr, "json_object_start called!\n"); abort(); } -/* Generated stub for memleak_add_helper_ */ -void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED, - const tal_t *)){ } -/* Generated stub for nannounce_different */ -bool nannounce_different(struct gossip_store *gs UNNEEDED, - const struct node *node UNNEEDED, - const u8 *nannounce UNNEEDED) -{ fprintf(stderr, "nannounce_different called!\n"); abort(); } -/* Generated stub for notleak_ */ -void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) -{ fprintf(stderr, "notleak_ called!\n"); abort(); } -/* Generated stub for peer_supplied_good_gossip */ -void peer_supplied_good_gossip(struct peer *peer UNNEEDED, size_t amount UNNEEDED) -{ fprintf(stderr, "peer_supplied_good_gossip called!\n"); abort(); } -/* Generated stub for private_channel_announcement */ -const u8 *private_channel_announcement(const tal_t *ctx UNNEEDED, - const struct short_channel_id *scid UNNEEDED, - const struct node_id *local_node_id UNNEEDED, - const struct node_id *remote_node_id UNNEEDED, - const u8 *features UNNEEDED) -{ fprintf(stderr, "private_channel_announcement called!\n"); abort(); } -/* Generated stub for sanitize_error */ -char *sanitize_error(const tal_t *ctx UNNEEDED, const u8 *errmsg UNNEEDED, - struct channel_id *channel_id UNNEEDED) -{ fprintf(stderr, "sanitize_error called!\n"); abort(); } -/* Generated stub for status_failed */ -void status_failed(enum status_failreason code UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "status_failed called!\n"); abort(); } -/* Generated stub for towire_warningfmt */ -u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, - const struct channel_id *channel UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "towire_warningfmt called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -#if DEVELOPER -/* Generated stub for memleak_remove_htable */ -void memleak_remove_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) -{ fprintf(stderr, "memleak_remove_htable called!\n"); abort(); } -/* Generated stub for memleak_remove_intmap_ */ -void memleak_remove_intmap_(struct htable *memtable UNNEEDED, const struct intmap *m UNNEEDED) -{ fprintf(stderr, "memleak_remove_intmap_ called!\n"); abort(); } -#endif - -/* NOOP for new_reltimer_ */ -struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, - const tal_t *ctx UNNEEDED, - struct timerel expire UNNEEDED, - void (*cb)(void *) UNNEEDED, void *arg UNNEEDED) -{ - return NULL; -} - -const void *trc; - -static struct half_chan * -get_or_make_connection(struct routing_state *rstate, - const struct node_id *from_id, - const struct node_id *to_id, - const char *shortid, - struct amount_sat satoshis) -{ - struct short_channel_id scid; - struct chan *chan; - const int idx = node_id_idx(from_id, to_id); - - if (!short_channel_id_from_str(shortid, strlen(shortid), &scid)) - abort(); - chan = get_channel(rstate, &scid); - if (!chan) - chan = new_chan(rstate, &scid, from_id, to_id, satoshis, NULL); - - /* Make sure it's seen as initialized (index non-zero). */ - chan->half[idx].bcast.index = 1; - chan->half[idx].htlc_minimum = AMOUNT_MSAT(0); - if (!amount_sat_to_msat(&chan->half[idx].htlc_maximum, satoshis)) - abort(); - - return &chan->half[idx]; -} - -static bool channel_is_between(const struct chan *chan, - const struct node_id *a, const struct node_id *b) -{ - if (node_id_eq(&chan->nodes[0]->id, a) - && node_id_eq(&chan->nodes[1]->id, b)) - return true; - - if (node_id_eq(&chan->nodes[0]->id, b) - && node_id_eq(&chan->nodes[1]->id, a)) - return true; - - return false; -} - -int main(int argc, char *argv[]) -{ - struct half_chan *nc; - struct routing_state *rstate; - struct node_id a, b, c, d; - struct amount_msat fee; - struct chan **route; - const double riskfactor = 1.0 / BLOCKS_PER_YEAR / 10000; - - common_setup(argv[0]); - - node_id_from_hexstr("03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf", - strlen("03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf"), - &a); - node_id_from_hexstr("0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae", - strlen("0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae"), - &b); - node_id_from_hexstr("02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06", - strlen("02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06"), - &c); - node_id_from_hexstr("02cca6c5c966fcf61d121e3a70e03a1cd9eeeea024b26ea666ce974d43b242e636", - strlen("02cca6c5c966fcf61d121e3a70e03a1cd9eeeea024b26ea666ce974d43b242e636"), - &d); - - rstate = new_routing_state(tmpctx, &a, NULL, NULL, NULL, false, false); - - /* [{'active': True, 'short_id': '6990:2:1/1', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 1, 'destination': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'source': '02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06', 'last_update': 1504064344}, */ - - nc = get_or_make_connection(rstate, &c, &b, "6990x2x1", AMOUNT_SAT(1000)); - nc->base_fee = 0; - nc->proportional_fee = 10; - nc->delay = 5; - nc->channel_flags = 1; - nc->message_flags = 0; - nc->bcast.timestamp = 1504064344; - - /* {'active': True, 'short_id': '6989:2:1/0', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 0, 'destination': '03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf', 'source': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'last_update': 1504064344}, */ - nc = get_or_make_connection(rstate, &b, &a, "6989x2x1", AMOUNT_SAT(1000)); - nc->base_fee = 0; - nc->proportional_fee = 10; - nc->delay = 5; - nc->channel_flags = 0; - nc->message_flags = 0; - nc->bcast.timestamp = 1504064344; - - /* {'active': True, 'short_id': '6990:2:1/0', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 0, 'destination': '02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06', 'source': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'last_update': 1504064344}, */ - nc = get_or_make_connection(rstate, &b, &c, "6990x2x1", AMOUNT_SAT(1000)); - nc->base_fee = 0; - nc->proportional_fee = 10; - nc->delay = 5; - nc->channel_flags = 0; - nc->message_flags = 0; - nc->bcast.timestamp = 1504064344; - nc->htlc_minimum = AMOUNT_MSAT(100); - - /* {'active': True, 'short_id': '6989:2:1/1', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 1, 'destination': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'source': '03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf', 'last_update': 1504064344}]} */ - nc = get_or_make_connection(rstate, &a, &b, "6989x2x1", AMOUNT_SAT(1000)); - nc->base_fee = 0; - nc->proportional_fee = 10; - nc->delay = 5; - nc->channel_flags = 1; - nc->message_flags = 0; - nc->bcast.timestamp = 1504064344; - - route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(100000), riskfactor, 0.0, NULL, - ROUTING_MAX_HOPS, &fee); - assert(route); - assert(tal_count(route) == 2); - assert(channel_is_between(route[0], &a, &b)); - assert(channel_is_between(route[1], &b, &c)); - - - /* We should not be able to find a route that exceeds our own capacity */ - route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(1000001), riskfactor, 0.0, NULL, - ROUTING_MAX_HOPS, &fee); - assert(!route); - - /* Now test with a query that exceeds the channel capacity after adding - * some fees */ - route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(999999), riskfactor, 0.0, NULL, - ROUTING_MAX_HOPS, &fee); - assert(!route); - - /* This should fail to return a route because it is smaller than these - * htlc_minimum_msat on the last channel. */ - route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(1), riskfactor, 0.0, NULL, - ROUTING_MAX_HOPS, &fee); - assert(!route); - - /* {'active': True, 'short_id': '6990:2:1/0', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 1, 'htlc_maximum_msat': 500000, 'htlc_minimum_msat': 100, 'channel_flags': 0, 'destination': '02cca6c5c966fcf61d121e3a70e03a1cd9eeeea024b26ea666ce974d43b242e636', 'source': '03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf', 'last_update': 1504064344}, */ - nc = get_or_make_connection(rstate, &a, &d, "6991x2x1", AMOUNT_SAT(1000)); - nc->base_fee = 0; - nc->proportional_fee = 0; - nc->delay = 5; - nc->channel_flags = 0; - nc->message_flags = 1; - nc->bcast.timestamp = 1504064344; - nc->htlc_minimum = AMOUNT_MSAT(100); - nc->htlc_maximum = AMOUNT_MSAT(500000); /* half capacity */ - - /* This should route correctly at the max_msat level */ - route = find_route(tmpctx, rstate, &a, &d, AMOUNT_MSAT(500000), riskfactor, 0.0, NULL, - ROUTING_MAX_HOPS, &fee); - assert(route); - - /* This should fail to return a route because it's larger than the - * htlc_maximum_msat on the last channel. */ - route = find_route(tmpctx, rstate, &a, &d, AMOUNT_MSAT(500001), riskfactor, 0.0, NULL, - ROUTING_MAX_HOPS, &fee); - assert(!route); - - common_shutdown(); - return 0; -} diff --git a/gossipd/test/run-find_route.c b/gossipd/test/run-find_route.c deleted file mode 100644 index 1e63f6700adf..000000000000 --- a/gossipd/test/run-find_route.c +++ /dev/null @@ -1,276 +0,0 @@ -#include "../routing.c" -#include "../gossip_store.c" -#include -#include -#include - -void status_fmt(enum log_level level UNUSED, - const struct node_id *node_id, - const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vprintf(fmt, ap); - printf("\n"); - va_end(ap); -} - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for cupdate_different */ -bool cupdate_different(struct gossip_store *gs UNNEEDED, - const struct half_chan *hc UNNEEDED, - const u8 *cupdate UNNEEDED) -{ fprintf(stderr, "cupdate_different called!\n"); abort(); } -/* Generated stub for fmt_wireaddr_without_port */ -char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) -{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr_array */ -struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx UNNEEDED, const u8 *ser UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr_array called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } -/* Generated stub for json_object_end */ -void json_object_end(struct json_stream *js UNNEEDED) -{ fprintf(stderr, "json_object_end called!\n"); abort(); } -/* Generated stub for json_object_start */ -void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) -{ fprintf(stderr, "json_object_start called!\n"); abort(); } -/* Generated stub for memleak_add_helper_ */ -void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED, - const tal_t *)){ } -/* Generated stub for nannounce_different */ -bool nannounce_different(struct gossip_store *gs UNNEEDED, - const struct node *node UNNEEDED, - const u8 *nannounce UNNEEDED) -{ fprintf(stderr, "nannounce_different called!\n"); abort(); } -/* Generated stub for notleak_ */ -void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) -{ fprintf(stderr, "notleak_ called!\n"); abort(); } -/* Generated stub for peer_supplied_good_gossip */ -void peer_supplied_good_gossip(struct peer *peer UNNEEDED, size_t amount UNNEEDED) -{ fprintf(stderr, "peer_supplied_good_gossip called!\n"); abort(); } -/* Generated stub for private_channel_announcement */ -const u8 *private_channel_announcement(const tal_t *ctx UNNEEDED, - const struct short_channel_id *scid UNNEEDED, - const struct node_id *local_node_id UNNEEDED, - const struct node_id *remote_node_id UNNEEDED, - const u8 *features UNNEEDED) -{ fprintf(stderr, "private_channel_announcement called!\n"); abort(); } -/* Generated stub for sanitize_error */ -char *sanitize_error(const tal_t *ctx UNNEEDED, const u8 *errmsg UNNEEDED, - struct channel_id *channel_id UNNEEDED) -{ fprintf(stderr, "sanitize_error called!\n"); abort(); } -/* Generated stub for status_failed */ -void status_failed(enum status_failreason code UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "status_failed called!\n"); abort(); } -/* Generated stub for towire_warningfmt */ -u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, - const struct channel_id *channel UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "towire_warningfmt called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -#if DEVELOPER -/* Generated stub for memleak_remove_htable */ -void memleak_remove_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) -{ fprintf(stderr, "memleak_remove_htable called!\n"); abort(); } -/* Generated stub for memleak_remove_intmap_ */ -void memleak_remove_intmap_(struct htable *memtable UNNEEDED, const struct intmap *m UNNEEDED) -{ fprintf(stderr, "memleak_remove_intmap_ called!\n"); abort(); } -#endif - -/* NOOP for new_reltimer_ */ -struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, - const tal_t *ctx UNNEEDED, - struct timerel expire UNNEEDED, - void (*cb)(void *) UNNEEDED, void *arg UNNEEDED) -{ - return NULL; -} - -static void node_id_from_privkey(const struct privkey *p, struct node_id *id) -{ - struct pubkey k; - pubkey_from_privkey(p, &k); - node_id_from_pubkey(id, &k); -} - -/* Updates existing route if required. */ -static void add_connection(struct routing_state *rstate, - const struct node_id *from, - const struct node_id *to, - u32 base_fee, s32 proportional_fee, - u32 delay) -{ - struct short_channel_id scid; - struct half_chan *c; - struct chan *chan; - struct amount_sat satoshis = AMOUNT_SAT(100000); - - /* Make a unique scid. */ - memcpy(&scid, from, sizeof(scid) / 2); - memcpy((char *)&scid + sizeof(scid) / 2, to, sizeof(scid) / 2); - - chan = get_channel(rstate, &scid); - if (!chan) - chan = new_chan(rstate, &scid, from, to, satoshis, NULL); - - c = &chan->half[node_id_idx(from, to)]; - /* Make sure it's seen as initialized (index non-zero). */ - c->bcast.index = 1; - c->base_fee = base_fee; - c->proportional_fee = proportional_fee; - c->delay = delay; - c->channel_flags = node_id_idx(from, to); - c->htlc_minimum = AMOUNT_MSAT(0); - c->htlc_maximum = AMOUNT_MSAT(100000 * 1000); -} - -/* Returns chan connecting from and to: *idx set to refer - * to connection with src=from, dst=to */ -static struct chan *find_channel(struct routing_state *rstate UNUSED, - const struct node *from, - const struct node *to, - int *idx) -{ - struct chan_map_iter i; - struct chan *c; - - *idx = node_id_idx(&from->id, &to->id); - - for (c = first_chan(to, &i); c; c = next_chan(to, &i)) { - if (c->nodes[*idx] == from) - return c; - } - return NULL; -} - -static struct half_chan *get_connection(struct routing_state *rstate, - const struct node_id *from_id, - const struct node_id *to_id) -{ - int idx; - struct node *from, *to; - struct chan *c; - - from = get_node(rstate, from_id); - to = get_node(rstate, to_id); - if (!from || ! to) - return NULL; - - c = find_channel(rstate, from, to, &idx); - if (!c) - return NULL; - return &c->half[idx]; -} - -static bool channel_is_between(const struct chan *chan, - const struct node_id *a, const struct node_id *b) -{ - if (node_id_eq(&chan->nodes[0]->id, a) - && node_id_eq(&chan->nodes[1]->id, b)) - return true; - - if (node_id_eq(&chan->nodes[0]->id, b) - && node_id_eq(&chan->nodes[1]->id, a)) - return true; - - return false; -} - -int main(int argc, char *argv[]) -{ - struct routing_state *rstate; - struct node_id a, b, c, d; - struct privkey tmp; - struct amount_msat fee; - struct chan **route; - const double riskfactor = 1.0 / BLOCKS_PER_YEAR / 10000; - - common_setup(argv[0]); - - memset(&tmp, 'a', sizeof(tmp)); - node_id_from_privkey(&tmp, &a); - rstate = new_routing_state(tmpctx, &a, NULL, NULL, NULL, false, false); - - new_node(rstate, &a); - - memset(&tmp, 'b', sizeof(tmp)); - node_id_from_privkey(&tmp, &b); - new_node(rstate, &b); - - /* A<->B */ - add_connection(rstate, &a, &b, 1, 1, 1); - - route = find_route(tmpctx, rstate, &a, &b, AMOUNT_MSAT(1000), riskfactor, 0.0, NULL, - ROUTING_MAX_HOPS, &fee); - assert(route); - assert(tal_count(route) == 1); - assert(amount_msat_eq(fee, AMOUNT_MSAT(0))); - - /* A<->B<->C */ - memset(&tmp, 'c', sizeof(tmp)); - node_id_from_privkey(&tmp, &c); - new_node(rstate, &c); - - status_debug("A = %s", type_to_string(tmpctx, struct node_id, &a)); - status_debug("B = %s", type_to_string(tmpctx, struct node_id, &b)); - status_debug("C = %s", type_to_string(tmpctx, struct node_id, &c)); - add_connection(rstate, &b, &c, 1, 1, 1); - - route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(1000), riskfactor, 0.0, NULL, - ROUTING_MAX_HOPS, &fee); - assert(route); - assert(tal_count(route) == 2); - assert(amount_msat_eq(fee, AMOUNT_MSAT(1))); - - /* A<->D<->C: Lower base, higher percentage. */ - memset(&tmp, 'd', sizeof(tmp)); - node_id_from_privkey(&tmp, &d); - new_node(rstate, &d); - status_debug("D = %s", type_to_string(tmpctx, struct node_id, &d)); - - add_connection(rstate, &a, &d, 0, 2, 1); - add_connection(rstate, &d, &c, 0, 2, 1); - - /* Will go via D for small amounts. */ - route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(1000), riskfactor, 0.0, NULL, - ROUTING_MAX_HOPS, &fee); - assert(route); - assert(tal_count(route) == 2); - assert(channel_is_between(route[0], &a, &d)); - assert(channel_is_between(route[1], &d, &c)); - assert(amount_msat_eq(fee, AMOUNT_MSAT(0))); - - /* Will go via B for large amounts. */ - route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(3000000), riskfactor, 0.0, NULL, - ROUTING_MAX_HOPS, &fee); - assert(route); - assert(tal_count(route) == 2); - assert(channel_is_between(route[0], &a, &b)); - assert(channel_is_between(route[1], &b, &c)); - assert(amount_msat_eq(fee, AMOUNT_MSAT(1 + 3))); - - /* Make B->C inactive, force it back via D */ - get_connection(rstate, &b, &c)->channel_flags |= ROUTING_FLAGS_DISABLED; - route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(3000000), riskfactor, 0.0, NULL, - ROUTING_MAX_HOPS, &fee); - assert(route); - assert(tal_count(route) == 2); - assert(channel_is_between(route[0], &a, &d)); - assert(channel_is_between(route[1], &d, &c)); - assert(amount_msat_eq(fee, AMOUNT_MSAT(0 + 6))); - - common_shutdown(); - return 0; -} diff --git a/gossipd/test/run-overlong.c b/gossipd/test/run-overlong.c deleted file mode 100644 index b643cd755df7..000000000000 --- a/gossipd/test/run-overlong.c +++ /dev/null @@ -1,196 +0,0 @@ -#include "../routing.c" -#include "../gossip_store.c" -#include -#include -#include - -void status_fmt(enum log_level level UNUSED, - const struct node_id *node_id, - const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vprintf(fmt, ap); - printf("\n"); - va_end(ap); -} - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for cupdate_different */ -bool cupdate_different(struct gossip_store *gs UNNEEDED, - const struct half_chan *hc UNNEEDED, - const u8 *cupdate UNNEEDED) -{ fprintf(stderr, "cupdate_different called!\n"); abort(); } -/* Generated stub for fmt_wireaddr_without_port */ -char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) -{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr_array */ -struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx UNNEEDED, const u8 *ser UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr_array called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } -/* Generated stub for json_object_end */ -void json_object_end(struct json_stream *js UNNEEDED) -{ fprintf(stderr, "json_object_end called!\n"); abort(); } -/* Generated stub for json_object_start */ -void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) -{ fprintf(stderr, "json_object_start called!\n"); abort(); } -/* Generated stub for memleak_add_helper_ */ -void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED, - const tal_t *)){ } -/* Generated stub for nannounce_different */ -bool nannounce_different(struct gossip_store *gs UNNEEDED, - const struct node *node UNNEEDED, - const u8 *nannounce UNNEEDED) -{ fprintf(stderr, "nannounce_different called!\n"); abort(); } -/* Generated stub for notleak_ */ -void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) -{ fprintf(stderr, "notleak_ called!\n"); abort(); } -/* Generated stub for peer_supplied_good_gossip */ -void peer_supplied_good_gossip(struct peer *peer UNNEEDED, size_t amount UNNEEDED) -{ fprintf(stderr, "peer_supplied_good_gossip called!\n"); abort(); } -/* Generated stub for private_channel_announcement */ -const u8 *private_channel_announcement(const tal_t *ctx UNNEEDED, - const struct short_channel_id *scid UNNEEDED, - const struct node_id *local_node_id UNNEEDED, - const struct node_id *remote_node_id UNNEEDED, - const u8 *features UNNEEDED) -{ fprintf(stderr, "private_channel_announcement called!\n"); abort(); } -/* Generated stub for sanitize_error */ -char *sanitize_error(const tal_t *ctx UNNEEDED, const u8 *errmsg UNNEEDED, - struct channel_id *channel_id UNNEEDED) -{ fprintf(stderr, "sanitize_error called!\n"); abort(); } -/* Generated stub for status_failed */ -void status_failed(enum status_failreason code UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "status_failed called!\n"); abort(); } -/* Generated stub for towire_warningfmt */ -u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, - const struct channel_id *channel UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "towire_warningfmt called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -#if DEVELOPER -/* Generated stub for memleak_remove_htable */ -void memleak_remove_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) -{ fprintf(stderr, "memleak_remove_htable called!\n"); abort(); } -/* Generated stub for memleak_remove_intmap_ */ -void memleak_remove_intmap_(struct htable *memtable UNNEEDED, const struct intmap *m UNNEEDED) -{ fprintf(stderr, "memleak_remove_intmap_ called!\n"); abort(); } -#endif - -/* NOOP for new_reltimer_ */ -struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, - const tal_t *ctx UNNEEDED, - struct timerel expire UNNEEDED, - void (*cb)(void *) UNNEEDED, void *arg UNNEEDED) -{ - return NULL; -} - -static void node_id_from_privkey(const struct privkey *p, struct node_id *id) -{ - struct pubkey k; - pubkey_from_privkey(p, &k); - node_id_from_pubkey(id, &k); -} - -#define NUM_NODES (ROUTING_MAX_HOPS + 1) - -/* We create an arrangement of nodes, each node N connected to N+1 and - * to node 1. The cost for each N to N+1 route is 1, for N to 1 is - * 2^N. That means it's always cheapest to go the longer route */ -int main(int argc, char *argv[]) -{ - struct routing_state *rstate; - struct node_id ids[NUM_NODES]; - struct chan **route; - struct amount_msat last_fee; - - common_setup(argv[0]); - - for (size_t i = 0; i < NUM_NODES; i++) { - struct privkey tmp; - memset(&tmp, i+1, sizeof(tmp)); - node_id_from_privkey(&tmp, &ids[i]); - } - /* We are node 0 */ - rstate = new_routing_state(tmpctx, &ids[0], NULL, NULL, NULL, - false, false); - - for (size_t i = 0; i < NUM_NODES; i++) { - struct chan *chan; - struct half_chan *hc; - struct short_channel_id scid; - - new_node(rstate, &ids[i]); - - if (i == 0) - continue; - if (!mk_short_channel_id(&scid, i, i-1, 0)) - abort(); - chan = new_chan(rstate, &scid, &ids[i], &ids[i-1], - AMOUNT_SAT(1000000), NULL); - - hc = &chan->half[node_id_idx(&ids[i-1], &ids[i])]; - hc->bcast.index = 1; - hc->base_fee = 1; - hc->proportional_fee = 0; - hc->delay = 0; - hc->channel_flags = node_id_idx(&ids[i-1], &ids[i]); - hc->htlc_minimum = AMOUNT_MSAT(0); - hc->htlc_maximum = AMOUNT_MSAT(1000000 * 1000); - SUPERVERBOSE("Joining %s to %s, fee %u", - type_to_string(tmpctx, struct node_id, &ids[i-1]), - type_to_string(tmpctx, struct node_id, &ids[i]), - (int)hc->base_fee); - - if (i <= 2) - continue; - if (!mk_short_channel_id(&scid, i, 1, 0)) - abort(); - chan = new_chan(rstate, &scid, &ids[i], &ids[1], - AMOUNT_SAT(1000000), NULL); - hc = &chan->half[node_id_idx(&ids[1], &ids[i])]; - hc->bcast.index = 1; - hc->base_fee = 1 << i; - hc->proportional_fee = 0; - hc->delay = 0; - hc->channel_flags = node_id_idx(&ids[1], &ids[i]); - hc->htlc_minimum = AMOUNT_MSAT(0); - hc->htlc_maximum = AMOUNT_MSAT(1000000 * 1000); - SUPERVERBOSE("Joining %s to %s, fee %u", - type_to_string(tmpctx, struct node_id, &ids[1]), - type_to_string(tmpctx, struct node_id, &ids[i]), - (int)hc->base_fee); - } - - for (size_t i = ROUTING_MAX_HOPS; i > 1; i--) { - struct amount_msat fee; - SUPERVERBOSE("%s -> %s:", - type_to_string(tmpctx, struct node_id, &ids[0]), - type_to_string(tmpctx, struct node_id, &ids[NUM_NODES-1])); - - route = find_route(tmpctx, rstate, &ids[0], &ids[NUM_NODES-1], - AMOUNT_MSAT(1000), 0, 0.0, NULL, - i, &fee); - assert(route); - assert(tal_count(route) == i); - if (i != ROUTING_MAX_HOPS) - assert(amount_msat_greater(fee, last_fee)); - last_fee = fee; - } - - common_shutdown(); - return 0; -} From 9a7d49d8aec6f67a18f8624eeec48dc1f7cfa649 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 15 Jun 2021 06:37:39 +0930 Subject: [PATCH 16/17] gossipd: remove routing, listchannels and listnodes infrastructure. This involves removing some fields from the now-misnamed routing.h datastructures, and various internal messages. One non-obvious change is to our "keepalive" logic which refreshes channels every 13 days: instead of using the 'enabled' flag on the last channel broadcast to decide whether to refresh it, we use the local connected status directly. Signed-off-by: Rusty Russell --- gossipd/gossip_generation.c | 39 +- gossipd/gossipd.c | 372 +------------- gossipd/gossipd_wire.csv | 50 -- gossipd/gossipd_wiregen.c | 327 +------------ gossipd/gossipd_wiregen.h | 52 +- gossipd/routing.c | 938 +----------------------------------- gossipd/routing.h | 66 +-- lightningd/gossip_control.c | 349 -------------- lightningd/gossip_msg.c | 29 -- lightningd/gossip_msg.h | 5 - lightningd/pay.c | 1 + 11 files changed, 41 insertions(+), 2187 deletions(-) diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index 1baddd1fca37..7defae9f434c 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -414,6 +415,12 @@ void refresh_local_channel(struct daemon *daemon, { const struct half_chan *hc; struct local_cupdate *lc; + u8 *prev; + secp256k1_ecdsa_signature signature; + struct bitcoin_blkid chain_hash; + struct short_channel_id short_channel_id; + u32 timestamp; + u8 message_flags, channel_flags; hc = &local_chan->chan->half[local_chan->direction]; @@ -425,14 +432,32 @@ void refresh_local_channel(struct daemon *daemon, lc->daemon = daemon; lc->local_chan = local_chan; lc->even_if_identical = even_if_identical; - lc->disable = (hc->channel_flags & ROUTING_FLAGS_DISABLED) - || local_chan->local_disabled; - lc->cltv_expiry_delta = hc->delay; - lc->htlc_minimum = hc->htlc_minimum; - lc->htlc_maximum = hc->htlc_maximum; - lc->fee_base_msat = hc->base_fee; - lc->fee_proportional_millionths = hc->proportional_fee; + prev = cast_const(u8 *, + gossip_store_get(tmpctx, daemon->rstate->gs, + local_chan->chan->half[local_chan->direction] + .bcast.index)); + + /* If it's a private update, unwrap */ + fromwire_gossip_store_private_update(tmpctx, prev, &prev); + + if (!fromwire_channel_update_option_channel_htlc_max(prev, + &signature, &chain_hash, + &short_channel_id, ×tamp, + &message_flags, &channel_flags, + &lc->cltv_expiry_delta, + &lc->htlc_minimum, + &lc->fee_base_msat, + &lc->fee_proportional_millionths, + &lc->htlc_maximum)) { + status_broken("Could not decode local channel_update %s!", + tal_hex(tmpctx, prev)); + tal_free(lc); + return; + } + + lc->disable = (channel_flags & ROUTING_FLAGS_DISABLED) + || local_chan->local_disabled; update_local_channel(lc); } diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index d495771e8008..eea5fd3d6904 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -1025,7 +1025,7 @@ static void gossip_refresh_network(struct daemon *daemon) continue; } - if (!is_halfchan_enabled(hc)) { + if (is_chan_local_disabled(daemon->rstate, c)) { /* Only send keepalives for active connections */ continue; } @@ -1138,285 +1138,6 @@ static struct io_plan *gossip_init(struct io_conn *conn, return daemon_conn_read_next(conn, daemon->master); } -/*~ lightningd can ask for a route between nodes. */ -static struct io_plan *getroute_req(struct io_conn *conn, struct daemon *daemon, - const u8 *msg) -{ - struct node_id *source, destination; - struct amount_msat msat; - u32 final_cltv; - /* risk factor 12.345% -> riskfactor_millionths = 12345000 */ - u64 riskfactor_millionths; - u32 max_hops; - u8 *out; - struct route_hop **hops; - /* fuzz 12.345% -> fuzz_millionths = 12345000 */ - u64 fuzz_millionths; - struct exclude_entry **excluded; - - /* To choose between variations, we need to know how much we're - * sending (eliminates too-small channels, and also effects the fees - * we'll pay), how to trade off more locktime vs. more fees, and how - * much cltv we need a the final node to give exact values for each - * intermediate hop, as well as how much random fuzz to inject to - * avoid being too predictable. - * - * We also treat routing slightly differently if we're asking - * for a route from ourselves (the usual case): in that case, - * we don't have to consider fees on our own outgoing channels. - */ - if (!fromwire_gossipd_getroute_request( - msg, msg, &source, &destination, &msat, &riskfactor_millionths, - &final_cltv, &fuzz_millionths, &excluded, &max_hops)) - master_badmsg(WIRE_GOSSIPD_GETROUTE_REQUEST, msg); - - status_debug("Trying to find a route from %s to %s for %s", - source - ? type_to_string(tmpctx, struct node_id, source) : "(me)", - type_to_string(tmpctx, struct node_id, &destination), - type_to_string(tmpctx, struct amount_msat, &msat)); - - /* routing.c does all the hard work; can return NULL. */ - hops = get_route(tmpctx, daemon->rstate, source, &destination, msat, - riskfactor_millionths / 1000000.0, final_cltv, - fuzz_millionths / 1000000.0, pseudorand_u64(), - excluded, max_hops); - - out = towire_gossipd_getroute_reply(NULL, - cast_const2(const struct route_hop **, - hops)); - daemon_conn_send(daemon->master, take(out)); - return daemon_conn_read_next(conn, daemon->master); -} - -/*~ When someone asks lightningd to `listchannels`, gossipd does the work: - * marshalling the channel information for all channels into an array of - * gossip_getchannels_entry, which lightningd converts to JSON. Each channel - * is represented by two half_chan; one in each direction. - */ -static struct gossip_halfchannel_entry *hc_entry(const tal_t *ctx, - const struct chan *chan, - int idx) -{ - /* Our 'struct chan' contains two nodes: they are in pubkey_cmp order - * (ie. chan->nodes[0] is the lesser pubkey) and this is the same as - * the direction bit in `channel_update`s `channel_flags`. - * - * The halfchans are arranged so that half[0] src == nodes[0], and we - * use that here. */ - const struct half_chan *c = &chan->half[idx]; - struct gossip_halfchannel_entry *e; - - /* If we've never seen a channel_update for this direction... */ - if (!is_halfchan_defined(c)) - return NULL; - - e = tal(ctx, struct gossip_halfchannel_entry); - e->channel_flags = c->channel_flags; - e->message_flags = c->message_flags; - e->last_update_timestamp = c->bcast.timestamp; - e->base_fee_msat = c->base_fee; - e->fee_per_millionth = c->proportional_fee; - e->delay = c->delay; - e->min = c->htlc_minimum; - e->max = c->htlc_maximum; - - return e; -} - -/*~ We don't keep channel features in memory; they're rarely used. So we - * remember if it exists, and load it off disk when needed. */ -static u8 *get_channel_features(const tal_t *ctx, - struct gossip_store *gs, - const struct chan *chan) -{ - secp256k1_ecdsa_signature sig; - u8 *features; - struct bitcoin_blkid chain_hash; - struct short_channel_id short_channel_id; - struct node_id node_id; - struct pubkey bitcoin_key; - struct amount_sat sats; - u8 *ann; - - /* This is where we stash a flag to indicate it exists. */ - if (!chan->half[0].any_features) - return NULL; - - ann = cast_const(u8 *, gossip_store_get(tmpctx, gs, chan->bcast.index)); - - /* Could be a private_channel */ - fromwire_gossip_store_private_channel(tmpctx, ann, &sats, &ann); - - if (!fromwire_channel_announcement(ctx, ann, &sig, &sig, &sig, &sig, - &features, &chain_hash, - &short_channel_id, - &node_id, &node_id, - &bitcoin_key, &bitcoin_key)) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "bad channel_announcement / local_add_channel at %u: %s", - chan->bcast.index, tal_hex(tmpctx, ann)); - - return features; -} - -/*~ Marshal (possibly) both channel directions into entries. */ -static void append_channel(struct routing_state *rstate, - const struct gossip_getchannels_entry ***entries, - const struct chan *chan, - const struct node_id *srcfilter) -{ - struct gossip_getchannels_entry *e = tal(*entries, struct gossip_getchannels_entry); - - e->node[0] = chan->nodes[0]->id; - e->node[1] = chan->nodes[1]->id; - e->sat = chan->sat; - e->local_disabled = is_chan_local_disabled(rstate, chan); - e->public = is_chan_public(chan); - e->short_channel_id = chan->scid; - e->features = get_channel_features(e, rstate->gs, chan); - if (!srcfilter || node_id_eq(&e->node[0], srcfilter)) - e->e[0] = hc_entry(*entries, chan, 0); - else - e->e[0] = NULL; - if (!srcfilter || node_id_eq(&e->node[1], srcfilter)) - e->e[1] = hc_entry(*entries, chan, 1); - else - e->e[1] = NULL; - - /* We choose not to tell lightningd about channels with no updates, - * as they're unusable and can't be represented in the listchannels - * JSON output we use anyway. */ - if (e->e[0] || e->e[1]) - tal_arr_expand(entries, e); -} - -/*~ This is where lightningd asks for all channels we know about. */ -static struct io_plan *getchannels_req(struct io_conn *conn, - struct daemon *daemon, - const u8 *msg) -{ - u8 *out; - const struct gossip_getchannels_entry **entries; - struct chan *chan; - struct short_channel_id *scid, *prev; - struct node_id *source; - bool complete = true; - - /* Note: scid is marked optional in gossip_wire.csv */ - if (!fromwire_gossipd_getchannels_request(msg, msg, &scid, &source, - &prev)) - master_badmsg(WIRE_GOSSIPD_GETCHANNELS_REQUEST, msg); - - entries = tal_arr(tmpctx, const struct gossip_getchannels_entry *, 0); - /* They can ask about a particular channel by short_channel_id */ - if (scid) { - chan = get_channel(daemon->rstate, scid); - if (chan) - append_channel(daemon->rstate, &entries, chan, NULL); - } else if (source) { - struct node *s = get_node(daemon->rstate, source); - if (s) { - struct chan_map_iter i; - struct chan *c; - - for (c = first_chan(s, &i); c; c = next_chan(s, &i)) { - append_channel(daemon->rstate, - &entries, c, source); - } - } - } else { - u64 idx; - - /* For the more general case, we just iterate through every - * short channel id, starting with previous if any (there is - * no scid 0). */ - idx = prev ? prev->u64 : 0; - while ((chan = uintmap_after(&daemon->rstate->chanmap, &idx))) { - append_channel(daemon->rstate, &entries, chan, NULL); - /* Limit how many we do at once. */ - if (tal_count(entries) == 4096) { - complete = false; - break; - } - } - } - - out = towire_gossipd_getchannels_reply(NULL, complete, entries); - daemon_conn_send(daemon->master, take(out)); - return daemon_conn_read_next(conn, daemon->master); -} - -/*~ Similarly, lightningd asks us for all nodes when it gets `listnodes` */ -/* We keep pointers into n, assuming it won't change. */ -static void add_node_entry(const tal_t *ctx, - struct daemon *daemon, - const struct node *n, - struct gossip_getnodes_entry *e) -{ - e->nodeid = n->id; - if (get_node_announcement(ctx, daemon, n, - e->color, e->alias, - &e->features, - &e->addresses)) { - e->last_timestamp = n->bcast.timestamp; - } else { - /* Timestamp on wire is an unsigned 32 bit: we use a 64-bit - * signed, so -1 means "we never received a - * channel_update". */ - e->last_timestamp = -1; - } -} - -/* Simply routine when they ask for `listnodes` */ -static struct io_plan *getnodes(struct io_conn *conn, struct daemon *daemon, - const u8 *msg) -{ - u8 *out; - struct node *n; - const struct gossip_getnodes_entry **nodes; - struct gossip_getnodes_entry *node_arr; - struct node_id *id; - - if (!fromwire_gossipd_getnodes_request(tmpctx, msg, &id)) - master_badmsg(WIRE_GOSSIPD_GETNODES_REQUEST, msg); - - /* Format of reply is the same whether they ask for a specific node - * (0 or one responses) or all nodes (0 or more) */ - if (id) { - n = get_node(daemon->rstate, id); - if (n) { - node_arr = tal_arr(tmpctx, - struct gossip_getnodes_entry, - 1); - add_node_entry(node_arr, daemon, n, &node_arr[0]); - } else { - nodes = NULL; - node_arr = NULL; - } - } else { - struct node_map_iter it; - size_t i = 0; - node_arr = tal_arr(tmpctx, struct gossip_getnodes_entry, - node_map_count(daemon->rstate->nodes)); - n = node_map_first(daemon->rstate->nodes, &it); - while (n != NULL) { - add_node_entry(node_arr, daemon, n, &node_arr[i++]); - n = node_map_next(daemon->rstate->nodes, &it); - } - assert(i == node_map_count(daemon->rstate->nodes)); - } - - /* FIXME: towire wants array of pointers. */ - nodes = tal_arr(tmpctx, const struct gossip_getnodes_entry *, - tal_count(node_arr)); - for (size_t i = 0; i < tal_count(node_arr); i++) - nodes[i] = &node_arr[i]; - out = towire_gossipd_getnodes_reply(NULL, nodes); - daemon_conn_send(daemon->master, take(out)); - return daemon_conn_read_next(conn, daemon->master); -} - /*~ We currently have a JSON command to ping a peer: it ends up here, where * gossipd generates the actual ping and sends it like any other gossip. */ static struct io_plan *ping_req(struct io_conn *conn, struct daemon *daemon, @@ -1472,81 +1193,6 @@ static struct io_plan *ping_req(struct io_conn *conn, struct daemon *daemon, return daemon_conn_read_next(conn, daemon->master); } -/*~ If a node has no public channels (other than the one to us), it's not - * a very useful route to tell anyone about. */ -static bool node_has_public_channels(const struct node *peer, - const struct chan *exclude) -{ - struct chan_map_iter i; - struct chan *c; - - for (c = first_chan(peer, &i); c; c = next_chan(peer, &i)) { - if (c == exclude) - continue; - if (is_chan_public(c)) - return true; - } - return false; -} - -/*~ For routeboost, we offer payers a hint of what incoming channels might - * have capacity for their payment. To do this, lightningd asks for the - * information about all channels to this node; but gossipd doesn't know about - * current capacities, so lightningd selects which to use. */ -static struct io_plan *get_incoming_channels(struct io_conn *conn, - struct daemon *daemon, - const u8 *msg) -{ - struct node *node; - struct route_info *public = tal_arr(tmpctx, struct route_info, 0); - struct route_info *private = tal_arr(tmpctx, struct route_info, 0); - bool *priv_deadends = tal_arr(tmpctx, bool, 0); - bool *pub_deadends = tal_arr(tmpctx, bool, 0); - - if (!fromwire_gossipd_get_incoming_channels(msg)) - master_badmsg(WIRE_GOSSIPD_GET_INCOMING_CHANNELS, msg); - - node = get_node(daemon->rstate, &daemon->rstate->local_id); - if (node) { - struct chan_map_iter i; - struct chan *c; - - for (c = first_chan(node, &i); c; c = next_chan(node, &i)) { - const struct half_chan *hc; - struct route_info ri; - bool deadend; - - hc = &c->half[half_chan_to(node, c)]; - - if (!is_halfchan_enabled(hc)) - continue; - - ri.pubkey = other_node(node, c)->id; - ri.short_channel_id = c->scid; - ri.fee_base_msat = hc->base_fee; - ri.fee_proportional_millionths = hc->proportional_fee; - ri.cltv_expiry_delta = hc->delay; - - deadend = !node_has_public_channels(other_node(node, c), - c); - if (is_chan_public(c)) { - tal_arr_expand(&public, ri); - tal_arr_expand(&pub_deadends, deadend); - } else { - tal_arr_expand(&private, ri); - tal_arr_expand(&priv_deadends, deadend); - } - } - } - - msg = towire_gossipd_get_incoming_channels_reply(NULL, - public, pub_deadends, - private, priv_deadends); - daemon_conn_send(daemon->master, take(msg)); - - return daemon_conn_read_next(conn, daemon->master); -} - static struct io_plan *new_blockheight(struct io_conn *conn, struct daemon *daemon, const u8 *msg) @@ -1816,15 +1462,6 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_GOSSIPD_INIT: return gossip_init(conn, daemon, msg); - case WIRE_GOSSIPD_GETNODES_REQUEST: - return getnodes(conn, daemon, msg); - - case WIRE_GOSSIPD_GETROUTE_REQUEST: - return getroute_req(conn, daemon, msg); - - case WIRE_GOSSIPD_GETCHANNELS_REQUEST: - return getchannels_req(conn, daemon, msg); - case WIRE_GOSSIPD_GET_STRIPPED_CUPDATE: return get_stripped_cupdate(conn, daemon, msg); @@ -1840,9 +1477,6 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_GOSSIPD_PING: return ping_req(conn, daemon, msg); - case WIRE_GOSSIPD_GET_INCOMING_CHANNELS: - return get_incoming_channels(conn, daemon, msg); - case WIRE_GOSSIPD_NEW_BLOCKHEIGHT: return new_blockheight(conn, daemon, msg); @@ -1872,12 +1506,8 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_GOSSIPD_SEND_ONIONMSG: return onionmsg_req(conn, daemon, msg); /* We send these, we don't receive them */ - case WIRE_GOSSIPD_GETNODES_REPLY: - case WIRE_GOSSIPD_GETROUTE_REPLY: - case WIRE_GOSSIPD_GETCHANNELS_REPLY: case WIRE_GOSSIPD_PING_REPLY: case WIRE_GOSSIPD_GET_STRIPPED_CUPDATE_REPLY: - case WIRE_GOSSIPD_GET_INCOMING_CHANNELS_REPLY: case WIRE_GOSSIPD_GET_TXOUT: case WIRE_GOSSIPD_DEV_MEMLEAK_REPLY: case WIRE_GOSSIPD_DEV_COMPACT_STORE_REPLY: diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index f9b6e0f61939..bb51590e2803 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -20,42 +20,6 @@ msgdata,gossipd_init,dev_fast_gossip_prune,bool, msgtype,gossipd_dev_set_time,3001 msgdata,gossipd_dev_set_time,dev_gossip_time,u32, -# Pass JSON-RPC getnodes call through -msgtype,gossipd_getnodes_request,3005 -msgdata,gossipd_getnodes_request,id,?node_id, - -#include -msgtype,gossipd_getnodes_reply,3105 -msgdata,gossipd_getnodes_reply,num_nodes,u32, -msgdata,gossipd_getnodes_reply,nodes,gossip_getnodes_entry,num_nodes - -# Pass JSON-RPC getroute call through -msgtype,gossipd_getroute_request,3006 -# Source defaults to "us", and means we don't consider first-hop channel fees -msgdata,gossipd_getroute_request,source,?node_id, -msgdata,gossipd_getroute_request,destination,node_id, -msgdata,gossipd_getroute_request,msatoshi,amount_msat, -msgdata,gossipd_getroute_request,riskfactor_millionths,u64, -msgdata,gossipd_getroute_request,final_cltv,u32, -msgdata,gossipd_getroute_request,fuzz_millionths,u64, -msgdata,gossipd_getroute_request,num_excluded,u16, -msgdata,gossipd_getroute_request,excluded,exclude_entry,num_excluded -msgdata,gossipd_getroute_request,max_hops,u32, - -msgtype,gossipd_getroute_reply,3106 -msgdata,gossipd_getroute_reply,num_hops,u16, -msgdata,gossipd_getroute_reply,hops,route_hop,num_hops - -msgtype,gossipd_getchannels_request,3007 -msgdata,gossipd_getchannels_request,short_channel_id,?short_channel_id, -msgdata,gossipd_getchannels_request,source,?node_id, -msgdata,gossipd_getchannels_request,prev,?short_channel_id, - -msgtype,gossipd_getchannels_reply,3107 -msgdata,gossipd_getchannels_reply,complete,bool, -msgdata,gossipd_getchannels_reply,num_channels,u32, -msgdata,gossipd_getchannels_reply,nodes,gossip_getchannels_entry,num_channels - # Ping/pong test. Waits for a reply if it expects one. msgtype,gossipd_ping,3008 msgdata,gossipd_ping,id,node_id, @@ -116,20 +80,6 @@ msgtype,gossipd_dev_compact_store,3034 msgtype,gossipd_dev_compact_store_reply,3134 msgdata,gossipd_dev_compact_store_reply,success,bool, -#include - -# master -> gossipd: get route_info for our incoming channels -msgtype,gossipd_get_incoming_channels,3025 - -# gossipd -> master: here they are. -msgtype,gossipd_get_incoming_channels_reply,3125 -msgdata,gossipd_get_incoming_channels_reply,num_public,u16, -msgdata,gossipd_get_incoming_channels_reply,public_route_info,route_info,num_public -msgdata,gossipd_get_incoming_channels_reply,public_deadends,bool,num_public -msgdata,gossipd_get_incoming_channels_reply,num_private,u16, -msgdata,gossipd_get_incoming_channels_reply,private_route_info,route_info,num_private -msgdata,gossipd_get_incoming_channels_reply,private_deadends,bool,num_private - # master -> gossipd: blockheight increased. msgtype,gossipd_new_blockheight,3026 msgdata,gossipd_new_blockheight,blockheight,u32, diff --git a/gossipd/gossipd_wiregen.c b/gossipd/gossipd_wiregen.c index 6e86ca01e0e8..e779e960192f 100644 --- a/gossipd/gossipd_wiregen.c +++ b/gossipd/gossipd_wiregen.c @@ -22,12 +22,6 @@ const char *gossipd_wire_name(int e) switch ((enum gossipd_wire)e) { case WIRE_GOSSIPD_INIT: return "WIRE_GOSSIPD_INIT"; case WIRE_GOSSIPD_DEV_SET_TIME: return "WIRE_GOSSIPD_DEV_SET_TIME"; - case WIRE_GOSSIPD_GETNODES_REQUEST: return "WIRE_GOSSIPD_GETNODES_REQUEST"; - case WIRE_GOSSIPD_GETNODES_REPLY: return "WIRE_GOSSIPD_GETNODES_REPLY"; - case WIRE_GOSSIPD_GETROUTE_REQUEST: return "WIRE_GOSSIPD_GETROUTE_REQUEST"; - case WIRE_GOSSIPD_GETROUTE_REPLY: return "WIRE_GOSSIPD_GETROUTE_REPLY"; - case WIRE_GOSSIPD_GETCHANNELS_REQUEST: return "WIRE_GOSSIPD_GETCHANNELS_REQUEST"; - case WIRE_GOSSIPD_GETCHANNELS_REPLY: return "WIRE_GOSSIPD_GETCHANNELS_REPLY"; case WIRE_GOSSIPD_PING: return "WIRE_GOSSIPD_PING"; case WIRE_GOSSIPD_PING_REPLY: return "WIRE_GOSSIPD_PING_REPLY"; case WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE: return "WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE"; @@ -42,8 +36,6 @@ const char *gossipd_wire_name(int e) case WIRE_GOSSIPD_DEV_MEMLEAK_REPLY: return "WIRE_GOSSIPD_DEV_MEMLEAK_REPLY"; case WIRE_GOSSIPD_DEV_COMPACT_STORE: return "WIRE_GOSSIPD_DEV_COMPACT_STORE"; case WIRE_GOSSIPD_DEV_COMPACT_STORE_REPLY: return "WIRE_GOSSIPD_DEV_COMPACT_STORE_REPLY"; - case WIRE_GOSSIPD_GET_INCOMING_CHANNELS: return "WIRE_GOSSIPD_GET_INCOMING_CHANNELS"; - case WIRE_GOSSIPD_GET_INCOMING_CHANNELS_REPLY: return "WIRE_GOSSIPD_GET_INCOMING_CHANNELS_REPLY"; case WIRE_GOSSIPD_NEW_BLOCKHEIGHT: return "WIRE_GOSSIPD_NEW_BLOCKHEIGHT"; case WIRE_GOSSIPD_GOT_ONIONMSG_TO_US: return "WIRE_GOSSIPD_GOT_ONIONMSG_TO_US"; case WIRE_GOSSIPD_GOT_ONIONMSG_FORWARD: return "WIRE_GOSSIPD_GOT_ONIONMSG_FORWARD"; @@ -61,12 +53,6 @@ bool gossipd_wire_is_defined(u16 type) switch ((enum gossipd_wire)type) { case WIRE_GOSSIPD_INIT:; case WIRE_GOSSIPD_DEV_SET_TIME:; - case WIRE_GOSSIPD_GETNODES_REQUEST:; - case WIRE_GOSSIPD_GETNODES_REPLY:; - case WIRE_GOSSIPD_GETROUTE_REQUEST:; - case WIRE_GOSSIPD_GETROUTE_REPLY:; - case WIRE_GOSSIPD_GETCHANNELS_REQUEST:; - case WIRE_GOSSIPD_GETCHANNELS_REPLY:; case WIRE_GOSSIPD_PING:; case WIRE_GOSSIPD_PING_REPLY:; case WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE:; @@ -81,8 +67,6 @@ bool gossipd_wire_is_defined(u16 type) case WIRE_GOSSIPD_DEV_MEMLEAK_REPLY:; case WIRE_GOSSIPD_DEV_COMPACT_STORE:; case WIRE_GOSSIPD_DEV_COMPACT_STORE_REPLY:; - case WIRE_GOSSIPD_GET_INCOMING_CHANNELS:; - case WIRE_GOSSIPD_GET_INCOMING_CHANNELS_REPLY:; case WIRE_GOSSIPD_NEW_BLOCKHEIGHT:; case WIRE_GOSSIPD_GOT_ONIONMSG_TO_US:; case WIRE_GOSSIPD_GOT_ONIONMSG_FORWARD:; @@ -177,242 +161,6 @@ bool fromwire_gossipd_dev_set_time(const void *p, u32 *dev_gossip_time) return cursor != NULL; } -/* WIRE: GOSSIPD_GETNODES_REQUEST */ -/* Pass JSON-RPC getnodes call through */ -u8 *towire_gossipd_getnodes_request(const tal_t *ctx, const struct node_id *id) -{ - u8 *p = tal_arr(ctx, u8, 0); - - towire_u16(&p, WIRE_GOSSIPD_GETNODES_REQUEST); - if (!id) - towire_bool(&p, false); - else { - towire_bool(&p, true); - towire_node_id(&p, id); - } - - return memcheck(p, tal_count(p)); -} -bool fromwire_gossipd_getnodes_request(const tal_t *ctx, const void *p, struct node_id **id) -{ - const u8 *cursor = p; - size_t plen = tal_count(p); - - if (fromwire_u16(&cursor, &plen) != WIRE_GOSSIPD_GETNODES_REQUEST) - return false; - if (!fromwire_bool(&cursor, &plen)) - *id = NULL; - else { - *id = tal(ctx, struct node_id); - fromwire_node_id(&cursor, &plen, *id); - } - return cursor != NULL; -} - -/* WIRE: GOSSIPD_GETNODES_REPLY */ -u8 *towire_gossipd_getnodes_reply(const tal_t *ctx, const struct gossip_getnodes_entry **nodes) -{ - u32 num_nodes = tal_count(nodes); - u8 *p = tal_arr(ctx, u8, 0); - - towire_u16(&p, WIRE_GOSSIPD_GETNODES_REPLY); - towire_u32(&p, num_nodes); - for (size_t i = 0; i < num_nodes; i++) - towire_gossip_getnodes_entry(&p, nodes[i]); - - return memcheck(p, tal_count(p)); -} -bool fromwire_gossipd_getnodes_reply(const tal_t *ctx, const void *p, struct gossip_getnodes_entry ***nodes) -{ - u32 num_nodes; - - const u8 *cursor = p; - size_t plen = tal_count(p); - - if (fromwire_u16(&cursor, &plen) != WIRE_GOSSIPD_GETNODES_REPLY) - return false; - num_nodes = fromwire_u32(&cursor, &plen); - // 2nd case nodes - *nodes = num_nodes ? tal_arr(ctx, struct gossip_getnodes_entry *, num_nodes) : NULL; - for (size_t i = 0; i < num_nodes; i++) - (*nodes)[i] = fromwire_gossip_getnodes_entry(*nodes, &cursor, &plen); - return cursor != NULL; -} - -/* WIRE: GOSSIPD_GETROUTE_REQUEST */ -/* Pass JSON-RPC getroute call through */ -u8 *towire_gossipd_getroute_request(const tal_t *ctx, const struct node_id *source, const struct node_id *destination, struct amount_msat msatoshi, u64 riskfactor_millionths, u32 final_cltv, u64 fuzz_millionths, const struct exclude_entry **excluded, u32 max_hops) -{ - u16 num_excluded = tal_count(excluded); - u8 *p = tal_arr(ctx, u8, 0); - - towire_u16(&p, WIRE_GOSSIPD_GETROUTE_REQUEST); - /* Source defaults to "us" */ - if (!source) - towire_bool(&p, false); - else { - towire_bool(&p, true); - towire_node_id(&p, source); - } - towire_node_id(&p, destination); - towire_amount_msat(&p, msatoshi); - towire_u64(&p, riskfactor_millionths); - towire_u32(&p, final_cltv); - towire_u64(&p, fuzz_millionths); - towire_u16(&p, num_excluded); - for (size_t i = 0; i < num_excluded; i++) - towire_exclude_entry(&p, excluded[i]); - towire_u32(&p, max_hops); - - return memcheck(p, tal_count(p)); -} -bool fromwire_gossipd_getroute_request(const tal_t *ctx, const void *p, struct node_id **source, struct node_id *destination, struct amount_msat *msatoshi, u64 *riskfactor_millionths, u32 *final_cltv, u64 *fuzz_millionths, struct exclude_entry ***excluded, u32 *max_hops) -{ - u16 num_excluded; - - const u8 *cursor = p; - size_t plen = tal_count(p); - - if (fromwire_u16(&cursor, &plen) != WIRE_GOSSIPD_GETROUTE_REQUEST) - return false; - /* Source defaults to "us" */ - if (!fromwire_bool(&cursor, &plen)) - *source = NULL; - else { - *source = tal(ctx, struct node_id); - fromwire_node_id(&cursor, &plen, *source); - } - fromwire_node_id(&cursor, &plen, destination); - *msatoshi = fromwire_amount_msat(&cursor, &plen); - *riskfactor_millionths = fromwire_u64(&cursor, &plen); - *final_cltv = fromwire_u32(&cursor, &plen); - *fuzz_millionths = fromwire_u64(&cursor, &plen); - num_excluded = fromwire_u16(&cursor, &plen); - // 2nd case excluded - *excluded = num_excluded ? tal_arr(ctx, struct exclude_entry *, num_excluded) : NULL; - for (size_t i = 0; i < num_excluded; i++) - (*excluded)[i] = fromwire_exclude_entry(*excluded, &cursor, &plen); - *max_hops = fromwire_u32(&cursor, &plen); - return cursor != NULL; -} - -/* WIRE: GOSSIPD_GETROUTE_REPLY */ -u8 *towire_gossipd_getroute_reply(const tal_t *ctx, const struct route_hop **hops) -{ - u16 num_hops = tal_count(hops); - u8 *p = tal_arr(ctx, u8, 0); - - towire_u16(&p, WIRE_GOSSIPD_GETROUTE_REPLY); - towire_u16(&p, num_hops); - for (size_t i = 0; i < num_hops; i++) - towire_route_hop(&p, hops[i]); - - return memcheck(p, tal_count(p)); -} -bool fromwire_gossipd_getroute_reply(const tal_t *ctx, const void *p, struct route_hop ***hops) -{ - u16 num_hops; - - const u8 *cursor = p; - size_t plen = tal_count(p); - - if (fromwire_u16(&cursor, &plen) != WIRE_GOSSIPD_GETROUTE_REPLY) - return false; - num_hops = fromwire_u16(&cursor, &plen); - // 2nd case hops - *hops = num_hops ? tal_arr(ctx, struct route_hop *, num_hops) : NULL; - for (size_t i = 0; i < num_hops; i++) - (*hops)[i] = fromwire_route_hop(*hops, &cursor, &plen); - return cursor != NULL; -} - -/* WIRE: GOSSIPD_GETCHANNELS_REQUEST */ -u8 *towire_gossipd_getchannels_request(const tal_t *ctx, const struct short_channel_id *short_channel_id, const struct node_id *source, const struct short_channel_id *prev) -{ - u8 *p = tal_arr(ctx, u8, 0); - - towire_u16(&p, WIRE_GOSSIPD_GETCHANNELS_REQUEST); - if (!short_channel_id) - towire_bool(&p, false); - else { - towire_bool(&p, true); - towire_short_channel_id(&p, short_channel_id); - } - if (!source) - towire_bool(&p, false); - else { - towire_bool(&p, true); - towire_node_id(&p, source); - } - if (!prev) - towire_bool(&p, false); - else { - towire_bool(&p, true); - towire_short_channel_id(&p, prev); - } - - return memcheck(p, tal_count(p)); -} -bool fromwire_gossipd_getchannels_request(const tal_t *ctx, const void *p, struct short_channel_id **short_channel_id, struct node_id **source, struct short_channel_id **prev) -{ - const u8 *cursor = p; - size_t plen = tal_count(p); - - if (fromwire_u16(&cursor, &plen) != WIRE_GOSSIPD_GETCHANNELS_REQUEST) - return false; - if (!fromwire_bool(&cursor, &plen)) - *short_channel_id = NULL; - else { - *short_channel_id = tal(ctx, struct short_channel_id); - fromwire_short_channel_id(&cursor, &plen, *short_channel_id); - } - if (!fromwire_bool(&cursor, &plen)) - *source = NULL; - else { - *source = tal(ctx, struct node_id); - fromwire_node_id(&cursor, &plen, *source); - } - if (!fromwire_bool(&cursor, &plen)) - *prev = NULL; - else { - *prev = tal(ctx, struct short_channel_id); - fromwire_short_channel_id(&cursor, &plen, *prev); - } - return cursor != NULL; -} - -/* WIRE: GOSSIPD_GETCHANNELS_REPLY */ -u8 *towire_gossipd_getchannels_reply(const tal_t *ctx, bool complete, const struct gossip_getchannels_entry **nodes) -{ - u32 num_channels = tal_count(nodes); - u8 *p = tal_arr(ctx, u8, 0); - - towire_u16(&p, WIRE_GOSSIPD_GETCHANNELS_REPLY); - towire_bool(&p, complete); - towire_u32(&p, num_channels); - for (size_t i = 0; i < num_channels; i++) - towire_gossip_getchannels_entry(&p, nodes[i]); - - return memcheck(p, tal_count(p)); -} -bool fromwire_gossipd_getchannels_reply(const tal_t *ctx, const void *p, bool *complete, struct gossip_getchannels_entry ***nodes) -{ - u32 num_channels; - - const u8 *cursor = p; - size_t plen = tal_count(p); - - if (fromwire_u16(&cursor, &plen) != WIRE_GOSSIPD_GETCHANNELS_REPLY) - return false; - *complete = fromwire_bool(&cursor, &plen); - num_channels = fromwire_u32(&cursor, &plen); - // 2nd case nodes - *nodes = num_channels ? tal_arr(ctx, struct gossip_getchannels_entry *, num_channels) : NULL; - for (size_t i = 0; i < num_channels; i++) - (*nodes)[i] = fromwire_gossip_getchannels_entry(*nodes, &cursor, &plen); - return cursor != NULL; -} - /* WIRE: GOSSIPD_PING */ /* Ping/pong test. Waits for a reply if it expects one. */ u8 *towire_gossipd_ping(const tal_t *ctx, const struct node_id *id, u16 num_pong_bytes, u16 len) @@ -742,79 +490,6 @@ bool fromwire_gossipd_dev_compact_store_reply(const void *p, bool *success) return cursor != NULL; } -/* WIRE: GOSSIPD_GET_INCOMING_CHANNELS */ -/* master -> gossipd: get route_info for our incoming channels */ -u8 *towire_gossipd_get_incoming_channels(const tal_t *ctx) -{ - u8 *p = tal_arr(ctx, u8, 0); - - towire_u16(&p, WIRE_GOSSIPD_GET_INCOMING_CHANNELS); - - return memcheck(p, tal_count(p)); -} -bool fromwire_gossipd_get_incoming_channels(const void *p) -{ - const u8 *cursor = p; - size_t plen = tal_count(p); - - if (fromwire_u16(&cursor, &plen) != WIRE_GOSSIPD_GET_INCOMING_CHANNELS) - return false; - return cursor != NULL; -} - -/* WIRE: GOSSIPD_GET_INCOMING_CHANNELS_REPLY */ -/* gossipd -> master: here they are. */ -u8 *towire_gossipd_get_incoming_channels_reply(const tal_t *ctx, const struct route_info *public_route_info, const bool *public_deadends, const struct route_info *private_route_info, const bool *private_deadends) -{ - u16 num_public = tal_count(public_deadends); - u16 num_private = tal_count(private_deadends); - u8 *p = tal_arr(ctx, u8, 0); - - towire_u16(&p, WIRE_GOSSIPD_GET_INCOMING_CHANNELS_REPLY); - towire_u16(&p, num_public); - for (size_t i = 0; i < num_public; i++) - towire_route_info(&p, public_route_info + i); - for (size_t i = 0; i < num_public; i++) - towire_bool(&p, public_deadends[i]); - towire_u16(&p, num_private); - for (size_t i = 0; i < num_private; i++) - towire_route_info(&p, private_route_info + i); - for (size_t i = 0; i < num_private; i++) - towire_bool(&p, private_deadends[i]); - - return memcheck(p, tal_count(p)); -} -bool fromwire_gossipd_get_incoming_channels_reply(const tal_t *ctx, const void *p, struct route_info **public_route_info, bool **public_deadends, struct route_info **private_route_info, bool **private_deadends) -{ - u16 num_public; - u16 num_private; - - const u8 *cursor = p; - size_t plen = tal_count(p); - - if (fromwire_u16(&cursor, &plen) != WIRE_GOSSIPD_GET_INCOMING_CHANNELS_REPLY) - return false; - num_public = fromwire_u16(&cursor, &plen); - // 2nd case public_route_info - *public_route_info = num_public ? tal_arr(ctx, struct route_info, num_public) : NULL; - for (size_t i = 0; i < num_public; i++) - fromwire_route_info(&cursor, &plen, *public_route_info + i); - // 2nd case public_deadends - *public_deadends = num_public ? tal_arr(ctx, bool, num_public) : NULL; - for (size_t i = 0; i < num_public; i++) - (*public_deadends)[i] = fromwire_bool(&cursor, &plen); - num_private = fromwire_u16(&cursor, &plen); - // 2nd case private_route_info - *private_route_info = num_private ? tal_arr(ctx, struct route_info, num_private) : NULL; - for (size_t i = 0; i < num_private; i++) - fromwire_route_info(&cursor, &plen, *private_route_info + i); - // 2nd case private_deadends - *private_deadends = num_private ? tal_arr(ctx, bool, num_private) : NULL; - for (size_t i = 0; i < num_private; i++) - (*private_deadends)[i] = fromwire_bool(&cursor, &plen); - return cursor != NULL; -} - /* WIRE: GOSSIPD_NEW_BLOCKHEIGHT */ /* master -> gossipd: blockheight increased. */ u8 *towire_gossipd_new_blockheight(const tal_t *ctx, u32 blockheight) @@ -1057,4 +732,4 @@ bool fromwire_gossipd_addgossip_reply(const tal_t *ctx, const void *p, wirestrin *err = fromwire_wirestring(ctx, &cursor, &plen); return cursor != NULL; } -// SHA256STAMP:a0d7494995d7f95fb7df295bab9d865e18670f15243116a0aaa9b9548534b922 +// SHA256STAMP:bc9045727cefbbe29118c8eae928972fe009a4d9d8c9e903f8b35f006973f462 diff --git a/gossipd/gossipd_wiregen.h b/gossipd/gossipd_wiregen.h index 0e989c517672..5059fb559d93 100644 --- a/gossipd/gossipd_wiregen.h +++ b/gossipd/gossipd_wiregen.h @@ -11,22 +11,12 @@ #include #include #include -#include -#include enum gossipd_wire { /* Initialize the gossip daemon. */ WIRE_GOSSIPD_INIT = 3000, /* In developer mode */ WIRE_GOSSIPD_DEV_SET_TIME = 3001, - /* Pass JSON-RPC getnodes call through */ - WIRE_GOSSIPD_GETNODES_REQUEST = 3005, - WIRE_GOSSIPD_GETNODES_REPLY = 3105, - /* Pass JSON-RPC getroute call through */ - WIRE_GOSSIPD_GETROUTE_REQUEST = 3006, - WIRE_GOSSIPD_GETROUTE_REPLY = 3106, - WIRE_GOSSIPD_GETCHANNELS_REQUEST = 3007, - WIRE_GOSSIPD_GETCHANNELS_REPLY = 3107, /* Ping/pong test. Waits for a reply if it expects one. */ WIRE_GOSSIPD_PING = 3008, WIRE_GOSSIPD_PING_REPLY = 3108, @@ -52,10 +42,6 @@ enum gossipd_wire { WIRE_GOSSIPD_DEV_COMPACT_STORE = 3034, /* gossipd -> master: ok */ WIRE_GOSSIPD_DEV_COMPACT_STORE_REPLY = 3134, - /* master -> gossipd: get route_info for our incoming channels */ - WIRE_GOSSIPD_GET_INCOMING_CHANNELS = 3025, - /* gossipd -> master: here they are. */ - WIRE_GOSSIPD_GET_INCOMING_CHANNELS_REPLY = 3125, /* master -> gossipd: blockheight increased. */ WIRE_GOSSIPD_NEW_BLOCKHEIGHT = 3026, /* Tell lightningd we got a onion message (for us */ @@ -91,32 +77,6 @@ bool fromwire_gossipd_init(const tal_t *ctx, const void *p, const struct chainpa u8 *towire_gossipd_dev_set_time(const tal_t *ctx, u32 dev_gossip_time); bool fromwire_gossipd_dev_set_time(const void *p, u32 *dev_gossip_time); -/* WIRE: GOSSIPD_GETNODES_REQUEST */ -/* Pass JSON-RPC getnodes call through */ -u8 *towire_gossipd_getnodes_request(const tal_t *ctx, const struct node_id *id); -bool fromwire_gossipd_getnodes_request(const tal_t *ctx, const void *p, struct node_id **id); - -/* WIRE: GOSSIPD_GETNODES_REPLY */ -u8 *towire_gossipd_getnodes_reply(const tal_t *ctx, const struct gossip_getnodes_entry **nodes); -bool fromwire_gossipd_getnodes_reply(const tal_t *ctx, const void *p, struct gossip_getnodes_entry ***nodes); - -/* WIRE: GOSSIPD_GETROUTE_REQUEST */ -/* Pass JSON-RPC getroute call through */ -u8 *towire_gossipd_getroute_request(const tal_t *ctx, const struct node_id *source, const struct node_id *destination, struct amount_msat msatoshi, u64 riskfactor_millionths, u32 final_cltv, u64 fuzz_millionths, const struct exclude_entry **excluded, u32 max_hops); -bool fromwire_gossipd_getroute_request(const tal_t *ctx, const void *p, struct node_id **source, struct node_id *destination, struct amount_msat *msatoshi, u64 *riskfactor_millionths, u32 *final_cltv, u64 *fuzz_millionths, struct exclude_entry ***excluded, u32 *max_hops); - -/* WIRE: GOSSIPD_GETROUTE_REPLY */ -u8 *towire_gossipd_getroute_reply(const tal_t *ctx, const struct route_hop **hops); -bool fromwire_gossipd_getroute_reply(const tal_t *ctx, const void *p, struct route_hop ***hops); - -/* WIRE: GOSSIPD_GETCHANNELS_REQUEST */ -u8 *towire_gossipd_getchannels_request(const tal_t *ctx, const struct short_channel_id *short_channel_id, const struct node_id *source, const struct short_channel_id *prev); -bool fromwire_gossipd_getchannels_request(const tal_t *ctx, const void *p, struct short_channel_id **short_channel_id, struct node_id **source, struct short_channel_id **prev); - -/* WIRE: GOSSIPD_GETCHANNELS_REPLY */ -u8 *towire_gossipd_getchannels_reply(const tal_t *ctx, bool complete, const struct gossip_getchannels_entry **nodes); -bool fromwire_gossipd_getchannels_reply(const tal_t *ctx, const void *p, bool *complete, struct gossip_getchannels_entry ***nodes); - /* WIRE: GOSSIPD_PING */ /* Ping/pong test. Waits for a reply if it expects one. */ u8 *towire_gossipd_ping(const tal_t *ctx, const struct node_id *id, u16 num_pong_bytes, u16 len); @@ -184,16 +144,6 @@ bool fromwire_gossipd_dev_compact_store(const void *p); u8 *towire_gossipd_dev_compact_store_reply(const tal_t *ctx, bool success); bool fromwire_gossipd_dev_compact_store_reply(const void *p, bool *success); -/* WIRE: GOSSIPD_GET_INCOMING_CHANNELS */ -/* master -> gossipd: get route_info for our incoming channels */ -u8 *towire_gossipd_get_incoming_channels(const tal_t *ctx); -bool fromwire_gossipd_get_incoming_channels(const void *p); - -/* WIRE: GOSSIPD_GET_INCOMING_CHANNELS_REPLY */ -/* gossipd -> master: here they are. */ -u8 *towire_gossipd_get_incoming_channels_reply(const tal_t *ctx, const struct route_info *public_route_info, const bool *public_deadends, const struct route_info *private_route_info, const bool *private_deadends); -bool fromwire_gossipd_get_incoming_channels_reply(const tal_t *ctx, const void *p, struct route_info **public_route_info, bool **public_deadends, struct route_info **private_route_info, bool **private_deadends); - /* WIRE: GOSSIPD_NEW_BLOCKHEIGHT */ /* master -> gossipd: blockheight increased. */ u8 *towire_gossipd_new_blockheight(const tal_t *ctx, u32 blockheight); @@ -225,4 +175,4 @@ bool fromwire_gossipd_addgossip_reply(const tal_t *ctx, const void *p, wirestrin #endif /* LIGHTNING_GOSSIPD_GOSSIPD_WIREGEN_H */ -// SHA256STAMP:a0d7494995d7f95fb7df295bab9d865e18670f15243116a0aaa9b9548534b922 +// SHA256STAMP:bc9045727cefbbe29118c8eae928972fe009a4d9d8c9e903f8b35f006973f462 diff --git a/gossipd/routing.c b/gossipd/routing.c index 96e24d9ae257..fabb469d6b55 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -94,8 +94,6 @@ HTABLE_DEFINE_TYPE(struct pending_node_announce, pending_node_announce_keyof, struct unupdated_channel { /* The channel_announcement message */ const u8 *channel_announce; - /* The feature bitmap within it */ - const u8 *features; /* The short_channel_id */ struct short_channel_id scid; /* The ids of the nodes */ @@ -381,8 +379,6 @@ static struct node *new_node(struct routing_state *rstate, n->id = *id; memset(n->chans.arr, 0, sizeof(n->chans.arr)); broadcastable_init(&n->bcast); - /* We don't know, so assume legacy. */ - n->hop_style = ROUTE_HOP_LEGACY; n->tokens = TOKEN_MAX; node_map_add(rstate->nodes, n); tal_add_destructor2(n, destroy_node, rstate); @@ -522,10 +518,6 @@ static void init_half_chan(struct routing_state *rstate, { struct half_chan *c = &chan->half[channel_idx]; - /* Set the channel direction */ - c->channel_flags = channel_idx; - // TODO: wireup message_flags - c->message_flags = 0; broadcastable_init(&c->bcast); c->tokens = TOKEN_MAX; } @@ -574,8 +566,7 @@ struct chan *new_chan(struct routing_state *rstate, const struct short_channel_id *scid, const struct node_id *id1, const struct node_id *id2, - struct amount_sat satoshis, - const u8 *features) + struct amount_sat satoshis) { struct chan *chan = tal(rstate, struct chan); int n1idx = node_id_idx(id1, id2); @@ -610,8 +601,6 @@ struct chan *new_chan(struct routing_state *rstate, init_half_chan(rstate, chan, n1idx); init_half_chan(rstate, chan, !n1idx); - /* Stash hint here about whether we have features */ - chan->half[0].any_features = tal_bytelen(features) != 0; uintmap_add(&rstate->chanmap, scid->u64, chan); /* Initialize shadow structure if it's local */ @@ -619,755 +608,6 @@ struct chan *new_chan(struct routing_state *rstate, return chan; } -/* Too big to reach, but don't overflow if added. */ -#define INFINITE AMOUNT_MSAT(0x3FFFFFFFFFFFFFFFULL) - -/* We hack a multimap into a uintmap to implement a minheap by cost. - * This is relatively inefficient, containing an array for each cost - * value, assuming there aren't too many at same cost. - * - * We further optimize by never freeing or shrinking these entries, - * but delete by replacing with NULL. This means that we cache the - * lowest index which actually contains something, since others may - * contain empty arrays. */ -struct unvisited { - u64 min_index; - UINTMAP(struct node **) map; -}; - - -/* Risk of passing through this channel. - * - * There are two ways this function is used: - * - * 1. Normally, riskbias = 1. A tiny bias here in order to prefer - * shorter routes, all things equal. - * 2. Trying to find a shorter route, riskbias > 1. By adding an extra - * cost to every hop, we're trying to bias against overlength routes. - */ -static WARN_UNUSED_RESULT bool risk_add_fee(struct amount_msat *risk, - struct amount_msat msat, - u32 delay, double riskfactor, - u64 riskbias) -{ - struct amount_msat riskfee; - - if (!amount_msat_scale(&riskfee, msat, riskfactor * delay)) - return false; - if (!amount_msat_add(&riskfee, riskfee, amount_msat(riskbias))) - return false; - return amount_msat_add(risk, *risk, riskfee); -} - -/* Check that we can fit through this channel's indicated - * maximum_ and minimum_msat requirements. - */ -static bool hc_can_carry(const struct half_chan *hc, - struct amount_msat requiredcap) -{ - return amount_msat_greater_eq(hc->htlc_maximum, requiredcap) && - amount_msat_less_eq(hc->htlc_minimum, requiredcap); -} - -/* Theoretically, this could overflow. */ -static bool fuzz_fee(u64 *fee, - const struct short_channel_id *scid, - double fuzz, const struct siphash_seed *base_seed) -{ - u64 fuzzed_fee, h; - double fee_scale; - - if (fuzz == 0.0) - return true; - - h = siphash24(base_seed, scid, sizeof(*scid)); - - /* Scale fees for this channel */ - /* rand = (h / UINT64_MAX) random number between 0.0 -> 1.0 - * 2*fuzz*rand random number between 0.0 -> 2*fuzz - * 2*fuzz*rand - fuzz random number between -fuzz -> +fuzz - */ - fee_scale = 1.0 + (2.0 * fuzz * h / (double)UINT64_MAX) - fuzz; - fuzzed_fee = *fee * fee_scale; - if (fee_scale > 1.0 && fuzzed_fee < *fee) - return false; - *fee = fuzzed_fee; - return true; -} - -/* Can we carry this amount across the channel? If so, returns true and - * sets newtotal and newrisk */ -static bool can_reach(const struct half_chan *c, - const struct short_channel_id *scid, - bool no_charge, - struct amount_msat total, - struct amount_msat risk, - double riskfactor, - u64 riskbias, - double fuzz, const struct siphash_seed *base_seed, - struct amount_msat *newtotal, struct amount_msat *newrisk) -{ - /* FIXME: Bias against smaller channels. */ - struct amount_msat fee; - - if (!amount_msat_fee(&fee, total, c->base_fee, c->proportional_fee)) - return false; - - if (!fuzz_fee(&fee.millisatoshis, scid, fuzz, base_seed)) /* Raw: double manipulation */ - return false; - - if (no_charge) { - *newtotal = total; - - /* We still want to consider the "charge", since it's indicative - * of a bias (we discounted one channel for a reason), but we - * don't pay it. So we count it as additional risk. */ - if (!amount_msat_add(newrisk, risk, fee)) - return false; - } else { - *newrisk = risk; - - if (!amount_msat_add(newtotal, total, fee)) - return false; - } - - /* Skip a channel if it indicated that it won't route the - * requested amount. */ - if (!hc_can_carry(c, *newtotal)) - return false; - - if (!risk_add_fee(newrisk, *newtotal, c->delay, riskfactor, riskbias)) - return false; - - return true; -} - -/* Returns false on overflow (shouldn't happen!) */ -typedef bool WARN_UNUSED_RESULT costfn_t(struct amount_msat *, - struct amount_msat, - struct amount_msat); - -static WARN_UNUSED_RESULT bool -normal_cost_function(struct amount_msat *cost, - struct amount_msat total, struct amount_msat risk) -{ - if (amount_msat_add(cost, total, risk)) - return true; - - status_broken("Can't add cost of node %s + %s", - type_to_string(tmpctx, struct amount_msat, &total), - type_to_string(tmpctx, struct amount_msat, &risk)); - return false; -} - -static WARN_UNUSED_RESULT bool -shortest_cost_function(struct amount_msat *cost, - struct amount_msat total, struct amount_msat risk) -{ - /* We add 1, so cost is never 0, for our hacky uintmap-as-minheap. */ - if (amount_msat_add(cost, risk, AMOUNT_MSAT(1))) - return true; - - status_broken("Can't add 1 to risk of node %s", - type_to_string(tmpctx, struct amount_msat, &risk)); - return false; -} - -/* Does totala+riska add up to less than totalb+riskb? - * Saves sums if you want them. - */ -static bool costs_less(struct amount_msat totala, - struct amount_msat riska, - struct amount_msat *costa, - struct amount_msat totalb, - struct amount_msat riskb, - struct amount_msat *costb, - costfn_t *costfn) -{ - struct amount_msat suma, sumb; - - if (!costfn(&suma, totala, riska)) - return false; - if (!costfn(&sumb, totalb, riskb)) - return false; - - if (costa) - *costa = suma; - if (costb) - *costb = sumb; - return amount_msat_less(suma, sumb); -} - -/* Determine if the given half_chan is routable */ -static bool hc_is_routable(struct routing_state *rstate, - const struct chan *chan, int idx) -{ - return is_halfchan_enabled(&chan->half[idx]) - && !is_chan_local_disabled(rstate, chan); -} - -static void unvisited_add(struct unvisited *unvisited, struct amount_msat cost, - struct node **arr) -{ - u64 idx = cost.millisatoshis; /* Raw: uintmap needs u64 index */ - if (idx < unvisited->min_index) { - assert(idx); /* We don't allow sending 0 satoshis */ - unvisited->min_index = idx - 1; - } - uintmap_add(&unvisited->map, idx, arr); -} - -static struct node **unvisited_get(const struct unvisited *unvisited, - struct amount_msat cost) -{ - return uintmap_get(&unvisited->map, cost.millisatoshis); /* Raw: uintmap */ -} - -static struct node **unvisited_del(struct unvisited *unvisited, - struct amount_msat cost) -{ - return uintmap_del(&unvisited->map, cost.millisatoshis); /* Raw: uintmap */ -} - -static bool is_unvisited(const struct node *node, - const struct unvisited *unvisited, - costfn_t *costfn) -{ - struct node **arr; - struct amount_msat cost; - - /* If it's infinite, definitely unvisited */ - if (amount_msat_eq(node->dijkstra.total, INFINITE)) - return true; - - /* Shouldn't happen! */ - if (!costfn(&cost, node->dijkstra.total, node->dijkstra.risk)) - return false; - - arr = unvisited_get(unvisited, cost); - for (size_t i = 0; i < tal_count(arr); i++) { - if (arr[i] == node) - return true; - } - return false; -} - -static void unvisited_del_node(struct unvisited *unvisited, - struct amount_msat cost, - const struct node *node) -{ - struct node **arr; - - arr = unvisited_get(unvisited, cost); - for (size_t i = 0; i < tal_count(arr); i++) { - if (arr[i] == node) { - arr[i] = NULL; - return; - } - } - abort(); -} - -static void adjust_unvisited(struct node *node, - struct unvisited *unvisited, - struct amount_msat cost_before, - struct amount_msat total, - struct amount_msat risk, - struct amount_msat cost_after) -{ - struct node **arr; - - /* If it was in unvisited map, remove it. */ - if (!amount_msat_eq(node->dijkstra.total, INFINITE)) - unvisited_del_node(unvisited, cost_before, node); - - /* Update node */ - node->dijkstra.total = total; - node->dijkstra.risk = risk; - - SUPERVERBOSE("%s now cost %s", - type_to_string(tmpctx, struct node_id, &node->id), - type_to_string(tmpctx, struct amount_msat, &cost_after)); - - /* Update map of unvisited nodes */ - arr = unvisited_get(unvisited, cost_after); - if (arr) { - struct node **old_arr; - /* Try for empty slot */ - for (size_t i = 0; i < tal_count(arr); i++) { - if (arr[i] == NULL) { - arr[i] = node; - return; - } - } - /* Nope, expand */ - old_arr = arr; - tal_arr_expand(&arr, node); - if (arr == old_arr) - return; - - /* Realloc moved it; del and add again. */ - unvisited_del(unvisited, cost_after); - } else { - arr = tal_arr(unvisited, struct node *, 1); - arr[0] = node; - } - - unvisited_add(unvisited, cost_after, arr); -} - -static void remove_unvisited(struct node *node, struct unvisited *unvisited, - costfn_t *costfn) -{ - struct amount_msat cost; - - /* Shouldn't happen! */ - if (!costfn(&cost, node->dijkstra.total, node->dijkstra.risk)) - return; - - unvisited_del_node(unvisited, cost, node); -} - -static void update_unvisited_neighbors(struct routing_state *rstate, - struct node *cur, - const struct node *me, - double riskfactor, - u64 riskbias, - double fuzz, - const struct siphash_seed *base_seed, - struct unvisited *unvisited, - costfn_t *costfn) -{ - struct chan_map_iter i; - struct chan *chan; - - /* Consider all neighbors */ - for (chan = first_chan(cur, &i); chan; chan = next_chan(cur, &i)) { - struct amount_msat total, risk, cost_before, cost_after; - int idx = half_chan_to(cur, chan); - struct node *peer = chan->nodes[idx]; - - SUPERVERBOSE("CONSIDERING: %s -> %s (%s/%s)", - type_to_string(tmpctx, struct node_id, - &cur->id), - type_to_string(tmpctx, struct node_id, - &peer->id), - type_to_string(tmpctx, struct amount_msat, - &peer->dijkstra.total), - type_to_string(tmpctx, struct amount_msat, - &peer->dijkstra.risk)); - - if (!hc_is_routable(rstate, chan, idx)) { - SUPERVERBOSE("... not routable"); - continue; - } - - if (!is_unvisited(peer, unvisited, costfn)) { - SUPERVERBOSE("... already visited"); - continue; - } - - /* We're looking at channels *backwards*, so peer == me - * is the right test here for whether we don't charge fees. */ - if (!can_reach(&chan->half[idx], &chan->scid, peer == me, - cur->dijkstra.total, cur->dijkstra.risk, - riskfactor, riskbias, fuzz, base_seed, - &total, &risk)) { - SUPERVERBOSE("... can't reach"); - continue; - } - - /* This effectively adds it to the map if it was infinite */ - if (costs_less(total, risk, &cost_after, - peer->dijkstra.total, peer->dijkstra.risk, - &cost_before, - costfn)) { - SUPERVERBOSE("...%s can reach %s" - " total %s risk %s", - type_to_string(tmpctx, struct node_id, - &cur->id), - type_to_string(tmpctx, struct node_id, - &peer->id), - type_to_string(tmpctx, struct amount_msat, - &total), - type_to_string(tmpctx, struct amount_msat, - &risk)); - adjust_unvisited(peer, unvisited, - cost_before, total, risk, cost_after); - } - } -} - -static struct node *first_unvisited(struct unvisited *unvisited) -{ - struct node **arr; - - while ((arr = uintmap_after(&unvisited->map, &unvisited->min_index))) { - for (size_t i = 0; i < tal_count(arr); i++) { - if (arr[i]) { - unvisited->min_index--; - return arr[i]; - } - } - } - - return NULL; -} - -static void dijkstra(struct routing_state *rstate, - const struct node *dst, - const struct node *me, - double riskfactor, - u64 riskbias, - double fuzz, const struct siphash_seed *base_seed, - struct unvisited *unvisited, - costfn_t *costfn) -{ - struct node *cur; - - while ((cur = first_unvisited(unvisited)) != NULL) { - update_unvisited_neighbors(rstate, cur, me, - riskfactor, riskbias, - fuzz, base_seed, unvisited, costfn); - remove_unvisited(cur, unvisited, costfn); - if (cur == dst) - return; - } -} - -/* Note that we calculated route *backwards*, for fees. So "from" - * here has a high cost, "to" has a cost of exact amount sent. */ -static struct chan **build_route(const tal_t *ctx, - struct routing_state *rstate, - const struct node *from, - const struct node *to, - const struct node *me, - double riskfactor, - u64 riskbias, - double fuzz, - const struct siphash_seed *base_seed, - struct amount_msat *fee) -{ - const struct node *i; - struct chan **route, *chan; - - SUPERVERBOSE("Building route from %s (%s) -> %s (%s)", - type_to_string(tmpctx, struct node_id, &from->id), - type_to_string(tmpctx, struct amount_msat, - &from->dijkstra.total), - type_to_string(tmpctx, struct node_id, &to->id), - type_to_string(tmpctx, struct amount_msat, - &to->dijkstra.total)); - /* Never reached? */ - if (amount_msat_eq(from->dijkstra.total, INFINITE)) - return NULL; - - /* Walk to find which neighbors we used */ - route = tal_arr(ctx, struct chan *, 0); - for (i = from; i != to; i = other_node(i, chan)) { - struct chan_map_iter it; - - /* Consider all neighbors */ - for (chan = first_chan(i, &it); chan; chan = next_chan(i, &it)) { - struct node *peer = other_node(i, chan); - struct half_chan *hc = half_chan_from(i, chan); - struct amount_msat total, risk; - - SUPERVERBOSE("CONSIDER: %s -> %s (%s/%s)", - type_to_string(tmpctx, struct node_id, - &i->id), - type_to_string(tmpctx, struct node_id, - &peer->id), - type_to_string(tmpctx, struct amount_msat, - &peer->dijkstra.total), - type_to_string(tmpctx, struct amount_msat, - &peer->dijkstra.risk)); - - /* If traversing this wasn't possible, ignore */ - if (!hc_is_routable(rstate, chan, !half_chan_to(i, chan))) { - continue; - } - - if (!can_reach(hc, &chan->scid, i == me, - peer->dijkstra.total, peer->dijkstra.risk, - riskfactor, - riskbias, - fuzz, base_seed, - &total, &risk)) - continue; - - /* If this was the path we took, we're done (if there are - * two identical ones, it doesn't matter which) */ - if (amount_msat_eq(total, i->dijkstra.total) - && amount_msat_eq(risk, i->dijkstra.risk)) - break; - } - - if (!chan) { - status_broken("Could not find hop to %s", - type_to_string(tmpctx, struct node_id, - &i->id)); - return tal_free(route); - } - tal_arr_expand(&route, chan); - } - - /* We don't charge ourselves fees, so skip first hop */ - if (!amount_msat_sub(fee, - other_node(from, route[0])->dijkstra.total, - to->dijkstra.total)) { - status_broken("Could not subtract %s - %s for fee", - type_to_string(tmpctx, struct amount_msat, - &other_node(from, route[0]) - ->dijkstra.total), - type_to_string(tmpctx, struct amount_msat, - &to->dijkstra.total)); - return tal_free(route); - } - - return route; -} - -static struct unvisited *dijkstra_prepare(const tal_t *ctx, - struct routing_state *rstate, - struct node *src, - struct amount_msat msat, - costfn_t *costfn) -{ - struct node_map_iter it; - struct unvisited *unvisited; - struct node *n; - struct node **arr; - struct amount_msat cost; - - unvisited = tal(tmpctx, struct unvisited); - uintmap_init(&unvisited->map); - unvisited->min_index = UINT64_MAX; - - /* Reset all the information. */ - for (n = node_map_first(rstate->nodes, &it); - n; - n = node_map_next(rstate->nodes, &it)) { - if (n == src) - continue; - n->dijkstra.total = INFINITE; - n->dijkstra.risk = INFINITE; - } - - /* Mark start cost: place in unvisited map. */ - src->dijkstra.total = msat; - src->dijkstra.risk = AMOUNT_MSAT(0); - arr = tal_arr(unvisited, struct node *, 1); - arr[0] = src; - /* Adding 0 can never fail */ - if (!costfn(&cost, src->dijkstra.total, src->dijkstra.risk)) - abort(); - unvisited_add(unvisited, cost, arr); - - return unvisited; -} - -static void dijkstra_cleanup(struct unvisited *unvisited) -{ - struct node **arr; - u64 idx; - - /* uintmap uses malloc, so manual cleaning needed */ - while ((arr = uintmap_first(&unvisited->map, &idx)) != NULL) { - tal_free(arr); - uintmap_del(&unvisited->map, idx); - } - tal_free(unvisited); -} - -/* We need to start biassing against long routes. */ -static struct chan ** -find_shorter_route(const tal_t *ctx, struct routing_state *rstate, - struct node *src, struct node *dst, - const struct node *me, - struct amount_msat msat, - u32 max_hops, - double fuzz, const struct siphash_seed *base_seed, - struct chan **long_route, - struct amount_msat *fee) -{ - struct unvisited *unvisited; - struct chan **short_route = NULL; - struct amount_msat long_cost, short_cost, cost_diff; - u64 min_bias, max_bias; - double riskfactor; - - /* We traverse backwards, so dst has largest total */ - if (!amount_msat_sub(&long_cost, - dst->dijkstra.total, src->dijkstra.total)) - goto bad_total; - tal_free(long_route); - - /* FIXME: It's hard to juggle both the riskfactor and riskbias here, - * so we set our riskfactor to rougly equate to 1 millisatoshi - * per block delay, which is close enough to zero to not break - * this algorithm, but still provide some bias towards - * low-delay routes. */ - riskfactor = amount_msat_ratio(AMOUNT_MSAT(1), msat); - - /* First, figure out if a short route is even possible. - * We set the cost function to ignore total, riskbias 1 and riskfactor - * ~0 so risk simply operates as a simple hop counter. */ - unvisited = dijkstra_prepare(tmpctx, rstate, src, msat, - shortest_cost_function); - SUPERVERBOSE("Running shortest path from %s -> %s", - type_to_string(tmpctx, struct node_id, &dst->id), - type_to_string(tmpctx, struct node_id, &src->id)); - dijkstra(rstate, dst, NULL, riskfactor, 1, fuzz, base_seed, - unvisited, shortest_cost_function); - dijkstra_cleanup(unvisited); - - /* This will usually succeed, since we found a route before; however - * it's possible that it fails in corner cases. Consider that the reduced - * riskfactor may make us select a more fee-expensive route, which then - * makes us unable to complete the route due to htlc_max restrictions. */ - short_route = build_route(ctx, rstate, dst, src, me, riskfactor, 1, - fuzz, base_seed, fee); - if (!short_route) { - status_info("Could't find short enough route %s->%s", - type_to_string(tmpctx, struct node_id, &dst->id), - type_to_string(tmpctx, struct node_id, &src->id)); - goto out; - } - - if (!amount_msat_sub(&short_cost, - dst->dijkstra.total, src->dijkstra.total)) - goto bad_total; - - /* Still too long? Oh well. */ - if (tal_count(short_route) > max_hops) { - status_info("Minimal possible route %s->%s is %zu", - type_to_string(tmpctx, struct node_id, &dst->id), - type_to_string(tmpctx, struct node_id, &src->id), - tal_count(short_route)); - goto out; - } - - /* OK, so it's possible, just more expensive. */ - min_bias = 0; - - if (!amount_msat_sub(&cost_diff, short_cost, long_cost)) { - status_broken("Short cost %s < long cost %s?", - type_to_string(tmpctx, struct amount_msat, - &short_cost), - type_to_string(tmpctx, struct amount_msat, - &long_cost)); - goto out; - } - - /* This is a gross overestimate, but it works. */ - max_bias = cost_diff.millisatoshis; /* Raw: bias calc */ - - SUPERVERBOSE("maxbias %"PRIu64" gave rlen %zu", - max_bias, tal_count(short_route)); - - /* Now, binary search */ - while (min_bias < max_bias) { - struct chan **route; - struct amount_msat this_fee; - u64 riskbias = (min_bias + max_bias) / 2; - - unvisited = dijkstra_prepare(tmpctx, rstate, src, msat, - normal_cost_function); - dijkstra(rstate, dst, me, riskfactor, riskbias, fuzz, base_seed, - unvisited, normal_cost_function); - dijkstra_cleanup(unvisited); - - route = build_route(ctx, rstate, dst, src, me, - riskfactor, riskbias, - fuzz, base_seed, &this_fee); - - SUPERVERBOSE("riskbias %"PRIu64" rlen %zu", - riskbias, tal_count(route)); - /* Too long still? This is our new min_bias */ - if (tal_count(route) > max_hops) { - tal_free(route); - min_bias = riskbias + 1; - } else { - /* This route is acceptable. */ - tal_free(short_route); - short_route = route; - /* Save this fee in case we exit loop */ - *fee = this_fee; - max_bias = riskbias; - } - } - - return short_route; - -bad_total: - status_broken("dst total %s < src total %s?", - type_to_string(tmpctx, struct amount_msat, - &dst->dijkstra.total), - type_to_string(tmpctx, struct amount_msat, - &src->dijkstra.total)); -out: - tal_free(short_route); - return NULL; -} - -/* riskfactor is already scaled to per-block amount */ -static struct chan ** -find_route(const tal_t *ctx, struct routing_state *rstate, - const struct node_id *from, const struct node_id *to, - struct amount_msat msat, - double riskfactor, - double fuzz, const struct siphash_seed *base_seed, - u32 max_hops, - struct amount_msat *fee) -{ - struct node *src, *dst; - const struct node *me; - struct unvisited *unvisited; - struct chan **route; - - /* Note: we map backwards, since we know the amount of satoshi we want - * at the end, and need to derive how much we need to send. */ - src = get_node(rstate, to); - - /* If from is NULL, that's means it's us. */ - if (!from) - me = dst = get_node(rstate, &rstate->local_id); - else { - dst = get_node(rstate, from); - me = NULL; - } - - if (!src) { - status_info("find_route: cannot find %s", - type_to_string(tmpctx, struct node_id, to)); - return NULL; - } else if (!dst) { - status_info("find_route: cannot find source (%s)", - type_to_string(tmpctx, struct node_id, to)); - return NULL; - } else if (dst == src) { - status_info("find_route: this is %s, refusing to create empty route", - type_to_string(tmpctx, struct node_id, to)); - return NULL; - } - - unvisited = dijkstra_prepare(tmpctx, rstate, src, msat, - normal_cost_function); - dijkstra(rstate, dst, me, riskfactor, 1, fuzz, base_seed, - unvisited, normal_cost_function); - dijkstra_cleanup(unvisited); - - route = build_route(ctx, rstate, dst, src, me, riskfactor, 1, - fuzz, base_seed, fee); - if (tal_count(route) <= max_hops) - return route; - - /* This is the far more unlikely case */ - return find_shorter_route(ctx, rstate, src, dst, me, msat, - max_hops, fuzz, base_seed, route, fee); -} - /* Checks that key is valid, and signed this hash */ static bool check_signed_hash_nodeid(const struct sha256_double *hash, const secp256k1_ecdsa_signature *signature, @@ -1656,7 +896,6 @@ bool routing_add_channel_announcement(struct routing_state *rstate, uc = tal(rstate, struct unupdated_channel); uc->channel_announce = tal_dup_talarr(uc, u8, msg); - uc->features = tal_steal(uc, features); uc->added = gossip_time_now(rstate); uc->index = index; uc->sat = sat; @@ -1990,34 +1229,6 @@ static void update_pending(struct pending_cannouncement *pending, } } -static void set_connection_values(struct chan *chan, - int idx, - u32 base_fee, - u32 proportional_fee, - u32 delay, - u8 message_flags, - u8 channel_flags, - u32 timestamp, - struct amount_msat htlc_minimum, - struct amount_msat htlc_maximum) -{ - struct half_chan *c = &chan->half[idx]; - - c->delay = delay; - c->htlc_minimum = htlc_minimum; - c->htlc_maximum = htlc_maximum; - c->base_fee = base_fee; - c->proportional_fee = proportional_fee; - c->message_flags = message_flags; - c->channel_flags = channel_flags; - c->bcast.timestamp = timestamp; - assert((c->channel_flags & ROUTING_FLAGS_DIRECTION) == idx); - - SUPERVERBOSE("Channel %s/%d was updated.", - type_to_string(tmpctx, struct short_channel_id, &chan->scid), - idx); -} - bool routing_add_channel_update(struct routing_state *rstate, const u8 *update TAKES, u32 index, @@ -2106,7 +1317,7 @@ bool routing_add_channel_update(struct routing_state *rstate, if (uc) { assert(!chan); chan = new_chan(rstate, &short_channel_id, - &uc->id[0], &uc->id[1], sat, uc->features); + &uc->id[0], &uc->id[1], sat); } /* Discard older updates */ @@ -2155,16 +1366,7 @@ bool routing_add_channel_update(struct routing_state *rstate, } } - /* FIXME: https://github.com/lightningnetwork/lightning-rfc/pull/512 - * says we MUST NOT exceed 2^32-1, but c-lightning did, so just trim - * rather than rejecting. */ - if (amount_msat_greater(htlc_maximum, chainparams->max_payment)) - htlc_maximum = chainparams->max_payment; - - set_connection_values(chan, direction, fee_base_msat, - fee_proportional_millionths, expiry, - message_flags, channel_flags, - timestamp, htlc_minimum, htlc_maximum); + chan->half[direction].bcast.timestamp = timestamp; /* Safe even if was never added, but if it's a private channel it * would be a WIRE_GOSSIP_STORE_PRIVATE_UPDATE. */ @@ -2500,12 +1702,6 @@ bool routing_add_node_announcement(struct routing_state *rstate, && node->bcast.timestamp < time_now().ts.tv_sec) rstate->last_timestamp = node->bcast.timestamp; - if (feature_offered(features, OPT_VAR_ONION)) - node->hop_style = ROUTE_HOP_TLV; - else - /* Reset it in case they no longer offer the feature */ - node->hop_style = ROUTE_HOP_LEGACY; - if (index) node->bcast.index = index; else { @@ -2608,131 +1804,6 @@ u8 *handle_node_announcement(struct routing_state *rstate, const u8 *node_ann, return NULL; } -struct route_hop **get_route(const tal_t *ctx, struct routing_state *rstate, - const struct node_id *source, - const struct node_id *destination, - struct amount_msat msat, double riskfactor, - u32 final_cltv, - double fuzz, u64 seed, - struct exclude_entry **excluded, - u32 max_hops) -{ - struct chan **route; - struct amount_msat total_amount; - unsigned int total_delay; - struct amount_msat fee; - struct route_hop **hops; - struct node *n; - struct amount_msat *saved_capacity; - struct short_channel_id_dir *excluded_chan; - struct siphash_seed base_seed; - - saved_capacity = tal_arr(tmpctx, struct amount_msat, 0); - excluded_chan = tal_arr(tmpctx, struct short_channel_id_dir, 0); - - base_seed.u.u64[0] = base_seed.u.u64[1] = seed; - - if (amount_msat_eq(msat, AMOUNT_MSAT(0))) - return NULL; - - /* Temporarily set the capacity of the excluded channels and the incoming channels - * of excluded nodes to zero. */ - for (size_t i = 0; i < tal_count(excluded); i++) { - if (excluded[i]->type == EXCLUDE_CHANNEL) { - struct short_channel_id_dir *chan_id = &excluded[i]->u.chan_id; - struct chan *chan = get_channel(rstate, &chan_id->scid); - if (!chan) - continue; - tal_arr_expand(&saved_capacity, chan->half[chan_id->dir].htlc_maximum); - tal_arr_expand(&excluded_chan, *chan_id); - chan->half[chan_id->dir].htlc_maximum = AMOUNT_MSAT(0); - } else { - assert(excluded[i]->type == EXCLUDE_NODE); - - struct node *node = get_node(rstate, &excluded[i]->u.node_id); - if (!node) - continue; - - struct chan_map_iter i; - struct chan *chan; - for (chan = first_chan(node, &i); chan; chan = next_chan(node, &i)) { - int dir = half_chan_to(node, chan); - tal_arr_expand(&saved_capacity, chan->half[dir].htlc_maximum); - - struct short_channel_id_dir id; - id.scid = chan->scid; - id.dir = dir; - tal_arr_expand(&excluded_chan, id); - - chan->half[dir].htlc_maximum = AMOUNT_MSAT(0); - } - } - } - - route = find_route(ctx, rstate, source, destination, msat, - riskfactor / BLOCKS_PER_YEAR / 100, - fuzz, &base_seed, max_hops, &fee); - - /* Now restore the capacity. */ - /* Restoring is done in reverse order, in order to properly - * handle the case where a channel is indicated twice in - * our input. - * Entries in `saved_capacity` of that channel beyond the - * first entry will be 0, only the first entry of that - * channel will be the correct capacity. - * By restoring in reverse order we ensure we can restore - * the correct capacity. - */ - for (ssize_t i = tal_count(excluded_chan) - 1; i >= 0; i--) { - struct chan *chan = get_channel(rstate, &excluded_chan[i].scid); - if (!chan) - continue; - chan->half[excluded_chan[i].dir].htlc_maximum = saved_capacity[i]; - } - - if (!route) { - return NULL; - } - - /* Fees, delays need to be calculated backwards along route. */ - hops = tal_arr(ctx, struct route_hop *, tal_count(route)); - total_amount = msat; - total_delay = final_cltv; - - /* Start at destination node. */ - n = get_node(rstate, destination); - for (int i = tal_count(route) - 1; i >= 0; i--) { - const struct half_chan *c; - - int idx = half_chan_to(n, route[i]); - c = &route[i]->half[idx]; - hops[i] = tal(hops, struct route_hop); - hops[i]->scid = route[i]->scid; - hops[i]->node_id = n->id; - hops[i]->amount = total_amount; - hops[i]->delay = total_delay; - hops[i]->direction = idx; - hops[i]->style = n->hop_style; - hops[i]->blinding = NULL; - hops[i]->enctlv = NULL; - - /* Since we calculated this route, it should not overflow! */ - if (!amount_msat_add_fee(&total_amount, - c->base_fee, c->proportional_fee)) { - status_broken("Route overflow step %i: %s + %u/%u!?", - i, type_to_string(tmpctx, struct amount_msat, - &total_amount), - c->base_fee, c->proportional_fee); - return tal_free(hops); - } - total_delay += c->delay; - n = other_node(n, route[i]); - } - assert(node_id_eq(&n->id, source ? source : &rstate->local_id)); - - return hops; -} - void route_prune(struct routing_state *rstate) { u64 now = gossip_time_now(rstate).ts.tv_sec; @@ -2834,8 +1905,7 @@ bool routing_add_private_channel(struct routing_state *rstate, type_to_string(tmpctx, struct short_channel_id, &scid)); /* Create new (unannounced) channel */ - chan = new_chan(rstate, &scid, &node_id[0], &node_id[1], sat, - features); + chan = new_chan(rstate, &scid, &node_id[0], &node_id[1], sat); if (!index) index = gossip_store_add(rstate->gs, msg, 0, false, NULL); chan->bcast.index = index; diff --git a/gossipd/routing.h b/gossipd/routing.h index 5cc4d5e96f88..a0ec756f09fe 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -20,34 +20,11 @@ struct peer; struct routing_state; struct half_chan { - /* millisatoshi. */ - u32 base_fee; - /* millionths */ - u32 proportional_fee; - - /* Delay for HTLC in blocks.*/ - u32 delay; - /* Timestamp and index into store file */ struct broadcastable bcast; - /* Flags as specified by the `channel_update`s, among other - * things indicated direction wrt the `channel_id` */ - u8 channel_flags; - - /* Flags as specified by the `channel_update`s, indicates - * optional fields. */ - u8 message_flags; - /* Token bucket */ u8 tokens; - - /* Feature cache for parent chan: squeezed in here where it would - * otherwise simply be padding. */ - u8 any_features; - - /* Minimum and maximum number of msatoshi in an HTLC */ - struct amount_msat htlc_minimum, htlc_maximum; }; struct chan { @@ -96,11 +73,6 @@ static inline bool is_halfchan_defined(const struct half_chan *hc) return hc->bcast.index != 0; } -static inline bool is_halfchan_enabled(const struct half_chan *hc) -{ - return is_halfchan_defined(hc) && !(hc->channel_flags & ROUTING_FLAGS_DISABLED); -} - /* Container for per-node channel pointers. Better cache performance * than uintmap, and we don't need ordering. */ static inline const struct short_channel_id *chan_map_scid(const struct chan *c) @@ -152,22 +124,11 @@ struct node { /* Token bucket */ u8 tokens; - /* route_hop_style */ - enum route_hop_style hop_style; - /* Channels connecting us to other nodes */ union { struct chan_map map; struct chan *arr[NUM_IMMEDIATE_CHANS+1]; } chans; - - /* Temporary data for routefinding. */ - struct { - /* Total to get to here from target. */ - struct amount_msat total; - /* Total risk premium of this route. */ - struct amount_msat risk; - } dijkstra; }; const struct node_id *node_map_keyof_node(const struct node *n); @@ -319,19 +280,6 @@ get_channel(const struct routing_state *rstate, return uintmap_get(&rstate->chanmap, scid->u64); } -enum exclude_entry_type { - EXCLUDE_CHANNEL = 1, - EXCLUDE_NODE = 2 -}; - -struct exclude_entry { - enum exclude_entry_type type; - union { - struct short_channel_id_dir chan_id; - struct node_id node_id; - } u; -}; - struct routing_state *new_routing_state(const tal_t *ctx, const struct node_id *local_id, struct list_head *peers, @@ -350,8 +298,7 @@ struct chan *new_chan(struct routing_state *rstate, const struct short_channel_id *scid, const struct node_id *id1, const struct node_id *id2, - struct amount_sat sat, - const u8 *features); + struct amount_sat sat); /* Handlers for incoming messages */ @@ -400,17 +347,6 @@ u8 *handle_node_announcement(struct routing_state *rstate, const u8 *node, struct node *get_node(struct routing_state *rstate, const struct node_id *id); -/* Compute a route to a destination, for a given amount and riskfactor. */ -struct route_hop **get_route(const tal_t *ctx, struct routing_state *rstate, - const struct node_id *source, - const struct node_id *destination, - const struct amount_msat msat, double riskfactor, - u32 final_cltv, - double fuzz, - u64 seed, - struct exclude_entry **excluded, - u32 max_hops); - void route_prune(struct routing_state *rstate); /** diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 6d1fb71d69b7..4063679e8a2e 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -134,14 +134,10 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) switch (t) { /* These are messages we send, not them. */ case WIRE_GOSSIPD_INIT: - case WIRE_GOSSIPD_GETNODES_REQUEST: - case WIRE_GOSSIPD_GETROUTE_REQUEST: - case WIRE_GOSSIPD_GETCHANNELS_REQUEST: case WIRE_GOSSIPD_PING: case WIRE_GOSSIPD_GET_STRIPPED_CUPDATE: case WIRE_GOSSIPD_GET_TXOUT_REPLY: case WIRE_GOSSIPD_OUTPOINT_SPENT: - case WIRE_GOSSIPD_GET_INCOMING_CHANNELS: case WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE: case WIRE_GOSSIPD_DEV_SUPPRESS: case WIRE_GOSSIPD_LOCAL_CHANNEL_CLOSE: @@ -152,10 +148,6 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_SEND_ONIONMSG: case WIRE_GOSSIPD_ADDGOSSIP: /* This is a reply, so never gets through to here. */ - case WIRE_GOSSIPD_GETNODES_REPLY: - case WIRE_GOSSIPD_GETROUTE_REPLY: - case WIRE_GOSSIPD_GETCHANNELS_REPLY: - case WIRE_GOSSIPD_GET_INCOMING_CHANNELS_REPLY: case WIRE_GOSSIPD_DEV_MEMLEAK_REPLY: case WIRE_GOSSIPD_DEV_COMPACT_STORE_REPLY: case WIRE_GOSSIPD_GET_STRIPPED_CUPDATE_REPLY: @@ -235,347 +227,6 @@ void gossipd_notify_spend(struct lightningd *ld, subd_send_msg(ld->gossip, msg); } -static void json_getnodes_reply(struct subd *gossip UNUSED, const u8 *reply, - const int *fds UNUSED, - struct command *cmd) -{ - struct gossip_getnodes_entry **nodes; - struct json_stream *response; - size_t i, j; - - if (!fromwire_gossipd_getnodes_reply(reply, reply, &nodes)) { - was_pending(command_fail(cmd, LIGHTNINGD, - "Malformed gossip_getnodes response")); - return; - } - - response = json_stream_success(cmd); - json_array_start(response, "nodes"); - - for (i = 0; i < tal_count(nodes); i++) { - struct json_escape *esc; - - json_object_start(response, NULL); - json_add_node_id(response, "nodeid", &nodes[i]->nodeid); - if (nodes[i]->last_timestamp < 0) { - json_object_end(response); - continue; - } - esc = json_escape(NULL, - take(tal_strndup(NULL, - (const char *)nodes[i]->alias, - ARRAY_SIZE(nodes[i]->alias)))); - json_add_escaped_string(response, "alias", take(esc)); - json_add_hex(response, "color", - nodes[i]->color, ARRAY_SIZE(nodes[i]->color)); - json_add_u64(response, "last_timestamp", - nodes[i]->last_timestamp); - json_add_hex_talarr(response, "features", nodes[i]->features); - json_array_start(response, "addresses"); - for (j=0; jaddresses); j++) { - json_add_address(response, NULL, &nodes[i]->addresses[j]); - } - json_array_end(response); - json_object_end(response); - } - json_array_end(response); - was_pending(command_success(cmd, response)); -} - -static struct command_result *json_listnodes(struct command *cmd, - const char *buffer, - const jsmntok_t *obj UNNEEDED, - const jsmntok_t *params) -{ - u8 *req; - struct node_id *id; - - if (!param(cmd, buffer, params, - p_opt("id", param_node_id, &id), - NULL)) - return command_param_failed(); - - req = towire_gossipd_getnodes_request(cmd, id); - subd_req(cmd, cmd->ld->gossip, req, -1, 0, json_getnodes_reply, cmd); - return command_still_pending(cmd); -} - -static const struct json_command listnodes_command = { - "listnodesold", - "network", - json_listnodes, - "Show node {id} (or all, if no {id}), in our local network view" -}; -AUTODATA(json_command, &listnodes_command); - -static void json_add_route_hop_style(struct json_stream *response, - const char *fieldname, - enum route_hop_style style) -{ - switch (style) { - case ROUTE_HOP_LEGACY: - json_add_string(response, fieldname, "legacy"); - return; - case ROUTE_HOP_TLV: - json_add_string(response, fieldname, "tlv"); - return; - } - fatal("Unknown route_hop_style %u", style); -} - -/* Output a route hop */ -static void json_add_route_hop(struct json_stream *r, char const *n, - const struct route_hop *h) -{ - /* Imitate what getroute/sendpay use */ - json_object_start(r, n); - json_add_node_id(r, "id", &h->node_id); - json_add_short_channel_id(r, "channel", &h->scid); - json_add_num(r, "direction", h->direction); - json_add_amount_msat_compat(r, h->amount, "msatoshi", "amount_msat"); - json_add_num(r, "delay", h->delay); - json_add_route_hop_style(r, "style", h->style); - json_object_end(r); -} - -/* Output a route */ -static void json_add_route(struct json_stream *r, char const *n, - struct route_hop **hops, size_t hops_len) -{ - size_t i; - json_array_start(r, n); - for (i = 0; i < hops_len; ++i) { - json_add_route_hop(r, NULL, hops[i]); - } - json_array_end(r); -} - -static void json_getroute_reply(struct subd *gossip UNUSED, const u8 *reply, const int *fds UNUSED, - struct command *cmd) -{ - struct json_stream *response; - struct route_hop **hops; - - fromwire_gossipd_getroute_reply(reply, reply, &hops); - - if (tal_count(hops) == 0) { - was_pending(command_fail(cmd, PAY_ROUTE_NOT_FOUND, - "Could not find a route")); - return; - } - - response = json_stream_success(cmd); - json_add_route(response, "route", hops, tal_count(hops)); - was_pending(command_success(cmd, response)); -} - -static struct command_result *json_getroute(struct command *cmd, - const char *buffer, - const jsmntok_t *obj UNNEEDED, - const jsmntok_t *params) -{ - struct lightningd *ld = cmd->ld; - struct node_id *destination; - struct node_id *source; - const jsmntok_t *excludetok; - struct amount_msat *msat; - u32 *cltv; - /* risk factor 12.345% -> riskfactor_millionths = 12345000 */ - u64 *riskfactor_millionths; - const struct exclude_entry **excluded; - u32 *max_hops; - - /* Higher fuzz means that some high-fee paths can be discounted - * for an even larger value, increasing the scope for route - * randomization (the higher-fee paths become more likely to - * be selected) at the cost of increasing the probability of - * selecting the higher-fee paths. */ - u64 *fuzz_millionths; /* fuzz 12.345% -> fuzz_millionths = 12345000 */ - - if (!param( - cmd, buffer, params, p_req("id", param_node_id, &destination), - p_req("msatoshi", param_msat, &msat), - p_req("riskfactor", param_millionths, &riskfactor_millionths), - p_opt_def("cltv", param_number, &cltv, 9), - p_opt("fromid", param_node_id, &source), - p_opt_def("fuzzpercent", param_millionths, &fuzz_millionths, - 5000000), - p_opt("exclude", param_array, &excludetok), - p_opt_def("maxhops", param_number, &max_hops, ROUTING_MAX_HOPS), - NULL)) - return command_param_failed(); - - /* Convert from percentage */ - *fuzz_millionths /= 100; - - if (excludetok) { - const jsmntok_t *t; - size_t i; - - excluded = tal_arr(cmd, const struct exclude_entry *, 0); - - json_for_each_arr(i, t, excludetok) { - struct exclude_entry *entry = tal(excluded, struct exclude_entry); - struct short_channel_id_dir *chan_id = tal(tmpctx, struct short_channel_id_dir); - if (!short_channel_id_dir_from_str(buffer + t->start, - t->end - t->start, - chan_id)) { - struct node_id *node_id = tal(tmpctx, struct node_id); - - if (!json_to_node_id(buffer, t, node_id)) - return command_fail_badparam(cmd, "exclude", - buffer, t, - "should be short_channel_id or node_id"); - - entry->type = EXCLUDE_NODE; - entry->u.node_id = *node_id; - } else { - entry->type = EXCLUDE_CHANNEL; - entry->u.chan_id = *chan_id; - } - - tal_arr_expand(&excluded, entry); - } - } else { - excluded = NULL; - } - - u8 *req = towire_gossipd_getroute_request( - cmd, source, destination, *msat, *riskfactor_millionths, *cltv, - *fuzz_millionths, excluded, *max_hops); - subd_req(ld->gossip, ld->gossip, req, -1, 0, json_getroute_reply, cmd); - return command_still_pending(cmd); -} - -static const struct json_command getroute_command = { - "getrouteold", - "channels", - json_getroute, - "Show route to {id} for {msatoshi}, using {riskfactor} and optional {cltv} (default 9). " - "If specified search from {fromid} otherwise use this node as source. " - "Randomize the route with up to {fuzzpercent} (default 5.0). " - "{exclude} an array of short-channel-id/direction (e.g. [ '564334x877x1/0', '564195x1292x0/1' ]) " - "or node-id from consideration. " - "Set the {maxhops} the route can take (default 20)." -}; -AUTODATA(json_command, &getroute_command); - -static void json_add_halfchan(struct json_stream *response, - const struct gossip_getchannels_entry *e, - int idx) -{ - const struct gossip_halfchannel_entry *he = e->e[idx]; - if (!he) - return; - - json_object_start(response, NULL); - json_add_node_id(response, "source", &e->node[idx]); - json_add_node_id(response, "destination", &e->node[!idx]); - json_add_short_channel_id(response, "short_channel_id", - &e->short_channel_id); - json_add_bool(response, "public", e->public); - json_add_amount_sat_compat(response, e->sat, - "satoshis", "amount_msat"); - json_add_num(response, "message_flags", he->message_flags); - json_add_num(response, "channel_flags", he->channel_flags); - json_add_bool(response, "active", - !(he->channel_flags & ROUTING_FLAGS_DISABLED) - && !e->local_disabled); - json_add_num(response, "last_update", he->last_update_timestamp); - json_add_num(response, "base_fee_millisatoshi", he->base_fee_msat); - json_add_num(response, "fee_per_millionth", he->fee_per_millionth); - json_add_num(response, "delay", he->delay); - json_add_amount_msat_only(response, "htlc_minimum_msat", he->min); - json_add_amount_msat_only(response, "htlc_maximum_msat", he->max); - json_add_hex_talarr(response, "features", e->features); - json_object_end(response); -} - -struct listchannels_info { - struct command *cmd; - struct json_stream *response; - struct short_channel_id *id; - struct node_id *source; -}; - -/* Called upon receiving a getchannels_reply from `gossipd` */ -static void json_listchannels_reply(struct subd *gossip UNUSED, const u8 *reply, - const int *fds UNUSED, - struct listchannels_info *linfo) -{ - size_t i; - struct gossip_getchannels_entry **entries; - bool complete; - - if (!fromwire_gossipd_getchannels_reply(reply, reply, - &complete, &entries)) { - /* Shouldn't happen: just end json stream. */ - log_broken(linfo->cmd->ld->log, "Invalid reply from gossipd"); - was_pending(command_raw_complete(linfo->cmd, linfo->response)); - return; - } - - for (i = 0; i < tal_count(entries); i++) { - json_add_halfchan(linfo->response, entries[i], 0); - json_add_halfchan(linfo->response, entries[i], 1); - } - - /* More coming? Ask from this point on.. */ - if (!complete) { - u8 *req; - assert(tal_count(entries) != 0); - req = towire_gossipd_getchannels_request(linfo->cmd, - linfo->id, - linfo->source, - &entries[i-1] - ->short_channel_id); - subd_req(linfo->cmd->ld->gossip, linfo->cmd->ld->gossip, - req, -1, 0, json_listchannels_reply, linfo); - } else { - json_array_end(linfo->response); - was_pending(command_success(linfo->cmd, linfo->response)); - } -} - -static struct command_result *json_listchannels(struct command *cmd, - const char *buffer, - const jsmntok_t *obj UNNEEDED, - const jsmntok_t *params) -{ - u8 *req; - struct listchannels_info *linfo = tal(cmd, struct listchannels_info); - - linfo->cmd = cmd; - if (!param(cmd, buffer, params, - p_opt("short_channel_id", param_short_channel_id, &linfo->id), - p_opt("source", param_node_id, &linfo->source), - NULL)) - return command_param_failed(); - - if (linfo->id && linfo->source) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Cannot specify both source and short_channel_id"); - - /* Start JSON response, then we stream. */ - linfo->response = json_stream_success(cmd); - json_array_start(linfo->response, "channels"); - - req = towire_gossipd_getchannels_request(cmd, linfo->id, linfo->source, - NULL); - subd_req(cmd->ld->gossip, cmd->ld->gossip, - req, -1, 0, json_listchannels_reply, linfo); - - return command_still_pending(cmd); -} - -static const struct json_command listchannels_command = { - "listchannelsold", - "channels", - json_listchannels, - "Show channel {short_channel_id} or {source} (or all known channels, if not specified)" -}; -AUTODATA(json_command, &listchannels_command); - /* Called upon receiving a addgossip_reply from `gossipd` */ static void json_addgossip_reply(struct subd *gossip UNUSED, const u8 *reply, const int *fds UNUSED, diff --git a/lightningd/gossip_msg.c b/lightningd/gossip_msg.c index bc827c938b91..47eb5bb93e2b 100644 --- a/lightningd/gossip_msg.c +++ b/lightningd/gossip_msg.c @@ -194,32 +194,3 @@ void towire_gossip_getchannels_entry(u8 **pptr, } else towire_bool(pptr, false); } - -struct exclude_entry *fromwire_exclude_entry(const tal_t *ctx, - const u8 **pptr, size_t *max) -{ - struct exclude_entry *entry = tal(ctx, struct exclude_entry); - entry->type = fromwire_u8(pptr, max); - switch (entry->type) { - case EXCLUDE_CHANNEL: - fromwire_short_channel_id_dir(pptr, max, &entry->u.chan_id); - return entry; - case EXCLUDE_NODE: - fromwire_node_id(pptr, max, &entry->u.node_id); - return entry; - default: - return fromwire_fail(pptr, max); - } -} - -void towire_exclude_entry(u8 **pptr, const struct exclude_entry *entry) -{ - assert(entry->type == EXCLUDE_CHANNEL || - entry->type == EXCLUDE_NODE); - - towire_u8(pptr, entry->type); - if (entry->type == EXCLUDE_CHANNEL) - towire_short_channel_id_dir(pptr, &entry->u.chan_id); - else - towire_node_id(pptr, &entry->u.node_id); -} diff --git a/lightningd/gossip_msg.h b/lightningd/gossip_msg.h index 5f405619212a..fe1f48eca82d 100644 --- a/lightningd/gossip_msg.h +++ b/lightningd/gossip_msg.h @@ -54,9 +54,4 @@ fromwire_gossip_getchannels_entry(const tal_t *ctx, void towire_gossip_getchannels_entry( u8 **pptr, const struct gossip_getchannels_entry *entry); -struct exclude_entry * -fromwire_exclude_entry(const tal_t *ctx, - const u8 **pptr, size_t *max); -void towire_exclude_entry(u8 **pptr, const struct exclude_entry *entry); - #endif /* LIGHTNING_LIGHTNINGD_GOSSIP_MSG_H */ diff --git a/lightningd/pay.c b/lightningd/pay.c index 778f955efb4a..f792d268cd4b 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include From a0e10204324f2468bd417cf0e6b4576a72d37e43 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 15 Jun 2021 06:37:39 +0930 Subject: [PATCH 17/17] lightningd: wait for gossipd to finish initalizing before starting plugins. This mainly helps our CI under valgrind, which starts a fresh instance and immediately calls the invoice command. This can cause the topology plugin to try to access the gossmap file before it's created. We can also move the gossmap reading in topology to init time. Signed-off-by: Rusty Russell --- gossipd/gossipd.c | 4 ++++ gossipd/gossipd_wire.csv | 2 ++ gossipd/gossipd_wiregen.c | 23 ++++++++++++++++++++++- gossipd/gossipd_wiregen.h | 7 ++++++- lightningd/gossip_control.c | 20 ++++++++++++++++++-- plugins/topology.c | 21 +++++++++------------ 6 files changed, 61 insertions(+), 16 deletions(-) diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index eea5fd3d6904..d13ba5e4652c 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -1135,6 +1135,9 @@ static struct io_plan *gossip_init(struct io_conn *conn, daemon->connectd = daemon_conn_new(daemon, CONNECTD_FD, connectd_req, NULL, daemon); + /* OK, we are ready. */ + daemon_conn_send(daemon->master, + take(towire_gossipd_init_reply(NULL))); return daemon_conn_read_next(conn, daemon->master); } @@ -1507,6 +1510,7 @@ static struct io_plan *recv_req(struct io_conn *conn, return onionmsg_req(conn, daemon, msg); /* We send these, we don't receive them */ case WIRE_GOSSIPD_PING_REPLY: + case WIRE_GOSSIPD_INIT_REPLY: case WIRE_GOSSIPD_GET_STRIPPED_CUPDATE_REPLY: case WIRE_GOSSIPD_GET_TXOUT: case WIRE_GOSSIPD_DEV_MEMLEAK_REPLY: diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index bb51590e2803..2dbd2b2245d7 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -16,6 +16,8 @@ msgdata,gossipd_init,dev_gossip_time,?u32, msgdata,gossipd_init,dev_fast_gossip,bool, msgdata,gossipd_init,dev_fast_gossip_prune,bool, +msgtype,gossipd_init_reply,3100 + # In developer mode, we can mess with time. msgtype,gossipd_dev_set_time,3001 msgdata,gossipd_dev_set_time,dev_gossip_time,u32, diff --git a/gossipd/gossipd_wiregen.c b/gossipd/gossipd_wiregen.c index e779e960192f..b5f6a2b328e0 100644 --- a/gossipd/gossipd_wiregen.c +++ b/gossipd/gossipd_wiregen.c @@ -21,6 +21,7 @@ const char *gossipd_wire_name(int e) switch ((enum gossipd_wire)e) { case WIRE_GOSSIPD_INIT: return "WIRE_GOSSIPD_INIT"; + case WIRE_GOSSIPD_INIT_REPLY: return "WIRE_GOSSIPD_INIT_REPLY"; case WIRE_GOSSIPD_DEV_SET_TIME: return "WIRE_GOSSIPD_DEV_SET_TIME"; case WIRE_GOSSIPD_PING: return "WIRE_GOSSIPD_PING"; case WIRE_GOSSIPD_PING_REPLY: return "WIRE_GOSSIPD_PING_REPLY"; @@ -52,6 +53,7 @@ bool gossipd_wire_is_defined(u16 type) { switch ((enum gossipd_wire)type) { case WIRE_GOSSIPD_INIT:; + case WIRE_GOSSIPD_INIT_REPLY:; case WIRE_GOSSIPD_DEV_SET_TIME:; case WIRE_GOSSIPD_PING:; case WIRE_GOSSIPD_PING_REPLY:; @@ -139,6 +141,25 @@ bool fromwire_gossipd_init(const tal_t *ctx, const void *p, const struct chainpa return cursor != NULL; } +/* WIRE: GOSSIPD_INIT_REPLY */ +u8 *towire_gossipd_init_reply(const tal_t *ctx) +{ + u8 *p = tal_arr(ctx, u8, 0); + + towire_u16(&p, WIRE_GOSSIPD_INIT_REPLY); + + return memcheck(p, tal_count(p)); +} +bool fromwire_gossipd_init_reply(const void *p) +{ + const u8 *cursor = p; + size_t plen = tal_count(p); + + if (fromwire_u16(&cursor, &plen) != WIRE_GOSSIPD_INIT_REPLY) + return false; + return cursor != NULL; +} + /* WIRE: GOSSIPD_DEV_SET_TIME */ /* In developer mode */ u8 *towire_gossipd_dev_set_time(const tal_t *ctx, u32 dev_gossip_time) @@ -732,4 +753,4 @@ bool fromwire_gossipd_addgossip_reply(const tal_t *ctx, const void *p, wirestrin *err = fromwire_wirestring(ctx, &cursor, &plen); return cursor != NULL; } -// SHA256STAMP:bc9045727cefbbe29118c8eae928972fe009a4d9d8c9e903f8b35f006973f462 +// SHA256STAMP:6f6810a7e9c5e3e7dc1dd716b6a8de019516f6e82e3664bdf760647b5a987883 diff --git a/gossipd/gossipd_wiregen.h b/gossipd/gossipd_wiregen.h index 5059fb559d93..5e2c5f4a3cd4 100644 --- a/gossipd/gossipd_wiregen.h +++ b/gossipd/gossipd_wiregen.h @@ -15,6 +15,7 @@ enum gossipd_wire { /* Initialize the gossip daemon. */ WIRE_GOSSIPD_INIT = 3000, + WIRE_GOSSIPD_INIT_REPLY = 3100, /* In developer mode */ WIRE_GOSSIPD_DEV_SET_TIME = 3001, /* Ping/pong test. Waits for a reply if it expects one. */ @@ -72,6 +73,10 @@ bool gossipd_wire_is_defined(u16 type); u8 *towire_gossipd_init(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_features, const struct node_id *id, const u8 rgb[3], const u8 alias[32], const struct wireaddr *announcable, u32 *dev_gossip_time, bool dev_fast_gossip, bool dev_fast_gossip_prune); bool fromwire_gossipd_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_features, struct node_id *id, u8 rgb[3], u8 alias[32], struct wireaddr **announcable, u32 **dev_gossip_time, bool *dev_fast_gossip, bool *dev_fast_gossip_prune); +/* WIRE: GOSSIPD_INIT_REPLY */ +u8 *towire_gossipd_init_reply(const tal_t *ctx); +bool fromwire_gossipd_init_reply(const void *p); + /* WIRE: GOSSIPD_DEV_SET_TIME */ /* In developer mode */ u8 *towire_gossipd_dev_set_time(const tal_t *ctx, u32 dev_gossip_time); @@ -175,4 +180,4 @@ bool fromwire_gossipd_addgossip_reply(const tal_t *ctx, const void *p, wirestrin #endif /* LIGHTNING_GOSSIPD_GOSSIPD_WIREGEN_H */ -// SHA256STAMP:bc9045727cefbbe29118c8eae928972fe009a4d9d8c9e903f8b35f006973f462 +// SHA256STAMP:6f6810a7e9c5e3e7dc1dd716b6a8de019516f6e82e3664bdf760647b5a987883 diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 4063679e8a2e..1b2fd6a70175 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -148,6 +148,7 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_SEND_ONIONMSG: case WIRE_GOSSIPD_ADDGOSSIP: /* This is a reply, so never gets through to here. */ + case WIRE_GOSSIPD_INIT_REPLY: case WIRE_GOSSIPD_DEV_MEMLEAK_REPLY: case WIRE_GOSSIPD_DEV_COMPACT_STORE_REPLY: case WIRE_GOSSIPD_GET_STRIPPED_CUPDATE_REPLY: @@ -187,6 +188,16 @@ static void gossip_topology_synced(struct chain_topology *topo, void *unused) gossip_notify_new_block(topo->ld, get_block_height(topo)); } +/* We make sure gossipd is started before plugins (which may want gossip_map) */ +static void gossipd_init_done(struct subd *gossipd, + const u8 *msg, + const int *fds, + void *unused) +{ + /* Break out of loop, so we can begin */ + io_break(gossipd); +} + /* Create the `gossipd` subdaemon and send the initialization * message */ void gossip_init(struct lightningd *ld, int connectd_fd) @@ -207,7 +218,7 @@ void gossip_init(struct lightningd *ld, int connectd_fd) gossip_topology_synced, NULL); msg = towire_gossipd_init( - tmpctx, + NULL, chainparams, ld->our_features, &ld->id, @@ -217,7 +228,12 @@ void gossip_init(struct lightningd *ld, int connectd_fd) IFDEV(ld->dev_gossip_time ? &ld->dev_gossip_time: NULL, NULL), IFDEV(ld->dev_fast_gossip, false), IFDEV(ld->dev_fast_gossip_prune, false)); - subd_send_msg(ld->gossip, msg); + + subd_req(ld->gossip, ld->gossip, take(msg), -1, 0, + gossipd_init_done, NULL); + + /* Wait for gossipd_init_reply */ + io_loop(NULL, NULL); } void gossipd_notify_spend(struct lightningd *ld, diff --git a/plugins/topology.c b/plugins/topology.c index 47490f754a62..2fbe1db76bd3 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -20,24 +20,15 @@ #include /* Access via get_gossmap() */ +static struct gossmap *global_gossmap; static struct node_id local_id; static struct plugin *plugin; /* We load this on demand, since we can start before gossipd. */ static struct gossmap *get_gossmap(void) { - static struct gossmap *gossmap; - - if (gossmap) - gossmap_refresh(gossmap); - else { - gossmap = notleak_with_children(gossmap_load(NULL, - GOSSIP_STORE_FILENAME)); - if (!gossmap) - plugin_err(plugin, "Could not load gossmap %s: %s", - GOSSIP_STORE_FILENAME, strerror(errno)); - } - return gossmap; + gossmap_refresh(global_gossmap); + return global_gossmap; } /* Convenience global since route_score_fuzz doesn't take args. 0 to 1. */ @@ -677,6 +668,12 @@ static const char *init(struct plugin *p, take(json_out_obj(NULL, NULL, NULL)), "{id:%}", JSON_SCAN(json_to_node_id, &local_id)); + global_gossmap = notleak_with_children(gossmap_load(NULL, + GOSSIP_STORE_FILENAME)); + if (!global_gossmap) + plugin_err(plugin, "Could not load gossmap %s: %s", + GOSSIP_STORE_FILENAME, strerror(errno)); + return NULL; }