Skip to content

Commit

Permalink
plugins/libplugin-pay.c: Add new payee_incoming_limit to limit number…
Browse files Browse the repository at this point in the history
… of HTLCs based on payee connectivity.

Fixes: #3926

(probably)

Changelog-Fixed: pay: Also limit the number of splits if the payee seems to have a low number of channels that can enter it, given the max-concurrent-htlcs limit.
  • Loading branch information
ZmnSCPxj authored and rustyrussell committed Sep 10, 2020
1 parent deced56 commit 0eb1e7e
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 8 deletions.
108 changes: 104 additions & 4 deletions plugins/libplugin-pay.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <bitcoin/preimage.h>
#include <ccan/array_size/array_size.h>
#include <ccan/tal/str/str.h>
#include <common/json_helpers.h>
#include <common/json_stream.h>
#include <common/pseudorand.h>
#include <common/random_select.h>
Expand Down Expand Up @@ -2925,9 +2926,6 @@ static u32 payment_max_htlcs(const struct payment *p)
return res;
}

/* Temporary comment out else GCC will complain that it is unused;
* will be used in a later commit. */
#if 0
/** payment_lower_max_htlcs
*
* @brief indicates that we have a good reason to believe that
Expand Down Expand Up @@ -2958,7 +2956,6 @@ static void payment_lower_max_htlcs(struct payment *p, u32 limit,
root->max_htlcs = limit;
}
}
#endif

static bool payment_supports_mpp(struct payment *p)
{
Expand Down Expand Up @@ -3258,3 +3255,106 @@ static void adaptive_splitter_cb(struct adaptive_split_mod_data *d, struct payme

REGISTER_PAYMENT_MODIFIER(adaptive_splitter, struct adaptive_split_mod_data *,
adaptive_splitter_data_init, adaptive_splitter_cb);


/*****************************************************************************
* payee_incoming_limit
*
* @desc every channel has a limit on the number of HTLCs it is willing to
* transport.
* This is particularly crucial for the payers and payees, as they represent
* the bottleneck to and from the network.
* The `payment_max_htlcs` function will, by itself, be able to count the
* payer-side channels, but assessing the payee requires us to probe the
* area around it.
*
* This paymod must be *after* `routehints` but *before* `presplit` paymods:
*
* - If we cannot find the destination on the public network, we can only
* use channels it put in the routehints.
* In this case, that is the number of channels we assess the payee as
* having.
* However, the `routehints` paymod may filter out some routehints, thus
* we should assess based on the post-filtered routehints.
* - The `presplit` is the first splitter that executes, so we have to have
* performed the payee-channels assessment by then.
*/

/* The default `max-concurrent-htlcs` is 30, but node operators might want
* to push it even lower to reduce their liabilities in case they have to
* unilaterally close.
* This will not necessarily improve even in a post-anchor-commitments world,
* since one of the reasons to unilaterally close is if some HTLC is about to
* expire, which of course requires the HTLCs to be published anyway, meaning
* it will still be potentially costly.
* So our initial assumption is 15 HTLCs per channel.
*
* The presplitter will divide this by `PRESPLIT_MAX_HTLC_SHARE` as well.
*/
#define ASSUMED_MAX_HTLCS_PER_CHANNEL 15

static struct command_result *
payee_incoming_limit_count(struct command *cmd,
const char *buf,
const jsmntok_t *result,
struct payment *p)
{
const jsmntok_t *channelstok;
size_t num_channels = 0;

channelstok = json_get_member(buf, result, "channels");
assert(channelstok);

/* Count channels.
* `listchannels` returns half-channels, i.e. it normally
* gives two objects per channel, one for each direction.
* However, `listchannels <source>` returns only half-channel
* objects whose `source` is the given channel.
* Thus, the length of `channels` is accurately the number
* of channels.
*/
num_channels = channelstok->size;

/* If num_channels is 0, check if there is an invoice. */
if (num_channels == 0 && p->invoice)
num_channels = tal_count(p->invoice->routes);

/* If we got a decent number of channels, limit! */
if (num_channels != 0) {
const char *why;
u32 lim;
why = tal_fmt(tmpctx,
"Destination %s has %zd channels, "
"assuming %d HTLCs per channel",
type_to_string(tmpctx, struct node_id,
p->destination),
num_channels,
ASSUMED_MAX_HTLCS_PER_CHANNEL);
lim = num_channels * ASSUMED_MAX_HTLCS_PER_CHANNEL;
payment_lower_max_htlcs(p, lim, why);
}

payment_continue(p);
return command_still_pending(cmd);
}

static void payee_incoming_limit_step_cb(void *d UNUSED, struct payment *p)
{
/* Only operate at the initialization of te root payment.
* Also, no point operating if payment does not support MPP anyway.
*/
if (p->parent || p->step != PAYMENT_STEP_INITIALIZED
|| !payment_supports_mpp(p))
return payment_continue(p);

/* Get information on the destination. */
struct out_req *req;
req = jsonrpc_request_start(p->plugin, NULL, "listchannels",
&payee_incoming_limit_count,
&payment_rpc_failure, p);
json_add_node_id(req->js, "source", p->destination);
(void) send_outreq(p->plugin, req);
}

REGISTER_PAYMENT_MODIFIER(payee_incoming_limit, void *, NULL,
payee_incoming_limit_step_cb);
5 changes: 5 additions & 0 deletions plugins/libplugin-pay.h
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,11 @@ REGISTER_PAYMENT_MODIFIER_HEADER(adaptive_splitter, struct adaptive_split_mod_da
* or are disabled. We do this only for the root payment, to minimize the
* overhead. */
REGISTER_PAYMENT_MODIFIER_HEADER(local_channel_hints, void);
/* The payee might be less well-connected than ourselves.
* This paymod limits the number of HTLCs based on the number of channels
* we detect the payee to have, in order to not exhaust the number of HTLCs
* each of those channels can bear. */
REGISTER_PAYMENT_MODIFIER_HEADER(payee_incoming_limit, void);

struct payment *payment_new(tal_t *ctx, struct command *cmd,
struct payment *parent,
Expand Down
6 changes: 4 additions & 2 deletions plugins/pay.c
Original file line number Diff line number Diff line change
Expand Up @@ -1915,9 +1915,10 @@ struct payment_modifier *paymod_mods[] = {
&exemptfee_pay_mod,
&directpay_pay_mod,
&shadowroute_pay_mod,
/* NOTE: The order in which these two paymods are executed is
/* NOTE: The order in which these three paymods are executed is
* significant!
* routehints *must* execute first before presplit.
* routehints *must* execute first before payee_incoming_limit
* which *must* execute bfore presplit.
*
* FIXME: Giving an ordered list of paymods to the paymod
* system is the wrong interface, given that the order in
Expand All @@ -1932,6 +1933,7 @@ struct payment_modifier *paymod_mods[] = {
* use.
*/
&routehints_pay_mod,
&payee_incoming_limit_pay_mod,
&presplit_pay_mod,
&waitblockheight_pay_mod,
&retry_pay_mod,
Expand Down
3 changes: 1 addition & 2 deletions tests/test_pay.py
Original file line number Diff line number Diff line change
Expand Up @@ -3520,10 +3520,9 @@ def test_large_mpp_presplit(node_factory):

@unittest.skipIf(not DEVELOPER, "builds large network, which is slow if not DEVELOPER")
@pytest.mark.slow_test
@pytest.mark.xfail(strict=True)
def test_mpp_overload_payee(node_factory, bitcoind):
"""
We have a bug where if the payer is unusually well-connected compared
We had a bug where if the payer is unusually well-connected compared
to the payee, the payee is unable to accept a large payment since the
payer will split it into lots of tiny payments, which would choke the
max-concurrent-htlcs limit of the payee.
Expand Down

0 comments on commit 0eb1e7e

Please sign in to comment.