Files
palladum-lightning/lightningd/htlc_set.c
Rusty Russell 6e5cb299dd global: remove unnecessary includes from C files.
Basically, `devtools/reduce-includes.sh */*.c`.

Build time from make clean (RUST=0) (includes building external libs):

Before:
	real    0m38.944000-40.416000(40.1131+/-0.4)s
	user    3m6.790000-17.159000(15.0571+/-2.8)s
	sys     0m35.304000-37.336000(36.8942+/-0.57)s
After:
	real    0m37.872000-39.974000(39.5466+/-0.59)s
	user    3m1.211000-14.968000(12.4556+/-3.9)s
	sys     0m35.008000-36.830000(36.4143+/-0.5)s

Build time after touch config.vars (RUST=0):

Before:
	real    0m19.831000-21.862000(21.5528+/-0.58)s
	user    2m15.361000-30.731000(28.4798+/-4.4)s
	sys     0m21.056000-22.339000(22.0346+/-0.35)s

After:
	real    0m18.384000-21.307000(20.8605+/-0.92)s
	user    2m5.585000-26.843000(23.6017+/-6.7)s
	sys     0m19.650000-22.003000(21.4943+/-0.69)s

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2025-10-23 06:44:04 +10:30

263 lines
7.7 KiB
C

#include "config.h"
#include <common/timeout.h>
#include <lightningd/chaintopology.h>
#include <lightningd/channel.h>
#include <lightningd/htlc_set.h>
#include <lightningd/invoice.h>
#include <lightningd/lightningd.h>
#include <lightningd/peer_htlcs.h>
static struct incoming_payment *new_inpay(const tal_t *ctx,
struct logger *log,
struct amount_msat msat,
void (*fail)(void *, const u8 *),
void (*succeeded)(void *,
const struct preimage *),
void *arg)
{
struct incoming_payment *inpay = tal(ctx, struct incoming_payment);
inpay->log = log;
inpay->msat = msat;
inpay->fail = fail;
inpay->succeeded = succeeded;
inpay->arg = arg;
return inpay;
}
/* If an HTLC times out, we need to free entire set, since we could be
* processing it in invoice.c right now. */
static void htlc_set_inpay_destroyed(struct incoming_payment *inpay,
struct htlc_set *set)
{
for (size_t i = 0; i < tal_count(set->inpays); i++) {
if (set->inpays[i] == inpay) {
/* Don't try to re-fail this HTLC! */
tal_arr_remove(&set->inpays, i);
/* Kind of the correct failure code. */
htlc_set_fail(set, take(towire_mpp_timeout(NULL)));
return;
}
}
abort();
}
static void destroy_htlc_set(struct htlc_set *set,
struct htlc_set_map *map)
{
htlc_set_map_del(map, set);
}
/* BOLT #4:
* - MUST fail all HTLCs in the HTLC set after some reasonable
* timeout.
*...
* - SHOULD use `mpp_timeout` for the failure message.
*/
static void timeout_htlc_set(struct htlc_set *set)
{
htlc_set_fail(set, take(towire_mpp_timeout(NULL)));
}
void htlc_set_fail_(struct htlc_set *set, const u8 *failmsg TAKES,
const char *file, int line)
{
/* Don't let local_fail_in_htlc take! */
if (taken(failmsg))
tal_steal(set, failmsg);
for (size_t i = 0; i < tal_count(set->inpays); i++) {
const u8 *this_failmsg;
/* Don't remove from set */
tal_del_destructor2(set->inpays[i], htlc_set_inpay_destroyed, set);
if (tal_bytelen(failmsg) == 0)
this_failmsg = towire_incorrect_or_unknown_payment_details(tmpctx, set->inpays[i]->msat, get_block_height(set->ld->topology));
else
this_failmsg = failmsg;
log_debug(set->inpays[i]->log,
"failing with %s: %s:%u",
onion_wire_name(fromwire_peektype(this_failmsg)),
file, line);
/* Attach inpays[i] to set so it's freed below (not with arg) */
tal_steal(set->inpays, set->inpays[i]);
set->inpays[i]->fail(set->inpays[i]->arg, this_failmsg);
}
tal_free(set);
}
void htlc_set_fulfill(struct htlc_set *set, const struct preimage *preimage)
{
for (size_t i = 0; i < tal_count(set->inpays); i++) {
/* Don't remove from set */
tal_del_destructor2(set->inpays[i],
htlc_set_inpay_destroyed, set);
/* Reparent set->inpays[i] so it's freed with set */
tal_steal(set->inpays, set->inpays[i]);
set->inpays[i]->succeeded(set->inpays[i]->arg, preimage);
}
tal_free(set);
}
static struct htlc_set *new_htlc_set(struct lightningd *ld,
struct incoming_payment *inpay,
const struct sha256 *payment_hash,
struct amount_msat total_msat)
{
struct htlc_set *set;
set = tal(ld, struct htlc_set);
set->ld = ld;
set->total_msat = total_msat;
set->payment_hash = *payment_hash;
set->so_far = AMOUNT_MSAT(0);
set->inpays = tal_arr(set, struct incoming_payment *, 1);
set->inpays[0] = inpay;
/* BOLT #4:
* - MUST fail all HTLCs in the HTLC set after some reasonable
* timeout.
* - SHOULD wait for at least 60 seconds after the initial
* HTLC.
*/
set->timeout = new_reltimer(ld->timers, set, time_from_sec(70),
timeout_htlc_set, set);
htlc_set_map_add(ld->htlc_sets, set);
tal_add_destructor2(set, destroy_htlc_set, ld->htlc_sets);
return set;
}
void htlc_set_add_(struct lightningd *ld,
struct logger *log,
struct amount_msat msat,
struct amount_msat total_msat,
const struct sha256 *payment_hash,
const struct secret *payment_secret,
void (*fail)(void *, const u8 *),
void (*succeeded)(void *, const struct preimage *),
void *arg)
{
struct incoming_payment *inpay;
struct htlc_set *set;
const struct invoice_details *details;
const char *err;
/* BOLT #4:
* The final node:
* - MUST fail the HTLC if dictated by Requirements under
* [Failure Messages](#failure-messages)
* - Note: "amount paid" specified there is the `total_msat` field.
*/
details = invoice_check_payment(tmpctx, ld, payment_hash,
total_msat, payment_secret, &err);
if (!details) {
log_debug(log, "payment failed: %s", err);
fail(arg, take(failmsg_incorrect_or_unknown(NULL, ld, msat)));
return;
}
/* If we insist on a payment secret, it must always have it */
if (feature_is_set(details->features, COMPULSORY_FEATURE(OPT_PAYMENT_SECRET))
&& !payment_secret) {
log_debug(log,
"Missing payment_secret, but required for %s",
fmt_sha256(tmpctx, payment_hash));
fail(arg, take(failmsg_incorrect_or_unknown(NULL, ld, msat)));
return;
}
/* BOLT #4:
* - otherwise, if it supports `basic_mpp`:
* - MUST add it to the HTLC set corresponding to that `payment_hash`.
*/
inpay = new_inpay(arg, log, msat, fail, succeeded, arg);
set = htlc_set_map_get(ld->htlc_sets, payment_hash);
if (!set)
set = new_htlc_set(ld, inpay, payment_hash, total_msat);
else {
/* BOLT #4:
*
* otherwise, if it supports `basic_mpp`:
* ...
* - otherwise, if the total `amt_to_forward` of this HTLC set is
* less than `total_msat`:
* ...
* - MUST require `payment_secret` for all HTLCs in the set.
*/
/* We check this now, since we want to fail with this as soon
* as possible, to avoid other probing attacks. */
if (!payment_secret) {
log_debug(log,
"Missing payment_secret, but required for MPP");
fail(arg, take(failmsg_incorrect_or_unknown(NULL, ld, msat)));
return;
}
tal_arr_expand(&set->inpays, inpay);
}
/* Remove from set should hin get destroyed somehow */
tal_add_destructor2(inpay, htlc_set_inpay_destroyed, set);
/* BOLT #4:
* - SHOULD fail the entire HTLC set if `total_msat` is not
* the same for all HTLCs in the set.
*/
if (!amount_msat_eq(total_msat, set->total_msat)) {
log_unusual(log, "Failing HTLC set %s:"
" total_msat %s new htlc total %s",
fmt_sha256(tmpctx, &set->payment_hash),
fmt_amount_msat(tmpctx, set->total_msat),
fmt_amount_msat(tmpctx, total_msat));
htlc_set_fail(set,
take(towire_final_incorrect_htlc_amount(NULL,
msat)));
return;
}
if (!amount_msat_accumulate(&set->so_far, msat)) {
log_unusual(ld->log, "Failing HTLC set %s:"
" overflow adding %s+%s",
fmt_sha256(tmpctx, &set->payment_hash),
fmt_amount_msat(tmpctx, set->so_far),
fmt_amount_msat(tmpctx, msat));
htlc_set_fail(set,
take(towire_final_incorrect_htlc_amount(NULL,
msat)));
return;
}
log_debug(ld->log,
"HTLC set contains %zu HTLCs, for a total of %s out of %s (%spayment_secret)",
tal_count(set->inpays),
fmt_amount_msat(tmpctx, set->so_far),
fmt_amount_msat(tmpctx, total_msat),
payment_secret ? "" : "no "
);
/* BOLT #4:
* - if the total `amt_to_forward` of this HTLC set is equal to or greater than
* `total_msat`:
* - SHOULD fulfill all HTLCs in the HTLC set
*/
if (amount_msat_greater_eq(set->so_far, total_msat)) {
/* Disable timer now, in case invoice_hook is slow! */
tal_free(set->timeout);
invoice_try_pay(ld, set, details, set->so_far, NULL);
return;
}
/* BOLT #4:
* - otherwise, if the total `amt_to_forward` of this HTLC set is less than
* `total_msat`:
* - MUST NOT fulfill any HTLCs in the HTLC set
*...
* - MUST require `payment_secret` for all HTLCs in the set. */
/* This catches the case of the first payment in a set. */
if (!payment_secret) {
htlc_set_fail(set, NULL);
return;
}
}