core-lightning/plugins/renepay/payment.c
2024-05-08 22:32:13 -05:00

416 lines
11 KiB
C

#include "config.h"
#include <bitcoin/preimage.h>
#include <bitcoin/privkey.h>
#include <ccan/tal/str/str.h>
#include <ccan/tal/tal.h>
#include <common/json_stream.h>
#include <common/memleak.h>
#include <plugins/renepay/json.h>
#include <plugins/renepay/payment.h>
#include <plugins/renepay/payplugin.h>
#include <plugins/renepay/routetracker.h>
static struct command_result *payment_finish(struct payment *p);
struct payment *payment_new(
const tal_t *ctx,
const struct sha256 *payment_hash,
const char *invstr TAKES,
const char *label TAKES,
const char *description TAKES,
const struct secret *payment_secret TAKES,
const u8 *payment_metadata TAKES,
const struct route_info **routehints TAKES,
const struct node_id *destination,
struct amount_msat amount,
struct amount_msat maxfee,
unsigned int maxdelay,
u64 retryfor,
u16 final_cltv,
/* Tweakable in --developer mode */
u64 base_fee_penalty_millionths,
u64 prob_cost_factor_millionths,
u64 riskfactor_millionths,
u64 min_prob_success_millionths,
bool use_shadow)
{
struct payment *p = tal(ctx, struct payment);
struct payment_info *pinfo = &p->payment_info;
/* === Unique properties === */
assert(payment_hash);
pinfo->payment_hash = *payment_hash;
assert(invstr);
pinfo->invstr = tal_strdup(p, invstr);
pinfo->label = tal_strdup_or_null(p, label);
pinfo->description = tal_strdup_or_null(p, description);
pinfo->payment_secret = tal_dup_or_null(p, struct secret, payment_secret);
pinfo->payment_metadata = tal_dup_talarr(p, u8, payment_metadata);
if (taken(routehints))
pinfo->routehints = tal_steal(p, routehints);
else {
/* Deep copy */
pinfo->routehints =
tal_dup_talarr(p, const struct route_info *, routehints);
for (size_t i = 0; i < tal_count(pinfo->routehints); i++)
pinfo->routehints[i] =
tal_steal(pinfo->routehints, pinfo->routehints[i]);
}
assert(destination);
pinfo->destination = *destination;
pinfo->amount = amount;
/* === Payment attempt parameters === */
if (!amount_msat_add(&pinfo->maxspend, amount, maxfee))
pinfo->maxspend = AMOUNT_MSAT(UINT64_MAX);
pinfo->maxdelay = maxdelay;
pinfo->start_time = time_now();
pinfo->stop_time = timeabs_add(pinfo->start_time, time_from_sec(retryfor));
pinfo->final_cltv = final_cltv;
/* === Developer options === */
pinfo->base_fee_penalty = base_fee_penalty_millionths / 1e6;
pinfo->prob_cost_factor = prob_cost_factor_millionths / 1e6;
pinfo->delay_feefactor = riskfactor_millionths / 1e6;
pinfo->min_prob_success = min_prob_success_millionths / 1e6;
pinfo->use_shadow = use_shadow;
/* === Public State === */
p->status = PAYMENT_PENDING;
p->preimage = NULL;
p->error_code = LIGHTNINGD;
p->error_msg = NULL;
p->total_sent = AMOUNT_MSAT(0);
p->total_delivering = AMOUNT_MSAT(0);
p->paynotes = tal_arr(p, const char *, 0);
p->groupid = 1;
/* === Hidden State === */
p->exec_state = INVALID_STATE;
p->next_partid = 1;
p->cmd_array = tal_arr(p, struct command *, 0);
p->local_gossmods = NULL;
p->disabledmap = disabledmap_new(p);
p->have_results = false;
p->retry = false;
p->waitresult_timer = NULL;
p->routes_computed = NULL;
p->routetracker = new_routetracker(p, p);
return p;
}
/* A payment that finishes execution must clean its hidden state. */
static void payment_cleanup(struct payment *p)
{
p->exec_state = INVALID_STATE;
tal_resize(&p->cmd_array, 0);
p->local_gossmods = tal_free(p->local_gossmods);
/* FIXME: for optimization, a cleanup should prune all the data that has
* no use after a payent is completed. The entire disablemap structure
* is no longer needed, hence I guess we should free it not just reset
* it. */
disabledmap_reset(p->disabledmap);
p->waitresult_timer = tal_free(p->waitresult_timer);
p->routes_computed = tal_free(p->routes_computed);
routetracker_cleanup(p->routetracker);
}
bool payment_update(
struct payment *p,
struct amount_msat maxfee,
unsigned int maxdelay,
u64 retryfor,
u16 final_cltv,
/* Tweakable in --developer mode */
u64 base_fee_penalty_millionths,
u64 prob_cost_factor_millionths,
u64 riskfactor_millionths,
u64 min_prob_success_millionths,
bool use_shadow)
{
assert(p);
struct payment_info *pinfo = &p->payment_info;
/* === Unique properties === */
// unchanged
/* === Payment attempt parameters === */
if (!amount_msat_add(&pinfo->maxspend, pinfo->amount, maxfee))
pinfo->maxspend = AMOUNT_MSAT(UINT64_MAX);
pinfo->maxdelay = maxdelay;
pinfo->start_time = time_now();
pinfo->stop_time = timeabs_add(pinfo->start_time, time_from_sec(retryfor));
pinfo->final_cltv = final_cltv;
/* === Developer options === */
pinfo->base_fee_penalty = base_fee_penalty_millionths / 1e6;
pinfo->prob_cost_factor = prob_cost_factor_millionths / 1e6;
pinfo->delay_feefactor = riskfactor_millionths / 1e6;
pinfo->min_prob_success = min_prob_success_millionths / 1e6;
pinfo->use_shadow = use_shadow;
/* === Public State === */
p->status = PAYMENT_PENDING;
/* I shouldn't be calling a payment_update on a payment that already
* succeed */
assert(p->preimage == NULL);
p->error_code = LIGHTNINGD;
p->error_msg = tal_free(p->error_msg);;
p->total_sent = AMOUNT_MSAT(0);
p->total_delivering = AMOUNT_MSAT(0);
// p->paynotes are unchanged, they accumulate messages
p->groupid++;
/* === Hidden State === */
p->exec_state = INVALID_STATE;
p->next_partid = 1;
/* I shouldn't be calling a payment_update on a payment that has pending
* cmds. */
assert(p->cmd_array);
assert(tal_count(p->cmd_array) == 0);
p->local_gossmods = tal_free(p->local_gossmods);
assert(p->disabledmap);
disabledmap_reset(p->disabledmap);
p->have_results = false;
p->retry = false;
p->waitresult_timer = tal_free(p->waitresult_timer);
/* It is weird to have routes here stuck. */
if (p->routes_computed)
plugin_log(pay_plugin->plugin, LOG_UNUSUAL,
"We have %zu unsent routes in this payment.",
tal_count(p->routes_computed));
p->routes_computed = tal_free(p->routes_computed);
return true;
}
struct amount_msat payment_sent(const struct payment *p)
{
assert(p);
return p->total_sent;
}
struct amount_msat payment_delivered(const struct payment *p)
{
assert(p);
return p->total_delivering;
}
struct amount_msat payment_amount(const struct payment *p)
{
assert(p);
return p->payment_info.amount;
}
struct amount_msat payment_fees(const struct payment *p)
{
assert(p);
struct amount_msat fees;
struct amount_msat sent = payment_sent(p),
delivered = payment_delivered(p);
if (!amount_msat_sub(&fees, sent, delivered))
plugin_err(
pay_plugin->plugin,
"Strange, sent amount (%s) is less than delivered (%s), "
"aborting.",
fmt_amount_msat(tmpctx, sent),
fmt_amount_msat(tmpctx, delivered));
return fees;
}
u64 payment_parts(const struct payment *payment)
{
assert(payment);
return payment->next_partid - 1;
}
/* attach a command to this payment */
bool payment_register_command(struct payment *p, struct command *cmd)
{
assert(p);
assert(cmd);
assert(p->cmd_array);
tal_arr_expand(&p->cmd_array, cmd);
return true;
}
/* are there pending commands on this payment? */
bool payment_commands_empty(const struct payment *p)
{
assert(p);
assert(p->cmd_array);
return tal_count(p->cmd_array) == 0;
}
struct command *payment_command(struct payment *p)
{
assert(p);
assert(p->cmd_array);
if (tal_count(p->cmd_array) == 0)
return NULL;
return p->cmd_array[0];
}
struct command_result *payment_success(struct payment *payment,
const struct preimage *preimage TAKES)
{
assert(payment);
assert(preimage);
payment->status = PAYMENT_SUCCESS;
payment->preimage = tal_free(payment->preimage);
if(taken(preimage))
payment->preimage = tal_steal(payment, preimage);
else
payment->preimage = tal_dup(payment, struct preimage, preimage);
return payment_finish(payment);
}
struct command_result *payment_fail(struct payment *payment,
enum jsonrpc_errcode code, const char *fmt,
...)
{
payment->status = PAYMENT_FAIL;
payment->error_code = code;
payment->error_msg = tal_free(payment->error_msg);
va_list args;
va_start(args, fmt);
payment->error_msg = tal_vfmt(payment, fmt, args);
va_end(args);
payment_note(payment, LOG_DBG, "Payment failed: %s",
payment->error_msg);
return payment_finish(payment);
}
void payment_note(struct payment *p, enum log_level lvl, const char *fmt, ...)
{
va_list ap;
const char *str;
va_start(ap, fmt);
str = tal_vfmt(p->paynotes, fmt, ap);
va_end(ap);
tal_arr_expand(&p->paynotes, str);
/* Log at debug, unless it's weird... */
plugin_log(pay_plugin->plugin, lvl < LOG_UNUSUAL ? LOG_DBG : lvl, "%s",
str);
for (size_t i = 0; i < tal_count(p->cmd_array); i++) {
struct command *cmd = p->cmd_array[i];
plugin_notify_message(cmd, lvl, "%s", str);
}
}
static struct command_result *my_command_finish(struct payment *p,
struct command *cmd)
{
struct json_stream *result;
if (p->status == PAYMENT_SUCCESS) {
result = jsonrpc_stream_success(cmd);
json_add_payment(result, p);
return command_finished(cmd, result);
}
assert(p->status == PAYMENT_FAIL);
assert(p->error_msg);
return command_fail(cmd, p->error_code, "%s", p->error_msg);
}
static struct command_result *payment_finish(struct payment *p)
{
assert(p->status == PAYMENT_FAIL || p->status == PAYMENT_SUCCESS);
assert(!payment_commands_empty(p));
struct command *cmd = p->cmd_array[0];
// notify all commands that the payment completed
for (size_t i = 1; i < tal_count(p->cmd_array); ++i) {
my_command_finish(p, p->cmd_array[i]);
}
// set the payment into a valid final state
payment_cleanup(p);
return my_command_finish(p, cmd);
}
void payment_disable_chan(struct payment *p, struct short_channel_id scid,
enum log_level lvl, const char *fmt, ...)
{
assert(p);
assert(p->disabledmap);
va_list ap;
const char *str;
va_start(ap, fmt);
str = tal_vfmt(tmpctx, fmt, ap);
va_end(ap);
payment_note(p, lvl, "disabling %s: %s",
fmt_short_channel_id(tmpctx, scid),
str);
disabledmap_add_channel(p->disabledmap, scid);
}
void payment_warn_chan(struct payment *p, struct short_channel_id scid,
enum log_level lvl, const char *fmt, ...)
{
assert(p);
assert(p->disabledmap);
va_list ap;
const char *str;
va_start(ap, fmt);
str = tal_vfmt(tmpctx, fmt, ap);
va_end(ap);
if (disabledmap_channel_is_warned(p->disabledmap, scid)) {
payment_disable_chan(p, scid, lvl, "%s, channel warned twice",
str);
return;
}
payment_note(
p, lvl, "flagged for warning %s: %s, next time it will be disabled",
fmt_short_channel_id(tmpctx, scid), str);
disabledmap_warn_channel(p->disabledmap, scid);
}
void payment_disable_node(struct payment *p, struct node_id node,
enum log_level lvl, const char *fmt, ...)
{
assert(p);
assert(p->disabledmap);
va_list ap;
const char *str;
va_start(ap, fmt);
str = tal_vfmt(tmpctx, fmt, ap);
va_end(ap);
payment_note(p, lvl, "disabling node %s: %s",
fmt_node_id(tmpctx, &node),
str);
disabledmap_add_node(p->disabledmap, node);
}