2021-12-04 12:23:56 +01:00
|
|
|
#include "config.h"
|
2020-05-16 03:29:05 +02:00
|
|
|
#include <bitcoin/chainparams.h>
|
2019-01-15 05:14:27 +01:00
|
|
|
#include <ccan/array_size/array_size.h>
|
2021-05-26 23:56:58 +02:00
|
|
|
#include <ccan/asort/asort.h>
|
2019-02-23 06:00:04 +01:00
|
|
|
#include <ccan/cast/cast.h>
|
2019-12-12 00:55:45 +01:00
|
|
|
#include <ccan/crypto/siphash24/siphash24.h>
|
|
|
|
#include <ccan/htable/htable_type.h>
|
2019-06-12 02:38:54 +02:00
|
|
|
#include <ccan/json_out/json_out.h>
|
2020-08-11 16:42:20 +02:00
|
|
|
#include <ccan/str/hex/hex.h>
|
2019-01-15 05:14:27 +01:00
|
|
|
#include <ccan/tal/str/str.h>
|
2020-12-14 02:25:37 +01:00
|
|
|
#include <common/bolt12_merkle.h>
|
|
|
|
#include <common/gossmap.h>
|
2022-07-04 05:49:38 +02:00
|
|
|
#include <common/json_param.h>
|
2020-01-30 00:55:55 +01:00
|
|
|
#include <common/json_stream.h>
|
2022-02-26 01:50:33 +01:00
|
|
|
#include <common/memleak.h>
|
2019-01-15 11:04:07 +01:00
|
|
|
#include <common/pseudorand.h>
|
2024-08-09 12:25:01 +02:00
|
|
|
#include <plugins/channel_hint.h>
|
2020-05-08 15:03:48 +02:00
|
|
|
#include <plugins/libplugin-pay.h>
|
2024-08-09 12:25:01 +02:00
|
|
|
#include <plugins/libplugin.h>
|
2019-01-15 11:04:07 +01:00
|
|
|
#include <stdio.h>
|
2019-01-15 05:14:27 +01:00
|
|
|
|
2019-01-15 05:20:27 +01:00
|
|
|
/* Public key of this node. */
|
2019-04-08 11:58:32 +02:00
|
|
|
static struct node_id my_id;
|
2019-01-15 05:21:27 +01:00
|
|
|
static unsigned int maxdelay_default;
|
2020-07-10 14:48:00 +02:00
|
|
|
static bool disablempp = false;
|
2024-08-22 18:34:38 +02:00
|
|
|
static struct channel_hint_set *global_hints;
|
2020-07-10 14:48:00 +02:00
|
|
|
|
2020-05-18 18:47:21 +02:00
|
|
|
static LIST_HEAD(payments);
|
|
|
|
|
2019-01-15 05:14:27 +01:00
|
|
|
struct pay_command {
|
2020-01-26 15:44:16 +01:00
|
|
|
/* Global state */
|
|
|
|
struct plugin *plugin;
|
|
|
|
|
2019-01-15 05:16:27 +01:00
|
|
|
/* Destination, as text */
|
|
|
|
const char *dest;
|
|
|
|
|
|
|
|
/* How much we're paying, and what riskfactor for routing. */
|
2019-02-21 04:45:44 +01:00
|
|
|
struct amount_msat msat;
|
2020-05-02 13:52:03 +02:00
|
|
|
/* Blank amount to pay, without fees and shadow route(s). */
|
|
|
|
struct amount_msat initial_msat;
|
2020-01-29 12:30:00 +01:00
|
|
|
/* riskfactor 12.345% -> riskfactor_millionths = 12345000 */
|
|
|
|
u64 riskfactor_millionths;
|
2019-01-15 05:22:27 +01:00
|
|
|
unsigned int final_cltv;
|
2019-01-15 05:16:27 +01:00
|
|
|
|
2019-01-15 05:19:27 +01:00
|
|
|
/* Limits on what routes we'll accept. */
|
2020-01-29 12:30:00 +01:00
|
|
|
/* 12.345% -> maxfee_pct_millionths = 12345000 */
|
|
|
|
u64 maxfee_pct_millionths;
|
2019-01-15 05:19:27 +01:00
|
|
|
unsigned int maxdelay;
|
2019-02-21 04:45:44 +01:00
|
|
|
struct amount_msat exemptfee;
|
2019-01-15 05:19:27 +01:00
|
|
|
|
2019-01-15 05:14:27 +01:00
|
|
|
/* Payment hash, as text. */
|
|
|
|
const char *payment_hash;
|
|
|
|
|
2019-11-23 01:19:23 +01:00
|
|
|
/* Payment secret, if specified by invoice. */
|
|
|
|
const char *payment_secret;
|
|
|
|
|
2022-03-31 11:10:50 +02:00
|
|
|
/* Payment metadata, if specified by invoice. */
|
|
|
|
const char *payment_metadata;
|
|
|
|
|
2019-01-15 05:14:27 +01:00
|
|
|
/* Description, if any. */
|
2019-02-23 06:00:02 +01:00
|
|
|
const char *label;
|
2019-01-15 05:16:27 +01:00
|
|
|
|
|
|
|
/* Chatty description of attempts. */
|
2019-01-15 11:04:07 +01:00
|
|
|
struct pay_status *ps;
|
2019-01-15 05:16:27 +01:00
|
|
|
|
2019-01-15 11:04:08 +01:00
|
|
|
/* Error to use if getroute says it can't find route. */
|
|
|
|
const char *expensive_route;
|
|
|
|
|
2019-01-15 05:16:27 +01:00
|
|
|
/* Time to stop retrying. */
|
|
|
|
struct timeabs stoptime;
|
|
|
|
|
|
|
|
/* Channels which have failed us. */
|
|
|
|
const char **excludes;
|
2019-01-15 11:04:01 +01:00
|
|
|
|
2019-02-01 04:03:27 +01:00
|
|
|
/* Current routehint, if any. */
|
|
|
|
struct route_info *current_routehint;
|
|
|
|
|
|
|
|
/* Any remaining routehints to try. */
|
2019-01-15 11:04:01 +01:00
|
|
|
struct route_info **routehints;
|
2019-01-15 11:04:07 +01:00
|
|
|
|
2023-09-21 07:36:27 +02:00
|
|
|
/* Disable the use of shadow route ? (--developer allows this) */
|
2019-11-04 15:59:01 +01:00
|
|
|
double use_shadow;
|
|
|
|
|
2019-01-15 11:04:07 +01:00
|
|
|
/* Current node during shadow route calculation. */
|
2019-01-16 21:37:11 +01:00
|
|
|
const char *shadow_dest;
|
2019-01-15 05:14:27 +01:00
|
|
|
};
|
|
|
|
|
2019-01-15 11:04:07 +01:00
|
|
|
/* FIXME: Add this to ccan/time? */
|
|
|
|
#define UTC_TIMELEN (sizeof("YYYY-mm-ddTHH:MM:SS.nnnZ"))
|
|
|
|
static void utc_timestring(const struct timeabs *time, char str[UTC_TIMELEN])
|
|
|
|
{
|
|
|
|
char iso8601_msec_fmt[sizeof("YYYY-mm-ddTHH:MM:SS.%03dZ")];
|
2022-03-24 03:44:53 +01:00
|
|
|
struct tm *t = gmtime(&time->ts.tv_sec);
|
2019-01-15 11:04:07 +01:00
|
|
|
|
2022-03-24 03:44:53 +01:00
|
|
|
/* Shouldn't happen, but see
|
|
|
|
* https://github.com/ElementsProject/lightning/issues/4991 :( */
|
|
|
|
if (!t) {
|
|
|
|
snprintf(str, UTC_TIMELEN, "1970-01-01T00:00:00.000Z");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
strftime(iso8601_msec_fmt, sizeof(iso8601_msec_fmt), "%FT%T.%%03dZ", t);
|
2019-01-15 11:04:07 +01:00
|
|
|
snprintf(str, UTC_TIMELEN, iso8601_msec_fmt,
|
|
|
|
(int) time->ts.tv_nsec / 1000000);
|
|
|
|
}
|
|
|
|
|
2020-05-29 15:25:00 +02:00
|
|
|
static void json_add_sendpay_result(struct json_stream *s, const struct payment_result *r)
|
|
|
|
{
|
|
|
|
if (r->code != 0) {
|
|
|
|
/* This is a failure */
|
|
|
|
json_add_string(s, "message", r->message);
|
|
|
|
json_add_u32(s, "code", r->code);
|
|
|
|
|
|
|
|
json_object_start(s, "data");
|
|
|
|
json_add_u32(s, "id", r->id);
|
|
|
|
json_add_hex(s, "raw_message", r->raw_message, tal_bytelen(r->raw_message));
|
|
|
|
json_add_num(s, "failcode", r->failcode);
|
|
|
|
json_add_string(s, "failcodename", r->failcodename);
|
|
|
|
|
|
|
|
if (r->erring_index)
|
|
|
|
json_add_num(s, "erring_index", *r->erring_index);
|
|
|
|
|
|
|
|
if (r->erring_node)
|
|
|
|
json_add_node_id(s, "erring_node", r->erring_node);
|
|
|
|
|
|
|
|
if (r->erring_channel)
|
|
|
|
json_add_short_channel_id(s, "erring_channel",
|
2024-03-20 02:59:51 +01:00
|
|
|
*r->erring_channel);
|
2020-05-29 15:25:00 +02:00
|
|
|
|
|
|
|
if (r->erring_direction)
|
|
|
|
json_add_num(s, "erring_direction",
|
|
|
|
*r->erring_direction);
|
2021-03-16 05:20:38 +01:00
|
|
|
|
2020-05-29 15:25:00 +02:00
|
|
|
json_object_end(s);
|
|
|
|
} else {
|
|
|
|
/* This is a success */
|
|
|
|
json_add_u32(s, "id", r->id);
|
|
|
|
json_add_preimage(s, "payment_preimage", r->payment_preimage);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void paystatus_add_payment(struct json_stream *s, const struct payment *p)
|
|
|
|
{
|
|
|
|
char timestr[UTC_TIMELEN];
|
|
|
|
|
|
|
|
utc_timestring(&p->start_time, timestr);
|
|
|
|
|
|
|
|
json_object_start(s, NULL);
|
2020-05-29 17:08:20 +02:00
|
|
|
if (p->why != NULL)
|
|
|
|
json_add_string(s, "strategy", p->why);
|
2020-05-29 15:25:00 +02:00
|
|
|
json_add_string(s, "start_time", timestr);
|
|
|
|
json_add_u64(s, "age_in_seconds",
|
|
|
|
time_to_sec(time_between(time_now(), p->start_time)));
|
|
|
|
|
|
|
|
/* Any final state will have an end time. */
|
|
|
|
if (p->step >= PAYMENT_STEP_SPLIT) {
|
|
|
|
utc_timestring(&p->end_time, timestr);
|
|
|
|
json_add_string(s, "end_time", timestr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO Add routehint. */
|
|
|
|
/* TODO Add route details */
|
|
|
|
|
2021-02-24 22:10:07 +01:00
|
|
|
if (p->step < PAYMENT_STEP_SPLIT)
|
|
|
|
json_add_string(s, "state", "pending");
|
|
|
|
else
|
|
|
|
json_add_string(s, "state", "completed");
|
|
|
|
|
2020-07-03 20:55:20 +02:00
|
|
|
if (p->step == PAYMENT_STEP_SPLIT) {
|
|
|
|
/* Don't add anything, this is neither a success nor a failure. */
|
|
|
|
} else if (p->result != NULL) {
|
2020-05-29 15:25:00 +02:00
|
|
|
if (p->step == PAYMENT_STEP_SUCCESS)
|
|
|
|
json_object_start(s, "success");
|
|
|
|
else
|
|
|
|
json_object_start(s, "failure");
|
|
|
|
json_add_sendpay_result(s, p->result);
|
|
|
|
json_object_end(s);
|
2021-02-24 22:10:07 +01:00
|
|
|
} else if (p->step >= PAYMENT_STEP_SPLIT) {
|
2020-05-29 17:08:20 +02:00
|
|
|
json_object_start(s, "failure");
|
|
|
|
json_add_num(s, "code", PAY_ROUTE_NOT_FOUND);
|
|
|
|
json_add_string(s, "message", "Call to getroute: Could not find a route");
|
|
|
|
json_object_end(s);
|
2020-05-29 15:25:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
json_object_end(s);
|
|
|
|
for (size_t i = 0; i < tal_count(p->children); i++)
|
|
|
|
paystatus_add_payment(s, p->children[i]);
|
|
|
|
}
|
|
|
|
|
2019-02-23 06:00:04 +01:00
|
|
|
static struct command_result *json_paystatus(struct command *cmd,
|
|
|
|
const char *buf,
|
|
|
|
const jsmntok_t *params)
|
2019-01-15 11:04:07 +01:00
|
|
|
{
|
2020-12-14 02:24:37 +01:00
|
|
|
const char *invstring;
|
2020-01-31 01:12:07 +01:00
|
|
|
struct json_stream *ret;
|
2020-05-29 15:25:00 +02:00
|
|
|
struct payment *p;
|
2019-01-15 11:04:07 +01:00
|
|
|
|
|
|
|
if (!param(cmd, buf, params,
|
2020-12-14 02:24:37 +01:00
|
|
|
/* FIXME: rename to invstring */
|
2023-06-13 12:33:31 +02:00
|
|
|
p_opt("bolt11", param_invstring, &invstring),
|
2019-01-15 11:04:07 +01:00
|
|
|
NULL))
|
2019-09-18 12:14:44 +02:00
|
|
|
return command_param_failed();
|
2019-01-15 11:04:07 +01:00
|
|
|
|
2020-01-31 01:12:07 +01:00
|
|
|
ret = jsonrpc_stream_success(cmd);
|
|
|
|
json_array_start(ret, "pay");
|
2019-06-12 02:38:54 +02:00
|
|
|
|
2020-05-29 15:25:00 +02:00
|
|
|
list_for_each(&payments, p, list) {
|
|
|
|
assert(p->parent == NULL);
|
2020-12-14 02:24:37 +01:00
|
|
|
if (invstring && !streq(invstring, p->invstring))
|
2020-05-29 15:25:00 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
json_object_start(ret, NULL);
|
2020-07-02 15:33:55 +02:00
|
|
|
if (p->label != NULL)
|
|
|
|
json_add_string(ret, "label", p->label);
|
|
|
|
|
2020-12-14 02:24:37 +01:00
|
|
|
if (p->invstring)
|
|
|
|
json_add_invstring(ret, p->invstring);
|
2024-03-19 05:29:59 +01:00
|
|
|
json_add_amount_msat(ret, "amount_msat", p->our_amount);
|
2020-05-29 15:25:00 +02:00
|
|
|
|
2024-07-17 10:58:11 +02:00
|
|
|
json_add_node_id(ret, "destination", p->pay_destination);
|
2020-05-29 15:25:00 +02:00
|
|
|
|
|
|
|
/* TODO(cdecker) Add label in once we track labels. */
|
|
|
|
/* TODO(cdecker) Add routehint_modifications in once we track
|
|
|
|
* them. */
|
|
|
|
/* TODO(cdecker) Add shadow route once we support it. */
|
|
|
|
|
|
|
|
/* If it's in listpeers right now, this can be 0 */
|
|
|
|
json_array_start(ret, "attempts");
|
|
|
|
paystatus_add_payment(ret, p);
|
|
|
|
json_array_end(ret);
|
|
|
|
json_object_end(ret);
|
|
|
|
}
|
2020-01-31 01:12:07 +01:00
|
|
|
json_array_end(ret);
|
2019-01-15 11:04:07 +01:00
|
|
|
|
2020-01-31 01:12:07 +01:00
|
|
|
return command_finished(cmd, ret);
|
2019-01-15 11:04:07 +01:00
|
|
|
}
|
|
|
|
|
2020-08-11 16:42:20 +02:00
|
|
|
static bool attempt_ongoing(const struct sha256 *payment_hash)
|
2019-05-21 02:27:14 +02:00
|
|
|
{
|
2020-05-27 14:49:40 +02:00
|
|
|
struct payment *root;
|
|
|
|
struct payment_tree_result res;
|
|
|
|
enum payment_step diff,
|
|
|
|
final_states = PAYMENT_STEP_FAILED | PAYMENT_STEP_SUCCESS;
|
2019-05-21 02:27:14 +02:00
|
|
|
|
2020-05-27 14:49:40 +02:00
|
|
|
list_for_each(&payments, root, list) {
|
2020-08-11 16:42:20 +02:00
|
|
|
if (!sha256_eq(payment_hash, root->payment_hash))
|
2020-05-27 14:49:40 +02:00
|
|
|
continue;
|
|
|
|
res = payment_collect_result(root);
|
|
|
|
diff = res.leafstates & ~final_states;
|
|
|
|
return diff != 0;
|
|
|
|
}
|
2019-05-21 02:27:14 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-05-28 22:04:04 +02:00
|
|
|
/* A unique key for each payment attempt, even if the same invoice was
|
|
|
|
* attempted multiple times. */
|
|
|
|
struct pay_sort_key {
|
|
|
|
const struct sha256 *payment_hash;
|
|
|
|
u64 groupid;
|
|
|
|
};
|
|
|
|
|
2019-12-12 00:55:45 +01:00
|
|
|
/* We consolidate multi-part payments into a single entry. */
|
|
|
|
struct pay_mpp {
|
2020-05-26 13:23:25 +02:00
|
|
|
/* payment_hash from the invoice and lookup key */
|
|
|
|
const struct sha256 *payment_hash;
|
|
|
|
|
2020-12-14 02:24:37 +01:00
|
|
|
/* This is the bolt11/bolt12 string */
|
|
|
|
const char *invstring;
|
2021-04-28 14:01:20 +02:00
|
|
|
|
|
|
|
/* Accumulated states of all sendpay commands involved. */
|
|
|
|
enum payment_result_state state;
|
|
|
|
|
2019-12-12 00:55:45 +01:00
|
|
|
/* Optional label (of first one!) */
|
|
|
|
const jsmntok_t *label;
|
2022-04-02 04:33:35 +02:00
|
|
|
/* Optional description (used for bolt11 with description_hash) */
|
|
|
|
const jsmntok_t *description;
|
2019-12-12 00:55:45 +01:00
|
|
|
/* Optional preimage (iff status is successful) */
|
|
|
|
const jsmntok_t *preimage;
|
|
|
|
/* Only counts "complete" or "pending" payments. */
|
|
|
|
size_t num_nonfailed_parts;
|
|
|
|
/* Total amount sent ("complete" or "pending" only). */
|
|
|
|
struct amount_msat amount_sent;
|
2020-07-27 16:27:30 +02:00
|
|
|
|
|
|
|
/* Total amount received by the recipient ("complete" or "pending"
|
|
|
|
* only). Null if we have any part for which we didn't know the
|
|
|
|
* amount. */
|
|
|
|
struct amount_msat *amount;
|
2020-08-06 16:52:33 +02:00
|
|
|
|
|
|
|
/* Timestamp of the first part */
|
|
|
|
u32 timestamp;
|
2020-07-31 17:19:22 +02:00
|
|
|
|
2022-07-04 18:02:10 +02:00
|
|
|
/* Completion timestamp. The lowest `completed_at` value for a
|
|
|
|
* successful part. */
|
|
|
|
u64 success_at;
|
|
|
|
|
2020-07-31 17:19:22 +02:00
|
|
|
/* The destination of the payment, if specified. */
|
2020-08-20 15:40:34 +02:00
|
|
|
const jsmntok_t *destination;
|
2019-12-12 00:55:45 +01:00
|
|
|
|
2021-05-28 22:04:04 +02:00
|
|
|
/* Which sendpay group is this? Necessary for invoices that have been
|
|
|
|
* attempted multiple times. */
|
|
|
|
struct pay_sort_key sortkey;
|
2024-06-10 19:19:58 +02:00
|
|
|
|
|
|
|
/* 1-based index indicating order this payment was created in. */
|
|
|
|
u64 created_index;
|
|
|
|
/* 1-based index indicating order this payment was changed
|
|
|
|
* (only present if it has changed since creation). */
|
|
|
|
u64 updated_index;
|
2021-05-28 22:04:04 +02:00
|
|
|
};
|
2019-12-12 00:55:45 +01:00
|
|
|
|
2021-05-28 22:04:04 +02:00
|
|
|
static const struct pay_sort_key *pay_mpp_key(const struct pay_mpp *pm)
|
2019-12-12 00:55:45 +01:00
|
|
|
{
|
2021-05-28 22:04:04 +02:00
|
|
|
return &pm->sortkey;
|
2019-12-12 00:55:45 +01:00
|
|
|
}
|
|
|
|
|
2021-05-28 22:04:04 +02:00
|
|
|
static size_t pay_mpp_hash(const struct pay_sort_key *key)
|
2019-12-12 00:55:45 +01:00
|
|
|
{
|
2021-05-28 22:04:04 +02:00
|
|
|
struct siphash24_ctx ctx;
|
|
|
|
siphash24_init(&ctx, siphash_seed());
|
|
|
|
siphash24_update(&ctx, key->payment_hash, sizeof(struct sha256));
|
|
|
|
siphash24_update(&ctx, &key->groupid, sizeof(u64));
|
|
|
|
return siphash24_done(&ctx);
|
2019-12-12 00:55:45 +01:00
|
|
|
}
|
|
|
|
|
2021-05-28 22:04:04 +02:00
|
|
|
static bool pay_mpp_eq(const struct pay_mpp *pm, const struct pay_sort_key *key)
|
2021-05-26 23:56:58 +02:00
|
|
|
{
|
2024-07-18 03:24:55 +02:00
|
|
|
return sha256_eq(pm->sortkey.payment_hash, key->payment_hash)
|
|
|
|
&& pm->sortkey.groupid == key->groupid;
|
2021-05-26 23:56:58 +02:00
|
|
|
}
|
|
|
|
|
2020-05-26 13:23:25 +02:00
|
|
|
HTABLE_DEFINE_TYPE(struct pay_mpp, pay_mpp_key, pay_mpp_hash, pay_mpp_eq,
|
2019-12-12 00:55:45 +01:00
|
|
|
pay_map);
|
|
|
|
|
2020-01-26 15:44:16 +01:00
|
|
|
static void add_amount_sent(struct plugin *p,
|
2020-12-14 02:24:37 +01:00
|
|
|
const char *invstring,
|
2020-07-27 16:27:30 +02:00
|
|
|
struct pay_mpp *mpp,
|
2019-12-12 00:55:45 +01:00
|
|
|
const char *buf,
|
|
|
|
const jsmntok_t *t)
|
|
|
|
{
|
2020-07-27 16:27:30 +02:00
|
|
|
struct amount_msat sent, recv;
|
|
|
|
const jsmntok_t *msattok;
|
|
|
|
|
|
|
|
|
2019-12-12 00:55:45 +01:00
|
|
|
json_to_msat(buf, json_get_member(buf, t, "amount_sent_msat"), &sent);
|
2024-09-18 09:33:27 +02:00
|
|
|
if (!amount_msat_accumulate(&mpp->amount_sent, sent))
|
2020-01-26 15:44:16 +01:00
|
|
|
plugin_log(p, LOG_BROKEN,
|
2019-12-12 00:55:45 +01:00
|
|
|
"Cannot add amount_sent_msat for %s: %s + %s",
|
2020-12-14 02:24:37 +01:00
|
|
|
invstring,
|
2024-03-20 01:47:52 +01:00
|
|
|
fmt_amount_msat(tmpctx, mpp->amount_sent),
|
|
|
|
fmt_amount_msat(tmpctx, sent));
|
2020-07-27 16:27:30 +02:00
|
|
|
|
|
|
|
msattok = json_get_member(buf, t, "amount_msat");
|
|
|
|
|
|
|
|
/* If this is an unannotated partial payment we drop out estimate for
|
|
|
|
* all parts. */
|
2021-01-29 01:00:09 +01:00
|
|
|
if (msattok == NULL) {
|
2020-07-27 16:27:30 +02:00
|
|
|
mpp->amount = tal_free(mpp->amount);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we had a part of this multi-part payment for which we don't know
|
|
|
|
* the amount, then this is NULL. No point in summing up if we don't
|
|
|
|
* have the exact value.*/
|
|
|
|
if (mpp->amount == NULL)
|
|
|
|
return;
|
|
|
|
|
2021-01-29 01:00:09 +01:00
|
|
|
if (!json_to_msat(buf, msattok, &recv))
|
|
|
|
plugin_err(p, "Cannot convert amount_sat %.*s",
|
|
|
|
json_tok_full_len(msattok),
|
|
|
|
json_tok_full(buf, msattok));
|
|
|
|
|
2024-09-18 09:33:27 +02:00
|
|
|
if (!amount_msat_accumulate(mpp->amount, recv))
|
2020-07-27 16:27:30 +02:00
|
|
|
plugin_log(p, LOG_BROKEN,
|
|
|
|
"Cannot add amount_msat for %s: %s + %s",
|
2020-12-14 02:24:37 +01:00
|
|
|
invstring,
|
2024-03-20 01:47:52 +01:00
|
|
|
fmt_amount_msat(tmpctx, *mpp->amount),
|
|
|
|
fmt_amount_msat(tmpctx, sent));
|
2019-12-12 00:55:45 +01:00
|
|
|
}
|
|
|
|
|
2020-01-31 01:12:07 +01:00
|
|
|
static void add_new_entry(struct json_stream *ret,
|
2019-12-12 00:55:45 +01:00
|
|
|
const char *buf,
|
|
|
|
const struct pay_mpp *pm)
|
|
|
|
{
|
2020-01-31 01:12:07 +01:00
|
|
|
json_object_start(ret, NULL);
|
2020-12-14 02:24:37 +01:00
|
|
|
if (pm->invstring)
|
|
|
|
json_add_invstring(ret, pm->invstring);
|
2022-04-02 04:33:35 +02:00
|
|
|
if (pm->description)
|
|
|
|
json_add_tok(ret, "description", pm->description, buf);
|
2020-07-31 17:19:22 +02:00
|
|
|
if (pm->destination)
|
2020-08-20 15:40:34 +02:00
|
|
|
json_add_tok(ret, "destination", pm->destination, buf);
|
2020-07-31 17:19:22 +02:00
|
|
|
|
2020-07-29 10:48:47 +02:00
|
|
|
json_add_sha256(ret, "payment_hash", pm->payment_hash);
|
2021-04-28 14:01:20 +02:00
|
|
|
|
|
|
|
if (pm->state & PAYMENT_COMPLETE)
|
|
|
|
json_add_string(ret, "status", "complete");
|
|
|
|
else if (pm->state & PAYMENT_PENDING || attempt_ongoing(pm->payment_hash))
|
|
|
|
json_add_string(ret, "status", "pending");
|
|
|
|
else
|
|
|
|
json_add_string(ret, "status", "failed");
|
|
|
|
|
2020-08-06 16:52:33 +02:00
|
|
|
json_add_u32(ret, "created_at", pm->timestamp);
|
|
|
|
|
2022-07-04 18:02:10 +02:00
|
|
|
if (pm->success_at < UINT64_MAX)
|
|
|
|
json_add_u64(ret, "completed_at", pm->success_at);
|
|
|
|
|
2019-12-12 00:55:45 +01:00
|
|
|
if (pm->label)
|
2020-01-31 01:12:07 +01:00
|
|
|
json_add_tok(ret, "label", pm->label, buf);
|
2019-12-12 00:55:45 +01:00
|
|
|
if (pm->preimage)
|
2020-01-31 01:12:07 +01:00
|
|
|
json_add_tok(ret, "preimage", pm->preimage, buf);
|
2020-07-27 16:27:30 +02:00
|
|
|
|
|
|
|
/* This is only tallied for pending and successful payments, not
|
|
|
|
* failures. */
|
|
|
|
if (pm->amount != NULL && pm->num_nonfailed_parts > 0)
|
2023-03-14 06:21:50 +01:00
|
|
|
json_add_amount_msat(ret, "amount_msat", *pm->amount);
|
2020-07-27 16:27:30 +02:00
|
|
|
|
2023-03-14 06:21:50 +01:00
|
|
|
json_add_amount_msat(ret, "amount_sent_msat", pm->amount_sent);
|
2019-12-12 00:55:45 +01:00
|
|
|
|
|
|
|
if (pm->num_nonfailed_parts > 1)
|
2020-01-31 01:12:07 +01:00
|
|
|
json_add_u64(ret, "number_of_parts",
|
|
|
|
pm->num_nonfailed_parts);
|
2024-06-10 19:19:58 +02:00
|
|
|
|
|
|
|
json_add_u64(ret, "created_index", pm->created_index);
|
|
|
|
|
|
|
|
if(pm->updated_index)
|
|
|
|
json_add_u64(ret, "updated_index", pm->updated_index);
|
2020-01-31 01:12:07 +01:00
|
|
|
json_object_end(ret);
|
2019-12-12 00:55:45 +01:00
|
|
|
}
|
|
|
|
|
2019-02-23 06:00:04 +01:00
|
|
|
static struct command_result *listsendpays_done(struct command *cmd,
|
2024-11-07 02:27:37 +01:00
|
|
|
const char *method,
|
2019-02-23 06:00:04 +01:00
|
|
|
const char *buf,
|
|
|
|
const jsmntok_t *result,
|
2020-12-14 02:24:37 +01:00
|
|
|
char *invstring)
|
2019-02-23 06:00:04 +01:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
const jsmntok_t *t, *arr;
|
2020-01-31 01:12:07 +01:00
|
|
|
struct json_stream *ret;
|
2023-01-12 02:13:13 +01:00
|
|
|
struct pay_map *pay_map;
|
2019-12-12 00:55:45 +01:00
|
|
|
struct pay_mpp *pm;
|
2021-05-28 22:04:04 +02:00
|
|
|
struct pay_sort_key *order = tal_arr(tmpctx, struct pay_sort_key, 0);
|
2019-12-12 00:55:45 +01:00
|
|
|
|
2023-01-12 02:13:13 +01:00
|
|
|
pay_map = tal(cmd, struct pay_map);
|
|
|
|
pay_map_init(pay_map);
|
2019-02-23 06:00:04 +01:00
|
|
|
|
|
|
|
arr = json_get_member(buf, result, "payments");
|
|
|
|
if (!arr || arr->type != JSMN_ARRAY)
|
|
|
|
return command_fail(cmd, LIGHTNINGD,
|
2019-02-23 06:00:04 +01:00
|
|
|
"Unexpected non-array result from listsendpays");
|
2019-02-23 06:00:04 +01:00
|
|
|
|
|
|
|
json_for_each_arr(i, t, arr) {
|
2022-07-04 18:02:10 +02:00
|
|
|
const jsmntok_t *status, *invstrtok, *hashtok, *createdtok,
|
2024-06-10 19:19:58 +02:00
|
|
|
*completedtok, *grouptok, *created_indextok, *updated_indextok;
|
2020-12-14 02:24:37 +01:00
|
|
|
const char *invstr = invstring;
|
2020-05-26 13:23:25 +02:00
|
|
|
struct sha256 payment_hash;
|
2020-08-06 16:52:33 +02:00
|
|
|
u32 created_at;
|
2022-07-04 18:02:10 +02:00
|
|
|
u64 completed_at;
|
2021-05-28 22:04:04 +02:00
|
|
|
u64 groupid;
|
|
|
|
struct pay_sort_key key;
|
2024-06-10 19:19:58 +02:00
|
|
|
u64 created_index;
|
|
|
|
u64 updated_index;
|
2019-02-23 06:00:04 +01:00
|
|
|
|
2020-12-14 02:24:37 +01:00
|
|
|
invstrtok = json_get_member(buf, t, "bolt11");
|
|
|
|
if (!invstrtok)
|
|
|
|
invstrtok = json_get_member(buf, t, "bolt12");
|
2020-05-26 13:23:25 +02:00
|
|
|
hashtok = json_get_member(buf, t, "payment_hash");
|
2020-08-06 16:52:33 +02:00
|
|
|
createdtok = json_get_member(buf, t, "created_at");
|
2022-07-04 18:02:10 +02:00
|
|
|
completedtok = json_get_member(buf, t, "completed_at");
|
2020-05-26 13:23:25 +02:00
|
|
|
assert(hashtok != NULL);
|
2020-08-06 16:52:33 +02:00
|
|
|
assert(createdtok != NULL);
|
2019-02-23 06:00:04 +01:00
|
|
|
|
2022-07-04 18:02:10 +02:00
|
|
|
if (completedtok != NULL)
|
|
|
|
json_to_u64(buf, completedtok, &completed_at);
|
|
|
|
else
|
|
|
|
completed_at = UINT64_MAX;
|
|
|
|
|
2021-05-28 22:04:04 +02:00
|
|
|
grouptok = json_get_member(buf, t, "groupid");
|
|
|
|
if (grouptok != NULL)
|
|
|
|
json_to_u64(buf, grouptok, &groupid);
|
|
|
|
else
|
|
|
|
groupid = 0;
|
|
|
|
|
2020-05-26 13:23:25 +02:00
|
|
|
json_to_sha256(buf, hashtok, &payment_hash);
|
2020-08-06 16:52:33 +02:00
|
|
|
json_to_u32(buf, createdtok, &created_at);
|
2020-12-14 02:24:37 +01:00
|
|
|
if (invstrtok)
|
|
|
|
invstr = json_strdup(cmd, buf, invstrtok);
|
2019-12-12 00:55:45 +01:00
|
|
|
|
2021-05-28 22:04:04 +02:00
|
|
|
key.payment_hash = &payment_hash;
|
|
|
|
key.groupid = groupid;
|
|
|
|
|
2024-06-10 19:19:58 +02:00
|
|
|
created_indextok = json_get_member(buf, t, "created_index");
|
|
|
|
updated_indextok = json_get_member(buf, t, "updated_index");
|
|
|
|
assert(created_indextok != NULL);
|
|
|
|
json_to_u64(buf, created_indextok, &created_index);
|
|
|
|
if (updated_indextok != NULL)
|
|
|
|
json_to_u64(buf, updated_indextok, &updated_index);
|
|
|
|
else
|
|
|
|
updated_index = 0;
|
|
|
|
|
2023-01-12 02:13:13 +01:00
|
|
|
pm = pay_map_get(pay_map, &key);
|
2019-12-12 00:55:45 +01:00
|
|
|
if (!pm) {
|
|
|
|
pm = tal(cmd, struct pay_mpp);
|
2021-04-28 14:01:20 +02:00
|
|
|
pm->state = 0;
|
2020-05-26 13:23:25 +02:00
|
|
|
pm->payment_hash = tal_dup(pm, struct sha256, &payment_hash);
|
2020-12-14 02:24:37 +01:00
|
|
|
pm->invstring = tal_steal(pm, invstr);
|
2020-08-20 15:40:34 +02:00
|
|
|
pm->destination = json_get_member(buf, t, "destination");
|
2019-12-12 00:55:45 +01:00
|
|
|
pm->label = json_get_member(buf, t, "label");
|
2022-04-02 04:33:35 +02:00
|
|
|
pm->description = json_get_member(buf, t, "description");
|
2019-12-12 00:55:45 +01:00
|
|
|
pm->preimage = NULL;
|
|
|
|
pm->amount_sent = AMOUNT_MSAT(0);
|
2020-07-27 16:27:30 +02:00
|
|
|
pm->amount = talz(pm, struct amount_msat);
|
2019-12-12 00:55:45 +01:00
|
|
|
pm->num_nonfailed_parts = 0;
|
2020-08-06 16:52:33 +02:00
|
|
|
pm->timestamp = created_at;
|
2021-05-28 22:04:04 +02:00
|
|
|
pm->sortkey.payment_hash = pm->payment_hash;
|
|
|
|
pm->sortkey.groupid = groupid;
|
2022-07-04 18:02:10 +02:00
|
|
|
pm->success_at = UINT64_MAX;
|
2024-06-10 19:19:58 +02:00
|
|
|
pm->created_index = created_index;
|
|
|
|
pm->updated_index = updated_index;
|
2023-01-12 02:13:13 +01:00
|
|
|
pay_map_add(pay_map, pm);
|
2021-05-28 22:04:04 +02:00
|
|
|
// First time we see the groupid we add it to the order
|
|
|
|
// array, so we can retrieve them in the correct order.
|
|
|
|
tal_arr_expand(&order, pm->sortkey);
|
2022-04-04 04:56:52 +02:00
|
|
|
} else {
|
|
|
|
/* Not all payments have bolt11/bolt12 or
|
|
|
|
* description, as an optimization */
|
|
|
|
if (!pm->invstring)
|
|
|
|
pm->invstring = tal_steal(pm, invstr);
|
|
|
|
if (!pm->description)
|
|
|
|
pm->description = json_get_member(buf, t, "description");
|
2024-06-10 19:19:58 +02:00
|
|
|
/* What's the "created_index" of the merged record?
|
|
|
|
* We take the *lowest*, since that will never change
|
|
|
|
* (unless they delete payments!). */
|
|
|
|
if (created_index < pm->created_index)
|
|
|
|
pm->created_index = created_index;
|
|
|
|
/* On the other hand, we take the *highest*
|
|
|
|
* updated_index, so we see any changes. */
|
|
|
|
if (updated_index > pm->updated_index)
|
|
|
|
pm->updated_index = updated_index;
|
2019-12-12 00:55:45 +01:00
|
|
|
}
|
|
|
|
|
2019-05-21 02:27:14 +02:00
|
|
|
status = json_get_member(buf, t, "status");
|
2019-12-12 00:55:45 +01:00
|
|
|
if (json_tok_streq(buf, status, "complete")) {
|
2020-12-14 02:24:37 +01:00
|
|
|
add_amount_sent(cmd->plugin, pm->invstring, pm, buf, t);
|
2019-12-12 00:55:45 +01:00
|
|
|
pm->num_nonfailed_parts++;
|
|
|
|
pm->preimage
|
|
|
|
= json_get_member(buf, t, "payment_preimage");
|
2021-04-28 14:01:20 +02:00
|
|
|
pm->state |= PAYMENT_COMPLETE;
|
2022-07-04 18:02:10 +02:00
|
|
|
|
|
|
|
/* We count down from UINT64_MAX. */
|
|
|
|
if (pm->success_at > completed_at)
|
|
|
|
pm->success_at = completed_at;
|
|
|
|
|
2019-12-12 00:55:45 +01:00
|
|
|
} else if (json_tok_streq(buf, status, "pending")) {
|
2020-12-14 02:24:37 +01:00
|
|
|
add_amount_sent(cmd->plugin, pm->invstring, pm, buf, t);
|
2019-12-12 00:55:45 +01:00
|
|
|
pm->num_nonfailed_parts++;
|
2021-04-28 14:01:20 +02:00
|
|
|
pm->state |= PAYMENT_PENDING;
|
2019-12-12 00:55:45 +01:00
|
|
|
} else {
|
2021-04-28 14:01:20 +02:00
|
|
|
pm->state |= PAYMENT_FAILED;
|
2019-05-21 02:27:14 +02:00
|
|
|
}
|
2019-02-23 06:00:04 +01:00
|
|
|
}
|
2019-12-12 00:55:45 +01:00
|
|
|
|
2021-05-26 23:56:58 +02:00
|
|
|
ret = jsonrpc_stream_success(cmd);
|
|
|
|
json_array_start(ret, "pays");
|
2022-07-25 09:00:09 +02:00
|
|
|
for (i = 0; i < tal_count(order); i++) {
|
2023-01-12 02:13:13 +01:00
|
|
|
pm = pay_map_get(pay_map, &order[i]);
|
2021-05-28 22:04:04 +02:00
|
|
|
assert(pm != NULL);
|
|
|
|
add_new_entry(ret, buf, pm);
|
|
|
|
}
|
2020-01-31 01:12:07 +01:00
|
|
|
json_array_end(ret);
|
|
|
|
return command_finished(cmd, ret);
|
2019-02-23 06:00:04 +01:00
|
|
|
}
|
|
|
|
|
2019-02-23 06:00:04 +01:00
|
|
|
static struct command_result *json_listpays(struct command *cmd,
|
|
|
|
const char *buf,
|
|
|
|
const jsmntok_t *params)
|
2019-02-23 06:00:04 +01:00
|
|
|
{
|
2021-09-06 10:51:21 +02:00
|
|
|
const char *invstring, *status_str;
|
2020-07-29 10:48:47 +02:00
|
|
|
struct sha256 *payment_hash;
|
2020-01-30 00:55:55 +01:00
|
|
|
struct out_req *req;
|
2024-06-10 19:19:58 +02:00
|
|
|
const char *listindex;
|
|
|
|
u64 *liststart;
|
|
|
|
u32 *listlimit;
|
2019-02-23 06:00:04 +01:00
|
|
|
|
|
|
|
/* FIXME: would be nice to parse as a bolt11 so check worked in future */
|
|
|
|
if (!param(cmd, buf, params,
|
2020-12-14 02:25:37 +01:00
|
|
|
/* FIXME: parameter should be invstring now */
|
2023-06-13 12:33:31 +02:00
|
|
|
p_opt("bolt11", param_invstring, &invstring),
|
2020-07-29 10:48:47 +02:00
|
|
|
p_opt("payment_hash", param_sha256, &payment_hash),
|
2021-09-06 10:51:21 +02:00
|
|
|
p_opt("status", param_string, &status_str),
|
2024-06-10 19:19:58 +02:00
|
|
|
p_opt("index", param_string, &listindex),
|
|
|
|
p_opt_def("start", param_u64, &liststart, 0),
|
|
|
|
p_opt("limit", param_u32, &listlimit),
|
2019-02-23 06:00:04 +01:00
|
|
|
NULL))
|
2019-09-18 12:14:44 +02:00
|
|
|
return command_param_failed();
|
2019-02-23 06:00:04 +01:00
|
|
|
|
2024-06-10 19:19:58 +02:00
|
|
|
if (*liststart != 0 && !listindex) {
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"Can only specify {start} with {index}");
|
|
|
|
}
|
|
|
|
if (listlimit && !listindex) {
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"Can only specify {limit} with {index}");
|
|
|
|
}
|
|
|
|
|
2024-11-06 12:00:36 +01:00
|
|
|
req = jsonrpc_request_start(cmd, "listsendpays",
|
2020-01-30 00:55:55 +01:00
|
|
|
listsendpays_done, forward_error,
|
2020-12-14 02:24:37 +01:00
|
|
|
cast_const(char *, invstring));
|
|
|
|
if (invstring)
|
|
|
|
json_add_string(req->js, "bolt11", invstring);
|
2020-07-29 10:48:47 +02:00
|
|
|
|
|
|
|
if (payment_hash)
|
|
|
|
json_add_sha256(req->js, "payment_hash", payment_hash);
|
2020-07-31 17:19:22 +02:00
|
|
|
|
2021-09-06 10:51:21 +02:00
|
|
|
if (status_str)
|
|
|
|
json_add_string(req->js, "status", status_str);
|
2024-06-10 19:19:58 +02:00
|
|
|
|
|
|
|
if (listindex){
|
|
|
|
json_add_string(req->js, "index", listindex);
|
|
|
|
if (liststart)
|
|
|
|
json_add_u64(req->js, "start", *liststart);
|
|
|
|
if (listlimit)
|
|
|
|
json_add_u32(req->js, "limit", *listlimit);
|
|
|
|
}
|
2024-11-06 12:00:36 +01:00
|
|
|
return send_outreq(req);
|
2019-02-23 06:00:04 +01:00
|
|
|
}
|
|
|
|
|
2022-02-26 01:50:33 +01:00
|
|
|
static void memleak_mark_payments(struct plugin *p, struct htable *memtable)
|
|
|
|
{
|
2022-09-16 05:14:39 +02:00
|
|
|
memleak_scan_list_head(memtable, &payments);
|
2022-02-26 01:50:33 +01:00
|
|
|
}
|
|
|
|
|
2024-11-06 12:00:36 +01:00
|
|
|
static const char *init(struct command *init_cmd,
|
2021-01-13 04:00:24 +01:00
|
|
|
const char *buf UNUSED, const jsmntok_t *config UNUSED)
|
2019-01-15 05:20:27 +01:00
|
|
|
{
|
2024-11-06 12:00:36 +01:00
|
|
|
rpc_scan(init_cmd, "getinfo", take(json_out_obj(NULL, NULL, NULL)),
|
2021-01-06 06:41:20 +01:00
|
|
|
"{id:%}", JSON_SCAN(json_to_node_id, &my_id));
|
|
|
|
|
2024-03-18 05:07:02 +01:00
|
|
|
/* BOLT #4:
|
|
|
|
* ## `max_htlc_cltv` Selection
|
|
|
|
*
|
|
|
|
* This ... value is defined as 2016 blocks, based on historical value
|
|
|
|
* deployed by Lightning implementations.
|
|
|
|
*/
|
|
|
|
/* FIXME: Typo in spec for CLTV in descripton! But it breaks our spelling check, so we omit it above */
|
|
|
|
maxdelay_default = 2016;
|
2024-07-29 15:02:51 +02:00
|
|
|
|
2024-11-06 12:00:36 +01:00
|
|
|
global_hints = notleak_with_children(channel_hint_set_new(init_cmd->plugin));
|
2024-07-29 15:02:51 +02:00
|
|
|
|
2024-11-06 12:00:36 +01:00
|
|
|
/* max-locktime-blocks deprecated in v24.05, but still grab it! */
|
|
|
|
rpc_scan(init_cmd, "listconfigs", take(json_out_obj(NULL, NULL, NULL)),
|
|
|
|
"{configs:"
|
2024-11-18 05:41:15 +01:00
|
|
|
"{max-locktime-blocks?:{value_int:%}}}",
|
|
|
|
JSON_SCAN(json_to_number, &maxdelay_default));
|
2021-01-13 04:00:24 +01:00
|
|
|
|
2024-11-06 12:00:36 +01:00
|
|
|
plugin_set_memleak_handler(init_cmd->plugin, memleak_mark_payments);
|
2021-01-13 04:00:24 +01:00
|
|
|
return NULL;
|
2019-01-15 05:20:27 +01:00
|
|
|
}
|
|
|
|
|
2021-10-04 21:13:11 +02:00
|
|
|
static void on_payment_success(struct payment *payment)
|
|
|
|
{
|
2022-08-03 16:56:55 +02:00
|
|
|
struct payment *p, *nxt;
|
2021-10-05 18:04:18 +02:00
|
|
|
struct payment_tree_result result = payment_collect_result(payment);
|
|
|
|
struct json_stream *ret;
|
2022-08-03 16:50:39 +02:00
|
|
|
struct command *cmd;
|
2021-10-05 18:04:18 +02:00
|
|
|
assert(result.treestates & PAYMENT_STEP_SUCCESS);
|
|
|
|
assert(result.leafstates & PAYMENT_STEP_SUCCESS);
|
|
|
|
assert(result.preimage != NULL);
|
|
|
|
|
|
|
|
/* Iterate through any pending payments we suspended and
|
|
|
|
* terminate them. */
|
|
|
|
|
2022-08-03 16:56:55 +02:00
|
|
|
list_for_each_safe(&payments, p, nxt, list) {
|
2021-10-05 18:04:18 +02:00
|
|
|
/* The result for the active payment is returned in
|
|
|
|
* `payment_finished`. */
|
|
|
|
if (payment == p)
|
|
|
|
continue;
|
2022-08-03 16:47:18 +02:00
|
|
|
|
|
|
|
/* Both groupid and payment_hash must match. This is
|
|
|
|
* because when we suspended the payment itself, we
|
|
|
|
* set the groupid to match. */
|
|
|
|
if (!sha256_eq(payment->payment_hash, p->payment_hash) ||
|
|
|
|
payment->groupid != p->groupid)
|
2021-10-05 18:04:18 +02:00
|
|
|
continue;
|
2024-11-06 11:56:36 +01:00
|
|
|
if (p->finished)
|
2021-10-05 18:04:18 +02:00
|
|
|
continue;
|
|
|
|
|
2022-08-03 16:50:39 +02:00
|
|
|
cmd = p->cmd;
|
2024-11-06 11:56:36 +01:00
|
|
|
p->cmd = aux_command(cmd);
|
|
|
|
p->finished = true;
|
2022-08-03 16:50:39 +02:00
|
|
|
|
|
|
|
ret = jsonrpc_stream_success(cmd);
|
2024-07-17 10:58:11 +02:00
|
|
|
json_add_node_id(ret, "destination", p->pay_destination);
|
2021-10-05 18:04:18 +02:00
|
|
|
json_add_sha256(ret, "payment_hash", p->payment_hash);
|
|
|
|
json_add_timeabs(ret, "created_at", p->start_time);
|
|
|
|
json_add_num(ret, "parts", result.attempts);
|
|
|
|
|
2024-03-19 05:29:59 +01:00
|
|
|
json_add_amount_msat(ret, "amount_msat", p->our_amount);
|
2023-03-14 06:19:50 +01:00
|
|
|
json_add_amount_msat(ret, "amount_sent_msat", result.sent);
|
2021-10-05 18:04:18 +02:00
|
|
|
|
|
|
|
if (result.leafstates != PAYMENT_STEP_SUCCESS)
|
|
|
|
json_add_string(
|
|
|
|
ret, "warning_partial_completion",
|
|
|
|
"Some parts of the payment are not yet "
|
|
|
|
"completed, but we have the confirmation "
|
|
|
|
"from the recipient.");
|
|
|
|
json_add_preimage(ret, "payment_preimage", result.preimage);
|
|
|
|
|
|
|
|
json_add_string(ret, "status", "complete");
|
2022-08-03 16:50:39 +02:00
|
|
|
if (command_finished(cmd, ret)) {/* Ignore result. */}
|
2021-10-05 18:04:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void payment_add_attempt(struct json_stream *s, const char *fieldname, struct payment *p, bool recurse)
|
|
|
|
{
|
|
|
|
bool finished = p->step >= PAYMENT_STEP_RETRY,
|
|
|
|
success = p->step == PAYMENT_STEP_SUCCESS;
|
|
|
|
|
|
|
|
/* A fieldname is only reasonable if we're not recursing. Otherwise the
|
|
|
|
* fieldname would be reused for all attempts. */
|
|
|
|
assert(!recurse || fieldname == NULL);
|
|
|
|
|
|
|
|
json_object_start(s, fieldname);
|
|
|
|
|
|
|
|
if (!finished)
|
|
|
|
json_add_string(s, "status", "pending");
|
|
|
|
else if (success)
|
|
|
|
json_add_string(s, "status", "success");
|
|
|
|
else
|
|
|
|
json_add_string(s, "status", "failed");
|
|
|
|
|
|
|
|
if (p->failreason != NULL)
|
|
|
|
json_add_string(s, "failreason", p->failreason);
|
|
|
|
|
|
|
|
json_add_u64(s, "partid", p->partid);
|
2024-03-19 05:29:59 +01:00
|
|
|
json_add_amount_msat(s, "amount_msat", p->our_amount);
|
2021-10-05 18:04:18 +02:00
|
|
|
if (p->parent != NULL)
|
|
|
|
json_add_u64(s, "parent_partid", p->parent->partid);
|
|
|
|
|
|
|
|
json_object_end(s);
|
|
|
|
for (size_t i=0; i<tal_count(p->children); i++) {
|
|
|
|
payment_add_attempt(s, fieldname, p->children[i], recurse);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void payment_json_add_attempts(struct json_stream *s,
|
|
|
|
const char *fieldname, struct payment *p)
|
|
|
|
{
|
|
|
|
assert(p == payment_root(p));
|
|
|
|
json_array_start(s, fieldname);
|
|
|
|
payment_add_attempt(s, NULL, p, true);
|
|
|
|
json_array_end(s);
|
2021-10-04 21:13:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void on_payment_failure(struct payment *payment)
|
|
|
|
{
|
2022-08-03 16:56:55 +02:00
|
|
|
struct payment *p, *nxt;
|
2021-10-05 18:04:18 +02:00
|
|
|
struct payment_tree_result result = payment_collect_result(payment);
|
2022-08-03 16:56:55 +02:00
|
|
|
list_for_each_safe(&payments, p, nxt, list)
|
2021-10-05 18:04:18 +02:00
|
|
|
{
|
|
|
|
struct json_stream *ret;
|
|
|
|
struct command *cmd;
|
|
|
|
const char *msg;
|
|
|
|
/* The result for the active payment is returned in
|
|
|
|
* `payment_finished`. */
|
|
|
|
if (payment == p)
|
|
|
|
continue;
|
2022-08-03 16:47:18 +02:00
|
|
|
|
|
|
|
/* When we suspended we've set the groupid to match so
|
|
|
|
* we'd know which calls were duplicates. */
|
|
|
|
if (!sha256_eq(payment->payment_hash, p->payment_hash) ||
|
|
|
|
payment->groupid != p->groupid)
|
2021-10-05 18:04:18 +02:00
|
|
|
continue;
|
2024-11-06 11:56:36 +01:00
|
|
|
if (p->finished)
|
2021-10-05 18:04:18 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
cmd = p->cmd;
|
2024-11-06 11:56:36 +01:00
|
|
|
p->cmd = aux_command(cmd);
|
|
|
|
p->finished = true;
|
2021-10-05 18:04:18 +02:00
|
|
|
if (p->aborterror != NULL) {
|
|
|
|
/* We set an explicit toplevel error message,
|
|
|
|
* so let's report that. */
|
|
|
|
ret = jsonrpc_stream_fail(cmd, PAY_STOPPED_RETRYING,
|
|
|
|
p->aborterror);
|
|
|
|
payment_json_add_attempts(ret, "attempts", p);
|
|
|
|
|
|
|
|
if (command_finished(cmd, ret)) {/* Ignore result. */}
|
|
|
|
} else if (result.failure == NULL || result.failure->failcode < NODE) {
|
|
|
|
/* This is failing because we have no more routes to try */
|
|
|
|
msg = tal_fmt(cmd,
|
|
|
|
"Ran out of routes to try after "
|
|
|
|
"%d attempt%s: see `paystatus`",
|
|
|
|
result.attempts,
|
|
|
|
result.attempts == 1 ? "" : "s");
|
|
|
|
ret = jsonrpc_stream_fail(cmd, PAY_STOPPED_RETRYING,
|
|
|
|
msg);
|
|
|
|
payment_json_add_attempts(ret, "attempts", p);
|
|
|
|
|
|
|
|
if (command_finished(cmd, ret)) {/* Ignore result. */}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
struct payment_result *failure = result.failure;
|
|
|
|
assert(failure!= NULL);
|
|
|
|
|
|
|
|
ret = jsonrpc_stream_fail(cmd, failure->code,
|
|
|
|
failure->message);
|
|
|
|
|
|
|
|
json_add_u64(ret, "id", failure->id);
|
|
|
|
|
|
|
|
json_add_u32(ret, "failcode", failure->failcode);
|
|
|
|
json_add_string(ret, "failcodename",
|
|
|
|
failure->failcodename);
|
|
|
|
|
|
|
|
if (p->invstring)
|
|
|
|
json_add_invstring(ret, p->invstring);
|
|
|
|
|
|
|
|
json_add_hex_talarr(ret, "raw_message",
|
|
|
|
result.failure->raw_message);
|
|
|
|
json_add_num(ret, "created_at", p->start_time.ts.tv_sec);
|
2024-07-17 10:58:11 +02:00
|
|
|
json_add_node_id(ret, "destination", p->pay_destination);
|
2021-10-05 18:04:18 +02:00
|
|
|
json_add_sha256(ret, "payment_hash", p->payment_hash);
|
|
|
|
// OK
|
|
|
|
if (result.leafstates & PAYMENT_STEP_SUCCESS) {
|
|
|
|
/* If one sub-payment succeeded then we have
|
|
|
|
* proof of payment, and the payment is a
|
|
|
|
* success. */
|
|
|
|
json_add_string(ret, "status", "complete");
|
|
|
|
|
|
|
|
} else if (result.leafstates & ~PAYMENT_STEP_FAILED) {
|
|
|
|
/* If there are non-failed leafs we are still trying. */
|
|
|
|
json_add_string(ret, "status", "pending");
|
|
|
|
|
|
|
|
} else {
|
|
|
|
json_add_string(ret, "status", "failed");
|
|
|
|
}
|
|
|
|
|
2024-03-19 05:29:59 +01:00
|
|
|
json_add_amount_msat(ret, "amount_msat", p->our_amount);
|
2021-10-05 18:04:18 +02:00
|
|
|
|
2023-03-14 06:19:50 +01:00
|
|
|
json_add_amount_msat(ret, "amount_sent_msat",
|
|
|
|
result.sent);
|
2021-10-05 18:04:18 +02:00
|
|
|
|
|
|
|
if (failure != NULL) {
|
|
|
|
if (failure->erring_index)
|
|
|
|
json_add_num(ret, "erring_index",
|
|
|
|
*failure->erring_index);
|
|
|
|
|
|
|
|
if (failure->erring_node)
|
|
|
|
json_add_node_id(ret, "erring_node",
|
|
|
|
failure->erring_node);
|
|
|
|
|
|
|
|
if (failure->erring_channel)
|
|
|
|
json_add_short_channel_id(
|
|
|
|
ret, "erring_channel",
|
2024-03-20 02:59:51 +01:00
|
|
|
*failure->erring_channel);
|
2021-10-05 18:04:18 +02:00
|
|
|
|
|
|
|
if (failure->erring_direction)
|
|
|
|
json_add_num(
|
|
|
|
ret, "erring_direction",
|
|
|
|
*failure->erring_direction);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (command_finished(cmd, ret)) { /* Ignore result. */}
|
|
|
|
}
|
|
|
|
}
|
2021-10-04 21:13:11 +02:00
|
|
|
}
|
|
|
|
|
2023-07-22 13:26:27 +02:00
|
|
|
static struct command_result *selfpay_success(struct command *cmd,
|
2024-11-07 02:27:37 +01:00
|
|
|
const char *method,
|
2023-07-22 13:26:27 +02:00
|
|
|
const char *buf,
|
|
|
|
const jsmntok_t *result,
|
|
|
|
struct payment *p)
|
|
|
|
{
|
|
|
|
struct json_stream *ret = jsonrpc_stream_success(cmd);
|
|
|
|
struct preimage preimage;
|
|
|
|
const char *err;
|
|
|
|
|
|
|
|
err = json_scan(tmpctx, buf, result,
|
|
|
|
"{payment_preimage:%}",
|
|
|
|
JSON_SCAN(json_to_preimage, &preimage));
|
|
|
|
if (err)
|
|
|
|
plugin_err(p->plugin,
|
|
|
|
"selfpay didn't have payment_preimage? %.*s",
|
|
|
|
json_tok_full_len(result),
|
|
|
|
json_tok_full(buf, result));
|
|
|
|
json_add_payment_success(ret, p, &preimage, NULL);
|
|
|
|
return command_finished(cmd, ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct command_result *selfpay(struct command *cmd, struct payment *p)
|
|
|
|
{
|
|
|
|
struct out_req *req;
|
|
|
|
|
|
|
|
/* This "struct payment" simply gets freed once command is done. */
|
|
|
|
tal_steal(cmd, p);
|
|
|
|
|
2024-11-06 12:00:36 +01:00
|
|
|
req = jsonrpc_request_start(cmd, "sendpay",
|
2023-07-22 13:26:27 +02:00
|
|
|
selfpay_success,
|
|
|
|
forward_error, p);
|
|
|
|
/* Empty route means "to-self" */
|
|
|
|
json_array_start(req->js, "route");
|
|
|
|
json_array_end(req->js);
|
|
|
|
json_add_sha256(req->js, "payment_hash", p->payment_hash);
|
|
|
|
if (p->label)
|
|
|
|
json_add_string(req->js, "label", p->label);
|
2024-03-19 05:29:59 +01:00
|
|
|
/* FIXME: This will fail if we try to do a partial amount to
|
|
|
|
* ourselves! */
|
|
|
|
json_add_amount_msat(req->js, "amount_msat", p->our_amount);
|
2023-07-22 13:26:27 +02:00
|
|
|
json_add_string(req->js, "bolt11", p->invstring);
|
|
|
|
if (p->payment_secret)
|
|
|
|
json_add_secret(req->js, "payment_secret", p->payment_secret);
|
|
|
|
json_add_u64(req->js, "groupid", p->groupid);
|
|
|
|
if (p->payment_metadata)
|
|
|
|
json_add_hex_talarr(req->js, "payment_metadata", p->payment_metadata);
|
|
|
|
if (p->description)
|
|
|
|
json_add_string(req->js, "description", p->description);
|
2024-11-06 12:00:36 +01:00
|
|
|
return send_outreq(req);
|
2023-07-22 13:26:27 +02:00
|
|
|
}
|
|
|
|
|
2021-09-29 17:56:54 +02:00
|
|
|
/* We are interested in any prior attempts to pay this payment_hash /
|
|
|
|
* invoice so we can set the `groupid` correctly and ensure we don't
|
2021-09-30 18:38:48 +02:00
|
|
|
* already have a pending payment running. We also collect the summary
|
|
|
|
* about an eventual previous complete payment so we can return that
|
|
|
|
* as a no-op. */
|
2021-09-29 17:56:54 +02:00
|
|
|
static struct command_result *
|
2024-11-07 02:27:37 +01:00
|
|
|
payment_listsendpays_previous(struct command *cmd,
|
|
|
|
const char *method,
|
|
|
|
const char *buf,
|
|
|
|
const jsmntok_t *result,
|
|
|
|
struct payment *p)
|
2021-09-29 17:56:54 +02:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
const jsmntok_t *t, *arr, *err;
|
|
|
|
/* What was the groupid of an eventual previous attempt? */
|
|
|
|
u64 last_group = 0;
|
|
|
|
/* Do we have pending sendpays for the previous attempt? */
|
|
|
|
bool pending = false;
|
2022-08-03 16:45:44 +02:00
|
|
|
|
|
|
|
/* Group ID of the first pending payment, this will be the one
|
|
|
|
* who's result gets replayed if we end up suspending. */
|
|
|
|
u64 pending_group_id = 0;
|
2021-09-29 17:56:54 +02:00
|
|
|
/* Did a prior attempt succeed? */
|
|
|
|
bool completed = false;
|
|
|
|
|
2021-09-30 18:38:48 +02:00
|
|
|
/* Metadata for a complete payment, if one exists. */
|
|
|
|
struct json_stream *ret;
|
|
|
|
u32 parts = 0;
|
|
|
|
struct preimage preimage;
|
|
|
|
struct amount_msat sent, msat;
|
|
|
|
struct node_id destination;
|
|
|
|
u32 created_at;
|
|
|
|
|
2021-09-29 17:56:54 +02:00
|
|
|
err = json_get_member(buf, result, "error");
|
|
|
|
if (err)
|
|
|
|
return command_fail(
|
|
|
|
cmd, LIGHTNINGD,
|
|
|
|
"Error retrieving previous pay attempts: %s",
|
|
|
|
json_strdup(tmpctx, buf, err));
|
|
|
|
|
|
|
|
arr = json_get_member(buf, result, "payments");
|
|
|
|
if (!arr || arr->type != JSMN_ARRAY)
|
|
|
|
return command_fail(
|
|
|
|
cmd, LIGHTNINGD,
|
|
|
|
"Unexpected non-array result from listsendpays");
|
|
|
|
|
|
|
|
/* We iterate through all prior sendpays, looking for the
|
|
|
|
* latest group and remembering what its state is. */
|
|
|
|
json_for_each_arr(i, t, arr)
|
|
|
|
{
|
|
|
|
u64 groupid;
|
|
|
|
const jsmntok_t *status, *grouptok;
|
2021-09-30 18:38:48 +02:00
|
|
|
struct amount_msat diff_sent, diff_msat;
|
2021-09-29 17:56:54 +02:00
|
|
|
grouptok = json_get_member(buf, t, "groupid");
|
|
|
|
json_to_u64(buf, grouptok, &groupid);
|
|
|
|
|
|
|
|
/* New group, reset what we collected. */
|
|
|
|
if (last_group != groupid) {
|
|
|
|
completed = false;
|
|
|
|
pending = false;
|
|
|
|
last_group = groupid;
|
2021-09-30 18:38:48 +02:00
|
|
|
|
|
|
|
parts = 1;
|
|
|
|
json_scan(tmpctx, buf, t,
|
|
|
|
"{destination:%"
|
|
|
|
",created_at:%"
|
|
|
|
",amount_msat:%"
|
|
|
|
",amount_sent_msat:%"
|
|
|
|
",payment_preimage:%}",
|
|
|
|
JSON_SCAN(json_to_node_id, &destination),
|
|
|
|
JSON_SCAN(json_to_u32, &created_at),
|
|
|
|
JSON_SCAN(json_to_msat, &msat),
|
|
|
|
JSON_SCAN(json_to_msat, &sent),
|
|
|
|
JSON_SCAN(json_to_preimage, &preimage));
|
|
|
|
} else {
|
|
|
|
json_scan(tmpctx, buf, t,
|
|
|
|
"{amount_msat:%"
|
|
|
|
",amount_sent_msat:%}",
|
|
|
|
JSON_SCAN(json_to_msat, &diff_msat),
|
|
|
|
JSON_SCAN(json_to_msat, &diff_sent));
|
2024-09-18 09:33:27 +02:00
|
|
|
if (!amount_msat_accumulate(&msat, diff_msat) ||
|
|
|
|
!amount_msat_accumulate(&sent, diff_sent))
|
2021-09-30 18:38:48 +02:00
|
|
|
plugin_err(p->plugin,
|
|
|
|
"msat overflow adding up parts");
|
|
|
|
parts++;
|
2021-09-29 17:56:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
status = json_get_member(buf, t, "status");
|
|
|
|
completed |= json_tok_streq(buf, status, "complete");
|
|
|
|
pending |= json_tok_streq(buf, status, "pending");
|
2022-08-03 16:45:44 +02:00
|
|
|
|
|
|
|
/* Remember the group id of the first pending group so
|
|
|
|
* we can replay its result later. */
|
|
|
|
if (!pending_group_id && pending)
|
|
|
|
pending_group_id = groupid;
|
2021-09-29 17:56:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (completed) {
|
2021-09-30 18:38:48 +02:00
|
|
|
ret = jsonrpc_stream_success(cmd);
|
|
|
|
json_add_preimage(ret, "payment_preimage", &preimage);
|
|
|
|
json_add_string(ret, "status", "complete");
|
2023-03-14 06:19:50 +01:00
|
|
|
json_add_amount_msat(ret, "amount_msat", msat);
|
|
|
|
json_add_amount_msat(ret, "amount_sent_msat", sent);
|
2024-07-17 10:58:11 +02:00
|
|
|
json_add_node_id(ret, "destination", p->pay_destination);
|
2021-09-30 18:38:48 +02:00
|
|
|
json_add_sha256(ret, "payment_hash", p->payment_hash);
|
|
|
|
json_add_u32(ret, "created_at", created_at);
|
|
|
|
json_add_num(ret, "parts", parts);
|
|
|
|
return command_finished(cmd, ret);
|
2021-09-29 17:56:54 +02:00
|
|
|
} else if (pending) {
|
2021-10-05 18:04:18 +02:00
|
|
|
/* We suspend this call and wait for the
|
|
|
|
* `on_payment_success` or `on_payment_failure`
|
|
|
|
* handler of the currently running payment to notify
|
2022-08-03 16:45:44 +02:00
|
|
|
* us about its completion. We latch on to the result
|
|
|
|
* from the call we extracted above. */
|
|
|
|
p->groupid = pending_group_id;
|
2021-10-05 18:04:18 +02:00
|
|
|
return command_still_pending(cmd);
|
2021-09-29 17:56:54 +02:00
|
|
|
}
|
|
|
|
p->groupid = last_group + 1;
|
2021-10-04 21:13:11 +02:00
|
|
|
p->on_payment_success = on_payment_success;
|
|
|
|
p->on_payment_failure = on_payment_failure;
|
2023-07-22 13:26:27 +02:00
|
|
|
|
|
|
|
/* Bypass everything if we're doing (synchronous) self-pay */
|
2024-07-17 10:58:11 +02:00
|
|
|
if (node_id_eq(&my_id, p->pay_destination))
|
2023-07-22 13:26:27 +02:00
|
|
|
return selfpay(cmd, p);
|
|
|
|
|
2021-09-29 17:56:54 +02:00
|
|
|
payment_start(p);
|
|
|
|
return command_still_pending(cmd);
|
|
|
|
}
|
|
|
|
|
2020-05-29 15:16:40 +02:00
|
|
|
struct payment_modifier *paymod_mods[] = {
|
2021-11-18 20:24:24 +01:00
|
|
|
/* NOTE: The order in which these four paymods are executed is
|
|
|
|
* significant!
|
|
|
|
* local_channel_hints *must* execute first before route_exclusions
|
|
|
|
* which *must* execute before directpay.
|
|
|
|
* exemptfee *must* also execute before directpay.
|
|
|
|
*/
|
2020-06-09 18:30:14 +02:00
|
|
|
&local_channel_hints_pay_mod,
|
2021-11-18 20:24:24 +01:00
|
|
|
&route_exclusions_pay_mod,
|
2020-06-03 17:58:20 +02:00
|
|
|
&exemptfee_pay_mod,
|
2020-07-18 10:10:51 +02:00
|
|
|
&directpay_pay_mod,
|
|
|
|
&shadowroute_pay_mod,
|
2023-07-14 12:53:13 +02:00
|
|
|
/* NOTE: The order in which these two paymods are executed is
|
|
|
|
* significant! `routehints` *must* execute first before
|
|
|
|
* payee_incoming_limit.
|
2020-08-06 06:56:25 +02:00
|
|
|
*
|
|
|
|
* FIXME: Giving an ordered list of paymods to the paymod
|
|
|
|
* system is the wrong interface, given that the order in
|
2023-07-14 12:53:13 +02:00
|
|
|
* which paymods execute is significant. (This is typical of
|
|
|
|
* Entity-Component-System pattern.) What should be done is
|
|
|
|
* that libplugin-pay should have a canonical list of paymods
|
|
|
|
* in the order they execute correctly, and whether they are
|
|
|
|
* default-enabled/default-disabled, and then clients like
|
|
|
|
* `pay` and `keysend` will disable/enable paymods that do not
|
|
|
|
* help them, instead of the current interface where clients
|
|
|
|
* provide an *ordered* list of paymods they want to use.
|
2020-08-06 06:56:25 +02:00
|
|
|
*/
|
2020-05-29 17:10:47 +02:00
|
|
|
&routehints_pay_mod,
|
2020-08-14 04:54:31 +02:00
|
|
|
&payee_incoming_limit_pay_mod,
|
2020-05-08 16:51:43 +02:00
|
|
|
&retry_pay_mod,
|
2020-07-06 22:21:40 +02:00
|
|
|
&adaptive_splitter_pay_mod,
|
2020-05-08 15:03:48 +02:00
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
2022-02-26 01:50:33 +01:00
|
|
|
static void destroy_payment(struct payment *p)
|
|
|
|
{
|
|
|
|
list_del(&p->list);
|
|
|
|
}
|
|
|
|
|
2024-04-04 05:36:14 +02:00
|
|
|
static struct command_result *
|
2024-07-17 11:01:11 +02:00
|
|
|
start_payment(struct command *cmd, struct payment *p)
|
2024-04-04 05:36:14 +02:00
|
|
|
{
|
|
|
|
struct out_req *req;
|
|
|
|
|
|
|
|
list_add_tail(&payments, &p->list);
|
|
|
|
tal_add_destructor(p, destroy_payment);
|
|
|
|
/* We're keeping this around now */
|
|
|
|
tal_steal(cmd->plugin, p);
|
|
|
|
|
2024-11-06 12:00:36 +01:00
|
|
|
req = jsonrpc_request_start(cmd, "listsendpays",
|
2024-04-04 05:36:14 +02:00
|
|
|
payment_listsendpays_previous,
|
|
|
|
payment_listsendpays_previous, p);
|
|
|
|
|
|
|
|
json_add_sha256(req->js, "payment_hash", p->payment_hash);
|
2024-11-06 12:00:36 +01:00
|
|
|
return send_outreq(req);
|
2024-04-04 05:36:14 +02:00
|
|
|
}
|
|
|
|
|
2024-07-17 11:07:11 +02:00
|
|
|
static bool scidtok_eq(const char *buf,
|
|
|
|
const jsmntok_t *scidtok,
|
|
|
|
struct short_channel_id scid)
|
|
|
|
{
|
|
|
|
struct short_channel_id scid_from_tok;
|
|
|
|
if (!scidtok)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!json_to_short_channel_id(buf, scidtok, &scid_from_tok))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return short_channel_id_eq(scid, scid_from_tok);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We are the entry point, so the next hop could actually be an scid alias,
|
|
|
|
* so we can't just use gossmap. */
|
|
|
|
static struct command_result *listpeerchannels_done(struct command *cmd,
|
2024-11-07 02:27:37 +01:00
|
|
|
const char *method,
|
2024-07-17 11:07:11 +02:00
|
|
|
const char *buf,
|
|
|
|
const jsmntok_t *result,
|
|
|
|
struct payment *p)
|
|
|
|
{
|
|
|
|
const jsmntok_t *arr, *t;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
assert(!p->blindedpath->first_node_id.is_pubkey);
|
|
|
|
arr = json_get_member(buf, result, "channels");
|
|
|
|
json_for_each_arr(i, t, arr) {
|
|
|
|
const jsmntok_t *alias, *local_alias, *scid;
|
|
|
|
struct pubkey id;
|
|
|
|
|
|
|
|
alias = json_get_member(buf, t, "alias");
|
|
|
|
if (alias)
|
|
|
|
local_alias = json_get_member(buf, alias, "local");
|
|
|
|
else
|
|
|
|
local_alias = NULL;
|
|
|
|
|
|
|
|
scid = json_get_member(buf, t, "short_channel_id");
|
|
|
|
if (!scidtok_eq(buf, scid, p->blindedpath->first_node_id.scidd.scid)
|
|
|
|
&& !scidtok_eq(buf, local_alias, p->blindedpath->first_node_id.scidd.scid)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!json_to_pubkey(buf, json_get_member(buf, t, "peer_id"), &id))
|
|
|
|
plugin_err(cmd->plugin, "listpeerchannels no peer_id: %.*s",
|
|
|
|
json_tok_full_len(result),
|
|
|
|
json_tok_full(buf, result));
|
|
|
|
|
|
|
|
plugin_log(cmd->plugin, LOG_DBG,
|
|
|
|
"Mapped decrypted next hop from %s -> %s",
|
|
|
|
fmt_short_channel_id(tmpctx, p->blindedpath->first_node_id.scidd.scid),
|
|
|
|
fmt_pubkey(tmpctx, &id));
|
|
|
|
sciddir_or_pubkey_from_pubkey(&p->blindedpath->first_node_id, &id);
|
|
|
|
/* Fix up our destination */
|
|
|
|
node_id_from_pubkey(p->route_destination, &p->blindedpath->first_node_id.pubkey);
|
|
|
|
return start_payment(cmd, p);
|
|
|
|
}
|
|
|
|
|
|
|
|
return command_fail(cmd, PAY_UNPARSEABLE_ONION,
|
|
|
|
"Invalid next short_channel_id %s for second hop of onion",
|
|
|
|
fmt_short_channel_id(tmpctx,
|
|
|
|
p->blindedpath->first_node_id.scidd.scid));
|
|
|
|
}
|
|
|
|
|
2024-07-17 11:01:11 +02:00
|
|
|
static struct command_result *
|
|
|
|
decrypt_done(struct command *cmd,
|
2024-11-07 02:27:37 +01:00
|
|
|
const char *method,
|
2024-07-17 11:01:11 +02:00
|
|
|
const char *buf,
|
|
|
|
const jsmntok_t *result,
|
|
|
|
struct payment *p)
|
|
|
|
{
|
|
|
|
const char *err;
|
|
|
|
u8 *encdata;
|
2024-10-14 18:03:49 +02:00
|
|
|
struct pubkey next_path_key;
|
2024-07-17 11:01:11 +02:00
|
|
|
struct tlv_encrypted_data_tlv *enctlv;
|
|
|
|
const u8 *cursor;
|
|
|
|
size_t maxlen;
|
|
|
|
|
|
|
|
err = json_scan(tmpctx, buf, result,
|
|
|
|
"{decryptencrypteddata:{decrypted:%"
|
2024-10-15 01:24:21 +02:00
|
|
|
",next_path_key:%}}",
|
2024-07-17 11:01:11 +02:00
|
|
|
JSON_SCAN_TAL(tmpctx, json_tok_bin_from_hex, &encdata),
|
2024-10-14 18:03:49 +02:00
|
|
|
JSON_SCAN(json_to_pubkey, &next_path_key));
|
2024-07-17 11:01:11 +02:00
|
|
|
if (err) {
|
|
|
|
return command_fail(cmd, LIGHTNINGD,
|
|
|
|
"Bad decryptencrypteddata response? %.*s: %s",
|
|
|
|
json_tok_full_len(result),
|
|
|
|
json_tok_full(buf, result),
|
|
|
|
err);
|
|
|
|
}
|
|
|
|
|
|
|
|
cursor = encdata;
|
|
|
|
maxlen = tal_bytelen(encdata);
|
|
|
|
enctlv = fromwire_tlv_encrypted_data_tlv(tmpctx, &cursor, &maxlen);
|
|
|
|
if (!enctlv) {
|
|
|
|
return command_fail(cmd, PAY_UNPARSEABLE_ONION,
|
|
|
|
"Invalid TLV for blinded path: %s",
|
|
|
|
tal_hex(tmpctx, encdata));
|
|
|
|
}
|
|
|
|
|
2024-07-17 11:04:11 +02:00
|
|
|
/* Was this a self-pay? Simply remove blinded path. */
|
|
|
|
if (tal_count(p->blindedpath->path) == 1) {
|
|
|
|
p->blindedpath = tal_free(p->blindedpath);
|
|
|
|
tal_free(p->pay_destination);
|
|
|
|
p->pay_destination = tal_dup(p, struct node_id, p->route_destination);
|
|
|
|
/* self-pay will want secret from inside TLV */
|
|
|
|
if (tal_bytelen(enctlv->path_id) == sizeof(*p->payment_secret)) {
|
|
|
|
p->payment_secret = tal(p, struct secret);
|
|
|
|
memcpy(p->payment_secret, enctlv->path_id, sizeof(struct secret));
|
|
|
|
}
|
|
|
|
return start_payment(cmd, p);
|
|
|
|
}
|
2024-07-17 11:01:11 +02:00
|
|
|
|
|
|
|
/* Promote second hop to first hop */
|
2024-10-14 18:03:49 +02:00
|
|
|
if (enctlv->next_path_key_override)
|
|
|
|
p->blindedpath->first_path_key = *enctlv->next_path_key_override;
|
2024-07-17 11:01:11 +02:00
|
|
|
else
|
2024-10-14 18:03:49 +02:00
|
|
|
p->blindedpath->first_path_key = next_path_key;
|
2024-07-17 11:01:11 +02:00
|
|
|
|
|
|
|
/* Remove now-decrypted part of path */
|
|
|
|
tal_free(p->blindedpath->path[0]);
|
|
|
|
tal_arr_remove(&p->blindedpath->path, 0);
|
|
|
|
|
2024-07-17 11:07:11 +02:00
|
|
|
if (enctlv->next_node_id) {
|
|
|
|
sciddir_or_pubkey_from_pubkey(&p->blindedpath->first_node_id,
|
|
|
|
enctlv->next_node_id);
|
2024-07-17 11:01:11 +02:00
|
|
|
|
2024-07-17 11:07:11 +02:00
|
|
|
/* Fix up our destination */
|
|
|
|
node_id_from_pubkey(p->route_destination, &p->blindedpath->first_node_id.pubkey);
|
|
|
|
return start_payment(cmd, p);
|
|
|
|
} else if (enctlv->short_channel_id) {
|
|
|
|
/* We need to resolve this: stash in first_node_id for now. */
|
|
|
|
struct out_req *req;
|
|
|
|
|
|
|
|
p->blindedpath->first_node_id.is_pubkey = false;
|
|
|
|
p->blindedpath->first_node_id.scidd.scid = *enctlv->short_channel_id;
|
|
|
|
|
2024-11-06 12:00:36 +01:00
|
|
|
req = jsonrpc_request_with_filter_start(cmd, "listpeerchannels",
|
2024-07-17 11:07:11 +02:00
|
|
|
"{\"channels\":[{\"peer_id\":true,\"short_channel_id\":true,\"alias\":{\"local\":true}}]}",
|
|
|
|
listpeerchannels_done, forward_error, p);
|
2024-11-06 12:00:36 +01:00
|
|
|
return send_outreq(req);
|
2024-07-17 11:07:11 +02:00
|
|
|
} else {
|
|
|
|
return command_fail(cmd, PAY_UNPARSEABLE_ONION,
|
|
|
|
"Invalid TLV for blinded path (no next!): %s",
|
|
|
|
tal_hex(tmpctx, encdata));
|
|
|
|
}
|
2024-07-17 11:01:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct command_result *
|
|
|
|
preapproveinvoice_succeed(struct command *cmd,
|
2024-11-07 02:27:37 +01:00
|
|
|
const char *method,
|
2024-07-17 11:01:11 +02:00
|
|
|
const char *buf,
|
|
|
|
const jsmntok_t *result,
|
|
|
|
struct payment *p)
|
|
|
|
{
|
|
|
|
/* Now we can conclude `check` command */
|
|
|
|
if (command_check_only(cmd)) {
|
|
|
|
return command_check_done(cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Blinded path which starts at us needs decryption. */
|
|
|
|
if (p->blindedpath && node_id_eq(p->route_destination, &my_id)) {
|
|
|
|
struct out_req *req;
|
|
|
|
|
2024-11-06 12:00:36 +01:00
|
|
|
req = jsonrpc_request_start(cmd, "decryptencrypteddata",
|
2024-07-17 11:01:11 +02:00
|
|
|
decrypt_done, forward_error, p);
|
|
|
|
|
|
|
|
json_add_hex_talarr(req->js, "encrypted_data",
|
|
|
|
p->blindedpath->path[0]->encrypted_recipient_data);
|
2024-10-15 01:24:21 +02:00
|
|
|
json_add_pubkey(req->js, "path_key", &p->blindedpath->first_path_key);
|
2024-11-06 12:00:36 +01:00
|
|
|
return send_outreq(req);
|
2024-07-17 11:01:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return start_payment(cmd, p);
|
|
|
|
}
|
2022-04-02 04:18:05 +02:00
|
|
|
static struct command_result *json_pay(struct command *cmd,
|
|
|
|
const char *buf,
|
|
|
|
const jsmntok_t *params)
|
2020-05-08 15:03:48 +02:00
|
|
|
{
|
|
|
|
struct payment *p;
|
|
|
|
const char *b11str;
|
|
|
|
struct bolt11 *b11;
|
2021-03-09 09:50:53 +01:00
|
|
|
char *b11_fail, *b12_fail;
|
2020-06-03 15:59:12 +02:00
|
|
|
u64 *maxfee_pct_millionths;
|
|
|
|
u32 *maxdelay;
|
2024-03-20 00:20:13 +01:00
|
|
|
struct amount_msat *exemptfee, *msat, *maxfee, *partial;
|
2022-04-02 04:33:05 +02:00
|
|
|
const char *label, *description;
|
2020-06-11 11:38:41 +02:00
|
|
|
unsigned int *retryfor;
|
2020-06-11 15:53:55 +02:00
|
|
|
u64 *riskfactor_millionths;
|
2020-07-03 17:24:46 +02:00
|
|
|
struct shadow_route_data *shadow_route;
|
2020-12-14 02:25:37 +01:00
|
|
|
struct amount_msat *invmsat;
|
|
|
|
u64 invexpiry;
|
2022-11-09 03:32:01 +01:00
|
|
|
struct sha256 *local_invreq_id;
|
2020-12-14 02:25:37 +01:00
|
|
|
const struct tlv_invoice *b12;
|
2021-09-29 17:56:54 +02:00
|
|
|
struct out_req *req;
|
2021-11-18 20:24:24 +01:00
|
|
|
struct route_exclusion **exclusions;
|
2023-09-21 07:36:27 +02:00
|
|
|
bool *dev_use_shadow;
|
2020-06-03 15:59:12 +02:00
|
|
|
|
2020-05-08 15:03:48 +02:00
|
|
|
/* If any of the modifiers need to add params to the JSON-RPC call we
|
|
|
|
* would add them to the `param()` call below, and have them be
|
|
|
|
* initialized directly that way. */
|
2024-04-04 05:36:14 +02:00
|
|
|
if (!param_check(cmd, buf, params,
|
2020-12-14 02:25:37 +01:00
|
|
|
/* FIXME: parameter should be invstring now */
|
2023-06-13 12:33:31 +02:00
|
|
|
p_req("bolt11", param_invstring, &b11str),
|
2024-01-25 01:28:48 +01:00
|
|
|
p_opt("amount_msat", param_msat, &msat),
|
2020-07-02 15:33:55 +02:00
|
|
|
p_opt("label", param_string, &label),
|
2020-06-11 15:53:55 +02:00
|
|
|
p_opt_def("riskfactor", param_millionths,
|
|
|
|
&riskfactor_millionths, 10000000),
|
2022-04-02 04:25:04 +02:00
|
|
|
p_opt("maxfeepercent", param_millionths,
|
|
|
|
&maxfee_pct_millionths),
|
2020-06-11 16:55:58 +02:00
|
|
|
p_opt_def("retry_for", param_number, &retryfor, 60),
|
|
|
|
p_opt_def("maxdelay", param_number, &maxdelay,
|
|
|
|
maxdelay_default),
|
2022-04-02 04:25:04 +02:00
|
|
|
p_opt("exemptfee", param_msat, &exemptfee),
|
2022-11-09 03:32:01 +01:00
|
|
|
p_opt("localinvreqid", param_sha256, &local_invreq_id),
|
2021-11-18 20:24:24 +01:00
|
|
|
p_opt("exclude", param_route_exclusion_array, &exclusions),
|
2022-04-02 04:25:04 +02:00
|
|
|
p_opt("maxfee", param_msat, &maxfee),
|
2023-06-15 07:22:50 +02:00
|
|
|
p_opt("description", param_escaped_string, &description),
|
2024-03-20 00:20:13 +01:00
|
|
|
p_opt("partial_msat", param_msat, &partial),
|
2023-09-21 07:36:27 +02:00
|
|
|
p_opt_dev("dev_use_shadow", param_bool, &dev_use_shadow, true),
|
2020-05-08 15:03:48 +02:00
|
|
|
NULL))
|
|
|
|
return command_param_failed();
|
|
|
|
|
2024-08-22 18:34:38 +02:00
|
|
|
p = payment_new(cmd, cmd, NULL /* No parent */, global_hints, paymod_mods);
|
2020-12-14 02:25:37 +01:00
|
|
|
p->invstring = tal_steal(p, b11str);
|
2022-04-02 04:33:35 +02:00
|
|
|
p->description = tal_steal(p, description);
|
2022-11-09 02:30:10 +01:00
|
|
|
/* Overridded by bolt12 if present */
|
|
|
|
p->blindedpath = NULL;
|
|
|
|
p->blindedpay = NULL;
|
2020-10-20 06:01:30 +02:00
|
|
|
|
2024-06-21 12:50:45 +02:00
|
|
|
paymod_log(p, LOG_INFORM, "Paying invoice bolt11=%s", b11str);
|
|
|
|
|
2021-03-09 09:50:53 +01:00
|
|
|
if (!bolt12_has_prefix(b11str)) {
|
|
|
|
b11 =
|
2022-02-26 01:50:33 +01:00
|
|
|
bolt11_decode(tmpctx, b11str, plugin_feature_set(cmd->plugin),
|
2022-04-02 04:33:05 +02:00
|
|
|
description, chainparams, &b11_fail);
|
2021-03-02 13:12:05 +01:00
|
|
|
if (b11 == NULL)
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
2021-03-09 09:50:53 +01:00
|
|
|
"Invalid bolt11: %s", b11_fail);
|
|
|
|
|
2020-12-14 02:25:37 +01:00
|
|
|
invmsat = b11->msat;
|
|
|
|
invexpiry = b11->timestamp + b11->expiry;
|
|
|
|
|
2024-07-17 10:58:11 +02:00
|
|
|
p->pay_destination = tal_dup(p, struct node_id, &b11->receiver_id);
|
|
|
|
p->route_destination = p->pay_destination;
|
2020-12-14 02:25:37 +01:00
|
|
|
p->payment_hash = tal_dup(p, struct sha256, &b11->payment_hash);
|
2021-03-02 13:12:05 +01:00
|
|
|
p->payment_secret =
|
2021-12-28 00:21:09 +01:00
|
|
|
tal_dup_or_null(p, struct secret, b11->payment_secret);
|
2022-03-31 11:10:50 +02:00
|
|
|
if (b11->metadata)
|
|
|
|
p->payment_metadata = tal_dup_talarr(p, u8, b11->metadata);
|
|
|
|
else
|
|
|
|
p->payment_metadata = NULL;
|
2022-02-26 01:50:33 +01:00
|
|
|
/* FIXME: libplugin-pay plays with this array, and there are many FIXMEs
|
|
|
|
* about it. But it looks like a leak, so we suppress it here. */
|
|
|
|
p->routes = notleak_with_children(tal_steal(p, b11->routes));
|
2020-12-14 02:25:37 +01:00
|
|
|
p->min_final_cltv_expiry = b11->min_final_cltv_expiry;
|
|
|
|
p->features = tal_steal(p, b11->features);
|
|
|
|
/* Sanity check */
|
2021-03-02 13:12:05 +01:00
|
|
|
if (feature_offered(b11->features, OPT_VAR_ONION) &&
|
|
|
|
!b11->payment_secret)
|
|
|
|
return command_fail(
|
|
|
|
cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"Invalid bolt11:"
|
|
|
|
" sets feature var_onion with no secret");
|
|
|
|
} else {
|
2022-02-26 01:50:33 +01:00
|
|
|
b12 = invoice_decode(tmpctx, b11str, strlen(b11str),
|
2021-03-02 13:12:05 +01:00
|
|
|
plugin_feature_set(cmd->plugin),
|
2021-03-09 09:50:53 +01:00
|
|
|
chainparams, &b12_fail);
|
2021-03-02 13:12:05 +01:00
|
|
|
if (b12 == NULL)
|
2020-12-14 02:25:37 +01:00
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
2021-03-09 09:50:53 +01:00
|
|
|
"Invalid bolt12: %s", b12_fail);
|
2021-01-13 09:58:38 +01:00
|
|
|
|
2022-11-09 02:30:10 +01:00
|
|
|
/* FIXME: We disable MPP for now */
|
|
|
|
/* p->features = tal_steal(p, b12->features); */
|
|
|
|
p->features = NULL;
|
2020-12-14 02:25:37 +01:00
|
|
|
|
2022-11-09 03:32:00 +01:00
|
|
|
invmsat = tal(cmd, struct amount_msat);
|
|
|
|
*invmsat = amount_msat(*b12->invoice_amount);
|
2020-12-14 02:25:37 +01:00
|
|
|
|
2024-07-17 10:58:11 +02:00
|
|
|
p->pay_destination = tal(p, struct node_id);
|
|
|
|
node_id_from_pubkey(p->pay_destination, b12->invoice_node_id);
|
2022-11-09 03:32:00 +01:00
|
|
|
p->payment_hash = tal_dup(p, struct sha256,
|
|
|
|
b12->invoice_payment_hash);
|
|
|
|
if (b12->invreq_recurrence_counter && !label)
|
2021-03-02 13:12:05 +01:00
|
|
|
return command_fail(
|
|
|
|
cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"recurring invoice requires a label");
|
2022-11-09 02:30:10 +01:00
|
|
|
|
2022-11-09 03:32:00 +01:00
|
|
|
/* FIXME: do MPP across these! We choose first one. */
|
|
|
|
p->blindedpath = tal_steal(p, b12->invoice_paths[0]);
|
|
|
|
p->blindedpay = tal_steal(p, b12->invoice_blindedpay[0]);
|
2024-07-17 11:06:11 +02:00
|
|
|
|
|
|
|
if (!gossmap_scidd_pubkey(get_raw_gossmap(p), &p->blindedpath->first_node_id)) {
|
2024-05-09 05:36:20 +02:00
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
2024-07-17 11:06:11 +02:00
|
|
|
"First hop of blinding scid %s unknown",
|
|
|
|
fmt_short_channel_id_dir(tmpctx,
|
|
|
|
&p->blindedpath->first_node_id.scidd));
|
2024-05-09 05:36:20 +02:00
|
|
|
}
|
2022-11-09 03:32:00 +01:00
|
|
|
p->min_final_cltv_expiry = p->blindedpay->cltv_expiry_delta;
|
|
|
|
|
2024-07-17 10:58:11 +02:00
|
|
|
/* Set route destination to introduction point */
|
|
|
|
p->route_destination = tal(p, struct node_id);
|
|
|
|
node_id_from_pubkey(p->route_destination, &p->blindedpath->first_node_id.pubkey);
|
2022-03-31 11:10:50 +02:00
|
|
|
p->payment_metadata = NULL;
|
2020-12-14 02:25:37 +01:00
|
|
|
p->routes = NULL;
|
|
|
|
/* BOLT-offers #12:
|
2022-11-09 03:32:00 +01:00
|
|
|
* - if `invoice_relative_expiry` is present:
|
2020-12-14 02:25:37 +01:00
|
|
|
* - MUST reject the invoice if the current time since
|
2022-11-09 03:32:00 +01:00
|
|
|
* 1970-01-01 UTC is greater than `invoice_created_at` plus
|
2021-07-21 03:31:39 +02:00
|
|
|
* `seconds_from_creation`.
|
2020-12-14 02:25:37 +01:00
|
|
|
* - otherwise:
|
|
|
|
* - MUST reject the invoice if the current time since
|
2022-11-09 03:32:00 +01:00
|
|
|
* 1970-01-01 UTC is greater than `invoice_created_at` plus
|
|
|
|
* 7200.
|
2020-12-14 02:25:37 +01:00
|
|
|
*/
|
2022-11-09 03:32:00 +01:00
|
|
|
if (b12->invoice_relative_expiry)
|
|
|
|
invexpiry = *b12->invoice_created_at + *b12->invoice_relative_expiry;
|
2020-12-14 02:25:37 +01:00
|
|
|
else
|
2022-11-09 03:32:00 +01:00
|
|
|
invexpiry = *b12->invoice_created_at + BOLT12_DEFAULT_REL_EXPIRY;
|
2022-11-09 03:32:01 +01:00
|
|
|
p->local_invreq_id = tal_steal(p, local_invreq_id);
|
2024-07-09 10:36:44 +02:00
|
|
|
|
|
|
|
/* No payment secrets in bolt 12 (we use path_secret) */
|
|
|
|
p->payment_secret = NULL;
|
2021-03-02 13:12:05 +01:00
|
|
|
}
|
2020-05-08 15:03:48 +02:00
|
|
|
|
2020-12-14 02:25:37 +01:00
|
|
|
if (time_now().ts.tv_sec > invexpiry)
|
2020-05-08 15:03:48 +02:00
|
|
|
return command_fail(cmd, PAY_INVOICE_EXPIRED, "Invoice expired");
|
|
|
|
|
2020-12-14 02:25:37 +01:00
|
|
|
if (invmsat) {
|
2020-06-08 13:53:24 +02:00
|
|
|
if (msat) {
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"msatoshi parameter unnecessary");
|
|
|
|
}
|
2024-03-19 05:29:59 +01:00
|
|
|
p->final_amount = *invmsat;
|
2022-02-26 01:50:33 +01:00
|
|
|
tal_free(invmsat);
|
2020-06-08 13:53:24 +02:00
|
|
|
} else {
|
|
|
|
if (!msat) {
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"msatoshi parameter required");
|
|
|
|
}
|
2024-03-19 05:29:59 +01:00
|
|
|
p->final_amount = *msat;
|
2020-06-08 13:53:24 +02:00
|
|
|
}
|
2020-05-08 15:03:48 +02:00
|
|
|
|
2024-03-20 00:20:13 +01:00
|
|
|
if (partial) {
|
|
|
|
if (amount_msat_greater(*partial, p->final_amount)) {
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"partial_msat must be less or equal to total amount %s",
|
|
|
|
fmt_amount_msat(tmpctx, p->final_amount));
|
|
|
|
}
|
2024-07-17 10:58:11 +02:00
|
|
|
if (node_id_eq(&my_id, p->pay_destination)) {
|
2024-03-20 00:20:13 +01:00
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"partial_msat not supported (yet?) for self-pay");
|
|
|
|
}
|
|
|
|
|
|
|
|
p->our_amount = *partial;
|
|
|
|
} else {
|
|
|
|
p->our_amount = p->final_amount;
|
|
|
|
}
|
2024-03-19 05:29:59 +01:00
|
|
|
|
2022-11-09 02:30:10 +01:00
|
|
|
/* We replace real final values if we're using a blinded path */
|
|
|
|
if (p->blindedpath) {
|
2024-03-19 05:29:59 +01:00
|
|
|
p->blindedouramount = p->our_amount;
|
|
|
|
p->blindedfinalamount = p->final_amount;
|
2022-11-09 02:30:10 +01:00
|
|
|
|
2024-03-19 05:29:59 +01:00
|
|
|
if (!amount_msat_add_fee(&p->final_amount,
|
2022-11-09 02:30:10 +01:00
|
|
|
p->blindedpay->fee_base_msat,
|
2024-03-19 05:29:59 +01:00
|
|
|
p->blindedpay->fee_proportional_millionths)
|
|
|
|
|| !amount_msat_add_fee(&p->our_amount,
|
|
|
|
p->blindedpay->fee_base_msat,
|
|
|
|
p->blindedpay->fee_proportional_millionths)) {
|
2022-11-09 02:30:10 +01:00
|
|
|
return command_fail(cmd, PAY_ROUTE_TOO_EXPENSIVE,
|
|
|
|
"This payment blinded path fee overflows!");
|
2024-03-19 05:29:59 +01:00
|
|
|
}
|
2022-11-09 02:30:10 +01:00
|
|
|
}
|
|
|
|
|
2020-05-30 15:33:49 +02:00
|
|
|
p->local_id = &my_id;
|
2023-07-22 13:26:27 +02:00
|
|
|
p->json_buffer = buf;
|
2020-05-08 15:03:48 +02:00
|
|
|
p->json_toks = params;
|
2020-05-29 17:08:20 +02:00
|
|
|
p->why = "Initial attempt";
|
2020-06-04 15:44:19 +02:00
|
|
|
p->constraints.cltv_budget = *maxdelay;
|
2022-02-26 01:50:33 +01:00
|
|
|
tal_free(maxdelay);
|
2020-06-11 11:38:41 +02:00
|
|
|
p->deadline = timeabs_add(time_now(), time_from_sec(*retryfor));
|
2022-02-26 01:50:33 +01:00
|
|
|
tal_free(retryfor);
|
2020-06-11 15:53:55 +02:00
|
|
|
p->getroute->riskfactorppm = *riskfactor_millionths;
|
2022-02-26 01:50:33 +01:00
|
|
|
tal_free(riskfactor_millionths);
|
2020-06-03 15:59:12 +02:00
|
|
|
|
2022-04-02 04:25:04 +02:00
|
|
|
if (maxfee) {
|
|
|
|
if (maxfee_pct_millionths || exemptfee) {
|
|
|
|
return command_fail(
|
|
|
|
cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"If you specify maxfee, cannot specify maxfeepercent or exemptfee.");
|
|
|
|
}
|
|
|
|
p->constraints.fee_budget = *maxfee;
|
|
|
|
payment_mod_exemptfee_get_data(p)->amount = AMOUNT_MSAT(0);
|
|
|
|
} else {
|
|
|
|
u64 maxppm;
|
|
|
|
|
|
|
|
if (maxfee_pct_millionths)
|
|
|
|
maxppm = *maxfee_pct_millionths / 100;
|
|
|
|
else
|
|
|
|
maxppm = 500000 / 100;
|
2024-03-19 05:29:59 +01:00
|
|
|
if (!amount_msat_fee(&p->constraints.fee_budget, p->our_amount, 0,
|
2022-04-02 04:25:04 +02:00
|
|
|
maxppm)) {
|
|
|
|
return command_fail(
|
|
|
|
cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"Overflow when computing fee budget, fee rate too high.");
|
|
|
|
}
|
|
|
|
payment_mod_exemptfee_get_data(p)->amount
|
|
|
|
= exemptfee ? *exemptfee : AMOUNT_MSAT(5000);
|
2020-06-03 15:59:12 +02:00
|
|
|
}
|
2020-06-03 17:58:20 +02:00
|
|
|
|
2020-07-03 17:24:46 +02:00
|
|
|
shadow_route = payment_mod_shadowroute_get_data(p);
|
2020-07-10 14:48:00 +02:00
|
|
|
payment_mod_adaptive_splitter_get_data(p)->disable = disablempp;
|
2021-11-18 20:24:24 +01:00
|
|
|
payment_mod_route_exclusions_get_data(p)->exclusions = exclusions;
|
2020-07-03 17:24:46 +02:00
|
|
|
|
|
|
|
/* This is an MPP enabled pay command, disable amount fuzzing. */
|
|
|
|
shadow_route->fuzz_amount = false;
|
2023-09-21 07:36:27 +02:00
|
|
|
shadow_route->use_shadow = *dev_use_shadow;
|
|
|
|
tal_free(dev_use_shadow);
|
|
|
|
|
2020-07-02 15:33:55 +02:00
|
|
|
p->label = tal_steal(p, label);
|
2020-05-08 15:03:48 +02:00
|
|
|
|
2024-04-04 05:36:14 +02:00
|
|
|
/* Now preapprove, then start payment. */
|
|
|
|
if (command_check_only(cmd)) {
|
2024-11-06 12:00:36 +01:00
|
|
|
req = jsonrpc_request_start(cmd, "check",
|
2024-04-04 05:36:14 +02:00
|
|
|
&preapproveinvoice_succeed,
|
|
|
|
&forward_error, p);
|
|
|
|
json_add_string(req->js, "command_to_check", "preapproveinvoice");
|
|
|
|
} else {
|
2024-11-06 12:00:36 +01:00
|
|
|
req = jsonrpc_request_start(cmd, "preapproveinvoice",
|
2024-04-04 05:36:14 +02:00
|
|
|
&preapproveinvoice_succeed,
|
|
|
|
&forward_error, p);
|
|
|
|
}
|
|
|
|
json_add_string(req->js, "bolt11", p->invstring);
|
2024-11-06 12:00:36 +01:00
|
|
|
return send_outreq(req);
|
2020-05-08 15:03:48 +02:00
|
|
|
}
|
|
|
|
|
2024-08-09 12:25:01 +02:00
|
|
|
static struct command_result *handle_channel_hint_update(struct command *cmd,
|
|
|
|
const char *buf,
|
|
|
|
const jsmntok_t *param)
|
|
|
|
{
|
|
|
|
struct channel_hint *hint = channel_hint_from_json(NULL, buf, param);
|
|
|
|
|
2024-08-23 16:12:44 +02:00
|
|
|
plugin_log(cmd->plugin, LOG_DBG,
|
|
|
|
"Received a channel_hint {.scid = %s, .enabled = %d, "
|
|
|
|
".estimate = %s, .capacity = %s }",
|
|
|
|
fmt_short_channel_id_dir(tmpctx, &hint->scid), hint->enabled,
|
|
|
|
fmt_amount_msat(tmpctx, hint->estimated_capacity),
|
2024-10-03 18:02:14 +02:00
|
|
|
fmt_amount_msat(tmpctx, hint->capacity)
|
2024-08-23 16:12:44 +02:00
|
|
|
);
|
|
|
|
channel_hint_set_add(global_hints, time_now().ts.tv_sec, &hint->scid,
|
|
|
|
hint->enabled, &hint->estimated_capacity,
|
|
|
|
hint->capacity, NULL);
|
2024-08-09 12:25:01 +02:00
|
|
|
tal_free(hint);
|
|
|
|
return notification_handled(cmd);
|
|
|
|
}
|
|
|
|
|
2020-07-02 14:52:16 +02:00
|
|
|
static const struct plugin_command commands[] = {
|
|
|
|
{
|
2019-01-15 11:04:07 +01:00
|
|
|
"paystatus",
|
2019-02-23 06:00:04 +01:00
|
|
|
json_paystatus
|
2019-02-23 06:00:04 +01:00
|
|
|
}, {
|
|
|
|
"listpays",
|
2019-02-23 06:00:04 +01:00
|
|
|
json_listpays
|
2020-05-08 15:03:48 +02:00
|
|
|
},
|
|
|
|
{
|
2020-07-02 14:52:16 +02:00
|
|
|
"pay",
|
2022-04-02 04:18:05 +02:00
|
|
|
json_pay
|
2020-05-08 15:03:48 +02:00
|
|
|
},
|
2019-01-15 05:14:27 +01:00
|
|
|
};
|
|
|
|
|
2021-04-28 18:36:56 +02:00
|
|
|
static const char *notification_topics[] = {
|
|
|
|
"pay_success",
|
|
|
|
"pay_failure",
|
2024-07-23 15:06:52 +02:00
|
|
|
"channel_hint_update",
|
2021-04-28 18:36:56 +02:00
|
|
|
};
|
|
|
|
|
2024-08-09 12:25:01 +02:00
|
|
|
static const struct plugin_notification notification_subs[] = {
|
|
|
|
{
|
|
|
|
"channel_hint_update",
|
|
|
|
handle_channel_hint_update,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2019-01-15 05:14:27 +01:00
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
2019-02-23 06:00:02 +01:00
|
|
|
setup_locale();
|
2024-08-07 03:49:52 +02:00
|
|
|
plugin_main(argv, init, NULL, PLUGIN_RESTARTABLE, true, NULL, commands,
|
2024-08-09 12:25:01 +02:00
|
|
|
ARRAY_SIZE(commands), notification_subs,
|
|
|
|
ARRAY_SIZE(notification_subs), NULL, 0, notification_topics,
|
|
|
|
ARRAY_SIZE(notification_topics),
|
2020-07-10 14:48:00 +02:00
|
|
|
plugin_option("disable-mpp", "flag",
|
2024-08-09 12:25:01 +02:00
|
|
|
"Disable multi-part payments.", flag_option,
|
|
|
|
flag_jsonfmt, &disablempp),
|
2020-07-10 14:48:00 +02:00
|
|
|
NULL);
|
2023-12-13 06:35:01 +01:00
|
|
|
io_poll_override(libplugin_pay_poll);
|
2019-01-15 05:14:27 +01:00
|
|
|
}
|