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.c \
|
||||||
lightningd/plugin_control.c \
|
lightningd/plugin_control.c \
|
||||||
lightningd/plugin_hook.c \
|
lightningd/plugin_hook.c \
|
||||||
|
lightningd/routehint.c \
|
||||||
lightningd/subd.c \
|
lightningd/subd.c \
|
||||||
lightningd/watch.c
|
lightningd/watch.c
|
||||||
|
|
||||||
|
@ -110,8 +110,6 @@ static void destroy_channel(struct channel *channel)
|
|||||||
channel_set_owner(channel, NULL);
|
channel_set_owner(channel, NULL);
|
||||||
|
|
||||||
list_del_from(&channel->peer->channels, &channel->list);
|
list_del_from(&channel->peer->channels, &channel->list);
|
||||||
|
|
||||||
list_del(&channel->rr_list);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void delete_channel(struct channel *channel STEALS)
|
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);
|
channel->forgets = tal_arr(channel, struct command *, 0);
|
||||||
|
|
||||||
list_add_tail(&peer->channels, &channel->list);
|
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);
|
tal_add_destructor(channel, destroy_channel);
|
||||||
|
|
||||||
/* Make sure we see any spends using this key */
|
/* Make sure we see any spends using this key */
|
||||||
|
@ -138,7 +138,7 @@ struct channel {
|
|||||||
struct command **forgets;
|
struct command **forgets;
|
||||||
|
|
||||||
/* Our position in the round-robin list. */
|
/* Our position in the round-robin list. */
|
||||||
struct list_node rr_list;
|
u64 rr_number;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct channel *new_channel(struct peer *peer, u64 dbid,
|
struct channel *new_channel(struct peer *peer, u64 dbid,
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <bitcoin/base58.h>
|
#include <bitcoin/base58.h>
|
||||||
#include <bitcoin/script.h>
|
#include <bitcoin/script.h>
|
||||||
#include <ccan/array_size/array_size.h>
|
#include <ccan/array_size/array_size.h>
|
||||||
|
#include <ccan/asort/asort.h>
|
||||||
#include <ccan/json_escape/json_escape.h>
|
#include <ccan/json_escape/json_escape.h>
|
||||||
#include <ccan/str/hex/hex.h>
|
#include <ccan/str/hex/hex.h>
|
||||||
#include <ccan/tal/str/str.h>
|
#include <ccan/tal/str/str.h>
|
||||||
@ -34,6 +35,7 @@
|
|||||||
#include <lightningd/peer_control.h>
|
#include <lightningd/peer_control.h>
|
||||||
#include <lightningd/peer_htlcs.h>
|
#include <lightningd/peer_htlcs.h>
|
||||||
#include <lightningd/plugin_hook.h>
|
#include <lightningd/plugin_hook.h>
|
||||||
|
#include <lightningd/routehint.h>
|
||||||
#include <lightningd/subd.h>
|
#include <lightningd/subd.h>
|
||||||
#include <sodium/randombytes.h>
|
#include <sodium/randombytes.h>
|
||||||
#include <wire/wire_sync.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
|
* Then use weighted reservoir sampling, which makes probing channel balances
|
||||||
* harder, to choose one channel from the set of suitable channels. It favors
|
* 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.
|
* 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,
|
static struct route_info **select_inchan(const tal_t *ctx,
|
||||||
struct lightningd *ld,
|
struct lightningd *ld,
|
||||||
struct amount_msat amount_needed,
|
struct amount_msat amount_needed,
|
||||||
const struct route_info *inchans,
|
const struct routehint_candidate
|
||||||
const bool *deadends,
|
*candidates)
|
||||||
bool *any_offline)
|
|
||||||
{
|
{
|
||||||
/* BOLT11 struct wants an array of arrays (can provide multiple routes) */
|
/* BOLT11 struct wants an array of arrays (can provide multiple routes) */
|
||||||
struct route_info **r = NULL;
|
struct route_info **r = NULL;
|
||||||
double total_weight = 0.0;
|
double total_weight = 0.0;
|
||||||
|
|
||||||
*any_offline = false;
|
|
||||||
|
|
||||||
/* Collect suitable channels and assign each a weight. */
|
/* Collect suitable channels and assign each a weight. */
|
||||||
for (size_t i = 0; i < tal_count(inchans); i++) {
|
for (size_t i = 0; i < tal_count(candidates); i++) {
|
||||||
struct peer *peer;
|
struct amount_msat excess, capacity;
|
||||||
struct channel *c;
|
|
||||||
struct amount_msat capacity_to_pay_us, excess, capacity;
|
|
||||||
struct amount_sat cumulative_reserve;
|
struct amount_sat cumulative_reserve;
|
||||||
double excess_frac;
|
double excess_frac;
|
||||||
|
|
||||||
/* Do we know about this peer? */
|
/* Does the peer have sufficient balance to pay us,
|
||||||
peer = peer_by_id(ld, &inchans[i].pubkey);
|
* even after having taken into account their reserve? */
|
||||||
if (!peer)
|
if (!amount_msat_sub(&excess, candidates[i].capacity,
|
||||||
continue;
|
amount_needed))
|
||||||
|
|
||||||
/* 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])
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Channel balance as seen by our node:
|
/* Channel balance as seen by our node:
|
||||||
@ -497,27 +484,14 @@ static struct route_info **select_inchan(const tal_t *ctx,
|
|||||||
0 ^ ^ ^ funding
|
0 ^ ^ ^ funding
|
||||||
our_reserve our_msat */
|
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 */
|
/* Find capacity and calculate its excess fraction */
|
||||||
if (!amount_sat_add(&cumulative_reserve,
|
if (!amount_sat_add(&cumulative_reserve,
|
||||||
c->our_config.channel_reserve,
|
candidates[i].c->our_config.channel_reserve,
|
||||||
c->channel_info.their_config.channel_reserve)
|
candidates[i].c->channel_info.their_config.channel_reserve)
|
||||||
|| !amount_sat_to_msat(&capacity, c->funding)
|
|| !amount_sat_to_msat(&capacity, candidates[i].c->funding)
|
||||||
|| !amount_msat_sub_sat(&capacity, capacity, cumulative_reserve)) {
|
|| !amount_msat_sub_sat(&capacity, capacity, cumulative_reserve)) {
|
||||||
log_broken(ld->log, "Channel %s capacity overflow!",
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -525,7 +499,9 @@ static struct route_info **select_inchan(const tal_t *ctx,
|
|||||||
* only one! So bump it by 1 msat */
|
* only one! So bump it by 1 msat */
|
||||||
if (!amount_msat_add(&excess, excess, AMOUNT_MSAT(1))) {
|
if (!amount_msat_add(&excess, excess, AMOUNT_MSAT(1))) {
|
||||||
log_broken(ld->log, "Channel %s excess overflow!",
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
excess_frac = amount_msat_ratio(excess, capacity);
|
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)) {
|
if (random_select(excess_frac, &total_weight)) {
|
||||||
tal_free(r);
|
tal_free(r);
|
||||||
r = tal_arr(ctx, struct route_info *, 1);
|
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;
|
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
|
/** select_inchan_mpp
|
||||||
*
|
*
|
||||||
* @brief fallback in case select_inchan cannot find a *single*
|
* @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,
|
static struct route_info **select_inchan_mpp(const tal_t *ctx,
|
||||||
struct lightningd *ld,
|
struct lightningd *ld,
|
||||||
struct amount_msat amount_needed,
|
struct amount_msat amount_needed,
|
||||||
const struct route_info *inchans,
|
struct routehint_candidate
|
||||||
const bool *deadends,
|
*candidates,
|
||||||
bool *any_offline,
|
|
||||||
bool *warning_mpp_capacity)
|
bool *warning_mpp_capacity)
|
||||||
{
|
{
|
||||||
/* The total amount we have gathered for incoming channels. */
|
/* The total amount we have gathered for incoming channels. */
|
||||||
struct amount_msat gathered;
|
struct amount_msat gathered;
|
||||||
/* Channels we have already processed. */
|
|
||||||
struct list_head processed;
|
|
||||||
/* Routehint array. */
|
/* Routehint array. */
|
||||||
struct route_info **routehints;
|
struct route_info **routehints;
|
||||||
|
|
||||||
gathered = AMOUNT_MSAT(0);
|
gathered = AMOUNT_MSAT(0);
|
||||||
list_head_init(&processed);
|
|
||||||
routehints = tal_arr(ctx, struct route_info *, 0);
|
routehints = tal_arr(ctx, struct route_info *, 0);
|
||||||
|
|
||||||
while (amount_msat_less(gathered, amount_needed)
|
/* Sort by rr_number, so we get fresh channels. */
|
||||||
&& !list_empty(&ld->rr_channels)) {
|
asort(candidates, tal_count(candidates), cmp_rr_number, NULL);
|
||||||
struct channel *c;
|
for (size_t i = 0; i < tal_count(candidates); i++) {
|
||||||
struct amount_msat capacity_to_pay_us;
|
if (amount_msat_greater_eq(gathered, amount_needed))
|
||||||
size_t found_i;
|
break;
|
||||||
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;
|
|
||||||
|
|
||||||
/* Add to current routehints set. */
|
/* 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,
|
log_broken(ld->log,
|
||||||
"Gathered channel capacity overflow: "
|
"Gathered channel capacity overflow: "
|
||||||
"%s + %s",
|
"%s + %s",
|
||||||
type_to_string(tmpctx, struct amount_msat, &gathered),
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
tal_arr_expand(&routehints,
|
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. */
|
/* Check if we gathered enough. */
|
||||||
*warning_mpp_capacity = amount_msat_less(gathered, amount_needed);
|
*warning_mpp_capacity = amount_msat_less(gathered, amount_needed);
|
||||||
|
|
||||||
@ -648,114 +594,39 @@ struct invoice_info {
|
|||||||
struct chanhints *chanhints;
|
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,
|
static void gossipd_incoming_channels_reply(struct subd *gossipd,
|
||||||
const u8 *msg,
|
const u8 *msg,
|
||||||
const int *fs,
|
const int *fs,
|
||||||
struct invoice_info *info)
|
struct invoice_info *info)
|
||||||
{
|
{
|
||||||
struct json_stream *response;
|
struct json_stream *response;
|
||||||
struct route_info *inchans, *private;
|
|
||||||
bool *inchan_deadends, *private_deadends;
|
|
||||||
struct invoice invoice;
|
struct invoice invoice;
|
||||||
char *b11enc;
|
char *b11enc;
|
||||||
const struct invoice_details *details;
|
const struct invoice_details *details;
|
||||||
struct wallet *wallet = info->cmd->ld->wallet;
|
struct wallet *wallet = info->cmd->ld->wallet;
|
||||||
const struct chanhints *chanhints = info->chanhints;
|
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 = false;
|
||||||
bool warning_mpp_capacity = false;
|
bool warning_mpp_capacity = false;
|
||||||
|
bool deadends;
|
||||||
bool node_unpublished;
|
bool node_unpublished;
|
||||||
|
|
||||||
if (!fromwire_gossipd_get_incoming_channels_reply(tmpctx, msg,
|
candidates = routehint_candidates(tmpctx, info->cmd->ld, msg,
|
||||||
&inchans,
|
chanhints ? chanhints->expose_all_private : false,
|
||||||
&inchan_deadends,
|
chanhints ? chanhints->hints : NULL,
|
||||||
&private,
|
&node_unpublished,
|
||||||
&private_deadends))
|
&deadends,
|
||||||
fatal("Gossip gave bad GOSSIP_GET_INCOMING_CHANNELS_REPLY %s",
|
&offline_amt);
|
||||||
tal_hex(msg, msg));
|
|
||||||
|
|
||||||
node_unpublished = (tal_count(inchans) == 0)
|
/* If they told us to use scids and we couldn't, fail. */
|
||||||
&& (tal_count(private) > 0);
|
if (tal_count(candidates) == 0
|
||||||
|
&& chanhints && tal_count(chanhints->hints) != 0) {
|
||||||
/* fromwire explicitly makes empty arrays into NULL */
|
was_pending(command_fail(info->cmd,
|
||||||
if (!inchans) {
|
INVOICE_HINTS_GAVE_NO_ROUTES,
|
||||||
inchans = tal_arr(tmpctx, struct route_info, 0);
|
"None of those hints were suitable local channels"));
|
||||||
inchan_deadends = tal_arr(tmpctx, bool, 0);
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
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 (tal_count(info->b11->routes) == 0) {
|
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->b11->routes = select_inchan(info->b11,
|
||||||
info->cmd->ld,
|
info->cmd->ld,
|
||||||
needed,
|
needed,
|
||||||
inchans,
|
candidates);
|
||||||
inchan_deadends,
|
|
||||||
&any_offline);
|
|
||||||
/* If we are completely unpublished, or if the above reservoir
|
/* If we are completely unpublished, or if the above reservoir
|
||||||
* sampling fails, select channels by round-robin. */
|
* sampling fails, select channels by round-robin. */
|
||||||
if (tal_count(info->b11->routes) == 0) {
|
if (tal_count(info->b11->routes) == 0) {
|
||||||
info->b11->routes = select_inchan_mpp(info->b11,
|
info->b11->routes = select_inchan_mpp(info->b11,
|
||||||
info->cmd->ld,
|
info->cmd->ld,
|
||||||
needed,
|
needed,
|
||||||
inchans,
|
candidates,
|
||||||
inchan_deadends,
|
|
||||||
&any_offline,
|
|
||||||
&warning_mpp_capacity);
|
&warning_mpp_capacity);
|
||||||
warning_mpp = (tal_count(info->b11->routes) > 1);
|
warning_mpp = (tal_count(info->b11->routes) > 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME: add private routes if necessary! */
|
|
||||||
b11enc = bolt11_encode(info, info->b11, false,
|
b11enc = bolt11_encode(info, info->b11, false,
|
||||||
hsm_sign_b11, info->cmd->ld);
|
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,
|
? type_to_string(tmpctx, struct amount_msat,
|
||||||
info->b11->msat)
|
info->b11->msat)
|
||||||
: "0",
|
: "0",
|
||||||
any_offline
|
amount_msat_greater(offline_amt, AMOUNT_MSAT(0))
|
||||||
? " (among currently connected peers)" : "");
|
? " (among currently connected peers)" : "");
|
||||||
|
|
||||||
if (tal_count(inchans) == 0)
|
if (amount_msat_greater(offline_amt, AMOUNT_MSAT(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)
|
|
||||||
json_add_string(response, "warning_offline",
|
json_add_string(response, "warning_offline",
|
||||||
"No channel with a peer that is currently connected"
|
"No channel with a peer that is currently connected"
|
||||||
" has sufficient incoming capacity");
|
" 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",
|
json_add_string(response, "warning_capacity",
|
||||||
"No channel with a peer that has sufficient incoming capacity");
|
"No channel with a peer that has sufficient incoming capacity");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (warning_mpp)
|
if (warning_mpp)
|
||||||
|
@ -288,7 +288,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx)
|
|||||||
/*~ We maintain a round-robin list of channels.
|
/*~ We maintain a round-robin list of channels.
|
||||||
* This round-robin list of channels is used to ensure that
|
* This round-robin list of channels is used to ensure that
|
||||||
* each invoice we generate has a different set of channels. */
|
* each invoice we generate has a different set of channels. */
|
||||||
list_head_init(&ld->rr_channels);
|
ld->rr_counter = 0;
|
||||||
|
|
||||||
return ld;
|
return ld;
|
||||||
}
|
}
|
||||||
|
@ -276,7 +276,7 @@ struct lightningd {
|
|||||||
int *exit_code;
|
int *exit_code;
|
||||||
|
|
||||||
/* The round-robin list of channels, for use when doing MPP. */
|
/* 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.
|
/* 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 "../invoice.c"
|
||||||
#include "../peer_control.c"
|
#include "../peer_control.c"
|
||||||
|
#include "../routehint.c"
|
||||||
#include <ccan/alignof/alignof.h>
|
#include <ccan/alignof/alignof.h>
|
||||||
#include <common/errcode.h>
|
#include <common/errcode.h>
|
||||||
|
|
||||||
bool deprecated_apis = false;
|
bool deprecated_apis = false;
|
||||||
|
|
||||||
/* AUTOGENERATED MOCKS START */
|
/* 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_ */
|
/* Generated stub for bitcoind_getutxout_ */
|
||||||
void bitcoind_getutxout_(struct bitcoind *bitcoind UNNEEDED,
|
void bitcoind_getutxout_(struct bitcoind *bitcoind UNNEEDED,
|
||||||
const struct bitcoin_txid *txid UNNEEDED, const u32 outnum 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,
|
bool success UNNEEDED,
|
||||||
const char *err))
|
const char *err))
|
||||||
{ fprintf(stderr, "broadcast_tx called!\n"); abort(); }
|
{ 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 */
|
/* Generated stub for channel_tell_depth */
|
||||||
bool channel_tell_depth(struct lightningd *ld UNNEEDED,
|
bool channel_tell_depth(struct lightningd *ld UNNEEDED,
|
||||||
struct channel *channel 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,
|
void delay_then_reconnect(struct channel *channel UNNEEDED, u32 seconds_delay UNNEEDED,
|
||||||
const struct wireaddr_internal *addrhint TAKES UNNEEDED)
|
const struct wireaddr_internal *addrhint TAKES UNNEEDED)
|
||||||
{ fprintf(stderr, "delay_then_reconnect called!\n"); abort(); }
|
{ 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 */
|
/* Generated stub for derive_channel_id */
|
||||||
void derive_channel_id(struct channel_id *channel_id UNNEEDED,
|
void derive_channel_id(struct channel_id *channel_id UNNEEDED,
|
||||||
const struct bitcoin_txid *txid UNNEEDED, u16 txout UNNEEDED)
|
const struct bitcoin_txid *txid UNNEEDED, u16 txout UNNEEDED)
|
||||||
{ fprintf(stderr, "derive_channel_id called!\n"); abort(); }
|
{ 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 */
|
/* Generated stub for encode_scriptpubkey_to_addr */
|
||||||
char *encode_scriptpubkey_to_addr(const tal_t *ctx UNNEEDED,
|
char *encode_scriptpubkey_to_addr(const tal_t *ctx UNNEEDED,
|
||||||
const struct chainparams *chainparams 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 */
|
/* 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)
|
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(); }
|
{ 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 */
|
/* 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)
|
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(); }
|
{ 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,
|
struct bolt11 *new_bolt11(const tal_t *ctx UNNEEDED,
|
||||||
const struct amount_msat *msat TAKES UNNEEDED)
|
const struct amount_msat *msat TAKES UNNEEDED)
|
||||||
{ fprintf(stderr, "new_bolt11 called!\n"); abort(); }
|
{ 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_ */
|
/* Generated stub for new_reltimer_ */
|
||||||
struct oneshot *new_reltimer_(struct timers *timers UNNEEDED,
|
struct oneshot *new_reltimer_(struct timers *timers UNNEEDED,
|
||||||
const tal_t *ctx 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,
|
const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
|
||||||
uint64_t **num UNNEEDED)
|
uint64_t **num UNNEEDED)
|
||||||
{ fprintf(stderr, "param_u64 called!\n"); abort(); }
|
{ 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 */
|
/* Generated stub for peer_get_owning_subd */
|
||||||
struct subd *peer_get_owning_subd(struct peer *peer UNNEEDED)
|
struct subd *peer_get_owning_subd(struct peer *peer UNNEEDED)
|
||||||
{ fprintf(stderr, "peer_get_owning_subd called!\n"); abort(); }
|
{ fprintf(stderr, "peer_get_owning_subd called!\n"); abort(); }
|
||||||
/* Generated stub for peer_memleak_done */
|
/* Generated stub for peer_memleak_done */
|
||||||
void peer_memleak_done(struct command *cmd UNNEEDED, struct subd *leaker UNNEEDED)
|
void peer_memleak_done(struct command *cmd UNNEEDED, struct subd *leaker UNNEEDED)
|
||||||
{ fprintf(stderr, "peer_memleak_done called!\n"); abort(); }
|
{ 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 */
|
/* Generated stub for peer_start_channeld */
|
||||||
void peer_start_channeld(struct channel *channel UNNEEDED,
|
void peer_start_channeld(struct channel *channel UNNEEDED,
|
||||||
struct per_peer_state *pps 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,
|
bool plugin_hook_call_(struct lightningd *ld UNNEEDED, const struct plugin_hook *hook UNNEEDED,
|
||||||
tal_t *cb_arg STEALS UNNEEDED)
|
tal_t *cb_arg STEALS UNNEEDED)
|
||||||
{ fprintf(stderr, "plugin_hook_call_ called!\n"); abort(); }
|
{ 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_ */
|
/* Generated stub for subd_req_ */
|
||||||
void subd_req_(const tal_t *ctx UNNEEDED,
|
void subd_req_(const tal_t *ctx UNNEEDED,
|
||||||
struct subd *sd 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 */
|
/* 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)
|
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(); }
|
{ 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 */
|
/* Generated stub for towire_errorfmt */
|
||||||
u8 *towire_errorfmt(const tal_t *ctx UNNEEDED,
|
u8 *towire_errorfmt(const tal_t *ctx UNNEEDED,
|
||||||
const struct channel_id *channel 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 */
|
/* Generated stub for towire_gossipd_get_incoming_channels */
|
||||||
u8 *towire_gossipd_get_incoming_channels(const tal_t *ctx UNNEEDED)
|
u8 *towire_gossipd_get_incoming_channels(const tal_t *ctx UNNEEDED)
|
||||||
{ fprintf(stderr, "towire_gossipd_get_incoming_channels called!\n"); abort(); }
|
{ 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 */
|
/* 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)
|
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(); }
|
{ 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,
|
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)
|
int outnum UNNEEDED, enum wallet_tx_type type UNNEEDED, u64 channel UNNEEDED)
|
||||||
{ fprintf(stderr, "wallet_annotate_txout called!\n"); abort(); }
|
{ 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 */
|
/* Generated stub for wallet_channel_save */
|
||||||
void wallet_channel_save(struct wallet *w UNNEEDED, struct channel *chan UNNEEDED)
|
void wallet_channel_save(struct wallet *w UNNEEDED, struct channel *chan UNNEEDED)
|
||||||
{ fprintf(stderr, "wallet_channel_save called!\n"); abort(); }
|
{ 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(); }
|
{ fprintf(stderr, "dev_disconnect_permanent called!\n"); abort(); }
|
||||||
#endif
|
#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;
|
struct routehint_candidate candidate;
|
||||||
memset(&r.pubkey, n, sizeof(r.pubkey));
|
|
||||||
memset(&r.short_channel_id, n, sizeof(r.short_channel_id));
|
candidate.r = tal(*candidates, struct route_info);
|
||||||
r.fee_base_msat = r.fee_proportional_millionths = r.cltv_expiry_delta
|
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;
|
= 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,
|
static struct channel *add_peer(struct lightningd *ld, int n,
|
||||||
bool connected)
|
enum channel_state state,
|
||||||
|
bool connected)
|
||||||
{
|
{
|
||||||
struct peer *peer = tal(ld, struct peer);
|
struct peer *peer = tal(ld, struct peer);
|
||||||
struct channel *c = tal(peer, struct channel);
|
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->our_config.htlc_minimum = AMOUNT_MSAT(0);
|
||||||
c->channel_info.their_config.channel_reserve = AMOUNT_SAT(0);
|
c->channel_info.their_config.channel_reserve = AMOUNT_SAT(0);
|
||||||
list_add_tail(&peer->channels, &c->list);
|
list_add_tail(&peer->channels, &c->list);
|
||||||
|
|
||||||
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* There *is* padding in this structure, after pubkey and after cltv_expiry_delta. */
|
/* There *is* padding in this structure, after pubkey and after cltv_expiry_delta. */
|
||||||
@ -709,9 +744,7 @@ STRUCTEQ_DEF(route_info,
|
|||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
struct lightningd *ld;
|
struct lightningd *ld;
|
||||||
bool any_offline;
|
struct routehint_candidate *candidates;
|
||||||
struct route_info *inchans;
|
|
||||||
bool *deadends;
|
|
||||||
struct route_info **ret;
|
struct route_info **ret;
|
||||||
size_t n;
|
size_t n;
|
||||||
|
|
||||||
@ -725,70 +758,51 @@ int main(void)
|
|||||||
htlc_in_map_init(&ld->htlcs_in);
|
htlc_in_map_init(&ld->htlcs_in);
|
||||||
chainparams = chainparams_for_network("regtest");
|
chainparams = chainparams_for_network("regtest");
|
||||||
|
|
||||||
inchans = tal_arr(tmpctx, struct route_info, 0);
|
candidates = tal_arr(tmpctx, struct routehint_candidate, 0);
|
||||||
deadends = tal_arrz(tmpctx, bool, 100);
|
|
||||||
|
|
||||||
/* 1. Nothing to choose from -> NULL result. */
|
/* 1. Nothing to choose from -> NULL result. */
|
||||||
assert(select_inchan(tmpctx, ld, AMOUNT_MSAT(0), inchans, deadends, &any_offline) == NULL);
|
ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(0), candidates);
|
||||||
assert(any_offline == false);
|
assert(tal_count(ret) == 0);
|
||||||
|
|
||||||
/* 2. inchan but no corresponding peer -> NULL result. */
|
/* 2. One peer (999 msat capacity) */
|
||||||
add_inchan(&inchans, 0);
|
add_candidate(&candidates, 1, add_peer(ld, 1, CHANNELD_NORMAL, true));
|
||||||
assert(select_inchan(tmpctx, ld, AMOUNT_MSAT(0), inchans, deadends, &any_offline) == NULL);
|
ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(1000), candidates);
|
||||||
assert(any_offline == false);
|
assert(tal_count(ret) == 0);
|
||||||
|
|
||||||
/* 3. inchan but its peer in awaiting lockin -> NULL result. */
|
ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(999), candidates);
|
||||||
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);
|
|
||||||
assert(tal_count(ret) == 1);
|
assert(tal_count(ret) == 1);
|
||||||
assert(tal_count(ret[0]) == 1);
|
assert(tal_count(ret[0]) == 1);
|
||||||
assert(any_offline == true); /* Peer 1 is offline */
|
assert(route_info_eq(ret[0], candidates[0].r));
|
||||||
assert(route_info_eq(ret[0], &inchans[2]));
|
|
||||||
|
/* 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 */
|
/* 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) == 1);
|
||||||
assert(tal_count(ret[0]) == 1);
|
assert(tal_count(ret[0]) == 1);
|
||||||
assert(any_offline == false); /* Other candidate insufficient funds. */
|
assert(route_info_eq(ret[0], candidates[1].r));
|
||||||
assert(route_info_eq(ret[0], &inchans[2]));
|
|
||||||
|
|
||||||
/* 8. Not if we ask for too much! Our balance is 1msat. */
|
/* 8. Not if we ask for too much! Our balance is 1msat. */
|
||||||
ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(2000), inchans, deadends, &any_offline);
|
ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(2000), candidates);
|
||||||
assert(ret == NULL);
|
assert(tal_count(ret) == 0);
|
||||||
assert(any_offline == false); /* Other candidate insufficient funds. */
|
|
||||||
|
|
||||||
/* 9. Add another peer */
|
/* 9. Add another peer (2999 capacity) */
|
||||||
add_inchan(&inchans, 3);
|
add_candidate(&candidates, 3, add_peer(ld, 3, CHANNELD_NORMAL, true));
|
||||||
add_peer(ld, 3, CHANNELD_NORMAL, true);
|
|
||||||
|
|
||||||
/* Simulate selection ratios between excesses 25% and 50% of capacity*/
|
/* Simulate selection ratios between excesses 25% and 50% of capacity*/
|
||||||
for (size_t i = n = 0; i < 1000; i++) {
|
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) == 1);
|
||||||
assert(tal_count(ret[0]) == 1);
|
assert(tal_count(ret[0]) == 1);
|
||||||
assert(any_offline == false); /* Other candidate insufficient funds. */
|
assert(route_info_eq(ret[0], candidates[1].r)
|
||||||
assert(route_info_eq(ret[0], &inchans[2])
|
|| route_info_eq(ret[0], candidates[2].r));
|
||||||
|| route_info_eq(ret[0], &inchans[3]));
|
n += route_info_eq(ret[0], candidates[1].r);
|
||||||
n += route_info_eq(ret[0], &inchans[2]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handwave over probability of this happening! Within 20% */
|
/* Handwave over probability of this happening! Within 20% */
|
||||||
@ -797,20 +811,16 @@ int main(void)
|
|||||||
n, 1000 - n);
|
n, 1000 - n);
|
||||||
assert(n > 333 - 66 && n < 333 + 66);
|
assert(n > 333 - 66 && n < 333 + 66);
|
||||||
|
|
||||||
/* 10. Last peer's capacity goes from 3 to 2 sat*/
|
/* 10. Last peer's capacity goes from 3 to 2 sat */
|
||||||
list_tail(&list_tail(&ld->peers, struct peer, list)->channels, struct channel, list)->
|
candidates[2].c->channel_info.their_config.channel_reserve = AMOUNT_SAT(1);
|
||||||
channel_info.their_config.channel_reserve = AMOUNT_SAT(1);
|
|
||||||
ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(1499), inchans, deadends, &any_offline);
|
|
||||||
|
|
||||||
/* Simulate selection ratios between excesses 25% and 75% of capacity*/
|
|
||||||
for (size_t i = n = 0; i < 1000; i++) {
|
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) == 1);
|
||||||
assert(tal_count(ret[0]) == 1);
|
assert(tal_count(ret[0]) == 1);
|
||||||
assert(any_offline == false); /* Other candidate insufficient funds. */
|
assert(route_info_eq(ret[0], candidates[1].r)
|
||||||
assert(route_info_eq(ret[0], &inchans[2])
|
|| route_info_eq(ret[0], candidates[2].r));
|
||||||
|| route_info_eq(ret[0], &inchans[3]));
|
n += route_info_eq(ret[0], candidates[1].r);
|
||||||
n += route_info_eq(ret[0], &inchans[2]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handwave over probability of this happening! Within 20% */
|
/* 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 */
|
#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 */
|
#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
|
#: wallet/test/run-wallet.c:1345
|
||||||
msgid "INSERT INTO channels (id) VALUES (1);"
|
msgid "INSERT INTO channels (id) VALUES (1);"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
# SHA256STAMP:b285290a275969586a0f96adf2f00a52d79b17dd48192976c341be6beed1daeb
|
# SHA256STAMP:53a054ad896208a2c445cd4ef4bdda2d961451d816cdb2f29f2c2f7be69683d2
|
||||||
|
@ -1509,7 +1509,7 @@ int main(int argc, const char *argv[])
|
|||||||
|
|
||||||
/* Only elements in ld we should access */
|
/* Only elements in ld we should access */
|
||||||
list_head_init(&ld->peers);
|
list_head_init(&ld->peers);
|
||||||
list_head_init(&ld->rr_channels);
|
ld->rr_counter = 0;
|
||||||
node_id_from_hexstr("02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc", 66, &ld->id);
|
node_id_from_hexstr("02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc", 66, &ld->id);
|
||||||
/* Accessed in peer destructor sanity check */
|
/* Accessed in peer destructor sanity check */
|
||||||
htlc_in_map_init(&ld->htlcs_in);
|
htlc_in_map_init(&ld->htlcs_in);
|
||||||
|
Loading…
Reference in New Issue
Block a user