LCOV - code coverage report
Current view: top level - src/tiertwo - net_masternodes.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 175 215 81.4 %
Date: 2025-02-23 09:33:43 Functions: 22 26 84.6 %

          Line data    Source code
       1             : // Copyright (c) 2020 The Dash developers
       2             : // Copyright (c) 2021-2022 The PIVX Core developers
       3             : // Distributed under the MIT software license, see the accompanying
       4             : // file COPYING or https://www.opensource.org/licenses/mit-license.php.
       5             : 
       6             : #include "tiertwo/net_masternodes.h"
       7             : 
       8             : #include "chainparams.h"
       9             : #include "evo/deterministicmns.h"
      10             : #include "netmessagemaker.h"
      11             : #include "scheduler.h"
      12             : #include "tiertwo/masternode_meta_manager.h" // for g_mmetaman
      13             : #include "tiertwo/tiertwo_sync_state.h"
      14             : 
      15         484 : TierTwoConnMan::TierTwoConnMan(CConnman* _connman) : connman(_connman) {}
      16         485 : TierTwoConnMan::~TierTwoConnMan() { connman = nullptr; }
      17             : 
      18        2485 : void TierTwoConnMan::setQuorumNodes(Consensus::LLMQType llmqType,
      19             :                                     const uint256& quorumHash,
      20             :                                     const std::set<uint256>& proTxHashes)
      21             : {
      22        2485 :     LOCK(cs_vPendingMasternodes);
      23        2485 :     auto it = masternodeQuorumNodes.emplace(QuorumTypeAndHash(llmqType, quorumHash), proTxHashes);
      24        2485 :     if (!it.second) {
      25        2485 :         it.first->second = proTxHashes;
      26             :     }
      27        2485 : }
      28             : 
      29        4496 : std::set<uint256> TierTwoConnMan::getQuorumNodes(Consensus::LLMQType llmqType)
      30             : {
      31        4496 :     LOCK(cs_vPendingMasternodes);
      32        4496 :     std::set<uint256> result;
      33        7682 :     for (const auto& p : masternodeQuorumNodes) {
      34        3186 :         if (p.first.first != llmqType) {
      35           0 :             continue;
      36             :         }
      37        6372 :         result.emplace(p.first.second);
      38             :     }
      39        8992 :     return result;
      40             : }
      41             : 
      42           0 : std::set<NodeId> TierTwoConnMan::getQuorumNodes(Consensus::LLMQType llmqType, uint256 quorumHash)
      43             : {
      44           0 :     LOCK(cs_vPendingMasternodes);
      45           0 :     std::set<NodeId> result;
      46           0 :     auto it = masternodeQuorumRelayMembers.find(std::make_pair(llmqType, quorumHash));
      47           0 :     if (it == masternodeQuorumRelayMembers.end()) {
      48           0 :         return {};
      49             :     }
      50           0 :     connman->ForEachNode([&](CNode* pnode) {
      51           0 :         if (pnode->fDisconnect) {
      52             :             return;
      53             :         }
      54           0 :         if (!it->second.count(pnode->verifiedProRegTxHash)) {
      55           0 :             return;
      56             :         }
      57           0 :         result.emplace(pnode->GetId());
      58             :     });
      59           0 :     return result;
      60             : }
      61             : 
      62        2483 : bool TierTwoConnMan::hasQuorumNodes(Consensus::LLMQType llmqType, const uint256& quorumHash)
      63             : {
      64        2483 :     LOCK(cs_vPendingMasternodes);
      65        4966 :     return masternodeQuorumNodes.count(QuorumTypeAndHash(llmqType, quorumHash));
      66             : }
      67             : 
      68          33 : void TierTwoConnMan::removeQuorumNodes(Consensus::LLMQType llmqType, const uint256& quorumHash)
      69             : {
      70          33 :     LOCK(cs_vPendingMasternodes);
      71          33 :     masternodeQuorumNodes.erase(std::make_pair(llmqType, quorumHash));
      72          33 : }
      73             : 
      74        3563 : void TierTwoConnMan::setMasternodeQuorumRelayMembers(Consensus::LLMQType llmqType, const uint256& quorumHash, const std::set<uint256>& proTxHashes)
      75             : {
      76        3563 :     LOCK(cs_vPendingMasternodes);
      77        3563 :     auto it = masternodeQuorumRelayMembers.emplace(std::make_pair(llmqType, quorumHash), proTxHashes);
      78        3563 :     if (!it.second) {
      79        3474 :         it.first->second = proTxHashes;
      80             :     }
      81             : 
      82             :     // Update existing connections
      83        3563 :     connman->ForEachNode([&](CNode* pnode) {
      84       33889 :         if (!pnode->m_masternode_iqr_connection && isMasternodeQuorumRelayMember(pnode->verifiedProRegTxHash)) {
      85             :             // Tell our peer that we're interested in plain LLMQ recovered signatures.
      86             :             // Otherwise, the peer would only announce/send messages resulting from QRECSIG,
      87             :             // future e.g. tx locks or chainlocks. SPV and regular full nodes should not send
      88             :             // this message as they are usually only interested in the higher level messages.
      89           8 :             CNetMsgMaker msgMaker(pnode->GetSendVersion());
      90           8 :             connman->PushMessage(pnode, msgMaker.Make(NetMsgType::QSENDRECSIGS, true));
      91           8 :             pnode->m_masternode_iqr_connection = true;
      92             :         }
      93       33889 :     });
      94        3563 : }
      95             : 
      96           0 : bool TierTwoConnMan::isMasternodeQuorumNode(const CNode* pnode)
      97             : {
      98             :     // Let's see if this is an outgoing connection to an address that is known to be a masternode
      99             :     // We however only need to know this if the node did not authenticate itself as a MN yet
     100           0 :     uint256 assumedProTxHash;
     101           0 :     if (pnode->verifiedProRegTxHash.IsNull() && !pnode->fInbound) {
     102           0 :         auto mnList = deterministicMNManager->GetListAtChainTip();
     103           0 :         auto dmn = mnList.GetMNByService(pnode->addr);
     104           0 :         if (dmn == nullptr) {
     105             :             // This is definitely not a masternode
     106           0 :             return false;
     107             :         }
     108           0 :         assumedProTxHash = dmn->proTxHash;
     109             :     }
     110             : 
     111           0 :     LOCK(cs_vPendingMasternodes);
     112           0 :     for (const auto& quorumConn : masternodeQuorumNodes) {
     113           0 :         if (!pnode->verifiedProRegTxHash.IsNull()) {
     114           0 :             if (quorumConn.second.count(pnode->verifiedProRegTxHash)) {
     115           0 :                 return true;
     116             :             }
     117           0 :         } else if (!assumedProTxHash.IsNull()) {
     118           0 :             if (quorumConn.second.count(assumedProTxHash)) {
     119           0 :                 return true;
     120             :             }
     121             :         }
     122             :     }
     123           0 :     return false;
     124             : }
     125             : 
     126       21075 : bool TierTwoConnMan::isMasternodeQuorumRelayMember(const uint256& protxHash)
     127             : {
     128       42150 :     if (protxHash.IsNull()) {
     129             :         return false;
     130             :     }
     131         133 :     LOCK(cs_vPendingMasternodes);
     132         180 :     for (const auto& p : masternodeQuorumRelayMembers) {
     133         202 :         if (p.second.count(protxHash) > 0) {
     134         108 :             return true;
     135             :         }
     136             :     }
     137          25 :     return false;
     138             : }
     139             : 
     140           1 : bool TierTwoConnMan::addPendingMasternode(const uint256& proTxHash)
     141             : {
     142           2 :     LOCK(cs_vPendingMasternodes);
     143           1 :     if (std::find(vPendingMasternodes.begin(), vPendingMasternodes.end(), proTxHash) != vPendingMasternodes.end()) {
     144             :         return false;
     145             :     }
     146           1 :     vPendingMasternodes.emplace_back(proTxHash);
     147             :     return true;
     148             : }
     149             : 
     150          63 : void TierTwoConnMan::addPendingProbeConnections(const std::set<uint256>& proTxHashes)
     151             : {
     152          63 :     LOCK(cs_vPendingMasternodes);
     153          63 :     masternodePendingProbes.insert(proTxHashes.begin(), proTxHashes.end());
     154          63 : }
     155             : 
     156           2 : void TierTwoConnMan::clear()
     157             : {
     158           2 :     LOCK(cs_vPendingMasternodes);
     159           2 :     masternodeQuorumNodes.clear();
     160           2 :     masternodeQuorumRelayMembers.clear();
     161           2 :     vPendingMasternodes.clear();
     162           2 :     masternodePendingProbes.clear();
     163           2 : }
     164             : 
     165         355 : void TierTwoConnMan::start(CScheduler& scheduler, const TierTwoConnMan::Options& options)
     166             : {
     167             :     // Must be started after connman
     168         355 :     assert(connman);
     169         355 :     interruptNet.reset();
     170             : 
     171             :     // Connecting to specific addresses, no masternode connections available
     172         355 :     if (options.m_has_specified_outgoing) return;
     173             :     // Initiate masternode connections
     174         355 :     threadOpenMasternodeConnections = std::thread(&TraceThread<std::function<void()> >, "mncon", std::function<void()>(std::bind(&TierTwoConnMan::ThreadOpenMasternodeConnections, this)));
     175             :     // Cleanup process every 60 seconds
     176         710 :     scheduler.scheduleEvery(std::bind(&TierTwoConnMan::doMaintenance, this), 60 * 1000);
     177             : }
     178             : 
     179         850 : void TierTwoConnMan::stop() {
     180         850 :     if (threadOpenMasternodeConnections.joinable()) {
     181         355 :         threadOpenMasternodeConnections.join();
     182             :     }
     183         850 : }
     184             : 
     185         850 : void TierTwoConnMan::interrupt()
     186             : {
     187         850 :     interruptNet();
     188         850 : }
     189             : 
     190         132 : void TierTwoConnMan::openConnection(const CAddress& addrConnect, bool isProbe)
     191             : {
     192         132 :     if (interruptNet) return;
     193             :     // Note: using ip:port string connection instead of the addr to bypass the "only connect to single IPs" validation.
     194         264 :     std::string conn = addrConnect.ToStringIPPort();
     195         264 :     CAddress dummyAddr;
     196         132 :     connman->OpenNetworkConnection(dummyAddr, false, nullptr, conn.data(), false, false, false, true, isProbe);
     197             : }
     198             : 
     199      825914 : class PeerData {
     200             : public:
     201      150934 :     PeerData(const CService& s, bool disconnect, bool is_mn_conn) : service(s), f_disconnect(disconnect), f_is_mn_conn(is_mn_conn) {}
     202             :     const CService service;
     203             :     bool f_disconnect{false};
     204             :     bool f_is_mn_conn{false};
     205       14665 :     bool operator==(const CService& s) const { return service == s; }
     206             : };
     207             : 
     208             : struct MnService {
     209             : public:
     210             :     uint256 verif_proreg_tx_hash{UINT256_ZERO};
     211             :     bool is_inbound{false};
     212       49709 :     bool operator==(const uint256& hash) const { return verif_proreg_tx_hash == hash; }
     213             : };
     214             : 
     215         355 : void TierTwoConnMan::ThreadOpenMasternodeConnections()
     216             : {
     217         355 :     const auto& chainParams = Params();
     218         355 :     bool triedConnect = false;
     219       95472 :     while (!interruptNet) {
     220             : 
     221             :         // Retry every 0.1 seconds if a connection was created, otherwise 1.5 seconds
     222       95472 :         int sleepTime = triedConnect ? 100 : (chainParams.IsRegTestNet() ? 200 : 1500);
     223       95472 :         if (!interruptNet.sleep_for(std::chrono::milliseconds(sleepTime))) {
     224         355 :             return;
     225             :         }
     226             : 
     227       95117 :         triedConnect = false;
     228             : 
     229       95117 :         if (!fMasterNode || !g_tiertwo_sync_state.IsBlockchainSynced() || !g_connman->GetNetworkActive()) {
     230       94985 :             continue;
     231             :         }
     232             : 
     233             :         // Gather all connected peers first, so we don't
     234             :         // try to connect to an already connected peer
     235       22702 :         std::vector<PeerData> connectedNodes;
     236         132 :         std::vector<MnService> connectedMnServices;
     237       22702 :         connman->ForEachNode([&](const CNode* pnode) {
     238      150934 :             connectedNodes.emplace_back(PeerData{pnode->addr, pnode->fDisconnect, pnode->m_masternode_connection});
     239      301868 :             if (!pnode->verifiedProRegTxHash.IsNull()) {
     240       27898 :                 connectedMnServices.emplace_back(MnService{pnode->verifiedProRegTxHash, pnode->fInbound});
     241             :             }
     242      150934 :         });
     243             : 
     244             :         // Try to connect to a single MN per cycle
     245         132 :         CDeterministicMNCPtr dmnToConnect{nullptr};
     246             :         // Current list
     247       22834 :         auto mnList = deterministicMNManager->GetListAtChainTip();
     248       22702 :         int64_t currentTime = GetAdjustedTime();
     249       22702 :         bool isProbe = false;
     250       22702 :         {
     251       22702 :             LOCK(cs_vPendingMasternodes);
     252             : 
     253             :             // First try to connect to pending MNs
     254       22702 :             if (!vPendingMasternodes.empty()) {
     255           2 :                 auto dmn = mnList.GetValidMN(vPendingMasternodes.front());
     256           1 :                 vPendingMasternodes.erase(vPendingMasternodes.begin());
     257           1 :                 if (dmn) {
     258           1 :                     auto peerData = std::find(connectedNodes.begin(), connectedNodes.end(), dmn->pdmnState->addr);
     259           1 :                     if (peerData == std::end(connectedNodes)) {
     260           1 :                         dmnToConnect = dmn;
     261           2 :                         LogPrint(BCLog::NET_MN, "%s -- opening pending masternode connection to %s, service=%s\n",
     262             :                                  __func__, dmn->proTxHash.ToString(), dmn->pdmnState->addr.ToString());
     263             :                     }
     264             :                 }
     265             :             }
     266             : 
     267             :             // Secondly, try to connect quorum members
     268       22702 :             if (!dmnToConnect) {
     269       45402 :                 std::vector<CDeterministicMNCPtr> pending;
     270       32167 :                 for (const auto& group: masternodeQuorumNodes) {
     271       24056 :                     for (const auto& proRegTxHash: group.second) {
     272             :                         // Skip if already have this member connected
     273       14590 :                         if (std::count(connectedMnServices.begin(), connectedMnServices.end(), proRegTxHash) > 0) {
     274       12482 :                             continue;
     275             :                         }
     276             : 
     277             :                         // Don't try to connect to ourselves
     278        6178 :                         if (WITH_LOCK(cs_vPendingMasternodes, return local_dmn_pro_tx_hash && *local_dmn_pro_tx_hash == proRegTxHash)) {
     279         146 :                             continue;
     280             :                         }
     281             : 
     282             :                         // Check if DMN exists in tip list
     283        2071 :                         const auto& dmn = mnList.GetValidMN(proRegTxHash);
     284       16443 :                         if (!dmn) continue;
     285        1719 :                         auto peerData = std::find(connectedNodes.begin(), connectedNodes.end(), dmn->pdmnState->addr);
     286             : 
     287             :                         // Skip already connected nodes.
     288        1719 :                         if (peerData != std::end(connectedNodes) &&
     289          57 :                             (peerData->f_disconnect || peerData->f_is_mn_conn)) {
     290           4 :                             continue;
     291             :                         }
     292             : 
     293             :                         // Check if we already tried this connection recently to not retry too often
     294        1715 :                         int64_t lastAttempt = g_mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastOutboundAttempt();
     295             :                         // back off trying connecting to an address if we already tried recently
     296        1715 :                         if (currentTime - lastAttempt < chainParams.LLMQConnectionRetryTimeout()) {
     297        1606 :                             continue;
     298             :                         }
     299         109 :                         pending.emplace_back(dmn);
     300             :                     }
     301             :                 }
     302             :                 // Select a random node to connect
     303       22701 :                 if (!pending.empty()) {
     304          82 :                     dmnToConnect = pending[GetRandInt((int) pending.size())];
     305         164 :                     LogPrint(BCLog::NET_MN, "TierTwoConnMan::%s -- opening quorum connection to %s, service=%s\n",
     306             :                              __func__, dmnToConnect->proTxHash.ToString(), dmnToConnect->pdmnState->addr.ToString());
     307             :                 }
     308             :             }
     309             : 
     310             :             // If no node was selected, let's try to probe nodes connection
     311       22702 :             if (!dmnToConnect) {
     312       45238 :                 std::vector<CDeterministicMNCPtr> pending;
     313       23065 :                 for (auto it = masternodePendingProbes.begin(); it != masternodePendingProbes.end(); ) {
     314         506 :                     auto dmn = mnList.GetMN(*it);
     315         446 :                     if (!dmn) {
     316           0 :                         it = masternodePendingProbes.erase(it);
     317         386 :                         continue;
     318             :                     }
     319             : 
     320             :                     // Discard already connected outbound MNs
     321         446 :                     auto mnService = std::find(connectedMnServices.begin(), connectedMnServices.end(), dmn->proTxHash);
     322         446 :                     bool connectedAndOutbound = mnService != std::end(connectedMnServices) && !mnService->is_inbound;
     323         494 :                     if (connectedAndOutbound) {
     324             :                         // we already have an outbound connection to this MN so there is no eed to probe it again
     325          48 :                         g_mmetaman.GetMetaInfo(dmn->proTxHash)->SetLastOutboundSuccess(currentTime);
     326          48 :                         it = masternodePendingProbes.erase(it);
     327          48 :                         continue;
     328             :                     }
     329             : 
     330         398 :                     ++it;
     331             : 
     332         398 :                     int64_t lastAttempt = g_mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastOutboundAttempt();
     333             :                     // back off trying connecting to an address if we already tried recently
     334         398 :                     if (currentTime - lastAttempt < chainParams.LLMQConnectionRetryTimeout()) {
     335         338 :                         continue;
     336             :                     }
     337          60 :                     pending.emplace_back(dmn);
     338             :                 }
     339             : 
     340             :                 // Select a random node to connect
     341       22619 :                 if (!pending.empty()) {
     342          49 :                     dmnToConnect = pending[GetRandInt((int)pending.size())];
     343          49 :                     masternodePendingProbes.erase(dmnToConnect->proTxHash);
     344          49 :                     isProbe = true;
     345             : 
     346          98 :                     LogPrint(BCLog::NET_MN, "CConnman::%s -- probing masternode %s, service=%s\n",
     347             :                              __func__, dmnToConnect->proTxHash.ToString(), dmnToConnect->pdmnState->addr.ToString());
     348             :                 }
     349             :             }
     350             :         }
     351             : 
     352             :         // No DMN to connect
     353       22702 :         if (!dmnToConnect || interruptNet) {
     354       22570 :             continue;
     355             :         }
     356             : 
     357             :         // Update last attempt and try connection
     358         132 :         g_mmetaman.GetMetaInfo(dmnToConnect->proTxHash)->SetLastOutboundAttempt(currentTime);
     359         132 :         triedConnect = true;
     360             : 
     361             :         // Now connect
     362         264 :         openConnection(CAddress(dmnToConnect->pdmnState->addr, NODE_NETWORK), isProbe);
     363             :         // should be in the list now if connection was opened
     364         264 :         bool connected = connman->ForNode(dmnToConnect->pdmnState->addr, CConnman::AllNodes, [&](CNode* pnode) {
     365         132 :             if (pnode->fDisconnect) { LogPrintf("about to be disconnected\n");
     366          63 :                 return false;
     367             :             }
     368             :             return true;
     369             :         });
     370         132 :         if (!connected) {
     371         126 :             LogPrint(BCLog::NET_MN, "TierTwoConnMan::%s -- connection failed for masternode  %s, service=%s\n",
     372             :                      __func__, dmnToConnect->proTxHash.ToString(), dmnToConnect->pdmnState->addr.ToString());
     373             :             // reset last outbound success
     374         126 :             g_mmetaman.GetMetaInfo(dmnToConnect->proTxHash)->SetLastOutboundSuccess(0);
     375             :         }
     376             :     }
     377             : }
     378             : 
     379         226 : static void ProcessMasternodeConnections(CConnman& connman, TierTwoConnMan& tierTwoConnMan)
     380             : {
     381             :     // Don't disconnect masternode connections when we have less than the desired amount of outbound nodes
     382         226 :     int nonMasternodeCount = 0;
     383         226 :     connman.ForEachNode([&](CNode* pnode) {
     384        1254 :         if (!pnode->fInbound && !pnode->fFeeler && !pnode->fAddnode && !pnode->m_masternode_connection && !pnode->m_masternode_probe_connection) {
     385         585 :             nonMasternodeCount++;
     386             :         }
     387        1254 :     });
     388         226 :     if (nonMasternodeCount < (int) connman.GetMaxOutboundNodeCount()) {
     389         226 :         return;
     390             :     }
     391             : 
     392           0 :     connman.ForEachNode([&](CNode* pnode) {
     393             :         // we're only disconnecting m_masternode_connection connections
     394           0 :         if (!pnode->m_masternode_connection) return;
     395             :         // we're only disconnecting outbound connections (inbound connections are disconnected in AcceptConnection())
     396           0 :         if (pnode->fInbound) return;
     397             :         // we're not disconnecting LLMQ connections
     398           0 :         if (tierTwoConnMan.isMasternodeQuorumNode(pnode)) return;
     399             :         // we're not disconnecting masternode probes for at least a few seconds
     400           0 :         if (pnode->m_masternode_probe_connection && GetSystemTimeInSeconds() - pnode->nTimeConnected < 5) return;
     401             : 
     402           0 :         if (fLogIPs) {
     403           0 :             LogPrintf("Closing Masternode connection: peer=%d, addr=%s\n", pnode->GetId(), pnode->addr.ToString());
     404             :         } else {
     405           0 :             LogPrintf("Closing Masternode connection: peer=%d\n", pnode->GetId());
     406             :         }
     407           0 :         pnode->fDisconnect = true;
     408             :     });
     409             : }
     410             : 
     411         229 : void TierTwoConnMan::doMaintenance()
     412             : {
     413         229 :     if(!g_tiertwo_sync_state.IsBlockchainSynced() || interruptNet) {
     414           3 :         return;
     415             :     }
     416         226 :     ProcessMasternodeConnections(*connman, *this);
     417             : }
     418             : 

Generated by: LCOV version 1.14