Merge 27770 via rpc_getblockfileinfo-26+knots

This commit is contained in:
Luke Dashjr 2024-06-21 19:28:12 +00:00
commit c9fb60733c
5 changed files with 74 additions and 9 deletions

View File

@ -755,7 +755,7 @@ void BlockManager::CleanupBlockRevFiles() const
CBlockFileInfo* BlockManager::GetBlockFileInfo(size_t n)
{
LOCK(cs_LastBlockFile);
if (n > m_blockfile_info.size()-1) return nullptr;
return &m_blockfile_info.at(n);
}

View File

@ -3111,6 +3111,46 @@ return RPCHelpMan{
};
}
static RPCHelpMan getblockfileinfo()
{
return RPCHelpMan{
"getblockfileinfo",
"Retrieves information about a certain block file.",
{
{"file_number", RPCArg::Type::NUM, RPCArg::Optional::NO, "block file number"},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::NUM, "blocks_num", "the number of blocks stored in the file"},
{RPCResult::Type::NUM, "lowest_block", "the height of the lowest block inside the file"},
{RPCResult::Type::NUM, "highest_block", "the height of the highest block inside the file"},
{RPCResult::Type::NUM, "data_size", "the number of used bytes in the block file"},
{RPCResult::Type::NUM, "undo_size", "the number of used bytes in the undo file"},
}
},
RPCExamples{ HelpExampleCli("getblockfileinfo", "0") },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
NodeContext& node = EnsureAnyNodeContext(request.context);
int block_num = request.params[0].getInt<int>();
if (block_num < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block number");
CBlockFileInfo* info = node.chainman->m_blockman.GetBlockFileInfo(block_num);
if (!info) throw JSONRPCError(RPC_INVALID_PARAMETER, "block file not found");
UniValue result(UniValue::VOBJ);
result.pushKV("blocks_num", info->nBlocks);
result.pushKV("lowest_block", info->nHeightFirst);
result.pushKV("highest_block", info->nHeightLast);
result.pushKV("data_size", info->nSize);
result.pushKV("undo_size", info->nUndoSize);
return result;
}
};
}
static RPCHelpMan getblocklocations()
{
return RPCHelpMan{"getblocklocations",
@ -3211,6 +3251,7 @@ void RegisterBlockchainRPCCommands(CRPCTable& t)
{"blockchain", &dumptxoutset},
{"hidden", &loadtxoutset},
{"blockchain", &getchainstates},
{"hidden", &getblockfileinfo},
{"hidden", &invalidateblock},
{"hidden", &reconsiderblock},
{"hidden", &waitfornewblock},

View File

@ -258,6 +258,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "verifychain", 1, "nblocks" },
{ "getblockstats", 0, "hash_or_height" },
{ "getblockstats", 1, "stats" },
{ "getblockfileinfo", 0, "file_number" },
{ "setprunelock", 1, "lock_info" },
{ "pruneblockchain", 0, "height" },
{ "keypoolrefill", 0, "newsize" },

View File

@ -121,6 +121,7 @@ const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
"getblockfrompeer", // when no peers are connected, no p2p message is sent
"getblockhash",
"getblockheader",
"getblockfileinfo",
"getblocklocations",
"getblockstats",
"getblocktemplate",

View File

@ -135,10 +135,21 @@ class GetBlockFromPeerTest(BitcoinTestFramework):
# We need to generate more blocks to be able to prune
self.generate(self.nodes[0], 400, sync_fun=self.no_op)
self.sync_blocks([self.nodes[0], pruned_node])
pruneheight = pruned_node.pruneblockchain(300)
assert_equal(pruneheight, 248)
# The goal now will be to mimic the automatic pruning process and verify what happens when we fetch an historic
# block at any point of time.
#
# Starting with three blocks files. The pruning process will prune them one by one. And, at the second pruning
# event, the test will fetch the past block. Which will be stored at the latest block file. Which can only be
# pruned when the latest block file is full (in this case, the third one), and a new one is created.
# First prune event, prune first block file
highest_pruned_block_num = pruned_node.getblockfileinfo(0)["highest_block"]
pruneheight = pruned_node.pruneblockchain(highest_pruned_block_num + 1)
assert_equal(pruneheight, highest_pruned_block_num)
# Ensure the block is actually pruned
pruned_block = self.nodes[0].getblockhash(2)
fetch_block_num = 2
pruned_block = self.nodes[0].getblockhash(fetch_block_num)
assert_raises_rpc_error(-1, "Block not available (pruned data)", pruned_node.getblock, pruned_block)
self.log.info("Fetch pruned block")
@ -149,18 +160,29 @@ class GetBlockFromPeerTest(BitcoinTestFramework):
self.wait_until(lambda: self.check_for_block(node=2, hash=pruned_block), timeout=1)
assert_equal(result, {})
# Validate that the re-fetched block was stored at the last, current, block file
assert_equal(fetch_block_num, pruned_node.getblockfileinfo(2)["lowest_block"])
self.log.info("Fetched block persists after next pruning event")
self.generate(self.nodes[0], 250, sync_fun=self.no_op)
self.sync_blocks([self.nodes[0], pruned_node])
pruneheight += 251
assert_equal(pruned_node.pruneblockchain(700), pruneheight)
# Second prune event, prune second block file
highest_pruned_block_num = pruned_node.getblockfileinfo(1)["highest_block"]
pruneheight = pruned_node.pruneblockchain(highest_pruned_block_num + 1)
assert_equal(pruneheight, highest_pruned_block_num)
# As the re-fetched block is in the third file, and we just pruned the second one, 'getblock' must work.
assert_equal(pruned_node.getblock(pruned_block)["hash"], "36c56c5b5ebbaf90d76b0d1a074dcb32d42abab75b7ec6fa0ffd9b4fbce8f0f7")
self.log.info("Fetched block can be pruned again when prune height exceeds the height of the tip at the time when the block was fetched")
self.log.info("Re-fetched block can be pruned again when a new block file is created")
self.generate(self.nodes[0], 250, sync_fun=self.no_op)
self.sync_blocks([self.nodes[0], pruned_node])
pruneheight += 250
assert_equal(pruned_node.pruneblockchain(1000), pruneheight)
# Third prune event, prune third block file
highest_pruned_block_num = pruned_node.getblockfileinfo(2)["highest_block"]
pruneheight = pruned_node.pruneblockchain(highest_pruned_block_num + 1)
assert_equal(pruneheight, highest_pruned_block_num)
# and check that the re-fetched block file is now pruned
assert_raises_rpc_error(-1, "Block not available (pruned data)", pruned_node.getblock, pruned_block)