PIVX Core  5.6.99
P2P Digital Currency
net_masternodes.cpp
Go to the documentation of this file.
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 
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
14 
15 TierTwoConnMan::TierTwoConnMan(CConnman* _connman) : connman(_connman) {}
17 
19  const uint256& quorumHash,
20  const std::set<uint256>& proTxHashes)
21 {
23  auto it = masternodeQuorumNodes.emplace(QuorumTypeAndHash(llmqType, quorumHash), proTxHashes);
24  if (!it.second) {
25  it.first->second = proTxHashes;
26  }
27 }
28 
30 {
32  std::set<uint256> result;
33  for (const auto& p : masternodeQuorumNodes) {
34  if (p.first.first != llmqType) {
35  continue;
36  }
37  result.emplace(p.first.second);
38  }
39  return result;
40 }
41 
42 std::set<NodeId> TierTwoConnMan::getQuorumNodes(Consensus::LLMQType llmqType, uint256 quorumHash)
43 {
45  std::set<NodeId> result;
46  auto it = masternodeQuorumRelayMembers.find(std::make_pair(llmqType, quorumHash));
47  if (it == masternodeQuorumRelayMembers.end()) {
48  return {};
49  }
50  connman->ForEachNode([&](CNode* pnode) {
51  if (pnode->fDisconnect) {
52  return;
53  }
54  if (!it->second.count(pnode->verifiedProRegTxHash)) {
55  return;
56  }
57  result.emplace(pnode->GetId());
58  });
59  return result;
60 }
61 
63 {
65  return masternodeQuorumNodes.count(QuorumTypeAndHash(llmqType, quorumHash));
66 }
67 
69 {
71  masternodeQuorumNodes.erase(std::make_pair(llmqType, quorumHash));
72 }
73 
74 void TierTwoConnMan::setMasternodeQuorumRelayMembers(Consensus::LLMQType llmqType, const uint256& quorumHash, const std::set<uint256>& proTxHashes)
75 {
77  auto it = masternodeQuorumRelayMembers.emplace(std::make_pair(llmqType, quorumHash), proTxHashes);
78  if (!it.second) {
79  it.first->second = proTxHashes;
80  }
81 
82  // Update existing connections
83  connman->ForEachNode([&](CNode* pnode) {
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  CNetMsgMaker msgMaker(pnode->GetSendVersion());
90  connman->PushMessage(pnode, msgMaker.Make(NetMsgType::QSENDRECSIGS, true));
91  pnode->m_masternode_iqr_connection = true;
92  }
93  });
94 }
95 
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  uint256 assumedProTxHash;
101  if (pnode->verifiedProRegTxHash.IsNull() && !pnode->fInbound) {
102  auto mnList = deterministicMNManager->GetListAtChainTip();
103  auto dmn = mnList.GetMNByService(pnode->addr);
104  if (dmn == nullptr) {
105  // This is definitely not a masternode
106  return false;
107  }
108  assumedProTxHash = dmn->proTxHash;
109  }
110 
112  for (const auto& quorumConn : masternodeQuorumNodes) {
113  if (!pnode->verifiedProRegTxHash.IsNull()) {
114  if (quorumConn.second.count(pnode->verifiedProRegTxHash)) {
115  return true;
116  }
117  } else if (!assumedProTxHash.IsNull()) {
118  if (quorumConn.second.count(assumedProTxHash)) {
119  return true;
120  }
121  }
122  }
123  return false;
124 }
125 
127 {
128  if (protxHash.IsNull()) {
129  return false;
130  }
132  for (const auto& p : masternodeQuorumRelayMembers) {
133  if (p.second.count(protxHash) > 0) {
134  return true;
135  }
136  }
137  return false;
138 }
139 
141 {
143  if (std::find(vPendingMasternodes.begin(), vPendingMasternodes.end(), proTxHash) != vPendingMasternodes.end()) {
144  return false;
145  }
146  vPendingMasternodes.emplace_back(proTxHash);
147  return true;
148 }
149 
150 void TierTwoConnMan::addPendingProbeConnections(const std::set<uint256>& proTxHashes)
151 {
153  masternodePendingProbes.insert(proTxHashes.begin(), proTxHashes.end());
154 }
155 
157 {
159  masternodeQuorumNodes.clear();
160  masternodeQuorumRelayMembers.clear();
161  vPendingMasternodes.clear();
162  masternodePendingProbes.clear();
163 }
164 
166 {
167  // Must be started after connman
168  assert(connman);
170 
171  // Connecting to specific addresses, no masternode connections available
172  if (options.m_has_specified_outgoing) return;
173  // Initiate masternode connections
174  threadOpenMasternodeConnections = std::thread(&TraceThread<std::function<void()> >, "mncon", std::function<void()>(std::bind(&TierTwoConnMan::ThreadOpenMasternodeConnections, this)));
175  // Cleanup process every 60 seconds
176  scheduler.scheduleEvery(std::bind(&TierTwoConnMan::doMaintenance, this), 60 * 1000);
177 }
178 
180  if (threadOpenMasternodeConnections.joinable()) {
182  }
183 }
184 
186 {
187  interruptNet();
188 }
189 
190 void TierTwoConnMan::openConnection(const CAddress& addrConnect, bool isProbe)
191 {
192  if (interruptNet) return;
193  // Note: using ip:port string connection instead of the addr to bypass the "only connect to single IPs" validation.
194  std::string conn = addrConnect.ToStringIPPort();
195  CAddress dummyAddr;
196  connman->OpenNetworkConnection(dummyAddr, false, nullptr, conn.data(), false, false, false, true, isProbe);
197 }
198 
199 class PeerData {
200 public:
201  PeerData(const CService& s, bool disconnect, bool is_mn_conn) : service(s), f_disconnect(disconnect), f_is_mn_conn(is_mn_conn) {}
203  bool f_disconnect{false};
204  bool f_is_mn_conn{false};
205  bool operator==(const CService& s) const { return service == s; }
206 };
207 
208 struct MnService {
209 public:
211  bool is_inbound{false};
212  bool operator==(const uint256& hash) const { return verif_proreg_tx_hash == hash; }
213 };
214 
216 {
217  const auto& chainParams = Params();
218  bool triedConnect = false;
219  while (!interruptNet) {
220 
221  // Retry every 0.1 seconds if a connection was created, otherwise 1.5 seconds
222  int sleepTime = triedConnect ? 100 : (chainParams.IsRegTestNet() ? 200 : 1500);
223  if (!interruptNet.sleep_for(std::chrono::milliseconds(sleepTime))) {
224  return;
225  }
226 
227  triedConnect = false;
228 
229  if (!fMasterNode || !g_tiertwo_sync_state.IsBlockchainSynced() || !g_connman->GetNetworkActive()) {
230  continue;
231  }
232 
233  // Gather all connected peers first, so we don't
234  // try to connect to an already connected peer
235  std::vector<PeerData> connectedNodes;
236  std::vector<MnService> connectedMnServices;
237  connman->ForEachNode([&](const CNode* pnode) {
238  connectedNodes.emplace_back(PeerData{pnode->addr, pnode->fDisconnect, pnode->m_masternode_connection});
239  if (!pnode->verifiedProRegTxHash.IsNull()) {
240  connectedMnServices.emplace_back(MnService{pnode->verifiedProRegTxHash, pnode->fInbound});
241  }
242  });
243 
244  // Try to connect to a single MN per cycle
245  CDeterministicMNCPtr dmnToConnect{nullptr};
246  // Current list
247  auto mnList = deterministicMNManager->GetListAtChainTip();
248  int64_t currentTime = GetAdjustedTime();
249  bool isProbe = false;
250  {
252 
253  // First try to connect to pending MNs
254  if (!vPendingMasternodes.empty()) {
255  auto dmn = mnList.GetValidMN(vPendingMasternodes.front());
256  vPendingMasternodes.erase(vPendingMasternodes.begin());
257  if (dmn) {
258  auto peerData = std::find(connectedNodes.begin(), connectedNodes.end(), dmn->pdmnState->addr);
259  if (peerData == std::end(connectedNodes)) {
260  dmnToConnect = dmn;
261  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  if (!dmnToConnect) {
269  std::vector<CDeterministicMNCPtr> pending;
270  for (const auto& group: masternodeQuorumNodes) {
271  for (const auto& proRegTxHash: group.second) {
272  // Skip if already have this member connected
273  if (std::count(connectedMnServices.begin(), connectedMnServices.end(), proRegTxHash) > 0) {
274  continue;
275  }
276 
277  // Don't try to connect to ourselves
278  if (WITH_LOCK(cs_vPendingMasternodes, return local_dmn_pro_tx_hash && *local_dmn_pro_tx_hash == proRegTxHash)) {
279  continue;
280  }
281 
282  // Check if DMN exists in tip list
283  const auto& dmn = mnList.GetValidMN(proRegTxHash);
284  if (!dmn) continue;
285  auto peerData = std::find(connectedNodes.begin(), connectedNodes.end(), dmn->pdmnState->addr);
286 
287  // Skip already connected nodes.
288  if (peerData != std::end(connectedNodes) &&
289  (peerData->f_disconnect || peerData->f_is_mn_conn)) {
290  continue;
291  }
292 
293  // Check if we already tried this connection recently to not retry too often
294  int64_t lastAttempt = g_mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastOutboundAttempt();
295  // back off trying connecting to an address if we already tried recently
296  if (currentTime - lastAttempt < chainParams.LLMQConnectionRetryTimeout()) {
297  continue;
298  }
299  pending.emplace_back(dmn);
300  }
301  }
302  // Select a random node to connect
303  if (!pending.empty()) {
304  dmnToConnect = pending[GetRandInt((int) pending.size())];
305  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  if (!dmnToConnect) {
312  std::vector<CDeterministicMNCPtr> pending;
313  for (auto it = masternodePendingProbes.begin(); it != masternodePendingProbes.end(); ) {
314  auto dmn = mnList.GetMN(*it);
315  if (!dmn) {
316  it = masternodePendingProbes.erase(it);
317  continue;
318  }
319 
320  // Discard already connected outbound MNs
321  auto mnService = std::find(connectedMnServices.begin(), connectedMnServices.end(), dmn->proTxHash);
322  bool connectedAndOutbound = mnService != std::end(connectedMnServices) && !mnService->is_inbound;
323  if (connectedAndOutbound) {
324  // we already have an outbound connection to this MN so there is no eed to probe it again
325  g_mmetaman.GetMetaInfo(dmn->proTxHash)->SetLastOutboundSuccess(currentTime);
326  it = masternodePendingProbes.erase(it);
327  continue;
328  }
329 
330  ++it;
331 
332  int64_t lastAttempt = g_mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastOutboundAttempt();
333  // back off trying connecting to an address if we already tried recently
334  if (currentTime - lastAttempt < chainParams.LLMQConnectionRetryTimeout()) {
335  continue;
336  }
337  pending.emplace_back(dmn);
338  }
339 
340  // Select a random node to connect
341  if (!pending.empty()) {
342  dmnToConnect = pending[GetRandInt((int)pending.size())];
343  masternodePendingProbes.erase(dmnToConnect->proTxHash);
344  isProbe = true;
345 
346  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  if (!dmnToConnect || interruptNet) {
354  continue;
355  }
356 
357  // Update last attempt and try connection
358  g_mmetaman.GetMetaInfo(dmnToConnect->proTxHash)->SetLastOutboundAttempt(currentTime);
359  triedConnect = true;
360 
361  // Now connect
362  openConnection(CAddress(dmnToConnect->pdmnState->addr, NODE_NETWORK), isProbe);
363  // should be in the list now if connection was opened
364  bool connected = connman->ForNode(dmnToConnect->pdmnState->addr, CConnman::AllNodes, [&](CNode* pnode) {
365  if (pnode->fDisconnect) { LogPrintf("about to be disconnected\n");
366  return false;
367  }
368  return true;
369  });
370  if (!connected) {
371  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  g_mmetaman.GetMetaInfo(dmnToConnect->proTxHash)->SetLastOutboundSuccess(0);
375  }
376  }
377 }
378 
379 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  int nonMasternodeCount = 0;
383  connman.ForEachNode([&](CNode* pnode) {
384  if (!pnode->fInbound && !pnode->fFeeler && !pnode->fAddnode && !pnode->m_masternode_connection && !pnode->m_masternode_probe_connection) {
385  nonMasternodeCount++;
386  }
387  });
388  if (nonMasternodeCount < (int) connman.GetMaxOutboundNodeCount()) {
389  return;
390  }
391 
392  connman.ForEachNode([&](CNode* pnode) {
393  // we're only disconnecting m_masternode_connection connections
394  if (!pnode->m_masternode_connection) return;
395  // we're only disconnecting outbound connections (inbound connections are disconnected in AcceptConnection())
396  if (pnode->fInbound) return;
397  // we're not disconnecting LLMQ connections
398  if (tierTwoConnMan.isMasternodeQuorumNode(pnode)) return;
399  // we're not disconnecting masternode probes for at least a few seconds
400  if (pnode->m_masternode_probe_connection && GetSystemTimeInSeconds() - pnode->nTimeConnected < 5) return;
401 
402  if (fLogIPs) {
403  LogPrintf("Closing Masternode connection: peer=%d, addr=%s\n", pnode->GetId(), pnode->addr.ToString());
404  } else {
405  LogPrintf("Closing Masternode connection: peer=%d\n", pnode->GetId());
406  }
407  pnode->fDisconnect = true;
408  });
409 }
410 
412 {
414  return;
415  }
416  ProcessMasternodeConnections(*connman, *this);
417 }
418 
const CChainParams & Params()
Return the currently selected parameters.
A CService with information about it as peer.
Definition: protocol.h:338
Definition: net.h:145
bool ForNode(NodeId id, std::function< bool(CNode *pnode)> func)
Definition: net.cpp:2799
void OpenNetworkConnection(const CAddress &addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound=nullptr, const char *strDest=nullptr, bool fOneShot=false, bool fFeeler=false, bool fAddnode=false, bool masternode_connection=false, bool masternode_probe_connection=false)
Definition: net.cpp:1937
size_t GetMaxOutboundNodeCount()
Definition: net.cpp:2513
constexpr static const CAllNodes AllNodes
Definition: net.h:222
void ForEachNode(Callable &&func)
Definition: net.h:257
CMasternodeMetaInfoPtr GetMetaInfo(const uint256 &proTxHash, bool fCreate=true)
Information about a peer.
Definition: net.h:669
bool fAddnode
Definition: net.h:709
std::atomic< bool > m_masternode_connection
Definition: net.h:710
NodeId GetId() const
Definition: net.h:825
bool fFeeler
Definition: net.h:707
uint256 verifiedProRegTxHash
Definition: net.h:802
const CAddress addr
Definition: net.h:698
const int64_t nTimeConnected
Definition: net.h:696
std::atomic< bool > m_masternode_probe_connection
Definition: net.h:711
std::atomic< bool > m_masternode_iqr_connection
Definition: net.h:712
const bool fInbound
Definition: net.h:715
std::atomic_bool fDisconnect
Definition: net.h:722
void scheduleEvery(Function f, int64_t deltaMilliSeconds)
Definition: scheduler.cpp:105
A combination of a network address (CNetAddr) and a (TCP) port.
Definition: netaddress.h:484
std::string ToStringIPPort() const
Definition: netaddress.cpp:945
std::string ToString() const
Definition: netaddress.cpp:954
bool sleep_for(std::chrono::milliseconds rel_time)
PeerData(const CService &s, bool disconnect, bool is_mn_conn)
const CService service
bool operator==(const CService &s) const
bool hasQuorumNodes(Consensus::LLMQType llmqType, const uint256 &quorumHash)
std::thread threadOpenMasternodeConnections
void addPendingProbeConnections(const std::set< uint256 > &proTxHashes)
void ThreadOpenMasternodeConnections()
RecursiveMutex cs_vPendingMasternodes
bool isMasternodeQuorumRelayMember(const uint256 &protxHash)
void start(CScheduler &scheduler, const TierTwoConnMan::Options &options)
CConnman * connman
std::pair< Consensus::LLMQType, uint256 > QuorumTypeAndHash
void openConnection(const CAddress &addrConnect, bool isProbe)
void setMasternodeQuorumRelayMembers(Consensus::LLMQType llmqType, const uint256 &quorumHash, const std::set< uint256 > &proTxHashes)
TierTwoConnMan(CConnman *_connman)
bool isMasternodeQuorumNode(const CNode *pnode)
void setQuorumNodes(Consensus::LLMQType llmqType, const uint256 &quorumHash, const std::set< uint256 > &proTxHashes)
CThreadInterrupt interruptNet
std::set< uint256 > getQuorumNodes(Consensus::LLMQType llmqType)
bool addPendingMasternode(const uint256 &proTxHash)
void removeQuorumNodes(Consensus::LLMQType llmqType, const uint256 &quorumHash)
bool IsBlockchainSynced() const
bool IsNull() const
Definition: uint256.h:36
256-bit opaque blob.
Definition: uint256.h:138
std::unique_ptr< CDeterministicMNManager > deterministicMNManager
std::shared_ptr< const CDeterministicMN > CDeterministicMNCPtr
if(!read_stdin(buffer))
Definition: fuzz.cpp:72
std::unique_ptr< CConnman > g_connman
Definition: init.cpp:90
@ LOCK
Definition: lockunlock.h:16
bool fLogIPs
Definition: logging.cpp:28
#define LogPrint(category,...)
Definition: logging.h:163
CMasternodeMetaMan g_mmetaman
@ NET_MN
Definition: logging.h:67
LLMQType
Definition: params.h:90
@ NODE_NETWORK
Definition: protocol.h:318
int GetRandInt(int nMax) noexcept
Definition: random.cpp:591
@ proTxHash
Definition: rpcevo.cpp:50
uint256 verif_proreg_tx_hash
bool operator==(const uint256 &hash) const
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:247
std::atomic< bool > fMasterNode
Definition: system.cpp:87
void TraceThread(const std::string name, Callable func)
Definition: system.h:271
TierTwoSyncState g_tiertwo_sync_state
int64_t GetAdjustedTime()
Definition: timedata.cpp:36
const uint256 UINT256_ZERO
constant uint256 instances
Definition: uint256.h:175
int64_t GetSystemTimeInSeconds()
Returns the system time (not mockable)
Definition: utiltime.cpp:69
CScheduler scheduler