LCOV - code coverage report
Current view: top level - src/active - masternode.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 111 170 65.3 %
Date: 2026-06-25 07:23:43 Functions: 21 23 91.3 %

          Line data    Source code
       1             : // Copyright (c) 2014-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 <active/masternode.h>
       6             : 
       7             : #include <bls/bls_ies.h>
       8             : #include <chainparams.h>
       9             : #include <deploymentstatus.h>
      10             : #include <evo/deterministicmns.h>
      11             : #include <net.h>
      12             : #include <netbase.h>
      13             : #include <protocol.h>
      14             : #include <util/check.h>
      15             : 
      16             : namespace {
      17        3340 : bool GetLocal(CService& addr, const CNetAddr* paddrPeer)
      18             : {
      19        3340 :     if (!fListen)
      20         193 :         return false;
      21             : 
      22        3147 :     int nBestScore = -1;
      23             :     {
      24        3147 :         LOCK(g_maplocalhost_mutex);
      25        3147 :         int nBestReachability = -1;
      26        3147 :         for (const auto& entry : mapLocalHost)
      27             :         {
      28             :             // For privacy reasons, don't advertise our privacy-network address
      29             :             // to other networks and don't advertise our other-network address
      30             :             // to privacy networks.
      31           0 :             const Network our_net{entry.first.GetNetwork()};
      32           0 :             const Network peers_net{paddrPeer->GetNetwork()};
      33           0 :             if (our_net != peers_net &&
      34           0 :                 (our_net == NET_ONION || our_net == NET_I2P ||
      35           0 :                  peers_net == NET_ONION || peers_net == NET_I2P)) {
      36           0 :                 continue;
      37             :             }
      38           0 :             int nScore = entry.second.nScore;
      39           0 :             int nReachability = entry.first.GetReachabilityFrom(*paddrPeer);
      40           0 :             if (nReachability > nBestReachability || (nReachability == nBestReachability && nScore > nBestScore))
      41             :             {
      42           0 :                 addr = CService(entry.first, entry.second.nPort);
      43           0 :                 nBestReachability = nReachability;
      44           0 :                 nBestScore = nScore;
      45           0 :             }
      46             :         }
      47        3147 :     }
      48        3147 :     return nBestScore >= 0;
      49        3340 : }
      50             : } // anonymous namespace
      51             : 
      52        1322 : CActiveMasternodeManager::CActiveMasternodeManager(CConnman& connman, CDeterministicMNManager& dmnman,
      53             :                                                    const CBLSSecretKey& sk) :
      54         661 :     m_connman{connman},
      55         661 :     m_dmnman{dmnman},
      56         661 :     m_operator_pk{sk.GetPublicKey()},
      57         661 :     m_operator_sk{sk}
      58         661 : {
      59             :     assert(sk.IsValid()); /* We can assume pk is valid if sk is valid */
      60             :     LogPrintf("MASTERNODE:\n  blsPubKeyOperator legacy: %s\n  blsPubKeyOperator basic: %s\n",
      61             :               m_operator_pk.ToString(/*specificLegacyScheme=*/true),
      62             :               m_operator_pk.ToString(/*specificLegacyScheme=*/false));
      63         661 : }
      64             : 
      65        1322 : CActiveMasternodeManager::~CActiveMasternodeManager() = default;
      66             : 
      67          12 : std::string CActiveMasternodeManager::GetStateString() const
      68             : {
      69          24 :     switch (WITH_READ_LOCK(cs, return m_state)) {
      70             :     case MasternodeState::WAITING_FOR_PROTX:
      71           0 :         return "WAITING_FOR_PROTX";
      72             :     case MasternodeState::POSE_BANNED:
      73           0 :         return "POSE_BANNED";
      74             :     case MasternodeState::REMOVED:
      75           0 :         return "REMOVED";
      76             :     case MasternodeState::OPERATOR_KEY_CHANGED:
      77           0 :         return "OPERATOR_KEY_CHANGED";
      78             :     case MasternodeState::PROTX_IP_CHANGED:
      79           0 :         return "PROTX_IP_CHANGED";
      80             :     case MasternodeState::READY:
      81          12 :         return "READY";
      82             :     case MasternodeState::SOME_ERROR:
      83           0 :         return "ERROR";
      84             :     default:
      85           0 :         return "UNKNOWN";
      86             :     }
      87          12 : }
      88             : 
      89          12 : std::string CActiveMasternodeManager::GetStatus() const
      90             : {
      91          12 :     READ_LOCK(cs);
      92          12 :     switch (m_state) {
      93             :     case MasternodeState::WAITING_FOR_PROTX:
      94           0 :         return "Waiting for ProTx to appear on-chain";
      95             :     case MasternodeState::POSE_BANNED:
      96           0 :         return "Masternode was PoSe banned";
      97             :     case MasternodeState::REMOVED:
      98           0 :         return "Masternode removed from list";
      99             :     case MasternodeState::OPERATOR_KEY_CHANGED:
     100           0 :         return "Operator key changed or revoked";
     101             :     case MasternodeState::PROTX_IP_CHANGED:
     102           0 :         return "IP address specified in ProTx changed";
     103             :     case MasternodeState::READY:
     104          12 :         return "Ready";
     105             :     case MasternodeState::SOME_ERROR:
     106           0 :         return "Error. " + m_error;
     107             :     default:
     108           0 :         return "Unknown";
     109             :     }
     110          12 : }
     111             : 
     112        3400 : void CActiveMasternodeManager::InitInternal(const CBlockIndex* pindex)
     113             : {
     114        3400 :     AssertLockHeld(cs);
     115             : 
     116        3400 :     if (!DeploymentDIP0003Enforced(pindex->nHeight, Params().GetConsensus())) return;
     117             : 
     118             :     // Check that our local network configuration is correct
     119        3340 :     if (!fListen && Params().RequireRoutableExternalIP()) {
     120             :         // listen option is probably overwritten by something else, no good
     121           0 :         m_state = MasternodeState::SOME_ERROR;
     122           0 :         m_error = "Masternode must accept connections from outside. Make sure listen configuration option is not overwritten by some another parameter.";
     123           0 :         LogPrintf("CActiveMasternodeManager::Init -- ERROR: %s\n", m_error);
     124           0 :         return;
     125             :     }
     126             : 
     127        3340 :     if (!GetLocalAddress(m_service)) {
     128           0 :         m_state = MasternodeState::SOME_ERROR;
     129           0 :         return;
     130             :     }
     131             : 
     132        3340 :     CDeterministicMNList mnList = m_dmnman.GetListForBlock(pindex);
     133             : 
     134        3340 :     auto dmn = mnList.GetMNByOperatorKey(m_operator_pk);
     135        3340 :     if (!dmn) {
     136             :         // MN not appeared on the chain yet
     137        1812 :         return;
     138             :     }
     139             : 
     140        1528 :     if (!mnList.IsMNValid(dmn->proTxHash)) {
     141         630 :         if (mnList.IsMNPoSeBanned(dmn->proTxHash)) {
     142         630 :             m_state = MasternodeState::POSE_BANNED;
     143         630 :         } else {
     144           0 :             m_state = MasternodeState::REMOVED;
     145             :         }
     146         630 :         return;
     147             :     }
     148             : 
     149         898 :     LogPrintf("CActiveMasternodeManager::Init -- proTxHash=%s, proTx=%s\n", dmn->proTxHash.ToString(), dmn->ToString());
     150             : 
     151         898 :     if (m_service != dmn->pdmnState->netInfo->GetPrimary()) {
     152          54 :         m_state = MasternodeState::SOME_ERROR;
     153          54 :         m_error = "Local address does not match the address from ProTx";
     154          54 :         LogPrintf("CActiveMasternodeManager::Init -- ERROR: %s\n", m_error);
     155          54 :         return;
     156             :     }
     157             : 
     158             :     // Check socket connectivity
     159         844 :     LogPrintf("CActiveMasternodeManager::Init -- Checking inbound connection to '%s'\n", m_service.ToStringAddrPort());
     160         844 :     std::unique_ptr<Sock> sock{ConnectDirectly(m_service, /*manual_connection=*/true)};
     161        1681 :     bool fConnected{sock && sock->IsSelectable(/*is_select=*/::g_socket_events_mode == SocketEventsMode::Select)};
     162         844 :     sock = std::make_unique<Sock>(INVALID_SOCKET);
     163         844 :     if (!fConnected && Params().RequireRoutableExternalIP()) {
     164           0 :         m_state = MasternodeState::SOME_ERROR;
     165           0 :         m_error = "Could not connect to " + m_service.ToStringAddrPort();
     166           0 :         LogPrintf("CActiveMasternodeManager::Init -- ERROR: %s\n", m_error);
     167           0 :         return;
     168             :     }
     169             : 
     170         844 :     m_protx_hash = dmn->proTxHash;
     171         844 :     m_outpoint = dmn->collateralOutpoint;
     172         844 :     m_state = MasternodeState::READY;
     173        3400 : }
     174             : 
     175       81273 : void CActiveMasternodeManager::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload)
     176             : {
     177       81273 :     if (!DeploymentDIP0003Enforced(pindexNew->nHeight, Params().GetConsensus())) return;
     178             : 
     179      291358 :     const auto [cur_state, cur_protx_hash] = WITH_READ_LOCK(cs, return std::make_pair(m_state, m_protx_hash));
     180       74109 :     if (cur_state == MasternodeState::READY) {
     181       71596 :         auto oldMNList = m_dmnman.GetListForBlock(pindexNew->pprev);
     182       71596 :         auto newMNList = m_dmnman.GetListForBlock(pindexNew);
     183       71823 :         auto reset = [this, pindexNew](MasternodeState state) -> void {
     184         227 :             LOCK(cs);
     185         227 :             m_state = state;
     186         227 :             m_protx_hash = uint256();
     187         227 :             m_outpoint.SetNull();
     188             :             // MN might have reappeared in same block with a new ProTx
     189         227 :             InitInternal(pindexNew);
     190         227 :         };
     191             : 
     192       71596 :         if (!newMNList.IsMNValid(cur_protx_hash)) {
     193             :             // MN disappeared from MN list
     194          52 :             return reset(MasternodeState::REMOVED);
     195             :         }
     196             : 
     197       71544 :         auto oldDmn = oldMNList.GetMN(cur_protx_hash);
     198       71544 :         auto newDmn = newMNList.GetMN(cur_protx_hash);
     199       71544 :         if (!oldDmn || !newDmn) {
     200         124 :             return reset(MasternodeState::SOME_ERROR);
     201             :         }
     202       71420 :         if (newDmn->pdmnState->pubKeyOperator != oldDmn->pdmnState->pubKeyOperator) {
     203             :             // MN operator key changed or revoked
     204           0 :             return reset(MasternodeState::OPERATOR_KEY_CHANGED);
     205             :         }
     206       71420 :         if (newDmn->pdmnState->netInfo->GetPrimary() != oldDmn->pdmnState->netInfo->GetPrimary()) {
     207             :             // MN IP changed
     208          51 :             return reset(MasternodeState::PROTX_IP_CHANGED);
     209             :         }
     210       71596 :     } else {
     211             :         // MN might have (re)appeared with a new ProTx or we've found some peers
     212             :         // and figured out our local address
     213        2513 :         Init(pindexNew);
     214             :     }
     215       81273 : }
     216             : 
     217        3340 : bool CActiveMasternodeManager::GetLocalAddress(CService& addrRet)
     218             : {
     219        3340 :     AssertLockHeld(cs);
     220             :     // First try to find whatever our own local address is known internally.
     221             :     // Addresses could be specified via externalip or bind option, discovered via UPnP
     222             :     // or added by TorController. Use some random dummy IPv4 peer to prefer the one
     223             :     // reachable via IPv4.
     224        3340 :     bool fFoundLocal{false};
     225        6680 :     if (auto peerAddr = LookupHost("8.8.8.8", false); peerAddr.has_value()) {
     226        3340 :         fFoundLocal = GetLocal(addrRet, &peerAddr.value()) && IsValidNetAddr(addrRet);
     227        3340 :     }
     228        3340 :     if (!fFoundLocal && !Params().RequireRoutableExternalIP()) {
     229        6680 :         if (auto addr = Lookup("127.0.0.1", GetListenPort(), false); addr.has_value()) {
     230        3340 :             addrRet = addr.value();
     231        3340 :             fFoundLocal = true;
     232        3340 :         }
     233        3340 :     }
     234        3340 :     if (!fFoundLocal) {
     235           0 :         bool empty = true;
     236             :         // If we have some peers, let's try to find our local address from one of them
     237           0 :         m_connman.ForEachNodeContinueIf(CConnman::AllNodes, [&](CNode* pnode) {
     238           0 :             empty = false;
     239           0 :             if (pnode->addr.IsIPv4()) {
     240           0 :                 if (auto addr = ::GetLocalAddress(*pnode); IsValidNetAddr(addr)) {
     241           0 :                     addrRet = addr;
     242           0 :                     fFoundLocal = true;
     243           0 :                 }
     244           0 :             }
     245           0 :             return !fFoundLocal;
     246           0 :         });
     247             :         // nothing and no live connections, can't do anything for now
     248           0 :         if (empty) {
     249           0 :             m_error = "Can't detect valid external address. Please consider using the externalip configuration option if problem persists. Make sure to use IPv4 address only.";
     250           0 :             LogPrintf("CActiveMasternodeManager::GetLocalAddress -- ERROR: %s\n", m_error);
     251           0 :             return false;
     252             :         }
     253           0 :     }
     254        3340 :     return true;
     255        3340 : }
     256             : 
     257           0 : bool CActiveMasternodeManager::IsValidNetAddr(const CService& addrIn)
     258             : {
     259           0 :     if (!addrIn.IsValid() || !addrIn.IsIPv4()) return false;
     260             :     // TODO: regtest is fine with any addresses for now,
     261             :     // should probably be a bit smarter if one day we start to implement tests for this
     262           0 :     return !Params().RequireRoutableExternalIP() || (g_reachable_nets.Contains(addrIn) && addrIn.IsRoutable());
     263           0 : }
     264             : 
     265             : template <template <typename> class EncryptedObj, typename Obj>
     266        9075 : [[nodiscard]] bool CActiveMasternodeManager::Decrypt(const EncryptedObj<Obj>& obj, size_t idx, Obj& ret_obj,
     267             :                                                      int version) const
     268             : {
     269        9075 :     AssertLockNotHeld(cs);
     270       18150 :     return WITH_READ_LOCK(cs, return obj.Decrypt(idx, m_operator_sk, ret_obj, version));
     271             : }
     272             : template bool CActiveMasternodeManager::Decrypt(const CBLSIESEncryptedObject<CBLSSecretKey>& obj, size_t idx,
     273             :                                                 CBLSSecretKey& ret_obj, int version) const;
     274             : template bool CActiveMasternodeManager::Decrypt(const CBLSIESMultiRecipientObjects<CBLSSecretKey>& obj, size_t idx,
     275             :                                                 CBLSSecretKey& ret_obj, int version) const;
     276             : 
     277       10086 : [[nodiscard]] CBLSSignature CActiveMasternodeManager::Sign(const uint256& hash, const bool is_legacy) const
     278             : {
     279       10086 :     AssertLockNotHeld(cs);
     280       20166 :     return WITH_READ_LOCK(cs, return m_operator_sk.Sign(hash, is_legacy));
     281             : }
     282             : 
     283         189 : [[nodiscard]] std::vector<uint8_t> CActiveMasternodeManager::SignBasic(const uint256& hash) const
     284             : {
     285         189 :     AssertLockNotHeld(cs);
     286         189 :     auto sig = Sign(hash, /*is_legacy=*/false);
     287         189 :     assert(sig.IsValid());
     288         189 :     return sig.ToByteVector(/*specificLegacyScheme=*/false);
     289             : }
     290             : 
     291             : // We need to pass a copy as opposed to a const ref because CBLSPublicKeyVersionWrapper
     292             : // does not accept a const ref in its construction args
     293        4497 : [[nodiscard]] CBLSPublicKey CActiveMasternodeManager::GetPubKey() const
     294             : {
     295        4497 :     READ_LOCK(cs);
     296        4497 :     return m_operator_pk;
     297        4497 : }

Generated by: LCOV version 1.16