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 484 : TierTwoConnMan::TierTwoConnMan(CConnman* _connman) : connman(_connman) {}
16 485 : TierTwoConnMan::~TierTwoConnMan() { connman = nullptr; }
17 :
18 2485 : void TierTwoConnMan::setQuorumNodes(Consensus::LLMQType llmqType,
19 : const uint256& quorumHash,
20 : const std::set<uint256>& proTxHashes)
21 : {
22 2485 : LOCK(cs_vPendingMasternodes);
23 2485 : auto it = masternodeQuorumNodes.emplace(QuorumTypeAndHash(llmqType, quorumHash), proTxHashes);
24 2485 : if (!it.second) {
25 2485 : it.first->second = proTxHashes;
26 : }
27 2485 : }
28 :
29 4496 : std::set<uint256> TierTwoConnMan::getQuorumNodes(Consensus::LLMQType llmqType)
30 : {
31 4496 : LOCK(cs_vPendingMasternodes);
32 4496 : std::set<uint256> result;
33 7682 : for (const auto& p : masternodeQuorumNodes) {
34 3186 : if (p.first.first != llmqType) {
35 0 : continue;
36 : }
37 6372 : result.emplace(p.first.second);
38 : }
39 8992 : 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 2483 : bool TierTwoConnMan::hasQuorumNodes(Consensus::LLMQType llmqType, const uint256& quorumHash)
63 : {
64 2483 : LOCK(cs_vPendingMasternodes);
65 4966 : return masternodeQuorumNodes.count(QuorumTypeAndHash(llmqType, quorumHash));
66 : }
67 :
68 33 : void TierTwoConnMan::removeQuorumNodes(Consensus::LLMQType llmqType, const uint256& quorumHash)
69 : {
70 33 : LOCK(cs_vPendingMasternodes);
71 33 : masternodeQuorumNodes.erase(std::make_pair(llmqType, quorumHash));
72 33 : }
73 :
74 3563 : void TierTwoConnMan::setMasternodeQuorumRelayMembers(Consensus::LLMQType llmqType, const uint256& quorumHash, const std::set<uint256>& proTxHashes)
75 : {
76 3563 : LOCK(cs_vPendingMasternodes);
77 3563 : auto it = masternodeQuorumRelayMembers.emplace(std::make_pair(llmqType, quorumHash), proTxHashes);
78 3563 : if (!it.second) {
79 3474 : it.first->second = proTxHashes;
80 : }
81 :
82 : // Update existing connections
83 3563 : connman->ForEachNode([&](CNode* pnode) {
84 33889 : 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 33889 : });
94 3563 : }
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 21075 : bool TierTwoConnMan::isMasternodeQuorumRelayMember(const uint256& protxHash)
127 : {
128 42150 : if (protxHash.IsNull()) {
129 : return false;
130 : }
131 133 : LOCK(cs_vPendingMasternodes);
132 180 : for (const auto& p : masternodeQuorumRelayMembers) {
133 202 : if (p.second.count(protxHash) > 0) {
134 108 : return true;
135 : }
136 : }
137 25 : 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 63 : void TierTwoConnMan::addPendingProbeConnections(const std::set<uint256>& proTxHashes)
151 : {
152 63 : LOCK(cs_vPendingMasternodes);
153 63 : masternodePendingProbes.insert(proTxHashes.begin(), proTxHashes.end());
154 63 : }
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 355 : void TierTwoConnMan::start(CScheduler& scheduler, const TierTwoConnMan::Options& options)
166 : {
167 : // Must be started after connman
168 355 : assert(connman);
169 355 : interruptNet.reset();
170 :
171 : // Connecting to specific addresses, no masternode connections available
172 355 : if (options.m_has_specified_outgoing) return;
173 : // Initiate masternode connections
174 355 : threadOpenMasternodeConnections = std::thread(&TraceThread<std::function<void()> >, "mncon", std::function<void()>(std::bind(&TierTwoConnMan::ThreadOpenMasternodeConnections, this)));
175 : // Cleanup process every 60 seconds
176 710 : scheduler.scheduleEvery(std::bind(&TierTwoConnMan::doMaintenance, this), 60 * 1000);
177 : }
178 :
179 850 : void TierTwoConnMan::stop() {
180 850 : if (threadOpenMasternodeConnections.joinable()) {
181 355 : threadOpenMasternodeConnections.join();
182 : }
183 850 : }
184 :
185 850 : void TierTwoConnMan::interrupt()
186 : {
187 850 : interruptNet();
188 850 : }
189 :
190 132 : void TierTwoConnMan::openConnection(const CAddress& addrConnect, bool isProbe)
191 : {
192 132 : if (interruptNet) return;
193 : // Note: using ip:port string connection instead of the addr to bypass the "only connect to single IPs" validation.
194 264 : std::string conn = addrConnect.ToStringIPPort();
195 264 : CAddress dummyAddr;
196 132 : connman->OpenNetworkConnection(dummyAddr, false, nullptr, conn.data(), false, false, false, true, isProbe);
197 : }
198 :
199 825914 : class PeerData {
200 : public:
201 150934 : 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 14665 : 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 49709 : bool operator==(const uint256& hash) const { return verif_proreg_tx_hash == hash; }
213 : };
214 :
215 355 : void TierTwoConnMan::ThreadOpenMasternodeConnections()
216 : {
217 355 : const auto& chainParams = Params();
218 355 : bool triedConnect = false;
219 95472 : while (!interruptNet) {
220 :
221 : // Retry every 0.1 seconds if a connection was created, otherwise 1.5 seconds
222 95472 : int sleepTime = triedConnect ? 100 : (chainParams.IsRegTestNet() ? 200 : 1500);
223 95472 : if (!interruptNet.sleep_for(std::chrono::milliseconds(sleepTime))) {
224 355 : return;
225 : }
226 :
227 95117 : triedConnect = false;
228 :
229 95117 : if (!fMasterNode || !g_tiertwo_sync_state.IsBlockchainSynced() || !g_connman->GetNetworkActive()) {
230 94985 : continue;
231 : }
232 :
233 : // Gather all connected peers first, so we don't
234 : // try to connect to an already connected peer
235 22702 : std::vector<PeerData> connectedNodes;
236 132 : std::vector<MnService> connectedMnServices;
237 22702 : connman->ForEachNode([&](const CNode* pnode) {
238 150934 : connectedNodes.emplace_back(PeerData{pnode->addr, pnode->fDisconnect, pnode->m_masternode_connection});
239 301868 : if (!pnode->verifiedProRegTxHash.IsNull()) {
240 27898 : connectedMnServices.emplace_back(MnService{pnode->verifiedProRegTxHash, pnode->fInbound});
241 : }
242 150934 : });
243 :
244 : // Try to connect to a single MN per cycle
245 132 : CDeterministicMNCPtr dmnToConnect{nullptr};
246 : // Current list
247 22834 : auto mnList = deterministicMNManager->GetListAtChainTip();
248 22702 : int64_t currentTime = GetAdjustedTime();
249 22702 : bool isProbe = false;
250 22702 : {
251 22702 : LOCK(cs_vPendingMasternodes);
252 :
253 : // First try to connect to pending MNs
254 22702 : 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 22702 : if (!dmnToConnect) {
269 45402 : std::vector<CDeterministicMNCPtr> pending;
270 32167 : for (const auto& group: masternodeQuorumNodes) {
271 24056 : for (const auto& proRegTxHash: group.second) {
272 : // Skip if already have this member connected
273 14590 : if (std::count(connectedMnServices.begin(), connectedMnServices.end(), proRegTxHash) > 0) {
274 12482 : continue;
275 : }
276 :
277 : // Don't try to connect to ourselves
278 6178 : if (WITH_LOCK(cs_vPendingMasternodes, return local_dmn_pro_tx_hash && *local_dmn_pro_tx_hash == proRegTxHash)) {
279 146 : continue;
280 : }
281 :
282 : // Check if DMN exists in tip list
283 2071 : const auto& dmn = mnList.GetValidMN(proRegTxHash);
284 16443 : if (!dmn) continue;
285 1719 : auto peerData = std::find(connectedNodes.begin(), connectedNodes.end(), dmn->pdmnState->addr);
286 :
287 : // Skip already connected nodes.
288 1719 : if (peerData != std::end(connectedNodes) &&
289 57 : (peerData->f_disconnect || peerData->f_is_mn_conn)) {
290 4 : continue;
291 : }
292 :
293 : // Check if we already tried this connection recently to not retry too often
294 1715 : int64_t lastAttempt = g_mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastOutboundAttempt();
295 : // back off trying connecting to an address if we already tried recently
296 1715 : if (currentTime - lastAttempt < chainParams.LLMQConnectionRetryTimeout()) {
297 1606 : continue;
298 : }
299 109 : pending.emplace_back(dmn);
300 : }
301 : }
302 : // Select a random node to connect
303 22701 : if (!pending.empty()) {
304 82 : dmnToConnect = pending[GetRandInt((int) pending.size())];
305 164 : 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 22702 : if (!dmnToConnect) {
312 45238 : std::vector<CDeterministicMNCPtr> pending;
313 23065 : for (auto it = masternodePendingProbes.begin(); it != masternodePendingProbes.end(); ) {
314 506 : auto dmn = mnList.GetMN(*it);
315 446 : if (!dmn) {
316 0 : it = masternodePendingProbes.erase(it);
317 386 : continue;
318 : }
319 :
320 : // Discard already connected outbound MNs
321 446 : auto mnService = std::find(connectedMnServices.begin(), connectedMnServices.end(), dmn->proTxHash);
322 446 : bool connectedAndOutbound = mnService != std::end(connectedMnServices) && !mnService->is_inbound;
323 494 : if (connectedAndOutbound) {
324 : // we already have an outbound connection to this MN so there is no eed to probe it again
325 48 : g_mmetaman.GetMetaInfo(dmn->proTxHash)->SetLastOutboundSuccess(currentTime);
326 48 : it = masternodePendingProbes.erase(it);
327 48 : continue;
328 : }
329 :
330 398 : ++it;
331 :
332 398 : int64_t lastAttempt = g_mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastOutboundAttempt();
333 : // back off trying connecting to an address if we already tried recently
334 398 : if (currentTime - lastAttempt < chainParams.LLMQConnectionRetryTimeout()) {
335 338 : continue;
336 : }
337 60 : pending.emplace_back(dmn);
338 : }
339 :
340 : // Select a random node to connect
341 22619 : if (!pending.empty()) {
342 49 : dmnToConnect = pending[GetRandInt((int)pending.size())];
343 49 : masternodePendingProbes.erase(dmnToConnect->proTxHash);
344 49 : isProbe = true;
345 :
346 98 : 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 22702 : if (!dmnToConnect || interruptNet) {
354 22570 : continue;
355 : }
356 :
357 : // Update last attempt and try connection
358 132 : g_mmetaman.GetMetaInfo(dmnToConnect->proTxHash)->SetLastOutboundAttempt(currentTime);
359 132 : triedConnect = true;
360 :
361 : // Now connect
362 264 : openConnection(CAddress(dmnToConnect->pdmnState->addr, NODE_NETWORK), isProbe);
363 : // should be in the list now if connection was opened
364 264 : bool connected = connman->ForNode(dmnToConnect->pdmnState->addr, CConnman::AllNodes, [&](CNode* pnode) {
365 132 : if (pnode->fDisconnect) { LogPrintf("about to be disconnected\n");
366 63 : return false;
367 : }
368 : return true;
369 : });
370 132 : if (!connected) {
371 126 : 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 126 : g_mmetaman.GetMetaInfo(dmnToConnect->proTxHash)->SetLastOutboundSuccess(0);
375 : }
376 : }
377 : }
378 :
379 226 : 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 226 : int nonMasternodeCount = 0;
383 226 : connman.ForEachNode([&](CNode* pnode) {
384 1254 : if (!pnode->fInbound && !pnode->fFeeler && !pnode->fAddnode && !pnode->m_masternode_connection && !pnode->m_masternode_probe_connection) {
385 585 : nonMasternodeCount++;
386 : }
387 1254 : });
388 226 : if (nonMasternodeCount < (int) connman.GetMaxOutboundNodeCount()) {
389 226 : 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 229 : void TierTwoConnMan::doMaintenance()
412 : {
413 229 : if(!g_tiertwo_sync_state.IsBlockchainSynced() || interruptNet) {
414 3 : return;
415 : }
416 226 : ProcessMasternodeConnections(*connman, *this);
417 : }
418 :
|