2021-12-04 12:23:56 +01:00
|
|
|
#include "config.h"
|
2022-08-08 23:51:36 +02:00
|
|
|
#include <ccan/mem/mem.h>
|
2017-10-26 05:07:19 +02:00
|
|
|
#include <ccan/tal/str/str.h>
|
2020-12-14 02:24:37 +01:00
|
|
|
#include <common/bolt12_merkle.h>
|
2022-03-23 01:00:06 +01:00
|
|
|
#include <common/configdir.h>
|
2018-12-08 01:39:28 +01:00
|
|
|
#include <common/json_command.h>
|
2022-07-04 05:49:38 +02:00
|
|
|
#include <common/json_param.h>
|
2020-01-23 00:38:04 +01:00
|
|
|
#include <common/onionreply.h>
|
2021-06-14 23:07:39 +02:00
|
|
|
#include <common/route.h>
|
2018-03-04 06:35:37 +01:00
|
|
|
#include <common/timeout.h>
|
2021-09-16 07:00:42 +02:00
|
|
|
#include <common/type_to_string.h>
|
2017-08-28 18:04:01 +02:00
|
|
|
#include <lightningd/chaintopology.h>
|
2021-09-17 00:31:38 +02:00
|
|
|
#include <lightningd/channel.h>
|
2019-06-25 10:32:53 +02:00
|
|
|
#include <lightningd/notification.h>
|
2021-12-04 12:23:56 +01:00
|
|
|
#include <lightningd/pay.h>
|
2017-04-01 15:05:29 +02:00
|
|
|
#include <lightningd/peer_control.h>
|
|
|
|
|
2019-01-17 16:25:32 +01:00
|
|
|
/* Routing failure object */
|
|
|
|
struct routing_failure {
|
|
|
|
unsigned int erring_index;
|
2020-08-31 03:13:25 +02:00
|
|
|
enum onion_wire failcode;
|
2019-11-11 16:14:35 +01:00
|
|
|
const struct node_id *erring_node;
|
2019-11-11 16:26:45 +01:00
|
|
|
const struct short_channel_id *erring_channel;
|
2019-01-17 16:25:32 +01:00
|
|
|
int channel_dir;
|
2019-08-28 06:08:26 +02:00
|
|
|
/* If remote sent us a message, this is it. */
|
|
|
|
const u8 *msg;
|
2019-01-17 16:25:32 +01:00
|
|
|
};
|
|
|
|
|
2018-02-11 02:40:43 +01:00
|
|
|
/* sendpay command */
|
|
|
|
struct sendpay_command {
|
2018-02-02 01:55:06 +01:00
|
|
|
struct list_node list;
|
|
|
|
|
|
|
|
struct sha256 payment_hash;
|
2019-12-12 00:52:32 +01:00
|
|
|
u64 partid;
|
2021-09-29 19:10:12 +02:00
|
|
|
u64 groupid;
|
2019-01-18 07:35:58 +01:00
|
|
|
struct command *cmd;
|
2018-02-02 01:55:06 +01:00
|
|
|
};
|
|
|
|
|
wallet: simplify payments lookup so sqlite3 uses index.
Filtering by status is rare, so we can do it in the caller; just let sqlite3
filter by payment_hash.
With ~650,000 payments in db:
Before:
```
129/300000 complete 5.60/sec (33078 invs, 169 pays, 0 retries) in 30 seconds. 19 hours-14 hours remaining.
201/300000 complete 7.20/sec (43519 invs, 241 pays, 0 retries) in 40 seconds. 16 hours-11 hours remaining.
257/300000 complete 5.60/sec (54568 invs, 289 pays, 0 retries) in 50 seconds. 16 hours-14 hours remaining.
305/300000 complete 4.80/sec (65772 invs, 337 pays, 0 retries) in 60 seconds. 16 hours-17 hours remaining.
361/300000 complete 5.60/sec (75875 invs, 401 pays, 0 retries) in 70 seconds. 16 hours-14 hours remaining.
```
After:
```
760/300000 complete 40.00/sec (19955 invs, 824 pays, 0 retries) in 20 seconds. 2 hours-2 hours remaining.
1176/300000 complete 41.60/sec (30082 invs, 1224 pays, 0 retries) in 30 seconds. 2 hours-119 minutes remaining.
1584/300000 complete 40.80/sec (40224 invs, 1640 pays, 0 retries) in 40 seconds. 2 hours-2 hours remaining.
1984/300000 complete 40.00/sec (49938 invs, 2048 pays, 0 retries) in 50 seconds. 2 hours-2 hours remaining.
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2022-09-19 02:49:53 +02:00
|
|
|
static bool string_to_payment_status(const char *status_str, size_t len,
|
2023-07-11 21:41:17 +02:00
|
|
|
enum payment_status *status)
|
2020-08-08 17:47:53 +02:00
|
|
|
{
|
wallet: simplify payments lookup so sqlite3 uses index.
Filtering by status is rare, so we can do it in the caller; just let sqlite3
filter by payment_hash.
With ~650,000 payments in db:
Before:
```
129/300000 complete 5.60/sec (33078 invs, 169 pays, 0 retries) in 30 seconds. 19 hours-14 hours remaining.
201/300000 complete 7.20/sec (43519 invs, 241 pays, 0 retries) in 40 seconds. 16 hours-11 hours remaining.
257/300000 complete 5.60/sec (54568 invs, 289 pays, 0 retries) in 50 seconds. 16 hours-14 hours remaining.
305/300000 complete 4.80/sec (65772 invs, 337 pays, 0 retries) in 60 seconds. 16 hours-17 hours remaining.
361/300000 complete 5.60/sec (75875 invs, 401 pays, 0 retries) in 70 seconds. 16 hours-14 hours remaining.
```
After:
```
760/300000 complete 40.00/sec (19955 invs, 824 pays, 0 retries) in 20 seconds. 2 hours-2 hours remaining.
1176/300000 complete 41.60/sec (30082 invs, 1224 pays, 0 retries) in 30 seconds. 2 hours-119 minutes remaining.
1584/300000 complete 40.80/sec (40224 invs, 1640 pays, 0 retries) in 40 seconds. 2 hours-2 hours remaining.
1984/300000 complete 40.00/sec (49938 invs, 2048 pays, 0 retries) in 50 seconds. 2 hours-2 hours remaining.
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2022-09-19 02:49:53 +02:00
|
|
|
if (memeqstr(status_str, len, "complete")) {
|
2020-08-08 17:47:53 +02:00
|
|
|
*status = PAYMENT_COMPLETE;
|
|
|
|
return true;
|
wallet: simplify payments lookup so sqlite3 uses index.
Filtering by status is rare, so we can do it in the caller; just let sqlite3
filter by payment_hash.
With ~650,000 payments in db:
Before:
```
129/300000 complete 5.60/sec (33078 invs, 169 pays, 0 retries) in 30 seconds. 19 hours-14 hours remaining.
201/300000 complete 7.20/sec (43519 invs, 241 pays, 0 retries) in 40 seconds. 16 hours-11 hours remaining.
257/300000 complete 5.60/sec (54568 invs, 289 pays, 0 retries) in 50 seconds. 16 hours-14 hours remaining.
305/300000 complete 4.80/sec (65772 invs, 337 pays, 0 retries) in 60 seconds. 16 hours-17 hours remaining.
361/300000 complete 5.60/sec (75875 invs, 401 pays, 0 retries) in 70 seconds. 16 hours-14 hours remaining.
```
After:
```
760/300000 complete 40.00/sec (19955 invs, 824 pays, 0 retries) in 20 seconds. 2 hours-2 hours remaining.
1176/300000 complete 41.60/sec (30082 invs, 1224 pays, 0 retries) in 30 seconds. 2 hours-119 minutes remaining.
1584/300000 complete 40.80/sec (40224 invs, 1640 pays, 0 retries) in 40 seconds. 2 hours-2 hours remaining.
1984/300000 complete 40.00/sec (49938 invs, 2048 pays, 0 retries) in 50 seconds. 2 hours-2 hours remaining.
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2022-09-19 02:49:53 +02:00
|
|
|
} else if (memeqstr(status_str, len, "pending")) {
|
2020-08-08 17:47:53 +02:00
|
|
|
*status = PAYMENT_PENDING;
|
|
|
|
return true;
|
wallet: simplify payments lookup so sqlite3 uses index.
Filtering by status is rare, so we can do it in the caller; just let sqlite3
filter by payment_hash.
With ~650,000 payments in db:
Before:
```
129/300000 complete 5.60/sec (33078 invs, 169 pays, 0 retries) in 30 seconds. 19 hours-14 hours remaining.
201/300000 complete 7.20/sec (43519 invs, 241 pays, 0 retries) in 40 seconds. 16 hours-11 hours remaining.
257/300000 complete 5.60/sec (54568 invs, 289 pays, 0 retries) in 50 seconds. 16 hours-14 hours remaining.
305/300000 complete 4.80/sec (65772 invs, 337 pays, 0 retries) in 60 seconds. 16 hours-17 hours remaining.
361/300000 complete 5.60/sec (75875 invs, 401 pays, 0 retries) in 70 seconds. 16 hours-14 hours remaining.
```
After:
```
760/300000 complete 40.00/sec (19955 invs, 824 pays, 0 retries) in 20 seconds. 2 hours-2 hours remaining.
1176/300000 complete 41.60/sec (30082 invs, 1224 pays, 0 retries) in 30 seconds. 2 hours-119 minutes remaining.
1584/300000 complete 40.80/sec (40224 invs, 1640 pays, 0 retries) in 40 seconds. 2 hours-2 hours remaining.
1984/300000 complete 40.00/sec (49938 invs, 2048 pays, 0 retries) in 50 seconds. 2 hours-2 hours remaining.
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2022-09-19 02:49:53 +02:00
|
|
|
} else if (memeqstr(status_str, len, "failed")) {
|
2020-08-08 17:47:53 +02:00
|
|
|
*status = PAYMENT_FAILED;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-07-11 21:41:17 +02:00
|
|
|
static const char *payment_status_to_string(const enum payment_status status)
|
2020-08-08 17:47:53 +02:00
|
|
|
{
|
|
|
|
switch (status) {
|
|
|
|
case PAYMENT_COMPLETE:
|
|
|
|
return "complete";
|
|
|
|
case PAYMENT_FAILED:
|
|
|
|
return "failed";
|
2020-08-19 12:38:50 +02:00
|
|
|
case PAYMENT_PENDING:
|
2020-08-08 17:47:53 +02:00
|
|
|
return "pending";
|
|
|
|
}
|
2020-08-19 12:38:50 +02:00
|
|
|
//This should never happen
|
|
|
|
abort();
|
2020-08-08 17:47:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-02-11 02:40:43 +01:00
|
|
|
static void destroy_sendpay_command(struct sendpay_command *pc)
|
2017-04-01 15:05:29 +02:00
|
|
|
{
|
2018-02-02 01:55:06 +01:00
|
|
|
list_del(&pc->list);
|
|
|
|
}
|
2017-04-01 15:05:29 +02:00
|
|
|
|
2019-01-18 07:37:06 +01:00
|
|
|
/* Owned by cmd, if cmd is deleted, then sendpay_success/sendpay_fail will
|
2018-02-14 01:09:23 +01:00
|
|
|
* no longer be called. */
|
2018-03-04 06:35:37 +01:00
|
|
|
static void
|
2019-01-18 07:35:58 +01:00
|
|
|
add_sendpay_waiter(struct lightningd *ld,
|
|
|
|
struct command *cmd,
|
2019-12-12 00:52:32 +01:00
|
|
|
const struct sha256 *payment_hash,
|
2021-09-29 19:10:12 +02:00
|
|
|
u64 partid, u64 groupid)
|
2018-02-02 01:55:06 +01:00
|
|
|
{
|
2019-01-18 07:35:58 +01:00
|
|
|
struct sendpay_command *pc = tal(cmd, struct sendpay_command);
|
2018-02-02 01:55:06 +01:00
|
|
|
|
2018-03-10 08:44:28 +01:00
|
|
|
pc->payment_hash = *payment_hash;
|
2019-12-12 00:52:32 +01:00
|
|
|
pc->partid = partid;
|
2021-09-29 19:10:12 +02:00
|
|
|
pc->groupid = groupid;
|
2019-01-18 07:35:58 +01:00
|
|
|
pc->cmd = cmd;
|
2018-03-10 08:44:28 +01:00
|
|
|
list_add(&ld->sendpay_commands, &pc->list);
|
|
|
|
tal_add_destructor(pc, destroy_sendpay_command);
|
|
|
|
}
|
|
|
|
|
2019-01-18 07:37:06 +01:00
|
|
|
/* Owned by cmd, if cmd is deleted, then sendpay_success/sendpay_fail will
|
2018-03-10 08:44:28 +01:00
|
|
|
* no longer be called. */
|
|
|
|
static void
|
2019-01-18 07:35:58 +01:00
|
|
|
add_waitsendpay_waiter(struct lightningd *ld,
|
|
|
|
struct command *cmd,
|
2019-12-12 00:52:32 +01:00
|
|
|
const struct sha256 *payment_hash,
|
2021-09-29 19:10:12 +02:00
|
|
|
u64 partid, u64 groupid)
|
2018-03-10 08:44:28 +01:00
|
|
|
{
|
2019-01-18 07:35:58 +01:00
|
|
|
struct sendpay_command *pc = tal(cmd, struct sendpay_command);
|
2018-03-10 08:44:28 +01:00
|
|
|
|
2018-02-02 01:55:06 +01:00
|
|
|
pc->payment_hash = *payment_hash;
|
2019-12-12 00:52:32 +01:00
|
|
|
pc->partid = partid;
|
2021-09-29 19:10:12 +02:00
|
|
|
pc->groupid = groupid;
|
2019-01-18 07:35:58 +01:00
|
|
|
pc->cmd = cmd;
|
2018-03-10 07:41:45 +01:00
|
|
|
list_add(&ld->waitsendpay_commands, &pc->list);
|
2018-02-11 02:40:43 +01:00
|
|
|
tal_add_destructor(pc, destroy_sendpay_command);
|
2018-02-02 01:55:06 +01:00
|
|
|
}
|
|
|
|
|
2019-01-18 07:35:58 +01:00
|
|
|
/* Outputs fields, not a separate object*/
|
plugin: A new notification type, 'sendpay_success'
`sendpay_success`
A notification for topic `sendpay_success` is sent every time a sendpay
success(with `complete` status). The json is same as the return value of
command `sendpay`/`waitsendpay` when these cammand succeeds.
```json
{
"sendpay_success": {
"id": 1,
"payment_hash": "5c85bf402b87d4860f4a728e2e58a2418bda92cd7aea0ce494f11670cfbfb206",
"destination": "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d",
"msatoshi": 100000000,
"amount_msat": "100000000msat",
"msatoshi_sent": 100001001,
"amount_sent_msat": "100001001msat",
"created_at": 1561390572,
"status": "complete",
"payment_preimage": "9540d98095fd7f37687ebb7759e733934234d4f934e34433d4998a37de3733ee"
}
}
```
`sendpay` doesn't wait for the result of sendpay and `waitsendpay`
returns the result of sendpay in specified time or timeout, but
`sendpay_success` will always return the result anytime when sendpay
successes if is was subscribed.
2019-06-25 09:27:35 +02:00
|
|
|
void json_add_payment_fields(struct json_stream *response,
|
|
|
|
const struct wallet_payment *t)
|
2019-01-18 07:35:58 +01:00
|
|
|
{
|
|
|
|
json_add_u64(response, "id", t->id);
|
2019-08-10 10:39:04 +02:00
|
|
|
json_add_sha256(response, "payment_hash", &t->payment_hash);
|
2021-09-28 18:05:33 +02:00
|
|
|
json_add_u64(response, "groupid", t->groupid);
|
2019-12-12 00:16:23 +01:00
|
|
|
if (t->partid)
|
|
|
|
json_add_u64(response, "partid", t->partid);
|
2019-11-07 23:13:29 +01:00
|
|
|
if (t->destination != NULL)
|
|
|
|
json_add_node_id(response, "destination", t->destination);
|
|
|
|
|
2019-11-25 14:32:28 +01:00
|
|
|
/* If we have a 0 amount delivered at the remote end we simply don't
|
|
|
|
* know since the onion was generated externally. */
|
|
|
|
if (amount_msat_greater(t->msatoshi, AMOUNT_MSAT(0)))
|
2023-03-14 06:19:50 +01:00
|
|
|
json_add_amount_msat(response, "amount_msat", t->msatoshi);
|
2019-11-25 14:32:28 +01:00
|
|
|
|
2023-03-14 06:19:50 +01:00
|
|
|
json_add_amount_msat(response, "amount_sent_msat", t->msatoshi_sent);
|
2022-06-09 18:36:00 +02:00
|
|
|
json_add_u32(response, "created_at", t->timestamp);
|
|
|
|
if (t->completed_at)
|
|
|
|
json_add_u32(response, "completed_at", *t->completed_at);
|
2019-01-18 07:35:58 +01:00
|
|
|
|
|
|
|
switch (t->status) {
|
|
|
|
case PAYMENT_PENDING:
|
|
|
|
json_add_string(response, "status", "pending");
|
|
|
|
break;
|
|
|
|
case PAYMENT_COMPLETE:
|
|
|
|
json_add_string(response, "status", "complete");
|
|
|
|
break;
|
|
|
|
case PAYMENT_FAILED:
|
|
|
|
json_add_string(response, "status", "failed");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (t->payment_preimage)
|
2019-11-26 02:33:19 +01:00
|
|
|
json_add_preimage(response, "payment_preimage",
|
|
|
|
t->payment_preimage);
|
2019-09-06 08:41:43 +02:00
|
|
|
if (t->label)
|
2019-02-23 04:11:14 +01:00
|
|
|
json_add_string(response, "label", t->label);
|
2020-12-14 02:24:37 +01:00
|
|
|
if (t->invstring) {
|
|
|
|
if (strstarts(t->invstring, "lni"))
|
|
|
|
json_add_string(response, "bolt12", t->invstring);
|
|
|
|
else
|
|
|
|
json_add_string(response, "bolt11", t->invstring);
|
|
|
|
}
|
2022-04-02 04:33:35 +02:00
|
|
|
if (t->description)
|
|
|
|
json_add_string(response, "description", t->description);
|
2019-11-11 19:38:27 +01:00
|
|
|
|
|
|
|
if (t->failonion)
|
|
|
|
json_add_hex(response, "erroronion", t->failonion,
|
|
|
|
tal_count(t->failonion));
|
2019-01-18 07:35:58 +01:00
|
|
|
}
|
|
|
|
|
2019-01-18 07:37:06 +01:00
|
|
|
static struct command_result *sendpay_success(struct command *cmd,
|
|
|
|
const struct wallet_payment *payment)
|
2019-01-18 07:35:58 +01:00
|
|
|
{
|
|
|
|
struct json_stream *response;
|
|
|
|
|
2019-01-18 07:37:06 +01:00
|
|
|
assert(payment->status == PAYMENT_COMPLETE);
|
2019-01-18 07:35:58 +01:00
|
|
|
|
|
|
|
response = json_stream_success(cmd);
|
2019-01-18 07:37:06 +01:00
|
|
|
json_add_payment_fields(response, payment);
|
|
|
|
return command_success(cmd, response);
|
2019-01-18 07:35:58 +01:00
|
|
|
}
|
|
|
|
|
2019-01-18 07:37:06 +01:00
|
|
|
static void
|
|
|
|
json_add_routefail_info(struct json_stream *js,
|
|
|
|
unsigned int erring_index,
|
2020-08-31 03:13:25 +02:00
|
|
|
enum onion_wire failcode,
|
2019-04-08 11:58:32 +02:00
|
|
|
const struct node_id *erring_node,
|
2019-01-18 07:37:06 +01:00
|
|
|
const struct short_channel_id *erring_channel,
|
2019-08-28 06:08:26 +02:00
|
|
|
int channel_dir,
|
|
|
|
const u8 *msg)
|
2019-01-18 07:35:58 +01:00
|
|
|
{
|
2020-08-31 03:13:25 +02:00
|
|
|
const char *failcodename = onion_wire_name(failcode);
|
2019-01-18 07:37:06 +01:00
|
|
|
|
|
|
|
json_add_num(js, "erring_index", erring_index);
|
|
|
|
json_add_num(js, "failcode", failcode);
|
|
|
|
/* FIXME: Better way to detect this? */
|
|
|
|
if (!strstarts(failcodename, "INVALID "))
|
|
|
|
json_add_string(js, "failcodename", failcodename);
|
2019-11-11 16:14:35 +01:00
|
|
|
|
|
|
|
if (erring_node != NULL)
|
|
|
|
json_add_node_id(js, "erring_node", erring_node);
|
2019-11-11 16:26:45 +01:00
|
|
|
|
|
|
|
if (erring_channel != NULL) {
|
|
|
|
json_add_short_channel_id(js, "erring_channel", erring_channel);
|
|
|
|
json_add_num(js, "erring_direction", channel_dir);
|
|
|
|
}
|
|
|
|
|
2019-08-28 06:08:26 +02:00
|
|
|
if (msg)
|
|
|
|
json_add_hex_talarr(js, "raw_message", msg);
|
2019-01-18 07:37:06 +01:00
|
|
|
}
|
2019-01-18 07:35:58 +01:00
|
|
|
|
2019-06-25 09:55:09 +02:00
|
|
|
void json_sendpay_fail_fields(struct json_stream *js,
|
2019-08-11 15:29:16 +02:00
|
|
|
const struct wallet_payment *payment,
|
2022-09-18 02:20:50 +02:00
|
|
|
enum jsonrpc_errcode pay_errcode,
|
2020-01-23 00:38:04 +01:00
|
|
|
const struct onionreply *onionreply,
|
2019-06-25 09:55:09 +02:00
|
|
|
const struct routing_failure *fail)
|
|
|
|
{
|
2019-08-11 15:29:16 +02:00
|
|
|
/* "immediate_routing_failure" is before payment creation. */
|
|
|
|
if (payment)
|
|
|
|
json_add_payment_fields(js, payment);
|
2020-01-23 06:49:42 +01:00
|
|
|
if (pay_errcode == PAY_UNPARSEABLE_ONION && onionreply)
|
2020-01-23 00:38:04 +01:00
|
|
|
json_add_hex_talarr(js, "onionreply", onionreply->contents);
|
2022-03-24 03:19:25 +01:00
|
|
|
else if (fail)
|
2019-06-25 09:55:09 +02:00
|
|
|
json_add_routefail_info(js,
|
|
|
|
fail->erring_index,
|
|
|
|
fail->failcode,
|
2019-11-11 16:14:35 +01:00
|
|
|
fail->erring_node,
|
2019-11-11 16:26:45 +01:00
|
|
|
fail->erring_channel,
|
2019-06-25 09:55:09 +02:00
|
|
|
fail->channel_dir,
|
|
|
|
fail->msg);
|
|
|
|
}
|
|
|
|
|
2022-09-18 02:20:50 +02:00
|
|
|
static const char *sendpay_errmsg_fmt(const tal_t *ctx, enum jsonrpc_errcode pay_errcode,
|
2020-01-07 16:31:00 +01:00
|
|
|
const struct routing_failure *fail,
|
|
|
|
const char *details)
|
|
|
|
{
|
|
|
|
char *errmsg;
|
|
|
|
if (pay_errcode == PAY_UNPARSEABLE_ONION)
|
|
|
|
errmsg = "Malformed error reply";
|
|
|
|
else {
|
|
|
|
assert(fail);
|
|
|
|
errmsg = tal_fmt(ctx, "failed: %s (%s)",
|
2020-08-31 03:13:25 +02:00
|
|
|
onion_wire_name(fail->failcode), details);
|
2020-01-07 16:31:00 +01:00
|
|
|
}
|
|
|
|
return errmsg;
|
|
|
|
}
|
|
|
|
|
2019-01-18 07:37:06 +01:00
|
|
|
/* onionreply used if pay_errcode == PAY_UNPARSEABLE_ONION */
|
|
|
|
static struct command_result *
|
|
|
|
sendpay_fail(struct command *cmd,
|
2019-08-11 15:29:16 +02:00
|
|
|
const struct wallet_payment *payment,
|
2022-09-18 02:20:50 +02:00
|
|
|
enum jsonrpc_errcode pay_errcode,
|
2020-01-23 00:38:04 +01:00
|
|
|
const struct onionreply *onionreply,
|
2019-01-18 07:37:06 +01:00
|
|
|
const struct routing_failure *fail,
|
2020-01-07 17:05:07 +01:00
|
|
|
const char *errmsg)
|
2019-01-18 07:37:06 +01:00
|
|
|
{
|
|
|
|
struct json_stream *data;
|
2019-06-25 10:32:53 +02:00
|
|
|
|
2019-01-18 07:37:06 +01:00
|
|
|
data = json_stream_fail(cmd, pay_errcode,
|
2019-06-25 09:55:09 +02:00
|
|
|
errmsg);
|
|
|
|
json_sendpay_fail_fields(data,
|
2019-08-11 15:29:16 +02:00
|
|
|
payment,
|
2019-06-25 09:55:09 +02:00
|
|
|
pay_errcode,
|
|
|
|
onionreply,
|
|
|
|
fail);
|
2019-06-12 02:38:54 +02:00
|
|
|
json_object_end(data);
|
2019-01-18 07:37:06 +01:00
|
|
|
return command_failed(cmd, data);
|
2019-01-18 07:35:58 +01:00
|
|
|
}
|
|
|
|
|
2019-01-18 07:37:06 +01:00
|
|
|
/* We defer sendpay "success" until we know it's pending; consumes cmd */
|
|
|
|
static struct command_result *
|
|
|
|
json_sendpay_in_progress(struct command *cmd,
|
|
|
|
const struct wallet_payment *payment)
|
2019-01-18 07:35:58 +01:00
|
|
|
{
|
2019-01-18 07:37:06 +01:00
|
|
|
struct json_stream *response = json_stream_success(cmd);
|
|
|
|
json_add_string(response, "message",
|
2020-01-07 14:31:47 +01:00
|
|
|
"Monitor status with listpays or waitsendpay");
|
2019-01-18 07:37:06 +01:00
|
|
|
json_add_payment_fields(response, payment);
|
|
|
|
return command_success(cmd, response);
|
2019-01-18 07:35:58 +01:00
|
|
|
}
|
|
|
|
|
2019-01-18 07:37:06 +01:00
|
|
|
static void tell_waiters_failed(struct lightningd *ld,
|
2018-03-10 07:41:45 +01:00
|
|
|
const struct sha256 *payment_hash,
|
2019-08-11 15:29:16 +02:00
|
|
|
const struct wallet_payment *payment,
|
2022-09-18 02:20:50 +02:00
|
|
|
enum jsonrpc_errcode pay_errcode,
|
2020-01-23 00:38:04 +01:00
|
|
|
const struct onionreply *onionreply,
|
2019-01-18 07:37:06 +01:00
|
|
|
const struct routing_failure *fail,
|
|
|
|
const char *details)
|
2018-02-02 01:55:06 +01:00
|
|
|
{
|
2018-02-14 01:09:23 +01:00
|
|
|
struct sendpay_command *pc;
|
|
|
|
struct sendpay_command *next;
|
2020-01-07 17:05:07 +01:00
|
|
|
const char *errmsg =
|
|
|
|
sendpay_errmsg_fmt(tmpctx, pay_errcode, fail, details);
|
2019-01-18 07:35:58 +01:00
|
|
|
|
2019-01-18 07:37:06 +01:00
|
|
|
/* Careful: sendpay_fail deletes cmd */
|
2018-03-10 07:41:45 +01:00
|
|
|
list_for_each_safe(&ld->waitsendpay_commands, pc, next, list) {
|
2018-07-04 07:30:02 +02:00
|
|
|
if (!sha256_eq(payment_hash, &pc->payment_hash))
|
2018-02-02 01:55:06 +01:00
|
|
|
continue;
|
2019-12-12 00:52:32 +01:00
|
|
|
if (payment->partid != pc->partid)
|
|
|
|
continue;
|
2021-09-29 19:10:12 +02:00
|
|
|
if (payment->groupid != pc->groupid)
|
|
|
|
continue;
|
2018-02-02 01:55:06 +01:00
|
|
|
|
2020-01-07 17:05:07 +01:00
|
|
|
sendpay_fail(pc->cmd, payment, pay_errcode, onionreply, fail,
|
|
|
|
errmsg);
|
2018-02-02 01:55:06 +01:00
|
|
|
}
|
2020-01-07 17:05:07 +01:00
|
|
|
|
|
|
|
notify_sendpay_failure(ld,
|
|
|
|
payment,
|
|
|
|
pay_errcode,
|
|
|
|
onionreply,
|
|
|
|
fail,
|
|
|
|
errmsg);
|
2018-02-02 01:55:06 +01:00
|
|
|
}
|
|
|
|
|
2019-01-18 07:37:06 +01:00
|
|
|
static void tell_waiters_success(struct lightningd *ld,
|
|
|
|
const struct sha256 *payment_hash,
|
|
|
|
struct wallet_payment *payment)
|
2018-03-03 01:11:39 +01:00
|
|
|
{
|
2019-01-18 07:37:06 +01:00
|
|
|
struct sendpay_command *pc;
|
|
|
|
struct sendpay_command *next;
|
2018-02-03 12:46:12 +01:00
|
|
|
|
2019-01-18 07:37:06 +01:00
|
|
|
/* Careful: sendpay_success deletes cmd */
|
|
|
|
list_for_each_safe(&ld->waitsendpay_commands, pc, next, list) {
|
|
|
|
if (!sha256_eq(payment_hash, &pc->payment_hash))
|
|
|
|
continue;
|
2019-12-12 00:52:32 +01:00
|
|
|
if (payment->partid != pc->partid)
|
|
|
|
continue;
|
2021-09-29 19:10:12 +02:00
|
|
|
if (payment->groupid != pc->groupid)
|
|
|
|
continue;
|
2018-03-03 01:11:39 +01:00
|
|
|
|
2019-01-18 07:37:06 +01:00
|
|
|
sendpay_success(pc->cmd, payment);
|
|
|
|
}
|
2020-01-07 17:05:07 +01:00
|
|
|
notify_sendpay_success(ld, payment);
|
2018-03-13 14:35:43 +01:00
|
|
|
}
|
|
|
|
|
2017-06-20 07:53:03 +02:00
|
|
|
void payment_succeeded(struct lightningd *ld, struct htlc_out *hout,
|
2017-04-01 15:05:29 +02:00
|
|
|
const struct preimage *rval)
|
|
|
|
{
|
2019-01-18 07:37:06 +01:00
|
|
|
struct wallet_payment *payment;
|
|
|
|
|
2018-01-17 21:29:49 +01:00
|
|
|
wallet_payment_set_status(ld->wallet, &hout->payment_hash,
|
2021-09-29 12:33:57 +02:00
|
|
|
hout->partid, hout->groupid,
|
2018-01-17 21:29:49 +01:00
|
|
|
PAYMENT_COMPLETE, rval);
|
2019-01-18 07:37:06 +01:00
|
|
|
payment = wallet_payment_by_hash(tmpctx, ld->wallet,
|
2019-12-12 00:16:23 +01:00
|
|
|
&hout->payment_hash,
|
2021-09-29 19:10:12 +02:00
|
|
|
hout->partid, hout->groupid);
|
2019-01-18 07:37:06 +01:00
|
|
|
assert(payment);
|
|
|
|
|
2022-11-09 03:32:01 +01:00
|
|
|
if (payment->local_invreq_id)
|
|
|
|
wallet_invoice_request_mark_used(ld->wallet->db,
|
|
|
|
payment->local_invreq_id);
|
2019-01-18 07:37:06 +01:00
|
|
|
tell_waiters_success(ld, &hout->payment_hash, payment);
|
2017-04-01 15:05:29 +02:00
|
|
|
}
|
|
|
|
|
2018-02-03 01:05:17 +01:00
|
|
|
/* Return a struct routing_failure for an immediate failure
|
|
|
|
* (returned directly from send_htlc_out). The returned
|
|
|
|
* failure is allocated from the given context. */
|
|
|
|
static struct routing_failure*
|
|
|
|
immediate_routing_failure(const tal_t *ctx,
|
|
|
|
const struct lightningd *ld,
|
2020-08-31 03:13:25 +02:00
|
|
|
enum onion_wire failcode,
|
2019-01-15 05:02:27 +01:00
|
|
|
const struct short_channel_id *channel0,
|
2019-04-08 11:58:32 +02:00
|
|
|
const struct node_id *dstid)
|
2018-02-03 01:05:17 +01:00
|
|
|
{
|
|
|
|
struct routing_failure *routing_failure;
|
|
|
|
|
|
|
|
assert(failcode);
|
|
|
|
|
|
|
|
routing_failure = tal(ctx, struct routing_failure);
|
2018-02-06 14:41:15 +01:00
|
|
|
routing_failure->erring_index = 0;
|
2018-02-03 01:05:17 +01:00
|
|
|
routing_failure->failcode = failcode;
|
2019-11-11 16:14:35 +01:00
|
|
|
routing_failure->erring_node =
|
|
|
|
tal_dup(routing_failure, struct node_id, &ld->id);
|
2019-11-11 16:26:45 +01:00
|
|
|
routing_failure->erring_channel =
|
|
|
|
tal_dup(routing_failure, struct short_channel_id, channel0);
|
2019-04-08 11:58:32 +02:00
|
|
|
routing_failure->channel_dir = node_id_idx(&ld->id, dstid);
|
2019-08-28 06:08:26 +02:00
|
|
|
routing_failure->msg = NULL;
|
2018-02-03 01:05:17 +01:00
|
|
|
|
|
|
|
return routing_failure;
|
|
|
|
}
|
|
|
|
|
2018-01-29 16:04:33 +01:00
|
|
|
/* Return a struct routing_failure for a local failure allocated
|
|
|
|
* from the given context. */
|
|
|
|
static struct routing_failure*
|
|
|
|
local_routing_failure(const tal_t *ctx,
|
|
|
|
const struct lightningd *ld,
|
|
|
|
const struct htlc_out *hout,
|
2020-08-31 03:13:25 +02:00
|
|
|
enum onion_wire failcode,
|
2018-01-29 16:04:33 +01:00
|
|
|
const struct wallet_payment *payment)
|
|
|
|
{
|
|
|
|
struct routing_failure *routing_failure;
|
|
|
|
|
|
|
|
routing_failure = tal(ctx, struct routing_failure);
|
2018-02-06 14:41:15 +01:00
|
|
|
routing_failure->erring_index = 0;
|
2020-05-04 09:19:53 +02:00
|
|
|
routing_failure->failcode = failcode;
|
2020-02-21 06:06:58 +01:00
|
|
|
|
2019-11-11 16:14:35 +01:00
|
|
|
routing_failure->erring_node =
|
|
|
|
tal_dup(routing_failure, struct node_id, &ld->id);
|
2019-11-11 16:26:45 +01:00
|
|
|
|
|
|
|
if (payment->route_nodes != NULL && payment->route_channels != NULL) {
|
|
|
|
routing_failure->erring_channel =
|
|
|
|
tal_dup(routing_failure, struct short_channel_id,
|
|
|
|
&payment->route_channels[0]);
|
|
|
|
routing_failure->channel_dir =
|
|
|
|
node_id_idx(&ld->id, &payment->route_nodes[0]);
|
|
|
|
} else {
|
|
|
|
routing_failure->erring_channel = NULL;
|
|
|
|
}
|
|
|
|
|
2019-08-28 06:08:26 +02:00
|
|
|
routing_failure->msg = NULL;
|
2018-01-29 16:04:33 +01:00
|
|
|
|
2019-01-17 16:24:32 +01:00
|
|
|
log_debug(hout->key.channel->log, "local_routing_failure: %u (%s)",
|
2020-02-21 06:06:58 +01:00
|
|
|
routing_failure->failcode,
|
2020-08-31 03:13:25 +02:00
|
|
|
onion_wire_name(routing_failure->failcode));
|
2018-01-29 16:04:33 +01:00
|
|
|
return routing_failure;
|
|
|
|
}
|
|
|
|
|
2019-01-18 07:37:06 +01:00
|
|
|
/* Fills in *pay_errcode with PAY_TRY_OTHER_ROUTE or PAY_DESTINATION_PERM_FAIL */
|
2018-02-03 11:44:31 +01:00
|
|
|
static struct routing_failure*
|
|
|
|
remote_routing_failure(const tal_t *ctx,
|
2019-01-17 16:24:32 +01:00
|
|
|
struct lightningd *ld,
|
2018-02-03 11:44:31 +01:00
|
|
|
const struct wallet_payment *payment,
|
2020-01-22 06:59:00 +01:00
|
|
|
const u8 *failuremsg,
|
|
|
|
int origin_index,
|
2023-07-16 07:56:52 +02:00
|
|
|
struct logger *log,
|
2022-09-18 02:20:50 +02:00
|
|
|
enum jsonrpc_errcode *pay_errcode)
|
2018-01-21 05:15:07 +01:00
|
|
|
{
|
2020-08-31 03:13:25 +02:00
|
|
|
enum onion_wire failcode = fromwire_peektype(failuremsg);
|
2018-02-03 11:44:31 +01:00
|
|
|
struct routing_failure *routing_failure;
|
2019-04-08 11:58:32 +02:00
|
|
|
const struct node_id *route_nodes;
|
|
|
|
const struct node_id *erring_node;
|
2018-01-21 05:15:07 +01:00
|
|
|
const struct short_channel_id *route_channels;
|
|
|
|
const struct short_channel_id *erring_channel;
|
2019-01-15 05:02:27 +01:00
|
|
|
int dir;
|
2018-01-21 05:15:07 +01:00
|
|
|
|
2018-02-03 11:44:31 +01:00
|
|
|
routing_failure = tal(ctx, struct routing_failure);
|
2018-01-21 05:15:07 +01:00
|
|
|
route_nodes = payment->route_nodes;
|
|
|
|
route_channels = payment->route_channels;
|
|
|
|
|
2019-11-11 16:14:35 +01:00
|
|
|
assert(route_nodes == NULL || origin_index < tal_count(route_nodes));
|
2018-01-21 05:15:07 +01:00
|
|
|
|
2019-11-11 17:18:10 +01:00
|
|
|
/* Either we have both channels and nodes, or neither */
|
|
|
|
assert((route_nodes == NULL) == (route_channels == NULL));
|
|
|
|
|
|
|
|
if (route_nodes == NULL) {
|
|
|
|
/* This means we have the `shared_secrets`, but cannot infer
|
|
|
|
* the erring channel and node since we don't have them. This
|
|
|
|
* can happen if the payment was initialized using `sendonion`
|
|
|
|
* and the `shared_secrets` where specified. */
|
|
|
|
dir = 0;
|
|
|
|
erring_channel = NULL;
|
|
|
|
erring_node = NULL;
|
|
|
|
|
|
|
|
/* We don't know if there's another route, that'd depend on
|
|
|
|
* where the failure occured and whether it was a node
|
|
|
|
* failure. Let's assume it wasn't a terminal one, and have
|
|
|
|
* the sendonion caller deal with the actual decision. */
|
|
|
|
*pay_errcode = PAY_TRY_OTHER_ROUTE;
|
|
|
|
} else if (origin_index == tal_count(route_nodes) - 1) {
|
2019-01-18 07:37:08 +01:00
|
|
|
/* If any channel is to blame, it's the last one. */
|
|
|
|
erring_channel = &route_channels[origin_index];
|
|
|
|
/* Single hop? */
|
|
|
|
if (origin_index == 0)
|
2019-04-08 11:58:32 +02:00
|
|
|
dir = node_id_idx(&ld->id,
|
|
|
|
&route_nodes[origin_index]);
|
2019-01-18 07:37:08 +01:00
|
|
|
else
|
2019-04-08 11:58:32 +02:00
|
|
|
dir = node_id_idx(&route_nodes[origin_index - 1],
|
|
|
|
&route_nodes[origin_index]);
|
2019-01-15 05:02:27 +01:00
|
|
|
|
2018-01-21 05:15:07 +01:00
|
|
|
/* BOLT #4:
|
|
|
|
*
|
|
|
|
* - if the _final node_ is returning the error:
|
|
|
|
* - if the PERM bit is set:
|
|
|
|
* - SHOULD fail the payment.
|
|
|
|
* */
|
2020-02-21 06:06:58 +01:00
|
|
|
if (failcode & BADONION)
|
|
|
|
*pay_errcode = PAY_UNPARSEABLE_ONION;
|
|
|
|
else if (failcode & PERM)
|
2019-01-18 07:37:06 +01:00
|
|
|
*pay_errcode = PAY_DESTINATION_PERM_FAIL;
|
2018-01-21 05:15:07 +01:00
|
|
|
else
|
2019-02-17 12:21:35 +01:00
|
|
|
/* FIXME: not right for WIRE_FINAL_EXPIRY_TOO_SOON */
|
2019-01-18 07:37:06 +01:00
|
|
|
*pay_errcode = PAY_TRY_OTHER_ROUTE;
|
2019-01-08 01:53:25 +01:00
|
|
|
erring_node = &route_nodes[origin_index];
|
|
|
|
} else {
|
2019-01-18 07:37:06 +01:00
|
|
|
*pay_errcode = PAY_TRY_OTHER_ROUTE;
|
|
|
|
|
2018-01-21 05:15:07 +01:00
|
|
|
/* Report the *next* channel as failing. */
|
|
|
|
erring_channel = &route_channels[origin_index + 1];
|
|
|
|
|
2019-04-08 11:58:32 +02:00
|
|
|
dir = node_id_idx(&route_nodes[origin_index],
|
|
|
|
&route_nodes[origin_index+1]);
|
2019-01-15 05:02:27 +01:00
|
|
|
|
2019-01-08 01:53:25 +01:00
|
|
|
/* If the error is a BADONION, then it's on behalf of the
|
|
|
|
* following node. */
|
|
|
|
if (failcode & BADONION) {
|
2019-01-08 01:53:25 +01:00
|
|
|
log_debug(log, "failcode %u from onionreply %s",
|
2020-01-22 06:59:00 +01:00
|
|
|
failcode, tal_hex(tmpctx, failuremsg));
|
2019-01-08 01:53:25 +01:00
|
|
|
erring_node = &route_nodes[origin_index + 1];
|
|
|
|
} else
|
|
|
|
erring_node = &route_nodes[origin_index];
|
|
|
|
}
|
2018-01-29 16:04:33 +01:00
|
|
|
|
2018-02-06 14:41:15 +01:00
|
|
|
routing_failure->erring_index = (unsigned int) (origin_index + 1);
|
2018-02-03 11:44:31 +01:00
|
|
|
routing_failure->failcode = failcode;
|
2020-02-27 03:17:01 +01:00
|
|
|
routing_failure->msg = tal_dup_talarr(routing_failure, u8, failuremsg);
|
2018-02-03 11:44:31 +01:00
|
|
|
|
2021-12-28 00:21:09 +01:00
|
|
|
routing_failure->erring_node =
|
|
|
|
tal_dup_or_null(routing_failure, struct node_id, erring_node);
|
2019-11-11 16:14:35 +01:00
|
|
|
|
2019-11-11 16:26:45 +01:00
|
|
|
if (erring_channel != NULL) {
|
|
|
|
routing_failure->erring_channel = tal_dup(
|
|
|
|
routing_failure, struct short_channel_id, erring_channel);
|
|
|
|
routing_failure->channel_dir = dir;
|
|
|
|
} else {
|
|
|
|
routing_failure->erring_channel = NULL;
|
|
|
|
routing_failure->channel_dir = 0;
|
|
|
|
}
|
|
|
|
|
2018-02-03 11:44:31 +01:00
|
|
|
return routing_failure;
|
2018-01-21 05:15:07 +01:00
|
|
|
}
|
|
|
|
|
2020-01-07 21:00:51 +01:00
|
|
|
void payment_store(struct lightningd *ld, struct wallet_payment *payment TAKES)
|
2018-03-10 07:39:11 +01:00
|
|
|
{
|
2018-03-10 08:44:28 +01:00
|
|
|
struct sendpay_command *pc;
|
|
|
|
struct sendpay_command *next;
|
2020-01-07 21:00:51 +01:00
|
|
|
/* Need to remember here otherwise wallet_payment_store will free us. */
|
|
|
|
bool ptaken = taken(payment);
|
2018-03-10 08:44:28 +01:00
|
|
|
|
2020-01-07 21:00:51 +01:00
|
|
|
wallet_payment_store(ld->wallet, payment);
|
2018-03-10 08:44:28 +01:00
|
|
|
|
|
|
|
/* Trigger any sendpay commands waiting for the store to occur. */
|
|
|
|
list_for_each_safe(&ld->sendpay_commands, pc, next, list) {
|
2020-01-07 21:00:51 +01:00
|
|
|
if (!sha256_eq(&payment->payment_hash, &pc->payment_hash))
|
2018-03-10 08:44:28 +01:00
|
|
|
continue;
|
|
|
|
|
2019-01-18 07:37:06 +01:00
|
|
|
/* Deletes from list, frees pc */
|
|
|
|
json_sendpay_in_progress(pc->cmd, payment);
|
2018-03-10 08:44:28 +01:00
|
|
|
}
|
2020-01-07 21:00:51 +01:00
|
|
|
|
|
|
|
if (ptaken)
|
|
|
|
tal_free(payment);
|
2018-03-10 07:39:11 +01:00
|
|
|
}
|
|
|
|
|
2017-06-20 08:12:03 +02:00
|
|
|
void payment_failed(struct lightningd *ld, const struct htlc_out *hout,
|
2022-01-24 21:01:52 +01:00
|
|
|
const char *localfail)
|
2017-04-01 15:05:29 +02:00
|
|
|
{
|
2018-01-21 05:15:07 +01:00
|
|
|
struct wallet_payment *payment;
|
2018-01-29 16:04:33 +01:00
|
|
|
struct routing_failure* fail = NULL;
|
2020-02-18 00:53:58 +01:00
|
|
|
const char *failstr;
|
2022-09-18 02:20:50 +02:00
|
|
|
enum jsonrpc_errcode pay_errcode;
|
2020-02-21 06:06:58 +01:00
|
|
|
const u8 *failmsg;
|
|
|
|
int origin_index;
|
2017-06-20 07:53:03 +02:00
|
|
|
|
2018-01-21 05:15:07 +01:00
|
|
|
payment = wallet_payment_by_hash(tmpctx, ld->wallet,
|
2019-12-12 00:16:23 +01:00
|
|
|
&hout->payment_hash,
|
2021-09-29 19:10:12 +02:00
|
|
|
hout->partid, hout->groupid);
|
2018-01-29 16:04:33 +01:00
|
|
|
|
2018-03-06 19:31:21 +01:00
|
|
|
#ifdef COMPAT_V052
|
2018-03-16 01:14:44 +01:00
|
|
|
/* Prior to "pay: delete HTLC when we delete payment." we would
|
|
|
|
* delete a payment on retry, but leave the HTLC. */
|
|
|
|
if (!payment) {
|
|
|
|
log_unusual(hout->key.channel->log,
|
|
|
|
"No payment for %s:"
|
|
|
|
" was this an old database?",
|
|
|
|
type_to_string(tmpctx, struct sha256,
|
|
|
|
&hout->payment_hash));
|
|
|
|
return;
|
|
|
|
}
|
2018-10-09 10:58:52 +02:00
|
|
|
#else
|
|
|
|
assert(payment);
|
2018-03-06 19:31:21 +01:00
|
|
|
#endif
|
2019-11-11 17:18:10 +01:00
|
|
|
assert((payment->route_channels == NULL) == (payment->route_nodes == NULL));
|
2018-02-17 05:42:35 +01:00
|
|
|
|
2018-01-29 16:04:33 +01:00
|
|
|
/* This gives more details than a generic failure message */
|
|
|
|
if (localfail) {
|
2020-05-04 09:19:53 +02:00
|
|
|
/* Use temporary_channel_failure if failmsg has it */
|
2020-08-31 03:13:25 +02:00
|
|
|
enum onion_wire failcode;
|
2022-01-24 21:01:52 +01:00
|
|
|
failcode = fromwire_peektype(hout->failmsg);
|
2020-05-04 09:19:53 +02:00
|
|
|
|
|
|
|
fail = local_routing_failure(tmpctx, ld, hout, failcode,
|
|
|
|
payment);
|
2020-02-18 00:53:58 +01:00
|
|
|
failstr = localfail;
|
2019-01-18 07:37:06 +01:00
|
|
|
pay_errcode = PAY_TRY_OTHER_ROUTE;
|
2019-11-08 17:14:53 +01:00
|
|
|
} else if (payment->path_secrets == NULL) {
|
|
|
|
/* This was a payment initiated with `sendonion`, we therefore
|
|
|
|
* don't have the path secrets and cannot decode the error
|
|
|
|
* onion. Let's store it and hope whatever called `sendonion`
|
|
|
|
* knows how to deal with these. */
|
|
|
|
|
|
|
|
pay_errcode = PAY_UNPARSEABLE_ONION;
|
|
|
|
fail = NULL;
|
2020-02-18 00:53:58 +01:00
|
|
|
failstr = NULL;
|
2020-02-21 06:06:58 +01:00
|
|
|
} else if (hout->failmsg) {
|
|
|
|
/* This can happen when a direct peer told channeld it's a
|
|
|
|
* malformed onion using update_fail_malformed_htlc. */
|
|
|
|
failstr = "local failure";
|
|
|
|
failmsg = hout->failmsg;
|
|
|
|
origin_index = 0;
|
|
|
|
pay_errcode = PAY_TRY_OTHER_ROUTE;
|
|
|
|
goto use_failmsg;
|
2017-11-28 06:03:04 +01:00
|
|
|
} else {
|
2020-01-23 06:49:42 +01:00
|
|
|
/* Must be normal remote fail with an onion-wrapped error. */
|
2020-02-18 00:53:58 +01:00
|
|
|
failstr = "reply from remote";
|
2018-01-29 16:04:33 +01:00
|
|
|
/* Try to parse reply. */
|
2018-03-25 21:51:11 +02:00
|
|
|
struct secret *path_secrets = payment->path_secrets;
|
2019-01-17 16:24:32 +01:00
|
|
|
|
2020-02-21 06:06:58 +01:00
|
|
|
failmsg = unwrap_onionreply(tmpctx, path_secrets,
|
|
|
|
tal_count(path_secrets),
|
|
|
|
hout->failonion, &origin_index);
|
|
|
|
if (!failmsg) {
|
2018-02-12 11:13:04 +01:00
|
|
|
log_info(hout->key.channel->log,
|
2018-01-29 16:04:33 +01:00
|
|
|
"htlc %"PRIu64" failed with bad reply (%s)",
|
|
|
|
hout->key.id,
|
2020-02-18 00:53:58 +01:00
|
|
|
tal_hex(tmpctx, hout->failonion->contents));
|
2019-01-17 16:24:32 +01:00
|
|
|
/* Cannot record failure. */
|
2018-01-29 16:04:33 +01:00
|
|
|
fail = NULL;
|
2019-01-18 07:37:06 +01:00
|
|
|
pay_errcode = PAY_UNPARSEABLE_ONION;
|
2018-01-29 16:04:33 +01:00
|
|
|
} else {
|
2020-08-31 03:13:25 +02:00
|
|
|
enum onion_wire failcode;
|
2020-02-21 06:06:58 +01:00
|
|
|
|
|
|
|
use_failmsg:
|
|
|
|
failcode = fromwire_peektype(failmsg);
|
2018-02-12 11:13:04 +01:00
|
|
|
log_info(hout->key.channel->log,
|
2018-01-29 16:04:33 +01:00
|
|
|
"htlc %"PRIu64" "
|
|
|
|
"failed from %ith node "
|
|
|
|
"with code 0x%04x (%s)",
|
|
|
|
hout->key.id,
|
2020-01-22 06:59:00 +01:00
|
|
|
origin_index,
|
2020-08-31 03:13:25 +02:00
|
|
|
failcode, onion_wire_name(failcode));
|
2019-01-17 16:24:32 +01:00
|
|
|
fail = remote_routing_failure(tmpctx, ld,
|
2020-02-21 06:06:58 +01:00
|
|
|
payment, failmsg,
|
2020-01-22 06:59:00 +01:00
|
|
|
origin_index,
|
2019-01-18 07:37:06 +01:00
|
|
|
hout->key.channel->log,
|
|
|
|
&pay_errcode);
|
2018-01-29 16:04:33 +01:00
|
|
|
}
|
2017-06-20 07:53:03 +02:00
|
|
|
}
|
|
|
|
|
2018-03-07 17:10:08 +01:00
|
|
|
/* Save to DB */
|
2020-01-07 21:00:51 +01:00
|
|
|
payment_store(ld, payment);
|
2019-12-12 00:39:07 +01:00
|
|
|
wallet_payment_set_status(ld->wallet, &hout->payment_hash,
|
2021-09-29 12:33:57 +02:00
|
|
|
hout->partid, hout->groupid,
|
2018-01-29 18:34:59 +01:00
|
|
|
PAYMENT_FAILED, NULL);
|
2018-03-07 17:10:08 +01:00
|
|
|
wallet_payment_set_failinfo(ld->wallet,
|
2019-12-12 00:39:07 +01:00
|
|
|
&hout->payment_hash,
|
|
|
|
hout->partid,
|
2020-02-18 00:53:58 +01:00
|
|
|
fail ? NULL : hout->failonion,
|
2019-01-18 07:37:06 +01:00
|
|
|
pay_errcode == PAY_DESTINATION_PERM_FAIL,
|
2018-03-07 17:10:08 +01:00
|
|
|
fail ? fail->erring_index : -1,
|
|
|
|
fail ? fail->failcode : 0,
|
2019-11-11 16:14:35 +01:00
|
|
|
fail ? fail->erring_node : NULL,
|
2019-11-11 16:26:45 +01:00
|
|
|
fail ? fail->erring_channel : NULL,
|
2019-01-17 16:24:32 +01:00
|
|
|
NULL,
|
2020-02-18 00:53:58 +01:00
|
|
|
failstr,
|
2019-01-15 05:02:27 +01:00
|
|
|
fail ? fail->channel_dir : 0);
|
2018-01-29 18:34:59 +01:00
|
|
|
|
2020-01-07 20:05:00 +01:00
|
|
|
tell_waiters_failed(ld, &hout->payment_hash, payment, pay_errcode,
|
2020-02-18 00:53:58 +01:00
|
|
|
hout->failonion, fail, failstr);
|
2017-04-01 15:05:29 +02:00
|
|
|
}
|
|
|
|
|
2019-06-25 10:54:27 +02:00
|
|
|
/* Wait for a payment. If cmd is deleted, then wait_payment()
|
2018-03-04 06:35:37 +01:00
|
|
|
* no longer be called.
|
2019-01-18 07:37:06 +01:00
|
|
|
* Return callback if we called already, otherwise NULL. */
|
|
|
|
static struct command_result *wait_payment(struct lightningd *ld,
|
|
|
|
struct command *cmd,
|
2019-12-12 00:16:23 +01:00
|
|
|
const struct sha256 *payment_hash,
|
2021-09-29 19:10:12 +02:00
|
|
|
u64 partid, u64 groupid)
|
2018-03-04 06:35:37 +01:00
|
|
|
{
|
|
|
|
struct wallet_payment *payment;
|
2020-01-23 00:38:04 +01:00
|
|
|
struct onionreply *failonionreply;
|
2018-03-07 17:10:08 +01:00
|
|
|
bool faildestperm;
|
|
|
|
int failindex;
|
2020-08-31 03:13:25 +02:00
|
|
|
enum onion_wire failcode;
|
2019-04-08 11:58:32 +02:00
|
|
|
struct node_id *failnode;
|
2018-03-07 17:10:08 +01:00
|
|
|
struct short_channel_id *failchannel;
|
|
|
|
u8 *failupdate;
|
2018-04-16 15:29:40 +02:00
|
|
|
char *faildetail;
|
2018-03-07 17:10:08 +01:00
|
|
|
struct routing_failure *fail;
|
2019-01-15 05:02:27 +01:00
|
|
|
int faildirection;
|
2022-09-18 02:20:50 +02:00
|
|
|
enum jsonrpc_errcode rpcerrorcode;
|
2018-03-04 06:35:37 +01:00
|
|
|
|
2019-12-12 00:16:23 +01:00
|
|
|
payment = wallet_payment_by_hash(tmpctx, ld->wallet,
|
2021-09-29 19:10:12 +02:00
|
|
|
payment_hash, partid, groupid);
|
2018-03-04 06:35:37 +01:00
|
|
|
if (!payment) {
|
2019-01-18 07:37:06 +01:00
|
|
|
return command_fail(cmd, PAY_NO_SUCH_PAYMENT,
|
2019-12-12 01:20:46 +01:00
|
|
|
"Never attempted payment part %"PRIu64
|
|
|
|
" for '%s'",
|
|
|
|
partid,
|
2019-01-18 07:37:06 +01:00
|
|
|
type_to_string(tmpctx, struct sha256,
|
|
|
|
payment_hash));
|
2018-03-04 06:35:37 +01:00
|
|
|
}
|
|
|
|
|
2021-09-29 19:10:12 +02:00
|
|
|
log_debug(cmd->ld->log, "Payment part %"PRIu64"/%"PRIu64"/%"PRIu64" status %u",
|
|
|
|
partid, payment->partid, payment->groupid, payment->status);
|
2019-12-12 00:52:32 +01:00
|
|
|
|
2018-03-04 06:35:37 +01:00
|
|
|
switch (payment->status) {
|
|
|
|
case PAYMENT_PENDING:
|
2021-09-29 19:10:12 +02:00
|
|
|
add_waitsendpay_waiter(ld, cmd, payment_hash, partid, groupid);
|
2019-01-18 07:37:06 +01:00
|
|
|
return NULL;
|
2018-03-04 06:35:37 +01:00
|
|
|
|
|
|
|
case PAYMENT_COMPLETE:
|
2019-01-18 07:37:06 +01:00
|
|
|
return sendpay_success(cmd, payment);
|
2018-03-04 06:35:37 +01:00
|
|
|
|
|
|
|
case PAYMENT_FAILED:
|
2018-03-07 17:10:08 +01:00
|
|
|
/* Get error from DB */
|
2019-12-12 00:16:23 +01:00
|
|
|
wallet_payment_get_failinfo(tmpctx, ld->wallet,
|
|
|
|
payment_hash,
|
|
|
|
partid,
|
2021-09-29 19:10:12 +02:00
|
|
|
groupid,
|
2018-03-07 17:10:08 +01:00
|
|
|
&failonionreply,
|
|
|
|
&faildestperm,
|
|
|
|
&failindex,
|
|
|
|
&failcode,
|
|
|
|
&failnode,
|
|
|
|
&failchannel,
|
2018-04-16 15:29:40 +02:00
|
|
|
&failupdate,
|
2019-01-15 05:02:27 +01:00
|
|
|
&faildetail,
|
|
|
|
&faildirection);
|
2018-03-07 17:10:08 +01:00
|
|
|
/* Old DB might not save failure information */
|
2019-01-18 07:37:06 +01:00
|
|
|
if (!failonionreply && !failnode) {
|
|
|
|
return command_fail(cmd, PAY_UNSPECIFIED_ERROR,
|
|
|
|
"Payment failure reason unknown");
|
|
|
|
} else if (failonionreply) {
|
2018-03-07 17:10:08 +01:00
|
|
|
/* failed to parse returned onion error */
|
2020-01-07 17:05:07 +01:00
|
|
|
return sendpay_fail(
|
|
|
|
cmd, payment, PAY_UNPARSEABLE_ONION, failonionreply,
|
|
|
|
NULL,
|
|
|
|
sendpay_errmsg_fmt(tmpctx, PAY_UNPARSEABLE_ONION,
|
|
|
|
NULL, faildetail));
|
2018-03-07 17:10:08 +01:00
|
|
|
} else {
|
|
|
|
/* Parsed onion error, get its details */
|
|
|
|
assert(failnode);
|
|
|
|
fail = tal(tmpctx, struct routing_failure);
|
|
|
|
fail->erring_index = failindex;
|
|
|
|
fail->failcode = failcode;
|
2019-11-11 16:14:35 +01:00
|
|
|
fail->erring_node =
|
|
|
|
tal_dup(fail, struct node_id, failnode);
|
2020-07-13 17:33:14 +02:00
|
|
|
|
|
|
|
if (failchannel) {
|
|
|
|
fail->erring_channel = tal_dup(
|
|
|
|
fail, struct short_channel_id, failchannel);
|
|
|
|
fail->channel_dir = faildirection;
|
|
|
|
} else {
|
|
|
|
fail->erring_channel = NULL;
|
|
|
|
}
|
|
|
|
|
2019-08-28 06:08:26 +02:00
|
|
|
/* FIXME: We don't store this! */
|
|
|
|
fail->msg = NULL;
|
2020-01-07 17:05:07 +01:00
|
|
|
|
|
|
|
rpcerrorcode = faildestperm ? PAY_DESTINATION_PERM_FAIL
|
|
|
|
: PAY_TRY_OTHER_ROUTE;
|
|
|
|
|
|
|
|
return sendpay_fail(
|
|
|
|
cmd, payment, rpcerrorcode, NULL, fail,
|
|
|
|
sendpay_errmsg_fmt(tmpctx, rpcerrorcode, fail,
|
|
|
|
faildetail));
|
2018-03-07 17:10:08 +01:00
|
|
|
}
|
2018-03-04 06:35:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Impossible. */
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
2020-02-21 06:10:02 +01:00
|
|
|
/* Returns failmsg on failure, tallocated off ctx */
|
|
|
|
static const u8 *send_onion(const tal_t *ctx, struct lightningd *ld,
|
2020-04-11 05:22:40 +02:00
|
|
|
const struct onionpacket *packet,
|
|
|
|
const struct route_hop *first_hop,
|
2021-12-07 21:09:28 +01:00
|
|
|
const struct amount_msat final_amount,
|
2020-04-11 05:22:40 +02:00
|
|
|
const struct sha256 *payment_hash,
|
|
|
|
const struct pubkey *blinding,
|
|
|
|
u64 partid,
|
2021-09-29 12:33:57 +02:00
|
|
|
u64 groupid,
|
2020-04-11 05:22:40 +02:00
|
|
|
struct channel *channel,
|
|
|
|
struct htlc_out **hout)
|
2019-11-07 21:58:32 +01:00
|
|
|
{
|
|
|
|
const u8 *onion;
|
|
|
|
unsigned int base_expiry;
|
2022-01-24 21:01:52 +01:00
|
|
|
|
2019-11-07 21:58:32 +01:00
|
|
|
base_expiry = get_block_height(ld->topology) + 1;
|
|
|
|
onion = serialize_onionpacket(tmpctx, packet);
|
2020-02-21 06:10:02 +01:00
|
|
|
return send_htlc_out(ctx, channel, first_hop->amount,
|
2021-12-07 21:09:28 +01:00
|
|
|
base_expiry + first_hop->delay,
|
|
|
|
final_amount, payment_hash,
|
2022-01-24 21:01:52 +01:00
|
|
|
blinding, partid, groupid, onion, NULL, hout);
|
2019-11-07 21:58:32 +01:00
|
|
|
}
|
|
|
|
|
2022-11-09 03:32:01 +01:00
|
|
|
static struct command_result *check_invoice_request_usage(struct command *cmd,
|
|
|
|
const struct sha256 *local_invreq_id)
|
2020-12-14 02:20:44 +01:00
|
|
|
{
|
|
|
|
enum offer_status status;
|
|
|
|
const struct wallet_payment **payments;
|
|
|
|
|
2022-11-09 03:32:01 +01:00
|
|
|
if (!local_invreq_id)
|
2020-12-14 02:20:44 +01:00
|
|
|
return NULL;
|
|
|
|
|
2022-11-09 03:32:01 +01:00
|
|
|
if (!wallet_invoice_request_find(tmpctx, cmd->ld->wallet,
|
|
|
|
local_invreq_id,
|
|
|
|
NULL, &status))
|
|
|
|
return command_fail(cmd, PAY_INVOICE_REQUEST_INVALID,
|
|
|
|
"Unknown invoice_request %s",
|
2020-12-14 02:20:44 +01:00
|
|
|
type_to_string(tmpctx, struct sha256,
|
2022-11-09 03:32:01 +01:00
|
|
|
local_invreq_id));
|
2020-12-14 02:20:44 +01:00
|
|
|
|
|
|
|
if (!offer_status_active(status))
|
2022-11-09 03:32:01 +01:00
|
|
|
return command_fail(cmd, PAY_INVOICE_REQUEST_INVALID,
|
|
|
|
"Inactive invoice_request %s",
|
2020-12-14 02:20:44 +01:00
|
|
|
type_to_string(tmpctx, struct sha256,
|
2022-11-09 03:32:01 +01:00
|
|
|
local_invreq_id));
|
2020-12-14 02:20:44 +01:00
|
|
|
|
|
|
|
if (!offer_status_single(status))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* OK, we must not attempt more than one payment at once for
|
2022-11-09 03:32:01 +01:00
|
|
|
* single_use invoice_request we publish! */
|
|
|
|
payments = wallet_payments_by_invoice_request(tmpctx, cmd->ld->wallet,
|
|
|
|
local_invreq_id);
|
2020-12-14 02:20:44 +01:00
|
|
|
for (size_t i = 0; i < tal_count(payments); i++) {
|
|
|
|
switch (payments[i]->status) {
|
|
|
|
case PAYMENT_COMPLETE:
|
2022-11-09 03:32:01 +01:00
|
|
|
return command_fail(cmd, PAY_INVOICE_REQUEST_INVALID,
|
|
|
|
"Single-use invoice_request already paid"
|
2020-12-14 02:20:44 +01:00
|
|
|
" with %s",
|
|
|
|
type_to_string(tmpctx, struct sha256,
|
|
|
|
&payments[i]
|
|
|
|
->payment_hash));
|
|
|
|
case PAYMENT_PENDING:
|
2022-11-09 03:32:01 +01:00
|
|
|
return command_fail(cmd, PAY_INVOICE_REQUEST_INVALID,
|
|
|
|
"Single-use invoice_request already"
|
2020-12-14 02:20:44 +01:00
|
|
|
" in progress with %s",
|
|
|
|
type_to_string(tmpctx, struct sha256,
|
|
|
|
&payments[i]
|
|
|
|
->payment_hash));
|
|
|
|
case PAYMENT_FAILED:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2023-07-23 16:58:58 +02:00
|
|
|
static struct channel *
|
|
|
|
find_channel_for_htlc_add(struct lightningd *ld, const struct node_id *node,
|
|
|
|
const struct short_channel_id *scid_or_alias,
|
|
|
|
const struct amount_msat *amount)
|
2022-03-22 23:59:20 +01:00
|
|
|
{
|
|
|
|
struct channel *channel;
|
2023-07-23 16:58:58 +02:00
|
|
|
struct short_channel_id *scid;
|
2022-03-22 23:59:20 +01:00
|
|
|
struct peer *peer = peer_by_id(ld, node);
|
|
|
|
if (!peer)
|
|
|
|
return NULL;
|
|
|
|
|
2022-12-23 00:52:55 +01:00
|
|
|
channel = find_channel_by_scid(peer, scid_or_alias);
|
2023-07-23 16:58:58 +02:00
|
|
|
if (channel && channel_can_add_htlc(channel)) {
|
|
|
|
goto found;
|
|
|
|
}
|
2022-12-23 00:52:55 +01:00
|
|
|
|
|
|
|
channel = find_channel_by_alias(peer, scid_or_alias, LOCAL);
|
2023-07-23 16:58:58 +02:00
|
|
|
if (channel && channel_can_add_htlc(channel)) {
|
|
|
|
goto found;
|
|
|
|
}
|
2022-08-08 23:51:36 +02:00
|
|
|
|
|
|
|
/* We used to ignore scid: now all-zero means "any" */
|
2023-07-23 16:58:58 +02:00
|
|
|
if (!channel && (ld->deprecated_apis ||
|
|
|
|
memeqzero(scid_or_alias, sizeof(*scid_or_alias)))) {
|
2022-08-08 23:51:36 +02:00
|
|
|
list_for_each(&peer->channels, channel, list) {
|
2023-07-23 16:58:58 +02:00
|
|
|
if (channel_can_add_htlc(channel) &&
|
|
|
|
amount_msat_greater(channel->our_msat, *amount)) {
|
|
|
|
goto found;
|
2022-08-08 23:51:36 +02:00
|
|
|
}
|
2022-03-22 23:59:20 +01:00
|
|
|
}
|
|
|
|
}
|
2023-07-23 16:58:58 +02:00
|
|
|
|
|
|
|
log_debug(ld->log, "No channel found for selector %s (%s)",
|
|
|
|
short_channel_id_to_str(tmpctx, scid_or_alias),
|
|
|
|
type_to_string(tmpctx, struct amount_msat, amount));
|
2022-03-22 23:59:20 +01:00
|
|
|
return NULL;
|
2023-07-23 16:58:58 +02:00
|
|
|
|
|
|
|
found:
|
|
|
|
scid = channel->scid ? channel->scid : channel->alias[LOCAL];
|
|
|
|
log_debug(
|
|
|
|
ld->log, "Selected channel %s (%s) for selector %s (%s)",
|
|
|
|
short_channel_id_to_str(tmpctx, scid),
|
|
|
|
type_to_string(tmpctx, struct amount_msat, &channel->our_msat),
|
|
|
|
short_channel_id_to_str(tmpctx, scid_or_alias),
|
|
|
|
type_to_string(tmpctx, struct amount_msat, amount));
|
|
|
|
|
|
|
|
return channel;
|
2022-03-22 23:59:20 +01:00
|
|
|
}
|
|
|
|
|
2019-12-12 00:50:22 +01:00
|
|
|
/* destination/route_channels/route_nodes are NULL (and path_secrets may be NULL)
|
|
|
|
* if we're sending a raw onion. */
|
2019-01-18 07:37:06 +01:00
|
|
|
static struct command_result *
|
2019-12-12 00:50:22 +01:00
|
|
|
send_payment_core(struct lightningd *ld,
|
|
|
|
struct command *cmd,
|
|
|
|
const struct sha256 *rhash,
|
|
|
|
u64 partid,
|
2021-09-28 18:05:33 +02:00
|
|
|
u64 group,
|
2019-12-12 00:50:22 +01:00
|
|
|
const struct route_hop *first_hop,
|
|
|
|
struct amount_msat msat,
|
|
|
|
struct amount_msat total_msat,
|
|
|
|
const char *label TAKES,
|
2020-12-14 02:24:37 +01:00
|
|
|
const char *invstring TAKES,
|
2022-04-02 04:33:35 +02:00
|
|
|
const char *description TAKES,
|
2019-12-12 00:50:22 +01:00
|
|
|
const struct onionpacket *packet,
|
|
|
|
const struct node_id *destination,
|
|
|
|
struct node_id *route_nodes TAKES,
|
|
|
|
struct short_channel_id *route_channels TAKES,
|
2020-12-14 02:20:44 +01:00
|
|
|
struct secret *path_secrets,
|
2022-11-09 03:32:01 +01:00
|
|
|
const struct sha256 *local_invreq_id)
|
2017-04-01 15:05:29 +02:00
|
|
|
{
|
2019-12-12 00:50:24 +01:00
|
|
|
const struct wallet_payment **payments, *old_payment = NULL;
|
2019-12-12 00:50:22 +01:00
|
|
|
struct channel *channel;
|
2020-02-21 06:10:02 +01:00
|
|
|
const u8 *failmsg;
|
2018-01-17 21:29:49 +01:00
|
|
|
struct htlc_out *hout;
|
2018-02-03 01:05:17 +01:00
|
|
|
struct routing_failure *fail;
|
2019-12-12 00:50:24 +01:00
|
|
|
struct amount_msat msat_already_pending = AMOUNT_MSAT(0);
|
2020-07-21 06:20:39 +02:00
|
|
|
bool have_complete = false;
|
2022-11-09 03:32:01 +01:00
|
|
|
struct command_result *invreq_err;
|
2019-12-12 00:50:24 +01:00
|
|
|
|
|
|
|
/* Now, do we already have one or more payments? */
|
wallet: simplify payments lookup so sqlite3 uses index.
Filtering by status is rare, so we can do it in the caller; just let sqlite3
filter by payment_hash.
With ~650,000 payments in db:
Before:
```
129/300000 complete 5.60/sec (33078 invs, 169 pays, 0 retries) in 30 seconds. 19 hours-14 hours remaining.
201/300000 complete 7.20/sec (43519 invs, 241 pays, 0 retries) in 40 seconds. 16 hours-11 hours remaining.
257/300000 complete 5.60/sec (54568 invs, 289 pays, 0 retries) in 50 seconds. 16 hours-14 hours remaining.
305/300000 complete 4.80/sec (65772 invs, 337 pays, 0 retries) in 60 seconds. 16 hours-17 hours remaining.
361/300000 complete 5.60/sec (75875 invs, 401 pays, 0 retries) in 70 seconds. 16 hours-14 hours remaining.
```
After:
```
760/300000 complete 40.00/sec (19955 invs, 824 pays, 0 retries) in 20 seconds. 2 hours-2 hours remaining.
1176/300000 complete 41.60/sec (30082 invs, 1224 pays, 0 retries) in 30 seconds. 2 hours-119 minutes remaining.
1584/300000 complete 40.80/sec (40224 invs, 1640 pays, 0 retries) in 40 seconds. 2 hours-2 hours remaining.
1984/300000 complete 40.00/sec (49938 invs, 2048 pays, 0 retries) in 50 seconds. 2 hours-2 hours remaining.
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2022-09-19 02:49:53 +02:00
|
|
|
payments = wallet_payment_list(tmpctx, ld->wallet, rhash);
|
2019-12-12 00:50:24 +01:00
|
|
|
for (size_t i = 0; i < tal_count(payments); i++) {
|
|
|
|
log_debug(ld->log, "Payment %zu/%zu: %s %s",
|
|
|
|
i, tal_count(payments),
|
|
|
|
type_to_string(tmpctx, struct amount_msat,
|
|
|
|
&payments[i]->msatoshi),
|
|
|
|
payments[i]->status == PAYMENT_COMPLETE ? "COMPLETE"
|
|
|
|
: payments[i]->status == PAYMENT_PENDING ? "PENDING"
|
|
|
|
: "FAILED");
|
|
|
|
|
|
|
|
switch (payments[i]->status) {
|
|
|
|
case PAYMENT_COMPLETE:
|
2020-07-21 06:20:39 +02:00
|
|
|
have_complete = true;
|
2019-12-12 00:50:24 +01:00
|
|
|
if (payments[i]->partid != partid)
|
|
|
|
continue;
|
2019-02-14 16:59:17 +01:00
|
|
|
|
2017-04-01 15:05:29 +02:00
|
|
|
/* Must match successful payment parameters. */
|
2019-12-12 00:50:24 +01:00
|
|
|
if (!amount_msat_eq(payments[i]->msatoshi, msat)) {
|
2019-01-18 07:37:06 +01:00
|
|
|
return command_fail(cmd, PAY_RHASH_ALREADY_USED,
|
2018-02-14 01:09:23 +01:00
|
|
|
"Already succeeded "
|
2020-07-21 06:20:39 +02:00
|
|
|
"with amount %s (not %s)",
|
2019-02-21 04:45:55 +01:00
|
|
|
type_to_string(tmpctx,
|
|
|
|
struct amount_msat,
|
2020-07-21 06:20:39 +02:00
|
|
|
&payments[i]->msatoshi),
|
|
|
|
type_to_string(tmpctx,
|
|
|
|
struct amount_msat, &msat));
|
2017-04-01 15:05:29 +02:00
|
|
|
}
|
2019-12-12 00:50:24 +01:00
|
|
|
if (payments[i]->destination && destination
|
|
|
|
&& !node_id_eq(payments[i]->destination,
|
|
|
|
destination)) {
|
2019-01-18 07:37:06 +01:00
|
|
|
return command_fail(cmd, PAY_RHASH_ALREADY_USED,
|
2018-02-14 01:09:23 +01:00
|
|
|
"Already succeeded to %s",
|
|
|
|
type_to_string(tmpctx,
|
2019-04-08 11:58:32 +02:00
|
|
|
struct node_id,
|
2019-12-12 00:50:24 +01:00
|
|
|
payments[i]->destination));
|
2017-04-01 15:05:29 +02:00
|
|
|
}
|
2019-12-12 00:50:24 +01:00
|
|
|
return sendpay_success(cmd, payments[i]);
|
|
|
|
|
|
|
|
case PAYMENT_PENDING:
|
2021-10-04 14:25:34 +02:00
|
|
|
/* At most one payment group can be in-flight at any
|
|
|
|
* time. */
|
|
|
|
if (payments[i]->groupid != group) {
|
|
|
|
return command_fail(
|
|
|
|
cmd, PAY_IN_PROGRESS,
|
|
|
|
"Payment with groupid=%" PRIu64
|
|
|
|
" still in progress, cannot retry before "
|
|
|
|
"that completes.",
|
|
|
|
payments[i]->groupid);
|
|
|
|
}
|
|
|
|
|
2019-12-12 00:50:24 +01:00
|
|
|
/* Can't mix non-parallel and parallel payments! */
|
|
|
|
if (!payments[i]->partid != !partid) {
|
|
|
|
return command_fail(cmd, PAY_IN_PROGRESS,
|
|
|
|
"Already have %s payment in progress",
|
|
|
|
payments[i]->partid ? "parallel" : "non-parallel");
|
|
|
|
}
|
2021-10-04 14:25:34 +02:00
|
|
|
|
2020-07-21 06:22:31 +02:00
|
|
|
if (payments[i]->partid == partid) {
|
|
|
|
/* You can't change details while it's pending */
|
|
|
|
if (!amount_msat_eq(payments[i]->msatoshi, msat)) {
|
|
|
|
return command_fail(cmd, PAY_RHASH_ALREADY_USED,
|
|
|
|
"Already pending "
|
|
|
|
"with amount %s (not %s)",
|
|
|
|
type_to_string(tmpctx,
|
|
|
|
struct amount_msat,
|
|
|
|
&payments[i]->msatoshi),
|
|
|
|
type_to_string(tmpctx,
|
|
|
|
struct amount_msat, &msat));
|
|
|
|
}
|
|
|
|
if (payments[i]->destination && destination
|
|
|
|
&& !node_id_eq(payments[i]->destination,
|
|
|
|
destination)) {
|
|
|
|
return command_fail(cmd, PAY_RHASH_ALREADY_USED,
|
|
|
|
"Already pending to %s",
|
|
|
|
type_to_string(tmpctx,
|
|
|
|
struct node_id,
|
|
|
|
payments[i]->destination));
|
|
|
|
}
|
2019-12-12 00:50:24 +01:00
|
|
|
return json_sendpay_in_progress(cmd, payments[i]);
|
2020-07-21 06:22:31 +02:00
|
|
|
}
|
2019-12-12 00:50:24 +01:00
|
|
|
/* You shouldn't change your mind about amount being
|
|
|
|
* sent, since we'll use it in onion! */
|
|
|
|
else if (!amount_msat_eq(payments[i]->total_msat,
|
|
|
|
total_msat))
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"msatoshi was previously %s, now %s",
|
|
|
|
type_to_string(tmpctx,
|
|
|
|
struct amount_msat,
|
|
|
|
&payments[i]->total_msat),
|
|
|
|
type_to_string(tmpctx,
|
|
|
|
struct amount_msat,
|
|
|
|
&total_msat));
|
|
|
|
|
|
|
|
|
|
|
|
if (!amount_msat_add(&msat_already_pending,
|
|
|
|
msat_already_pending,
|
|
|
|
payments[i]->msatoshi)) {
|
|
|
|
return command_fail(cmd, LIGHTNINGD,
|
|
|
|
"Internal amount overflow!"
|
|
|
|
" %s + %s in %zu/%zu",
|
|
|
|
type_to_string(tmpctx,
|
|
|
|
struct amount_msat,
|
|
|
|
&msat_already_pending),
|
|
|
|
type_to_string(tmpctx,
|
|
|
|
struct amount_msat,
|
|
|
|
&payments[i]->msatoshi),
|
|
|
|
i, tal_count(payments));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PAYMENT_FAILED:
|
|
|
|
if (payments[i]->partid == partid)
|
|
|
|
old_payment = payments[i];
|
|
|
|
}
|
2021-10-04 19:27:30 +02:00
|
|
|
/* There is no way for us to add a payment with the
|
|
|
|
* same (payment_hash, partid, groupid) tuple since
|
|
|
|
* it'd collide with the database primary key. So
|
|
|
|
* report this as soon as possible. */
|
|
|
|
|
|
|
|
if (payments[i]->partid == partid && payments[i]->groupid == group) {
|
|
|
|
return command_fail(
|
|
|
|
cmd, PAY_RHASH_ALREADY_USED,
|
|
|
|
"There already is a payment with payment_hash=%s, "
|
|
|
|
"groupid=%" PRIu64 ", partid=%" PRIu64
|
|
|
|
". Either change the partid, or wait for the "
|
|
|
|
"payment to complete and start a new group.",
|
|
|
|
type_to_string(tmpctx, struct sha256, rhash), group,
|
|
|
|
partid);
|
|
|
|
}
|
|
|
|
|
2019-12-12 00:50:24 +01:00
|
|
|
}
|
|
|
|
|
2020-07-21 06:20:39 +02:00
|
|
|
/* If any part has succeeded, you can't start a new one! */
|
|
|
|
if (have_complete) {
|
|
|
|
return command_fail(cmd, PAY_RHASH_ALREADY_USED,
|
|
|
|
"Already succeeded other parts");
|
|
|
|
}
|
|
|
|
|
2020-01-31 02:40:36 +01:00
|
|
|
/* BOLT #4:
|
2019-12-12 00:50:24 +01:00
|
|
|
*
|
2023-04-07 08:55:26 +02:00
|
|
|
* - MUST NOT send another HTLC if the total `amt_to_forward` of the HTLC
|
2019-12-12 00:50:24 +01:00
|
|
|
* set is already greater or equal to `total_msat`.
|
|
|
|
*/
|
|
|
|
/* We don't do this for single 0-value payments (sendonion does this) */
|
|
|
|
if (!amount_msat_eq(total_msat, AMOUNT_MSAT(0))
|
|
|
|
&& amount_msat_greater_eq(msat_already_pending, total_msat)) {
|
|
|
|
return command_fail(cmd, PAY_IN_PROGRESS,
|
|
|
|
"Already have %s of %s payments in progress",
|
|
|
|
type_to_string(tmpctx, struct amount_msat,
|
|
|
|
&msat_already_pending),
|
|
|
|
type_to_string(tmpctx, struct amount_msat,
|
|
|
|
&total_msat));
|
2017-04-01 15:05:29 +02:00
|
|
|
}
|
|
|
|
|
2022-11-09 03:32:01 +01:00
|
|
|
invreq_err = check_invoice_request_usage(cmd, local_invreq_id);
|
|
|
|
if (invreq_err)
|
|
|
|
return invreq_err;
|
2020-12-14 02:20:44 +01:00
|
|
|
|
2022-08-08 23:51:36 +02:00
|
|
|
channel = find_channel_for_htlc_add(ld, &first_hop->node_id,
|
2023-07-23 16:58:58 +02:00
|
|
|
&first_hop->scid, &msat);
|
2022-03-22 23:59:20 +01:00
|
|
|
if (!channel) {
|
2019-01-18 07:37:06 +01:00
|
|
|
struct json_stream *data
|
|
|
|
= json_stream_fail(cmd, PAY_TRY_OTHER_ROUTE,
|
|
|
|
"No connection to first "
|
|
|
|
"peer found");
|
|
|
|
|
|
|
|
json_add_routefail_info(data, 0, WIRE_UNKNOWN_NEXT_PEER,
|
2021-06-04 03:18:10 +02:00
|
|
|
&ld->id, NULL,
|
2021-05-22 09:10:00 +02:00
|
|
|
node_id_idx(&ld->id,
|
|
|
|
&first_hop->node_id),
|
2019-08-28 06:08:26 +02:00
|
|
|
NULL);
|
2019-06-12 02:38:54 +02:00
|
|
|
json_object_end(data);
|
2019-01-18 07:37:06 +01:00
|
|
|
return command_failed(cmd, data);
|
2017-04-01 15:05:29 +02:00
|
|
|
}
|
|
|
|
|
2021-12-07 21:09:28 +01:00
|
|
|
failmsg = send_onion(tmpctx, ld, packet, first_hop, msat,
|
|
|
|
rhash, NULL, partid,
|
2021-09-29 12:33:57 +02:00
|
|
|
group, channel, &hout);
|
2018-01-17 21:29:49 +01:00
|
|
|
|
2020-02-21 06:10:02 +01:00
|
|
|
if (failmsg) {
|
2022-05-31 23:42:23 +02:00
|
|
|
fail = immediate_routing_failure(
|
|
|
|
cmd, ld, fromwire_peektype(failmsg),
|
|
|
|
channel_scid_or_local_alias(channel),
|
|
|
|
&channel->peer->id);
|
2018-02-03 01:05:17 +01:00
|
|
|
|
2020-01-07 17:05:07 +01:00
|
|
|
return sendpay_fail(
|
|
|
|
cmd, old_payment, PAY_TRY_OTHER_ROUTE, NULL, fail,
|
|
|
|
sendpay_errmsg_fmt(tmpctx, PAY_TRY_OTHER_ROUTE, fail,
|
|
|
|
"First peer not ready"));
|
2017-06-20 08:14:03 +02:00
|
|
|
}
|
2018-01-17 21:29:49 +01:00
|
|
|
|
2021-05-31 12:41:45 +02:00
|
|
|
/* If we're retrying we delete outgoing HTLC otherwise it gets
|
|
|
|
* reported to onchaind as a possibility, and we end up in
|
|
|
|
* handle_missing_htlc_output -> onchain_failed_our_htlc ->
|
|
|
|
* payment_failed with no payment.
|
2018-03-16 01:09:37 +01:00
|
|
|
*/
|
2019-12-12 00:50:24 +01:00
|
|
|
if (old_payment) {
|
2019-12-12 00:16:23 +01:00
|
|
|
wallet_local_htlc_out_delete(ld->wallet, channel, rhash,
|
2019-12-12 00:50:24 +01:00
|
|
|
partid);
|
2018-03-16 01:09:37 +01:00
|
|
|
}
|
|
|
|
|
2018-01-17 21:29:49 +01:00
|
|
|
/* If hout fails, payment should be freed too. */
|
2019-12-12 00:50:24 +01:00
|
|
|
struct wallet_payment *payment = tal(hout, struct wallet_payment);
|
2018-01-17 21:29:49 +01:00
|
|
|
payment->id = 0;
|
|
|
|
payment->payment_hash = *rhash;
|
2019-12-12 00:16:23 +01:00
|
|
|
payment->partid = partid;
|
2021-09-28 18:05:33 +02:00
|
|
|
payment->groupid = group;
|
2021-12-28 00:21:09 +01:00
|
|
|
payment->destination = tal_dup_or_null(payment, struct node_id,
|
|
|
|
destination);
|
2018-01-17 21:29:49 +01:00
|
|
|
payment->status = PAYMENT_PENDING;
|
2019-02-21 04:45:55 +01:00
|
|
|
payment->msatoshi = msat;
|
2019-12-12 00:50:22 +01:00
|
|
|
payment->msatoshi_sent = first_hop->amount;
|
2019-12-12 00:16:23 +01:00
|
|
|
payment->total_msat = total_msat;
|
2018-01-17 21:29:49 +01:00
|
|
|
payment->timestamp = time_now().ts.tv_sec;
|
|
|
|
payment->payment_preimage = NULL;
|
|
|
|
payment->path_secrets = tal_steal(payment, path_secrets);
|
2021-12-28 00:21:09 +01:00
|
|
|
payment->route_nodes = tal_steal(payment, route_nodes);
|
|
|
|
payment->route_channels = tal_steal(payment, route_channels);
|
2019-11-11 19:38:27 +01:00
|
|
|
payment->failonion = NULL;
|
2022-06-09 18:36:00 +02:00
|
|
|
payment->completed_at = NULL;
|
|
|
|
|
2019-02-23 04:11:14 +01:00
|
|
|
if (label != NULL)
|
|
|
|
payment->label = tal_strdup(payment, label);
|
2018-07-23 13:59:54 +02:00
|
|
|
else
|
2019-02-23 04:11:14 +01:00
|
|
|
payment->label = NULL;
|
2020-12-14 02:24:37 +01:00
|
|
|
if (invstring != NULL)
|
|
|
|
payment->invstring = tal_strdup(payment, invstring);
|
2019-02-23 04:11:12 +01:00
|
|
|
else
|
2020-12-14 02:24:37 +01:00
|
|
|
payment->invstring = NULL;
|
2022-04-02 04:33:35 +02:00
|
|
|
if (description != NULL)
|
|
|
|
payment->description = tal_strdup(payment, description);
|
|
|
|
else
|
|
|
|
payment->description = NULL;
|
2022-11-09 03:32:01 +01:00
|
|
|
payment->local_invreq_id = tal_dup_or_null(payment, struct sha256,
|
|
|
|
local_invreq_id);
|
2018-01-17 21:29:49 +01:00
|
|
|
|
|
|
|
/* We write this into db when HTLC is actually sent. */
|
2018-02-14 01:09:23 +01:00
|
|
|
wallet_payment_setup(ld->wallet, payment);
|
2018-01-17 21:29:49 +01:00
|
|
|
|
2021-09-29 19:10:12 +02:00
|
|
|
add_sendpay_waiter(ld, cmd, rhash, partid, group);
|
2019-12-12 00:50:22 +01:00
|
|
|
return command_still_pending(cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct command_result *
|
|
|
|
send_payment(struct lightningd *ld,
|
|
|
|
struct command *cmd,
|
|
|
|
const struct sha256 *rhash,
|
|
|
|
u64 partid,
|
2021-09-28 18:05:33 +02:00
|
|
|
u64 group,
|
2019-12-12 00:50:22 +01:00
|
|
|
const struct route_hop *route,
|
|
|
|
struct amount_msat msat,
|
|
|
|
struct amount_msat total_msat,
|
|
|
|
const char *label TAKES,
|
2020-12-14 02:24:37 +01:00
|
|
|
const char *invstring TAKES,
|
2022-04-02 04:33:35 +02:00
|
|
|
const char *description TAKES,
|
2022-11-09 03:32:01 +01:00
|
|
|
const struct sha256 *local_invreq_id,
|
2022-03-31 11:10:50 +02:00
|
|
|
const struct secret *payment_secret,
|
|
|
|
const u8 *payment_metadata)
|
2019-12-12 00:50:22 +01:00
|
|
|
{
|
|
|
|
unsigned int base_expiry;
|
|
|
|
struct onionpacket *packet;
|
|
|
|
struct secret *path_secrets;
|
|
|
|
size_t i, n_hops = tal_count(route);
|
|
|
|
struct node_id *ids = tal_arr(tmpctx, struct node_id, n_hops);
|
|
|
|
struct short_channel_id *channels;
|
|
|
|
struct sphinx_path *path;
|
|
|
|
struct pubkey pubkey;
|
2022-03-08 21:22:22 +01:00
|
|
|
bool ret;
|
2019-12-12 00:50:22 +01:00
|
|
|
u8 *onion;
|
|
|
|
|
|
|
|
/* Expiry for HTLCs is absolute. And add one to give some margin. */
|
|
|
|
base_expiry = get_block_height(ld->topology) + 1;
|
|
|
|
|
|
|
|
path = sphinx_path_new(tmpctx, rhash->u.u8);
|
|
|
|
/* Extract IDs for each hop: create_onionpacket wants array. */
|
|
|
|
for (i = 0; i < n_hops; i++)
|
2021-05-22 09:10:00 +02:00
|
|
|
ids[i] = route[i].node_id;
|
2019-12-12 00:50:22 +01:00
|
|
|
|
|
|
|
/* Create sphinx path */
|
|
|
|
for (i = 0; i < n_hops - 1; i++) {
|
|
|
|
ret = pubkey_from_node_id(&pubkey, &ids[i]);
|
|
|
|
assert(ret);
|
|
|
|
|
2022-09-28 06:49:37 +02:00
|
|
|
sphinx_add_hop_has_length(path, &pubkey,
|
2019-12-12 00:50:22 +01:00
|
|
|
take(onion_nonfinal_hop(NULL,
|
2021-05-22 09:10:00 +02:00
|
|
|
&route[i + 1].scid,
|
2019-12-12 00:50:22 +01:00
|
|
|
route[i + 1].amount,
|
2022-10-17 02:44:39 +02:00
|
|
|
base_expiry + route[i + 1].delay)));
|
2019-12-12 00:50:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* And finally set the final hop to the special values in
|
|
|
|
* BOLT04 */
|
|
|
|
ret = pubkey_from_node_id(&pubkey, &ids[i]);
|
|
|
|
assert(ret);
|
|
|
|
|
|
|
|
onion = onion_final_hop(cmd,
|
|
|
|
route[i].amount,
|
|
|
|
base_expiry + route[i].delay,
|
2022-10-17 02:44:39 +02:00
|
|
|
total_msat,
|
2022-03-31 11:10:50 +02:00
|
|
|
payment_secret, payment_metadata);
|
2019-12-12 00:50:22 +01:00
|
|
|
if (!onion) {
|
|
|
|
return command_fail(cmd, PAY_DESTINATION_PERM_FAIL,
|
|
|
|
"Destination does not support"
|
|
|
|
" payment_secret");
|
|
|
|
}
|
2022-09-28 06:49:37 +02:00
|
|
|
sphinx_add_hop_has_length(path, &pubkey, onion);
|
2019-12-12 00:50:22 +01:00
|
|
|
|
|
|
|
/* Copy channels used along the route. */
|
|
|
|
channels = tal_arr(tmpctx, struct short_channel_id, n_hops);
|
|
|
|
for (i = 0; i < n_hops; ++i)
|
2021-05-22 09:10:00 +02:00
|
|
|
channels[i] = route[i].scid;
|
2019-12-12 00:50:22 +01:00
|
|
|
|
|
|
|
log_info(ld->log, "Sending %s over %zu hops to deliver %s",
|
|
|
|
type_to_string(tmpctx, struct amount_msat, &route[0].amount),
|
|
|
|
n_hops, type_to_string(tmpctx, struct amount_msat, &msat));
|
2020-12-08 11:35:32 +01:00
|
|
|
packet = create_onionpacket(tmpctx, path, ROUTING_INFO_SIZE, &path_secrets);
|
2021-09-28 18:05:33 +02:00
|
|
|
return send_payment_core(ld, cmd, rhash, partid, group, &route[0],
|
2021-12-07 21:09:28 +01:00
|
|
|
msat, total_msat,
|
2022-04-02 04:33:35 +02:00
|
|
|
label, invstring, description,
|
2019-12-12 00:50:22 +01:00
|
|
|
packet, &ids[n_hops - 1], ids,
|
2022-11-09 03:32:01 +01:00
|
|
|
channels, path_secrets, local_invreq_id);
|
2017-04-01 15:05:29 +02:00
|
|
|
}
|
|
|
|
|
2019-11-07 23:14:47 +01:00
|
|
|
static struct command_result *
|
2019-11-08 12:55:43 +01:00
|
|
|
param_route_hop(struct command *cmd, const char *name, const char *buffer,
|
|
|
|
const jsmntok_t *tok, struct route_hop **hop)
|
|
|
|
{
|
2021-05-16 17:56:34 +02:00
|
|
|
const jsmntok_t *idtok, *channeltok, *amounttok, *delaytok;
|
2019-11-08 12:55:43 +01:00
|
|
|
struct route_hop *res;
|
|
|
|
|
|
|
|
res = tal(cmd, struct route_hop);
|
|
|
|
idtok = json_get_member(buffer, tok, "id");
|
|
|
|
channeltok = json_get_member(buffer, tok, "channel");
|
|
|
|
amounttok = json_get_member(buffer, tok, "amount_msat");
|
|
|
|
delaytok = json_get_member(buffer, tok, "delay");
|
|
|
|
|
|
|
|
/* General verification that all fields that we need are present. */
|
|
|
|
if (!idtok && !channeltok)
|
|
|
|
return command_fail(
|
|
|
|
cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"Either 'id' or 'channel' is required for a route_hop");
|
|
|
|
|
|
|
|
if (!amounttok)
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"'amount_msat' is required");
|
|
|
|
|
|
|
|
if (!delaytok)
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"'delay' is required");
|
|
|
|
|
|
|
|
/* Parsing of actual values including sanity check for all parsed
|
|
|
|
* values. */
|
|
|
|
if (!idtok) {
|
2021-05-22 09:10:00 +02:00
|
|
|
memset(&res->node_id, 0, sizeof(struct node_id));
|
|
|
|
} else if (!json_to_node_id(buffer, idtok, &res->node_id)) {
|
2020-08-25 23:20:50 +02:00
|
|
|
return command_fail_badparam(cmd, name, buffer, idtok,
|
|
|
|
"should be a node_id");
|
2019-11-08 12:55:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!channeltok) {
|
2021-05-22 09:10:00 +02:00
|
|
|
memset(&res->scid, 0, sizeof(struct short_channel_id));
|
|
|
|
} else if (!json_to_short_channel_id(buffer, channeltok, &res->scid)) {
|
2020-08-25 23:20:50 +02:00
|
|
|
return command_fail_badparam(cmd, name, buffer, channeltok,
|
|
|
|
"should be a short_channel_id");
|
2019-11-08 12:55:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!json_to_msat(buffer, amounttok, &res->amount))
|
2020-08-25 23:20:50 +02:00
|
|
|
return command_fail_badparam(cmd, name, buffer, amounttok,
|
|
|
|
"should be a valid amount_msat");
|
2019-11-08 12:55:43 +01:00
|
|
|
|
|
|
|
if (!json_to_number(buffer, delaytok, &res->delay) || res->delay < 1)
|
2020-08-25 23:20:50 +02:00
|
|
|
return command_fail_badparam(cmd, name, buffer, delaytok,
|
|
|
|
"should be a positive, non-zero, number");
|
2019-11-08 12:55:43 +01:00
|
|
|
|
|
|
|
*hop = res;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-11-07 23:14:47 +01:00
|
|
|
static struct command_result *json_sendonion(struct command *cmd,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *obj UNNEEDED,
|
|
|
|
const jsmntok_t *params)
|
|
|
|
{
|
|
|
|
u8 *onion;
|
2020-12-08 11:35:32 +01:00
|
|
|
struct onionpacket *packet;
|
2020-08-31 03:13:25 +02:00
|
|
|
enum onion_wire failcode;
|
2019-11-07 23:14:47 +01:00
|
|
|
struct route_hop *first_hop;
|
|
|
|
struct sha256 *payment_hash;
|
|
|
|
struct lightningd *ld = cmd->ld;
|
2022-04-02 04:33:35 +02:00
|
|
|
const char *label, *invstring, *description;
|
2020-07-31 17:19:22 +02:00
|
|
|
struct node_id *destination;
|
2019-11-11 17:18:10 +01:00
|
|
|
struct secret *path_secrets;
|
2020-07-27 16:01:49 +02:00
|
|
|
struct amount_msat *msat;
|
2021-09-28 18:05:33 +02:00
|
|
|
u64 *partid, *group;
|
2022-11-09 03:32:01 +01:00
|
|
|
struct sha256 *local_invreq_id = NULL;
|
2019-11-07 23:14:47 +01:00
|
|
|
|
|
|
|
if (!param(cmd, buffer, params,
|
|
|
|
p_req("onion", param_bin_from_hex, &onion),
|
|
|
|
p_req("first_hop", param_route_hop, &first_hop),
|
|
|
|
p_req("payment_hash", param_sha256, &payment_hash),
|
|
|
|
p_opt("label", param_escaped_string, &label),
|
2019-11-25 13:42:23 +01:00
|
|
|
p_opt("shared_secrets", param_secrets_array, &path_secrets),
|
2019-12-12 00:51:36 +01:00
|
|
|
p_opt_def("partid", param_u64, &partid, 0),
|
2020-12-15 00:43:42 +01:00
|
|
|
/* FIXME: parameter should be invstring now */
|
2020-12-14 02:24:37 +01:00
|
|
|
p_opt("bolt11", param_string, &invstring),
|
lightningd: change `msatoshi` args to `amount_msat`.
This is consistent with our output changes, and increases consistency.
It also keeps future sanity checks happy, that we only use JSON msat
helpers with '_msat' fields.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Changed: JSON-RPC: `invoice`, `sendonion`, `sendpay`, `pay`, `keysend`, `fetchinvoice`, `sendinvoice`: `msatoshi` argument is now called `amount_msat` to match other fields.
Changelog-Deprecated: JSON-RPC: `invoice`, `sendonion`, `sendpay`, `pay`, `keysend`, `fetchinvoice`, `sendinvoice` `msatoshi` (use `amount_msat`)
2022-06-19 09:20:11 +02:00
|
|
|
p_opt_def("amount_msat|msatoshi", param_msat, &msat, AMOUNT_MSAT(0)),
|
2020-07-31 17:19:22 +02:00
|
|
|
p_opt("destination", param_node_id, &destination),
|
2022-11-09 03:32:01 +01:00
|
|
|
p_opt("localinvreqid", param_sha256, &local_invreq_id),
|
2021-10-04 14:28:11 +02:00
|
|
|
p_opt("groupid", param_u64, &group),
|
2022-04-02 04:33:35 +02:00
|
|
|
p_opt("description", param_string, &description),
|
2019-11-07 23:14:47 +01:00
|
|
|
NULL))
|
|
|
|
return command_param_failed();
|
|
|
|
|
2021-10-04 14:28:11 +02:00
|
|
|
/* 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, payment_hash) +
|
|
|
|
1;
|
|
|
|
}
|
|
|
|
|
2020-12-08 11:35:32 +01:00
|
|
|
packet = parse_onionpacket(cmd, onion, tal_bytelen(onion), &failcode);
|
2019-11-07 23:14:47 +01:00
|
|
|
|
2020-12-08 11:35:32 +01:00
|
|
|
if (!packet)
|
2019-11-07 23:14:47 +01:00
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"Could not parse the onion. Parsing failed "
|
|
|
|
"with failcode=%d",
|
|
|
|
failcode);
|
|
|
|
|
2021-09-28 18:05:33 +02:00
|
|
|
return send_payment_core(ld, cmd, payment_hash, *partid, *group,
|
2020-07-27 16:01:49 +02:00
|
|
|
first_hop, *msat, AMOUNT_MSAT(0),
|
2022-04-02 04:33:35 +02:00
|
|
|
label, invstring, description,
|
|
|
|
packet, destination, NULL, NULL,
|
2022-11-09 03:32:01 +01:00
|
|
|
path_secrets, local_invreq_id);
|
2019-11-07 23:14:47 +01:00
|
|
|
}
|
2019-12-12 00:50:22 +01:00
|
|
|
|
2019-11-07 23:14:47 +01:00
|
|
|
static const struct json_command sendonion_command = {
|
|
|
|
"sendonion",
|
|
|
|
"payment",
|
|
|
|
json_sendonion,
|
|
|
|
"Send a payment with a pre-computed onion."
|
|
|
|
};
|
|
|
|
AUTODATA(json_command, &sendonion_command);
|
|
|
|
|
2018-02-14 01:09:23 +01:00
|
|
|
/*-----------------------------------------------------------------------------
|
|
|
|
JSON-RPC sendpay interface
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
|
2022-03-08 21:22:22 +01:00
|
|
|
/* FIXME: We accept his parameter for now, will deprecate eventually */
|
2019-11-14 01:18:53 +01:00
|
|
|
static struct command_result *param_route_hop_style(struct command *cmd,
|
|
|
|
const char *name,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *tok,
|
2022-03-08 21:22:22 +01:00
|
|
|
int **unused)
|
2019-11-14 01:18:53 +01:00
|
|
|
{
|
2022-03-08 21:22:22 +01:00
|
|
|
if (json_tok_streq(buffer, tok, "tlv")) {
|
2019-11-14 01:18:53 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-08-25 23:20:50 +02:00
|
|
|
return command_fail_badparam(cmd, name, buffer, tok,
|
2022-03-08 21:22:22 +01:00
|
|
|
"should be 'tlv' ('legacy' not supported)");
|
2019-11-14 01:18:53 +01:00
|
|
|
}
|
|
|
|
|
2020-04-01 01:51:03 +02:00
|
|
|
static struct command_result *param_route_hops(struct command *cmd,
|
|
|
|
const char *name,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *tok,
|
|
|
|
struct route_hop **hops)
|
2017-10-26 04:56:19 +02:00
|
|
|
{
|
2019-01-17 03:12:13 +01:00
|
|
|
size_t i;
|
2020-04-01 01:51:03 +02:00
|
|
|
const jsmntok_t *t;
|
2017-10-26 04:56:19 +02:00
|
|
|
|
2020-04-01 01:51:03 +02:00
|
|
|
if (tok->type != JSMN_ARRAY || tok->size == 0)
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"%s must be an (non-empty) array", name);
|
2017-10-26 04:56:19 +02:00
|
|
|
|
2020-04-01 01:51:03 +02:00
|
|
|
*hops = tal_arr(cmd, struct route_hop, tok->size);
|
|
|
|
json_for_each_arr(i, t, tok) {
|
2022-06-20 12:22:09 +02:00
|
|
|
struct amount_msat *amount_msat;
|
2019-04-08 11:58:32 +02:00
|
|
|
struct node_id *id;
|
2018-08-23 18:08:32 +02:00
|
|
|
struct short_channel_id *channel;
|
2019-01-15 11:04:07 +01:00
|
|
|
unsigned *delay, *direction;
|
2022-03-08 21:22:22 +01:00
|
|
|
int *ignored;
|
2018-08-23 18:08:32 +02:00
|
|
|
|
|
|
|
if (!param(cmd, buffer, t,
|
2022-07-12 06:47:35 +02:00
|
|
|
/* deprecated: getroute gives both, so we allow both! */
|
|
|
|
p_req_dup_ok("amount_msat|msatoshi", param_msat, &amount_msat),
|
2022-06-20 12:22:09 +02:00
|
|
|
p_req("id", param_node_id, &id),
|
|
|
|
p_req("delay", param_number, &delay),
|
|
|
|
p_req("channel", param_short_channel_id, &channel),
|
2021-05-16 17:56:34 +02:00
|
|
|
/* Allowed (getroute supplies it) but ignored */
|
2019-01-15 11:04:07 +01:00
|
|
|
p_opt("direction", param_number, &direction),
|
2022-03-08 21:22:22 +01:00
|
|
|
p_opt("style", param_route_hop_style, &ignored),
|
2018-08-23 18:08:32 +02:00
|
|
|
NULL))
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_param_failed();
|
2017-10-26 04:56:19 +02:00
|
|
|
|
2022-06-20 12:22:09 +02:00
|
|
|
(*hops)[i].amount = *amount_msat;
|
2021-05-22 09:10:00 +02:00
|
|
|
(*hops)[i].node_id = *id;
|
2020-04-01 01:51:03 +02:00
|
|
|
(*hops)[i].delay = *delay;
|
2021-05-22 09:10:00 +02:00
|
|
|
(*hops)[i].scid = *channel;
|
2017-10-26 04:56:19 +02:00
|
|
|
}
|
|
|
|
|
2020-04-01 01:51:03 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct command_result *json_sendpay(struct command *cmd,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *obj UNNEEDED,
|
|
|
|
const jsmntok_t *params)
|
|
|
|
{
|
|
|
|
struct sha256 *rhash;
|
|
|
|
struct route_hop *route;
|
|
|
|
struct amount_msat *msat;
|
2022-04-02 04:33:35 +02:00
|
|
|
const char *invstring, *label, *description;
|
2021-09-28 18:05:33 +02:00
|
|
|
u64 *partid, *group;
|
2020-04-01 01:51:03 +02:00
|
|
|
struct secret *payment_secret;
|
2022-11-09 03:32:01 +01:00
|
|
|
struct sha256 *local_invreq_id;
|
2022-03-31 11:10:50 +02:00
|
|
|
u8 *payment_metadata;
|
2020-04-01 01:51:03 +02:00
|
|
|
|
|
|
|
/* For generating help, give new-style. */
|
|
|
|
if (!param(cmd, buffer, params,
|
|
|
|
p_req("route", param_route_hops, &route),
|
|
|
|
p_req("payment_hash", param_sha256, &rhash),
|
|
|
|
p_opt("label", param_escaped_string, &label),
|
lightningd: change `msatoshi` args to `amount_msat`.
This is consistent with our output changes, and increases consistency.
It also keeps future sanity checks happy, that we only use JSON msat
helpers with '_msat' fields.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Changed: JSON-RPC: `invoice`, `sendonion`, `sendpay`, `pay`, `keysend`, `fetchinvoice`, `sendinvoice`: `msatoshi` argument is now called `amount_msat` to match other fields.
Changelog-Deprecated: JSON-RPC: `invoice`, `sendonion`, `sendpay`, `pay`, `keysend`, `fetchinvoice`, `sendinvoice` `msatoshi` (use `amount_msat`)
2022-06-19 09:20:11 +02:00
|
|
|
p_opt("amount_msat|msatoshi", param_msat, &msat),
|
2020-12-15 00:43:42 +01:00
|
|
|
/* FIXME: parameter should be invstring now */
|
2023-06-13 12:33:31 +02:00
|
|
|
p_opt("bolt11", param_invstring, &invstring),
|
2020-04-01 01:51:03 +02:00
|
|
|
p_opt("payment_secret", param_secret, &payment_secret),
|
|
|
|
p_opt_def("partid", param_u64, &partid, 0),
|
2022-11-09 03:32:01 +01:00
|
|
|
p_opt("localinvreqid", param_sha256, &local_invreq_id),
|
2021-10-04 14:28:11 +02:00
|
|
|
p_opt("groupid", param_u64, &group),
|
2022-03-31 11:10:50 +02:00
|
|
|
p_opt("payment_metadata", param_bin_from_hex, &payment_metadata),
|
2022-04-02 04:33:35 +02:00
|
|
|
p_opt("description", param_string, &description),
|
2020-04-01 01:51:03 +02:00
|
|
|
NULL))
|
|
|
|
return command_param_failed();
|
|
|
|
|
2019-12-12 00:51:36 +01:00
|
|
|
if (*partid && !msat)
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"Must specify msatoshi with partid");
|
|
|
|
|
2020-04-01 01:51:03 +02:00
|
|
|
const struct amount_msat final_amount = route[tal_count(route)-1].amount;
|
2019-02-21 03:39:21 +01:00
|
|
|
|
2021-10-04 14:28:11 +02:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2020-01-30 03:02:37 +01:00
|
|
|
if (msat && !*partid && !amount_msat_eq(*msat, final_amount))
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"Do not specify msatoshi (%s) without"
|
|
|
|
" partid: if you do, it must be exactly"
|
|
|
|
" the final amount (%s)",
|
|
|
|
type_to_string(tmpctx, struct amount_msat,
|
|
|
|
msat),
|
|
|
|
type_to_string(tmpctx, struct amount_msat,
|
|
|
|
&final_amount));
|
2019-12-12 00:51:36 +01:00
|
|
|
|
2020-01-30 03:02:37 +01:00
|
|
|
/* For MPP, the total we send must *exactly* equal the amount
|
|
|
|
* we promise to send (msatoshi). So no single payment can be
|
|
|
|
* > than that. */
|
|
|
|
if (*partid) {
|
|
|
|
if (amount_msat_greater(final_amount, *msat))
|
2019-02-21 03:39:21 +01:00
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
2020-01-30 03:02:37 +01:00
|
|
|
"Final amount %s is greater than"
|
|
|
|
" %s, despite MPP",
|
2019-02-21 03:39:21 +01:00
|
|
|
type_to_string(tmpctx,
|
|
|
|
struct amount_msat,
|
2020-01-30 03:02:37 +01:00
|
|
|
&final_amount),
|
2019-02-21 03:39:21 +01:00
|
|
|
type_to_string(tmpctx,
|
|
|
|
struct amount_msat,
|
2019-12-12 01:18:27 +01:00
|
|
|
msat));
|
2018-07-20 03:14:02 +02:00
|
|
|
}
|
2018-03-22 13:49:09 +01:00
|
|
|
|
2019-12-12 00:52:28 +01:00
|
|
|
if (*partid && !payment_secret)
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"partid requires payment_secret");
|
|
|
|
|
2021-09-28 18:05:33 +02:00
|
|
|
return send_payment(cmd->ld, cmd, rhash, *partid, *group,
|
2019-12-12 00:50:22 +01:00
|
|
|
route,
|
2020-01-30 03:02:37 +01:00
|
|
|
final_amount,
|
|
|
|
msat ? *msat : final_amount,
|
2022-11-09 03:32:01 +01:00
|
|
|
label, invstring, description, local_invreq_id,
|
2022-03-31 11:10:50 +02:00
|
|
|
payment_secret, payment_metadata);
|
2017-10-26 04:56:19 +02:00
|
|
|
}
|
|
|
|
|
2017-04-01 15:05:29 +02:00
|
|
|
static const struct json_command sendpay_command = {
|
|
|
|
"sendpay",
|
2019-05-22 16:08:16 +02:00
|
|
|
"payment",
|
2017-04-01 15:05:29 +02:00
|
|
|
json_sendpay,
|
2018-03-02 13:54:37 +01:00
|
|
|
"Send along {route} in return for preimage of {payment_hash}"
|
2017-04-01 15:05:29 +02:00
|
|
|
};
|
|
|
|
AUTODATA(json_command, &sendpay_command);
|
2017-10-26 05:07:19 +02:00
|
|
|
|
2018-03-04 06:35:37 +01:00
|
|
|
static void waitsendpay_timeout(struct command *cmd)
|
|
|
|
{
|
2018-12-16 05:53:06 +01:00
|
|
|
was_pending(command_fail(cmd, PAY_IN_PROGRESS,
|
|
|
|
"Timed out while waiting"));
|
2018-03-04 06:35:37 +01:00
|
|
|
}
|
|
|
|
|
2018-12-16 05:52:06 +01:00
|
|
|
static struct command_result *json_waitsendpay(struct command *cmd,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *obj UNNEEDED,
|
|
|
|
const jsmntok_t *params)
|
2018-03-04 06:35:37 +01:00
|
|
|
{
|
2018-08-14 22:16:49 +02:00
|
|
|
struct sha256 *rhash;
|
2018-07-20 03:14:02 +02:00
|
|
|
unsigned int *timeout;
|
2019-01-18 07:37:06 +01:00
|
|
|
struct command_result *res;
|
2021-09-29 19:10:12 +02:00
|
|
|
u64 *partid, *groupid;
|
2018-03-04 06:35:37 +01:00
|
|
|
|
2018-07-20 03:14:02 +02:00
|
|
|
if (!param(cmd, buffer, params,
|
2018-12-16 05:50:06 +01:00
|
|
|
p_req("payment_hash", param_sha256, &rhash),
|
|
|
|
p_opt("timeout", param_number, &timeout),
|
2019-12-12 00:52:32 +01:00
|
|
|
p_opt_def("partid", param_u64, &partid, 0),
|
2021-09-29 19:10:12 +02:00
|
|
|
p_opt("groupid", param_u64, &groupid),
|
2018-07-20 03:14:02 +02:00
|
|
|
NULL))
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_param_failed();
|
2018-03-04 06:35:37 +01:00
|
|
|
|
2021-09-29 19:10:12 +02:00
|
|
|
if (groupid == NULL) {
|
|
|
|
groupid = tal(cmd, u64);
|
|
|
|
*groupid = wallet_payment_get_groupid(cmd->ld->wallet, rhash);
|
|
|
|
}
|
|
|
|
res = wait_payment(cmd->ld, cmd, rhash, *partid, *groupid);
|
2019-01-18 07:37:06 +01:00
|
|
|
if (res)
|
|
|
|
return res;
|
2018-03-04 06:35:37 +01:00
|
|
|
|
2018-07-20 03:14:02 +02:00
|
|
|
if (timeout)
|
2019-06-14 05:49:23 +02:00
|
|
|
new_reltimer(cmd->ld->timers, cmd, time_from_sec(*timeout),
|
2018-03-04 06:35:37 +01:00
|
|
|
&waitsendpay_timeout, cmd);
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_still_pending(cmd);
|
2018-03-04 06:35:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct json_command waitsendpay_command = {
|
|
|
|
"waitsendpay",
|
2019-05-22 16:08:16 +02:00
|
|
|
"payment",
|
2018-03-04 06:35:37 +01:00
|
|
|
json_waitsendpay,
|
|
|
|
"Wait for payment attempt on {payment_hash} to succeed or fail, "
|
|
|
|
"but only up to {timeout} seconds."
|
|
|
|
};
|
|
|
|
AUTODATA(json_command, &waitsendpay_command);
|
|
|
|
|
wallet: simplify payments lookup so sqlite3 uses index.
Filtering by status is rare, so we can do it in the caller; just let sqlite3
filter by payment_hash.
With ~650,000 payments in db:
Before:
```
129/300000 complete 5.60/sec (33078 invs, 169 pays, 0 retries) in 30 seconds. 19 hours-14 hours remaining.
201/300000 complete 7.20/sec (43519 invs, 241 pays, 0 retries) in 40 seconds. 16 hours-11 hours remaining.
257/300000 complete 5.60/sec (54568 invs, 289 pays, 0 retries) in 50 seconds. 16 hours-14 hours remaining.
305/300000 complete 4.80/sec (65772 invs, 337 pays, 0 retries) in 60 seconds. 16 hours-17 hours remaining.
361/300000 complete 5.60/sec (75875 invs, 401 pays, 0 retries) in 70 seconds. 16 hours-14 hours remaining.
```
After:
```
760/300000 complete 40.00/sec (19955 invs, 824 pays, 0 retries) in 20 seconds. 2 hours-2 hours remaining.
1176/300000 complete 41.60/sec (30082 invs, 1224 pays, 0 retries) in 30 seconds. 2 hours-119 minutes remaining.
1584/300000 complete 40.80/sec (40224 invs, 1640 pays, 0 retries) in 40 seconds. 2 hours-2 hours remaining.
1984/300000 complete 40.00/sec (49938 invs, 2048 pays, 0 retries) in 50 seconds. 2 hours-2 hours remaining.
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2022-09-19 02:49:53 +02:00
|
|
|
static struct command_result *param_payment_status(struct command *cmd,
|
|
|
|
const char *name,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *tok,
|
2023-07-11 21:41:17 +02:00
|
|
|
enum payment_status **status)
|
wallet: simplify payments lookup so sqlite3 uses index.
Filtering by status is rare, so we can do it in the caller; just let sqlite3
filter by payment_hash.
With ~650,000 payments in db:
Before:
```
129/300000 complete 5.60/sec (33078 invs, 169 pays, 0 retries) in 30 seconds. 19 hours-14 hours remaining.
201/300000 complete 7.20/sec (43519 invs, 241 pays, 0 retries) in 40 seconds. 16 hours-11 hours remaining.
257/300000 complete 5.60/sec (54568 invs, 289 pays, 0 retries) in 50 seconds. 16 hours-14 hours remaining.
305/300000 complete 4.80/sec (65772 invs, 337 pays, 0 retries) in 60 seconds. 16 hours-17 hours remaining.
361/300000 complete 5.60/sec (75875 invs, 401 pays, 0 retries) in 70 seconds. 16 hours-14 hours remaining.
```
After:
```
760/300000 complete 40.00/sec (19955 invs, 824 pays, 0 retries) in 20 seconds. 2 hours-2 hours remaining.
1176/300000 complete 41.60/sec (30082 invs, 1224 pays, 0 retries) in 30 seconds. 2 hours-119 minutes remaining.
1584/300000 complete 40.80/sec (40224 invs, 1640 pays, 0 retries) in 40 seconds. 2 hours-2 hours remaining.
1984/300000 complete 40.00/sec (49938 invs, 2048 pays, 0 retries) in 50 seconds. 2 hours-2 hours remaining.
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2022-09-19 02:49:53 +02:00
|
|
|
{
|
2023-07-11 21:41:17 +02:00
|
|
|
*status = tal(cmd, enum payment_status);
|
wallet: simplify payments lookup so sqlite3 uses index.
Filtering by status is rare, so we can do it in the caller; just let sqlite3
filter by payment_hash.
With ~650,000 payments in db:
Before:
```
129/300000 complete 5.60/sec (33078 invs, 169 pays, 0 retries) in 30 seconds. 19 hours-14 hours remaining.
201/300000 complete 7.20/sec (43519 invs, 241 pays, 0 retries) in 40 seconds. 16 hours-11 hours remaining.
257/300000 complete 5.60/sec (54568 invs, 289 pays, 0 retries) in 50 seconds. 16 hours-14 hours remaining.
305/300000 complete 4.80/sec (65772 invs, 337 pays, 0 retries) in 60 seconds. 16 hours-17 hours remaining.
361/300000 complete 5.60/sec (75875 invs, 401 pays, 0 retries) in 70 seconds. 16 hours-14 hours remaining.
```
After:
```
760/300000 complete 40.00/sec (19955 invs, 824 pays, 0 retries) in 20 seconds. 2 hours-2 hours remaining.
1176/300000 complete 41.60/sec (30082 invs, 1224 pays, 0 retries) in 30 seconds. 2 hours-119 minutes remaining.
1584/300000 complete 40.80/sec (40224 invs, 1640 pays, 0 retries) in 40 seconds. 2 hours-2 hours remaining.
1984/300000 complete 40.00/sec (49938 invs, 2048 pays, 0 retries) in 50 seconds. 2 hours-2 hours remaining.
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2022-09-19 02:49:53 +02:00
|
|
|
if (string_to_payment_status(buffer + tok->start,
|
|
|
|
tok->end - tok->start,
|
|
|
|
*status))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return command_fail_badparam(cmd, name, buffer, tok,
|
|
|
|
"should be an invoice status");
|
|
|
|
}
|
|
|
|
|
2019-02-23 06:00:04 +01:00
|
|
|
static struct command_result *json_listsendpays(struct command *cmd,
|
2018-12-16 05:52:06 +01:00
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *obj UNNEEDED,
|
|
|
|
const jsmntok_t *params)
|
2017-11-16 19:09:09 +01:00
|
|
|
{
|
|
|
|
const struct wallet_payment **payments;
|
2018-10-19 03:17:49 +02:00
|
|
|
struct json_stream *response;
|
2018-08-30 15:48:36 +02:00
|
|
|
struct sha256 *rhash;
|
wallet: simplify payments lookup so sqlite3 uses index.
Filtering by status is rare, so we can do it in the caller; just let sqlite3
filter by payment_hash.
With ~650,000 payments in db:
Before:
```
129/300000 complete 5.60/sec (33078 invs, 169 pays, 0 retries) in 30 seconds. 19 hours-14 hours remaining.
201/300000 complete 7.20/sec (43519 invs, 241 pays, 0 retries) in 40 seconds. 16 hours-11 hours remaining.
257/300000 complete 5.60/sec (54568 invs, 289 pays, 0 retries) in 50 seconds. 16 hours-14 hours remaining.
305/300000 complete 4.80/sec (65772 invs, 337 pays, 0 retries) in 60 seconds. 16 hours-17 hours remaining.
361/300000 complete 5.60/sec (75875 invs, 401 pays, 0 retries) in 70 seconds. 16 hours-14 hours remaining.
```
After:
```
760/300000 complete 40.00/sec (19955 invs, 824 pays, 0 retries) in 20 seconds. 2 hours-2 hours remaining.
1176/300000 complete 41.60/sec (30082 invs, 1224 pays, 0 retries) in 30 seconds. 2 hours-119 minutes remaining.
1584/300000 complete 40.80/sec (40224 invs, 1640 pays, 0 retries) in 40 seconds. 2 hours-2 hours remaining.
1984/300000 complete 40.00/sec (49938 invs, 2048 pays, 0 retries) in 50 seconds. 2 hours-2 hours remaining.
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2022-09-19 02:49:53 +02:00
|
|
|
const char *invstring;
|
2023-07-11 21:41:17 +02:00
|
|
|
enum payment_status *status;
|
2017-11-16 19:09:09 +01:00
|
|
|
|
2018-07-20 03:14:02 +02:00
|
|
|
if (!param(cmd, buffer, params,
|
2020-12-15 00:43:42 +01:00
|
|
|
/* FIXME: parameter should be invstring now */
|
2020-12-14 02:24:37 +01:00
|
|
|
p_opt("bolt11", param_string, &invstring),
|
2018-12-16 05:50:06 +01:00
|
|
|
p_opt("payment_hash", param_sha256, &rhash),
|
wallet: simplify payments lookup so sqlite3 uses index.
Filtering by status is rare, so we can do it in the caller; just let sqlite3
filter by payment_hash.
With ~650,000 payments in db:
Before:
```
129/300000 complete 5.60/sec (33078 invs, 169 pays, 0 retries) in 30 seconds. 19 hours-14 hours remaining.
201/300000 complete 7.20/sec (43519 invs, 241 pays, 0 retries) in 40 seconds. 16 hours-11 hours remaining.
257/300000 complete 5.60/sec (54568 invs, 289 pays, 0 retries) in 50 seconds. 16 hours-14 hours remaining.
305/300000 complete 4.80/sec (65772 invs, 337 pays, 0 retries) in 60 seconds. 16 hours-17 hours remaining.
361/300000 complete 5.60/sec (75875 invs, 401 pays, 0 retries) in 70 seconds. 16 hours-14 hours remaining.
```
After:
```
760/300000 complete 40.00/sec (19955 invs, 824 pays, 0 retries) in 20 seconds. 2 hours-2 hours remaining.
1176/300000 complete 41.60/sec (30082 invs, 1224 pays, 0 retries) in 30 seconds. 2 hours-119 minutes remaining.
1584/300000 complete 40.80/sec (40224 invs, 1640 pays, 0 retries) in 40 seconds. 2 hours-2 hours remaining.
1984/300000 complete 40.00/sec (49938 invs, 2048 pays, 0 retries) in 50 seconds. 2 hours-2 hours remaining.
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2022-09-19 02:49:53 +02:00
|
|
|
p_opt("status", param_payment_status, &status),
|
2018-07-20 03:14:02 +02:00
|
|
|
NULL))
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_param_failed();
|
2018-01-16 20:44:32 +01:00
|
|
|
|
2020-12-14 02:24:37 +01:00
|
|
|
if (rhash && invstring) {
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"Can only specify one of"
|
|
|
|
" {bolt11} or {payment_hash}");
|
2018-07-23 14:46:03 +02:00
|
|
|
}
|
|
|
|
|
2020-12-14 02:24:37 +01:00
|
|
|
if (invstring) {
|
2018-01-16 20:44:32 +01:00
|
|
|
struct bolt11 *b11;
|
2018-08-30 00:53:00 +02:00
|
|
|
char *fail;
|
2018-01-16 20:44:32 +01:00
|
|
|
|
2020-12-14 02:24:37 +01:00
|
|
|
b11 = bolt11_decode(cmd, invstring, cmd->ld->our_features, NULL,
|
2020-09-23 03:07:19 +02:00
|
|
|
chainparams, &fail);
|
2020-12-14 02:24:37 +01:00
|
|
|
if (b11) {
|
|
|
|
rhash = &b11->payment_hash;
|
|
|
|
} else {
|
|
|
|
struct tlv_invoice *b12;
|
|
|
|
|
|
|
|
b12 = invoice_decode(cmd, invstring, strlen(invstring),
|
|
|
|
cmd->ld->our_features,
|
|
|
|
chainparams, &fail);
|
2022-11-09 03:32:00 +01:00
|
|
|
if (b12 && b12->invoice_payment_hash)
|
|
|
|
rhash = b12->invoice_payment_hash;
|
2020-12-14 02:24:37 +01:00
|
|
|
else
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"Invalid invstring: %s", fail);
|
2018-01-16 20:44:32 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
wallet: simplify payments lookup so sqlite3 uses index.
Filtering by status is rare, so we can do it in the caller; just let sqlite3
filter by payment_hash.
With ~650,000 payments in db:
Before:
```
129/300000 complete 5.60/sec (33078 invs, 169 pays, 0 retries) in 30 seconds. 19 hours-14 hours remaining.
201/300000 complete 7.20/sec (43519 invs, 241 pays, 0 retries) in 40 seconds. 16 hours-11 hours remaining.
257/300000 complete 5.60/sec (54568 invs, 289 pays, 0 retries) in 50 seconds. 16 hours-14 hours remaining.
305/300000 complete 4.80/sec (65772 invs, 337 pays, 0 retries) in 60 seconds. 16 hours-17 hours remaining.
361/300000 complete 5.60/sec (75875 invs, 401 pays, 0 retries) in 70 seconds. 16 hours-14 hours remaining.
```
After:
```
760/300000 complete 40.00/sec (19955 invs, 824 pays, 0 retries) in 20 seconds. 2 hours-2 hours remaining.
1176/300000 complete 41.60/sec (30082 invs, 1224 pays, 0 retries) in 30 seconds. 2 hours-119 minutes remaining.
1584/300000 complete 40.80/sec (40224 invs, 1640 pays, 0 retries) in 40 seconds. 2 hours-2 hours remaining.
1984/300000 complete 40.00/sec (49938 invs, 2048 pays, 0 retries) in 50 seconds. 2 hours-2 hours remaining.
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2022-09-19 02:49:53 +02:00
|
|
|
payments = wallet_payment_list(cmd, cmd->ld->wallet, rhash);
|
2018-10-19 03:17:48 +02:00
|
|
|
response = json_stream_success(cmd);
|
2018-07-23 14:46:03 +02:00
|
|
|
|
2018-01-17 00:53:18 +01:00
|
|
|
json_array_start(response, "payments");
|
2018-07-23 14:46:03 +02:00
|
|
|
for (size_t i = 0; i < tal_count(payments); i++) {
|
wallet: simplify payments lookup so sqlite3 uses index.
Filtering by status is rare, so we can do it in the caller; just let sqlite3
filter by payment_hash.
With ~650,000 payments in db:
Before:
```
129/300000 complete 5.60/sec (33078 invs, 169 pays, 0 retries) in 30 seconds. 19 hours-14 hours remaining.
201/300000 complete 7.20/sec (43519 invs, 241 pays, 0 retries) in 40 seconds. 16 hours-11 hours remaining.
257/300000 complete 5.60/sec (54568 invs, 289 pays, 0 retries) in 50 seconds. 16 hours-14 hours remaining.
305/300000 complete 4.80/sec (65772 invs, 337 pays, 0 retries) in 60 seconds. 16 hours-17 hours remaining.
361/300000 complete 5.60/sec (75875 invs, 401 pays, 0 retries) in 70 seconds. 16 hours-14 hours remaining.
```
After:
```
760/300000 complete 40.00/sec (19955 invs, 824 pays, 0 retries) in 20 seconds. 2 hours-2 hours remaining.
1176/300000 complete 41.60/sec (30082 invs, 1224 pays, 0 retries) in 30 seconds. 2 hours-119 minutes remaining.
1584/300000 complete 40.80/sec (40224 invs, 1640 pays, 0 retries) in 40 seconds. 2 hours-2 hours remaining.
1984/300000 complete 40.00/sec (49938 invs, 2048 pays, 0 retries) in 50 seconds. 2 hours-2 hours remaining.
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2022-09-19 02:49:53 +02:00
|
|
|
if (status && payments[i]->status != *status)
|
|
|
|
continue;
|
2017-11-16 19:09:09 +01:00
|
|
|
json_object_start(response, NULL);
|
2018-03-14 01:30:56 +01:00
|
|
|
json_add_payment_fields(response, payments[i]);
|
2017-11-16 19:09:09 +01:00
|
|
|
json_object_end(response);
|
|
|
|
}
|
|
|
|
json_array_end(response);
|
2018-07-23 14:46:03 +02:00
|
|
|
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_success(cmd, response);
|
2017-11-16 19:09:09 +01:00
|
|
|
}
|
|
|
|
|
2019-02-23 06:00:04 +01:00
|
|
|
static const struct json_command listsendpays_command = {
|
|
|
|
"listsendpays",
|
2019-05-22 16:08:16 +02:00
|
|
|
"payment",
|
2019-02-23 06:00:04 +01:00
|
|
|
json_listsendpays,
|
|
|
|
"Show sendpay, old and current, optionally limiting to {bolt11} or {payment_hash}."
|
|
|
|
};
|
|
|
|
AUTODATA(json_command, &listsendpays_command);
|
2019-11-07 19:06:43 +01:00
|
|
|
|
2022-09-19 02:54:26 +02:00
|
|
|
static struct command_result *
|
|
|
|
param_payment_status_nopending(struct command *cmd,
|
|
|
|
const char *name,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *tok,
|
2023-07-11 21:41:17 +02:00
|
|
|
enum payment_status **status)
|
2022-09-19 02:54:26 +02:00
|
|
|
{
|
|
|
|
struct command_result *res;
|
|
|
|
|
|
|
|
res = param_payment_status(cmd, name, buffer, tok, status);
|
|
|
|
if (res)
|
|
|
|
return res;
|
|
|
|
|
|
|
|
switch (**status) {
|
|
|
|
case PAYMENT_COMPLETE:
|
|
|
|
case PAYMENT_FAILED:
|
|
|
|
break;
|
|
|
|
case PAYMENT_PENDING:
|
|
|
|
return command_fail_badparam(cmd, name, buffer, tok,
|
|
|
|
"Cannot delete pending status");
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2020-08-08 17:47:53 +02:00
|
|
|
|
|
|
|
static struct command_result *json_delpay(struct command *cmd,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *obj UNNEEDED,
|
|
|
|
const jsmntok_t *params)
|
|
|
|
{
|
2023-07-11 21:41:17 +02:00
|
|
|
const enum payment_status *found_status = NULL;
|
2020-08-08 17:47:53 +02:00
|
|
|
struct json_stream *response;
|
|
|
|
const struct wallet_payment **payments;
|
2023-07-11 21:41:17 +02:00
|
|
|
enum payment_status *status;
|
2020-08-08 17:47:53 +02:00
|
|
|
struct sha256 *payment_hash;
|
2022-09-19 02:54:26 +02:00
|
|
|
u64 *groupid, *partid;
|
|
|
|
bool found;
|
2020-08-08 17:47:53 +02:00
|
|
|
|
|
|
|
if (!param(cmd, buffer, params,
|
2022-09-19 02:54:26 +02:00
|
|
|
p_req("payment_hash", param_sha256, &payment_hash),
|
|
|
|
p_req("status", param_payment_status_nopending, &status),
|
|
|
|
p_opt("partid", param_u64, &partid),
|
|
|
|
p_opt("groupid", param_u64, &groupid),
|
|
|
|
NULL))
|
2020-08-08 17:47:53 +02:00
|
|
|
return command_param_failed();
|
|
|
|
|
2022-09-19 02:54:26 +02:00
|
|
|
if ((partid != NULL) != (groupid != NULL))
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"Must set both partid and groupid, or neither");
|
2020-08-08 17:47:53 +02:00
|
|
|
|
wallet: simplify payments lookup so sqlite3 uses index.
Filtering by status is rare, so we can do it in the caller; just let sqlite3
filter by payment_hash.
With ~650,000 payments in db:
Before:
```
129/300000 complete 5.60/sec (33078 invs, 169 pays, 0 retries) in 30 seconds. 19 hours-14 hours remaining.
201/300000 complete 7.20/sec (43519 invs, 241 pays, 0 retries) in 40 seconds. 16 hours-11 hours remaining.
257/300000 complete 5.60/sec (54568 invs, 289 pays, 0 retries) in 50 seconds. 16 hours-14 hours remaining.
305/300000 complete 4.80/sec (65772 invs, 337 pays, 0 retries) in 60 seconds. 16 hours-17 hours remaining.
361/300000 complete 5.60/sec (75875 invs, 401 pays, 0 retries) in 70 seconds. 16 hours-14 hours remaining.
```
After:
```
760/300000 complete 40.00/sec (19955 invs, 824 pays, 0 retries) in 20 seconds. 2 hours-2 hours remaining.
1176/300000 complete 41.60/sec (30082 invs, 1224 pays, 0 retries) in 30 seconds. 2 hours-119 minutes remaining.
1584/300000 complete 40.80/sec (40224 invs, 1640 pays, 0 retries) in 40 seconds. 2 hours-2 hours remaining.
1984/300000 complete 40.00/sec (49938 invs, 2048 pays, 0 retries) in 50 seconds. 2 hours-2 hours remaining.
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2022-09-19 02:49:53 +02:00
|
|
|
payments = wallet_payment_list(cmd, cmd->ld->wallet, payment_hash);
|
2020-08-08 17:47:53 +02:00
|
|
|
|
|
|
|
if (tal_count(payments) == 0)
|
2020-08-19 12:38:50 +02:00
|
|
|
return command_fail(cmd, PAY_NO_SUCH_PAYMENT, "Unknown payment with payment_hash: %s",
|
2020-08-08 17:47:53 +02:00
|
|
|
type_to_string(tmpctx, struct sha256, payment_hash));
|
|
|
|
|
2022-09-19 02:54:26 +02:00
|
|
|
found = false;
|
2020-08-08 17:47:53 +02:00
|
|
|
for (int i = 0; i < tal_count(payments); i++) {
|
2022-09-19 02:54:26 +02:00
|
|
|
if (groupid && payments[i]->groupid != *groupid)
|
|
|
|
continue;
|
|
|
|
if (partid && payments[i]->partid != *partid)
|
|
|
|
continue;
|
|
|
|
|
delpay: delete the payment by status from the db
There are cases (difficult to reproduce with a test) where
a payment will fail one time and succeed later.
As far I understand in this case the groupid field of the payment
is the same, and the only thing that change is the status, so
our logic inside the delpay is ambiguous where it is not
possible to delete a payment as described in https://github.com/ElementsProject/lightning/issues/6114
A sequence of commands that explain the problem is
```
$ lc -k listpays payment_hash=H
{
"pays": [
{
"bolt11": "I",
"destination": "redacted",
"payment_hash": "H",
"status": "complete",
"created_at": redacted,
"completed_at": redacted,
"preimage": "P",
"amount_msat": "redacted",
"amount_sent_msat": "redacted"
}
]
}
$ lc delpay H complete
{
"code": 211,
"message": "Payment with hash H has failed status but it should be complete"
}
```
In this case, the delpay is not able to delete a payment because the
listpays is returning only the succeeded one, so by running the
listsendpays we may see the following result where our delpay logic
will be stuck because it works to ensure that all the payments stored
in the database has the status specified by the user
```
➜ VincentSSD clightning --testnet listsendpays -k payment_hash=7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4
{
"payments": [
{
"id": 322,
"payment_hash": "7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4",
"groupid": 1,
"partid": 1,
"destination": "030b686a163aa2bba03cebb8bab7778fac251536498141df0a436d688352d426f6",
"amount_msat": 300,
"amount_sent_msat": 1664,
"created_at": 1679510203,
"completed_at": 1679510205,
"status": "failed",
"bolt11": "lntb1pjpkj4xsp52trda39rfpe7qtqahx8jjplhnj3tatxy8rh6sc6afgvmdz7n0llspp50lr5hmdm0re0xvcp2hv3nf2wwvx0r8q3h3e7jmqz0awdfg6w206qdp0w3jhxarfdenjqargv5sxgetvwpshjgrzw4njqun9wphhyaqxqyjw5qcqp2rzjqtp28uqy77te96ylt7ek703h4ayldljsf8rnlztgf3p8mg7pd0qzwf8a3yqqpdqqqyqqqqt2qqqqqqgqqc9qxpqysgqgeya2lguaj6sflc4hx2d89jvah8mw9uax4j77d8rzkut3rkm0554x37fc7gy92ws9l76yprdva2lalrs7fqjp9lcx40zuty8gca0g5spme3dup"
},
{
"id": 323,
"payment_hash": "7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4",
"groupid": 1,
"partid": 2,
"destination": "030b686a163aa2bba03cebb8bab7778fac251536498141df0a436d688352d426f6",
"amount_msat": 300,
"amount_sent_msat": 3663,
"created_at": 1679510205,
"completed_at": 1679510207,
"status": "failed"
},
{
"id": 324,
"payment_hash": "7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4",
"groupid": 1,
"partid": 3,
"destination": "030b686a163aa2bba03cebb8bab7778fac251536498141df0a436d688352d426f6",
"amount_msat": 300,
"amount_sent_msat": 3663,
"created_at": 1679510207,
"completed_at": 1679510209,
"status": "failed"
},
{
"id": 325,
"payment_hash": "7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4",
"groupid": 1,
"partid": 4,
"destination": "030b686a163aa2bba03cebb8bab7778fac251536498141df0a436d688352d426f6",
"amount_msat": 300,
"amount_sent_msat": 4663,
"created_at": 1679510209,
"completed_at": 1679510221,
"status": "complete",
"payment_preimage": "43f746f2d28d4902489cbde9b3b8f3d04db5db7e973f8a55b7229ce774bf33a7"
}
]
}
```
This commit solves the problem by forcing the delete query in the
database to specify status too, and work around this kind of
ambiguous case.
Fixes: f52ff07558709bd1f7ed0cdca65c891d80b1a785 (lightningd: allow delpay to delete a specific payment.)
Reported-by: Antoine Poinsot <darosior@protonmail.com>
Link: https://github.com/ElementsProject/lightning/issues/6114
Signed-off-by: Vincenzo Palazzo <vincenzopalazzodev@gmail.com>
Co-Developed-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Fixed: delpay be more pedantic about delete logic by allowing
delete payments by status directly on the database.
2023-03-22 20:22:25 +01:00
|
|
|
if (payments[i]->status == *status) {
|
|
|
|
found = true;
|
|
|
|
break;
|
2020-08-08 17:47:53 +02:00
|
|
|
}
|
delpay: delete the payment by status from the db
There are cases (difficult to reproduce with a test) where
a payment will fail one time and succeed later.
As far I understand in this case the groupid field of the payment
is the same, and the only thing that change is the status, so
our logic inside the delpay is ambiguous where it is not
possible to delete a payment as described in https://github.com/ElementsProject/lightning/issues/6114
A sequence of commands that explain the problem is
```
$ lc -k listpays payment_hash=H
{
"pays": [
{
"bolt11": "I",
"destination": "redacted",
"payment_hash": "H",
"status": "complete",
"created_at": redacted,
"completed_at": redacted,
"preimage": "P",
"amount_msat": "redacted",
"amount_sent_msat": "redacted"
}
]
}
$ lc delpay H complete
{
"code": 211,
"message": "Payment with hash H has failed status but it should be complete"
}
```
In this case, the delpay is not able to delete a payment because the
listpays is returning only the succeeded one, so by running the
listsendpays we may see the following result where our delpay logic
will be stuck because it works to ensure that all the payments stored
in the database has the status specified by the user
```
➜ VincentSSD clightning --testnet listsendpays -k payment_hash=7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4
{
"payments": [
{
"id": 322,
"payment_hash": "7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4",
"groupid": 1,
"partid": 1,
"destination": "030b686a163aa2bba03cebb8bab7778fac251536498141df0a436d688352d426f6",
"amount_msat": 300,
"amount_sent_msat": 1664,
"created_at": 1679510203,
"completed_at": 1679510205,
"status": "failed",
"bolt11": "lntb1pjpkj4xsp52trda39rfpe7qtqahx8jjplhnj3tatxy8rh6sc6afgvmdz7n0llspp50lr5hmdm0re0xvcp2hv3nf2wwvx0r8q3h3e7jmqz0awdfg6w206qdp0w3jhxarfdenjqargv5sxgetvwpshjgrzw4njqun9wphhyaqxqyjw5qcqp2rzjqtp28uqy77te96ylt7ek703h4ayldljsf8rnlztgf3p8mg7pd0qzwf8a3yqqpdqqqyqqqqt2qqqqqqgqqc9qxpqysgqgeya2lguaj6sflc4hx2d89jvah8mw9uax4j77d8rzkut3rkm0554x37fc7gy92ws9l76yprdva2lalrs7fqjp9lcx40zuty8gca0g5spme3dup"
},
{
"id": 323,
"payment_hash": "7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4",
"groupid": 1,
"partid": 2,
"destination": "030b686a163aa2bba03cebb8bab7778fac251536498141df0a436d688352d426f6",
"amount_msat": 300,
"amount_sent_msat": 3663,
"created_at": 1679510205,
"completed_at": 1679510207,
"status": "failed"
},
{
"id": 324,
"payment_hash": "7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4",
"groupid": 1,
"partid": 3,
"destination": "030b686a163aa2bba03cebb8bab7778fac251536498141df0a436d688352d426f6",
"amount_msat": 300,
"amount_sent_msat": 3663,
"created_at": 1679510207,
"completed_at": 1679510209,
"status": "failed"
},
{
"id": 325,
"payment_hash": "7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4",
"groupid": 1,
"partid": 4,
"destination": "030b686a163aa2bba03cebb8bab7778fac251536498141df0a436d688352d426f6",
"amount_msat": 300,
"amount_sent_msat": 4663,
"created_at": 1679510209,
"completed_at": 1679510221,
"status": "complete",
"payment_preimage": "43f746f2d28d4902489cbde9b3b8f3d04db5db7e973f8a55b7229ce774bf33a7"
}
]
}
```
This commit solves the problem by forcing the delete query in the
database to specify status too, and work around this kind of
ambiguous case.
Fixes: f52ff07558709bd1f7ed0cdca65c891d80b1a785 (lightningd: allow delpay to delete a specific payment.)
Reported-by: Antoine Poinsot <darosior@protonmail.com>
Link: https://github.com/ElementsProject/lightning/issues/6114
Signed-off-by: Vincenzo Palazzo <vincenzopalazzodev@gmail.com>
Co-Developed-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Fixed: delpay be more pedantic about delete logic by allowing
delete payments by status directly on the database.
2023-03-22 20:22:25 +01:00
|
|
|
|
|
|
|
found_status = &payments[i]->status;
|
2020-08-08 17:47:53 +02:00
|
|
|
}
|
|
|
|
|
2022-09-19 02:54:26 +02:00
|
|
|
if (!found) {
|
delpay: delete the payment by status from the db
There are cases (difficult to reproduce with a test) where
a payment will fail one time and succeed later.
As far I understand in this case the groupid field of the payment
is the same, and the only thing that change is the status, so
our logic inside the delpay is ambiguous where it is not
possible to delete a payment as described in https://github.com/ElementsProject/lightning/issues/6114
A sequence of commands that explain the problem is
```
$ lc -k listpays payment_hash=H
{
"pays": [
{
"bolt11": "I",
"destination": "redacted",
"payment_hash": "H",
"status": "complete",
"created_at": redacted,
"completed_at": redacted,
"preimage": "P",
"amount_msat": "redacted",
"amount_sent_msat": "redacted"
}
]
}
$ lc delpay H complete
{
"code": 211,
"message": "Payment with hash H has failed status but it should be complete"
}
```
In this case, the delpay is not able to delete a payment because the
listpays is returning only the succeeded one, so by running the
listsendpays we may see the following result where our delpay logic
will be stuck because it works to ensure that all the payments stored
in the database has the status specified by the user
```
➜ VincentSSD clightning --testnet listsendpays -k payment_hash=7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4
{
"payments": [
{
"id": 322,
"payment_hash": "7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4",
"groupid": 1,
"partid": 1,
"destination": "030b686a163aa2bba03cebb8bab7778fac251536498141df0a436d688352d426f6",
"amount_msat": 300,
"amount_sent_msat": 1664,
"created_at": 1679510203,
"completed_at": 1679510205,
"status": "failed",
"bolt11": "lntb1pjpkj4xsp52trda39rfpe7qtqahx8jjplhnj3tatxy8rh6sc6afgvmdz7n0llspp50lr5hmdm0re0xvcp2hv3nf2wwvx0r8q3h3e7jmqz0awdfg6w206qdp0w3jhxarfdenjqargv5sxgetvwpshjgrzw4njqun9wphhyaqxqyjw5qcqp2rzjqtp28uqy77te96ylt7ek703h4ayldljsf8rnlztgf3p8mg7pd0qzwf8a3yqqpdqqqyqqqqt2qqqqqqgqqc9qxpqysgqgeya2lguaj6sflc4hx2d89jvah8mw9uax4j77d8rzkut3rkm0554x37fc7gy92ws9l76yprdva2lalrs7fqjp9lcx40zuty8gca0g5spme3dup"
},
{
"id": 323,
"payment_hash": "7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4",
"groupid": 1,
"partid": 2,
"destination": "030b686a163aa2bba03cebb8bab7778fac251536498141df0a436d688352d426f6",
"amount_msat": 300,
"amount_sent_msat": 3663,
"created_at": 1679510205,
"completed_at": 1679510207,
"status": "failed"
},
{
"id": 324,
"payment_hash": "7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4",
"groupid": 1,
"partid": 3,
"destination": "030b686a163aa2bba03cebb8bab7778fac251536498141df0a436d688352d426f6",
"amount_msat": 300,
"amount_sent_msat": 3663,
"created_at": 1679510207,
"completed_at": 1679510209,
"status": "failed"
},
{
"id": 325,
"payment_hash": "7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4",
"groupid": 1,
"partid": 4,
"destination": "030b686a163aa2bba03cebb8bab7778fac251536498141df0a436d688352d426f6",
"amount_msat": 300,
"amount_sent_msat": 4663,
"created_at": 1679510209,
"completed_at": 1679510221,
"status": "complete",
"payment_preimage": "43f746f2d28d4902489cbde9b3b8f3d04db5db7e973f8a55b7229ce774bf33a7"
}
]
}
```
This commit solves the problem by forcing the delete query in the
database to specify status too, and work around this kind of
ambiguous case.
Fixes: f52ff07558709bd1f7ed0cdca65c891d80b1a785 (lightningd: allow delpay to delete a specific payment.)
Reported-by: Antoine Poinsot <darosior@protonmail.com>
Link: https://github.com/ElementsProject/lightning/issues/6114
Signed-off-by: Vincenzo Palazzo <vincenzopalazzodev@gmail.com>
Co-Developed-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Fixed: delpay be more pedantic about delete logic by allowing
delete payments by status directly on the database.
2023-03-22 20:22:25 +01:00
|
|
|
if (found_status)
|
|
|
|
return command_fail(cmd, PAY_NO_SUCH_PAYMENT, "Payment with hash %s has %s status but it different from the one provided %s",
|
|
|
|
type_to_string(tmpctx, struct sha256, payment_hash),
|
|
|
|
payment_status_to_string(*found_status),
|
|
|
|
payment_status_to_string(*status));
|
|
|
|
|
2022-09-19 02:54:26 +02:00
|
|
|
return command_fail(cmd, PAY_NO_SUCH_PAYMENT,
|
|
|
|
"No payment for that payment_hash with that partid and groupid");
|
|
|
|
}
|
|
|
|
|
delpay: delete the payment by status from the db
There are cases (difficult to reproduce with a test) where
a payment will fail one time and succeed later.
As far I understand in this case the groupid field of the payment
is the same, and the only thing that change is the status, so
our logic inside the delpay is ambiguous where it is not
possible to delete a payment as described in https://github.com/ElementsProject/lightning/issues/6114
A sequence of commands that explain the problem is
```
$ lc -k listpays payment_hash=H
{
"pays": [
{
"bolt11": "I",
"destination": "redacted",
"payment_hash": "H",
"status": "complete",
"created_at": redacted,
"completed_at": redacted,
"preimage": "P",
"amount_msat": "redacted",
"amount_sent_msat": "redacted"
}
]
}
$ lc delpay H complete
{
"code": 211,
"message": "Payment with hash H has failed status but it should be complete"
}
```
In this case, the delpay is not able to delete a payment because the
listpays is returning only the succeeded one, so by running the
listsendpays we may see the following result where our delpay logic
will be stuck because it works to ensure that all the payments stored
in the database has the status specified by the user
```
➜ VincentSSD clightning --testnet listsendpays -k payment_hash=7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4
{
"payments": [
{
"id": 322,
"payment_hash": "7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4",
"groupid": 1,
"partid": 1,
"destination": "030b686a163aa2bba03cebb8bab7778fac251536498141df0a436d688352d426f6",
"amount_msat": 300,
"amount_sent_msat": 1664,
"created_at": 1679510203,
"completed_at": 1679510205,
"status": "failed",
"bolt11": "lntb1pjpkj4xsp52trda39rfpe7qtqahx8jjplhnj3tatxy8rh6sc6afgvmdz7n0llspp50lr5hmdm0re0xvcp2hv3nf2wwvx0r8q3h3e7jmqz0awdfg6w206qdp0w3jhxarfdenjqargv5sxgetvwpshjgrzw4njqun9wphhyaqxqyjw5qcqp2rzjqtp28uqy77te96ylt7ek703h4ayldljsf8rnlztgf3p8mg7pd0qzwf8a3yqqpdqqqyqqqqt2qqqqqqgqqc9qxpqysgqgeya2lguaj6sflc4hx2d89jvah8mw9uax4j77d8rzkut3rkm0554x37fc7gy92ws9l76yprdva2lalrs7fqjp9lcx40zuty8gca0g5spme3dup"
},
{
"id": 323,
"payment_hash": "7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4",
"groupid": 1,
"partid": 2,
"destination": "030b686a163aa2bba03cebb8bab7778fac251536498141df0a436d688352d426f6",
"amount_msat": 300,
"amount_sent_msat": 3663,
"created_at": 1679510205,
"completed_at": 1679510207,
"status": "failed"
},
{
"id": 324,
"payment_hash": "7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4",
"groupid": 1,
"partid": 3,
"destination": "030b686a163aa2bba03cebb8bab7778fac251536498141df0a436d688352d426f6",
"amount_msat": 300,
"amount_sent_msat": 3663,
"created_at": 1679510207,
"completed_at": 1679510209,
"status": "failed"
},
{
"id": 325,
"payment_hash": "7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4",
"groupid": 1,
"partid": 4,
"destination": "030b686a163aa2bba03cebb8bab7778fac251536498141df0a436d688352d426f6",
"amount_msat": 300,
"amount_sent_msat": 4663,
"created_at": 1679510209,
"completed_at": 1679510221,
"status": "complete",
"payment_preimage": "43f746f2d28d4902489cbde9b3b8f3d04db5db7e973f8a55b7229ce774bf33a7"
}
]
}
```
This commit solves the problem by forcing the delete query in the
database to specify status too, and work around this kind of
ambiguous case.
Fixes: f52ff07558709bd1f7ed0cdca65c891d80b1a785 (lightningd: allow delpay to delete a specific payment.)
Reported-by: Antoine Poinsot <darosior@protonmail.com>
Link: https://github.com/ElementsProject/lightning/issues/6114
Signed-off-by: Vincenzo Palazzo <vincenzopalazzodev@gmail.com>
Co-Developed-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Fixed: delpay be more pedantic about delete logic by allowing
delete payments by status directly on the database.
2023-03-22 20:22:25 +01:00
|
|
|
wallet_payment_delete(cmd->ld->wallet, payment_hash, groupid, partid, status);
|
2020-08-08 17:47:53 +02:00
|
|
|
|
|
|
|
response = json_stream_success(cmd);
|
|
|
|
json_array_start(response, "payments");
|
|
|
|
for (int i = 0; i < tal_count(payments); i++) {
|
2022-09-19 02:54:26 +02:00
|
|
|
if (groupid && payments[i]->groupid != *groupid)
|
|
|
|
continue;
|
|
|
|
if (partid && payments[i]->partid != *partid)
|
|
|
|
continue;
|
delpay: delete the payment by status from the db
There are cases (difficult to reproduce with a test) where
a payment will fail one time and succeed later.
As far I understand in this case the groupid field of the payment
is the same, and the only thing that change is the status, so
our logic inside the delpay is ambiguous where it is not
possible to delete a payment as described in https://github.com/ElementsProject/lightning/issues/6114
A sequence of commands that explain the problem is
```
$ lc -k listpays payment_hash=H
{
"pays": [
{
"bolt11": "I",
"destination": "redacted",
"payment_hash": "H",
"status": "complete",
"created_at": redacted,
"completed_at": redacted,
"preimage": "P",
"amount_msat": "redacted",
"amount_sent_msat": "redacted"
}
]
}
$ lc delpay H complete
{
"code": 211,
"message": "Payment with hash H has failed status but it should be complete"
}
```
In this case, the delpay is not able to delete a payment because the
listpays is returning only the succeeded one, so by running the
listsendpays we may see the following result where our delpay logic
will be stuck because it works to ensure that all the payments stored
in the database has the status specified by the user
```
➜ VincentSSD clightning --testnet listsendpays -k payment_hash=7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4
{
"payments": [
{
"id": 322,
"payment_hash": "7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4",
"groupid": 1,
"partid": 1,
"destination": "030b686a163aa2bba03cebb8bab7778fac251536498141df0a436d688352d426f6",
"amount_msat": 300,
"amount_sent_msat": 1664,
"created_at": 1679510203,
"completed_at": 1679510205,
"status": "failed",
"bolt11": "lntb1pjpkj4xsp52trda39rfpe7qtqahx8jjplhnj3tatxy8rh6sc6afgvmdz7n0llspp50lr5hmdm0re0xvcp2hv3nf2wwvx0r8q3h3e7jmqz0awdfg6w206qdp0w3jhxarfdenjqargv5sxgetvwpshjgrzw4njqun9wphhyaqxqyjw5qcqp2rzjqtp28uqy77te96ylt7ek703h4ayldljsf8rnlztgf3p8mg7pd0qzwf8a3yqqpdqqqyqqqqt2qqqqqqgqqc9qxpqysgqgeya2lguaj6sflc4hx2d89jvah8mw9uax4j77d8rzkut3rkm0554x37fc7gy92ws9l76yprdva2lalrs7fqjp9lcx40zuty8gca0g5spme3dup"
},
{
"id": 323,
"payment_hash": "7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4",
"groupid": 1,
"partid": 2,
"destination": "030b686a163aa2bba03cebb8bab7778fac251536498141df0a436d688352d426f6",
"amount_msat": 300,
"amount_sent_msat": 3663,
"created_at": 1679510205,
"completed_at": 1679510207,
"status": "failed"
},
{
"id": 324,
"payment_hash": "7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4",
"groupid": 1,
"partid": 3,
"destination": "030b686a163aa2bba03cebb8bab7778fac251536498141df0a436d688352d426f6",
"amount_msat": 300,
"amount_sent_msat": 3663,
"created_at": 1679510207,
"completed_at": 1679510209,
"status": "failed"
},
{
"id": 325,
"payment_hash": "7fc74bedbb78f2f3330155d919a54e730cf19c11bc73e96c027f5cd4a34e53f4",
"groupid": 1,
"partid": 4,
"destination": "030b686a163aa2bba03cebb8bab7778fac251536498141df0a436d688352d426f6",
"amount_msat": 300,
"amount_sent_msat": 4663,
"created_at": 1679510209,
"completed_at": 1679510221,
"status": "complete",
"payment_preimage": "43f746f2d28d4902489cbde9b3b8f3d04db5db7e973f8a55b7229ce774bf33a7"
}
]
}
```
This commit solves the problem by forcing the delete query in the
database to specify status too, and work around this kind of
ambiguous case.
Fixes: f52ff07558709bd1f7ed0cdca65c891d80b1a785 (lightningd: allow delpay to delete a specific payment.)
Reported-by: Antoine Poinsot <darosior@protonmail.com>
Link: https://github.com/ElementsProject/lightning/issues/6114
Signed-off-by: Vincenzo Palazzo <vincenzopalazzodev@gmail.com>
Co-Developed-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Fixed: delpay be more pedantic about delete logic by allowing
delete payments by status directly on the database.
2023-03-22 20:22:25 +01:00
|
|
|
if (payments[i]->status != *status)
|
|
|
|
continue;
|
2020-08-08 17:47:53 +02:00
|
|
|
json_object_start(response, NULL);
|
|
|
|
json_add_payment_fields(response, payments[i]);
|
|
|
|
json_object_end(response);
|
|
|
|
}
|
|
|
|
json_array_end(response);
|
|
|
|
return command_success(cmd, response);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct json_command delpay_command = {
|
|
|
|
"delpay",
|
|
|
|
"payment",
|
|
|
|
json_delpay,
|
|
|
|
"Delete payment with {payment_hash} and {status}",
|
|
|
|
};
|
|
|
|
AUTODATA(json_command, &delpay_command);
|
|
|
|
|
2019-11-07 19:06:43 +01:00
|
|
|
static struct command_result *json_createonion(struct command *cmd,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *obj UNNEEDED,
|
|
|
|
const jsmntok_t *params)
|
|
|
|
{
|
|
|
|
struct json_stream *response;
|
|
|
|
struct secret *session_key, *shared_secrets;
|
|
|
|
struct sphinx_path *sp;
|
2019-11-24 19:09:19 +01:00
|
|
|
u8 *assocdata, *serialized;
|
2021-05-10 05:55:04 +02:00
|
|
|
u32 *packet_size;
|
2019-11-07 19:06:43 +01:00
|
|
|
struct onionpacket *packet;
|
2019-11-24 19:09:19 +01:00
|
|
|
struct sphinx_hop *hops;
|
2019-11-07 19:06:43 +01:00
|
|
|
|
|
|
|
if (!param(cmd, buffer, params,
|
2019-11-24 19:09:19 +01:00
|
|
|
p_req("hops", param_hops_array, &hops),
|
2019-11-07 19:06:43 +01:00
|
|
|
p_req("assocdata", param_bin_from_hex, &assocdata),
|
|
|
|
p_opt("session_key", param_secret, &session_key),
|
2021-05-10 05:55:04 +02:00
|
|
|
p_opt_def("onion_size", param_number, &packet_size, ROUTING_INFO_SIZE),
|
2019-11-07 19:06:43 +01:00
|
|
|
NULL)) {
|
|
|
|
return command_param_failed();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (session_key == NULL)
|
|
|
|
sp = sphinx_path_new(cmd, assocdata);
|
|
|
|
else
|
|
|
|
sp = sphinx_path_new_with_key(cmd, assocdata, session_key);
|
|
|
|
|
2022-09-28 06:49:37 +02:00
|
|
|
for (size_t i=0; i<tal_count(hops); i++) {
|
|
|
|
if (!sphinx_add_hop_has_length(sp, &hops[i].pubkey, hops[i].raw_payload))
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"hops[%zi] payload is not prefixed with length!",
|
|
|
|
i);
|
|
|
|
}
|
2019-11-07 19:06:43 +01:00
|
|
|
|
2021-05-10 05:55:04 +02:00
|
|
|
if (sphinx_path_payloads_size(sp) > *packet_size)
|
2020-01-08 22:05:53 +01:00
|
|
|
return command_fail(
|
|
|
|
cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"Payloads exceed maximum onion packet size.");
|
|
|
|
|
2021-05-10 05:55:04 +02:00
|
|
|
packet = create_onionpacket(cmd, sp, *packet_size, &shared_secrets);
|
2019-11-07 19:06:43 +01:00
|
|
|
if (!packet)
|
|
|
|
return command_fail(cmd, LIGHTNINGD,
|
|
|
|
"Could not create onion packet");
|
|
|
|
|
|
|
|
serialized = serialize_onionpacket(cmd, packet);
|
|
|
|
|
|
|
|
response = json_stream_success(cmd);
|
|
|
|
json_add_hex(response, "onion", serialized, tal_bytelen(serialized));
|
|
|
|
json_array_start(response, "shared_secrets");
|
2019-11-24 19:09:19 +01:00
|
|
|
for (size_t i=0; i<tal_count(hops); i++) {
|
2019-11-07 19:06:43 +01:00
|
|
|
json_add_secret(response, NULL, &shared_secrets[i]);
|
|
|
|
}
|
|
|
|
json_array_end(response);
|
|
|
|
return command_success(cmd, response);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct json_command createonion_command = {
|
|
|
|
"createonion",
|
|
|
|
"payment",
|
|
|
|
json_createonion,
|
|
|
|
"Create an onion going through the provided nodes, each with its own payload"
|
|
|
|
};
|
|
|
|
AUTODATA(json_command, &createonion_command);
|