mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 05:12:45 +01:00
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>
This commit is contained in:
parent
ca4730b5ff
commit
2be1f3fe1b
@ -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
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
|
@ -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));
|
||||
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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
Normal file
134
lightningd/routehint.c
Normal file
@ -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
Normal file
40
lightningd/routehint.h
Normal file
@ -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 */
|
@ -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);
|
||||
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% */
|
||||
|
2
wallet/db_postgres_sqlgen.c
generated
2
wallet/db_postgres_sqlgen.c
generated
@ -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
generated
2
wallet/db_sqlite3_sqlgen.c
generated
@ -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
generated
2
wallet/statements_gettextgen.po
generated
@ -1081,4 +1081,4 @@ msgstr ""
|
||||
#: wallet/test/run-wallet.c:1345
|
||||
msgid "INSERT INTO channels (id) VALUES (1);"
|
||||
msgstr ""
|
||||
# SHA256STAMP:b285290a275969586a0f96adf2f00a52d79b17dd48192976c341be6beed1daeb
|
||||
# SHA256STAMP:53a054ad896208a2c445cd4ef4bdda2d961451d816cdb2f29f2c2f7be69683d2
|
||||
|
@ -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…
Reference in New Issue
Block a user