Skip to content

Commit 4a19750

Browse files
committed
rpc: Make pruneheight also reflect undo data presence
1 parent 96b4fac commit 4a19750

File tree

2 files changed

+33
-7
lines changed

2 files changed

+33
-7
lines changed

src/rpc/blockchain.cpp

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -786,16 +786,24 @@ static RPCHelpMan getblock()
786786
std::optional<int> GetPruneHeight(const BlockManager& blockman, const CChain& chain) {
787787
AssertLockHeld(::cs_main);
788788

789+
// Search for the last block missing block data or undo data. Don't let the
790+
// search consider the genesis block, because the genesis block does not
791+
// have undo data, but should not be considered pruned.
792+
const CBlockIndex* first_block{chain[1]};
789793
const CBlockIndex* chain_tip{chain.Tip()};
790-
if (!(chain_tip->nStatus & BLOCK_HAVE_DATA)) return chain_tip->nHeight;
791794

792-
// Get first block with data, after the last block without data.
793-
// This is the start of the unpruned range of blocks.
794-
const auto& first_unpruned{*Assert(blockman.GetFirstBlock(*chain_tip, /*status_mask=*/BLOCK_HAVE_DATA))};
795-
if (!first_unpruned.pprev) {
796-
// No block before the first unpruned block means nothing is pruned.
797-
return std::nullopt;
795+
// If there are no blocks after the genesis block, or no blocks at all, nothing is pruned.
796+
if (!first_block || !chain_tip) return std::nullopt;
797+
798+
// If the chain tip is pruned, everything is pruned.
799+
if (!((chain_tip->nStatus & BLOCK_HAVE_MASK) == BLOCK_HAVE_MASK)) return chain_tip->nHeight;
800+
801+
const auto& first_unpruned{*Assert(blockman.GetFirstBlock(*chain_tip, /*status_mask=*/BLOCK_HAVE_MASK, first_block))};
802+
if (&first_unpruned == first_block) {
803+
// All blocks between first_block and chain_tip have data, so nothing is pruned.
804+
return std::nullopt;
798805
}
806+
799807
// Block before the first unpruned block is the last pruned block.
800808
return Assert(first_unpruned.pprev)->nHeight;
801809
}

test/functional/feature_pruning.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
assert_equal,
2626
assert_greater_than,
2727
assert_raises_rpc_error,
28+
try_rpc,
2829
)
2930

3031
# Rescans start at the earliest block up to 2 hours before a key timestamp, so
@@ -479,8 +480,12 @@ def run_test(self):
479480
self.log.info("Test invalid pruning command line options")
480481
self.test_invalid_command_line_options()
481482

483+
self.log.info("Test scanblocks can not return pruned data")
482484
self.test_scanblocks_pruned()
483485

486+
self.log.info("Test pruneheight reflects the presence of block and undo data")
487+
self.test_pruneheight_undo_presence()
488+
484489
self.log.info("Done")
485490

486491
def test_scanblocks_pruned(self):
@@ -494,5 +499,18 @@ def test_scanblocks_pruned(self):
494499
assert_raises_rpc_error(-1, "Block not available (pruned data)", node.scanblocks,
495500
"start", [{"desc": f"raw({false_positive_spk.hex()})"}], 0, 0, "basic", {"filter_false_positives": True})
496501

502+
def test_pruneheight_undo_presence(self):
503+
node = self.nodes[2]
504+
pruneheight = node.getblockchaininfo()["pruneheight"]
505+
fetch_block = node.getblockhash(pruneheight - 1)
506+
507+
self.connect_nodes(1, 2)
508+
peers = node.getpeerinfo()
509+
node.getblockfrompeer(fetch_block, peers[0]["id"])
510+
self.wait_until(lambda: not try_rpc(-1, "Block not available (pruned data)", node.getblock, fetch_block), timeout=5)
511+
512+
new_pruneheight = node.getblockchaininfo()["pruneheight"]
513+
assert_equal(pruneheight, new_pruneheight)
514+
497515
if __name__ == '__main__':
498516
PruneTest().main()

0 commit comments

Comments
 (0)