LCOV - code coverage report
Current view: top level - src/governance - governance.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 522 725 72.0 %
Date: 2026-06-25 07:23:43 Functions: 57 63 90.5 %

          Line data    Source code
       1             : // Copyright (c) 2014-2025 The Dash Core developers
       2             : // Distributed under the MIT/X11 software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <governance/governance.h>
       6             : 
       7             : #include <evo/deterministicmns.h>
       8             : #include <flat-database.h>
       9             : #include <governance/common.h>
      10             : #include <governance/object.h>
      11             : #include <governance/superblock.h>
      12             : #include <masternode/meta.h>
      13             : #include <masternode/sync.h>
      14             : 
      15             : #include <chain.h>
      16             : #include <chainparams.h>
      17             : #include <common/bloom.h>
      18             : #include <deploymentstatus.h>
      19             : #include <net.h>
      20             : #include <node/interface_ui.h>
      21             : #include <protocol.h>
      22             : #include <timedata.h>
      23             : #include <util/check.h>
      24             : #include <util/time.h>
      25             : #include <validationinterface.h>
      26             : 
      27             : #include <ranges>
      28             : 
      29        3308 : const std::string GovernanceStore::SERIALIZATION_VERSION_STRING = "CGovernanceManager-Version-16";
      30             : 
      31             : namespace {
      32             : constexpr std::chrono::seconds GOVERNANCE_DELETION_DELAY{10min};
      33             : constexpr std::chrono::seconds GOVERNANCE_ORPHAN_EXPIRATION_TIME{10min};
      34             : constexpr std::chrono::seconds MAX_TIME_FUTURE_DEVIATION{1h};
      35             : constexpr std::chrono::seconds RELIABLE_PROPAGATION_TIME{1min};
      36             : 
      37             : class ScopedLockBool
      38             : {
      39             :     bool& ref;
      40             :     bool fPrevValue;
      41             : 
      42             : public:
      43      198680 :     ScopedLockBool(Mutex& _cs, bool& _ref, bool _value) :
      44       99340 :         ref(_ref)
      45       99340 :     {
      46       99340 :         AssertLockHeld(_cs);
      47       99340 :         fPrevValue = ref;
      48       99340 :         ref = _value;
      49      198680 :     }
      50             : 
      51      198680 :     ~ScopedLockBool() { ref = fPrevValue; }
      52             : };
      53             : } // anonymous namespace
      54             : 
      55       10467 : GovernanceStore::GovernanceStore() :
      56        6751 :     cs_store(),
      57        6751 :     mapObjects(),
      58        6751 :     mapErasedGovernanceObjects(),
      59        6751 :     cmapInvalidVotes(MAX_CACHE_SIZE),
      60        6751 :     cmmapOrphanVotes(MAX_CACHE_SIZE),
      61        6751 :     mapLastMasternodeObject(),
      62        6751 :     lastMNListForVotingKeys(std::make_shared<CDeterministicMNList>())
      63        3716 : {
      64       10467 : }
      65             : 
      66        6070 : CGovernanceManager::CGovernanceManager(CMasternodeMetaMan& mn_metaman, const ChainstateManager& chainman,
      67             :                                        governance::SuperblockManager& superblocks, CDeterministicMNManager& dmnman,
      68             :                                        CMasternodeSync& mn_sync) :
      69        3035 :     m_db{std::make_unique<db_type>("governance.dat", "magicGovernanceCache")},
      70             :     m_mn_metaman{mn_metaman},
      71             :     m_chainman{chainman},
      72             :     m_superblocks{superblocks},
      73             :     m_dmnman{dmnman},
      74             :     m_mn_sync{mn_sync},
      75             :     cmapVoteToObject{MAX_CACHE_SIZE},
      76             :     mapPostponedObjects{}
      77        3035 : {
      78        3035 : }
      79             : 
      80        6070 : CGovernanceManager::~CGovernanceManager()
      81        3035 : {
      82             :     // Reset the paired SuperblockManager so its loaded-flag and trigger map don't
      83             :     // outlive us. chain_helper (its owner) must outlive govman -- see PrepareShutdown
      84             :     // in init.cpp and the test teardown ordering.
      85        3035 :     m_superblocks.Clear();
      86        3035 :     if (!is_loaded) return;
      87        2831 :     m_db->Store(*this);
      88        6070 : }
      89             : 
      90        2831 : bool CGovernanceManager::LoadCache(bool load_cache)
      91             : {
      92        2831 :     AssertLockNotHeld(cs_store);
      93        2831 :     assert(m_db != nullptr);
      94        2831 :     is_loaded = load_cache ? m_db->Load(*this) : m_db->Store(*this);
      95        2831 :     if (is_loaded && load_cache) {
      96        1946 :         CheckAndRemove();
      97        1946 :         InitOnLoad();
      98        1946 :     }
      99        2831 :     m_superblocks.SetLoaded(is_loaded);
     100        2831 :     return is_loaded;
     101             : }
     102             : 
     103         300 : void CGovernanceManager::RelayObject(const CGovernanceObject& obj)
     104             : {
     105         300 :     AssertLockNotHeld(cs_relay);
     106         300 :     if (!m_mn_sync.IsSynced()) {
     107           8 :         LogPrint(BCLog::GOBJECT, "%s -- won't relay until fully synced\n", __func__);
     108           8 :         return;
     109             :     }
     110             : 
     111         292 :     LOCK(cs_relay);
     112         292 :     m_relay_invs.emplace_back(MSG_GOVERNANCE_OBJECT, obj.GetHash());
     113         300 : }
     114             : 
     115        1469 : void CGovernanceManager::RelayVote(const CGovernanceVote& vote)
     116             : {
     117        1469 :     AssertLockNotHeld(cs_relay);
     118        1469 :     if (!m_mn_sync.IsSynced()) {
     119           0 :         LogPrint(BCLog::GOBJECT, "%s -- won't relay until fully synced\n", __func__);
     120           0 :         return;
     121             :     }
     122             : 
     123        1469 :     const auto tip_mn_list = m_dmnman.GetListAtChainTip();
     124        1469 :     auto dmn = tip_mn_list.GetMNByCollateral(vote.GetMasternodeOutpoint());
     125        1469 :     if (!dmn) {
     126           0 :         return;
     127             :     }
     128             : 
     129        1469 :     LOCK(cs_relay);
     130        1469 :     m_relay_invs.emplace_back(MSG_GOVERNANCE_OBJECT_VOTE, vote.GetHash());
     131        1469 : }
     132             : 
     133             : // Accessors for thread-safe access to maps
     134         248 : bool CGovernanceManager::HaveObjectForHash(const uint256& nHash) const
     135             : {
     136         248 :     LOCK(cs_store);
     137         248 :     return (mapObjects.count(nHash) == 1 || mapPostponedObjects.count(nHash) == 1);
     138         248 : }
     139             : 
     140         248 : bool CGovernanceManager::SerializeObjectForHash(const uint256& nHash, CDataStream& ss) const
     141             : {
     142         248 :     LOCK(cs_store);
     143         248 :     auto it = mapObjects.find(nHash);
     144         248 :     if (it == mapObjects.end()) {
     145           0 :         it = mapPostponedObjects.find(nHash);
     146           0 :         if (it == mapPostponedObjects.end())
     147           0 :             return false;
     148           0 :     }
     149         248 :     ss << it->second;
     150         248 :     return true;
     151         248 : }
     152             : 
     153        1224 : bool CGovernanceManager::HaveVoteForHash(const uint256& nHash) const
     154             : {
     155        1224 :     LOCK(cs_store);
     156             : 
     157        1224 :     std::shared_ptr<CGovernanceObject> pGovobj{nullptr};
     158        2448 :     return cmapVoteToObject.Get(nHash, pGovobj) && WITH_LOCK(pGovobj->cs, return pGovobj->GetVoteFile().HasVote(nHash));
     159        1224 : }
     160             : 
     161         136 : int CGovernanceManager::GetVoteCount() const
     162             : {
     163         136 :     LOCK(cs_store);
     164         136 :     return (int)cmapVoteToObject.GetSize();
     165         136 : }
     166             : 
     167        1224 : bool CGovernanceManager::SerializeVoteForHash(const uint256& nHash, CDataStream& ss) const
     168             : {
     169        1224 :     LOCK(cs_store);
     170             : 
     171        1224 :     std::shared_ptr<CGovernanceObject> pGovobj{nullptr};
     172        2448 :     return cmapVoteToObject.Get(nHash, pGovobj) && WITH_LOCK(pGovobj->cs, return pGovobj->GetVoteFile().SerializeVoteToStream(nHash, ss));
     173        1224 : }
     174             : 
     175           0 : void CGovernanceManager::AddPostponedObject(const CGovernanceObject& govobj)
     176             : {
     177           0 :     LOCK(cs_store);
     178           0 :     AddPostponedObjectInternal(govobj);
     179           0 : }
     180             : 
     181           0 : void CGovernanceManager::AddPostponedObjectInternal(const CGovernanceObject& govobj)
     182             : {
     183           0 :     AssertLockHeld(cs_store);
     184           0 :     mapPostponedObjects.emplace(govobj.GetHash(), std::make_shared<CGovernanceObject>(govobj));
     185           0 : }
     186             : 
     187         248 : bool CGovernanceManager::ProcessObject(const std::string& peer_str, const uint256& nHash, CGovernanceObject& govobj)
     188             : {
     189         248 :     std::string strHash = nHash.ToString();
     190             : 
     191         248 :     LOCK(cs_store);
     192             : 
     193         248 :     if (mapObjects.count(nHash) || mapPostponedObjects.count(nHash) || mapErasedGovernanceObjects.count(nHash)) {
     194             :         // TODO - print error code? what if it's GOVOBJ_ERROR_IMMATURE?
     195           0 :         LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- Received already seen object: %s\n", strHash);
     196           0 :         return true;
     197             :     }
     198             : 
     199         248 :     bool fRateCheckBypassed = false;
     200         248 :     if (!MasternodeRateCheck(govobj, true, false, fRateCheckBypassed)) {
     201           0 :         LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- masternode rate check failed - %s - (current block height %d) \n",
     202             :                  strHash, nCachedBlockHeight);
     203           0 :         return true;
     204             :     }
     205             : 
     206         248 :     std::string strError;
     207             :     // CHECK OBJECT AGAINST LOCAL BLOCKCHAIN
     208             : 
     209         248 :     const auto tip_mn_list = m_dmnman.GetListAtChainTip();
     210         248 :     bool fMissingConfirmations = false;
     211         248 :     bool fIsValid = govobj.IsValidLocally(tip_mn_list, m_chainman, strError, fMissingConfirmations, true);
     212             : 
     213             :     bool unused_rcb;
     214         248 :     if (fRateCheckBypassed && fIsValid && !MasternodeRateCheck(govobj, true, true, unused_rcb)) {
     215           0 :         LogPrint(BCLog::GOBJECT, /* Continued */
     216             :                  "MNGOVERNANCEOBJECT -- masternode rate check failed (after signature verification) - %s - (current "
     217             :                  "block height %d)\n",
     218             :                  strHash, nCachedBlockHeight);
     219           0 :         return true;
     220             :     }
     221             : 
     222         248 :     if (!fIsValid) {
     223           0 :         if (fMissingConfirmations) {
     224           0 :             AddPostponedObjectInternal(govobj);
     225           0 :             LogPrintf("MNGOVERNANCEOBJECT -- Not enough fee confirmations for: %s, strError = %s\n", strHash, strError);
     226           0 :             return true;
     227             :         } else {
     228           0 :             LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- Governance object is invalid - %s\n", strError);
     229           0 :             return false;
     230             :         }
     231             :     }
     232             : 
     233         248 :     AddGovernanceObjectInternal(govobj, peer_str);
     234         248 :     return true;
     235         248 : }
     236             : 
     237         300 : void CGovernanceManager::CheckOrphanVotes(CGovernanceObject& govobj)
     238             : {
     239         300 :     AssertLockHeld(cs_store);
     240         300 :     AssertLockNotHeld(cs_relay);
     241             : 
     242         300 :     uint256 nHash = govobj.GetHash();
     243         300 :     std::vector<vote_time_pair_t> vecVotePairs;
     244         300 :     cmmapOrphanVotes.GetAll(nHash, vecVotePairs);
     245             : 
     246         300 :     ScopedLockBool guard(cs_store, fRateChecksEnabled, false);
     247             : 
     248         300 :     int64_t nNow = GetAdjustedTime();
     249         300 :     const auto tip_mn_list = m_dmnman.GetListAtChainTip();
     250         300 :     for (const auto& pairVote : vecVotePairs) {
     251           0 :         const auto& [vote, time] = pairVote;
     252           0 :         bool fRemove = false;
     253           0 :         CGovernanceException e;
     254           0 :         if (time < nNow) {
     255           0 :             fRemove = true;
     256           0 :         } else if (govobj.ProcessVote(m_mn_metaman, fRateChecksEnabled, tip_mn_list, vote, e)) {
     257           0 :             RelayVote(vote);
     258           0 :             fRemove = true;
     259           0 :         }
     260           0 :         if (fRemove) {
     261           0 :             cmmapOrphanVotes.Erase(nHash, pairVote);
     262           0 :         }
     263           0 :     }
     264         300 : }
     265             : 
     266         300 : void CGovernanceManager::AddGovernanceObjectInternal(CGovernanceObject& insert_obj, const std::string& peer_str)
     267             : {
     268         300 :     AssertLockHeld(::cs_main);
     269         300 :     AssertLockHeld(cs_store);
     270         300 :     AssertLockNotHeld(cs_relay);
     271             : 
     272         300 :     uint256 nHash = insert_obj.GetHash();
     273         300 :     std::string strHash = nHash.ToString();
     274             : 
     275         300 :     const auto tip_mn_list = m_dmnman.GetListAtChainTip();
     276             : 
     277             :     // UPDATE CACHED VARIABLES FOR THIS OBJECT AND ADD IT TO OUR MANAGED DATA
     278             : 
     279         300 :     insert_obj.UpdateSentinelVariables(tip_mn_list); //this sets local vars in object
     280             : 
     281         300 :     std::string strError;
     282             : 
     283             :     // MAKE SURE THIS OBJECT IS OK
     284             : 
     285         300 :     if (!insert_obj.IsValidLocally(tip_mn_list, m_chainman, strError, true)) {
     286           0 :         LogPrint(BCLog::GOBJECT, "CGovernanceManager::AddGovernanceObject -- invalid governance object - %s - (nCachedBlockHeight %d) \n", strError, nCachedBlockHeight);
     287           0 :         return;
     288             :     }
     289             : 
     290         300 :     LogPrint(BCLog::GOBJECT, "CGovernanceManager::AddGovernanceObject -- Adding object: hash = %s, type = %d\n", nHash.ToString(),
     291             :              std23::to_underlying(insert_obj.GetObjectType()));
     292             : 
     293             :     // INSERT INTO OUR GOVERNANCE OBJECT MEMORY
     294             :     // IF WE HAVE THIS OBJECT ALREADY, WE DON'T WANT ANOTHER COPY
     295         300 :     auto [emplace_ret, emplace_status] = mapObjects.emplace(nHash, std::make_shared<CGovernanceObject>(insert_obj));
     296             : 
     297         300 :     if (!emplace_status) {
     298           0 :         LogPrint(BCLog::GOBJECT, "CGovernanceManager::AddGovernanceObject -- already have governance object %s\n", nHash.ToString());
     299           0 :         return;
     300             :     }
     301             : 
     302             :     // SHOULD WE ADD THIS OBJECT TO ANY OTHER MANAGERS?
     303             : 
     304        2288 :     auto& [_, govobj] = *emplace_ret;
     305         300 :     LogPrint(BCLog::GOBJECT, "CGovernanceManager::AddGovernanceObject -- Before trigger block, GetDataAsPlainString = %s, nObjectType = %d\n",
     306             :                 Assert(govobj)->GetDataAsPlainString(), std23::to_underlying(govobj->GetObjectType()));
     307             : 
     308         300 :     if (govobj->GetObjectType() == GovernanceObject::TRIGGER && !m_superblocks.AddTrigger(govobj, nCachedBlockHeight)) {
     309           0 :         LogPrint(BCLog::GOBJECT, "CGovernanceManager::AddGovernanceObject -- undo adding invalid trigger object: hash = %s\n", nHash.ToString());
     310           0 :         govobj->PrepareDeletion(GetTime<std::chrono::seconds>().count());
     311           0 :         return;
     312             :     }
     313             : 
     314         300 :     LogPrint(BCLog::GOBJECT, "CGovernanceManager::AddGovernanceObject -- %s new, received from peer %s\n", strHash, peer_str);
     315         300 :     RelayObject(*govobj);
     316             : 
     317             :     // Update the rate buffer
     318         300 :     MasternodeRateUpdate(*govobj);
     319             : 
     320         300 :     m_mn_sync.BumpAssetLastTime("CGovernanceManager::AddGovernanceObject");
     321             : 
     322             :     // WE MIGHT HAVE PENDING/ORPHAN VOTES FOR THIS OBJECT
     323             : 
     324         300 :     CheckOrphanVotes(*govobj);
     325             : 
     326             :     // SEND NOTIFICATION TO SCRIPT/ZMQ
     327         300 :     GetMainSignals().NotifyGovernanceObject(std::make_shared<const Governance::Object>(govobj->Object()), nHash.ToString());
     328         300 :     uiInterface.NotifyGovernanceChanged();
     329         300 : }
     330             : 
     331          52 : void CGovernanceManager::AddGovernanceObject(CGovernanceObject& govobj, const std::string& peer_str)
     332             : {
     333          52 :     LOCK2(::cs_main, cs_store);
     334          52 :     AddGovernanceObjectInternal(govobj, peer_str);
     335          52 : }
     336             : 
     337      125597 : void CGovernanceManager::CheckAndRemove()
     338             : {
     339      125597 :     AssertLockNotHeld(cs_store);
     340      125597 :     assert(m_mn_metaman.IsValid());
     341             : 
     342             :     // Return on initial sync, spammed the debug.log and provided no use
     343      125597 :     if (!m_mn_sync.IsBlockchainSynced()) return;
     344             : 
     345      119748 :     LogPrint(BCLog::GOBJECT, "CGovernanceManager::UpdateCachesAndClean\n");
     346             : 
     347      119748 :     std::vector<uint256> vecDirtyHashes = m_mn_metaman.GetAndClearDirtyGovernanceObjectHashes();
     348             : 
     349      119748 :     const auto tip_mn_list = m_dmnman.GetListAtChainTip();
     350             : 
     351             :     {
     352      119748 :     LOCK2(::cs_main, cs_store);
     353             : 
     354      119748 :     for (const uint256& nHash : vecDirtyHashes) {
     355       20708 :         auto it = mapObjects.find(nHash);
     356           0 :         if (it == mapObjects.end()) {
     357           0 :             continue;
     358             :         }
     359           0 :         Assert(it->second)->ClearMasternodeVotes(tip_mn_list);
     360             :     }
     361             : 
     362       99040 :     ScopedLockBool guard(cs_store, fRateChecksEnabled, false);
     363             : 
     364             :     // Clean up any expired or invalid triggers
     365      119748 :     m_superblocks.Clean(nCachedBlockHeight);
     366             : 
     367       99040 :     const auto nNow = GetTime<std::chrono::seconds>();
     368      111966 :     for (auto it = mapObjects.begin(); it != mapObjects.end();) {
     369       84822 :         auto [nHash, pObj] = *it;
     370       33634 :         std::string strHash = nHash.ToString();
     371             : 
     372             :         // IF CACHE IS NOT DIRTY, WHY DO THIS?
     373       33634 :         if (Assert(pObj)->IsSetDirtyCache()) {
     374             :             // UPDATE LOCAL VALIDITY AGAINST CRYPTO DATA
     375         592 :             pObj->UpdateLocalValidity(tip_mn_list, m_chainman);
     376             : 
     377             :             // UPDATE SENTINEL SIGNALING VARIABLES
     378         592 :             pObj->UpdateSentinelVariables(tip_mn_list);
     379         592 :         }
     380             : 
     381             :         // IF DELETE=TRUE, THEN CLEAN THE MESS UP!
     382             : 
     383       12926 :         const auto nTimeSinceDeletion = nNow - std::chrono::seconds{pObj->GetDeletionTime()};
     384             : 
     385       12926 :         LogPrint(BCLog::GOBJECT, "CGovernanceManager::UpdateCachesAndClean -- Checking object for deletion: %s, deletion time = %d, time since deletion = %d, delete flag = %d, expired flag = %d\n",
     386             :             strHash, pObj->GetDeletionTime(), nTimeSinceDeletion.count(), pObj->IsSetCachedDelete(), pObj->IsSetExpired());
     387             : 
     388       15498 :         if ((pObj->IsSetCachedDelete() || pObj->IsSetExpired()) &&
     389       12926 :             (nTimeSinceDeletion >= GOVERNANCE_DELETION_DELAY)) {
     390          60 :             LogPrint(BCLog::GOBJECT, "CGovernanceManager::UpdateCachesAndClean -- erase obj %s\n", nHash.ToString());
     391         120 :             m_mn_metaman.RemoveGovernanceObject(pObj->GetHash());
     392             : 
     393             :             // Remove vote references
     394          60 :             const object_ref_cm_t::list_t& listItems = cmapVoteToObject.GetItemList();
     395         600 :             for (auto lit = listItems.begin(); lit != listItems.end();) {
     396         540 :                 if (lit->value == pObj) {
     397         280 :                     uint256 nKey = lit->key;
     398         280 :                     ++lit;
     399         280 :                     cmapVoteToObject.Erase(nKey);
     400         280 :                 } else {
     401         260 :                     ++lit;
     402             :                 }
     403             :             }
     404             : 
     405          60 :             int64_t nTimeExpired{0};
     406             : 
     407          60 :             if (pObj->GetObjectType() == GovernanceObject::PROPOSAL) {
     408             :                 // keep hashes of deleted proposals forever
     409          40 :                 nTimeExpired = std::numeric_limits<int64_t>::max();
     410          40 :             } else {
     411          20 :                 int64_t nSuperblockCycleSeconds = Params().GetConsensus().nSuperblockCycle * Params().GetConsensus().nPowTargetSpacing;
     412          60 :                 nTimeExpired = (std::chrono::seconds{pObj->GetCreationTime()} +
     413          40 :                                 std::chrono::seconds{2 * nSuperblockCycleSeconds} + GOVERNANCE_DELETION_DELAY)
     414          20 :                                    .count();
     415             :             }
     416             : 
     417         120 :             mapErasedGovernanceObjects.insert(std::make_pair(nHash, nTimeExpired));
     418          60 :             if (pObj->GetObjectType() == GovernanceObject::TRIGGER) {
     419          40 :                 m_superblocks.RemoveTrigger(nHash);
     420          20 :             }
     421          60 :             mapObjects.erase(it++);
     422          60 :         } else {
     423        2512 :             if (pObj->GetObjectType() == GovernanceObject::PROPOSAL) {
     424        5616 :                 std::string strValidationError;
     425        5616 :                 if (!governance::ValidateProposal(pObj->GetDataAsHexString(), strValidationError)) {
     426         238 :                     LogPrint(BCLog::GOBJECT, "CGovernanceManager::UpdateCachesAndClean -- set for deletion expired obj %s\n", strHash);
     427         238 :                     pObj->PrepareDeletion(nNow.count());
     428         238 :                 }
     429        5616 :             }
     430       12866 :             ++it;
     431             :         }
     432       12926 :     }
     433             : 
     434             :     // forget about expired deleted objects
     435      100094 :     for (auto s_it = mapErasedGovernanceObjects.begin(); s_it != mapErasedGovernanceObjects.end();) {
     436        1054 :         if (s_it->second < nNow.count()) {
     437           0 :             mapErasedGovernanceObjects.erase(s_it++);
     438           0 :         } else {
     439        1054 :             ++s_it;
     440             :         }
     441             :     }
     442             : 
     443             :     // forget about expired requests
     444       99690 :     for (auto r_it = m_requested_hash_time.begin(); r_it != m_requested_hash_time.end();) {
     445         650 :         if (r_it->second < nNow) {
     446           6 :             m_requested_hash_time.erase(r_it++);
     447           6 :         } else {
     448         644 :             ++r_it;
     449             :         }
     450             :     }
     451       99040 :     }
     452             : 
     453       99040 :     LogPrint(BCLog::GOBJECT, "CGovernanceManager::UpdateCachesAndClean -- %s, m_requested_hash_time size=%d\n",
     454             :              ToString(), m_requested_hash_time.size());
     455      311969 : }
     456             : 
     457       95252 : std::vector<CInv> CGovernanceManager::FetchRelayInventory()
     458             : {
     459       95252 :     std::vector<CInv> ret;
     460       95252 :     LOCK(cs_relay);
     461       95252 :     swap(ret, m_relay_invs);
     462       95252 :     return ret;
     463       95252 : }
     464             : 
     465         210 : std::shared_ptr<const CGovernanceObject> CGovernanceManager::FindConstGovernanceObject(const uint256& nHash) const
     466             : {
     467         210 :     LOCK(cs_store);
     468         210 :     return FindConstGovernanceObjectInternal(nHash);
     469         210 : }
     470             : 
     471         834 : std::shared_ptr<const CGovernanceObject> CGovernanceManager::FindConstGovernanceObjectInternal(const uint256& nHash) const
     472             : {
     473         834 :     AssertLockHeld(cs_store);
     474             : 
     475         834 :     auto it = mapObjects.find(nHash);
     476         834 :     if (it != mapObjects.end()) return (it->second);
     477             : 
     478           0 :     return nullptr;
     479         834 : }
     480             : 
     481        3836 : std::shared_ptr<CGovernanceObject> CGovernanceManager::FindGovernanceObject(const uint256& nHash)
     482             : {
     483        3836 :     AssertLockNotHeld(cs_store);
     484        3836 :     LOCK(cs_store);
     485        3836 :     return FindGovernanceObjectInternal(nHash);
     486        3836 : }
     487             : 
     488        3836 : std::shared_ptr<CGovernanceObject> CGovernanceManager::FindGovernanceObjectInternal(const uint256& nHash)
     489             : {
     490        3836 :     AssertLockHeld(cs_store);
     491        3836 :     if (mapObjects.count(nHash)) return mapObjects[nHash];
     492           0 :     return nullptr;
     493        3836 : }
     494             : 
     495         204 : std::shared_ptr<CGovernanceObject> CGovernanceManager::FindGovernanceObjectByDataHash(const uint256 &nDataHash)
     496             : {
     497         204 :     AssertLockNotHeld(cs_store);
     498         204 :     LOCK(cs_store);
     499        1184 :     for (const auto& [nHash, govobj] : mapObjects) {
     500         980 :         if (Assert(govobj)->GetDataHash() == nDataHash) return govobj;
     501             :     }
     502         150 :     return nullptr;
     503         204 : }
     504             : 
     505           0 : std::vector<CGovernanceVote> CGovernanceManager::GetCurrentVotes(const uint256& nParentHash, const COutPoint& mnCollateralOutpointFilter) const
     506             : {
     507           0 :     LOCK(cs_store);
     508           0 :     std::vector<CGovernanceVote> vecResult;
     509             : 
     510             :     // Find the governance object or short-circuit.
     511           0 :     auto it = mapObjects.find(nParentHash);
     512           0 :     if (it == mapObjects.end()) return vecResult;
     513           0 :     const auto& govobj = *Assert(it->second);
     514             : 
     515           0 :     const auto tip_mn_list = m_dmnman.GetListAtChainTip();
     516           0 :     std::map<COutPoint, CDeterministicMNCPtr> mapMasternodes;
     517           0 :     if (mnCollateralOutpointFilter.IsNull()) {
     518           0 :         tip_mn_list.ForEachMNShared(/*onlyValid=*/false,
     519           0 :                                     [&](const auto& dmn) { mapMasternodes.emplace(dmn->collateralOutpoint, dmn); });
     520           0 :     } else {
     521           0 :         auto dmn = tip_mn_list.GetMNByCollateral(mnCollateralOutpointFilter);
     522           0 :         if (dmn) {
     523           0 :             mapMasternodes.emplace(dmn->collateralOutpoint, dmn);
     524           0 :         }
     525           0 :     }
     526             : 
     527             :     // Loop through each MN collateral outpoint and get the votes for the `nParentHash` governance object
     528           0 :     for (const auto& [outpoint, _] : mapMasternodes) {
     529             :         // get a vote_rec_t from the govobj
     530           0 :         vote_rec_t voteRecord;
     531           0 :         if (!govobj.GetCurrentMNVotes(outpoint, voteRecord)) continue;
     532             : 
     533           0 :         for (const auto& [signal, vote_instance] : voteRecord.mapInstances) {
     534           0 :             CGovernanceVote vote = CGovernanceVote(outpoint, nParentHash, (vote_signal_enum_t)signal,
     535           0 :                                                    vote_instance.eOutcome);
     536           0 :             vote.SetTime(vote_instance.nCreationTime);
     537           0 :             vecResult.push_back(vote);
     538           0 :         }
     539           0 :     }
     540             : 
     541           0 :     return vecResult;
     542           0 : }
     543             : 
     544        4498 : void CGovernanceManager::GetAllNewerThan(std::vector<CGovernanceObject>& objs, int64_t nMoreThanTime,
     545             :                                           bool include_postponed) const
     546             : {
     547        4498 :     LOCK(cs_store);
     548             : 
     549       35438 :     for (const auto& [_, govobj] : mapObjects) {
     550       30940 :         if (Assert(govobj)->GetCreationTime() < nMoreThanTime) {
     551           0 :             continue;
     552             :         }
     553       61880 :         objs.push_back(*govobj);
     554             :     }
     555             : 
     556        4498 :     if (include_postponed) {
     557           0 :         for (const auto& [_, govobj] : mapPostponedObjects) {
     558           0 :             if (Assert(govobj)->GetCreationTime() < nMoreThanTime) {
     559           0 :                 continue;
     560             :             }
     561           0 :             objs.push_back(*govobj);
     562             :         }
     563           0 :     }
     564        4498 : }
     565             : 
     566        3478 : bool CGovernanceManager::ConfirmInventoryRequest(const CInv& inv)
     567             : {
     568        3478 :     AssertLockNotHeld(cs_store);
     569             : 
     570             :     // do not request objects until it's time to sync
     571        3478 :     if (!m_mn_sync.IsBlockchainSynced()) return false;
     572             : 
     573        3478 :     LOCK(cs_store);
     574             : 
     575        3478 :     LogPrint(BCLog::GOBJECT, "CGovernanceManager::ConfirmInventoryRequest inv = %s\n", inv.ToString());
     576             : 
     577             :     // First check if we've already recorded this object
     578        3478 :     switch (inv.type) {
     579             :     case MSG_GOVERNANCE_OBJECT: {
     580         605 :         if (mapObjects.count(inv.hash) == 1 || mapPostponedObjects.count(inv.hash) == 1) {
     581          99 :             LogPrint(BCLog::GOBJECT, "CGovernanceManager::ConfirmInventoryRequest already have governance object, returning false\n");
     582          99 :             return false;
     583             :         }
     584         506 :         break;
     585             :     }
     586             :     case MSG_GOVERNANCE_OBJECT_VOTE: {
     587        2873 :         if (cmapVoteToObject.HasKey(inv.hash)) {
     588         413 :             LogPrint(BCLog::GOBJECT, "CGovernanceManager::ConfirmInventoryRequest already have governance vote, returning false\n");
     589         413 :             return false;
     590             :         }
     591        2460 :         break;
     592             :     }
     593             :     default:
     594           0 :         LogPrint(BCLog::GOBJECT, "CGovernanceManager::ConfirmInventoryRequest unknown type, returning false\n");
     595           0 :         return false;
     596             :     }
     597             : 
     598        2966 :     const auto valid_until = GetTime<std::chrono::seconds>() + RELIABLE_PROPAGATION_TIME;
     599        2966 :     const auto& [_itr, inserted] = m_requested_hash_time.emplace(inv.hash, valid_until);
     600             : 
     601        2966 :     if (inserted) {
     602        1480 :         LogPrint(BCLog::GOBJECT, /* Continued */
     603             :                  "CGovernanceManager::ConfirmInventoryRequest added %s inv hash to m_requested_hash_time, size=%d\n",
     604             :                  inv.type == MSG_GOVERNANCE_OBJECT ? "object" : "vote", m_requested_hash_time.size());
     605        1480 :     }
     606             : 
     607        2966 :     LogPrint(BCLog::GOBJECT, "CGovernanceManager::ConfirmInventoryRequest reached end, returning true\n");
     608        2966 :     return true;
     609        3478 : }
     610             : 
     611         606 : std::vector<CInv> CGovernanceManager::GetSyncableVoteInvs(const uint256& nProp, const CBloomFilter& filter) const
     612             : {
     613         606 :     LOCK(cs_store);
     614             : 
     615         606 :     auto it = mapObjects.find(nProp);
     616         606 :     if (it == mapObjects.end()) {
     617          54 :         return {};
     618             :     }
     619             : 
     620         552 :     const auto& govobj = *Assert(it->second);
     621         552 :     if (govobj.IsSetCachedDelete() || govobj.IsSetExpired()) {
     622          30 :         return {};
     623             :     }
     624             : 
     625         522 :     std::vector<CInv> invs;
     626         522 :     const auto tip_mn_list = m_dmnman.GetListAtChainTip();
     627             : 
     628         522 :     LOCK(govobj.cs);
     629         522 :     const auto& fileVotes = govobj.GetVoteFile();
     630        2774 :     for (const auto& vote : fileVotes.GetVotes()) {
     631        2252 :         uint256 nVoteHash = vote.GetHash();
     632             : 
     633        2252 :         bool onlyVotingKeyAllowed = govobj.GetObjectType() == GovernanceObject::PROPOSAL && vote.GetSignal() == VOTE_SIGNAL_FUNDING;
     634             : 
     635        2252 :         if (filter.contains(nVoteHash) || !vote.IsValid(tip_mn_list, onlyVotingKeyAllowed)) {
     636        2220 :             continue;
     637             :         }
     638          32 :         invs.emplace_back(MSG_GOVERNANCE_OBJECT_VOTE, nVoteHash);
     639             :     }
     640             : 
     641         522 :     return invs;
     642        1128 : }
     643             : 
     644          88 : std::vector<CInv> CGovernanceManager::GetSyncableObjectInvs() const
     645             : {
     646          88 :     LOCK(cs_store);
     647             : 
     648          88 :     std::vector<CInv> invs;
     649          88 :     invs.reserve(mapObjects.size());
     650             : 
     651         448 :     for (const auto& [nHash, govobj] : mapObjects) {
     652         180 :         if (Assert(govobj)->IsSetCachedDelete() || govobj->IsSetExpired()) {
     653           0 :             continue;
     654             :         }
     655         360 :         invs.emplace_back(MSG_GOVERNANCE_OBJECT, nHash);
     656             :     }
     657             : 
     658          88 :     return invs;
     659          88 : }
     660             : 
     661         300 : void CGovernanceManager::MasternodeRateUpdate(const CGovernanceObject& govobj)
     662             : {
     663         300 :     AssertLockHeld(cs_store);
     664             : 
     665         300 :     if (govobj.GetObjectType() != GovernanceObject::TRIGGER) return;
     666             : 
     667         188 :     const COutPoint& masternodeOutpoint = govobj.GetMasternodeOutpoint();
     668         188 :     auto it = mapLastMasternodeObject.find(masternodeOutpoint);
     669             : 
     670         188 :     if (it == mapLastMasternodeObject.end()) {
     671          86 :         it = mapLastMasternodeObject.insert(txout_m_t::value_type(masternodeOutpoint, last_object_rec(true))).first;
     672          86 :     }
     673             : 
     674         188 :     int64_t nTimestamp = govobj.GetCreationTime();
     675         188 :     it->second.triggerBuffer.AddTimestamp(nTimestamp);
     676             : 
     677         188 :     if (nTimestamp > GetTime() + count_seconds(MAX_TIME_FUTURE_DEVIATION) - count_seconds(RELIABLE_PROPAGATION_TIME)) {
     678             :         // schedule additional relay for the object
     679           0 :         setAdditionalRelayObjects.insert(govobj.GetHash());
     680           0 :     }
     681             : 
     682         188 :     it->second.fStatusOK = true;
     683         300 : }
     684             : 
     685          52 : bool CGovernanceManager::MasternodeRateCheck(const CGovernanceObject& govobj, bool fUpdateFailStatus)
     686             : {
     687          52 :     LOCK(cs_store);
     688             :     bool fRateCheckBypassed;
     689          52 :     return MasternodeRateCheck(govobj, fUpdateFailStatus, true, fRateCheckBypassed);
     690          52 : }
     691             : 
     692         385 : bool CGovernanceManager::MasternodeRateCheck(const CGovernanceObject& govobj, bool fUpdateFailStatus, bool fForce, bool& fRateCheckBypassed)
     693             : {
     694         385 :     AssertLockHeld(cs_store);
     695             : 
     696         385 :     fRateCheckBypassed = false;
     697             : 
     698         385 :     if (!m_mn_sync.IsSynced() || !fRateChecksEnabled) {
     699           8 :         return true;
     700             :     }
     701             : 
     702         377 :     if (govobj.GetObjectType() != GovernanceObject::TRIGGER) {
     703         112 :         return true;
     704             :     }
     705             : 
     706         265 :     const COutPoint& masternodeOutpoint = govobj.GetMasternodeOutpoint();
     707         265 :     int64_t nTimestamp = govobj.GetCreationTime();
     708         265 :     int64_t nNow = GetAdjustedTime();
     709         265 :     int64_t nSuperblockCycleSeconds = Params().GetConsensus().nSuperblockCycle * Params().GetConsensus().nPowTargetSpacing;
     710             : 
     711         265 :     std::string strHash = govobj.GetHash().ToString();
     712             : 
     713         265 :     if (nTimestamp < nNow - 2 * nSuperblockCycleSeconds) {
     714           0 :         LogPrint(BCLog::GOBJECT, "CGovernanceManager::MasternodeRateCheck -- object %s rejected due to too old timestamp, masternode = %s, timestamp = %d, current time = %d\n",
     715             :             strHash, masternodeOutpoint.ToStringShort(), nTimestamp, nNow);
     716           0 :         return false;
     717             :     }
     718             : 
     719         265 :     if (nTimestamp > nNow + count_seconds(MAX_TIME_FUTURE_DEVIATION)) {
     720           0 :         LogPrint(BCLog::GOBJECT, "CGovernanceManager::MasternodeRateCheck -- object %s rejected due to too new (future) timestamp, masternode = %s, timestamp = %d, current time = %d\n",
     721             :             strHash, masternodeOutpoint.ToStringShort(), nTimestamp, nNow);
     722           0 :         return false;
     723             :     }
     724             : 
     725         265 :     auto it = mapLastMasternodeObject.find(masternodeOutpoint);
     726         265 :     if (it == mapLastMasternodeObject.end()) return true;
     727             : 
     728         187 :     if (it->second.fStatusOK && !fForce) {
     729          85 :         fRateCheckBypassed = true;
     730          85 :         return true;
     731             :     }
     732             : 
     733             :     // Allow 1 trigger per mn per cycle, with a small fudge factor
     734         102 :     double dMaxRate = 2 * 1.1 / double(nSuperblockCycleSeconds);
     735             : 
     736             :     // Temporary copy to check rate after new timestamp is added
     737         102 :     CRateCheckBuffer buffer = it->second.triggerBuffer;
     738             : 
     739         102 :     buffer.AddTimestamp(nTimestamp);
     740         102 :     double dRate = buffer.GetRate();
     741             : 
     742         102 :     if (dRate < dMaxRate) {
     743         102 :         return true;
     744             :     }
     745             : 
     746           0 :     LogPrint(BCLog::GOBJECT, "CGovernanceManager::MasternodeRateCheck -- Rate too high: object hash = %s, masternode = %s, object timestamp = %d, rate = %f, max rate = %f\n",
     747             :         strHash, masternodeOutpoint.ToStringShort(), nTimestamp, dRate, dMaxRate);
     748             : 
     749           0 :     if (fUpdateFailStatus) {
     750           0 :         it->second.fStatusOK = false;
     751           0 :     }
     752             : 
     753           0 :     return false;
     754         385 : }
     755             : 
     756         276 : bool CGovernanceManager::ProcessVoteAndRelay(const CGovernanceVote& vote, CGovernanceException& exception, CConnman& connman)
     757             : {
     758         276 :     AssertLockNotHeld(cs_store);
     759         276 :     AssertLockNotHeld(cs_relay);
     760         276 :     uint256 hashToRequest; // Ignored for local votes (no peer to request from)
     761         276 :     bool fOK = ProcessVote(vote, exception, hashToRequest);
     762         276 :     if (fOK) {
     763         256 :         RelayVote(vote);
     764         256 :     }
     765         276 :     return fOK;
     766             : }
     767             : 
     768        1500 : bool CGovernanceManager::ProcessVote(const CGovernanceVote& vote, CGovernanceException& exception,
     769             :                                      uint256& hashToRequest)
     770             : {
     771        1500 :     AssertLockNotHeld(cs_store);
     772        1500 :     hashToRequest = uint256{};
     773             : 
     774        1500 :     LOCK(cs_store);
     775        1500 :     uint256 nHashVote = vote.GetHash();
     776        1500 :     uint256 nHashGovobj = vote.GetParentHash();
     777             : 
     778        1500 :     if (cmapVoteToObject.HasKey(nHashVote)) {
     779           0 :         LogPrint(BCLog::GOBJECT, "CGovernanceObject::%s -- skipping known valid vote %s for object %s\n", __func__,
     780             :             nHashVote.ToString(), nHashGovobj.ToString());
     781           0 :         return false;
     782             :     }
     783             : 
     784        1500 :     if (cmapInvalidVotes.HasKey(nHashVote)) {
     785           0 :         std::string msg{strprintf("CGovernanceManager::%s -- Old invalid vote, MN outpoint = %s, governance object hash = %s",
     786           0 :             __func__, vote.GetMasternodeOutpoint().ToStringShort(), nHashGovobj.ToString())};
     787           0 :         LogPrint(BCLog::GOBJECT, "%s\n", msg);
     788           0 :         exception = CGovernanceException(msg, GOVERNANCE_EXCEPTION_PERMANENT_ERROR, 20);
     789           0 :         return false;
     790           0 :     }
     791             : 
     792        1500 :     auto it = mapObjects.find(nHashGovobj);
     793        1500 :     if (it == mapObjects.end()) {
     794           0 :         std::string msg{strprintf("CGovernanceManager::%s -- Unknown parent object %s, MN outpoint = %s", __func__,
     795           0 :             nHashGovobj.ToString(), vote.GetMasternodeOutpoint().ToStringShort())};
     796           0 :         exception = CGovernanceException(msg, GOVERNANCE_EXCEPTION_WARNING);
     797           0 :         if (cmmapOrphanVotes.Insert(nHashGovobj, vote_time_pair_t(vote, count_seconds(GetTime<std::chrono::seconds>() +
     798             :                                                                                       GOVERNANCE_ORPHAN_EXPIRATION_TIME)))) {
     799           0 :             hashToRequest = nHashGovobj; // Caller should request this object
     800           0 :         }
     801           0 :         LogPrint(BCLog::GOBJECT, "%s\n", msg);
     802           0 :         return false;
     803           0 :     }
     804             : 
     805        1500 :     auto& govobj = *Assert(it->second);
     806             : 
     807        1500 :     if (govobj.IsSetCachedDelete() || govobj.IsSetExpired()) {
     808           0 :         LogPrint(BCLog::GOBJECT, "CGovernanceObject::%s -- ignoring vote for expired or deleted object, hash = %s\n",
     809             :             __func__, nHashGovobj.ToString());
     810           0 :         return false;
     811             :     }
     812             : 
     813        1500 :     bool fOk = govobj.ProcessVote(m_mn_metaman, fRateChecksEnabled, m_dmnman.GetListAtChainTip(), vote, exception);
     814        1500 :     if (fOk) {
     815        1480 :         fOk = cmapVoteToObject.Insert(nHashVote, it->second);
     816        1500 :     } else if (exception.GetType() == GOVERNANCE_EXCEPTION_PERMANENT_ERROR && exception.GetNodePenalty() == 20) {
     817           0 :         cmapInvalidVotes.Insert(nHashVote, vote);
     818           0 :     }
     819        1500 :     return fOk;
     820        1500 : }
     821             : 
     822      206314 : void CGovernanceManager::CheckPostponedObjects()
     823             : {
     824      206314 :     AssertLockHeld(::cs_main);
     825      206314 :     AssertLockHeld(cs_store);
     826      206314 :     AssertLockNotHeld(cs_relay);
     827      206314 :     if (!m_mn_sync.IsSynced()) return;
     828             : 
     829             :     // Check postponed proposals
     830       99244 :     for (auto it = mapPostponedObjects.begin(); it != mapPostponedObjects.end();) {
     831           0 :         const uint256& nHash = it->first;
     832           0 :         auto& govobj = *Assert(it->second);
     833             : 
     834           0 :         assert(govobj.GetObjectType() != GovernanceObject::TRIGGER);
     835             : 
     836           0 :         std::string strError;
     837             :         bool fMissingConfirmations;
     838           0 :         if (govobj.IsCollateralValid(m_chainman, strError, fMissingConfirmations)) {
     839           0 :             if (govobj.IsValidLocally(m_dmnman.GetListAtChainTip(), m_chainman, strError, false)) {
     840           0 :                 AddGovernanceObjectInternal(govobj, "<postponed>");
     841           0 :             } else {
     842           0 :                 LogPrint(BCLog::GOBJECT, "CGovernanceManager::CheckPostponedObjects -- %s invalid\n", nHash.ToString());
     843             :             }
     844             : 
     845           0 :         } else if (fMissingConfirmations) {
     846             :             // wait for more confirmations
     847           0 :             ++it;
     848           0 :             continue;
     849             :         }
     850             : 
     851             :         // remove processed or invalid object from the queue
     852           0 :         mapPostponedObjects.erase(it++);
     853           0 :     }
     854             : 
     855             : 
     856             :     // Perform additional relays for triggers
     857       99244 :     int64_t nNow = GetAdjustedTime();
     858       99244 :     int64_t nSuperblockCycleSeconds = Params().GetConsensus().nSuperblockCycle * Params().GetConsensus().nPowTargetSpacing;
     859             : 
     860       99244 :     for (auto it = setAdditionalRelayObjects.begin(); it != setAdditionalRelayObjects.end();) {
     861           0 :         auto itObject = mapObjects.find(*it);
     862           0 :         if (itObject != mapObjects.end()) {
     863           0 :             const auto& govobj = *Assert(itObject->second);
     864             : 
     865           0 :             int64_t nTimestamp = govobj.GetCreationTime();
     866             : 
     867           0 :             bool fValid = (nTimestamp <= nNow + count_seconds(MAX_TIME_FUTURE_DEVIATION)) &&
     868           0 :                           (nTimestamp >= nNow - 2 * nSuperblockCycleSeconds);
     869           0 :             bool fReady = (nTimestamp <=
     870           0 :                            nNow + count_seconds(MAX_TIME_FUTURE_DEVIATION) - count_seconds(RELIABLE_PROPAGATION_TIME));
     871             : 
     872           0 :             if (fValid) {
     873           0 :                 if (fReady) {
     874           0 :                     LogPrint(BCLog::GOBJECT, "CGovernanceManager::CheckPostponedObjects -- additional relay: hash = %s\n", govobj.GetHash().ToString());
     875           0 :                     RelayObject(govobj);
     876           0 :                 } else {
     877           0 :                     it++;
     878           0 :                     continue;
     879             :                 }
     880           0 :             }
     881             : 
     882           0 :         } else {
     883           0 :             LogPrint(BCLog::GOBJECT, "CGovernanceManager::CheckPostponedObjects -- additional relay of unknown object: %s\n", it->ToString());
     884             :         }
     885             : 
     886           0 :         setAdditionalRelayObjects.erase(it++);
     887             :     }
     888      206314 : }
     889             : 
     890         624 : CBloomFilter CGovernanceManager::GetVoteBloomFilter(const uint256& nHash) const
     891             : {
     892         624 :     LOCK(cs_store);
     893             : 
     894         624 :     auto pObj = FindConstGovernanceObjectInternal(nHash);
     895         624 :     if (!pObj) {
     896           0 :         return CBloomFilter{};
     897             :     }
     898             : 
     899         624 :     CBloomFilter filter(Params().GetConsensus().nGovernanceFilterElements, GOVERNANCE_FILTER_FP_RATE,
     900         624 :                         GetRand<int>(/*nMax=*/999999), BLOOM_UPDATE_ALL);
     901             : 
     902        1248 :     std::vector<CGovernanceVote> vecVotes = WITH_LOCK(pObj->cs, return pObj->GetVoteFile().GetVotes());
     903        3216 :     for (const auto& vote : vecVotes) {
     904        2592 :         filter.insert(vote.GetHash());
     905             :     }
     906             : 
     907         624 :     return filter;
     908        1248 : }
     909             : 
     910       13591 : CDeterministicMNManager& CGovernanceManager::GetMNManager() { return m_dmnman; }
     911             : 
     912       12367 : std::pair<std::vector<uint256>, std::vector<uint256>> CGovernanceManager::FetchGovernanceObjectVotes(
     913             :     size_t nPeersPerHashMax, int64_t nNow, std::map<uint256, std::map<CService, int64_t>>& mapAskedRecently) const
     914             : {
     915       12367 :     std::vector<uint256> vTriggerObjHashes;
     916       12367 :     std::vector<uint256> vOtherObjHashes;
     917             :     {
     918       12367 :         LOCK(cs_store);
     919             : 
     920       61374 :         for (const auto& [nHash, govobj] : mapObjects) {
     921        5697 :             if (Assert(govobj)->IsSetCachedDelete()) continue;
     922       11038 :             if (mapAskedRecently.count(nHash)) {
     923       21158 :                 for (auto it = mapAskedRecently[nHash].begin(); it != mapAskedRecently[nHash].end();) {
     924       10480 :                     if (it->second < nNow) {
     925         512 :                         mapAskedRecently[nHash].erase(it++);
     926         256 :                     } else {
     927       10224 :                         ++it;
     928             :                     }
     929             :                 }
     930       10678 :                 if (mapAskedRecently[nHash].size() >= nPeersPerHashMax) continue;
     931        5339 :             }
     932             : 
     933        5519 :             if (govobj->GetObjectType() == GovernanceObject::TRIGGER) {
     934        2233 :                 vTriggerObjHashes.push_back(nHash);
     935        2233 :             } else {
     936        3286 :                 vOtherObjHashes.push_back(nHash);
     937             :             }
     938             :         }
     939       12367 :     }
     940       12367 :     return {vTriggerObjHashes, vOtherObjHashes};
     941       12367 : }
     942             : 
     943        1472 : bool CGovernanceManager::AcceptMessage(const uint256& nHash)
     944             : {
     945        1472 :     AssertLockNotHeld(cs_store);
     946        1472 :     LOCK(cs_store);
     947        1472 :     auto it = m_requested_hash_time.find(nHash);
     948        1472 :     if (it == m_requested_hash_time.end()) {
     949             :         // We never requested this
     950           0 :         return false;
     951             :     }
     952             :     // Only accept one response
     953        1472 :     m_requested_hash_time.erase(it);
     954        1472 :     return true;
     955        1472 : }
     956             : 
     957        1946 : void CGovernanceManager::RebuildIndexes()
     958             : {
     959        1946 :     AssertLockHeld(cs_store);
     960             : 
     961        1946 :     cmapVoteToObject.Clear();
     962        1946 :     for (auto& [_, govobj] : mapObjects) {
     963           0 :         assert(govobj);
     964           0 :         std::vector<CGovernanceVote> vecVotes = WITH_LOCK(govobj->cs, return govobj->GetVoteFile().GetVotes());
     965           0 :         for (const auto& vecVote : vecVotes) {
     966           0 :             cmapVoteToObject.Insert(vecVote.GetHash(), govobj);
     967             :         }
     968           0 :     }
     969        1946 : }
     970             : 
     971        1946 : void CGovernanceManager::InitOnLoad()
     972             : {
     973             :     {
     974        1946 :     LOCK(cs_store);
     975        1946 :     const auto start{SteadyClock::now()};
     976        1946 :     LogPrintf("Preparing masternode indexes and governance triggers...\n");
     977        1946 :     RebuildIndexes();
     978             : 
     979        1946 :     const int64_t nNow = GetTime<std::chrono::seconds>().count();
     980        1946 :     for (auto& [_, govobj] : mapObjects) {
     981           0 :         if (Assert(govobj)->GetObjectType() != GovernanceObject::TRIGGER) continue;
     982           0 :         if (!m_superblocks.AddTrigger(govobj, nCachedBlockHeight)) {
     983           0 :             govobj->PrepareDeletion(nNow);
     984           0 :         }
     985             :     }
     986        1946 :     LogPrintf("Masternode indexes and governance triggers prepared  %dms\n",
     987             :               Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
     988        1946 :     }
     989        1946 :     LogPrintf("     %s\n", ToString());
     990        1946 : }
     991             : 
     992        3410 : void GovernanceStore::Clear()
     993             : {
     994        3410 :     LOCK(cs_store);
     995        3410 :     mapObjects.clear();
     996        3410 :     mapErasedGovernanceObjects.clear();
     997        3410 :     cmapInvalidVotes.Clear();
     998        3410 :     cmmapOrphanVotes.Clear();
     999        3410 :     mapLastMasternodeObject.clear();
    1000        3410 :     lastMNListForVotingKeys = std::make_shared<CDeterministicMNList>();
    1001        3410 : }
    1002             : 
    1003           0 : void CGovernanceManager::Clear()
    1004             : {
    1005           0 :     AssertLockNotHeld(cs_store);
    1006           0 :     LogPrint(BCLog::GOBJECT, "Governance object manager was cleared\n");
    1007           0 :     GovernanceStore::Clear();
    1008           0 :     nTimeLastDiff = 0;
    1009           0 :     nCachedBlockHeight = 0;
    1010           0 :     cmapVoteToObject.Clear();
    1011           0 :     mapPostponedObjects.clear();
    1012           0 :     setAdditionalRelayObjects.clear();
    1013           0 :     m_requested_hash_time.clear();
    1014           0 :     fRateChecksEnabled = true;
    1015           0 :     m_superblocks.Clear();
    1016           0 : }
    1017             : 
    1018      108112 : std::string GovernanceStore::ToString() const
    1019             : {
    1020      108112 :     LOCK(cs_store);
    1021             : 
    1022      108112 :     int nProposalCount = 0;
    1023      108112 :     int nTriggerCount = 0;
    1024      108112 :     int nOtherCount = 0;
    1025             : 
    1026      121218 :     for (const auto& [_, govobj] : mapObjects) {
    1027       13106 :         switch (Assert(govobj)->GetObjectType()) {
    1028             :         case GovernanceObject::PROPOSAL:
    1029        5688 :             nProposalCount++;
    1030        5688 :             break;
    1031             :         case GovernanceObject::TRIGGER:
    1032        7418 :             nTriggerCount++;
    1033        7418 :             break;
    1034             :         default:
    1035           0 :             nOtherCount++;
    1036           0 :             break;
    1037             :         }
    1038             :     }
    1039             : 
    1040      108112 :     return strprintf("Governance Objects: %d (Proposals: %d, Triggers: %d, Other: %d; Erased: %d)",
    1041      108112 :         (int)mapObjects.size(),
    1042      108112 :         nProposalCount, nTriggerCount, nOtherCount, (int)mapErasedGovernanceObjects.size());
    1043      108112 : }
    1044             : 
    1045      100986 : std::string CGovernanceManager::ToString() const
    1046             : {
    1047      100986 :     AssertLockNotHeld(cs_store);
    1048      100986 :     return strprintf("%s, Votes: %d", GovernanceStore::ToString(), (int)cmapVoteToObject.GetSize());
    1049           0 : }
    1050             : 
    1051      206314 : void CGovernanceManager::UpdatedBlockTip(const CBlockIndex* pindex)
    1052             : {
    1053      206314 :     AssertLockNotHeld(cs_store);
    1054      206314 :     AssertLockNotHeld(cs_relay);
    1055             :     // Note this gets called from ActivateBestChain without cs_main being held
    1056             :     // so it should be safe to lock our mutex here without risking a deadlock
    1057             :     // On the other hand it should be safe for us to access pindex without holding a lock
    1058             :     // on cs_main because the CBlockIndex objects are dynamically allocated and
    1059             :     // presumably never deleted.
    1060      206314 :     if (!pindex) {
    1061           0 :         return;
    1062             :     }
    1063             : 
    1064      206314 :     nCachedBlockHeight = pindex->nHeight;
    1065      206314 :     LogPrint(BCLog::GOBJECT, "CGovernanceManager::UpdatedBlockTip -- nCachedBlockHeight: %d\n", nCachedBlockHeight);
    1066             : 
    1067      206314 :     LOCK2(::cs_main, cs_store);
    1068      206314 :     if (DeploymentDIP0003Enforced(pindex->nHeight, Params().GetConsensus())) {
    1069      113460 :         RemoveInvalidVotes();
    1070      113460 :     }
    1071             : 
    1072      206314 :     CheckPostponedObjects();
    1073             : 
    1074      206314 :     m_superblocks.ExecuteBestSuperblock(m_dmnman.GetListAtChainTip(), pindex->nHeight);
    1075      206314 : }
    1076             : 
    1077        3202 : std::vector<uint256> CGovernanceManager::GetOrphanVoteObjectHashes()
    1078             : {
    1079        3202 :     LOCK(cs_store);
    1080             : 
    1081        3202 :     int64_t nNow = GetTime<std::chrono::seconds>().count();
    1082             : 
    1083             :     // Clean up expired orphan votes
    1084        3202 :     const vote_cmm_t::list_t& items = cmmapOrphanVotes.GetItemList();
    1085        3202 :     for (auto it = items.begin(); it != items.end();) {
    1086           0 :         auto prevIt = it;
    1087           0 :         ++it;
    1088           0 :         const auto& [_, time] = prevIt->value;
    1089           0 :         if (time < nNow) {
    1090           0 :             cmmapOrphanVotes.Erase(prevIt->key, prevIt->value);
    1091           0 :         }
    1092             :     }
    1093             : 
    1094             :     // Get hashes of objects we don't have yet
    1095        3202 :     std::vector<uint256> vecHashesFiltered;
    1096        3202 :     std::vector<uint256> vecHashes;
    1097        3202 :     cmmapOrphanVotes.GetKeys(vecHashes);
    1098        3202 :     for (const uint256& nHash : vecHashes) {
    1099           0 :         if (mapObjects.find(nHash) == mapObjects.end()) {
    1100           0 :             vecHashesFiltered.push_back(nHash);
    1101           0 :         }
    1102             :     }
    1103             : 
    1104        3202 :     return vecHashesFiltered;
    1105        3202 : }
    1106             : 
    1107      113460 : void CGovernanceManager::RemoveInvalidVotes()
    1108             : {
    1109      113460 :     AssertLockHeld(cs_store);
    1110             : 
    1111      113460 :     if (!m_mn_sync.IsSynced()) {
    1112       22190 :         return;
    1113             :     }
    1114             : 
    1115       91270 :     const auto tip_mn_list = m_dmnman.GetListAtChainTip();
    1116       91270 :     auto diff = lastMNListForVotingKeys->BuildDiff(tip_mn_list);
    1117             : 
    1118       91270 :     std::vector<COutPoint> changedKeyMNs;
    1119      276798 :     for (const auto& [internalId, pdmnState] : diff.updatedMNs) {
    1120      185528 :         auto oldDmn = lastMNListForVotingKeys->GetMNByInternalId(internalId);
    1121             :         // BuildDiff will construct itself with MNs that we already have knowledge
    1122             :         // of, meaning that fetch operations should never fail.
    1123       92764 :         assert(oldDmn);
    1124       92764 :         if ((pdmnState.fields & CDeterministicMNStateDiff::Field_keyIDVoting) && pdmnState.state.keyIDVoting != oldDmn->pdmnState->keyIDVoting) {
    1125          52 :             changedKeyMNs.emplace_back(oldDmn->collateralOutpoint);
    1126       92764 :         } else if ((pdmnState.fields & CDeterministicMNStateDiff::Field_pubKeyOperator) && pdmnState.state.pubKeyOperator != oldDmn->pdmnState->pubKeyOperator) {
    1127          54 :             changedKeyMNs.emplace_back(oldDmn->collateralOutpoint);
    1128          54 :         }
    1129       92764 :     }
    1130       91682 :     for (const auto& id : diff.removedMns) {
    1131         412 :         auto oldDmn = lastMNListForVotingKeys->GetMNByInternalId(id);
    1132             :         // BuildDiff will construct itself with MNs that we already have knowledge
    1133             :         // of, meaning that fetch operations should never fail.
    1134         412 :         assert(oldDmn);
    1135         412 :         changedKeyMNs.emplace_back(oldDmn->collateralOutpoint);
    1136         412 :     }
    1137             : 
    1138       91788 :     for (const auto& outpoint : changedKeyMNs) {
    1139         518 :         for (auto& [_, govobj] : mapObjects) {
    1140           0 :             auto removed = Assert(govobj)->RemoveInvalidVotes(tip_mn_list, outpoint);
    1141           0 :             if (removed.empty()) {
    1142           0 :                 continue;
    1143             :             }
    1144           0 :             for (auto& voteHash : removed) {
    1145           0 :                 cmapVoteToObject.Erase(voteHash);
    1146           0 :                 cmapInvalidVotes.Erase(voteHash);
    1147           0 :                 cmmapOrphanVotes.Erase(voteHash);
    1148           0 :                 m_requested_hash_time.erase(voteHash);
    1149             :             }
    1150           0 :         }
    1151             :     }
    1152             : 
    1153             :     // store current MN list for the next run so that we can determine which keys changed
    1154       91270 :     lastMNListForVotingKeys = std::make_shared<CDeterministicMNList>(tip_mn_list);
    1155      113460 : }
    1156             : 
    1157       39483 : std::vector<std::shared_ptr<const CGovernanceObject>> CGovernanceManager::GetApprovedProposals(
    1158             :     const CDeterministicMNList& tip_mn_list)
    1159             : {
    1160       39483 :     AssertLockNotHeld(cs_store);
    1161             : 
    1162             :     // Use std::vector of std::shared_ptr<const CGovernanceObject> because CGovernanceObject doesn't support move operations (needed for sorting the vector later)
    1163       39483 :     std::vector<std::shared_ptr<const CGovernanceObject>> ret{};
    1164             : 
    1165             :     // A proposal is considered passing if (YES votes) >= (Total Weight of Masternodes / 10),
    1166             :     // count total valid (ENABLED) masternodes to determine passing threshold.
    1167       39483 :     const int nWeightedMnCount = tip_mn_list.GetCounts().m_valid_weighted;
    1168       39483 :     const int nAbsVoteReq = std::max(Params().GetConsensus().nGovernanceMinQuorum, nWeightedMnCount / 10);
    1169             : 
    1170       39483 :     LOCK(cs_store);
    1171       41315 :     for (const auto& [_, govobj] : mapObjects) {
    1172             :         // Skip all non-proposals objects
    1173        1256 :         if (Assert(govobj)->GetObjectType() != GovernanceObject::PROPOSAL) continue;
    1174             :         // Skip non-passing proposals
    1175         576 :         const int absYesCount = govobj->GetAbsoluteYesCount(tip_mn_list, VOTE_SIGNAL_FUNDING);
    1176         576 :         if (absYesCount < nAbsVoteReq) continue;
    1177         576 :         ret.emplace_back(govobj);
    1178             :     }
    1179             : 
    1180             :     // Sort approved proposals by absolute Yes votes descending
    1181       39976 :     std::sort(ret.begin(), ret.end(), [&tip_mn_list](auto& lhs, auto& rhs) {
    1182         493 :         const auto lhs_yes = lhs->GetAbsoluteYesCount(tip_mn_list, VOTE_SIGNAL_FUNDING);
    1183         493 :         const auto rhs_yes = rhs->GetAbsoluteYesCount(tip_mn_list, VOTE_SIGNAL_FUNDING);
    1184         493 :         return lhs_yes == rhs_yes ? UintToArith256(lhs->GetHash()) > UintToArith256(rhs->GetHash()) : lhs_yes > rhs_yes;
    1185             :     });
    1186             : 
    1187       39483 :     return ret;
    1188       39483 : }

Generated by: LCOV version 1.16