refactor: rpc: Pass CBlockIndex by reference instead of pointer

All functions assume that the pointer is never null, so pass by
reference, to avoid accidental segfaults at runtime, or at least make
them more obvious.

Also, remove unused c-style casts in touched lines.

Also, add CHECK_NONFATAL checks, to turn segfault crashes into an
recoverable runtime error with debug information.
This commit is contained in:
MarcoFalke
2023-12-07 11:01:06 +01:00
parent fa604eb6cf
commit fa5989d514
6 changed files with 51 additions and 53 deletions

View File

@@ -73,13 +73,11 @@ static CUpdatedBlock latestblock GUARDED_BY(cs_blockchange);
/* Calculate the difficulty for a given block index.
*/
double GetDifficulty(const CBlockIndex* blockindex)
double GetDifficulty(const CBlockIndex& blockindex)
{
CHECK_NONFATAL(blockindex);
int nShift = (blockindex->nBits >> 24) & 0xff;
int nShift = (blockindex.nBits >> 24) & 0xff;
double dDiff =
(double)0x0000ffff / (double)(blockindex->nBits & 0x00ffffff);
(double)0x0000ffff / (double)(blockindex.nBits & 0x00ffffff);
while (nShift < 29)
{
@@ -95,14 +93,14 @@ double GetDifficulty(const CBlockIndex* blockindex)
return dDiff;
}
static int ComputeNextBlockAndDepth(const CBlockIndex* tip, const CBlockIndex* blockindex, const CBlockIndex*& next)
static int ComputeNextBlockAndDepth(const CBlockIndex& tip, const CBlockIndex& blockindex, const CBlockIndex*& next)
{
next = tip->GetAncestor(blockindex->nHeight + 1);
if (next && next->pprev == blockindex) {
return tip->nHeight - blockindex->nHeight + 1;
next = tip.GetAncestor(blockindex.nHeight + 1);
if (next && next->pprev == &blockindex) {
return tip.nHeight - blockindex.nHeight + 1;
}
next = nullptr;
return blockindex == tip ? 1 : -1;
return &blockindex == &tip ? 1 : -1;
}
static const CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateManager& chainman)
@@ -133,36 +131,36 @@ static const CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateMan
}
}
UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex)
UniValue blockheaderToJSON(const CBlockIndex& tip, const CBlockIndex& blockindex)
{
// Serialize passed information without accessing chain state of the active chain!
AssertLockNotHeld(cs_main); // For performance reasons
UniValue result(UniValue::VOBJ);
result.pushKV("hash", blockindex->GetBlockHash().GetHex());
result.pushKV("hash", blockindex.GetBlockHash().GetHex());
const CBlockIndex* pnext;
int confirmations = ComputeNextBlockAndDepth(tip, blockindex, pnext);
result.pushKV("confirmations", confirmations);
result.pushKV("height", blockindex->nHeight);
result.pushKV("version", blockindex->nVersion);
result.pushKV("versionHex", strprintf("%08x", blockindex->nVersion));
result.pushKV("merkleroot", blockindex->hashMerkleRoot.GetHex());
result.pushKV("time", (int64_t)blockindex->nTime);
result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast());
result.pushKV("nonce", (uint64_t)blockindex->nNonce);
result.pushKV("bits", strprintf("%08x", blockindex->nBits));
result.pushKV("height", blockindex.nHeight);
result.pushKV("version", blockindex.nVersion);
result.pushKV("versionHex", strprintf("%08x", blockindex.nVersion));
result.pushKV("merkleroot", blockindex.hashMerkleRoot.GetHex());
result.pushKV("time", blockindex.nTime);
result.pushKV("mediantime", blockindex.GetMedianTimePast());
result.pushKV("nonce", blockindex.nNonce);
result.pushKV("bits", strprintf("%08x", blockindex.nBits));
result.pushKV("difficulty", GetDifficulty(blockindex));
result.pushKV("chainwork", blockindex->nChainWork.GetHex());
result.pushKV("nTx", (uint64_t)blockindex->nTx);
result.pushKV("chainwork", blockindex.nChainWork.GetHex());
result.pushKV("nTx", blockindex.nTx);
if (blockindex->pprev)
result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex());
if (blockindex.pprev)
result.pushKV("previousblockhash", blockindex.pprev->GetBlockHash().GetHex());
if (pnext)
result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex());
return result;
}
UniValue blockToJSON(BlockManager& blockman, const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, TxVerbosity verbosity)
UniValue blockToJSON(BlockManager& blockman, const CBlock& block, const CBlockIndex& tip, const CBlockIndex& blockindex, TxVerbosity verbosity)
{
UniValue result = blockheaderToJSON(tip, blockindex);
@@ -181,8 +179,8 @@ UniValue blockToJSON(BlockManager& blockman, const CBlock& block, const CBlockIn
case TxVerbosity::SHOW_DETAILS:
case TxVerbosity::SHOW_DETAILS_AND_PREVOUT:
CBlockUndo blockUndo;
const bool is_not_pruned{WITH_LOCK(::cs_main, return !blockman.IsBlockPruned(*blockindex))};
const bool have_undo{is_not_pruned && blockman.UndoReadFromDisk(blockUndo, *blockindex)};
const bool is_not_pruned{WITH_LOCK(::cs_main, return !blockman.IsBlockPruned(blockindex))};
const bool have_undo{is_not_pruned && blockman.UndoReadFromDisk(blockUndo, blockindex)};
for (size_t i = 0; i < block.vtx.size(); ++i) {
const CTransactionRef& tx = block.vtx.at(i);
@@ -418,7 +416,7 @@ static RPCHelpMan getdifficulty()
{
ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
return GetDifficulty(chainman.ActiveChain().Tip());
return GetDifficulty(*CHECK_NONFATAL(chainman.ActiveChain().Tip()));
},
};
}
@@ -571,22 +569,22 @@ static RPCHelpMan getblockheader()
return strHex;
}
return blockheaderToJSON(tip, pblockindex);
return blockheaderToJSON(*tip, *pblockindex);
},
};
}
static CBlock GetBlockChecked(BlockManager& blockman, const CBlockIndex* pblockindex)
static CBlock GetBlockChecked(BlockManager& blockman, const CBlockIndex& blockindex)
{
CBlock block;
{
LOCK(cs_main);
if (blockman.IsBlockPruned(*pblockindex)) {
if (blockman.IsBlockPruned(blockindex)) {
throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)");
}
}
if (!blockman.ReadBlockFromDisk(block, *pblockindex)) {
if (!blockman.ReadBlockFromDisk(block, blockindex)) {
// Block not found on disk. This could be because we have the block
// header in our index but not yet have the block or did not accept the
// block. Or if the block was pruned right after we released the lock above.
@@ -596,21 +594,21 @@ static CBlock GetBlockChecked(BlockManager& blockman, const CBlockIndex* pblocki
return block;
}
static CBlockUndo GetUndoChecked(BlockManager& blockman, const CBlockIndex* pblockindex)
static CBlockUndo GetUndoChecked(BlockManager& blockman, const CBlockIndex& blockindex)
{
CBlockUndo blockUndo;
// The Genesis block does not have undo data
if (pblockindex->nHeight == 0) return blockUndo;
if (blockindex.nHeight == 0) return blockUndo;
{
LOCK(cs_main);
if (blockman.IsBlockPruned(*pblockindex)) {
if (blockman.IsBlockPruned(blockindex)) {
throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available (pruned data)");
}
}
if (!blockman.UndoReadFromDisk(blockUndo, *pblockindex)) {
if (!blockman.UndoReadFromDisk(blockUndo, blockindex)) {
throw JSONRPCError(RPC_MISC_ERROR, "Can't read undo data from disk");
}
@@ -736,7 +734,7 @@ static RPCHelpMan getblock()
}
}
const CBlock block{GetBlockChecked(chainman.m_blockman, pblockindex)};
const CBlock block{GetBlockChecked(chainman.m_blockman, *pblockindex)};
if (verbosity <= 0)
{
@@ -755,7 +753,7 @@ static RPCHelpMan getblock()
tx_verbosity = TxVerbosity::SHOW_DETAILS_AND_PREVOUT;
}
return blockToJSON(chainman.m_blockman, block, tip, pblockindex, tx_verbosity);
return blockToJSON(chainman.m_blockman, block, *tip, *pblockindex, tx_verbosity);
},
};
}
@@ -1257,7 +1255,7 @@ RPCHelpMan getblockchaininfo()
obj.pushKV("blocks", height);
obj.pushKV("headers", chainman.m_best_header ? chainman.m_best_header->nHeight : -1);
obj.pushKV("bestblockhash", tip.GetBlockHash().GetHex());
obj.pushKV("difficulty", GetDifficulty(&tip));
obj.pushKV("difficulty", GetDifficulty(tip));
obj.pushKV("time", tip.GetBlockTime());
obj.pushKV("mediantime", tip.GetMedianTimePast());
obj.pushKV("verificationprogress", GuessVerificationProgress(chainman.GetParams().TxData(), &tip));
@@ -1815,8 +1813,8 @@ static RPCHelpMan getblockstats()
}
}
const CBlock& block = GetBlockChecked(chainman.m_blockman, &pindex);
const CBlockUndo& blockUndo = GetUndoChecked(chainman.m_blockman, &pindex);
const CBlock& block = GetBlockChecked(chainman.m_blockman, pindex);
const CBlockUndo& blockUndo = GetUndoChecked(chainman.m_blockman, pindex);
const bool do_all = stats.size() == 0; // Calculate everything if nothing selected (default)
const bool do_mediantxsize = do_all || stats.count("mediantxsize") != 0;
@@ -2275,8 +2273,8 @@ public:
static bool CheckBlockFilterMatches(BlockManager& blockman, const CBlockIndex& blockindex, const GCSFilter::ElementSet& needles)
{
const CBlock block{GetBlockChecked(blockman, &blockindex)};
const CBlockUndo block_undo{GetUndoChecked(blockman, &blockindex)};
const CBlock block{GetBlockChecked(blockman, blockindex)};
const CBlockUndo block_undo{GetUndoChecked(blockman, blockindex)};
// Check if any of the outputs match the scriptPubKey
for (const auto& tx : block.vtx) {
@@ -2845,7 +2843,7 @@ return RPCHelpMan{
data.pushKV("blocks", (int)chain.Height());
data.pushKV("bestblockhash", tip->GetBlockHash().GetHex());
data.pushKV("difficulty", (double)GetDifficulty(tip));
data.pushKV("difficulty", GetDifficulty(*tip));
data.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip));
data.pushKV("coins_db_cache_bytes", cs.m_coinsdb_cache_size_bytes);
data.pushKV("coins_tip_cache_bytes", cs.m_coinstip_cache_size_bytes);