Skip to content

Commit f1e0fe5

Browse files
committed
fetchinvoice: add developer option to send raw invoice_request.
This will be used for bootstrap.bolt12.org to provide a raw request API. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
1 parent 12fbb23 commit f1e0fe5

File tree

2 files changed

+94
-0
lines changed

2 files changed

+94
-0
lines changed

plugins/fetchinvoice.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,16 @@ static struct command_result *handle_invreq_response(struct command *cmd,
194194
goto badinv;
195195
}
196196

197+
#if DEVELOPER
198+
/* Raw send? Just fwd reply. */
199+
if (!sent->offer) {
200+
out = jsonrpc_stream_success(sent->cmd);
201+
json_add_string(out, "invoice", invoice_encode(tmpctx, inv));
202+
discard_result(command_finished(sent->cmd, out));
203+
return command_hook_success(cmd);
204+
}
205+
#endif /* DEVELOPER */
206+
197207
/* BOLT-offers #12:
198208
* - MUST reject the invoice unless `node_id` is equal to the offer.
199209
*/
@@ -1490,6 +1500,64 @@ static struct command_result *json_sendinvoice(struct command *cmd,
14901500
return sign_invoice(cmd, sent);
14911501
}
14921502

1503+
#if DEVELOPER
1504+
static struct command_result *param_invreq(struct command *cmd,
1505+
const char *name,
1506+
const char *buffer,
1507+
const jsmntok_t *tok,
1508+
struct tlv_invoice_request **invreq)
1509+
{
1510+
char *fail;
1511+
1512+
*invreq = invrequest_decode(cmd, buffer + tok->start, tok->end - tok->start,
1513+
plugin_feature_set(cmd->plugin), chainparams,
1514+
&fail);
1515+
if (!*invreq)
1516+
return command_fail_badparam(cmd, name, buffer, tok,
1517+
tal_fmt(cmd,
1518+
"Unparsable invreq: %s",
1519+
fail));
1520+
return NULL;
1521+
}
1522+
1523+
static struct command_result *json_rawrequest(struct command *cmd,
1524+
const char *buffer,
1525+
const jsmntok_t *params)
1526+
{
1527+
struct sent *sent = tal(cmd, struct sent);
1528+
u32 *timeout;
1529+
struct node_id *node_id;
1530+
struct pubkey32 node_id32;
1531+
bool try_connect;
1532+
1533+
if (!param(cmd, buffer, params,
1534+
p_req("invreq", param_invreq, &sent->invreq),
1535+
p_req("nodeid", param_node_id, &node_id),
1536+
p_opt_def("timeout", param_number, &timeout, 60),
1537+
NULL))
1538+
return command_param_failed();
1539+
1540+
/* Skip over 02/03 in node_id */
1541+
if (!secp256k1_xonly_pubkey_parse(secp256k1_ctx,
1542+
&node_id32.pubkey,
1543+
node_id->k + 1))
1544+
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
1545+
"Invalid nodeid");
1546+
/* This is how long we'll wait for a reply for. */
1547+
sent->wait_timeout = *timeout;
1548+
sent->cmd = cmd;
1549+
sent->offer = NULL;
1550+
1551+
sent->path = path_to_node(sent, get_gossmap(cmd->plugin),
1552+
&node_id32, &try_connect);
1553+
if (try_connect)
1554+
return connect_direct(cmd, node_id,
1555+
sendinvreq_after_connect, sent);
1556+
1557+
return sendinvreq_after_connect(cmd, NULL, NULL, sent);
1558+
}
1559+
#endif /* DEVELOPER */
1560+
14931561
static const struct plugin_command commands[] = {
14941562
{
14951563
"fetchinvoice",
@@ -1505,6 +1573,15 @@ static const struct plugin_command commands[] = {
15051573
NULL,
15061574
json_sendinvoice,
15071575
},
1576+
#if DEVELOPER
1577+
{
1578+
"dev-rawrequest",
1579+
"util",
1580+
"Send {invreq} to {nodeid}, wait {timeout} (60 seconds by default)",
1581+
NULL,
1582+
json_rawrequest,
1583+
},
1584+
#endif /* DEVELOPER */
15081585
};
15091586

15101587
static const char *init(struct plugin *p, const char *buf UNUSED,

tests/test_pay.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4403,6 +4403,23 @@ def test_pay_waitblockheight_timeout(node_factory, bitcoind):
44034403
assert len(status['pay'][0]['attempts']) == 1
44044404

44054405

4406+
@pytest.mark.developer("dev-rawrequest is DEVELOPER-only")
4407+
def test_dev_rawrequest(node_factory):
4408+
l1, l2 = node_factory.line_graph(2, fundchannel=False,
4409+
opts={'experimental-offers': None})
4410+
4411+
offer = l2.rpc.call('offer', {'amount': '2msat',
4412+
'description': 'simple test'})
4413+
# Get fetchinvoice to make us an invoice_request
4414+
l1.rpc.call('fetchinvoice', {'offer': offer['bolt12']})
4415+
4416+
m = re.search(r'invoice_request: \\"([a-z0-9]*)\\"', l1.daemon.is_in_log('invoice_request:'))
4417+
ret = l1.rpc.call('dev-rawrequest', {'invreq': m.group(1),
4418+
'nodeid': l2.info['id'],
4419+
'timeout': 10})
4420+
assert 'invoice' in ret
4421+
4422+
44064423
def test_sendinvoice(node_factory, bitcoind):
44074424
l1, l2 = node_factory.line_graph(2, wait_for_announce=True,
44084425
opts={'experimental-offers': None})

0 commit comments

Comments
 (0)