mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-11-19 09:54:16 +01:00
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:
parent
6e39b0a642
commit
7b4de8e445
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
184
daemon/watch.c
184
daemon/watch.c
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user