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 0 : bool GetLocal(CService& addr, const CNetAddr* paddrPeer)
18 : {
19 0 : if (!fListen)
20 0 : return false;
21 :
22 0 : int nBestScore = -1;
23 : {
24 0 : LOCK(g_maplocalhost_mutex);
25 0 : int nBestReachability = -1;
26 0 : 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 0 : }
48 0 : return nBestScore >= 0;
49 0 : }
50 : } // anonymous namespace
51 :
52 2 : CActiveMasternodeManager::CActiveMasternodeManager(CConnman& connman, CDeterministicMNManager& dmnman,
53 : const CBLSSecretKey& sk) :
54 1 : m_connman{connman},
55 1 : m_dmnman{dmnman},
56 1 : m_operator_pk{sk.GetPublicKey()},
57 1 : m_operator_sk{sk}
58 1 : {
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 1 : }
64 :
65 2 : CActiveMasternodeManager::~CActiveMasternodeManager() = default;
66 :
67 0 : std::string CActiveMasternodeManager::GetStateString() const
68 : {
69 0 : 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 0 : return "READY";
82 : case MasternodeState::SOME_ERROR:
83 0 : return "ERROR";
84 : default:
85 0 : return "UNKNOWN";
86 : }
87 0 : }
88 :
89 0 : std::string CActiveMasternodeManager::GetStatus() const
90 : {
91 0 : READ_LOCK(cs);
92 0 : 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 0 : return "Ready";
105 : case MasternodeState::SOME_ERROR:
106 0 : return "Error. " + m_error;
107 : default:
108 0 : return "Unknown";
109 : }
110 0 : }
111 :
112 0 : void CActiveMasternodeManager::InitInternal(const CBlockIndex* pindex)
113 : {
114 0 : AssertLockHeld(cs);
115 :
116 0 : if (!DeploymentDIP0003Enforced(pindex->nHeight, Params().GetConsensus())) return;
117 :
118 : // Check that our local network configuration is correct
119 0 : 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 0 : if (!GetLocalAddress(m_service)) {
128 0 : m_state = MasternodeState::SOME_ERROR;
129 0 : return;
130 : }
131 :
132 0 : CDeterministicMNList mnList = m_dmnman.GetListForBlock(pindex);
133 :
134 0 : auto dmn = mnList.GetMNByOperatorKey(m_operator_pk);
135 0 : if (!dmn) {
136 : // MN not appeared on the chain yet
137 0 : return;
138 : }
139 :
140 0 : if (!mnList.IsMNValid(dmn->proTxHash)) {
141 0 : if (mnList.IsMNPoSeBanned(dmn->proTxHash)) {
142 0 : m_state = MasternodeState::POSE_BANNED;
143 0 : } else {
144 0 : m_state = MasternodeState::REMOVED;
145 : }
146 0 : return;
147 : }
148 :
149 0 : LogPrintf("CActiveMasternodeManager::Init -- proTxHash=%s, proTx=%s\n", dmn->proTxHash.ToString(), dmn->ToString());
150 :
151 0 : if (m_service != dmn->pdmnState->netInfo->GetPrimary()) {
152 0 : m_state = MasternodeState::SOME_ERROR;
153 0 : m_error = "Local address does not match the address from ProTx";
154 0 : LogPrintf("CActiveMasternodeManager::Init -- ERROR: %s\n", m_error);
155 0 : return;
156 : }
157 :
158 : // Check socket connectivity
159 0 : LogPrintf("CActiveMasternodeManager::Init -- Checking inbound connection to '%s'\n", m_service.ToStringAddrPort());
160 0 : std::unique_ptr<Sock> sock{ConnectDirectly(m_service, /*manual_connection=*/true)};
161 0 : bool fConnected{sock && sock->IsSelectable(/*is_select=*/::g_socket_events_mode == SocketEventsMode::Select)};
162 0 : sock = std::make_unique<Sock>(INVALID_SOCKET);
163 0 : 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 0 : m_protx_hash = dmn->proTxHash;
171 0 : m_outpoint = dmn->collateralOutpoint;
172 0 : m_state = MasternodeState::READY;
173 0 : }
174 :
175 0 : void CActiveMasternodeManager::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload)
176 : {
177 0 : if (!DeploymentDIP0003Enforced(pindexNew->nHeight, Params().GetConsensus())) return;
178 :
179 0 : const auto [cur_state, cur_protx_hash] = WITH_READ_LOCK(cs, return std::make_pair(m_state, m_protx_hash));
180 0 : if (cur_state == MasternodeState::READY) {
181 0 : auto oldMNList = m_dmnman.GetListForBlock(pindexNew->pprev);
182 0 : auto newMNList = m_dmnman.GetListForBlock(pindexNew);
183 0 : auto reset = [this, pindexNew](MasternodeState state) -> void {
184 0 : LOCK(cs);
185 0 : m_state = state;
186 0 : m_protx_hash = uint256();
187 0 : m_outpoint.SetNull();
188 : // MN might have reappeared in same block with a new ProTx
189 0 : InitInternal(pindexNew);
190 0 : };
191 :
192 0 : if (!newMNList.IsMNValid(cur_protx_hash)) {
193 : // MN disappeared from MN list
194 0 : return reset(MasternodeState::REMOVED);
195 : }
196 :
197 0 : auto oldDmn = oldMNList.GetMN(cur_protx_hash);
198 0 : auto newDmn = newMNList.GetMN(cur_protx_hash);
199 0 : if (!oldDmn || !newDmn) {
200 0 : return reset(MasternodeState::SOME_ERROR);
201 : }
202 0 : if (newDmn->pdmnState->pubKeyOperator != oldDmn->pdmnState->pubKeyOperator) {
203 : // MN operator key changed or revoked
204 0 : return reset(MasternodeState::OPERATOR_KEY_CHANGED);
205 : }
206 0 : if (newDmn->pdmnState->netInfo->GetPrimary() != oldDmn->pdmnState->netInfo->GetPrimary()) {
207 : // MN IP changed
208 0 : return reset(MasternodeState::PROTX_IP_CHANGED);
209 : }
210 0 : } 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 0 : Init(pindexNew);
214 : }
215 0 : }
216 :
217 0 : bool CActiveMasternodeManager::GetLocalAddress(CService& addrRet)
218 : {
219 0 : 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 0 : bool fFoundLocal{false};
225 0 : if (auto peerAddr = LookupHost("8.8.8.8", false); peerAddr.has_value()) {
226 0 : fFoundLocal = GetLocal(addrRet, &peerAddr.value()) && IsValidNetAddr(addrRet);
227 0 : }
228 0 : if (!fFoundLocal && !Params().RequireRoutableExternalIP()) {
229 0 : if (auto addr = Lookup("127.0.0.1", GetListenPort(), false); addr.has_value()) {
230 0 : addrRet = addr.value();
231 0 : fFoundLocal = true;
232 0 : }
233 0 : }
234 0 : 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 0 : return true;
255 0 : }
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 0 : [[nodiscard]] bool CActiveMasternodeManager::Decrypt(const EncryptedObj<Obj>& obj, size_t idx, Obj& ret_obj,
267 : int version) const
268 : {
269 0 : AssertLockNotHeld(cs);
270 0 : 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 1 : [[nodiscard]] CBLSSignature CActiveMasternodeManager::Sign(const uint256& hash, const bool is_legacy) const
278 : {
279 1 : AssertLockNotHeld(cs);
280 2 : return WITH_READ_LOCK(cs, return m_operator_sk.Sign(hash, is_legacy));
281 : }
282 :
283 1 : [[nodiscard]] std::vector<uint8_t> CActiveMasternodeManager::SignBasic(const uint256& hash) const
284 : {
285 1 : AssertLockNotHeld(cs);
286 1 : auto sig = Sign(hash, /*is_legacy=*/false);
287 1 : assert(sig.IsValid());
288 1 : 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 1 : [[nodiscard]] CBLSPublicKey CActiveMasternodeManager::GetPubKey() const
294 : {
295 1 : READ_LOCK(cs);
296 1 : return m_operator_pk;
297 1 : }
|