xpay: restrict maxparts to 6 for non-public nodes, but remove it if we can't route.

This attempts to solve a problem we have with Phoenix clients:

	This payment has been split in two many parts by the sender: 31 parts vs max 6 parts allowed for on-the-fly funding.

The problem is that we don't have any way in bolt11 or bolt12 to
specify the maximum number of HTLCs.

As a workaround, we start by restricting askrene to 6 parts if the
node is not openly reachable, and if it struggles, we remove the
restriction.  This would work much better if askrene handled maxparts
more completely!

See-Also: https://github.com/ElementsProject/lightning/issues/8331
Changelog-Fixed: `xpay` will not try to send too many HTLCs through unknown channels (6, as that is Phoenix's limit) unless it has no choice
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2025-11-17 16:14:28 +10:30
parent 145af08feb
commit 73147dafe2
2 changed files with 39 additions and 23 deletions

View File

@@ -85,7 +85,7 @@ struct payment {
struct amount_msat maxfee;
/* Maximum delay on the route we're ok with */
u32 maxdelay;
/* Maximum number of payment routes that can be pending. */
/* If non-zero: maximum number of payment routes that can be pending. */
u32 maxparts;
/* Do we have to do it all in a single part? */
bool disable_mpp;
@@ -181,7 +181,6 @@ static struct command_result *xpay_core(struct command *cmd,
u32 retryfor,
const struct amount_msat *partial,
u32 maxdelay,
u32 dev_maxparts,
bool as_pay);
/* Wrapper for pending commands (ignores return) */
@@ -1319,6 +1318,16 @@ static struct command_result *getroutes_done_err(struct command *aux_cmd,
msg = json_strdup(tmpctx, buf, json_get_member(buf, error, "message"));
json_to_int(buf, json_get_member(buf, error, "code"), &code);
/* If we were restricting the number of parts, we remove that
* restriction and try again. */
if (payment->maxparts) {
payment_log(payment, LOG_INFORM,
"getroute failed with maxparts=%u, so retrying without that restriction",
payment->maxparts);
payment->maxparts = 0;
return getroutes_for(aux_cmd, payment, payment->amount_being_routed);
}
/* Simple case: failed immediately. */
if (payment->total_num_attempts == 0) {
payment_give_up(aux_cmd, payment, code, "Failed: %s", msg);
@@ -1380,7 +1389,6 @@ static struct command_result *getroutes_for(struct command *aux_cmd,
struct out_req *req;
const struct pubkey *dst;
struct amount_msat maxfee;
size_t count_pending;
/* I would normally assert here, but we have reports of this happening... */
if (amount_msat_is_zero(deliver)) {
@@ -1463,9 +1471,11 @@ static struct command_result *getroutes_for(struct command *aux_cmd,
json_add_amount_msat(req->js, "maxfee_msat", maxfee);
json_add_u32(req->js, "final_cltv", payment->final_cltv);
json_add_u32(req->js, "maxdelay", payment->maxdelay);
count_pending = count_current_attempts(payment);
assert(payment->maxparts > count_pending);
json_add_u32(req->js, "maxparts", payment->maxparts - count_pending);
if (payment->maxparts) {
size_t count_pending = count_current_attempts(payment);
assert(payment->maxparts > count_pending);
json_add_u32(req->js, "maxparts", payment->maxparts - count_pending);
}
return send_payment_req(aux_cmd, payment, req);
}
@@ -1776,7 +1786,7 @@ struct xpay_params {
struct amount_msat *msat, *maxfee, *partial;
const char **layers;
unsigned int retryfor;
u32 maxdelay, dev_maxparts;
u32 maxdelay;
const char *bip353;
};
@@ -1793,7 +1803,7 @@ invoice_fetched(struct command *cmd,
return xpay_core(cmd, take(to_canonical_invstr(NULL, take(inv))),
NULL, params->maxfee, params->layers,
params->retryfor, params->partial, params->maxdelay,
params->dev_maxparts, false);
false);
}
static struct command_result *
@@ -1854,7 +1864,7 @@ static struct command_result *json_xpay_params(struct command *cmd,
struct amount_msat *msat, *maxfee, *partial;
const char *invstring;
const char **layers;
u32 *maxdelay, *maxparts;
u32 *maxdelay;
unsigned int *retryfor;
struct out_req *req;
struct xpay_params *xparams;
@@ -1867,14 +1877,9 @@ static struct command_result *json_xpay_params(struct command *cmd,
p_opt_def("retry_for", param_number, &retryfor, 60),
p_opt("partial_msat", param_msat, &partial),
p_opt_def("maxdelay", param_u32, &maxdelay, 2016),
p_opt_dev("dev_maxparts", param_u32, &maxparts, 100),
NULL))
return command_param_failed();
if (*maxparts == 0)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"maxparts cannot be zero");
/* Is this a one-shot vibe payment? Kids these days! */
if (!as_pay && bolt12_has_offer_prefix(invstring)) {
struct command_result *ret;
@@ -1893,7 +1898,6 @@ static struct command_result *json_xpay_params(struct command *cmd,
xparams->layers = layers;
xparams->retryfor = *retryfor;
xparams->maxdelay = *maxdelay;
xparams->dev_maxparts = *maxparts;
xparams->bip353 = NULL;
return do_fetchinvoice(cmd, invstring, xparams);
@@ -1908,7 +1912,6 @@ static struct command_result *json_xpay_params(struct command *cmd,
xparams->layers = layers;
xparams->retryfor = *retryfor;
xparams->maxdelay = *maxdelay;
xparams->dev_maxparts = *maxparts;
xparams->bip353 = invstring;
req = jsonrpc_request_start(cmd, "fetchbip353",
@@ -1919,7 +1922,7 @@ static struct command_result *json_xpay_params(struct command *cmd,
}
return xpay_core(cmd, invstring,
msat, maxfee, layers, *retryfor, partial, *maxdelay, *maxparts,
msat, maxfee, layers, *retryfor, partial, *maxdelay,
as_pay);
}
@@ -1931,11 +1934,12 @@ static struct command_result *xpay_core(struct command *cmd,
u32 retryfor,
const struct amount_msat *partial,
u32 maxdelay,
u32 dev_maxparts,
bool as_pay)
{
struct payment *payment = tal(cmd, struct payment);
struct xpay *xpay = xpay_of(cmd->plugin);
struct gossmap *gossmap = get_gossmap(xpay);
struct node_id dstid;
u64 now, invexpiry;
struct out_req *req;
char *err;
@@ -1959,10 +1963,8 @@ static struct command_result *xpay_core(struct command *cmd,
else
payment->layers = NULL;
payment->maxdelay = maxdelay;
payment->maxparts = dev_maxparts;
if (bolt12_has_prefix(payment->invstring)) {
struct gossmap *gossmap = get_gossmap(xpay);
struct tlv_invoice *b12inv
= invoice_decode(tmpctx, payment->invstring,
strlen(payment->invstring),
@@ -2088,6 +2090,15 @@ static struct command_result *xpay_core(struct command *cmd,
} else
payment->maxfee = *maxfee;
/* If we are using an unannounced channel, we assume we can
* only do 6 HTLCs at a time. This is currently true for
* Phoenix, which is a large and significant node. */
node_id_from_pubkey(&dstid, &payment->destination);
if (!gossmap_find_node(gossmap, &dstid))
payment->maxparts = 6;
else
payment->maxparts = 0;
/* Now preapprove, then start payment. */
if (command_check_only(cmd)) {
req = jsonrpc_request_start(cmd, "check",