mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-09 15:18:09 +01:00
3d9cb81215
With segregated witness, we can (in advance!) specify the txid or tx output we want to watch, so convert to that now. For the moment it's done by pretending we have normalized txids; that goes away after the conversion. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
297 lines
7.1 KiB
C
297 lines
7.1 KiB
C
/* 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 <ccan/hash/hash.h>
|
|
#include <ccan/structeq/structeq.h>
|
|
|
|
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->dstate->txowatches, w);
|
|
}
|
|
|
|
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->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,
|
|
void (*cb)(struct peer *peer, int depth,
|
|
const struct sha256_double *blkhash,
|
|
const struct sha256_double *txid,
|
|
void *arg),
|
|
void *cb_arg)
|
|
{
|
|
struct txwatch *w;
|
|
|
|
assert(!txwatch_hash_get(&peer->dstate->txwatches, txid));
|
|
|
|
w = tal(ctx, struct txwatch);
|
|
w->depth = -1;
|
|
w->txid = *txid;
|
|
w->dstate = peer->dstate;
|
|
w->peer = peer;
|
|
w->cb = cb;
|
|
w->cbdata = cb_arg;
|
|
|
|
txwatch_hash_add(&w->dstate->txwatches, w);
|
|
tal_add_destructor(w, destroy_txwatch);
|
|
|
|
return w;
|
|
}
|
|
|
|
struct txwatch *watch_tx_(const tal_t *ctx,
|
|
struct peer *peer,
|
|
const struct bitcoin_tx *tx,
|
|
void (*cb)(struct peer *peer, int depth,
|
|
const struct sha256_double *blkhash,
|
|
const struct sha256_double *txid,
|
|
void *arg),
|
|
void *cb_arg)
|
|
{
|
|
struct sha256_double txid;
|
|
|
|
normalized_txid(tx, &txid);
|
|
return watch_txid(ctx, peer, &txid, cb, cb_arg);
|
|
}
|
|
|
|
struct txowatch *watch_txo_(const tal_t *ctx,
|
|
struct peer *peer,
|
|
const struct sha256_double *txid,
|
|
unsigned int output,
|
|
void (*cb)(struct peer *peer,
|
|
const struct bitcoin_tx *tx,
|
|
void *),
|
|
void *cbdata)
|
|
{
|
|
struct txowatch *w = tal(ctx, struct txowatch);
|
|
|
|
w->out.txid = *txid;
|
|
w->out.index = output;
|
|
w->peer = peer;
|
|
w->cb = cb;
|
|
w->cbdata = cbdata;
|
|
|
|
txowatch_hash_add(&w->peer->dstate->txowatches, w);
|
|
tal_add_destructor(w, destroy_txowatch);
|
|
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
|
|
static void watched_normalized_txid(struct lightningd_state *dstate,
|
|
const struct bitcoin_tx *tx,
|
|
struct tx_info *txinfo)
|
|
{
|
|
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",
|
|
txid.sha.u.u8[0],
|
|
txid.sha.u.u8[1],
|
|
txid.sha.u.u8[2]);
|
|
insert_null_txwatch(dstate, &txid);
|
|
}
|
|
|
|
static void watched_txid(struct lightningd_state *dstate,
|
|
const struct sha256_double *txid,
|
|
int confirmations,
|
|
bool is_coinbase,
|
|
const struct sha256_double *blkhash)
|
|
|
|
{
|
|
struct txwatch *txw;
|
|
struct tx_info *txinfo;
|
|
|
|
/* Maybe it spent an output we're watching? */
|
|
if (is_coinbase)
|
|
return;
|
|
|
|
/* 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);
|
|
}
|
|
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);
|
|
}
|