From bcafa282a362b17e059293abb6a66b48efec50c5 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sun, 30 Jun 2024 18:31:48 +0000 Subject: [PATCH] merge bitcoin#24909: Move and rename pindexBestHeader, fHavePruned --- src/bench/rpc_blockchain.cpp | 4 +-- src/dsnotificationinterface.cpp | 2 +- src/init.cpp | 12 +++---- src/masternode/sync.cpp | 3 +- src/masternode/sync.h | 2 +- src/net_processing.cpp | 47 ++++++++++++++-------------- src/node/blockstorage.cpp | 20 ++++++------ src/node/blockstorage.h | 15 +++++---- src/node/interfaces.cpp | 9 +++--- src/rest.cpp | 10 +++--- src/rpc/blockchain.cpp | 48 ++++++++++++++-------------- src/rpc/blockchain.h | 3 +- src/validation.cpp | 55 +++++++++++++++++---------------- src/validation.h | 6 ++-- 14 files changed, 121 insertions(+), 115 deletions(-) diff --git a/src/bench/rpc_blockchain.cpp b/src/bench/rpc_blockchain.cpp index d0294cc4eb..a95397897a 100644 --- a/src/bench/rpc_blockchain.cpp +++ b/src/bench/rpc_blockchain.cpp @@ -45,7 +45,7 @@ static void BlockToJsonVerbose(benchmark::Bench& bench) TestBlockAndIndex data; const LLMQContext& llmq_ctx = *data.testing_setup->m_node.llmq_ctx; bench.run([&] { - auto univalue = blockToJSON(data.block, &data.blockindex, &data.blockindex, *llmq_ctx.clhandler, *llmq_ctx.isman, /*verbose*/ true); + auto univalue = blockToJSON(data.testing_setup->m_node.chainman->m_blockman, data.block, &data.blockindex, &data.blockindex, *llmq_ctx.clhandler, *llmq_ctx.isman, /*verbose*/ true); ankerl::nanobench::doNotOptimizeAway(univalue); }); } @@ -56,7 +56,7 @@ static void BlockToJsonVerboseWrite(benchmark::Bench& bench) { TestBlockAndIndex data; const LLMQContext& llmq_ctx = *data.testing_setup->m_node.llmq_ctx; - auto univalue = blockToJSON(data.block, &data.blockindex, &data.blockindex, *llmq_ctx.clhandler, *llmq_ctx.isman, /*verbose*/ true); + auto univalue = blockToJSON(data.testing_setup->m_node.chainman->m_blockman, data.block, &data.blockindex, &data.blockindex, *llmq_ctx.clhandler, *llmq_ctx.isman, /*verbose*/ true); bench.run([&] { auto str = univalue.write(); ankerl::nanobench::doNotOptimizeAway(str); diff --git a/src/dsnotificationinterface.cpp b/src/dsnotificationinterface.cpp index 0741ecc91b..cb0e44e21b 100644 --- a/src/dsnotificationinterface.cpp +++ b/src/dsnotificationinterface.cpp @@ -79,7 +79,7 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, con if (pindexNew == pindexFork) // blocks were disconnected without any new ones return; - m_mn_sync.UpdatedBlockTip(pindexNew, fInitialDownload); + m_mn_sync.UpdatedBlockTip(m_chainman.m_best_header, pindexNew, fInitialDownload); // Update global DIP0001 activation status fDIP0001ActiveAtTip = pindexNew->nHeight >= Params().GetConsensus().DIP0001Height; diff --git a/src/init.cpp b/src/init.cpp index 6a57dc4650..71a1751b19 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1890,7 +1890,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) if (ShutdownRequested()) break; - // LoadBlockIndex will load fHavePruned if we've ever removed a + // LoadBlockIndex will load m_have_pruned if we've ever removed a // block file from disk. // Note that it also sets fReindex based on the disk flag! // From here on out fReindex and fReset mean something different! @@ -1936,7 +1936,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // Check for changed -prune state. What we are concerned about is a user who has pruned blocks // in the past, but is now trying to run unpruned. - if (fHavePruned && !fPruneMode) { + if (chainman.m_blockman.m_have_pruned && !fPruneMode) { strLoadError = _("You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain"); break; } @@ -2022,7 +2022,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) for (CChainState* chainstate : chainman.GetAll()) { if (!is_coinsview_empty(chainstate)) { uiInterface.InitMessage(_("Verifying blocks…").translated); - if (fHavePruned && args.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) { + if (chainman.m_blockman.m_have_pruned && args.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) { LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n", MIN_BLOCKS_TO_KEEP); } @@ -2328,9 +2328,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) tip_info->block_hash = chainman.ActiveChain().Tip() ? chainman.ActiveChain().Tip()->GetBlockHash() : Params().GenesisBlock().GetHash(); tip_info->verification_progress = GuessVerificationProgress(Params().TxData(), chainman.ActiveChain().Tip()); } - if (tip_info && ::pindexBestHeader) { - tip_info->header_height = ::pindexBestHeader->nHeight; - tip_info->header_time = ::pindexBestHeader->GetBlockTime(); + if (tip_info && chainman.m_best_header) { + tip_info->header_height = chainman.m_best_header->nHeight; + tip_info->header_time = chainman.m_best_header->GetBlockTime(); } } LogPrintf("nBestHeight = %d\n", chain_active_height); diff --git a/src/masternode/sync.cpp b/src/masternode/sync.cpp index 2deceadf92..9e3f53a0c2 100644 --- a/src/masternode/sync.cpp +++ b/src/masternode/sync.cpp @@ -329,7 +329,7 @@ void CMasternodeSync::NotifyHeaderTip(const CBlockIndex *pindexNew, bool fInitia } } -void CMasternodeSync::UpdatedBlockTip(const CBlockIndex *pindexNew, bool fInitialDownload) +void CMasternodeSync::UpdatedBlockTip(const CBlockIndex *pindexTip, const CBlockIndex *pindexNew, bool fInitialDownload) { LogPrint(BCLog::MNSYNC, "CMasternodeSync::UpdatedBlockTip -- pindexNew->nHeight: %d fInitialDownload=%d\n", pindexNew->nHeight, fInitialDownload); nTimeLastUpdateBlockTip = GetTime().count(); @@ -353,7 +353,6 @@ void CMasternodeSync::UpdatedBlockTip(const CBlockIndex *pindexNew, bool fInitia } // Note: since we sync headers first, it should be ok to use this - CBlockIndex* pindexTip = WITH_LOCK(cs_main, return pindexBestHeader); if (pindexTip == nullptr) return; bool fReachedBestHeaderNew = pindexNew->GetBlockHash() == pindexTip->GetBlockHash(); diff --git a/src/masternode/sync.h b/src/masternode/sync.h index aeba83ae77..2692cd1736 100644 --- a/src/masternode/sync.h +++ b/src/masternode/sync.h @@ -75,7 +75,7 @@ public: void AcceptedBlockHeader(const CBlockIndex *pindexNew); void NotifyHeaderTip(const CBlockIndex *pindexNew, bool fInitialDownload); - void UpdatedBlockTip(const CBlockIndex *pindexNew, bool fInitialDownload); + void UpdatedBlockTip(const CBlockIndex *pindexTip, const CBlockIndex *pindexNew, bool fInitialDownload); void DoMaintenance(); }; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index e287f125f0..7494dd8642 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1856,9 +1856,9 @@ bool PeerManagerImpl::BlockRequestAllowed(const CBlockIndex* pindex) { AssertLockHeld(cs_main); if (m_chainman.ActiveChain().Contains(pindex)) return true; - return pindex->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != nullptr) && - (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() < STALE_RELAY_AGE_LIMIT) && - (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, m_chainparams.GetConsensus()) < STALE_RELAY_AGE_LIMIT); + return pindex->IsValid(BLOCK_VALID_SCRIPTS) && (m_chainman.m_best_header != nullptr) && + (m_chainman.m_best_header->GetBlockTime() - pindex->GetBlockTime() < STALE_RELAY_AGE_LIMIT) && + (GetBlockProofEquivalentTime(*m_chainman.m_best_header, *pindex, *m_chainman.m_best_header, m_chainparams.GetConsensus()) < STALE_RELAY_AGE_LIMIT); } std::optional PeerManagerImpl::FetchBlock(NodeId peer_id, const CBlockIndex& block_index) @@ -2434,7 +2434,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& const CNetMsgMaker msgMaker(pfrom.GetCommonVersion()); // disconnect node in case we have reached the outbound limit for serving historical blocks if (m_connman.OutboundTargetReached(true) && - (((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.IsMsgFilteredBlk()) && + (((m_chainman.m_best_header != nullptr) && (m_chainman.m_best_header->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.IsMsgFilteredBlk()) && !pfrom.HasPermission(NetPermissionFlags::Download) // nodes with the download permission may exceed target ) { LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom.GetId()); @@ -2824,13 +2824,13 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer, if (!m_chainman.m_blockman.LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) { nodestate->nUnconnectingHeaders++; std::string msg_type = (pfrom.nServices & NODE_HEADERS_COMPRESSED) ? NetMsgType::GETHEADERS2 : NetMsgType::GETHEADERS; - m_connman.PushMessage(&pfrom, msgMaker.Make(msg_type, m_chainman.ActiveChain().GetLocator(pindexBestHeader), uint256())); + m_connman.PushMessage(&pfrom, msgMaker.Make(msg_type, m_chainman.ActiveChain().GetLocator(m_chainman.m_best_header), uint256())); LogPrint(BCLog::NET, "received header %s: missing prev block %s, sending %s (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n", - headers[0].GetHash().ToString(), - headers[0].hashPrevBlock.ToString(), - msg_type, - pindexBestHeader->nHeight, - pfrom.GetId(), nodestate->nUnconnectingHeaders); + headers[0].GetHash().ToString(), + headers[0].hashPrevBlock.ToString(), + msg_type, + m_chainman.m_best_header->nHeight, + pfrom.GetId(), nodestate->nUnconnectingHeaders); // Set hashLastUnknownBlock for this peer, so that if we // eventually get the headers - even from a different peer - // we can use this peer to download. @@ -2887,7 +2887,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer, if (nCount == MAX_HEADERS_RESULTS) { // Headers message had its maximum size; the peer may have more headers. - // TODO: optimize: if pindexLast is an ancestor of m_chainman.ActiveChain().Tip or pindexBestHeader, continue + // TODO: optimize: if pindexLast is an ancestor of m_chainman.ActiveChain().Tip or m_chainman.m_best_header, continue // from there instead. std::string msg_type = (pfrom.nServices & NODE_HEADERS_COMPRESSED) ? NetMsgType::GETHEADERS2 : NetMsgType::GETHEADERS; LogPrint(BCLog::NET, "more %s (%d) to end to peer=%d (startheight:%d)\n", @@ -3819,7 +3819,7 @@ void PeerManagerImpl::ProcessMessage( // Download if this is a nice peer, or we have no nice peers and this one might do. bool fFetch = state->fPreferredDownload || (nPreferredDownload == 0 && !pfrom.IsAddrFetchConn()); // Only actively request headers from a single peer, unless we're close to end of initial download. - if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - nMaxTipAge) { + if ((nSyncStarted == 0 && fFetch) || m_chainman.m_best_header->GetBlockTime() > GetAdjustedTime() - nMaxTipAge) { // Make sure to mark this peer as the one we are currently syncing with etc. state->fSyncStarted = true; state->m_headers_sync_timeout = current_time + HEADERS_DOWNLOAD_TIMEOUT_BASE + @@ -3827,7 +3827,7 @@ void PeerManagerImpl::ProcessMessage( // Convert HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER to microseconds before scaling // to maintain precision std::chrono::microseconds{HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER} * - (GetAdjustedTime() - pindexBestHeader->GetBlockTime()) / m_chainparams.GetConsensus().nPowTargetSpacing + (GetAdjustedTime() - m_chainman.m_best_header->GetBlockTime()) / m_chainparams.GetConsensus().nPowTargetSpacing ); nSyncStarted++; // Headers-first is the primary method of announcement on @@ -3872,8 +3872,8 @@ void PeerManagerImpl::ProcessMessage( } if (best_block != nullptr) { std::string msg_type = (pfrom.nServices & NODE_HEADERS_COMPRESSED) ? NetMsgType::GETHEADERS2 : NetMsgType::GETHEADERS; - m_connman.PushMessage(&pfrom, msgMaker.Make(msg_type, m_chainman.ActiveChain().GetLocator(pindexBestHeader), *best_block)); - LogPrint(BCLog::NET, "%s (%d) %s to peer=%d\n", msg_type, pindexBestHeader->nHeight, best_block->ToString(), pfrom.GetId()); + m_connman.PushMessage(&pfrom, msgMaker.Make(msg_type, m_chainman.ActiveChain().GetLocator(m_chainman.m_best_header), *best_block)); + LogPrint(BCLog::NET, "%s (%d) %s to peer=%d\n", msg_type, m_chainman.m_best_header->nHeight, best_block->ToString(), pfrom.GetId()); } return; @@ -4289,7 +4289,7 @@ void PeerManagerImpl::ProcessMessage( if (!m_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.hashPrevBlock)) { // Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers if (!m_chainman.ActiveChainstate().IsInitialBlockDownload()) - m_connman.PushMessage(&pfrom, msgMaker.Make((pfrom.nServices & NODE_HEADERS_COMPRESSED) ? NetMsgType::GETHEADERS2 : NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexBestHeader), uint256())); + m_connman.PushMessage(&pfrom, msgMaker.Make((pfrom.nServices & NODE_HEADERS_COMPRESSED) ? NetMsgType::GETHEADERS2 : NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(m_chainman.m_best_header), uint256())); return; } @@ -5445,28 +5445,29 @@ bool PeerManagerImpl::SendMessages(CNode* pto) CNodeState &state = *State(pto->GetId()); // Start block sync - if (pindexBestHeader == nullptr) - pindexBestHeader = m_chainman.ActiveChain().Tip(); + if (m_chainman.m_best_header == nullptr) { + m_chainman.m_best_header = m_chainman.ActiveChain().Tip(); + } bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->IsAddrFetchConn()); // Download if this is a nice peer, or we have no nice peers and this one might do. if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex && pto->CanRelay()) { // Only actively request headers from a single peer, unless we're close to end of initial download. - if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - nMaxTipAge) { + if ((nSyncStarted == 0 && fFetch) || m_chainman.m_best_header->GetBlockTime() > GetAdjustedTime() - nMaxTipAge) { state.fSyncStarted = true; state.m_headers_sync_timeout = current_time + HEADERS_DOWNLOAD_TIMEOUT_BASE + ( // Convert HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER to microseconds before scaling // to maintain precision std::chrono::microseconds{HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER} * - (GetAdjustedTime() - pindexBestHeader->GetBlockTime()) / consensusParams.nPowTargetSpacing + (GetAdjustedTime() - m_chainman.m_best_header->GetBlockTime()) / consensusParams.nPowTargetSpacing ); nSyncStarted++; - const CBlockIndex *pindexStart = pindexBestHeader; + const CBlockIndex* pindexStart = m_chainman.m_best_header; /* If possible, start at the block preceding the currently best known header. This ensures that we always get a non-empty list of headers back as long as the peer is up-to-date. With a non-empty response, we can initialise the peer's known best block. This wouldn't be possible - if we requested starting at pindexBestHeader and + if we requested starting at m_chainman.m_best_header and got back an empty response. */ if (pindexStart->pprev) pindexStart = pindexStart->pprev; @@ -5835,7 +5836,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto) // Check for headers sync timeouts if (state.fSyncStarted && state.m_headers_sync_timeout < std::chrono::microseconds::max()) { // Detect whether this is a stalling initial-headers-sync peer - if (pindexBestHeader->GetBlockTime() <= GetAdjustedTime() - nMaxTipAge) { + if (m_chainman.m_best_header->GetBlockTime() <= GetAdjustedTime() - nMaxTipAge) { if (current_time > state.m_headers_sync_timeout && nSyncStarted == 1 && (nPreferredDownload - state.fPreferredDownload >= 1)) { // Disconnect a peer (without NetPermissionFlags::NoBan permission) if it is our only sync peer, // and we have others we could be using instead. diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp index 6e43cb59c1..3ae23ef9bf 100644 --- a/src/node/blockstorage.cpp +++ b/src/node/blockstorage.cpp @@ -25,7 +25,6 @@ std::atomic_bool fImporting(false); std::atomic_bool fReindex(false); -bool fHavePruned = false; bool fPruneMode = false; uint64_t nPruneTarget = 0; @@ -86,7 +85,8 @@ const CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash) const return it == m_block_index.end() ? nullptr : &it->second; } -CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block, const uint256& hash, enum BlockStatus nStatus) +CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block, const uint256& hash, CBlockIndex*& best_header, + enum BlockStatus nStatus) { assert(!(nStatus & BLOCK_FAILED_MASK)); // no failed blocks allowed AssertLockHeld(cs_main); @@ -113,8 +113,8 @@ CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block, const uint pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + GetBlockProof(*pindexNew); if (nStatus & BLOCK_VALID_MASK) { pindexNew->RaiseValidity(nStatus); - if (pindexBestHeader == nullptr || pindexBestHeader->nChainWork < pindexNew->nChainWork) { - pindexBestHeader = pindexNew; + if (best_header == nullptr || best_header->nChainWork < pindexNew->nChainWork) { + best_header = pindexNew; } } else { pindexNew->RaiseValidity(BLOCK_VALID_TREE); // required validity level @@ -309,8 +309,6 @@ bool BlockManager::LoadBlockIndex(const Consensus::Params& consensus_params) if (pindex->pprev) { pindex->BuildSkip(); } - if (pindex->IsValid(BLOCK_VALID_TREE) && (pindexBestHeader == nullptr || CBlockIndexWorkComparator()(pindexBestHeader, pindex))) - pindexBestHeader = pindex; } return true; @@ -327,6 +325,8 @@ void BlockManager::Unload() m_last_blockfile = 0; m_dirty_blockindex.clear(); m_dirty_fileinfo.clear(); + + m_have_pruned = false; } bool BlockManager::WriteBlockIndexDB() @@ -389,8 +389,8 @@ bool BlockManager::LoadBlockIndexDB() } // Check whether we have ever pruned block & undo files - m_block_tree_db->ReadFlag("prunedblockfiles", fHavePruned); - if (fHavePruned) { + m_block_tree_db->ReadFlag("prunedblockfiles", m_have_pruned); + if (m_have_pruned) { LogPrintf("LoadBlockIndexDB(): Block files have previously been pruned\n"); } @@ -428,10 +428,10 @@ const CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data) return nullptr; } -bool IsBlockPruned(const CBlockIndex* pblockindex) +bool BlockManager::IsBlockPruned(const CBlockIndex* pblockindex) { AssertLockHeld(::cs_main); - return (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0); + return (m_have_pruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0); } // If we're using -prune with -reindex, then delete block files that will be ignored by the diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h index 480c60248e..7e5087a6b2 100644 --- a/src/node/blockstorage.h +++ b/src/node/blockstorage.h @@ -49,8 +49,6 @@ static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB extern std::atomic_bool fImporting; extern std::atomic_bool fReindex; /** Pruning-related variables and constants */ -/** True if any block files have ever been pruned. */ -extern bool fHavePruned; /** True if we're running in -prune mode. */ extern bool fPruneMode; /** Number of bytes of block files that we're trying to stay below. */ @@ -160,7 +158,9 @@ public: /** Clear all data members. */ void Unload() EXCLUSIVE_LOCKS_REQUIRED(cs_main); - CBlockIndex* AddToBlockIndex(const CBlockHeader& block, const uint256& hash, enum BlockStatus nStatus = BLOCK_VALID_TREE) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + CBlockIndex* AddToBlockIndex(const CBlockHeader& block, const uint256& hash, CBlockIndex*& best_header, + enum BlockStatus nStatus = BLOCK_VALID_TREE) + EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** Create a new block index entry for a given block hash */ CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main); @@ -184,6 +184,12 @@ public: //! Returns last CBlockIndex* that is a checkpoint const CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + /** True if any block files have ever been pruned. */ + bool m_have_pruned = false; + + //! Check whether the block associated with this index entry is pruned or not. + bool IsBlockPruned(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + /** * Return the spend height, which is one more than the inputs.GetBestBlock(). * While checking, GetBestBlock() refers to the parent block. (protected by cs_main) @@ -197,9 +203,6 @@ public: } }; -//! Check whether the block associated with this index entry is pruned or not. -bool IsBlockPruned(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); - void CleanupBlockRevFiles(); /** Open a block file (blk?????.dat) */ diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index f89d8c2164..eba23b0195 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -419,9 +419,10 @@ public: bool getHeaderTip(int& height, int64_t& block_time) override { LOCK(::cs_main); - if (::pindexBestHeader) { - height = ::pindexBestHeader->nHeight; - block_time = ::pindexBestHeader->GetBlockTime(); + auto best_header = chainman().m_best_header; + if (best_header) { + height = best_header->nHeight; + block_time = best_header->GetBlockTime(); return true; } return false; @@ -934,7 +935,7 @@ public: bool havePruned() override { LOCK(cs_main); - return ::fHavePruned; + return m_node.chainman->m_blockman.m_have_pruned; } bool isReadyToBroadcast() override { return !::fImporting && !::fReindex && !isInitialBlockDownload(); } bool isInitialBlockDownload() override { diff --git a/src/rest.cpp b/src/rest.cpp index 65ba16e52e..fc268870d5 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -273,10 +273,10 @@ static bool rest_block(const CoreContext& context, CBlock block; const CBlockIndex* pblockindex = nullptr; const CBlockIndex* tip = nullptr; + ChainstateManager* maybe_chainman = GetChainman(context, req); + if (!maybe_chainman) return false; + ChainstateManager& chainman = *maybe_chainman; { - ChainstateManager* maybe_chainman = GetChainman(context, req); - if (!maybe_chainman) return false; - ChainstateManager& chainman = *maybe_chainman; LOCK(cs_main); tip = chainman.ActiveChain().Tip(); pblockindex = chainman.m_blockman.LookupBlockIndex(hash); @@ -284,7 +284,7 @@ static bool rest_block(const CoreContext& context, return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found"); } - if (IsBlockPruned(pblockindex)) + if (chainman.m_blockman.IsBlockPruned(pblockindex)) return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)"); if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) @@ -311,7 +311,7 @@ static bool rest_block(const CoreContext& context, } case RetFormat::JSON: { - UniValue objBlock = blockToJSON(block, tip, pblockindex, *llmq::chainLocksHandler, *llmq::quorumInstantSendManager, showTxDetails); + UniValue objBlock = blockToJSON(chainman.m_blockman, block, tip, pblockindex, *llmq::chainLocksHandler, *llmq::quorumInstantSendManager, showTxDetails); std::string strJSON = objBlock.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); req->WriteReply(HTTP_OK, strJSON); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index e6b7a49492..fc1d8367ea 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -156,7 +156,7 @@ UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex return result; } -UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, bool txDetails) +UniValue blockToJSON(BlockManager& blockman, const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, bool txDetails) { UniValue result = blockheaderToJSON(tip, blockindex, clhandler, isman); @@ -164,7 +164,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn UniValue txs(UniValue::VARR); if (txDetails) { CBlockUndo blockUndo; - const bool have_undo{WITH_LOCK(::cs_main, return !IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex))}; + const bool have_undo{WITH_LOCK(::cs_main, return !blockman.IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex))}; for (size_t i = 0; i < block.vtx.size(); ++i) { const CTransactionRef& tx = block.vtx.at(i); // coinbase transaction (i == 0) doesn't have undo data @@ -1060,11 +1060,11 @@ static RPCHelpMan getblockheaders() }; } -static CBlock GetBlockChecked(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) +static CBlock GetBlockChecked(BlockManager& blockman, const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { AssertLockHeld(::cs_main); CBlock block; - if (IsBlockPruned(pblockindex)) { + if (blockman.IsBlockPruned(pblockindex)) { throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)"); } @@ -1078,11 +1078,11 @@ static CBlock GetBlockChecked(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_RE return block; } -static CBlockUndo GetUndoChecked(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +static CBlockUndo GetUndoChecked(BlockManager& blockman, const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(::cs_main); CBlockUndo blockUndo; - if (IsBlockPruned(pblockindex)) { + if (blockman.IsBlockPruned(pblockindex)) { throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available (pruned data)"); } @@ -1137,7 +1137,7 @@ static RPCHelpMan getmerkleblocks() throw JSONRPCError(RPC_INVALID_PARAMETER, "Count is out of range"); } - CBlock block = GetBlockChecked(pblockindex); + CBlock block = GetBlockChecked(chainman.m_blockman, pblockindex); UniValue arrMerkleBlocks(UniValue::VARR); @@ -1247,8 +1247,8 @@ static RPCHelpMan getblock() CBlock block; const CBlockIndex* pblockindex; const CBlockIndex* tip; + ChainstateManager& chainman = EnsureChainman(node); { - ChainstateManager& chainman = EnsureChainman(node); LOCK(cs_main); pblockindex = chainman.m_blockman.LookupBlockIndex(hash); tip = chainman.ActiveChain().Tip(); @@ -1257,7 +1257,7 @@ static RPCHelpMan getblock() throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } - block = GetBlockChecked(pblockindex); + block = GetBlockChecked(chainman.m_blockman, pblockindex); } if (verbosity <= 0) @@ -1269,7 +1269,7 @@ static RPCHelpMan getblock() } LLMQContext& llmq_ctx = EnsureLLMQContext(node); - return blockToJSON(block, tip, pblockindex, *llmq_ctx.clhandler, *llmq_ctx.isman, verbosity >= 2); + return blockToJSON(chainman.m_blockman, block, tip, pblockindex, *llmq_ctx.clhandler, *llmq_ctx.isman, verbosity >= 2); }, }; } @@ -1760,18 +1760,18 @@ RPCHelpMan getblockchaininfo() const auto ehfSignals = node.mnhf_manager->GetSignalsStage(tip); UniValue obj(UniValue::VOBJ); - obj.pushKV("chain", strChainName); - obj.pushKV("blocks", height); - obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1); - obj.pushKV("bestblockhash", tip->GetBlockHash().GetHex()); - obj.pushKV("difficulty", (double)GetDifficulty(tip)); - obj.pushKV("time", (int64_t)tip->nTime); - obj.pushKV("mediantime", (int64_t)tip->GetMedianTimePast()); - obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip)); - obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload()); - obj.pushKV("chainwork", tip->nChainWork.GetHex()); + obj.pushKV("chain", strChainName); + 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", (double)GetDifficulty(tip)); + obj.pushKV("time", (int64_t)tip->nTime); + obj.pushKV("mediantime", (int64_t)tip->GetMedianTimePast()); + obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip)); + obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload()); + obj.pushKV("chainwork", tip->nChainWork.GetHex()); obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage()); - obj.pushKV("pruned", fPruneMode); + obj.pushKV("pruned", fPruneMode); if (fPruneMode) { const CBlockIndex* block = tip; CHECK_NONFATAL(block); @@ -2358,8 +2358,8 @@ static RPCHelpMan getblockstats() } } - const CBlock block = GetBlockChecked(pindex); - const CBlockUndo blockUndo = GetUndoChecked(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; @@ -2571,7 +2571,7 @@ static RPCHelpMan getspecialtxes() throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } - const CBlock block = GetBlockChecked(pblockindex); + const CBlock block = GetBlockChecked(chainman.m_blockman, pblockindex); int nTxNum = 0; UniValue result(UniValue::VARR); diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h index aebfb40000..f8fc13d081 100644 --- a/src/rpc/blockchain.h +++ b/src/rpc/blockchain.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -40,7 +41,7 @@ double GetDifficulty(const CBlockIndex* blockindex); void RPCNotifyBlockChange(const CBlockIndex*); /** Block description to JSON */ -UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, bool txDetails = false) LOCKS_EXCLUDED(cs_main); +UniValue blockToJSON(BlockManager& blockman, const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, bool txDetails = false) LOCKS_EXCLUDED(cs_main); /** Mempool information to JSON */ UniValue MempoolInfoToJSON(const CTxMemPool& pool, llmq::CInstantSendManager& isman); diff --git a/src/validation.cpp b/src/validation.cpp index 479ff88680..dd463bcc56 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -105,7 +105,6 @@ const std::vector CHECKLEVEL_DOC { */ RecursiveMutex cs_main; -CBlockIndex* pindexBestHeader = nullptr; Mutex g_best_block_mutex; std::condition_variable g_best_block_cv; uint256 g_best_block; @@ -348,8 +347,9 @@ static bool IsCurrentForFeeEstimation(CChainState& active_chainstate) EXCLUSIVE_ return false; if (active_chainstate.m_chain.Tip()->GetBlockTime() < count_seconds(GetTime() - MAX_FEE_ESTIMATION_TIP_AGE)) return false; - if (active_chainstate.m_chain.Height() < pindexBestHeader->nHeight - 1) + if (active_chainstate.m_chain.Height() < active_chainstate.m_chainman.m_best_header->nHeight - 1) { return false; + } return true; } @@ -1296,8 +1296,8 @@ void CChainState::InvalidChainFound(CBlockIndex* pindexNew) if (!m_chainman.m_best_invalid || pindexNew->nChainWork > m_chainman.m_best_invalid->nChainWork) { m_chainman.m_best_invalid = pindexNew; } - if (pindexBestHeader != nullptr && pindexBestHeader->GetAncestor(pindexNew->nHeight) == pindexNew) { - pindexBestHeader = m_chain.Tip(); + if (m_chainman.m_best_header != nullptr && m_chainman.m_best_header->GetAncestor(pindexNew->nHeight) == pindexNew) { + m_chainman.m_best_header = m_chain.Tip(); } LogPrintf("%s: invalid block=%s height=%d log2_work=%f date=%s\n", __func__, @@ -1888,8 +1888,8 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state, BlockMap::const_iterator it = m_blockman.m_block_index.find(hashAssumeValid); if (it != m_blockman.m_block_index.end()) { if (it->second.GetAncestor(pindex->nHeight) == pindex && - pindexBestHeader->GetAncestor(pindex->nHeight) == pindex && - pindexBestHeader->nChainWork >= nMinimumChainWork) { + m_chainman.m_best_header->GetAncestor(pindex->nHeight) == pindex && + m_chainman.m_best_header->nChainWork >= nMinimumChainWork) { // This block is a member of the assumed verified chain and an ancestor of the best header. // Script verification is skipped when connecting blocks under the // assumevalid block. Assuming the assumevalid block is valid this @@ -1904,7 +1904,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state, // artificially set the default assumed verified block further back. // The test against nMinimumChainWork prevents the skipping when denied access to any chain at // least as good as the expected chain. - fScriptChecks = (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, m_params.GetConsensus()) <= 60 * 60 * 24 * 7 * 2); + fScriptChecks = (GetBlockProofEquivalentTime(*m_chainman.m_best_header, *pindex, *m_chainman.m_best_header, m_params.GetConsensus()) <= 60 * 60 * 24 * 7 * 2); } } } @@ -2348,9 +2348,9 @@ bool CChainState::FlushStateToDisk( } if (!setFilesToPrune.empty()) { fFlushForPrune = true; - if (!fHavePruned) { + if (!m_blockman.m_have_pruned) { m_blockman.m_block_tree_db->WriteFlag("prunedblockfiles", true); - fHavePruned = true; + m_blockman.m_have_pruned = true; } } } @@ -2911,7 +2911,7 @@ static bool NotifyHeaderTip(CChainState& chainstate) LOCKS_EXCLUDED(cs_main) { CBlockIndex* pindexHeader = nullptr; { LOCK(cs_main); - pindexHeader = pindexBestHeader; + pindexHeader = chainstate.m_chainman.m_best_header; if (pindexHeader != pindexHeaderOld) { fNotify = true; @@ -3129,9 +3129,9 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind CBlockIndex *invalid_walk_tip = m_chain.Tip(); const CBlockIndex* pindexOldTip = m_chain.Tip(); - if (pindex == pindexBestHeader) { - m_chainman.m_best_invalid = pindexBestHeader; - pindexBestHeader = pindexBestHeader->pprev; + if (pindex == m_chainman.m_best_header) { + m_chainman.m_best_invalid = m_chainman.m_best_header; + m_chainman.m_best_header = m_chainman.m_best_header->pprev; } // ActivateBestChain considers blocks already in m_chain @@ -3147,9 +3147,9 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind if (!ret) return false; assert(invalid_walk_tip->pprev == m_chain.Tip()); - if (pindexOldTip == pindexBestHeader) { - m_chainman.m_best_invalid = pindexBestHeader; - pindexBestHeader = pindexBestHeader->pprev; + if (pindexOldTip == m_chainman.m_best_header) { + m_chainman.m_best_invalid = m_chainman.m_best_header; + m_chainman.m_best_header = m_chainman.m_best_header->pprev; } // We immediately mark the disconnected blocks as invalid. @@ -3264,8 +3264,8 @@ bool CChainState::MarkConflictingBlock(BlockValidationState& state, CBlockIndex bool pindex_was_in_chain = false; CBlockIndex *conflicting_walk_tip = m_chain.Tip(); - if (pindex == pindexBestHeader) { - pindexBestHeader = pindexBestHeader->pprev; + if (pindex == m_chainman.m_best_header) { + m_chainman.m_best_header = m_chainman.m_best_header->pprev; } { @@ -3282,8 +3282,8 @@ bool CChainState::MarkConflictingBlock(BlockValidationState& state, CBlockIndex MaybeUpdateMempoolForReorg(disconnectpool, false); return false; } - if (pindexOldTip == pindexBestHeader) { - pindexBestHeader = pindexBestHeader->pprev; + if (pindexOldTip == m_chainman.m_best_header) { + m_chainman.m_best_header = m_chainman.m_best_header->pprev; } } @@ -3749,13 +3749,13 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida if (llmq::chainLocksHandler->HasConflictingChainLock(pindexPrev->nHeight + 1, hash)) { if (miSelf == m_blockman.m_block_index.end()) { - m_blockman.AddToBlockIndex(block, hash, BLOCK_CONFLICT_CHAINLOCK); + m_blockman.AddToBlockIndex(block, hash, m_best_header, BLOCK_CONFLICT_CHAINLOCK); } LogPrintf("ERROR: %s: header %s conflicts with chainlock\n", __func__, hash.ToString()); return state.Invalid(BlockValidationResult::BLOCK_CHAINLOCK, "bad-chainlock"); } } - CBlockIndex* pindex{m_blockman.AddToBlockIndex(block, hash)}; + CBlockIndex* pindex{m_blockman.AddToBlockIndex(block, hash, m_best_header)}; if (ppindex) *ppindex = pindex; @@ -4342,13 +4342,11 @@ void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman) { LOCK(cs_main); chainman.Unload(); - pindexBestHeader = nullptr; if (mempool) mempool->clear(); g_versionbitscache.Clear(); for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) { warningcache[b].clear(); } - fHavePruned = false; } bool ChainstateManager::LoadBlockIndex() @@ -4423,6 +4421,8 @@ bool ChainstateManager::LoadBlockIndex() if (pindex->nStatus & BLOCK_FAILED_MASK && (!m_best_invalid || pindex->nChainWork > m_best_invalid->nChainWork)) { m_best_invalid = pindex; } + if (pindex->IsValid(BLOCK_VALID_TREE) && (m_best_header == nullptr || CBlockIndexWorkComparator()(m_best_header, pindex))) + m_best_header = pindex; } needs_init = m_blockman.m_block_index.empty(); @@ -4458,7 +4458,7 @@ bool CChainState::AddGenesisBlock(const CBlock& block, BlockValidationState& sta if (blockPos.IsNull()) { return error("%s: writing genesis block to disk failed (%s)", __func__, state.ToString()); } - CBlockIndex* pindex = m_blockman.AddToBlockIndex(block, block.GetHash()); + CBlockIndex* pindex = m_blockman.AddToBlockIndex(block, block.GetHash(), m_chainman.m_best_header); ReceivedBlockTransactions(block, pindex, blockPos); return true; } @@ -4700,7 +4700,7 @@ void CChainState::CheckBlockIndex() // HAVE_DATA is only equivalent to nTx > 0 (or VALID_TRANSACTIONS) if no pruning has occurred. // Unless these indexes are assumed valid and pending block download on a // background chainstate. - if (!fHavePruned && !pindex->IsAssumedValid()) { + if (!m_blockman.m_have_pruned && !pindex->IsAssumedValid()) { // If we've never pruned, then HAVE_DATA should be equivalent to nTx > 0 assert(!(pindex->nStatus & BLOCK_HAVE_DATA) == (pindex->nTx == 0)); assert(pindexFirstMissing == pindexFirstNeverProcessed); @@ -4778,7 +4778,7 @@ void CChainState::CheckBlockIndex() if (pindexFirstMissing == nullptr) assert(!foundInUnlinked); // We aren't missing data for any parent -- cannot be in m_blocks_unlinked. if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed == nullptr && pindexFirstMissing != nullptr) { // We HAVE_DATA for this block, have received data for all parents at some point, but we're currently missing data for some parent. - assert(fHavePruned); // We must have pruned. + assert(m_blockman.m_have_pruned); // We must have pruned. // This block may have entered m_blocks_unlinked if: // - it has a descendant that at some point had more work than the // tip, and @@ -5424,6 +5424,7 @@ void ChainstateManager::Unload() m_failed_blocks.clear(); m_blockman.Unload(); + m_best_header = nullptr; m_best_invalid = nullptr; } diff --git a/src/validation.h b/src/validation.h index f7ae8db37b..9db87d36db 100644 --- a/src/validation.h +++ b/src/validation.h @@ -150,9 +150,6 @@ extern uint256 hashAssumeValid; /** Minimum work we will assume exists on some valid chain. */ extern arith_uint256 nMinimumChainWork; -/** Best header we've seen so far (used for getheaders queries' starting points). */ -extern CBlockIndex *pindexBestHeader; - /** Documentation for argument 'checklevel'. */ extern const std::vector CHECKLEVEL_DOC; @@ -881,6 +878,9 @@ public: */ std::set m_failed_blocks; + /** Best header we've seen so far (used for getheaders queries' starting points). */ + CBlockIndex* m_best_header = nullptr; + //! The total number of bytes available for us to use across all in-memory //! coins caches. This will be split somehow across chainstates. int64_t m_total_coinstip_cache{0};