lightningd: sendpay implement zero-length path == self-pay.

Previously, the "payment" and "invoice" paths were completely separate, but this now calls both.  It bypasses htlc_sets (and thus, cannot do MPP), and bypasses the hook too: the former is tied closely to HTLCs, and the hook is also very htlc-centric.

Includes finishing unfinished sentence in sendpay man page, as a bonus.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Added: Plugins: `sendpay` now allows self-payment of invoices, by specifying an empty route.
This commit is contained in:
Rusty Russell 2023-07-22 20:56:27 +09:30
parent c377edfbd1
commit 18760db66d
3 changed files with 125 additions and 10 deletions

View File

@ -17,18 +17,19 @@ route.
Generally, a client would call lightning-getroute(7) to resolve a route,
then use **sendpay** to send it. If it fails, it would call
lightning-getroute(7) again to retry.
lightning-getroute(7) again to retry. If the route is empty, a payment-to-self is attempted.
The response will occur when the payment is on its way to the
destination. The **sendpay** RPC command does not wait for definite
success or definite failure of the payment. Instead, use the
success or definite failure of the payment (except for already-succeeded
payments, or to-self payments). Instead, use the
**waitsendpay** RPC command to poll or wait for definite success or
definite failure.
The *label* and *bolt11* parameters, if provided, will be returned in
*waitsendpay* and *listsendpays* results.
The *amount\_msat* amount must be provided if *partid* is non-zero, otherwise
The *amount\_msat* amount must be provided if *partid* is non-zero, or the payment is to-self, otherwise
it must be equal to the final
amount to the destination. By default it is in millisatoshi precision; it can be a whole number, or a whole number
ending in *msat* or *sat*, or a number with three decimal places ending
@ -39,10 +40,10 @@ accept the payment, as defined by the `payment_data` field in BOLT 4
and the `s` field in the BOLT 11 invoice format. It is required if
*partid* is non-zero.
The *partid* value, if provided and non-zero, allows for multiple parallel
The *partid* value must not be provided for self-payments. If provided and non-zero, allows for multiple parallel
partial payments with the same *payment\_hash*. The *amount\_msat* amount
(which must be provided) for each **sendpay** with matching
*payment\_hash* must be equal, and **sendpay** will fail if there are
*payment\_hash* must be equal, and **sendpay** will fail if there are differing values given.
The *localinvreqid* value indicates that this payment is being made for a local
invoice\_request: this ensures that we only send a payment for a single-use

View File

@ -1,4 +1,5 @@
#include "config.h"
#include <ccan/json_escape/json_escape.h>
#include <ccan/mem/mem.h>
#include <ccan/tal/str/str.h>
#include <common/bolt12_merkle.h>
@ -11,9 +12,11 @@
#include <common/type_to_string.h>
#include <lightningd/chaintopology.h>
#include <lightningd/channel.h>
#include <lightningd/invoice.h>
#include <lightningd/notification.h>
#include <lightningd/pay.h>
#include <lightningd/peer_control.h>
#include <wallet/invoices.h>
/* Routing failure object */
struct routing_failure {
@ -1398,9 +1401,9 @@ static struct command_result *param_route_hops(struct command *cmd,
size_t i;
const jsmntok_t *t;
if (tok->type != JSMN_ARRAY || tok->size == 0)
if (tok->type != JSMN_ARRAY)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"%s must be an (non-empty) array", name);
"%s must be an array", name);
*hops = tal_arr(cmd, struct route_hop, tok->size);
json_for_each_arr(i, t, tok) {
@ -1431,6 +1434,106 @@ static struct command_result *param_route_hops(struct command *cmd,
return NULL;
}
/* We're paying ourselves! */
static struct command_result *self_payment(struct lightningd *ld,
struct command *cmd,
const struct sha256 *rhash,
u64 partid,
u64 groupid,
struct amount_msat msat,
const char *label TAKES,
const char *invstring TAKES,
const char *description TAKES,
const struct sha256 *local_invreq_id,
const struct secret *payment_secret,
const u8 *payment_metadata)
{
struct wallet_payment *payment;
const struct invoice_details *inv;
u64 inv_dbid;
const char *err;
payment = wallet_payment_new(tmpctx,
0, /* ID is not in db yet */
time_now().ts.tv_sec,
NULL,
rhash,
partid,
groupid,
PAYMENT_PENDING,
&ld->id,
msat,
msat,
msat,
NULL,
NULL,
NULL,
NULL,
invstring,
label,
description,
NULL,
local_invreq_id);
/* We write this into db immediately, but we're expected to do
* it in two stages like a normal payment. */
wallet_payment_setup(ld->wallet, payment);
payment_store(ld, payment);
/* Now, resolved the invoice */
inv = invoice_check_payment(tmpctx, ld, rhash, msat, payment_secret, &err);
if (!inv) {
struct routing_failure *fail;
wallet_payment_set_status(ld->wallet, rhash, partid, groupid,
PAYMENT_FAILED, NULL);
/* tell_waiters_failed expects one of these! */
fail = tal(payment, struct routing_failure);
fail->failcode = WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS;
fail->erring_node = &ld->id;
fail->erring_index = 0;
fail->erring_channel = NULL;
fail->msg = NULL;
/* Only some of these fields make sense for self payments */
wallet_payment_set_failinfo(ld->wallet,
rhash,
partid, NULL,
true,
0,
fail->failcode, fail->erring_node,
NULL, NULL,
err,
0);
/* We do this even though there really can't be any waiters,
* since we didn't block. */
tell_waiters_failed(ld, rhash, payment, PAY_DESTINATION_PERM_FAIL,
NULL, fail, err);
return sendpay_fail(cmd, payment, PAY_DESTINATION_PERM_FAIL, NULL,
fail, err);
}
/* These should not fail, given the above succeded! */
if (!invoices_find_by_rhash(ld->wallet->invoices, &inv_dbid, rhash)
|| !invoices_resolve(ld->wallet->invoices, inv_dbid, msat)) {
log_broken(ld->log, "Could not resolve invoice %"PRIu64"!?!", inv_dbid);
return sendpay_fail(cmd, payment, PAY_DESTINATION_PERM_FAIL, NULL, NULL, "broken");
}
log_info(ld->log, "Self-resolved invoice '%s' with amount %s",
inv->label->s,
type_to_string(tmpctx, struct amount_msat, &msat));
notify_invoice_payment(ld, msat, inv->r, inv->label);
/* Now resolve the payment */
payment_succeeded(ld, rhash, partid, groupid, &inv->r);
/* Now the specific command which called this. */
payment->status = PAYMENT_COMPLETE;
payment->payment_preimage = tal_dup(payment, struct preimage, &inv->r);
return sendpay_success(cmd, payment);
}
static struct command_result *json_sendpay(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
@ -1466,14 +1569,26 @@ static struct command_result *json_sendpay(struct command *cmd,
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Must specify msatoshi with partid");
const struct amount_msat final_amount = route[tal_count(route)-1].amount;
/* If groupid was not provided default to incrementing from the previous one. */
if (group == NULL) {
group = tal(tmpctx, u64);
*group = wallet_payment_get_groupid(cmd->ld->wallet, rhash) + 1;
}
if (tal_count(route) == 0) {
if (!msat)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Self-payment requires amount_msat");
if (*partid)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Self-payment does not allow (non-zero) partid");
return self_payment(cmd->ld, cmd, rhash, *partid, *group, *msat,
label, invstring, description, local_invreq_id,
payment_secret, payment_metadata);
}
const struct amount_msat final_amount = route[tal_count(route)-1].amount;
if (msat && !*partid && !amount_msat_eq(*msat, final_amount))
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Do not specify msatoshi (%s) without"

View File

@ -5289,7 +5289,6 @@ def test_invoice_pay_desc_with_quotes(node_factory):
l1.rpc.pay(invoice, description=description)
@pytest.mark.xfail(strict=True)
def test_self_sendpay(node_factory):
"""We get much more descriptive errors from a self-payment than a remote payment, since we're not relying on a single WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS but can share more useful information"""
l1 = node_factory.get_node()