Skip to content

Commit 05fbcb4

Browse files
committed
xpay: make sure to call preapproveinvoice!
This is required for VLS which wants to know (and potentially decline) invoices we're trying to pay. As a nice side effect, our "check" command for xpay now does much more thorough checking of arguments. Signed-off-by: Rusty Russell <[email protected]>
1 parent 2c15dc0 commit 05fbcb4

File tree

2 files changed

+54
-12
lines changed

2 files changed

+54
-12
lines changed

plugins/xpay/xpay.c

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1380,6 +1380,26 @@ static struct command_result *param_string_array(struct command *cmd, const char
13801380
return NULL;
13811381
}
13821382

1383+
static struct command_result *
1384+
preapproveinvoice_succeed(struct command *cmd,
1385+
const char *method,
1386+
const char *buf,
1387+
const jsmntok_t *result,
1388+
struct payment *payment)
1389+
{
1390+
struct xpay *xpay = xpay_of(cmd->plugin);
1391+
1392+
/* Now we can conclude `check` command */
1393+
if (command_check_only(cmd)) {
1394+
return command_check_done(cmd);
1395+
}
1396+
1397+
payment->unique_id = xpay->counter++;
1398+
payment->private_layer = tal_fmt(payment,
1399+
"xpay-%"PRIu64, payment->unique_id);
1400+
return populate_private_layer(cmd, payment);
1401+
}
1402+
13831403
static struct command_result *json_xpay(struct command *cmd,
13841404
const char *buffer,
13851405
const jsmntok_t *params)
@@ -1388,26 +1408,24 @@ static struct command_result *json_xpay(struct command *cmd,
13881408
struct amount_msat *msat, *maxfee, *partial;
13891409
struct payment *payment = tal(cmd, struct payment);
13901410
unsigned int *retryfor;
1411+
struct out_req *req;
13911412
char *err;
13921413

1393-
if (!param(cmd, buffer, params,
1394-
p_req("invstring", param_invstring, &payment->invstring),
1395-
p_opt("amount_msat", param_msat, &msat),
1396-
p_opt("maxfee", param_msat, &maxfee),
1397-
p_opt("layers", param_string_array, &payment->layers),
1398-
p_opt_def("retry_for", param_number, &retryfor, 60),
1399-
p_opt("partial_msat", param_msat, &partial),
1400-
NULL))
1414+
if (!param_check(cmd, buffer, params,
1415+
p_req("invstring", param_invstring, &payment->invstring),
1416+
p_opt("amount_msat", param_msat, &msat),
1417+
p_opt("maxfee", param_msat, &maxfee),
1418+
p_opt("layers", param_string_array, &payment->layers),
1419+
p_opt_def("retry_for", param_number, &retryfor, 60),
1420+
p_opt("partial_msat", param_msat, &partial),
1421+
NULL))
14011422
return command_param_failed();
14021423

14031424
list_head_init(&payment->current_attempts);
14041425
list_head_init(&payment->past_attempts);
14051426
payment->plugin = cmd->plugin;
14061427
payment->cmd = cmd;
14071428
payment->amount_being_routed = AMOUNT_MSAT(0);
1408-
payment->unique_id = xpay->counter++;
1409-
payment->private_layer = tal_fmt(payment,
1410-
"xpay-%"PRIu64, payment->unique_id);
14111429
payment->group_id = pseudorand(INT64_MAX);
14121430
payment->total_num_attempts = payment->num_failures = 0;
14131431
payment->requests = tal_arr(payment, struct out_req *, 0);
@@ -1516,7 +1534,19 @@ static struct command_result *json_xpay(struct command *cmd,
15161534
} else
15171535
payment->maxfee = *maxfee;
15181536

1519-
return populate_private_layer(cmd, payment);
1537+
/* Now preapprove, then start payment. */
1538+
if (command_check_only(cmd)) {
1539+
req = jsonrpc_request_start(cmd, "check",
1540+
&preapproveinvoice_succeed,
1541+
&forward_error, payment);
1542+
json_add_string(req->js, "command_to_check", "preapproveinvoice");
1543+
} else {
1544+
req = jsonrpc_request_start(cmd, "preapproveinvoice",
1545+
&preapproveinvoice_succeed,
1546+
&forward_error, payment);
1547+
}
1548+
json_add_string(req->js, "bolt11", payment->invstring);
1549+
return send_outreq(req);
15201550
}
15211551

15221552
static struct command_result *getchaininfo_done(struct command *aux_cmd,

tests/test_xpay.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,3 +464,15 @@ def test_xpay_takeover(node_factory, executor):
464464
inv = l3.rpc.invoice(100000, "test_xpay_takeover13", "test_xpay_takeover13")['bolt11']
465465
l1.rpc.pay(inv)
466466
l1.daemon.wait_for_log('Redirecting pay->xpay')
467+
468+
469+
def test_xpay_preapprove(node_factory):
470+
l1, l2 = node_factory.line_graph(2, opts={'dev-hsmd-fail-preapprove': None})
471+
472+
inv = l2.rpc.invoice(100000, "test_xpay_preapprove", "test_xpay_preapprove")['bolt11']
473+
474+
with pytest.raises(RpcError, match=r"invoice was declined"):
475+
l1.rpc.check('xpay', invstring=inv)
476+
477+
with pytest.raises(RpcError, match=r"invoice was declined"):
478+
l1.rpc.xpay(inv)

0 commit comments

Comments
 (0)