LCOV - code coverage report
Current view: top level - src/llmq - quorums_connections.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 97 114 85.1 %
Date: 2025-02-23 09:33:43 Functions: 6 7 85.7 %

          Line data    Source code
       1             : // Copyright (c) 2018-2019 The Dash Core developers
       2             : // Copyright (c) 2022 The PIVX Core developers
       3             : // Distributed under the MIT software license, see the accompanying
       4             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       5             : 
       6             : #include "llmq/quorums_connections.h"
       7             : 
       8             : #include "evo/deterministicmns.h"
       9             : #include "llmq/quorums.h"
      10             : #include "net.h"
      11             : #include "tiertwo/masternode_meta_manager.h" // for g_mmetaman
      12             : #include "tiertwo/net_masternodes.h"
      13             : #include "validation.h"
      14             : 
      15             : #include <vector>
      16             : 
      17             : namespace llmq
      18             : {
      19             : 
      20             : 
      21        7112 : uint256 DeterministicOutboundConnection(const uint256& proTxHash1, const uint256& proTxHash2)
      22             : {
      23             :     // We need to deterministically select who is going to initiate the connection. The naive way would be to simply
      24             :     // return the min(proTxHash1, proTxHash2), but this would create a bias towards MNs with a numerically low
      25             :     // hash. To fix this, we return the proTxHash that has the lowest value of:
      26             :     //   hash(min(proTxHash1, proTxHash2), max(proTxHash1, proTxHash2), proTxHashX)
      27             :     // where proTxHashX is the proTxHash to compare
      28        7112 :     uint256 h1;
      29       14224 :     uint256 h2;
      30        7112 :     if (proTxHash1 < proTxHash2) {
      31        3530 :         h1 = ::SerializeHash(std::make_tuple(proTxHash1, proTxHash2, proTxHash1));
      32        3530 :         h2 = ::SerializeHash(std::make_tuple(proTxHash1, proTxHash2, proTxHash2));
      33             :     } else {
      34        3582 :         h1 = ::SerializeHash(std::make_tuple(proTxHash2, proTxHash1, proTxHash1));
      35        3582 :         h2 = ::SerializeHash(std::make_tuple(proTxHash2, proTxHash1, proTxHash2));
      36             :     }
      37        7112 :     if (h1 < h2) {
      38        3390 :         return proTxHash1;
      39             :     }
      40        3722 :     return proTxHash2;
      41             : }
      42             : 
      43     2004638 : std::set<uint256> GetQuorumRelayMembers(const std::vector<CDeterministicMNCPtr>& mnList,
      44             :                                         unsigned int forMemberIndex)
      45             : {
      46     2004638 :     assert(forMemberIndex < mnList.size());
      47             : 
      48             :     // Special case
      49     2004638 :     if (mnList.size() == 2) {
      50           2 :         return {mnList[1 - forMemberIndex]->proTxHash};
      51             :     }
      52             : 
      53             :     // Relay to nodes at indexes (i+2^k)%n, where
      54             :     //   k: 0..max(1, floor(log2(n-1))-1)
      55             :     //   n: size of the quorum/ring
      56     4009276 :     std::set<uint256> r;
      57     2004638 :     int gap = 1;
      58     2004638 :     int gap_max = (int)mnList.size() - 1;
      59     2004638 :     int k = 0;
      60    21321814 :     while ((gap_max >>= 1) || k <= 1) {
      61    19317176 :         size_t idx = (forMemberIndex + gap) % mnList.size();
      62    19317176 :         r.emplace(mnList[idx]->proTxHash);
      63    19317176 :         gap <<= 1;
      64    19317176 :         k++;
      65             :     }
      66     4009266 :     return r;
      67             : }
      68             : 
      69        3556 : static std::set<uint256> GetQuorumConnections(const std::vector<CDeterministicMNCPtr>& mns, const uint256& forMember, bool onlyOutbound)
      70             : {
      71        3556 :     std::set<uint256> result;
      72       14224 :     for (auto& dmn : mns) {
      73       10668 :         if (dmn->proTxHash == forMember) {
      74        3556 :             continue;
      75             :         }
      76             :         // Determine which of the two MNs (forMember vs dmn) should initiate the outbound connection and which
      77             :         // one should wait for the inbound connection. We do this in a deterministic way, so that even when we
      78             :         // end up with both connecting to each other, we know which one to disconnect
      79        7112 :         uint256 deterministicOutbound = DeterministicOutboundConnection(forMember, dmn->proTxHash);
      80        7112 :         if (!onlyOutbound || deterministicOutbound == dmn->proTxHash) {
      81       10834 :             result.emplace(dmn->proTxHash);
      82             :         }
      83             :     }
      84        3556 :     return result;
      85             : }
      86             : 
      87           0 : std::set<size_t> CalcDeterministicWatchConnections(Consensus::LLMQType llmqType, const CBlockIndex* pindexQuorum, size_t memberCount, size_t connectionCount)
      88             : {
      89           0 :     static uint256 qwatchConnectionSeed;
      90           0 :     static std::atomic<bool> qwatchConnectionSeedGenerated{false};
      91           0 :     static RecursiveMutex qwatchConnectionSeedCs;
      92           0 :     if (!qwatchConnectionSeedGenerated) {
      93           0 :         LOCK(qwatchConnectionSeedCs);
      94           0 :         if (!qwatchConnectionSeedGenerated) {
      95           0 :             qwatchConnectionSeed = GetRandHash();
      96           0 :             qwatchConnectionSeedGenerated = true;
      97             :         }
      98             :     }
      99             : 
     100           0 :     std::set<size_t> result;
     101           0 :     uint256 rnd = qwatchConnectionSeed;
     102           0 :     for (size_t i = 0; i < connectionCount; i++) {
     103           0 :         rnd = ::SerializeHash(std::make_pair(rnd, std::make_pair(static_cast<uint8_t>(llmqType), pindexQuorum->GetBlockHash())));
     104           0 :         result.emplace(rnd.GetUint64(0) % memberCount);
     105             :     }
     106           0 :     return result;
     107             : }
     108             : 
     109             : // ensure connection to a given list of quorums
     110        4496 : void EnsureLatestQuorumConnections(Consensus::LLMQType llmqType, const CBlockIndex* pindexNew, const uint256& myProTxHash, std::vector<CQuorumCPtr>& lastQuorums)
     111             : {
     112        4496 :     const auto& params = Params().GetConsensus().llmqs.at(llmqType);
     113        4496 :     auto connman = g_connman->GetTierTwoConnMan();
     114             : 
     115        4496 :     auto connmanQuorumsToDelete = connman->getQuorumNodes(llmqType);
     116             : 
     117             :     // don't remove connections for the currently in-progress DKG round
     118        4496 :     int curDkgHeight = pindexNew->nHeight - (pindexNew->nHeight % params.dkgInterval);
     119        4496 :     auto curDkgBlock = pindexNew->GetAncestor(curDkgHeight)->GetBlockHash();
     120        4496 :     connmanQuorumsToDelete.erase(curDkgBlock);
     121             : 
     122       11577 :     for (auto& quorum : lastQuorums) {
     123        7081 :         if (!quorum->IsMember(myProTxHash)) {
     124        3607 :             continue;
     125             :         }
     126             : 
     127        3474 :         EnsureQuorumConnections(llmqType, quorum->pindexQuorum, myProTxHash);
     128             : 
     129        3474 :         connmanQuorumsToDelete.erase(quorum->pindexQuorum->GetBlockHash());
     130             :     }
     131             : 
     132        4529 :     for (auto& qh : connmanQuorumsToDelete) {
     133          33 :         LogPrintf("CQuorumManager::%s -- removing masternodes quorum connections for quorum %s:\n", __func__, qh.ToString());
     134          33 :         connman->removeQuorumNodes(llmqType, qh);
     135             :     }
     136        4496 : }
     137             : 
     138             : // ensure connection to a given quorum
     139        3642 : void EnsureQuorumConnections(Consensus::LLMQType llmqType, const CBlockIndex* pindexQuorum, const uint256& myProTxHash)
     140             : {
     141        3642 :     const auto& members = deterministicMNManager->GetAllQuorumMembers(llmqType, pindexQuorum);
     142       10890 :     auto itMember = std::find_if(members.begin(), members.end(), [&](const CDeterministicMNCPtr& dmn) { return dmn->proTxHash == myProTxHash; });
     143        3642 :     bool isMember = itMember != members.end();
     144             : 
     145        3642 :     if (!isMember) { // && !CLLMQUtils::IsWatchQuorumsEnabled()) {
     146          86 :         return;
     147             :     }
     148             : 
     149        7112 :     std::set<uint256> connections;
     150        3556 :     std::set<uint256> relayMembers;
     151        3556 :     if (isMember) {
     152        7112 :         connections = GetQuorumConnections(members, myProTxHash, true);
     153        3556 :         unsigned int memberIndex = itMember - members.begin();
     154        7112 :         relayMembers = GetQuorumRelayMembers(members, memberIndex);
     155             :     } else {
     156             :         auto cindexes = CalcDeterministicWatchConnections(llmqType, pindexQuorum, members.size(), 1);
     157             :         for (auto idx : cindexes) {
     158             :             connections.emplace(members[idx]->proTxHash);
     159             :         }
     160             :         relayMembers = connections;
     161             :     }
     162        3556 :     if (!connections.empty()) {
     163        2483 :         auto connman = g_connman->GetTierTwoConnMan();
     164        2540 :         if (!connman->hasQuorumNodes(llmqType, pindexQuorum->GetBlockHash()) && LogAcceptCategory(BCLog::LLMQ)) {
     165          57 :             auto mnList = deterministicMNManager->GetListAtChainTip();
     166         114 :             std::string debugMsg = strprintf("CLLMQUtils::%s -- adding masternodes quorum connections for quorum %s:\n", __func__, pindexQuorum->GetBlockHash().ToString());
     167         140 :             for (auto& c : connections) {
     168         166 :                 auto dmn = mnList.GetValidMN(c);
     169          83 :                 if (!dmn) {
     170           0 :                     debugMsg += strprintf("  %s (not in valid MN set anymore)\n", c.ToString());
     171             :                 } else {
     172         332 :                     debugMsg += strprintf("  %s (%s)\n", c.ToString(), dmn->pdmnState->addr.ToString());
     173             :                 }
     174             :             }
     175          57 :             LogPrint(BCLog::LLMQ, debugMsg.c_str()); /* Continued */
     176             :         }
     177        2483 :         connman->setQuorumNodes(llmqType, pindexQuorum->GetBlockHash(), connections);
     178             :     }
     179        3556 :     if (!relayMembers.empty()) {
     180        3556 :         auto connman = g_connman->GetTierTwoConnMan();
     181        3556 :         connman->setMasternodeQuorumRelayMembers(llmqType, pindexQuorum->GetBlockHash(), relayMembers);
     182             :     }
     183             : }
     184             : 
     185          82 : void AddQuorumProbeConnections(Consensus::LLMQType llmqType, const CBlockIndex *pindexQuorum, const uint256 &myProTxHash)
     186             : {
     187          82 :     auto members = deterministicMNManager->GetAllQuorumMembers(llmqType, pindexQuorum);
     188          82 :     auto curTime = GetAdjustedTime();
     189             : 
     190         164 :     std::set<uint256> probeConnections;
     191         328 :     for (auto& dmn : members) {
     192         246 :         if (dmn->proTxHash == myProTxHash) {
     193          82 :             continue;
     194             :         }
     195         164 :         auto lastOutbound = g_mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastOutboundSuccess();
     196             :         // re-probe after 50 minutes so that the "good connection" check in the DKG doesn't fail just because we're on
     197             :         // the brink of timeout
     198         164 :         if (curTime - lastOutbound > 50 * 60) {
     199         346 :             probeConnections.emplace(dmn->proTxHash);
     200             :         }
     201             :     }
     202             : 
     203          82 :     if (!probeConnections.empty()) {
     204          62 :         if (LogAcceptCategory(BCLog::LLMQ)) {
     205          62 :             auto mnList = deterministicMNManager->GetListAtChainTip();
     206         124 :             std::string debugMsg = strprintf("CLLMQUtils::%s -- adding masternodes probes for quorum %s:\n", __func__, pindexQuorum->GetBlockHash().ToString());
     207         162 :             for (auto& c : probeConnections) {
     208         200 :                 auto dmn = mnList.GetValidMN(c);
     209         100 :                 if (!dmn) {
     210           0 :                     debugMsg += strprintf("  %s (not in valid MN set anymore)\n", c.ToString());
     211             :                 } else {
     212         400 :                     debugMsg += strprintf("  %s (%s)\n", c.ToString(), dmn->pdmnState->addr.ToString());
     213             :                 }
     214             :             }
     215          62 :             LogPrint(BCLog::LLMQ, debugMsg.c_str()); /* Continued */
     216             :         }
     217          62 :         g_connman->GetTierTwoConnMan()->addPendingProbeConnections(probeConnections);
     218             :     }
     219          82 : }
     220             : 
     221             : } // namespace llmq

Generated by: LCOV version 1.14