From 01e46d154e5ac96e1c1fb335a368fb2d79a1cbf4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 24 Apr 2016 19:36:13 +0930 Subject: [PATCH] bitcoind: getchaintips support. This allows us to sae the block topology in detail. Signed-off-by: Rusty Russell --- daemon/bitcoind.c | 253 +++++++++++++++++++++++++++++++++++++++++++++- daemon/bitcoind.h | 64 ++++++++++++ 2 files changed, 315 insertions(+), 2 deletions(-) diff --git a/daemon/bitcoind.c b/daemon/bitcoind.c index fcf6f1166..24472c10c 100644 --- a/daemon/bitcoind.c +++ b/daemon/bitcoind.c @@ -453,7 +453,7 @@ void bitcoind_send_tx(struct lightningd_state *dstate, tal_free(raw); } -static void process_getblock(struct bitcoin_cli *bcli) +static void process_getblock_for_mediantime(struct bitcoin_cli *bcli) { const jsmntok_t *tokens, *mediantime; bool valid; @@ -496,10 +496,259 @@ void bitcoind_get_mediantime(struct lightningd_state *dstate, char hex[hex_str_size(sizeof(*blockid))]; hex_encode(blockid, sizeof(*blockid), hex, sizeof(hex)); - start_bitcoin_cli(dstate, process_getblock, NULL, mediantime, + start_bitcoin_cli(dstate, process_getblock_for_mediantime, + NULL, mediantime, "getblock", hex, NULL); } +static void process_chaintips(struct bitcoin_cli *bcli) +{ + const jsmntok_t *tokens, *t, *end; + bool valid; + size_t i; + struct sha256_double *tips; + void (*cb)(struct lightningd_state *dstate, + struct sha256_double *blockids, + void *arg) = bcli->cb; + + log_debug(bcli->dstate->base_log, "Got getchaintips result"); + if (!bcli->output) + fatal("%s failed", bcli_args(bcli)); + + tokens = json_parse_input(bcli->output, bcli->output_bytes, &valid); + if (!tokens) + fatal("%s: %s response", + bcli_args(bcli), + valid ? "partial" : "invalid"); + + if (tokens[0].type != JSMN_ARRAY) + fatal("%s: gave non-array (%.*s)?", + bcli_args(bcli), + (int)bcli->output_bytes, bcli->output); + + tips = tal_arr(bcli, struct sha256_double, 0); + end = json_next(tokens); + for (i = 0, t = tokens + 1; t < end; t = json_next(t), i++) { + const jsmntok_t *hash = json_get_member(bcli->output, t, "hash"); + tal_resize(&tips, i+1); + if (!hex_decode(bcli->output + hash->start, + hash->end - hash->start, + &tips[i], sizeof(tips[i]))) + fatal("%s: gave bad hash for %zu'th tip (%.*s)?", + bcli_args(bcli), i, + (int)bcli->output_bytes, bcli->output); + } + if (i == 0) + fatal("%s: gave empty array (%.*s)?", + bcli_args(bcli), (int)bcli->output_bytes, bcli->output); + + cb(bcli->dstate, tips, bcli->cb_arg); +} + +void bitcoind_get_chaintips_(struct lightningd_state *dstate, + void (*cb)(struct lightningd_state *dstate, + struct sha256_double *blockids, + void *arg), + void *arg) +{ + start_bitcoin_cli(dstate, process_chaintips, cb, arg, + "getchaintips", NULL); +} + +static void process_getblock(struct bitcoin_cli *bcli) +{ + const jsmntok_t *tokens, *prevblk_tok, *txs_tok, + *mediantime_tok, *blkid_tok, *t, *end; + bool valid; + u32 mediantime; + struct sha256_double *txids; + struct sha256_double *prevblk, blkid; + size_t i; + void (*cb)(struct lightningd_state *dstate, + struct sha256_double *blkid, + struct sha256_double *prevblock, + struct sha256_double *txids, + u32 mediantime, + void *arg) = bcli->cb; + + log_debug(bcli->dstate->base_log, "Got getblock result"); + if (!bcli->output) + fatal("%s failed", bcli_args(bcli)); + + tokens = json_parse_input(bcli->output, bcli->output_bytes, &valid); + if (!tokens) + fatal("%s: %s response", + bcli_args(bcli), + valid ? "partial" : "invalid"); + + if (tokens[0].type != JSMN_OBJECT) + fatal("%s: gave non-object (%.*s)?", + bcli_args(bcli), + (int)bcli->output_bytes, bcli->output); + + mediantime_tok = json_get_member(bcli->output, tokens, "mediantime"); + if (!mediantime_tok) + fatal("%s: gave no mediantime field (%.*s)?", + bcli_args(bcli), + (int)bcli->output_bytes, bcli->output); + + if (!json_tok_number(bcli->output, mediantime_tok, &mediantime)) + fatal("%s: gave invalid mediantime (%.*s)?", + bcli_args(bcli), + mediantime_tok->end - mediantime_tok->start, + bcli->output + mediantime_tok->start); + + /* Genesis block doesn't have this! */ + prevblk_tok = json_get_member(bcli->output, tokens, "previousblockhash"); + if (prevblk_tok) { + prevblk = tal(bcli, struct sha256_double); + if (!hex_decode(bcli->output + prevblk_tok->start, + prevblk_tok->end - prevblk_tok->start, + prevblk, sizeof(*prevblk))) { + fatal("%s: bad previousblockhash '%.*s'", + bcli_args(bcli), + (int)(prevblk_tok->end - prevblk_tok->start), + bcli->output + prevblk_tok->start); + } + } else + prevblk = NULL; + + blkid_tok = json_get_member(bcli->output, tokens, "hash"); + if (!blkid_tok) + fatal("%s: gave no hash field (%.*s)?", + bcli_args(bcli), + (int)bcli->output_bytes, bcli->output); + + if (!hex_decode(bcli->output + blkid_tok->start, + blkid_tok->end - blkid_tok->start, + &blkid, sizeof(blkid))) { + fatal("%s: bad hash '%.*s'", + bcli_args(bcli), + (int)(blkid_tok->end - blkid_tok->start), + bcli->output + blkid_tok->start); + } + + txs_tok = json_get_member(bcli->output, tokens, "tx"); + if (!txs_tok) + fatal("%s: gave no tx field (%.*s)?", + bcli_args(bcli), + (int)bcli->output_bytes, bcli->output); + + if (txs_tok->type != JSMN_ARRAY) + fatal("%s: gave non-array txs field (%.*s)?", + bcli_args(bcli), + (int)bcli->output_bytes, bcli->output); + + /* It's a flat array of strings, so we can just use tok->size here. */ + txids = tal_arr(bcli, struct sha256_double, txs_tok->size); + end = json_next(txs_tok); + for (i = 0, t = txs_tok + 1; t < end; t = json_next(t), i++) { + assert(i < txs_tok->size); + if (!bitcoin_txid_from_hex(bcli->output + t->start, + t->end - t->start, + &txids[i])) + fatal("%s: gave bad txid for %zu'th tx (%.*s)?", + bcli_args(bcli), i, + (int)bcli->output_bytes, bcli->output); + } + /* We must have coinbase, at least! */ + if (i == 0) + fatal("%s: gave empty txids array (%.*s)?", + bcli_args(bcli), (int)bcli->output_bytes, bcli->output); + + log_debug(bcli->dstate->base_log, + "%.*s: mediantime = %u, prevhash=%.*s, %zu txids", + (int)(blkid_tok->end - blkid_tok->start), + bcli->output + blkid_tok->start, + mediantime, + prevblk_tok ? (int)(prevblk_tok->end - prevblk_tok->start) : 0, + prevblk_tok ? bcli->output + prevblk_tok->start : NULL, + i); + + cb(bcli->dstate, &blkid, prevblk, txids, mediantime, bcli->cb_arg); +} + +void bitcoind_getblock_(struct lightningd_state *dstate, + const struct sha256_double *blockid, + void (*cb)(struct lightningd_state *dstate, + struct sha256_double *blkid, + struct sha256_double *prevblock, + struct sha256_double *txids, + u32 mediantime, + void *arg), + void *arg) +{ + char hex[hex_str_size(sizeof(*blockid))]; + + hex_encode(blockid, sizeof(*blockid), hex, sizeof(hex)); + start_bitcoin_cli(dstate, process_getblock, cb, arg, + "getblock", hex, NULL); +} + +static void process_getblockcount(struct bitcoin_cli *bcli) +{ + u32 blockcount; + char *p, *end; + void (*cb)(struct lightningd_state *dstate, + u32 blockcount, + void *arg) = bcli->cb; + + if (!bcli->output) + fatal("%s: failed", bcli_args(bcli)); + + p = tal_strndup(bcli, bcli->output, bcli->output_bytes); + blockcount = strtol(p, &end, 10); + if (end == p || *end != '\n') + fatal("%s: gave non-numeric blockcount %s", + bcli_args(bcli), p); + + cb(bcli->dstate, blockcount, bcli->cb_arg); +} + +void bitcoind_getblockcount_(struct lightningd_state *dstate, + void (*cb)(struct lightningd_state *dstate, + u32 blockcount, + void *arg), + void *arg) +{ + start_bitcoin_cli(dstate, process_getblockcount, cb, arg, + "getblockcount", NULL); +} + +static void process_getblockhash(struct bitcoin_cli *bcli) +{ + struct sha256_double blkid; + void (*cb)(struct lightningd_state *dstate, + const struct sha256_double *blkid, + void *arg) = bcli->cb; + + if (!bcli->output) + fatal("%s: failed", bcli_args(bcli)); + + if (bcli->output_bytes == 0 + || !hex_decode(bcli->output, bcli->output_bytes-1, + &blkid, sizeof(blkid))) { + fatal("%s: bad blockid '%.*s'", + bcli_args(bcli), (int)bcli->output_bytes, bcli->output); + } + + cb(bcli->dstate, &blkid, bcli->cb_arg); +} + +void bitcoind_getblockhash_(struct lightningd_state *dstate, + u32 height, + void (*cb)(struct lightningd_state *dstate, + const struct sha256_double *blkid, + void *arg), + void *arg) +{ + char str[STR_MAX_CHARS(height)]; + sprintf(str, "%u", height); + + start_bitcoin_cli(dstate, process_getblockhash, cb, arg, + "getblockhash", str, NULL); +} + /* Make testnet/regtest status matches us. */ void check_bitcoind_config(struct lightningd_state *dstate) { diff --git a/daemon/bitcoind.h b/daemon/bitcoind.h index db1ed99cb..467dd50ac 100644 --- a/daemon/bitcoind.h +++ b/daemon/bitcoind.h @@ -57,5 +57,69 @@ void bitcoind_get_mediantime(struct lightningd_state *dstate, const struct sha256_double *blockid, u32 *mediantime); +void bitcoind_get_chaintips_(struct lightningd_state *dstate, + void (*cb)(struct lightningd_state *dstate, + struct sha256_double *blockids, + void *arg), + void *arg); + +#define bitcoind_get_chaintips(dstate, cb, arg) \ + bitcoind_get_chaintips_((dstate), \ + typesafe_cb_preargs(void, void *, \ + (cb), (arg), \ + struct lightningd_state *, \ + struct sha256_double *), \ + (arg)) + +void bitcoind_getblock_(struct lightningd_state *dstate, + const struct sha256_double *blockid, + void (*cb)(struct lightningd_state *dstate, + struct sha256_double *blkid, + struct sha256_double *prevblock, + struct sha256_double *txids, + u32 mediantime, + void *arg), + void *arg); + +#define bitcoind_getblock(dstate, blockid, cb, arg) \ + bitcoind_getblock_((dstate), (blockid), \ + typesafe_cb_preargs(void, void *, \ + (cb), (arg), \ + struct lightningd_state *, \ + struct sha256_double *, \ + struct sha256_double *, \ + struct sha256_double *, \ + u32 mediantime), \ + (arg)) + +void bitcoind_getblockcount_(struct lightningd_state *dstate, + void (*cb)(struct lightningd_state *dstate, + u32 blockcount, + void *arg), + void *arg); + +#define bitcoind_getblockcount(dstate, cb, arg) \ + bitcoind_getblockcount_((dstate), \ + typesafe_cb_preargs(void, void *, \ + (cb), (arg), \ + struct lightningd_state *, \ + u32 blockcount), \ + (arg)) + +void bitcoind_getblockhash_(struct lightningd_state *dstate, + u32 height, + void (*cb)(struct lightningd_state *dstate, + const struct sha256_double *blkid, + void *arg), + void *arg); +#define bitcoind_getblockhash(dstate, height, cb, arg) \ + bitcoind_getblockhash_((dstate), \ + (height), \ + typesafe_cb_preargs(void, void *, \ + (cb), (arg), \ + struct lightningd_state *, \ + const struct sha256_double *), \ + (arg)) + void check_bitcoind_config(struct lightningd_state *dstate); #endif /* LIGHTNING_DAEMON_BITCOIND_H */