Files
palladum-lightning/plugins/renepay/payment.c
Lagrang3 1311223da5 renepay: parse bolt12 invoices
A first step towards supporting bolt12 invoices and blinded paths.

Changelog-None

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2025-02-12 13:36:06 +10:30

377 lines
9.7 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)
{
struct payment *p = tal(ctx, struct payment);
memset(p, 0, sizeof(struct payment));
struct payment_info *pinfo = &p->payment_info;
assert(payment_hash);
pinfo->payment_hash = *payment_hash;
assert(invstr);
pinfo->invstr = tal_strdup(p, invstr);
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;
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->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);
routetracker_cleanup(p->routetracker);
}
/* Sets state values to ongoing payment */
bool payment_refresh(struct payment *p){
assert(p);
struct payment_info *pinfo = &p->payment_info;
/* === 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);
p->have_results = false;
p->retry = false;
p->waitresult_timer = tal_free(p->waitresult_timer);
pinfo->start_time = time_now();
pinfo->stop_time =
timeabs_add(pinfo->start_time, time_from_sec(pinfo->retryfor));
return true;
}
bool payment_set_constraints(
struct payment *p,
struct amount_msat amount,
struct amount_msat maxfee,
unsigned int maxdelay,
u64 retryfor,
u64 base_fee_penalty_millionths,
u64 prob_cost_factor_millionths,
u64 riskfactor_millionths,
u64 min_prob_success_millionths,
u64 base_prob_success_millionths,
bool use_shadow,
const struct route_exclusion **exclusions)
{
assert(p);
struct payment_info *pinfo = &p->payment_info;
/* === Payment attempt parameters === */
pinfo->amount = amount;
if (!amount_msat_add(&pinfo->maxspend, pinfo->amount, maxfee))
pinfo->maxspend = AMOUNT_MSAT(UINT64_MAX);
pinfo->maxdelay = maxdelay;
pinfo->retryfor = retryfor;
/* === 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->base_prob_success = base_prob_success_millionths / 1e6;
pinfo->use_shadow = use_shadow;
assert(p->disabledmap);
disabledmap_reset(p->disabledmap);
for (size_t i = 0; i < tal_count(exclusions); i++) {
const struct route_exclusion *ex = exclusions[i];
if (ex->type == EXCLUDE_CHANNEL)
disabledmap_add_channel(p->disabledmap, ex->u.chan_id);
else
disabledmap_add_node(p->disabledmap, ex->u.node_id);
}
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];
}
void register_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);
}
struct command_result *payment_success(struct payment *payment,
const struct preimage *preimage TAKES)
{
register_payment_success(payment, preimage);
return payment_finish(payment);
}
void register_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);
}
struct command_result *payment_fail(struct payment *payment,
enum jsonrpc_errcode code, const char *fmt,
...)
{
/* can't pass variadic arguments forward, so let's expand them. */
va_list args;
va_start(args, fmt);
const char *error_msg = tal_vfmt(tmpctx, fmt, args);
va_end(args);
register_payment_fail(payment, code, "%s", error_msg);
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_dir scidd,
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_dir(tmpctx, &scidd),
str);
disabledmap_add_channel(p->disabledmap, scidd);
}
void payment_warn_chan(struct payment *p, struct short_channel_id_dir scidd,
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, scidd)) {
payment_disable_chan(p, scidd, 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_dir(tmpctx, &scidd), str);
disabledmap_warn_channel(p->disabledmap, scidd);
}
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);
}