/* 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/feerate.h>
#include <bitcoin/script.h>
#include <ccan/array_size/array_size.h>
#include <ccan/io/io.h>
#include <ccan/tal/str/str.h>
#include <common/trace.h>
#include <lightningd/bitcoind.h>
#include <lightningd/chaintopology.h>
#include <lightningd/io_loop_with_timers.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->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 NORETURN
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);
}

/* Check response for error (fatal if found) and return result token (fatal if not found). */
static const jsmntok_t *get_bitcoin_result(struct bitcoind *bitcoind,
					   const char *buf,
					   const jsmntok_t *toks,
					   const char *method)
{
	const jsmntok_t *err_tok, *msg_tok, *result_tok;

	err_tok = json_get_member(buf, toks, "error");
	if (err_tok) {
		msg_tok = json_get_member(buf, err_tok, "message");
		if (msg_tok)
			bitcoin_plugin_error(bitcoind, buf, toks, method,
					     "%.*s", json_tok_full_len(msg_tok),
					     json_tok_full(buf, msg_tok));
		else
			bitcoin_plugin_error(bitcoind, buf, toks, method,
					     "%.*s", json_tok_full_len(err_tok),
					     json_tok_full(buf, err_tok));
	}

	result_tok = json_get_member(buf, toks, "result");
	if (!result_tok)
		bitcoin_plugin_error(bitcoind, buf, toks, method,
				     "missing 'result' field");

	return result_tok;
}

/* 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:
 * {
 *	"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, void *);
	void *cb_arg;
};

/* 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 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 = get_bitcoin_result(call->bitcoind, buf, toks, "estimatefees");

	/* 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 {
		bitcoin_plugin_error(call->bitcoind, buf, resulttok,
				     "estimatefees",
				     "missing feerate_floor field");
	}

	/* 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, call->cb_arg);
	tal_free(call);
}

void bitcoind_estimate_fees_(const tal_t *ctx,
			     struct bitcoind *bitcoind,
			     void (*cb)(struct lightningd *ld,
					u32 feerate_floor,
					const struct feerate_est *feerates,
					void *arg),
			     void *cb_arg)
{
	struct jsonrpc_request *req;
	struct estimatefee_call *call = tal(ctx, struct estimatefee_call);

	call->bitcoind = bitcoind;
	call->cb = cb;
	call->cb_arg = cb_arg;

	req = jsonrpc_request_start(call, "estimatefees", NULL,
				    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 jsmntok_t *resulttok;
	const char *err;
	const char *errmsg = NULL;
	bool success = false;

	resulttok = get_bitcoin_result(call->bitcoind, buf, toks, "sendrawtransaction");

	err = json_scan(tmpctx, buf, resulttok, "{success:%}",
			JSON_SCAN(json_to_bool, &success));
	if (err) {
		bitcoin_plugin_error(call->bitcoind, buf, resulttok,
				     "sendrawtransaction",
				     "bad 'result' field: %s", err);
	} else if (!success) {
		err = json_scan(tmpctx, buf, resulttok, "{errmsg:%}",
				JSON_SCAN_TAL(tmpctx, json_strdup, &errmsg));
		if (err)
			bitcoin_plugin_error(call->bitcoind, buf, resulttok,
					     "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,
				    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;
	u32 height;
	void (*cb)(struct bitcoind *bitcoind,
		   u32 height,
		   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 jsmntok_t *resulttok;
	const char *block_str, *err;
	struct bitcoin_blkid blkid;
	struct bitcoin_block *blk;
	const tal_t *ctx;
	trace_span_resume(call);
	trace_span_end(call);

	resulttok = get_bitcoin_result(call->bitcoind, buf, toks, "getrawblockbyheight");

	/* Callback may free parent of call, so steal onto context to
	 * free if it doesn't */
	ctx = tal(NULL, char);
	tal_steal(ctx, call);

	/* If block hash is `null`, this means not found! Call the callback
	 * with NULL values. */
	err = json_scan(tmpctx, buf, resulttok, "{blockhash:null}");
	if (!err) {
		call->cb(call->bitcoind, call->height, NULL, NULL, call->cb_arg);
		goto clean;
	}

	err = json_scan(tmpctx, buf, resulttok, "{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, resulttok,
				     "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, resulttok,
				     "getrawblockbyheight",
				     "bad block");

	call->cb(call->bitcoind, call->height, &blkid, blk, call->cb_arg);

clean:
	tal_free(ctx);
}

void bitcoind_getrawblockbyheight_(const tal_t *ctx,
				   struct bitcoind *bitcoind,
				   u32 height,
				   void (*cb)(struct bitcoind *bitcoind,
					      u32 blockheight,
					      struct bitcoin_blkid *blkid,
					      struct bitcoin_block *blk,
					      void *arg),
				   void *cb_arg)
{
	struct jsonrpc_request *req;
	struct getrawblockbyheight_call *call = tal(ctx,
						    struct getrawblockbyheight_call);

	call->bitcoind = bitcoind;
	call->cb = cb;
	call->cb_arg = cb_arg;
	call->height = height;

	trace_span_start("plugin/bitcoind", call);
	trace_span_tag(call, "method", "getrawblockbyheight");
	trace_span_suspend(call);
	req = jsonrpc_request_start(call, "getrawblockbyheight", NULL,
				    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;
	void (*cb)(struct bitcoind *bitcoind,
		   const char *chain,
		   u32 headercount,
		   u32 blockcount,
		   const bool ibd,
		   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;
	const char *err, *chain;
	u32 headers, blocks;
	bool ibd;

	resulttok = get_bitcoin_result(call->bitcoind, buf, toks, "getchaininfo");

	err = json_scan(tmpctx, buf, resulttok,
			"{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, resulttok, "getchaininfo",
				     "bad 'result' field: %s", err);

	call->cb(call->bitcoind, chain, headers, blocks, ibd,
		 call->cb_arg);

	tal_free(call);
}

void bitcoind_getchaininfo_(const tal_t *ctx,
			    struct bitcoind *bitcoind,
			    const u32 height,
			    void (*cb)(struct bitcoind *bitcoind,
				       const char *chain,
				       u32 headercount,
				       u32 blockcount,
				       const bool ibd,
				       void *),
			    void *cb_arg)
{
	struct jsonrpc_request *req;
	struct getchaininfo_call *call = tal(ctx, struct getchaininfo_call);

	call->bitcoind = bitcoind;
	call->cb = cb;
	call->cb_arg = cb_arg;

	req = jsonrpc_request_start(call, "getchaininfo", NULL,
				    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 jsmntok_t *resulttok;
	const char *err;
	struct bitcoin_tx_output txout;

	/* Whatever happens, we want to free this. */
	tal_steal(tmpctx, call);

	resulttok = get_bitcoin_result(call->bitcoind, buf, toks, "getutxout");

	err = json_scan(tmpctx, buf, resulttok, "{script:null}");
	if (!err) {
		call->cb(call->bitcoind, NULL, call->cb_arg);
		return;
	}

	err = json_scan(tmpctx, buf, resulttok, "{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, resulttok, "getutxout",
				     "bad 'result' field: %s", err);

	call->cb(call->bitcoind, &txout, call->cb_arg);
}

void bitcoind_getutxout_(const tal_t *ctx,
			 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(ctx, struct getutxout_call);

	call->bitcoind = bitcoind;
	call->cb = cb;
	call->cb_arg = cb_arg;

	req = jsonrpc_request_start(call, "getutxout", NULL,
				    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;
	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(call, 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,
					   u32 height,
					   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 struct wally_tx_output *output;
			output = &tx->wtx->outputs[j];

			if (!is_p2wsh(output->script, output->script_len, NULL))
				continue;

			struct amount_asset amount = wally_tx_output_get_amount(output);
			if (amount_asset_is_main(&amount)) {
				/* 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_dup_arr(o, u8, output->script, output->script_len, 0);
				tal_arr_expand(&call->outpoints, o);
			}
		}
	}

	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(call, 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);
			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, bitcoind, c->height,
					     process_getfilteredblock_step1, c);
	}
}

static void destroy_filteredblock_call(struct filteredblock_call *call)
{
	list_del(&call->list);
}

void bitcoind_getfilteredblock_(const tal_t *ctx,
				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(ctx, 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->result = NULL;
	call->current_outpoint = 0;

	list_add_tail(&bitcoind->pending_getfilteredblock, &call->list);
	tal_add_destructor(call, destroy_filteredblock_call);
	if (start)
		bitcoind_getrawblockbyheight(call, 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;
}
