This assertion in CheckBlockIndex() can fail upon startup of a pruned node.
<details> <summary>Functional test to reproduce</summary>
"""Trigger startup m_blocks_unlinked inconsistency on pruned side-chain blocks.
Reproduces a bug where BlockManager::LoadBlockIndex inserts a block into
m_blocks_unlinked based on nTx > 0 without checking BLOCK_HAVE_DATA.
When a block on a stale fork has been pruned (BLOCK_HAVE_DATA cleared, nTx still > 0)
and its parent was header-only (nTx == 0 => m_chain_tx_count == 0), the pruned block
is incorrectly re-added to m_blocks_unlinked on restart, violating the invariant asserted
by CheckBlockIndex:
"Can't be in m_blocks_unlinked if we don't HAVE_DATA"
"""
from test_framework.blocktools import (
create_block,
create_coinbase,
)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
class FeaturePruneUnlinkedBugTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.extra_args = [["-prune=1", "-fastprune"]]
def run_test(self):
node = self.nodes[0]
self.log.info("Create a 2-block side fork: header-only parent, data-only child")
fork_point = node.getblockhash(1)
tip_time = node.getblock(node.getbestblockhash())["time"] + 1
side_parent = create_block(int(fork_point, 16), create_coinbase(2), tip_time)
side_parent.solve()
side_child = create_block(side_parent.hash_int, create_coinbase(3), tip_time + 1)
side_child.solve()
node.submitheader(side_parent.serialize().hex())
node.submitblock(side_child.serialize().hex())
assert_equal(node.getblockheader(side_parent.hash_hex)["nTx"], 0)
assert_equal(node.getblockheader(side_child.hash_hex)["nTx"], 1)
self.log.info("Advance chain and prune so the side child loses BLOCK_HAVE_DATA")
self.generate(node, 500)
node.pruneblockchain(node.getblockcount() - 100)
self.log.info("Restart and mine one block to trigger CheckBlockIndex assertion")
self.restart_node(0)
self.generate(node, 1)
if __name__ == "__main__":
FeaturePruneUnlinkedBugTest(__file__).main()
</details>
This was found with a test running on Antithesis.