Browse Source

bitcoind: Add a multi-step getfilteredblock method

This will eventually replace the multi-step `getblockhash` + `getblock` +
`gettxout` mechanism, and return entire filtered blocks which can be added to
the DB, and represent the full set of P2WSH UTXOs.

Signed-off-by: Christian Decker <decker.christian@gmail.com>
pull/2938/head
Christian Decker 5 years ago
committed by Rusty Russell
parent
commit
f4e434d8e1
  1. 119
      lightningd/bitcoind.c
  2. 31
      lightningd/bitcoind.h

119
lightningd/bitcoind.c

@ -2,6 +2,7 @@
#include "bitcoin/base58.h"
#include "bitcoin/block.h"
#include "bitcoin/feerate.h"
#include "bitcoin/script.h"
#include "bitcoin/shadouble.h"
#include "bitcoind.h"
#include "lightningd.h"
@ -784,6 +785,124 @@ void bitcoind_gettxout(struct bitcoind *bitcoind,
NULL);
}
/* Context for the getfilteredblock call. Wraps the actual arguments while we
* process the various steps. */
struct filteredblock_call {
void (*cb)(struct bitcoind *bitcoind, struct filteredblock *fb,
void *arg);
void *arg;
struct filteredblock *result;
struct filteredblock_outpoint **outpoints;
size_t current_outpoint;
struct timeabs start_time;
};
static void
process_getfilteredblock_step3(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_gettxout(bitcoind, &o->txid, o->outnum,
process_getfilteredblock_step3, call);
} else {
/* If there were no more outpoints to check, we call the callback. */
call->cb(bitcoind, call->result, call->arg);
tal_free(call);
}
}
static void process_getfilteredblock_step2(struct bitcoind *bitcoind,
struct bitcoin_block *block,
struct filteredblock_call *call)
{
struct filteredblock_outpoint *o;
struct bitcoin_tx *tx;
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);
if (is_p2wsh(script, NULL)) {
/* This is an interesting output, remember it. */
o = tal(call->outpoints, struct filteredblock_outpoint);
bitcoin_txid(tx, &o->txid);
o->satoshis = bitcoin_tx_output_get_amount(tx, j);
o->txindex = i;
o->outnum = j;
o->scriptPubKey = tal_steal(o, script);
tal_arr_expand(&call->outpoints, o);
} else {
tal_free(script);
}
}
}
call->result->outpoints = tal_arr(call->result, struct filteredblock_outpoint *, 0);
call->current_outpoint = 0;
if (tal_count(call->outpoints) == 0) {
/* If there were no outpoints to check, we can short-circuit
* and just call the callback. */
call->cb(bitcoind, call->result, call->arg);
tal_free(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_gettxout(bitcoind, &o->txid, o->outnum,
process_getfilteredblock_step3, call);
}
}
static void process_getfilteredblock_step1(struct bitcoind *bitcoind,
const struct bitcoin_blkid *blkid,
struct filteredblock_call *call)
{
/* So we have the first piece of the puzzle, the block hash */
call->result->id = *blkid;
/* Now get the raw block to get all outpoints that were created in
* this block. */
bitcoind_getrawblock(bitcoind, blkid, process_getfilteredblock_step2, call);
}
void bitcoind_getfilteredblock_(struct bitcoind *bitcoind, u32 height,
void (*cb)(struct bitcoind *bitcoind,
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);
call->cb = cb;
call->arg = arg;
call->result = tal(call, struct filteredblock);
assert(call->cb != NULL);
call->start_time = time_now();
call->result->height = height;
bitcoind_getblockhash(bitcoind, height, process_getfilteredblock_step1, call);
}
static bool extract_numeric_version(struct bitcoin_cli *bcli,
const char *output, size_t output_bytes,
u64 *version)

31
lightningd/bitcoind.h

@ -67,6 +67,23 @@ struct bitcoind {
char *rpcuser, *rpcpass, *rpcconnect, *rpcport;
};
/* A single outpoint in a filtered block */
struct filteredblock_outpoint {
struct bitcoin_txid txid;
u32 outnum;
u32 txindex;
const u8 *scriptPubKey;
struct amount_sat satoshis;
};
/* A struct representing a block with most of the parts filtered out. */
struct filteredblock {
struct bitcoin_blkid id;
u32 height;
struct bitcoin_blkid prev_hash;
struct filteredblock_outpoint **outpoints;
};
struct bitcoind *new_bitcoind(const tal_t *ctx,
struct lightningd *ld,
struct log *log);
@ -132,6 +149,20 @@ void bitcoind_getblockhash_(struct bitcoind *bitcoind,
const struct bitcoin_blkid *), \
(arg))
void bitcoind_getfilteredblock_(struct bitcoind *bitcoind, u32 height,
void (*cb)(struct bitcoind *bitcoind,
struct filteredblock *fb,
void *arg),
void *arg);
#define bitcoind_getfilteredblock(bitcoind_, height, cb, arg) \
bitcoind_getfilteredblock_((bitcoind_), \
(height), \
typesafe_cb_preargs(void, void *, \
(cb), (arg), \
struct bitcoind *, \
const struct filteredblock *), \
(arg))
void bitcoind_getrawblock_(struct bitcoind *bitcoind,
const struct bitcoin_blkid *blockid,
void (*cb)(struct bitcoind *bitcoind,

Loading…
Cancel
Save