From fa7934dfe35a8298d7869851d545929cd2f32cb1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 1 Jul 2016 11:19:28 +0930 Subject: [PATCH] htlc: implement deadline as per BOLT. Thus a node MUST estimate the deadline for successful redemption for each HTLC it offers. A node MUST NOT offer a HTLC after this deadline, and MUST fail the connection if an HTLC which it offered is in either node's current commitment transaction past this deadline. Signed-off-by: Rusty Russell --- daemon/htlc.h | 2 ++ daemon/lightningd.c | 17 +++++++++++++++ daemon/lightningd.h | 3 +++ daemon/peer.c | 52 +++++++++++++++++++++++++++++++++++++++++---- daemon/test/test.sh | 2 ++ 5 files changed, 72 insertions(+), 4 deletions(-) diff --git a/daemon/htlc.h b/daemon/htlc.h index da1225520..6b9bbc692 100644 --- a/daemon/htlc.h +++ b/daemon/htlc.h @@ -27,6 +27,8 @@ struct htlc { const u8 *routing; /* Previous HTLC (if any) which made us offer this (OURS only) */ struct htlc *src; + /* Block number where we abort if it's still live (OURS only) */ + u32 deadline; }; /* htlc_map: ID -> htlc mapping. */ diff --git a/daemon/lightningd.c b/daemon/lightningd.c index 33b7e32aa..085310418 100644 --- a/daemon/lightningd.c +++ b/daemon/lightningd.c @@ -122,6 +122,9 @@ static void config_register_opts(struct lightningd_state *dstate) opt_register_arg("--max-htlc-expiry", opt_set_u32, opt_show_u32, &dstate->config.max_htlc_expiry, "Maximum number of blocks to accept an HTLC before expiry"); + opt_register_arg("--deadline-blocks", opt_set_u32, opt_show_u32, + &dstate->config.deadline_blocks, + "Number of blocks before HTLC timeout before we drop connection"); opt_register_arg("--bitcoind-poll", opt_set_time, opt_show_time, &dstate->config.poll_time, "Time between polling for new transactions"); @@ -181,6 +184,10 @@ static void default_config(struct config *config) /* Don't lock up channel for more than 5 days. */ config->max_htlc_expiry = 5 * 6 * 24; + /* If we're closing on HTLC expiry, and you're unresponsive, we abort. */ + config->deadline_blocks = 10; + + /* How often to bother bitcoind. */ config->poll_time = time_from_sec(30); /* Send commit 10msec after receiving; almost immediately. */ @@ -217,6 +224,16 @@ static void check_config(struct lightningd_state *dstate) log_unusual(dstate->base_log, "Warning: forever-confirms of %u is less than 100!", dstate->config.forever_confirms); + + /* BOLT #2: + * + * a node MUST estimate the deadline for successful redemption + * for each HTLC it offers. A node MUST NOT offer a HTLC + * after this deadline */ + if (dstate->config.deadline_blocks >= dstate->config.min_htlc_expiry) + fatal("Deadline %u can't be more than minimum expiry %u", + dstate->config.deadline_blocks, + dstate->config.min_htlc_expiry); } static struct lightningd_state *lightningd_state(void) diff --git a/daemon/lightningd.h b/daemon/lightningd.h index f88379c28..78fd393e0 100644 --- a/daemon/lightningd.h +++ b/daemon/lightningd.h @@ -41,6 +41,9 @@ struct config { /* Minimum/maximum time for an expiring HTLC (blocks). */ u32 min_htlc_expiry, max_htlc_expiry; + /* How many blocks before upstream HTLC expiry do we panic and dump? */ + u32 deadline_blocks; + /* Fee rates. */ u32 fee_base; s32 fee_per_satoshi; diff --git a/daemon/peer.c b/daemon/peer.c index 610174e79..0af60500f 100644 --- a/daemon/peer.c +++ b/daemon/peer.c @@ -1028,9 +1028,16 @@ struct htlc *peer_new_htlc(struct peer *peer, fatal("Invalid HTLC expiry %u", expiry); h->routing = tal_dup_arr(h, u8, route, routelen, 0); h->src = src; - if (side == OURS) + if (side == OURS) { + if (src) { + h->deadline = abs_locktime_to_blocks(&src->expiry) + - peer->dstate->config.deadline_blocks; + } else + /* If we're paying, give it a little longer. */ + h->deadline = expiry + + peer->dstate->config.min_htlc_expiry; htlc_map_add(&peer->local.htlcs, h); - else { + } else { assert(side == THEIRS); htlc_map_add(&peer->remote.htlcs, h); } @@ -1232,9 +1239,24 @@ const struct json_command connect_command = { "Returns an empty result on success" }; -/* FIXME: Keep a timeout for each peer, in case they're unresponsive. */ +/* Have any of our HTLCs passed their deadline? */ +static bool any_deadline_past(struct peer *peer, + const struct channel_state *cstate) +{ + size_t i; + u32 height = get_block_height(peer->dstate); + struct htlc **htlcs = cstate->side[OURS].htlcs; -/* FIXME: Make sure no HTLCs in any unrevoked commit tx are live. */ + for (i = 0; i < tal_count(htlcs); i++) { + if (height >= htlcs[i]->deadline) { + log_unusual_struct(peer->log, + "HTLC %s deadline has passed", + struct htlc, htlcs[i]); + return true; + } + } + return false; +} static void check_htlc_expiry(struct peer *peer) { @@ -1259,6 +1281,28 @@ again: return; goto again; } + + /* BOLT #2: + * + * A node MUST NOT offer a HTLC after this deadline, and MUST + * fail the connection if an HTLC which it offered is in + * either node's current commitment transaction past this + * deadline. + */ + + /* To save logic elsewhere (ie. to avoid signing a new commit with a + * past-deadline HTLC) we also check staged HTLCs. + */ + if (!state_is_normal(peer->state)) + return; + + if (any_deadline_past(peer, peer->remote.staging_cstate) + || any_deadline_past(peer, peer->local.staging_cstate) + || any_deadline_past(peer, peer->remote.commit->cstate) + || any_deadline_past(peer, peer->local.commit->cstate)) { + set_peer_state(peer, STATE_ERR_BREAKDOWN, __func__); + peer_breakdown(peer); + } } struct anchor_watch { diff --git a/daemon/test/test.sh b/daemon/test/test.sh index 972ce1a8c..e15799da2 100755 --- a/daemon/test/test.sh +++ b/daemon/test/test.sh @@ -251,6 +251,7 @@ fi cat > $DIR1/config < $DIR2/config <