From 81fc61f15c11ff8eca4c1e4e239cb6885ecf4817 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 10 Jan 2022 15:26:13 -0500 Subject: [PATCH 1/7] merge bitcoin#24024: Remove cs_main lock annotation from ChainstateManager.m_blockman --- src/test/fuzz/coins_view.cpp | 2 +- src/validation.cpp | 2 +- src/validation.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp index 2367e71dde48..f5fcbcd84c5a 100644 --- a/src/test/fuzz/coins_view.cpp +++ b/src/test/fuzz/coins_view.cpp @@ -270,7 +270,7 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view) CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED}; bool expected_code_path = false; try { - (void)GetUTXOStats(&coins_view_cache, WITH_LOCK(::cs_main, return std::ref(g_setup->m_node.chainman->m_blockman)), stats); + (void)GetUTXOStats(&coins_view_cache, g_setup->m_node.chainman->m_blockman, stats); } catch (const std::logic_error&) { expected_code_path = true; } diff --git a/src/validation.cpp b/src/validation.cpp index 63ee38791e13..479ff886806e 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -5341,7 +5341,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot( // about the snapshot_chainstate. CCoinsViewDB* snapshot_coinsdb = WITH_LOCK(::cs_main, return &snapshot_chainstate.CoinsDB()); - if (!GetUTXOStats(snapshot_coinsdb, WITH_LOCK(::cs_main, return std::ref(m_blockman)), stats, breakpoint_fnc)) { + if (!GetUTXOStats(snapshot_coinsdb, m_blockman, stats, breakpoint_fnc)) { LogPrintf("[snapshot] failed to generate coins stats\n"); return false; } diff --git a/src/validation.h b/src/validation.h index 7803865b8c91..f7ae8db37b38 100644 --- a/src/validation.h +++ b/src/validation.h @@ -858,7 +858,7 @@ class ChainstateManager std::thread m_load_block; //! A single BlockManager instance is shared across each constructed //! chainstate to avoid duplicating block metadata. - BlockManager m_blockman GUARDED_BY(::cs_main); + BlockManager m_blockman; /** * In order to efficiently track invalidity of headers, we keep the set of From d8186dc04e6da9f3b6f6ad749c565d74219a896b 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 2/7] 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 d0294cc4ebca..a95397897a65 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 0741ecc91b7b..cb0e44e21bcb 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 82be1dc73ab8..b1299839d028 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1887,7 +1887,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! @@ -1933,7 +1933,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; } @@ -2019,7 +2019,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); } @@ -2325,9 +2325,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 2deceadf9229..9e3f53a0c221 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 aeba83ae779a..2692cd17368e 100644 --- a/src/masternode/sync.h +++ b/src/masternode/sync.h @@ -75,7 +75,7 @@ class CMasternodeSync 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 384e1febeb3c..2c0c03938c16 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1855,9 +1855,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) @@ -2429,7 +2429,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()); @@ -2819,13 +2819,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. @@ -2882,7 +2882,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", @@ -3814,7 +3814,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 + @@ -3822,7 +3822,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 @@ -3867,8 +3867,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; @@ -4284,7 +4284,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; } @@ -5436,28 +5436,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; @@ -5826,7 +5827,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 120b86d23e6a..c31f3f02a456 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 480c60248e7b..7e5087a6b2ec 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 @@ class BlockManager /** 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 @@ class BlockManager //! 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 @@ class BlockManager } }; -//! 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 f89d8c21640b..eba23b01952b 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -419,9 +419,10 @@ class NodeImpl : public Node 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 @@ class ChainImpl : public Chain 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 65ba16e52ea6..fc268870d5e4 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 accb196633da..9e94ae18c277 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 aebfb40000cd..f8fc13d0817a 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 479ff886806e..dd463bcc5642 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 f7ae8db37b38..9db87d36db93 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 @@ class ChainstateManager */ 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}; From 37162e0a95796f26e7cb17d6eae76cc3c72d7ddf Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sun, 16 Jun 2024 23:14:21 +0000 Subject: [PATCH 3/7] partial bitcoin#23832: Changes time variables from int to chrono includes: - 6111b0d7fac89b7a0a03284ca6ec030ca7f30b99 --- src/net_processing.cpp | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 2c0c03938c16..af94c255a815 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -101,8 +101,8 @@ static constexpr auto HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1ms; * behind headers chain. */ static constexpr int32_t MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT = 4; -/** Timeout for (unprotected) outbound peers to sync to our chainwork, in seconds */ -static constexpr int64_t CHAIN_SYNC_TIMEOUT = 20 * 60; // 20 minutes +/** Timeout for (unprotected) outbound peers to sync to our chainwork */ +static constexpr auto CHAIN_SYNC_TIMEOUT{20min}; /** How frequently to check for stale tips */ static constexpr auto STALE_CHECK_INTERVAL{150s}; // 2.5 minutes (~block interval) /** How frequently to check for extra outbound peers and disconnect */ @@ -421,7 +421,7 @@ class PeerManagerImpl final : public PeerManager void ProcessPeerMsgRet(const PeerMsgRet& ret, CNode& pfrom) EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); /** Consider evicting an outbound peer based on the amount of time they've been behind our tip */ - void ConsiderEviction(CNode& pto, int64_t time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + void ConsiderEviction(CNode& pto, std::chrono::seconds time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** If we have extra outbound peers, try to disconnect the one with the oldest block announcement */ void EvictExtraOutboundPeers(std::chrono::seconds now) EXCLUSIVE_LOCKS_REQUIRED(cs_main); @@ -818,7 +818,7 @@ struct CNodeState { */ struct ChainSyncTimeoutState { //! A timeout used for checking whether our peer has sufficiently synced - int64_t m_timeout{0}; + std::chrono::seconds m_timeout{0s}; //! A header with the work we require on our peer's chain const CBlockIndex* m_work_header{nullptr}; //! After timeout is reached, set to true after sending getheaders @@ -1112,10 +1112,10 @@ bool PeerManagerImpl::TipMayBeStale() { AssertLockHeld(cs_main); const Consensus::Params& consensusParams = m_chainparams.GetConsensus(); - if (count_seconds(m_last_tip_update) == 0) { + if (m_last_tip_update.load() == 0s) { m_last_tip_update = GetTime(); } - return count_seconds(m_last_tip_update) < GetTime() - consensusParams.nPowTargetSpacing * 3 && mapBlocksInFlight.empty(); + return m_last_tip_update.load() < GetTime() - std::chrono::seconds{consensusParams.nPowTargetSpacing * 3} && mapBlocksInFlight.empty(); } bool PeerManagerImpl::CanDirectFetch() @@ -5065,7 +5065,7 @@ bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic& interrupt return fMoreWork; } -void PeerManagerImpl::ConsiderEviction(CNode& pto, int64_t time_in_seconds) +void PeerManagerImpl::ConsiderEviction(CNode& pto, std::chrono::seconds time_in_seconds) { AssertLockHeld(cs_main); @@ -5080,12 +5080,12 @@ void PeerManagerImpl::ConsiderEviction(CNode& pto, int64_t time_in_seconds) // unless it's invalid, in which case we should find that out and // disconnect from them elsewhere). if (state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= m_chainman.ActiveChain().Tip()->nChainWork) { - if (state.m_chain_sync.m_timeout != 0) { - state.m_chain_sync.m_timeout = 0; + if (state.m_chain_sync.m_timeout != 0s) { + state.m_chain_sync.m_timeout = 0s; state.m_chain_sync.m_work_header = nullptr; state.m_chain_sync.m_sent_getheaders = false; } - } else if (state.m_chain_sync.m_timeout == 0 || (state.m_chain_sync.m_work_header != nullptr && state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= state.m_chain_sync.m_work_header->nChainWork)) { + } else if (state.m_chain_sync.m_timeout == 0s || (state.m_chain_sync.m_work_header != nullptr && state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= state.m_chain_sync.m_work_header->nChainWork)) { // Our best block known by this peer is behind our tip, and we're either noticing // that for the first time, OR this peer was able to catch up to some earlier point // where we checked against our tip. @@ -5093,7 +5093,7 @@ void PeerManagerImpl::ConsiderEviction(CNode& pto, int64_t time_in_seconds) state.m_chain_sync.m_timeout = time_in_seconds + CHAIN_SYNC_TIMEOUT; state.m_chain_sync.m_work_header = m_chainman.ActiveChain().Tip(); state.m_chain_sync.m_sent_getheaders = false; - } else if (state.m_chain_sync.m_timeout > 0 && time_in_seconds > state.m_chain_sync.m_timeout) { + } else if (state.m_chain_sync.m_timeout > 0s && time_in_seconds > state.m_chain_sync.m_timeout) { // No evidence yet that our peer has synced to a chain with work equal to that // of our tip, when we first detected it was behind. Send a single getheaders // message to give the peer a chance to update us. @@ -5111,7 +5111,7 @@ void PeerManagerImpl::ConsiderEviction(CNode& pto, int64_t time_in_seconds) state.m_chain_sync.m_work_header->GetBlockHash().ToString()); m_connman.PushMessage(&pto, msgMaker.Make(msg_type, m_chainman.ActiveChain().GetLocator(state.m_chain_sync.m_work_header->pprev), uint256())); state.m_chain_sync.m_sent_getheaders = true; - constexpr int64_t HEADERS_RESPONSE_TIME = 120; // 2 minutes + constexpr auto HEADERS_RESPONSE_TIME{2min}; // Bump the timeout to allow a response, which could clear the timeout // (if the response shows the peer has synced), reset the timeout (if // the peer syncs to the required work but not to our tip), or result @@ -5240,7 +5240,8 @@ void PeerManagerImpl::CheckForStaleTipAndEvictPeers() // Check whether our tip is stale, and if so, allow using an extra // outbound peer if (!fImporting && !fReindex && m_connman.GetNetworkActive() && m_connman.GetUseAddrmanOutgoing() && TipMayBeStale()) { - LogPrintf("Potential stale tip detected, will try using extra outbound peer (last tip update: %d seconds ago)\n", count_seconds(now) - count_seconds(m_last_tip_update)); + LogPrintf("Potential stale tip detected, will try using extra outbound peer (last tip update: %d seconds ago)\n", + count_seconds(now - m_last_tip_update.load())); m_connman.SetTryNewOutboundPeer(true); } else if (m_connman.GetTryNewOutboundPeer()) { m_connman.SetTryNewOutboundPeer(false); @@ -5859,7 +5860,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto) // Check that outbound peers have reasonable chains // GetTime() is used by this anti-DoS logic so we can test this using mocktime - ConsiderEviction(*pto, GetTime()); + ConsiderEviction(*pto, GetTime()); // // Message: getdata (blocks) From e185b5eb7b84297b7abc949646ad8c89c1966f0d Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Wed, 12 Jun 2024 20:27:12 +0000 Subject: [PATCH 4/7] merge bitcoin#23880: Serialize cmpctblock at most once in NewPoWValidBlock this commit will not work with `--enable-c++20` as c++20 does away with aggregate initialization when constructors are declared. a partial backport of bitcoin#24169 will sort that out. --- src/net_processing.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index af94c255a815..543575c948fb 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -2042,6 +2043,8 @@ void PeerManagerImpl::NewPoWValidBlock(const CBlockIndex *pindex, const std::sha nHighestFastAnnounce = pindex->nHeight; uint256 hashBlock(pblock->GetHash()); + const std::shared_future lazy_ser{ + std::async(std::launch::deferred, [&] { return msgMaker.Make(NetMsgType::CMPCTBLOCK, *pcmpctblock); })}; { LOCK(cs_most_recent_block); @@ -2050,7 +2053,7 @@ void PeerManagerImpl::NewPoWValidBlock(const CBlockIndex *pindex, const std::sha most_recent_compact_block = pcmpctblock; } - m_connman.ForEachNode([this, &pcmpctblock, pindex, &msgMaker, &hashBlock](CNode* pnode) { + m_connman.ForEachNode([this, pindex, &lazy_ser, &hashBlock](CNode* pnode) { LockAssertion lock(::cs_main); // TODO: Avoid the repeated-serialization here if (pnode->fDisconnect) @@ -2062,7 +2065,9 @@ void PeerManagerImpl::NewPoWValidBlock(const CBlockIndex *pindex, const std::sha if (state.m_requested_hb_cmpctblocks && !PeerHasHeader(&state, pindex) && PeerHasHeader(&state, pindex->pprev)) { LogPrint(BCLog::NET, "%s sending header-and-ids %s to peer=%d\n", "PeerManager::NewPoWValidBlock", hashBlock.ToString(), pnode->GetId()); - m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::CMPCTBLOCK, *pcmpctblock)); + + const CSerializedNetMsg& ser_cmpctblock{lazy_ser.get()}; + m_connman.PushMessage(pnode, CSerializedNetMsg{ser_cmpctblock.data, ser_cmpctblock.m_type}); state.pindexBestHeaderSent = pindex; } }); From 9107be7efe36bdc22d5d550447854b58f0183842 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sun, 16 Jun 2024 21:14:00 +0000 Subject: [PATCH 5/7] partial bitcoin#24169: Add --enable-c++20 option includes: - fae679065e4ef0c6383bbdd1876aaed6c1e40104 --- src/net.h | 13 ++++++++++--- src/net_processing.cpp | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/net.h b/src/net.h index a0bb7bbba8f1..313b260f0a07 100644 --- a/src/net.h +++ b/src/net.h @@ -131,15 +131,22 @@ struct AddedNodeInfo class CNodeStats; class CClientUIInterface; -struct CSerializedNetMsg -{ +struct CSerializedNetMsg { CSerializedNetMsg() = default; CSerializedNetMsg(CSerializedNetMsg&&) = default; CSerializedNetMsg& operator=(CSerializedNetMsg&&) = default; - // No copying, only moves. + // No implicit copying, only moves. CSerializedNetMsg(const CSerializedNetMsg& msg) = delete; CSerializedNetMsg& operator=(const CSerializedNetMsg&) = delete; + CSerializedNetMsg Copy() const + { + CSerializedNetMsg copy; + copy.data = data; + copy.m_type = m_type; + return copy; + } + std::vector data; std::string m_type; }; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 543575c948fb..8a67e55ea9ae 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -2067,7 +2067,7 @@ void PeerManagerImpl::NewPoWValidBlock(const CBlockIndex *pindex, const std::sha hashBlock.ToString(), pnode->GetId()); const CSerializedNetMsg& ser_cmpctblock{lazy_ser.get()}; - m_connman.PushMessage(pnode, CSerializedNetMsg{ser_cmpctblock.data, ser_cmpctblock.m_type}); + m_connman.PushMessage(pnode, ser_cmpctblock.Copy()); state.pindexBestHeaderSent = pindex; } }); From f3d857b33cffa9d6fc8a859498112def911b3644 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Fri, 17 Jun 2022 14:37:26 -0400 Subject: [PATCH 6/7] merge bitcoin#25404: Use MAX_BLOCKS_TO_ANNOUNCE consistently --- src/net_processing.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 8a67e55ea9ae..9d2cc8d2e517 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -2813,7 +2813,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer, LOCK(cs_main); CNodeState *nodestate = State(pfrom.GetId()); - // If this looks like it could be a block announcement (nCount < + // If this looks like it could be a block announcement (nCount <= // MAX_BLOCKS_TO_ANNOUNCE), use special logic for handling headers that // don't connect: // - Send a getheaders message in response to try to connect the chain. @@ -2821,7 +2821,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer, // don't connect before giving DoS points // - Once a headers message is received that is valid and does connect, // nUnconnectingHeaders gets reset back to 0. - if (!m_chainman.m_blockman.LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) { + 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(m_chainman.m_best_header), uint256())); @@ -5478,7 +5478,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto) // Try sending block announcements via headers // if (pto->CanRelay()) { - // If we have less than MAX_BLOCKS_TO_ANNOUNCE in our + // If we have no more than MAX_BLOCKS_TO_ANNOUNCE in our // list of block hashes we're relaying, and our peer wants // headers announcements, then find the first header // not yet known to our peer but would connect, and send. From 679c422c924f7caba69f2b7fb29960be1a9a2f6c Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Thu, 4 Jul 2024 16:14:11 +0000 Subject: [PATCH 7/7] test: drop genesis block from `blockheader_testnet3` bitcoin#25404 introduces a 10 point penalty for remitting more than MAX_BLOCKS_TO_ANNOUNCE unconnected block headers. Whether they are connected or not is determined by taking the first entry and running its hashPrevBlock through LookupBlockIndex. This new behaviour causes a test failure in p2p_dos_header_tree.py in Dash. Bitcoin doesn't face a test failure with this new behaviour as the first non-fork block in its test data is the dump for block 1 (00000000b873e7 9784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206) but Dash uses block 0 (00000bafbc94add76cb75e2ec92894837288a481e5c005f6563d91623bf8bc2c), the genesis block. By definition of a genesis block, it has a hashPrevBlock of 0, which cannot be looked up. This trips the penalty. This doesn't cause any problems in the field as nobody is expected to ever broadcast the genesis block but it does cause a test failure for us. We need to correct that by getting rid of the genesis block from the test data. --- test/functional/data/blockheader_testnet3.hex | 1 - 1 file changed, 1 deletion(-) diff --git a/test/functional/data/blockheader_testnet3.hex b/test/functional/data/blockheader_testnet3.hex index 246ecd2ac451..23c3356f300f 100644 --- a/test/functional/data/blockheader_testnet3.hex +++ b/test/functional/data/blockheader_testnet3.hex @@ -1,6 +1,5 @@ fork:000000202cbcf83b62913d56f605c0e581a48872839428c92e5eb76cd7ad94bcaf0b0000c41f893a0d296b3b17730b42beb432aedc62473d0e6839910209d7f21af31a9bb9968054ffff0f1efd6b0e00 fork:000000201ed4bb12db90cfb7c68051d82f922b86b24c6a247de6168678c9a489ef020000e5968c16ea68f5f8d6ce1f1a987a7484986710c7cae2ac81ca1b5552f059aeceba968054f0ff0f1ef38f0c00 -010000000000000000000000000000000000000000000000000000000000000000000000c762a6567f3cc092f0684bb62b7e00a84890b990f07cc71a6bb58d64b98e02e0dee1e352f0ff0f1ec3c927e6 020000002cbcf83b62913d56f605c0e581a48872839428c92e5eb76cd7ad94bcaf0b00007f11dcce14075520e8f74cc4ddf092b4e26ebd23b8d8665a1ae5bfc41b58fdb4c3a95e53ffff0f1ef37a0000 02000000c108f2910109954fcdec2f962f1a9094be266cb6aeaae37b345e63247d0400004d29e4f9b2e05a9ac97dd5ae4128b3c0104bdc95aabaa566cc8eeb682e336d0dc4a95e53f0ff0f1e7b190000 02000000c2cc6fa3fe4d9fb73476bc3cf02248aa762af4960399232dbab4fa64620c000058968247522cc488db01996de610d6f3b8c348e748198dc528a305941211c71cc6a95e53f0ff0f1ecacf0000