mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-03-12 18:49:42 +01:00
lightningd: generalize htlc_set.
Make it a set of arbitrary data, so we can use it for local payments, not just HTLCs. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
41610d7bab
commit
93c1876d1e
6 changed files with 146 additions and 63 deletions
|
@ -8,15 +8,32 @@
|
|||
#include <lightningd/lightningd.h>
|
||||
#include <lightningd/peer_htlcs.h>
|
||||
|
||||
/* 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_hin_destroyed(struct htlc_in *hin,
|
||||
struct htlc_set *set)
|
||||
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)
|
||||
{
|
||||
for (size_t i = 0; i < tal_count(set->htlcs); i++) {
|
||||
if (set->htlcs[i] == hin) {
|
||||
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->htlcs, i);
|
||||
tal_arr_remove(&set->inpays, i);
|
||||
/* Kind of the correct failure code. */
|
||||
htlc_set_fail(set, take(towire_mpp_timeout(NULL)));
|
||||
return;
|
||||
|
@ -49,42 +66,45 @@ void htlc_set_fail_(struct htlc_set *set, const u8 *failmsg TAKES,
|
|||
if (taken(failmsg))
|
||||
tal_steal(set, failmsg);
|
||||
|
||||
for (size_t i = 0; i < tal_count(set->htlcs); i++) {
|
||||
for (size_t i = 0; i < tal_count(set->inpays); i++) {
|
||||
const u8 *this_failmsg;
|
||||
|
||||
/* Don't remove from set */
|
||||
tal_del_destructor2(set->htlcs[i], htlc_set_hin_destroyed, 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->htlcs[i]->msat, get_block_height(set->ld->topology));
|
||||
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->htlcs[i]->key.channel->log,
|
||||
log_debug(set->inpays[i]->log,
|
||||
"failing with %s: %s:%u",
|
||||
onion_wire_name(fromwire_peektype(this_failmsg)),
|
||||
file, line);
|
||||
local_fail_in_htlc(set->htlcs[i], this_failmsg);
|
||||
/* 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->htlcs); i++) {
|
||||
for (size_t i = 0; i < tal_count(set->inpays); i++) {
|
||||
/* Don't remove from set */
|
||||
tal_del_destructor2(set->htlcs[i], htlc_set_hin_destroyed, set);
|
||||
tal_del_destructor2(set->inpays[i],
|
||||
htlc_set_inpay_destroyed, set);
|
||||
|
||||
/* mark that we filled -- needed for tagging coin mvt */
|
||||
set->htlcs[i]->we_filled = tal(set->htlcs[i], bool);
|
||||
*set->htlcs[i]->we_filled = true;
|
||||
fulfill_htlc(set->htlcs[i], preimage);
|
||||
/* 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 htlc_in *hin,
|
||||
struct incoming_payment *inpay,
|
||||
const struct sha256 *payment_hash,
|
||||
struct amount_msat total_msat)
|
||||
{
|
||||
struct htlc_set *set;
|
||||
|
@ -92,10 +112,10 @@ static struct htlc_set *new_htlc_set(struct lightningd *ld,
|
|||
set = tal(ld, struct htlc_set);
|
||||
set->ld = ld;
|
||||
set->total_msat = total_msat;
|
||||
set->payment_hash = hin->payment_hash;
|
||||
set->payment_hash = *payment_hash;
|
||||
set->so_far = AMOUNT_MSAT(0);
|
||||
set->htlcs = tal_arr(set, struct htlc_in *, 1);
|
||||
set->htlcs[0] = hin;
|
||||
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
|
||||
|
@ -110,11 +130,17 @@ static struct htlc_set *new_htlc_set(struct lightningd *ld,
|
|||
return set;
|
||||
}
|
||||
|
||||
void htlc_set_add(struct lightningd *ld,
|
||||
struct htlc_in *hin,
|
||||
struct amount_msat total_msat,
|
||||
const struct secret *payment_secret)
|
||||
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;
|
||||
|
@ -125,23 +151,21 @@ void htlc_set_add(struct lightningd *ld,
|
|||
* [Failure Messages](#failure-messages)
|
||||
* - Note: "amount paid" specified there is the `total_msat` field.
|
||||
*/
|
||||
details = invoice_check_payment(tmpctx, ld, &hin->payment_hash,
|
||||
details = invoice_check_payment(tmpctx, ld, payment_hash,
|
||||
total_msat, payment_secret, &err);
|
||||
if (!details) {
|
||||
log_debug(hin->key.channel->log, "payment failed: %s", err);
|
||||
local_fail_in_htlc(hin,
|
||||
take(failmsg_incorrect_or_unknown(NULL, ld, hin->msat)));
|
||||
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(hin->key.channel->log,
|
||||
log_debug(log,
|
||||
"Missing payment_secret, but required for %s",
|
||||
fmt_sha256(tmpctx, &hin->payment_hash));
|
||||
local_fail_in_htlc(hin,
|
||||
take(failmsg_incorrect_or_unknown(NULL, ld, hin->msat)));
|
||||
fmt_sha256(tmpctx, payment_hash));
|
||||
fail(arg, take(failmsg_incorrect_or_unknown(NULL, ld, msat)));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -149,9 +173,10 @@ void htlc_set_add(struct lightningd *ld,
|
|||
* - otherwise, if it supports `basic_mpp`:
|
||||
* - MUST add it to the HTLC set corresponding to that `payment_hash`.
|
||||
*/
|
||||
set = htlc_set_map_get(ld->htlc_sets, &hin->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, hin, total_msat);
|
||||
set = new_htlc_set(ld, inpay, payment_hash, total_msat);
|
||||
else {
|
||||
/* BOLT #4:
|
||||
*
|
||||
|
@ -165,48 +190,48 @@ void htlc_set_add(struct lightningd *ld,
|
|||
/* 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(hin->key.channel->log,
|
||||
log_debug(log,
|
||||
"Missing payment_secret, but required for MPP");
|
||||
local_fail_in_htlc(hin, take(failmsg_incorrect_or_unknown(NULL, ld, hin->msat)));
|
||||
fail(arg, take(failmsg_incorrect_or_unknown(NULL, ld, msat)));
|
||||
return;
|
||||
}
|
||||
tal_arr_expand(&set->htlcs, hin);
|
||||
tal_arr_expand(&set->inpays, inpay);
|
||||
}
|
||||
|
||||
/* Remove from set should hin get destroyed somehow */
|
||||
tal_add_destructor2(hin, htlc_set_hin_destroyed, set);
|
||||
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(ld->log, "Failing HTLC set %s:"
|
||||
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,
|
||||
hin->msat)));
|
||||
msat)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!amount_msat_accumulate(&set->so_far, hin->msat)) {
|
||||
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, hin->msat));
|
||||
fmt_amount_msat(tmpctx, msat));
|
||||
htlc_set_fail(set,
|
||||
take(towire_final_incorrect_htlc_amount(NULL,
|
||||
hin->msat)));
|
||||
msat)));
|
||||
return;
|
||||
}
|
||||
|
||||
log_debug(ld->log,
|
||||
"HTLC set contains %zu HTLCs, for a total of %s out of %s (%spayment_secret)",
|
||||
tal_count(set->htlcs),
|
||||
tal_count(set->inpays),
|
||||
fmt_amount_msat(tmpctx, set->so_far),
|
||||
fmt_amount_msat(tmpctx, total_msat),
|
||||
payment_secret ? "" : "no "
|
||||
|
|
|
@ -9,15 +9,28 @@
|
|||
#include <common/utils.h>
|
||||
#include <wire/onion_wire.h>
|
||||
|
||||
struct htlc_in;
|
||||
struct lightningd;
|
||||
struct logger;
|
||||
|
||||
/* Could be an incoming HTLC, could be a local payment */
|
||||
struct incoming_payment {
|
||||
/* Where to log */
|
||||
struct logger *log;
|
||||
/* Amount of this payment */
|
||||
struct amount_msat msat;
|
||||
/* If it fails */
|
||||
void (*fail)(void *arg, const u8 *failmsg TAKES);
|
||||
/* If it succeeded: here's the preimage. */
|
||||
void (*succeeded)(void *arg, const struct preimage *preimage);
|
||||
void *arg;
|
||||
};
|
||||
|
||||
/* Set of incoming HTLCs for multi-part-payments */
|
||||
struct htlc_set {
|
||||
struct lightningd *ld;
|
||||
struct amount_msat total_msat, so_far;
|
||||
struct sha256 payment_hash;
|
||||
struct htlc_in **htlcs;
|
||||
struct incoming_payment **inpays;
|
||||
struct oneshot *timeout;
|
||||
};
|
||||
|
||||
|
@ -43,11 +56,28 @@ HTABLE_DEFINE_TYPE(struct htlc_set,
|
|||
htlc_set_eq,
|
||||
htlc_set_map);
|
||||
|
||||
/* Handles hin: if it completes a set, hands that to invoice_try_pay */
|
||||
void htlc_set_add(struct lightningd *ld,
|
||||
struct htlc_in *hin,
|
||||
struct amount_msat total_msat,
|
||||
const struct secret *payment_secret);
|
||||
/* Handles arg: if it completes a set, calls invoice_try_pay */
|
||||
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);
|
||||
|
||||
#define htlc_set_add(ld, log, msat, total_msat, payment_hash, payment_secret, \
|
||||
fail, succeeded, arg) \
|
||||
htlc_set_add_((ld), (log), (msat), (total_msat), (payment_hash), \
|
||||
(payment_secret), \
|
||||
typesafe_cb_postargs(void, void *, \
|
||||
(fail), (arg), \
|
||||
const u8 *), \
|
||||
typesafe_cb_postargs(void, void *, \
|
||||
(succeeded), (arg), \
|
||||
const struct preimage *), \
|
||||
(arg))
|
||||
|
||||
/* Fail every htlc in the set: frees set. If failmsg is NULL/zero-length,
|
||||
* it sends each one a WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS. */
|
||||
|
|
|
@ -176,10 +176,14 @@ static void invoice_payment_add_tlvs(struct json_stream *stream,
|
|||
{
|
||||
struct htlc_in *hin;
|
||||
const struct tlv_payload *tlvs;
|
||||
assert(tal_count(hset->htlcs) > 0);
|
||||
assert(tal_count(hset->inpays) > 0);
|
||||
|
||||
/* Only do this if it's actually an HTLC */
|
||||
if ((void *)hset->inpays[0]->fail != (void *)local_fail_in_htlc)
|
||||
return;
|
||||
|
||||
/* Pick the first HTLC as representative for the entire set. */
|
||||
hin = hset->htlcs[0];
|
||||
hin = hset->inpays[0]->arg;
|
||||
|
||||
tlvs = hin->payload->tlv;
|
||||
|
||||
|
@ -291,7 +295,7 @@ invoice_payment_hooks_done(struct invoice_payment_hook_payload *payload STEALS)
|
|||
log_info(ld->log, "Resolved invoice '%s' with amount %s in %zu htlcs",
|
||||
payload->label->s,
|
||||
fmt_amount_msat(tmpctx, payload->msat),
|
||||
payload->set ? tal_count(payload->set->htlcs) : 0);
|
||||
payload->set ? tal_count(payload->set->inpays) : 0);
|
||||
if (payload->set)
|
||||
htlc_set_fulfill(payload->set, &payload->preimage);
|
||||
|
||||
|
|
|
@ -407,6 +407,16 @@ void fulfill_htlc(struct htlc_in *hin, const struct preimage *preimage)
|
|||
subd_send_msg(channel->owner, take(msg));
|
||||
}
|
||||
|
||||
/* HTLC-specific wrappers */
|
||||
static void htlc_set_fulfill_htlc(struct htlc_in *hin,
|
||||
const struct preimage *preimage)
|
||||
{
|
||||
/* mark that we filled -- needed for tagging coin mvt */
|
||||
hin->we_filled = tal(hin, bool);
|
||||
*hin->we_filled = true;
|
||||
fulfill_htlc(hin, preimage);
|
||||
}
|
||||
|
||||
static void handle_localpay(struct htlc_in *hin,
|
||||
struct amount_msat amt_to_forward,
|
||||
u32 outgoing_cltv_value,
|
||||
|
@ -499,7 +509,13 @@ static void handle_localpay(struct htlc_in *hin,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
htlc_set_add(ld, hin, total_msat, payment_secret);
|
||||
htlc_set_add(ld, hin->key.channel->log,
|
||||
hin->msat, total_msat,
|
||||
&hin->payment_hash,
|
||||
payment_secret,
|
||||
local_fail_in_htlc,
|
||||
htlc_set_fulfill_htlc,
|
||||
hin);
|
||||
return;
|
||||
|
||||
fail:
|
||||
|
|
|
@ -694,6 +694,9 @@ struct jsonrpc_request *jsonrpc_request_start_(
|
|||
void kill_uncommitted_channel(struct uncommitted_channel *uc UNNEEDED,
|
||||
const char *why UNNEEDED)
|
||||
{ fprintf(stderr, "kill_uncommitted_channel called!\n"); abort(); }
|
||||
/* Generated stub for local_fail_in_htlc */
|
||||
void local_fail_in_htlc(struct htlc_in *hin UNNEEDED, const u8 *failmsg TAKES UNNEEDED)
|
||||
{ fprintf(stderr, "local_fail_in_htlc called!\n"); abort(); }
|
||||
/* Generated stub for lockin_complete */
|
||||
void lockin_complete(struct channel *channel UNNEEDED,
|
||||
enum channel_state expected_state UNNEEDED)
|
||||
|
|
|
@ -359,12 +359,17 @@ bool htlc_is_trimmed(enum side htlc_owner UNNEEDED,
|
|||
bool option_anchor_outputs UNNEEDED,
|
||||
bool option_anchors_zero_fee_htlc_tx UNNEEDED)
|
||||
{ fprintf(stderr, "htlc_is_trimmed called!\n"); abort(); }
|
||||
/* Generated stub for htlc_set_add */
|
||||
void htlc_set_add(struct lightningd *ld UNNEEDED,
|
||||
struct htlc_in *hin UNNEEDED,
|
||||
struct amount_msat total_msat UNNEEDED,
|
||||
const struct secret *payment_secret UNNEEDED)
|
||||
{ fprintf(stderr, "htlc_set_add called!\n"); abort(); }
|
||||
/* Generated stub for htlc_set_add_ */
|
||||
void htlc_set_add_(struct lightningd *ld UNNEEDED,
|
||||
struct logger *log UNNEEDED,
|
||||
struct amount_msat msat UNNEEDED,
|
||||
struct amount_msat total_msat UNNEEDED,
|
||||
const struct sha256 *payment_hash UNNEEDED,
|
||||
const struct secret *payment_secret UNNEEDED,
|
||||
void (*fail)(void * UNNEEDED, const u8 *) UNNEEDED,
|
||||
void (*succeeded)(void * UNNEEDED, const struct preimage *) UNNEEDED,
|
||||
void *arg UNNEEDED)
|
||||
{ fprintf(stderr, "htlc_set_add_ called!\n"); abort(); }
|
||||
/* Generated stub for invoice_check_payment */
|
||||
const struct invoice_details *invoice_check_payment(const tal_t *ctx UNNEEDED,
|
||||
struct lightningd *ld UNNEEDED,
|
||||
|
|
Loading…
Add table
Reference in a new issue