Merge #12151: rpc: Remove cs_main lock from blockToJSON and blockheaderToJSON
b9f226b41f
rpc: Remove cs_main lock from blockToJSON and blockHeaderToJSON (João Barbosa)343b98cbcd
rpc: Specify chain tip instead of chain in GetDifficulty (João Barbosa)54dc13b6a2
rpc: Fix SoftForkMajorityDesc and SoftForkDesc signatures (João Barbosa) Pull request description: Motivated by https://github.com/bitcoin/bitcoin/pull/11913#discussion_r157798157, this pull makes `blockToJSON` and `blockheaderToJSON` free of `cs_main` locks. Locking `cs_main` was required to access `chainActive` in order to check if the block was in the chain and to retrieve the next block index. With the this approach, `CBlockIndex::GetAncestor()` is used in a way to check if the block belongs to the specified chain tip and, at the same time, get the next block index. Tree-SHA512: a6720ace0182c19033bbed1a404f729d793574db8ab16e0966ffe412145611e32c30aaab02975d225df6d439d7b9ef2070e732b16137a902b0293c8cddfeb85f
This commit is contained in:
commit
f7e182a973
4 changed files with 37 additions and 48 deletions
17
src/rest.cpp
17
src/rest.cpp
|
@ -136,10 +136,12 @@ static bool rest_headers(HTTPRequest* req,
|
||||||
if (!ParseHashStr(hashStr, hash))
|
if (!ParseHashStr(hashStr, hash))
|
||||||
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
|
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
|
||||||
|
|
||||||
|
const CBlockIndex* tip = nullptr;
|
||||||
std::vector<const CBlockIndex *> headers;
|
std::vector<const CBlockIndex *> headers;
|
||||||
headers.reserve(count);
|
headers.reserve(count);
|
||||||
{
|
{
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
|
tip = chainActive.Tip();
|
||||||
const CBlockIndex* pindex = LookupBlockIndex(hash);
|
const CBlockIndex* pindex = LookupBlockIndex(hash);
|
||||||
while (pindex != nullptr && chainActive.Contains(pindex)) {
|
while (pindex != nullptr && chainActive.Contains(pindex)) {
|
||||||
headers.push_back(pindex);
|
headers.push_back(pindex);
|
||||||
|
@ -175,11 +177,8 @@ static bool rest_headers(HTTPRequest* req,
|
||||||
}
|
}
|
||||||
case RetFormat::JSON: {
|
case RetFormat::JSON: {
|
||||||
UniValue jsonHeaders(UniValue::VARR);
|
UniValue jsonHeaders(UniValue::VARR);
|
||||||
{
|
for (const CBlockIndex *pindex : headers) {
|
||||||
LOCK(cs_main);
|
jsonHeaders.push_back(blockheaderToJSON(tip, pindex));
|
||||||
for (const CBlockIndex *pindex : headers) {
|
|
||||||
jsonHeaders.push_back(blockheaderToJSON(pindex));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
std::string strJSON = jsonHeaders.write() + "\n";
|
std::string strJSON = jsonHeaders.write() + "\n";
|
||||||
req->WriteHeader("Content-Type", "application/json");
|
req->WriteHeader("Content-Type", "application/json");
|
||||||
|
@ -207,8 +206,10 @@ static bool rest_block(HTTPRequest* req,
|
||||||
|
|
||||||
CBlock block;
|
CBlock block;
|
||||||
CBlockIndex* pblockindex = nullptr;
|
CBlockIndex* pblockindex = nullptr;
|
||||||
|
CBlockIndex* tip = nullptr;
|
||||||
{
|
{
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
|
tip = chainActive.Tip();
|
||||||
pblockindex = LookupBlockIndex(hash);
|
pblockindex = LookupBlockIndex(hash);
|
||||||
if (!pblockindex) {
|
if (!pblockindex) {
|
||||||
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
|
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
|
||||||
|
@ -241,11 +242,7 @@ static bool rest_block(HTTPRequest* req,
|
||||||
}
|
}
|
||||||
|
|
||||||
case RetFormat::JSON: {
|
case RetFormat::JSON: {
|
||||||
UniValue objBlock;
|
UniValue objBlock = blockToJSON(block, tip, pblockindex, showTxDetails);
|
||||||
{
|
|
||||||
LOCK(cs_main);
|
|
||||||
objBlock = blockToJSON(block, pblockindex, showTxDetails);
|
|
||||||
}
|
|
||||||
std::string strJSON = objBlock.write() + "\n";
|
std::string strJSON = objBlock.write() + "\n";
|
||||||
req->WriteHeader("Content-Type", "application/json");
|
req->WriteHeader("Content-Type", "application/json");
|
||||||
req->WriteReply(HTTP_OK, strJSON);
|
req->WriteReply(HTTP_OK, strJSON);
|
||||||
|
|
|
@ -59,10 +59,7 @@ static CUpdatedBlock latestblock;
|
||||||
*/
|
*/
|
||||||
double GetDifficulty(const CBlockIndex* blockindex)
|
double GetDifficulty(const CBlockIndex* blockindex)
|
||||||
{
|
{
|
||||||
if (blockindex == nullptr)
|
assert(blockindex);
|
||||||
{
|
|
||||||
return 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nShift = (blockindex->nBits >> 24) & 0xff;
|
int nShift = (blockindex->nBits >> 24) & 0xff;
|
||||||
double dDiff =
|
double dDiff =
|
||||||
|
@ -82,15 +79,22 @@ double GetDifficulty(const CBlockIndex* blockindex)
|
||||||
return dDiff;
|
return dDiff;
|
||||||
}
|
}
|
||||||
|
|
||||||
UniValue blockheaderToJSON(const CBlockIndex* blockindex)
|
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)
|
||||||
{
|
{
|
||||||
AssertLockHeld(cs_main);
|
|
||||||
UniValue result(UniValue::VOBJ);
|
UniValue result(UniValue::VOBJ);
|
||||||
result.pushKV("hash", blockindex->GetBlockHash().GetHex());
|
result.pushKV("hash", blockindex->GetBlockHash().GetHex());
|
||||||
int confirmations = -1;
|
const CBlockIndex* pnext;
|
||||||
// Only report confirmations if the block is on the main chain
|
int confirmations = ComputeNextBlockAndDepth(tip, blockindex, pnext);
|
||||||
if (chainActive.Contains(blockindex))
|
|
||||||
confirmations = chainActive.Height() - blockindex->nHeight + 1;
|
|
||||||
result.pushKV("confirmations", confirmations);
|
result.pushKV("confirmations", confirmations);
|
||||||
result.pushKV("height", blockindex->nHeight);
|
result.pushKV("height", blockindex->nHeight);
|
||||||
result.pushKV("version", blockindex->nVersion);
|
result.pushKV("version", blockindex->nVersion);
|
||||||
|
@ -106,21 +110,17 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex)
|
||||||
|
|
||||||
if (blockindex->pprev)
|
if (blockindex->pprev)
|
||||||
result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex());
|
result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex());
|
||||||
CBlockIndex *pnext = chainActive.Next(blockindex);
|
|
||||||
if (pnext)
|
if (pnext)
|
||||||
result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex());
|
result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails)
|
UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, bool txDetails)
|
||||||
{
|
{
|
||||||
AssertLockHeld(cs_main);
|
|
||||||
UniValue result(UniValue::VOBJ);
|
UniValue result(UniValue::VOBJ);
|
||||||
result.pushKV("hash", blockindex->GetBlockHash().GetHex());
|
result.pushKV("hash", blockindex->GetBlockHash().GetHex());
|
||||||
int confirmations = -1;
|
const CBlockIndex* pnext;
|
||||||
// Only report confirmations if the block is on the main chain
|
int confirmations = ComputeNextBlockAndDepth(tip, blockindex, pnext);
|
||||||
if (chainActive.Contains(blockindex))
|
|
||||||
confirmations = chainActive.Height() - blockindex->nHeight + 1;
|
|
||||||
result.pushKV("confirmations", confirmations);
|
result.pushKV("confirmations", confirmations);
|
||||||
result.pushKV("strippedsize", (int)::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS));
|
result.pushKV("strippedsize", (int)::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS));
|
||||||
result.pushKV("size", (int)::GetSerializeSize(block, PROTOCOL_VERSION));
|
result.pushKV("size", (int)::GetSerializeSize(block, PROTOCOL_VERSION));
|
||||||
|
@ -152,7 +152,6 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
|
||||||
|
|
||||||
if (blockindex->pprev)
|
if (blockindex->pprev)
|
||||||
result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex());
|
result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex());
|
||||||
CBlockIndex *pnext = chainActive.Next(blockindex);
|
|
||||||
if (pnext)
|
if (pnext)
|
||||||
result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex());
|
result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex());
|
||||||
return result;
|
return result;
|
||||||
|
@ -769,7 +768,7 @@ static UniValue getblockheader(const JSONRPCRequest& request)
|
||||||
return strHex;
|
return strHex;
|
||||||
}
|
}
|
||||||
|
|
||||||
return blockheaderToJSON(pblockindex);
|
return blockheaderToJSON(chainActive.Tip(), pblockindex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
|
static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
|
||||||
|
@ -871,7 +870,7 @@ static UniValue getblock(const JSONRPCRequest& request)
|
||||||
return strHex;
|
return strHex;
|
||||||
}
|
}
|
||||||
|
|
||||||
return blockToJSON(block, pblockindex, verbosity >= 2);
|
return blockToJSON(block, chainActive.Tip(), pblockindex, verbosity >= 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CCoinsStats
|
struct CCoinsStats
|
||||||
|
@ -1150,7 +1149,7 @@ static UniValue verifychain(const JSONRPCRequest& request)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Implementation of IsSuperMajority with better feedback */
|
/** Implementation of IsSuperMajority with better feedback */
|
||||||
static UniValue SoftForkMajorityDesc(int version, CBlockIndex* pindex, const Consensus::Params& consensusParams)
|
static UniValue SoftForkMajorityDesc(int version, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
|
||||||
{
|
{
|
||||||
UniValue rv(UniValue::VOBJ);
|
UniValue rv(UniValue::VOBJ);
|
||||||
bool activated = false;
|
bool activated = false;
|
||||||
|
@ -1170,7 +1169,7 @@ static UniValue SoftForkMajorityDesc(int version, CBlockIndex* pindex, const Con
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
static UniValue SoftForkDesc(const std::string &name, int version, CBlockIndex* pindex, const Consensus::Params& consensusParams)
|
static UniValue SoftForkDesc(const std::string &name, int version, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
|
||||||
{
|
{
|
||||||
UniValue rv(UniValue::VOBJ);
|
UniValue rv(UniValue::VOBJ);
|
||||||
rv.pushKV("id", name);
|
rv.pushKV("id", name);
|
||||||
|
@ -1277,20 +1276,21 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
|
||||||
|
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
|
|
||||||
|
const CBlockIndex* tip = chainActive.Tip();
|
||||||
UniValue obj(UniValue::VOBJ);
|
UniValue obj(UniValue::VOBJ);
|
||||||
obj.pushKV("chain", Params().NetworkIDString());
|
obj.pushKV("chain", Params().NetworkIDString());
|
||||||
obj.pushKV("blocks", (int)chainActive.Height());
|
obj.pushKV("blocks", (int)chainActive.Height());
|
||||||
obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1);
|
obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1);
|
||||||
obj.pushKV("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex());
|
obj.pushKV("bestblockhash", tip->GetBlockHash().GetHex());
|
||||||
obj.pushKV("difficulty", (double)GetDifficulty(chainActive.Tip()));
|
obj.pushKV("difficulty", (double)GetDifficulty(tip));
|
||||||
obj.pushKV("mediantime", (int64_t)chainActive.Tip()->GetMedianTimePast());
|
obj.pushKV("mediantime", (int64_t)tip->GetMedianTimePast());
|
||||||
obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), chainActive.Tip()));
|
obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip));
|
||||||
obj.pushKV("initialblockdownload", IsInitialBlockDownload());
|
obj.pushKV("initialblockdownload", IsInitialBlockDownload());
|
||||||
obj.pushKV("chainwork", chainActive.Tip()->nChainWork.GetHex());
|
obj.pushKV("chainwork", tip->nChainWork.GetHex());
|
||||||
obj.pushKV("size_on_disk", CalculateCurrentUsage());
|
obj.pushKV("size_on_disk", CalculateCurrentUsage());
|
||||||
obj.pushKV("pruned", fPruneMode);
|
obj.pushKV("pruned", fPruneMode);
|
||||||
if (fPruneMode) {
|
if (fPruneMode) {
|
||||||
CBlockIndex* block = chainActive.Tip();
|
const CBlockIndex* block = tip;
|
||||||
assert(block);
|
assert(block);
|
||||||
while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
|
while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
|
||||||
block = block->pprev;
|
block = block->pprev;
|
||||||
|
@ -1307,7 +1307,6 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
|
||||||
}
|
}
|
||||||
|
|
||||||
const Consensus::Params& consensusParams = Params().GetConsensus();
|
const Consensus::Params& consensusParams = Params().GetConsensus();
|
||||||
CBlockIndex* tip = chainActive.Tip();
|
|
||||||
UniValue softforks(UniValue::VARR);
|
UniValue softforks(UniValue::VARR);
|
||||||
UniValue bip9_softforks(UniValue::VOBJ);
|
UniValue bip9_softforks(UniValue::VOBJ);
|
||||||
softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams));
|
softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams));
|
||||||
|
|
|
@ -27,7 +27,7 @@ double GetDifficulty(const CBlockIndex* blockindex);
|
||||||
void RPCNotifyBlockChange(bool ibd, const CBlockIndex *);
|
void RPCNotifyBlockChange(bool ibd, const CBlockIndex *);
|
||||||
|
|
||||||
/** Block description to JSON */
|
/** Block description to JSON */
|
||||||
UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false);
|
UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, bool txDetails = false);
|
||||||
|
|
||||||
/** Mempool information to JSON */
|
/** Mempool information to JSON */
|
||||||
UniValue mempoolInfoToJSON();
|
UniValue mempoolInfoToJSON();
|
||||||
|
@ -36,7 +36,7 @@ UniValue mempoolInfoToJSON();
|
||||||
UniValue mempoolToJSON(bool fVerbose = false);
|
UniValue mempoolToJSON(bool fVerbose = false);
|
||||||
|
|
||||||
/** Block header to JSON */
|
/** Block header to JSON */
|
||||||
UniValue blockheaderToJSON(const CBlockIndex* blockindex);
|
UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex);
|
||||||
|
|
||||||
/** Used by getblockstats to get feerates at different percentiles by weight */
|
/** Used by getblockstats to get feerates at different percentiles by weight */
|
||||||
void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], std::vector<std::pair<CAmount, int64_t>>& scores, int64_t total_weight);
|
void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], std::vector<std::pair<CAmount, int64_t>>& scores, int64_t total_weight);
|
||||||
|
|
|
@ -68,11 +68,4 @@ BOOST_AUTO_TEST_CASE(get_difficulty_for_very_high_target)
|
||||||
TestDifficulty(0x12345678, 5913134931067755359633408.0);
|
TestDifficulty(0x12345678, 5913134931067755359633408.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that difficulty is 1.0 for an empty chain.
|
|
||||||
BOOST_AUTO_TEST_CASE(get_difficulty_for_null_tip)
|
|
||||||
{
|
|
||||||
double difficulty = GetDifficulty(nullptr);
|
|
||||||
RejectDifficultyMismatch(difficulty, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
Loading…
Reference in a new issue