Browse Source
This isn't plumbed in yet, but the idea is that every htlc gets put into a "set" and then we process them once the set is satisfied. For the !EXPERIMENTAL_FEATURES, the set is simply always size 1. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>travis-debug
Rusty Russell
5 years ago
committed by
Christian Decker
5 changed files with 258 additions and 0 deletions
@ -0,0 +1,190 @@ |
|||
#include <common/memleak.h> |
|||
#include <common/timeout.h> |
|||
#include <lightningd/htlc_end.h> |
|||
#include <lightningd/htlc_set.h> |
|||
#include <lightningd/lightningd.h> |
|||
#include <lightningd/peer_htlcs.h> |
|||
|
|||
#if EXPERIMENTAL_FEATURES |
|||
/* 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) |
|||
{ |
|||
for (size_t i = 0; i < tal_count(set->htlcs); i++) { |
|||
if (set->htlcs[i] == hin) { |
|||
/* Don't try to re-fail this HTLC! */ |
|||
tal_arr_remove(&set->htlcs, i); |
|||
/* Kind of the correct failure code. */ |
|||
htlc_set_fail(set, WIRE_MPP_TIMEOUT); |
|||
return; |
|||
} |
|||
} |
|||
abort(); |
|||
} |
|||
|
|||
static void destroy_htlc_set(struct htlc_set *set, |
|||
struct htlc_set_map *map) |
|||
{ |
|||
htlc_set_map_del(map, set); |
|||
} |
|||
|
|||
/* BOLT-9441a66faad63edc8cd89860b22fbf24a86f0dcd #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, WIRE_MPP_TIMEOUT); |
|||
} |
|||
#endif /* EXPERIMENTAL_FEATURES */ |
|||
|
|||
void htlc_set_fail(struct htlc_set *set, enum onion_type failcode) |
|||
{ |
|||
for (size_t i = 0; i < tal_count(set->htlcs); i++) { |
|||
#if EXPERIMENTAL_FEATURES |
|||
/* Don't remove from set */ |
|||
tal_del_destructor2(set->htlcs[i], htlc_set_hin_destroyed, set); |
|||
#endif |
|||
fail_htlc(set->htlcs[i], failcode); |
|||
} |
|||
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++) { |
|||
#if EXPERIMENTAL_FEATURES |
|||
/* Don't remove from set */ |
|||
tal_del_destructor2(set->htlcs[i], htlc_set_hin_destroyed, set); |
|||
#endif |
|||
fulfill_htlc(set->htlcs[i], preimage); |
|||
} |
|||
tal_free(set); |
|||
} |
|||
|
|||
static struct htlc_set *new_htlc_set(struct lightningd *ld, |
|||
struct htlc_in *hin, |
|||
struct amount_msat total_msat) |
|||
{ |
|||
struct htlc_set *set; |
|||
|
|||
set = tal(ld, struct htlc_set); |
|||
set->total_msat = total_msat; |
|||
set->payment_hash = hin->payment_hash; |
|||
set->so_far = AMOUNT_MSAT(0); |
|||
set->htlcs = tal_arr(set, struct htlc_in *, 1); |
|||
set->htlcs[0] = hin; |
|||
|
|||
#if EXPERIMENTAL_FEATURES |
|||
/* BOLT-9441a66faad63edc8cd89860b22fbf24a86f0dcd #4:
|
|||
* - MUST fail all HTLCs in the HTLC set after some reasonable |
|||
* timeout. |
|||
* - SHOULD wait for at least 60 seconds after the initial |
|||
* HTLC. |
|||
*/ |
|||
notleak(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); |
|||
#endif |
|||
return set; |
|||
} |
|||
|
|||
void htlc_set_add(struct lightningd *ld, |
|||
struct htlc_in *hin, |
|||
struct amount_msat total_msat, |
|||
const struct secret *payment_secret) |
|||
{ |
|||
struct htlc_set *set; |
|||
const struct invoice_details *details; |
|||
|
|||
/* BOLT-9441a66faad63edc8cd89860b22fbf24a86f0dcd #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, &hin->payment_hash, |
|||
total_msat, payment_secret); |
|||
if (!details) { |
|||
fail_htlc(hin, WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS); |
|||
return; |
|||
} |
|||
|
|||
#if !EXPERIMENTAL_FEATURES |
|||
/* BOLT-9441a66faad63edc8cd89860b22fbf24a86f0dcd #4:
|
|||
* - if it does not support `basic_mpp`: |
|||
* - MUST fail the HTLC if `total_msat` is not exactly equal to |
|||
* `amt_to_forward`. |
|||
*/ |
|||
if (!amount_msat_eq(hin->msat, total_msat)) { |
|||
fail_htlc(hin, WIRE_FINAL_INCORRECT_HTLC_AMOUNT); |
|||
return; |
|||
} |
|||
|
|||
/* We create a transient set which just has one entry. */ |
|||
set = new_htlc_set(ld, hin, total_msat); |
|||
#else |
|||
/* BOLT-9441a66faad63edc8cd89860b22fbf24a86f0dcd #4:
|
|||
* - otherwise, if it supports `basic_mpp`: |
|||
* - MUST add it to the HTLC set corresponding to that `payment_hash`. |
|||
* - if the total `amount_msat` of this HTLC set equals `total_msat`: |
|||
* - SHOULD fulfill all HTLCs in the HTLC set |
|||
*/ |
|||
set = htlc_set_map_get(&ld->htlc_sets, &hin->payment_hash); |
|||
if (!set) |
|||
set = new_htlc_set(ld, hin, total_msat); |
|||
else |
|||
tal_arr_expand(&set->htlcs, hin); |
|||
|
|||
/* Remove from set should hin get destroyed somehow */ |
|||
tal_add_destructor2(hin, htlc_set_hin_destroyed, set); |
|||
|
|||
/* BOLT-9441a66faad63edc8cd89860b22fbf24a86f0dcd #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:" |
|||
" total_msat %s new htlc total %s", |
|||
type_to_string(tmpctx, struct sha256, |
|||
&set->payment_hash), |
|||
type_to_string(tmpctx, struct amount_msat, |
|||
&set->total_msat), |
|||
type_to_string(tmpctx, struct amount_msat, |
|||
&total_msat)); |
|||
htlc_set_fail(set, WIRE_FINAL_INCORRECT_HTLC_AMOUNT); |
|||
return; |
|||
} |
|||
#endif /* EXPERIMENTAL_FEATURES */ |
|||
|
|||
/* BOLT-9441a66faad63edc8cd89860b22fbf24a86f0dcd #4:
|
|||
* - if the total `amount_msat` of this HTLC set equals `total_msat`: |
|||
* - SHOULD fulfill all HTLCs in the HTLC set |
|||
*/ |
|||
if (!amount_msat_add(&set->so_far, set->so_far, hin->msat)) { |
|||
log_unusual(ld->log, "Failing HTLC set %s:" |
|||
" overflow adding %s+%s", |
|||
type_to_string(tmpctx, struct sha256, |
|||
&set->payment_hash), |
|||
type_to_string(tmpctx, struct amount_msat, |
|||
&set->so_far), |
|||
type_to_string(tmpctx, struct amount_msat, |
|||
&hin->msat)); |
|||
htlc_set_fail(set, WIRE_FINAL_INCORRECT_HTLC_AMOUNT); |
|||
return; |
|||
} |
|||
|
|||
if (amount_msat_eq(set->so_far, total_msat)) { |
|||
/* FIXME: hand to invoice_try_pay! */ |
|||
tal_free(set); |
|||
return; |
|||
} |
|||
|
|||
/* BOLT-9441a66faad63edc8cd89860b22fbf24a86f0dcd #4:
|
|||
* - otherwise, if the total `amount_msat` of this HTLC set is less than |
|||
* `total_msat`: |
|||
* - MUST NOT fulfill any HTLCs in the HTLC set */ |
|||
} |
@ -0,0 +1,55 @@ |
|||
#ifndef LIGHTNING_LIGHTNINGD_HTLC_SET_H |
|||
#define LIGHTNING_LIGHTNINGD_HTLC_SET_H |
|||
#include "config.h" |
|||
#include <ccan/crypto/sha256/sha256.h> |
|||
#include <ccan/crypto/siphash24/siphash24.h> |
|||
#include <ccan/htable/htable_type.h> |
|||
#include <common/amount.h> |
|||
#include <common/pseudorand.h> |
|||
#include <common/utils.h> |
|||
#include <wire/gen_onion_wire.h> |
|||
|
|||
struct htlc_in; |
|||
struct lightningd; |
|||
|
|||
/* Set of incoming HTLCs for multi-part-payments */ |
|||
struct htlc_set { |
|||
struct amount_msat total_msat, so_far; |
|||
struct sha256 payment_hash; |
|||
struct htlc_in **htlcs; |
|||
}; |
|||
|
|||
static inline const struct sha256 *keyof_htlc_set(const struct htlc_set *set) |
|||
{ |
|||
return &set->payment_hash; |
|||
} |
|||
|
|||
static inline size_t hash_payment_hash(const struct sha256 *payment_hash) |
|||
{ |
|||
return siphash24(siphash_seed(), payment_hash, sizeof(&payment_hash)); |
|||
} |
|||
|
|||
static inline bool htlc_set_eq(const struct htlc_set *set, |
|||
const struct sha256 *payment_hash) |
|||
{ |
|||
return sha256_eq(payment_hash, &set->payment_hash); |
|||
} |
|||
|
|||
HTABLE_DEFINE_TYPE(struct htlc_set, |
|||
keyof_htlc_set, |
|||
hash_payment_hash, |
|||
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); |
|||
|
|||
/* Fail every htlc in the set: frees set */ |
|||
void htlc_set_fail(struct htlc_set *set, enum onion_type failcode); |
|||
|
|||
/* Fulfill every htlc in the set: frees set */ |
|||
void htlc_set_fulfill(struct htlc_set *set, const struct preimage *preimage); |
|||
#endif /* LIGHTNING_LIGHTNINGD_HTLC_SET_H */ |
Loading…
Reference in new issue