mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-11-19 09:54:16 +01:00
daemon/watch: API to watch various bitcoin transactions.
This uses the functions in bitcoind to provide callbacks when various things happen. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
5ec8ff91e4
commit
e9237f94b1
@ -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)
|
||||
|
@ -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 */
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define LIGHTNING_DAEMON_LIGHTNING_H
|
||||
#include "config.h"
|
||||
#include "bitcoin/pubkey.h"
|
||||
#include "watch.h"
|
||||
#include <ccan/list/list.h>
|
||||
#include <ccan/short_types/short_types.h>
|
||||
#include <ccan/timer/timer.h>
|
||||
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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 <ccan/list/list.h>
|
||||
@ -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;
|
||||
|
230
daemon/watch.c
Normal file
230
daemon/watch.c
Normal file
@ -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);
|
||||
}
|
98
daemon/watch.h
Normal file
98
daemon/watch.h
Normal file
@ -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
Block a user