diff --git a/src/rest.cpp b/src/rest.cpp --- a/src/rest.cpp +++ b/src/rest.cpp @@ -159,10 +159,12 @@ return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr); } + const CBlockIndex *tip = nullptr; std::vector headers; headers.reserve(count); { LOCK(cs_main); + tip = chainActive.Tip(); const CBlockIndex *pindex = LookupBlockIndex(hash); while (pindex != nullptr && chainActive.Contains(pindex)) { headers.push_back(pindex); @@ -195,11 +197,9 @@ } case RetFormat::JSON: { UniValue jsonHeaders(UniValue::VARR); - { - LOCK(cs_main); - for (const CBlockIndex *pindex : headers) { - jsonHeaders.push_back(blockheaderToJSON(pindex)); - } + + for (const CBlockIndex *pindex : headers) { + jsonHeaders.push_back(blockheaderToJSON(tip, pindex)); } std::string strJSON = jsonHeaders.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); @@ -233,8 +233,10 @@ CBlock block; CBlockIndex *pblockindex = nullptr; + CBlockIndex *tip = nullptr; { LOCK(cs_main); + tip = chainActive.Tip(); pblockindex = LookupBlockIndex(hash); if (!pblockindex) { return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found"); @@ -271,12 +273,9 @@ } case RetFormat::JSON: { - UniValue objBlock; - { - LOCK(cs_main); - objBlock = - blockToJSON(config, block, pblockindex, showTxDetails); - } + UniValue objBlock = + blockToJSON(config, block, tip, pblockindex, showTxDetails); + std::string strJSON = objBlock.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); req->WriteReply(HTTP_OK, strJSON); diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h --- a/src/rpc/blockchain.h +++ b/src/rpc/blockchain.h @@ -5,8 +5,15 @@ #ifndef BITCOIN_RPCBLOCKCHAIN_H #define BITCOIN_RPCBLOCKCHAIN_H +#include +#include #include +#include +#include + +extern CCriticalSection cs_main; + class CBlock; class CBlockIndex; class Config; @@ -17,8 +24,11 @@ double GetDifficulty(const CBlockIndex *blockindex); UniValue blockToJSON(const Config &config, const CBlock &block, - const CBlockIndex *blockindex, bool txDetails = false); + const CBlockIndex *tip, const CBlockIndex *blockindex, + bool txDetails = false) LOCKS_EXCLUDED(cs_main); -UniValue blockheaderToJSON(const CBlockIndex *blockindex); +UniValue blockheaderToJSON(const CBlockIndex *tip, + const CBlockIndex *blockindex) + LOCKS_EXCLUDED(cs_main); #endif // BITCOIN_RPCBLOCKCHAIN_H diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -60,24 +60,31 @@ } double GetDifficulty(const CBlockIndex *blockindex) { - // Floating point number that is a multiple of the minimum difficulty, - // minimum difficulty = 1.0. - if (blockindex == nullptr) { - return 1.0; - } + assert(blockindex); return GetDifficultyFromBits(blockindex->nBits); } -UniValue blockheaderToJSON(const CBlockIndex *blockindex) { - AssertLockHeld(cs_main); +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 = nullptr; + return blockindex == tip ? 1 : -1; +} + +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()); - int confirmations = -1; - // Only report confirmations if the block is on the main chain - if (chainActive.Contains(blockindex)) { - confirmations = chainActive.Height() - blockindex->nHeight + 1; - } + const CBlockIndex *pnext; + int confirmations = ComputeNextBlockAndDepth(tip, blockindex, pnext); result.pushKV("confirmations", confirmations); result.pushKV("height", blockindex->nHeight); result.pushKV("version", blockindex->nVersion); @@ -94,7 +101,6 @@ result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()); } - CBlockIndex *pnext = chainActive.Next(blockindex); if (pnext) { result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex()); } @@ -102,15 +108,15 @@ } UniValue blockToJSON(const Config &config, const CBlock &block, - const CBlockIndex *blockindex, bool txDetails) { - AssertLockHeld(cs_main); + const CBlockIndex *tip, const CBlockIndex *blockindex, + bool txDetails) { + // 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()); - int confirmations = -1; - // Only report confirmations if the block is on the main chain - if (chainActive.Contains(blockindex)) { - confirmations = chainActive.Height() - blockindex->nHeight + 1; - } + const CBlockIndex *pnext; + int confirmations = ComputeNextBlockAndDepth(tip, blockindex, pnext); result.pushKV("confirmations", confirmations); result.pushKV( "size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION)); @@ -140,7 +146,6 @@ result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()); } - CBlockIndex *pnext = chainActive.Next(blockindex); if (pnext) { result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex()); } @@ -801,7 +806,7 @@ return strHex; } - return blockheaderToJSON(pblockindex); + return blockheaderToJSON(chainActive.Tip(), pblockindex); } static UniValue getblock(const Config &config, const JSONRPCRequest &request) { @@ -870,8 +875,6 @@ "214adbda81d7e2a3dd146f6ed09\"")); } - LOCK(cs_main); - std::string strHash = request.params[0].get_str(); uint256 hash(uint256S(strHash)); @@ -884,23 +887,32 @@ } } - const CBlockIndex *pblockindex = LookupBlockIndex(hash); - if (!pblockindex) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - } - CBlock block; - if (fHavePruned && !pblockindex->nStatus.hasData() && - pblockindex->nTx > 0) { - throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)"); - } + const CBlockIndex *pblockindex; + const CBlockIndex *tip; + { + LOCK(cs_main); + pblockindex = LookupBlockIndex(hash); + tip = chainActive.Tip(); + + if (!pblockindex) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + } - if (!ReadBlockFromDisk(block, pblockindex, config)) { - // Block not found on disk. This could be because we have the block - // header in our index but don't have the block (for example if a - // non-whitelisted node sends us an unrequested long chain of valid - // blocks, we add the headers to our index, but don't accept the block). - throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk"); + if (fHavePruned && !pblockindex->nStatus.hasData() && + pblockindex->nTx > 0) { + throw JSONRPCError(RPC_MISC_ERROR, + "Block not available (pruned data)"); + } + + if (!ReadBlockFromDisk(block, pblockindex, config)) { + // Block not found on disk. This could be because we have the block + // header in our index but don't have the block (for example if a + // non-whitelisted node sends us an unrequested long chain of valid + // blocks, we add the headers to our index, but don't accept the + // block). + throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk"); + } } if (verbosity <= 0) { @@ -911,7 +923,7 @@ return strHex; } - return blockToJSON(config, block, pblockindex, verbosity >= 2); + return blockToJSON(config, block, tip, pblockindex, verbosity >= 2); } struct CCoinsStats { @@ -1222,7 +1234,7 @@ } /** Implementation of IsSuperMajority with better feedback */ -static UniValue SoftForkMajorityDesc(int version, CBlockIndex *pindex, +static UniValue SoftForkMajorityDesc(int version, const CBlockIndex *pindex, const Consensus::Params &consensusParams) { UniValue rv(UniValue::VOBJ); bool activated = false; @@ -1245,7 +1257,7 @@ } static UniValue SoftForkDesc(const std::string &name, int version, - CBlockIndex *pindex, + const CBlockIndex *pindex, const Consensus::Params &consensusParams) { UniValue rv(UniValue::VOBJ); rv.pushKV("id", name); @@ -1311,22 +1323,23 @@ LOCK(cs_main); + const CBlockIndex *tip = chainActive.Tip(); UniValue obj(UniValue::VOBJ); obj.pushKV("chain", config.GetChainParams().NetworkIDString()); obj.pushKV("blocks", int(chainActive.Height())); obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1); - obj.pushKV("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex()); - obj.pushKV("difficulty", double(GetDifficulty(chainActive.Tip()))); - obj.pushKV("mediantime", int64_t(chainActive.Tip()->GetMedianTimePast())); - obj.pushKV("verificationprogress", - GuessVerificationProgress(config.GetChainParams().TxData(), - chainActive.Tip())); - obj.pushKV("chainwork", chainActive.Tip()->nChainWork.GetHex()); + obj.pushKV("bestblockhash", tip->GetBlockHash().GetHex()); + obj.pushKV("difficulty", double(GetDifficulty(tip))); + obj.pushKV("mediantime", int64_t(tip->GetMedianTimePast())); + obj.pushKV( + "verificationprogress", + GuessVerificationProgress(config.GetChainParams().TxData(), tip)); + obj.pushKV("chainwork", tip->nChainWork.GetHex()); obj.pushKV("size_on_disk", CalculateCurrentUsage()); obj.pushKV("pruned", fPruneMode); if (fPruneMode) { - CBlockIndex *block = chainActive.Tip(); + const CBlockIndex *block = tip; assert(block); while (block->pprev && (block->pprev->nStatus.hasData())) { block = block->pprev; @@ -1344,7 +1357,6 @@ const Consensus::Params &consensusParams = config.GetChainParams().GetConsensus(); - CBlockIndex *tip = chainActive.Tip(); UniValue softforks(UniValue::VARR); softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams));