daemon: HTLC expiry limits.

Don't accept an HTLC which is about to expire, nor one which will take
too long to expire.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2016-01-22 06:45:28 +10:30
parent a3e3f83d9f
commit 1018823f97
5 changed files with 79 additions and 7 deletions

View File

@ -88,21 +88,31 @@ static void config_register_opts(struct lightningd_state *dstate)
opt_register_arg("--closing-fee", opt_set_u64, opt_show_u64, opt_register_arg("--closing-fee", opt_set_u64, opt_show_u64,
&dstate->config.closing_fee, &dstate->config.closing_fee,
"Satoshis to use for mutual close transaction fee"); "Satoshis to use for mutual close transaction fee");
opt_register_arg("--min-expiry", opt_set_u32, opt_show_u32,
&dstate->config.min_expiry,
"Minimum number of seconds to accept an HTLC before expiry");
opt_register_arg("--max-expiry", opt_set_u32, opt_show_u32,
&dstate->config.max_expiry,
"Maximum number of seconds to accept an HTLC before expiry");
opt_register_arg("--bitcoind-poll", opt_set_u32, opt_show_u32, opt_register_arg("--bitcoind-poll", opt_set_u32, opt_show_u32,
&dstate->config.poll_seconds, &dstate->config.poll_seconds,
"Seconds between polling for new transactions"); "Seconds between polling for new transactions");
} }
#define MINUTES 60
#define HOURS (60 * MINUTES)
#define DAYS (24 * HOURS)
static void default_config(struct config *config) static void default_config(struct config *config)
{ {
/* aka. "Dude, where's my coins?" */ /* aka. "Dude, where's my coins?" */
config->testnet = true; config->testnet = true;
/* One day to catch cheating attempts. */ /* One day to catch cheating attempts. */
config->rel_locktime = 60 * 60 * 24; config->rel_locktime = 1 * DAYS;
/* They can have up to 3 days. */ /* They can have up to 3 days. */
config->rel_locktime_max = 60 * 60 * 24 * 3; config->rel_locktime_max = 2 * DAYS;
/* We're fairly trusting, under normal circumstances. */ /* We're fairly trusting, under normal circumstances. */
config->anchor_confirms = 3; config->anchor_confirms = 3;
@ -120,7 +130,12 @@ static void default_config(struct config *config)
/* Use this for mutual close. */ /* Use this for mutual close. */
config->closing_fee = 10000; config->closing_fee = 10000;
/* Don't bother me unless I have 6 hours to collect. */
config->min_expiry = 6 * HOURS;
/* Don't lock up channel for more than 5 days. */
config->max_expiry = 5 * DAYS;
config->poll_seconds = 30; config->poll_seconds = 30;
} }

View File

@ -35,6 +35,9 @@ struct config {
/* What fee we use for the closing transaction (satoshis) */ /* What fee we use for the closing transaction (satoshis) */
u64 closing_fee; u64 closing_fee;
/* Minimum/maximum time for an expiring HTLC (seconds). */
u32 min_expiry, max_expiry;
/* How long (seconds) between polling bitcoind. */ /* How long (seconds) between polling bitcoind. */
u32 poll_seconds; u32 poll_seconds;
}; };

View File

@ -1,5 +1,6 @@
#include "bitcoin/script.h" #include "bitcoin/script.h"
#include "bitcoin/tx.h" #include "bitcoin/tx.h"
#include "controlled_time.h"
#include "find_p2sh_out.h" #include "find_p2sh_out.h"
#include "lightningd.h" #include "lightningd.h"
#include "log.h" #include "log.h"
@ -386,7 +387,19 @@ Pkt *accept_pkt_open_commit_sig(const tal_t *ctx,
return NULL; return NULL;
} }
static Pkt *decline_htlc(const tal_t *ctx, const char *why)
{
UpdateDeclineHtlc *d = tal(ctx, UpdateDeclineHtlc);
update_decline_htlc__init(d);
/* FIXME: Define why in protocol! */
d->reason_case = UPDATE_DECLINE_HTLC__REASON_CANNOT_ROUTE;
d->cannot_route = true;
return make_pkt(ctx, PKT__PKT_UPDATE_DECLINE_HTLC, d);
}
Pkt *accept_pkt_htlc_update(const tal_t *ctx, Pkt *accept_pkt_htlc_update(const tal_t *ctx,
struct peer *peer, const Pkt *pkt, struct peer *peer, const Pkt *pkt,
Pkt **decline) Pkt **decline)
@ -403,6 +416,26 @@ Pkt *accept_pkt_htlc_update(const tal_t *ctx,
err = pkt_err(ctx, "Invalid HTLC expiry"); err = pkt_err(ctx, "Invalid HTLC expiry");
goto fail; goto fail;
} }
/* FIXME: Handle block-based expiry! */
if (!abs_locktime_is_seconds(&cur->htlc->expiry)) {
*decline = decline_htlc(ctx,
"HTLC expiry in blocks not supported!");
goto decline;
}
if (abs_locktime_to_seconds(&cur->htlc->expiry) <
controlled_time().ts.tv_sec + peer->dstate->config.min_expiry) {
*decline = decline_htlc(ctx, "HTLC expiry too soon!");
goto decline;
}
if (abs_locktime_to_seconds(&cur->htlc->expiry) >
controlled_time().ts.tv_sec + peer->dstate->config.max_expiry) {
*decline = decline_htlc(ctx, "HTLC expiry too far!");
goto decline;
}
cur->cstate = copy_funding(cur, peer->cstate); cur->cstate = copy_funding(cur, peer->cstate);
if (!funding_delta(peer->them.offer_anchor == CMD_OPEN_WITH_ANCHOR, if (!funding_delta(peer->them.offer_anchor == CMD_OPEN_WITH_ANCHOR,
peer->anchor.satoshis, peer->anchor.satoshis,
@ -434,6 +467,11 @@ Pkt *accept_pkt_htlc_update(const tal_t *ctx,
fail: fail:
tal_free(cur); tal_free(cur);
return err; return err;
decline:
assert(*decline);
tal_free(cur);
return NULL;
}; };
Pkt *accept_pkt_htlc_routefail(const tal_t *ctx, Pkt *accept_pkt_htlc_routefail(const tal_t *ctx,

View File

@ -1,6 +1,7 @@
#include "bitcoind.h" #include "bitcoind.h"
#include "close_tx.h" #include "close_tx.h"
#include "commit_tx.h" #include "commit_tx.h"
#include "controlled_time.h"
#include "cryptopkt.h" #include "cryptopkt.h"
#include "dns.h" #include "dns.h"
#include "find_p2sh_out.h" #include "find_p2sh_out.h"
@ -715,7 +716,9 @@ void peer_unexpected_pkt(struct peer *peer, const Pkt *pkt)
/* Someone declined our HTLC: details in pkt (we will also get CMD_FAIL) */ /* Someone declined our HTLC: details in pkt (we will also get CMD_FAIL) */
void peer_htlc_declined(struct peer *peer, const Pkt *pkt) void peer_htlc_declined(struct peer *peer, const Pkt *pkt)
{ {
FIXME_STUB(peer); log_unusual(peer->log, "Peer declined htlc, reason %i",
pkt->update_decline_htlc->reason_case);
peer->current_htlc = tal_free(peer->current_htlc);
} }
/* Called when their update overrides our update cmd. */ /* Called when their update overrides our update cmd. */
@ -1157,6 +1160,19 @@ static void json_newhtlc(struct command *cmd,
buffer + expirytok->start); buffer + expirytok->start);
return; return;
} }
if (abs_locktime_to_seconds(&newhtlc->htlc->expiry) <
controlled_time().ts.tv_sec + peer->dstate->config.min_expiry) {
command_fail(cmd, "HTLC expiry too soon!");
return;
}
if (abs_locktime_to_seconds(&newhtlc->htlc->expiry) >
controlled_time().ts.tv_sec + peer->dstate->config.max_expiry) {
command_fail(cmd, "HTLC expiry too far!");
return;
}
if (!hex_decode(buffer + rhashtok->start, if (!hex_decode(buffer + rhashtok->start,
rhashtok->end - rhashtok->start, rhashtok->end - rhashtok->start,
&newhtlc->htlc->rhash, &newhtlc->htlc->rhash,

View File

@ -53,8 +53,8 @@ check_status()
trap "echo Results in $DIR1 and $DIR2" EXIT trap "echo Results in $DIR1 and $DIR2" EXIT
mkdir $DIR1 $DIR2 mkdir $DIR1 $DIR2
$PREFIX1 ../daemon/lightningd --log-level=debug --bitcoind-poll=1 --lightning-dir=$DIR1 > $REDIR1 & $PREFIX1 ../daemon/lightningd --log-level=debug --bitcoind-poll=1 --min-expiry=900 --lightning-dir=$DIR1 > $REDIR1 &
$PREFIX2 ../daemon/lightningd --log-level=debug --bitcoind-poll=1 --lightning-dir=$DIR2 > $REDIR2 & $PREFIX2 ../daemon/lightningd --log-level=debug --bitcoind-poll=1 --min-expiry=900 --lightning-dir=$DIR2 > $REDIR2 &
i=0 i=0
while ! $LCLI1 getlog | grep Hello; do while ! $LCLI1 getlog | grep Hello; do