xpay: add a special offer payable condition ...
What changed, and why it matters
This change adds a new validation check in Core Lightning's xpay plugin for a specific way of paying BOLT12 offers (sendamount with the includefees flag). It ensures such payments can only be used with offers that allow any amount, are one-time, and are in the same currency. The change appears to be a hardening/validation improvement rather than a fix for an active vulnerability.
Review as a normal defensive code change. No immediate security response appears necessary based on the commit alone. If this change relates to a known issue, request the associated security advisory or disclosure reference.
Security signals we found
New input validation for BOLT12 offer payments
Restricts sendamount/includefees payments to any-amount, non-recurring, same-currency offers
No changelog entry (Changelog-None)
No CVE, advisory, or security disclosure references present
Evidence from the diff
The commit introduces check_offer_sendamount_payable() in plugins/xpay/xpay.c, which validates BOLT12 offers when xparams->includefees_msat is set (i.e., sendamount payments with includefees). It rejects offers that are invalid, denominated in a different currency, have a fixed amount, or are recurring. The existing check_offer_payable() remains used for normal payments. This is a defensive validation addition.
Changed components
plugins/xpay/xpay.cBOLT12 offer payment validationxpay sendamount/includefees payment pathInspect captured patch +33 / −1
diff --git a/plugins/xpay/xpay.c b/plugins/xpay/xpay.c
index af516cfa..ca5c0a95 100644
--- a/plugins/xpay/xpay.c
+++ b/plugins/xpay/xpay.c
@@ -2173,6 +2173,34 @@ static struct command_result *check_offer_payable(struct command *cmd,
return NULL;
}
+static struct command_result *
+check_offer_sendamount_payable(struct command *cmd, const char *offerstr)
+{
+ const char *err;
+ struct tlv_offer *b12offer =
+ offer_decode(tmpctx, offerstr, strlen(offerstr),
+ plugin_feature_set(cmd->plugin), chainparams, &err);
+ /* Is it a valid offer? */
+ if (!b12offer)
+ return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
+ "Invalid bolt12 offer: %s", err);
+ /* FIXME: add currency support */
+ if (b12offer->offer_currency)
+ return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
+ "Cannot pay offer in different currency %s",
+ b12offer->offer_currency);
+ /* Can only be applied to *any amount* offers. */
+ if (b12offer->offer_amount) {
+ return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
+ "Offer does not allow any amount.");
+ }
+ /* Not recurrence, one time only. */
+ if (offer_recurrence(b12offer))
+ return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
+ "Cannot sendamount recurring offers");
+ return NULL;
+}
+
struct xpay_params {
struct amount_msat *msat, *maxfee, *partial, *includefees_msat;
const char **layers;
@@ -2245,7 +2273,11 @@ bip353_fetched(struct command *cmd,
json_tok_full(buf, result));
offerstr = json_strdup(tmpctx, buf, offertok);
- ret = check_offer_payable(cmd, offerstr, xparams->msat);
+ if (xparams->includefees_msat)
+ ret = check_offer_sendamount_payable(cmd, offerstr);
+ else
+ ret = check_offer_payable(cmd, offerstr, xparams->msat);
+
if (ret)
return ret;
Why this scored 29/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.