paymod: Teach the presplit modifier to respect the chan HTLC limit

The presplit modifier could end up exceeding the maximum number of HTLCs we
can add to a channel right out the gate, so we switch to a dynamic presplit if
that is the case. The presplit will now at most use 1/3rd of the available
HTLCs on the channels if the normal split would exceed the number of availabe
HTLCs. And we also abort early if we don't have a sufficient HTLCs available.
This commit is contained in:
Christian Decker 2020-07-20 21:56:05 +02:00 committed by Rusty Russell
parent a1dc9cbd97
commit acdd9b8762
1 changed files with 57 additions and 21 deletions

View File

@ -2373,10 +2373,16 @@ REGISTER_PAYMENT_MODIFIER(waitblockheight, void *, NULL, waitblockheight_cb);
* really the case, so this is likely a lower bound on the success rate.
*
* As the network evolves these numbers are also likely to change.
*
* Finally, if applied trivially this splitter may end up creating more splits
* than the sum of all channels can support, i.e., each split results in an
* HTLC, and each channel has an upper limit on the number of HTLCs it'll
* allow us to add. If the initial split would result in more than 1/3rd of
* the total available HTLCs we clamp the number of splits to 1/3rd. We don't
* use 3/3rds in order to retain flexibility in the adaptive splitter.
*/
#define MPP_TARGET_SIZE (10 * 1000 * 1000)
#define MPP_TARGET_MSAT AMOUNT_MSAT(MPP_TARGET_SIZE)
#define MPP_TARGET_FUZZ ( 1 * 1000 * 1000)
#define PRESPLIT_MAX_HTLC_SHARE 3
static struct presplit_mod_data *presplit_mod_data_init(struct payment *p)
{
@ -2390,6 +2396,18 @@ static struct presplit_mod_data *presplit_mod_data_init(struct payment *p)
}
}
static u32 payment_max_htlcs(const struct payment *p)
{
struct channel_hint *h;
u32 res = 0;
for (size_t i = 0; i < tal_count(p->channel_hints); i++) {
h = &p->channel_hints[i];
if (h->local && h->enabled)
res += h->htlc_budget;
}
return res;
}
static bool payment_supports_mpp(struct payment *p)
{
if (p->invoice == NULL || p->invoice->features == NULL)
@ -2398,10 +2416,26 @@ static bool payment_supports_mpp(struct payment *p)
return feature_offered(p->invoice->features, OPT_BASIC_MPP);
}
/* Return fuzzed amount ~= target, but never exceeding max */
static struct amount_msat fuzzed_near(struct amount_msat target,
struct amount_msat max)
{
s64 fuzz;
struct amount_msat res = target;
/* Somewhere within 25% of target please. */
fuzz = pseudorand(target.millisatoshis / 2) /* Raw: fuzz */
- target.millisatoshis / 4; /* Raw: fuzz */
res.millisatoshis = target.millisatoshis + fuzz; /* Raw: fuzz < msat */
if (amount_msat_greater(res, max))
res = max;
return res;
}
static void presplit_cb(struct presplit_mod_data *d, struct payment *p)
{
struct payment *root = payment_root(p);
struct amount_msat amt = root->amount;
if (d->disable || p->parent != NULL || !payment_supports_mpp(p))
return payment_continue(p);
@ -2422,6 +2456,8 @@ static void presplit_cb(struct presplit_mod_data *d, struct payment *p)
/* The presplitter only acts on the root and only in the first
* step. */
size_t count = 0;
u32 htlcs = payment_max_htlcs(p) / PRESPLIT_MAX_HTLC_SHARE;
struct amount_msat target, amt = p->amount;
/* We need to opt-in to the MPP sending facility no matter
* what we do. That means setting all partids to a non-zero
@ -2434,31 +2470,31 @@ static void presplit_cb(struct presplit_mod_data *d, struct payment *p)
* but makes debugging a bit easier. */
root->next_partid++;
if (htlcs == 0) {
p->abort = true;
return payment_fail(
p, "Cannot attempt payment, we have no channel to "
"which we can add an HTLC");
} else if (p->amount.millisatoshis / MPP_TARGET_SIZE > htlcs) /* Raw: division */
target.millisatoshis = p->amount.millisatoshis / htlcs; /* Raw: division */
else
target = AMOUNT_MSAT(MPP_TARGET_SIZE);
/* If we are already below the target size don't split it
* either. */
if (amount_msat_greater(MPP_TARGET_MSAT, p->amount))
if (amount_msat_greater(target, p->amount))
return payment_continue(p);
/* Ok, we know we should split, so split here and then skip this
* payment and start the children instead. */
while (!amount_msat_eq(amt, AMOUNT_MSAT(0))) {
double multiplier;
struct payment *c =
payment_new(p, NULL, p, p->modifiers);
/* Pseudorandom number in the range [-1, 1]. */
double rand = pseudorand_double() * 2 - 1;
double multiplier;
c->amount.millisatoshis = rand * MPP_TARGET_FUZZ + MPP_TARGET_SIZE; /* Raw: Multiplication */
/* Clamp the value to the total amount, so the fuzzing
* doesn't go above the total. */
if (amount_msat_greater(c->amount, amt))
c->amount = amt;
multiplier =
(double)c->amount.millisatoshis / (double)p->amount.millisatoshis; /* Raw: msat division. */
/* Get ~ target, but don't exceed amt */
c->amount = fuzzed_near(target, amt);
if (!amount_msat_sub(&amt, amt, c->amount))
plugin_err(
@ -2471,6 +2507,7 @@ static void presplit_cb(struct presplit_mod_data *d, struct payment *p)
/* Now adjust the constraints so we don't multiply them
* when splitting. */
multiplier = (double)c->amount.millisatoshis / (double)p->amount.millisatoshis; /* Raw: msat division. */
c->constraints.fee_budget.millisatoshis *= multiplier; /* Raw: Multiplication */
payment_start(c);
count++;
@ -2480,11 +2517,10 @@ static void presplit_cb(struct presplit_mod_data *d, struct payment *p)
p->route = NULL;
p->why = tal_fmt(
p,
"Split into %zu sub-payments due to initial size (%s > "
"%dmsat)",
"Split into %zu sub-payments due to initial size (%s > %s)",
count,
type_to_string(tmpctx, struct amount_msat, &root->amount),
MPP_TARGET_SIZE);
type_to_string(tmpctx, struct amount_msat, &target));
payment_set_step(p, PAYMENT_STEP_SPLIT);
plugin_log(p->plugin, LOG_INFORM, "%s", p->why);
}