pay: accept invstring as first parameter name.
What changed, and why it matters
This is a routine feature change, not a security fix. The `pay` command now accepts either `bolt11` or `invstring` as the name for its invoice-string parameter, so users don't have to change their scripts when a future version renames the command. No vulnerability is present.
No security action needed. Treat as a normal API-compatibility improvement.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit adds an alias parameter name invstring for the existing bolt11 parameter in the JSON-RPC pay plugin. It introduces a p_req_var macro to allow a runtime-chosen parameter name, selects invstring when the request uses that key, and updates schemas, help text, and tests accordingly. The underlying parser and validation (param_invstring) are unchanged.
Changed components
plugins/pay.ccommon/json_param.hdoc/schemas/pay.jsoncontrib/msggen/msggen/schema.jsontests/test_pay.pytests/test_plugin.pyInspect captured patch +22 / −5
diff --git a/common/json_param.h b/common/json_param.h
index a000c71b..7b25715a 100644
--- a/common/json_param.h
+++ b/common/json_param.h
@@ -89,7 +89,7 @@ enum param_style {
* Add a required parameter.
*/
#define p_req_depr(name, depr_start, depr_end, cbx, arg) \
- name"", \
+ name, \
PARAM_REQUIRED, \
(depr_start), (depr_end), \
(param_cbx)(cbx), \
@@ -99,7 +99,10 @@ enum param_style {
(const jsmntok_t *)NULL, \
(arg)) == (struct command_result *)NULL)
-#define p_req(name, cbx, arg) p_req_depr(name, NULL, NULL, (cbx), (arg))
+#define p_req(name, cbx, arg) p_req_depr(name"", NULL, NULL, (cbx), (arg))
+
+/* name is not a literal: I promise its lifetime is infinite though! */
+#define p_req_var(name, cbx, arg) p_req_depr(name, NULL, NULL, (cbx), (arg))
/*
* Add an optional parameter. *arg is set to NULL if it isn't found.
diff --git a/contrib/msggen/msggen/schema.json b/contrib/msggen/msggen/schema.json
index 8c929073..7841b7cb 100644
--- a/contrib/msggen/msggen/schema.json
+++ b/contrib/msggen/msggen/schema.json
@@ -30449,6 +30449,8 @@
"bolt11": {
"type": "string",
"description": [
+ "NOTE: `invstring` is an alternate name for this parameter, since v26.06, for xpay compatibility.",
+ "",
"Bolt11 or bolt12 invoice (such as one received from lightningd-fetchinvoice(7)). For a bolt11 invoice, if it does not contain an amount, *amount_msat* is required, otherwise if it is specified it must be *null*."
]
},
diff --git a/doc/schemas/pay.json b/doc/schemas/pay.json
index 5e89a6ca..8d023c5b 100644
--- a/doc/schemas/pay.json
+++ b/doc/schemas/pay.json
@@ -21,6 +21,8 @@
"bolt11": {
"type": "string",
"description": [
+ "NOTE: `invstring` is an alternate name for this parameter, since v26.06, for xpay compatibility.",
+ "",
"Bolt11 or bolt12 invoice (such as one received from lightningd-fetchinvoice(7)). For a bolt11 invoice, if it does not contain an amount, *amount_msat* is required, otherwise if it is specified it must be *null*."
]
},
diff --git a/plugins/pay.c b/plugins/pay.c
index 64bc4879..5f338ed7 100644
--- a/plugins/pay.c
+++ b/plugins/pay.c
@@ -1274,6 +1274,7 @@ static struct command_result *json_pay(struct command *cmd,
struct out_req *req;
struct route_exclusion **exclusions;
bool *dev_use_shadow;
+ bool use_invstring;
/* If any of the modifiers need to add params to the JSON-RPC call we
* would add them to the `param()` call below, and have them be
@@ -1285,9 +1286,10 @@ static struct command_result *json_pay(struct command *cmd,
* deployed by Lightning implementations.
*/
/* FIXME: Typo in spec for CLTV in descripton! But it breaks our spelling check, so we omit it above */
+ /* We accept invstring, too (and use it in the help message) */
+ use_invstring = (!params || (params->type == JSMN_OBJECT && json_get_member(buf, params, "invstring")));
if (!param_check(cmd, buf, params,
- /* FIXME: parameter should be invstring now */
- p_req("bolt11", param_invstring, &b11str),
+ p_req_var(use_invstring ? "invstring" : "bolt11", param_invstring, &b11str),
p_opt("amount_msat", param_msat, &msat),
p_opt("label", param_string, &label),
p_opt_def("riskfactor", param_millionths,
diff --git a/tests/test_pay.py b/tests/test_pay.py
index 81e2b1e7..e33df239 100644
--- a/tests/test_pay.py
+++ b/tests/test_pay.py
@@ -84,6 +84,14 @@ def test_pay(node_factory):
assert apys_1[0]['routed_in_msat'] == apys_2[0]['routed_out_msat']
+def test_pay_invstring(node_factory):
+ l1, l2 = node_factory.line_graph(2)
+
+ l1.rpc.check_request_schemas = False
+ inv = l2.rpc.invoice(123000, 'test_pay_invstring', 'description')['bolt11']
+ l1.rpc.call('pay', {'invstring': inv})
+
+
def test_pay_amounts(node_factory):
l1, l2 = node_factory.line_graph(2)
inv = l2.rpc.invoice(Millisatoshi("123sat"), 'test_pay_amounts', 'description')['bolt11']
diff --git a/tests/test_plugin.py b/tests/test_plugin.py
index b4e215f2..b75df23b 100644
--- a/tests/test_plugin.py
+++ b/tests/test_plugin.py
@@ -441,7 +441,7 @@ def test_pay_plugin(node_factory):
l1.rpc.call('pay')
# Make sure usage messages are present.
- msg = 'pay bolt11 [amount_msat] [label] [riskfactor] [maxfeepercent] '\
+ msg = 'pay invstring [amount_msat] [label] [riskfactor] [maxfeepercent] '\
'[retry_for] [maxdelay] [exemptfee] [localinvreqid] [exclude] '\
'[maxfee] [description] [partial_msat]'
# We run with --developer:
Why this scored 15/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.