core-lightning/lightningd/bitcoind.c
Rusty Russell 1bdbb9261e lightningd: add context arg to bitcoind_sendrawtx()
If the context is freed, the callback isn't called.  This doesn't matter
yet, since our callbacks tend to be such that the callback itself is
required to free things, but it's clearer this way and allows more
flexible usage in following patches.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-10-24 15:07:08 +10:30

864 lines
25 KiB
C

/* Code for talking to bitcoind. We use a plugin as the Bitcoin backend.
* The default one shipped with C-lightning is a plugin which talks to bitcoind
* by using bitcoin-cli, but the interface we use to gather Bitcoin data is
* standardized and you can use another plugin as the Bitcoin backend, or
* even make your own! */
#include "config.h"
#include <bitcoin/base58.h>
#include <bitcoin/block.h>
#include <bitcoin/feerate.h>
#include <bitcoin/script.h>
#include <bitcoin/shadouble.h>
#include <ccan/array_size/array_size.h>
#include <ccan/io/io.h>
#include <ccan/tal/str/str.h>
#include <common/configdir.h>
#include <common/json_parse.h>
#include <common/memleak.h>
#include <common/trace.h>
#include <db/exec.h>
#include <lightningd/bitcoind.h>
#include <lightningd/chaintopology.h>
#include <lightningd/io_loop_with_timers.h>
#include <lightningd/lightningd.h>
#include <lightningd/log.h>
#include <lightningd/plugin.h>
/* The names of the requests we can make to our Bitcoin backend. */
static const char *methods[] = {"getchaininfo", "getrawblockbyheight",
"sendrawtransaction", "getutxout",
"estimatefees"};
static void bitcoin_destructor(struct plugin *p)
{
if (p->plugins->ld->state == LD_STATE_SHUTDOWN)
return;
fatal("The Bitcoin backend died.");
}
static void plugin_config_cb(const char *buffer,
const jsmntok_t *toks,
const jsmntok_t *idtok,
struct plugin *plugin)
{
plugin->plugin_state = INIT_COMPLETE;
log_debug(plugin->plugins->ld->log, "io_break: %s", __func__);
io_break(plugin);
}
static void config_plugin(struct plugin *plugin)
{
struct jsonrpc_request *req;
void *ret;
req = jsonrpc_request_start(plugin, "init", NULL,
plugin->non_numeric_ids, plugin->log,
NULL, plugin_config_cb, plugin);
plugin_populate_init_request(plugin, req);
jsonrpc_request_end(req);
plugin_request_send(plugin, req);
tal_add_destructor(plugin, bitcoin_destructor);
ret = io_loop_with_timers(plugin->plugins->ld);
log_debug(plugin->plugins->ld->log, "io_loop_with_timers: %s", __func__);
assert(ret == plugin);
}
static void wait_plugin(struct bitcoind *bitcoind, const char *method,
struct plugin *p)
{
/* We need our Bitcoin backend to be initialized, but the plugins have
* not yet been started at this point.
* So send `init` to each plugin which registered for a Bitcoin method
* and wait for its response, which we take as an ACK that it is
* operational (i.e. bcli will wait for `bitcoind` to be warmed up
* before responding to `init`).
* Note that lightningd/plugin will not send `init` to an already
* configured plugin. */
if (p->plugin_state == NEEDS_INIT)
config_plugin(p);
strmap_add(&bitcoind->pluginsmap, method, p);
}
void bitcoind_check_commands(struct bitcoind *bitcoind)
{
size_t i;
struct plugin *p;
for (i = 0; i < ARRAY_SIZE(methods); i++) {
p = find_plugin_for_command(bitcoind->ld, methods[i]);
if (p == NULL) {
/* For testing .. */
log_debug(bitcoind->ld->log, "Missing a Bitcoin plugin"
" command");
fatal("Could not access the plugin for %s, is a "
"Bitcoin plugin (by default plugins/bcli) "
"registered ?", methods[i]);
}
wait_plugin(bitcoind, methods[i], p);
}
}
/* 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 *fmt, ...)
{
va_list ap;
char *reason;
struct plugin *p;
va_start(ap, fmt);
reason = tal_vfmt(NULL, fmt, ap);
va_end(ap);
p = strmap_getn(&bitcoind->pluginsmap, method, strcspn(method, "."));
fatal("%s error: bad response to %s (%s), response was %.*s",
p ? p->cmd : "UNKNOWN CALL", method, reason,
toks->end - toks->start, buf + toks->start);
}
/* Send a request to the Bitcoin plugin which registered that method,
* if it's still alive. */
static void bitcoin_plugin_send(struct bitcoind *bitcoind,
struct jsonrpc_request *req)
{
struct plugin *plugin = strmap_get(&bitcoind->pluginsmap, req->method);
if (!plugin)
fatal("Bitcoin backend plugin for %s died.", req->method);
plugin_request_send(plugin, req);
}
/* `estimatefees`
*
* Gather feerate from our Bitcoin backend. Will set the feerate to `null`
* if estimation failed.
*
* - `opening` is used for funding and also misc transactions
* - `mutual_close` is used for the mutual close transaction
* - `unilateral_close` is used for unilateral close (commitment transactions)
* - `delayed_to_us` is used for resolving our output from our unilateral close
* - `htlc_resolution` is used for resolving onchain HTLCs
* - `penalty` is used for resolving revoked transactions
* - `min` is the minimum acceptable feerate
* - `max` is the maximum acceptable feerate
*
* Plugin response (deprecated):
* {
* "opening": <sat per kVB>,
* "mutual_close": <sat per kVB>,
* "unilateral_close": <sat per kVB>,
* "delayed_to_us": <sat per kVB>,
* "htlc_resolution": <sat per kVB>,
* "penalty": <sat per kVB>,
* "min_acceptable": <sat per kVB>,
* "max_acceptable": <sat per kVB>,
* }
*
* Plugin response (modern):
* {
* "feerate_floor": <sat per kVB>,
* "feerates": {
* { "blocks": 2, "feerate": <sat per kVB> },
* { "blocks": 6, "feerate": <sat per kVB> },
* { "blocks": 12, "feerate": <sat per kVB> }
* { "blocks": 100, "feerate": <sat per kVB> }
* }
* }
*
* If rates are missing, we linearly interpolate (we don't extrapolate tho!).
*/
struct estimatefee_call {
struct bitcoind *bitcoind;
void (*cb)(struct lightningd *ld, u32 feerate_floor,
const struct feerate_est *rates);
};
/* Note: returns estimates in perkb, caller converts! */
static struct feerate_est *parse_feerate_ranges(const tal_t *ctx,
struct bitcoind *bitcoind,
const char *buf,
const jsmntok_t *floortok,
const jsmntok_t *feerates,
u32 *floor)
{
size_t i;
const jsmntok_t *t;
struct feerate_est *rates = tal_arr(ctx, struct feerate_est, 0);
if (!json_to_u32(buf, floortok, floor))
bitcoin_plugin_error(bitcoind, buf, floortok,
"estimatefees.feerate_floor", "Not a u32?");
json_for_each_arr(i, t, feerates) {
struct feerate_est rate;
const char *err;
err = json_scan(tmpctx, buf, t, "{blocks:%,feerate:%}",
JSON_SCAN(json_to_u32, &rate.blockcount),
JSON_SCAN(json_to_u32, &rate.rate));
if (err)
bitcoin_plugin_error(bitcoind, buf, t,
"estimatefees.feerates", err);
/* Block count must be in order. If rates go up somehow, we
* reduce to prev. */
if (tal_count(rates) != 0) {
const struct feerate_est *prev = &rates[tal_count(rates)-1];
if (rate.blockcount <= prev->blockcount)
bitcoin_plugin_error(bitcoind, buf, feerates,
"estimatefees.feerates",
"Blocks must be ascending"
" order: %u <= %u!",
rate.blockcount,
prev->blockcount);
if (rate.rate > prev->rate) {
log_unusual(bitcoind->log,
"Feerate for %u blocks (%u) is > rate"
" for %u blocks (%u)!",
rate.blockcount, rate.rate,
prev->blockcount, prev->rate);
rate.rate = prev->rate;
}
}
tal_arr_expand(&rates, rate);
}
if (tal_count(rates) == 0) {
if (chainparams->testnet)
log_debug(bitcoind->log, "Unable to estimate any fees");
else
log_unusual(bitcoind->log, "Unable to estimate any fees");
}
return rates;
}
static struct feerate_est *parse_deprecated_feerates(const tal_t *ctx,
struct bitcoind *bitcoind,
const char *buf,
const jsmntok_t *toks)
{
struct feerate_est *rates = tal_arr(ctx, struct feerate_est, 0);
struct oldstyle {
const char *name;
size_t blockcount;
size_t multiplier;
} oldstyles[] = { { "max_acceptable", 2, 10 },
{ "unilateral_close", 6, 1 },
{ "opening", 12, 1 },
{ "mutual_close", 100, 1 } };
for (size_t i = 0; i < ARRAY_SIZE(oldstyles); i++) {
const jsmntok_t *feeratetok;
struct feerate_est rate;
feeratetok = json_get_member(buf, toks, oldstyles[i].name);
if (!feeratetok) {
bitcoin_plugin_error(bitcoind, buf, toks,
"estimatefees",
"missing '%s' field",
oldstyles[i].name);
}
if (!json_to_u32(buf, feeratetok, &rate.rate)) {
if (chainparams->testnet)
log_debug(bitcoind->log,
"Unable to estimate %s fees",
oldstyles[i].name);
else
log_unusual(bitcoind->log,
"Unable to estimate %s fees",
oldstyles[i].name);
continue;
}
if (rate.rate == 0)
continue;
/* Cancel out the 10x multiplier on max_acceptable */
rate.rate /= oldstyles[i].multiplier;
rate.blockcount = oldstyles[i].blockcount;
tal_arr_expand(&rates, rate);
}
return rates;
}
static void estimatefees_callback(const char *buf, const jsmntok_t *toks,
const jsmntok_t *idtok,
struct estimatefee_call *call)
{
const jsmntok_t *resulttok, *floortok;
struct feerate_est *feerates;
u32 floor;
resulttok = json_get_member(buf, toks, "result");
if (!resulttok)
bitcoin_plugin_error(call->bitcoind, buf, toks,
"estimatefees",
"bad 'result' field");
/* Modern style has floor. */
floortok = json_get_member(buf, resulttok, "feerate_floor");
if (floortok) {
feerates = parse_feerate_ranges(call, call->bitcoind,
buf, floortok,
json_get_member(buf, resulttok,
"feerates"),
&floor);
} else {
if (!call->bitcoind->ld->deprecated_apis)
bitcoin_plugin_error(call->bitcoind, buf, resulttok,
"estimatefees",
"missing fee_floor field");
feerates = parse_deprecated_feerates(call, call->bitcoind,
buf, resulttok);
floor = feerate_from_style(FEERATE_FLOOR, FEERATE_PER_KSIPA);
}
/* Convert to perkw */
floor = feerate_from_style(floor, FEERATE_PER_KBYTE);
if (floor < FEERATE_FLOOR)
floor = FEERATE_FLOOR;
/* FIXME: We could let this go below the dynamic floor, but we'd
* need to know if the floor is because of their node's policy
* (minrelaytxfee) or mempool conditions (mempoolminfee). */
for (size_t i = 0; i < tal_count(feerates); i++) {
feerates[i].rate = feerate_from_style(feerates[i].rate,
FEERATE_PER_KBYTE);
if (feerates[i].rate < floor)
feerates[i].rate = floor;
}
call->cb(call->bitcoind->ld, floor, feerates);
tal_free(call);
}
void bitcoind_estimate_fees(struct bitcoind *bitcoind,
void (*cb)(struct lightningd *ld,
u32 feerate_floor,
const struct feerate_est *feerates))
{
struct jsonrpc_request *req;
struct estimatefee_call *call = tal(bitcoind, struct estimatefee_call);
call->bitcoind = bitcoind;
call->cb = cb;
req = jsonrpc_request_start(bitcoind, "estimatefees", NULL, true,
bitcoind->log,
NULL, estimatefees_callback, call);
jsonrpc_request_end(req);
plugin_request_send(strmap_get(&bitcoind->pluginsmap,
"estimatefees"), req);
}
/* `sendrawtransaction`
*
* Send a transaction to the Bitcoin backend plugin. If the broadcast was
* not successful on its end, the plugin will populate the `errmsg` with
* the reason.
*
* Plugin response:
* {
* "success": <true|false>,
* "errmsg": "<not empty if !success>"
* }
*/
struct sendrawtx_call {
struct bitcoind *bitcoind;
void (*cb)(struct bitcoind *bitcoind,
bool success,
const char *err_msg,
void *);
void *cb_arg;
};
static void sendrawtx_callback(const char *buf, const jsmntok_t *toks,
const jsmntok_t *idtok,
struct sendrawtx_call *call)
{
const char *err;
const char *errmsg = NULL;
bool success = false;
err = json_scan(tmpctx, buf, toks, "{result:{success:%}}",
JSON_SCAN(json_to_bool, &success));
if (err) {
bitcoin_plugin_error(call->bitcoind, buf, toks,
"sendrawtransaction",
"bad 'result' field: %s", err);
} else if (!success) {
err = json_scan(tmpctx, buf, toks, "{result:{errmsg:%}}",
JSON_SCAN_TAL(tmpctx, json_strdup, &errmsg));
if (err)
bitcoin_plugin_error(call->bitcoind, buf, toks,
"sendrawtransaction",
"bad 'errmsg' field: %s",
err);
}
/* In case they don't free it, we will. */
tal_steal(tmpctx, call);
call->cb(call->bitcoind, success, errmsg, call->cb_arg);
}
void bitcoind_sendrawtx_(const tal_t *ctx,
struct bitcoind *bitcoind,
const char *id_prefix,
const char *hextx,
bool allowhighfees,
void (*cb)(struct bitcoind *bitcoind,
bool success, const char *msg, void *),
void *cb_arg)
{
struct jsonrpc_request *req;
struct sendrawtx_call *call = tal(ctx, struct sendrawtx_call);
call->bitcoind = bitcoind;
call->cb = cb;
call->cb_arg = cb_arg;
log_debug(bitcoind->log, "sendrawtransaction: %s", hextx);
req = jsonrpc_request_start(call, "sendrawtransaction",
id_prefix, true,
bitcoind->log,
NULL, sendrawtx_callback,
call);
json_add_string(req->stream, "tx", hextx);
json_add_bool(req->stream, "allowhighfees", allowhighfees);
jsonrpc_request_end(req);
bitcoin_plugin_send(bitcoind, req);
}
/* `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)
{
const char *block_str, *err;
struct bitcoin_blkid blkid;
struct bitcoin_block *blk;
trace_span_resume(call);
trace_span_end(call);
/* If block hash is `null`, this means not found! Call the callback
* with NULL values. */
err = json_scan(tmpctx, buf, toks, "{result:{blockhash:null}}");
if (!err) {
call->cb(call->bitcoind, NULL, NULL, call->cb_arg);
goto clean;
}
err = json_scan(tmpctx, buf, toks, "{result:{blockhash:%,block:%}}",
JSON_SCAN(json_to_sha256, &blkid.shad.sha),
JSON_SCAN_TAL(tmpctx, json_strdup, &block_str));
if (err)
bitcoin_plugin_error(call->bitcoind, buf, toks,
"getrawblockbyheight",
"bad 'result' field: %s", err);
blk = bitcoin_block_from_hex(tmpctx, chainparams, block_str,
strlen(block_str));
if (!blk)
bitcoin_plugin_error(call->bitcoind, buf, toks,
"getrawblockbyheight",
"bad block");
call->cb(call->bitcoind, &blkid, blk, call->cb_arg);
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;
trace_span_start("plugin/bitcoind", call);
trace_span_tag(call, "method", "getrawblockbyheight");
trace_span_suspend(call);
req = jsonrpc_request_start(bitcoind, "getrawblockbyheight", NULL, true,
bitcoind->log,
NULL, getrawblockbyheight_callback,
call);
json_add_num(req->stream, "height", height);
jsonrpc_request_end(req);
bitcoin_plugin_send(bitcoind, 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 char *err, *chain;
u32 headers, blocks;
bool ibd;
err = json_scan(tmpctx, buf, toks,
"{result:{chain:%,headercount:%,blockcount:%,ibd:%}}",
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)
bitcoin_plugin_error(call->bitcoind, buf, toks, "getchaininfo",
"bad 'result' field: %s", err);
call->cb(call->bitcoind, chain, headers, blocks, ibd,
call->first_call, call->cb_arg);
tal_free(call);
}
void bitcoind_getchaininfo_(struct bitcoind *bitcoind,
const bool first_call,
const u32 height,
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", NULL, true,
bitcoind->log,
NULL, getchaininfo_callback, call);
json_add_u32(req->stream, "last_height", height);
jsonrpc_request_end(req);
bitcoin_plugin_send(bitcoind, req);
}
/* `getutxout`
*
* Get information about an UTXO. If the TXO is spent, the plugin will set
* all fields to `null`.
* {
* "amount": <The output's amount in *sats*>,
* "script": "The output's scriptPubKey",
* }
*/
struct getutxout_call {
struct bitcoind *bitcoind;
unsigned int blocknum, txnum, outnum;
/* The real callback */
void (*cb)(struct bitcoind *bitcoind,
const struct bitcoin_tx_output *txout, void *arg);
/* The real callback arg */
void *cb_arg;
};
static void getutxout_callback(const char *buf, const jsmntok_t *toks,
const jsmntok_t *idtok,
struct getutxout_call *call)
{
const char *err;
struct bitcoin_tx_output txout;
err = json_scan(tmpctx, buf, toks, "{result:{script:null}}");
if (!err) {
call->cb(call->bitcoind, NULL, call->cb_arg);
goto clean;
}
err = json_scan(tmpctx, buf, toks, "{result:{script:%,amount:%}}",
JSON_SCAN_TAL(tmpctx, json_tok_bin_from_hex,
&txout.script),
JSON_SCAN(json_to_sat, &txout.amount));
if (err)
bitcoin_plugin_error(call->bitcoind, buf, toks, "getutxout",
"bad 'result' field: %s", err);
call->cb(call->bitcoind, &txout, call->cb_arg);
clean:
tal_free(call);
}
void bitcoind_getutxout_(struct bitcoind *bitcoind,
const struct bitcoin_outpoint *outpoint,
void (*cb)(struct bitcoind *,
const struct bitcoin_tx_output *,
void *),
void *cb_arg)
{
struct jsonrpc_request *req;
struct getutxout_call *call = tal(bitcoind, struct getutxout_call);
call->bitcoind = bitcoind;
call->cb = cb;
call->cb_arg = cb_arg;
req = jsonrpc_request_start(bitcoind, "getutxout", NULL, true,
bitcoind->log,
NULL, getutxout_callback, call);
json_add_txid(req->stream, "txid", &outpoint->txid);
json_add_num(req->stream, "vout", outpoint->n);
jsonrpc_request_end(req);
bitcoin_plugin_send(bitcoind, req);
}
/* Context for the getfilteredblock call. Wraps the actual arguments while we
* process the various steps. */
struct filteredblock_call {
struct list_node list;
void (*cb)(struct bitcoind *bitcoind, const struct filteredblock *fb,
void *arg);
void *arg;
struct filteredblock *result;
struct filteredblock_outpoint **outpoints;
size_t current_outpoint;
struct timeabs start_time;
u32 height;
};
/* Declaration for recursion in process_getfilteredblock_step1 */
static void
process_getfiltered_block_final(struct bitcoind *bitcoind,
const struct filteredblock_call *call);
static void
process_getfilteredblock_step2(struct bitcoind *bitcoind,
const struct bitcoin_tx_output *output,
void *arg)
{
struct filteredblock_call *call = (struct filteredblock_call *)arg;
struct filteredblock_outpoint *o = call->outpoints[call->current_outpoint];
/* If this output is unspent, add it to the filteredblock result. */
if (output)
tal_arr_expand(&call->result->outpoints, tal_steal(call->result, o));
call->current_outpoint++;
if (call->current_outpoint < tal_count(call->outpoints)) {
o = call->outpoints[call->current_outpoint];
bitcoind_getutxout(bitcoind, &o->outpoint,
process_getfilteredblock_step2, call);
} else {
/* If there were no more outpoints to check, we call the callback. */
process_getfiltered_block_final(bitcoind, call);
}
}
static void process_getfilteredblock_step1(struct bitcoind *bitcoind,
struct bitcoin_blkid *blkid,
struct bitcoin_block *block,
struct filteredblock_call *call)
{
struct filteredblock_outpoint *o;
struct bitcoin_tx *tx;
/* If we were unable to fetch the block hash (bitcoind doesn't know
* about a block at that height), we can short-circuit and just call
* the callback. */
if (!blkid)
return process_getfiltered_block_final(bitcoind, call);
/* So we have the first piece of the puzzle, the block hash */
call->result = tal(call, struct filteredblock);
call->result->height = call->height;
call->result->outpoints = tal_arr(call->result, struct filteredblock_outpoint *, 0);
call->result->id = *blkid;
/* If the plugin gave us a block id, they MUST send us a block. */
assert(block != NULL);
call->result->prev_hash = block->hdr.prev_hash;
/* Allocate an array containing all the potentially interesting
* outpoints. We will later copy the ones we're interested in into the
* call->result if they are unspent. */
call->outpoints = tal_arr(call, struct filteredblock_outpoint *, 0);
for (size_t i = 0; i < tal_count(block->tx); i++) {
tx = block->tx[i];
for (size_t j = 0; j < tx->wtx->num_outputs; j++) {
const u8 *script = bitcoin_tx_output_get_script(NULL, tx, j);
struct amount_asset amount = bitcoin_tx_output_get_amount(tx, j);
if (amount_asset_is_main(&amount) && is_p2wsh(script, NULL)) {
/* This is an interesting output, remember it. */
o = tal(call->outpoints, struct filteredblock_outpoint);
bitcoin_txid(tx, &o->outpoint.txid);
o->outpoint.n = j;
o->amount = amount_asset_to_sat(&amount);
o->txindex = i;
o->scriptPubKey = tal_steal(o, script);
tal_arr_expand(&call->outpoints, o);
} else {
tal_free(script);
}
}
}
if (tal_count(call->outpoints) == 0) {
/* If there were no outpoints to check, we can short-circuit
* and just call the callback. */
process_getfiltered_block_final(bitcoind, call);
} else {
/* Otherwise we start iterating through call->outpoints and
* store the one's that are unspent in
* call->result->outpoints. */
o = call->outpoints[call->current_outpoint];
bitcoind_getutxout(bitcoind, &o->outpoint,
process_getfilteredblock_step2, call);
}
}
/* Takes a call, dispatches it to all queued requests that match the same
* height, and then kicks off the next call. */
static void
process_getfiltered_block_final(struct bitcoind *bitcoind,
const struct filteredblock_call *call)
{
struct filteredblock_call *c, *next;
u32 height = call->height;
if (call->result == NULL)
goto next;
/* Need to steal so we don't accidentally free it while iterating through the list below. */
struct filteredblock *fb = tal_steal(NULL, call->result);
list_for_each_safe(&bitcoind->pending_getfilteredblock, c, next, list) {
if (c->height == height) {
c->cb(bitcoind, fb, c->arg);
list_del(&c->list);
tal_free(c);
}
}
tal_free(fb);
next:
/* Nothing to free here, since `*call` was already deleted during the
* iteration above. It was also removed from the list, so no need to
* pop here. */
if (!list_empty(&bitcoind->pending_getfilteredblock)) {
c = list_top(&bitcoind->pending_getfilteredblock, struct filteredblock_call, list);
bitcoind_getrawblockbyheight(bitcoind, c->height,
process_getfilteredblock_step1, c);
}
}
void bitcoind_getfilteredblock_(struct bitcoind *bitcoind, u32 height,
void (*cb)(struct bitcoind *bitcoind,
const struct filteredblock *fb,
void *arg),
void *arg)
{
/* Stash the call context for when we need to call the callback after
* all the bitcoind calls we need to perform. */
struct filteredblock_call *call = tal(bitcoind, struct filteredblock_call);
/* If this is the first request, we should start processing it. */
bool start = list_empty(&bitcoind->pending_getfilteredblock);
call->cb = cb;
call->arg = arg;
call->height = height;
assert(call->cb != NULL);
call->start_time = time_now();
call->result = NULL;
call->current_outpoint = 0;
list_add_tail(&bitcoind->pending_getfilteredblock, &call->list);
if (start)
bitcoind_getrawblockbyheight(bitcoind, height,
process_getfilteredblock_step1, call);
}
static void destroy_bitcoind(struct bitcoind *bitcoind)
{
strmap_clear(&bitcoind->pluginsmap);
}
struct bitcoind *new_bitcoind(const tal_t *ctx,
struct lightningd *ld,
struct logger *log)
{
struct bitcoind *bitcoind = tal(ctx, struct bitcoind);
strmap_init(&bitcoind->pluginsmap);
bitcoind->ld = ld;
bitcoind->log = log;
list_head_init(&bitcoind->pending_getfilteredblock);
tal_add_destructor(bitcoind, destroy_bitcoind);
bitcoind->synced = false;
return bitcoind;
}