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:
Rusty Russell 2016-07-01 11:19:28 +09:30
parent 45cac95331
commit fa7934dfe3
5 changed files with 72 additions and 4 deletions

View File

@ -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. */

View File

@ -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)

View File

@ -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;

View File

@ -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 {

View File

@ -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