Browse Source
This uses the functions in bitcoind to provide callbacks when various things happen. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>ppa-0.6.1
Rusty Russell
9 years ago
7 changed files with 347 additions and 2 deletions
@ -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 <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->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); |
|||
} |
@ -0,0 +1,98 @@ |
|||
#ifndef LIGHTNING_DAEMON_WATCH_H |
|||
#define LIGHTNING_DAEMON_WATCH_H |
|||
#include "config.h" |
|||
#include "bitcoin/shadouble.h" |
|||
#include <ccan/crypto/ripemd160/ripemd160.h> |
|||
#include <ccan/htable/htable_type.h> |
|||
#include <ccan/list/list.h> |
|||
#include <ccan/short_types/short_types.h> |
|||
#include <ccan/typesafe_cb/typesafe_cb.h> |
|||
|
|||
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 */ |
Loading…
Reference in new issue