bitcoind: allow processing callbacks to indicate that a failure is spurious.

In particular, process_getblockhash() exits with status 8 when the block
number is out of range, which is expected.  Any other exit status should
be treated as a spurious error.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2018-02-24 15:41:18 +10:30 committed by Christian Decker
parent 7c8883ae7d
commit 0204c44243

View File

@ -16,6 +16,7 @@
#include <ccan/tal/tal.h> #include <ccan/tal/tal.h>
#include <common/json.h> #include <common/json.h>
#include <common/memleak.h> #include <common/memleak.h>
#include <common/timeout.h>
#include <common/utils.h> #include <common/utils.h>
#include <errno.h> #include <errno.h>
#include <inttypes.h> #include <inttypes.h>
@ -77,7 +78,7 @@ struct bitcoin_cli {
char *output; char *output;
size_t output_bytes; size_t output_bytes;
size_t new_output; size_t new_output;
void (*process)(struct bitcoin_cli *); bool (*process)(struct bitcoin_cli *);
void *cb; void *cb;
void *cb_arg; void *cb_arg;
struct bitcoin_cli **stopper; struct bitcoin_cli **stopper;
@ -115,10 +116,36 @@ static char *bcli_args(struct bitcoin_cli *bcli)
return ret; return ret;
} }
/* We allow 60 seconds of spurious errors, eg. reorg. */
static void bcli_failure(struct bitcoind *bitcoind,
struct bitcoin_cli *bcli,
int exitstatus)
{
struct timerel t;
if (!bitcoind->error_count)
bitcoind->first_error_time = time_mono();
t = timemono_between(time_mono(), bitcoind->first_error_time);
if (time_greater(t, time_from_sec(60)))
fatal("%s exited %u (after %u other errors) '%.*s'",
bcli_args(bcli),
exitstatus,
bitcoind->error_count,
(int)bcli->output_bytes,
bcli->output);
log_unusual(bitcoind->log,
"%s exited with status %u", bcli_args(bcli), exitstatus);
bitcoind->error_count++;
}
static void bcli_finished(struct io_conn *conn UNUSED, struct bitcoin_cli *bcli) static void bcli_finished(struct io_conn *conn UNUSED, struct bitcoin_cli *bcli)
{ {
int ret, status; int ret, status;
struct bitcoind *bitcoind = bcli->bitcoind; struct bitcoind *bitcoind = bcli->bitcoind;
bool ok;
/* FIXME: If we waited for SIGCHILD, this could never hang! */ /* FIXME: If we waited for SIGCHILD, this could never hang! */
ret = waitpid(bcli->pid, &status, 0); ret = waitpid(bcli->pid, &status, 0);
@ -133,27 +160,7 @@ static void bcli_finished(struct io_conn *conn UNUSED, struct bitcoin_cli *bcli)
if (!bcli->exitstatus) { if (!bcli->exitstatus) {
if (WEXITSTATUS(status) != 0) { if (WEXITSTATUS(status) != 0) {
/* Allow 60 seconds of spurious errors, eg. reorg. */ bcli_failure(bitcoind, bcli, WEXITSTATUS(status));
struct timerel t;
log_unusual(bcli->bitcoind->log,
"%s exited with status %u",
bcli_args(bcli),
WEXITSTATUS(status));
if (!bitcoind->error_count)
bitcoind->first_error_time = time_mono();
t = timemono_between(time_mono(),
bitcoind->first_error_time);
if (time_greater(t, time_from_sec(60)))
fatal("%s exited %u (after %u other errors) '%.*s'",
bcli_args(bcli),
WEXITSTATUS(status),
bitcoind->error_count,
(int)bcli->output_bytes,
bcli->output);
bitcoind->error_count++;
bitcoind->req_running = false; bitcoind->req_running = false;
goto done; goto done;
} }
@ -170,9 +177,12 @@ static void bcli_finished(struct io_conn *conn UNUSED, struct bitcoin_cli *bcli)
return; return;
db_begin_transaction(bitcoind->ld->wallet->db); db_begin_transaction(bitcoind->ld->wallet->db);
bcli->process(bcli); ok = bcli->process(bcli);
db_commit_transaction(bitcoind->ld->wallet->db); db_commit_transaction(bitcoind->ld->wallet->db);
if (!ok)
bcli_failure(bitcoind, bcli, WEXITSTATUS(status));
done: done:
tal_free(bcli); tal_free(bcli);
@ -202,8 +212,9 @@ static void next_bcli(struct bitcoind *bitcoind)
io_set_finish(conn, bcli_finished, bcli); io_set_finish(conn, bcli_finished, bcli);
} }
static void process_donothing(struct bitcoin_cli *bcli UNUSED) static bool process_donothing(struct bitcoin_cli *bcli UNUSED)
{ {
return true;
} }
/* If stopper gets freed first, set process() to a noop. */ /* If stopper gets freed first, set process() to a noop. */
@ -220,11 +231,12 @@ static void remove_stopper(struct bitcoin_cli *bcli)
tal_free(bcli->stopper); tal_free(bcli->stopper);
} }
/* If ctx is non-NULL, and is freed before we return, we don't call process() */ /* If ctx is non-NULL, and is freed before we return, we don't call process().
* process returns false() if it's a spurious error, and we should retry. */
static void static void
start_bitcoin_cli(struct bitcoind *bitcoind, start_bitcoin_cli(struct bitcoind *bitcoind,
const tal_t *ctx, const tal_t *ctx,
void (*process)(struct bitcoin_cli *), bool (*process)(struct bitcoin_cli *),
bool nonzero_exit_ok, bool nonzero_exit_ok,
void *cb, void *cb_arg, void *cb, void *cb_arg,
char *cmd, ...) char *cmd, ...)
@ -299,7 +311,7 @@ struct estimatefee {
static void do_one_estimatefee(struct bitcoind *bitcoind, static void do_one_estimatefee(struct bitcoind *bitcoind,
struct estimatefee *efee); struct estimatefee *efee);
static void process_estimatefee(struct bitcoin_cli *bcli) static bool process_estimatefee(struct bitcoin_cli *bcli)
{ {
double feerate; double feerate;
struct estimatefee *efee = bcli->cb_arg; struct estimatefee *efee = bcli->cb_arg;
@ -321,6 +333,7 @@ static void process_estimatefee(struct bitcoin_cli *bcli)
/* Next */ /* Next */
do_one_estimatefee(bcli->bitcoind, efee); do_one_estimatefee(bcli->bitcoind, efee);
} }
return true;
} }
static void do_one_estimatefee(struct bitcoind *bitcoind, static void do_one_estimatefee(struct bitcoind *bitcoind,
@ -354,7 +367,7 @@ void bitcoind_estimate_fees_(struct bitcoind *bitcoind,
do_one_estimatefee(bitcoind, efee); do_one_estimatefee(bitcoind, efee);
} }
static void process_sendrawtx(struct bitcoin_cli *bcli) static bool process_sendrawtx(struct bitcoin_cli *bcli)
{ {
void (*cb)(struct bitcoind *bitcoind, void (*cb)(struct bitcoind *bitcoind,
int, const char *msg, void *) = bcli->cb; int, const char *msg, void *) = bcli->cb;
@ -365,6 +378,7 @@ static void process_sendrawtx(struct bitcoin_cli *bcli)
*bcli->exitstatus, msg); *bcli->exitstatus, msg);
cb(bcli->bitcoind, *bcli->exitstatus, msg, bcli->cb_arg); cb(bcli->bitcoind, *bcli->exitstatus, msg, bcli->cb_arg);
return true;
} }
void bitcoind_sendrawtx_(struct bitcoind *bitcoind, void bitcoind_sendrawtx_(struct bitcoind *bitcoind,
@ -378,14 +392,13 @@ void bitcoind_sendrawtx_(struct bitcoind *bitcoind,
"sendrawtransaction", hextx, NULL); "sendrawtransaction", hextx, NULL);
} }
static void process_rawblock(struct bitcoin_cli *bcli) static bool process_rawblock(struct bitcoin_cli *bcli)
{ {
struct bitcoin_block *blk; struct bitcoin_block *blk;
void (*cb)(struct bitcoind *bitcoind, void (*cb)(struct bitcoind *bitcoind,
struct bitcoin_block *blk, struct bitcoin_block *blk,
void *arg) = bcli->cb; void *arg) = bcli->cb;
/* FIXME: Just get header if we can't get full block. */
blk = bitcoin_block_from_hex(bcli, bcli->output, bcli->output_bytes); blk = bitcoin_block_from_hex(bcli, bcli->output, bcli->output_bytes);
if (!blk) if (!blk)
fatal("%s: bad block '%.*s'?", fatal("%s: bad block '%.*s'?",
@ -393,6 +406,7 @@ static void process_rawblock(struct bitcoin_cli *bcli)
(int)bcli->output_bytes, bcli->output); (int)bcli->output_bytes, bcli->output);
cb(bcli->bitcoind, blk, bcli->cb_arg); cb(bcli->bitcoind, blk, bcli->cb_arg);
return true;
} }
void bitcoind_getrawblock_(struct bitcoind *bitcoind, void bitcoind_getrawblock_(struct bitcoind *bitcoind,
@ -409,7 +423,7 @@ void bitcoind_getrawblock_(struct bitcoind *bitcoind,
"getblock", hex, "false", NULL); "getblock", hex, "false", NULL);
} }
static void process_getblockcount(struct bitcoin_cli *bcli) static bool process_getblockcount(struct bitcoin_cli *bcli)
{ {
u32 blockcount; u32 blockcount;
char *p, *end; char *p, *end;
@ -424,6 +438,7 @@ static void process_getblockcount(struct bitcoin_cli *bcli)
bcli_args(bcli), p); bcli_args(bcli), p);
cb(bcli->bitcoind, blockcount, bcli->cb_arg); cb(bcli->bitcoind, blockcount, bcli->cb_arg);
return true;
} }
void bitcoind_getblockcount_(struct bitcoind *bitcoind, void bitcoind_getblockcount_(struct bitcoind *bitcoind,
@ -452,7 +467,7 @@ static void process_get_output(struct bitcoind *bitcoind, const struct bitcoin_t
go->cb(bitcoind, txout, go->cbarg); go->cb(bitcoind, txout, go->cbarg);
} }
static void process_gettxout(struct bitcoin_cli *bcli) static bool process_gettxout(struct bitcoin_cli *bcli)
{ {
void (*cb)(struct bitcoind *bitcoind, void (*cb)(struct bitcoind *bitcoind,
const struct bitcoin_tx_output *output, const struct bitcoin_tx_output *output,
@ -467,7 +482,7 @@ static void process_gettxout(struct bitcoin_cli *bcli)
log_debug(bcli->bitcoind->log, "%s: not unspent output?", log_debug(bcli->bitcoind->log, "%s: not unspent output?",
bcli_args(bcli)); bcli_args(bcli));
cb(bcli->bitcoind, NULL, bcli->cb_arg); cb(bcli->bitcoind, NULL, bcli->cb_arg);
return; return true;
} }
tokens = json_parse_input(bcli->output, bcli->output_bytes, &valid); tokens = json_parse_input(bcli->output, bcli->output_bytes, &valid);
@ -504,6 +519,7 @@ static void process_gettxout(struct bitcoin_cli *bcli)
bcli_args(bcli), (int)bcli->output_bytes, bcli->output); bcli_args(bcli), (int)bcli->output_bytes, bcli->output);
cb(bcli->bitcoind, &out, bcli->cb_arg); cb(bcli->bitcoind, &out, bcli->cb_arg);
return true;
} }
/** /**
@ -512,7 +528,7 @@ static void process_gettxout(struct bitcoin_cli *bcli)
* Used to resolve a `txoutput` after identifying the blockhash, and * Used to resolve a `txoutput` after identifying the blockhash, and
* before extracting the outpoint from the UTXO. * before extracting the outpoint from the UTXO.
*/ */
static void process_getblock(struct bitcoin_cli *bcli) static bool process_getblock(struct bitcoin_cli *bcli)
{ {
void (*cb)(struct bitcoind *bitcoind, void (*cb)(struct bitcoind *bitcoind,
const struct bitcoin_tx_output *output, const struct bitcoin_tx_output *output,
@ -532,7 +548,7 @@ static void process_getblock(struct bitcoin_cli *bcli)
bcli_args(bcli)); bcli_args(bcli));
cb(bcli->bitcoind, NULL, cbarg); cb(bcli->bitcoind, NULL, cbarg);
tal_free(go); tal_free(go);
return; return true;
} }
if (tokens[0].type != JSMN_OBJECT) if (tokens[0].type != JSMN_OBJECT)
@ -556,7 +572,7 @@ static void process_getblock(struct bitcoin_cli *bcli)
bcli_args(bcli), go->txnum); bcli_args(bcli), go->txnum);
cb(bcli->bitcoind, NULL, cbarg); cb(bcli->bitcoind, NULL, cbarg);
tal_free(go); tal_free(go);
return; return true;
} }
if (!bitcoin_txid_from_hex(bcli->output + txidtok->start, if (!bitcoin_txid_from_hex(bcli->output + txidtok->start,
@ -570,9 +586,10 @@ static void process_getblock(struct bitcoin_cli *bcli)
go->cb = cb; go->cb = cb;
/* Now get the raw tx output. */ /* Now get the raw tx output. */
bitcoind_gettxout(bcli->bitcoind, &txid, go->outnum, process_get_output, go); bitcoind_gettxout(bcli->bitcoind, &txid, go->outnum, process_get_output, go);
return true;
} }
static void process_getblockhash_for_txout(struct bitcoin_cli *bcli) static bool process_getblockhash_for_txout(struct bitcoin_cli *bcli)
{ {
void (*cb)(struct bitcoind *bitcoind, void (*cb)(struct bitcoind *bitcoind,
const struct bitcoin_tx_output *output, const struct bitcoin_tx_output *output,
@ -585,13 +602,14 @@ static void process_getblockhash_for_txout(struct bitcoin_cli *bcli)
bcli_args(bcli)); bcli_args(bcli));
tal_free(go); tal_free(go);
cb(bcli->bitcoind, NULL, cbarg); cb(bcli->bitcoind, NULL, cbarg);
return; return true;
} }
start_bitcoin_cli(bcli->bitcoind, NULL, process_getblock, false, cb, go, start_bitcoin_cli(bcli->bitcoind, NULL, process_getblock, false, cb, go,
"getblock", "getblock",
take(tal_strndup(go, bcli->output,bcli->output_bytes)), take(tal_strndup(go, bcli->output,bcli->output_bytes)),
NULL); NULL);
return true;
} }
void bitcoind_getoutput_(struct bitcoind *bitcoind, void bitcoind_getoutput_(struct bitcoind *bitcoind,
@ -618,17 +636,20 @@ void bitcoind_getoutput_(struct bitcoind *bitcoind,
notleak(go); notleak(go);
} }
static void process_getblockhash(struct bitcoin_cli *bcli) static bool process_getblockhash(struct bitcoin_cli *bcli)
{ {
struct bitcoin_blkid blkid; struct bitcoin_blkid blkid;
void (*cb)(struct bitcoind *bitcoind, void (*cb)(struct bitcoind *bitcoind,
const struct bitcoin_blkid *blkid, const struct bitcoin_blkid *blkid,
void *arg) = bcli->cb; void *arg) = bcli->cb;
/* If it failed, call with NULL block. */ /* If it failed with error 8, call with NULL block. */
if (*bcli->exitstatus != 0) { if (*bcli->exitstatus != 0) {
/* Other error means we have to retry. */
if (*bcli->exitstatus != 8)
return false;
cb(bcli->bitcoind, NULL, bcli->cb_arg); cb(bcli->bitcoind, NULL, bcli->cb_arg);
return; return true;
} }
if (bcli->output_bytes == 0 if (bcli->output_bytes == 0
@ -639,6 +660,7 @@ static void process_getblockhash(struct bitcoin_cli *bcli)
} }
cb(bcli->bitcoind, &blkid, bcli->cb_arg); cb(bcli->bitcoind, &blkid, bcli->cb_arg);
return true;
} }
void bitcoind_getblockhash_(struct bitcoind *bitcoind, void bitcoind_getblockhash_(struct bitcoind *bitcoind,