Line data Source code
1 : // Copyright (c) 2020 The Dash 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 "tiertwo/net_masternodes.h"
7 :
8 : #include "chainparams.h"
9 : #include "evo/deterministicmns.h"
10 : #include "netmessagemaker.h"
11 : #include "scheduler.h"
12 : #include "tiertwo/masternode_meta_manager.h" // for g_mmetaman
13 : #include "tiertwo/tiertwo_sync_state.h"
14 :
15 422 : TierTwoConnMan::TierTwoConnMan(CConnman* _connman) : connman(_connman) {}
16 423 : TierTwoConnMan::~TierTwoConnMan() { connman = nullptr; }
17 :
18 2 : void TierTwoConnMan::setQuorumNodes(Consensus::LLMQType llmqType,
19 : const uint256& quorumHash,
20 : const std::set<uint256>& proTxHashes)
21 : {
22 2 : LOCK(cs_vPendingMasternodes);
23 2 : auto it = masternodeQuorumNodes.emplace(QuorumTypeAndHash(llmqType, quorumHash), proTxHashes);
24 2 : if (!it.second) {
25 2 : it.first->second = proTxHashes;
26 : }
27 2 : }
28 :
29 12 : std::set<uint256> TierTwoConnMan::getQuorumNodes(Consensus::LLMQType llmqType)
30 : {
31 12 : LOCK(cs_vPendingMasternodes);
32 12 : std::set<uint256> result;
33 12 : for (const auto& p : masternodeQuorumNodes) {
34 0 : if (p.first.first != llmqType) {
35 0 : continue;
36 : }
37 0 : result.emplace(p.first.second);
38 : }
39 24 : return result;
40 : }
41 :
42 0 : std::set<NodeId> TierTwoConnMan::getQuorumNodes(Consensus::LLMQType llmqType, uint256 quorumHash)
43 : {
44 0 : LOCK(cs_vPendingMasternodes);
45 0 : std::set<NodeId> result;
46 0 : auto it = masternodeQuorumRelayMembers.find(std::make_pair(llmqType, quorumHash));
47 0 : if (it == masternodeQuorumRelayMembers.end()) {
48 0 : return {};
49 : }
50 0 : connman->ForEachNode([&](CNode* pnode) {
51 0 : if (pnode->fDisconnect) {
52 : return;
53 : }
54 0 : if (!it->second.count(pnode->verifiedProRegTxHash)) {
55 0 : return;
56 : }
57 0 : result.emplace(pnode->GetId());
58 : });
59 0 : return result;
60 : }
61 :
62 0 : bool TierTwoConnMan::hasQuorumNodes(Consensus::LLMQType llmqType, const uint256& quorumHash)
63 : {
64 0 : LOCK(cs_vPendingMasternodes);
65 0 : return masternodeQuorumNodes.count(QuorumTypeAndHash(llmqType, quorumHash));
66 : }
67 :
68 0 : void TierTwoConnMan::removeQuorumNodes(Consensus::LLMQType llmqType, const uint256& quorumHash)
69 : {
70 0 : LOCK(cs_vPendingMasternodes);
71 0 : masternodeQuorumNodes.erase(std::make_pair(llmqType, quorumHash));
72 0 : }
73 :
74 7 : void TierTwoConnMan::setMasternodeQuorumRelayMembers(Consensus::LLMQType llmqType, const uint256& quorumHash, const std::set<uint256>& proTxHashes)
75 : {
76 7 : LOCK(cs_vPendingMasternodes);
77 7 : auto it = masternodeQuorumRelayMembers.emplace(std::make_pair(llmqType, quorumHash), proTxHashes);
78 7 : if (!it.second) {
79 0 : it.first->second = proTxHashes;
80 : }
81 :
82 : // Update existing connections
83 7 : connman->ForEachNode([&](CNode* pnode) {
84 50 : if (!pnode->m_masternode_iqr_connection && isMasternodeQuorumRelayMember(pnode->verifiedProRegTxHash)) {
85 : // Tell our peer that we're interested in plain LLMQ recovered signatures.
86 : // Otherwise, the peer would only announce/send messages resulting from QRECSIG,
87 : // future e.g. tx locks or chainlocks. SPV and regular full nodes should not send
88 : // this message as they are usually only interested in the higher level messages.
89 8 : CNetMsgMaker msgMaker(pnode->GetSendVersion());
90 8 : connman->PushMessage(pnode, msgMaker.Make(NetMsgType::QSENDRECSIGS, true));
91 8 : pnode->m_masternode_iqr_connection = true;
92 : }
93 50 : });
94 7 : }
95 :
96 0 : bool TierTwoConnMan::isMasternodeQuorumNode(const CNode* pnode)
97 : {
98 : // Let's see if this is an outgoing connection to an address that is known to be a masternode
99 : // We however only need to know this if the node did not authenticate itself as a MN yet
100 0 : uint256 assumedProTxHash;
101 0 : if (pnode->verifiedProRegTxHash.IsNull() && !pnode->fInbound) {
102 0 : auto mnList = deterministicMNManager->GetListAtChainTip();
103 0 : auto dmn = mnList.GetMNByService(pnode->addr);
104 0 : if (dmn == nullptr) {
105 : // This is definitely not a masternode
106 0 : return false;
107 : }
108 0 : assumedProTxHash = dmn->proTxHash;
109 : }
110 :
111 0 : LOCK(cs_vPendingMasternodes);
112 0 : for (const auto& quorumConn : masternodeQuorumNodes) {
113 0 : if (!pnode->verifiedProRegTxHash.IsNull()) {
114 0 : if (quorumConn.second.count(pnode->verifiedProRegTxHash)) {
115 0 : return true;
116 : }
117 0 : } else if (!assumedProTxHash.IsNull()) {
118 0 : if (quorumConn.second.count(assumedProTxHash)) {
119 0 : return true;
120 : }
121 : }
122 : }
123 0 : return false;
124 : }
125 :
126 68 : bool TierTwoConnMan::isMasternodeQuorumRelayMember(const uint256& protxHash)
127 : {
128 136 : if (protxHash.IsNull()) {
129 : return false;
130 : }
131 26 : LOCK(cs_vPendingMasternodes);
132 26 : for (const auto& p : masternodeQuorumRelayMembers) {
133 12 : if (p.second.count(protxHash) > 0) {
134 12 : return true;
135 : }
136 : }
137 14 : return false;
138 : }
139 :
140 1 : bool TierTwoConnMan::addPendingMasternode(const uint256& proTxHash)
141 : {
142 2 : LOCK(cs_vPendingMasternodes);
143 1 : if (std::find(vPendingMasternodes.begin(), vPendingMasternodes.end(), proTxHash) != vPendingMasternodes.end()) {
144 : return false;
145 : }
146 1 : vPendingMasternodes.emplace_back(proTxHash);
147 : return true;
148 : }
149 :
150 1 : void TierTwoConnMan::addPendingProbeConnections(const std::set<uint256>& proTxHashes)
151 : {
152 1 : LOCK(cs_vPendingMasternodes);
153 1 : masternodePendingProbes.insert(proTxHashes.begin(), proTxHashes.end());
154 1 : }
155 :
156 2 : void TierTwoConnMan::clear()
157 : {
158 2 : LOCK(cs_vPendingMasternodes);
159 2 : masternodeQuorumNodes.clear();
160 2 : masternodeQuorumRelayMembers.clear();
161 2 : vPendingMasternodes.clear();
162 2 : masternodePendingProbes.clear();
163 2 : }
164 :
165 293 : void TierTwoConnMan::start(CScheduler& scheduler, const TierTwoConnMan::Options& options)
166 : {
167 : // Must be started after connman
168 293 : assert(connman);
169 293 : interruptNet.reset();
170 :
171 : // Connecting to specific addresses, no masternode connections available
172 293 : if (options.m_has_specified_outgoing) return;
173 : // Initiate masternode connections
174 293 : threadOpenMasternodeConnections = std::thread(&TraceThread<std::function<void()> >, "mncon", std::function<void()>(std::bind(&TierTwoConnMan::ThreadOpenMasternodeConnections, this)));
175 : // Cleanup process every 60 seconds
176 586 : scheduler.scheduleEvery(std::bind(&TierTwoConnMan::doMaintenance, this), 60 * 1000);
177 : }
178 :
179 726 : void TierTwoConnMan::stop() {
180 726 : if (threadOpenMasternodeConnections.joinable()) {
181 293 : threadOpenMasternodeConnections.join();
182 : }
183 726 : }
184 :
185 726 : void TierTwoConnMan::interrupt()
186 : {
187 726 : interruptNet();
188 726 : }
189 :
190 10 : void TierTwoConnMan::openConnection(const CAddress& addrConnect, bool isProbe)
191 : {
192 10 : if (interruptNet) return;
193 : // Note: using ip:port string connection instead of the addr to bypass the "only connect to single IPs" validation.
194 20 : std::string conn = addrConnect.ToStringIPPort();
195 20 : CAddress dummyAddr;
196 10 : connman->OpenNetworkConnection(dummyAddr, false, nullptr, conn.data(), false, false, false, true, isProbe);
197 : }
198 :
199 79436 : class PeerData {
200 : public:
201 15348 : PeerData(const CService& s, bool disconnect, bool is_mn_conn) : service(s), f_disconnect(disconnect), f_is_mn_conn(is_mn_conn) {}
202 : const CService service;
203 : bool f_disconnect{false};
204 : bool f_is_mn_conn{false};
205 251 : bool operator==(const CService& s) const { return service == s; }
206 : };
207 :
208 : struct MnService {
209 : public:
210 : uint256 verif_proreg_tx_hash{UINT256_ZERO};
211 : bool is_inbound{false};
212 2365 : bool operator==(const uint256& hash) const { return verif_proreg_tx_hash == hash; }
213 : };
214 :
215 293 : void TierTwoConnMan::ThreadOpenMasternodeConnections()
216 : {
217 293 : const auto& chainParams = Params();
218 293 : bool triedConnect = false;
219 48787 : while (!interruptNet) {
220 :
221 : // Retry every 0.1 seconds if a connection was created, otherwise 1.5 seconds
222 48787 : int sleepTime = triedConnect ? 100 : (chainParams.IsRegTestNet() ? 200 : 1500);
223 48787 : if (!interruptNet.sleep_for(std::chrono::milliseconds(sleepTime))) {
224 293 : return;
225 : }
226 :
227 48494 : triedConnect = false;
228 :
229 48494 : if (!fMasterNode || !g_tiertwo_sync_state.IsBlockchainSynced() || !g_connman->GetNetworkActive()) {
230 48484 : continue;
231 : }
232 :
233 : // Gather all connected peers first, so we don't
234 : // try to connect to an already connected peer
235 4235 : std::vector<PeerData> connectedNodes;
236 10 : std::vector<MnService> connectedMnServices;
237 4235 : connman->ForEachNode([&](const CNode* pnode) {
238 15348 : connectedNodes.emplace_back(PeerData{pnode->addr, pnode->fDisconnect, pnode->m_masternode_connection});
239 30696 : if (!pnode->verifiedProRegTxHash.IsNull()) {
240 950 : connectedMnServices.emplace_back(MnService{pnode->verifiedProRegTxHash, pnode->fInbound});
241 : }
242 15348 : });
243 :
244 : // Try to connect to a single MN per cycle
245 10 : CDeterministicMNCPtr dmnToConnect{nullptr};
246 : // Current list
247 4245 : auto mnList = deterministicMNManager->GetListAtChainTip();
248 4235 : int64_t currentTime = GetAdjustedTime();
249 4235 : bool isProbe = false;
250 4235 : {
251 4235 : LOCK(cs_vPendingMasternodes);
252 :
253 : // First try to connect to pending MNs
254 4235 : if (!vPendingMasternodes.empty()) {
255 2 : auto dmn = mnList.GetValidMN(vPendingMasternodes.front());
256 1 : vPendingMasternodes.erase(vPendingMasternodes.begin());
257 1 : if (dmn) {
258 1 : auto peerData = std::find(connectedNodes.begin(), connectedNodes.end(), dmn->pdmnState->addr);
259 1 : if (peerData == std::end(connectedNodes)) {
260 1 : dmnToConnect = dmn;
261 2 : LogPrint(BCLog::NET_MN, "%s -- opening pending masternode connection to %s, service=%s\n",
262 : __func__, dmn->proTxHash.ToString(), dmn->pdmnState->addr.ToString());
263 : }
264 : }
265 : }
266 :
267 : // Secondly, try to connect quorum members
268 4235 : if (!dmnToConnect) {
269 8468 : std::vector<CDeterministicMNCPtr> pending;
270 4384 : for (const auto& group: masternodeQuorumNodes) {
271 876 : for (const auto& proRegTxHash: group.second) {
272 : // Skip if already have this member connected
273 726 : if (std::count(connectedMnServices.begin(), connectedMnServices.end(), proRegTxHash) > 0) {
274 477 : continue;
275 : }
276 :
277 : // Don't try to connect to ourselves
278 603 : if (WITH_LOCK(cs_vPendingMasternodes, return local_dmn_pro_tx_hash && *local_dmn_pro_tx_hash == proRegTxHash)) {
279 144 : continue;
280 : }
281 :
282 : // Check if DMN exists in tip list
283 119 : const auto& dmn = mnList.GetValidMN(proRegTxHash);
284 817 : if (!dmn) continue;
285 105 : auto peerData = std::find(connectedNodes.begin(), connectedNodes.end(), dmn->pdmnState->addr);
286 :
287 : // Skip already connected nodes.
288 105 : if (peerData != std::end(connectedNodes) &&
289 1 : (peerData->f_disconnect || peerData->f_is_mn_conn)) {
290 0 : continue;
291 : }
292 :
293 : // Check if we already tried this connection recently to not retry too often
294 105 : int64_t lastAttempt = g_mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastOutboundAttempt();
295 : // back off trying connecting to an address if we already tried recently
296 105 : if (currentTime - lastAttempt < chainParams.LLMQConnectionRetryTimeout()) {
297 91 : continue;
298 : }
299 14 : pending.emplace_back(dmn);
300 : }
301 : }
302 : // Select a random node to connect
303 4234 : if (!pending.empty()) {
304 8 : dmnToConnect = pending[GetRandInt((int) pending.size())];
305 16 : LogPrint(BCLog::NET_MN, "TierTwoConnMan::%s -- opening quorum connection to %s, service=%s\n",
306 : __func__, dmnToConnect->proTxHash.ToString(), dmnToConnect->pdmnState->addr.ToString());
307 : }
308 : }
309 :
310 : // If no node was selected, let's try to probe nodes connection
311 4235 : if (!dmnToConnect) {
312 8452 : std::vector<CDeterministicMNCPtr> pending;
313 4227 : for (auto it = masternodePendingProbes.begin(); it != masternodePendingProbes.end(); ) {
314 2 : auto dmn = mnList.GetMN(*it);
315 1 : if (!dmn) {
316 0 : it = masternodePendingProbes.erase(it);
317 0 : continue;
318 : }
319 :
320 : // Discard already connected outbound MNs
321 1 : auto mnService = std::find(connectedMnServices.begin(), connectedMnServices.end(), dmn->proTxHash);
322 1 : bool connectedAndOutbound = mnService != std::end(connectedMnServices) && !mnService->is_inbound;
323 1 : if (connectedAndOutbound) {
324 : // we already have an outbound connection to this MN so there is no eed to probe it again
325 0 : g_mmetaman.GetMetaInfo(dmn->proTxHash)->SetLastOutboundSuccess(currentTime);
326 0 : it = masternodePendingProbes.erase(it);
327 0 : continue;
328 : }
329 :
330 1 : ++it;
331 :
332 1 : int64_t lastAttempt = g_mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastOutboundAttempt();
333 : // back off trying connecting to an address if we already tried recently
334 1 : if (currentTime - lastAttempt < chainParams.LLMQConnectionRetryTimeout()) {
335 0 : continue;
336 : }
337 1 : pending.emplace_back(dmn);
338 : }
339 :
340 : // Select a random node to connect
341 4226 : if (!pending.empty()) {
342 1 : dmnToConnect = pending[GetRandInt((int)pending.size())];
343 1 : masternodePendingProbes.erase(dmnToConnect->proTxHash);
344 1 : isProbe = true;
345 :
346 2 : LogPrint(BCLog::NET_MN, "CConnman::%s -- probing masternode %s, service=%s\n",
347 : __func__, dmnToConnect->proTxHash.ToString(), dmnToConnect->pdmnState->addr.ToString());
348 : }
349 : }
350 : }
351 :
352 : // No DMN to connect
353 4235 : if (!dmnToConnect || interruptNet) {
354 4225 : continue;
355 : }
356 :
357 : // Update last attempt and try connection
358 10 : g_mmetaman.GetMetaInfo(dmnToConnect->proTxHash)->SetLastOutboundAttempt(currentTime);
359 10 : triedConnect = true;
360 :
361 : // Now connect
362 20 : openConnection(CAddress(dmnToConnect->pdmnState->addr, NODE_NETWORK), isProbe);
363 : // should be in the list now if connection was opened
364 20 : bool connected = connman->ForNode(dmnToConnect->pdmnState->addr, CConnman::AllNodes, [&](CNode* pnode) {
365 10 : if (pnode->fDisconnect) { LogPrintf("about to be disconnected\n");
366 1 : return false;
367 : }
368 : return true;
369 : });
370 10 : if (!connected) {
371 2 : LogPrint(BCLog::NET_MN, "TierTwoConnMan::%s -- connection failed for masternode %s, service=%s\n",
372 : __func__, dmnToConnect->proTxHash.ToString(), dmnToConnect->pdmnState->addr.ToString());
373 : // reset last outbound success
374 2 : g_mmetaman.GetMetaInfo(dmnToConnect->proTxHash)->SetLastOutboundSuccess(0);
375 : }
376 : }
377 : }
378 :
379 123 : static void ProcessMasternodeConnections(CConnman& connman, TierTwoConnMan& tierTwoConnMan)
380 : {
381 : // Don't disconnect masternode connections when we have less than the desired amount of outbound nodes
382 123 : int nonMasternodeCount = 0;
383 123 : connman.ForEachNode([&](CNode* pnode) {
384 221 : if (!pnode->fInbound && !pnode->fFeeler && !pnode->fAddnode && !pnode->m_masternode_connection && !pnode->m_masternode_probe_connection) {
385 103 : nonMasternodeCount++;
386 : }
387 221 : });
388 123 : if (nonMasternodeCount < (int) connman.GetMaxOutboundNodeCount()) {
389 123 : return;
390 : }
391 :
392 0 : connman.ForEachNode([&](CNode* pnode) {
393 : // we're only disconnecting m_masternode_connection connections
394 0 : if (!pnode->m_masternode_connection) return;
395 : // we're only disconnecting outbound connections (inbound connections are disconnected in AcceptConnection())
396 0 : if (pnode->fInbound) return;
397 : // we're not disconnecting LLMQ connections
398 0 : if (tierTwoConnMan.isMasternodeQuorumNode(pnode)) return;
399 : // we're not disconnecting masternode probes for at least a few seconds
400 0 : if (pnode->m_masternode_probe_connection && GetSystemTimeInSeconds() - pnode->nTimeConnected < 5) return;
401 :
402 0 : if (fLogIPs) {
403 0 : LogPrintf("Closing Masternode connection: peer=%d, addr=%s\n", pnode->GetId(), pnode->addr.ToString());
404 : } else {
405 0 : LogPrintf("Closing Masternode connection: peer=%d\n", pnode->GetId());
406 : }
407 0 : pnode->fDisconnect = true;
408 : });
409 : }
410 :
411 126 : void TierTwoConnMan::doMaintenance()
412 : {
413 126 : if(!g_tiertwo_sync_state.IsBlockchainSynced() || interruptNet) {
414 3 : return;
415 : }
416 123 : ProcessMasternodeConnections(*connman, *this);
417 : }
418 :
|