LCOV - code coverage report
Current view: top level - src/llmq - net_dkg.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 0 429 0.0 %
Date: 2026-06-25 07:23:51 Functions: 0 59 0.0 %

          Line data    Source code
       1             : // Copyright (c) 2018-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 <llmq/net_dkg.h>
       6             : 
       7             : #include <active/dkgsessionhandler.h>
       8             : #include <chainparams.h>
       9             : #include <evo/deterministicmns.h>
      10             : #include <hash.h>
      11             : #include <llmq/blockprocessor.h>
      12             : #include <llmq/commitment.h>
      13             : #include <llmq/debug.h>
      14             : #include <llmq/dkgsession.h>
      15             : #include <llmq/dkgsessionmgr.h>
      16             : #include <llmq/net_quorum.h>
      17             : #include <llmq/options.h>
      18             : #include <llmq/quorumsman.h>
      19             : #include <llmq/utils.h>
      20             : #include <masternode/meta.h>
      21             : #include <net.h>
      22             : #include <netmessagemaker.h>
      23             : #include <protocol.h>
      24             : #include <span.h>
      25             : #include <unordered_lru_cache.h>
      26             : #include <util/std23.h>
      27             : #include <util/thread.h>
      28             : #include <validation.h>
      29             : 
      30             : namespace llmq {
      31             : 
      32             : namespace {
      33             : // returns a set of NodeIds which sent invalid messages
      34             : template <typename Message>
      35           0 : std::unordered_set<NodeId> BatchVerifyMessageSigs(CDKGSession& session,
      36             :                                                   const std::vector<std::pair<NodeId, std::shared_ptr<Message>>>& messages)
      37             : {
      38           0 :     if (messages.empty()) {
      39           0 :         return {};
      40             :     }
      41             : 
      42           0 :     std::unordered_set<NodeId> ret;
      43           0 :     bool revertToSingleVerification = false;
      44             : 
      45           0 :     CBLSSignature aggSig;
      46           0 :     std::vector<CBLSPublicKey> pubKeys;
      47           0 :     std::vector<uint256> messageHashes;
      48           0 :     Uint256HashSet messageHashesSet;
      49           0 :     pubKeys.reserve(messages.size());
      50           0 :     messageHashes.reserve(messages.size());
      51           0 :     bool first = true;
      52           0 :     for (const auto& [nodeId, msg] : messages) {
      53           0 :         auto member = session.GetMember(msg->proTxHash);
      54           0 :         if (!member) {
      55             :             // should not happen as it was verified before
      56           0 :             ret.emplace(nodeId);
      57           0 :             continue;
      58             :         }
      59             : 
      60           0 :         if (first) {
      61           0 :             aggSig = msg->sig;
      62           0 :         } else {
      63           0 :             aggSig.AggregateInsecure(msg->sig);
      64             :         }
      65           0 :         first = false;
      66             : 
      67           0 :         auto msgHash = msg->GetSignHash();
      68           0 :         if (!messageHashesSet.emplace(msgHash).second) {
      69             :             // can only happen in 2 cases:
      70             :             // 1. Someone sent us the same message twice but with differing signature, meaning that at least one of them
      71             :             //    must be invalid. In this case, we'd have to revert to single message verification nevertheless
      72             :             // 2. Someone managed to find a way to create two different binary representations of a message that deserializes
      73             :             //    to the same object representation. This would be some form of malleability. However, this shouldn't be
      74             :             //    possible as only deterministic/unique BLS signatures and very simple data types are involved
      75           0 :             revertToSingleVerification = true;
      76           0 :             break;
      77             :         }
      78             : 
      79           0 :         pubKeys.emplace_back(member->dmn->pdmnState->pubKeyOperator.Get());
      80           0 :         messageHashes.emplace_back(msgHash);
      81             :     }
      82           0 :     if (!revertToSingleVerification) {
      83           0 :         if (aggSig.VerifyInsecureAggregated(pubKeys, messageHashes)) {
      84             :             // all good
      85           0 :             return ret;
      86             :         }
      87             : 
      88             :         // are all messages from the same node?
      89           0 :         bool nodeIdsAllSame = std::adjacent_find(messages.begin(), messages.end(),
      90           0 :                                                  [](const auto& first, const auto& second) {
      91           0 :                                                      return first.first != second.first;
      92           0 :                                                  }) == messages.end();
      93             : 
      94             :         // if yes, take a short path and return a set with only him
      95           0 :         if (nodeIdsAllSame) {
      96           0 :             ret.emplace(messages[0].first);
      97           0 :             return ret;
      98             :         }
      99             :         // different nodes, let's figure out who are the bad ones
     100           0 :     }
     101             : 
     102           0 :     for (const auto& [nodeId, msg] : messages) {
     103           0 :         if (ret.count(nodeId)) {
     104           0 :             continue;
     105             :         }
     106             : 
     107           0 :         auto member = session.GetMember(msg->proTxHash);
     108           0 :         bool valid = msg->sig.VerifyInsecure(member->dmn->pdmnState->pubKeyOperator.Get(), msg->GetSignHash());
     109           0 :         if (!valid) {
     110           0 :             ret.emplace(nodeId);
     111           0 :         }
     112             :     }
     113           0 :     return ret;
     114           0 : }
     115             : 
     116           0 : void RelayInvToParticipants(const CDKGSession& session, const CConnman& connman, PeerManagerInternal& peerman,
     117             :                             const CInv& inv)
     118             : {
     119           0 :     CDKGLogger logger(session, __func__, __LINE__);
     120           0 :     std::stringstream ss;
     121           0 :     const auto& relayMembers = session.RelayMembers();
     122           0 :     for (const auto& r : relayMembers) {
     123           0 :         ss << r.ToString().substr(0, 4) << " | ";
     124             :     }
     125           0 :     logger.Batch("RelayInvToParticipants inv[%s] relayMembers[%d] GetNodeCount[%d] GetNetworkActive[%d] "
     126             :                  "HasMasternodeQuorumNodes[%d] for quorumHash[%s] forMember[%s] relayMembers[%s]",
     127           0 :                  inv.ToString(), relayMembers.size(), connman.GetNodeCount(ConnectionDirection::Both),
     128           0 :                  connman.GetNetworkActive(),
     129           0 :                  connman.HasMasternodeQuorumNodes(session.GetType(), session.BlockIndex()->GetBlockHash()),
     130           0 :                  session.BlockIndex()->GetBlockHash().ToString(), session.ProTx().ToString().substr(0, 4), ss.str());
     131             : 
     132           0 :     std::stringstream ss2;
     133           0 :     connman.ForEachNode([&](const CNode* pnode) {
     134           0 :         if (pnode->qwatch ||
     135           0 :             (!pnode->GetVerifiedProRegTxHash().IsNull() && (relayMembers.count(pnode->GetVerifiedProRegTxHash()) != 0))) {
     136           0 :             peerman.PeerPushInventory(pnode->GetId(), inv);
     137           0 :         }
     138             : 
     139           0 :         if (pnode->GetVerifiedProRegTxHash().IsNull()) {
     140           0 :             logger.Batch("node[%d:%s] not mn", pnode->GetId(), pnode->m_addr_name);
     141           0 :         } else if (relayMembers.count(pnode->GetVerifiedProRegTxHash()) == 0) {
     142           0 :             ss2 << pnode->GetVerifiedProRegTxHash().ToString().substr(0, 4) << " | ";
     143           0 :         }
     144           0 :     });
     145           0 :     logger.Batch("forMember[%s] NOTrelayMembers[%s]", session.ProTx().ToString().substr(0, 4), ss2.str());
     146           0 :     logger.Flush();
     147           0 : }
     148             : 
     149             : template <typename Message>
     150           0 : void EnqueueOwn(CDKGPendingMessages& pending, const Message& msg)
     151             : {
     152           0 :     CDataStream ds(SER_NETWORK, PROTOCOL_VERSION);
     153           0 :     ds << msg;
     154           0 :     auto pm = std::make_shared<CDataStream>(std::move(ds));
     155           0 :     CHashWriter hw(SER_GETHASH, 0);
     156           0 :     hw.write(AsWritableBytes(Span{*pm}));
     157           0 :     pending.PushPendingMessage(/*from=*/-1, std::move(pm), hw.GetHash());
     158           0 : }
     159             : 
     160             : template <typename Message>
     161           0 : bool ProcessPendingMessageBatch(const CConnman& connman, CDKGSession& session, CDKGPendingMessages& pendingMessages,
     162             :                                 PeerManagerInternal& peerman, size_t maxCount)
     163             : {
     164           0 :     auto msgs = pendingMessages.PopAndDeserializeMessages<Message>(maxCount);
     165           0 :     if (msgs.empty()) {
     166           0 :         return false;
     167             :     }
     168             : 
     169           0 :     std::vector<std::pair<NodeId, std::shared_ptr<Message>>> preverifiedMessages;
     170           0 :     preverifiedMessages.reserve(msgs.size());
     171             : 
     172           0 :     for (const auto& p : msgs) {
     173           0 :         const NodeId& nodeId = p.first;
     174           0 :         if (!p.second) {
     175           0 :             LogPrint(BCLog::LLMQ_DKG, "%s -- failed to deserialize message, peer=%d\n", __func__, nodeId);
     176           0 :             peerman.PeerMisbehaving(nodeId, 100);
     177           0 :             continue;
     178             :         }
     179           0 :         bool ban = false;
     180           0 :         if (!session.PreVerifyMessage(*p.second, ban)) {
     181           0 :             if (ban) {
     182           0 :                 LogPrint(BCLog::LLMQ_DKG, "%s -- banning node due to failed preverification, peer=%d\n", __func__, nodeId);
     183           0 :                 peerman.PeerMisbehaving(nodeId, 100);
     184           0 :             }
     185           0 :             LogPrint(BCLog::LLMQ_DKG, "%s -- skipping message due to failed preverification, peer=%d\n", __func__, nodeId);
     186           0 :             continue;
     187             :         }
     188           0 :         preverifiedMessages.emplace_back(p);
     189             :     }
     190           0 :     if (preverifiedMessages.empty()) {
     191           0 :         return true;
     192             :     }
     193             : 
     194           0 :     auto badNodes = BatchVerifyMessageSigs(session, preverifiedMessages);
     195           0 :     if (!badNodes.empty()) {
     196           0 :         for (auto nodeId : badNodes) {
     197           0 :             LogPrint(BCLog::LLMQ_DKG, "%s -- failed to verify signature, peer=%d\n", __func__, nodeId);
     198           0 :             peerman.PeerMisbehaving(nodeId, 100);
     199             :         }
     200           0 :     }
     201             : 
     202           0 :     for (const auto& p : preverifiedMessages) {
     203           0 :         const NodeId& nodeId = p.first;
     204           0 :         if (badNodes.count(nodeId)) {
     205           0 :             continue;
     206             :         }
     207           0 :         const std::optional<CInv> inv = session.ReceiveMessage(*p.second);
     208           0 :         if (inv) {
     209           0 :             RelayInvToParticipants(session, connman, peerman, *inv);
     210           0 :         }
     211             :     }
     212             : 
     213           0 :     return true;
     214           0 : }
     215             : } // namespace
     216             : 
     217             : 
     218           0 : NetDKG::NetDKG(PeerManagerInternal* peer_manager, const CSporkManager& sporkman, CDKGSessionManager& qdkgsman,
     219             :                const ChainstateManager& chainman, CQuorumManager& qman, QuorumRole& role) :
     220           0 :     NetHandler(peer_manager),
     221           0 :     m_qdkgsman{qdkgsman},
     222           0 :     m_qman{qman},
     223           0 :     m_sporkman{sporkman},
     224           0 :     m_chainman{chainman},
     225           0 :     m_active{nullptr}
     226           0 : {
     227           0 :     m_qdkgsman.InitializeHandlers([](const Consensus::LLMQParams& llmq_params,
     228             :                                      [[maybe_unused]] int quorum_idx) -> std::unique_ptr<CDKGSessionHandler> {
     229           0 :         return std::make_unique<CDKGSessionHandler>(llmq_params);
     230             :     });
     231           0 :     m_qman.ConnectManagers(&role, &m_qdkgsman);
     232           0 : }
     233             : 
     234           0 : NetDKG::NetDKG(PeerManagerInternal* peer_manager, const CSporkManager& sporkman, CDKGSessionManager& qdkgsman,
     235             :                const ChainstateManager& chainman, bool quorums_watch, CQuorumManager& qman, QuorumRole& role,
     236             :                CBLSWorker& bls_worker, CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_metaman,
     237             :                CDKGDebugManager& dkgdbgman, CQuorumBlockProcessor& qblockman, CQuorumSnapshotManager& qsnapman,
     238             :                const CActiveMasternodeManager& mn_activeman, CConnman& connman) :
     239           0 :     NetHandler(peer_manager),
     240           0 :     m_qdkgsman{qdkgsman},
     241           0 :     m_qman{qman},
     242           0 :     m_sporkman{sporkman},
     243           0 :     m_chainman{chainman},
     244           0 :     m_active{std::make_unique<ActiveDKG>(ActiveDKG{dmnman, mn_metaman, dkgdbgman, qblockman, qsnapman, connman})}
     245           0 : {
     246           0 :     m_qdkgsman.InitializeHandlers(
     247           0 :         [&](const Consensus::LLMQParams& llmq_params, int quorum_idx) -> std::unique_ptr<ActiveDKGSessionHandler> {
     248           0 :             return std::make_unique<ActiveDKGSessionHandler>(bls_worker, dmnman, mn_metaman, dkgdbgman, qdkgsman,
     249           0 :                                                              qblockman, qsnapman, mn_activeman, chainman, sporkman,
     250           0 :                                                              llmq_params, quorums_watch, quorum_idx);
     251             :         });
     252           0 :     m_qman.ConnectManagers(&role, &m_qdkgsman);
     253           0 : }
     254             : 
     255           0 : NetDKG::~NetDKG()
     256           0 : {
     257           0 :     Stop();
     258           0 :     m_qman.DisconnectManagers();
     259           0 : }
     260             : 
     261           0 : void NetDKG::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv)
     262             : {
     263           0 :     if (!IsQuorumDKGEnabled(m_sporkman)) return;
     264             : 
     265           0 :     if (msg_type != NetMsgType::QCONTRIB && msg_type != NetMsgType::QCOMPLAINT && msg_type != NetMsgType::QJUSTIFICATION &&
     266           0 :         msg_type != NetMsgType::QPCOMMITMENT && msg_type != NetMsgType::QWATCH) {
     267           0 :         return;
     268             :     }
     269             : 
     270           0 :     const bool is_masternode = m_active != nullptr;
     271             : 
     272           0 :     if (msg_type == NetMsgType::QWATCH) {
     273           0 :         if (!is_masternode) {
     274             :             // non-masternodes should never receive this
     275           0 :             m_peer_manager->PeerMisbehaving(pfrom.GetId(), 10);
     276           0 :             return;
     277             :         }
     278           0 :         pfrom.qwatch = true;
     279           0 :         return;
     280             :     }
     281             : 
     282           0 :     if (vRecv.empty()) {
     283           0 :         m_peer_manager->PeerMisbehaving(pfrom.GetId(), 100);
     284           0 :         return;
     285             :     }
     286             : 
     287             :     Consensus::LLMQType llmqType;
     288           0 :     uint256 quorumHash;
     289           0 :     vRecv >> llmqType;
     290           0 :     vRecv >> quorumHash;
     291           0 :     vRecv.Rewind(sizeof(uint256));
     292           0 :     vRecv.Rewind(sizeof(uint8_t));
     293             : 
     294           0 :     const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
     295           0 :     if (!llmq_params_opt.has_value()) {
     296           0 :         LogPrintf("NetDKG -- invalid llmqType [%d]\n", std23::to_underlying(llmqType));
     297           0 :         m_peer_manager->PeerMisbehaving(pfrom.GetId(), 100);
     298           0 :         return;
     299             :     }
     300           0 :     const auto& llmq_params = llmq_params_opt.value();
     301             : 
     302           0 :     int quorumIndex{-1};
     303             :     {
     304           0 :         LOCK(cs_indexed_quorums_cache);
     305           0 :         if (indexed_quorums_cache.empty()) {
     306           0 :             utils::InitQuorumsCache(indexed_quorums_cache, m_chainman.GetConsensus());
     307           0 :         }
     308           0 :         indexed_quorums_cache[llmqType].get(quorumHash, quorumIndex);
     309           0 :     }
     310             : 
     311           0 :     if (quorumIndex == -1) {
     312           0 :         const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(::cs_main,
     313             :                                                              return m_chainman.m_blockman.LookupBlockIndex(quorumHash));
     314           0 :         if (pQuorumBaseBlockIndex == nullptr) {
     315           0 :             LogPrintf("NetDKG -- unknown quorumHash %s\n", quorumHash.ToString());
     316             :             // NOTE: do not insta-ban for this, we might be lagging behind
     317           0 :             m_peer_manager->PeerMisbehaving(pfrom.GetId(), 10);
     318           0 :             return;
     319             :         }
     320           0 :         if (!m_chainman.IsQuorumTypeEnabled(llmqType, pQuorumBaseBlockIndex->pprev)) {
     321           0 :             LogPrintf("NetDKG -- llmqType [%d] quorums aren't active\n", std23::to_underlying(llmqType));
     322           0 :             m_peer_manager->PeerMisbehaving(pfrom.GetId(), 100);
     323           0 :             return;
     324             :         }
     325           0 :         quorumIndex = pQuorumBaseBlockIndex->nHeight % llmq_params.dkgInterval;
     326           0 :         const int quorumIndexMax = IsQuorumRotationEnabled(llmq_params, pQuorumBaseBlockIndex)
     327           0 :                                        ? llmq_params.signingActiveQuorumCount - 1
     328             :                                        : 0;
     329           0 :         if (quorumIndex > quorumIndexMax) {
     330           0 :             LogPrintf("NetDKG -- invalid quorumHash %s\n", quorumHash.ToString());
     331           0 :             m_peer_manager->PeerMisbehaving(pfrom.GetId(), 100);
     332           0 :             return;
     333             :         }
     334           0 :     }
     335             : 
     336           0 :     int inv_type = 0;
     337           0 :     if (msg_type == NetMsgType::QCONTRIB)
     338           0 :         inv_type = MSG_QUORUM_CONTRIB;
     339           0 :     else if (msg_type == NetMsgType::QCOMPLAINT)
     340           0 :         inv_type = MSG_QUORUM_COMPLAINT;
     341           0 :     else if (msg_type == NetMsgType::QJUSTIFICATION)
     342           0 :         inv_type = MSG_QUORUM_JUSTIFICATION;
     343           0 :     else if (msg_type == NetMsgType::QPCOMMITMENT)
     344           0 :         inv_type = MSG_QUORUM_PREMATURE_COMMITMENT;
     345           0 :     Assume(inv_type != 0); // guarded by the early-return above
     346             : 
     347           0 :     auto pm = std::make_shared<CDataStream>(std::move(vRecv));
     348           0 :     CHashWriter hw(SER_GETHASH, 0);
     349           0 :     hw.write(AsWritableBytes(Span{*pm}));
     350           0 :     const uint256 hash = hw.GetHash();
     351             : 
     352           0 :     const NodeId from = pfrom.GetId();
     353           0 :     const bool dispatched = m_qdkgsman.DoForHandler({llmqType, quorumIndex}, [&](CDKGSessionHandler& handler) {
     354           0 :         CDKGPendingMessages* pending = nullptr;
     355           0 :         switch (inv_type) {
     356             :         case MSG_QUORUM_CONTRIB:
     357           0 :             pending = &handler.pendingContributions;
     358           0 :             break;
     359             :         case MSG_QUORUM_COMPLAINT:
     360           0 :             pending = &handler.pendingComplaints;
     361           0 :             break;
     362             :         case MSG_QUORUM_JUSTIFICATION:
     363           0 :             pending = &handler.pendingJustifications;
     364           0 :             break;
     365             :         case MSG_QUORUM_PREMATURE_COMMITMENT:
     366           0 :             pending = &handler.pendingPrematureCommitments;
     367           0 :             break;
     368             :         }
     369           0 :         Assume(pending != nullptr);
     370           0 :         WITH_LOCK(::cs_main, m_peer_manager->PeerEraseObjectRequest(from, CInv{static_cast<uint32_t>(inv_type), hash}));
     371           0 :         pending->PushPendingMessage(from, std::move(pm), hash);
     372           0 :     });
     373           0 :     if (!dispatched) {
     374           0 :         LogPrintf("NetDKG -- no session handlers for quorumIndex [%d]\n", quorumIndex);
     375           0 :         m_peer_manager->PeerMisbehaving(pfrom.GetId(), 100);
     376           0 :         return;
     377             :     }
     378             : 
     379           0 :     WITH_LOCK(cs_indexed_quorums_cache, indexed_quorums_cache[llmqType].insert(quorumHash, quorumIndex));
     380           0 : }
     381             : 
     382           0 : bool NetDKG::AlreadyHave(const CInv& inv)
     383             : {
     384           0 :     switch (inv.type) {
     385             :     case MSG_QUORUM_CONTRIB:
     386             :     case MSG_QUORUM_COMPLAINT:
     387             :     case MSG_QUORUM_JUSTIFICATION:
     388             :     case MSG_QUORUM_PREMATURE_COMMITMENT: {
     389           0 :         if (!IsQuorumDKGEnabled(m_sporkman)) return false;
     390           0 :         bool seen = false;
     391           0 :         m_qdkgsman.ForEachHandler([&](CDKGSessionHandler& h) {
     392           0 :             if (seen) return;
     393           0 :             if (h.pendingContributions.HasSeen(inv.hash) || h.pendingComplaints.HasSeen(inv.hash) ||
     394           0 :                 h.pendingJustifications.HasSeen(inv.hash) || h.pendingPrematureCommitments.HasSeen(inv.hash)) {
     395           0 :                 seen = true;
     396           0 :             }
     397           0 :         });
     398           0 :         return seen;
     399             :     }
     400             :     }
     401           0 :     return false;
     402           0 : }
     403             : 
     404           0 : bool NetDKG::ProcessGetData(CNode& pfrom, const CInv& inv, CConnman& connman, const CNetMsgMaker& msgMaker)
     405             : {
     406             :     // Default implementations of GetContribution and the other virtual methods
     407             :     // return false in observer mode; m_active is only an early exit and does
     408             :     // not affect logic.
     409           0 :     if (m_active == nullptr) return false;
     410             : 
     411           0 :     switch (inv.type) {
     412             :     case MSG_QUORUM_CONTRIB: {
     413           0 :         CDKGContribution o;
     414           0 :         if (m_qdkgsman.GetContribution(inv.hash, o)) {
     415           0 :             connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QCONTRIB, o));
     416           0 :             return true;
     417             :         }
     418           0 :         return false;
     419           0 :     }
     420             :     case MSG_QUORUM_COMPLAINT: {
     421           0 :         CDKGComplaint o;
     422           0 :         if (m_qdkgsman.GetComplaint(inv.hash, o)) {
     423           0 :             connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QCOMPLAINT, o));
     424           0 :             return true;
     425             :         }
     426           0 :         return false;
     427           0 :     }
     428             :     case MSG_QUORUM_JUSTIFICATION: {
     429           0 :         CDKGJustification o;
     430           0 :         if (m_qdkgsman.GetJustification(inv.hash, o)) {
     431           0 :             connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QJUSTIFICATION, o));
     432           0 :             return true;
     433             :         }
     434           0 :         return false;
     435           0 :     }
     436             :     case MSG_QUORUM_PREMATURE_COMMITMENT: {
     437           0 :         CDKGPrematureCommitment o;
     438           0 :         if (m_qdkgsman.GetPrematureCommitment(inv.hash, o)) {
     439           0 :             connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QPCOMMITMENT, o));
     440           0 :             return true;
     441             :         }
     442           0 :         return false;
     443           0 :     }
     444             :     }
     445           0 :     return false;
     446           0 : }
     447             : 
     448           0 : void NetDKG::Start()
     449             : {
     450           0 :     if (m_active == nullptr) return;
     451           0 :     if (!m_phase_threads.empty()) {
     452           0 :         throw std::runtime_error("Tried to start PhaseHandlerThreads again.");
     453             :     }
     454             : 
     455           0 :     m_qdkgsman.ForEachHandler([this](CDKGSessionHandler& base) {
     456           0 :         auto& handler = dynamic_cast<ActiveDKGSessionHandler&>(base);
     457           0 :         std::string thread_name = strprintf("llmq-%d-%d", std23::to_underlying(handler.params.type), handler.QuorumIndex());
     458           0 :         m_phase_threads.emplace_back([this, name = std::move(thread_name), &handler] {
     459           0 :             util::TraceThread(name.c_str(), [this, &handler] { PhaseHandlerThread(handler); });
     460           0 :         });
     461           0 :     });
     462           0 : }
     463             : 
     464           0 : void NetDKG::Stop()
     465             : {
     466           0 :     Interrupt();
     467           0 :     for (auto& t : m_phase_threads) {
     468           0 :         if (t.joinable()) t.join();
     469             :     }
     470           0 :     m_phase_threads.clear();
     471           0 : }
     472             : 
     473           0 : void NetDKG::Interrupt()
     474             : {
     475           0 :     if (m_active == nullptr) return;
     476           0 :     m_qdkgsman.ForEachHandler([](CDKGSessionHandler& base) {
     477           0 :         if (auto* handler = dynamic_cast<ActiveDKGSessionHandler*>(&base)) {
     478           0 :             handler->RequestStop();
     479           0 :         }
     480           0 :     });
     481           0 : }
     482             : 
     483           0 : void NetDKG::PhaseHandlerThread(ActiveDKGSessionHandler& handler)
     484             : {
     485           0 :     while (!handler.IsStopRequested()) {
     486             :         try {
     487           0 :             LogPrint(BCLog::LLMQ_DKG, "NetDKG::%s -- %s qi[%d] - starting HandleDKGRound\n", __func__,
     488             :                      handler.params.name, handler.QuorumIndex());
     489           0 :             HandleDKGRound(handler);
     490           0 :         } catch (AbortPhaseException& e) {
     491           0 :             m_active->dkgdbgman.MarkAborted(handler.params.type, handler.QuorumIndex());
     492           0 :             LogPrint(BCLog::LLMQ_DKG, "NetDKG::%s -- %s qi[%d] - aborted current DKG session\n", __func__,
     493             :                      handler.params.name, handler.QuorumIndex());
     494           0 :         }
     495             :     }
     496           0 : }
     497             : 
     498           0 : static void AddQuorumProbeConnections(const Consensus::LLMQParams& llmqParams, CConnman& connman,
     499             :                                       CMasternodeMetaMan& mn_metaman, const CSporkManager& sporkman,
     500             :                                       const UtilParameters& util_params, const CDeterministicMNList& tip_mn_list,
     501             :                                       const uint256& myProTxHash)
     502             : {
     503           0 :     assert(mn_metaman.IsValid());
     504             : 
     505           0 :     if (!IsQuorumPoseEnabled(llmqParams.type, sporkman)) {
     506           0 :         return;
     507             :     }
     508             : 
     509           0 :     auto members = utils::GetAllQuorumMembers(llmqParams.type, util_params);
     510           0 :     auto curTime = GetTime<std::chrono::seconds>().count();
     511             : 
     512           0 :     Uint256HashSet probeConnections;
     513           0 :     for (const auto& dmn : members) {
     514           0 :         if (dmn->proTxHash == myProTxHash) {
     515           0 :             continue;
     516             :         }
     517           0 :         auto lastOutbound = mn_metaman.GetLastOutboundSuccess(dmn->proTxHash);
     518           0 :         if (curTime - lastOutbound < 10 * 60) {
     519             :             // avoid re-probing nodes too often
     520           0 :             continue;
     521             :         }
     522           0 :         probeConnections.emplace(dmn->proTxHash);
     523             :     }
     524             : 
     525           0 :     if (!probeConnections.empty()) {
     526           0 :         if (LogAcceptDebug(BCLog::LLMQ)) {
     527           0 :             std::string debugMsg = strprintf("%s -- adding masternodes probes for quorum %s:\n", __func__,
     528           0 :                                              util_params.m_base_index->GetBlockHash().ToString());
     529           0 :             for (const auto& c : probeConnections) {
     530           0 :                 auto dmn = tip_mn_list.GetValidMN(c);
     531           0 :                 if (!dmn) {
     532           0 :                     debugMsg += strprintf("  %s (not in valid MN set anymore)\n", c.ToString());
     533           0 :                 } else {
     534           0 :                     debugMsg += strprintf("  %s (%s)\n", c.ToString(),
     535           0 :                                           dmn->pdmnState->netInfo->GetPrimary().ToStringAddrPort());
     536             :                 }
     537           0 :             }
     538           0 :             LogPrint(BCLog::NET_NETCONN, debugMsg.c_str()); /* Continued */
     539           0 :         }
     540           0 :         connman.AddPendingProbeConnections(probeConnections);
     541           0 :     }
     542           0 : }
     543             : 
     544           0 : void NetDKG::HandleDKGRound(ActiveDKGSessionHandler& handler)
     545             : {
     546           0 :     auto& active = *Assert(m_active);
     547             : 
     548           0 :     handler.WaitForNextPhase(std::nullopt, QuorumPhase::Initialized);
     549             : 
     550           0 :     handler.ClearPendingMessages();
     551           0 :     uint256 curQuorumHash = handler.GetCurrentQuorumHash();
     552             : 
     553           0 :     const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(::cs_main,
     554             :                                                          return m_chainman.m_blockman.LookupBlockIndex(curQuorumHash));
     555             : 
     556           0 :     if (!pQuorumBaseBlockIndex || !handler.InitNewQuorum(pQuorumBaseBlockIndex)) {
     557             :         // should actually never happen
     558           0 :         handler.WaitForNewQuorum(curQuorumHash);
     559           0 :         throw AbortPhaseException();
     560             :     }
     561             : 
     562           0 :     active.dkgdbgman.MarkPhaseAdvanced(handler.params.type, handler.QuorumIndex(), QuorumPhase::Initialized);
     563             : 
     564           0 :     auto* curSession = handler.GetCurSession();
     565           0 :     if (handler.params.is_single_member()) {
     566           0 :         auto finalCommitment = curSession->FinalizeSingleCommitment();
     567           0 :         if (!finalCommitment.IsNull()) { // it can be null only if we are not member
     568           0 :             if (auto inv_opt = active.qblockman.AddMineableCommitment(finalCommitment); inv_opt.has_value()) {
     569           0 :                 m_peer_manager->PeerRelayInv(inv_opt.value());
     570           0 :             }
     571           0 :         }
     572           0 :         handler.WaitForNextPhase(QuorumPhase::Initialized, QuorumPhase::Contribute, curQuorumHash);
     573             :         return;
     574           0 :     }
     575             : 
     576           0 :     const auto tip_mn_list = active.dmnman.GetListAtChainTip();
     577           0 :     llmq::EnsureQuorumConnections(handler.params, active.connman, m_sporkman,
     578           0 :                                   {active.dmnman, active.qsnapman, m_chainman, pQuorumBaseBlockIndex}, tip_mn_list,
     579           0 :                                   curSession->ProTx(), /*is_masternode=*/true, handler.QuorumsWatch());
     580           0 :     if (curSession->AreWeMember()) {
     581           0 :         AddQuorumProbeConnections(handler.params, active.connman, active.mn_metaman, m_sporkman,
     582           0 :                                   {active.dmnman, active.qsnapman, m_chainman, pQuorumBaseBlockIndex}, tip_mn_list,
     583           0 :                                   curSession->ProTx());
     584           0 :     }
     585             : 
     586           0 :     handler.WaitForNextPhase(QuorumPhase::Initialized, QuorumPhase::Contribute, curQuorumHash);
     587             : 
     588             :     // Contribute
     589           0 :     auto fContributeStart = [curSession, &handler]() {
     590           0 :         if (auto qc = curSession->Contribute(); qc) {
     591           0 :             EnqueueOwn(handler.pendingContributions, *qc);
     592           0 :         }
     593           0 :     };
     594           0 :     auto fContributeWait = [this, curSession, &handler, &active] {
     595           0 :         return ProcessPendingMessageBatch<CDKGContribution>(active.connman, *curSession, handler.pendingContributions,
     596           0 :                                                             *m_peer_manager, 8);
     597             :     };
     598           0 :     handler.HandlePhase(QuorumPhase::Contribute, QuorumPhase::Complain, curQuorumHash, 0.05, fContributeStart,
     599           0 :                         fContributeWait);
     600             : 
     601             :     // Complain
     602           0 :     auto fComplainStart = [curSession, &handler, &active]() {
     603           0 :         if (auto qc = curSession->VerifyAndComplain(active.connman); qc) {
     604           0 :             EnqueueOwn(handler.pendingComplaints, *qc);
     605           0 :         }
     606           0 :     };
     607           0 :     auto fComplainWait = [this, curSession, &handler, &active] {
     608           0 :         return ProcessPendingMessageBatch<CDKGComplaint>(active.connman, *curSession, handler.pendingComplaints,
     609           0 :                                                          *m_peer_manager, 8);
     610             :     };
     611           0 :     handler.HandlePhase(QuorumPhase::Complain, QuorumPhase::Justify, curQuorumHash, 0.05, fComplainStart, fComplainWait);
     612             : 
     613             :     // Justify
     614           0 :     auto fJustifyStart = [curSession, &handler]() {
     615           0 :         if (auto qj = curSession->VerifyAndJustify(); qj) {
     616           0 :             EnqueueOwn(handler.pendingJustifications, *qj);
     617           0 :         }
     618           0 :     };
     619           0 :     auto fJustifyWait = [this, curSession, &handler, &active] {
     620           0 :         return ProcessPendingMessageBatch<CDKGJustification>(active.connman, *curSession, handler.pendingJustifications,
     621           0 :                                                              *m_peer_manager, 8);
     622             :     };
     623           0 :     handler.HandlePhase(QuorumPhase::Justify, QuorumPhase::Commit, curQuorumHash, 0.05, fJustifyStart, fJustifyWait);
     624             : 
     625             :     // Commit
     626           0 :     auto fCommitStart = [curSession, &handler]() {
     627           0 :         if (auto qc = curSession->VerifyAndCommit(); qc) {
     628           0 :             EnqueueOwn(handler.pendingPrematureCommitments, *qc);
     629           0 :         }
     630           0 :     };
     631           0 :     auto fCommitWait = [this, curSession, &handler, &active] {
     632           0 :         return ProcessPendingMessageBatch<CDKGPrematureCommitment>(active.connman, *curSession,
     633           0 :                                                                    handler.pendingPrematureCommitments, *m_peer_manager,
     634             :                                                                    8);
     635             :     };
     636           0 :     handler.HandlePhase(QuorumPhase::Commit, QuorumPhase::Finalize, curQuorumHash, 0.1, fCommitStart, fCommitWait);
     637             : 
     638           0 :     auto finalCommitments = curSession->FinalizeCommitments();
     639           0 :     for (const auto& fqc : finalCommitments) {
     640           0 :         if (auto inv_opt = active.qblockman.AddMineableCommitment(fqc); inv_opt.has_value()) {
     641           0 :             m_peer_manager->PeerRelayInv(inv_opt.value());
     642           0 :         }
     643             :     }
     644           0 : }
     645             : 
     646           0 : void NetDKGStub::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv)
     647             : {
     648           0 :     if (msg_type == NetMsgType::QCONTRIB || msg_type == NetMsgType::QCOMPLAINT || msg_type == NetMsgType::QJUSTIFICATION ||
     649           0 :         msg_type == NetMsgType::QPCOMMITMENT || msg_type == NetMsgType::QWATCH) {
     650           0 :         m_peer_manager->PeerMisbehaving(pfrom.GetId(), 10);
     651           0 :     }
     652           0 : }
     653             : 
     654             : } // namespace llmq

Generated by: LCOV version 1.16