mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-04-18 11:37:40 +02:00
Merge bitcoin/bitcoin#29668: prune, rpc: Check undo data when finding pruneheight
8789dc8f31doc: Add note to getblockfrompeer on missing undo data (Fabian Jahr)4a1975008brpc: Make pruneheight also reflect undo data presence (Fabian Jahr)96b4facc91refactor, blockstorage: Generalize GetFirstStoredBlock (Fabian Jahr) Pull request description: The function `GetFirstStoredBlock()` helps us find the first block for which we have data. So far this function only looked for a block with `BLOCK_HAVE_DATA`. However, this doesn't mean that we also have the undo data of that block, and undo data might be required for what a user would like to do with those blocks. One example of how this might happen is if some blocks were fetched using the `getblockfrompeer` RPC. Blocks fetched from a peer will have data but no undo data. The first commit here allows `GetFirstStoredBlock()` to check for undo data as well by passing a parameter. This alone is useful for #29553 and I would use it there. In the second commit I am applying the undo check to the RPCs that report `pruneheight` to the user. I find this much more intuitive because I think the user expects to be able to do all operations on blocks up until the `pruneheight` but that is not the case if undo data is missing. I personally ran into this once before and now again when testing for assumeutxo when I had used `getblockfrompeer`. The following commit adds test coverage for this change of behavior. The last commit adds a note in the docs of `getblockfrompeer` that undo data will not be available. ACKs for top commit: achow101: ACK8789dc8f31furszy: Code review ACK8789dc8f31. stickies-v: ACK8789dc8f31Tree-SHA512: 90ae8bdd07a496ade579aa25240609c61c9ed173ad38d30533f6c631fe674e5a41727478ade69ca4b71a571ad94c9da4b33ebba6b5d8821109313c2de3bdfb3d
This commit is contained in:
@@ -431,6 +431,7 @@ static RPCHelpMan getblockfrompeer()
|
||||
"getblockfrompeer",
|
||||
"Attempt to fetch block from a given peer.\n\n"
|
||||
"We must have the header for this block, e.g. using submitheader.\n"
|
||||
"The block will not have any undo data which can limit the usage of the block data in a context where the undo data is needed.\n"
|
||||
"Subsequent calls for the same block may cause the response from the previous peer to be ignored.\n"
|
||||
"Peers generally ignore requests for a stale block that they never fully verified, or one that is more than a month old.\n"
|
||||
"When a peer does not respond with a block, we will disconnect.\n"
|
||||
@@ -784,6 +785,32 @@ static RPCHelpMan getblock()
|
||||
};
|
||||
}
|
||||
|
||||
//! Return height of highest block that has been pruned, or std::nullopt if no blocks have been pruned
|
||||
std::optional<int> GetPruneHeight(const BlockManager& blockman, const CChain& chain) {
|
||||
AssertLockHeld(::cs_main);
|
||||
|
||||
// Search for the last block missing block data or undo data. Don't let the
|
||||
// search consider the genesis block, because the genesis block does not
|
||||
// have undo data, but should not be considered pruned.
|
||||
const CBlockIndex* first_block{chain[1]};
|
||||
const CBlockIndex* chain_tip{chain.Tip()};
|
||||
|
||||
// If there are no blocks after the genesis block, or no blocks at all, nothing is pruned.
|
||||
if (!first_block || !chain_tip) return std::nullopt;
|
||||
|
||||
// If the chain tip is pruned, everything is pruned.
|
||||
if (!((chain_tip->nStatus & BLOCK_HAVE_MASK) == BLOCK_HAVE_MASK)) return chain_tip->nHeight;
|
||||
|
||||
const auto& first_unpruned{*Assert(blockman.GetFirstBlock(*chain_tip, /*status_mask=*/BLOCK_HAVE_MASK, first_block))};
|
||||
if (&first_unpruned == first_block) {
|
||||
// All blocks between first_block and chain_tip have data, so nothing is pruned.
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Block before the first unpruned block is the last pruned block.
|
||||
return Assert(first_unpruned.pprev)->nHeight;
|
||||
}
|
||||
|
||||
static RPCHelpMan pruneblockchain()
|
||||
{
|
||||
return RPCHelpMan{"pruneblockchain", "",
|
||||
@@ -836,8 +863,7 @@ static RPCHelpMan pruneblockchain()
|
||||
}
|
||||
|
||||
PruneBlockFilesManual(active_chainstate, height);
|
||||
const CBlockIndex& block{*CHECK_NONFATAL(active_chain.Tip())};
|
||||
return block.nStatus & BLOCK_HAVE_DATA ? active_chainstate.m_blockman.GetFirstStoredBlock(block)->nHeight - 1 : block.nHeight;
|
||||
return GetPruneHeight(chainman.m_blockman, active_chain).value_or(-1);
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1297,8 +1323,8 @@ RPCHelpMan getblockchaininfo()
|
||||
obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage());
|
||||
obj.pushKV("pruned", chainman.m_blockman.IsPruneMode());
|
||||
if (chainman.m_blockman.IsPruneMode()) {
|
||||
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 auto prune_height{GetPruneHeight(chainman.m_blockman, active_chainstate.m_chain)};
|
||||
obj.pushKV("pruneheight", prune_height ? prune_height.value() + 1 : 0);
|
||||
|
||||
const bool automatic_pruning{chainman.m_blockman.GetPruneTarget() != BlockManager::PRUNE_TARGET_MANUAL};
|
||||
obj.pushKV("automatic_pruning", automatic_pruning);
|
||||
|
||||
Reference in New Issue
Block a user