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

          Line data    Source code
       1             : // Copyright (c) 2019-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 <evo/mnauth.h>
       6             : 
       7             : #include <active/masternode.h>
       8             : #include <bls/bls.h>
       9             : #include <evo/deterministicmns.h>
      10             : #include <llmq/utils.h>
      11             : #include <masternode/meta.h>
      12             : #include <masternode/sync.h>
      13             : 
      14             : #include <chainparams.h>
      15             : #include <net.h>
      16             : #include <netmessagemaker.h>
      17             : #include <util/time.h>
      18             : 
      19           0 : void CMNAuth::PushMNAUTH(CNode& peer, CConnman& connman, const CActiveMasternodeManager& mn_activeman)
      20             : {
      21           0 :     CMNAuth mnauth;
      22           0 :     if (mn_activeman.GetProTxHash().IsNull()) {
      23           0 :         return;
      24             :     }
      25             : 
      26           0 :     const auto receivedMNAuthChallenge = peer.GetReceivedMNAuthChallenge();
      27           0 :     if (receivedMNAuthChallenge.IsNull()) {
      28           0 :         return;
      29             :     }
      30             :     // We include fInbound in signHash to forbid interchanging of challenges by a man in the middle (MITM). This way
      31             :     // we protect ourselves against MITM in this form:
      32             :     //   node1 <- Eve -> node2
      33             :     // It does not protect against:
      34             :     //   node1 -> Eve -> node2
      35             :     // This is ok as we only use MNAUTH as a DoS protection and not for sensitive stuff
      36           0 :     int nOurNodeVersion{PROTOCOL_VERSION};
      37           0 :     if (Params().NetworkIDString() != CBaseChainParams::MAIN && gArgs.IsArgSet("-pushversion")) {
      38           0 :         nOurNodeVersion = gArgs.GetIntArg("-pushversion", PROTOCOL_VERSION);
      39           0 :     }
      40           0 :     const uint256 signHash{::SerializeHash(std::make_tuple(mn_activeman.GetPubKey(), receivedMNAuthChallenge, peer.IsInboundConn(), nOurNodeVersion))};
      41             : 
      42           0 :     mnauth.proRegTxHash = mn_activeman.GetProTxHash();
      43             : 
      44             :     // all clients uses basic BLS
      45           0 :     mnauth.sig = mn_activeman.Sign(signHash, false);
      46             : 
      47           0 :     LogPrint(BCLog::NET_NETCONN, "CMNAuth::%s -- Sending MNAUTH, peer=%d\n", __func__, peer.GetId());
      48           0 :     connman.PushMessage(&peer, CNetMsgMaker(peer.GetCommonVersion()).Make(NetMsgType::MNAUTH, mnauth));
      49           0 : }
      50             : 
      51           0 : MessageProcessingResult CMNAuth::ProcessMessage(CNode& peer, ServiceFlags node_services, CConnman& connman, CMasternodeMetaMan& mn_metaman,
      52             :                                                 const CActiveMasternodeManager* const mn_activeman, const CMasternodeSync& mn_sync,
      53             :                                                 const CDeterministicMNList& tip_mn_list, std::string_view msg_type, CDataStream& vRecv)
      54             : {
      55           0 :     assert(mn_metaman.IsValid());
      56             : 
      57           0 :     if (msg_type != NetMsgType::MNAUTH || !mn_sync.IsBlockchainSynced()) {
      58             :         // we can't verify MNAUTH messages when we don't have the latest MN list
      59           0 :         return {};
      60             :     }
      61             : 
      62           0 :     CMNAuth mnauth;
      63           0 :     vRecv >> mnauth;
      64             : 
      65             :     // only one MNAUTH allowed
      66           0 :     if (!peer.GetVerifiedProRegTxHash().IsNull()) {
      67           0 :         return MisbehavingError{100, "duplicate mnauth"};
      68             :     }
      69             : 
      70           0 :     if ((~node_services) & (NODE_NETWORK | NODE_BLOOM)) {
      71             :         // either NODE_NETWORK or NODE_BLOOM bit is missing in node's services
      72           0 :         return MisbehavingError{100, "mnauth from a node with invalid services"};
      73             :     }
      74             : 
      75           0 :     if (mnauth.proRegTxHash.IsNull()) {
      76           0 :         return MisbehavingError{100, "empty mnauth proRegTxHash"};
      77             :     }
      78             : 
      79           0 :     if (!mnauth.sig.IsValid()) {
      80           0 :         LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- invalid mnauth for protx=%s with sig=%s\n",
      81             :                  mnauth.proRegTxHash.ToString(), mnauth.sig.ToString(false));
      82           0 :         return MisbehavingError{100, "invalid mnauth signature"};
      83             :     }
      84             : 
      85           0 :     const auto dmn = tip_mn_list.GetMN(mnauth.proRegTxHash);
      86           0 :     if (!dmn) {
      87             :         // in case node was unlucky and not up to date, just let it be connected as a regular node, which gives it
      88             :         // a chance to get up-to-date and thus realize that it's not a MN anymore. We still give it a
      89             :         // low DoS score.
      90           0 :         return MisbehavingError{10, "missing mnauth masternode"};
      91             :     }
      92             : 
      93           0 :     const CBLSPublicKey pubKey(dmn->pdmnState->pubKeyOperator.Get());
      94             :     // See comment in PushMNAUTH (fInbound is negated here as we're on the other side of the connection)
      95           0 :     const uint256 signHash{::SerializeHash(std::make_tuple(pubKey, peer.GetSentMNAuthChallenge(), !peer.IsInboundConn(), peer.nVersion.load()))};
      96           0 :     LogPrint(BCLog::NET_NETCONN, "CMNAuth::%s -- constructed signHash for nVersion %d, peer=%d\n", __func__, peer.nVersion, peer.GetId());
      97             : 
      98           0 :     if (!mnauth.sig.VerifyInsecure(dmn->pdmnState->pubKeyOperator.Get(), signHash, false)) {
      99             :         // Same as above, MN seems to not know its fate yet, so give it a chance to update. If this is a
     100             :         // malicious node (DoSing us), it'll get banned soon.
     101           0 :         return MisbehavingError{10, "mnauth signature verification failed"};
     102             :     }
     103             : 
     104           0 :     if (!peer.IsInboundConn()) {
     105           0 :         mn_metaman.SetLastOutboundSuccess(mnauth.proRegTxHash, GetTime<std::chrono::seconds>().count());
     106           0 :         if (peer.m_masternode_probe_connection) {
     107           0 :             LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- Masternode probe successful for %s, disconnecting. peer=%d\n",
     108             :                      mnauth.proRegTxHash.ToString(), peer.GetId());
     109           0 :             peer.fDisconnect = true;
     110           0 :             return {};
     111             :         }
     112           0 :     }
     113             : 
     114           0 :     const uint256 myProTxHash = mn_activeman != nullptr ? mn_activeman->GetProTxHash() : uint256();
     115             : 
     116           0 :     connman.ForEachNode([&](CNode* pnode2) {
     117           0 :         if (peer.fDisconnect) {
     118             :             // we've already disconnected the new peer
     119           0 :             return;
     120             :         }
     121             : 
     122           0 :         if (pnode2->GetVerifiedProRegTxHash() == mnauth.proRegTxHash) {
     123           0 :             if (mn_activeman != nullptr && !myProTxHash.IsNull()) {
     124           0 :                 const auto deterministicOutbound = llmq::utils::DeterministicOutboundConnection(myProTxHash, mnauth.proRegTxHash);
     125           0 :                 LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- Masternode %s has already verified as peer %d, deterministicOutbound=%s. peer=%d\n",
     126             :                          mnauth.proRegTxHash.ToString(), pnode2->GetId(), deterministicOutbound.ToString(), peer.GetId());
     127           0 :                 if (deterministicOutbound == myProTxHash) {
     128             :                     // NOTE: do not drop inbound nodes here, mark them as probes so that
     129             :                     // they would be disconnected later in CMasternodeUtils::DoMaintenance
     130           0 :                     if (pnode2->IsInboundConn()) {
     131           0 :                         LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- marking old inbound for dropping it later, peer=%d\n", pnode2->GetId());
     132           0 :                         pnode2->m_masternode_probe_connection = true;
     133           0 :                     } else if (peer.IsInboundConn()) {
     134           0 :                         LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- marking new inbound for dropping it later, peer=%d\n", peer.GetId());
     135           0 :                         peer.m_masternode_probe_connection = true;
     136           0 :                     }
     137           0 :                 } else {
     138           0 :                     if (!pnode2->IsInboundConn()) {
     139           0 :                         LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- dropping old outbound, peer=%d\n", pnode2->GetId());
     140           0 :                         pnode2->fDisconnect = true;
     141           0 :                     } else if (!peer.IsInboundConn()) {
     142           0 :                         LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- dropping new outbound, peer=%d\n", peer.GetId());
     143           0 :                         peer.fDisconnect = true;
     144           0 :                     }
     145             :                 }
     146           0 :             } else {
     147           0 :                 LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- Masternode %s has already verified as peer %d, dropping new connection. peer=%d\n",
     148             :                          mnauth.proRegTxHash.ToString(), pnode2->GetId(), peer.GetId());
     149           0 :                 peer.fDisconnect = true;
     150             :             }
     151           0 :         }
     152           0 :     });
     153             : 
     154           0 :     if (peer.fDisconnect) {
     155           0 :         return {};
     156             :     }
     157             : 
     158           0 :     peer.SetVerifiedProRegTxHash(mnauth.proRegTxHash);
     159           0 :     peer.SetVerifiedPubKeyHash(dmn->pdmnState->pubKeyOperator.GetHash());
     160             : 
     161           0 :     if (!peer.m_masternode_iqr_connection && connman.IsMasternodeQuorumRelayMember(peer.GetVerifiedProRegTxHash())) {
     162             :         // Tell our peer that we're interested in plain LLMQ recovered signatures.
     163             :         // Otherwise, the peer would only announce/send messages resulting from QRECSIG,
     164             :         // e.g. InstantSend locks or ChainLocks. SPV and regular full nodes should not send
     165             :         // this message as they are usually only interested in the higher level messages.
     166           0 :         const CNetMsgMaker msgMaker(peer.GetCommonVersion());
     167           0 :         connman.PushMessage(&peer, msgMaker.Make(NetMsgType::QSENDRECSIGS, true));
     168           0 :         peer.m_masternode_iqr_connection = true;
     169           0 :     }
     170             : 
     171           0 :     LogPrint(BCLog::NET_NETCONN, "CMNAuth::%s -- Valid MNAUTH for %s, peer=%d\n", __func__, mnauth.proRegTxHash.ToString(), peer.GetId());
     172           0 :     return {};
     173           0 : }
     174             : 
     175           0 : void CMNAuth::NotifyMasternodeListChanged(bool undo, const CDeterministicMNList& oldMNList, const CDeterministicMNListDiff& diff, CConnman& connman)
     176             : {
     177             :     // we're only interested in updated/removed MNs. Added MNs are of no interest for us
     178           0 :     if (diff.updatedMNs.empty() && diff.removedMns.empty()) {
     179           0 :         return;
     180             :     }
     181             : 
     182           0 :     connman.ForEachNode([&oldMNList, &diff](CNode* pnode) {
     183           0 :         const auto verifiedProRegTxHash = pnode->GetVerifiedProRegTxHash();
     184           0 :         if (verifiedProRegTxHash.IsNull()) {
     185           0 :             return;
     186             :         }
     187           0 :         const auto verifiedDmn = oldMNList.GetMN(verifiedProRegTxHash);
     188           0 :         if (!verifiedDmn) {
     189           0 :             return;
     190             :         }
     191           0 :         bool doRemove = false;
     192           0 :         if (diff.removedMns.count(verifiedDmn->GetInternalId())) {
     193           0 :             doRemove = true;
     194           0 :         } else if (const auto it = diff.updatedMNs.find(verifiedDmn->GetInternalId()); it != diff.updatedMNs.end()) {
     195           0 :             if ((it->second.fields & CDeterministicMNStateDiff::Field_pubKeyOperator) && it->second.state.pubKeyOperator.GetHash() != pnode->GetVerifiedPubKeyHash()) {
     196           0 :                 doRemove = true;
     197           0 :             }
     198           0 :         }
     199             : 
     200           0 :         if (doRemove) {
     201           0 :             LogPrint(BCLog::NET_NETCONN, "CMNAuth::NotifyMasternodeListChanged -- Disconnecting MN %s due to key changed/removed, peer=%d\n",
     202             :                      verifiedProRegTxHash.ToString(), pnode->GetId());
     203           0 :             pnode->fDisconnect = true;
     204           0 :         }
     205           0 :     });
     206           0 : }

Generated by: LCOV version 1.16