mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-19 15:14:23 +01:00
lightningd/chaintopology: use plugin backend to setup topology
This adds `getchaininfo` and `getrawblockbyheight` handling lightningd-side, and use them in setup_topology(). We then remove legacy bitcoind_getblockcount() (we already get the count in `getchaininfo`), bitcoind_getblockchaininfo() (it was only used in setup_topology()), and wait_for_bitcoind() (this was specific to bitcoin-core and we assume our Bitcoin backend to be functional if the plugin responds to `init`).
This commit is contained in:
@@ -556,34 +556,211 @@ void bitcoind_getrawblock_(struct bitcoind *bitcoind,
|
||||
"getblock", hex, "false", NULL);
|
||||
}
|
||||
|
||||
static bool process_getblockcount(struct bitcoin_cli *bcli)
|
||||
/* Our Bitcoin backend plugin gave us a bad response. We can't recover. */
|
||||
static void bitcoin_plugin_error(struct bitcoind *bitcoind, const char *buf,
|
||||
const jsmntok_t *toks, const char *method,
|
||||
const char *reason)
|
||||
{
|
||||
u32 blockcount;
|
||||
char *p, *end;
|
||||
void (*cb)(struct bitcoind *bitcoind,
|
||||
u32 blockcount,
|
||||
void *arg) = bcli->cb;
|
||||
|
||||
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(tmpctx, bcli), p);
|
||||
|
||||
cb(bcli->bitcoind, blockcount, bcli->cb_arg);
|
||||
return true;
|
||||
struct plugin *p = strmap_get(&bitcoind->pluginsmap, method);
|
||||
fatal("%s error: bad response to %s (%s), response was %.*s",
|
||||
p->cmd, method, reason,
|
||||
toks->end - toks->start, buf + toks->start);
|
||||
}
|
||||
|
||||
void bitcoind_getblockcount_(struct bitcoind *bitcoind,
|
||||
void (*cb)(struct bitcoind *bitcoind,
|
||||
u32 blockcount,
|
||||
void *arg),
|
||||
void *arg)
|
||||
/* `getrawblockbyheight`
|
||||
*
|
||||
* If no block were found at that height, will set each field to `null`.
|
||||
* Plugin response:
|
||||
* {
|
||||
* "blockhash": "<blkid>",
|
||||
* "block": "rawblock"
|
||||
* }
|
||||
*/
|
||||
|
||||
struct getrawblockbyheight_call {
|
||||
struct bitcoind *bitcoind;
|
||||
void (*cb)(struct bitcoind *bitcoind,
|
||||
struct bitcoin_blkid *blkid,
|
||||
struct bitcoin_block *block,
|
||||
void *);
|
||||
void *cb_arg;
|
||||
};
|
||||
|
||||
static void
|
||||
getrawblockbyheight_callback(const char *buf, const jsmntok_t *toks,
|
||||
const jsmntok_t *idtok,
|
||||
struct getrawblockbyheight_call *call)
|
||||
{
|
||||
start_bitcoin_cli(bitcoind, NULL, process_getblockcount, false,
|
||||
BITCOIND_HIGH_PRIO,
|
||||
cb, arg,
|
||||
"getblockcount", NULL);
|
||||
const jsmntok_t *resulttok, *blockhashtok, *blocktok;
|
||||
const char *block_str, *blockhash_str;
|
||||
struct bitcoin_blkid blkid;
|
||||
struct bitcoin_block *blk;
|
||||
|
||||
resulttok = json_get_member(buf, toks, "result");
|
||||
if (!resulttok)
|
||||
bitcoin_plugin_error(call->bitcoind, buf, toks,
|
||||
"getrawblockbyheight",
|
||||
"bad 'result' field");
|
||||
|
||||
blockhashtok = json_get_member(buf, resulttok, "blockhash");
|
||||
if (!blockhashtok)
|
||||
bitcoin_plugin_error(call->bitcoind, buf, toks,
|
||||
"getrawblockbyheight",
|
||||
"bad 'blockhash' field");
|
||||
/* If block hash is `null`, this means not found! Call the callback
|
||||
* with NULL values. */
|
||||
if (json_tok_is_null(buf, blockhashtok)) {
|
||||
db_begin_transaction(call->bitcoind->ld->wallet->db);
|
||||
call->cb(call->bitcoind, NULL, NULL, call->cb_arg);
|
||||
db_commit_transaction(call->bitcoind->ld->wallet->db);
|
||||
goto clean;
|
||||
}
|
||||
blockhash_str = json_strdup(tmpctx, buf, blockhashtok);
|
||||
if (!bitcoin_blkid_from_hex(blockhash_str, strlen(blockhash_str), &blkid))
|
||||
bitcoin_plugin_error(call->bitcoind, buf, toks,
|
||||
"getrawblockbyheight",
|
||||
"bad block hash");
|
||||
|
||||
blocktok = json_get_member(buf, resulttok, "block");
|
||||
if (!blocktok)
|
||||
bitcoin_plugin_error(call->bitcoind, buf, toks,
|
||||
"getrawblockbyheight",
|
||||
"bad 'block' field");
|
||||
block_str = json_strdup(tmpctx, buf, blocktok);
|
||||
blk = bitcoin_block_from_hex(tmpctx, chainparams, block_str,
|
||||
strlen(block_str));
|
||||
if (!blk)
|
||||
bitcoin_plugin_error(call->bitcoind, buf, toks,
|
||||
"getrawblockbyheight",
|
||||
"bad block");
|
||||
|
||||
db_begin_transaction(call->bitcoind->ld->wallet->db);
|
||||
call->cb(call->bitcoind, &blkid, blk, call->cb_arg);
|
||||
db_commit_transaction(call->bitcoind->ld->wallet->db);
|
||||
|
||||
clean:
|
||||
tal_free(call);
|
||||
}
|
||||
|
||||
void bitcoind_getrawblockbyheight_(struct bitcoind *bitcoind,
|
||||
u32 height,
|
||||
void (*cb)(struct bitcoind *bitcoind,
|
||||
struct bitcoin_blkid *blkid,
|
||||
struct bitcoin_block *blk,
|
||||
void *arg),
|
||||
void *cb_arg)
|
||||
{
|
||||
struct jsonrpc_request *req;
|
||||
struct getrawblockbyheight_call *call = tal(NULL,
|
||||
struct getrawblockbyheight_call);
|
||||
|
||||
call->bitcoind = bitcoind;
|
||||
call->cb = cb;
|
||||
call->cb_arg = cb_arg;
|
||||
|
||||
req = jsonrpc_request_start(bitcoind, "getrawblockbyheight",
|
||||
bitcoind->log, getrawblockbyheight_callback,
|
||||
/* Freed in cb. */
|
||||
notleak(call));
|
||||
json_add_num(req->stream, "height", height);
|
||||
jsonrpc_request_end(req);
|
||||
plugin_request_send(strmap_get(&bitcoind->pluginsmap,
|
||||
"getrawblockbyheight"), req);
|
||||
}
|
||||
|
||||
/* `getchaininfo`
|
||||
*
|
||||
* Called at startup to check the network we are operating on, and to check
|
||||
* if the Bitcoin backend is synced to the network tip. This also allows to
|
||||
* get the current block count.
|
||||
* {
|
||||
* "chain": "<bip70_chainid>",
|
||||
* "headercount": <number of fetched headers>,
|
||||
* "blockcount": <number of fetched block>,
|
||||
* "ibd": <synced?>
|
||||
* }
|
||||
*/
|
||||
|
||||
struct getchaininfo_call {
|
||||
struct bitcoind *bitcoind;
|
||||
/* Should we log verbosely? */
|
||||
bool first_call;
|
||||
void (*cb)(struct bitcoind *bitcoind,
|
||||
const char *chain,
|
||||
u32 headercount,
|
||||
u32 blockcount,
|
||||
const bool ibd,
|
||||
const bool first_call,
|
||||
void *);
|
||||
void *cb_arg;
|
||||
};
|
||||
|
||||
static void getchaininfo_callback(const char *buf, const jsmntok_t *toks,
|
||||
const jsmntok_t *idtok,
|
||||
struct getchaininfo_call *call)
|
||||
{
|
||||
const jsmntok_t *resulttok, *chaintok, *headerstok, *blktok, *ibdtok;
|
||||
u32 headers = 0;
|
||||
u32 blocks = 0;
|
||||
bool ibd = false;
|
||||
|
||||
resulttok = json_get_member(buf, toks, "result");
|
||||
if (!resulttok)
|
||||
bitcoin_plugin_error(call->bitcoind, buf, toks, "getchaininfo",
|
||||
"bad 'result' field");
|
||||
|
||||
chaintok = json_get_member(buf, resulttok, "chain");
|
||||
if (!chaintok)
|
||||
bitcoin_plugin_error(call->bitcoind, buf, toks, "getchaininfo",
|
||||
"bad 'chain' field");
|
||||
|
||||
headerstok = json_get_member(buf, resulttok, "headercount");
|
||||
if (!headerstok || !json_to_number(buf, headerstok, &headers))
|
||||
bitcoin_plugin_error(call->bitcoind, buf, toks, "getchaininfo",
|
||||
"bad 'headercount' field");
|
||||
|
||||
blktok = json_get_member(buf, resulttok, "blockcount");
|
||||
if (!blktok || !json_to_number(buf, blktok, &blocks))
|
||||
bitcoin_plugin_error(call->bitcoind, buf, toks, "getchaininfo",
|
||||
"bad 'blockcount' field");
|
||||
|
||||
ibdtok = json_get_member(buf, resulttok, "ibd");
|
||||
if (!ibdtok || !json_to_bool(buf, ibdtok, &ibd))
|
||||
bitcoin_plugin_error(call->bitcoind, buf, toks, "getchaininfo",
|
||||
"bad 'ibd' field");
|
||||
|
||||
db_begin_transaction(call->bitcoind->ld->wallet->db);
|
||||
call->cb(call->bitcoind, json_strdup(tmpctx, buf, chaintok), headers,
|
||||
blocks, ibd, call->first_call, call->cb_arg);
|
||||
db_commit_transaction(call->bitcoind->ld->wallet->db);
|
||||
|
||||
tal_free(call);
|
||||
}
|
||||
|
||||
void bitcoind_getchaininfo_(struct bitcoind *bitcoind,
|
||||
const bool first_call,
|
||||
void (*cb)(struct bitcoind *bitcoind,
|
||||
const char *chain,
|
||||
u32 headercount,
|
||||
u32 blockcount,
|
||||
const bool ibd,
|
||||
const bool first_call,
|
||||
void *),
|
||||
void *cb_arg)
|
||||
{
|
||||
struct jsonrpc_request *req;
|
||||
struct getchaininfo_call *call = tal(bitcoind, struct getchaininfo_call);
|
||||
|
||||
call->bitcoind = bitcoind;
|
||||
call->cb = cb;
|
||||
call->cb_arg = cb_arg;
|
||||
call->first_call = first_call;
|
||||
|
||||
req = jsonrpc_request_start(bitcoind, "getchaininfo", bitcoind->log,
|
||||
getchaininfo_callback, call);
|
||||
jsonrpc_request_end(req);
|
||||
plugin_request_send(strmap_get(&bitcoind->pluginsmap, "getchaininfo"),
|
||||
req);
|
||||
}
|
||||
|
||||
struct get_output {
|
||||
@@ -890,103 +1067,6 @@ void bitcoind_getfilteredblock_(struct bitcoind *bitcoind, u32 height,
|
||||
bitcoind_getblockhash(bitcoind, height, process_getfilteredblock_step1, call);
|
||||
}
|
||||
|
||||
/* Mutual recursion */
|
||||
static bool process_getblockchaininfo(struct bitcoin_cli *bcli);
|
||||
|
||||
static void retry_getblockchaininfo(struct bitcoind *bitcoind)
|
||||
{
|
||||
assert(!bitcoind->synced);
|
||||
start_bitcoin_cli(bitcoind, NULL,
|
||||
process_getblockchaininfo,
|
||||
false, BITCOIND_LOW_PRIO, NULL, NULL,
|
||||
"getblockchaininfo", NULL);
|
||||
}
|
||||
|
||||
/* Given JSON object from getblockchaininfo, are we synced? Poll if not. */
|
||||
static void is_bitcoind_synced_yet(struct bitcoind *bitcoind,
|
||||
const char *output, size_t output_len,
|
||||
const jsmntok_t *obj,
|
||||
bool initial)
|
||||
{
|
||||
const jsmntok_t *t;
|
||||
unsigned int headers, blocks;
|
||||
bool ibd;
|
||||
|
||||
t = json_get_member(output, obj, "headers");
|
||||
if (!t || !json_to_number(output, t, &headers))
|
||||
fatal("Invalid 'headers' field in getblockchaininfo '%.*s'",
|
||||
(int)output_len, output);
|
||||
|
||||
t = json_get_member(output, obj, "blocks");
|
||||
if (!t || !json_to_number(output, t, &blocks))
|
||||
fatal("Invalid 'blocks' field in getblockchaininfo '%.*s'",
|
||||
(int)output_len, output);
|
||||
|
||||
t = json_get_member(output, obj, "initialblockdownload");
|
||||
if (!t || !json_to_bool(output, t, &ibd))
|
||||
fatal("Invalid 'initialblockdownload' field in getblockchaininfo '%.*s'",
|
||||
(int)output_len, output);
|
||||
|
||||
if (ibd) {
|
||||
if (initial)
|
||||
log_unusual(bitcoind->log,
|
||||
"Waiting for initial block download"
|
||||
" (this can take a while!)");
|
||||
else
|
||||
log_debug(bitcoind->log,
|
||||
"Still waiting for initial block download");
|
||||
} else if (headers != blocks) {
|
||||
if (initial)
|
||||
log_unusual(bitcoind->log,
|
||||
"Waiting for bitcoind to catch up"
|
||||
" (%u blocks of %u)",
|
||||
blocks, headers);
|
||||
else
|
||||
log_debug(bitcoind->log,
|
||||
"Waiting for bitcoind to catch up"
|
||||
" (%u blocks of %u)",
|
||||
blocks, headers);
|
||||
} else {
|
||||
if (!initial)
|
||||
log_info(bitcoind->log, "Bitcoind now synced.");
|
||||
bitcoind->synced = true;
|
||||
return;
|
||||
}
|
||||
|
||||
bitcoind->synced = false;
|
||||
notleak(new_reltimer(bitcoind->ld->timers, bitcoind,
|
||||
/* Be 4x more aggressive in this case. */
|
||||
time_divide(time_from_sec(bitcoind->ld->topology
|
||||
->poll_seconds), 4),
|
||||
retry_getblockchaininfo, bitcoind));
|
||||
}
|
||||
|
||||
static bool process_getblockchaininfo(struct bitcoin_cli *bcli)
|
||||
{
|
||||
const jsmntok_t *tokens;
|
||||
bool valid;
|
||||
|
||||
tokens = json_parse_input(bcli, bcli->output, bcli->output_bytes,
|
||||
&valid);
|
||||
if (!tokens)
|
||||
fatal("%s: %s response (%.*s)",
|
||||
bcli_args(tmpctx, bcli),
|
||||
valid ? "partial" : "invalid",
|
||||
(int)bcli->output_bytes, bcli->output);
|
||||
|
||||
if (tokens[0].type != JSMN_OBJECT) {
|
||||
log_unusual(bcli->bitcoind->log,
|
||||
"%s: gave non-object (%.*s)?",
|
||||
bcli_args(tmpctx, bcli),
|
||||
(int)bcli->output_bytes, bcli->output);
|
||||
return false;
|
||||
}
|
||||
|
||||
is_bitcoind_synced_yet(bcli->bitcoind, bcli->output, bcli->output_bytes,
|
||||
tokens, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void destroy_bitcoind(struct bitcoind *bitcoind)
|
||||
{
|
||||
strmap_clear(&bitcoind->pluginsmap);
|
||||
@@ -994,131 +1074,6 @@ static void destroy_bitcoind(struct bitcoind *bitcoind)
|
||||
bitcoind->shutdown = true;
|
||||
}
|
||||
|
||||
static const char **cmdarr(const tal_t *ctx, const struct bitcoind *bitcoind,
|
||||
const char *cmd, ...)
|
||||
{
|
||||
va_list ap;
|
||||
const char **args;
|
||||
|
||||
va_start(ap, cmd);
|
||||
args = gather_args(bitcoind, ctx, cmd, ap);
|
||||
va_end(ap);
|
||||
return args;
|
||||
}
|
||||
|
||||
static void fatal_bitcoind_failure(struct bitcoind *bitcoind, const char *error_message)
|
||||
{
|
||||
const char **cmd = cmdarr(bitcoind, bitcoind, "echo", NULL);
|
||||
|
||||
fprintf(stderr, "%s\n\n", error_message);
|
||||
fprintf(stderr, "Make sure you have bitcoind running and that bitcoin-cli is able to connect to bitcoind.\n\n");
|
||||
fprintf(stderr, "You can verify that your Bitcoin Core installation is ready for use by running:\n\n");
|
||||
fprintf(stderr, " $ %s 'hello world'\n", args_string(cmd, cmd));
|
||||
tal_free(cmd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* This function is used to check "chain" field from
|
||||
* bitcoin-cli "getblockchaininfo" API */
|
||||
static char* check_blockchain_from_bitcoincli(const tal_t *ctx,
|
||||
struct bitcoind *bitcoind,
|
||||
char* output, const char **cmd)
|
||||
{
|
||||
size_t output_bytes;
|
||||
const jsmntok_t *tokens, *valuetok;
|
||||
bool valid;
|
||||
|
||||
if (!output)
|
||||
return tal_fmt(ctx, "Reading from %s failed: %s",
|
||||
args_string(tmpctx, cmd), strerror(errno));
|
||||
|
||||
output_bytes = tal_count(output);
|
||||
|
||||
tokens = json_parse_input(cmd, output, output_bytes,
|
||||
&valid);
|
||||
|
||||
if (!tokens)
|
||||
return tal_fmt(ctx, "%s: %s response",
|
||||
args_string(tmpctx, cmd),
|
||||
valid ? "partial" : "invalid");
|
||||
|
||||
if (tokens[0].type != JSMN_OBJECT)
|
||||
return tal_fmt(ctx, "%s: gave non-object (%.*s)?",
|
||||
args_string(tmpctx, cmd),
|
||||
(int)output_bytes, output);
|
||||
|
||||
valuetok = json_get_member(output, tokens, "chain");
|
||||
if (!valuetok)
|
||||
return tal_fmt(ctx, "%s: had no chain member (%.*s)?",
|
||||
args_string(tmpctx, cmd),
|
||||
(int)output_bytes, output);
|
||||
|
||||
if (!json_tok_streq(output, valuetok,
|
||||
chainparams->bip70_name))
|
||||
return tal_fmt(ctx, "Error blockchain for bitcoin-cli?"
|
||||
" Should be: %s",
|
||||
chainparams->bip70_name);
|
||||
|
||||
is_bitcoind_synced_yet(bitcoind, output, output_bytes, tokens, true);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void wait_for_bitcoind(struct bitcoind *bitcoind)
|
||||
{
|
||||
int from, status, ret;
|
||||
pid_t child;
|
||||
const char **cmd = cmdarr(bitcoind, bitcoind, "getblockchaininfo", NULL);
|
||||
bool printed = false;
|
||||
char *errstr;
|
||||
|
||||
for (;;) {
|
||||
child = pipecmdarr(NULL, &from, &from, cast_const2(char **,cmd));
|
||||
if (child < 0) {
|
||||
if (errno == ENOENT) {
|
||||
fatal_bitcoind_failure(bitcoind, "bitcoin-cli not found. Is bitcoin-cli (part of Bitcoin Core) available in your PATH?");
|
||||
}
|
||||
fatal("%s exec failed: %s", cmd[0], strerror(errno));
|
||||
}
|
||||
|
||||
char *output = grab_fd(cmd, from);
|
||||
|
||||
while ((ret = waitpid(child, &status, 0)) < 0 && errno == EINTR);
|
||||
if (ret != child)
|
||||
fatal("Waiting for %s: %s", cmd[0], strerror(errno));
|
||||
if (!WIFEXITED(status))
|
||||
fatal("Death of %s: signal %i",
|
||||
cmd[0], WTERMSIG(status));
|
||||
|
||||
if (WEXITSTATUS(status) == 0) {
|
||||
/* If succeeded, so check answer it gave. */
|
||||
errstr = check_blockchain_from_bitcoincli(tmpctx, bitcoind, output, cmd);
|
||||
if (errstr)
|
||||
fatal("%s", errstr);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* bitcoin/src/rpc/protocol.h:
|
||||
* RPC_IN_WARMUP = -28, //!< Client still warming up
|
||||
*/
|
||||
if (WEXITSTATUS(status) != 28) {
|
||||
if (WEXITSTATUS(status) == 1) {
|
||||
fatal_bitcoind_failure(bitcoind, "Could not connect to bitcoind using bitcoin-cli. Is bitcoind running?");
|
||||
}
|
||||
fatal("%s exited with code %i: %s",
|
||||
cmd[0], WEXITSTATUS(status), output);
|
||||
}
|
||||
|
||||
if (!printed) {
|
||||
log_unusual(bitcoind->log,
|
||||
"Waiting for bitcoind to warm up...");
|
||||
printed = true;
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
tal_free(cmd);
|
||||
}
|
||||
|
||||
struct bitcoind *new_bitcoind(const tal_t *ctx,
|
||||
struct lightningd *ld,
|
||||
struct log *log)
|
||||
@@ -1143,6 +1098,7 @@ struct bitcoind *new_bitcoind(const tal_t *ctx,
|
||||
bitcoind->rpcconnect = NULL;
|
||||
bitcoind->rpcport = NULL;
|
||||
tal_add_destructor(bitcoind, destroy_bitcoind);
|
||||
bitcoind->synced = false;
|
||||
|
||||
return bitcoind;
|
||||
}
|
||||
|
||||
@@ -89,8 +89,6 @@ struct bitcoind *new_bitcoind(const tal_t *ctx,
|
||||
struct lightningd *ld,
|
||||
struct log *log);
|
||||
|
||||
void wait_for_bitcoind(struct bitcoind *bitcoind);
|
||||
|
||||
void bitcoind_estimate_fees_(struct bitcoind *bitcoind,
|
||||
const u32 blocks[], const char *estmode[],
|
||||
size_t num_estimates,
|
||||
@@ -120,20 +118,6 @@ void bitcoind_sendrawtx_(struct bitcoind *bitcoind,
|
||||
int, const char *), \
|
||||
(arg))
|
||||
|
||||
void bitcoind_getblockcount_(struct bitcoind *bitcoind,
|
||||
void (*cb)(struct bitcoind *bitcoind,
|
||||
u32 blockcount,
|
||||
void *arg),
|
||||
void *arg);
|
||||
|
||||
#define bitcoind_getblockcount(bitcoind_, cb, arg) \
|
||||
bitcoind_getblockcount_((bitcoind_), \
|
||||
typesafe_cb_preargs(void, void *, \
|
||||
(cb), (arg), \
|
||||
struct bitcoind *, \
|
||||
u32 blockcount), \
|
||||
(arg))
|
||||
|
||||
/* blkid is NULL if call fails. */
|
||||
void bitcoind_getblockhash_(struct bitcoind *bitcoind,
|
||||
u32 height,
|
||||
@@ -178,6 +162,40 @@ void bitcoind_getrawblock_(struct bitcoind *bitcoind,
|
||||
struct bitcoin_block *), \
|
||||
(arg))
|
||||
|
||||
void bitcoind_getchaininfo_(struct bitcoind *bitcoind,
|
||||
const bool first_call,
|
||||
void (*cb)(struct bitcoind *bitcoind,
|
||||
const char *chain,
|
||||
u32 headercount,
|
||||
u32 blockcount,
|
||||
const bool ibd,
|
||||
const bool first_call, void *),
|
||||
void *cb_arg);
|
||||
#define bitcoind_getchaininfo(bitcoind_, first_call_, cb, arg) \
|
||||
bitcoind_getchaininfo_((bitcoind_), (first_call_), \
|
||||
typesafe_cb_preargs(void, void *, \
|
||||
(cb), (arg), \
|
||||
struct bitcoind *, \
|
||||
const char *, u32, u32, \
|
||||
const bool, const bool), \
|
||||
(arg))
|
||||
|
||||
void bitcoind_getrawblockbyheight_(struct bitcoind *bitcoind,
|
||||
u32 height,
|
||||
void (*cb)(struct bitcoind *bitcoind,
|
||||
struct bitcoin_blkid *blkid,
|
||||
struct bitcoin_block *blk,
|
||||
void *arg),
|
||||
void *arg);
|
||||
#define bitcoind_getrawblockbyheight(bitcoind_, height_, cb, arg) \
|
||||
bitcoind_getrawblockbyheight_((bitcoind_), (height_), \
|
||||
typesafe_cb_preargs(void, void *, \
|
||||
(cb), (arg), \
|
||||
struct bitcoind *, \
|
||||
struct bitcoin_blkid *, \
|
||||
struct bitcoin_block *),\
|
||||
(arg))
|
||||
|
||||
void bitcoind_gettxout(struct bitcoind *bitcoind,
|
||||
const struct bitcoin_txid *txid, const u32 outnum,
|
||||
void (*cb)(struct bitcoind *bitcoind,
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
/* Mutual recursion via timer. */
|
||||
static void try_extend_tip(struct chain_topology *topo);
|
||||
|
||||
/* get_init_blockhash sets topo->root, start_fee_estimate clears
|
||||
/* init_topo sets topo->root, start_fee_estimate clears
|
||||
* feerate_uninitialized (even if unsuccessful) */
|
||||
static void maybe_completed_init(struct chain_topology *topo)
|
||||
{
|
||||
@@ -780,6 +780,7 @@ static void try_extend_tip(struct chain_topology *topo)
|
||||
}
|
||||
|
||||
static void init_topo(struct bitcoind *bitcoind UNUSED,
|
||||
struct bitcoin_blkid *blkid UNUSED,
|
||||
struct bitcoin_block *blk,
|
||||
struct chain_topology *topo)
|
||||
{
|
||||
@@ -795,45 +796,6 @@ static void init_topo(struct bitcoind *bitcoind UNUSED,
|
||||
maybe_completed_init(topo);
|
||||
}
|
||||
|
||||
static void get_init_block(struct bitcoind *bitcoind,
|
||||
const struct bitcoin_blkid *blkid,
|
||||
struct chain_topology *topo)
|
||||
{
|
||||
bitcoind_getrawblock(bitcoind, blkid, init_topo, topo);
|
||||
}
|
||||
|
||||
static void get_init_blockhash(struct bitcoind *bitcoind, u32 blockcount,
|
||||
struct chain_topology *topo)
|
||||
{
|
||||
/* If bitcoind's current blockheight is below the requested
|
||||
* height, refuse. You can always explicitly request a reindex from
|
||||
* that block number using --rescan=. */
|
||||
if (blockcount < topo->max_blockheight) {
|
||||
/* UINT32_MAX == no blocks in database */
|
||||
if (topo->max_blockheight == UINT32_MAX) {
|
||||
/* Relative rescan, but we didn't know the blockheight */
|
||||
/* Protect against underflow in subtraction.
|
||||
* Possible in regtest mode. */
|
||||
if (blockcount < bitcoind->ld->config.rescan)
|
||||
topo->max_blockheight = 0;
|
||||
else
|
||||
topo->max_blockheight = blockcount - bitcoind->ld->config.rescan;
|
||||
} else
|
||||
fatal("bitcoind has gone backwards from %u to %u blocks!",
|
||||
topo->max_blockheight, blockcount);
|
||||
}
|
||||
|
||||
/* Rollback to the given blockheight, so we start track
|
||||
* correctly again */
|
||||
wallet_blocks_rollback(topo->ld->wallet, topo->max_blockheight);
|
||||
/* This may have unconfirmed txs: reconfirm as we add blocks. */
|
||||
watch_for_utxo_reconfirmation(topo, topo->ld->wallet);
|
||||
|
||||
/* Get up to speed with topology. */
|
||||
bitcoind_getblockhash(bitcoind, topo->max_blockheight,
|
||||
get_init_block, topo);
|
||||
}
|
||||
|
||||
u32 get_block_height(const struct chain_topology *topo)
|
||||
{
|
||||
return topo->tip->height;
|
||||
@@ -942,9 +904,95 @@ struct chain_topology *new_topology(struct lightningd *ld, struct log *log)
|
||||
topo->root = NULL;
|
||||
topo->sync_waiters = tal(topo, struct list_head);
|
||||
list_head_init(topo->sync_waiters);
|
||||
|
||||
return topo;
|
||||
}
|
||||
|
||||
static void check_blockcount(struct chain_topology *topo, u32 blockcount)
|
||||
{
|
||||
/* If bitcoind's current blockheight is below the requested
|
||||
* height, refuse. You can always explicitly request a reindex from
|
||||
* that block number using --rescan=. */
|
||||
if (blockcount < topo->max_blockheight) {
|
||||
/* UINT32_MAX == no blocks in database */
|
||||
if (topo->max_blockheight == UINT32_MAX) {
|
||||
/* Relative rescan, but we didn't know the blockheight */
|
||||
/* Protect against underflow in subtraction.
|
||||
* Possible in regtest mode. */
|
||||
if (blockcount < topo->bitcoind->ld->config.rescan)
|
||||
topo->max_blockheight = 0;
|
||||
else
|
||||
topo->max_blockheight = blockcount - topo->bitcoind->ld->config.rescan;
|
||||
} else
|
||||
fatal("bitcoind has gone backwards from %u to %u blocks!",
|
||||
topo->max_blockheight, blockcount);
|
||||
}
|
||||
|
||||
/* Rollback to the given blockheight, so we start track
|
||||
* correctly again */
|
||||
wallet_blocks_rollback(topo->ld->wallet, topo->max_blockheight);
|
||||
/* This may have unconfirmed txs: reconfirm as we add blocks. */
|
||||
watch_for_utxo_reconfirmation(topo, topo->ld->wallet);
|
||||
}
|
||||
|
||||
static void retry_check_chain(struct chain_topology *topo);
|
||||
|
||||
static void
|
||||
check_chain(struct bitcoind *bitcoind, const char *chain,
|
||||
const u32 headercount, const u32 blockcount, const bool ibd,
|
||||
const bool first_call, struct chain_topology *topo)
|
||||
{
|
||||
if (!streq(chain, chainparams->bip70_name))
|
||||
fatal("Wrong network! Our Bitcoin backend is running on '%s',"
|
||||
" but we expect '%s'.", chain, chainparams->bip70_name);
|
||||
|
||||
if (first_call) {
|
||||
/* Has the Bitcoin backend gone backward ? */
|
||||
check_blockcount(topo, blockcount);
|
||||
/* Get up to speed with topology. */
|
||||
bitcoind_getrawblockbyheight(topo->bitcoind, topo->max_blockheight,
|
||||
init_topo, topo);
|
||||
}
|
||||
|
||||
if (ibd) {
|
||||
if (first_call)
|
||||
log_unusual(bitcoind->log,
|
||||
"Waiting for initial block download (this can take"
|
||||
" a while!)");
|
||||
else
|
||||
log_debug(bitcoind->log,
|
||||
"Still waiting for initial block download");
|
||||
} else if (headercount != blockcount) {
|
||||
if (first_call)
|
||||
log_unusual(bitcoind->log,
|
||||
"Waiting for bitcoind to catch up"
|
||||
" (%u blocks of %u)",
|
||||
blockcount, headercount);
|
||||
else
|
||||
log_debug(bitcoind->log,
|
||||
"Waiting for bitcoind to catch up"
|
||||
" (%u blocks of %u)",
|
||||
blockcount, headercount);
|
||||
} else {
|
||||
if (!first_call)
|
||||
log_unusual(bitcoind->log,
|
||||
"Bitcoin backend now synced.");
|
||||
bitcoind->synced = true;
|
||||
return;
|
||||
}
|
||||
|
||||
notleak(new_reltimer(bitcoind->ld->timers, bitcoind,
|
||||
/* Be 4x more aggressive in this case. */
|
||||
time_divide(time_from_sec(bitcoind->ld->topology
|
||||
->poll_seconds), 4),
|
||||
retry_check_chain, bitcoind->ld->topology));
|
||||
}
|
||||
|
||||
static void retry_check_chain(struct chain_topology *topo)
|
||||
{
|
||||
bitcoind_getchaininfo(topo->bitcoind, true, check_chain, topo);
|
||||
}
|
||||
|
||||
void setup_topology(struct chain_topology *topo,
|
||||
struct timers *timers,
|
||||
u32 min_blockheight, u32 max_blockheight)
|
||||
@@ -958,10 +1006,8 @@ void setup_topology(struct chain_topology *topo,
|
||||
/* This waits for bitcoind. */
|
||||
bitcoind_check_commands(topo->bitcoind);
|
||||
|
||||
/* Make sure bitcoind is started, and ready */
|
||||
wait_for_bitcoind(topo->bitcoind);
|
||||
|
||||
bitcoind_getblockcount(topo->bitcoind, get_init_blockhash, topo);
|
||||
/* Sanity checks, then topology initialization. */
|
||||
bitcoind_getchaininfo(topo->bitcoind, true, check_chain, topo);
|
||||
|
||||
tal_add_destructor(topo, destroy_chain_topology);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user