mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-19 07:04:22 +01:00
invoice: overhaul routehints to use topology.listincoming, cleanup.
This turned into a more extensive cleanup than intended. The previous warnings were overlapping and confusing, especially now MPP is the norm. *warning_capacity* is now the "even under best circumstances, we don't have enough incoming capacity", which is really what warning_mpp_capacity was trying to say (no longer printed). *warning_offline* and *warning_deadends* are only given if adding such peers would have helped give capacity (i.e. not if *warning_capacity* is set). The new *warning_private_unused* tells you that we would have sufficient capacity, but we refused to expose private channels. The test cases have been enhanced to cover the new warnings. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> Changelog-Added: JSON-RPC: `invoice` now gives `warning_private_unused` if unused unannounced channels could have provided sufficient capacity. Changelog-Changed: JSON-RPC: `invoice` warnings are now better defined, and `warning_mpp_capacity` is no longer included (since `warning_capacity` covers that).
This commit is contained in:
@@ -1,26 +1,12 @@
|
||||
#include <common/bolt11.h>
|
||||
#include <common/json_helpers.h>
|
||||
#include <common/utils.h>
|
||||
#include <gossipd/gossipd_wiregen.h>
|
||||
#include <lightningd/lightningd.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)
|
||||
{
|
||||
@@ -34,100 +20,204 @@ static bool scid_in_arr(const struct short_channel_id *scidarr,
|
||||
struct routehint_candidate *
|
||||
routehint_candidates(const tal_t *ctx,
|
||||
struct lightningd *ld,
|
||||
const u8 *incoming_channels_reply,
|
||||
bool expose_all_private,
|
||||
const char *buf,
|
||||
const jsmntok_t *toks,
|
||||
const bool *expose_all_private,
|
||||
const struct short_channel_id *hints,
|
||||
bool *none_public,
|
||||
bool *deadends,
|
||||
struct amount_msat *amount_offline)
|
||||
struct amount_msat *avail_capacity,
|
||||
struct amount_msat *private_capacity,
|
||||
struct amount_msat *deadend_capacity,
|
||||
struct amount_msat *offline_capacity)
|
||||
{
|
||||
struct routehint_candidate *candidates;
|
||||
struct route_info *inchans, *private;
|
||||
bool *inchan_deadends, *private_deadends;
|
||||
struct routehint_candidate *candidates, *privcandidates;
|
||||
const jsmntok_t *t, *arr;
|
||||
size_t i;
|
||||
|
||||
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));
|
||||
log_debug(ld->log, "routehint: %.*s",
|
||||
json_tok_full_len(toks),
|
||||
json_tok_full(buf, toks));
|
||||
|
||||
*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);
|
||||
}
|
||||
}
|
||||
/* We get the full JSON, including result. */
|
||||
t = json_get_member(buf, toks, "result");
|
||||
if (!t)
|
||||
fatal("Missing result from listincoming: %.*s",
|
||||
json_tok_full_len(toks),
|
||||
json_tok_full(buf, toks));
|
||||
arr = json_get_member(buf, t, "incoming");
|
||||
|
||||
candidates = tal_arr(ctx, struct routehint_candidate, 0);
|
||||
*amount_offline = AMOUNT_MSAT(0);
|
||||
privcandidates = tal_arr(tmpctx, struct routehint_candidate, 0);
|
||||
*none_public = true;
|
||||
*deadend_capacity = AMOUNT_MSAT(0);
|
||||
*offline_capacity = AMOUNT_MSAT(0);
|
||||
*avail_capacity = AMOUNT_MSAT(0);
|
||||
*private_capacity = AMOUNT_MSAT(0);
|
||||
|
||||
for (size_t i = 0; i < tal_count(inchans); i++) {
|
||||
struct peer *peer;
|
||||
/* We combine the JSON output which knows the peers' details,
|
||||
* with our internal information */
|
||||
json_for_each_arr(i, t, arr) {
|
||||
struct amount_msat capacity;
|
||||
const char *err;
|
||||
struct routehint_candidate candidate;
|
||||
struct amount_msat fee_base;
|
||||
struct route_info *r;
|
||||
struct peer *peer;
|
||||
bool is_public;
|
||||
|
||||
r = tal(tmpctx, struct route_info);
|
||||
|
||||
err = json_scan(tmpctx, buf, t,
|
||||
"{id:%"
|
||||
",short_channel_id:%"
|
||||
",fee_base_msat:%"
|
||||
",fee_proportional_millionths:%"
|
||||
",cltv_expiry_delta:%"
|
||||
",incoming_capacity_msat:%"
|
||||
"}",
|
||||
JSON_SCAN(json_to_node_id, &r->pubkey),
|
||||
JSON_SCAN(json_to_short_channel_id,
|
||||
&r->short_channel_id),
|
||||
JSON_SCAN(json_to_msat, &fee_base),
|
||||
JSON_SCAN(json_to_u32,
|
||||
&r->fee_proportional_millionths),
|
||||
JSON_SCAN(json_to_u16, &r->cltv_expiry_delta),
|
||||
JSON_SCAN(json_to_msat, &capacity));
|
||||
|
||||
if (err) {
|
||||
fatal("Invalid return from listincoming (%s): %.*s",
|
||||
err,
|
||||
json_tok_full_len(toks),
|
||||
json_tok_full(buf, toks));
|
||||
}
|
||||
|
||||
/* Do we know about this peer? */
|
||||
peer = peer_by_id(ld, &inchans[i].pubkey);
|
||||
if (!peer)
|
||||
peer = peer_by_id(ld, &r->pubkey);
|
||||
if (!peer) {
|
||||
log_debug(ld->log, "%s: unknown peer",
|
||||
type_to_string(tmpctx,
|
||||
struct short_channel_id,
|
||||
&r->short_channel_id));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Does it have a channel in state CHANNELD_NORMAL */
|
||||
candidate.c = peer_normal_channel(peer);
|
||||
if (!candidate.c)
|
||||
continue;
|
||||
|
||||
/* Is it a dead-end? */
|
||||
if (inchan_deadends[i]) {
|
||||
*deadends = true;
|
||||
if (!candidate.c) {
|
||||
log_debug(ld->log, "%s: abnormal channel",
|
||||
type_to_string(tmpctx,
|
||||
struct short_channel_id,
|
||||
&r->short_channel_id));
|
||||
continue;
|
||||
}
|
||||
|
||||
candidate.capacity = channel_amount_receivable(candidate.c);
|
||||
|
||||
/* Now we can tell if it's public. If so (even if it's otherwise
|
||||
* unusable), we *don't* expose private channels! */
|
||||
is_public = (candidate.c->channel_flags
|
||||
& CHANNEL_FLAGS_ANNOUNCE_CHANNEL);
|
||||
|
||||
if (is_public)
|
||||
*none_public = false;
|
||||
|
||||
/* If they explicitly say to expose all private ones, consider
|
||||
* it public. */
|
||||
if (expose_all_private != NULL && *expose_all_private)
|
||||
is_public = true;
|
||||
|
||||
r->fee_base_msat = fee_base.millisatoshis; /* Raw: route_info */
|
||||
/* Could wrap: if so ignore */
|
||||
if (!amount_msat_eq(amount_msat(r->fee_base_msat), fee_base)) {
|
||||
log_debug(ld->log,
|
||||
"Peer charging insane fee %.*s; ignoring",
|
||||
json_tok_full_len(t),
|
||||
json_tok_full(buf, t));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Consider only hints they gave */
|
||||
if (hints) {
|
||||
log_debug(ld->log, "We have hints!");
|
||||
if (!scid_in_arr(hints, &r->short_channel_id)) {
|
||||
log_debug(ld->log, "scid %s not in hints",
|
||||
type_to_string(tmpctx,
|
||||
struct short_channel_id,
|
||||
&r->short_channel_id));
|
||||
continue;
|
||||
}
|
||||
/* If they give us a hint, we use even if capacity 0 */
|
||||
} else if (amount_msat_eq(capacity, AMOUNT_MSAT(0))) {
|
||||
log_debug(ld->log, "%s: deadend",
|
||||
type_to_string(tmpctx,
|
||||
struct short_channel_id,
|
||||
&r->short_channel_id));
|
||||
if (!amount_msat_add(deadend_capacity,
|
||||
*deadend_capacity,
|
||||
candidate.capacity))
|
||||
fatal("Overflow summing deadend capacity!");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Is it offline? */
|
||||
if (candidate.c->owner == NULL) {
|
||||
if (!amount_msat_add(amount_offline,
|
||||
*amount_offline,
|
||||
log_debug(ld->log, "%s: offline",
|
||||
type_to_string(tmpctx,
|
||||
struct short_channel_id,
|
||||
&r->short_channel_id));
|
||||
if (!amount_msat_add(offline_capacity,
|
||||
*offline_capacity,
|
||||
candidate.capacity))
|
||||
fatal("Overflow summing offline capacity!");
|
||||
continue;
|
||||
}
|
||||
candidate.r = &inchans[i];
|
||||
tal_arr_expand(&candidates, candidate);
|
||||
|
||||
/* OK, finish it and append to one of the arrays. */
|
||||
if (is_public) {
|
||||
log_debug(ld->log, "%s: added to public",
|
||||
type_to_string(tmpctx,
|
||||
struct short_channel_id,
|
||||
&r->short_channel_id));
|
||||
candidate.r = tal_steal(candidates, r);
|
||||
tal_arr_expand(&candidates, candidate);
|
||||
if (!amount_msat_add(avail_capacity,
|
||||
*avail_capacity,
|
||||
candidate.capacity)) {
|
||||
fatal("Overflow summing pub capacities %s + %s",
|
||||
type_to_string(tmpctx, struct amount_msat,
|
||||
avail_capacity),
|
||||
type_to_string(tmpctx, struct amount_msat,
|
||||
&candidate.capacity));
|
||||
}
|
||||
} else {
|
||||
log_debug(ld->log, "%s: added to private",
|
||||
type_to_string(tmpctx,
|
||||
struct short_channel_id,
|
||||
&r->short_channel_id));
|
||||
candidate.r = tal_steal(privcandidates, r);
|
||||
tal_arr_expand(&privcandidates, candidate);
|
||||
if (!amount_msat_add(private_capacity,
|
||||
*private_capacity,
|
||||
candidate.capacity)) {
|
||||
fatal("Overflow summing priv capacities %s + %s",
|
||||
type_to_string(tmpctx, struct amount_msat,
|
||||
private_capacity),
|
||||
type_to_string(tmpctx, struct amount_msat,
|
||||
&candidate.capacity));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* By default, only consider private channels if there are
|
||||
* no public channels *at all* */
|
||||
if (expose_all_private == NULL
|
||||
&& tal_count(candidates) == 0 && *none_public) {
|
||||
log_debug(ld->log, "No publics: using private channels");
|
||||
tal_free(candidates);
|
||||
candidates = tal_steal(ctx, privcandidates);
|
||||
*avail_capacity = *private_capacity;
|
||||
/* This reflects *unused* private capacity. */
|
||||
*private_capacity = AMOUNT_MSAT(0);
|
||||
}
|
||||
|
||||
return candidates;
|
||||
|
||||
Reference in New Issue
Block a user