mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-11-19 01:43:36 +01:00
lightningd: implement htlc sets.
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>
This commit is contained in:
parent
7f629e545e
commit
36b1e884dd
@ -75,6 +75,7 @@ LIGHTNINGD_SRC := \
|
||||
lightningd/gossip_msg.c \
|
||||
lightningd/hsm_control.c \
|
||||
lightningd/htlc_end.c \
|
||||
lightningd/htlc_set.c \
|
||||
lightningd/invoice.c \
|
||||
lightningd/io_loop_with_timers.c \
|
||||
lightningd/json.c \
|
||||
|
190
lightningd/htlc_set.c
Normal file
190
lightningd/htlc_set.c
Normal file
@ -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 */
|
||||
}
|
55
lightningd/htlc_set.h
Normal file
55
lightningd/htlc_set.h
Normal file
@ -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 */
|
@ -161,6 +161,12 @@ static struct lightningd *new_lightningd(const tal_t *ctx)
|
||||
htlc_in_map_init(&ld->htlcs_in);
|
||||
htlc_out_map_init(&ld->htlcs_out);
|
||||
|
||||
#if EXPERIMENTAL_FEATURES
|
||||
/*~ For multi-part payments, we need to keep some incoming payments
|
||||
* in limbo until we get all the parts, or we time them out. */
|
||||
htlc_set_map_init(&ld->htlc_sets);
|
||||
#endif /* EXPERIMENTAL_FEATURES */
|
||||
|
||||
/*~ We have a multi-entry log-book infrastructure: we define a 100MB log
|
||||
* book to hold all the entries (and trims as necessary), and multiple
|
||||
* log objects which each can write into it, each with a unique
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <ccan/time/time.h>
|
||||
#include <ccan/timer/timer.h>
|
||||
#include <lightningd/htlc_end.h>
|
||||
#include <lightningd/htlc_set.h>
|
||||
#include <lightningd/plugin.h>
|
||||
#include <stdio.h>
|
||||
#include <wallet/txfilter.h>
|
||||
@ -162,6 +163,11 @@ struct lightningd {
|
||||
struct htlc_in_map htlcs_in;
|
||||
struct htlc_out_map htlcs_out;
|
||||
|
||||
#if EXPERIMENTAL_FEATURES
|
||||
/* Sets of HTLCs we are holding onto for MPP. */
|
||||
struct htlc_set_map htlc_sets;
|
||||
#endif
|
||||
|
||||
struct wallet *wallet;
|
||||
|
||||
/* Outstanding waitsendpay commands. */
|
||||
|
Loading…
Reference in New Issue
Block a user