mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-11-19 18:11:28 +01:00
b768804734
The presplitter modifier would split a payment before trying the first attempt based on some common sizes. Its goal was to have smaller parts in flight over different paths, in order to make it more difficult for a forwarding node to learn payment amount. However it was causing some issues for direct payments, and estimates on spendable amounts which considers only the first HTLC being added, but presplitter would always cause multiple HTLCs to be kicked off, causing the estimate to be off. Removing the presplitter fixes this, making draining channels easier, and worse success rates, due to more HTLCs in flight directly impacting the changes of getting stuck. Changelog-Removed: pay: `pay` no longer splits based on common size, as it was causing issues in various scenarios.
1273 lines
38 KiB
C
1273 lines
38 KiB
C
#include "config.h"
|
|
#include <bitcoin/chainparams.h>
|
|
#include <ccan/array_size/array_size.h>
|
|
#include <ccan/asort/asort.h>
|
|
#include <ccan/cast/cast.h>
|
|
#include <ccan/crypto/siphash24/siphash24.h>
|
|
#include <ccan/htable/htable_type.h>
|
|
#include <ccan/json_out/json_out.h>
|
|
#include <ccan/str/hex/hex.h>
|
|
#include <ccan/tal/str/str.h>
|
|
#include <common/bolt12_merkle.h>
|
|
#include <common/gossmap.h>
|
|
#include <common/json_param.h>
|
|
#include <common/json_stream.h>
|
|
#include <common/memleak.h>
|
|
#include <common/pseudorand.h>
|
|
#include <common/type_to_string.h>
|
|
#include <plugins/libplugin-pay.h>
|
|
#include <stdio.h>
|
|
|
|
/* Public key of this node. */
|
|
static struct node_id my_id;
|
|
static unsigned int maxdelay_default;
|
|
static bool exp_offers;
|
|
static bool disablempp = false;
|
|
|
|
static LIST_HEAD(payments);
|
|
|
|
struct pay_command {
|
|
/* Global state */
|
|
struct plugin *plugin;
|
|
|
|
/* Destination, as text */
|
|
const char *dest;
|
|
|
|
/* How much we're paying, and what riskfactor for routing. */
|
|
struct amount_msat msat;
|
|
/* Blank amount to pay, without fees and shadow route(s). */
|
|
struct amount_msat initial_msat;
|
|
/* riskfactor 12.345% -> riskfactor_millionths = 12345000 */
|
|
u64 riskfactor_millionths;
|
|
unsigned int final_cltv;
|
|
|
|
/* Limits on what routes we'll accept. */
|
|
/* 12.345% -> maxfee_pct_millionths = 12345000 */
|
|
u64 maxfee_pct_millionths;
|
|
unsigned int maxdelay;
|
|
struct amount_msat exemptfee;
|
|
|
|
/* Payment hash, as text. */
|
|
const char *payment_hash;
|
|
|
|
/* Payment secret, if specified by invoice. */
|
|
const char *payment_secret;
|
|
|
|
/* Payment metadata, if specified by invoice. */
|
|
const char *payment_metadata;
|
|
|
|
/* Description, if any. */
|
|
const char *label;
|
|
|
|
/* Chatty description of attempts. */
|
|
struct pay_status *ps;
|
|
|
|
/* Error to use if getroute says it can't find route. */
|
|
const char *expensive_route;
|
|
|
|
/* Time to stop retrying. */
|
|
struct timeabs stoptime;
|
|
|
|
/* Channels which have failed us. */
|
|
const char **excludes;
|
|
|
|
/* Current routehint, if any. */
|
|
struct route_info *current_routehint;
|
|
|
|
/* Any remaining routehints to try. */
|
|
struct route_info **routehints;
|
|
|
|
#if DEVELOPER
|
|
/* Disable the use of shadow route ? */
|
|
double use_shadow;
|
|
#endif
|
|
|
|
/* Current node during shadow route calculation. */
|
|
const char *shadow_dest;
|
|
};
|
|
|
|
/* 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")];
|
|
struct tm *t = gmtime(&time->ts.tv_sec);
|
|
|
|
/* 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);
|
|
snprintf(str, UTC_TIMELEN, iso8601_msec_fmt,
|
|
(int) time->ts.tv_nsec / 1000000);
|
|
}
|
|
|
|
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",
|
|
r->erring_channel);
|
|
|
|
if (r->erring_direction)
|
|
json_add_num(s, "erring_direction",
|
|
*r->erring_direction);
|
|
|
|
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);
|
|
if (p->why != NULL)
|
|
json_add_string(s, "strategy", p->why);
|
|
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 */
|
|
|
|
if (p->step < PAYMENT_STEP_SPLIT)
|
|
json_add_string(s, "state", "pending");
|
|
else
|
|
json_add_string(s, "state", "completed");
|
|
|
|
if (p->step == PAYMENT_STEP_SPLIT) {
|
|
/* Don't add anything, this is neither a success nor a failure. */
|
|
} else if (p->result != NULL) {
|
|
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);
|
|
} else if (p->step >= PAYMENT_STEP_SPLIT) {
|
|
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);
|
|
}
|
|
|
|
json_object_end(s);
|
|
for (size_t i = 0; i < tal_count(p->children); i++)
|
|
paystatus_add_payment(s, p->children[i]);
|
|
}
|
|
|
|
static struct command_result *json_paystatus(struct command *cmd,
|
|
const char *buf,
|
|
const jsmntok_t *params)
|
|
{
|
|
const char *invstring;
|
|
struct json_stream *ret;
|
|
struct payment *p;
|
|
|
|
if (!param(cmd, buf, params,
|
|
/* FIXME: rename to invstring */
|
|
p_opt("bolt11", param_invstring, &invstring),
|
|
NULL))
|
|
return command_param_failed();
|
|
|
|
ret = jsonrpc_stream_success(cmd);
|
|
json_array_start(ret, "pay");
|
|
|
|
list_for_each(&payments, p, list) {
|
|
assert(p->parent == NULL);
|
|
if (invstring && !streq(invstring, p->invstring))
|
|
continue;
|
|
|
|
json_object_start(ret, NULL);
|
|
if (p->label != NULL)
|
|
json_add_string(ret, "label", p->label);
|
|
|
|
if (p->invstring)
|
|
json_add_invstring(ret, p->invstring);
|
|
json_add_amount_msat(ret, "amount_msat", p->amount);
|
|
|
|
json_add_node_id(ret, "destination", p->destination);
|
|
|
|
/* 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);
|
|
}
|
|
json_array_end(ret);
|
|
|
|
return command_finished(cmd, ret);
|
|
}
|
|
|
|
static bool attempt_ongoing(const struct sha256 *payment_hash)
|
|
{
|
|
struct payment *root;
|
|
struct payment_tree_result res;
|
|
enum payment_step diff,
|
|
final_states = PAYMENT_STEP_FAILED | PAYMENT_STEP_SUCCESS;
|
|
|
|
list_for_each(&payments, root, list) {
|
|
if (!sha256_eq(payment_hash, root->payment_hash))
|
|
continue;
|
|
res = payment_collect_result(root);
|
|
diff = res.leafstates & ~final_states;
|
|
return diff != 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* 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;
|
|
};
|
|
|
|
/* We consolidate multi-part payments into a single entry. */
|
|
struct pay_mpp {
|
|
/* payment_hash from the invoice and lookup key */
|
|
const struct sha256 *payment_hash;
|
|
|
|
/* This is the bolt11/bolt12 string */
|
|
const char *invstring;
|
|
|
|
/* Accumulated states of all sendpay commands involved. */
|
|
enum payment_result_state state;
|
|
|
|
/* Optional label (of first one!) */
|
|
const jsmntok_t *label;
|
|
/* Optional description (used for bolt11 with description_hash) */
|
|
const jsmntok_t *description;
|
|
/* 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;
|
|
|
|
/* 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;
|
|
|
|
/* Timestamp of the first part */
|
|
u32 timestamp;
|
|
|
|
/* Completion timestamp. The lowest `completed_at` value for a
|
|
* successful part. */
|
|
u64 success_at;
|
|
|
|
/* The destination of the payment, if specified. */
|
|
const jsmntok_t *destination;
|
|
|
|
/* Which sendpay group is this? Necessary for invoices that have been
|
|
* attempted multiple times. */
|
|
struct pay_sort_key sortkey;
|
|
};
|
|
|
|
static const struct pay_sort_key *pay_mpp_key(const struct pay_mpp *pm)
|
|
{
|
|
return &pm->sortkey;
|
|
}
|
|
|
|
static size_t pay_mpp_hash(const struct pay_sort_key *key)
|
|
{
|
|
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);
|
|
}
|
|
|
|
static bool pay_mpp_eq(const struct pay_mpp *pm, const struct pay_sort_key *key)
|
|
{
|
|
return memcmp(pm->sortkey.payment_hash, key->payment_hash,
|
|
sizeof(struct sha256)) == 0 &&
|
|
pm->sortkey.groupid == key->groupid;
|
|
}
|
|
|
|
HTABLE_DEFINE_TYPE(struct pay_mpp, pay_mpp_key, pay_mpp_hash, pay_mpp_eq,
|
|
pay_map);
|
|
|
|
static void add_amount_sent(struct plugin *p,
|
|
const char *invstring,
|
|
struct pay_mpp *mpp,
|
|
const char *buf,
|
|
const jsmntok_t *t)
|
|
{
|
|
struct amount_msat sent, recv;
|
|
const jsmntok_t *msattok;
|
|
|
|
|
|
json_to_msat(buf, json_get_member(buf, t, "amount_sent_msat"), &sent);
|
|
if (!amount_msat_add(&mpp->amount_sent, mpp->amount_sent, sent))
|
|
plugin_log(p, LOG_BROKEN,
|
|
"Cannot add amount_sent_msat for %s: %s + %s",
|
|
invstring,
|
|
type_to_string(tmpctx, struct amount_msat, &mpp->amount_sent),
|
|
type_to_string(tmpctx, struct amount_msat, &sent));
|
|
|
|
msattok = json_get_member(buf, t, "amount_msat");
|
|
|
|
/* If this is an unannotated partial payment we drop out estimate for
|
|
* all parts. */
|
|
if (msattok == NULL) {
|
|
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;
|
|
|
|
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));
|
|
|
|
if (!amount_msat_add(mpp->amount, *mpp->amount, recv))
|
|
plugin_log(p, LOG_BROKEN,
|
|
"Cannot add amount_msat for %s: %s + %s",
|
|
invstring,
|
|
type_to_string(tmpctx, struct amount_msat, mpp->amount),
|
|
type_to_string(tmpctx, struct amount_msat, &sent));
|
|
}
|
|
|
|
static void add_new_entry(struct json_stream *ret,
|
|
const char *buf,
|
|
const struct pay_mpp *pm)
|
|
{
|
|
json_object_start(ret, NULL);
|
|
if (pm->invstring)
|
|
json_add_invstring(ret, pm->invstring);
|
|
if (pm->description)
|
|
json_add_tok(ret, "description", pm->description, buf);
|
|
if (pm->destination)
|
|
json_add_tok(ret, "destination", pm->destination, buf);
|
|
|
|
json_add_sha256(ret, "payment_hash", pm->payment_hash);
|
|
|
|
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");
|
|
|
|
json_add_u32(ret, "created_at", pm->timestamp);
|
|
|
|
if (pm->success_at < UINT64_MAX)
|
|
json_add_u64(ret, "completed_at", pm->success_at);
|
|
|
|
if (pm->label)
|
|
json_add_tok(ret, "label", pm->label, buf);
|
|
if (pm->preimage)
|
|
json_add_tok(ret, "preimage", pm->preimage, buf);
|
|
|
|
/* This is only tallied for pending and successful payments, not
|
|
* failures. */
|
|
if (pm->amount != NULL && pm->num_nonfailed_parts > 0)
|
|
json_add_amount_msat(ret, "amount_msat", *pm->amount);
|
|
|
|
json_add_amount_msat(ret, "amount_sent_msat", pm->amount_sent);
|
|
|
|
if (pm->num_nonfailed_parts > 1)
|
|
json_add_u64(ret, "number_of_parts",
|
|
pm->num_nonfailed_parts);
|
|
json_object_end(ret);
|
|
}
|
|
|
|
static struct command_result *listsendpays_done(struct command *cmd,
|
|
const char *buf,
|
|
const jsmntok_t *result,
|
|
char *invstring)
|
|
{
|
|
size_t i;
|
|
const jsmntok_t *t, *arr;
|
|
struct json_stream *ret;
|
|
struct pay_map *pay_map;
|
|
struct pay_mpp *pm;
|
|
struct pay_sort_key *order = tal_arr(tmpctx, struct pay_sort_key, 0);
|
|
|
|
pay_map = tal(cmd, struct pay_map);
|
|
pay_map_init(pay_map);
|
|
|
|
arr = json_get_member(buf, result, "payments");
|
|
if (!arr || arr->type != JSMN_ARRAY)
|
|
return command_fail(cmd, LIGHTNINGD,
|
|
"Unexpected non-array result from listsendpays");
|
|
|
|
json_for_each_arr(i, t, arr) {
|
|
const jsmntok_t *status, *invstrtok, *hashtok, *createdtok,
|
|
*completedtok, *grouptok;
|
|
const char *invstr = invstring;
|
|
struct sha256 payment_hash;
|
|
u32 created_at;
|
|
u64 completed_at;
|
|
u64 groupid;
|
|
struct pay_sort_key key;
|
|
|
|
invstrtok = json_get_member(buf, t, "bolt11");
|
|
if (!invstrtok)
|
|
invstrtok = json_get_member(buf, t, "bolt12");
|
|
hashtok = json_get_member(buf, t, "payment_hash");
|
|
createdtok = json_get_member(buf, t, "created_at");
|
|
completedtok = json_get_member(buf, t, "completed_at");
|
|
assert(hashtok != NULL);
|
|
assert(createdtok != NULL);
|
|
|
|
if (completedtok != NULL)
|
|
json_to_u64(buf, completedtok, &completed_at);
|
|
else
|
|
completed_at = UINT64_MAX;
|
|
|
|
grouptok = json_get_member(buf, t, "groupid");
|
|
if (grouptok != NULL)
|
|
json_to_u64(buf, grouptok, &groupid);
|
|
else
|
|
groupid = 0;
|
|
|
|
json_to_sha256(buf, hashtok, &payment_hash);
|
|
json_to_u32(buf, createdtok, &created_at);
|
|
if (invstrtok)
|
|
invstr = json_strdup(cmd, buf, invstrtok);
|
|
|
|
key.payment_hash = &payment_hash;
|
|
key.groupid = groupid;
|
|
|
|
pm = pay_map_get(pay_map, &key);
|
|
if (!pm) {
|
|
pm = tal(cmd, struct pay_mpp);
|
|
pm->state = 0;
|
|
pm->payment_hash = tal_dup(pm, struct sha256, &payment_hash);
|
|
pm->invstring = tal_steal(pm, invstr);
|
|
pm->destination = json_get_member(buf, t, "destination");
|
|
pm->label = json_get_member(buf, t, "label");
|
|
pm->description = json_get_member(buf, t, "description");
|
|
pm->preimage = NULL;
|
|
pm->amount_sent = AMOUNT_MSAT(0);
|
|
pm->amount = talz(pm, struct amount_msat);
|
|
pm->num_nonfailed_parts = 0;
|
|
pm->timestamp = created_at;
|
|
pm->sortkey.payment_hash = pm->payment_hash;
|
|
pm->sortkey.groupid = groupid;
|
|
pm->success_at = UINT64_MAX;
|
|
pay_map_add(pay_map, pm);
|
|
// 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);
|
|
} 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");
|
|
}
|
|
|
|
status = json_get_member(buf, t, "status");
|
|
if (json_tok_streq(buf, status, "complete")) {
|
|
add_amount_sent(cmd->plugin, pm->invstring, pm, buf, t);
|
|
pm->num_nonfailed_parts++;
|
|
pm->preimage
|
|
= json_get_member(buf, t, "payment_preimage");
|
|
pm->state |= PAYMENT_COMPLETE;
|
|
|
|
/* We count down from UINT64_MAX. */
|
|
if (pm->success_at > completed_at)
|
|
pm->success_at = completed_at;
|
|
|
|
} else if (json_tok_streq(buf, status, "pending")) {
|
|
add_amount_sent(cmd->plugin, pm->invstring, pm, buf, t);
|
|
pm->num_nonfailed_parts++;
|
|
pm->state |= PAYMENT_PENDING;
|
|
} else {
|
|
pm->state |= PAYMENT_FAILED;
|
|
}
|
|
}
|
|
|
|
ret = jsonrpc_stream_success(cmd);
|
|
json_array_start(ret, "pays");
|
|
for (i = 0; i < tal_count(order); i++) {
|
|
pm = pay_map_get(pay_map, &order[i]);
|
|
assert(pm != NULL);
|
|
add_new_entry(ret, buf, pm);
|
|
}
|
|
json_array_end(ret);
|
|
return command_finished(cmd, ret);
|
|
}
|
|
|
|
static struct command_result *json_listpays(struct command *cmd,
|
|
const char *buf,
|
|
const jsmntok_t *params)
|
|
{
|
|
const char *invstring, *status_str;
|
|
struct sha256 *payment_hash;
|
|
struct out_req *req;
|
|
|
|
/* FIXME: would be nice to parse as a bolt11 so check worked in future */
|
|
if (!param(cmd, buf, params,
|
|
/* FIXME: parameter should be invstring now */
|
|
p_opt("bolt11", param_invstring, &invstring),
|
|
p_opt("payment_hash", param_sha256, &payment_hash),
|
|
p_opt("status", param_string, &status_str),
|
|
NULL))
|
|
return command_param_failed();
|
|
|
|
req = jsonrpc_request_start(cmd->plugin, cmd, "listsendpays",
|
|
listsendpays_done, forward_error,
|
|
cast_const(char *, invstring));
|
|
if (invstring)
|
|
json_add_string(req->js, "bolt11", invstring);
|
|
|
|
if (payment_hash)
|
|
json_add_sha256(req->js, "payment_hash", payment_hash);
|
|
|
|
if (status_str)
|
|
json_add_string(req->js, "status", status_str);
|
|
return send_outreq(cmd->plugin, req);
|
|
}
|
|
|
|
#if DEVELOPER
|
|
static void memleak_mark_payments(struct plugin *p, struct htable *memtable)
|
|
{
|
|
memleak_scan_list_head(memtable, &payments);
|
|
}
|
|
#endif
|
|
|
|
static const char *init(struct plugin *p,
|
|
const char *buf UNUSED, const jsmntok_t *config UNUSED)
|
|
{
|
|
rpc_scan(p, "getinfo", take(json_out_obj(NULL, NULL, NULL)),
|
|
"{id:%}", JSON_SCAN(json_to_node_id, &my_id));
|
|
|
|
rpc_scan(p, "listconfigs",
|
|
take(json_out_obj(NULL, NULL, NULL)),
|
|
"{configs:"
|
|
"{max-locktime-blocks:{value_int:%},"
|
|
"experimental-offers:{set:%}}}",
|
|
JSON_SCAN(json_to_number, &maxdelay_default),
|
|
JSON_SCAN(json_to_bool, &exp_offers));
|
|
|
|
#if DEVELOPER
|
|
plugin_set_memleak_handler(p, memleak_mark_payments);
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
static void on_payment_success(struct payment *payment)
|
|
{
|
|
struct payment *p, *nxt;
|
|
struct payment_tree_result result = payment_collect_result(payment);
|
|
struct json_stream *ret;
|
|
struct command *cmd;
|
|
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. */
|
|
|
|
list_for_each_safe(&payments, p, nxt, list) {
|
|
/* The result for the active payment is returned in
|
|
* `payment_finished`. */
|
|
if (payment == p)
|
|
continue;
|
|
|
|
/* 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)
|
|
continue;
|
|
if (p->cmd == NULL)
|
|
continue;
|
|
|
|
cmd = p->cmd;
|
|
p->cmd = NULL;
|
|
|
|
ret = jsonrpc_stream_success(cmd);
|
|
json_add_node_id(ret, "destination", p->destination);
|
|
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);
|
|
|
|
json_add_amount_msat(ret, "amount_msat", p->amount);
|
|
json_add_amount_msat(ret, "amount_sent_msat", result.sent);
|
|
|
|
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");
|
|
if (command_finished(cmd, ret)) {/* Ignore result. */}
|
|
}
|
|
}
|
|
|
|
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);
|
|
json_add_amount_msat(s, "amount_msat", p->amount);
|
|
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);
|
|
}
|
|
|
|
static void on_payment_failure(struct payment *payment)
|
|
{
|
|
struct payment *p, *nxt;
|
|
struct payment_tree_result result = payment_collect_result(payment);
|
|
list_for_each_safe(&payments, p, nxt, list)
|
|
{
|
|
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;
|
|
|
|
/* 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)
|
|
continue;
|
|
if (p->cmd == NULL)
|
|
continue;
|
|
|
|
cmd = p->cmd;
|
|
p->cmd = NULL;
|
|
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);
|
|
json_add_node_id(ret, "destination", p->destination);
|
|
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");
|
|
}
|
|
|
|
json_add_amount_msat(ret, "amount_msat", p->amount);
|
|
|
|
json_add_amount_msat(ret, "amount_sent_msat",
|
|
result.sent);
|
|
|
|
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",
|
|
failure->erring_channel);
|
|
|
|
if (failure->erring_direction)
|
|
json_add_num(
|
|
ret, "erring_direction",
|
|
*failure->erring_direction);
|
|
}
|
|
|
|
if (command_finished(cmd, ret)) { /* Ignore result. */}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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
|
|
* 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. */
|
|
static struct command_result *
|
|
payment_listsendpays_previous(struct command *cmd, const char *buf,
|
|
const jsmntok_t *result, struct payment *p)
|
|
{
|
|
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;
|
|
|
|
/* 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;
|
|
/* Did a prior attempt succeed? */
|
|
bool completed = false;
|
|
|
|
/* 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;
|
|
|
|
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;
|
|
struct amount_msat diff_sent, diff_msat;
|
|
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;
|
|
|
|
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));
|
|
if (!amount_msat_add(&msat, msat, diff_msat) ||
|
|
!amount_msat_add(&sent, sent, diff_sent))
|
|
plugin_err(p->plugin,
|
|
"msat overflow adding up parts");
|
|
parts++;
|
|
}
|
|
|
|
status = json_get_member(buf, t, "status");
|
|
completed |= json_tok_streq(buf, status, "complete");
|
|
pending |= json_tok_streq(buf, status, "pending");
|
|
|
|
/* 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;
|
|
}
|
|
|
|
if (completed) {
|
|
ret = jsonrpc_stream_success(cmd);
|
|
json_add_preimage(ret, "payment_preimage", &preimage);
|
|
json_add_string(ret, "status", "complete");
|
|
json_add_amount_msat(ret, "amount_msat", msat);
|
|
json_add_amount_msat(ret, "amount_sent_msat", sent);
|
|
json_add_node_id(ret, "destination", p->destination);
|
|
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);
|
|
} else if (pending) {
|
|
/* We suspend this call and wait for the
|
|
* `on_payment_success` or `on_payment_failure`
|
|
* handler of the currently running payment to notify
|
|
* us about its completion. We latch on to the result
|
|
* from the call we extracted above. */
|
|
p->groupid = pending_group_id;
|
|
return command_still_pending(cmd);
|
|
}
|
|
p->groupid = last_group + 1;
|
|
p->on_payment_success = on_payment_success;
|
|
p->on_payment_failure = on_payment_failure;
|
|
payment_start(p);
|
|
return command_still_pending(cmd);
|
|
}
|
|
|
|
struct payment_modifier *paymod_mods[] = {
|
|
&check_preapproveinvoice_pay_mod,
|
|
/* 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.
|
|
*/
|
|
&local_channel_hints_pay_mod,
|
|
&route_exclusions_pay_mod,
|
|
&exemptfee_pay_mod,
|
|
&directpay_pay_mod,
|
|
&shadowroute_pay_mod,
|
|
/* NOTE: The order in which these two paymods are executed is
|
|
* significant! `routehints` *must* execute first before
|
|
* payee_incoming_limit.
|
|
*
|
|
* FIXME: Giving an ordered list of paymods to the paymod
|
|
* system is the wrong interface, given that the order in
|
|
* 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.
|
|
*/
|
|
&routehints_pay_mod,
|
|
&payee_incoming_limit_pay_mod,
|
|
&waitblockheight_pay_mod,
|
|
&retry_pay_mod,
|
|
&adaptive_splitter_pay_mod,
|
|
NULL,
|
|
};
|
|
|
|
static void destroy_payment(struct payment *p)
|
|
{
|
|
list_del(&p->list);
|
|
}
|
|
|
|
static struct command_result *json_pay(struct command *cmd,
|
|
const char *buf,
|
|
const jsmntok_t *params)
|
|
{
|
|
struct payment *p;
|
|
const char *b11str;
|
|
struct bolt11 *b11;
|
|
char *b11_fail, *b12_fail;
|
|
u64 *maxfee_pct_millionths;
|
|
u32 *maxdelay;
|
|
struct amount_msat *exemptfee, *msat, *maxfee;
|
|
const char *label, *description;
|
|
unsigned int *retryfor;
|
|
u64 *riskfactor_millionths;
|
|
struct shadow_route_data *shadow_route;
|
|
struct amount_msat *invmsat;
|
|
u64 invexpiry;
|
|
struct sha256 *local_invreq_id;
|
|
const struct tlv_invoice *b12;
|
|
struct out_req *req;
|
|
struct route_exclusion **exclusions;
|
|
#if DEVELOPER
|
|
bool *use_shadow;
|
|
#endif
|
|
|
|
/* 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. */
|
|
if (!param(cmd, buf, params,
|
|
/* FIXME: parameter should be invstring now */
|
|
p_req("bolt11", param_invstring, &b11str),
|
|
p_opt("amount_msat|msatoshi", param_msat, &msat),
|
|
p_opt("label", param_string, &label),
|
|
p_opt_def("riskfactor", param_millionths,
|
|
&riskfactor_millionths, 10000000),
|
|
p_opt("maxfeepercent", param_millionths,
|
|
&maxfee_pct_millionths),
|
|
p_opt_def("retry_for", param_number, &retryfor, 60),
|
|
p_opt_def("maxdelay", param_number, &maxdelay,
|
|
maxdelay_default),
|
|
p_opt("exemptfee", param_msat, &exemptfee),
|
|
p_opt("localinvreqid", param_sha256, &local_invreq_id),
|
|
p_opt("exclude", param_route_exclusion_array, &exclusions),
|
|
p_opt("maxfee", param_msat, &maxfee),
|
|
p_opt("description", param_escaped_string, &description),
|
|
#if DEVELOPER
|
|
p_opt_def("use_shadow", param_bool, &use_shadow, true),
|
|
#endif
|
|
NULL))
|
|
return command_param_failed();
|
|
|
|
p = payment_new(cmd, cmd, NULL /* No parent */, paymod_mods);
|
|
p->invstring = tal_steal(p, b11str);
|
|
p->description = tal_steal(p, description);
|
|
/* Overridded by bolt12 if present */
|
|
p->blindedpath = NULL;
|
|
p->blindedpay = NULL;
|
|
|
|
if (!bolt12_has_prefix(b11str)) {
|
|
b11 =
|
|
bolt11_decode(tmpctx, b11str, plugin_feature_set(cmd->plugin),
|
|
description, chainparams, &b11_fail);
|
|
if (b11 == NULL)
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
"Invalid bolt11: %s", b11_fail);
|
|
|
|
invmsat = b11->msat;
|
|
invexpiry = b11->timestamp + b11->expiry;
|
|
|
|
p->destination = tal_dup(p, struct node_id, &b11->receiver_id);
|
|
p->payment_hash = tal_dup(p, struct sha256, &b11->payment_hash);
|
|
p->payment_secret =
|
|
tal_dup_or_null(p, struct secret, b11->payment_secret);
|
|
if (b11->metadata)
|
|
p->payment_metadata = tal_dup_talarr(p, u8, b11->metadata);
|
|
else
|
|
p->payment_metadata = NULL;
|
|
/* 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));
|
|
p->min_final_cltv_expiry = b11->min_final_cltv_expiry;
|
|
p->features = tal_steal(p, b11->features);
|
|
/* Sanity check */
|
|
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 {
|
|
b12 = invoice_decode(tmpctx, b11str, strlen(b11str),
|
|
plugin_feature_set(cmd->plugin),
|
|
chainparams, &b12_fail);
|
|
if (b12 == NULL)
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
"Invalid bolt12: %s", b12_fail);
|
|
if (!exp_offers)
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
"experimental-offers disabled");
|
|
|
|
/* FIXME: We disable MPP for now */
|
|
/* p->features = tal_steal(p, b12->features); */
|
|
p->features = NULL;
|
|
|
|
if (!b12->invoice_node_id)
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
"invoice missing node_id");
|
|
if (!b12->invoice_payment_hash)
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
"invoice missing payment_hash");
|
|
if (!b12->invoice_created_at)
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
"invoice missing created_at");
|
|
if (!b12->invoice_amount)
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
"invoice missing invoice_amount");
|
|
invmsat = tal(cmd, struct amount_msat);
|
|
*invmsat = amount_msat(*b12->invoice_amount);
|
|
|
|
p->destination = tal(p, struct node_id);
|
|
node_id_from_pubkey(p->destination, b12->invoice_node_id);
|
|
p->payment_hash = tal_dup(p, struct sha256,
|
|
b12->invoice_payment_hash);
|
|
if (b12->invreq_recurrence_counter && !label)
|
|
return command_fail(
|
|
cmd, JSONRPC2_INVALID_PARAMS,
|
|
"recurring invoice requires a label");
|
|
|
|
/* BOLT-offers #12:
|
|
* - MUST reject the invoice if `invoice_paths` is not present
|
|
* or is empty.
|
|
*/
|
|
if (tal_count(b12->invoice_paths) == 0)
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
"invoice missing invoice_paths");
|
|
|
|
/* BOLT-offers #12:
|
|
* - MUST reject the invoice if `invoice_blindedpay` does not
|
|
* contain exactly one `blinded_payinfo` per
|
|
* `invoice_paths`.`blinded_path`. */
|
|
if (tal_count(b12->invoice_paths)
|
|
!= tal_count(b12->invoice_blindedpay)) {
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
"Wrong blinding info: %zu paths, %zu payinfo",
|
|
tal_count(b12->invoice_paths),
|
|
tal_count(b12->invoice_blindedpay));
|
|
}
|
|
|
|
/* 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]);
|
|
p->min_final_cltv_expiry = p->blindedpay->cltv_expiry_delta;
|
|
|
|
/* Set destination to introduction point */
|
|
node_id_from_pubkey(p->destination, &p->blindedpath->first_node_id);
|
|
p->payment_metadata = NULL;
|
|
p->routes = NULL;
|
|
/* BOLT-offers #12:
|
|
* - if `invoice_relative_expiry` is present:
|
|
* - MUST reject the invoice if the current time since
|
|
* 1970-01-01 UTC is greater than `invoice_created_at` plus
|
|
* `seconds_from_creation`.
|
|
* - otherwise:
|
|
* - MUST reject the invoice if the current time since
|
|
* 1970-01-01 UTC is greater than `invoice_created_at` plus
|
|
* 7200.
|
|
*/
|
|
if (b12->invoice_relative_expiry)
|
|
invexpiry = *b12->invoice_created_at + *b12->invoice_relative_expiry;
|
|
else
|
|
invexpiry = *b12->invoice_created_at + BOLT12_DEFAULT_REL_EXPIRY;
|
|
p->local_invreq_id = tal_steal(p, local_invreq_id);
|
|
}
|
|
|
|
if (time_now().ts.tv_sec > invexpiry)
|
|
return command_fail(cmd, PAY_INVOICE_EXPIRED, "Invoice expired");
|
|
|
|
if (invmsat) {
|
|
if (msat) {
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
"msatoshi parameter unnecessary");
|
|
}
|
|
p->amount = *invmsat;
|
|
tal_free(invmsat);
|
|
} else {
|
|
if (!msat) {
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
"msatoshi parameter required");
|
|
}
|
|
p->amount = *msat;
|
|
}
|
|
|
|
/* We replace real final values if we're using a blinded path */
|
|
if (p->blindedpath) {
|
|
p->blindedfinalcltv = p->min_final_cltv_expiry;
|
|
p->blindedfinalamount = p->amount;
|
|
|
|
p->min_final_cltv_expiry += p->blindedpay->cltv_expiry_delta;
|
|
if (!amount_msat_add_fee(&p->amount,
|
|
p->blindedpay->fee_base_msat,
|
|
p->blindedpay->fee_proportional_millionths))
|
|
return command_fail(cmd, PAY_ROUTE_TOO_EXPENSIVE,
|
|
"This payment blinded path fee overflows!");
|
|
}
|
|
|
|
if (node_id_eq(&my_id, p->destination))
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
"This payment is destined for ourselves. "
|
|
"Self-payments are not supported");
|
|
|
|
p->local_id = &my_id;
|
|
p->json_buffer = tal_steal(p, buf);
|
|
p->json_toks = params;
|
|
p->why = "Initial attempt";
|
|
p->constraints.cltv_budget = *maxdelay;
|
|
tal_free(maxdelay);
|
|
p->deadline = timeabs_add(time_now(), time_from_sec(*retryfor));
|
|
tal_free(retryfor);
|
|
p->getroute->riskfactorppm = *riskfactor_millionths;
|
|
tal_free(riskfactor_millionths);
|
|
|
|
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;
|
|
if (!amount_msat_fee(&p->constraints.fee_budget, p->amount, 0,
|
|
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);
|
|
}
|
|
|
|
shadow_route = payment_mod_shadowroute_get_data(p);
|
|
payment_mod_adaptive_splitter_get_data(p)->disable = disablempp;
|
|
payment_mod_route_exclusions_get_data(p)->exclusions = exclusions;
|
|
|
|
/* This is an MPP enabled pay command, disable amount fuzzing. */
|
|
shadow_route->fuzz_amount = false;
|
|
#if DEVELOPER
|
|
shadow_route->use_shadow = *use_shadow;
|
|
tal_free(use_shadow);
|
|
#endif
|
|
p->label = tal_steal(p, label);
|
|
list_add_tail(&payments, &p->list);
|
|
tal_add_destructor(p, destroy_payment);
|
|
/* We're keeping this around now */
|
|
tal_steal(cmd->plugin, p);
|
|
|
|
req = jsonrpc_request_start(cmd->plugin, cmd, "listsendpays",
|
|
payment_listsendpays_previous,
|
|
payment_listsendpays_previous, p);
|
|
|
|
json_add_sha256(req->js, "payment_hash", p->payment_hash);
|
|
return send_outreq(cmd->plugin, req);
|
|
}
|
|
|
|
static const struct plugin_command commands[] = {
|
|
{
|
|
"paystatus",
|
|
"payment",
|
|
"Detail status of attempts to pay {bolt11}, or all",
|
|
"Covers both old payments and current ones.",
|
|
json_paystatus
|
|
}, {
|
|
"listpays",
|
|
"payment",
|
|
"List result of payment {bolt11} or {payment_hash}, or all",
|
|
"Covers old payments (failed and succeeded) and current ones.",
|
|
json_listpays
|
|
},
|
|
{
|
|
"pay",
|
|
"payment",
|
|
"Send payment specified by {bolt11}",
|
|
"Attempt to pay the {bolt11} invoice.",
|
|
json_pay
|
|
},
|
|
};
|
|
|
|
static const char *notification_topics[] = {
|
|
"pay_success",
|
|
"pay_failure",
|
|
};
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
setup_locale();
|
|
plugin_main(argv, init, PLUGIN_RESTARTABLE, true, NULL, commands,
|
|
ARRAY_SIZE(commands), NULL, 0, NULL, 0,
|
|
notification_topics, ARRAY_SIZE(notification_topics),
|
|
plugin_option("disable-mpp", "flag",
|
|
"Disable multi-part payments.",
|
|
flag_option, &disablempp),
|
|
NULL);
|
|
}
|