diff --git a/lightningd/htlc_set.c b/lightningd/htlc_set.c index c5eca979f..47677678d 100644 --- a/lightningd/htlc_set.c +++ b/lightningd/htlc_set.c @@ -8,15 +8,32 @@ #include #include -/* 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 " diff --git a/lightningd/htlc_set.h b/lightningd/htlc_set.h index f63d7610a..4bc44bad5 100644 --- a/lightningd/htlc_set.h +++ b/lightningd/htlc_set.h @@ -9,15 +9,28 @@ #include #include -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. */ diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 0d9f33775..52f283984 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -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); diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 41744b9aa..8c477ba95 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -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: diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 5aa92ab66..1a0b4bce0 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -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) diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 9fe88a2cb..6713382b4 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -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,