bcli: remove unused async code after sync refactor

Remove the asynchronous execution infrastructure no longer needed after converting all bcli commands to synchronous execution. This includes removing the async callbacks, the pending request queue, etc.

Fix missing `close(from)` file descriptor leak in `run_bitcoin_cliv`.

Changelog-Changed: bcli plugin now uses synchronous execution, simplifying bitcoin backend communication and improving error handling reliability.
This commit is contained in:
dovgopoly
2026-01-10 00:02:25 +02:00
committed by Sangbida
parent 3e979d1b20
commit 57d60c025b

View File

@@ -1,7 +1,6 @@
#include "config.h"
#include <ccan/array_size/array_size.h>
#include <ccan/cast/cast.h>
#include <ccan/io/io.h>
#include <ccan/pipecmd/pipecmd.h>
#include <ccan/read_write_all/read_write_all.h>
#include <ccan/tal/grab_file/grab_file.h>
@@ -12,21 +11,10 @@
#include <errno.h>
#include <inttypes.h>
#include <plugins/libplugin.h>
#include <unistd.h>
/* Bitcoind's web server has a default of 4 threads, with queue depth 16.
* It will *fail* rather than queue beyond that, so we must not stress it!
*
* This is how many request for each priority level we have.
*/
#define BITCOIND_MAX_PARALLEL 4
#define RPC_TRANSACTION_ALREADY_IN_CHAIN -27
enum bitcoind_prio {
BITCOIND_LOW_PRIO,
BITCOIND_HIGH_PRIO
};
#define BITCOIND_NUM_PRIO (BITCOIND_HIGH_PRIO+1)
struct bitcoind {
/* eg. "bitcoin-cli" */
char *cli;
@@ -37,22 +25,6 @@ struct bitcoind {
/* bitcoind's version, used for compatibility checks. */
u32 version;
/* Is bitcoind synced? If not, we retry. */
bool synced;
/* How many high/low prio requests are we running (it's ratelimited) */
size_t num_requests[BITCOIND_NUM_PRIO];
/* Pending requests (high and low prio). */
struct list_head pending[BITCOIND_NUM_PRIO];
/* In flight requests (in a list for memleak detection) */
struct list_head current;
/* If non-zero, time we first hit a bitcoind error. */
unsigned int error_count;
struct timemono first_error_time;
/* How long to keep trying to contact bitcoind
* before fatally exiting. */
u64 retry_timeout;
@@ -73,24 +45,6 @@ struct bitcoind {
static struct bitcoind *bitcoind;
struct bitcoin_cli {
struct list_node list;
int fd;
int *exitstatus;
pid_t pid;
const char **args;
const char **stdinargs;
struct timemono start;
enum bitcoind_prio prio;
char *output;
size_t output_bytes;
size_t new_output;
struct command_result *(*process)(struct bitcoin_cli *);
struct command *cmd;
/* Used to stash content between multiple calls */
void *stash;
};
/* Result of a synchronous bitcoin-cli call */
struct bcli_result {
char *output;
@@ -171,25 +125,6 @@ gather_args(const tal_t *ctx, const char ***stdinargs, const char *cmd, ...)
return ret;
}
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 next_bcli(enum bitcoind_prio prio);
/* For printing: simple string of args (no secrets!) */
static char *args_string(const tal_t *ctx, const char **args, const char **stdinargs)
{
@@ -251,9 +186,15 @@ run_bitcoin_cliv(const tal_t *ctx,
res->output = grab_fd_str(res, from);
res->output_len = strlen(res->output);
res->args = args_string(res, cmd, stdinargs);
close(from);
/* Wait for child to exit */
while (waitpid(child, &status, 0) < 0 && errno == EINTR);
while (waitpid(child, &status, 0) < 0) {
if (errno == EINTR)
continue;
plugin_err(plugin, "waitpid(%s) failed: %s",
res->args, strerror(errno));
}
if (!WIFEXITED(status))
plugin_err(plugin, "%s died with signal %i",
@@ -279,202 +220,6 @@ run_bitcoin_cli(const tal_t *ctx,
return res;
}
static char *bcli_args(const tal_t *ctx, struct bitcoin_cli *bcli)
{
return args_string(ctx, bcli->args, bcli->stdinargs);
}
/* Only set as destructor once bcli is in current. */
static void destroy_bcli(struct bitcoin_cli *bcli)
{
list_del_from(&bitcoind->current, &bcli->list);
}
static struct command_result *retry_bcli(struct command *cmd,
struct bitcoin_cli *bcli)
{
list_del_from(&bitcoind->current, &bcli->list);
tal_del_destructor(bcli, destroy_bcli);
list_add_tail(&bitcoind->pending[bcli->prio], &bcli->list);
tal_free(bcli->output);
next_bcli(bcli->prio);
return timer_complete(cmd);
}
/* We allow 60 seconds of spurious errors, eg. reorg. */
static void bcli_failure(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(bitcoind->retry_timeout)))
plugin_err(bcli->cmd->plugin,
"%s exited %u (after %u other errors) '%.*s'; "
"we have been retrying command for "
"--bitcoin-retry-timeout=%"PRIu64" seconds; "
"bitcoind setup or our --bitcoin-* configs broken?",
bcli_args(tmpctx, bcli),
exitstatus,
bitcoind->error_count,
(int)bcli->output_bytes,
bcli->output,
bitcoind->retry_timeout);
plugin_log(bcli->cmd->plugin, LOG_UNUSUAL, "%s exited with status %u",
bcli_args(tmpctx, bcli), exitstatus);
bitcoind->error_count++;
/* Retry in 1 second */
command_timer(bcli->cmd, time_from_sec(1), retry_bcli, bcli);
}
static void bcli_finished(struct io_conn *conn UNUSED, struct bitcoin_cli *bcli)
{
int ret, status;
struct command_result *res;
enum bitcoind_prio prio = bcli->prio;
u64 msec = time_to_msec(timemono_between(time_mono(), bcli->start));
/* If it took over 10 seconds, that's rather strange. */
if (msec > 10000)
plugin_log(bcli->cmd->plugin, LOG_UNUSUAL,
"bitcoin-cli: finished %s (%"PRIu64" ms)",
bcli_args(tmpctx, bcli), msec);
assert(bitcoind->num_requests[prio] > 0);
/* FIXME: If we waited for SIGCHILD, this could never hang! */
while ((ret = waitpid(bcli->pid, &status, 0)) < 0 && errno == EINTR);
if (ret != bcli->pid)
plugin_err(bcli->cmd->plugin, "%s %s", bcli_args(tmpctx, bcli),
ret == 0 ? "not exited?" : strerror(errno));
if (!WIFEXITED(status))
plugin_err(bcli->cmd->plugin, "%s died with signal %i",
bcli_args(tmpctx, bcli),
WTERMSIG(status));
/* Implicit nonzero_exit_ok == false */
if (!bcli->exitstatus) {
if (WEXITSTATUS(status) != 0) {
bcli_failure(bcli, WEXITSTATUS(status));
bitcoind->num_requests[prio]--;
goto done;
}
} else
*bcli->exitstatus = WEXITSTATUS(status);
if (WEXITSTATUS(status) == 0)
bitcoind->error_count = 0;
bitcoind->num_requests[bcli->prio]--;
res = bcli->process(bcli);
if (!res)
bcli_failure(bcli, WEXITSTATUS(status));
else
tal_free(bcli);
done:
next_bcli(prio);
}
static void next_bcli(enum bitcoind_prio prio)
{
struct bitcoin_cli *bcli;
struct io_conn *conn;
int in;
if (bitcoind->num_requests[prio] >= BITCOIND_MAX_PARALLEL)
return;
bcli = list_pop(&bitcoind->pending[prio], struct bitcoin_cli, list);
if (!bcli)
return;
bcli->pid = pipecmdarr(&in, &bcli->fd, &bcli->fd,
cast_const2(char **, bcli->args));
if (bcli->pid < 0)
plugin_err(bcli->cmd->plugin, "%s exec failed: %s",
bcli->args[0], strerror(errno));
if (bitcoind->rpcpass) {
write_all(in, bitcoind->rpcpass, strlen(bitcoind->rpcpass));
write_all(in, "\n", strlen("\n"));
}
for (size_t i = 0; i < tal_count(bcli->stdinargs); i++) {
write_all(in, bcli->stdinargs[i], strlen(bcli->stdinargs[i]));
write_all(in, "\n", strlen("\n"));
}
close(in);
bcli->start = time_mono();
bitcoind->num_requests[prio]++;
/* We don't keep a pointer to this, but it's not a leak */
conn = notleak(io_new_conn(bcli, bcli->fd, output_init, bcli));
io_set_finish(conn, bcli_finished, bcli);
list_add_tail(&bitcoind->current, &bcli->list);
tal_add_destructor(bcli, destroy_bcli);
}
static void
start_bitcoin_cliv(const tal_t *ctx,
struct command *cmd,
struct command_result *(*process)(struct bitcoin_cli *),
bool nonzero_exit_ok,
enum bitcoind_prio prio,
void *stash,
const char *method,
va_list ap)
{
struct bitcoin_cli *bcli = tal(bitcoind, struct bitcoin_cli);
bcli->process = process;
bcli->cmd = cmd;
bcli->prio = prio;
if (nonzero_exit_ok)
bcli->exitstatus = tal(bcli, int);
else
bcli->exitstatus = NULL;
bcli->stdinargs = tal_arr(bcli, const char *, 0);
bcli->args = gather_argsv(bcli, &bcli->stdinargs, method, ap);
bcli->stash = stash;
list_add_tail(&bitcoind->pending[bcli->prio], &bcli->list);
next_bcli(bcli->prio);
}
/* 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 LAST_ARG_NULL
start_bitcoin_cli(const tal_t *ctx,
struct command *cmd,
struct command_result *(*process)(struct bitcoin_cli *),
bool nonzero_exit_ok,
enum bitcoind_prio prio,
void *stash,
const char *method,
...)
{
va_list ap;
va_start(ap, method);
start_bitcoin_cliv(ctx, cmd, process, nonzero_exit_ok, prio, stash, method,
ap);
va_end(ap);
}
static void strip_trailing_whitespace(char *str, size_t len)
{
size_t stripped_len = len;
@@ -484,15 +229,6 @@ static void strip_trailing_whitespace(char *str, size_t len)
str[stripped_len] = 0x00;
}
static struct command_result *command_err_bcli_badjson(struct bitcoin_cli *bcli,
const char *errmsg)
{
char *err = tal_fmt(bcli, "%s: bad JSON: %s (%.*s)",
bcli_args(tmpctx, bcli), errmsg,
(int)bcli->output_bytes, bcli->output);
return command_done_err(bcli->cmd, BCLI_ERROR, err, NULL);
}
static struct command_result *command_err(struct command *cmd,
struct bcli_result *res,
const char *errmsg)
@@ -508,80 +244,6 @@ static void json_add_null(struct json_stream *stream, const char *fieldname)
json_add_primitive(stream, fieldname, "null");
}
static UNNEEDED struct command_result *process_getutxout(struct bitcoin_cli *bcli)
{
const jsmntok_t *tokens;
struct json_stream *response;
struct bitcoin_tx_output output;
const char *err;
/* As of at least v0.15.1.0, bitcoind returns "success" but an empty
string on a spent txout. */
if (*bcli->exitstatus != 0 || bcli->output_bytes == 0) {
response = jsonrpc_stream_success(bcli->cmd);
json_add_null(response, "amount");
json_add_null(response, "script");
return command_finished(bcli->cmd, response);
}
tokens = json_parse_simple(bcli->output, bcli->output,
bcli->output_bytes);
if (!tokens) {
return command_err_bcli_badjson(bcli, "cannot parse");
}
err = json_scan(tmpctx, bcli->output, tokens,
"{value:%,scriptPubKey:{hex:%}}",
JSON_SCAN(json_to_bitcoin_amount,
&output.amount.satoshis), /* Raw: bitcoind */
JSON_SCAN_TAL(bcli, json_tok_bin_from_hex,
&output.script));
if (err)
return command_err_bcli_badjson(bcli, err);
response = jsonrpc_stream_success(bcli->cmd);
json_add_sats(response, "amount", output.amount);
json_add_string(response, "script", tal_hex(response, output.script));
return command_finished(bcli->cmd, response);
}
static UNNEEDED struct command_result *process_getblockchaininfo(struct bitcoin_cli *bcli)
{
const jsmntok_t *tokens;
struct json_stream *response;
bool ibd;
u32 headers, blocks;
const char *chain, *err;
tokens = json_parse_simple(bcli->output,
bcli->output, bcli->output_bytes);
if (!tokens) {
return command_err_bcli_badjson(bcli, "cannot parse");
}
err = json_scan(tmpctx, bcli->output, tokens,
"{chain:%,headers:%,blocks:%,initialblockdownload:%}",
JSON_SCAN_TAL(tmpctx, json_strdup, &chain),
JSON_SCAN(json_to_number, &headers),
JSON_SCAN(json_to_number, &blocks),
JSON_SCAN(json_to_bool, &ibd));
if (err)
return command_err_bcli_badjson(bcli, err);
if (bitcoind->dev_ignore_ibd)
ibd = false;
response = jsonrpc_stream_success(bcli->cmd);
json_add_string(response, "chain", chain);
json_add_u32(response, "headercount", headers);
json_add_u32(response, "blockcount", blocks);
json_add_bool(response, "ibd", ibd);
return command_finished(bcli->cmd, response);
}
struct estimatefee_params {
u32 blocks;
const char *style;
@@ -594,14 +256,6 @@ static const struct estimatefee_params estimatefee_params[] = {
{ 100, "ECONOMICAL" },
};
struct estimatefees_stash {
/* This is max(mempoolminfee,minrelaytxfee) */
u64 perkb_floor;
u32 cursor;
/* FIXME: We use u64 but lightningd will store them as u32. */
u64 perkb[ARRAY_SIZE(estimatefee_params)];
};
static struct command_result *
estimatefees_null_response(struct command *cmd)
{
@@ -615,228 +269,6 @@ estimatefees_null_response(struct command *cmd)
return command_finished(cmd, response);
}
static UNNEEDED struct command_result *
estimatefees_parse_feerate(struct bitcoin_cli *bcli, u64 *feerate)
{
const jsmntok_t *tokens;
tokens = json_parse_simple(bcli->output,
bcli->output, bcli->output_bytes);
if (!tokens) {
return command_err_bcli_badjson(bcli, "cannot parse");
}
if (json_scan(tmpctx, bcli->output, tokens, "{feerate:%}",
JSON_SCAN(json_to_bitcoin_amount, feerate)) != NULL) {
/* Paranoia: if it had a feerate, but was malformed: */
if (json_get_member(bcli->output, tokens, "feerate"))
return command_err_bcli_badjson(bcli, "cannot scan");
/* Regtest fee estimation is generally awful: Fake it at min. */
if (bitcoind->fake_fees) {
*feerate = 1000;
return NULL;
}
/* We return null if estimation failed, and bitcoin-cli will
* exit with 0 but no feerate field on failure. */
return estimatefees_null_response(bcli->cmd);
}
return NULL;
}
static UNNEEDED struct command_result *process_sendrawtransaction(struct bitcoin_cli *bcli)
{
struct json_stream *response;
/* This is useful for functional tests. */
if (bcli->exitstatus)
plugin_log(bcli->cmd->plugin, LOG_DBG,
"sendrawtx exit %i (%s) %.*s",
*bcli->exitstatus, bcli_args(tmpctx, bcli),
*bcli->exitstatus ?
(u32)bcli->output_bytes-1 : 0,
bcli->output);
response = jsonrpc_stream_success(bcli->cmd);
json_add_bool(response, "success",
*bcli->exitstatus == 0 ||
*bcli->exitstatus ==
RPC_TRANSACTION_ALREADY_IN_CHAIN);
json_add_string(response, "errmsg",
*bcli->exitstatus ?
tal_strndup(bcli->cmd,
bcli->output, bcli->output_bytes-1)
: "");
return command_finished(bcli->cmd, response);
}
struct getrawblock_stash {
const char *block_hash;
u32 block_height;
const char *block_hex;
int *peers;
};
/* Mutual recursion. */
static UNNEEDED struct command_result *getrawblock(struct bitcoin_cli *bcli);
static UNNEEDED struct command_result *process_rawblock(struct bitcoin_cli *bcli)
{
struct json_stream *response;
struct getrawblock_stash *stash = bcli->stash;
strip_trailing_whitespace(bcli->output, bcli->output_bytes);
stash->block_hex = tal_steal(stash, bcli->output);
response = jsonrpc_stream_success(bcli->cmd);
json_add_string(response, "blockhash", stash->block_hash);
json_add_string(response, "block", stash->block_hex);
return command_finished(bcli->cmd, response);
}
static UNNEEDED struct command_result *process_getblockfrompeer(struct bitcoin_cli *bcli)
{
/* Remove the peer that we tried to get the block from and move along,
* we may also check on errors here */
struct getrawblock_stash *stash = bcli->stash;
if (bcli->exitstatus && *bcli->exitstatus != 0) {
/* We still continue with the execution if we can not fetch the
* block from peer */
plugin_log(bcli->cmd->plugin, LOG_DBG,
"failed to fetch block %s from peer %i, skip.",
stash->block_hash, stash->peers[tal_count(stash->peers) - 1]);
} else {
plugin_log(bcli->cmd->plugin, LOG_DBG,
"try to fetch block %s from peer %i.",
stash->block_hash, stash->peers[tal_count(stash->peers) - 1]);
}
tal_resize(&stash->peers, tal_count(stash->peers) - 1);
/* `getblockfrompeer` is an async call. sleep for a second to allow the
* block to be delivered by the peer. fixme: We could also sleep for
* double the last ping here (with sanity limit)*/
sleep(1);
return getrawblock(bcli);
}
static UNNEEDED struct command_result *process_getpeerinfo(struct bitcoin_cli *bcli)
{
const jsmntok_t *t, *toks;
struct getrawblock_stash *stash = bcli->stash;
size_t i;
toks =
json_parse_simple(bcli->output, bcli->output, bcli->output_bytes);
if (!toks) {
return command_err_bcli_badjson(bcli, "cannot parse");
}
stash->peers = tal_arr(bcli->stash, int, 0);
json_for_each_arr(i, t, toks)
{
int id;
u8 *services;
if (json_scan(tmpctx, bcli->output, t, "{id:%,services:%}",
JSON_SCAN(json_to_int, &id),
JSON_SCAN_TAL(tmpctx, json_tok_bin_from_hex, &services)) == NULL) {
/* From bitcoin source:
* // NODE_NETWORK means that the node is capable of serving the complete block chain. It is currently
* // set by all Bitcoin Core non pruned nodes, and is unset by SPV clients or other light clients.
* NODE_NETWORK = (1 << 0)
*/
if (tal_count(services) > 0 && (services[tal_count(services)-1] & (1<<0))) {
// fixme: future optimization: sort by last ping
tal_arr_expand(&stash->peers, id);
}
}
}
if (tal_count(stash->peers) <= 0) {
/* We don't have peers yet, retry from `getrawblock` */
plugin_log(bcli->cmd->plugin, LOG_DBG,
"got an empty peer list.");
return getrawblock(bcli);
}
start_bitcoin_cli(NULL, bcli->cmd, process_getblockfrompeer, true,
BITCOIND_HIGH_PRIO, stash, "getblockfrompeer",
stash->block_hash,
take(tal_fmt(NULL, "%i", stash->peers[tal_count(stash->peers) - 1])), NULL);
return command_still_pending(bcli->cmd);
}
static UNNEEDED struct command_result *process_getrawblock(struct bitcoin_cli *bcli)
{
/* We failed to get the raw block. */
if (bcli->exitstatus && *bcli->exitstatus != 0) {
struct getrawblock_stash *stash = bcli->stash;
plugin_log(bcli->cmd->plugin, LOG_DBG,
"failed to fetch block %s from the bitcoin backend (maybe pruned).",
stash->block_hash);
if (bitcoind->version >= 230000) {
/* `getblockformpeer` was introduced in v23.0.0 */
if (!stash->peers) {
/* We don't have peers to fetch blocks from, get
* some! */
start_bitcoin_cli(NULL, bcli->cmd,
process_getpeerinfo, true,
BITCOIND_HIGH_PRIO, stash,
"getpeerinfo", NULL);
return command_still_pending(bcli->cmd);
}
if (tal_count(stash->peers) > 0) {
/* We have peers left that we can ask for the
* block */
start_bitcoin_cli(
NULL, bcli->cmd, process_getblockfrompeer,
true, BITCOIND_HIGH_PRIO, stash,
"getblockfrompeer", stash->block_hash,
take(tal_fmt(NULL, "%i", stash->peers[tal_count(stash->peers) - 1])),
NULL);
return command_still_pending(bcli->cmd);
}
/* We failed to fetch the block from from any peer we
* got. */
plugin_log(
bcli->cmd->plugin, LOG_DBG,
"asked all known peers about block %s, retry",
stash->block_hash);
stash->peers = tal_free(stash->peers);
}
return NULL;
}
return process_rawblock(bcli);
}
static UNNEEDED struct command_result *
getrawblockbyheight_notfound_bcli(struct bitcoin_cli *bcli)
{
struct json_stream *response;
response = jsonrpc_stream_success(bcli->cmd);
json_add_null(response, "blockhash");
json_add_null(response, "block");
return command_finished(bcli->cmd, response);
}
static struct command_result *
getrawblockbyheight_notfound(struct command *cmd)
{
@@ -849,40 +281,6 @@ getrawblockbyheight_notfound(struct command *cmd)
return command_finished(cmd, response);
}
static UNNEEDED struct command_result *getrawblock(struct bitcoin_cli *bcli)
{
struct getrawblock_stash *stash = bcli->stash;
start_bitcoin_cli(NULL, bcli->cmd, process_getrawblock, true,
BITCOIND_HIGH_PRIO, stash, "getblock",
stash->block_hash,
/* Non-verbose: raw block. */
"0", NULL);
return command_still_pending(bcli->cmd);
}
static UNNEEDED struct command_result *process_getblockhash(struct bitcoin_cli *bcli)
{
struct getrawblock_stash *stash = bcli->stash;
/* If it failed with error 8, give an empty response. */
if (bcli->exitstatus && *bcli->exitstatus != 0) {
/* Other error means we have to retry. */
if (*bcli->exitstatus != 8)
return NULL;
return getrawblockbyheight_notfound_bcli(bcli);
}
strip_trailing_whitespace(bcli->output, bcli->output_bytes);
stash->block_hash = tal_strdup(stash, bcli->output);
if (!stash->block_hash || strlen(stash->block_hash) != 64) {
return command_err_bcli_badjson(bcli, "bad blockhash");
}
return getrawblock(bcli);
}
/* Get peers that support NODE_NETWORK (full nodes).
* Returns array of peer ids, or empty array if none found. */
static int *get_fullnode_peers(const tal_t *ctx, struct command *cmd)
@@ -1081,9 +479,6 @@ static struct command_result *getchaininfo(struct command *cmd,
return command_finished(cmd, response);
}
/* Mutual recursion. */
static struct command_result *estimatefees_done(struct bitcoin_cli *bcli);
/* Add a feerate, but don't publish one that bitcoind won't accept. */
static void json_add_feerate(struct json_stream *result, const char *fieldname,
struct command *cmd,
@@ -1110,69 +505,6 @@ static void json_add_feerate(struct json_stream *result, const char *fieldname,
}
}
static UNNEEDED struct command_result *estimatefees_next(struct command *cmd,
struct estimatefees_stash *stash)
{
struct json_stream *response;
if (stash->cursor < ARRAY_SIZE(stash->perkb)) {
start_bitcoin_cli(NULL, cmd, estimatefees_done, true,
BITCOIND_LOW_PRIO, stash,
"estimatesmartfee",
take(tal_fmt(NULL, "%u",
estimatefee_params[stash->cursor].blocks)),
estimatefee_params[stash->cursor].style,
NULL);
return command_still_pending(cmd);
}
response = jsonrpc_stream_success(cmd);
/* Present an ordered array of block deadlines, and a floor. */
json_array_start(response, "feerates");
for (size_t i = 0; i < ARRAY_SIZE(stash->perkb); i++) {
if (!stash->perkb[i])
continue;
json_object_start(response, NULL);
json_add_u32(response, "blocks", estimatefee_params[i].blocks);
json_add_feerate(response, "feerate", cmd, stash->perkb_floor, stash->perkb[i]);
json_object_end(response);
}
json_array_end(response);
json_add_u64(response, "feerate_floor", stash->perkb_floor);
return command_finished(cmd, response);
}
static UNNEEDED struct command_result *getminfees_done(struct bitcoin_cli *bcli)
{
const jsmntok_t *tokens;
const char *err;
u64 mempoolfee, relayfee;
struct estimatefees_stash *stash = bcli->stash;
if (*bcli->exitstatus != 0)
return estimatefees_null_response(bcli->cmd);
tokens = json_parse_simple(bcli->output,
bcli->output, bcli->output_bytes);
if (!tokens)
return command_err_bcli_badjson(bcli,
"cannot parse getmempoolinfo");
/* Look at minrelaytxfee they configured, and current min fee to get
* into mempool. */
err = json_scan(tmpctx, bcli->output, tokens,
"{mempoolminfee:%,minrelaytxfee:%}",
JSON_SCAN(json_to_bitcoin_amount, &mempoolfee),
JSON_SCAN(json_to_bitcoin_amount, &relayfee));
if (err)
return command_err_bcli_badjson(bcli, err);
stash->perkb_floor = max_u64(mempoolfee, relayfee);
stash->cursor = 0;
return estimatefees_next(bcli->cmd, stash);
}
/* Get the feerate floor from getmempoolinfo.
* Returns NULL on success (floor stored in *perkb_floor), or error response. */
static struct command_result *get_feerate_floor(struct command *cmd,
@@ -1248,7 +580,7 @@ static struct command_result *estimatefees(struct command *cmd,
const jsmntok_t *toks UNUSED)
{
struct command_result *err;
u64 perkb_floor;
u64 perkb_floor = 0;
u64 perkb[ARRAY_SIZE(estimatefee_params)];
struct json_stream *response;
@@ -1281,23 +613,6 @@ static struct command_result *estimatefees(struct command *cmd,
return command_finished(cmd, response);
}
static UNNEEDED struct command_result *estimatefees_done(struct bitcoin_cli *bcli)
{
struct command_result *err;
struct estimatefees_stash *stash = bcli->stash;
/* If we cannot estimate fees, no need to continue bothering bitcoind. */
if (*bcli->exitstatus != 0)
return estimatefees_null_response(bcli->cmd);
err = estimatefees_parse_feerate(bcli, &stash->perkb[stash->cursor]);
if (err)
return err;
stash->cursor++;
return estimatefees_next(bcli->cmd, stash);
}
/* Send a transaction to the Bitcoin network.
* Calls `sendrawtransaction` using the first parameter as the raw tx.
*/
@@ -1326,11 +641,11 @@ static struct command_result *sendrawtransaction(struct command *cmd,
"sendrawtransaction", tx, highfeesarg, NULL);
/* This is useful for functional tests. */
if (res->exitstatus)
plugin_log(cmd->plugin, LOG_DBG,
"sendrawtx exit %i (%s)",
res->exitstatus,
res->output);
plugin_log(cmd->plugin, LOG_DBG,
"sendrawtx exit %i (%s) %.*s",
res->exitstatus, res->args,
res->exitstatus ? (int)res->output_len : 0,
res->output);
response = jsonrpc_stream_success(cmd);
json_add_bool(response, "success",
@@ -1468,6 +783,7 @@ static void wait_and_check_bitcoind(struct plugin *p)
}
output = grab_fd_str(cmd, from);
close(from);
waitpid(child, &status, 0);
@@ -1543,12 +859,6 @@ static struct bitcoind *new_bitcoind(const tal_t *ctx)
bitcoind->cli = NULL;
bitcoind->datadir = NULL;
for (size_t i = 0; i < BITCOIND_NUM_PRIO; i++) {
bitcoind->num_requests[i] = 0;
list_head_init(&bitcoind->pending[i]);
}
list_head_init(&bitcoind->current);
bitcoind->error_count = 0;
bitcoind->retry_timeout = 60;
bitcoind->rpcuser = NULL;
bitcoind->rpcpass = NULL;