From 91129d4eb115af48c1836b8d118edb21a3f29363 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 21 Nov 2025 04:25:24 +0700 Subject: [PATCH 01/20] refactor: drop dead code with ERROR_TXINDEX_DISABLED_WHEN_GOV_ENABLED It is used to show this warning: Transaction index can't be disabled with governance validation enabled. Either start with -disablegovernance command line switch or enable transaction index. New message is shown in this situation now; behavior is changed by forcing disabling governance: Warning: You are starting with governance validation disabled. --- src/init.cpp | 5 ----- src/node/chainstate.cpp | 9 --------- src/node/chainstate.h | 4 ---- src/test/util/setup_common.cpp | 3 --- 4 files changed, 21 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 65d2bd8c9256..f4e1a699cb08 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2016,12 +2016,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) args.GetDataDirNet(), fPruneMode, args.GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX), - is_governance_enabled, args.GetBoolArg("-spentindex", DEFAULT_SPENTINDEX), args.GetBoolArg("-timestampindex", DEFAULT_TIMESTAMPINDEX), - args.GetBoolArg("-txindex", DEFAULT_TXINDEX), chainparams.GetConsensus(), - chainparams.NetworkIDString(), fReindexChainState, cache_sizes.block_tree_db, cache_sizes.coins_db, @@ -2050,8 +2047,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?")); case ChainstateLoadingError::ERROR_BAD_DEVNET_GENESIS_BLOCK: return InitError(_("Incorrect or no devnet genesis block found. Wrong datadir for devnet specified?")); - case ChainstateLoadingError::ERROR_TXINDEX_DISABLED_WHEN_GOV_ENABLED: - return InitError(_("Transaction index can't be disabled with governance validation enabled. Either start with -disablegovernance command line switch or enable transaction index.")); case ChainstateLoadingError::ERROR_ADDRIDX_NEEDS_REINDEX: strLoadError = _("You need to rebuild the database using -reindex to enable -addressindex"); break; diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index 8812f7b64829..98f3ae44e15f 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -51,12 +51,9 @@ std::optional LoadChainstate(bool fReset, const fs::path& data_dir, bool fPruneMode, bool is_addrindex_enabled, - bool is_governance_enabled, bool is_spentindex_enabled, bool is_timeindex_enabled, - bool is_txindex_enabled, const Consensus::Params& consensus_params, - const std::string& network_id, bool fReindexChainState, int64_t nBlockTreeDBCache, int64_t nCoinDBCache, @@ -111,12 +108,6 @@ std::optional LoadChainstate(bool fReset, return ChainstateLoadingError::ERROR_LOADING_BLOCK_DB; } - // TODO: Remove this when pruning is fixed. - // See https://github.com/dashpay/dash/pull/1817 and https://github.com/dashpay/dash/pull/1743 - if (is_governance_enabled && !is_txindex_enabled && network_id != CBaseChainParams::REGTEST) { - return ChainstateLoadingError::ERROR_TXINDEX_DISABLED_WHEN_GOV_ENABLED; - } - if (!chainman.BlockIndex().empty() && !chainman.m_blockman.LookupBlockIndex(consensus_params.hashGenesisBlock)) { return ChainstateLoadingError::ERROR_BAD_GENESIS_BLOCK; diff --git a/src/node/chainstate.h b/src/node/chainstate.h index aba1ccaa5fc6..c8d321b89931 100644 --- a/src/node/chainstate.h +++ b/src/node/chainstate.h @@ -37,7 +37,6 @@ enum class ChainstateLoadingError { ERROR_LOADING_BLOCK_DB, ERROR_BAD_GENESIS_BLOCK, ERROR_BAD_DEVNET_GENESIS_BLOCK, - ERROR_TXINDEX_DISABLED_WHEN_GOV_ENABLED, ERROR_ADDRIDX_NEEDS_REINDEX, ERROR_SPENTIDX_NEEDS_REINDEX, ERROR_TIMEIDX_NEEDS_REINDEX, @@ -96,12 +95,9 @@ std::optional LoadChainstate(bool fReset, const fs::path& data_dir, bool fPruneMode, bool is_addrindex_enabled, - bool is_governance_enabled, bool is_spentindex_enabled, bool is_timeindex_enabled, - bool is_txindex_enabled, const Consensus::Params& consensus_params, - const std::string& network_id, bool fReindexChainState, int64_t nBlockTreeDBCache, int64_t nCoinDBCache, diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 5d657d1c4d15..2dfc5dcb66c1 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -321,12 +321,9 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vectorGetDataDirNet(), fPruneMode, m_args.GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX), - !m_args.GetBoolArg("-disablegovernance", !DEFAULT_GOVERNANCE_ENABLE), m_args.GetBoolArg("-spentindex", DEFAULT_SPENTINDEX), m_args.GetBoolArg("-timestampindex", DEFAULT_TIMESTAMPINDEX), - m_args.GetBoolArg("-txindex", DEFAULT_TXINDEX), chainparams.GetConsensus(), - chainparams.NetworkIDString(), m_args.GetBoolArg("-reindex-chainstate", false), m_cache_sizes.block_tree_db, m_cache_sizes.coins_db, From 78c6062536d1d206468ec21472a572240f14f7e2 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 21 Nov 2025 03:30:09 +0700 Subject: [PATCH 02/20] refactor: drop unused includes and forward declarations from masternode/sync and governance/governance --- src/governance/governance.h | 3 --- src/masternode/sync.h | 1 - 2 files changed, 4 deletions(-) diff --git a/src/governance/governance.h b/src/governance/governance.h index 6d4a9bbf9b02..fb3af2d1ff90 100644 --- a/src/governance/governance.h +++ b/src/governance/governance.h @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -46,11 +45,9 @@ class CMasternodeSync; class CNetFulfilledRequestManager; class CSporkManager; class CSuperblock; -class GovernanceSigner; class UniValue; -using CDeterministicMNListPtr = std::shared_ptr; using CSuperblock_sptr = std::shared_ptr; using vote_time_pair_t = std::pair; diff --git a/src/masternode/sync.h b/src/masternode/sync.h index 2af331c64e36..9e6f3b671873 100644 --- a/src/masternode/sync.h +++ b/src/masternode/sync.h @@ -5,7 +5,6 @@ #define BITCOIN_MASTERNODE_SYNC_H #include -#include #include class CConnman; From 285def6c2f817b34e378f88339e7bed0f7364be8 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 20 Nov 2025 18:05:59 +0700 Subject: [PATCH 03/20] refactor: use NetHandler to move scheduler & partially network related code from governance --- src/Makefile.am | 4 ++- src/governance/governance.cpp | 40 ++++++------------------------ src/governance/governance.h | 17 +++++-------- src/governance/net_governance.cpp | 41 +++++++++++++++++++++++++++++++ src/governance/net_governance.h | 29 ++++++++++++++++++++++ src/init.cpp | 7 +++--- src/net_processing.cpp | 8 ++++++ src/net_processing.h | 2 ++ 8 files changed, 100 insertions(+), 48 deletions(-) create mode 100644 src/governance/net_governance.cpp create mode 100644 src/governance/net_governance.h diff --git a/src/Makefile.am b/src/Makefile.am index d62df14146fd..fa0388545e8f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -220,8 +220,9 @@ BITCOIN_CORE_H = \ dsnotificationinterface.h \ governance/classes.h \ governance/common.h \ - governance/governance.h \ governance/exceptions.h \ + governance/governance.h \ + governance/net_governance.h \ governance/object.h \ governance/signing.h \ governance/validators.h \ @@ -508,6 +509,7 @@ libbitcoin_node_a_SOURCES = \ governance/classes.cpp \ governance/exceptions.cpp \ governance/governance.cpp \ + governance/net_governance.cpp \ governance/object.cpp \ governance/signing.cpp \ governance/validators.cpp \ diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp index 108f3690b163..48a93a07adf7 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -85,38 +85,6 @@ CGovernanceManager::~CGovernanceManager() m_db->Store(*this); } -void CGovernanceManager::Schedule(CScheduler& scheduler, CConnman& connman, PeerManager& peerman) -{ - AssertLockNotHeld(cs_store); - AssertLockNotHeld(cs_relay); - - assert(IsValid()); - - scheduler.scheduleEvery( - [this, &connman]() -> void { - if (!m_mn_sync.IsSynced()) return; - - // CHECK OBJECTS WE'VE ASKED FOR, REMOVE OLD ENTRIES - CleanOrphanObjects(); - RequestOrphanObjects(connman); - - // CHECK AND REMOVE - REPROCESS GOVERNANCE OBJECTS - CheckAndRemove(); - }, - std::chrono::minutes{5}); - - scheduler.scheduleEvery( - [this, &peerman]() -> void { - LOCK(cs_relay); - for (const auto& inv : m_relay_invs) { - peerman.RelayInv(inv); - } - m_relay_invs.clear(); - }, - // Tests need tighter timings to avoid timeouts, use more relaxed pacing otherwise - Params().IsMockableChain() ? std::chrono::seconds{1} : std::chrono::seconds{5}); -} - bool CGovernanceManager::LoadCache(bool load_cache) { AssertLockNotHeld(cs_store); @@ -576,6 +544,14 @@ void CGovernanceManager::CheckAndRemove() ToString(), m_requested_hash_time.size()); } +std::vector CGovernanceManager::FetchRelayInventory() +{ + std::vector ret; + LOCK(cs_relay); + swap(ret, m_relay_invs); + return ret; +} + std::shared_ptr CGovernanceManager::FindConstGovernanceObject(const uint256& nHash) const { LOCK(cs_store); diff --git a/src/governance/governance.h b/src/governance/governance.h index fb3af2d1ff90..8546bea7756d 100644 --- a/src/governance/governance.h +++ b/src/governance/governance.h @@ -31,7 +31,6 @@ template class CFlatDB; class CInv; class CNode; -class CScheduler; class PeerManager; class CDeterministicMNList; @@ -290,10 +289,6 @@ class CGovernanceManager : public GovernanceStore, public GovernanceSignerParent EXCLUSIVE_LOCKS_REQUIRED(!cs_store); void Clear() EXCLUSIVE_LOCKS_REQUIRED(!cs_store); - void CheckAndRemove() - EXCLUSIVE_LOCKS_REQUIRED(!cs_store); - void Schedule(CScheduler& scheduler, CConnman& connman, PeerManager& peerman) - EXCLUSIVE_LOCKS_REQUIRED(!cs_store, !cs_relay); // CGovernanceObject bool AreRateChecksEnabled() const { return fRateChecksEnabled; } @@ -378,6 +373,12 @@ class CGovernanceManager : public GovernanceStore, public GovernanceSignerParent std::shared_ptr FindConstGovernanceObject(const uint256& nHash) const EXCLUSIVE_LOCKS_REQUIRED(!cs_store); + // Used by NetGovernance + std::vector FetchRelayInventory() EXCLUSIVE_LOCKS_REQUIRED(!cs_relay); + void CheckAndRemove() EXCLUSIVE_LOCKS_REQUIRED(!cs_store); + void RequestOrphanObjects(CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(!cs_store); + void CleanOrphanObjects() EXCLUSIVE_LOCKS_REQUIRED(!cs_store); + private: // Branches of ProcessMessage [[nodiscard]] MessageProcessingResult SyncObjects(CNode& peer, CConnman& connman) const @@ -449,12 +450,6 @@ class CGovernanceManager : public GovernanceStore, public GovernanceSignerParent void AddCachedTriggers() EXCLUSIVE_LOCKS_REQUIRED(cs_store); - void RequestOrphanObjects(CConnman& connman) - EXCLUSIVE_LOCKS_REQUIRED(!cs_store); - - void CleanOrphanObjects() - EXCLUSIVE_LOCKS_REQUIRED(!cs_store); - void RemoveInvalidVotes() EXCLUSIVE_LOCKS_REQUIRED(cs_store); }; diff --git a/src/governance/net_governance.cpp b/src/governance/net_governance.cpp new file mode 100644 index 000000000000..4156e35bd0f3 --- /dev/null +++ b/src/governance/net_governance.cpp @@ -0,0 +1,41 @@ +// Copyright (c) 2025 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include +#include +#include +#include + +class CConnman; + +void NetGovernance::Schedule(CScheduler& scheduler, CConnman& connman) +{ + if (!m_gov_manager.IsValid()) return; + + scheduler.scheduleEvery( + [this, &connman]() -> void { + if (!m_node_sync.IsSynced()) return; + + // CHECK OBJECTS WE'VE ASKED FOR, REMOVE OLD ENTRIES + m_gov_manager.CleanOrphanObjects(); + m_gov_manager.RequestOrphanObjects(connman); + + // CHECK AND REMOVE - REPROCESS GOVERNANCE OBJECTS + m_gov_manager.CheckAndRemove(); + }, + std::chrono::minutes{5}); + + scheduler.scheduleEvery( + [this]() -> void { + auto relay_invs = m_gov_manager.FetchRelayInventory(); + for (const auto& inv : relay_invs) { + m_peer_manager->PeerRelayInv(inv); + } + }, + // Tests need tighter timings to avoid timeouts, use more relaxed pacing otherwise + Params().IsMockableChain() ? std::chrono::seconds{1} : std::chrono::seconds{5}); +} diff --git a/src/governance/net_governance.h b/src/governance/net_governance.h new file mode 100644 index 000000000000..b779005b519a --- /dev/null +++ b/src/governance/net_governance.h @@ -0,0 +1,29 @@ +// Copyright (c) 2025 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_GOVERNANCE_NET_GOVERNANCE_H +#define BITCOIN_GOVERNANCE_NET_GOVERNANCE_H + +#include + +class CGovernanceManager; +class CMasternodeSync; + +class NetGovernance final : public NetHandler +{ +public: + NetGovernance(PeerManagerInternal* peer_manager, CGovernanceManager& gov_manager, CMasternodeSync& node_sync) : + NetHandler(peer_manager), + m_gov_manager(gov_manager), + m_node_sync(node_sync) + { + } + void Schedule(CScheduler& scheduler, CConnman& connman) override; + +private: + CGovernanceManager& m_gov_manager; + CMasternodeSync& m_node_sync; +}; + +#endif // BITCOIN_GOVERNANCE_NET_GOVERNANCE_H diff --git a/src/init.cpp b/src/init.cpp index f4e1a699cb08..b5b2e295773f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -88,6 +88,7 @@ #include #include #include +#include #include #include #include @@ -2233,6 +2234,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) } return InitError(strprintf(_("Failed to clear governance cache at %s"), file_path)); } + node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.govman, *node.mn_sync)); } // ********************************************************* Step 8: start indexers @@ -2306,10 +2308,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) node.scheduler->scheduleEvery(std::bind(&CMasternodeSync::DoMaintenance, std::ref(*node.mn_sync), std::cref(*node.peerman), std::cref(*node.govman)), std::chrono::seconds{1}); node.scheduler->scheduleEvery(std::bind(&CMasternodeUtils::DoMaintenance, std::ref(*node.connman), std::ref(*node.dmnman), std::ref(*node.mn_sync), node.cj_walletman.get()), std::chrono::minutes{1}); node.scheduler->scheduleEvery(std::bind(&CDeterministicMNManager::DoMaintenance, std::ref(*node.dmnman)), std::chrono::seconds{10}); - - if (node.govman->IsValid()) { - node.govman->Schedule(*node.scheduler, *node.connman, *node.peerman); - } + node.peerman->ScheduleHandlers(*node.scheduler); if (node.mn_activeman) { node.scheduler->scheduleEvery(std::bind(&CCoinJoinServer::DoMaintenance, std::ref(*node.active_ctx->cj_server)), std::chrono::seconds{1}); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 5bbbe2e3c510..afba4be4009e 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -644,6 +644,7 @@ class PeerManagerImpl final : public PeerManager void StartHandlers() override; void StopHandlers() override; void InterruptHandlers() override; + void ScheduleHandlers(CScheduler& scheduler) override; /** Implement PeerManagerInternal */ void PeerMisbehaving(const NodeId pnode, const int howmuch, const std::string& message = "") override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); @@ -1691,6 +1692,13 @@ void PeerManagerImpl::InterruptHandlers() } } +void PeerManagerImpl::ScheduleHandlers(CScheduler& scheduler) +{ + for (auto& handler : m_handlers) { + handler->Schedule(scheduler, m_connman); + } +} + void PeerManagerImpl::UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds) { LOCK(cs_main); diff --git a/src/net_processing.h b/src/net_processing.h index f0ab54554686..15e0809fe096 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -79,6 +79,7 @@ class NetHandler virtual void Start() {} virtual void Stop() {} virtual void Interrupt() {} + virtual void Schedule(CScheduler& scheduler, CConnman& connman) {} virtual void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) {} protected: PeerManagerInternal* m_peer_manager; @@ -169,6 +170,7 @@ class PeerManager : public CValidationInterface, public NetEventsInterface, publ virtual void StartHandlers() = 0; virtual void StopHandlers() = 0; virtual void InterruptHandlers() = 0; + virtual void ScheduleHandlers(CScheduler& scheduler) = 0; }; #endif // BITCOIN_NET_PROCESSING_H From 7e4849a03ee4aa4075e49366d58e6030b022f2fa Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 20 Nov 2025 21:53:23 +0700 Subject: [PATCH 04/20] refactor: inline method CleanOrphanObjects to CGovernanceManager::RequestOrphanObjects It helps to manage amount of public methods of CGovernanceManager --- src/governance/governance.cpp | 32 ++++++++++++++----------------- src/governance/governance.h | 1 - src/governance/net_governance.cpp | 1 - 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp index 48a93a07adf7..c51d6dd986e4 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -1352,11 +1352,23 @@ void CGovernanceManager::UpdatedBlockTip(const CBlockIndex* pindex) void CGovernanceManager::RequestOrphanObjects(CConnman& connman) { AssertLockNotHeld(cs_store); - const CConnman::NodesSnapshot snap{connman, /* cond = */ CConnman::FullyConnectedOnly}; std::vector vecHashesFiltered; + int64_t nNow = GetTime().count(); { LOCK(cs_store); + + // clean up outdated before requesting + const vote_cmm_t::list_t& items = cmmapOrphanVotes.GetItemList(); + for (auto it = items.begin(); it != items.end();) { + auto prevIt = it; + ++it; + const auto& [_, time] = prevIt->value; + if (time < nNow) { + cmmapOrphanVotes.Erase(prevIt->key, prevIt->value); + } + } + std::vector vecHashes; cmmapOrphanVotes.GetKeys(vecHashes); for (const uint256& nHash : vecHashes) { @@ -1366,6 +1378,7 @@ void CGovernanceManager::RequestOrphanObjects(CConnman& connman) } } + const CConnman::NodesSnapshot snap{connman, /* cond = */ CConnman::FullyConnectedOnly}; LogPrint(BCLog::GOBJECT, "CGovernanceObject::RequestOrphanObjects -- number objects = %d\n", vecHashesFiltered.size()); for (const uint256& nHash : vecHashesFiltered) { for (CNode* pnode : snap.Nodes()) { @@ -1377,23 +1390,6 @@ void CGovernanceManager::RequestOrphanObjects(CConnman& connman) } } -void CGovernanceManager::CleanOrphanObjects() -{ - LOCK(cs_store); - const vote_cmm_t::list_t& items = cmmapOrphanVotes.GetItemList(); - - int64_t nNow = GetTime().count(); - - for (auto it = items.begin(); it != items.end();) { - auto prevIt = it; - ++it; - const auto& [vote, time] = prevIt->value; - if (time < nNow) { - cmmapOrphanVotes.Erase(prevIt->key, prevIt->value); - } - } -} - void CGovernanceManager::RemoveInvalidVotes() { AssertLockHeld(cs_store); diff --git a/src/governance/governance.h b/src/governance/governance.h index 8546bea7756d..c28d8ef01d06 100644 --- a/src/governance/governance.h +++ b/src/governance/governance.h @@ -377,7 +377,6 @@ class CGovernanceManager : public GovernanceStore, public GovernanceSignerParent std::vector FetchRelayInventory() EXCLUSIVE_LOCKS_REQUIRED(!cs_relay); void CheckAndRemove() EXCLUSIVE_LOCKS_REQUIRED(!cs_store); void RequestOrphanObjects(CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(!cs_store); - void CleanOrphanObjects() EXCLUSIVE_LOCKS_REQUIRED(!cs_store); private: // Branches of ProcessMessage diff --git a/src/governance/net_governance.cpp b/src/governance/net_governance.cpp index 4156e35bd0f3..92e49ccb4282 100644 --- a/src/governance/net_governance.cpp +++ b/src/governance/net_governance.cpp @@ -21,7 +21,6 @@ void NetGovernance::Schedule(CScheduler& scheduler, CConnman& connman) if (!m_node_sync.IsSynced()) return; // CHECK OBJECTS WE'VE ASKED FOR, REMOVE OLD ENTRIES - m_gov_manager.CleanOrphanObjects(); m_gov_manager.RequestOrphanObjects(connman); // CHECK AND REMOVE - REPROCESS GOVERNANCE OBJECTS From e878592e37740773214957f51f4fe94c05c22b64 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 21 Nov 2025 03:13:03 +0700 Subject: [PATCH 05/20] refactor: drop dependency of masternode/sync on governance by moving relevant code to NetGovernance Please note that circular dependency masternode/sync -> net -> masternode/sync existed for a long time, it's not newly introduced --- src/governance/governance.cpp | 6 +- src/governance/governance.h | 6 +- src/governance/net_governance.cpp | 203 +++++++++++++++++++++++- src/governance/net_governance.h | 11 +- src/init.cpp | 3 +- src/masternode/sync.cpp | 202 +---------------------- src/masternode/sync.h | 10 +- src/net_processing.cpp | 6 + src/net_processing.h | 1 + test/lint/lint-circular-dependencies.py | 2 +- 10 files changed, 230 insertions(+), 220 deletions(-) diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp index c51d6dd986e4..f54cb27eae2a 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -1055,14 +1055,14 @@ void CGovernanceManager::RequestGovernanceObject(CNode* pfrom, const uint256& nH connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::MNGOVERNANCESYNC, nHash, filter)); } -int CGovernanceManager::RequestGovernanceObjectVotes(CNode& peer, CConnman& connman, const PeerManager& peerman) const +int CGovernanceManager::RequestGovernanceObjectVotes(CNode& peer, CConnman& connman, const PeerManagerInternal* peerman) const { const std::vector vNodeCopy{&peer}; return RequestGovernanceObjectVotes(vNodeCopy, connman, peerman); } int CGovernanceManager::RequestGovernanceObjectVotes(const std::vector& vNodesCopy, CConnman& connman, - const PeerManager& peerman) const + const PeerManagerInternal* peerman) const { AssertLockNotHeld(cs_store); @@ -1147,7 +1147,7 @@ int CGovernanceManager::RequestGovernanceObjectVotes(const std::vector& // stop early to prevent setAskFor overflow { LOCK(::cs_main); - size_t nProjectedSize = peerman.GetRequestedObjectCount(pnode->GetId()) + nProjectedVotes; + size_t nProjectedSize = peerman->PeerGetRequestedObjectCount(pnode->GetId()) + nProjectedVotes; if (nProjectedSize > MAX_INV_SZ) continue; // to early to ask the same node if (mapAskedRecently[nHashGovobj].count(pnode->addr)) continue; diff --git a/src/governance/governance.h b/src/governance/governance.h index c28d8ef01d06..a9242e4e29b7 100644 --- a/src/governance/governance.h +++ b/src/governance/governance.h @@ -31,7 +31,7 @@ template class CFlatDB; class CInv; class CNode; -class PeerManager; +class PeerManagerInternal; class CDeterministicMNList; class CDeterministicMNManager; @@ -312,10 +312,10 @@ class CGovernanceManager : public GovernanceStore, public GovernanceSignerParent EXCLUSIVE_LOCKS_REQUIRED(!cs_store); bool ProcessVoteAndRelay(const CGovernanceVote& vote, CGovernanceException& exception, CConnman& connman) override EXCLUSIVE_LOCKS_REQUIRED(!cs_store, !cs_relay); - int RequestGovernanceObjectVotes(CNode& peer, CConnman& connman, const PeerManager& peerman) const + int RequestGovernanceObjectVotes(CNode& peer, CConnman& connman, const PeerManagerInternal* peerman) const EXCLUSIVE_LOCKS_REQUIRED(!cs_store); int RequestGovernanceObjectVotes(const std::vector& vNodesCopy, CConnman& connman, - const PeerManager& peerman) const + const PeerManagerInternal* peerman) const EXCLUSIVE_LOCKS_REQUIRED(!cs_store); [[nodiscard]] MessageProcessingResult ProcessMessage(CNode& peer, CConnman& connman, std::string_view msg_type, CDataStream& vRecv) diff --git a/src/governance/net_governance.cpp b/src/governance/net_governance.cpp index 92e49ccb4282..aee1513b1f67 100644 --- a/src/governance/net_governance.cpp +++ b/src/governance/net_governance.cpp @@ -6,16 +6,29 @@ #include #include +#include #include #include +#include +#include +#include #include +#include +#include // TODO: remove it; required for DEFAULT_SYNC_MEMPOOL class CConnman; void NetGovernance::Schedule(CScheduler& scheduler, CConnman& connman) { - if (!m_gov_manager.IsValid()) return; + scheduler.scheduleEvery( + [this, &connman]() -> void { + if (ShutdownRequested()) return; + ProcessTick(connman); + }, + std::chrono::seconds{1}); + // Code below is meant to be running only if governance validation is enabled + if (!m_gov_manager.IsValid()) return; scheduler.scheduleEvery( [this, &connman]() -> void { if (!m_node_sync.IsSynced()) return; @@ -38,3 +51,191 @@ void NetGovernance::Schedule(CScheduler& scheduler, CConnman& connman) // Tests need tighter timings to avoid timeouts, use more relaxed pacing otherwise Params().IsMockableChain() ? std::chrono::seconds{1} : std::chrono::seconds{5}); } + +void NetGovernance::SendGovernanceSyncRequest(CNode* pnode, CConnman& connman) const +{ + CNetMsgMaker msgMaker(pnode->GetCommonVersion()); + CBloomFilter filter; + connman.PushMessage(pnode, msgMaker.Make(NetMsgType::MNGOVERNANCESYNC, uint256(), filter)); +} + +void NetGovernance::ProcessTick(CConnman& connman) +{ + assert(m_netfulfilledman.IsValid()); + + static int nTick = 0; + nTick++; + + const static int64_t nSyncStart = TicksSinceEpoch(SystemClock::now()); + const static std::string strAllow = strprintf("allow-sync-%lld", nSyncStart); + + // reset the sync process if the last call to this function was more than 60 minutes ago (client was in sleep mode) + static int64_t nTimeLastProcess = GetTime(); + if (!Params().IsMockableChain() && GetTime() - nTimeLastProcess > 60 * 60 && !connman.IsActiveMasternode()) { + LogPrintf("NetGovernance::ProcessTick -- WARNING: no actions for too long, restarting sync...\n"); + m_node_sync.Reset(true); + nTimeLastProcess = GetTime(); + return; + } + + if(GetTime() - nTimeLastProcess < MASTERNODE_SYNC_TICK_SECONDS) { + // too early, nothing to do here + return; + } + + nTimeLastProcess = GetTime(); + const CConnman::NodesSnapshot snap{connman, /* cond = */ CConnman::FullyConnectedOnly}; + + // gradually request the rest of the votes after sync finished + if(m_node_sync.IsSynced()) { + m_gov_manager.RequestGovernanceObjectVotes(snap.Nodes(), connman, m_peer_manager); + return; + } + + // Calculate "progress" for LOG reporting / GUI notification + int attempt = m_node_sync.GetAttempt(); + int asset_id = m_node_sync.GetAssetID(); + double nSyncProgress = double(attempt + (asset_id - 1) * 8) / (8*4); + LogPrint(BCLog::MNSYNC, "CMasternodeSync::ProcessTick -- nTick %d asset_id %d nTriedPeerCount %d nSyncProgress %f\n", nTick, asset_id, attempt, nSyncProgress); + uiInterface.NotifyAdditionalDataSyncProgressChanged(nSyncProgress); + + for (auto& pnode : snap.Nodes()) + { + CNetMsgMaker msgMaker(pnode->GetCommonVersion()); + + // Don't try to sync any data from outbound non-relay "masternode" connections. + // Inbound connection this early is most likely a "masternode" connection + // initiated from another node, so skip it too. + if (!pnode->CanRelay() || (connman.IsActiveMasternode() && pnode->IsInboundConn())) continue; + + { + if ((pnode->HasPermission(NetPermissionFlags::NoBan) || pnode->IsManualConn()) && !m_netfulfilledman.HasFulfilledRequest(pnode->addr, strAllow)) { + m_netfulfilledman.RemoveAllFulfilledRequests(pnode->addr); + m_netfulfilledman.AddFulfilledRequest(pnode->addr, strAllow); + LogPrintf("CMasternodeSync::ProcessTick -- skipping mnsync restrictions for peer=%d\n", pnode->GetId()); + } + + if(m_netfulfilledman.HasFulfilledRequest(pnode->addr, "full-sync")) { + // We already fully synced from this node recently, + // disconnect to free this connection slot for another peer. + pnode->fDisconnect = true; + LogPrintf("CMasternodeSync::ProcessTick -- disconnecting from recently synced peer=%d\n", pnode->GetId()); + continue; + } + + // SPORK : ALWAYS ASK FOR SPORKS AS WE SYNC + + if(!m_netfulfilledman.HasFulfilledRequest(pnode->addr, "spork-sync")) { + // always get sporks first, only request once from each peer + m_netfulfilledman.AddFulfilledRequest(pnode->addr, "spork-sync"); + // get current network sporks + connman.PushMessage(pnode, msgMaker.Make(NetMsgType::GETSPORKS)); + LogPrint(BCLog::MNSYNC, "CMasternodeSync::ProcessTick -- nTick %d asset_id %d -- requesting sporks from peer=%d\n", nTick, asset_id, pnode->GetId()); + } + + if (asset_id == MASTERNODE_SYNC_BLOCKCHAIN) { + int64_t nTimeSyncTimeout = snap.Nodes().size() > 3 ? MASTERNODE_SYNC_TICK_SECONDS : MASTERNODE_SYNC_TIMEOUT_SECONDS; + if (m_node_sync.IsReachedBestHeader() && (GetTime() - m_node_sync.GetLastBump() > nTimeSyncTimeout)) { + // At this point we know that: + // a) there are peers (because we are looping on at least one of them); + // b) we waited for at least MASTERNODE_SYNC_TICK_SECONDS/MASTERNODE_SYNC_TIMEOUT_SECONDS + // (depending on the number of connected peers) since we reached the headers tip the last + // time (i.e. since fReachedBestHeader has been set to true); + // c) there were no blocks (UpdatedBlockTip, NotifyHeaderTip) or headers (AcceptedBlockHeader) + // for at least MASTERNODE_SYNC_TICK_SECONDS/MASTERNODE_SYNC_TIMEOUT_SECONDS (depending on + // the number of connected peers). + // We must be at the tip already, let's move to the next asset. + m_node_sync.SwitchToNextAsset(); + uiInterface.NotifyAdditionalDataSyncProgressChanged(nSyncProgress); + + if (gArgs.GetBoolArg("-syncmempool", DEFAULT_SYNC_MEMPOOL)) { + // Now that the blockchain is synced request the mempool from the connected outbound nodes if possible + for (auto pNodeTmp : snap.Nodes()) { + bool fRequestedEarlier = m_netfulfilledman.HasFulfilledRequest(pNodeTmp->addr, "mempool-sync"); + if (!pNodeTmp->IsInboundConn() && !fRequestedEarlier && !pNodeTmp->IsBlockRelayOnly()) { + m_netfulfilledman.AddFulfilledRequest(pNodeTmp->addr, "mempool-sync"); + connman.PushMessage(pNodeTmp, msgMaker.Make(NetMsgType::MEMPOOL)); + LogPrint(BCLog::MNSYNC, "CMasternodeSync::ProcessTick -- nTick %d asset_id %d -- syncing mempool from peer=%d\n", nTick, asset_id, pNodeTmp->GetId()); + } + } + } + } + } + + // GOVOBJ : SYNC GOVERNANCE ITEMS FROM OUR PEERS + + if(asset_id == MASTERNODE_SYNC_GOVERNANCE) { + if (!m_gov_manager.IsValid()) { + m_node_sync.SwitchToNextAsset(); + return; + } + LogPrint(BCLog::GOBJECT, "CMasternodeSync::ProcessTick -- nTick %d asset_id %d last_bump %lld GetTime() %lld diff %lld\n", nTick, asset_id, m_node_sync.GetLastBump(), GetTime(), GetTime() - m_node_sync.GetLastBump()); + + // check for timeout first + if(GetTime() - m_node_sync.GetLastBump() > MASTERNODE_SYNC_TIMEOUT_SECONDS) { + LogPrint(BCLog::MNSYNC, "CMasternodeSync::ProcessTick -- nTick %d asset_id %d -- timeout\n", nTick, asset_id); + if(attempt == 0) { + LogPrintf("CMasternodeSync::ProcessTick -- WARNING: failed to sync %s\n", m_node_sync.GetAssetName()); + // it's kind of ok to skip this for now, hopefully we'll catch up later? + } + m_node_sync.SwitchToNextAsset(); + return; + } + + // only request obj sync once from each peer + if(m_netfulfilledman.HasFulfilledRequest(pnode->addr, "governance-sync")) { + // will request votes on per-obj basis from each node in a separate loop below + // to avoid deadlocks here + continue; + } + m_netfulfilledman.AddFulfilledRequest(pnode->addr, "governance-sync"); + + m_node_sync.BumpAttempt(); + + SendGovernanceSyncRequest(pnode, connman); + + break; //this will cause each peer to get one request each six seconds for the various assets we need + } + } + } + + + if (asset_id != MASTERNODE_SYNC_GOVERNANCE) { + // looped through all nodes and not syncing governance yet/already, release them + return; + } + + // request votes on per-obj basis from each node + for (const auto& pnode : snap.Nodes()) { + if(!m_netfulfilledman.HasFulfilledRequest(pnode->addr, "governance-sync")) { + continue; // to early for this node + } + int nObjsLeftToAsk = m_gov_manager.RequestGovernanceObjectVotes(*pnode, connman, m_peer_manager); + // check for data + if(nObjsLeftToAsk == 0) { + static int64_t nTimeNoObjectsLeft = 0; + static int nLastTick = 0; + static int nLastVotes = 0; + if(nTimeNoObjectsLeft == 0) { + // asked all objects for votes for the first time + nTimeNoObjectsLeft = GetTime(); + } + // make sure the condition below is checked only once per tick + if(nLastTick == nTick) continue; + if (GetTime() - nTimeNoObjectsLeft > MASTERNODE_SYNC_TIMEOUT_SECONDS && + m_gov_manager.GetVoteCount() - nLastVotes < std::max(int(0.0001 * nLastVotes), MASTERNODE_SYNC_TICK_SECONDS)) { + // We already asked for all objects, waited for MASTERNODE_SYNC_TIMEOUT_SECONDS + // after that and less then 0.01% or MASTERNODE_SYNC_TICK_SECONDS + // (i.e. 1 per second) votes were received during the last tick. + // We can be pretty sure that we are done syncing. + LogPrintf("CMasternodeSync::ProcessTick -- nTick %d asset_id %d -- asked for all objects, nothing to do\n", nTick, MASTERNODE_SYNC_GOVERNANCE); + // reset nTimeNoObjectsLeft to be able to use the same condition on resync + nTimeNoObjectsLeft = 0; + m_node_sync.SwitchToNextAsset(); + return; + } + nLastTick = nTick; + nLastVotes = m_gov_manager.GetVoteCount(); + } + } +} diff --git a/src/governance/net_governance.h b/src/governance/net_governance.h index b779005b519a..f73c8d066566 100644 --- a/src/governance/net_governance.h +++ b/src/governance/net_governance.h @@ -9,21 +9,26 @@ class CGovernanceManager; class CMasternodeSync; - +class CNetFulfilledRequestManager; class NetGovernance final : public NetHandler { public: - NetGovernance(PeerManagerInternal* peer_manager, CGovernanceManager& gov_manager, CMasternodeSync& node_sync) : + NetGovernance(PeerManagerInternal* peer_manager, CGovernanceManager& gov_manager, CMasternodeSync& node_sync, CNetFulfilledRequestManager& netfulfilledman) : NetHandler(peer_manager), m_gov_manager(gov_manager), - m_node_sync(node_sync) + m_node_sync(node_sync), + m_netfulfilledman(netfulfilledman) { } void Schedule(CScheduler& scheduler, CConnman& connman) override; private: + void SendGovernanceSyncRequest(CNode* pnode, CConnman& connman) const; + void ProcessTick(CConnman& connman); + CGovernanceManager& m_gov_manager; CMasternodeSync& m_node_sync; + CNetFulfilledRequestManager& m_netfulfilledman; }; #endif // BITCOIN_GOVERNANCE_NET_GOVERNANCE_H diff --git a/src/init.cpp b/src/init.cpp index b5b2e295773f..82b4b47eb326 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2234,8 +2234,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) } return InitError(strprintf(_("Failed to clear governance cache at %s"), file_path)); } - node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.govman, *node.mn_sync)); } + node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.govman, *node.mn_sync, *node.netfulfilledman)); // ********************************************************* Step 8: start indexers if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) { @@ -2305,7 +2305,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) if (node.active_ctx) node.active_ctx->Start(*node.connman, *node.peerman); node.scheduler->scheduleEvery(std::bind(&CNetFulfilledRequestManager::DoMaintenance, std::ref(*node.netfulfilledman)), std::chrono::minutes{1}); - node.scheduler->scheduleEvery(std::bind(&CMasternodeSync::DoMaintenance, std::ref(*node.mn_sync), std::cref(*node.peerman), std::cref(*node.govman)), std::chrono::seconds{1}); node.scheduler->scheduleEvery(std::bind(&CMasternodeUtils::DoMaintenance, std::ref(*node.connman), std::ref(*node.dmnman), std::ref(*node.mn_sync), node.cj_walletman.get()), std::chrono::minutes{1}); node.scheduler->scheduleEvery(std::bind(&CDeterministicMNManager::DoMaintenance, std::ref(*node.dmnman)), std::chrono::seconds{10}); node.peerman->ScheduleHandlers(*node.scheduler); diff --git a/src/masternode/sync.cpp b/src/masternode/sync.cpp index 674e6d7994c1..99fd544aaa19 100644 --- a/src/masternode/sync.cpp +++ b/src/masternode/sync.cpp @@ -4,15 +4,12 @@ #include -#include -#include +#include +#include #include -#include #include -#include #include #include -#include CMasternodeSync::CMasternodeSync(CConnman& _connman, CNetFulfilledRequestManager& netfulfilledman) : nTimeAssetSyncStarted{GetTime()}, @@ -114,194 +111,6 @@ void CMasternodeSync::ProcessMessage(const CNode& peer, std::string_view msg_typ LogPrint(BCLog::MNSYNC, "SYNCSTATUSCOUNT -- got inventory count: nItemID=%d nCount=%d peer=%d\n", nItemID, nCount, peer.GetId()); } -void CMasternodeSync::ProcessTick(const PeerManager& peerman, const CGovernanceManager& govman) -{ - assert(m_netfulfilledman.IsValid()); - - static int nTick = 0; - nTick++; - - const static int64_t nSyncStart = TicksSinceEpoch(SystemClock::now()); - const static std::string strAllow = strprintf("allow-sync-%lld", nSyncStart); - - // reset the sync process if the last call to this function was more than 60 minutes ago (client was in sleep mode) - static int64_t nTimeLastProcess = GetTime(); - if (!Params().IsMockableChain() && GetTime() - nTimeLastProcess > 60 * 60 && !connman.IsActiveMasternode()) { - LogPrintf("CMasternodeSync::ProcessTick -- WARNING: no actions for too long, restarting sync...\n"); - Reset(true); - nTimeLastProcess = GetTime(); - return; - } - - if(GetTime() - nTimeLastProcess < MASTERNODE_SYNC_TICK_SECONDS) { - // too early, nothing to do here - return; - } - - nTimeLastProcess = GetTime(); - const CConnman::NodesSnapshot snap{connman, /* cond = */ CConnman::FullyConnectedOnly}; - - // gradually request the rest of the votes after sync finished - if(IsSynced()) { - govman.RequestGovernanceObjectVotes(snap.Nodes(), connman, peerman); - return; - } - - // Calculate "progress" for LOG reporting / GUI notification - double nSyncProgress = double(nTriedPeerCount + (nCurrentAsset - 1) * 8) / (8*4); - LogPrint(BCLog::MNSYNC, "CMasternodeSync::ProcessTick -- nTick %d nCurrentAsset %d nTriedPeerCount %d nSyncProgress %f\n", nTick, nCurrentAsset, nTriedPeerCount, nSyncProgress); - uiInterface.NotifyAdditionalDataSyncProgressChanged(nSyncProgress); - - for (auto& pnode : snap.Nodes()) - { - CNetMsgMaker msgMaker(pnode->GetCommonVersion()); - - // Don't try to sync any data from outbound non-relay "masternode" connections. - // Inbound connection this early is most likely a "masternode" connection - // initiated from another node, so skip it too. - if (!pnode->CanRelay() || (connman.IsActiveMasternode() && pnode->IsInboundConn())) continue; - - { - if ((pnode->HasPermission(NetPermissionFlags::NoBan) || pnode->IsManualConn()) && !m_netfulfilledman.HasFulfilledRequest(pnode->addr, strAllow)) { - m_netfulfilledman.RemoveAllFulfilledRequests(pnode->addr); - m_netfulfilledman.AddFulfilledRequest(pnode->addr, strAllow); - LogPrintf("CMasternodeSync::ProcessTick -- skipping mnsync restrictions for peer=%d\n", pnode->GetId()); - } - - if(m_netfulfilledman.HasFulfilledRequest(pnode->addr, "full-sync")) { - // We already fully synced from this node recently, - // disconnect to free this connection slot for another peer. - pnode->fDisconnect = true; - LogPrintf("CMasternodeSync::ProcessTick -- disconnecting from recently synced peer=%d\n", pnode->GetId()); - continue; - } - - // SPORK : ALWAYS ASK FOR SPORKS AS WE SYNC - - if(!m_netfulfilledman.HasFulfilledRequest(pnode->addr, "spork-sync")) { - // always get sporks first, only request once from each peer - m_netfulfilledman.AddFulfilledRequest(pnode->addr, "spork-sync"); - // get current network sporks - connman.PushMessage(pnode, msgMaker.Make(NetMsgType::GETSPORKS)); - LogPrint(BCLog::MNSYNC, "CMasternodeSync::ProcessTick -- nTick %d nCurrentAsset %d -- requesting sporks from peer=%d\n", nTick, nCurrentAsset, pnode->GetId()); - } - - if (nCurrentAsset == MASTERNODE_SYNC_BLOCKCHAIN) { - int64_t nTimeSyncTimeout = snap.Nodes().size() > 3 ? MASTERNODE_SYNC_TICK_SECONDS : MASTERNODE_SYNC_TIMEOUT_SECONDS; - if (fReachedBestHeader && (GetTime() - nTimeLastBumped > nTimeSyncTimeout)) { - // At this point we know that: - // a) there are peers (because we are looping on at least one of them); - // b) we waited for at least MASTERNODE_SYNC_TICK_SECONDS/MASTERNODE_SYNC_TIMEOUT_SECONDS - // (depending on the number of connected peers) since we reached the headers tip the last - // time (i.e. since fReachedBestHeader has been set to true); - // c) there were no blocks (UpdatedBlockTip, NotifyHeaderTip) or headers (AcceptedBlockHeader) - // for at least MASTERNODE_SYNC_TICK_SECONDS/MASTERNODE_SYNC_TIMEOUT_SECONDS (depending on - // the number of connected peers). - // We must be at the tip already, let's move to the next asset. - SwitchToNextAsset(); - uiInterface.NotifyAdditionalDataSyncProgressChanged(nSyncProgress); - - if (gArgs.GetBoolArg("-syncmempool", DEFAULT_SYNC_MEMPOOL)) { - // Now that the blockchain is synced request the mempool from the connected outbound nodes if possible - for (auto pNodeTmp : snap.Nodes()) { - bool fRequestedEarlier = m_netfulfilledman.HasFulfilledRequest(pNodeTmp->addr, "mempool-sync"); - if (!pNodeTmp->IsInboundConn() && !fRequestedEarlier && !pNodeTmp->IsBlockRelayOnly()) { - m_netfulfilledman.AddFulfilledRequest(pNodeTmp->addr, "mempool-sync"); - connman.PushMessage(pNodeTmp, msgMaker.Make(NetMsgType::MEMPOOL)); - LogPrint(BCLog::MNSYNC, "CMasternodeSync::ProcessTick -- nTick %d nCurrentAsset %d -- syncing mempool from peer=%d\n", nTick, nCurrentAsset, pNodeTmp->GetId()); - } - } - } - } - } - - // GOVOBJ : SYNC GOVERNANCE ITEMS FROM OUR PEERS - - if(nCurrentAsset == MASTERNODE_SYNC_GOVERNANCE) { - if (!govman.IsValid()) { - SwitchToNextAsset(); - return; - } - LogPrint(BCLog::GOBJECT, "CMasternodeSync::ProcessTick -- nTick %d nCurrentAsset %d nTimeLastBumped %lld GetTime() %lld diff %lld\n", nTick, nCurrentAsset, nTimeLastBumped, GetTime(), GetTime() - nTimeLastBumped); - - // check for timeout first - if(GetTime() - nTimeLastBumped > MASTERNODE_SYNC_TIMEOUT_SECONDS) { - LogPrint(BCLog::MNSYNC, "CMasternodeSync::ProcessTick -- nTick %d nCurrentAsset %d -- timeout\n", nTick, nCurrentAsset); - if(nTriedPeerCount == 0) { - LogPrintf("CMasternodeSync::ProcessTick -- WARNING: failed to sync %s\n", GetAssetName()); - // it's kind of ok to skip this for now, hopefully we'll catch up later? - } - SwitchToNextAsset(); - return; - } - - // only request obj sync once from each peer - if(m_netfulfilledman.HasFulfilledRequest(pnode->addr, "governance-sync")) { - // will request votes on per-obj basis from each node in a separate loop below - // to avoid deadlocks here - continue; - } - m_netfulfilledman.AddFulfilledRequest(pnode->addr, "governance-sync"); - - nTriedPeerCount++; - - SendGovernanceSyncRequest(pnode); - - break; //this will cause each peer to get one request each six seconds for the various assets we need - } - } - } - - - if (nCurrentAsset != MASTERNODE_SYNC_GOVERNANCE) { - // looped through all nodes and not syncing governance yet/already, release them - return; - } - - // request votes on per-obj basis from each node - for (const auto& pnode : snap.Nodes()) { - if(!m_netfulfilledman.HasFulfilledRequest(pnode->addr, "governance-sync")) { - continue; // to early for this node - } - int nObjsLeftToAsk = govman.RequestGovernanceObjectVotes(*pnode, connman, peerman); - // check for data - if(nObjsLeftToAsk == 0) { - static int64_t nTimeNoObjectsLeft = 0; - static int nLastTick = 0; - static int nLastVotes = 0; - if(nTimeNoObjectsLeft == 0) { - // asked all objects for votes for the first time - nTimeNoObjectsLeft = GetTime(); - } - // make sure the condition below is checked only once per tick - if(nLastTick == nTick) continue; - if (GetTime() - nTimeNoObjectsLeft > MASTERNODE_SYNC_TIMEOUT_SECONDS && - govman.GetVoteCount() - nLastVotes < std::max(int(0.0001 * nLastVotes), MASTERNODE_SYNC_TICK_SECONDS)) { - // We already asked for all objects, waited for MASTERNODE_SYNC_TIMEOUT_SECONDS - // after that and less then 0.01% or MASTERNODE_SYNC_TICK_SECONDS - // (i.e. 1 per second) votes were received during the last tick. - // We can be pretty sure that we are done syncing. - LogPrintf("CMasternodeSync::ProcessTick -- nTick %d nCurrentAsset %d -- asked for all objects, nothing to do\n", nTick, MASTERNODE_SYNC_GOVERNANCE); - // reset nTimeNoObjectsLeft to be able to use the same condition on resync - nTimeNoObjectsLeft = 0; - SwitchToNextAsset(); - return; - } - nLastTick = nTick; - nLastVotes = govman.GetVoteCount(); - } - } -} - -void CMasternodeSync::SendGovernanceSyncRequest(CNode* pnode) const -{ - CNetMsgMaker msgMaker(pnode->GetCommonVersion()); - - CBloomFilter filter; - - connman.PushMessage(pnode, msgMaker.Make(NetMsgType::MNGOVERNANCESYNC, uint256(), filter)); -} - void CMasternodeSync::AcceptedBlockHeader(const CBlockIndex *pindexNew) { LogPrint(BCLog::MNSYNC, "CMasternodeSync::AcceptedBlockHeader -- pindexNew->nHeight: %d\n", pindexNew->nHeight); @@ -365,10 +174,3 @@ void CMasternodeSync::UpdatedBlockTip(const CBlockIndex *pindexTip, const CBlock LogPrint(BCLog::MNSYNC, "CMasternodeSync::UpdatedBlockTip -- pindexNew->nHeight: %d pindexTip->nHeight: %d fInitialDownload=%d fReachedBestHeader=%d\n", pindexNew->nHeight, pindexTip->nHeight, fInitialDownload, fReachedBestHeader); } - -void CMasternodeSync::DoMaintenance(const PeerManager& peerman, const CGovernanceManager& govman) -{ - if (ShutdownRequested()) return; - - ProcessTick(peerman, govman); -} diff --git a/src/masternode/sync.h b/src/masternode/sync.h index 9e6f3b671873..4cc895afcd7e 100644 --- a/src/masternode/sync.h +++ b/src/masternode/sync.h @@ -10,11 +10,9 @@ class CConnman; class CBlockIndex; class CDataStream; -class CGovernanceManager; class CMasternodeSync; class CNetFulfilledRequestManager; class CNode; -class PeerManager; static constexpr int MASTERNODE_SYNC_BLOCKCHAIN = 1; static constexpr int MASTERNODE_SYNC_GOVERNANCE = 4; @@ -58,29 +56,27 @@ class CMasternodeSync explicit CMasternodeSync(CConnman& _connman, CNetFulfilledRequestManager& netfulfilledman); ~CMasternodeSync(); - void SendGovernanceSyncRequest(CNode* pnode) const; - bool IsBlockchainSynced() const { return nCurrentAsset > MASTERNODE_SYNC_BLOCKCHAIN; } bool IsSynced() const { return nCurrentAsset == MASTERNODE_SYNC_FINISHED; } int GetAssetID() const { return nCurrentAsset; } int GetAttempt() const { return nTriedPeerCount; } + void BumpAttempt() { ++nTriedPeerCount; } void BumpAssetLastTime(const std::string& strFuncName); + int64_t GetLastBump() const { return nTimeLastBumped; } int64_t GetAssetStartTime() const { return nTimeAssetSyncStarted; } std::string GetAssetName() const; std::string GetSyncStatus() const; + bool IsReachedBestHeader() const { return fReachedBestHeader; } void Reset(bool fForce = false, bool fNotifyReset = true); void SwitchToNextAsset(); void ProcessMessage(const CNode& peer, std::string_view msg_type, CDataStream& vRecv) const; - void ProcessTick(const PeerManager& peerman, const CGovernanceManager& govman); void AcceptedBlockHeader(const CBlockIndex *pindexNew); void NotifyHeaderTip(const CBlockIndex *pindexNew, bool fInitialDownload); void UpdatedBlockTip(const CBlockIndex *pindexTip, const CBlockIndex *pindexNew, bool fInitialDownload); - - void DoMaintenance(const PeerManager& peerman, const CGovernanceManager& govman); }; #endif // BITCOIN_MASTERNODE_SYNC_H diff --git a/src/net_processing.cpp b/src/net_processing.cpp index afba4be4009e..c45d2d250655 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -653,6 +653,7 @@ class PeerManagerImpl final : public PeerManager void PeerRelayInvFiltered(const CInv& inv, const CTransaction& relatedTx) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); void PeerRelayInvFiltered(const CInv& inv, const uint256& relatedTxHash) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); void PeerAskPeersForTransaction(const uint256& txid) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); + size_t PeerGetRequestedObjectCount(NodeId nodeid) const override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, ::cs_main); void PeerPostProcessMessage(MessageProcessingResult&& ret) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); private: @@ -6578,6 +6579,11 @@ void PeerManagerImpl::PeerAskPeersForTransaction(const uint256& txid) AskPeersForTransaction(txid); } +size_t PeerManagerImpl::PeerGetRequestedObjectCount(NodeId nodeid) const +{ + return GetRequestedObjectCount(nodeid); +} + void PeerManagerImpl::PeerPostProcessMessage(MessageProcessingResult&& ret) { PostProcessMessage(std::move(ret), /*node=*/-1); diff --git a/src/net_processing.h b/src/net_processing.h index 15e0809fe096..9f3816f9cb5a 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -64,6 +64,7 @@ class PeerManagerInternal virtual void PeerRelayInvFiltered(const CInv& inv, const CTransaction& relatedTx) = 0; virtual void PeerRelayInvFiltered(const CInv& inv, const uint256& relatedTxHash) = 0; virtual void PeerAskPeersForTransaction(const uint256& txid) = 0; + virtual size_t PeerGetRequestedObjectCount(NodeId nodeid) const = 0; virtual void PeerPostProcessMessage(MessageProcessingResult&& ret) = 0; }; diff --git a/test/lint/lint-circular-dependencies.py b/test/lint/lint-circular-dependencies.py index 87a6f11e6fcd..6fd391c65110 100755 --- a/test/lint/lint-circular-dependencies.py +++ b/test/lint/lint-circular-dependencies.py @@ -48,7 +48,7 @@ "evo/specialtxman -> validation -> evo/specialtxman", "governance/classes -> governance/object -> governance/governance -> governance/classes", "governance/governance -> governance/signing -> governance/object -> governance/governance", - "governance/governance -> masternode/sync -> governance/governance", + "masternode/sync -> net -> masternode/sync", "governance/governance -> net_processing -> masternode/active/context -> governance/governance", "governance/governance -> net_processing -> governance/governance", "instantsend/instantsend -> instantsend/signing -> llmq/signing_shares -> net_processing -> instantsend/instantsend", From be0be6b2c50238941d0cb277a85845ee33b040d1 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 21 Nov 2025 15:14:28 +0700 Subject: [PATCH 06/20] chore: fix clang-format and adjust log messages --- src/governance/governance.h | 3 +- src/governance/net_governance.cpp | 79 +++++++++++++++++-------------- src/governance/net_governance.h | 3 +- 3 files changed, 47 insertions(+), 38 deletions(-) diff --git a/src/governance/governance.h b/src/governance/governance.h index a9242e4e29b7..bebc293d2765 100644 --- a/src/governance/governance.h +++ b/src/governance/governance.h @@ -315,8 +315,7 @@ class CGovernanceManager : public GovernanceStore, public GovernanceSignerParent int RequestGovernanceObjectVotes(CNode& peer, CConnman& connman, const PeerManagerInternal* peerman) const EXCLUSIVE_LOCKS_REQUIRED(!cs_store); int RequestGovernanceObjectVotes(const std::vector& vNodesCopy, CConnman& connman, - const PeerManagerInternal* peerman) const - EXCLUSIVE_LOCKS_REQUIRED(!cs_store); + const PeerManagerInternal* peerman) const EXCLUSIVE_LOCKS_REQUIRED(!cs_store); [[nodiscard]] MessageProcessingResult ProcessMessage(CNode& peer, CConnman& connman, std::string_view msg_type, CDataStream& vRecv) EXCLUSIVE_LOCKS_REQUIRED(!cs_store, !cs_relay); diff --git a/src/governance/net_governance.cpp b/src/governance/net_governance.cpp index aee1513b1f67..517cfb821f26 100644 --- a/src/governance/net_governance.cpp +++ b/src/governance/net_governance.cpp @@ -21,11 +21,11 @@ class CConnman; void NetGovernance::Schedule(CScheduler& scheduler, CConnman& connman) { scheduler.scheduleEvery( - [this, &connman]() -> void { - if (ShutdownRequested()) return; - ProcessTick(connman); - }, - std::chrono::seconds{1}); + [this, &connman]() -> void { + if (ShutdownRequested()) return; + ProcessTick(connman); + }, + std::chrono::seconds{1}); // Code below is meant to be running only if governance validation is enabled if (!m_gov_manager.IsValid()) return; @@ -72,13 +72,13 @@ void NetGovernance::ProcessTick(CConnman& connman) // reset the sync process if the last call to this function was more than 60 minutes ago (client was in sleep mode) static int64_t nTimeLastProcess = GetTime(); if (!Params().IsMockableChain() && GetTime() - nTimeLastProcess > 60 * 60 && !connman.IsActiveMasternode()) { - LogPrintf("NetGovernance::ProcessTick -- WARNING: no actions for too long, restarting sync...\n"); + LogPrintf("Sync Tick -- WARNING: no actions for too long, restarting sync...\n"); m_node_sync.Reset(true); nTimeLastProcess = GetTime(); return; } - if(GetTime() - nTimeLastProcess < MASTERNODE_SYNC_TICK_SECONDS) { + if (GetTime() - nTimeLastProcess < MASTERNODE_SYNC_TICK_SECONDS) { // too early, nothing to do here return; } @@ -87,7 +87,7 @@ void NetGovernance::ProcessTick(CConnman& connman) const CConnman::NodesSnapshot snap{connman, /* cond = */ CConnman::FullyConnectedOnly}; // gradually request the rest of the votes after sync finished - if(m_node_sync.IsSynced()) { + if (m_node_sync.IsSynced()) { m_gov_manager.RequestGovernanceObjectVotes(snap.Nodes(), connman, m_peer_manager); return; } @@ -95,12 +95,12 @@ void NetGovernance::ProcessTick(CConnman& connman) // Calculate "progress" for LOG reporting / GUI notification int attempt = m_node_sync.GetAttempt(); int asset_id = m_node_sync.GetAssetID(); - double nSyncProgress = double(attempt + (asset_id - 1) * 8) / (8*4); - LogPrint(BCLog::MNSYNC, "CMasternodeSync::ProcessTick -- nTick %d asset_id %d nTriedPeerCount %d nSyncProgress %f\n", nTick, asset_id, attempt, nSyncProgress); + double nSyncProgress = double(attempt + (asset_id - 1) * 8) / (8 * 4); + LogPrint(BCLog::MNSYNC, "Sync Tick -- nTick %d asset_id %d nTriedPeerCount %d nSyncProgress %f\n", nTick, asset_id, + attempt, nSyncProgress); uiInterface.NotifyAdditionalDataSyncProgressChanged(nSyncProgress); - for (auto& pnode : snap.Nodes()) - { + for (auto& pnode : snap.Nodes()) { CNetMsgMaker msgMaker(pnode->GetCommonVersion()); // Don't try to sync any data from outbound non-relay "masternode" connections. @@ -109,32 +109,35 @@ void NetGovernance::ProcessTick(CConnman& connman) if (!pnode->CanRelay() || (connman.IsActiveMasternode() && pnode->IsInboundConn())) continue; { - if ((pnode->HasPermission(NetPermissionFlags::NoBan) || pnode->IsManualConn()) && !m_netfulfilledman.HasFulfilledRequest(pnode->addr, strAllow)) { + if ((pnode->HasPermission(NetPermissionFlags::NoBan) || pnode->IsManualConn()) && + !m_netfulfilledman.HasFulfilledRequest(pnode->addr, strAllow)) { m_netfulfilledman.RemoveAllFulfilledRequests(pnode->addr); m_netfulfilledman.AddFulfilledRequest(pnode->addr, strAllow); - LogPrintf("CMasternodeSync::ProcessTick -- skipping mnsync restrictions for peer=%d\n", pnode->GetId()); + LogPrintf("Sync Tick -- skipping mnsync restrictions for peer=%d\n", pnode->GetId()); } - if(m_netfulfilledman.HasFulfilledRequest(pnode->addr, "full-sync")) { + if (m_netfulfilledman.HasFulfilledRequest(pnode->addr, "full-sync")) { // We already fully synced from this node recently, // disconnect to free this connection slot for another peer. pnode->fDisconnect = true; - LogPrintf("CMasternodeSync::ProcessTick -- disconnecting from recently synced peer=%d\n", pnode->GetId()); + LogPrintf("Sync Tick -- disconnecting from recently synced peer=%d\n", pnode->GetId()); continue; } // SPORK : ALWAYS ASK FOR SPORKS AS WE SYNC - if(!m_netfulfilledman.HasFulfilledRequest(pnode->addr, "spork-sync")) { + if (!m_netfulfilledman.HasFulfilledRequest(pnode->addr, "spork-sync")) { // always get sporks first, only request once from each peer m_netfulfilledman.AddFulfilledRequest(pnode->addr, "spork-sync"); // get current network sporks connman.PushMessage(pnode, msgMaker.Make(NetMsgType::GETSPORKS)); - LogPrint(BCLog::MNSYNC, "CMasternodeSync::ProcessTick -- nTick %d asset_id %d -- requesting sporks from peer=%d\n", nTick, asset_id, pnode->GetId()); + LogPrint(BCLog::MNSYNC, "Sync Tick -- nTick %d asset_id %d -- requesting sporks from peer=%d\n", nTick, + asset_id, pnode->GetId()); } if (asset_id == MASTERNODE_SYNC_BLOCKCHAIN) { - int64_t nTimeSyncTimeout = snap.Nodes().size() > 3 ? MASTERNODE_SYNC_TICK_SECONDS : MASTERNODE_SYNC_TIMEOUT_SECONDS; + int64_t nTimeSyncTimeout = snap.Nodes().size() > 3 ? MASTERNODE_SYNC_TICK_SECONDS + : MASTERNODE_SYNC_TIMEOUT_SECONDS; if (m_node_sync.IsReachedBestHeader() && (GetTime() - m_node_sync.GetLastBump() > nTimeSyncTimeout)) { // At this point we know that: // a) there are peers (because we are looping on at least one of them); @@ -151,11 +154,14 @@ void NetGovernance::ProcessTick(CConnman& connman) if (gArgs.GetBoolArg("-syncmempool", DEFAULT_SYNC_MEMPOOL)) { // Now that the blockchain is synced request the mempool from the connected outbound nodes if possible for (auto pNodeTmp : snap.Nodes()) { - bool fRequestedEarlier = m_netfulfilledman.HasFulfilledRequest(pNodeTmp->addr, "mempool-sync"); + bool fRequestedEarlier = m_netfulfilledman.HasFulfilledRequest(pNodeTmp->addr, + "mempool-sync"); if (!pNodeTmp->IsInboundConn() && !fRequestedEarlier && !pNodeTmp->IsBlockRelayOnly()) { m_netfulfilledman.AddFulfilledRequest(pNodeTmp->addr, "mempool-sync"); connman.PushMessage(pNodeTmp, msgMaker.Make(NetMsgType::MEMPOOL)); - LogPrint(BCLog::MNSYNC, "CMasternodeSync::ProcessTick -- nTick %d asset_id %d -- syncing mempool from peer=%d\n", nTick, asset_id, pNodeTmp->GetId()); + LogPrint(BCLog::MNSYNC, /* Continued */ + "Sync Tick -- nTick %d asset_id %d -- syncing mempool from peer=%d\n", nTick, + asset_id, pNodeTmp->GetId()); } } } @@ -164,18 +170,19 @@ void NetGovernance::ProcessTick(CConnman& connman) // GOVOBJ : SYNC GOVERNANCE ITEMS FROM OUR PEERS - if(asset_id == MASTERNODE_SYNC_GOVERNANCE) { + if (asset_id == MASTERNODE_SYNC_GOVERNANCE) { if (!m_gov_manager.IsValid()) { m_node_sync.SwitchToNextAsset(); return; } - LogPrint(BCLog::GOBJECT, "CMasternodeSync::ProcessTick -- nTick %d asset_id %d last_bump %lld GetTime() %lld diff %lld\n", nTick, asset_id, m_node_sync.GetLastBump(), GetTime(), GetTime() - m_node_sync.GetLastBump()); + LogPrint(BCLog::GOBJECT, "Sync Tick -- nTick %d asset_id %d last_bump %lld GetTime() %lld diff %lld\n", + nTick, asset_id, m_node_sync.GetLastBump(), GetTime(), GetTime() - m_node_sync.GetLastBump()); // check for timeout first - if(GetTime() - m_node_sync.GetLastBump() > MASTERNODE_SYNC_TIMEOUT_SECONDS) { - LogPrint(BCLog::MNSYNC, "CMasternodeSync::ProcessTick -- nTick %d asset_id %d -- timeout\n", nTick, asset_id); - if(attempt == 0) { - LogPrintf("CMasternodeSync::ProcessTick -- WARNING: failed to sync %s\n", m_node_sync.GetAssetName()); + if (GetTime() - m_node_sync.GetLastBump() > MASTERNODE_SYNC_TIMEOUT_SECONDS) { + LogPrint(BCLog::MNSYNC, "Sync Tick -- nTick %d asset_id %d -- timeout\n", nTick, asset_id); + if (attempt == 0) { + LogPrintf("Sync Tick -- WARNING: failed to sync %s\n", m_node_sync.GetAssetName()); // it's kind of ok to skip this for now, hopefully we'll catch up later? } m_node_sync.SwitchToNextAsset(); @@ -183,7 +190,7 @@ void NetGovernance::ProcessTick(CConnman& connman) } // only request obj sync once from each peer - if(m_netfulfilledman.HasFulfilledRequest(pnode->addr, "governance-sync")) { + if (m_netfulfilledman.HasFulfilledRequest(pnode->addr, "governance-sync")) { // will request votes on per-obj basis from each node in a separate loop below // to avoid deadlocks here continue; @@ -194,7 +201,7 @@ void NetGovernance::ProcessTick(CConnman& connman) SendGovernanceSyncRequest(pnode, connman); - break; //this will cause each peer to get one request each six seconds for the various assets we need + break; // this will cause each peer to get one request each six seconds for the various assets we need } } } @@ -207,28 +214,30 @@ void NetGovernance::ProcessTick(CConnman& connman) // request votes on per-obj basis from each node for (const auto& pnode : snap.Nodes()) { - if(!m_netfulfilledman.HasFulfilledRequest(pnode->addr, "governance-sync")) { + if (!m_netfulfilledman.HasFulfilledRequest(pnode->addr, "governance-sync")) { continue; // to early for this node } int nObjsLeftToAsk = m_gov_manager.RequestGovernanceObjectVotes(*pnode, connman, m_peer_manager); // check for data - if(nObjsLeftToAsk == 0) { + if (nObjsLeftToAsk == 0) { static int64_t nTimeNoObjectsLeft = 0; static int nLastTick = 0; static int nLastVotes = 0; - if(nTimeNoObjectsLeft == 0) { + if (nTimeNoObjectsLeft == 0) { // asked all objects for votes for the first time nTimeNoObjectsLeft = GetTime(); } // make sure the condition below is checked only once per tick - if(nLastTick == nTick) continue; + if (nLastTick == nTick) continue; if (GetTime() - nTimeNoObjectsLeft > MASTERNODE_SYNC_TIMEOUT_SECONDS && - m_gov_manager.GetVoteCount() - nLastVotes < std::max(int(0.0001 * nLastVotes), MASTERNODE_SYNC_TICK_SECONDS)) { + m_gov_manager.GetVoteCount() - nLastVotes < + std::max(int(0.0001 * nLastVotes), MASTERNODE_SYNC_TICK_SECONDS)) { // We already asked for all objects, waited for MASTERNODE_SYNC_TIMEOUT_SECONDS // after that and less then 0.01% or MASTERNODE_SYNC_TICK_SECONDS // (i.e. 1 per second) votes were received during the last tick. // We can be pretty sure that we are done syncing. - LogPrintf("CMasternodeSync::ProcessTick -- nTick %d asset_id %d -- asked for all objects, nothing to do\n", nTick, MASTERNODE_SYNC_GOVERNANCE); + LogPrintf("Sync Tick -- nTick %d asset_id %d -- asked for all objects, nothing to do\n", nTick, + MASTERNODE_SYNC_GOVERNANCE); // reset nTimeNoObjectsLeft to be able to use the same condition on resync nTimeNoObjectsLeft = 0; m_node_sync.SwitchToNextAsset(); diff --git a/src/governance/net_governance.h b/src/governance/net_governance.h index f73c8d066566..bdfb3267cfbd 100644 --- a/src/governance/net_governance.h +++ b/src/governance/net_governance.h @@ -13,7 +13,8 @@ class CNetFulfilledRequestManager; class NetGovernance final : public NetHandler { public: - NetGovernance(PeerManagerInternal* peer_manager, CGovernanceManager& gov_manager, CMasternodeSync& node_sync, CNetFulfilledRequestManager& netfulfilledman) : + NetGovernance(PeerManagerInternal* peer_manager, CGovernanceManager& gov_manager, CMasternodeSync& node_sync, + CNetFulfilledRequestManager& netfulfilledman) : NetHandler(peer_manager), m_gov_manager(gov_manager), m_node_sync(node_sync), From eb9607e446cecc00e6e98e216bbc9fb1a9089aea Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 21 Nov 2025 15:18:19 +0700 Subject: [PATCH 07/20] refactor: move DEFAULT_SYNC_MEMPOOL form validation.h to masternode/sync.h --- src/governance/net_governance.cpp | 1 - src/governance/net_governance.h | 1 + src/masternode/sync.h | 3 +++ src/validation.h | 2 -- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/governance/net_governance.cpp b/src/governance/net_governance.cpp index 517cfb821f26..6f6771658bea 100644 --- a/src/governance/net_governance.cpp +++ b/src/governance/net_governance.cpp @@ -14,7 +14,6 @@ #include #include #include -#include // TODO: remove it; required for DEFAULT_SYNC_MEMPOOL class CConnman; diff --git a/src/governance/net_governance.h b/src/governance/net_governance.h index bdfb3267cfbd..535f1a316572 100644 --- a/src/governance/net_governance.h +++ b/src/governance/net_governance.h @@ -10,6 +10,7 @@ class CGovernanceManager; class CMasternodeSync; class CNetFulfilledRequestManager; + class NetGovernance final : public NetHandler { public: diff --git a/src/masternode/sync.h b/src/masternode/sync.h index 4cc895afcd7e..537761b6340a 100644 --- a/src/masternode/sync.h +++ b/src/masternode/sync.h @@ -14,6 +14,9 @@ class CMasternodeSync; class CNetFulfilledRequestManager; class CNode; +/** Default for -syncmempool */ +static const bool DEFAULT_SYNC_MEMPOOL = true; + static constexpr int MASTERNODE_SYNC_BLOCKCHAIN = 1; static constexpr int MASTERNODE_SYNC_GOVERNANCE = 4; static constexpr int MASTERNODE_SYNC_GOVOBJ = 10; diff --git a/src/validation.h b/src/validation.h index 290f721280c9..6d78bedc6207 100644 --- a/src/validation.h +++ b/src/validation.h @@ -81,8 +81,6 @@ static const bool DEFAULT_CHECKPOINTS_ENABLED = true; /** Default for -persistmempool */ static const bool DEFAULT_PERSIST_MEMPOOL = true; -/** Default for -syncmempool */ -static const bool DEFAULT_SYNC_MEMPOOL = true; /** Default for -stopatheight */ static const int DEFAULT_STOPATHEIGHT = 0; From e7f9218b3cc34ccf8dfc210eb777b62c3f9c63f2 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 21 Nov 2025 15:28:32 +0700 Subject: [PATCH 08/20] refactor: drop a wrapper RequestGovernanceObjectVotes for a single node --- src/governance/governance.cpp | 6 ------ src/governance/governance.h | 2 -- src/governance/net_governance.cpp | 3 ++- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp index f54cb27eae2a..9115fc664dda 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -1055,12 +1055,6 @@ void CGovernanceManager::RequestGovernanceObject(CNode* pfrom, const uint256& nH connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::MNGOVERNANCESYNC, nHash, filter)); } -int CGovernanceManager::RequestGovernanceObjectVotes(CNode& peer, CConnman& connman, const PeerManagerInternal* peerman) const -{ - const std::vector vNodeCopy{&peer}; - return RequestGovernanceObjectVotes(vNodeCopy, connman, peerman); -} - int CGovernanceManager::RequestGovernanceObjectVotes(const std::vector& vNodesCopy, CConnman& connman, const PeerManagerInternal* peerman) const { diff --git a/src/governance/governance.h b/src/governance/governance.h index bebc293d2765..5c78ea46d753 100644 --- a/src/governance/governance.h +++ b/src/governance/governance.h @@ -312,8 +312,6 @@ class CGovernanceManager : public GovernanceStore, public GovernanceSignerParent EXCLUSIVE_LOCKS_REQUIRED(!cs_store); bool ProcessVoteAndRelay(const CGovernanceVote& vote, CGovernanceException& exception, CConnman& connman) override EXCLUSIVE_LOCKS_REQUIRED(!cs_store, !cs_relay); - int RequestGovernanceObjectVotes(CNode& peer, CConnman& connman, const PeerManagerInternal* peerman) const - EXCLUSIVE_LOCKS_REQUIRED(!cs_store); int RequestGovernanceObjectVotes(const std::vector& vNodesCopy, CConnman& connman, const PeerManagerInternal* peerman) const EXCLUSIVE_LOCKS_REQUIRED(!cs_store); [[nodiscard]] MessageProcessingResult ProcessMessage(CNode& peer, CConnman& connman, std::string_view msg_type, diff --git a/src/governance/net_governance.cpp b/src/governance/net_governance.cpp index 6f6771658bea..9799ca72ebb7 100644 --- a/src/governance/net_governance.cpp +++ b/src/governance/net_governance.cpp @@ -216,7 +216,8 @@ void NetGovernance::ProcessTick(CConnman& connman) if (!m_netfulfilledman.HasFulfilledRequest(pnode->addr, "governance-sync")) { continue; // to early for this node } - int nObjsLeftToAsk = m_gov_manager.RequestGovernanceObjectVotes(*pnode, connman, m_peer_manager); + const std::vector vNodeCopy{pnode}; + int nObjsLeftToAsk = m_gov_manager.RequestGovernanceObjectVotes(vNodeCopy, connman, m_peer_manager); // check for data if (nObjsLeftToAsk == 0) { static int64_t nTimeNoObjectsLeft = 0; From 77faacce82821f2f0493f7106a100cc0cc1ff7e8 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sat, 22 Nov 2025 01:34:26 +0700 Subject: [PATCH 09/20] refactor: split FetchGovernanceObjectVotes to own member of CGovernanceManager --- src/governance/governance.cpp | 62 ++++++++++++++++++++--------------- src/governance/governance.h | 3 ++ 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp index 9115fc664dda..0cd0821976b8 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -1055,12 +1055,42 @@ void CGovernanceManager::RequestGovernanceObject(CNode* pfrom, const uint256& nH connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::MNGOVERNANCESYNC, nHash, filter)); } +std::pair, std::vector> CGovernanceManager::FetchGovernanceObjectVotes( + size_t nPeersPerHashMax, int64_t nNow, std::map>& mapAskedRecently) const +{ + std::vector vTriggerObjHashes; + std::vector vOtherObjHashes; + { + LOCK(cs_store); + + for (const auto& [nHash, govobj] : mapObjects) { + if (Assert(govobj)->IsSetCachedDelete()) continue; + if (mapAskedRecently.count(nHash)) { + for (auto it = mapAskedRecently[nHash].begin(); it != mapAskedRecently[nHash].end();) { + if (it->second < nNow) { + mapAskedRecently[nHash].erase(it++); + } else { + ++it; + } + } + if (mapAskedRecently[nHash].size() >= nPeersPerHashMax) continue; + } + + if (govobj->GetObjectType() == GovernanceObject::TRIGGER) { + vTriggerObjHashes.push_back(nHash); + } else { + vOtherObjHashes.push_back(nHash); + } + } + } + return {vTriggerObjHashes, vOtherObjHashes}; +} + int CGovernanceManager::RequestGovernanceObjectVotes(const std::vector& vNodesCopy, CConnman& connman, const PeerManagerInternal* peerman) const { AssertLockNotHeld(cs_store); - static std::map > mapAskedRecently; // Maximum number of nodes to request votes from for the same object hash on real networks // (mainnet, testnet, devnets). Keep this low to avoid unnecessary bandwidth usage. static constexpr size_t REALNET_PEERS_PER_HASH{3}; @@ -1076,8 +1106,6 @@ int CGovernanceManager::RequestGovernanceObjectVotes(const std::vector& int nTimeout = 60 * 60; size_t nPeersPerHashMax = Params().IsMockableChain() ? REGTEST_PEERS_PER_HASH : REALNET_PEERS_PER_HASH; - std::vector vTriggerObjHashes; - std::vector vOtherObjHashes; // This should help us to get some idea about an impact this can bring once deployed on mainnet. // Testnet is ~40 times smaller in masternode count, but only ~1000 masternodes usually vote, @@ -1090,31 +1118,13 @@ int CGovernanceManager::RequestGovernanceObjectVotes(const std::vector& nMaxObjRequestsPerNode = std::max(1, int(nProjectedVotes / std::max(1, (int)Assert(m_dmnman)->GetListAtChainTip().GetValidMNsCount()))); } - { - LOCK(cs_store); - - if (mapObjects.empty()) return -2; + static Mutex cs_recently; + static std::map> mapAskedRecently GUARDED_BY(cs_recently); + LOCK(cs_recently); - for (const auto& [nHash, govobj] : mapObjects) { - if (Assert(govobj)->IsSetCachedDelete()) continue; - if (mapAskedRecently.count(nHash)) { - for (auto it = mapAskedRecently[nHash].begin(); it != mapAskedRecently[nHash].end();) { - if (it->second < nNow) { - mapAskedRecently[nHash].erase(it++); - } else { - ++it; - } - } - if (mapAskedRecently[nHash].size() >= nPeersPerHashMax) continue; - } + auto [vTriggerObjHashes, vOtherObjHashes] = FetchGovernanceObjectVotes(nMaxObjRequestsPerNode, nNow, mapAskedRecently); - if (govobj->GetObjectType() == GovernanceObject::TRIGGER) { - vTriggerObjHashes.push_back(nHash); - } else { - vOtherObjHashes.push_back(nHash); - } - } - } + if (vTriggerObjHashes.empty() && vOtherObjHashes.empty()) return -2; LogPrint(BCLog::GOBJECT, "CGovernanceManager::RequestGovernanceObjectVotes -- start: vTriggerObjHashes %d vOtherObjHashes %d mapAskedRecently %d\n", vTriggerObjHashes.size(), vOtherObjHashes.size(), mapAskedRecently.size()); diff --git a/src/governance/governance.h b/src/governance/governance.h index 5c78ea46d753..c47421678ecf 100644 --- a/src/governance/governance.h +++ b/src/governance/governance.h @@ -374,6 +374,9 @@ class CGovernanceManager : public GovernanceStore, public GovernanceSignerParent std::vector FetchRelayInventory() EXCLUSIVE_LOCKS_REQUIRED(!cs_relay); void CheckAndRemove() EXCLUSIVE_LOCKS_REQUIRED(!cs_store); void RequestOrphanObjects(CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(!cs_store); + std::pair, std::vector> FetchGovernanceObjectVotes( + size_t peers_per_hash_max, int64_t now, std::map>& map_asked_recently) const + EXCLUSIVE_LOCKS_REQUIRED(!cs_store); private: // Branches of ProcessMessage From 8425ca1304d111f21f2a7076da26fb43741a01b4 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sat, 22 Nov 2025 01:44:12 +0700 Subject: [PATCH 10/20] fix: reduce scope of ::cs_main during PeerGetRequestedObjectCount --- src/governance/governance.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp index 0cd0821976b8..1a15e60826c1 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -1153,9 +1153,9 @@ int CGovernanceManager::RequestGovernanceObjectVotes(const std::vector& LOCK(::cs_main); size_t nProjectedSize = peerman->PeerGetRequestedObjectCount(pnode->GetId()) + nProjectedVotes; if (nProjectedSize > MAX_INV_SZ) continue; - // to early to ask the same node - if (mapAskedRecently[nHashGovobj].count(pnode->addr)) continue; } + // to early to ask the same node + if (mapAskedRecently[nHashGovobj].count(pnode->addr)) continue; RequestGovernanceObject(pnode, nHashGovobj, connman, true); mapAskedRecently[nHashGovobj][pnode->addr] = nNow + nTimeout; From 461392e1fcca0a411acead6500bc5e9868953f8a Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sat, 22 Nov 2025 08:41:42 +0700 Subject: [PATCH 11/20] refactor: move RequestGovernanceObjectVotes to NetGovernance to drop dependency governance on net_processing --- src/governance/governance.cpp | 99 ++----------------------- src/governance/governance.h | 10 +-- src/governance/net_governance.cpp | 94 ++++++++++++++++++++++- src/governance/net_governance.h | 1 + test/lint/lint-circular-dependencies.py | 2 - 5 files changed, 103 insertions(+), 103 deletions(-) diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp index 1a15e60826c1..01d5601df62c 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -25,7 +24,7 @@ #include #include #include -#include +#include const std::string GovernanceStore::SERIALIZATION_VERSION_STRING = "CGovernanceManager-Version-16"; @@ -1055,6 +1054,11 @@ void CGovernanceManager::RequestGovernanceObject(CNode* pfrom, const uint256& nH connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::MNGOVERNANCESYNC, nHash, filter)); } +CDeterministicMNManager& CGovernanceManager::GetMNManager() +{ + return *Assert(m_dmnman); +} + std::pair, std::vector> CGovernanceManager::FetchGovernanceObjectVotes( size_t nPeersPerHashMax, int64_t nNow, std::map>& mapAskedRecently) const { @@ -1086,97 +1090,6 @@ std::pair, std::vector> CGovernanceManager::FetchG return {vTriggerObjHashes, vOtherObjHashes}; } -int CGovernanceManager::RequestGovernanceObjectVotes(const std::vector& vNodesCopy, CConnman& connman, - const PeerManagerInternal* peerman) const -{ - AssertLockNotHeld(cs_store); - - // Maximum number of nodes to request votes from for the same object hash on real networks - // (mainnet, testnet, devnets). Keep this low to avoid unnecessary bandwidth usage. - static constexpr size_t REALNET_PEERS_PER_HASH{3}; - // Maximum number of nodes to request votes from for the same object hash on regtest. - // During testing, nodes are isolated to create conflicting triggers. Using the real - // networks limit of 3 nodes often results in querying only "non-isolated" nodes, missing the - // isolated ones we need to test. This high limit ensures all available nodes are queried. - static constexpr size_t REGTEST_PEERS_PER_HASH{std::numeric_limits::max()}; - - if (vNodesCopy.empty()) return -1; - - int64_t nNow = GetTime(); - int nTimeout = 60 * 60; - size_t nPeersPerHashMax = Params().IsMockableChain() ? REGTEST_PEERS_PER_HASH : REALNET_PEERS_PER_HASH; - - - // This should help us to get some idea about an impact this can bring once deployed on mainnet. - // Testnet is ~40 times smaller in masternode count, but only ~1000 masternodes usually vote, - // so 1 obj on mainnet == ~10 objs or ~1000 votes on testnet. However we want to test a higher - // number of votes to make sure it's robust enough, so aim at 2000 votes per masternode per request. - // On mainnet nMaxObjRequestsPerNode is always set to 1. - int nMaxObjRequestsPerNode = 1; - size_t nProjectedVotes = 2000; - if (Params().NetworkIDString() != CBaseChainParams::MAIN) { - nMaxObjRequestsPerNode = std::max(1, int(nProjectedVotes / std::max(1, (int)Assert(m_dmnman)->GetListAtChainTip().GetValidMNsCount()))); - } - - static Mutex cs_recently; - static std::map> mapAskedRecently GUARDED_BY(cs_recently); - LOCK(cs_recently); - - auto [vTriggerObjHashes, vOtherObjHashes] = FetchGovernanceObjectVotes(nMaxObjRequestsPerNode, nNow, mapAskedRecently); - - if (vTriggerObjHashes.empty() && vOtherObjHashes.empty()) return -2; - - LogPrint(BCLog::GOBJECT, "CGovernanceManager::RequestGovernanceObjectVotes -- start: vTriggerObjHashes %d vOtherObjHashes %d mapAskedRecently %d\n", - vTriggerObjHashes.size(), vOtherObjHashes.size(), mapAskedRecently.size()); - - Shuffle(vTriggerObjHashes.begin(), vTriggerObjHashes.end(), FastRandomContext()); - Shuffle(vOtherObjHashes.begin(), vOtherObjHashes.end(), FastRandomContext()); - - for (int i = 0; i < nMaxObjRequestsPerNode; ++i) { - uint256 nHashGovobj; - - // ask for triggers first - if (!vTriggerObjHashes.empty()) { - nHashGovobj = vTriggerObjHashes.back(); - } else { - if (vOtherObjHashes.empty()) break; - nHashGovobj = vOtherObjHashes.back(); - } - bool fAsked = false; - for (const auto& pnode : vNodesCopy) { - // Don't try to sync any data from outbound non-relay "masternode" connections. - // Inbound connection this early is most likely a "masternode" connection - // initiated from another node, so skip it too. - if (!pnode->CanRelay() || (connman.IsActiveMasternode() && pnode->IsInboundConn())) continue; - // stop early to prevent setAskFor overflow - { - LOCK(::cs_main); - size_t nProjectedSize = peerman->PeerGetRequestedObjectCount(pnode->GetId()) + nProjectedVotes; - if (nProjectedSize > MAX_INV_SZ) continue; - } - // to early to ask the same node - if (mapAskedRecently[nHashGovobj].count(pnode->addr)) continue; - - RequestGovernanceObject(pnode, nHashGovobj, connman, true); - mapAskedRecently[nHashGovobj][pnode->addr] = nNow + nTimeout; - fAsked = true; - // stop loop if max number of peers per obj was asked - if (mapAskedRecently[nHashGovobj].size() >= nPeersPerHashMax) break; - } - // NOTE: this should match `if` above (the one before `while`) - if (!vTriggerObjHashes.empty()) { - vTriggerObjHashes.pop_back(); - } else { - vOtherObjHashes.pop_back(); - } - if (!fAsked) i--; - } - LogPrint(BCLog::GOBJECT, "CGovernanceManager::RequestGovernanceObjectVotes -- end: vTriggerObjHashes %d vOtherObjHashes %d mapAskedRecently %d\n", - vTriggerObjHashes.size(), vOtherObjHashes.size(), mapAskedRecently.size()); - - return int(vTriggerObjHashes.size() + vOtherObjHashes.size()); -} - bool CGovernanceManager::AcceptMessage(const uint256& nHash) { AssertLockNotHeld(cs_store); diff --git a/src/governance/governance.h b/src/governance/governance.h index c47421678ecf..845ac52010c3 100644 --- a/src/governance/governance.h +++ b/src/governance/governance.h @@ -31,7 +31,6 @@ template class CFlatDB; class CInv; class CNode; -class PeerManagerInternal; class CDeterministicMNList; class CDeterministicMNManager; @@ -312,8 +311,6 @@ class CGovernanceManager : public GovernanceStore, public GovernanceSignerParent EXCLUSIVE_LOCKS_REQUIRED(!cs_store); bool ProcessVoteAndRelay(const CGovernanceVote& vote, CGovernanceException& exception, CConnman& connman) override EXCLUSIVE_LOCKS_REQUIRED(!cs_store, !cs_relay); - int RequestGovernanceObjectVotes(const std::vector& vNodesCopy, CConnman& connman, - const PeerManagerInternal* peerman) const EXCLUSIVE_LOCKS_REQUIRED(!cs_store); [[nodiscard]] MessageProcessingResult ProcessMessage(CNode& peer, CConnman& connman, std::string_view msg_type, CDataStream& vRecv) EXCLUSIVE_LOCKS_REQUIRED(!cs_store, !cs_relay); @@ -377,6 +374,10 @@ class CGovernanceManager : public GovernanceStore, public GovernanceSignerParent std::pair, std::vector> FetchGovernanceObjectVotes( size_t peers_per_hash_max, int64_t now, std::map>& map_asked_recently) const EXCLUSIVE_LOCKS_REQUIRED(!cs_store); + void RequestGovernanceObject(CNode* pfrom, const uint256& nHash, CConnman& connman, bool fUseFilter = false) const + EXCLUSIVE_LOCKS_REQUIRED(!cs_store); + CDeterministicMNManager& GetMNManager(); + private: // Branches of ProcessMessage @@ -430,9 +431,6 @@ class CGovernanceManager : public GovernanceStore, public GovernanceSignerParent void ExecuteBestSuperblock(const CDeterministicMNList& tip_mn_list, int nBlockHeight) EXCLUSIVE_LOCKS_REQUIRED(cs_store); - void RequestGovernanceObject(CNode* pfrom, const uint256& nHash, CConnman& connman, bool fUseFilter = false) const - EXCLUSIVE_LOCKS_REQUIRED(!cs_store); - bool ProcessVote(CNode* pfrom, const CGovernanceVote& vote, CGovernanceException& exception, CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(!cs_store); diff --git a/src/governance/net_governance.cpp b/src/governance/net_governance.cpp index 9799ca72ebb7..eed3419882ac 100644 --- a/src/governance/net_governance.cpp +++ b/src/governance/net_governance.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -12,6 +13,7 @@ #include #include #include +#include #include #include @@ -58,6 +60,94 @@ void NetGovernance::SendGovernanceSyncRequest(CNode* pnode, CConnman& connman) c connman.PushMessage(pnode, msgMaker.Make(NetMsgType::MNGOVERNANCESYNC, uint256(), filter)); } +int NetGovernance::RequestGovernanceObjectVotes(const std::vector& vNodesCopy, CConnman& connman) const +{ + // Maximum number of nodes to request votes from for the same object hash on real networks + // (mainnet, testnet, devnets). Keep this low to avoid unnecessary bandwidth usage. + static constexpr size_t REALNET_PEERS_PER_HASH{3}; + // Maximum number of nodes to request votes from for the same object hash on regtest. + // During testing, nodes are isolated to create conflicting triggers. Using the real + // networks limit of 3 nodes often results in querying only "non-isolated" nodes, missing the + // isolated ones we need to test. This high limit ensures all available nodes are queried. + static constexpr size_t REGTEST_PEERS_PER_HASH{std::numeric_limits::max()}; + + if (vNodesCopy.empty()) return -1; + + int64_t nNow = GetTime(); + int nTimeout = 60 * 60; + size_t nPeersPerHashMax = Params().IsMockableChain() ? REGTEST_PEERS_PER_HASH : REALNET_PEERS_PER_HASH; + + + // This should help us to get some idea about an impact this can bring once deployed on mainnet. + // Testnet is ~40 times smaller in masternode count, but only ~1000 masternodes usually vote, + // so 1 obj on mainnet == ~10 objs or ~1000 votes on testnet. However we want to test a higher + // number of votes to make sure it's robust enough, so aim at 2000 votes per masternode per request. + // On mainnet nMaxObjRequestsPerNode is always set to 1. + int nMaxObjRequestsPerNode = 1; + size_t nProjectedVotes = 2000; + if (Params().NetworkIDString() != CBaseChainParams::MAIN) { + nMaxObjRequestsPerNode = std::max(1, int(nProjectedVotes / std::max(1, (int)m_gov_manager.GetMNManager().GetListAtChainTip().GetValidMNsCount()))); + } + + static Mutex cs_recently; + static std::map > mapAskedRecently GUARDED_BY(cs_recently); + LOCK(cs_recently); + + auto [vTriggerObjHashes, vOtherObjHashes] = m_gov_manager.FetchGovernanceObjectVotes(nMaxObjRequestsPerNode, nNow, mapAskedRecently); + + if (vTriggerObjHashes.empty() && vOtherObjHashes.empty()) return -2; + + LogPrint(BCLog::GOBJECT, "CGovernanceManager::RequestGovernanceObjectVotes -- start: vTriggerObjHashes %d vOtherObjHashes %d mapAskedRecently %d\n", + vTriggerObjHashes.size(), vOtherObjHashes.size(), mapAskedRecently.size()); + + Shuffle(vTriggerObjHashes.begin(), vTriggerObjHashes.end(), FastRandomContext()); + Shuffle(vOtherObjHashes.begin(), vOtherObjHashes.end(), FastRandomContext()); + + for (int i = 0; i < nMaxObjRequestsPerNode; ++i) { + uint256 nHashGovobj; + + // ask for triggers first + if (!vTriggerObjHashes.empty()) { + nHashGovobj = vTriggerObjHashes.back(); + } else { + if (vOtherObjHashes.empty()) break; + nHashGovobj = vOtherObjHashes.back(); + } + bool fAsked = false; + for (const auto& pnode : vNodesCopy) { + // Don't try to sync any data from outbound non-relay "masternode" connections. + // Inbound connection this early is most likely a "masternode" connection + // initiated from another node, so skip it too. + if (!pnode->CanRelay() || (connman.IsActiveMasternode() && pnode->IsInboundConn())) continue; + // stop early to prevent setAskFor overflow + { + LOCK(::cs_main); + size_t nProjectedSize = m_peer_manager->PeerGetRequestedObjectCount(pnode->GetId()) + nProjectedVotes; + if (nProjectedSize > MAX_INV_SZ) continue; + } + // to early to ask the same node + if (mapAskedRecently[nHashGovobj].count(pnode->addr)) continue; + + m_gov_manager.RequestGovernanceObject(pnode, nHashGovobj, connman, true); + mapAskedRecently[nHashGovobj][pnode->addr] = nNow + nTimeout; + fAsked = true; + // stop loop if max number of peers per obj was asked + if (mapAskedRecently[nHashGovobj].size() >= nPeersPerHashMax) break; + } + // NOTE: this should match `if` above (the one before `while`) + if (!vTriggerObjHashes.empty()) { + vTriggerObjHashes.pop_back(); + } else { + vOtherObjHashes.pop_back(); + } + if (!fAsked) i--; + } + LogPrint(BCLog::GOBJECT, "CGovernanceManager::RequestGovernanceObjectVotes -- end: vTriggerObjHashes %d vOtherObjHashes %d mapAskedRecently %d\n", + vTriggerObjHashes.size(), vOtherObjHashes.size(), mapAskedRecently.size()); + + return int(vTriggerObjHashes.size() + vOtherObjHashes.size()); +} + void NetGovernance::ProcessTick(CConnman& connman) { assert(m_netfulfilledman.IsValid()); @@ -87,7 +177,7 @@ void NetGovernance::ProcessTick(CConnman& connman) // gradually request the rest of the votes after sync finished if (m_node_sync.IsSynced()) { - m_gov_manager.RequestGovernanceObjectVotes(snap.Nodes(), connman, m_peer_manager); + RequestGovernanceObjectVotes(snap.Nodes(), connman); return; } @@ -217,7 +307,7 @@ void NetGovernance::ProcessTick(CConnman& connman) continue; // to early for this node } const std::vector vNodeCopy{pnode}; - int nObjsLeftToAsk = m_gov_manager.RequestGovernanceObjectVotes(vNodeCopy, connman, m_peer_manager); + int nObjsLeftToAsk = RequestGovernanceObjectVotes(vNodeCopy, connman); // check for data if (nObjsLeftToAsk == 0) { static int64_t nTimeNoObjectsLeft = 0; diff --git a/src/governance/net_governance.h b/src/governance/net_governance.h index 535f1a316572..c7f0333a46e1 100644 --- a/src/governance/net_governance.h +++ b/src/governance/net_governance.h @@ -26,6 +26,7 @@ class NetGovernance final : public NetHandler private: void SendGovernanceSyncRequest(CNode* pnode, CConnman& connman) const; + int RequestGovernanceObjectVotes(const std::vector& vNodesCopy, CConnman& connman) const; void ProcessTick(CConnman& connman); CGovernanceManager& m_gov_manager; diff --git a/test/lint/lint-circular-dependencies.py b/test/lint/lint-circular-dependencies.py index 6fd391c65110..a3054113f073 100755 --- a/test/lint/lint-circular-dependencies.py +++ b/test/lint/lint-circular-dependencies.py @@ -49,8 +49,6 @@ "governance/classes -> governance/object -> governance/governance -> governance/classes", "governance/governance -> governance/signing -> governance/object -> governance/governance", "masternode/sync -> net -> masternode/sync", - "governance/governance -> net_processing -> masternode/active/context -> governance/governance", - "governance/governance -> net_processing -> governance/governance", "instantsend/instantsend -> instantsend/signing -> llmq/signing_shares -> net_processing -> instantsend/instantsend", "instantsend/instantsend -> instantsend/signing -> llmq/signing_shares -> net_processing -> llmq/context -> instantsend/instantsend", "instantsend/instantsend -> instantsend/signing -> llmq/signing_shares -> net_processing -> masternode/active/context -> instantsend/instantsend", From 7f3d16ddc96376f84e173b5e73f73babd04686f9 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sat, 22 Nov 2025 08:55:49 +0700 Subject: [PATCH 12/20] chore: apply clang-format and adjust logs --- src/governance/governance.cpp | 5 +---- src/governance/net_governance.cpp | 17 ++++++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp index 01d5601df62c..dc69a8e814ff 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -1054,10 +1054,7 @@ void CGovernanceManager::RequestGovernanceObject(CNode* pfrom, const uint256& nH connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::MNGOVERNANCESYNC, nHash, filter)); } -CDeterministicMNManager& CGovernanceManager::GetMNManager() -{ - return *Assert(m_dmnman); -} +CDeterministicMNManager& CGovernanceManager::GetMNManager() { return *Assert(m_dmnman); } std::pair, std::vector> CGovernanceManager::FetchGovernanceObjectVotes( size_t nPeersPerHashMax, int64_t nNow, std::map>& mapAskedRecently) const diff --git a/src/governance/net_governance.cpp b/src/governance/net_governance.cpp index eed3419882ac..3347ef2fd904 100644 --- a/src/governance/net_governance.cpp +++ b/src/governance/net_governance.cpp @@ -86,19 +86,22 @@ int NetGovernance::RequestGovernanceObjectVotes(const std::vector& vNode int nMaxObjRequestsPerNode = 1; size_t nProjectedVotes = 2000; if (Params().NetworkIDString() != CBaseChainParams::MAIN) { - nMaxObjRequestsPerNode = std::max(1, int(nProjectedVotes / std::max(1, (int)m_gov_manager.GetMNManager().GetListAtChainTip().GetValidMNsCount()))); + nMaxObjRequestsPerNode = + std::max(1, int(nProjectedVotes / + std::max(1, (int)m_gov_manager.GetMNManager().GetListAtChainTip().GetValidMNsCount()))); } static Mutex cs_recently; - static std::map > mapAskedRecently GUARDED_BY(cs_recently); + static std::map> mapAskedRecently GUARDED_BY(cs_recently); LOCK(cs_recently); - auto [vTriggerObjHashes, vOtherObjHashes] = m_gov_manager.FetchGovernanceObjectVotes(nMaxObjRequestsPerNode, nNow, mapAskedRecently); + auto [vTriggerObjHashes, vOtherObjHashes] = m_gov_manager.FetchGovernanceObjectVotes(nMaxObjRequestsPerNode, nNow, + mapAskedRecently); if (vTriggerObjHashes.empty() && vOtherObjHashes.empty()) return -2; - LogPrint(BCLog::GOBJECT, "CGovernanceManager::RequestGovernanceObjectVotes -- start: vTriggerObjHashes %d vOtherObjHashes %d mapAskedRecently %d\n", - vTriggerObjHashes.size(), vOtherObjHashes.size(), mapAskedRecently.size()); + LogPrint(BCLog::GOBJECT, "%s -- start: vTriggerObjHashes %d vOtherObjHashes %d mapAskedRecently %d\n", __func__, + vTriggerObjHashes.size(), vOtherObjHashes.size(), mapAskedRecently.size()); Shuffle(vTriggerObjHashes.begin(), vTriggerObjHashes.end(), FastRandomContext()); Shuffle(vOtherObjHashes.begin(), vOtherObjHashes.end(), FastRandomContext()); @@ -142,8 +145,8 @@ int NetGovernance::RequestGovernanceObjectVotes(const std::vector& vNode } if (!fAsked) i--; } - LogPrint(BCLog::GOBJECT, "CGovernanceManager::RequestGovernanceObjectVotes -- end: vTriggerObjHashes %d vOtherObjHashes %d mapAskedRecently %d\n", - vTriggerObjHashes.size(), vOtherObjHashes.size(), mapAskedRecently.size()); + LogPrint(BCLog::GOBJECT, "%s -- end: vTriggerObjHashes %d vOtherObjHashes %d mapAskedRecently %d\n", __func__, + vTriggerObjHashes.size(), vOtherObjHashes.size(), mapAskedRecently.size()); return int(vTriggerObjHashes.size() + vOtherObjHashes.size()); } From fa0e173b773a524f1ac9e1a9a3b555fa08ccdfa3 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sun, 23 Nov 2025 05:26:29 +0700 Subject: [PATCH 13/20] refactor: add CConnman to NetHandler's ProcessMessage --- src/instantsend/net_instantsend.cpp | 2 +- src/instantsend/net_instantsend.h | 2 +- src/llmq/net_signing.cpp | 2 +- src/llmq/net_signing.h | 2 +- src/net_processing.cpp | 2 +- src/net_processing.h | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/instantsend/net_instantsend.cpp b/src/instantsend/net_instantsend.cpp index efd217c10d51..e4cd953fdc1d 100644 --- a/src/instantsend/net_instantsend.cpp +++ b/src/instantsend/net_instantsend.cpp @@ -14,7 +14,7 @@ #include #include -void NetInstantSend::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) +void NetInstantSend::ProcessMessage(CNode& pfrom, CConnman&, const std::string& msg_type, CDataStream& vRecv) { if (msg_type != NetMsgType::ISDLOCK) { return; diff --git a/src/instantsend/net_instantsend.h b/src/instantsend/net_instantsend.h index d21ffb734ef1..dab44bd4a461 100644 --- a/src/instantsend/net_instantsend.h +++ b/src/instantsend/net_instantsend.h @@ -31,7 +31,7 @@ class NetInstantSend final : public NetHandler { workInterrupt.reset(); } - void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) override; + void ProcessMessage(CNode& pfrom, CConnman&, const std::string& msg_type, CDataStream& vRecv) override; void Start() override; void Stop() override; diff --git a/src/llmq/net_signing.cpp b/src/llmq/net_signing.cpp index 575dc8ca3a8a..077d0bf4f9ba 100644 --- a/src/llmq/net_signing.cpp +++ b/src/llmq/net_signing.cpp @@ -16,7 +16,7 @@ #include -void NetSigning::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) +void NetSigning::ProcessMessage(CNode& pfrom, CConnman&, const std::string& msg_type, CDataStream& vRecv) { if (msg_type != NetMsgType::QSIGREC) return; diff --git a/src/llmq/net_signing.h b/src/llmq/net_signing.h index d77b4b916eed..0b9d8161fb5a 100644 --- a/src/llmq/net_signing.h +++ b/src/llmq/net_signing.h @@ -25,7 +25,7 @@ class NetSigning final : public NetHandler { workInterrupt.reset(); } - void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) override; + void ProcessMessage(CNode& pfrom, CConnman&, const std::string& msg_type, CDataStream& vRecv) override; [[nodiscard]] bool ProcessPendingRecoveredSigs(); void ProcessRecoveredSig(std::shared_ptr recoveredSig, bool consider_proactive_relay); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index c45d2d250655..78745e9cbc85 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -5466,7 +5466,7 @@ void PeerManagerImpl::ProcessMessage( } for (const auto& handler : m_handlers) { - handler->ProcessMessage(pfrom, msg_type, vRecv); + handler->ProcessMessage(pfrom, m_connman, msg_type, vRecv); } return; } diff --git a/src/net_processing.h b/src/net_processing.h index 9f3816f9cb5a..dce3bafd2f7e 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -81,7 +81,7 @@ class NetHandler virtual void Stop() {} virtual void Interrupt() {} virtual void Schedule(CScheduler& scheduler, CConnman& connman) {} - virtual void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) {} + virtual void ProcessMessage(CNode& pfrom, CConnman& connman, const std::string& msg_type, CDataStream& vRecv) {} protected: PeerManagerInternal* m_peer_manager; }; From fc21e513befe46da47e509e9ffa1b7adb1eec20c Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Mon, 24 Nov 2025 00:25:18 +0700 Subject: [PATCH 14/20] refactor: move ProcessMessage from CGovernanceManager to NetGovernance --- src/governance/governance.cpp | 165 ++++++------------------------ src/governance/governance.h | 31 +++--- src/governance/net_governance.cpp | 105 +++++++++++++++++++ src/governance/net_governance.h | 1 + src/net_processing.cpp | 1 - 5 files changed, 152 insertions(+), 151 deletions(-) diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp index dc69a8e814ff..95c1b7a4f58f 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -180,151 +180,50 @@ void CGovernanceManager::AddPostponedObjectInternal(const CGovernanceObject& gov mapPostponedObjects.emplace(govobj.GetHash(), std::make_shared(govobj)); } -MessageProcessingResult CGovernanceManager::ProcessMessage(CNode& peer, CConnman& connman, std::string_view msg_type, - CDataStream& vRecv) +bool CGovernanceManager::ProcessObject(const CNode& peer, const uint256& nHash, CGovernanceObject& govobj) { - AssertLockNotHeld(cs_store); - AssertLockNotHeld(cs_relay); - if (!IsValid()) return {}; - if (!m_mn_sync.IsBlockchainSynced()) return {}; + std::string strHash = nHash.ToString(); - const auto tip_mn_list = Assert(m_dmnman)->GetListAtChainTip(); - // ANOTHER USER IS ASKING US TO HELP THEM SYNC GOVERNANCE OBJECT DATA - if (msg_type == NetMsgType::MNGOVERNANCESYNC) { - // Ignore such requests until we are fully synced. - // We could start processing this after masternode list is synced - // but this is a heavy one so it's better to finish sync first. - if (!m_mn_sync.IsSynced()) return {}; - - uint256 nProp; - CBloomFilter filter; - vRecv >> nProp; - vRecv >> filter; - - LogPrint(BCLog::GOBJECT, "MNGOVERNANCESYNC -- syncing governance objects to our peer %s\n", peer.GetLogString()); - LOCK(cs_store); - if (nProp == uint256()) { - return SyncObjects(peer, connman); - } else { - return SyncSingleObjVotes(peer, nProp, filter, connman); - } + LOCK(cs_store); - return {}; + if (mapObjects.count(nHash) || mapPostponedObjects.count(nHash) || mapErasedGovernanceObjects.count(nHash)) { + // TODO - print error code? what if it's GOVOBJ_ERROR_IMMATURE? + LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- Received already seen object: %s\n", strHash); + return true; } - // A NEW GOVERNANCE OBJECT HAS ARRIVED - else if (msg_type == NetMsgType::MNGOVERNANCEOBJECT) { - // MAKE SURE WE HAVE A VALID REFERENCE TO THE TIP BEFORE CONTINUING - - CGovernanceObject govobj; - vRecv >> govobj; - - uint256 nHash = govobj.GetHash(); - - MessageProcessingResult ret{}; - ret.m_to_erase = CInv{MSG_GOVERNANCE_OBJECT, nHash}; - - if (!m_mn_sync.IsBlockchainSynced()) { - LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- masternode list not synced\n"); - return ret; - } - - std::string strHash = nHash.ToString(); - - LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- Received object: %s\n", strHash); - - if (!AcceptMessage(nHash)) { - LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- Received unrequested object: %s\n", strHash); - return ret; - } - - LOCK2(::cs_main, cs_store); - - if (mapObjects.count(nHash) || mapPostponedObjects.count(nHash) || mapErasedGovernanceObjects.count(nHash)) { - // TODO - print error code? what if it's GOVOBJ_ERROR_IMMATURE? - LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- Received already seen object: %s\n", strHash); - return ret; - } - - bool fRateCheckBypassed = false; - if (!MasternodeRateCheck(govobj, true, false, fRateCheckBypassed)) { - LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- masternode rate check failed - %s - (current block height %d) \n", strHash, nCachedBlockHeight); - return ret; - } - - std::string strError; - // CHECK OBJECT AGAINST LOCAL BLOCKCHAIN - - bool fMissingConfirmations = false; - bool fIsValid = govobj.IsValidLocally(tip_mn_list, m_chainman, strError, fMissingConfirmations, true); - - bool unused_rcb; - if (fRateCheckBypassed && fIsValid && !MasternodeRateCheck(govobj, true, true, unused_rcb)) { - LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- masternode rate check failed (after signature verification) - %s - (current block height %d)\n", strHash, nCachedBlockHeight); - return ret; - } - - if (!fIsValid) { - if (fMissingConfirmations) { - AddPostponedObjectInternal(govobj); - LogPrintf("MNGOVERNANCEOBJECT -- Not enough fee confirmations for: %s, strError = %s\n", strHash, strError); - } else { - LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- Governance object is invalid - %s\n", strError); - // apply node's ban score - ret.m_error = MisbehavingError{20}; - return ret; - } - - return ret; - } - - AddGovernanceObjectInternal(govobj, &peer); - return ret; + bool fRateCheckBypassed = false; + if (!MasternodeRateCheck(govobj, true, false, fRateCheckBypassed)) { + LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- masternode rate check failed - %s - (current block height %d) \n", strHash, nCachedBlockHeight); + return true; } - // A NEW GOVERNANCE OBJECT VOTE HAS ARRIVED - else if (msg_type == NetMsgType::MNGOVERNANCEOBJECTVOTE) { - CGovernanceVote vote; - vRecv >> vote; - - uint256 nHash = vote.GetHash(); - - MessageProcessingResult ret{}; - ret.m_to_erase = CInv{MSG_GOVERNANCE_OBJECT_VOTE, nHash}; - - // Ignore such messages until masternode list is synced - if (!m_mn_sync.IsBlockchainSynced()) { - LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECTVOTE -- masternode list not synced\n"); - return ret; - } - - LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECTVOTE -- Received vote: %s\n", vote.ToString(tip_mn_list)); + std::string strError; + // CHECK OBJECT AGAINST LOCAL BLOCKCHAIN - std::string strHash = nHash.ToString(); + const auto tip_mn_list = GetMNManager().GetListAtChainTip(); + bool fMissingConfirmations = false; + bool fIsValid = govobj.IsValidLocally(tip_mn_list, m_chainman, strError, fMissingConfirmations, true); - if (!AcceptMessage(nHash)) { - LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECTVOTE -- Received unrequested vote object: %s, hash: %s, peer = %d\n", - vote.ToString(tip_mn_list), strHash, peer.GetId()); - return ret; - } + bool unused_rcb; + if (fRateCheckBypassed && fIsValid && !MasternodeRateCheck(govobj, true, true, unused_rcb)) { + LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- masternode rate check failed (after signature verification) - %s - (current block height %d)\n", strHash, nCachedBlockHeight); + return true; + } - CGovernanceException exception; - if (ProcessVote(&peer, vote, exception, connman)) { - LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECTVOTE -- %s new\n", strHash); - m_mn_sync.BumpAssetLastTime("MNGOVERNANCEOBJECTVOTE"); - RelayVote(vote); + if (!fIsValid) { + if (fMissingConfirmations) { + AddPostponedObjectInternal(govobj); + LogPrintf("MNGOVERNANCEOBJECT -- Not enough fee confirmations for: %s, strError = %s\n", strHash, strError); + return true; } else { - LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECTVOTE -- Rejected vote, error = %s\n", exception.what()); - if ((exception.GetNodePenalty() != 0) && m_mn_sync.IsSynced()) { - ret.m_error = MisbehavingError{exception.GetNodePenalty()}; - return ret; - } - return ret; + LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- Governance object is invalid - %s\n", strError); + return false; } - return ret; } - return {}; + AddGovernanceObjectInternal(govobj, &peer); + return true; } void CGovernanceManager::CheckOrphanVotes(CGovernanceObject& govobj) @@ -693,7 +592,7 @@ bool CGovernanceManager::ConfirmInventoryRequest(const CInv& inv) MessageProcessingResult CGovernanceManager::SyncSingleObjVotes(CNode& peer, const uint256& nProp, const CBloomFilter& filter, CConnman& connman) { - AssertLockHeld(cs_store); + LOCK(cs_store); // do not provide any data until our node is synced if (!m_mn_sync.IsSynced()) return {}; @@ -746,7 +645,7 @@ MessageProcessingResult CGovernanceManager::SyncSingleObjVotes(CNode& peer, cons MessageProcessingResult CGovernanceManager::SyncObjects(CNode& peer, CConnman& connman) const { - AssertLockHeld(cs_store); + LOCK(cs_store); assert(m_netfulfilledman.IsValid()); // do not provide any data until our node is synced diff --git a/src/governance/governance.h b/src/governance/governance.h index 845ac52010c3..e07ae5f5642f 100644 --- a/src/governance/governance.h +++ b/src/governance/governance.h @@ -311,9 +311,6 @@ class CGovernanceManager : public GovernanceStore, public GovernanceSignerParent EXCLUSIVE_LOCKS_REQUIRED(!cs_store); bool ProcessVoteAndRelay(const CGovernanceVote& vote, CGovernanceException& exception, CConnman& connman) override EXCLUSIVE_LOCKS_REQUIRED(!cs_store, !cs_relay); - [[nodiscard]] MessageProcessingResult ProcessMessage(CNode& peer, CConnman& connman, std::string_view msg_type, - CDataStream& vRecv) - EXCLUSIVE_LOCKS_REQUIRED(!cs_store, !cs_relay); void RelayObject(const CGovernanceObject& obj) EXCLUSIVE_LOCKS_REQUIRED(!cs_relay); void RelayVote(const CGovernanceVote& vote) @@ -376,17 +373,24 @@ class CGovernanceManager : public GovernanceStore, public GovernanceSignerParent EXCLUSIVE_LOCKS_REQUIRED(!cs_store); void RequestGovernanceObject(CNode* pfrom, const uint256& nHash, CConnman& connman, bool fUseFilter = false) const EXCLUSIVE_LOCKS_REQUIRED(!cs_store); - CDeterministicMNManager& GetMNManager(); - - -private: - // Branches of ProcessMessage [[nodiscard]] MessageProcessingResult SyncObjects(CNode& peer, CConnman& connman) const - EXCLUSIVE_LOCKS_REQUIRED(cs_store); + EXCLUSIVE_LOCKS_REQUIRED(!cs_store); [[nodiscard]] MessageProcessingResult SyncSingleObjVotes(CNode& peer, const uint256& nProp, const CBloomFilter& filter, CConnman& connman) - EXCLUSIVE_LOCKS_REQUIRED(cs_store); + EXCLUSIVE_LOCKS_REQUIRED(!cs_store); + /// Called to indicate a requested object or vote has been received + bool AcceptMessage(const uint256& nHash) + EXCLUSIVE_LOCKS_REQUIRED(!cs_store); + bool ProcessObject(const CNode& peer, const uint256& hash, CGovernanceObject& govobj) + EXCLUSIVE_LOCKS_REQUIRED(::cs_main, !cs_store, !cs_relay); + + CDeterministicMNManager& GetMNManager(); + bool ProcessVote(CNode* pfrom, const CGovernanceVote& vote, CGovernanceException& exception, CConnman& connman) + EXCLUSIVE_LOCKS_REQUIRED(!cs_store); + + +private: // Internal counterparts to "Thread-safe accessors" void AddPostponedObjectInternal(const CGovernanceObject& govobj) EXCLUSIVE_LOCKS_REQUIRED(cs_store); @@ -431,13 +435,6 @@ class CGovernanceManager : public GovernanceStore, public GovernanceSignerParent void ExecuteBestSuperblock(const CDeterministicMNList& tip_mn_list, int nBlockHeight) EXCLUSIVE_LOCKS_REQUIRED(cs_store); - bool ProcessVote(CNode* pfrom, const CGovernanceVote& vote, CGovernanceException& exception, CConnman& connman) - EXCLUSIVE_LOCKS_REQUIRED(!cs_store); - - /// Called to indicate a requested object or vote has been received - bool AcceptMessage(const uint256& nHash) - EXCLUSIVE_LOCKS_REQUIRED(!cs_store); - void CheckOrphanVotes(CGovernanceObject& govobj) EXCLUSIVE_LOCKS_REQUIRED(cs_store, !cs_relay); diff --git a/src/governance/net_governance.cpp b/src/governance/net_governance.cpp index 3347ef2fd904..f70e3eb899cd 100644 --- a/src/governance/net_governance.cpp +++ b/src/governance/net_governance.cpp @@ -341,3 +341,108 @@ void NetGovernance::ProcessTick(CConnman& connman) } } } + +void NetGovernance::ProcessMessage(CNode& peer, CConnman& connman, const std::string& msg_type, CDataStream& vRecv) +{ + if (!m_gov_manager.IsValid()) return; + if (!m_node_sync.IsBlockchainSynced()) return; + + // ANOTHER USER IS ASKING US TO HELP THEM SYNC GOVERNANCE OBJECT DATA + if (msg_type == NetMsgType::MNGOVERNANCESYNC) { + // Ignore such requests until we are fully synced. + // We could start processing this after masternode list is synced + // but this is a heavy one so it's better to finish sync first. + if (!m_node_sync.IsSynced()) return; + + uint256 nProp; + CBloomFilter filter; + vRecv >> nProp; + vRecv >> filter; + + LogPrint(BCLog::GOBJECT, "MNGOVERNANCESYNC -- syncing governance objects to our peer %s\n", peer.GetLogString()); + if (nProp == uint256()) { + m_peer_manager->PeerPostProcessMessage(m_gov_manager.SyncObjects(peer, connman)); + } else { + m_peer_manager->PeerPostProcessMessage(m_gov_manager.SyncSingleObjVotes(peer, nProp, filter, connman)); + } + } + // A NEW GOVERNANCE OBJECT HAS ARRIVED + else if (msg_type == NetMsgType::MNGOVERNANCEOBJECT) { + // MAKE SURE WE HAVE A VALID REFERENCE TO THE TIP BEFORE CONTINUING + CGovernanceObject govobj; + vRecv >> govobj; + + uint256 nHash = govobj.GetHash(); + + WITH_LOCK(::cs_main, m_peer_manager->PeerEraseObjectRequest(peer.GetId(), CInv{MSG_GOVERNANCE_OBJECT, nHash})); + + if (!m_node_sync.IsBlockchainSynced()) { + LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- masternode list not synced\n"); + return; + } + + std::string strHash = nHash.ToString(); + + LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- Received object: %s\n", strHash); + + if (!m_gov_manager.AcceptMessage(nHash)) { + LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- Received unrequested object: %s\n", strHash); + return; + } + + if (!WITH_LOCK(::cs_main, return m_gov_manager.ProcessObject(peer, nHash, govobj))) { + // apply node's ban score + m_peer_manager->PeerMisbehaving(peer.GetId(), 20); + } + } + + // A NEW GOVERNANCE OBJECT VOTE HAS ARRIVED + else if (msg_type == NetMsgType::MNGOVERNANCEOBJECTVOTE) { + CGovernanceVote vote; + vRecv >> vote; + + uint256 nHash = vote.GetHash(); + + WITH_LOCK(::cs_main, m_peer_manager->PeerEraseObjectRequest(peer.GetId(), CInv{MSG_GOVERNANCE_OBJECT_VOTE, nHash})); + + // Ignore such messages until masternode list is synced + if (!m_node_sync.IsBlockchainSynced()) { + LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECTVOTE -- masternode list not synced\n"); + return; + } + + const auto tip_mn_list = m_gov_manager.GetMNManager().GetListAtChainTip(); + LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECTVOTE -- Received vote: %s\n", vote.ToString(tip_mn_list)); + + std::string strHash = nHash.ToString(); + + if (!m_gov_manager.AcceptMessage(nHash)) { + LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECTVOTE -- Received unrequested vote object: %s, hash: %s, peer = %d\n", + vote.ToString(tip_mn_list), strHash, peer.GetId()); + return; + } + + CGovernanceException exception; + if (m_gov_manager.ProcessVote(&peer, vote, exception, connman)) { + LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECTVOTE -- %s new\n", strHash); + m_node_sync.BumpAssetLastTime("MNGOVERNANCEOBJECTVOTE"); + + if (!m_node_sync.IsSynced()) { + LogPrint(BCLog::GOBJECT, "%s -- won't relay until fully synced\n", __func__); + return; + } + auto dmn = tip_mn_list.GetMNByCollateral(vote.GetMasternodeOutpoint()); + if (!dmn) { + return; + } + m_gov_manager.RelayVote(vote); + // TODO: figure out why immediate sending of inventory doesn't work here! + // m_peer_manager->PeerRelayInv(CInv{MSG_GOVERNANCE_OBJECT_VOTE, nHash}); + } else { + LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECTVOTE -- Rejected vote, error = %s\n", exception.what()); + if ((exception.GetNodePenalty() != 0) && m_node_sync.IsSynced()) { + m_peer_manager->PeerMisbehaving(peer.GetId(), exception.GetNodePenalty()); + } + } + } +} diff --git a/src/governance/net_governance.h b/src/governance/net_governance.h index c7f0333a46e1..6f5e2e2c0dbb 100644 --- a/src/governance/net_governance.h +++ b/src/governance/net_governance.h @@ -24,6 +24,7 @@ class NetGovernance final : public NetHandler } void Schedule(CScheduler& scheduler, CConnman& connman) override; + void ProcessMessage(CNode& peer, CConnman& connman, const std::string& msg_type, CDataStream& vRecv) override; private: void SendGovernanceSyncRequest(CNode* pnode, CConnman& connman) const; int RequestGovernanceObjectVotes(const std::vector& vNodesCopy, CConnman& connman) const; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 78745e9cbc85..1407bb120498 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -5447,7 +5447,6 @@ void PeerManagerImpl::ProcessMessage( } PostProcessMessage(m_sporkman.ProcessMessage(pfrom, m_connman, msg_type, vRecv), pfrom.GetId()); m_mn_sync.ProcessMessage(pfrom, msg_type, vRecv); - PostProcessMessage(m_govman.ProcessMessage(pfrom, m_connman, msg_type, vRecv), pfrom.GetId()); PostProcessMessage(CMNAuth::ProcessMessage(pfrom, peer->m_their_services, m_connman, m_mn_metaman, m_mn_activeman, m_mn_sync, m_dmnman->GetListAtChainTip(), msg_type, vRecv), pfrom.GetId()); PostProcessMessage(m_llmq_ctx->quorum_block_processor->ProcessMessage(pfrom, msg_type, vRecv), pfrom.GetId()); PostProcessMessage(m_llmq_ctx->qdkgsman->ProcessMessage(pfrom, is_masternode, msg_type, vRecv), pfrom.GetId()); From 106827c7368d5f6b10d3392be68c015eb085bb03 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Mon, 24 Nov 2025 16:03:00 +0700 Subject: [PATCH 15/20] fmt: clang-format --- src/governance/governance.cpp | 8 ++++++-- src/governance/governance.h | 7 ++----- src/governance/net_governance.cpp | 5 +++-- src/governance/net_governance.h | 1 + 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp index 95c1b7a4f58f..2a421fd2db7a 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -194,7 +194,8 @@ bool CGovernanceManager::ProcessObject(const CNode& peer, const uint256& nHash, bool fRateCheckBypassed = false; if (!MasternodeRateCheck(govobj, true, false, fRateCheckBypassed)) { - LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- masternode rate check failed - %s - (current block height %d) \n", strHash, nCachedBlockHeight); + LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- masternode rate check failed - %s - (current block height %d) \n", + strHash, nCachedBlockHeight); return true; } @@ -207,7 +208,10 @@ bool CGovernanceManager::ProcessObject(const CNode& peer, const uint256& nHash, bool unused_rcb; if (fRateCheckBypassed && fIsValid && !MasternodeRateCheck(govobj, true, true, unused_rcb)) { - LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- masternode rate check failed (after signature verification) - %s - (current block height %d)\n", strHash, nCachedBlockHeight); + LogPrint(BCLog::GOBJECT, /* Continued */ + "MNGOVERNANCEOBJECT -- masternode rate check failed (after signature verification) - %s - (current " + "block height %d)\n", + strHash, nCachedBlockHeight); return true; } diff --git a/src/governance/governance.h b/src/governance/governance.h index e07ae5f5642f..781ac81fe3c4 100644 --- a/src/governance/governance.h +++ b/src/governance/governance.h @@ -376,11 +376,9 @@ class CGovernanceManager : public GovernanceStore, public GovernanceSignerParent [[nodiscard]] MessageProcessingResult SyncObjects(CNode& peer, CConnman& connman) const EXCLUSIVE_LOCKS_REQUIRED(!cs_store); [[nodiscard]] MessageProcessingResult SyncSingleObjVotes(CNode& peer, const uint256& nProp, const CBloomFilter& filter, - CConnman& connman) - EXCLUSIVE_LOCKS_REQUIRED(!cs_store); + CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(!cs_store); /// Called to indicate a requested object or vote has been received - bool AcceptMessage(const uint256& nHash) - EXCLUSIVE_LOCKS_REQUIRED(!cs_store); + bool AcceptMessage(const uint256& nHash) EXCLUSIVE_LOCKS_REQUIRED(!cs_store); bool ProcessObject(const CNode& peer, const uint256& hash, CGovernanceObject& govobj) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, !cs_store, !cs_relay); @@ -389,7 +387,6 @@ class CGovernanceManager : public GovernanceStore, public GovernanceSignerParent EXCLUSIVE_LOCKS_REQUIRED(!cs_store); - private: // Internal counterparts to "Thread-safe accessors" void AddPostponedObjectInternal(const CGovernanceObject& govobj) diff --git a/src/governance/net_governance.cpp b/src/governance/net_governance.cpp index f70e3eb899cd..b1c73f1769d6 100644 --- a/src/governance/net_governance.cpp +++ b/src/governance/net_governance.cpp @@ -417,8 +417,9 @@ void NetGovernance::ProcessMessage(CNode& peer, CConnman& connman, const std::st std::string strHash = nHash.ToString(); if (!m_gov_manager.AcceptMessage(nHash)) { - LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECTVOTE -- Received unrequested vote object: %s, hash: %s, peer = %d\n", - vote.ToString(tip_mn_list), strHash, peer.GetId()); + LogPrint(BCLog::GOBJECT, /* Continued */ + "MNGOVERNANCEOBJECTVOTE -- Received unrequested vote object: %s, hash: %s, peer = %d\n", + vote.ToString(tip_mn_list), strHash, peer.GetId()); return; } diff --git a/src/governance/net_governance.h b/src/governance/net_governance.h index 6f5e2e2c0dbb..e582ff41b7e1 100644 --- a/src/governance/net_governance.h +++ b/src/governance/net_governance.h @@ -25,6 +25,7 @@ class NetGovernance final : public NetHandler void Schedule(CScheduler& scheduler, CConnman& connman) override; void ProcessMessage(CNode& peer, CConnman& connman, const std::string& msg_type, CDataStream& vRecv) override; + private: void SendGovernanceSyncRequest(CNode* pnode, CConnman& connman) const; int RequestGovernanceObjectVotes(const std::vector& vNodesCopy, CConnman& connman) const; From b9aec509a7d60bab3edb2fb1452c9579555e3eaa Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sat, 13 Dec 2025 02:56:39 +0700 Subject: [PATCH 16/20] refactor: split NetGovernance to SyncManager and NetGovernance NetGovernance is not a place to have ProcessTick which control overall sync status (not only governance step) --- src/Makefile.am | 2 + src/governance/net_governance.cpp | 304 +--------------------------- src/governance/net_governance.h | 12 +- src/init.cpp | 4 +- src/node/sync_manager.cpp | 320 ++++++++++++++++++++++++++++++ src/node/sync_manager.h | 37 ++++ 6 files changed, 366 insertions(+), 313 deletions(-) create mode 100644 src/node/sync_manager.cpp create mode 100644 src/node/sync_manager.h diff --git a/src/Makefile.am b/src/Makefile.am index fa0388545e8f..5b93496ea800 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -312,6 +312,7 @@ BITCOIN_CORE_H = \ node/miner.h \ node/minisketchwrapper.h \ node/psbt.h \ + node/sync_manager.h \ node/transaction.h \ node/txreconciliation.h \ node/interface_ui.h \ @@ -569,6 +570,7 @@ libbitcoin_node_a_SOURCES = \ node/miner.cpp \ node/minisketchwrapper.cpp \ node/psbt.cpp \ + node/sync_manager.cpp \ node/transaction.cpp \ node/txreconciliation.cpp \ node/interface_ui.cpp \ diff --git a/src/governance/net_governance.cpp b/src/governance/net_governance.cpp index b1c73f1769d6..9505075c8d9c 100644 --- a/src/governance/net_governance.cpp +++ b/src/governance/net_governance.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2025 The Dash Core developers +// Copyright (c) 2024-2025 The Dash Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -10,25 +10,14 @@ #include #include #include -#include -#include -#include -#include #include -#include class CConnman; void NetGovernance::Schedule(CScheduler& scheduler, CConnman& connman) { - scheduler.scheduleEvery( - [this, &connman]() -> void { - if (ShutdownRequested()) return; - ProcessTick(connman); - }, - std::chrono::seconds{1}); - // Code below is meant to be running only if governance validation is enabled + // if (!m_gov_manager.IsValid()) return; scheduler.scheduleEvery( [this, &connman]() -> void { @@ -53,295 +42,6 @@ void NetGovernance::Schedule(CScheduler& scheduler, CConnman& connman) Params().IsMockableChain() ? std::chrono::seconds{1} : std::chrono::seconds{5}); } -void NetGovernance::SendGovernanceSyncRequest(CNode* pnode, CConnman& connman) const -{ - CNetMsgMaker msgMaker(pnode->GetCommonVersion()); - CBloomFilter filter; - connman.PushMessage(pnode, msgMaker.Make(NetMsgType::MNGOVERNANCESYNC, uint256(), filter)); -} - -int NetGovernance::RequestGovernanceObjectVotes(const std::vector& vNodesCopy, CConnman& connman) const -{ - // Maximum number of nodes to request votes from for the same object hash on real networks - // (mainnet, testnet, devnets). Keep this low to avoid unnecessary bandwidth usage. - static constexpr size_t REALNET_PEERS_PER_HASH{3}; - // Maximum number of nodes to request votes from for the same object hash on regtest. - // During testing, nodes are isolated to create conflicting triggers. Using the real - // networks limit of 3 nodes often results in querying only "non-isolated" nodes, missing the - // isolated ones we need to test. This high limit ensures all available nodes are queried. - static constexpr size_t REGTEST_PEERS_PER_HASH{std::numeric_limits::max()}; - - if (vNodesCopy.empty()) return -1; - - int64_t nNow = GetTime(); - int nTimeout = 60 * 60; - size_t nPeersPerHashMax = Params().IsMockableChain() ? REGTEST_PEERS_PER_HASH : REALNET_PEERS_PER_HASH; - - - // This should help us to get some idea about an impact this can bring once deployed on mainnet. - // Testnet is ~40 times smaller in masternode count, but only ~1000 masternodes usually vote, - // so 1 obj on mainnet == ~10 objs or ~1000 votes on testnet. However we want to test a higher - // number of votes to make sure it's robust enough, so aim at 2000 votes per masternode per request. - // On mainnet nMaxObjRequestsPerNode is always set to 1. - int nMaxObjRequestsPerNode = 1; - size_t nProjectedVotes = 2000; - if (Params().NetworkIDString() != CBaseChainParams::MAIN) { - nMaxObjRequestsPerNode = - std::max(1, int(nProjectedVotes / - std::max(1, (int)m_gov_manager.GetMNManager().GetListAtChainTip().GetValidMNsCount()))); - } - - static Mutex cs_recently; - static std::map> mapAskedRecently GUARDED_BY(cs_recently); - LOCK(cs_recently); - - auto [vTriggerObjHashes, vOtherObjHashes] = m_gov_manager.FetchGovernanceObjectVotes(nMaxObjRequestsPerNode, nNow, - mapAskedRecently); - - if (vTriggerObjHashes.empty() && vOtherObjHashes.empty()) return -2; - - LogPrint(BCLog::GOBJECT, "%s -- start: vTriggerObjHashes %d vOtherObjHashes %d mapAskedRecently %d\n", __func__, - vTriggerObjHashes.size(), vOtherObjHashes.size(), mapAskedRecently.size()); - - Shuffle(vTriggerObjHashes.begin(), vTriggerObjHashes.end(), FastRandomContext()); - Shuffle(vOtherObjHashes.begin(), vOtherObjHashes.end(), FastRandomContext()); - - for (int i = 0; i < nMaxObjRequestsPerNode; ++i) { - uint256 nHashGovobj; - - // ask for triggers first - if (!vTriggerObjHashes.empty()) { - nHashGovobj = vTriggerObjHashes.back(); - } else { - if (vOtherObjHashes.empty()) break; - nHashGovobj = vOtherObjHashes.back(); - } - bool fAsked = false; - for (const auto& pnode : vNodesCopy) { - // Don't try to sync any data from outbound non-relay "masternode" connections. - // Inbound connection this early is most likely a "masternode" connection - // initiated from another node, so skip it too. - if (!pnode->CanRelay() || (connman.IsActiveMasternode() && pnode->IsInboundConn())) continue; - // stop early to prevent setAskFor overflow - { - LOCK(::cs_main); - size_t nProjectedSize = m_peer_manager->PeerGetRequestedObjectCount(pnode->GetId()) + nProjectedVotes; - if (nProjectedSize > MAX_INV_SZ) continue; - } - // to early to ask the same node - if (mapAskedRecently[nHashGovobj].count(pnode->addr)) continue; - - m_gov_manager.RequestGovernanceObject(pnode, nHashGovobj, connman, true); - mapAskedRecently[nHashGovobj][pnode->addr] = nNow + nTimeout; - fAsked = true; - // stop loop if max number of peers per obj was asked - if (mapAskedRecently[nHashGovobj].size() >= nPeersPerHashMax) break; - } - // NOTE: this should match `if` above (the one before `while`) - if (!vTriggerObjHashes.empty()) { - vTriggerObjHashes.pop_back(); - } else { - vOtherObjHashes.pop_back(); - } - if (!fAsked) i--; - } - LogPrint(BCLog::GOBJECT, "%s -- end: vTriggerObjHashes %d vOtherObjHashes %d mapAskedRecently %d\n", __func__, - vTriggerObjHashes.size(), vOtherObjHashes.size(), mapAskedRecently.size()); - - return int(vTriggerObjHashes.size() + vOtherObjHashes.size()); -} - -void NetGovernance::ProcessTick(CConnman& connman) -{ - assert(m_netfulfilledman.IsValid()); - - static int nTick = 0; - nTick++; - - const static int64_t nSyncStart = TicksSinceEpoch(SystemClock::now()); - const static std::string strAllow = strprintf("allow-sync-%lld", nSyncStart); - - // reset the sync process if the last call to this function was more than 60 minutes ago (client was in sleep mode) - static int64_t nTimeLastProcess = GetTime(); - if (!Params().IsMockableChain() && GetTime() - nTimeLastProcess > 60 * 60 && !connman.IsActiveMasternode()) { - LogPrintf("Sync Tick -- WARNING: no actions for too long, restarting sync...\n"); - m_node_sync.Reset(true); - nTimeLastProcess = GetTime(); - return; - } - - if (GetTime() - nTimeLastProcess < MASTERNODE_SYNC_TICK_SECONDS) { - // too early, nothing to do here - return; - } - - nTimeLastProcess = GetTime(); - const CConnman::NodesSnapshot snap{connman, /* cond = */ CConnman::FullyConnectedOnly}; - - // gradually request the rest of the votes after sync finished - if (m_node_sync.IsSynced()) { - RequestGovernanceObjectVotes(snap.Nodes(), connman); - return; - } - - // Calculate "progress" for LOG reporting / GUI notification - int attempt = m_node_sync.GetAttempt(); - int asset_id = m_node_sync.GetAssetID(); - double nSyncProgress = double(attempt + (asset_id - 1) * 8) / (8 * 4); - LogPrint(BCLog::MNSYNC, "Sync Tick -- nTick %d asset_id %d nTriedPeerCount %d nSyncProgress %f\n", nTick, asset_id, - attempt, nSyncProgress); - uiInterface.NotifyAdditionalDataSyncProgressChanged(nSyncProgress); - - for (auto& pnode : snap.Nodes()) { - CNetMsgMaker msgMaker(pnode->GetCommonVersion()); - - // Don't try to sync any data from outbound non-relay "masternode" connections. - // Inbound connection this early is most likely a "masternode" connection - // initiated from another node, so skip it too. - if (!pnode->CanRelay() || (connman.IsActiveMasternode() && pnode->IsInboundConn())) continue; - - { - if ((pnode->HasPermission(NetPermissionFlags::NoBan) || pnode->IsManualConn()) && - !m_netfulfilledman.HasFulfilledRequest(pnode->addr, strAllow)) { - m_netfulfilledman.RemoveAllFulfilledRequests(pnode->addr); - m_netfulfilledman.AddFulfilledRequest(pnode->addr, strAllow); - LogPrintf("Sync Tick -- skipping mnsync restrictions for peer=%d\n", pnode->GetId()); - } - - if (m_netfulfilledman.HasFulfilledRequest(pnode->addr, "full-sync")) { - // We already fully synced from this node recently, - // disconnect to free this connection slot for another peer. - pnode->fDisconnect = true; - LogPrintf("Sync Tick -- disconnecting from recently synced peer=%d\n", pnode->GetId()); - continue; - } - - // SPORK : ALWAYS ASK FOR SPORKS AS WE SYNC - - if (!m_netfulfilledman.HasFulfilledRequest(pnode->addr, "spork-sync")) { - // always get sporks first, only request once from each peer - m_netfulfilledman.AddFulfilledRequest(pnode->addr, "spork-sync"); - // get current network sporks - connman.PushMessage(pnode, msgMaker.Make(NetMsgType::GETSPORKS)); - LogPrint(BCLog::MNSYNC, "Sync Tick -- nTick %d asset_id %d -- requesting sporks from peer=%d\n", nTick, - asset_id, pnode->GetId()); - } - - if (asset_id == MASTERNODE_SYNC_BLOCKCHAIN) { - int64_t nTimeSyncTimeout = snap.Nodes().size() > 3 ? MASTERNODE_SYNC_TICK_SECONDS - : MASTERNODE_SYNC_TIMEOUT_SECONDS; - if (m_node_sync.IsReachedBestHeader() && (GetTime() - m_node_sync.GetLastBump() > nTimeSyncTimeout)) { - // At this point we know that: - // a) there are peers (because we are looping on at least one of them); - // b) we waited for at least MASTERNODE_SYNC_TICK_SECONDS/MASTERNODE_SYNC_TIMEOUT_SECONDS - // (depending on the number of connected peers) since we reached the headers tip the last - // time (i.e. since fReachedBestHeader has been set to true); - // c) there were no blocks (UpdatedBlockTip, NotifyHeaderTip) or headers (AcceptedBlockHeader) - // for at least MASTERNODE_SYNC_TICK_SECONDS/MASTERNODE_SYNC_TIMEOUT_SECONDS (depending on - // the number of connected peers). - // We must be at the tip already, let's move to the next asset. - m_node_sync.SwitchToNextAsset(); - uiInterface.NotifyAdditionalDataSyncProgressChanged(nSyncProgress); - - if (gArgs.GetBoolArg("-syncmempool", DEFAULT_SYNC_MEMPOOL)) { - // Now that the blockchain is synced request the mempool from the connected outbound nodes if possible - for (auto pNodeTmp : snap.Nodes()) { - bool fRequestedEarlier = m_netfulfilledman.HasFulfilledRequest(pNodeTmp->addr, - "mempool-sync"); - if (!pNodeTmp->IsInboundConn() && !fRequestedEarlier && !pNodeTmp->IsBlockRelayOnly()) { - m_netfulfilledman.AddFulfilledRequest(pNodeTmp->addr, "mempool-sync"); - connman.PushMessage(pNodeTmp, msgMaker.Make(NetMsgType::MEMPOOL)); - LogPrint(BCLog::MNSYNC, /* Continued */ - "Sync Tick -- nTick %d asset_id %d -- syncing mempool from peer=%d\n", nTick, - asset_id, pNodeTmp->GetId()); - } - } - } - } - } - - // GOVOBJ : SYNC GOVERNANCE ITEMS FROM OUR PEERS - - if (asset_id == MASTERNODE_SYNC_GOVERNANCE) { - if (!m_gov_manager.IsValid()) { - m_node_sync.SwitchToNextAsset(); - return; - } - LogPrint(BCLog::GOBJECT, "Sync Tick -- nTick %d asset_id %d last_bump %lld GetTime() %lld diff %lld\n", - nTick, asset_id, m_node_sync.GetLastBump(), GetTime(), GetTime() - m_node_sync.GetLastBump()); - - // check for timeout first - if (GetTime() - m_node_sync.GetLastBump() > MASTERNODE_SYNC_TIMEOUT_SECONDS) { - LogPrint(BCLog::MNSYNC, "Sync Tick -- nTick %d asset_id %d -- timeout\n", nTick, asset_id); - if (attempt == 0) { - LogPrintf("Sync Tick -- WARNING: failed to sync %s\n", m_node_sync.GetAssetName()); - // it's kind of ok to skip this for now, hopefully we'll catch up later? - } - m_node_sync.SwitchToNextAsset(); - return; - } - - // only request obj sync once from each peer - if (m_netfulfilledman.HasFulfilledRequest(pnode->addr, "governance-sync")) { - // will request votes on per-obj basis from each node in a separate loop below - // to avoid deadlocks here - continue; - } - m_netfulfilledman.AddFulfilledRequest(pnode->addr, "governance-sync"); - - m_node_sync.BumpAttempt(); - - SendGovernanceSyncRequest(pnode, connman); - - break; // this will cause each peer to get one request each six seconds for the various assets we need - } - } - } - - - if (asset_id != MASTERNODE_SYNC_GOVERNANCE) { - // looped through all nodes and not syncing governance yet/already, release them - return; - } - - // request votes on per-obj basis from each node - for (const auto& pnode : snap.Nodes()) { - if (!m_netfulfilledman.HasFulfilledRequest(pnode->addr, "governance-sync")) { - continue; // to early for this node - } - const std::vector vNodeCopy{pnode}; - int nObjsLeftToAsk = RequestGovernanceObjectVotes(vNodeCopy, connman); - // check for data - if (nObjsLeftToAsk == 0) { - static int64_t nTimeNoObjectsLeft = 0; - static int nLastTick = 0; - static int nLastVotes = 0; - if (nTimeNoObjectsLeft == 0) { - // asked all objects for votes for the first time - nTimeNoObjectsLeft = GetTime(); - } - // make sure the condition below is checked only once per tick - if (nLastTick == nTick) continue; - if (GetTime() - nTimeNoObjectsLeft > MASTERNODE_SYNC_TIMEOUT_SECONDS && - m_gov_manager.GetVoteCount() - nLastVotes < - std::max(int(0.0001 * nLastVotes), MASTERNODE_SYNC_TICK_SECONDS)) { - // We already asked for all objects, waited for MASTERNODE_SYNC_TIMEOUT_SECONDS - // after that and less then 0.01% or MASTERNODE_SYNC_TICK_SECONDS - // (i.e. 1 per second) votes were received during the last tick. - // We can be pretty sure that we are done syncing. - LogPrintf("Sync Tick -- nTick %d asset_id %d -- asked for all objects, nothing to do\n", nTick, - MASTERNODE_SYNC_GOVERNANCE); - // reset nTimeNoObjectsLeft to be able to use the same condition on resync - nTimeNoObjectsLeft = 0; - m_node_sync.SwitchToNextAsset(); - return; - } - nLastTick = nTick; - nLastVotes = m_gov_manager.GetVoteCount(); - } - } -} - void NetGovernance::ProcessMessage(CNode& peer, CConnman& connman, const std::string& msg_type, CDataStream& vRecv) { if (!m_gov_manager.IsValid()) return; diff --git a/src/governance/net_governance.h b/src/governance/net_governance.h index e582ff41b7e1..a8f5e300c341 100644 --- a/src/governance/net_governance.h +++ b/src/governance/net_governance.h @@ -9,17 +9,14 @@ class CGovernanceManager; class CMasternodeSync; -class CNetFulfilledRequestManager; class NetGovernance final : public NetHandler { public: - NetGovernance(PeerManagerInternal* peer_manager, CGovernanceManager& gov_manager, CMasternodeSync& node_sync, - CNetFulfilledRequestManager& netfulfilledman) : + NetGovernance(PeerManagerInternal* peer_manager, CGovernanceManager& gov_manager, CMasternodeSync& node_sync) : NetHandler(peer_manager), m_gov_manager(gov_manager), - m_node_sync(node_sync), - m_netfulfilledman(netfulfilledman) + m_node_sync(node_sync) { } void Schedule(CScheduler& scheduler, CConnman& connman) override; @@ -27,13 +24,8 @@ class NetGovernance final : public NetHandler void ProcessMessage(CNode& peer, CConnman& connman, const std::string& msg_type, CDataStream& vRecv) override; private: - void SendGovernanceSyncRequest(CNode* pnode, CConnman& connman) const; - int RequestGovernanceObjectVotes(const std::vector& vNodesCopy, CConnman& connman) const; - void ProcessTick(CConnman& connman); - CGovernanceManager& m_gov_manager; CMasternodeSync& m_node_sync; - CNetFulfilledRequestManager& m_netfulfilledman; }; #endif // BITCOIN_GOVERNANCE_NET_GOVERNANCE_H diff --git a/src/init.cpp b/src/init.cpp index 82b4b47eb326..82627dd11e0b 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -2234,8 +2235,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) } return InitError(strprintf(_("Failed to clear governance cache at %s"), file_path)); } + node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.govman, *node.mn_sync)); } - node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.govman, *node.mn_sync, *node.netfulfilledman)); + node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.govman, *node.mn_sync, *node.netfulfilledman)); // ********************************************************* Step 8: start indexers if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) { diff --git a/src/node/sync_manager.cpp b/src/node/sync_manager.cpp new file mode 100644 index 000000000000..5893be1d91b3 --- /dev/null +++ b/src/node/sync_manager.cpp @@ -0,0 +1,320 @@ +// Copyright (c) 2024-2025 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class CConnman; + +void SyncManager::Schedule(CScheduler& scheduler, CConnman& connman) +{ + scheduler.scheduleEvery( + [this, &connman]() -> void { + if (ShutdownRequested()) return; + ProcessTick(connman); + }, + std::chrono::seconds{1}); +} + +void SyncManager::SendGovernanceSyncRequest(CNode* pnode, CConnman& connman) const +{ + CNetMsgMaker msgMaker(pnode->GetCommonVersion()); + CBloomFilter filter; + connman.PushMessage(pnode, msgMaker.Make(NetMsgType::MNGOVERNANCESYNC, uint256(), filter)); +} + +int SyncManager::RequestGovernanceObjectVotes(const std::vector& vNodesCopy, CConnman& connman) const +{ + // Maximum number of nodes to request votes from for the same object hash on real networks + // (mainnet, testnet, devnets). Keep this low to avoid unnecessary bandwidth usage. + static constexpr size_t REALNET_PEERS_PER_HASH{3}; + // Maximum number of nodes to request votes from for the same object hash on regtest. + // During testing, nodes are isolated to create conflicting triggers. Using the real + // networks limit of 3 nodes often results in querying only "non-isolated" nodes, missing the + // isolated ones we need to test. This high limit ensures all available nodes are queried. + static constexpr size_t REGTEST_PEERS_PER_HASH{std::numeric_limits::max()}; + + if (vNodesCopy.empty()) return -1; + + int64_t nNow = GetTime(); + int nTimeout = 60 * 60; + size_t nPeersPerHashMax = Params().IsMockableChain() ? REGTEST_PEERS_PER_HASH : REALNET_PEERS_PER_HASH; + + + // This should help us to get some idea about an impact this can bring once deployed on mainnet. + // Testnet is ~40 times smaller in masternode count, but only ~1000 masternodes usually vote, + // so 1 obj on mainnet == ~10 objs or ~1000 votes on testnet. However we want to test a higher + // number of votes to make sure it's robust enough, so aim at 2000 votes per masternode per request. + // On mainnet nMaxObjRequestsPerNode is always set to 1. + int nMaxObjRequestsPerNode = 1; + size_t nProjectedVotes = 2000; + if (Params().NetworkIDString() != CBaseChainParams::MAIN) { + nMaxObjRequestsPerNode = + std::max(1, int(nProjectedVotes / + std::max(1, (int)m_gov_manager.GetMNManager().GetListAtChainTip().GetValidMNsCount()))); + } + + static Mutex cs_recently; + static std::map> mapAskedRecently GUARDED_BY(cs_recently); + LOCK(cs_recently); + + auto [vTriggerObjHashes, vOtherObjHashes] = m_gov_manager.FetchGovernanceObjectVotes(nMaxObjRequestsPerNode, nNow, + mapAskedRecently); + + if (vTriggerObjHashes.empty() && vOtherObjHashes.empty()) return -2; + + LogPrint(BCLog::GOBJECT, "%s -- start: vTriggerObjHashes %d vOtherObjHashes %d mapAskedRecently %d\n", __func__, + vTriggerObjHashes.size(), vOtherObjHashes.size(), mapAskedRecently.size()); + + Shuffle(vTriggerObjHashes.begin(), vTriggerObjHashes.end(), FastRandomContext()); + Shuffle(vOtherObjHashes.begin(), vOtherObjHashes.end(), FastRandomContext()); + + for (int i = 0; i < nMaxObjRequestsPerNode; ++i) { + uint256 nHashGovobj; + + // ask for triggers first + if (!vTriggerObjHashes.empty()) { + nHashGovobj = vTriggerObjHashes.back(); + } else { + if (vOtherObjHashes.empty()) break; + nHashGovobj = vOtherObjHashes.back(); + } + bool fAsked = false; + for (const auto& pnode : vNodesCopy) { + // Don't try to sync any data from outbound non-relay "masternode" connections. + // Inbound connection this early is most likely a "masternode" connection + // initiated from another node, so skip it too. + if (!pnode->CanRelay() || (connman.IsActiveMasternode() && pnode->IsInboundConn())) continue; + // stop early to prevent setAskFor overflow + { + LOCK(::cs_main); + size_t nProjectedSize = m_peer_manager->PeerGetRequestedObjectCount(pnode->GetId()) + nProjectedVotes; + if (nProjectedSize > MAX_INV_SZ) continue; + } + // to early to ask the same node + if (mapAskedRecently[nHashGovobj].count(pnode->addr)) continue; + + m_gov_manager.RequestGovernanceObject(pnode, nHashGovobj, connman, true); + mapAskedRecently[nHashGovobj][pnode->addr] = nNow + nTimeout; + fAsked = true; + // stop loop if max number of peers per obj was asked + if (mapAskedRecently[nHashGovobj].size() >= nPeersPerHashMax) break; + } + // NOTE: this should match `if` above (the one before `while`) + if (!vTriggerObjHashes.empty()) { + vTriggerObjHashes.pop_back(); + } else { + vOtherObjHashes.pop_back(); + } + if (!fAsked) i--; + } + LogPrint(BCLog::GOBJECT, "%s -- end: vTriggerObjHashes %d vOtherObjHashes %d mapAskedRecently %d\n", __func__, + vTriggerObjHashes.size(), vOtherObjHashes.size(), mapAskedRecently.size()); + + return int(vTriggerObjHashes.size() + vOtherObjHashes.size()); +} + +void SyncManager::ProcessTick(CConnman& connman) +{ + assert(m_netfulfilledman.IsValid()); + + static int nTick = 0; + nTick++; + + const static int64_t nSyncStart = TicksSinceEpoch(SystemClock::now()); + const static std::string strAllow = strprintf("allow-sync-%lld", nSyncStart); + + // reset the sync process if the last call to this function was more than 60 minutes ago (client was in sleep mode) + static int64_t nTimeLastProcess = GetTime(); + if (!Params().IsMockableChain() && GetTime() - nTimeLastProcess > 60 * 60 && !connman.IsActiveMasternode()) { + LogPrintf("Sync Tick -- WARNING: no actions for too long, restarting sync...\n"); + m_node_sync.Reset(true); + nTimeLastProcess = GetTime(); + return; + } + + if (GetTime() - nTimeLastProcess < MASTERNODE_SYNC_TICK_SECONDS) { + // too early, nothing to do here + return; + } + + nTimeLastProcess = GetTime(); + const CConnman::NodesSnapshot snap{connman, /* cond = */ CConnman::FullyConnectedOnly}; + + // gradually request the rest of the votes after sync finished + if (m_node_sync.IsSynced()) { + RequestGovernanceObjectVotes(snap.Nodes(), connman); + return; + } + + // Calculate "progress" for LOG reporting / GUI notification + int attempt = m_node_sync.GetAttempt(); + int asset_id = m_node_sync.GetAssetID(); + double nSyncProgress = double(attempt + (asset_id - 1) * 8) / (8 * 4); + LogPrint(BCLog::MNSYNC, "Sync Tick -- nTick %d asset_id %d nTriedPeerCount %d nSyncProgress %f\n", nTick, asset_id, + attempt, nSyncProgress); + uiInterface.NotifyAdditionalDataSyncProgressChanged(nSyncProgress); + + // TODO: move switch-case out from this loop; logic & nodes code to be separated + for (auto& pnode : snap.Nodes()) { + CNetMsgMaker msgMaker(pnode->GetCommonVersion()); + + // Don't try to sync any data from outbound non-relay "masternode" connections. + // Inbound connection this early is most likely a "masternode" connection + // initiated from another node, so skip it too. + if (!pnode->CanRelay() || (connman.IsActiveMasternode() && pnode->IsInboundConn())) continue; + + { + if ((pnode->HasPermission(NetPermissionFlags::NoBan) || pnode->IsManualConn()) && + !m_netfulfilledman.HasFulfilledRequest(pnode->addr, strAllow)) { + m_netfulfilledman.RemoveAllFulfilledRequests(pnode->addr); + m_netfulfilledman.AddFulfilledRequest(pnode->addr, strAllow); + LogPrintf("Sync Tick -- skipping mnsync restrictions for peer=%d\n", pnode->GetId()); + } + + if (m_netfulfilledman.HasFulfilledRequest(pnode->addr, "full-sync")) { + // We already fully synced from this node recently, + // disconnect to free this connection slot for another peer. + pnode->fDisconnect = true; + LogPrintf("Sync Tick -- disconnecting from recently synced peer=%d\n", pnode->GetId()); + continue; + } + + // SPORK : ALWAYS ASK FOR SPORKS AS WE SYNC + + if (!m_netfulfilledman.HasFulfilledRequest(pnode->addr, "spork-sync")) { + // always get sporks first, only request once from each peer + m_netfulfilledman.AddFulfilledRequest(pnode->addr, "spork-sync"); + // get current network sporks + connman.PushMessage(pnode, msgMaker.Make(NetMsgType::GETSPORKS)); + LogPrint(BCLog::MNSYNC, "Sync Tick -- nTick %d asset_id %d -- requesting sporks from peer=%d\n", nTick, + asset_id, pnode->GetId()); + } + + if (asset_id == MASTERNODE_SYNC_BLOCKCHAIN) { + int64_t nTimeSyncTimeout = snap.Nodes().size() > 3 ? MASTERNODE_SYNC_TICK_SECONDS + : MASTERNODE_SYNC_TIMEOUT_SECONDS; + if (m_node_sync.IsReachedBestHeader() && (GetTime() - m_node_sync.GetLastBump() > nTimeSyncTimeout)) { + // At this point we know that: + // a) there are peers (because we are looping on at least one of them); + // b) we waited for at least MASTERNODE_SYNC_TICK_SECONDS/MASTERNODE_SYNC_TIMEOUT_SECONDS + // (depending on the number of connected peers) since we reached the headers tip the last + // time (i.e. since fReachedBestHeader has been set to true); + // c) there were no blocks (UpdatedBlockTip, NotifyHeaderTip) or headers (AcceptedBlockHeader) + // for at least MASTERNODE_SYNC_TICK_SECONDS/MASTERNODE_SYNC_TIMEOUT_SECONDS (depending on + // the number of connected peers). + // We must be at the tip already, let's move to the next asset. + m_node_sync.SwitchToNextAsset(); + uiInterface.NotifyAdditionalDataSyncProgressChanged(nSyncProgress); + + if (gArgs.GetBoolArg("-syncmempool", DEFAULT_SYNC_MEMPOOL)) { + // Now that the blockchain is synced request the mempool from the connected outbound nodes if possible + for (auto pNodeTmp : snap.Nodes()) { + bool fRequestedEarlier = m_netfulfilledman.HasFulfilledRequest(pNodeTmp->addr, + "mempool-sync"); + if (!pNodeTmp->IsInboundConn() && !fRequestedEarlier && !pNodeTmp->IsBlockRelayOnly()) { + m_netfulfilledman.AddFulfilledRequest(pNodeTmp->addr, "mempool-sync"); + connman.PushMessage(pNodeTmp, msgMaker.Make(NetMsgType::MEMPOOL)); + LogPrint(BCLog::MNSYNC, /* Continued */ + "Sync Tick -- nTick %d asset_id %d -- syncing mempool from peer=%d\n", nTick, + asset_id, pNodeTmp->GetId()); + } + } + } + } + } + + // GOVOBJ : SYNC GOVERNANCE ITEMS FROM OUR PEERS + + if (asset_id == MASTERNODE_SYNC_GOVERNANCE) { + if (!m_gov_manager.IsValid()) { + m_node_sync.SwitchToNextAsset(); + return; + } + LogPrint(BCLog::GOBJECT, "Sync Tick -- nTick %d asset_id %d last_bump %lld GetTime() %lld diff %lld\n", + nTick, asset_id, m_node_sync.GetLastBump(), GetTime(), GetTime() - m_node_sync.GetLastBump()); + + // check for timeout first + if (GetTime() - m_node_sync.GetLastBump() > MASTERNODE_SYNC_TIMEOUT_SECONDS) { + LogPrint(BCLog::MNSYNC, "Sync Tick -- nTick %d asset_id %d -- timeout\n", nTick, asset_id); + if (attempt == 0) { + LogPrintf("Sync Tick -- WARNING: failed to sync %s\n", m_node_sync.GetAssetName()); + // it's kind of ok to skip this for now, hopefully we'll catch up later? + } + m_node_sync.SwitchToNextAsset(); + return; + } + + // only request obj sync once from each peer + if (m_netfulfilledman.HasFulfilledRequest(pnode->addr, "governance-sync")) { + // will request votes on per-obj basis from each node in a separate loop below + // to avoid deadlocks here + continue; + } + m_netfulfilledman.AddFulfilledRequest(pnode->addr, "governance-sync"); + + m_node_sync.BumpAttempt(); + + SendGovernanceSyncRequest(pnode, connman); + + break; // this will cause each peer to get one request each six seconds for the various assets we need + } + } + } + + + if (asset_id != MASTERNODE_SYNC_GOVERNANCE) { + // looped through all nodes and not syncing governance yet/already, release them + return; + } + + // request votes on per-obj basis from each node + for (const auto& pnode : snap.Nodes()) { + if (!m_netfulfilledman.HasFulfilledRequest(pnode->addr, "governance-sync")) { + continue; // to early for this node + } + const std::vector vNodeCopy{pnode}; + int nObjsLeftToAsk = RequestGovernanceObjectVotes(vNodeCopy, connman); + // check for data + if (nObjsLeftToAsk == 0) { + static int64_t nTimeNoObjectsLeft = 0; + static int nLastTick = 0; + static int nLastVotes = 0; + if (nTimeNoObjectsLeft == 0) { + // asked all objects for votes for the first time + nTimeNoObjectsLeft = GetTime(); + } + // make sure the condition below is checked only once per tick + if (nLastTick == nTick) continue; + if (GetTime() - nTimeNoObjectsLeft > MASTERNODE_SYNC_TIMEOUT_SECONDS && + m_gov_manager.GetVoteCount() - nLastVotes < + std::max(int(0.0001 * nLastVotes), MASTERNODE_SYNC_TICK_SECONDS)) { + // We already asked for all objects, waited for MASTERNODE_SYNC_TIMEOUT_SECONDS + // after that and less then 0.01% or MASTERNODE_SYNC_TICK_SECONDS + // (i.e. 1 per second) votes were received during the last tick. + // We can be pretty sure that we are done syncing. + LogPrintf("Sync Tick -- nTick %d asset_id %d -- asked for all objects, nothing to do\n", nTick, + MASTERNODE_SYNC_GOVERNANCE); + // reset nTimeNoObjectsLeft to be able to use the same condition on resync + nTimeNoObjectsLeft = 0; + m_node_sync.SwitchToNextAsset(); + return; + } + nLastTick = nTick; + nLastVotes = m_gov_manager.GetVoteCount(); + } + } +} diff --git a/src/node/sync_manager.h b/src/node/sync_manager.h new file mode 100644 index 000000000000..c014f41e4ffd --- /dev/null +++ b/src/node/sync_manager.h @@ -0,0 +1,37 @@ +// Copyright (c) 2014-2025 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_NODE_SYNC_MANAGER_H +#define BITCOIN_NODE_SYNC_MANAGER_H + +#include + +class CGovernanceManager; +class CMasternodeSync; +class CNetFulfilledRequestManager; + +class SyncManager final : public NetHandler +{ +public: + SyncManager(PeerManagerInternal* peer_manager, CGovernanceManager& gov_manager, CMasternodeSync& node_sync, + CNetFulfilledRequestManager& netfulfilledman) : + NetHandler(peer_manager), + m_gov_manager(gov_manager), + m_node_sync(node_sync), + m_netfulfilledman(netfulfilledman) + { + } + void Schedule(CScheduler& scheduler, CConnman& connman) override; + +private: + void SendGovernanceSyncRequest(CNode* pnode, CConnman& connman) const; + int RequestGovernanceObjectVotes(const std::vector& vNodesCopy, CConnman& connman) const; + void ProcessTick(CConnman& connman); + + CGovernanceManager& m_gov_manager; + CMasternodeSync& m_node_sync; + CNetFulfilledRequestManager& m_netfulfilledman; +}; + +#endif // BITCOIN_NODE_SYNC_MANAGER_H From 9ac269ce40d7ad652c8fb77b47c273706824126d Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sat, 13 Dec 2025 03:24:33 +0700 Subject: [PATCH 17/20] refactor: move ProcessMessage from CMasternodeSync to SyncManager --- src/masternode/sync.cpp | 17 +---------------- src/masternode/sync.h | 4 ---- src/net_processing.cpp | 1 - src/node/sync_manager.cpp | 15 +++++++++++++++ src/node/sync_manager.h | 1 + 5 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/masternode/sync.cpp b/src/masternode/sync.cpp index 99fd544aaa19..8b7f70b0f150 100644 --- a/src/masternode/sync.cpp +++ b/src/masternode/sync.cpp @@ -73,7 +73,7 @@ void CMasternodeSync::SwitchToNextAsset() LogPrintf("CMasternodeSync::SwitchToNextAsset -- Completed %s in %llds\n", GetAssetName(), GetTime() - nTimeAssetSyncStarted); nCurrentAsset = MASTERNODE_SYNC_FINISHED; uiInterface.NotifyAdditionalDataSyncProgressChanged(1); - + // TODO: move connman to notifier connman.ForEachNode(CConnman::AllNodes, [this](const CNode* pnode) { m_netfulfilledman.AddFulfilledRequest(pnode->addr, "full-sync"); }); @@ -96,21 +96,6 @@ std::string CMasternodeSync::GetSyncStatus() const } } -void CMasternodeSync::ProcessMessage(const CNode& peer, std::string_view msg_type, CDataStream& vRecv) const -{ - //Sync status count - if (msg_type != NetMsgType::SYNCSTATUSCOUNT) return; - - //do not care about stats if sync process finished - if (IsSynced()) return; - - int nItemID; - int nCount; - vRecv >> nItemID >> nCount; - - LogPrint(BCLog::MNSYNC, "SYNCSTATUSCOUNT -- got inventory count: nItemID=%d nCount=%d peer=%d\n", nItemID, nCount, peer.GetId()); -} - void CMasternodeSync::AcceptedBlockHeader(const CBlockIndex *pindexNew) { LogPrint(BCLog::MNSYNC, "CMasternodeSync::AcceptedBlockHeader -- pindexNew->nHeight: %d\n", pindexNew->nHeight); diff --git a/src/masternode/sync.h b/src/masternode/sync.h index 537761b6340a..d1e0111a5326 100644 --- a/src/masternode/sync.h +++ b/src/masternode/sync.h @@ -9,10 +9,8 @@ class CConnman; class CBlockIndex; -class CDataStream; class CMasternodeSync; class CNetFulfilledRequestManager; -class CNode; /** Default for -syncmempool */ static const bool DEFAULT_SYNC_MEMPOOL = true; @@ -75,8 +73,6 @@ class CMasternodeSync void Reset(bool fForce = false, bool fNotifyReset = true); void SwitchToNextAsset(); - void ProcessMessage(const CNode& peer, std::string_view msg_type, CDataStream& vRecv) const; - void AcceptedBlockHeader(const CBlockIndex *pindexNew); void NotifyHeaderTip(const CBlockIndex *pindexNew, bool fInitialDownload); void UpdatedBlockTip(const CBlockIndex *pindexTip, const CBlockIndex *pindexNew, bool fInitialDownload); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 1407bb120498..4e110c7e3ab5 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -5446,7 +5446,6 @@ void PeerManagerImpl::ProcessMessage( m_active_ctx->shareman->ProcessMessage(pfrom, msg_type, vRecv); } PostProcessMessage(m_sporkman.ProcessMessage(pfrom, m_connman, msg_type, vRecv), pfrom.GetId()); - m_mn_sync.ProcessMessage(pfrom, msg_type, vRecv); PostProcessMessage(CMNAuth::ProcessMessage(pfrom, peer->m_their_services, m_connman, m_mn_metaman, m_mn_activeman, m_mn_sync, m_dmnman->GetListAtChainTip(), msg_type, vRecv), pfrom.GetId()); PostProcessMessage(m_llmq_ctx->quorum_block_processor->ProcessMessage(pfrom, msg_type, vRecv), pfrom.GetId()); PostProcessMessage(m_llmq_ctx->qdkgsman->ProcessMessage(pfrom, is_masternode, msg_type, vRecv), pfrom.GetId()); diff --git a/src/node/sync_manager.cpp b/src/node/sync_manager.cpp index 5893be1d91b3..bb346850719b 100644 --- a/src/node/sync_manager.cpp +++ b/src/node/sync_manager.cpp @@ -318,3 +318,18 @@ void SyncManager::ProcessTick(CConnman& connman) } } } + +void SyncManager::ProcessMessage(CNode& peer, CConnman&, const std::string& msg_type, CDataStream& vRecv) +{ + //Sync status count + if (msg_type != NetMsgType::SYNCSTATUSCOUNT) return; + + //do not care about stats if sync process finished + if (m_node_sync.IsSynced()) return; + + int nItemID; + int nCount; + vRecv >> nItemID >> nCount; + + LogPrint(BCLog::MNSYNC, "SYNCSTATUSCOUNT -- got inventory count: nItemID=%d nCount=%d peer=%d\n", nItemID, nCount, peer.GetId()); +} diff --git a/src/node/sync_manager.h b/src/node/sync_manager.h index c014f41e4ffd..db7670be982b 100644 --- a/src/node/sync_manager.h +++ b/src/node/sync_manager.h @@ -23,6 +23,7 @@ class SyncManager final : public NetHandler { } void Schedule(CScheduler& scheduler, CConnman& connman) override; + void ProcessMessage(CNode& peer, CConnman&, const std::string& msg_type, CDataStream& vRecv) override; private: void SendGovernanceSyncRequest(CNode* pnode, CConnman& connman) const; From dc08ef42fef13e474b3f1329ff991b8ab3b61ffa Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sat, 13 Dec 2025 04:06:37 +0700 Subject: [PATCH 18/20] refactor: break circular dependency of CMasternodeManager over net.h --- src/init.cpp | 2 +- src/masternode/sync.cpp | 19 +++++-------------- src/masternode/sync.h | 17 ++++++++++------- src/node/sync_manager.cpp | 15 +++++++++++++++ src/node/sync_manager.h | 15 +++++++++++++++ src/test/util/setup_common.cpp | 3 ++- test/lint/lint-circular-dependencies.py | 1 - 7 files changed, 48 insertions(+), 24 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 82627dd11e0b..5eea06368d9d 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1990,7 +1990,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) * need it or not further down and then query if the database is initialized * to check if validation is enabled. */ - node.mn_sync = std::make_unique(*node.connman, *node.netfulfilledman); + node.mn_sync = std::make_unique(std::make_unique(*node.connman, *node.netfulfilledman)); node.govman = std::make_unique(*node.mn_metaman, *node.netfulfilledman, *node.chainman, node.dmnman, *node.mn_sync); diff --git a/src/masternode/sync.cpp b/src/masternode/sync.cpp index 8b7f70b0f150..93ba75c8dcfa 100644 --- a/src/masternode/sync.cpp +++ b/src/masternode/sync.cpp @@ -5,18 +5,15 @@ #include #include -#include -#include -#include #include #include -CMasternodeSync::CMasternodeSync(CConnman& _connman, CNetFulfilledRequestManager& netfulfilledman) : +CMasternodeSync::CMasternodeSync(std::unique_ptr&& sync_notifier) : nTimeAssetSyncStarted{GetTime()}, nTimeLastBumped{GetTime()}, - connman{_connman}, - m_netfulfilledman{netfulfilledman} + m_sync_notifier{std::move(sync_notifier)} { + assert(m_sync_notifier != nullptr); } CMasternodeSync::~CMasternodeSync() = default; @@ -36,7 +33,7 @@ void CMasternodeSync::Reset(bool fForce, bool fNotifyReset) nTimeLastUpdateBlockTip = 0; fReachedBestHeader = false; if (fNotifyReset) { - uiInterface.NotifyAdditionalDataSyncProgressChanged(-1); + m_sync_notifier->SyncReset(); } } @@ -60,8 +57,6 @@ std::string CMasternodeSync::GetAssetName() const void CMasternodeSync::SwitchToNextAsset() { - assert(m_netfulfilledman.IsValid()); - switch(nCurrentAsset) { case(MASTERNODE_SYNC_BLOCKCHAIN): @@ -72,11 +67,7 @@ void CMasternodeSync::SwitchToNextAsset() case(MASTERNODE_SYNC_GOVERNANCE): LogPrintf("CMasternodeSync::SwitchToNextAsset -- Completed %s in %llds\n", GetAssetName(), GetTime() - nTimeAssetSyncStarted); nCurrentAsset = MASTERNODE_SYNC_FINISHED; - uiInterface.NotifyAdditionalDataSyncProgressChanged(1); - // TODO: move connman to notifier - connman.ForEachNode(CConnman::AllNodes, [this](const CNode* pnode) { - m_netfulfilledman.AddFulfilledRequest(pnode->addr, "full-sync"); - }); + m_sync_notifier->SyncFinished(); LogPrintf("CMasternodeSync::SwitchToNextAsset -- Sync has finished\n"); break; diff --git a/src/masternode/sync.h b/src/masternode/sync.h index d1e0111a5326..b58110fc93db 100644 --- a/src/masternode/sync.h +++ b/src/masternode/sync.h @@ -5,12 +5,10 @@ #define BITCOIN_MASTERNODE_SYNC_H #include +#include #include -class CConnman; class CBlockIndex; -class CMasternodeSync; -class CNetFulfilledRequestManager; /** Default for -syncmempool */ static const bool DEFAULT_SYNC_MEMPOOL = true; @@ -25,10 +23,16 @@ static constexpr int MASTERNODE_SYNC_TICK_SECONDS = 6; static constexpr int MASTERNODE_SYNC_TIMEOUT_SECONDS = 30; // our blocks are 2.5 minutes so 30 seconds should be fine static constexpr int MASTERNODE_SYNC_RESET_SECONDS = 900; // Reset fReachedBestHeader in CMasternodeSync::Reset if UpdateBlockTip hasn't been called for this seconds +class NodeSyncNotifier +{ +public: + virtual void SyncReset() = 0; + virtual void SyncFinished() = 0; +}; + // // CMasternodeSync : Sync masternode assets in stages // - class CMasternodeSync { private: @@ -47,14 +51,13 @@ class CMasternodeSync /// Last time UpdateBlockTip has been called std::atomic nTimeLastUpdateBlockTip{0}; - CConnman& connman; - CNetFulfilledRequestManager& m_netfulfilledman; + std::unique_ptr m_sync_notifier; public: CMasternodeSync() = delete; CMasternodeSync(const CMasternodeSync&) = delete; CMasternodeSync& operator=(const CMasternodeSync&) = delete; - explicit CMasternodeSync(CConnman& _connman, CNetFulfilledRequestManager& netfulfilledman); + explicit CMasternodeSync(std::unique_ptr&& sync_notifier); ~CMasternodeSync(); bool IsBlockchainSynced() const { return nCurrentAsset > MASTERNODE_SYNC_BLOCKCHAIN; } diff --git a/src/node/sync_manager.cpp b/src/node/sync_manager.cpp index bb346850719b..3e0f3cbbffca 100644 --- a/src/node/sync_manager.cpp +++ b/src/node/sync_manager.cpp @@ -333,3 +333,18 @@ void SyncManager::ProcessMessage(CNode& peer, CConnman&, const std::string& msg_ LogPrint(BCLog::MNSYNC, "SYNCSTATUSCOUNT -- got inventory count: nItemID=%d nCount=%d peer=%d\n", nItemID, nCount, peer.GetId()); } + +void NodeSyncNotifierImpl::SyncReset() +{ + uiInterface.NotifyAdditionalDataSyncProgressChanged(-1); +} + +void NodeSyncNotifierImpl::SyncFinished() +{ + assert(m_netfulfilledman.IsValid()); + + uiInterface.NotifyAdditionalDataSyncProgressChanged(1); + m_connman.ForEachNode(CConnman::AllNodes, [this](const CNode* pnode) { + m_netfulfilledman.AddFulfilledRequest(pnode->addr, "full-sync"); + }); +} diff --git a/src/node/sync_manager.h b/src/node/sync_manager.h index db7670be982b..8bb884af8ade 100644 --- a/src/node/sync_manager.h +++ b/src/node/sync_manager.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_NODE_SYNC_MANAGER_H #define BITCOIN_NODE_SYNC_MANAGER_H +#include #include class CGovernanceManager; @@ -35,4 +36,18 @@ class SyncManager final : public NetHandler CNetFulfilledRequestManager& m_netfulfilledman; }; +class NodeSyncNotifierImpl : public NodeSyncNotifier +{ +public: + NodeSyncNotifierImpl(CConnman& connman, CNetFulfilledRequestManager& netfulfilledman) : + m_connman(connman), + m_netfulfilledman(netfulfilledman) + {} + + void SyncReset() override; + void SyncFinished() override; +private: + CConnman& m_connman; + CNetFulfilledRequestManager& m_netfulfilledman; +}; #endif // BITCOIN_NODE_SYNC_MANAGER_H diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 2dfc5dcb66c1..821fb4a9cb48 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -273,7 +274,7 @@ ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::ve m_node.chainman = std::make_unique(); m_node.chainman->m_blockman.m_block_tree_db = std::make_unique(m_cache_sizes.block_tree_db, true); - m_node.mn_sync = std::make_unique(*m_node.connman, *m_node.netfulfilledman); + m_node.mn_sync = std::make_unique(std::make_unique(*m_node.connman, *m_node.netfulfilledman)); m_node.govman = std::make_unique(*m_node.mn_metaman, *m_node.netfulfilledman, *m_node.chainman, m_node.dmnman, *m_node.mn_sync); // Start script-checking threads. Set g_parallel_script_checks to true so they are used. diff --git a/test/lint/lint-circular-dependencies.py b/test/lint/lint-circular-dependencies.py index a3054113f073..03cae3d37b63 100755 --- a/test/lint/lint-circular-dependencies.py +++ b/test/lint/lint-circular-dependencies.py @@ -48,7 +48,6 @@ "evo/specialtxman -> validation -> evo/specialtxman", "governance/classes -> governance/object -> governance/governance -> governance/classes", "governance/governance -> governance/signing -> governance/object -> governance/governance", - "masternode/sync -> net -> masternode/sync", "instantsend/instantsend -> instantsend/signing -> llmq/signing_shares -> net_processing -> instantsend/instantsend", "instantsend/instantsend -> instantsend/signing -> llmq/signing_shares -> net_processing -> llmq/context -> instantsend/instantsend", "instantsend/instantsend -> instantsend/signing -> llmq/signing_shares -> net_processing -> masternode/active/context -> instantsend/instantsend", From be62366e6a25c7991b41b5e60541fc8fc254cbae Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sat, 13 Dec 2025 04:31:34 +0700 Subject: [PATCH 19/20] refactor: pass connman in constructor of NetHandler's child which need it --- src/governance/net_governance.cpp | 14 +++++------ src/governance/net_governance.h | 11 +++++---- src/init.cpp | 4 ++-- src/instantsend/net_instantsend.cpp | 2 +- src/instantsend/net_instantsend.h | 2 +- src/llmq/net_signing.cpp | 2 +- src/llmq/net_signing.h | 2 +- src/net_processing.cpp | 4 ++-- src/net_processing.h | 4 ++-- src/node/sync_manager.cpp | 36 ++++++++++++++--------------- src/node/sync_manager.h | 14 ++++++----- 11 files changed, 50 insertions(+), 45 deletions(-) diff --git a/src/governance/net_governance.cpp b/src/governance/net_governance.cpp index 9505075c8d9c..63ca68f85671 100644 --- a/src/governance/net_governance.cpp +++ b/src/governance/net_governance.cpp @@ -14,17 +14,17 @@ class CConnman; -void NetGovernance::Schedule(CScheduler& scheduler, CConnman& connman) +void NetGovernance::Schedule(CScheduler& scheduler) { // Code below is meant to be running only if governance validation is enabled // if (!m_gov_manager.IsValid()) return; scheduler.scheduleEvery( - [this, &connman]() -> void { + [this]() -> void { if (!m_node_sync.IsSynced()) return; // CHECK OBJECTS WE'VE ASKED FOR, REMOVE OLD ENTRIES - m_gov_manager.RequestOrphanObjects(connman); + m_gov_manager.RequestOrphanObjects(m_connman); // CHECK AND REMOVE - REPROCESS GOVERNANCE OBJECTS m_gov_manager.CheckAndRemove(); @@ -42,7 +42,7 @@ void NetGovernance::Schedule(CScheduler& scheduler, CConnman& connman) Params().IsMockableChain() ? std::chrono::seconds{1} : std::chrono::seconds{5}); } -void NetGovernance::ProcessMessage(CNode& peer, CConnman& connman, const std::string& msg_type, CDataStream& vRecv) +void NetGovernance::ProcessMessage(CNode& peer, const std::string& msg_type, CDataStream& vRecv) { if (!m_gov_manager.IsValid()) return; if (!m_node_sync.IsBlockchainSynced()) return; @@ -61,9 +61,9 @@ void NetGovernance::ProcessMessage(CNode& peer, CConnman& connman, const std::st LogPrint(BCLog::GOBJECT, "MNGOVERNANCESYNC -- syncing governance objects to our peer %s\n", peer.GetLogString()); if (nProp == uint256()) { - m_peer_manager->PeerPostProcessMessage(m_gov_manager.SyncObjects(peer, connman)); + m_peer_manager->PeerPostProcessMessage(m_gov_manager.SyncObjects(peer, m_connman)); } else { - m_peer_manager->PeerPostProcessMessage(m_gov_manager.SyncSingleObjVotes(peer, nProp, filter, connman)); + m_peer_manager->PeerPostProcessMessage(m_gov_manager.SyncSingleObjVotes(peer, nProp, filter, m_connman)); } } // A NEW GOVERNANCE OBJECT HAS ARRIVED @@ -124,7 +124,7 @@ void NetGovernance::ProcessMessage(CNode& peer, CConnman& connman, const std::st } CGovernanceException exception; - if (m_gov_manager.ProcessVote(&peer, vote, exception, connman)) { + if (m_gov_manager.ProcessVote(&peer, vote, exception, m_connman)) { LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECTVOTE -- %s new\n", strHash); m_node_sync.BumpAssetLastTime("MNGOVERNANCEOBJECTVOTE"); diff --git a/src/governance/net_governance.h b/src/governance/net_governance.h index a8f5e300c341..bfa14bb05939 100644 --- a/src/governance/net_governance.h +++ b/src/governance/net_governance.h @@ -13,19 +13,22 @@ class CMasternodeSync; class NetGovernance final : public NetHandler { public: - NetGovernance(PeerManagerInternal* peer_manager, CGovernanceManager& gov_manager, CMasternodeSync& node_sync) : + NetGovernance(PeerManagerInternal* peer_manager, CGovernanceManager& gov_manager, CMasternodeSync& node_sync, + CConnman& connman) : NetHandler(peer_manager), m_gov_manager(gov_manager), - m_node_sync(node_sync) + m_node_sync(node_sync), + m_connman(connman) { } - void Schedule(CScheduler& scheduler, CConnman& connman) override; + void Schedule(CScheduler& scheduler) override; - void ProcessMessage(CNode& peer, CConnman& connman, const std::string& msg_type, CDataStream& vRecv) override; + void ProcessMessage(CNode& peer, const std::string& msg_type, CDataStream& vRecv) override; private: CGovernanceManager& m_gov_manager; CMasternodeSync& m_node_sync; + CConnman& m_connman; }; #endif // BITCOIN_GOVERNANCE_NET_GOVERNANCE_H diff --git a/src/init.cpp b/src/init.cpp index 5eea06368d9d..23c61b3319b6 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2235,9 +2235,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) } return InitError(strprintf(_("Failed to clear governance cache at %s"), file_path)); } - node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.govman, *node.mn_sync)); + node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.govman, *node.mn_sync, *node.connman)); } - node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.govman, *node.mn_sync, *node.netfulfilledman)); + node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.govman, *node.mn_sync, *node.connman, *node.netfulfilledman)); // ********************************************************* Step 8: start indexers if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) { diff --git a/src/instantsend/net_instantsend.cpp b/src/instantsend/net_instantsend.cpp index e4cd953fdc1d..efd217c10d51 100644 --- a/src/instantsend/net_instantsend.cpp +++ b/src/instantsend/net_instantsend.cpp @@ -14,7 +14,7 @@ #include #include -void NetInstantSend::ProcessMessage(CNode& pfrom, CConnman&, const std::string& msg_type, CDataStream& vRecv) +void NetInstantSend::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) { if (msg_type != NetMsgType::ISDLOCK) { return; diff --git a/src/instantsend/net_instantsend.h b/src/instantsend/net_instantsend.h index dab44bd4a461..d21ffb734ef1 100644 --- a/src/instantsend/net_instantsend.h +++ b/src/instantsend/net_instantsend.h @@ -31,7 +31,7 @@ class NetInstantSend final : public NetHandler { workInterrupt.reset(); } - void ProcessMessage(CNode& pfrom, CConnman&, const std::string& msg_type, CDataStream& vRecv) override; + void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) override; void Start() override; void Stop() override; diff --git a/src/llmq/net_signing.cpp b/src/llmq/net_signing.cpp index 077d0bf4f9ba..575dc8ca3a8a 100644 --- a/src/llmq/net_signing.cpp +++ b/src/llmq/net_signing.cpp @@ -16,7 +16,7 @@ #include -void NetSigning::ProcessMessage(CNode& pfrom, CConnman&, const std::string& msg_type, CDataStream& vRecv) +void NetSigning::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) { if (msg_type != NetMsgType::QSIGREC) return; diff --git a/src/llmq/net_signing.h b/src/llmq/net_signing.h index 0b9d8161fb5a..d77b4b916eed 100644 --- a/src/llmq/net_signing.h +++ b/src/llmq/net_signing.h @@ -25,7 +25,7 @@ class NetSigning final : public NetHandler { workInterrupt.reset(); } - void ProcessMessage(CNode& pfrom, CConnman&, const std::string& msg_type, CDataStream& vRecv) override; + void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) override; [[nodiscard]] bool ProcessPendingRecoveredSigs(); void ProcessRecoveredSig(std::shared_ptr recoveredSig, bool consider_proactive_relay); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 4e110c7e3ab5..d69a44eeb4cb 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1696,7 +1696,7 @@ void PeerManagerImpl::InterruptHandlers() void PeerManagerImpl::ScheduleHandlers(CScheduler& scheduler) { for (auto& handler : m_handlers) { - handler->Schedule(scheduler, m_connman); + handler->Schedule(scheduler); } } @@ -5464,7 +5464,7 @@ void PeerManagerImpl::ProcessMessage( } for (const auto& handler : m_handlers) { - handler->ProcessMessage(pfrom, m_connman, msg_type, vRecv); + handler->ProcessMessage(pfrom, msg_type, vRecv); } return; } diff --git a/src/net_processing.h b/src/net_processing.h index dce3bafd2f7e..a449ee2a625b 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -80,8 +80,8 @@ class NetHandler virtual void Start() {} virtual void Stop() {} virtual void Interrupt() {} - virtual void Schedule(CScheduler& scheduler, CConnman& connman) {} - virtual void ProcessMessage(CNode& pfrom, CConnman& connman, const std::string& msg_type, CDataStream& vRecv) {} + virtual void Schedule(CScheduler& scheduler) {} + virtual void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) {} protected: PeerManagerInternal* m_peer_manager; }; diff --git a/src/node/sync_manager.cpp b/src/node/sync_manager.cpp index 3e0f3cbbffca..fb368f03e5df 100644 --- a/src/node/sync_manager.cpp +++ b/src/node/sync_manager.cpp @@ -19,24 +19,24 @@ class CConnman; -void SyncManager::Schedule(CScheduler& scheduler, CConnman& connman) +void SyncManager::Schedule(CScheduler& scheduler) { scheduler.scheduleEvery( - [this, &connman]() -> void { + [this]() -> void { if (ShutdownRequested()) return; - ProcessTick(connman); + ProcessTick(); }, std::chrono::seconds{1}); } -void SyncManager::SendGovernanceSyncRequest(CNode* pnode, CConnman& connman) const +void SyncManager::SendGovernanceSyncRequest(CNode* pnode) const { CNetMsgMaker msgMaker(pnode->GetCommonVersion()); CBloomFilter filter; - connman.PushMessage(pnode, msgMaker.Make(NetMsgType::MNGOVERNANCESYNC, uint256(), filter)); + m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::MNGOVERNANCESYNC, uint256(), filter)); } -int SyncManager::RequestGovernanceObjectVotes(const std::vector& vNodesCopy, CConnman& connman) const +int SyncManager::RequestGovernanceObjectVotes(const std::vector& vNodesCopy) const { // Maximum number of nodes to request votes from for the same object hash on real networks // (mainnet, testnet, devnets). Keep this low to avoid unnecessary bandwidth usage. @@ -97,7 +97,7 @@ int SyncManager::RequestGovernanceObjectVotes(const std::vector& vNodesC // Don't try to sync any data from outbound non-relay "masternode" connections. // Inbound connection this early is most likely a "masternode" connection // initiated from another node, so skip it too. - if (!pnode->CanRelay() || (connman.IsActiveMasternode() && pnode->IsInboundConn())) continue; + if (!pnode->CanRelay() || (m_connman.IsActiveMasternode() && pnode->IsInboundConn())) continue; // stop early to prevent setAskFor overflow { LOCK(::cs_main); @@ -107,7 +107,7 @@ int SyncManager::RequestGovernanceObjectVotes(const std::vector& vNodesC // to early to ask the same node if (mapAskedRecently[nHashGovobj].count(pnode->addr)) continue; - m_gov_manager.RequestGovernanceObject(pnode, nHashGovobj, connman, true); + m_gov_manager.RequestGovernanceObject(pnode, nHashGovobj, m_connman, true); mapAskedRecently[nHashGovobj][pnode->addr] = nNow + nTimeout; fAsked = true; // stop loop if max number of peers per obj was asked @@ -127,7 +127,7 @@ int SyncManager::RequestGovernanceObjectVotes(const std::vector& vNodesC return int(vTriggerObjHashes.size() + vOtherObjHashes.size()); } -void SyncManager::ProcessTick(CConnman& connman) +void SyncManager::ProcessTick() { assert(m_netfulfilledman.IsValid()); @@ -139,7 +139,7 @@ void SyncManager::ProcessTick(CConnman& connman) // reset the sync process if the last call to this function was more than 60 minutes ago (client was in sleep mode) static int64_t nTimeLastProcess = GetTime(); - if (!Params().IsMockableChain() && GetTime() - nTimeLastProcess > 60 * 60 && !connman.IsActiveMasternode()) { + if (!Params().IsMockableChain() && GetTime() - nTimeLastProcess > 60 * 60 && !m_connman.IsActiveMasternode()) { LogPrintf("Sync Tick -- WARNING: no actions for too long, restarting sync...\n"); m_node_sync.Reset(true); nTimeLastProcess = GetTime(); @@ -152,11 +152,11 @@ void SyncManager::ProcessTick(CConnman& connman) } nTimeLastProcess = GetTime(); - const CConnman::NodesSnapshot snap{connman, /* cond = */ CConnman::FullyConnectedOnly}; + const CConnman::NodesSnapshot snap{m_connman, /* cond = */ CConnman::FullyConnectedOnly}; // gradually request the rest of the votes after sync finished if (m_node_sync.IsSynced()) { - RequestGovernanceObjectVotes(snap.Nodes(), connman); + RequestGovernanceObjectVotes(snap.Nodes()); return; } @@ -175,7 +175,7 @@ void SyncManager::ProcessTick(CConnman& connman) // Don't try to sync any data from outbound non-relay "masternode" connections. // Inbound connection this early is most likely a "masternode" connection // initiated from another node, so skip it too. - if (!pnode->CanRelay() || (connman.IsActiveMasternode() && pnode->IsInboundConn())) continue; + if (!pnode->CanRelay() || (m_connman.IsActiveMasternode() && pnode->IsInboundConn())) continue; { if ((pnode->HasPermission(NetPermissionFlags::NoBan) || pnode->IsManualConn()) && @@ -199,7 +199,7 @@ void SyncManager::ProcessTick(CConnman& connman) // always get sporks first, only request once from each peer m_netfulfilledman.AddFulfilledRequest(pnode->addr, "spork-sync"); // get current network sporks - connman.PushMessage(pnode, msgMaker.Make(NetMsgType::GETSPORKS)); + m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::GETSPORKS)); LogPrint(BCLog::MNSYNC, "Sync Tick -- nTick %d asset_id %d -- requesting sporks from peer=%d\n", nTick, asset_id, pnode->GetId()); } @@ -227,7 +227,7 @@ void SyncManager::ProcessTick(CConnman& connman) "mempool-sync"); if (!pNodeTmp->IsInboundConn() && !fRequestedEarlier && !pNodeTmp->IsBlockRelayOnly()) { m_netfulfilledman.AddFulfilledRequest(pNodeTmp->addr, "mempool-sync"); - connman.PushMessage(pNodeTmp, msgMaker.Make(NetMsgType::MEMPOOL)); + m_connman.PushMessage(pNodeTmp, msgMaker.Make(NetMsgType::MEMPOOL)); LogPrint(BCLog::MNSYNC, /* Continued */ "Sync Tick -- nTick %d asset_id %d -- syncing mempool from peer=%d\n", nTick, asset_id, pNodeTmp->GetId()); @@ -268,7 +268,7 @@ void SyncManager::ProcessTick(CConnman& connman) m_node_sync.BumpAttempt(); - SendGovernanceSyncRequest(pnode, connman); + SendGovernanceSyncRequest(pnode); break; // this will cause each peer to get one request each six seconds for the various assets we need } @@ -287,7 +287,7 @@ void SyncManager::ProcessTick(CConnman& connman) continue; // to early for this node } const std::vector vNodeCopy{pnode}; - int nObjsLeftToAsk = RequestGovernanceObjectVotes(vNodeCopy, connman); + int nObjsLeftToAsk = RequestGovernanceObjectVotes(vNodeCopy); // check for data if (nObjsLeftToAsk == 0) { static int64_t nTimeNoObjectsLeft = 0; @@ -319,7 +319,7 @@ void SyncManager::ProcessTick(CConnman& connman) } } -void SyncManager::ProcessMessage(CNode& peer, CConnman&, const std::string& msg_type, CDataStream& vRecv) +void SyncManager::ProcessMessage(CNode& peer, const std::string& msg_type, CDataStream& vRecv) { //Sync status count if (msg_type != NetMsgType::SYNCSTATUSCOUNT) return; diff --git a/src/node/sync_manager.h b/src/node/sync_manager.h index 8bb884af8ade..ac6d5fe75ec3 100644 --- a/src/node/sync_manager.h +++ b/src/node/sync_manager.h @@ -15,24 +15,26 @@ class CNetFulfilledRequestManager; class SyncManager final : public NetHandler { public: - SyncManager(PeerManagerInternal* peer_manager, CGovernanceManager& gov_manager, CMasternodeSync& node_sync, + SyncManager(PeerManagerInternal* peer_manager, CGovernanceManager& gov_manager, CMasternodeSync& node_sync, CConnman& connman, CNetFulfilledRequestManager& netfulfilledman) : NetHandler(peer_manager), m_gov_manager(gov_manager), m_node_sync(node_sync), + m_connman(connman), m_netfulfilledman(netfulfilledman) { } - void Schedule(CScheduler& scheduler, CConnman& connman) override; - void ProcessMessage(CNode& peer, CConnman&, const std::string& msg_type, CDataStream& vRecv) override; + void Schedule(CScheduler& scheduler) override; + void ProcessMessage(CNode& peer, const std::string& msg_type, CDataStream& vRecv) override; private: - void SendGovernanceSyncRequest(CNode* pnode, CConnman& connman) const; - int RequestGovernanceObjectVotes(const std::vector& vNodesCopy, CConnman& connman) const; - void ProcessTick(CConnman& connman); + void SendGovernanceSyncRequest(CNode* pnode) const; + int RequestGovernanceObjectVotes(const std::vector& vNodesCopy) const; + void ProcessTick(); CGovernanceManager& m_gov_manager; CMasternodeSync& m_node_sync; + CConnman& m_connman; CNetFulfilledRequestManager& m_netfulfilledman; }; From a67300c6edfa3093bc02695ba315d9cf235d6a3d Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sat, 13 Dec 2025 04:54:29 +0700 Subject: [PATCH 20/20] fix: multiple CI failures 1. add a destructor ~NodeSyncNotifier 2. add a missing logging.h include to masternode/sync --- src/masternode/sync.cpp | 1 + src/masternode/sync.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/masternode/sync.cpp b/src/masternode/sync.cpp index 93ba75c8dcfa..14fc2d50ad4b 100644 --- a/src/masternode/sync.cpp +++ b/src/masternode/sync.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include diff --git a/src/masternode/sync.h b/src/masternode/sync.h index b58110fc93db..5bae8cdf9774 100644 --- a/src/masternode/sync.h +++ b/src/masternode/sync.h @@ -28,6 +28,8 @@ class NodeSyncNotifier public: virtual void SyncReset() = 0; virtual void SyncFinished() = 0; + + virtual ~NodeSyncNotifier() = default; }; //