diff --git a/daemon/Makefile b/daemon/Makefile index 221125da4..24b432d64 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -22,7 +22,8 @@ DAEMON_SRC := \ daemon/netaddr.c \ daemon/peer.c \ daemon/secrets.c \ - daemon/timeout.c + daemon/timeout.c \ + daemon/watch.c DAEMON_OBJS := $(DAEMON_SRC:.c=.o) DAEMON_CLI_SRC := daemon/lightning-cli.c @@ -44,7 +45,8 @@ DAEMON_HEADERS := \ daemon/peer.h \ daemon/pseudorand.h \ daemon/secrets.h \ - daemon/timeout.h + daemon/timeout.h \ + daemon/watch.h $(DAEMON_OBJS) $(DAEMON_LIB_OBJS) $(DAEMON_CLI_OBJS): $(DAEMON_HEADERS) $(DAEMON_JSMN_HEADERS) $(BITCOIN_HEADERS) $(CORE_HEADERS) $(GEN_HEADERS) $(CCAN_HEADERS) $(DAEMON_JSMN_OBJS): $(DAEMON_JSMN_HEADERS) diff --git a/daemon/lightningd.c b/daemon/lightningd.c index 24d5d1fb6..95b190dc0 100644 --- a/daemon/lightningd.c +++ b/daemon/lightningd.c @@ -121,6 +121,8 @@ static struct lightningd_state *lightningd_state(void) list_head_init(&state->peers); timers_init(&state->timers, time_now()); + txwatch_hash_init(&state->txwatches); + txowatch_hash_init(&state->txowatches); state->secpctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); default_config(&state->config); @@ -203,6 +205,9 @@ int main(int argc, char *argv[]) /* Set up node ID and private key. */ secrets_init(state); + /* Create timer to do watches. */ + setup_watch_timer(state); + log_info(state->base_log, "Hello world!"); /* If io_loop returns NULL, either a timer expired, or all fds closed */ diff --git a/daemon/lightningd.h b/daemon/lightningd.h index 92f18bdb7..dfe63039e 100644 --- a/daemon/lightningd.h +++ b/daemon/lightningd.h @@ -2,6 +2,7 @@ #define LIGHTNING_DAEMON_LIGHTNING_H #include "config.h" #include "bitcoin/pubkey.h" +#include "watch.h" #include #include #include @@ -63,5 +64,9 @@ struct lightningd_state { /* Number of bitcoind commands outstanding. */ unsigned int bitcoind_in_progress; + + /* Transactions/txos we are watching. */ + struct txwatch_hash txwatches; + struct txowatch_hash txowatches; }; #endif /* LIGHTNING_DAEMON_LIGHTNING_H */ diff --git a/daemon/peer.c b/daemon/peer.c index f6ee5f3b5..afaf08987 100644 --- a/daemon/peer.c +++ b/daemon/peer.c @@ -71,6 +71,7 @@ static struct peer *new_peer(struct lightningd_state *state, peer->addr.protocol = addr_protocol; peer->io_data = NULL; peer->secrets = NULL; + list_head_init(&peer->watches); /* FIXME: Attach IO logging for this peer. */ tal_add_destructor(peer, destroy_peer); diff --git a/daemon/peer.h b/daemon/peer.h index 1ea0bd424..e0360ee1e 100644 --- a/daemon/peer.h +++ b/daemon/peer.h @@ -1,6 +1,7 @@ #ifndef LIGHTNING_DAEMON_PEER_H #define LIGHTNING_DAEMON_PEER_H #include "config.h" +#include "bitcoin/pubkey.h" #include "lightning.pb-c.h" #include "netaddr.h" #include @@ -26,6 +27,9 @@ struct peer { /* What happened. */ struct log *log; + + /* Things we're watching for (see watches.c) */ + struct list_head watches; /* Keys for transactions with this peer. */ struct pubkey their_commitkey, their_finalkey; diff --git a/daemon/watch.c b/daemon/watch.c new file mode 100644 index 000000000..8c231e521 --- /dev/null +++ b/daemon/watch.c @@ -0,0 +1,230 @@ +/* Code to talk to bitcoind to watch for various events. + * + * Here's what we want to know: + * + * - An anchor tx: + * - Reached given depth + * - Times out. + * - Is unspent after reaching given depth. + * + * - Our own commitment tx: + * - Reached a given depth. + * + * - HTLC spend tx: + * - Reached a given depth. + * + * - Anchor tx output: + * - Is spent by their current tx. + * - Is spent by a revoked tx. + * + * - Commitment tx HTLC outputs: + * - HTLC timed out + * - HTLC spent + * + * We do this by adding the P2SH address to the wallet, and then querying + * that using listtransactions. + * + * WE ASSUME NO MALLEABILITY! This requires segregated witness. + */ +#include "bitcoin/script.h" +#include "bitcoin/tx.h" +#include "bitcoind.h" +#include "lightningd.h" +#include "log.h" +#include "peer.h" +#include "timeout.h" +#include "watch.h" +#include +#include + +const struct txwatch_output *txowatch_keyof(const struct txowatch *w) +{ + return &w->out; +} + +size_t txo_hash(const struct txwatch_output *out) +{ + return hash(&out->txid, 1, out->index); +} + +bool txowatch_eq(const struct txowatch *w, const struct txwatch_output *out) +{ + return structeq(&w->out.txid, &out->txid) + && w->out.index == out->index; +} + +static void destroy_txowatch(struct txowatch *w) +{ + txowatch_hash_del(&w->peer->state->txowatches, w); +} + +/* Watch a txo. */ +static void insert_txo_watch(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); + + w->out.txid = *txid; + w->out.index = txout; + w->peer = peer; + w->cb = cb; + w->cbdata = cbdata; + + txowatch_hash_add(&w->peer->state->txowatches, w); + tal_add_destructor(w, destroy_txowatch); +} + +const struct sha256_double *txwatch_keyof(const struct txwatch *w) +{ + return &w->txid; +} + +size_t txid_hash(const struct sha256_double *txid) +{ + return hash(txid->sha.u.u8, sizeof(txid->sha.u.u8), 0); +} + +bool txwatch_eq(const struct txwatch *w, const struct sha256_double *txid) +{ + return structeq(&w->txid, txid); +} + +static void destroy_txwatch(struct txwatch *w) +{ + txwatch_hash_del(&w->state->txwatches, w); +} + +static struct txwatch *insert_txwatch(const tal_t *ctx, + struct lightningd_state *state, + struct peer *peer, + const struct sha256_double *txid, + void (*cb)(struct peer *, int, void *), + void *cbdata) +{ + struct txwatch *w; + + /* We could have a null-watch on it because we saw it spend a TXO */ + w = txwatch_hash_get(&state->txwatches, txid); + if (w) { + assert(!w->cb); + tal_free(w); + } + + w = tal(ctx, struct txwatch); + w->depth = 0; + w->txid = *txid; + w->state = state; + w->peer = peer; + w->cb = cb; + w->cbdata = cbdata; + + txwatch_hash_add(&w->state->txwatches, w); + tal_add_destructor(w, destroy_txwatch); + + return w; +} + +void add_anchor_watch_(struct peer *peer, + const struct sha256_double *txid, + unsigned int out, + void (*anchor_cb)(struct peer *peer, int depth, void *), + void (*spend_cb)(struct peer *peer, + const struct bitcoin_tx *, void *), + void *cbdata) +{ + struct sha256 h; + struct ripemd160 redeemhash; + u8 *redeemscript; + + insert_txwatch(peer, peer->state, peer, txid, anchor_cb, cbdata); + insert_txo_watch(peer, txid, out, spend_cb, cbdata); + + redeemscript = bitcoin_redeem_2of2(peer, &peer->their_commitkey, + &peer->our_commitkey); + sha256(&h, redeemscript, tal_count(redeemscript)); + ripemd160(&redeemhash, h.u.u8, sizeof(h)); + tal_free(redeemscript); + + /* Telling bitcoind to watch the redeemhash address means + * it'll tell is about the anchor itself (spend to that + * address), and any commit txs (spend from that address).*/ + bitcoind_watch_addr(peer->state, &redeemhash); +} + +void add_commit_tx_watch_(struct peer *peer, + const struct sha256_double *txid, + void (*cb)(struct peer *peer, int depth, void *), + void *cbdata) +{ + insert_txwatch(peer, peer->state, peer, txid, cb, cbdata); + + /* We are already watching the anchor txo, so we don't need to + * watch anything else. */ +} + +static void tx_watched_inputs(struct lightningd_state *state, + const struct bitcoin_tx *tx, void *unused) +{ + size_t in; + + for (in = 0; in < tx->input_count; in++) { + struct txwatch_output out; + struct txowatch *txow; + + out.txid = tx->input[in].txid; + out.index = tx->input[in].index; + + txow = txowatch_hash_get(&state->txowatches, &out); + if (txow) + txow->cb(txow->peer, tx, txow->cbdata); + } +} + +static void watched_transaction(struct lightningd_state *state, + const struct sha256_double *txid, + int confirmations) +{ + struct txwatch *txw; + + /* Are we watching this txid directly (or already reported)? */ + txw = txwatch_hash_get(&state->txwatches, txid); + if (txw) { + if (confirmations != txw->depth) { + txw->depth = confirmations; + if (txw->cb) + txw->cb(txw->peer, txw->depth, txw->cbdata); + } + return; + } + + /* Don't report about this txid twice. */ + insert_txwatch(state, state, NULL, txid, NULL, NULL); + + /* Maybe it spent an output we're watching? */ + bitcoind_txid_lookup(state, txid, tx_watched_inputs, NULL); +} + +static struct timeout watch_timeout; + +static void start_poll_transactions(struct lightningd_state *state) +{ + if (state->bitcoind_in_progress != 0) { + log_unusual(state->base_log, + "Delaying start poll: %u commands in progress", + state->bitcoind_in_progress); + } else + bitcoind_poll_transactions(state, watched_transaction); + refresh_timeout(state, &watch_timeout); +} + +void setup_watch_timer(struct lightningd_state *state) +{ + init_timeout(&watch_timeout, 30, start_poll_transactions, state); + /* Run once immediately, in case there are issues. */ + start_poll_transactions(state); +} diff --git a/daemon/watch.h b/daemon/watch.h new file mode 100644 index 000000000..037888827 --- /dev/null +++ b/daemon/watch.h @@ -0,0 +1,98 @@ +#ifndef LIGHTNING_DAEMON_WATCH_H +#define LIGHTNING_DAEMON_WATCH_H +#include "config.h" +#include "bitcoin/shadouble.h" +#include +#include +#include +#include +#include + +struct bitcoin_tx; +struct lightningd_state; + +struct txwatch_output { + struct sha256_double txid; + unsigned int index; +}; + +/* Watching an output */ +struct txowatch { + /* Peer who owns us. */ + struct peer *peer; + + /* Output to watch. */ + struct txwatch_output out; + + /* A new tx. */ + void (*cb)(struct peer *peer, + const struct bitcoin_tx *tx, + void *cbdata); + + void *cbdata; +}; + +const struct txwatch_output *txowatch_keyof(const struct txowatch *w); +size_t txo_hash(const struct txwatch_output *out); +bool txowatch_eq(const struct txowatch *w, const struct txwatch_output *out); + +HTABLE_DEFINE_TYPE(struct txowatch, txowatch_keyof, txo_hash, txowatch_eq, + txowatch_hash); + +struct txwatch { + struct lightningd_state *state; + + /* Peer who owns us. */ + struct peer *peer; + + /* Transaction to watch. */ + struct sha256_double txid; + int depth; + + /* A new depth (-1 if conflicted) */ + void (*cb)(struct peer *peer, int depth, void *cbdata); + void *cbdata; +}; + +const struct sha256_double *txwatch_keyof(const struct txwatch *w); +size_t txid_hash(const struct sha256_double *txid); +bool txwatch_eq(const struct txwatch *w, const struct sha256_double *txid); +HTABLE_DEFINE_TYPE(struct txwatch, txwatch_keyof, txid_hash, txwatch_eq, + txwatch_hash); + + +void add_anchor_watch_(struct peer *peer, + const struct sha256_double *txid, + unsigned int out, + void (*anchor_cb)(struct peer *peer, int depth, void *), + void (*spend_cb)(struct peer *peer, + const struct bitcoin_tx *, void *), + void *cbdata); + +#define add_anchor_watch(peer, txid, out, anchor_cb, spend_cb, cbdata) \ + add_anchor_watch_((peer), (txid), (out), \ + typesafe_cb_preargs(void, void *, \ + (anchor_cb), (cbdata), \ + struct peer *, \ + int depth), \ + typesafe_cb_preargs(void, void *, \ + (spend_cb), (cbdata), \ + struct peer *, \ + const struct bitcoin_tx *), \ + (cbdata)) + +void add_commit_tx_watch_(struct peer *peer, + const struct sha256_double *txid, + void (*cb)(struct peer *peer, int depth, void *), + void *cbdata); + +#define add_commit_tx_watch(peer, txid, cb, cbdata) \ + add_commit_tx_watch_((peer), (txid), \ + typesafe_cb_preargs(void, void *, \ + (cb), (cbdata), \ + struct peer *, \ + int depth), \ + (cbdata)) + +void setup_watch_timer(struct lightningd_state *state); +#endif /* LIGHTNING_DAEMON_WATCH_H */