Skip to content

Commit c22cc11

Browse files
rustyrussellendothermicdev
authored andcommitted
fetchinvoice: allow user to specify bip353 name corresponding to how they got offer.
Changelog-Added: JSON-RPC: `fetchinvoice` BIP 353 name support (`bip353`). Signed-off-by: Rusty Russell <[email protected]>
1 parent 22a2366 commit c22cc11

File tree

9 files changed

+723
-650
lines changed

9 files changed

+723
-650
lines changed

.msggen.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1548,6 +1548,7 @@
15481548
},
15491549
"FetchinvoiceRequest": {
15501550
"FetchInvoice.amount_msat": 2,
1551+
"FetchInvoice.bip353": 10,
15511552
"FetchInvoice.offer": 1,
15521553
"FetchInvoice.payer_metadata": 9,
15531554
"FetchInvoice.payer_note": 8,
@@ -6779,6 +6780,10 @@
67796780
"added": "pre-v0.10.1",
67806781
"deprecated": null
67816782
},
6783+
"FetchInvoice.bip353": {
6784+
"added": "v25.02",
6785+
"deprecated": null
6786+
},
67826787
"FetchInvoice.changes": {
67836788
"added": "pre-v0.10.1",
67846789
"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
@@ -13397,6 +13397,13 @@
1339713397
],
1339813398
"added": "v24.11"
1339913399
},
13400+
"bip353": {
13401+
"type": "string",
13402+
"description": [
13403+
"BIP353 string (optionally with \u20bf) indicating where we fetched the offer from"
13404+
],
13405+
"added": "v25.02"
13406+
},
1340013407
"dev_reply_path": {
1340113408
"hidden": true
1340213409
},

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

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

doc/schemas/lightning-fetchinvoice.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@
6969
],
7070
"added": "v24.11"
7171
},
72+
"bip353": {
73+
"type": "string",
74+
"description": [
75+
"BIP353 string (optionally with ₿) indicating where we fetched the offer from"
76+
],
77+
"added": "v25.02"
78+
},
7279
"dev_reply_path": {
7380
"hidden": true
7481
},

plugins/fetchinvoice.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <common/onion_message.h>
1919
#include <common/overflows.h>
2020
#include <common/route.h>
21+
#include <common/utils.h>
2122
#include <errno.h>
2223
#include <plugins/establish_onion_path.h>
2324
#include <plugins/fetchinvoice.h>
@@ -823,6 +824,7 @@ struct command_result *json_fetchinvoice(struct command *cmd,
823824
struct out_req *req;
824825
struct tlv_invoice_request *invreq;
825826
struct sent *sent = tal(cmd, struct sent);
827+
const char *bip353;
826828
u32 *timeout;
827829
u64 *quantity;
828830
u32 *recurrence_counter, *recurrence_start;
@@ -837,6 +839,7 @@ struct command_result *json_fetchinvoice(struct command *cmd,
837839
p_opt_def("timeout", param_number, &timeout, 60),
838840
p_opt("payer_note", param_string, &payer_note),
839841
p_opt("payer_metadata", param_bin_from_hex, &payer_metadata),
842+
p_opt("bip353", param_string, &bip353),
840843
p_opt("dev_path_use_scidd", param_dev_scidd, &sent->dev_path_use_scidd),
841844
p_opt("dev_reply_path", param_dev_reply_path, &sent->dev_reply_path),
842845
NULL))
@@ -1018,6 +1021,37 @@ struct command_result *json_fetchinvoice(struct command *cmd,
10181021
}
10191022
}
10201023

1024+
/* BOLT #12:
1025+
* - if it received the offer from which it constructed this
1026+
* `invoice_request` using BIP 353 resolution:
1027+
* - MUST include `invreq_bip_353_name` with,
1028+
* - `name` set to the post-₿, pre-@ part of the BIP 353 HRN,
1029+
* - `domain` set to the post-@ part of the BIP 353 HRN.
1030+
*/
1031+
if (bip353) {
1032+
char *at;
1033+
if (!utf8_check(bip353, strlen(bip353)))
1034+
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
1035+
"invalid UTF-8 for bip353");
1036+
at = strchr(bip353, '@');
1037+
if (!at)
1038+
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
1039+
"missing @ for bip353");
1040+
1041+
/* Strip ₿ if present (0xE2 0x82 0xBF) */
1042+
if (strstarts(bip353, "₿"))
1043+
bip353 += strlen("₿");
1044+
invreq->invreq_bip_353_name
1045+
= tal(invreq, struct tlv_invoice_request_invreq_bip_353_name);
1046+
/* Not nul-terminated! */
1047+
invreq->invreq_bip_353_name->name
1048+
= tal_dup_arr(invreq->invreq_bip_353_name, u8,
1049+
(const u8 *)bip353, at - bip353, 0);
1050+
invreq->invreq_bip_353_name->domain
1051+
= tal_dup_arr(invreq->invreq_bip_353_name, u8,
1052+
(const u8 *)at + 1, strlen(at + 1), 0);
1053+
}
1054+
10211055
/* We derive transient payer_id from invreq_metadata */
10221056
invreq->invreq_payer_id = tal(invreq, struct pubkey);
10231057
if (!payer_key(invreq->invreq_metadata,

tests/test_pay.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4544,6 +4544,21 @@ def test_fetchinvoice(node_factory, bitcoind):
45444544
# We've done 3 onion calls: sleep now to avoid hitting ratelimit!
45454545
time.sleep(1)
45464546

4547+
# Test bip353.
4548+
inv1 = l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12'], 'bip353': '₿[email protected]'})['invoice']
4549+
assert l1.rpc.decode(inv1)['invreq_bip_353_name'] == {'name': 'rusty',
4550+
'domain': 'blockstream.com'}
4551+
# Without ₿ works too.
4552+
inv1 = l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12'], 'bip353': '[email protected]'})['invoice']
4553+
assert l1.rpc.decode(inv1)['invreq_bip_353_name'] == {'name': 'rusty',
4554+
'domain': 'blockstream.com'}
4555+
# Needs @
4556+
with pytest.raises(RpcError, match="missing @"):
4557+
l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12'], 'bip353': 'rustyblockstream.com'})
4558+
4559+
# We've done 3 onion calls: sleep now to avoid hitting ratelimit!
4560+
time.sleep(1)
4561+
45474562
# If we remove plugin, it can no longer give us an invoice.
45484563
l3.rpc.plugin_stop(plugin)
45494564

0 commit comments

Comments
 (0)