Browse Source

paymod: Implement adaptive splitter

This modifier splits a payment that has been attempted a number of times (by a
modifier earlier in the mod chain) and has failed consistently. It splits the
amount roughly in half, with a but if random fuzz, and then starts a new round
of attempts for the two smaller amounts.
release-0.9.0
Christian Decker 4 years ago
parent
commit
535aaca109
  1. 71
      plugins/libplugin-pay.c
  2. 1
      plugins/libplugin-pay.h

71
plugins/libplugin-pay.c

@ -2167,3 +2167,74 @@ static void presplit_cb(void *d, struct payment *p)
}
REGISTER_PAYMENT_MODIFIER(presplit, void *, NULL, presplit_cb);
/*****************************************************************************
* Adaptive splitter -- Split payment if we can't get it through.
*
* The adaptive splitter splits the amount of a failed payment in half, with
* +/- 10% randomness, and then starts two attempts, one for either side of
* the split. The goal is to find two smaller routes, that still adhere to our
* constraints, but that can complete the payment.
*/
#define MPP_ADAPTIVE_LOWER_LIMIT AMOUNT_MSAT(100 * 1000)
static void adaptive_splitter_cb(void *d, struct payment *p)
{
struct payment *root = payment_root(p);
if (p->step == PAYMENT_STEP_ONION_PAYLOAD) {
/* We need to tell the last hop the total we're going to
* send. Presplit disables amount fuzzing, so we should always
* get the exact value through. */
size_t lastidx = tal_count(p->createonion_request->hops) - 1;
struct createonion_hop *hop = &p->createonion_request->hops[lastidx];
if (hop->style == ROUTE_HOP_TLV) {
struct tlv_field **fields = &hop->tlv_payload->fields;
tlvstream_set_tlv_payload_data(
fields, root->payment_secret,
root->amount.millisatoshis); /* Raw: onion payload */
}
} else if (p->step == PAYMENT_STEP_FAILED && !p->abort) {
if (amount_msat_greater(p->amount, MPP_ADAPTIVE_LOWER_LIMIT)) {
struct payment *a, *b;
/* Random number in the range [90%, 110%] */
double rand = pseudorand_double() * 0.2 + 0.9;
u64 mid = p->amount.millisatoshis / 2 * rand; /* Raw: multiplication */
bool ok;
a = payment_new(p, NULL, p, p->modifiers);
b = payment_new(p, NULL, p, p->modifiers);
a->amount.millisatoshis = mid; /* Raw: split. */
b->amount.millisatoshis -= mid; /* Raw: split. */
/* Adjust constraints since we don't want to double our
* fee allowance when we split. */
a->constraints.fee_budget.millisatoshis *= (double)a->amount.millisatoshis / (double)p->amount.millisatoshis; /* Raw: msat division. */
ok = amount_msat_sub(&b->constraints.fee_budget,
p->constraints.fee_budget,
a->constraints.fee_budget);
/* Should not fail, mid is less than 55% of original
* amount. fee_budget_a <= 55% of fee_budget_p (parent
* of the new payments).*/
assert(ok);
payment_start(a);
payment_start(b);
p->step = PAYMENT_STEP_SPLIT;
} else {
plugin_log(p->plugin, LOG_INFORM,
"Lower limit of adaptive splitter reached "
"(%s < %s), not splitting further.",
type_to_string(tmpctx, struct amount_msat,
&p->amount),
type_to_string(tmpctx, struct amount_msat,
&MPP_ADAPTIVE_LOWER_LIMIT));
}
}
payment_continue(p);
}
REGISTER_PAYMENT_MODIFIER(adaptive_splitter, void *, NULL,
adaptive_splitter_cb);

1
plugins/libplugin-pay.h

@ -327,6 +327,7 @@ REGISTER_PAYMENT_MODIFIER_HEADER(shadowroute, struct shadow_route_data);
REGISTER_PAYMENT_MODIFIER_HEADER(directpay, struct direct_pay_data);
extern struct payment_modifier waitblockheight_pay_mod;
extern struct payment_modifier presplit_pay_mod;
extern struct payment_modifier adaptive_splitter_pay_mod;
/* For the root payment we can seed the channel_hints with the result from
* `listpeers`, hence avoid channels that we know have insufficient capacity

Loading…
Cancel
Save