LCOV - code coverage report
Current view: top level - src/governance - net_governance.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 109 142 76.8 %
Date: 2026-06-25 07:23:43 Functions: 9 9 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2024-2025 The Dash Core developers
       2             : // Distributed under the MIT software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <governance/net_governance.h>
       6             : 
       7             : #include <chainparams.h>
       8             : #include <common/bloom.h>
       9             : #include <evo/deterministicmns.h>
      10             : #include <governance/governance.h>
      11             : #include <governance/object.h>
      12             : #include <logging.h>
      13             : #include <masternode/sync.h>
      14             : #include <net.h>
      15             : #include <netfulfilledman.h>
      16             : #include <netmessagemaker.h>
      17             : #include <scheduler.h>
      18             : 
      19             : class CConnman;
      20             : 
      21        2831 : void NetGovernance::Schedule(CScheduler& scheduler)
      22             : {
      23             :     // Code below is meant to be running only if governance validation is enabled
      24             :     //
      25        2831 :     if (!m_gov_manager.IsValid()) return;
      26        5610 :     scheduler.scheduleEvery(
      27        6071 :         [this]() -> void {
      28        3266 :             if (!m_node_sync.IsSynced()) return;
      29             : 
      30             :             // Request governance objects for orphan votes
      31        3202 :             auto vecOrphanHashes = m_gov_manager.GetOrphanVoteObjectHashes();
      32        3202 :             if (!vecOrphanHashes.empty()) {
      33           0 :                 LogPrint(BCLog::GOBJECT, "NetGovernance::Schedule -- requesting %d orphan objects\n",
      34             :                          vecOrphanHashes.size());
      35           0 :                 const CConnman::NodesSnapshot snap{m_connman, CConnman::FullyConnectedOnly};
      36           0 :                 for (const uint256& nHash : vecOrphanHashes) {
      37           0 :                     for (CNode* pnode : snap.Nodes()) {
      38           0 :                         if (!pnode->CanRelay()) continue;
      39           0 :                         CNetMsgMaker msgMaker(pnode->GetCommonVersion());
      40           0 :                         CBloomFilter filter; // Empty filter - we want the object, not votes
      41           0 :                         m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::MNGOVERNANCESYNC, nHash, filter));
      42           0 :                     }
      43             :                 }
      44           0 :             }
      45             : 
      46             :             // CHECK AND REMOVE - REPROCESS GOVERNANCE OBJECTS
      47        3202 :             m_gov_manager.CheckAndRemove();
      48        3266 :         },
      49        2805 :         std::chrono::minutes{5});
      50             : 
      51        5610 :     scheduler.scheduleEvery(
      52       98057 :         [this]() -> void {
      53       95252 :             auto relay_invs = m_gov_manager.FetchRelayInventory();
      54       97013 :             for (const auto& inv : relay_invs) {
      55        1761 :                 m_peer_manager->PeerRelayInv(inv);
      56             :             }
      57       95252 :         },
      58             :         // Tests need tighter timings to avoid timeouts, use more relaxed pacing otherwise
      59        2805 :         Params().IsMockableChain() ? std::chrono::seconds{1} : std::chrono::seconds{5});
      60        2831 : }
      61             : 
      62       96785 : void NetGovernance::ProcessMessage(CNode& peer, const std::string& msg_type, CDataStream& vRecv)
      63             : {
      64       96785 :     if (!m_gov_manager.IsValid()) return;
      65       96785 :     if (!m_node_sync.IsBlockchainSynced()) return;
      66             : 
      67             :     // ANOTHER USER IS ASKING US TO HELP THEM SYNC GOVERNANCE OBJECT DATA
      68       96673 :     if (msg_type == NetMsgType::MNGOVERNANCESYNC) {
      69             :         // Ignore such requests until we are fully synced.
      70             :         // We could start processing this after masternode list is synced
      71             :         // but this is a heavy one so it's better to finish sync first.
      72         765 :         if (!m_node_sync.IsSynced()) return;
      73             : 
      74         694 :         uint256 nProp;
      75         694 :         CBloomFilter filter;
      76         694 :         vRecv >> nProp;
      77         694 :         vRecv >> filter;
      78             : 
      79         694 :         LogPrint(BCLog::GOBJECT, "MNGOVERNANCESYNC -- syncing governance objects to our peer %s\n", peer.GetLogString());
      80         694 :         if (nProp == uint256()) {
      81             :             // Full sync of all governance objects
      82          88 :             assert(m_netfulfilledman.IsValid());
      83          88 :             if (m_netfulfilledman.HasFulfilledRequest(peer.addr, NetMsgType::MNGOVERNANCESYNC)) {
      84             :                 // Asking for the whole list multiple times in a short period of time is no good
      85           0 :                 LogPrint(BCLog::GOBJECT, "MNGOVERNANCESYNC -- peer already asked me for the list\n");
      86           0 :                 m_peer_manager->PeerMisbehaving(peer.GetId(), 20);
      87           0 :                 return;
      88             :             }
      89          88 :             m_netfulfilledman.AddFulfilledRequest(peer.addr, NetMsgType::MNGOVERNANCESYNC);
      90             : 
      91          88 :             auto invs = m_gov_manager.GetSyncableObjectInvs();
      92          88 :             LogPrint(BCLog::GOBJECT, "MNGOVERNANCESYNC -- syncing %d objects to peer=%d\n", invs.size(), peer.GetId());
      93             : 
      94          88 :             CNetMsgMaker msgMaker(peer.GetCommonVersion());
      95          88 :             m_connman.PushMessage(&peer, msgMaker.Make(NetMsgType::SYNCSTATUSCOUNT, MASTERNODE_SYNC_GOVOBJ,
      96          88 :                                                        static_cast<int>(invs.size())));
      97         268 :             for (const auto& inv : invs) {
      98         180 :                 m_peer_manager->PeerRelayInv(inv);
      99             :             }
     100          88 :         } else {
     101             :             // Sync votes for a specific governance object
     102         606 :             auto invs = m_gov_manager.GetSyncableVoteInvs(nProp, filter);
     103         606 :             LogPrint(BCLog::GOBJECT, "MNGOVERNANCESYNC -- syncing %d votes for %s to peer=%d\n", invs.size(),
     104             :                      nProp.ToString(), peer.GetId());
     105             : 
     106         606 :             CNetMsgMaker msgMaker(peer.GetCommonVersion());
     107         606 :             m_connman.PushMessage(&peer, msgMaker.Make(NetMsgType::SYNCSTATUSCOUNT, MASTERNODE_SYNC_GOVOBJ_VOTE,
     108         606 :                                                        static_cast<int>(invs.size())));
     109         638 :             for (const auto& inv : invs) {
     110          32 :                 m_peer_manager->PeerRelayInv(inv);
     111             :             }
     112         606 :         }
     113         694 :     }
     114             :     // A NEW GOVERNANCE OBJECT HAS ARRIVED
     115       95908 :     else if (msg_type == NetMsgType::MNGOVERNANCEOBJECT) {
     116             :         // MAKE SURE WE HAVE A VALID REFERENCE TO THE TIP BEFORE CONTINUING
     117         248 :         CGovernanceObject govobj;
     118         248 :         vRecv >> govobj;
     119             : 
     120         248 :         uint256 nHash = govobj.GetHash();
     121             : 
     122         496 :         WITH_LOCK(::cs_main, m_peer_manager->PeerEraseObjectRequest(peer.GetId(), CInv{MSG_GOVERNANCE_OBJECT, nHash}));
     123             : 
     124         248 :         if (!m_node_sync.IsBlockchainSynced()) {
     125           0 :             LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- masternode list not synced\n");
     126           0 :             return;
     127             :         }
     128             : 
     129         248 :         std::string strHash = nHash.ToString();
     130             : 
     131         248 :         LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- Received object: %s\n", strHash);
     132             : 
     133         248 :         if (!m_gov_manager.AcceptMessage(nHash)) {
     134           0 :             LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- Received unrequested object: %s\n", strHash);
     135           0 :             return;
     136             :         }
     137             : 
     138         496 :         if (!WITH_LOCK(::cs_main, return m_gov_manager.ProcessObject(peer.GetLogString(), nHash, govobj))) {
     139             :             // apply node's ban score
     140           0 :             m_peer_manager->PeerMisbehaving(peer.GetId(), 20);
     141           0 :         }
     142         248 :     }
     143             : 
     144             :     // A NEW GOVERNANCE OBJECT VOTE HAS ARRIVED
     145       95660 :     else if (msg_type == NetMsgType::MNGOVERNANCEOBJECTVOTE) {
     146        1224 :         CGovernanceVote vote;
     147        1224 :         vRecv >> vote;
     148             : 
     149        1224 :         uint256 nHash = vote.GetHash();
     150             : 
     151        2448 :         WITH_LOCK(::cs_main, m_peer_manager->PeerEraseObjectRequest(peer.GetId(), CInv{MSG_GOVERNANCE_OBJECT_VOTE, nHash}));
     152             : 
     153             :         // Ignore such messages until masternode list is synced
     154        1224 :         if (!m_node_sync.IsBlockchainSynced()) {
     155           0 :             LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECTVOTE -- masternode list not synced\n");
     156           0 :             return;
     157             :         }
     158             : 
     159        1224 :         const auto tip_mn_list = m_gov_manager.GetMNManager().GetListAtChainTip();
     160        1224 :         LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECTVOTE -- Received vote: %s\n", vote.ToString(tip_mn_list));
     161             : 
     162        1224 :         std::string strHash = nHash.ToString();
     163             : 
     164        1224 :         if (!m_gov_manager.AcceptMessage(nHash)) {
     165           0 :             LogPrint(BCLog::GOBJECT, /* Continued */
     166             :                      "MNGOVERNANCEOBJECTVOTE -- Received unrequested vote object: %s, hash: %s, peer = %d\n",
     167             :                      vote.ToString(tip_mn_list), strHash, peer.GetId());
     168           0 :             return;
     169             :         }
     170             : 
     171        1224 :         CGovernanceException exception;
     172        1224 :         uint256 hashToRequest;
     173        1224 :         if (m_gov_manager.ProcessVote(vote, exception, hashToRequest)) {
     174        1224 :             LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECTVOTE -- %s new\n", strHash);
     175        1224 :             m_node_sync.BumpAssetLastTime("MNGOVERNANCEOBJECTVOTE");
     176             : 
     177        1224 :             if (!m_node_sync.IsSynced()) {
     178          11 :                 LogPrint(BCLog::GOBJECT, "%s -- won't relay until fully synced\n", __func__);
     179          11 :                 return;
     180             :             }
     181        1213 :             auto dmn = tip_mn_list.GetMNByCollateral(vote.GetMasternodeOutpoint());
     182        1213 :             if (!dmn) {
     183           0 :                 return;
     184             :             }
     185        1213 :             m_gov_manager.RelayVote(vote);
     186             :             // TODO: figure out why immediate sending of inventory doesn't work here!
     187             :             // m_peer_manager->PeerRelayInv(CInv{MSG_GOVERNANCE_OBJECT_VOTE, nHash});
     188        1213 :         } else {
     189           0 :             LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECTVOTE -- Rejected vote, error = %s\n", exception.what());
     190           0 :             if (hashToRequest != uint256()) {
     191             :                 // Orphan vote - request the missing governance object
     192           0 :                 CNetMsgMaker msgMaker(peer.GetCommonVersion());
     193           0 :                 CBloomFilter filter; // Empty filter - we just want the object, not votes
     194           0 :                 m_connman.PushMessage(&peer, msgMaker.Make(NetMsgType::MNGOVERNANCESYNC, hashToRequest, filter));
     195           0 :             }
     196           0 :             if ((exception.GetNodePenalty() != 0) && m_node_sync.IsSynced()) {
     197           0 :                 m_peer_manager->PeerMisbehaving(peer.GetId(), exception.GetNodePenalty());
     198           0 :             }
     199             :         }
     200        1224 :     }
     201       96785 : }
     202             : 
     203       33825 : bool NetGovernance::AlreadyHave(const CInv& inv)
     204             : {
     205       33825 :     if (inv.type != MSG_GOVERNANCE_OBJECT && inv.type != MSG_GOVERNANCE_OBJECT_VOTE) {
     206       30347 :         return false;
     207             :     }
     208             :     // When governance isn't loaded (e.g. -disablegovernance), claim we already have
     209             :     // the item so we don't fetch or track it. ConfirmInventoryRequest would otherwise
     210             :     // grow m_requested_hash_time unbounded since CheckAndRemove never runs in that mode.
     211        3478 :     if (!m_gov_manager.IsValid()) return true;
     212        3478 :     return !m_gov_manager.ConfirmInventoryRequest(inv);
     213       33825 : }
     214             : 
     215        2074 : bool NetGovernance::ProcessGetData(CNode& pfrom, const CInv& inv, CConnman& connman, const CNetMsgMaker& msgMaker)
     216             : {
     217        2074 :     if (inv.type == MSG_GOVERNANCE_OBJECT) {
     218         248 :         if (!m_gov_manager.HaveObjectForHash(inv.hash)) return false;
     219         248 :         CDataStream ss(SER_NETWORK, pfrom.GetCommonVersion());
     220         248 :         ss.reserve(1000);
     221         248 :         if (!m_gov_manager.SerializeObjectForHash(inv.hash, ss)) return false;
     222         248 :         connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::MNGOVERNANCEOBJECT, ss));
     223         248 :         return true;
     224         248 :     }
     225        1826 :     if (inv.type == MSG_GOVERNANCE_OBJECT_VOTE) {
     226        1224 :         if (!m_gov_manager.HaveVoteForHash(inv.hash)) return false;
     227        1224 :         CDataStream ss(SER_NETWORK, pfrom.GetCommonVersion());
     228        1224 :         ss.reserve(1000);
     229        1224 :         if (!m_gov_manager.SerializeVoteForHash(inv.hash, ss)) return false;
     230        1224 :         connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::MNGOVERNANCEOBJECTVOTE, ss));
     231        1224 :         return true;
     232        1224 :     }
     233         602 :     return false;
     234        2074 : }

Generated by: LCOV version 1.16