diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index a88fb2bf6..62b59d91c 100644 --- a/plugins/libplugin-pay.c +++ b/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); diff --git a/plugins/libplugin-pay.h b/plugins/libplugin-pay.h index 7cbb4a4be..3851bac6e 100644 --- a/plugins/libplugin-pay.h +++ b/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