Browse Source

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 <rusty@rustcorp.com.au>
bump-pyln-proto
Rusty Russell 4 years ago
parent
commit
2be1f3fe1b
  1. 1
      lightningd/Makefile
  2. 4
      lightningd/channel.c
  3. 2
      lightningd/channel.h
  4. 275
      lightningd/invoice.c
  5. 2
      lightningd/lightningd.c
  6. 2
      lightningd/lightningd.h
  7. 134
      lightningd/routehint.c
  8. 40
      lightningd/routehint.h
  9. 198
      lightningd/test/run-invoice-select-inchan.c
  10. 2
      wallet/db_postgres_sqlgen.c
  11. 2
      wallet/db_sqlite3_sqlgen.c
  12. 2
      wallet/statements_gettextgen.po
  13. 2
      wallet/test/run-wallet.c

1
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

4
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 */

2
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,

275
lightningd/invoice.c

@ -3,6 +3,7 @@
#include <bitcoin/base58.h>
#include <bitcoin/script.h>
#include <ccan/array_size/array_size.h>
#include <ccan/asort/asort.h>
#include <ccan/json_escape/json_escape.h>
#include <ccan/str/hex/hex.h>
#include <ccan/tal/str/str.h>
@ -34,6 +35,7 @@
#include <lightningd/peer_control.h>
#include <lightningd/peer_htlcs.h>
#include <lightningd/plugin_hook.h>
#include <lightningd/routehint.h>
#include <lightningd/subd.h>
#include <sodium/randombytes.h>
#include <wire/wire_sync.h>
@ -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));
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;
}
candidates = routehint_candidates(tmpctx, info->cmd->ld, msg,
chanhints ? chanhints->expose_all_private : false,
chanhints ? chanhints->hints : NULL,
&node_unpublished,
&deadends,
&offline_amt);
/* 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)

2
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;
}

2
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.

134
lightningd/routehint.c

@ -0,0 +1,134 @@
#include <common/bolt11.h>
#include <common/utils.h>
#include <gossipd/gossipd_wiregen.h>
#include <lightningd/log.h>
#include <lightningd/peer_control.h>
#include <lightningd/routehint.h>
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;
}

40
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 <ccan/short_types/short_types.h>
#include <ccan/tal/tal.h>
#include <common/amount.h>
#include <stdbool.h>
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 */

198
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 <ccan/alignof/alignof.h>
#include <common/errcode.h>
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);
/* 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);
/* 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(0), candidates);
assert(tal_count(ret) == 0);
/* 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);
ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(999), candidates);
assert(tal_count(ret) == 1);
assert(tal_count(ret[0]) == 1);
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(any_offline == true); /* Peer 1 is offline */
assert(route_info_eq(ret[0], &inchans[2]));
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% */

2
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

2
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

2
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

2
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);

Loading…
Cancel
Save