Skip to content

Commit fff7dca

Browse files
committed
xpay: allow setting payer_note for spamming when paying BIP353 or raw offers.
Simply forwards this to the fetchinvoice call. Signed-off-by: Rusty Russell <[email protected]> Changelog-Added: JSON-RPC: `xpay` supports `payer_note` to embed a note in the invoice when paying BIP353 or offers.
1 parent 52822d6 commit fff7dca

File tree

9 files changed

+91
-42
lines changed

9 files changed

+91
-42
lines changed

.msggen.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3887,6 +3887,7 @@
38873887
"Xpay.maxdelay": 7,
38883888
"Xpay.maxfee": 3,
38893889
"Xpay.partial_msat": 6,
3890+
"Xpay.payer_note": 8,
38903891
"Xpay.retry_for": 5
38913892
},
38923893
"XpayResponse": {
@@ -13422,6 +13423,10 @@
1342213423
"added": "v24.11",
1342313424
"deprecated": null
1342413425
},
13426+
"Xpay.payer_note": {
13427+
"added": "v25.12",
13428+
"deprecated": null
13429+
},
1342513430
"Xpay.payment_preimage": {
1342613431
"added": "v24.11",
1342713432
"deprecated": null

cln-grpc/proto/node.proto

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cln-grpc/src/convert.rs

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cln-rpc/src/model.rs

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

contrib/msggen/msggen/schema.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36058,6 +36058,13 @@
3605836058
"A payment may be delayed for up to `maxdelay` blocks by another node; clients should be prepared for this worst case."
3605936059
],
3606036060
"default": "2016"
36061+
},
36062+
"payer_note": {
36063+
"type": "string",
36064+
"added": "v25.12",
36065+
"description": [
36066+
"To ask the issuer to include in the fetched invoice (only if invstring is a BIP353 address or offer)"
36067+
]
3606136068
}
3606236069
}
3606336070
},

contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py

Lines changed: 42 additions & 42 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

doc/schemas/xpay.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@
6565
"A payment may be delayed for up to `maxdelay` blocks by another node; clients should be prepared for this worst case."
6666
],
6767
"default": "2016"
68+
},
69+
"payer_note": {
70+
"type": "string",
71+
"added": "v25.12",
72+
"description": [
73+
"To ask the issuer to include in the fetched invoice (only if invstring is a BIP353 address or offer)"
74+
]
6875
}
6976
}
7077
},

plugins/xpay/xpay.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1695,6 +1695,7 @@ struct xpay_params {
16951695
unsigned int retryfor;
16961696
u32 maxdelay, dev_maxparts;
16971697
const char *bip353;
1698+
const char *payer_note;
16981699
};
16991700

17001701
static struct command_result *
@@ -1729,6 +1730,8 @@ do_fetchinvoice(struct command *cmd, const char *offerstr, struct xpay_params *x
17291730
json_add_amount_msat(req->js, "amount_msat", *xparams->msat);
17301731
if (xparams->bip353)
17311732
json_add_string(req->js, "bip353", xparams->bip353);
1733+
if (xparams->payer_note)
1734+
json_add_string(req->js, "payer_note", xparams->payer_note);
17321735
return send_outreq(req);
17331736
}
17341737

@@ -1777,6 +1780,7 @@ static struct command_result *json_xpay_params(struct command *cmd,
17771780
unsigned int *retryfor;
17781781
struct out_req *req;
17791782
struct xpay_params *xparams;
1783+
const char *payer_note;
17801784

17811785
if (!param_check(cmd, buffer, params,
17821786
p_req("invstring", param_invstring, &invstring),
@@ -1786,6 +1790,7 @@ static struct command_result *json_xpay_params(struct command *cmd,
17861790
p_opt_def("retry_for", param_number, &retryfor, 60),
17871791
p_opt("partial_msat", param_msat, &partial),
17881792
p_opt_def("maxdelay", param_u32, &maxdelay, 2016),
1793+
p_opt("payer_note", param_string, &payer_note),
17891794
p_opt_dev("dev_maxparts", param_u32, &maxparts, 100),
17901795
NULL))
17911796
return command_param_failed();
@@ -1812,6 +1817,7 @@ static struct command_result *json_xpay_params(struct command *cmd,
18121817
xparams->layers = layers;
18131818
xparams->retryfor = *retryfor;
18141819
xparams->maxdelay = *maxdelay;
1820+
xparams->payer_note = tal_steal(xparams, payer_note);
18151821
xparams->dev_maxparts = *maxparts;
18161822
xparams->bip353 = NULL;
18171823

@@ -1827,6 +1833,7 @@ static struct command_result *json_xpay_params(struct command *cmd,
18271833
xparams->layers = layers;
18281834
xparams->retryfor = *retryfor;
18291835
xparams->maxdelay = *maxdelay;
1836+
xparams->payer_note = tal_steal(xparams, payer_note);
18301837
xparams->dev_maxparts = *maxparts;
18311838
xparams->bip353 = invstring;
18321839

@@ -1841,6 +1848,10 @@ static struct command_result *json_xpay_params(struct command *cmd,
18411848
return send_outreq(req);
18421849
}
18431850

1851+
if (payer_note)
1852+
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
1853+
"payer_note only valid when paying an offer or BIP353 address");
1854+
18441855
return xpay_core(cmd, invstring,
18451856
msat, maxfee, layers, *retryfor, partial, *maxdelay, *maxparts,
18461857
as_pay);

tests/test_xpay.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,12 @@ def test_xpay_simple(node_factory):
201201
with pytest.raises(RpcError, match="Already paid"):
202202
l1.rpc.xpay(b11_paid)
203203

204+
# Cannot specify payer_note with normal invoice
205+
with pytest.raises(RpcError, match=r"payer_note only valid when paying an offer or BIP353 address"):
206+
l1.rpc.xpay(invstring=b11, payer_note='x')
207+
with pytest.raises(RpcError, match=r"payer_note only valid when paying an offer or BIP353 address"):
208+
l1.rpc.xpay(invstring=b12, payer_note='x')
209+
204210

205211
def test_xpay_selfpay(node_factory):
206212
l1 = node_factory.get_node()
@@ -997,6 +1003,10 @@ def test_xpay_offer(node_factory):
9971003
l1.rpc.xpay(offer2)
9981004
l1.rpc.xpay(offer2, 5000)
9991005

1006+
# Now with a payer_note:
1007+
l1.rpc.xpay(invstring=offer2, payer_note="Eat at Joes!")
1008+
assert l1.rpc.decode(l2.rpc.listinvoices()['invoices'][3]['bolt12'])['invreq_payer_note'] == "Eat at Joes!"
1009+
10001010

10011011
def test_xpay_bip353(node_factory):
10021012
fakebip353_plugin = Path(__file__).parent / "plugins" / "fakebip353.py"
@@ -1022,3 +1032,7 @@ def test_xpay_bip353(node_factory):
10221032
l2.daemon.wait_for_log('plugin-cln-xpay: notify msg info: DNS lookup for [email protected]')
10231033
l2.daemon.wait_for_log('plugin-cln-xpay: notify msg info: Fetching invoice for offer')
10241034
l2.daemon.wait_for_log(f'plugin-cln-xpay: notify msg debug: offer is {offer}')
1035+
1036+
# Now with a payer_note:
1037+
l2.rpc.xpay(invstring='[email protected]', amount_msat=123, payer_note="Eat at Joes!")
1038+
assert l1.rpc.decode(l1.rpc.listinvoices()['invoices'][1]['bolt12'])['invreq_payer_note'] == "Eat at Joes!"

0 commit comments

Comments
 (0)