diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp --- a/src/interfaces/chain.cpp +++ b/src/interfaces/chain.cpp @@ -347,10 +347,10 @@ bool getPruneMode() override { return ::fPruneMode; } bool p2pEnabled() override { return g_connman != nullptr; } bool isReadyToBroadcast() override { - return !::fImporting && !::fReindex && !IsInitialBlockDownload(); + return !::fImporting && !::fReindex && !isInitialBlockDownload(); } bool isInitialBlockDownload() override { - return IsInitialBlockDownload(); + return ::ChainstateActive().IsInitialBlockDownload(); } bool shutdownRequested() override { return ShutdownRequested(); } int64_t getAdjustedTime() override { return GetAdjustedTime(); } diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -218,7 +218,7 @@ return GuessVerificationProgress(Params().TxData(), tip); } bool isInitialBlockDownload() override { - return IsInitialBlockDownload(); + return ::ChainstateActive().IsInitialBlockDownload(); } bool getReindex() override { return ::fReindex; } bool getImporting() override { return ::fImporting; } diff --git a/src/net_processing.cpp b/src/net_processing.cpp --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1399,7 +1399,8 @@ // 3. This is currently the best block we're aware of. We haven't updated // the tip yet so we have no way to check this directly here. Instead we // just check that there are currently no other blocks in flight. - else if (state.IsValid() && !IsInitialBlockDownload() && + else if (state.IsValid() && + !::ChainstateActive().IsInitialBlockDownload() && mapBlocksInFlight.count(hash) == mapBlocksInFlight.size()) { if (it != mapBlockSource.end()) { MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, connman); @@ -2003,7 +2004,8 @@ } // If we're in IBD, we want outbound peers that will serve us a useful // chain. Disconnect peers that are on chains with insufficient work. - if (IsInitialBlockDownload() && nCount != MAX_HEADERS_RESULTS) { + if (::ChainstateActive().IsInitialBlockDownload() && + nCount != MAX_HEADERS_RESULTS) { // When nCount < MAX_HEADERS_RESULTS, we know we have no more // headers to fetch from this peer. if (nodestate->pindexBestKnownBlock && @@ -2244,7 +2246,7 @@ if (!pfrom->fInbound) { // Advertise our address - if (fListen && !IsInitialBlockDownload()) { + if (fListen && !::ChainstateActive().IsInitialBlockDownload()) { CAddress addr = GetLocalAddress(&pfrom->addr, pfrom->GetLocalServices()); FastRandomContext insecure_rand; @@ -2497,7 +2499,7 @@ "protocol peer=%d\n", inv.hash.ToString(), pfrom->GetId()); } else if (!fAlreadyHave && !fImporting && !fReindex && - !IsInitialBlockDownload()) { + !::ChainstateActive().IsInitialBlockDownload()) { RequestTx(State(pfrom->GetId()), TxId(inv.hash), nNow); } } @@ -2681,7 +2683,8 @@ } LOCK(cs_main); - if (IsInitialBlockDownload() && !pfrom->HasPermission(PF_NOBAN)) { + if (::ChainstateActive().IsInitialBlockDownload() && + !pfrom->HasPermission(PF_NOBAN)) { LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in " "initial block download\n", @@ -2984,7 +2987,7 @@ if (!LookupBlockIndex(cmpctblock.header.hashPrevBlock)) { // Doesn't connect (or is genesis), instead of DoSing in // AcceptBlockHeader, request deeper headers - if (!IsInitialBlockDownload()) { + if (!::ChainstateActive().IsInitialBlockDownload()) { connman->PushMessage( pfrom, msgMaker.Make( NetMsgType::GETHEADERS, @@ -3378,8 +3381,8 @@ // unless we're still syncing with the network. Such an unrequested // block may still be processed, subject to the conditions in // AcceptBlock(). - bool forceProcessing = - pfrom->HasPermission(PF_NOBAN) && !IsInitialBlockDownload(); + bool forceProcessing = pfrom->HasPermission(PF_NOBAN) && + !::ChainstateActive().IsInitialBlockDownload(); const uint256 hash(pblock->GetHash()); { LOCK(cs_main); @@ -4216,7 +4219,8 @@ // Address refresh broadcast int64_t nNow = GetTimeMicros(); - if (!IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) { + if (!::ChainstateActive().IsInitialBlockDownload() && + pto->nNextLocalAddrSend < nNow) { AdvertiseLocal(pto); pto->nNextLocalAddrSend = PoissonNextSend(nNow, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL); @@ -4704,7 +4708,8 @@ // std::vector vGetData; if (!pto->fClient && - ((fFetch && !pto->m_limited_node) || !IsInitialBlockDownload()) && + ((fFetch && !pto->m_limited_node) || + !::ChainstateActive().IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { std::vector vToDownload; NodeId staller = -1; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1534,7 +1534,8 @@ obj.pushKV("mediantime", int64_t(tip->GetMedianTimePast())); obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip)); - obj.pushKV("initialblockdownload", IsInitialBlockDownload()); + obj.pushKV("initialblockdownload", + ::ChainstateActive().IsInitialBlockDownload()); obj.pushKV("chainwork", tip->nChainWork.GetHex()); obj.pushKV("size_on_disk", CalculateCurrentUsage()); obj.pushKV("pruned", fPruneMode); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -562,9 +562,9 @@ "Bitcoin is not connected!"); } - if (IsInitialBlockDownload()) { - throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, - "Bitcoin is downloading blocks..."); + if (::ChainstateActive().IsInitialBlockDownload()) { + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME + " is in initial sync and waiting for blocks..."); } static unsigned int nTransactionsUpdatedLast; diff --git a/src/validation.h b/src/validation.h --- a/src/validation.h +++ b/src/validation.h @@ -376,12 +376,6 @@ */ void ThreadScriptCheck(int worker_num); -/** - * Check whether we are doing an initial block download (synchronizing from disk - * or network) - */ -bool IsInitialBlockDownload(); - /** * Retrieve a transaction (from memory pool, or from disk, if possible). */ @@ -767,6 +761,14 @@ */ std::set m_failed_blocks; + /** + * Whether this chainstate is undergoing initial block download. + * + * Mutable because we need to be able to mark IsInitialBlockDownload() + * const, which latches this for caching purposes. + */ + mutable std::atomic m_cached_finished_ibd{false}; + public: CChain m_chain; BlockMap mapBlockIndex GUARDED_BY(cs_main); @@ -856,6 +858,12 @@ void UnloadBlockIndex(); + /** + * Check whether we are doing an initial block download (synchronizing from + * disk or network) + */ + bool IsInitialBlockDownload() const; + private: bool ActivateBestChainStep(const Config &config, CValidationState &state, CBlockIndex *pindexMostWork, diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -838,32 +838,35 @@ return ((nSubsidy / SATOSHI) >> halvings) * SATOSHI; } -bool IsInitialBlockDownload() { - // Once this function has returned false, it must remain false. - static std::atomic latchToFalse{false}; +// Note that though this is marked const, we may end up modifying +// `m_cached_finished_ibd`, which is a performance-related implementation +// detail. This function must be marked `const` so that `CValidationInterface` +// clients (which are given a `const CChainState*`) can call it. +// +bool CChainState::IsInitialBlockDownload() const { // Optimization: pre-test latch before taking the lock. - if (latchToFalse.load(std::memory_order_relaxed)) { + if (m_cached_finished_ibd.load(std::memory_order_relaxed)) { return false; } LOCK(cs_main); - if (latchToFalse.load(std::memory_order_relaxed)) { + if (m_cached_finished_ibd.load(std::memory_order_relaxed)) { return false; } if (fImporting || fReindex) { return true; } - if (::ChainActive().Tip() == nullptr) { + if (m_chain.Tip() == nullptr) { return true; } - if (::ChainActive().Tip()->nChainWork < nMinimumChainWork) { + if (m_chain.Tip()->nChainWork < nMinimumChainWork) { return true; } - if (::ChainActive().Tip()->GetBlockTime() < (GetTime() - nMaxTipAge)) { + if (m_chain.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge)) { return true; } LogPrintf("Leaving InitialBlockDownload (latching to false)\n"); - latchToFalse.store(true, std::memory_order_relaxed); + m_cached_finished_ibd.store(true, std::memory_order_relaxed); return false; } @@ -895,7 +898,7 @@ // Before we get past initial download, we cannot reliably alert about forks // (we assume we don't get stuck on a fork before finishing our initial // sync) - if (IsInitialBlockDownload()) { + if (::ChainstateActive().IsInitialBlockDownload()) { return; } @@ -2798,7 +2801,8 @@ if (pindexHeader != pindexHeaderOld) { fNotify = true; - fInitialBlockDownload = IsInitialBlockDownload(); + fInitialBlockDownload = + ::ChainstateActive().IsInitialBlockDownload(); pindexHeaderOld = pindexHeader; } } @@ -4365,7 +4369,7 @@ // values, we should not prune too rapidly. // So when pruning in IBD, increase the buffer a bit to avoid a re-prune // too soon. - if (IsInitialBlockDownload()) { + if (::ChainstateActive().IsInitialBlockDownload()) { // Since this is only relevant during IBD, we use a fixed 10% nBuffer += nPruneTarget / 10; }