xpay: add developer option dev_maxparts
What changed, and why it matters
This commit adds a developer-only tuning knob called dev_maxparts to the xpay plugin in Core Lightning. It lets advanced users limit how many simultaneous payment routes the plugin tries at once. The change is defensive: it caps resource usage and prevents unbounded route exploration, but it is not described by the project as fixing a known security bug.
Treat as a routine hardening/resource-governance change. No urgent action required. Operators using xpay may review whether the default dev_maxparts=100 suits their node capacity. If backporting, note it is a developer option and may not be enabled in release builds.
Security signals we found
Adds a bounded resource limit (maxparts) for pending payment routes
Prevents zero-value maxparts via JSON-RPC parameter validation
Developer-only option, reducing accidental production exposure
No mention of vulnerability, CVE, or security fix in commit message
Evidence from the diff
The patch introduces a new optional developer option dev_maxparts (default 100) in plugins/xpay/xpay.c. It counts currently pending payment attempts and passes maxparts - count_pending to the underlying getroutes request, ensuring the total in-flight routes stay bounded. It also rejects maxparts == 0 at parameter validation time. The option is marked p_opt_dev, meaning it is intended for developer/testing use and is not exposed in normal production configuration.
Changed components
plugins/xpay/xpay.cxpay plugin route/request handlinggetroutes RPC parameter constructionInspect captured patch +19 / −0
diff --git a/plugins/xpay/xpay.c b/plugins/xpay/xpay.c
index 4f853a31..ec911652 100644
--- a/plugins/xpay/xpay.c
+++ b/plugins/xpay/xpay.c
@@ -78,6 +78,8 @@ 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. */
+ u32 *maxparts;
/* Do we have to do it all in a single part? */
bool disable_mpp;
/* BOLT11 payment secret (NULL for BOLT12, it uses blinded paths) */
@@ -334,6 +336,15 @@ static u32 initial_cltv_delta(const struct attempt *attempt)
return attempt->hops[0].cltv_value_in;
}
+/* Find the total number of pending attempts */
+static size_t count_current_attempts(const struct payment *payment)
+{
+ const struct attempt *i;
+ size_t result = 0;
+ list_for_each(&payment->current_attempts, i, list) { result++; }
+ return result;
+}
+
/* We total up all attempts which succeeded in the past (if we're not
* in slow mode, that's only the one which just succeeded), and then we
* assume any others currently-in-flight will also succeed. */
@@ -1296,6 +1307,7 @@ 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)) {
@@ -1356,6 +1368,9 @@ 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);
return send_payment_req(aux_cmd, payment, req);
}
@@ -1646,8 +1661,12 @@ static struct command_result *json_xpay_core(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, &payment->maxdelay, 2016),
+ p_opt_dev("dev_maxparts", param_u32, &payment->maxparts, 100),
NULL))
return command_param_failed();
+ if (*payment->maxparts == 0)
+ return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
+ "maxparts cannot be zero");
list_head_init(&payment->current_attempts);
list_head_init(&payment->past_attempts);
Why this scored 19/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.