PIVX Core  5.6.99
P2P Digital Currency
quorums_connections.cpp
Go to the documentation of this file.
1 // Copyright (c) 2018-2019 The Dash Core developers
2 // Copyright (c) 2022 The PIVX Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
7 
8 #include "evo/deterministicmns.h"
9 #include "llmq/quorums.h"
10 #include "net.h"
11 #include "tiertwo/masternode_meta_manager.h" // for g_mmetaman
13 #include "validation.h"
14 
15 #include <vector>
16 
17 namespace llmq
18 {
19 
20 
21 uint256 DeterministicOutboundConnection(const uint256& proTxHash1, const uint256& proTxHash2)
22 {
23  // We need to deterministically select who is going to initiate the connection. The naive way would be to simply
24  // return the min(proTxHash1, proTxHash2), but this would create a bias towards MNs with a numerically low
25  // hash. To fix this, we return the proTxHash that has the lowest value of:
26  // hash(min(proTxHash1, proTxHash2), max(proTxHash1, proTxHash2), proTxHashX)
27  // where proTxHashX is the proTxHash to compare
28  uint256 h1;
29  uint256 h2;
30  if (proTxHash1 < proTxHash2) {
31  h1 = ::SerializeHash(std::make_tuple(proTxHash1, proTxHash2, proTxHash1));
32  h2 = ::SerializeHash(std::make_tuple(proTxHash1, proTxHash2, proTxHash2));
33  } else {
34  h1 = ::SerializeHash(std::make_tuple(proTxHash2, proTxHash1, proTxHash1));
35  h2 = ::SerializeHash(std::make_tuple(proTxHash2, proTxHash1, proTxHash2));
36  }
37  if (h1 < h2) {
38  return proTxHash1;
39  }
40  return proTxHash2;
41 }
42 
43 std::set<uint256> GetQuorumRelayMembers(const std::vector<CDeterministicMNCPtr>& mnList,
44  unsigned int forMemberIndex)
45 {
46  assert(forMemberIndex < mnList.size());
47 
48  // Special case
49  if (mnList.size() == 2) {
50  return {mnList[1 - forMemberIndex]->proTxHash};
51  }
52 
53  // Relay to nodes at indexes (i+2^k)%n, where
54  // k: 0..max(1, floor(log2(n-1))-1)
55  // n: size of the quorum/ring
56  std::set<uint256> r;
57  int gap = 1;
58  int gap_max = (int)mnList.size() - 1;
59  int k = 0;
60  while ((gap_max >>= 1) || k <= 1) {
61  size_t idx = (forMemberIndex + gap) % mnList.size();
62  r.emplace(mnList[idx]->proTxHash);
63  gap <<= 1;
64  k++;
65  }
66  return r;
67 }
68 
69 static std::set<uint256> GetQuorumConnections(const std::vector<CDeterministicMNCPtr>& mns, const uint256& forMember, bool onlyOutbound)
70 {
71  std::set<uint256> result;
72  for (auto& dmn : mns) {
73  if (dmn->proTxHash == forMember) {
74  continue;
75  }
76  // Determine which of the two MNs (forMember vs dmn) should initiate the outbound connection and which
77  // one should wait for the inbound connection. We do this in a deterministic way, so that even when we
78  // end up with both connecting to each other, we know which one to disconnect
79  uint256 deterministicOutbound = DeterministicOutboundConnection(forMember, dmn->proTxHash);
80  if (!onlyOutbound || deterministicOutbound == dmn->proTxHash) {
81  result.emplace(dmn->proTxHash);
82  }
83  }
84  return result;
85 }
86 
87 std::set<size_t> CalcDeterministicWatchConnections(Consensus::LLMQType llmqType, const CBlockIndex* pindexQuorum, size_t memberCount, size_t connectionCount)
88 {
89  static uint256 qwatchConnectionSeed;
90  static std::atomic<bool> qwatchConnectionSeedGenerated{false};
91  static RecursiveMutex qwatchConnectionSeedCs;
92  if (!qwatchConnectionSeedGenerated) {
93  LOCK(qwatchConnectionSeedCs);
94  if (!qwatchConnectionSeedGenerated) {
95  qwatchConnectionSeed = GetRandHash();
96  qwatchConnectionSeedGenerated = true;
97  }
98  }
99 
100  std::set<size_t> result;
101  uint256 rnd = qwatchConnectionSeed;
102  for (size_t i = 0; i < connectionCount; i++) {
103  rnd = ::SerializeHash(std::make_pair(rnd, std::make_pair(static_cast<uint8_t>(llmqType), pindexQuorum->GetBlockHash())));
104  result.emplace(rnd.GetUint64(0) % memberCount);
105  }
106  return result;
107 }
108 
109 // ensure connection to a given list of quorums
110 void EnsureLatestQuorumConnections(Consensus::LLMQType llmqType, const CBlockIndex* pindexNew, const uint256& myProTxHash, std::vector<CQuorumCPtr>& lastQuorums)
111 {
112  const auto& params = Params().GetConsensus().llmqs.at(llmqType);
113  auto connman = g_connman->GetTierTwoConnMan();
114 
115  auto connmanQuorumsToDelete = connman->getQuorumNodes(llmqType);
116 
117  // don't remove connections for the currently in-progress DKG round
118  int curDkgHeight = pindexNew->nHeight - (pindexNew->nHeight % params.dkgInterval);
119  auto curDkgBlock = pindexNew->GetAncestor(curDkgHeight)->GetBlockHash();
120  connmanQuorumsToDelete.erase(curDkgBlock);
121 
122  for (auto& quorum : lastQuorums) {
123  if (!quorum->IsMember(myProTxHash)) {
124  continue;
125  }
126 
127  EnsureQuorumConnections(llmqType, quorum->pindexQuorum, myProTxHash);
128 
129  connmanQuorumsToDelete.erase(quorum->pindexQuorum->GetBlockHash());
130  }
131 
132  for (auto& qh : connmanQuorumsToDelete) {
133  LogPrintf("CQuorumManager::%s -- removing masternodes quorum connections for quorum %s:\n", __func__, qh.ToString());
134  connman->removeQuorumNodes(llmqType, qh);
135  }
136 }
137 
138 // ensure connection to a given quorum
139 void EnsureQuorumConnections(Consensus::LLMQType llmqType, const CBlockIndex* pindexQuorum, const uint256& myProTxHash)
140 {
141  const auto& members = deterministicMNManager->GetAllQuorumMembers(llmqType, pindexQuorum);
142  auto itMember = std::find_if(members.begin(), members.end(), [&](const CDeterministicMNCPtr& dmn) { return dmn->proTxHash == myProTxHash; });
143  bool isMember = itMember != members.end();
144 
145  if (!isMember) { // && !CLLMQUtils::IsWatchQuorumsEnabled()) {
146  return;
147  }
148 
149  std::set<uint256> connections;
150  std::set<uint256> relayMembers;
151  if (isMember) {
152  connections = GetQuorumConnections(members, myProTxHash, true);
153  unsigned int memberIndex = itMember - members.begin();
154  relayMembers = GetQuorumRelayMembers(members, memberIndex);
155  } else {
156  auto cindexes = CalcDeterministicWatchConnections(llmqType, pindexQuorum, members.size(), 1);
157  for (auto idx : cindexes) {
158  connections.emplace(members[idx]->proTxHash);
159  }
160  relayMembers = connections;
161  }
162  if (!connections.empty()) {
163  auto connman = g_connman->GetTierTwoConnMan();
164  if (!connman->hasQuorumNodes(llmqType, pindexQuorum->GetBlockHash()) && LogAcceptCategory(BCLog::LLMQ)) {
165  auto mnList = deterministicMNManager->GetListAtChainTip();
166  std::string debugMsg = strprintf("CLLMQUtils::%s -- adding masternodes quorum connections for quorum %s:\n", __func__, pindexQuorum->GetBlockHash().ToString());
167  for (auto& c : connections) {
168  auto dmn = mnList.GetValidMN(c);
169  if (!dmn) {
170  debugMsg += strprintf(" %s (not in valid MN set anymore)\n", c.ToString());
171  } else {
172  debugMsg += strprintf(" %s (%s)\n", c.ToString(), dmn->pdmnState->addr.ToString());
173  }
174  }
175  LogPrint(BCLog::LLMQ, debugMsg.c_str()); /* Continued */
176  }
177  connman->setQuorumNodes(llmqType, pindexQuorum->GetBlockHash(), connections);
178  }
179  if (!relayMembers.empty()) {
180  auto connman = g_connman->GetTierTwoConnMan();
181  connman->setMasternodeQuorumRelayMembers(llmqType, pindexQuorum->GetBlockHash(), relayMembers);
182  }
183 }
184 
185 void AddQuorumProbeConnections(Consensus::LLMQType llmqType, const CBlockIndex *pindexQuorum, const uint256 &myProTxHash)
186 {
187  auto members = deterministicMNManager->GetAllQuorumMembers(llmqType, pindexQuorum);
188  auto curTime = GetAdjustedTime();
189 
190  std::set<uint256> probeConnections;
191  for (auto& dmn : members) {
192  if (dmn->proTxHash == myProTxHash) {
193  continue;
194  }
195  auto lastOutbound = g_mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastOutboundSuccess();
196  // re-probe after 50 minutes so that the "good connection" check in the DKG doesn't fail just because we're on
197  // the brink of timeout
198  if (curTime - lastOutbound > 50 * 60) {
199  probeConnections.emplace(dmn->proTxHash);
200  }
201  }
202 
203  if (!probeConnections.empty()) {
204  if (LogAcceptCategory(BCLog::LLMQ)) {
205  auto mnList = deterministicMNManager->GetListAtChainTip();
206  std::string debugMsg = strprintf("CLLMQUtils::%s -- adding masternodes probes for quorum %s:\n", __func__, pindexQuorum->GetBlockHash().ToString());
207  for (auto& c : probeConnections) {
208  auto dmn = mnList.GetValidMN(c);
209  if (!dmn) {
210  debugMsg += strprintf(" %s (not in valid MN set anymore)\n", c.ToString());
211  } else {
212  debugMsg += strprintf(" %s (%s)\n", c.ToString(), dmn->pdmnState->addr.ToString());
213  }
214  }
215  LogPrint(BCLog::LLMQ, debugMsg.c_str()); /* Continued */
216  }
217  g_connman->GetTierTwoConnMan()->addPendingProbeConnections(probeConnections);
218  }
219 }
220 
221 } // namespace llmq
const CChainParams & Params()
Return the currently selected parameters.
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: chain.h:139
uint256 GetBlockHash() const
Definition: chain.h:215
CBlockIndex * GetAncestor(int height)
Efficiently find an ancestor of this block.
Definition: chain.cpp:113
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: chain.h:151
const Consensus::Params & GetConsensus() const
Definition: chainparams.h:72
CMasternodeMetaInfoPtr GetMetaInfo(const uint256 &proTxHash, bool fCreate=true)
std::string ToString() const
Definition: uint256.cpp:65
uint64_t GetUint64(int pos) const
Definition: uint256.h:88
256-bit opaque blob.
Definition: uint256.h:138
std::unique_ptr< CDeterministicMNManager > deterministicMNManager
std::shared_ptr< const CDeterministicMN > CDeterministicMNCPtr
uint256 SerializeHash(const T &obj, int nType=SER_GETHASH, int nVersion=PROTOCOL_VERSION)
Compute the 256-bit hash of an object's serialization.
Definition: hash.h:289
std::unique_ptr< CConnman > g_connman
Definition: init.cpp:90
@ LOCK
Definition: lockunlock.h:16
#define LogPrint(category,...)
Definition: logging.h:163
CMasternodeMetaMan g_mmetaman
@ LLMQ
Definition: logging.h:66
LLMQType
Definition: params.h:90
Definition: quorums.cpp:26
uint256 DeterministicOutboundConnection(const uint256 &proTxHash1, const uint256 &proTxHash2)
void EnsureLatestQuorumConnections(Consensus::LLMQType llmqType, const CBlockIndex *pindexNew, const uint256 &myProTxHash, std::vector< CQuorumCPtr > &lastQuorums)
void EnsureQuorumConnections(Consensus::LLMQType llmqType, const CBlockIndex *pindexQuorum, const uint256 &myProTxHash)
void AddQuorumProbeConnections(Consensus::LLMQType llmqType, const CBlockIndex *pindexQuorum, const uint256 &myProTxHash)
std::set< size_t > CalcDeterministicWatchConnections(Consensus::LLMQType llmqType, const CBlockIndex *pindexQuorum, size_t memberCount, size_t connectionCount)
std::set< uint256 > GetQuorumRelayMembers(const std::vector< CDeterministicMNCPtr > &mnList, unsigned int forMemberIndex)
uint256 GetRandHash() noexcept
Definition: random.cpp:596
@ proTxHash
Definition: rpcevo.cpp:50
std::map< LLMQType, LLMQParams > llmqs
Definition: params.h:279
int64_t GetAdjustedTime()
Definition: timedata.cpp:36
#define strprintf
Definition: tinyformat.h:1056