LCOV - code coverage report
Current view: top level - src/governance - governance.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 19 725 2.6 %
Date: 2026-06-25 07:23:51 Functions: 6 63 9.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         146 : 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           0 :     ScopedLockBool(Mutex& _cs, bool& _ref, bool _value) :
      44           0 :         ref(_ref)
      45           0 :     {
      46           0 :         AssertLockHeld(_cs);
      47           0 :         fPrevValue = ref;
      48           0 :         ref = _value;
      49           0 :     }
      50             : 
      51           0 :     ~ScopedLockBool() { ref = fPrevValue; }
      52             : };
      53             : } // anonymous namespace
      54             : 
      55         178 : GovernanceStore::GovernanceStore() :
      56         178 :     cs_store(),
      57         178 :     mapObjects(),
      58         178 :     mapErasedGovernanceObjects(),
      59         178 :     cmapInvalidVotes(MAX_CACHE_SIZE),
      60         178 :     cmmapOrphanVotes(MAX_CACHE_SIZE),
      61         178 :     mapLastMasternodeObject(),
      62         178 :     lastMNListForVotingKeys(std::make_shared<CDeterministicMNList>())
      63           0 : {
      64         178 : }
      65             : 
      66         356 : CGovernanceManager::CGovernanceManager(CMasternodeMetaMan& mn_metaman, const ChainstateManager& chainman,
      67             :                                        governance::SuperblockManager& superblocks, CDeterministicMNManager& dmnman,
      68             :                                        CMasternodeSync& mn_sync) :
      69         178 :     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         178 : {
      78         178 : }
      79             : 
      80         356 : CGovernanceManager::~CGovernanceManager()
      81         178 : {
      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         178 :     m_superblocks.Clear();
      86         178 :     if (!is_loaded) return;
      87           0 :     m_db->Store(*this);
      88         356 : }
      89             : 
      90           0 : bool CGovernanceManager::LoadCache(bool load_cache)
      91             : {
      92           0 :     AssertLockNotHeld(cs_store);
      93           0 :     assert(m_db != nullptr);
      94           0 :     is_loaded = load_cache ? m_db->Load(*this) : m_db->Store(*this);
      95           0 :     if (is_loaded && load_cache) {
      96           0 :         CheckAndRemove();
      97           0 :         InitOnLoad();
      98           0 :     }
      99           0 :     m_superblocks.SetLoaded(is_loaded);
     100           0 :     return is_loaded;
     101             : }
     102             : 
     103           0 : void CGovernanceManager::RelayObject(const CGovernanceObject& obj)
     104             : {
     105           0 :     AssertLockNotHeld(cs_relay);
     106           0 :     if (!m_mn_sync.IsSynced()) {
     107           0 :         LogPrint(BCLog::GOBJECT, "%s -- won't relay until fully synced\n", __func__);
     108           0 :         return;
     109             :     }
     110             : 
     111           0 :     LOCK(cs_relay);
     112           0 :     m_relay_invs.emplace_back(MSG_GOVERNANCE_OBJECT, obj.GetHash());
     113           0 : }
     114             : 
     115           0 : void CGovernanceManager::RelayVote(const CGovernanceVote& vote)
     116             : {
     117           0 :     AssertLockNotHeld(cs_relay);
     118           0 :     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           0 :     const auto tip_mn_list = m_dmnman.GetListAtChainTip();
     124           0 :     auto dmn = tip_mn_list.GetMNByCollateral(vote.GetMasternodeOutpoint());
     125           0 :     if (!dmn) {
     126           0 :         return;
     127             :     }
     128             : 
     129           0 :     LOCK(cs_relay);
     130           0 :     m_relay_invs.emplace_back(MSG_GOVERNANCE_OBJECT_VOTE, vote.GetHash());
     131           0 : }
     132             : 
     133             : // Accessors for thread-safe access to maps
     134           0 : bool CGovernanceManager::HaveObjectForHash(const uint256& nHash) const
     135             : {
     136           0 :     LOCK(cs_store);
     137           0 :     return (mapObjects.count(nHash) == 1 || mapPostponedObjects.count(nHash) == 1);
     138           0 : }
     139             : 
     140           0 : bool CGovernanceManager::SerializeObjectForHash(const uint256& nHash, CDataStream& ss) const
     141             : {
     142           0 :     LOCK(cs_store);
     143           0 :     auto it = mapObjects.find(nHash);
     144           0 :     if (it == mapObjects.end()) {
     145           0 :         it = mapPostponedObjects.find(nHash);
     146           0 :         if (it == mapPostponedObjects.end())
     147           0 :             return false;
     148           0 :     }
     149           0 :     ss << it->second;
     150           0 :     return true;
     151           0 : }
     152             : 
     153           0 : bool CGovernanceManager::HaveVoteForHash(const uint256& nHash) const
     154             : {
     155           0 :     LOCK(cs_store);
     156             : 
     157           0 :     std::shared_ptr<CGovernanceObject> pGovobj{nullptr};
     158           0 :     return cmapVoteToObject.Get(nHash, pGovobj) && WITH_LOCK(pGovobj->cs, return pGovobj->GetVoteFile().HasVote(nHash));
     159           0 : }
     160             : 
     161           0 : int CGovernanceManager::GetVoteCount() const
     162             : {
     163           0 :     LOCK(cs_store);
     164           0 :     return (int)cmapVoteToObject.GetSize();
     165           0 : }
     166             : 
     167           0 : bool CGovernanceManager::SerializeVoteForHash(const uint256& nHash, CDataStream& ss) const
     168             : {
     169           0 :     LOCK(cs_store);
     170             : 
     171           0 :     std::shared_ptr<CGovernanceObject> pGovobj{nullptr};
     172           0 :     return cmapVoteToObject.Get(nHash, pGovobj) && WITH_LOCK(pGovobj->cs, return pGovobj->GetVoteFile().SerializeVoteToStream(nHash, ss));
     173           0 : }
     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           0 : bool CGovernanceManager::ProcessObject(const std::string& peer_str, const uint256& nHash, CGovernanceObject& govobj)
     188             : {
     189           0 :     std::string strHash = nHash.ToString();
     190             : 
     191           0 :     LOCK(cs_store);
     192             : 
     193           0 :     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           0 :     bool fRateCheckBypassed = false;
     200           0 :     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           0 :     std::string strError;
     207             :     // CHECK OBJECT AGAINST LOCAL BLOCKCHAIN
     208             : 
     209           0 :     const auto tip_mn_list = m_dmnman.GetListAtChainTip();
     210           0 :     bool fMissingConfirmations = false;
     211           0 :     bool fIsValid = govobj.IsValidLocally(tip_mn_list, m_chainman, strError, fMissingConfirmations, true);
     212             : 
     213             :     bool unused_rcb;
     214           0 :     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           0 :     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           0 :     AddGovernanceObjectInternal(govobj, peer_str);
     234           0 :     return true;
     235           0 : }
     236             : 
     237           0 : void CGovernanceManager::CheckOrphanVotes(CGovernanceObject& govobj)
     238             : {
     239           0 :     AssertLockHeld(cs_store);
     240           0 :     AssertLockNotHeld(cs_relay);
     241             : 
     242           0 :     uint256 nHash = govobj.GetHash();
     243           0 :     std::vector<vote_time_pair_t> vecVotePairs;
     244           0 :     cmmapOrphanVotes.GetAll(nHash, vecVotePairs);
     245             : 
     246           0 :     ScopedLockBool guard(cs_store, fRateChecksEnabled, false);
     247             : 
     248           0 :     int64_t nNow = GetAdjustedTime();
     249           0 :     const auto tip_mn_list = m_dmnman.GetListAtChainTip();
     250           0 :     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           0 : }
     265             : 
     266           0 : void CGovernanceManager::AddGovernanceObjectInternal(CGovernanceObject& insert_obj, const std::string& peer_str)
     267             : {
     268           0 :     AssertLockHeld(::cs_main);
     269           0 :     AssertLockHeld(cs_store);
     270           0 :     AssertLockNotHeld(cs_relay);
     271             : 
     272           0 :     uint256 nHash = insert_obj.GetHash();
     273           0 :     std::string strHash = nHash.ToString();
     274             : 
     275           0 :     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           0 :     insert_obj.UpdateSentinelVariables(tip_mn_list); //this sets local vars in object
     280             : 
     281           0 :     std::string strError;
     282             : 
     283             :     // MAKE SURE THIS OBJECT IS OK
     284             : 
     285           0 :     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           0 :     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           0 :     auto [emplace_ret, emplace_status] = mapObjects.emplace(nHash, std::make_shared<CGovernanceObject>(insert_obj));
     296             : 
     297           0 :     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           0 :     auto& [_, govobj] = *emplace_ret;
     305           0 :     LogPrint(BCLog::GOBJECT, "CGovernanceManager::AddGovernanceObject -- Before trigger block, GetDataAsPlainString = %s, nObjectType = %d\n",
     306             :                 Assert(govobj)->GetDataAsPlainString(), std23::to_underlying(govobj->GetObjectType()));
     307             : 
     308           0 :     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           0 :     LogPrint(BCLog::GOBJECT, "CGovernanceManager::AddGovernanceObject -- %s new, received from peer %s\n", strHash, peer_str);
     315           0 :     RelayObject(*govobj);
     316             : 
     317             :     // Update the rate buffer
     318           0 :     MasternodeRateUpdate(*govobj);
     319             : 
     320           0 :     m_mn_sync.BumpAssetLastTime("CGovernanceManager::AddGovernanceObject");
     321             : 
     322             :     // WE MIGHT HAVE PENDING/ORPHAN VOTES FOR THIS OBJECT
     323             : 
     324           0 :     CheckOrphanVotes(*govobj);
     325             : 
     326             :     // SEND NOTIFICATION TO SCRIPT/ZMQ
     327           0 :     GetMainSignals().NotifyGovernanceObject(std::make_shared<const Governance::Object>(govobj->Object()), nHash.ToString());
     328           0 :     uiInterface.NotifyGovernanceChanged();
     329           0 : }
     330             : 
     331           0 : void CGovernanceManager::AddGovernanceObject(CGovernanceObject& govobj, const std::string& peer_str)
     332             : {
     333           0 :     LOCK2(::cs_main, cs_store);
     334           0 :     AddGovernanceObjectInternal(govobj, peer_str);
     335           0 : }
     336             : 
     337           0 : void CGovernanceManager::CheckAndRemove()
     338             : {
     339           0 :     AssertLockNotHeld(cs_store);
     340           0 :     assert(m_mn_metaman.IsValid());
     341             : 
     342             :     // Return on initial sync, spammed the debug.log and provided no use
     343           0 :     if (!m_mn_sync.IsBlockchainSynced()) return;
     344             : 
     345           0 :     LogPrint(BCLog::GOBJECT, "CGovernanceManager::UpdateCachesAndClean\n");
     346             : 
     347           0 :     std::vector<uint256> vecDirtyHashes = m_mn_metaman.GetAndClearDirtyGovernanceObjectHashes();
     348             : 
     349           0 :     const auto tip_mn_list = m_dmnman.GetListAtChainTip();
     350             : 
     351             :     {
     352           0 :     LOCK2(::cs_main, cs_store);
     353             : 
     354           0 :     for (const uint256& nHash : vecDirtyHashes) {
     355           0 :         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           0 :     ScopedLockBool guard(cs_store, fRateChecksEnabled, false);
     363             : 
     364             :     // Clean up any expired or invalid triggers
     365           0 :     m_superblocks.Clean(nCachedBlockHeight);
     366             : 
     367           0 :     const auto nNow = GetTime<std::chrono::seconds>();
     368           0 :     for (auto it = mapObjects.begin(); it != mapObjects.end();) {
     369           0 :         auto [nHash, pObj] = *it;
     370           0 :         std::string strHash = nHash.ToString();
     371             : 
     372             :         // IF CACHE IS NOT DIRTY, WHY DO THIS?
     373           0 :         if (Assert(pObj)->IsSetDirtyCache()) {
     374             :             // UPDATE LOCAL VALIDITY AGAINST CRYPTO DATA
     375           0 :             pObj->UpdateLocalValidity(tip_mn_list, m_chainman);
     376             : 
     377             :             // UPDATE SENTINEL SIGNALING VARIABLES
     378           0 :             pObj->UpdateSentinelVariables(tip_mn_list);
     379           0 :         }
     380             : 
     381             :         // IF DELETE=TRUE, THEN CLEAN THE MESS UP!
     382             : 
     383           0 :         const auto nTimeSinceDeletion = nNow - std::chrono::seconds{pObj->GetDeletionTime()};
     384             : 
     385           0 :         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           0 :         if ((pObj->IsSetCachedDelete() || pObj->IsSetExpired()) &&
     389           0 :             (nTimeSinceDeletion >= GOVERNANCE_DELETION_DELAY)) {
     390           0 :             LogPrint(BCLog::GOBJECT, "CGovernanceManager::UpdateCachesAndClean -- erase obj %s\n", nHash.ToString());
     391           0 :             m_mn_metaman.RemoveGovernanceObject(pObj->GetHash());
     392             : 
     393             :             // Remove vote references
     394           0 :             const object_ref_cm_t::list_t& listItems = cmapVoteToObject.GetItemList();
     395           0 :             for (auto lit = listItems.begin(); lit != listItems.end();) {
     396           0 :                 if (lit->value == pObj) {
     397           0 :                     uint256 nKey = lit->key;
     398           0 :                     ++lit;
     399           0 :                     cmapVoteToObject.Erase(nKey);
     400           0 :                 } else {
     401           0 :                     ++lit;
     402             :                 }
     403             :             }
     404             : 
     405           0 :             int64_t nTimeExpired{0};
     406             : 
     407           0 :             if (pObj->GetObjectType() == GovernanceObject::PROPOSAL) {
     408             :                 // keep hashes of deleted proposals forever
     409           0 :                 nTimeExpired = std::numeric_limits<int64_t>::max();
     410           0 :             } else {
     411           0 :                 int64_t nSuperblockCycleSeconds = Params().GetConsensus().nSuperblockCycle * Params().GetConsensus().nPowTargetSpacing;
     412           0 :                 nTimeExpired = (std::chrono::seconds{pObj->GetCreationTime()} +
     413           0 :                                 std::chrono::seconds{2 * nSuperblockCycleSeconds} + GOVERNANCE_DELETION_DELAY)
     414           0 :                                    .count();
     415             :             }
     416             : 
     417           0 :             mapErasedGovernanceObjects.insert(std::make_pair(nHash, nTimeExpired));
     418           0 :             if (pObj->GetObjectType() == GovernanceObject::TRIGGER) {
     419           0 :                 m_superblocks.RemoveTrigger(nHash);
     420           0 :             }
     421           0 :             mapObjects.erase(it++);
     422           0 :         } else {
     423           0 :             if (pObj->GetObjectType() == GovernanceObject::PROPOSAL) {
     424           0 :                 std::string strValidationError;
     425           0 :                 if (!governance::ValidateProposal(pObj->GetDataAsHexString(), strValidationError)) {
     426           0 :                     LogPrint(BCLog::GOBJECT, "CGovernanceManager::UpdateCachesAndClean -- set for deletion expired obj %s\n", strHash);
     427           0 :                     pObj->PrepareDeletion(nNow.count());
     428           0 :                 }
     429           0 :             }
     430           0 :             ++it;
     431             :         }
     432           0 :     }
     433             : 
     434             :     // forget about expired deleted objects
     435           0 :     for (auto s_it = mapErasedGovernanceObjects.begin(); s_it != mapErasedGovernanceObjects.end();) {
     436           0 :         if (s_it->second < nNow.count()) {
     437           0 :             mapErasedGovernanceObjects.erase(s_it++);
     438           0 :         } else {
     439           0 :             ++s_it;
     440             :         }
     441             :     }
     442             : 
     443             :     // forget about expired requests
     444           0 :     for (auto r_it = m_requested_hash_time.begin(); r_it != m_requested_hash_time.end();) {
     445           0 :         if (r_it->second < nNow) {
     446           0 :             m_requested_hash_time.erase(r_it++);
     447           0 :         } else {
     448           0 :             ++r_it;
     449             :         }
     450             :     }
     451           0 :     }
     452             : 
     453           0 :     LogPrint(BCLog::GOBJECT, "CGovernanceManager::UpdateCachesAndClean -- %s, m_requested_hash_time size=%d\n",
     454             :              ToString(), m_requested_hash_time.size());
     455           0 : }
     456             : 
     457           0 : std::vector<CInv> CGovernanceManager::FetchRelayInventory()
     458             : {
     459           0 :     std::vector<CInv> ret;
     460           0 :     LOCK(cs_relay);
     461           0 :     swap(ret, m_relay_invs);
     462           0 :     return ret;
     463           0 : }
     464             : 
     465           0 : std::shared_ptr<const CGovernanceObject> CGovernanceManager::FindConstGovernanceObject(const uint256& nHash) const
     466             : {
     467           0 :     LOCK(cs_store);
     468           0 :     return FindConstGovernanceObjectInternal(nHash);
     469           0 : }
     470             : 
     471           0 : std::shared_ptr<const CGovernanceObject> CGovernanceManager::FindConstGovernanceObjectInternal(const uint256& nHash) const
     472             : {
     473           0 :     AssertLockHeld(cs_store);
     474             : 
     475           0 :     auto it = mapObjects.find(nHash);
     476           0 :     if (it != mapObjects.end()) return (it->second);
     477             : 
     478           0 :     return nullptr;
     479           0 : }
     480             : 
     481           0 : std::shared_ptr<CGovernanceObject> CGovernanceManager::FindGovernanceObject(const uint256& nHash)
     482             : {
     483           0 :     AssertLockNotHeld(cs_store);
     484           0 :     LOCK(cs_store);
     485           0 :     return FindGovernanceObjectInternal(nHash);
     486           0 : }
     487             : 
     488           0 : std::shared_ptr<CGovernanceObject> CGovernanceManager::FindGovernanceObjectInternal(const uint256& nHash)
     489             : {
     490           0 :     AssertLockHeld(cs_store);
     491           0 :     if (mapObjects.count(nHash)) return mapObjects[nHash];
     492           0 :     return nullptr;
     493           0 : }
     494             : 
     495           0 : std::shared_ptr<CGovernanceObject> CGovernanceManager::FindGovernanceObjectByDataHash(const uint256 &nDataHash)
     496             : {
     497           0 :     AssertLockNotHeld(cs_store);
     498           0 :     LOCK(cs_store);
     499           0 :     for (const auto& [nHash, govobj] : mapObjects) {
     500           0 :         if (Assert(govobj)->GetDataHash() == nDataHash) return govobj;
     501             :     }
     502           0 :     return nullptr;
     503           0 : }
     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           0 : void CGovernanceManager::GetAllNewerThan(std::vector<CGovernanceObject>& objs, int64_t nMoreThanTime,
     545             :                                           bool include_postponed) const
     546             : {
     547           0 :     LOCK(cs_store);
     548             : 
     549           0 :     for (const auto& [_, govobj] : mapObjects) {
     550           0 :         if (Assert(govobj)->GetCreationTime() < nMoreThanTime) {
     551           0 :             continue;
     552             :         }
     553           0 :         objs.push_back(*govobj);
     554             :     }
     555             : 
     556           0 :     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           0 : }
     565             : 
     566           0 : bool CGovernanceManager::ConfirmInventoryRequest(const CInv& inv)
     567             : {
     568           0 :     AssertLockNotHeld(cs_store);
     569             : 
     570             :     // do not request objects until it's time to sync
     571           0 :     if (!m_mn_sync.IsBlockchainSynced()) return false;
     572             : 
     573           0 :     LOCK(cs_store);
     574             : 
     575           0 :     LogPrint(BCLog::GOBJECT, "CGovernanceManager::ConfirmInventoryRequest inv = %s\n", inv.ToString());
     576             : 
     577             :     // First check if we've already recorded this object
     578           0 :     switch (inv.type) {
     579             :     case MSG_GOVERNANCE_OBJECT: {
     580           0 :         if (mapObjects.count(inv.hash) == 1 || mapPostponedObjects.count(inv.hash) == 1) {
     581           0 :             LogPrint(BCLog::GOBJECT, "CGovernanceManager::ConfirmInventoryRequest already have governance object, returning false\n");
     582           0 :             return false;
     583             :         }
     584           0 :         break;
     585             :     }
     586             :     case MSG_GOVERNANCE_OBJECT_VOTE: {
     587           0 :         if (cmapVoteToObject.HasKey(inv.hash)) {
     588           0 :             LogPrint(BCLog::GOBJECT, "CGovernanceManager::ConfirmInventoryRequest already have governance vote, returning false\n");
     589           0 :             return false;
     590             :         }
     591           0 :         break;
     592             :     }
     593             :     default:
     594           0 :         LogPrint(BCLog::GOBJECT, "CGovernanceManager::ConfirmInventoryRequest unknown type, returning false\n");
     595           0 :         return false;
     596             :     }
     597             : 
     598           0 :     const auto valid_until = GetTime<std::chrono::seconds>() + RELIABLE_PROPAGATION_TIME;
     599           0 :     const auto& [_itr, inserted] = m_requested_hash_time.emplace(inv.hash, valid_until);
     600             : 
     601           0 :     if (inserted) {
     602           0 :         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           0 :     }
     606             : 
     607           0 :     LogPrint(BCLog::GOBJECT, "CGovernanceManager::ConfirmInventoryRequest reached end, returning true\n");
     608           0 :     return true;
     609           0 : }
     610             : 
     611           0 : std::vector<CInv> CGovernanceManager::GetSyncableVoteInvs(const uint256& nProp, const CBloomFilter& filter) const
     612             : {
     613           0 :     LOCK(cs_store);
     614             : 
     615           0 :     auto it = mapObjects.find(nProp);
     616           0 :     if (it == mapObjects.end()) {
     617           0 :         return {};
     618             :     }
     619             : 
     620           0 :     const auto& govobj = *Assert(it->second);
     621           0 :     if (govobj.IsSetCachedDelete() || govobj.IsSetExpired()) {
     622           0 :         return {};
     623             :     }
     624             : 
     625           0 :     std::vector<CInv> invs;
     626           0 :     const auto tip_mn_list = m_dmnman.GetListAtChainTip();
     627             : 
     628           0 :     LOCK(govobj.cs);
     629           0 :     const auto& fileVotes = govobj.GetVoteFile();
     630           0 :     for (const auto& vote : fileVotes.GetVotes()) {
     631           0 :         uint256 nVoteHash = vote.GetHash();
     632             : 
     633           0 :         bool onlyVotingKeyAllowed = govobj.GetObjectType() == GovernanceObject::PROPOSAL && vote.GetSignal() == VOTE_SIGNAL_FUNDING;
     634             : 
     635           0 :         if (filter.contains(nVoteHash) || !vote.IsValid(tip_mn_list, onlyVotingKeyAllowed)) {
     636           0 :             continue;
     637             :         }
     638           0 :         invs.emplace_back(MSG_GOVERNANCE_OBJECT_VOTE, nVoteHash);
     639             :     }
     640             : 
     641           0 :     return invs;
     642           0 : }
     643             : 
     644           0 : std::vector<CInv> CGovernanceManager::GetSyncableObjectInvs() const
     645             : {
     646           0 :     LOCK(cs_store);
     647             : 
     648           0 :     std::vector<CInv> invs;
     649           0 :     invs.reserve(mapObjects.size());
     650             : 
     651           0 :     for (const auto& [nHash, govobj] : mapObjects) {
     652           0 :         if (Assert(govobj)->IsSetCachedDelete() || govobj->IsSetExpired()) {
     653           0 :             continue;
     654             :         }
     655           0 :         invs.emplace_back(MSG_GOVERNANCE_OBJECT, nHash);
     656             :     }
     657             : 
     658           0 :     return invs;
     659           0 : }
     660             : 
     661           0 : void CGovernanceManager::MasternodeRateUpdate(const CGovernanceObject& govobj)
     662             : {
     663           0 :     AssertLockHeld(cs_store);
     664             : 
     665           0 :     if (govobj.GetObjectType() != GovernanceObject::TRIGGER) return;
     666             : 
     667           0 :     const COutPoint& masternodeOutpoint = govobj.GetMasternodeOutpoint();
     668           0 :     auto it = mapLastMasternodeObject.find(masternodeOutpoint);
     669             : 
     670           0 :     if (it == mapLastMasternodeObject.end()) {
     671           0 :         it = mapLastMasternodeObject.insert(txout_m_t::value_type(masternodeOutpoint, last_object_rec(true))).first;
     672           0 :     }
     673             : 
     674           0 :     int64_t nTimestamp = govobj.GetCreationTime();
     675           0 :     it->second.triggerBuffer.AddTimestamp(nTimestamp);
     676             : 
     677           0 :     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           0 :     it->second.fStatusOK = true;
     683           0 : }
     684             : 
     685           0 : bool CGovernanceManager::MasternodeRateCheck(const CGovernanceObject& govobj, bool fUpdateFailStatus)
     686             : {
     687           0 :     LOCK(cs_store);
     688             :     bool fRateCheckBypassed;
     689           0 :     return MasternodeRateCheck(govobj, fUpdateFailStatus, true, fRateCheckBypassed);
     690           0 : }
     691             : 
     692           0 : bool CGovernanceManager::MasternodeRateCheck(const CGovernanceObject& govobj, bool fUpdateFailStatus, bool fForce, bool& fRateCheckBypassed)
     693             : {
     694           0 :     AssertLockHeld(cs_store);
     695             : 
     696           0 :     fRateCheckBypassed = false;
     697             : 
     698           0 :     if (!m_mn_sync.IsSynced() || !fRateChecksEnabled) {
     699           0 :         return true;
     700             :     }
     701             : 
     702           0 :     if (govobj.GetObjectType() != GovernanceObject::TRIGGER) {
     703           0 :         return true;
     704             :     }
     705             : 
     706           0 :     const COutPoint& masternodeOutpoint = govobj.GetMasternodeOutpoint();
     707           0 :     int64_t nTimestamp = govobj.GetCreationTime();
     708           0 :     int64_t nNow = GetAdjustedTime();
     709           0 :     int64_t nSuperblockCycleSeconds = Params().GetConsensus().nSuperblockCycle * Params().GetConsensus().nPowTargetSpacing;
     710             : 
     711           0 :     std::string strHash = govobj.GetHash().ToString();
     712             : 
     713           0 :     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           0 :     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           0 :     auto it = mapLastMasternodeObject.find(masternodeOutpoint);
     726           0 :     if (it == mapLastMasternodeObject.end()) return true;
     727             : 
     728           0 :     if (it->second.fStatusOK && !fForce) {
     729           0 :         fRateCheckBypassed = true;
     730           0 :         return true;
     731             :     }
     732             : 
     733             :     // Allow 1 trigger per mn per cycle, with a small fudge factor
     734           0 :     double dMaxRate = 2 * 1.1 / double(nSuperblockCycleSeconds);
     735             : 
     736             :     // Temporary copy to check rate after new timestamp is added
     737           0 :     CRateCheckBuffer buffer = it->second.triggerBuffer;
     738             : 
     739           0 :     buffer.AddTimestamp(nTimestamp);
     740           0 :     double dRate = buffer.GetRate();
     741             : 
     742           0 :     if (dRate < dMaxRate) {
     743           0 :         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           0 : }
     755             : 
     756           0 : bool CGovernanceManager::ProcessVoteAndRelay(const CGovernanceVote& vote, CGovernanceException& exception, CConnman& connman)
     757             : {
     758           0 :     AssertLockNotHeld(cs_store);
     759           0 :     AssertLockNotHeld(cs_relay);
     760           0 :     uint256 hashToRequest; // Ignored for local votes (no peer to request from)
     761           0 :     bool fOK = ProcessVote(vote, exception, hashToRequest);
     762           0 :     if (fOK) {
     763           0 :         RelayVote(vote);
     764           0 :     }
     765           0 :     return fOK;
     766             : }
     767             : 
     768           0 : bool CGovernanceManager::ProcessVote(const CGovernanceVote& vote, CGovernanceException& exception,
     769             :                                      uint256& hashToRequest)
     770             : {
     771           0 :     AssertLockNotHeld(cs_store);
     772           0 :     hashToRequest = uint256{};
     773             : 
     774           0 :     LOCK(cs_store);
     775           0 :     uint256 nHashVote = vote.GetHash();
     776           0 :     uint256 nHashGovobj = vote.GetParentHash();
     777             : 
     778           0 :     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           0 :     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           0 :     auto it = mapObjects.find(nHashGovobj);
     793           0 :     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           0 :     auto& govobj = *Assert(it->second);
     806             : 
     807           0 :     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           0 :     bool fOk = govobj.ProcessVote(m_mn_metaman, fRateChecksEnabled, m_dmnman.GetListAtChainTip(), vote, exception);
     814           0 :     if (fOk) {
     815           0 :         fOk = cmapVoteToObject.Insert(nHashVote, it->second);
     816           0 :     } else if (exception.GetType() == GOVERNANCE_EXCEPTION_PERMANENT_ERROR && exception.GetNodePenalty() == 20) {
     817           0 :         cmapInvalidVotes.Insert(nHashVote, vote);
     818           0 :     }
     819           0 :     return fOk;
     820           0 : }
     821             : 
     822           0 : void CGovernanceManager::CheckPostponedObjects()
     823             : {
     824           0 :     AssertLockHeld(::cs_main);
     825           0 :     AssertLockHeld(cs_store);
     826           0 :     AssertLockNotHeld(cs_relay);
     827           0 :     if (!m_mn_sync.IsSynced()) return;
     828             : 
     829             :     // Check postponed proposals
     830           0 :     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           0 :     int64_t nNow = GetAdjustedTime();
     858           0 :     int64_t nSuperblockCycleSeconds = Params().GetConsensus().nSuperblockCycle * Params().GetConsensus().nPowTargetSpacing;
     859             : 
     860           0 :     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           0 : }
     889             : 
     890           0 : CBloomFilter CGovernanceManager::GetVoteBloomFilter(const uint256& nHash) const
     891             : {
     892           0 :     LOCK(cs_store);
     893             : 
     894           0 :     auto pObj = FindConstGovernanceObjectInternal(nHash);
     895           0 :     if (!pObj) {
     896           0 :         return CBloomFilter{};
     897             :     }
     898             : 
     899           0 :     CBloomFilter filter(Params().GetConsensus().nGovernanceFilterElements, GOVERNANCE_FILTER_FP_RATE,
     900           0 :                         GetRand<int>(/*nMax=*/999999), BLOOM_UPDATE_ALL);
     901             : 
     902           0 :     std::vector<CGovernanceVote> vecVotes = WITH_LOCK(pObj->cs, return pObj->GetVoteFile().GetVotes());
     903           0 :     for (const auto& vote : vecVotes) {
     904           0 :         filter.insert(vote.GetHash());
     905             :     }
     906             : 
     907           0 :     return filter;
     908           0 : }
     909             : 
     910           0 : CDeterministicMNManager& CGovernanceManager::GetMNManager() { return m_dmnman; }
     911             : 
     912           0 : 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           0 :     std::vector<uint256> vTriggerObjHashes;
     916           0 :     std::vector<uint256> vOtherObjHashes;
     917             :     {
     918           0 :         LOCK(cs_store);
     919             : 
     920           0 :         for (const auto& [nHash, govobj] : mapObjects) {
     921           0 :             if (Assert(govobj)->IsSetCachedDelete()) continue;
     922           0 :             if (mapAskedRecently.count(nHash)) {
     923           0 :                 for (auto it = mapAskedRecently[nHash].begin(); it != mapAskedRecently[nHash].end();) {
     924           0 :                     if (it->second < nNow) {
     925           0 :                         mapAskedRecently[nHash].erase(it++);
     926           0 :                     } else {
     927           0 :                         ++it;
     928             :                     }
     929             :                 }
     930           0 :                 if (mapAskedRecently[nHash].size() >= nPeersPerHashMax) continue;
     931           0 :             }
     932             : 
     933           0 :             if (govobj->GetObjectType() == GovernanceObject::TRIGGER) {
     934           0 :                 vTriggerObjHashes.push_back(nHash);
     935           0 :             } else {
     936           0 :                 vOtherObjHashes.push_back(nHash);
     937             :             }
     938             :         }
     939           0 :     }
     940           0 :     return {vTriggerObjHashes, vOtherObjHashes};
     941           0 : }
     942             : 
     943           0 : bool CGovernanceManager::AcceptMessage(const uint256& nHash)
     944             : {
     945           0 :     AssertLockNotHeld(cs_store);
     946           0 :     LOCK(cs_store);
     947           0 :     auto it = m_requested_hash_time.find(nHash);
     948           0 :     if (it == m_requested_hash_time.end()) {
     949             :         // We never requested this
     950           0 :         return false;
     951             :     }
     952             :     // Only accept one response
     953           0 :     m_requested_hash_time.erase(it);
     954           0 :     return true;
     955           0 : }
     956             : 
     957           0 : void CGovernanceManager::RebuildIndexes()
     958             : {
     959           0 :     AssertLockHeld(cs_store);
     960             : 
     961           0 :     cmapVoteToObject.Clear();
     962           0 :     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           0 : }
     970             : 
     971           0 : void CGovernanceManager::InitOnLoad()
     972             : {
     973             :     {
     974           0 :     LOCK(cs_store);
     975           0 :     const auto start{SteadyClock::now()};
     976           0 :     LogPrintf("Preparing masternode indexes and governance triggers...\n");
     977           0 :     RebuildIndexes();
     978             : 
     979           0 :     const int64_t nNow = GetTime<std::chrono::seconds>().count();
     980           0 :     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           0 :     LogPrintf("Masternode indexes and governance triggers prepared  %dms\n",
     987             :               Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
     988           0 :     }
     989           0 :     LogPrintf("     %s\n", ToString());
     990           0 : }
     991             : 
     992           0 : void GovernanceStore::Clear()
     993             : {
     994           0 :     LOCK(cs_store);
     995           0 :     mapObjects.clear();
     996           0 :     mapErasedGovernanceObjects.clear();
     997           0 :     cmapInvalidVotes.Clear();
     998           0 :     cmmapOrphanVotes.Clear();
     999           0 :     mapLastMasternodeObject.clear();
    1000           0 :     lastMNListForVotingKeys = std::make_shared<CDeterministicMNList>();
    1001           0 : }
    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           0 : std::string GovernanceStore::ToString() const
    1019             : {
    1020           0 :     LOCK(cs_store);
    1021             : 
    1022           0 :     int nProposalCount = 0;
    1023           0 :     int nTriggerCount = 0;
    1024           0 :     int nOtherCount = 0;
    1025             : 
    1026           0 :     for (const auto& [_, govobj] : mapObjects) {
    1027           0 :         switch (Assert(govobj)->GetObjectType()) {
    1028             :         case GovernanceObject::PROPOSAL:
    1029           0 :             nProposalCount++;
    1030           0 :             break;
    1031             :         case GovernanceObject::TRIGGER:
    1032           0 :             nTriggerCount++;
    1033           0 :             break;
    1034             :         default:
    1035           0 :             nOtherCount++;
    1036           0 :             break;
    1037             :         }
    1038             :     }
    1039             : 
    1040           0 :     return strprintf("Governance Objects: %d (Proposals: %d, Triggers: %d, Other: %d; Erased: %d)",
    1041           0 :         (int)mapObjects.size(),
    1042           0 :         nProposalCount, nTriggerCount, nOtherCount, (int)mapErasedGovernanceObjects.size());
    1043           0 : }
    1044             : 
    1045           0 : std::string CGovernanceManager::ToString() const
    1046             : {
    1047           0 :     AssertLockNotHeld(cs_store);
    1048           0 :     return strprintf("%s, Votes: %d", GovernanceStore::ToString(), (int)cmapVoteToObject.GetSize());
    1049           0 : }
    1050             : 
    1051           0 : void CGovernanceManager::UpdatedBlockTip(const CBlockIndex* pindex)
    1052             : {
    1053           0 :     AssertLockNotHeld(cs_store);
    1054           0 :     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           0 :     if (!pindex) {
    1061           0 :         return;
    1062             :     }
    1063             : 
    1064           0 :     nCachedBlockHeight = pindex->nHeight;
    1065           0 :     LogPrint(BCLog::GOBJECT, "CGovernanceManager::UpdatedBlockTip -- nCachedBlockHeight: %d\n", nCachedBlockHeight);
    1066             : 
    1067           0 :     LOCK2(::cs_main, cs_store);
    1068           0 :     if (DeploymentDIP0003Enforced(pindex->nHeight, Params().GetConsensus())) {
    1069           0 :         RemoveInvalidVotes();
    1070           0 :     }
    1071             : 
    1072           0 :     CheckPostponedObjects();
    1073             : 
    1074           0 :     m_superblocks.ExecuteBestSuperblock(m_dmnman.GetListAtChainTip(), pindex->nHeight);
    1075           0 : }
    1076             : 
    1077           0 : std::vector<uint256> CGovernanceManager::GetOrphanVoteObjectHashes()
    1078             : {
    1079           0 :     LOCK(cs_store);
    1080             : 
    1081           0 :     int64_t nNow = GetTime<std::chrono::seconds>().count();
    1082             : 
    1083             :     // Clean up expired orphan votes
    1084           0 :     const vote_cmm_t::list_t& items = cmmapOrphanVotes.GetItemList();
    1085           0 :     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           0 :     std::vector<uint256> vecHashesFiltered;
    1096           0 :     std::vector<uint256> vecHashes;
    1097           0 :     cmmapOrphanVotes.GetKeys(vecHashes);
    1098           0 :     for (const uint256& nHash : vecHashes) {
    1099           0 :         if (mapObjects.find(nHash) == mapObjects.end()) {
    1100           0 :             vecHashesFiltered.push_back(nHash);
    1101           0 :         }
    1102             :     }
    1103             : 
    1104           0 :     return vecHashesFiltered;
    1105           0 : }
    1106             : 
    1107           0 : void CGovernanceManager::RemoveInvalidVotes()
    1108             : {
    1109           0 :     AssertLockHeld(cs_store);
    1110             : 
    1111           0 :     if (!m_mn_sync.IsSynced()) {
    1112           0 :         return;
    1113             :     }
    1114             : 
    1115           0 :     const auto tip_mn_list = m_dmnman.GetListAtChainTip();
    1116           0 :     auto diff = lastMNListForVotingKeys->BuildDiff(tip_mn_list);
    1117             : 
    1118           0 :     std::vector<COutPoint> changedKeyMNs;
    1119           0 :     for (const auto& [internalId, pdmnState] : diff.updatedMNs) {
    1120           0 :         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           0 :         assert(oldDmn);
    1124           0 :         if ((pdmnState.fields & CDeterministicMNStateDiff::Field_keyIDVoting) && pdmnState.state.keyIDVoting != oldDmn->pdmnState->keyIDVoting) {
    1125           0 :             changedKeyMNs.emplace_back(oldDmn->collateralOutpoint);
    1126           0 :         } else if ((pdmnState.fields & CDeterministicMNStateDiff::Field_pubKeyOperator) && pdmnState.state.pubKeyOperator != oldDmn->pdmnState->pubKeyOperator) {
    1127           0 :             changedKeyMNs.emplace_back(oldDmn->collateralOutpoint);
    1128           0 :         }
    1129           0 :     }
    1130           0 :     for (const auto& id : diff.removedMns) {
    1131           0 :         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           0 :         assert(oldDmn);
    1135           0 :         changedKeyMNs.emplace_back(oldDmn->collateralOutpoint);
    1136           0 :     }
    1137             : 
    1138           0 :     for (const auto& outpoint : changedKeyMNs) {
    1139           0 :         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           0 :     lastMNListForVotingKeys = std::make_shared<CDeterministicMNList>(tip_mn_list);
    1155           0 : }
    1156             : 
    1157           0 : std::vector<std::shared_ptr<const CGovernanceObject>> CGovernanceManager::GetApprovedProposals(
    1158             :     const CDeterministicMNList& tip_mn_list)
    1159             : {
    1160           0 :     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           0 :     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           0 :     const int nWeightedMnCount = tip_mn_list.GetCounts().m_valid_weighted;
    1168           0 :     const int nAbsVoteReq = std::max(Params().GetConsensus().nGovernanceMinQuorum, nWeightedMnCount / 10);
    1169             : 
    1170           0 :     LOCK(cs_store);
    1171           0 :     for (const auto& [_, govobj] : mapObjects) {
    1172             :         // Skip all non-proposals objects
    1173           0 :         if (Assert(govobj)->GetObjectType() != GovernanceObject::PROPOSAL) continue;
    1174             :         // Skip non-passing proposals
    1175           0 :         const int absYesCount = govobj->GetAbsoluteYesCount(tip_mn_list, VOTE_SIGNAL_FUNDING);
    1176           0 :         if (absYesCount < nAbsVoteReq) continue;
    1177           0 :         ret.emplace_back(govobj);
    1178             :     }
    1179             : 
    1180             :     // Sort approved proposals by absolute Yes votes descending
    1181           0 :     std::sort(ret.begin(), ret.end(), [&tip_mn_list](auto& lhs, auto& rhs) {
    1182           0 :         const auto lhs_yes = lhs->GetAbsoluteYesCount(tip_mn_list, VOTE_SIGNAL_FUNDING);
    1183           0 :         const auto rhs_yes = rhs->GetAbsoluteYesCount(tip_mn_list, VOTE_SIGNAL_FUNDING);
    1184           0 :         return lhs_yes == rhs_yes ? UintToArith256(lhs->GetHash()) > UintToArith256(rhs->GetHash()) : lhs_yes > rhs_yes;
    1185             :     });
    1186             : 
    1187           0 :     return ret;
    1188           0 : }

Generated by: LCOV version 1.16