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 : }
|