LCOV - code coverage report
Current view: top level - src/evo - mnauth.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 82 110 74.5 %
Date: 2025-02-23 09:33:43 Functions: 6 6 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2019-2021 The Dash Core 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 "evo/mnauth.h"
       7             : 
       8             : #include "activemasternode.h"
       9             : #include "chainparams.h"
      10             : #include "consensus/validation.h"
      11             : #include "net.h" // for CSerializedNetMsg
      12             : #include "netmessagemaker.h"
      13             : #include "llmq/quorums_connections.h"
      14             : #include "tiertwo/masternode_meta_manager.h"
      15             : #include "tiertwo/net_masternodes.h"
      16             : #include "tiertwo/tiertwo_sync_state.h"
      17             : #include "util/system.h" // for fMasternode and gArgs access
      18             : 
      19             : #include "version.h" // for MNAUTH_NODE_VER_VERSION
      20             : 
      21        1309 : void CMNAuth::PushMNAUTH(CNode* pnode, CConnman& connman)
      22             : {
      23        1309 :     const CActiveMasternodeInfo* activeMnInfo{nullptr};
      24        1309 :     if (!fMasterNode || !activeMasternodeManager ||
      25         356 :         (activeMnInfo = activeMasternodeManager->GetInfo())->proTxHash.IsNull()) {
      26        1150 :         return;
      27             :     }
      28             : 
      29         178 :     uint256 signHash;
      30         178 :     {
      31         178 :         LOCK(pnode->cs_mnauth);
      32         946 :         if (pnode->receivedMNAuthChallenge.IsNull()) {
      33          38 :             return;
      34             :         }
      35             :         // We include fInbound in signHash to forbid interchanging of challenges by a man in the middle (MITM). This way
      36             :         // we protect ourselves against MITM in this form:
      37             :         //   node1 <- Eve -> node2
      38             :         // It does not protect against:
      39             :         //   node1 -> Eve -> node2
      40             :         // This is ok as we only use MNAUTH as a DoS protection and not for sensitive stuff
      41         159 :         int nOurNodeVersion{PROTOCOL_VERSION};
      42         636 :         if (Params().NetworkIDString() != CBaseChainParams::MAIN && gArgs.IsArgSet("-pushversion")) {
      43           0 :             nOurNodeVersion = (int)gArgs.GetArg("-pushversion", PROTOCOL_VERSION);
      44             :         }
      45         159 :         if (pnode->nVersion < MNAUTH_NODE_VER_VERSION || nOurNodeVersion < MNAUTH_NODE_VER_VERSION) {
      46           0 :             signHash = ::SerializeHash(std::make_tuple(activeMnInfo->pubKeyOperator, pnode->receivedMNAuthChallenge, pnode->fInbound));
      47             :         } else {
      48         159 :             signHash = ::SerializeHash(std::make_tuple(activeMnInfo->pubKeyOperator, pnode->receivedMNAuthChallenge, pnode->fInbound, nOurNodeVersion));
      49             :         }
      50             :     }
      51             : 
      52         159 :     CMNAuth mnauth;
      53         159 :     mnauth.proRegTxHash = activeMnInfo->proTxHash;
      54         159 :     mnauth.sig = activeMnInfo->keyOperator.Sign(signHash);
      55             : 
      56         159 :     LogPrint(BCLog::NET_MN, "CMNAuth::%s -- Sending MNAUTH, peer=%d\n", __func__, pnode->GetId());
      57         159 :     connman.PushMessage(pnode, CNetMsgMaker(pnode->GetSendVersion()).Make(NetMsgType::MNAUTH, mnauth));
      58             : }
      59             : 
      60       55734 : bool CMNAuth::ProcessMessage(CNode* pnode, const std::string& strCommand, CDataStream& vRecv, CConnman& connman, CValidationState& state)
      61             : {
      62       55734 :     if (!g_tiertwo_sync_state.IsBlockchainSynced()) {
      63             :         // we can't verify MNAUTH messages when we don't have the latest MN list
      64             :         return true;
      65             :     }
      66             : 
      67       55724 :     if (strCommand == NetMsgType::MNAUTH) {
      68         159 :         CMNAuth mnauth;
      69         159 :         vRecv >> mnauth;
      70             :         // only one MNAUTH allowed
      71         477 :         bool fAlreadyHaveMNAUTH = WITH_LOCK(pnode->cs_mnauth, return !pnode->verifiedProRegTxHash.IsNull(););
      72         159 :         if (fAlreadyHaveMNAUTH) {
      73           0 :             return state.DoS(100, false, REJECT_INVALID, "duplicate mnauth");
      74             :         }
      75             : 
      76         159 :         if ((~pnode->nServices) & (NODE_NETWORK | NODE_BLOOM)) {
      77             :             // either NODE_NETWORK or NODE_BLOOM bit is missing in node's services
      78           0 :             return state.DoS(100, false, REJECT_INVALID, "mnauth from a node with invalid services");
      79             :         }
      80             : 
      81         320 :         if (mnauth.proRegTxHash.IsNull()) {
      82           0 :             return state.DoS(100, false, REJECT_INVALID, "empty mnauth proRegTxHash");
      83             :         }
      84             : 
      85         159 :         if (!mnauth.sig.IsValid()) {
      86           0 :             return state.DoS(100, false, REJECT_INVALID, "invalid mnauth signature");
      87             :         }
      88             : 
      89         159 :         auto mnList = deterministicMNManager->GetListAtChainTip();
      90         269 :         auto dmn = mnList.GetMN(mnauth.proRegTxHash);
      91         159 :         if (!dmn) {
      92             :             // in case node was unlucky and not up to date, just let it be connected as a regular node, which gives it
      93             :             // a chance to get up-to-date and thus realize that it's not a MN anymore. We still give it a
      94             :             // low DoS score.
      95           0 :             return state.DoS(10, false, REJECT_INVALID, "missing mnauth masternode");
      96             :         }
      97             : 
      98         159 :         uint256 signHash;
      99         159 :         {
     100         159 :             LOCK(pnode->cs_mnauth);
     101         159 :             int nOurNodeVersion{PROTOCOL_VERSION};
     102         636 :             if (Params().NetworkIDString() != CBaseChainParams::MAIN && gArgs.IsArgSet("-pushversion")) {
     103           0 :                 nOurNodeVersion = gArgs.GetArg("-pushversion", PROTOCOL_VERSION);
     104             :             }
     105             :             // See comment in PushMNAUTH (fInbound is negated here as we're on the other side of the connection)
     106         159 :             if (pnode->nVersion < MNAUTH_NODE_VER_VERSION || nOurNodeVersion < MNAUTH_NODE_VER_VERSION) {
     107           0 :                 signHash = ::SerializeHash(std::make_tuple(dmn->pdmnState->pubKeyOperator, pnode->sentMNAuthChallenge, !pnode->fInbound));
     108             :             } else {
     109         477 :                 signHash = ::SerializeHash(std::make_tuple(dmn->pdmnState->pubKeyOperator, pnode->sentMNAuthChallenge, !pnode->fInbound, pnode->nVersion.load()));
     110             :             }
     111         159 :             LogPrint(BCLog::NET_MN, "CMNAuth::%s -- constructed signHash for nVersion %d, peer=%d\n", __func__, pnode->nVersion, pnode->GetId());
     112             :         }
     113             : 
     114         159 :         if (!mnauth.sig.VerifyInsecure(dmn->pdmnState->pubKeyOperator.Get(), signHash)) {
     115             :             // Same as above, MN seems to not know its fate yet, so give it a chance to update. If this is a
     116             :             // malicious node (DoSing us), it'll get banned soon.
     117           0 :             return state.DoS(10, false, REJECT_INVALID, "mnauth signature verification failed");
     118             :         }
     119             : 
     120         159 :         if (!pnode->fInbound) {
     121         104 :             g_mmetaman.GetMetaInfo(mnauth.proRegTxHash)->SetLastOutboundSuccess(GetAdjustedTime());
     122         104 :             if (pnode->m_masternode_probe_connection) {
     123          98 :                 LogPrint(BCLog::NET_MN, "%s -- Masternode probe successful for %s, disconnecting. peer=%d\n",
     124             :                          __func__, mnauth.proRegTxHash.ToString(), pnode->GetId());
     125          49 :                 pnode->fDisconnect = true;
     126          49 :                 return true;
     127             :             }
     128             :         }
     129             : 
     130             :         // future: Move this to the first line of this function..
     131         110 :         const CActiveMasternodeInfo* activeMnInfo{nullptr};
     132         110 :         if (!fMasterNode || !activeMasternodeManager ||
     133         220 :             (activeMnInfo = activeMasternodeManager->GetInfo())->proTxHash.IsNull()) {
     134           0 :             return true;
     135             :         }
     136             : 
     137         110 :         connman.ForEachNode([&](CNode* pnode2) {
     138        1088 :             if (pnode->fDisconnect) {
     139             :                 // we've already disconnected the new peer
     140             :                 return;
     141             :             }
     142             : 
     143        1088 :             if (pnode2->verifiedProRegTxHash == mnauth.proRegTxHash) {
     144           0 :                 if (fMasterNode) {
     145           0 :                     auto deterministicOutbound = llmq::DeterministicOutboundConnection(activeMnInfo->proTxHash, mnauth.proRegTxHash);
     146           0 :                     LogPrint(BCLog::NET_MN, "CMNAuth::ProcessMessage -- Masternode %s has already verified as peer %d, deterministicOutbound=%s. peer=%d\n",
     147             :                              mnauth.proRegTxHash.ToString(), pnode2->GetId(), deterministicOutbound.ToString(), pnode->GetId());
     148           0 :                     if (deterministicOutbound == activeMnInfo->proTxHash) {
     149           0 :                         if (pnode2->fInbound) {
     150           0 :                             LogPrint(BCLog::NET_MN, "CMNAuth::ProcessMessage -- dropping old inbound, peer=%d\n", pnode2->GetId());
     151           0 :                             pnode2->fDisconnect = true;
     152           0 :                         } else if (pnode->fInbound) {
     153           0 :                             LogPrint(BCLog::NET_MN, "CMNAuth::ProcessMessage -- dropping new inbound, peer=%d\n", pnode->GetId());
     154           0 :                             pnode->fDisconnect = true;
     155             :                         }
     156             :                     } else {
     157           0 :                         if (!pnode2->fInbound) {
     158           0 :                             LogPrint(BCLog::NET_MN, "CMNAuth::ProcessMessage -- dropping old outbound, peer=%d\n", pnode2->GetId());
     159           0 :                             pnode2->fDisconnect = true;
     160           0 :                         } else if (!pnode->fInbound) {
     161           0 :                             LogPrint(BCLog::NET_MN, "CMNAuth::ProcessMessage -- dropping new outbound, peer=%d\n", pnode->GetId());
     162           0 :                             pnode->fDisconnect = true;
     163             :                         }
     164             :                     }
     165             :                 } else {
     166           0 :                     LogPrint(BCLog::NET_MN, "CMNAuth::ProcessMessage -- Masternode %s has already verified as peer %d, dropping new connection. peer=%d\n",
     167             :                              mnauth.proRegTxHash.ToString(), pnode2->GetId(), pnode->GetId());
     168        1088 :                     pnode->fDisconnect = true;
     169             :                 }
     170             :             }
     171             :         });
     172             : 
     173         110 :         if (pnode->fDisconnect) {
     174             :             return true;
     175             :         }
     176             : 
     177         110 :         {
     178         110 :             LOCK(pnode->cs_mnauth);
     179         110 :             pnode->verifiedProRegTxHash = mnauth.proRegTxHash;
     180         110 :             pnode->verifiedPubKeyHash = dmn->pdmnState->pubKeyOperator.GetHash();
     181             :         }
     182             : 
     183         110 :         if (!pnode->m_masternode_iqr_connection && connman.GetTierTwoConnMan()->isMasternodeQuorumRelayMember(pnode->verifiedProRegTxHash)) {
     184             :             // Tell our peer that we're interested in plain LLMQ recovered signatures.
     185             :             // Otherwise, the peer would only announce/send messages resulting from QRECSIG,
     186             :             // future e.g. tx locks or chainlocks. SPV and regular full nodes should not send
     187             :             // this message as they are usually only interested in the higher level messages.
     188         100 :             CNetMsgMaker msgMaker(pnode->GetSendVersion());
     189         100 :             connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QSENDRECSIGS, true));
     190         100 :             pnode->m_masternode_iqr_connection = true;
     191             :         }
     192             : 
     193         220 :         LogPrint(BCLog::NET_MN, "CMNAuth::%s -- Valid MNAUTH for %s, peer=%d\n", __func__, mnauth.proRegTxHash.ToString(), pnode->GetId());
     194             :     }
     195             :     return true;
     196             : }
     197             : 
     198        7251 : void CMNAuth::NotifyMasternodeListChanged(bool undo, const CDeterministicMNList& oldMNList, const CDeterministicMNListDiff& diff)
     199             : {
     200             :     // we're only interested in updated/removed MNs. Added MNs are of no interest for us
     201        7251 :     if (diff.updatedMNs.empty() && diff.removedMns.empty()) {
     202             :         return;
     203             :     }
     204             : 
     205        7179 :     g_connman->ForEachNode([&](CNode* pnode) {
     206       80155 :         LOCK(pnode->cs_mnauth);
     207     2030050 :         if (pnode->verifiedProRegTxHash.IsNull()) {
     208       60933 :             return;
     209             :         }
     210       19222 :         auto verifiedDmn = oldMNList.GetMN(pnode->verifiedProRegTxHash);
     211        9611 :         if (!verifiedDmn) {
     212       60933 :             return;
     213             :         }
     214        9611 :         bool doRemove = false;
     215        9611 :         if (diff.removedMns.count(verifiedDmn->GetInternalId())) {
     216             :             doRemove = true;
     217             :         } else {
     218        9611 :             auto it = diff.updatedMNs.find(verifiedDmn->GetInternalId());
     219        9611 :             if (it != diff.updatedMNs.end()) {
     220        2190 :                 if ((it->second.fields & CDeterministicMNStateDiff::Field_pubKeyOperator) && it->second.state.pubKeyOperator.GetHash() != pnode->verifiedPubKeyHash) {
     221           4 :                     doRemove = true;
     222             :                 }
     223             :             }
     224             :         }
     225             : 
     226        9611 :         if (doRemove) {
     227           8 :             LogPrint(BCLog::NET_MN, "CMNAuth::NotifyMasternodeListChanged -- Disconnecting MN %s due to key changed/removed, peer=%d\n",
     228             :                      pnode->verifiedProRegTxHash.ToString(), pnode->GetId());
     229        9611 :             pnode->fDisconnect = true;
     230             :         }
     231             :     });
     232             : }

Generated by: LCOV version 1.14