From 2be1f3fe1bf78b28bce45facdb2ad35711473678 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 8 Sep 2020 14:27:53 +0930 Subject: [PATCH] lightningd: extract routehint selection code. We're going to want this for bolt13 formation as well. As a result of reworking the logic into "candidate selection" then "route hint selection", we need to change the way round-robin works. We use a simple incrementing index now. Signed-off-by: Rusty Russell --- lightningd/Makefile | 1 + lightningd/channel.c | 4 +- lightningd/channel.h | 2 +- lightningd/invoice.c | 275 +++++--------------- lightningd/lightningd.c | 2 +- lightningd/lightningd.h | 2 +- lightningd/routehint.c | 134 ++++++++++ lightningd/routehint.h | 40 +++ lightningd/test/run-invoice-select-inchan.c | 194 +++++++------- wallet/db_postgres_sqlgen.c | 2 +- wallet/db_sqlite3_sqlgen.c | 2 +- wallet/statements_gettextgen.po | 2 +- wallet/test/run-wallet.c | 2 +- 13 files changed, 356 insertions(+), 306 deletions(-) create mode 100644 lightningd/routehint.c create mode 100644 lightningd/routehint.h diff --git a/lightningd/Makefile b/lightningd/Makefile index 09e41a536..91707998a 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -33,6 +33,7 @@ LIGHTNINGD_SRC := \ lightningd/plugin.c \ lightningd/plugin_control.c \ lightningd/plugin_hook.c \ + lightningd/routehint.c \ lightningd/subd.c \ lightningd/watch.c diff --git a/lightningd/channel.c b/lightningd/channel.c index 5c641c351..4feed7e47 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -110,8 +110,6 @@ static void destroy_channel(struct channel *channel) channel_set_owner(channel, NULL); list_del_from(&channel->peer->channels, &channel->list); - - list_del(&channel->rr_list); } void delete_channel(struct channel *channel STEALS) @@ -277,7 +275,7 @@ struct channel *new_channel(struct peer *peer, u64 dbid, channel->forgets = tal_arr(channel, struct command *, 0); list_add_tail(&peer->channels, &channel->list); - list_add_tail(&peer->ld->rr_channels, &channel->rr_list); + channel->rr_number = peer->ld->rr_counter++; tal_add_destructor(channel, destroy_channel); /* Make sure we see any spends using this key */ diff --git a/lightningd/channel.h b/lightningd/channel.h index 3d817c00e..3d23a7558 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -138,7 +138,7 @@ struct channel { struct command **forgets; /* Our position in the round-robin list. */ - struct list_node rr_list; + u64 rr_number; }; struct channel *new_channel(struct peer *peer, u64 dbid, diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 7c1a46598..fbf5a0396 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -445,42 +447,27 @@ static struct command_result *parse_fallback(struct command *cmd, * Then use weighted reservoir sampling, which makes probing channel balances * harder, to choose one channel from the set of suitable channels. It favors * channels that have less balance on our side as fraction of their capacity. - * - * [any_offline] is set if the peer of any suitable channel appears offline. */ static struct route_info **select_inchan(const tal_t *ctx, struct lightningd *ld, struct amount_msat amount_needed, - const struct route_info *inchans, - const bool *deadends, - bool *any_offline) + const struct routehint_candidate + *candidates) { /* BOLT11 struct wants an array of arrays (can provide multiple routes) */ struct route_info **r = NULL; double total_weight = 0.0; - *any_offline = false; - /* Collect suitable channels and assign each a weight. */ - for (size_t i = 0; i < tal_count(inchans); i++) { - struct peer *peer; - struct channel *c; - struct amount_msat capacity_to_pay_us, excess, capacity; + for (size_t i = 0; i < tal_count(candidates); i++) { + struct amount_msat excess, capacity; struct amount_sat cumulative_reserve; double excess_frac; - /* Do we know about this peer? */ - peer = peer_by_id(ld, &inchans[i].pubkey); - if (!peer) - continue; - - /* Does it have a channel in state CHANNELD_NORMAL */ - c = peer_normal_channel(peer); - if (!c) - continue; - - /* Is it a dead-end? */ - if (deadends[i]) + /* Does the peer have sufficient balance to pay us, + * even after having taken into account their reserve? */ + if (!amount_msat_sub(&excess, candidates[i].capacity, + amount_needed)) continue; /* Channel balance as seen by our node: @@ -497,27 +484,14 @@ static struct route_info **select_inchan(const tal_t *ctx, 0 ^ ^ ^ funding our_reserve our_msat */ - capacity_to_pay_us = channel_amount_receivable(c); - - /* Does the peer have sufficient balance to pay us, - * even after having taken into account their reserve? */ - if (!amount_msat_sub(&excess, capacity_to_pay_us, amount_needed)) - continue; - - /* Is it offline? */ - if (c->owner == NULL) { - *any_offline = true; - continue; - } - /* Find capacity and calculate its excess fraction */ if (!amount_sat_add(&cumulative_reserve, - c->our_config.channel_reserve, - c->channel_info.their_config.channel_reserve) - || !amount_sat_to_msat(&capacity, c->funding) + candidates[i].c->our_config.channel_reserve, + candidates[i].c->channel_info.their_config.channel_reserve) + || !amount_sat_to_msat(&capacity, candidates[i].c->funding) || !amount_msat_sub_sat(&capacity, capacity, cumulative_reserve)) { log_broken(ld->log, "Channel %s capacity overflow!", - type_to_string(tmpctx, struct short_channel_id, c->scid)); + type_to_string(tmpctx, struct short_channel_id, candidates[i].c->scid)); continue; } @@ -525,7 +499,9 @@ static struct route_info **select_inchan(const tal_t *ctx, * only one! So bump it by 1 msat */ if (!amount_msat_add(&excess, excess, AMOUNT_MSAT(1))) { log_broken(ld->log, "Channel %s excess overflow!", - type_to_string(tmpctx, struct short_channel_id, c->scid)); + type_to_string(tmpctx, + struct short_channel_id, + candidates[i].c->scid)); continue; } excess_frac = amount_msat_ratio(excess, capacity); @@ -533,13 +509,24 @@ static struct route_info **select_inchan(const tal_t *ctx, if (random_select(excess_frac, &total_weight)) { tal_free(r); r = tal_arr(ctx, struct route_info *, 1); - r[0] = tal_dup(r, struct route_info, &inchans[i]); + r[0] = tal_dup(r, struct route_info, candidates[i].r); } } return r; } +static int cmp_rr_number(const struct routehint_candidate *a, + const struct routehint_candidate *b, + void *unused) +{ + /* They're unique, so can't be equal */ + if (a->c->rr_number > b->c->rr_number) + return 1; + assert(a->c->rr_number < b->c->rr_number); + return -1; +} + /** select_inchan_mpp * * @brief fallback in case select_inchan cannot find a *single* @@ -552,82 +539,41 @@ static struct route_info **select_inchan(const tal_t *ctx, static struct route_info **select_inchan_mpp(const tal_t *ctx, struct lightningd *ld, struct amount_msat amount_needed, - const struct route_info *inchans, - const bool *deadends, - bool *any_offline, + struct routehint_candidate + *candidates, bool *warning_mpp_capacity) { /* The total amount we have gathered for incoming channels. */ struct amount_msat gathered; - /* Channels we have already processed. */ - struct list_head processed; /* Routehint array. */ struct route_info **routehints; gathered = AMOUNT_MSAT(0); - list_head_init(&processed); routehints = tal_arr(ctx, struct route_info *, 0); - while (amount_msat_less(gathered, amount_needed) - && !list_empty(&ld->rr_channels)) { - struct channel *c; - struct amount_msat capacity_to_pay_us; - size_t found_i; - const struct route_info *found; - - /* Get a channel and put it in the processed list. */ - c = list_pop(&ld->rr_channels, struct channel, rr_list); - list_add_tail(&processed, &c->rr_list); - - /* Is the channel even useful? */ - if (c->state != CHANNELD_NORMAL) - continue; - /* SCID should have been set when we locked in, and we - * can only CHANNELD_NORMAL if both us and peer are - * locked in. */ - assert(c->scid != NULL); - - /* Is the peer offline? */ - if (c->owner == NULL) { - *any_offline = true; - continue; - } - - capacity_to_pay_us = channel_amount_receivable(c); - - /* Is the channel in the inchans input? */ - found = NULL; - found_i = 0; - for (size_t i = 0; i < tal_count(inchans); ++i) { - if (short_channel_id_eq(&inchans[i].short_channel_id, - c->scid)) { - found = &inchans[i]; - found_i = i; - break; - } - } - if (!found) - continue; - - /* Is it a deadend? */ - if (deadends[found_i]) - continue; + /* Sort by rr_number, so we get fresh channels. */ + asort(candidates, tal_count(candidates), cmp_rr_number, NULL); + for (size_t i = 0; i < tal_count(candidates); i++) { + if (amount_msat_greater_eq(gathered, amount_needed)) + break; /* Add to current routehints set. */ - if (!amount_msat_add(&gathered, gathered, capacity_to_pay_us)) { + if (!amount_msat_add(&gathered, gathered, candidates[i].capacity)) { log_broken(ld->log, "Gathered channel capacity overflow: " "%s + %s", type_to_string(tmpctx, struct amount_msat, &gathered), - type_to_string(tmpctx, struct amount_msat, &capacity_to_pay_us)); + type_to_string(tmpctx, struct amount_msat, + &candidates[i].capacity)); continue; } tal_arr_expand(&routehints, - tal_dup(routehints, struct route_info, found)); + tal_dup(routehints, struct route_info, + candidates[i].r)); + /* Put to the back of the round-robin list */ + candidates[i].c->rr_number = ld->rr_counter++; } - /* Append the processed list back to the rr_channels. */ - list_append_list(&ld->rr_channels, &processed); /* Check if we gathered enough. */ *warning_mpp_capacity = amount_msat_less(gathered, amount_needed); @@ -648,114 +594,39 @@ struct invoice_info { struct chanhints *chanhints; }; -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 all_true(const bool *barr, size_t n) -{ - for (size_t i = 0; i < n; i++) { - if (!barr[i]) - return false; - } - return true; -} - -static bool scid_in_arr(const struct short_channel_id *scidarr, - const struct short_channel_id *scid) -{ - for (size_t i = 0; i < tal_count(scidarr); i++) - if (short_channel_id_eq(&scidarr[i], scid)) - return true; - - return false; -} - static void gossipd_incoming_channels_reply(struct subd *gossipd, const u8 *msg, const int *fs, struct invoice_info *info) { struct json_stream *response; - struct route_info *inchans, *private; - bool *inchan_deadends, *private_deadends; struct invoice invoice; char *b11enc; const struct invoice_details *details; struct wallet *wallet = info->cmd->ld->wallet; const struct chanhints *chanhints = info->chanhints; - bool any_offline = false; + struct routehint_candidate *candidates; + struct amount_msat offline_amt; bool warning_mpp = false; bool warning_mpp_capacity = false; + bool deadends; bool node_unpublished; - if (!fromwire_gossipd_get_incoming_channels_reply(tmpctx, msg, - &inchans, - &inchan_deadends, - &private, - &private_deadends)) - fatal("Gossip gave bad GOSSIP_GET_INCOMING_CHANNELS_REPLY %s", - tal_hex(msg, msg)); + candidates = routehint_candidates(tmpctx, info->cmd->ld, msg, + chanhints ? chanhints->expose_all_private : false, + chanhints ? chanhints->hints : NULL, + &node_unpublished, + &deadends, + &offline_amt); - node_unpublished = (tal_count(inchans) == 0) - && (tal_count(private) > 0); - - /* 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 (chanhints && chanhints->expose_all_private) { - append_routes(&inchans, private); - append_bools(&inchan_deadends, private_deadends); - } else if (chanhints && chanhints->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(chanhints->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; - } - - /* If they told us to use scids and we couldn't, fail. */ - if (tal_count(inchans) == 0 - && 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; - } - } else { - assert(!chanhints); - /* 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); - } + /* 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; } if (tal_count(info->b11->routes) == 0) { @@ -781,24 +652,19 @@ static void gossipd_incoming_channels_reply(struct subd *gossipd, info->b11->routes = select_inchan(info->b11, info->cmd->ld, needed, - inchans, - inchan_deadends, - &any_offline); + 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, - inchans, - inchan_deadends, - &any_offline, + candidates, &warning_mpp_capacity); warning_mpp = (tal_count(info->b11->routes) > 1); } } - /* FIXME: add private routes if necessary! */ b11enc = bolt11_encode(info, info->b11, false, hsm_sign_b11, info->cmd->ld); @@ -846,22 +712,23 @@ static void gossipd_incoming_channels_reply(struct subd *gossipd, ? type_to_string(tmpctx, struct amount_msat, info->b11->msat) : "0", - any_offline + amount_msat_greater(offline_amt, AMOUNT_MSAT(0)) ? " (among currently connected peers)" : ""); - if (tal_count(inchans) == 0) - json_add_string(response, "warning_capacity", - "No channels"); - else if (all_true(inchan_deadends, tal_count(inchans))) - json_add_string(response, "warning_deadends", - "No channel with a peer that is not a dead end"); - else if (any_offline) + 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 + } 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_mpp) diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index bc8cb2efc..b2bff4431 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -288,7 +288,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) /*~ We maintain a round-robin list of channels. * This round-robin list of channels is used to ensure that * each invoice we generate has a different set of channels. */ - list_head_init(&ld->rr_channels); + ld->rr_counter = 0; return ld; } diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 603be2003..11c4dc6aa 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -276,7 +276,7 @@ struct lightningd { int *exit_code; /* The round-robin list of channels, for use when doing MPP. */ - struct list_head rr_channels; + u64 rr_counter; }; /* Turning this on allows a tal allocation to return NULL, rather than aborting. diff --git a/lightningd/routehint.c b/lightningd/routehint.c new file mode 100644 index 000000000..284dedcae --- /dev/null +++ b/lightningd/routehint.c @@ -0,0 +1,134 @@ +#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) +{ + for (size_t i = 0; i < tal_count(scidarr); i++) + if (short_channel_id_eq(&scidarr[i], scid)) + return true; + + return false; +} + +struct routehint_candidate * +routehint_candidates(const tal_t *ctx, + struct lightningd *ld, + const u8 *incoming_channels_reply, + bool expose_all_private, + const struct short_channel_id *hints, + bool *none_public, + bool *deadends, + struct amount_msat *amount_offline) +{ + 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); + } + } + + 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; + struct routehint_candidate candidate; + + /* Do we know about this peer? */ + peer = peer_by_id(ld, &inchans[i].pubkey); + if (!peer) + continue; + + /* Does it have a channel in state CHANNELD_NORMAL */ + candidate.c = peer_normal_channel(peer); + if (!candidate.c) + continue; + + /* Is it a dead-end? */ + if (inchan_deadends[i]) { + *deadends = true; + continue; + } + + candidate.capacity = channel_amount_receivable(candidate.c); + + /* Is it offline? */ + if (candidate.c->owner == NULL) { + if (!amount_msat_add(amount_offline, + *amount_offline, + candidate.capacity)) + fatal("Overflow summing offline capacity!"); + continue; + } + candidate.r = &inchans[i]; + tal_arr_expand(&candidates, candidate); + } + + return candidates; +} diff --git a/lightningd/routehint.h b/lightningd/routehint.h new file mode 100644 index 000000000..8eb8476ec --- /dev/null +++ b/lightningd/routehint.h @@ -0,0 +1,40 @@ +/* Code for routehints to be inserted into invoices and offers */ +#ifndef LIGHTNING_LIGHTNINGD_ROUTEHINT_H +#define LIGHTNING_LIGHTNINGD_ROUTEHINT_H +#include "config.h" +#include +#include +#include +#include + +struct lightningd; +struct short_channel_id; + +struct routehint_candidate { + struct route_info *r; + struct channel *c; + struct amount_msat capacity; +}; + +/** + * 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) + * @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. + */ +struct routehint_candidate * +routehint_candidates(const tal_t *ctx, + struct lightningd *ld, + const u8 *incoming_channels_reply, + bool expose_all_private, + const struct short_channel_id *hints, + bool *none_public, + bool *deadends, + struct amount_msat *amount_offline); + +#endif /* LIGHTNING_LIGHTNINGD_ROUTEHINT_H */ diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 767ddc513..f9327a138 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -1,12 +1,16 @@ -#include "../channel.c" #include "../invoice.c" #include "../peer_control.c" +#include "../routehint.c" #include #include bool deprecated_apis = false; /* AUTOGENERATED MOCKS START */ +/* Generated stub for active_channel_by_scid */ +struct channel *active_channel_by_scid(struct lightningd *ld UNNEEDED, + const struct short_channel_id *scid UNNEEDED) +{ fprintf(stderr, "active_channel_by_scid called!\n"); abort(); } /* Generated stub for bitcoind_getutxout_ */ void bitcoind_getutxout_(struct bitcoind *bitcoind UNNEEDED, const struct bitcoin_txid *txid UNNEEDED, const u32 outnum UNNEEDED, @@ -36,6 +40,41 @@ void broadcast_tx(struct chain_topology *topo UNNEEDED, bool success UNNEEDED, const char *err)) { fprintf(stderr, "broadcast_tx called!\n"); abort(); } +/* Generated stub for channel_fail_forget */ +void channel_fail_forget(struct channel *channel UNNEEDED, const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "channel_fail_forget called!\n"); abort(); } +/* Generated stub for channel_fail_permanent */ +void channel_fail_permanent(struct channel *channel UNNEEDED, const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "channel_fail_permanent called!\n"); abort(); } +/* Generated stub for channel_fail_reconnect */ +void channel_fail_reconnect(struct channel *channel UNNEEDED, + const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "channel_fail_reconnect called!\n"); abort(); } +/* Generated stub for channel_fail_reconnect_later */ +void channel_fail_reconnect_later(struct channel *channel UNNEEDED, + const char *fmt UNNEEDED,...) +{ fprintf(stderr, "channel_fail_reconnect_later called!\n"); abort(); } +/* Generated stub for channel_has_htlc_in */ +struct htlc_in *channel_has_htlc_in(struct channel *channel UNNEEDED) +{ fprintf(stderr, "channel_has_htlc_in called!\n"); abort(); } +/* Generated stub for channel_has_htlc_out */ +struct htlc_out *channel_has_htlc_out(struct channel *channel UNNEEDED) +{ fprintf(stderr, "channel_has_htlc_out called!\n"); abort(); } +/* Generated stub for channel_internal_error */ +void channel_internal_error(struct channel *channel UNNEEDED, const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "channel_internal_error called!\n"); abort(); } +/* Generated stub for channel_set_billboard */ +void channel_set_billboard(struct channel *channel UNNEEDED, bool perm UNNEEDED, + const char *str TAKES UNNEEDED) +{ fprintf(stderr, "channel_set_billboard called!\n"); abort(); } +/* Generated stub for channel_set_state */ +void channel_set_state(struct channel *channel UNNEEDED, + enum channel_state old_state UNNEEDED, + enum channel_state state UNNEEDED) +{ fprintf(stderr, "channel_set_state called!\n"); abort(); } +/* Generated stub for channel_state_name */ +const char *channel_state_name(const struct channel *channel UNNEEDED) +{ fprintf(stderr, "channel_state_name called!\n"); abort(); } /* Generated stub for channel_tell_depth */ bool channel_tell_depth(struct lightningd *ld UNNEEDED, struct channel *channel UNNEEDED, @@ -75,14 +114,13 @@ void connect_succeeded(struct lightningd *ld UNNEEDED, const struct peer *peer U void delay_then_reconnect(struct channel *channel UNNEEDED, u32 seconds_delay UNNEEDED, const struct wireaddr_internal *addrhint TAKES UNNEEDED) { fprintf(stderr, "delay_then_reconnect called!\n"); abort(); } +/* Generated stub for delete_channel */ +void delete_channel(struct channel *channel STEALS UNNEEDED) +{ fprintf(stderr, "delete_channel called!\n"); abort(); } /* Generated stub for derive_channel_id */ void derive_channel_id(struct channel_id *channel_id UNNEEDED, const struct bitcoin_txid *txid UNNEEDED, u16 txout UNNEEDED) { fprintf(stderr, "derive_channel_id called!\n"); abort(); } -/* Generated stub for dup_fee_states */ -struct fee_states *dup_fee_states(const tal_t *ctx UNNEEDED, - const struct fee_states *fee_states TAKES UNNEEDED) -{ fprintf(stderr, "dup_fee_states called!\n"); abort(); } /* Generated stub for encode_scriptpubkey_to_addr */ char *encode_scriptpubkey_to_addr(const tal_t *ctx UNNEEDED, const struct chainparams *chainparams UNNEEDED, @@ -119,9 +157,6 @@ bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p U /* 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_get_channel_basepoints_reply */ -bool fromwire_hsmd_get_channel_basepoints_reply(const void *p UNNEEDED, struct basepoints *basepoints UNNEEDED, struct pubkey *funding_pubkey UNNEEDED) -{ fprintf(stderr, "fromwire_hsmd_get_channel_basepoints_reply called!\n"); abort(); } /* Generated stub for fromwire_hsmd_sign_commitment_tx_reply */ bool fromwire_hsmd_sign_commitment_tx_reply(const void *p UNNEEDED, struct bitcoin_signature *sig UNNEEDED) { fprintf(stderr, "fromwire_hsmd_sign_commitment_tx_reply called!\n"); abort(); } @@ -287,11 +322,6 @@ void log_(struct log *log UNNEEDED, enum log_level level UNNEEDED, struct bolt11 *new_bolt11(const tal_t *ctx UNNEEDED, const struct amount_msat *msat TAKES UNNEEDED) { fprintf(stderr, "new_bolt11 called!\n"); abort(); } -/* Generated stub for new_log */ -struct log *new_log(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED, - const struct node_id *default_node_id UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "new_log called!\n"); abort(); } /* Generated stub for new_reltimer_ */ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, const tal_t *ctx UNNEEDED, @@ -416,12 +446,18 @@ struct command_result *param_u64(struct command *cmd UNNEEDED, const char *name const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, uint64_t **num UNNEEDED) { fprintf(stderr, "param_u64 called!\n"); abort(); } +/* Generated stub for peer_active_channel */ +struct channel *peer_active_channel(struct peer *peer UNNEEDED) +{ fprintf(stderr, "peer_active_channel called!\n"); abort(); } /* Generated stub for peer_get_owning_subd */ struct subd *peer_get_owning_subd(struct peer *peer UNNEEDED) { fprintf(stderr, "peer_get_owning_subd called!\n"); abort(); } /* Generated stub for peer_memleak_done */ void peer_memleak_done(struct command *cmd UNNEEDED, struct subd *leaker UNNEEDED) { fprintf(stderr, "peer_memleak_done called!\n"); abort(); } +/* Generated stub for peer_normal_channel */ +struct channel *peer_normal_channel(struct peer *peer UNNEEDED) +{ fprintf(stderr, "peer_normal_channel called!\n"); abort(); } /* Generated stub for peer_start_channeld */ void peer_start_channeld(struct channel *channel UNNEEDED, struct per_peer_state *pps UNNEEDED, @@ -447,9 +483,6 @@ 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 subd_release_channel */ -void subd_release_channel(struct subd *owner UNNEEDED, void *channel UNNEEDED) -{ fprintf(stderr, "subd_release_channel called!\n"); abort(); } /* Generated stub for subd_req_ */ void subd_req_(const tal_t *ctx UNNEEDED, struct subd *sd UNNEEDED, @@ -482,9 +515,6 @@ u8 *towire_channeld_specific_feerates(const tal_t *ctx UNNEEDED, u32 feerate_bas /* Generated stub for towire_connectd_connect_to_peer */ u8 *towire_connectd_connect_to_peer(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, u32 seconds_waited UNNEEDED, const struct wireaddr_internal *addrhint UNNEEDED) { fprintf(stderr, "towire_connectd_connect_to_peer called!\n"); abort(); } -/* Generated stub for towire_connectd_peer_disconnected */ -u8 *towire_connectd_peer_disconnected(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED) -{ fprintf(stderr, "towire_connectd_peer_disconnected called!\n"); abort(); } /* Generated stub for towire_errorfmt */ u8 *towire_errorfmt(const tal_t *ctx UNNEEDED, const struct channel_id *channel UNNEEDED, @@ -493,9 +523,6 @@ u8 *towire_errorfmt(const tal_t *ctx UNNEEDED, /* 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_get_channel_basepoints */ -u8 *towire_hsmd_get_channel_basepoints(const tal_t *ctx UNNEEDED, const struct node_id *peerid UNNEEDED, u64 dbid UNNEEDED) -{ fprintf(stderr, "towire_hsmd_get_channel_basepoints called!\n"); abort(); } /* Generated stub for towire_hsmd_sign_commitment_tx */ u8 *towire_hsmd_sign_commitment_tx(const tal_t *ctx UNNEEDED, const struct node_id *peer_id UNNEEDED, u64 channel_dbid UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const struct pubkey *remote_funding_key UNNEEDED) { fprintf(stderr, "towire_hsmd_sign_commitment_tx called!\n"); abort(); } @@ -518,9 +545,6 @@ const char *version(void) void wallet_annotate_txout(struct wallet *w UNNEEDED, const struct bitcoin_txid *txid UNNEEDED, int outnum UNNEEDED, enum wallet_tx_type type UNNEEDED, u64 channel UNNEEDED) { fprintf(stderr, "wallet_annotate_txout called!\n"); abort(); } -/* Generated stub for wallet_channel_close */ -void wallet_channel_close(struct wallet *w UNNEEDED, u64 wallet_id UNNEEDED) -{ fprintf(stderr, "wallet_channel_close called!\n"); abort(); } /* Generated stub for wallet_channel_save */ void wallet_channel_save(struct wallet *w UNNEEDED, struct channel *chan UNNEEDED) { fprintf(stderr, "wallet_channel_save called!\n"); abort(); } @@ -663,18 +687,27 @@ bool dev_disconnect_permanent(struct lightningd *ld UNNEEDED) { fprintf(stderr, "dev_disconnect_permanent called!\n"); abort(); } #endif -static void add_inchan(struct route_info **inchans, int n) +static void add_candidate(struct routehint_candidate **candidates, int n, + struct channel *c) { - struct route_info r; - memset(&r.pubkey, n, sizeof(r.pubkey)); - memset(&r.short_channel_id, n, sizeof(r.short_channel_id)); - r.fee_base_msat = r.fee_proportional_millionths = r.cltv_expiry_delta + struct routehint_candidate candidate; + + candidate.r = tal(*candidates, struct route_info); + memset(&candidate.r->pubkey, n, sizeof(candidate.r->pubkey)); + memset(&candidate.r->short_channel_id, n, + sizeof(candidate.r->short_channel_id)); + candidate.r->fee_base_msat + = candidate.r->fee_proportional_millionths + = candidate.r->cltv_expiry_delta = n; - tal_arr_expand(inchans, r); + candidate.c = c; + candidate.capacity = amount_msat(n * 1000 - 1); + tal_arr_expand(candidates, candidate); } -static void add_peer(struct lightningd *ld, int n, enum channel_state state, - bool connected) +static struct channel *add_peer(struct lightningd *ld, int n, + enum channel_state state, + bool connected) { struct peer *peer = tal(ld, struct peer); struct channel *c = tal(peer, struct channel); @@ -695,6 +728,8 @@ static void add_peer(struct lightningd *ld, int n, enum channel_state state, c->our_config.htlc_minimum = AMOUNT_MSAT(0); c->channel_info.their_config.channel_reserve = AMOUNT_SAT(0); list_add_tail(&peer->channels, &c->list); + + return c; } /* There *is* padding in this structure, after pubkey and after cltv_expiry_delta. */ @@ -709,9 +744,7 @@ STRUCTEQ_DEF(route_info, int main(void) { struct lightningd *ld; - bool any_offline; - struct route_info *inchans; - bool *deadends; + struct routehint_candidate *candidates; struct route_info **ret; size_t n; @@ -725,70 +758,51 @@ int main(void) htlc_in_map_init(&ld->htlcs_in); chainparams = chainparams_for_network("regtest"); - inchans = tal_arr(tmpctx, struct route_info, 0); - deadends = tal_arrz(tmpctx, bool, 100); + candidates = tal_arr(tmpctx, struct routehint_candidate, 0); /* 1. Nothing to choose from -> NULL result. */ - assert(select_inchan(tmpctx, ld, AMOUNT_MSAT(0), inchans, deadends, &any_offline) == NULL); - assert(any_offline == false); + ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(0), candidates); + assert(tal_count(ret) == 0); - /* 2. inchan but no corresponding peer -> NULL result. */ - add_inchan(&inchans, 0); - assert(select_inchan(tmpctx, ld, AMOUNT_MSAT(0), inchans, deadends, &any_offline) == NULL); - assert(any_offline == false); + /* 2. One peer (999 msat capacity) */ + add_candidate(&candidates, 1, add_peer(ld, 1, CHANNELD_NORMAL, true)); + ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(1000), candidates); + assert(tal_count(ret) == 0); - /* 3. inchan but its peer in awaiting lockin -> NULL result. */ - add_peer(ld, 0, CHANNELD_AWAITING_LOCKIN, true); - assert(select_inchan(tmpctx, ld, AMOUNT_MSAT(0), inchans, deadends, &any_offline) == NULL); - assert(any_offline == false); - - /* 4. connected peer but no corresponding inchan -> NULL result. */ - add_peer(ld, 1, CHANNELD_NORMAL, true); - assert(select_inchan(tmpctx, ld, AMOUNT_MSAT(0), inchans, deadends, &any_offline) == NULL); - assert(any_offline == false); - - /* 5. inchan but its peer (replaced with one) offline -> NULL result. */ - list_del_from(&ld->peers, &list_tail(&ld->peers, struct peer, list)->list); - add_peer(ld, 1, CHANNELD_NORMAL, false); - add_inchan(&inchans, 1); - assert(select_inchan(tmpctx, ld, AMOUNT_MSAT(0), inchans, deadends, &any_offline) == NULL); - assert(any_offline == true); - - /* 6. Finally, a correct peer! */ - add_inchan(&inchans, 2); - add_peer(ld, 2, CHANNELD_NORMAL, true); - - ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(0), inchans, deadends, &any_offline); + ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(999), candidates); assert(tal_count(ret) == 1); assert(tal_count(ret[0]) == 1); - assert(any_offline == true); /* Peer 1 is offline */ - assert(route_info_eq(ret[0], &inchans[2])); + assert(route_info_eq(ret[0], candidates[0].r)); + + /* 3. Two peers (999 msat capacity and 1999 msat) */ + add_candidate(&candidates, 2, add_peer(ld, 2, CHANNELD_NORMAL, true)); + + ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(1000), candidates); + assert(tal_count(ret) == 1); + assert(tal_count(ret[0]) == 1); + assert(route_info_eq(ret[0], candidates[1].r)); /* 7. Correct peer with just enough capacity_to_pay_us */ - ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(1999), inchans, deadends, &any_offline); + ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(1999), candidates); assert(tal_count(ret) == 1); assert(tal_count(ret[0]) == 1); - assert(any_offline == false); /* Other candidate insufficient funds. */ - assert(route_info_eq(ret[0], &inchans[2])); + assert(route_info_eq(ret[0], candidates[1].r)); /* 8. Not if we ask for too much! Our balance is 1msat. */ - ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(2000), inchans, deadends, &any_offline); - assert(ret == NULL); - assert(any_offline == false); /* Other candidate insufficient funds. */ + ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(2000), candidates); + assert(tal_count(ret) == 0); - /* 9. Add another peer */ - add_inchan(&inchans, 3); - add_peer(ld, 3, CHANNELD_NORMAL, true); + /* 9. Add another peer (2999 capacity) */ + add_candidate(&candidates, 3, add_peer(ld, 3, CHANNELD_NORMAL, true)); /* Simulate selection ratios between excesses 25% and 50% of capacity*/ for (size_t i = n = 0; i < 1000; i++) { - ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(1499), inchans, deadends, &any_offline); + ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(1499), candidates); assert(tal_count(ret) == 1); assert(tal_count(ret[0]) == 1); - assert(any_offline == false); /* Other candidate insufficient funds. */ - assert(route_info_eq(ret[0], &inchans[2]) - || route_info_eq(ret[0], &inchans[3])); - n += route_info_eq(ret[0], &inchans[2]); + assert(route_info_eq(ret[0], candidates[1].r) + || route_info_eq(ret[0], candidates[2].r)); + n += route_info_eq(ret[0], candidates[1].r); } /* Handwave over probability of this happening! Within 20% */ @@ -797,20 +811,16 @@ int main(void) n, 1000 - n); assert(n > 333 - 66 && n < 333 + 66); - /* 10. Last peer's capacity goes from 3 to 2 sat*/ - list_tail(&list_tail(&ld->peers, struct peer, list)->channels, struct channel, list)-> - channel_info.their_config.channel_reserve = AMOUNT_SAT(1); - ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(1499), inchans, deadends, &any_offline); + /* 10. Last peer's capacity goes from 3 to 2 sat */ + candidates[2].c->channel_info.their_config.channel_reserve = AMOUNT_SAT(1); - /* Simulate selection ratios between excesses 25% and 75% of capacity*/ for (size_t i = n = 0; i < 1000; i++) { - ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(1499), inchans, deadends, &any_offline); + ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(1499), candidates); assert(tal_count(ret) == 1); assert(tal_count(ret[0]) == 1); - assert(any_offline == false); /* Other candidate insufficient funds. */ - assert(route_info_eq(ret[0], &inchans[2]) - || route_info_eq(ret[0], &inchans[3])); - n += route_info_eq(ret[0], &inchans[2]); + assert(route_info_eq(ret[0], candidates[1].r) + || route_info_eq(ret[0], candidates[2].r)); + n += route_info_eq(ret[0], candidates[1].r); } /* Handwave over probability of this happening! Within 20% */ diff --git a/wallet/db_postgres_sqlgen.c b/wallet/db_postgres_sqlgen.c index 2b3aaa477..a9cc1f9e3 100644 --- a/wallet/db_postgres_sqlgen.c +++ b/wallet/db_postgres_sqlgen.c @@ -1642,4 +1642,4 @@ struct db_query db_postgres_queries[] = { #endif /* LIGHTNINGD_WALLET_GEN_DB_POSTGRES */ -// SHA256STAMP:a4a3f2d9ee677312ff2586432ffae389eedb72f1bf34d36bfb564d37889f8eeb +// SHA256STAMP:fa885142376ef8ac5cae84c02d379d7e1bf97d3b0c69af46a6054316d2e6a1bc diff --git a/wallet/db_sqlite3_sqlgen.c b/wallet/db_sqlite3_sqlgen.c index b5488addf..413a684c9 100644 --- a/wallet/db_sqlite3_sqlgen.c +++ b/wallet/db_sqlite3_sqlgen.c @@ -1642,4 +1642,4 @@ struct db_query db_sqlite3_queries[] = { #endif /* LIGHTNINGD_WALLET_GEN_DB_SQLITE3 */ -// SHA256STAMP:a4a3f2d9ee677312ff2586432ffae389eedb72f1bf34d36bfb564d37889f8eeb +// SHA256STAMP:fa885142376ef8ac5cae84c02d379d7e1bf97d3b0c69af46a6054316d2e6a1bc diff --git a/wallet/statements_gettextgen.po b/wallet/statements_gettextgen.po index 0798bed35..9d1c8170f 100644 --- a/wallet/statements_gettextgen.po +++ b/wallet/statements_gettextgen.po @@ -1081,4 +1081,4 @@ msgstr "" #: wallet/test/run-wallet.c:1345 msgid "INSERT INTO channels (id) VALUES (1);" msgstr "" -# SHA256STAMP:b285290a275969586a0f96adf2f00a52d79b17dd48192976c341be6beed1daeb +# SHA256STAMP:53a054ad896208a2c445cd4ef4bdda2d961451d816cdb2f29f2c2f7be69683d2 diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index dec6a47e0..fa70120ce 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -1509,7 +1509,7 @@ int main(int argc, const char *argv[]) /* Only elements in ld we should access */ list_head_init(&ld->peers); - list_head_init(&ld->rr_channels); + ld->rr_counter = 0; node_id_from_hexstr("02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc", 66, &ld->id); /* Accessed in peer destructor sanity check */ htlc_in_map_init(&ld->htlcs_in);