Skip to content

Commit

Permalink
daemon: implement anchor watch timeout.
Browse files Browse the repository at this point in the history
We abort when this happens, but still worth testing.

This involves a refactor so we can allocate watches off a specific context,
for easy freeing when they're no longer wanted.

Signed-off-by: Rusty Russell <[email protected]>
  • Loading branch information
rustyrussell committed Jan 21, 2016
1 parent 4e5ced4 commit b70c18a
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 33 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ $(TEST_CLI_PROGRAMS:=.o): $(TEST_CLI_HEADERS)
# These don't work in parallel, so we open-code them
test-cli-tests: $(TEST_CLI_PROGRAMS) daemon-all
cd test-cli; scripts/shutdown.sh 2>/dev/null || true
set -e; for args in ""; do daemon/test/test.sh; done
set -e; for arg in "" "--timeout-anchor"; do daemon/test/test.sh $$arg; done
set -e; cd test-cli; for args in "" --steal --unilateral --htlc-onchain; do scripts/setup.sh && scripts/test.sh $$args && scripts/shutdown.sh; done

test-onion: test/test_onion test/onion_key
Expand Down
56 changes: 51 additions & 5 deletions daemon/peer.c
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ static struct peer *new_peer(struct lightningd_state *dstate,
peer->close_tx = NULL;
peer->cstate = NULL;
peer->close_watch_timeout = NULL;
peer->anchor.watches = NULL;

/* If we free peer, conn should be closed, but can't be freed
* immediately so don't make peer a parent. */
Expand Down Expand Up @@ -545,11 +546,15 @@ const struct json_command connect_command = {
};

struct anchor_watch {
struct peer *peer;
enum state_input depthok;
enum state_input timeout;
enum state_input unspent;
enum state_input theyspent;
enum state_input otherspent;

/* If timeout != INPUT_NONE, this is the timer. */
struct oneshot *timer;
};

static void anchor_depthchange(struct peer *peer, int depth,
Expand All @@ -561,6 +566,8 @@ static void anchor_depthchange(struct peer *peer, int depth,
if (depth >= (int)peer->us.mindepth) {
enum state_input in = w->depthok;
w->depthok = INPUT_NONE;
/* We don't need the timeout timer any more. */
w->timer = tal_free(w->timer);
state_event(peer, in, NULL);
}
} else {
Expand Down Expand Up @@ -646,39 +653,78 @@ static void anchor_spent(struct peer *peer,
if (txmatch(tx, peer->them.commit))
state_event(peer, w->theyspent, &idata);
else if (is_mutual_close(tx, peer->close_tx))
add_close_tx_watch(peer, tx, close_depth_cb);
add_close_tx_watch(peer, peer, tx, close_depth_cb);
else
state_event(peer, w->otherspent, &idata);
}

static void anchor_timeout(struct anchor_watch *w)
{
assert(w == w->peer->anchor.watches);
state_event(w->peer, w->timeout, NULL);

/* Freeing this gets rid of the other watches, and timer, too. */
w->peer->anchor.watches = tal_free(w);
}

void peer_watch_anchor(struct peer *peer,
enum state_input depthok,
enum state_input timeout,
enum state_input unspent,
enum state_input theyspent,
enum state_input otherspent)
{
struct anchor_watch *w = tal(peer, struct anchor_watch);
struct anchor_watch *w;

w = peer->anchor.watches = tal(peer, struct anchor_watch);

w->peer = peer;
w->depthok = depthok;
w->timeout = timeout;
w->unspent = unspent;
w->theyspent = theyspent;
w->otherspent = otherspent;

add_anchor_watch(peer, &peer->anchor.txid, peer->anchor.index,
add_anchor_watch(w, peer, &peer->anchor.txid, peer->anchor.index,
anchor_depthchange,
anchor_spent,
w);

/* FIXME: add timeout */
/* For anchor timeout, expect 20 minutes per block, +2 hours.
*
* Probability(no block in time N) = e^(-N/600).
* Thus for 1 block, P = e^(-(7200+1*1200)/600) = 0.83 in a million.
*
* Glenn Willen says, if we want to know how many 10-minute intervals for
* a 1 in a million chance of spurious failure for N blocks, put
* this into http://www.wolframalpha.com:
*
* e^(-x) * sum x^i / fact(i), i=0 to N < 1/1000000
*
* N=20: 51
* N=10: 35
* N=8: 31
* N=6: 28
* N=4: 24
* N=3: 22
* N=2: 20
*
* So, our formula of 12 + N*2 holds for N <= 20 at least.
*/
if (w->timeout != INPUT_NONE) {
w->timer = oneshot_timeout(peer->dstate, w,
7200 + 20*peer->us.mindepth,
anchor_timeout, w);
} else
w->timer = NULL;
}

void peer_unwatch_anchor_depth(struct peer *peer,
enum state_input depthok,
enum state_input timeout)
{
FIXME_STUB(peer);
assert(peer->anchor.watches);
peer->anchor.watches = tal_free(peer->anchor.watches);
}

void peer_watch_delayed(struct peer *peer,
Expand Down
2 changes: 2 additions & 0 deletions daemon/peer.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ struct peer {
u8 *redeemscript;
/* If we created it, we keep entire tx. */
const struct bitcoin_tx *tx;
struct anchor_watch *watches;
} anchor;

/* Their signature for our current commit sig. */
Expand All @@ -104,6 +105,7 @@ struct peer {
/* Number of HTLC updates (== number of previous commit txs) */
u64 num_htlcs;

/* FIXME: Group closing fields together in anon struct. */
/* Closing tx and signature once we've generated it */
struct bitcoin_tx *close_tx;
struct bitcoin_signature our_close_sig;
Expand Down
52 changes: 46 additions & 6 deletions daemon/test/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ if [ x"$1" = x"--valgrind" ]; then
PREFIX2="valgrind --vgdb-error=1"
REDIR1="/dev/tty"
REDIR2="/dev/tty"
shift
elif [ x"$1" = x"--gdb1" ]; then
PREFIX1="gdb --args -ex run"
REDIR1="/dev/tty"
shift
elif [ x"$1" = x"--gdb2" ]; then
PREFIX2="gdb --args -ex run"
REDIR2="/dev/tty"
shift
fi

LCLI1="../daemon/lightning-cli --lightning-dir=$DIR1"
Expand All @@ -50,7 +53,15 @@ check_status()
return 1
fi
}


all_ok()
{
scripts/shutdown.sh

trap "rm -rf $DIR1 $DIR2" EXIT
exit 0
}

trap "echo Results in $DIR1 and $DIR2" EXIT
mkdir $DIR1 $DIR2
$PREFIX1 ../daemon/lightningd --log-level=debug --bitcoind-poll=1 --min-expiry=900 --lightning-dir=$DIR1 > $REDIR1 &
Expand Down Expand Up @@ -87,6 +98,36 @@ sleep 2
$LCLI1 getpeers | grep STATE_OPEN_WAITING_OURANCHOR
$LCLI2 getpeers | grep STATE_OPEN_WAITING_THEIRANCHOR

if [ "x$1" = x"--timeout-anchor" ]; then
# Timeout before anchor committed.
TIME=$((`date +%s` + 7200 + 3 * 1200 + 1))

# This will crash in a moment.
$LCLI1 dev-mocktime $TIME

# This will crash immediately
if $LCLI2 dev-mocktime $TIME >&2; then
echo Node2 did not crash >&2
exit 1
fi

sleep 1

# Check crash logs
if [ ! -f $DIR1/crash.log ]; then
echo Node1 did not crash >&2
exit 1
fi
if [ ! -f $DIR2/crash.log ]; then
echo Node2 did not crash >&2
exit 1
fi

fgrep 'Entered error state STATE_ERR_ANCHOR_TIMEOUT' $DIR2/crash.log
all_ok
fi


# Now make it pass anchor.
$CLI generate 3

Expand Down Expand Up @@ -158,16 +199,15 @@ $LCLI2 getpeers | tr -s '\012\011 ' ' ' | fgrep '"STATE_CLOSE_WAIT_CLOSE"'

# Now the final one.
$CLI generate 1
$LCLI1 dev-mocktime $(($EXPIRY + 33))
$LCLI2 dev-mocktime $(($EXPIRY + 33))
TIME=$(($EXPIRY + 33))
$LCLI1 dev-mocktime $TIME
$LCLI2 dev-mocktime $TIME
sleep 1

$LCLI1 getpeers | tr -s '\012\011 ' ' ' | fgrep '"peers" : [ ]'
$LCLI2 getpeers | tr -s '\012\011 ' ' ' | fgrep '"peers" : [ ]'

$LCLI1 stop
$LCLI2 stop
scripts/shutdown.sh

trap "rm -rf $DIR1 $DIR2" EXIT

all_ok
48 changes: 34 additions & 14 deletions daemon/watch.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,16 @@ static void destroy_txowatch(struct txowatch *w)
}

/* Watch a txo. */
static void insert_txo_watch(struct peer *peer,
static void insert_txo_watch(const tal_t *ctx,
struct peer *peer,
const struct sha256_double *txid,
unsigned int txout,
void (*cb)(struct peer *peer,
const struct bitcoin_tx *tx,
void *cbdata),
void *cbdata)
{
struct txowatch *w = tal(peer, struct txowatch);
struct txowatch *w = tal(ctx, struct txowatch);

w->out.txid = *txid;
w->out.index = txout;
Expand Down Expand Up @@ -100,7 +101,6 @@ static void destroy_txwatch(struct txwatch *w)
}

static struct txwatch *insert_txwatch(const tal_t *ctx,
struct lightningd_state *dstate,
struct peer *peer,
const struct sha256_double *txid,
void (*cb)(struct peer *, int, void *),
Expand All @@ -109,7 +109,7 @@ static struct txwatch *insert_txwatch(const tal_t *ctx,
struct txwatch *w;

/* We could have a null-watch on it because we saw it spend a TXO */
w = txwatch_hash_get(&dstate->txwatches, txid);
w = txwatch_hash_get(&peer->dstate->txwatches, txid);
if (w) {
assert(!w->cb);
tal_free(w);
Expand All @@ -118,7 +118,7 @@ static struct txwatch *insert_txwatch(const tal_t *ctx,
w = tal(ctx, struct txwatch);
w->depth = 0;
w->txid = *txid;
w->dstate = dstate;
w->dstate = peer->dstate;
w->peer = peer;
w->cb = cb;
w->cbdata = cbdata;
Expand All @@ -129,7 +129,25 @@ static struct txwatch *insert_txwatch(const tal_t *ctx,
return w;
}

void add_anchor_watch_(struct peer *peer,
/* This just serves to avoid us doing bitcoind_txid_lookup repeatedly
* on unknown txs. */
static void insert_null_txwatch(struct lightningd_state *dstate,
const struct sha256_double *txid)
{
struct txwatch *w = tal(dstate, struct txwatch);
w->depth = 0;
w->txid = *txid;
w->dstate = dstate;
w->peer = NULL;
w->cb = NULL;
w->cbdata = NULL;

txwatch_hash_add(&w->dstate->txwatches, w);
tal_add_destructor(w, destroy_txwatch);
}

void add_anchor_watch_(const tal_t *ctx,
struct peer *peer,
const struct sha256_double *txid,
unsigned int out,
void (*anchor_cb)(struct peer *peer, int depth, void *),
Expand All @@ -141,10 +159,10 @@ void add_anchor_watch_(struct peer *peer,
struct ripemd160 redeemhash;
u8 *redeemscript;

insert_txwatch(peer, peer->dstate, peer, txid, anchor_cb, cbdata);
insert_txo_watch(peer, txid, out, spend_cb, cbdata);
insert_txwatch(ctx, peer, txid, anchor_cb, cbdata);
insert_txo_watch(ctx, peer, txid, out, spend_cb, cbdata);

redeemscript = bitcoin_redeem_2of2(peer, &peer->them.commitkey,
redeemscript = bitcoin_redeem_2of2(ctx, &peer->them.commitkey,
&peer->us.commitkey);
sha256(&h, redeemscript, tal_count(redeemscript));
ripemd160(&redeemhash, h.u.u8, sizeof(h));
Expand All @@ -156,12 +174,13 @@ void add_anchor_watch_(struct peer *peer,
bitcoind_watch_addr(peer->dstate, &redeemhash);
}

void add_commit_tx_watch_(struct peer *peer,
void add_commit_tx_watch_(const tal_t *ctx,
struct peer *peer,
const struct sha256_double *txid,
void (*cb)(struct peer *peer, int depth, void *),
void *cbdata)
{
insert_txwatch(peer, peer->dstate, peer, txid, cb, cbdata);
insert_txwatch(ctx, peer, txid, cb, cbdata);

/* We are already watching the anchor txo, so we don't need to
* watch anything else. */
Expand All @@ -173,13 +192,14 @@ static void cb_no_arg(struct peer *peer, int depth, void *vcb)
cb(peer, depth);
}

void add_close_tx_watch(struct peer *peer,
void add_close_tx_watch(const tal_t *ctx,
struct peer *peer,
const struct bitcoin_tx *tx,
void (*cb)(struct peer *peer, int depth))
{
struct sha256_double txid;
bitcoin_txid(tx, &txid);
insert_txwatch(peer, peer->dstate, peer, &txid, cb_no_arg, cb);
insert_txwatch(ctx, peer, &txid, cb_no_arg, cb);

/* We are already watching the anchor txo, so we don't need to
* watch anything else. */
Expand Down Expand Up @@ -223,7 +243,7 @@ static void watched_transaction(struct lightningd_state *dstate,
}

/* Don't report about this txid twice. */
insert_txwatch(dstate, dstate, NULL, txid, NULL, NULL);
insert_null_txwatch(dstate, txid);

/* Maybe it spent an output we're watching? */
if (!is_coinbase)
Expand Down
Loading

0 comments on commit b70c18a

Please sign in to comment.