aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/index/base.cpp3
-rw-r--r--src/node/blockstorage.cpp1
-rw-r--r--src/node/blockstorage.h4
-rw-r--r--src/rpc/blockchain.cpp7
-rw-r--r--src/test/blockmanager_tests.cpp31
5 files changed, 40 insertions, 6 deletions
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 0f25881804..ef3ba72cad 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -115,7 +115,8 @@ bool BaseIndex::Init()
if (!start_block) {
// index is not built yet
// make sure we have all block data back to the genesis
- prune_violation = m_chainstate->m_blockman.GetFirstStoredBlock(*active_chain.Tip()) != active_chain.Genesis();
+ bool has_tip_data = active_chain.Tip()->nStatus & BLOCK_HAVE_DATA;
+ prune_violation = !has_tip_data || m_chainstate->m_blockman.GetFirstStoredBlock(*active_chain.Tip()) != active_chain.Genesis();
}
// in case the index has a best block set and is not fully synced
// check if we have the required blocks to continue building the index
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index 0e9ae0ae27..cd057dd708 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -407,6 +407,7 @@ const CBlockIndex* BlockManager::GetFirstStoredBlock(const CBlockIndex& start_bl
{
AssertLockHeld(::cs_main);
const CBlockIndex* last_block = &start_block;
+ assert(last_block->nStatus & BLOCK_HAVE_DATA);
while (last_block->pprev && (last_block->pprev->nStatus & BLOCK_HAVE_DATA)) {
last_block = last_block->pprev;
}
diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h
index 2c219e7a59..a963cad358 100644
--- a/src/node/blockstorage.h
+++ b/src/node/blockstorage.h
@@ -222,7 +222,9 @@ public:
//! Returns last CBlockIndex* that is a checkpoint
const CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- //! Find the first block that is not pruned
+ //! Find the first stored ancestor of start_block immediately after the last
+ //! pruned ancestor. Return value will never be null. Caller is responsible
+ //! for ensuring that start_block has data is not pruned.
const CBlockIndex* GetFirstStoredBlock(const CBlockIndex& start_block LIFETIMEBOUND) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
/** True if any block files have ever been pruned. */
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index ee3237638e..717a119b56 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -812,9 +812,7 @@ static RPCHelpMan pruneblockchain()
PruneBlockFilesManual(active_chainstate, height);
const CBlockIndex& block{*CHECK_NONFATAL(active_chain.Tip())};
- const CBlockIndex* last_block{active_chainstate.m_blockman.GetFirstStoredBlock(block)};
-
- return static_cast<int64_t>(last_block->nHeight - 1);
+ return block.nStatus & BLOCK_HAVE_DATA ? active_chainstate.m_blockman.GetFirstStoredBlock(block)->nHeight - 1 : block.nHeight;
},
};
}
@@ -1267,7 +1265,8 @@ RPCHelpMan getblockchaininfo()
obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage());
obj.pushKV("pruned", chainman.m_blockman.IsPruneMode());
if (chainman.m_blockman.IsPruneMode()) {
- obj.pushKV("pruneheight", chainman.m_blockman.GetFirstStoredBlock(tip)->nHeight);
+ bool has_tip_data = tip.nStatus & BLOCK_HAVE_DATA;
+ obj.pushKV("pruneheight", has_tip_data ? chainman.m_blockman.GetFirstStoredBlock(tip)->nHeight : tip.nHeight + 1);
const bool automatic_pruning{chainman.m_blockman.GetPruneTarget() != BlockManager::PRUNE_TARGET_MANUAL};
obj.pushKV("automatic_pruning", automatic_pruning);
diff --git a/src/test/blockmanager_tests.cpp b/src/test/blockmanager_tests.cpp
index 2ab2fa55f0..58ab49a329 100644
--- a/src/test/blockmanager_tests.cpp
+++ b/src/test/blockmanager_tests.cpp
@@ -90,4 +90,35 @@ BOOST_FIXTURE_TEST_CASE(blockmanager_scan_unlink_already_pruned_files, TestChain
BOOST_CHECK(!AutoFile(blockman.OpenBlockFile(new_pos, true)).IsNull());
}
+BOOST_FIXTURE_TEST_CASE(blockmanager_block_data_availability, TestChain100Setup)
+{
+ LOCK(::cs_main);
+ auto& chainman = m_node.chainman;
+ auto& blockman = chainman->m_blockman;
+ const CBlockIndex& tip = *chainman->ActiveTip();
+
+ // Function to prune all blocks from 'last_pruned_block' down to the genesis block
+ const auto& func_prune_blocks = [&](CBlockIndex* last_pruned_block)
+ {
+ LOCK(::cs_main);
+ CBlockIndex* it = last_pruned_block;
+ while (it != nullptr && it->nStatus & BLOCK_HAVE_DATA) {
+ it->nStatus &= ~BLOCK_HAVE_DATA;
+ it = it->pprev;
+ }
+ };
+
+ // 1) Return genesis block when all blocks are available
+ BOOST_CHECK_EQUAL(blockman.GetFirstStoredBlock(tip), chainman->ActiveChain()[0]);
+
+ // Prune half of the blocks
+ int height_to_prune = tip.nHeight / 2;
+ CBlockIndex* first_available_block = chainman->ActiveChain()[height_to_prune + 1];
+ CBlockIndex* last_pruned_block = first_available_block->pprev;
+ func_prune_blocks(last_pruned_block);
+
+ // 2) The last block not pruned is in-between upper-block and the genesis block
+ BOOST_CHECK_EQUAL(blockman.GetFirstStoredBlock(tip), first_available_block);
+}
+
BOOST_AUTO_TEST_SUITE_END()