watch: use chaintopology

Rather than polling for interesting bitcoin txs via importaddress, we use
the chain topology to register our interest directly.x 

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2016-04-24 19:48:35 +09:30
parent 6e39b0a642
commit 7b4de8e445
6 changed files with 126 additions and 178 deletions

View File

@ -39,6 +39,9 @@ struct block {
/* Transactions in this block we care about */
struct list_head txs;
/* Full copy of txs (trimmed to txs list in connect_blocks) */
struct bitcoin_tx **full_txs;
};
/* Hash blocks by sha */
@ -77,7 +80,7 @@ static int cmp_times(const u32 *a, const u32 *b, void *unused)
return 0;
}
/* Mediantime is median of previous 11 blocks. */
/* Mediantime is median of this and previous 10 blocks. */
static u32 get_mediantime(const struct topology *topo, const struct block *b)
{
unsigned int i;
@ -93,24 +96,76 @@ static u32 get_mediantime(const struct topology *topo, const struct block *b)
return times[ARRAY_SIZE(times) / 2];
}
/* Fills in prev, height, mediantime. */
static void connect_blocks(struct topology *topo, struct block *b)
static void remove_tx(struct tx_in_block *t)
{
size_t n;
list_del_from(&t->block->txs, &t->list);
}
static void add_tx_to_block(struct block *b, struct txwatch *w)
{
/* We attach this to watch, so removed when that is */
struct tx_in_block *t = tal(w, struct tx_in_block);
t->block = b;
t->w = w;
list_add_tail(&b->txs, &t->list);
tal_add_destructor(t, remove_tx);
}
/* Fills in prev, height, mediantime. */
static void connect_blocks(struct lightningd_state *dstate, struct block *b)
{
const struct topology *topo = dstate->topology;
size_t n, i;
/* Hooked in already? */
if (b->height != -1)
return;
b->prev = block_map_get(&topo->block_map, &b->hdr.prev_hash);
connect_blocks(topo, b->prev);
connect_blocks(dstate, b->prev);
b->height = b->prev->height + 1;
n = tal_count(b->prev->nexts);
tal_resize(&b->prev->nexts, n+1);
b->prev->nexts[n] = b;
b->mediantime = get_mediantime(topo, b->prev);
b->mediantime = get_mediantime(topo, b);
/* Now we see if any of those txs are interesting. */
for (i = 0; i < tal_count(b->full_txs); i++) {
struct bitcoin_tx *tx = b->full_txs[i];
struct txwatch *w;
struct sha256_double txid;
size_t j;
/* Tell them if it spends a txo we care about. */
for (j = 0; j < tx->input_count; j++) {
struct txwatch_output out;
struct txowatch *txo;
out.txid = tx->input[j].txid;
out.index = tx->input[j].index;
txo = txowatch_hash_get(&dstate->txowatches, &out);
if (txo)
txowatch_fire(dstate, txo, tx);
}
/* We do spends first, in case that tells us to watch tx. */
normalized_txid(tx, &txid);
w = txwatch_hash_get(&dstate->txwatches, &txid);
if (!w) {
bitcoin_txid(tx, &txid);
w = txwatch_hash_get(&dstate->txwatches, &txid);
}
if (w) {
add_tx_to_block(b, w);
/* Fire if it's the first we've seen it: this might
* set up txo watches, which could fire in this block */
txwatch_fire(dstate, w, 0, &b->blkid);
}
}
b->full_txs = tal_free(b->full_txs);
}
/* This is expensive, but reorgs are usually short and txs are few.
@ -279,32 +334,16 @@ static void topology_changed(struct lightningd_state *dstate)
for (i = 0; i < tal_count(topo->newtips); i++) {
topo->tips[i] = block_map_get(&topo->block_map,
&topo->newtips[i]);
connect_blocks(topo, topo->tips[i]);
connect_blocks(dstate, topo->tips[i]);
}
/* FIXME: Tell watch code to re-evaluate all txs. */
}
static void remove_tx(struct tx_in_block *t)
{
list_del_from(&t->block->txs, &t->list);
}
static void add_tx_to_block(struct block *b, struct txwatch *w)
{
/* We attach this to watch, so removed when that is */
struct tx_in_block *t = tal(w, struct tx_in_block);
t->block = b;
t->w = w;
list_add_tail(&b->txs, &t->list);
tal_add_destructor(t, remove_tx);
/* Tell watch code to re-evaluate all txs. */
watch_topology_changed(dstate);
}
static struct block *add_block(struct lightningd_state *dstate,
struct bitcoin_block *blk)
{
size_t i;
struct topology *topo = dstate->topology;
struct block *b = tal(topo, struct block);
@ -324,17 +363,8 @@ static struct block *add_block(struct lightningd_state *dstate,
b->hdr = blk->hdr;
/* See if any of those txs are interesting. */
list_head_init(&b->txs);
for (i = 0; i < tal_count(blk->tx); i++) {
struct txwatch *w;
struct sha256_double txid;
bitcoin_txid(blk->tx[i], &txid);
w = txwatch_hash_get(&dstate->txwatches, &txid);
if (w)
add_tx_to_block(b, w);
}
b->full_txs = tal_steal(b, blk->tx);
block_map_add(&topo->block_map, b);
return b;

View File

@ -274,9 +274,6 @@ int main(int argc, char *argv[])
/* Set up node ID and private key. */
secrets_init(dstate);
/* Create timer to do watches. */
setup_watch_timer(dstate);
/* Initialize block topology. */
setup_topology(dstate);

View File

@ -795,7 +795,6 @@ void peer_watch_anchor(struct peer *peer,
w->theyspent = theyspent;
w->otherspent = otherspent;
peer_watch_setup(peer);
watch_txid(w, peer, &peer->anchor.txid, anchor_depthchange, NULL);
watch_txo(w, peer, &peer->anchor.txid, 0, anchor_spent, NULL);

View File

@ -259,9 +259,6 @@ if [ -n "$TIMEOUT_ANCHOR" ]; then
lcli1 dev-mocktime $TIME
sleep 2
# Now we need to trigger it again (first time it gets the mediantime).
$CLI generate 1
# Sometimes it skips poll because it's busy. Do it again.
TIME=$(($TIME + 1))
lcli1 dev-mocktime $TIME

View File

@ -29,6 +29,7 @@
#include "bitcoin/script.h"
#include "bitcoin/tx.h"
#include "bitcoind.h"
#include "chaintopology.h"
#include "lightningd.h"
#include "log.h"
#include "peer.h"
@ -78,22 +79,6 @@ static void destroy_txwatch(struct txwatch *w)
txwatch_hash_del(&w->dstate->txwatches, w);
}
/* FIXME: This is a hack! */
void peer_watch_setup(struct peer *peer)
{
struct sha256 h;
struct ripemd160 redeemhash;
/* 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).*/
sha256(&h, peer->anchor.redeemscript,
tal_count(peer->anchor.redeemscript));
ripemd160(&redeemhash, h.u.u8, sizeof(h));
bitcoind_watch_addr(peer->dstate, &redeemhash);
}
struct txwatch *watch_txid_(const tal_t *ctx,
struct peer *peer,
const struct sha256_double *txid,
@ -159,138 +144,71 @@ struct txowatch *watch_txo_(const tal_t *ctx,
return w;
}
struct tx_info {
struct sha256_double blkhash;
int conf;
};
static void insert_null_txwatch(struct lightningd_state *dstate,
const struct sha256_double *txid)
void txwatch_fire(struct lightningd_state *dstate,
struct txwatch *txw,
unsigned int depth,
const struct sha256_double *blkhash)
{
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);
if (depth != txw->depth) {
log_debug(txw->peer->log,
"Got depth change %u for %02x%02x%02x...\n",
txw->depth,
txw->txid.sha.u.u8[0],
txw->txid.sha.u.u8[1],
txw->txid.sha.u.u8[2]);
txw->depth = depth;
txw->cb(txw->peer, txw->depth, blkhash, &txw->txid,
txw->cbdata);
}
}
static void watched_normalized_txid(struct lightningd_state *dstate,
const struct bitcoin_tx *tx,
struct tx_info *txinfo)
void txowatch_fire(struct lightningd_state *dstate,
const struct txowatch *txow,
const struct bitcoin_tx *tx)
{
struct txwatch *txw;
struct sha256_double txid;
size_t i;
normalized_txid(tx, &txid);
txw = txwatch_hash_get(&dstate->txwatches, &txid);
/* Reset to real txid for logging. */
bitcoin_txid(tx, &txid);
if (txw) {
if (txinfo->conf != txw->depth) {
log_debug(txw->peer->log,
"Got depth change %u for %02x%02x%02x...\n",
txinfo->conf,
txid.sha.u.u8[0],
txid.sha.u.u8[1],
txid.sha.u.u8[2]);
txw->depth = txinfo->conf;
txw->cb(txw->peer, txw->depth, &txinfo->blkhash, &txid,
txw->cbdata);
}
return;
}
/* Hmm, otherwise it may be new */
for (i = 0; i < tx->input_count; i++) {
struct txowatch *txo;
struct txwatch_output out;
out.txid = tx->input[i].txid;
out.index = tx->input[i].index;
txo = txowatch_hash_get(&dstate->txowatches, &out);
/* Presumably, this sets a watch on it. */
if (txo) {
log_debug(txo->peer->log,
"New tx spending %02x%02x%02x output %u:"
" %02x%02x%02x...\n",
out.txid.sha.u.u8[0],
out.txid.sha.u.u8[1],
out.txid.sha.u.u8[2],
out.index,
txid.sha.u.u8[0],
txid.sha.u.u8[1],
txid.sha.u.u8[2]);
txo->cb(txo->peer, tx, txo->cbdata);
return;
}
}
/* OK, not interesting. Put in fake (on original txid). */
log_debug(dstate->base_log, "Ignoring tx %02x%02x%02x...\n",
log_debug(txow->peer->log,
"Got UTXO spend for %02x%02x%02x:%u: %02x%02x%02x%02x...\n",
txow->out.txid.sha.u.u8[0],
txow->out.txid.sha.u.u8[1],
txow->out.txid.sha.u.u8[2],
txow->out.index,
txid.sha.u.u8[0],
txid.sha.u.u8[1],
txid.sha.u.u8[2]);
insert_null_txwatch(dstate, &txid);
txid.sha.u.u8[2],
txid.sha.u.u8[3]);
txow->cb(txow->peer, tx, txow->cbdata);
}
static void watched_txid(struct lightningd_state *dstate,
const struct sha256_double *txid,
int confirmations,
bool is_coinbase,
const struct sha256_double *blkhash)
void watch_topology_changed(struct lightningd_state *dstate)
{
struct txwatch *txw;
struct tx_info *txinfo;
struct txwatch_hash_iter i;
struct txwatch *w;
bool needs_rerun;
/* Maybe it spent an output we're watching? */
if (is_coinbase)
return;
again:
/* Iterating a htable during deletes is safe, but might skip entries. */
needs_rerun = false;
for (w = txwatch_hash_first(&dstate->txwatches, &i);
w;
w = txwatch_hash_next(&dstate->txwatches, &i)) {
struct sha256_double blkid;
size_t depth;
/* Are we watching this txid directly (or already reported)? */
txw = txwatch_hash_get(&dstate->txwatches, txid);
if (txw) {
if (txw->cb && confirmations != txw->depth) {
txw->depth = confirmations;
txw->cb(txw->peer, txw->depth, blkhash, txid,
txw->cbdata);
/* Don't fire if we haven't seen it at all. */
if (w->depth == -1)
continue;
depth = get_tx_depth(dstate, w, &blkid);
if (depth != w->depth) {
w->depth = depth;
w->cb(w->peer, w->depth, &blkid, &w->txid, w->cbdata);
needs_rerun = true;
}
return;
}
txinfo = tal(dstate, struct tx_info);
txinfo->conf = confirmations;
if (blkhash)
txinfo->blkhash = *blkhash;
/* FIXME: Since we don't use segwit, we need to normalize txids. */
bitcoind_txid_lookup(dstate, txid, watched_normalized_txid, txinfo);
}
static struct timeout watch_timeout;
static void start_poll_transactions(struct lightningd_state *dstate)
{
if (!list_empty(&dstate->bitcoin_req)) {
log_unusual(dstate->base_log,
"Delaying start poll: commands in progress");
} else
bitcoind_poll_transactions(dstate, watched_txid);
refresh_timeout(dstate, &watch_timeout);
}
void setup_watch_timer(struct lightningd_state *dstate)
{
init_timeout(&watch_timeout, dstate->config.poll_seconds,
start_poll_transactions, dstate);
/* Run once immediately, in case there are issues. */
start_poll_transactions(dstate);
if (needs_rerun)
goto again;
}

View File

@ -119,10 +119,17 @@ struct txowatch *watch_txo_(const tal_t *ctx,
const struct bitcoin_tx *), \
(cbdata))
void peer_watch_setup(struct peer *peer);
/* FIXME: Seg witness removes need for this! */
void normalized_txid(const struct bitcoin_tx *tx, struct sha256_double *txid);
void setup_watch_timer(struct lightningd_state *dstate);
void txwatch_fire(struct lightningd_state *dstate,
struct txwatch *txw,
unsigned int depth,
const struct sha256_double *blkhash);
void txowatch_fire(struct lightningd_state *dstate,
const struct txowatch *txow,
const struct bitcoin_tx *tx);
void watch_topology_changed(struct lightningd_state *dstate);
#endif /* LIGHTNING_DAEMON_WATCH_H */