mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 05:12:45 +01:00
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 <rusty@rustcorp.com.au>
This commit is contained in:
parent
45cac95331
commit
fa7934dfe3
@ -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. */
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -251,6 +251,7 @@ fi
|
||||
cat > $DIR1/config <<EOF
|
||||
log-level=debug
|
||||
bitcoind-poll=1s
|
||||
deadline-blocks=5
|
||||
min-htlc-expiry=6
|
||||
bitcoin-datadir=$DATADIR
|
||||
locktime-blocks=6
|
||||
@ -260,6 +261,7 @@ EOF
|
||||
cat > $DIR2/config <<EOF
|
||||
log-level=debug
|
||||
bitcoind-poll=1s
|
||||
deadline-blocks=5
|
||||
min-htlc-expiry=6
|
||||
bitcoin-datadir=$DATADIR
|
||||
locktime-blocks=6
|
||||
|
Loading…
Reference in New Issue
Block a user