Skip to content

Commit

Permalink
test: assumeutxo stale block CheckBlockIndex crash test
Browse files Browse the repository at this point in the history
Add a test for a CheckBlockIndex crash that would happen before previous
"assumeutxo: Get rid of faked nTx and nChainTx values" commit.

The crash was an assert failure in the (pindex->nChainTx == pindex->nTx +
prev_chain_tx) check that would previously happen if a snapshot was loaded, and
a block was submitted which forked from the chain before the snapshot block and
after the last downloaded background chain block. This block would not be
marked assumed-valid because it would not be an ancestor of the snapshot, and
it would have nTx set, nChainTx unset, and prev->nChainTx set with a fake
value, so the assert would fail. After the fix, prev->nChainTx is unset instead
of being set to a fake value, so the assert succeeds. This test was originally
posted by maflcko in
bitcoin#29261 (comment)

Co-authored-by: MarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz>
  • Loading branch information
ryanofsky and MarcoFalke committed Feb 8, 2024
1 parent b3d695f commit 83f7cdc
Showing 1 changed file with 17 additions and 0 deletions.
17 changes: 17 additions & 0 deletions test/functional/feature_assumeutxo.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,14 @@ def run_test(self):
hash = n0.getbestblockhash()
newblock = n0.getblock(hash, 0)
blocks[height] = Block(hash, block_tx, blocks[height-1].chain_tx + block_tx)
if i == 4:
# Create a stale block that forks off the main chain before the snapshot.
temp_invalid = n0.getbestblockhash()
n0.invalidateblock(temp_invalid)
stale_hash = self.generateblock(n0, output="raw(aaaa)", transactions=[], sync_fun=self.no_op)["hash"]
n0.invalidateblock(stale_hash)
n0.reconsiderblock(temp_invalid)
stale_block = n0.getblock(stale_hash, 0)

# make n1 aware of the new header, but don't give it the block.
n1.submitheader(newblock)
Expand Down Expand Up @@ -246,6 +254,15 @@ def check_tx_counts(final: bool) -> None:

assert_equal(n1.getblockchaininfo()["blocks"], SNAPSHOT_BASE_HEIGHT)

self.log.info("Submit a stale block that forked off the chain before the snapshot")
# Normally a block like this would not be downloaded, but if it is
# submitted early before the background chain catches up to the fork
# point, it winds up in m_blocks_unlinked and triggers a corner case
# that previously crashed CheckBlockIndex.
n1.submitblock(stale_block)
n1.getchaintips()
n1.getblock(stale_hash)

self.log.info("Submit a spending transaction for a snapshot chainstate coin to the mempool")
# spend the coinbase output of the first block that is not available on node1
spend_coin_blockhash = n1.getblockhash(START_HEIGHT + 1)
Expand Down

0 comments on commit 83f7cdc

Please sign in to comment.