diff --git a/daemon/Makefile b/daemon/Makefile index dfdcc0f19..221125da4 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -14,6 +14,7 @@ DAEMON_LIB_SRC := \ DAEMON_LIB_OBJS := $(DAEMON_LIB_SRC:.c=.o) DAEMON_SRC := \ + daemon/bitcoind.c \ daemon/cryptopkt.c \ daemon/dns.c \ daemon/jsonrpc.c \ @@ -31,6 +32,7 @@ DAEMON_JSMN_OBJS := daemon/jsmn.o DAEMON_JSMN_HEADERS := daemon/jsmn/jsmn.h DAEMON_HEADERS := \ + daemon/bitcoind.h \ daemon/configdir.h \ daemon/cryptopkt.h \ daemon/dns.h \ diff --git a/daemon/bitcoind.c b/daemon/bitcoind.c new file mode 100644 index 000000000..579bc36d2 --- /dev/null +++ b/daemon/bitcoind.c @@ -0,0 +1,231 @@ +/* Code for talking to bitcoind. We use bitcoin-cli. */ +#include "bitcoin/base58.h" +#include "bitcoin/shadouble.h" +#include "bitcoin/tx.h" +#include "bitcoind.h" +#include "json.h" +#include "lightningd.h" +#include "log.h" +#include +#include +#include +#include +#include +#include +#include + +#define BITCOIN_CLI "bitcoin-cli" + +static char **gather_args(const tal_t *ctx, const char *cmd, va_list ap) +{ + size_t n = 2; + char **args = tal_arr(ctx, char *, n+1); + + args[0] = BITCOIN_CLI; + args[1] = cast_const(char *, cmd); + + while ((args[n] = va_arg(ap, char *)) != NULL) { + args[n] = tal_strdup(args, args[n]); + n++; + tal_resize(&args, n + 1); + } + return args; +} + +struct bitcoin_cli { + struct lightningd_state *state; + int fd; + pid_t pid; + char **args; + char *output; + size_t output_bytes; + size_t new_output; + void (*process)(struct bitcoin_cli *); + void *cb; + void *cb_arg; +}; + +static struct io_plan *read_more(struct io_conn *conn, struct bitcoin_cli *bcli) +{ + bcli->output_bytes += bcli->new_output; + if (bcli->output_bytes == tal_count(bcli->output)) + tal_resize(&bcli->output, bcli->output_bytes * 2); + return io_read_partial(conn, bcli->output + bcli->output_bytes, + tal_count(bcli->output) - bcli->output_bytes, + &bcli->new_output, read_more, bcli); +} + +static struct io_plan *output_init(struct io_conn *conn, struct bitcoin_cli *bcli) +{ + bcli->output_bytes = bcli->new_output = 0; + bcli->output = tal_arr(bcli, char, 100); + return read_more(conn, bcli); +} + +static void bcli_finished(struct io_conn *conn, struct bitcoin_cli *bcli) +{ + int ret, status; + + /* FIXME: If we waited for SIGCHILD, this could never hang! */ + ret = waitpid(bcli->pid, &status, 0); + if (ret != bcli->pid) + fatal("bitcoind: '%s' '%s' %s", + bcli->args[0], bcli->args[1], + ret == 0 ? "not exited?" : strerror(errno)); + + if (!WIFEXITED(status)) + fatal("bitcoind: '%s' '%s' died with signal %i", + bcli->args[0], bcli->args[1], + WTERMSIG(status)); + + if (WEXITSTATUS(status) != 0) + fatal("bitcoind: '%s' '%s' failed (%i '%.*s')", + bcli->args[0], bcli->args[1], WEXITSTATUS(status), + (int)bcli->output_bytes, bcli->output); + + assert(bcli->state->bitcoind_in_progress); + bcli->state->bitcoind_in_progress--; + bcli->process(bcli); +} + +static void +start_bitcoin_cli(struct lightningd_state *state, + void (*process)(struct bitcoin_cli *), + void *cb, void *cb_arg, + char *cmd, ...) +{ + va_list ap; + struct bitcoin_cli *bcli = tal(state, struct bitcoin_cli); + struct io_conn *conn; + + bcli->state = state; + bcli->process = process; + bcli->cb = cb; + bcli->cb_arg = cb_arg; + va_start(ap, cmd); + bcli->args = gather_args(bcli, cmd, ap); + va_end(ap); + + bcli->pid = pipecmdarr(&bcli->fd, NULL, &bcli->fd, bcli->args); + if (bcli->pid < 0) + fatal("%s exec failed: %s", bcli->args[0], strerror(errno)); + + conn = io_new_conn(state, bcli->fd, output_init, bcli); + tal_steal(conn, bcli); + state->bitcoind_in_progress++; + io_set_finish(conn, bcli_finished, bcli); +} + +static void process_importaddress(struct bitcoin_cli *bcli) +{ + if (bcli->output_bytes != 0) + fatal("bitcoind: '%s' '%s' unexpeced output '%.*s'", + bcli->args[0], bcli->args[1], + (int)bcli->output_bytes, bcli->output); +} + +void bitcoind_watch_addr(struct lightningd_state *state, + const struct ripemd160 *redeemhash) +{ + char *p2shaddr = p2sh_to_base58(state, state->config.testnet, + redeemhash); + + start_bitcoin_cli(state, process_importaddress, NULL, NULL, + "importaddress", p2shaddr, "", "false", NULL); + tal_free(p2shaddr); +} + +static void process_transactions(struct bitcoin_cli *bcli) +{ + const jsmntok_t *tokens, *t, *end; + bool valid; + void (*cb)(struct lightningd_state *state, + const struct sha256_double *txid, + int confirmations) = bcli->cb; + + tokens = json_parse_input(bcli->output, bcli->output_bytes, &valid); + if (!tokens) + fatal("bitcoind: '%s' '%s' %s response", + bcli->args[0], bcli->args[1], + valid ? "partial" : "invalid"); + + if (tokens[0].type != JSMN_ARRAY) + fatal("listtransactions: '%s' '%s' gave non-array (%.*s)?", + bcli->args[0], bcli->args[1], + (int)bcli->output_bytes, bcli->output); + + end = json_next(tokens); + for (t = tokens + 1; t < end; t = json_next(t)) { + struct sha256_double txid; + const jsmntok_t *txidtok, *conftok; + long int conf; + char *end; + + txidtok = json_get_member(bcli->output, t, "txid"); + conftok = json_get_member(bcli->output, t, "confirmations"); + if (!txidtok || !conftok) + fatal("listtransactions: no %s field!", + txidtok ? "confirmations" : "txid"); + if (!bitcoin_txid_from_hex(bcli->output + txidtok->start, + txidtok->end - txidtok->start, + &txid)) { + fatal("listtransactions: bad txid '%.*s'", + (int)(txidtok->end - txidtok->start), + bcli->output + txidtok->start); + } + conf = strtol(bcli->output + conftok->start, &end, 10); + if (end != bcli->output + conftok->end) + fatal("listtransactions: bad confirmations '%.*s'", + (int)(conftok->end - conftok->start), + bcli->output + conftok->start); + + /* FIXME: log txid */ + log_debug(bcli->state->base_log, + "txid %02x%02x%02x%02x..., conf %li", + txid.sha.u.u8[0], txid.sha.u.u8[1], + txid.sha.u.u8[2], txid.sha.u.u8[3], + conf); + + cb(bcli->state, &txid, conf); + } +} + +void bitcoind_poll_transactions(struct lightningd_state *state, + void (*cb)(struct lightningd_state *state, + const struct sha256_double *txid, + int confirmations)) +{ + /* FIXME: Iterate and detect duplicates. */ + start_bitcoin_cli(state, process_transactions, cb, NULL, + "listtransactions", "*", "100000", "0", "true", + NULL); +} + +static void process_rawtx(struct bitcoin_cli *bcli) +{ + struct bitcoin_tx *tx; + void (*cb)(struct lightningd_state *state, + const struct bitcoin_tx *tx, void *arg) = bcli->cb; + + tx = bitcoin_tx_from_hex(bcli, bcli->output, bcli->output_bytes); + if (!tx) + fatal("Unknown txid (output %.*s)", + (int)bcli->output_bytes, (char *)bcli->output); + cb(bcli->state, tx, bcli->cb_arg); +} + +/* FIXME: Cache! */ +void bitcoind_txid_lookup_(struct lightningd_state *state, + const struct sha256_double *txid, + void (*cb)(struct lightningd_state *state, + const struct bitcoin_tx *tx, + void *arg), + void *arg) +{ + char txidhex[hex_str_size(sizeof(*txid))]; + + if (!bitcoin_txid_to_hex(txid, txidhex, sizeof(txidhex))) + fatal("Incorrect txid size"); + start_bitcoin_cli(state, process_rawtx, cb, arg, + "getrawtransaction", txidhex, NULL); +} diff --git a/daemon/bitcoind.h b/daemon/bitcoind.h new file mode 100644 index 000000000..038980e79 --- /dev/null +++ b/daemon/bitcoind.h @@ -0,0 +1,33 @@ +#ifndef LIGHTNING_DAEMON_BITCOIND_H +#define LIGHTNING_DAEMON_BITCOIND_H +#include "config.h" +#include + +struct sha256_double; +struct lightningd_state; +struct ripemd160; +struct bitcoin_tx; + +void bitcoind_watch_addr(struct lightningd_state *state, + const struct ripemd160 *redeemhash); + +void bitcoind_poll_transactions(struct lightningd_state *state, + void (*cb)(struct lightningd_state *state, + const struct sha256_double *txid, + int confirmations)); + +void bitcoind_txid_lookup_(struct lightningd_state *state, + const struct sha256_double *txid, + void (*cb)(struct lightningd_state *state, + const struct bitcoin_tx *tx, void *), + void *arg); + +#define bitcoind_txid_lookup(state, txid, cb, arg) \ + bitcoind_txid_lookup_((state), (txid), \ + typesafe_cb_preargs(struct io_plan *, void *, \ + (cb), (arg), \ + struct lightningd_state *, \ + const struct bitcoin_tx *), \ + (arg)) + +#endif /* LIGHTNING_DAEMON_BITCOIND_H */ diff --git a/daemon/lightningd.c b/daemon/lightningd.c index 4eed41b46..24d5d1fb6 100644 --- a/daemon/lightningd.c +++ b/daemon/lightningd.c @@ -124,6 +124,7 @@ static struct lightningd_state *lightningd_state(void) state->secpctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); default_config(&state->config); + state->bitcoind_in_progress = 0; return state; } diff --git a/daemon/lightningd.h b/daemon/lightningd.h index 4e4ef49ab..92f18bdb7 100644 --- a/daemon/lightningd.h +++ b/daemon/lightningd.h @@ -60,5 +60,8 @@ struct lightningd_state { /* This is us. */ struct pubkey id; + + /* Number of bitcoind commands outstanding. */ + unsigned int bitcoind_in_progress; }; #endif /* LIGHTNING_DAEMON_LIGHTNING_H */