LCOV - code coverage report
Current view: top level - src/evo - mnauth.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 103 112 92.0 %
Date: 2026-06-25 07:23:43 Functions: 5 5 100.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        4631 : void CMNAuth::PushMNAUTH(CNode& peer, CConnman& connman, const CActiveMasternodeManager& mn_activeman)
      20             : {
      21        4631 :     CMNAuth mnauth;
      22        4631 :     if (mn_activeman.GetProTxHash().IsNull()) {
      23          66 :         return;
      24             :     }
      25             : 
      26        4565 :     const auto receivedMNAuthChallenge = peer.GetReceivedMNAuthChallenge();
      27        4565 :     if (receivedMNAuthChallenge.IsNull()) {
      28         102 :         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        4463 :     int nOurNodeVersion{PROTOCOL_VERSION};
      37        8926 :     if (Params().NetworkIDString() != CBaseChainParams::MAIN && gArgs.IsArgSet("-pushversion")) {
      38          25 :         nOurNodeVersion = gArgs.GetIntArg("-pushversion", PROTOCOL_VERSION);
      39          25 :     }
      40        4463 :     const uint256 signHash{::SerializeHash(std::make_tuple(mn_activeman.GetPubKey(), receivedMNAuthChallenge, peer.IsInboundConn(), nOurNodeVersion))};
      41             : 
      42        4463 :     mnauth.proRegTxHash = mn_activeman.GetProTxHash();
      43             : 
      44             :     // all clients uses basic BLS
      45        4463 :     mnauth.sig = mn_activeman.Sign(signHash, false);
      46             : 
      47        4463 :     LogPrint(BCLog::NET_NETCONN, "CMNAuth::%s -- Sending MNAUTH, peer=%d\n", __func__, peer.GetId());
      48        4463 :     connman.PushMessage(&peer, CNetMsgMaker(peer.GetCommonVersion()).Make(NetMsgType::MNAUTH, mnauth));
      49        4631 : }
      50             : 
      51      105757 : 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      105757 :     assert(mn_metaman.IsValid());
      56             : 
      57      105757 :     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      101378 :         return {};
      60             :     }
      61             : 
      62        4379 :     CMNAuth mnauth;
      63        4379 :     vRecv >> mnauth;
      64             : 
      65             :     // only one MNAUTH allowed
      66        4379 :     if (!peer.GetVerifiedProRegTxHash().IsNull()) {
      67           0 :         return MisbehavingError{100, "duplicate mnauth"};
      68             :     }
      69             : 
      70        4379 :     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        4379 :     if (mnauth.proRegTxHash.IsNull()) {
      76           0 :         return MisbehavingError{100, "empty mnauth proRegTxHash"};
      77             :     }
      78             : 
      79        4379 :     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        4379 :     const auto dmn = tip_mn_list.GetMN(mnauth.proRegTxHash);
      86        4379 :     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        4379 :     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        4379 :     const uint256 signHash{::SerializeHash(std::make_tuple(pubKey, peer.GetSentMNAuthChallenge(), !peer.IsInboundConn(), peer.nVersion.load()))};
      96        4379 :     LogPrint(BCLog::NET_NETCONN, "CMNAuth::%s -- constructed signHash for nVersion %d, peer=%d\n", __func__, peer.nVersion, peer.GetId());
      97             : 
      98        4379 :     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           3 :         return MisbehavingError{10, "mnauth signature verification failed"};
     102             :     }
     103             : 
     104        4376 :     if (!peer.IsInboundConn()) {
     105        1966 :         mn_metaman.SetLastOutboundSuccess(mnauth.proRegTxHash, GetTime<std::chrono::seconds>().count());
     106        1966 :         if (peer.m_masternode_probe_connection) {
     107         246 :             LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- Masternode probe successful for %s, disconnecting. peer=%d\n",
     108             :                      mnauth.proRegTxHash.ToString(), peer.GetId());
     109         246 :             peer.fDisconnect = true;
     110         246 :             return {};
     111             :         }
     112        1720 :     }
     113             : 
     114        4130 :     const uint256 myProTxHash = mn_activeman != nullptr ? mn_activeman->GetProTxHash() : uint256();
     115             : 
     116       22821 :     connman.ForEachNode([&](CNode* pnode2) {
     117       18691 :         if (peer.fDisconnect) {
     118             :             // we've already disconnected the new peer
     119          61 :             return;
     120             :         }
     121             : 
     122       18630 :         if (pnode2->GetVerifiedProRegTxHash() == mnauth.proRegTxHash) {
     123         247 :             if (mn_activeman != nullptr && !myProTxHash.IsNull()) {
     124         247 :                 const auto deterministicOutbound = llmq::utils::DeterministicOutboundConnection(myProTxHash, mnauth.proRegTxHash);
     125         247 :                 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         247 :                 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         116 :                     if (pnode2->IsInboundConn()) {
     131          73 :                         LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- marking old inbound for dropping it later, peer=%d\n", pnode2->GetId());
     132          73 :                         pnode2->m_masternode_probe_connection = true;
     133         116 :                     } else if (peer.IsInboundConn()) {
     134          43 :                         LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- marking new inbound for dropping it later, peer=%d\n", peer.GetId());
     135          43 :                         peer.m_masternode_probe_connection = true;
     136          43 :                     }
     137         116 :                 } else {
     138         131 :                     if (!pnode2->IsInboundConn()) {
     139          87 :                         LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- dropping old outbound, peer=%d\n", pnode2->GetId());
     140          87 :                         pnode2->fDisconnect = true;
     141         131 :                     } else if (!peer.IsInboundConn()) {
     142          44 :                         LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- dropping new outbound, peer=%d\n", peer.GetId());
     143          44 :                         peer.fDisconnect = true;
     144          44 :                     }
     145             :                 }
     146         247 :             } 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         247 :         }
     152       18691 :     });
     153             : 
     154        4130 :     if (peer.fDisconnect) {
     155          49 :         return {};
     156             :     }
     157             : 
     158        4081 :     peer.SetVerifiedProRegTxHash(mnauth.proRegTxHash);
     159        4081 :     peer.SetVerifiedPubKeyHash(dmn->pdmnState->pubKeyOperator.GetHash());
     160             : 
     161        8162 :     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        2782 :         const CNetMsgMaker msgMaker(peer.GetCommonVersion());
     167        2782 :         connman.PushMessage(&peer, msgMaker.Make(NetMsgType::QSENDRECSIGS, true));
     168        2782 :         peer.m_masternode_iqr_connection = true;
     169        2782 :     }
     170             : 
     171        4081 :     LogPrint(BCLog::NET_NETCONN, "CMNAuth::%s -- Valid MNAUTH for %s, peer=%d\n", __func__, mnauth.proRegTxHash.ToString(), peer.GetId());
     172        4081 :     return {};
     173      105757 : }
     174             : 
     175       99741 : 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       99741 :     if (diff.updatedMNs.empty() && diff.removedMns.empty()) {
     179         245 :         return;
     180             :     }
     181             : 
     182      474176 :     connman.ForEachNode([&oldMNList, &diff](CNode* pnode) {
     183      374680 :         const auto verifiedProRegTxHash = pnode->GetVerifiedProRegTxHash();
     184      374680 :         if (verifiedProRegTxHash.IsNull()) {
     185       99121 :             return;
     186             :         }
     187      275559 :         const auto verifiedDmn = oldMNList.GetMN(verifiedProRegTxHash);
     188      275559 :         if (!verifiedDmn) {
     189          48 :             return;
     190             :         }
     191      275511 :         bool doRemove = false;
     192      275511 :         if (diff.removedMns.count(verifiedDmn->GetInternalId())) {
     193           0 :             doRemove = true;
     194      275511 :         } else if (const auto it = diff.updatedMNs.find(verifiedDmn->GetInternalId()); it != diff.updatedMNs.end()) {
     195       50721 :             if ((it->second.fields & CDeterministicMNStateDiff::Field_pubKeyOperator) && it->second.state.pubKeyOperator.GetHash() != pnode->GetVerifiedPubKeyHash()) {
     196          11 :                 doRemove = true;
     197          11 :             }
     198       50710 :         }
     199             : 
     200      275511 :         if (doRemove) {
     201          11 :             LogPrint(BCLog::NET_NETCONN, "CMNAuth::NotifyMasternodeListChanged -- Disconnecting MN %s due to key changed/removed, peer=%d\n",
     202             :                      verifiedProRegTxHash.ToString(), pnode->GetId());
     203          11 :             pnode->fDisconnect = true;
     204          11 :         }
     205      374680 :     });
     206       99741 : }

Generated by: LCOV version 1.16