bitcoind: Defer initialization of filteredblock_call->result

During sync it is highly likely that we can coalesce multiple calls and share
results among them. We also report back failures for non-existing blocks early
on, so we don't run into issues with blocks that our bitcoind doesn't have
yet.

Signed-off-by: Christian Decker <decker.christian@gmail.com>
This commit is contained in:
Christian Decker 2019-08-19 19:49:09 +02:00 committed by Rusty Russell
parent 187e493ab8
commit 8b8538024d
3 changed files with 31 additions and 7 deletions

View File

@ -795,6 +795,7 @@ struct filteredblock_call {
struct filteredblock_outpoint **outpoints; struct filteredblock_outpoint **outpoints;
size_t current_outpoint; size_t current_outpoint;
struct timeabs start_time; struct timeabs start_time;
u32 height;
}; };
/* Declaration for recursion in process_getfilteredblock_step1 */ /* Declaration for recursion in process_getfilteredblock_step1 */
@ -831,6 +832,12 @@ static void process_getfilteredblock_step2(struct bitcoind *bitcoind,
{ {
struct filteredblock_outpoint *o; struct filteredblock_outpoint *o;
struct bitcoin_tx *tx; struct bitcoin_tx *tx;
/* If for some reason we couldn't get the block, just report a
* failure. */
if (block == NULL)
return process_getfiltered_block_final(bitcoind, call);
call->result->prev_hash = block->hdr.prev_hash; call->result->prev_hash = block->hdr.prev_hash;
/* Allocate an array containing all the potentially interesting /* Allocate an array containing all the potentially interesting
@ -876,7 +883,16 @@ static void process_getfilteredblock_step1(struct bitcoind *bitcoind,
const struct bitcoin_blkid *blkid, const struct bitcoin_blkid *blkid,
struct filteredblock_call *call) struct filteredblock_call *call)
{ {
/* 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 */ /* 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; call->result->id = *blkid;
/* Now get the raw block to get all outpoints that were created in /* Now get the raw block to get all outpoints that were created in
@ -891,11 +907,15 @@ process_getfiltered_block_final(struct bitcoind *bitcoind,
const struct filteredblock_call *call) const struct filteredblock_call *call)
{ {
struct filteredblock_call *c, *next; struct filteredblock_call *c, *next;
u32 height = call->result->height; 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. */ /* Need to steal so we don't accidentally free it while iterating through the list below. */
struct filteredblock *fb = tal_steal(NULL, call->result); struct filteredblock *fb = tal_steal(NULL, call->result);
list_for_each_safe(&bitcoind->pending_getfilteredblock, c, next, list) { list_for_each_safe(&bitcoind->pending_getfilteredblock, c, next, list) {
if (c->result->height == height) { if (c->height == height) {
c->cb(bitcoind, fb, c->arg); c->cb(bitcoind, fb, c->arg);
list_del(&c->list); list_del(&c->list);
tal_free(c); tal_free(c);
@ -903,12 +923,13 @@ process_getfiltered_block_final(struct bitcoind *bitcoind,
} }
tal_free(fb); tal_free(fb);
next:
/* Nothing to free here, since `*call` was already deleted during the /* Nothing to free here, since `*call` was already deleted during the
* iteration above. It was also removed from the list, so no need to * iteration above. It was also removed from the list, so no need to
* pop here. */ * pop here. */
if (!list_empty(&bitcoind->pending_getfilteredblock)) { if (!list_empty(&bitcoind->pending_getfilteredblock)) {
c = list_top(&bitcoind->pending_getfilteredblock, struct filteredblock_call, list); c = list_top(&bitcoind->pending_getfilteredblock, struct filteredblock_call, list);
bitcoind_getblockhash(bitcoind, c->result->height, process_getfilteredblock_step1, c); bitcoind_getblockhash(bitcoind, c->height, process_getfilteredblock_step1, c);
} }
} }
@ -925,11 +946,10 @@ void bitcoind_getfilteredblock_(struct bitcoind *bitcoind, u32 height,
bool start = list_empty(&bitcoind->pending_getfilteredblock); bool start = list_empty(&bitcoind->pending_getfilteredblock);
call->cb = cb; call->cb = cb;
call->arg = arg; call->arg = arg;
call->result = tal(call, struct filteredblock); call->height = height;
assert(call->cb != NULL); assert(call->cb != NULL);
call->start_time = time_now(); call->start_time = time_now();
call->result->height = height; call->result = NULL;
call->result->outpoints = tal_arr(call->result, struct filteredblock_outpoint *, 0);
call->current_outpoint = 0; call->current_outpoint = 0;
list_add_tail(&bitcoind->pending_getfilteredblock, &call->list); list_add_tail(&bitcoind->pending_getfilteredblock, &call->list);

View File

@ -66,6 +66,11 @@ static void got_filteredblock(struct bitcoind *bitcoind,
struct filteredblock_outpoint *fbo = NULL, *o; struct filteredblock_outpoint *fbo = NULL, *o;
struct bitcoin_tx_output txo; struct bitcoin_tx_output txo;
/* If we failed to the filtered block we report the failure to
* got_txout. */
if (fb == NULL)
return got_txout(bitcoind, NULL, scid);
/* Only fill in blocks that we are not going to scan later. */ /* Only fill in blocks that we are not going to scan later. */
if (bitcoind->ld->topology->max_blockheight > fb->height) if (bitcoind->ld->topology->max_blockheight > fb->height)
wallet_filteredblock_add(bitcoind->ld->wallet, fb); wallet_filteredblock_add(bitcoind->ld->wallet, fb);

View File

@ -1319,7 +1319,6 @@ def test_gossip_store_compact_on_load(node_factory, bitcoind):
wait_for(lambda: l2.daemon.is_in_log(r'gossip_store: Read 1/4/2/0 cannounce/cupdate/nannounce/cdelete from store \(0 deleted\) in 1446 bytes')) wait_for(lambda: l2.daemon.is_in_log(r'gossip_store: Read 1/4/2/0 cannounce/cupdate/nannounce/cdelete from store \(0 deleted\) in 1446 bytes'))
@pytest.mark.xfail(strict=True)
def test_gossip_announce_invalid_block(node_factory, bitcoind): def test_gossip_announce_invalid_block(node_factory, bitcoind):
"""bitcoind lags and we might get an announcement for a block we don't have. """bitcoind lags and we might get an announcement for a block we don't have.