Dash Core Source Documentation (0.16.0.1)

Find detailed information regarding the Dash Core source code.

quorums_utils.cpp
Go to the documentation of this file.
1 // Copyright (c) 2018-2019 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 <llmq/quorums.h>
6 #include <llmq/quorums_utils.h>
7 
8 #include <chainparams.h>
9 #include <random.h>
10 #include <spork.h>
11 #include <validation.h>
12 
14 
15 namespace llmq
16 {
17 
18 std::vector<CDeterministicMNCPtr> CLLMQUtils::GetAllQuorumMembers(Consensus::LLMQType llmqType, const CBlockIndex* pindexQuorum)
19 {
20  auto& params = Params().GetConsensus().llmqs.at(llmqType);
21  auto allMns = deterministicMNManager->GetListForBlock(pindexQuorum);
22  auto modifier = ::SerializeHash(std::make_pair(llmqType, pindexQuorum->GetBlockHash()));
23  return allMns.CalculateQuorum(params.size, modifier);
24 }
25 
26 uint256 CLLMQUtils::BuildCommitmentHash(Consensus::LLMQType llmqType, const uint256& blockHash, const std::vector<bool>& validMembers, const CBLSPublicKey& pubKey, const uint256& vvecHash)
27 {
28  CHashWriter hw(SER_NETWORK, 0);
29  hw << llmqType;
30  hw << blockHash;
31  hw << DYNBITSET(validMembers);
32  hw << pubKey;
33  hw << vvecHash;
34  return hw.GetHash();
35 }
36 
37 uint256 CLLMQUtils::BuildSignHash(Consensus::LLMQType llmqType, const uint256& quorumHash, const uint256& id, const uint256& msgHash)
38 {
40  h << llmqType;
41  h << quorumHash;
42  h << id;
43  h << msgHash;
44  return h.GetHash();
45 }
46 
48 {
50  if (spork21 == 0) {
51  return true;
52  }
53  if (spork21 == 1 && llmqType != Consensus::LLMQ_400_60 && llmqType != Consensus::LLMQ_400_85) {
54  return true;
55  }
56  return false;
57 }
58 
60 {
61  // We need to deterministically select who is going to initiate the connection. The naive way would be to simply
62  // return the min(proTxHash1, proTxHash2), but this would create a bias towards MNs with a numerically low
63  // hash. To fix this, we return the proTxHash that has the lowest value of:
64  // hash(min(proTxHash1, proTxHash2), max(proTxHash1, proTxHash2), proTxHashX)
65  // where proTxHashX is the proTxHash to compare
66  uint256 h1;
67  uint256 h2;
68  if (proTxHash1 < proTxHash2) {
69  h1 = ::SerializeHash(std::make_tuple(proTxHash1, proTxHash2, proTxHash1));
70  h2 = ::SerializeHash(std::make_tuple(proTxHash1, proTxHash2, proTxHash2));
71  } else {
72  h1 = ::SerializeHash(std::make_tuple(proTxHash2, proTxHash1, proTxHash1));
73  h2 = ::SerializeHash(std::make_tuple(proTxHash2, proTxHash1, proTxHash2));
74  }
75  if (h1 < h2) {
76  return proTxHash1;
77  }
78  return proTxHash2;
79 }
80 
81 std::set<uint256> CLLMQUtils::GetQuorumConnections(Consensus::LLMQType llmqType, const CBlockIndex* pindexQuorum, const uint256& forMember, bool onlyOutbound)
82 {
83  auto& params = Params().GetConsensus().llmqs.at(llmqType);
84 
85  if (IsAllMembersConnectedEnabled(llmqType)) {
86  auto mns = GetAllQuorumMembers(llmqType, pindexQuorum);
87  std::set<uint256> result;
88 
89  for (auto& dmn : mns) {
90  if (dmn->proTxHash == forMember) {
91  continue;
92  }
93  // Determine which of the two MNs (forMember vs dmn) should initiate the outbound connection and which
94  // one should wait for the inbound connection. We do this in a deterministic way, so that even when we
95  // end up with both connecting to each other, we know which one to disconnect
96  uint256 deterministicOutbound = DeterministicOutboundConnection(forMember, dmn->proTxHash);
97  if (!onlyOutbound || deterministicOutbound == dmn->proTxHash) {
98  result.emplace(dmn->proTxHash);
99  }
100  }
101  return result;
102  } else {
103  return GetQuorumRelayMembers(llmqType, pindexQuorum, forMember, onlyOutbound);
104  }
105 }
106 
107 std::set<uint256> CLLMQUtils::GetQuorumRelayMembers(Consensus::LLMQType llmqType, const CBlockIndex *pindexQuorum, const uint256 &forMember, bool onlyOutbound)
108 {
109  auto mns = GetAllQuorumMembers(llmqType, pindexQuorum);
110  std::set<uint256> result;
111 
112  auto calcOutbound = [&](size_t i, const uint256 proTxHash) {
113  // Relay to nodes at indexes (i+2^k)%n, where
114  // k: 0..max(1, floor(log2(n-1))-1)
115  // n: size of the quorum/ring
116  std::set<uint256> r;
117  int gap = 1;
118  int gap_max = (int)mns.size() - 1;
119  int k = 0;
120  while ((gap_max >>= 1) || k <= 1) {
121  size_t idx = (i + gap) % mns.size();
122  auto& otherDmn = mns[idx];
123  if (otherDmn->proTxHash == proTxHash) {
124  continue;
125  }
126  r.emplace(otherDmn->proTxHash);
127  gap <<= 1;
128  k++;
129  }
130  return r;
131  };
132 
133  for (size_t i = 0; i < mns.size(); i++) {
134  auto& dmn = mns[i];
135  if (dmn->proTxHash == forMember) {
136  auto r = calcOutbound(i, dmn->proTxHash);
137  result.insert(r.begin(), r.end());
138  } else if (!onlyOutbound) {
139  auto r = calcOutbound(i, dmn->proTxHash);
140  if (r.count(forMember)) {
141  result.emplace(dmn->proTxHash);
142  }
143  }
144  }
145 
146  return result;
147 }
148 
149 std::set<size_t> CLLMQUtils::CalcDeterministicWatchConnections(Consensus::LLMQType llmqType, const CBlockIndex* pindexQuorum, size_t memberCount, size_t connectionCount)
150 {
151  static uint256 qwatchConnectionSeed;
152  static std::atomic<bool> qwatchConnectionSeedGenerated{false};
153  static CCriticalSection qwatchConnectionSeedCs;
154  if (!qwatchConnectionSeedGenerated) {
155  LOCK(qwatchConnectionSeedCs);
156  if (!qwatchConnectionSeedGenerated) {
157  qwatchConnectionSeed = GetRandHash();
158  qwatchConnectionSeedGenerated = true;
159  }
160  }
161 
162  std::set<size_t> result;
163  uint256 rnd = qwatchConnectionSeed;
164  for (size_t i = 0; i < connectionCount; i++) {
165  rnd = ::SerializeHash(std::make_pair(rnd, std::make_pair(llmqType, pindexQuorum->GetBlockHash())));
166  result.emplace(rnd.GetUint64(0) % memberCount);
167  }
168  return result;
169 }
170 
171 void CLLMQUtils::EnsureQuorumConnections(Consensus::LLMQType llmqType, const CBlockIndex *pindexQuorum, const uint256& myProTxHash, bool allowWatch)
172 {
173  auto members = GetAllQuorumMembers(llmqType, pindexQuorum);
174  bool isMember = std::find_if(members.begin(), members.end(), [&](const CDeterministicMNCPtr& dmn) { return dmn->proTxHash == myProTxHash; }) != members.end();
175 
176  if (!isMember && !allowWatch) {
177  return;
178  }
179 
180  std::set<uint256> connections;
181  if (isMember) {
182  connections = CLLMQUtils::GetQuorumConnections(llmqType, pindexQuorum, myProTxHash, true);
183  } else {
184  auto cindexes = CLLMQUtils::CalcDeterministicWatchConnections(llmqType, pindexQuorum, members.size(), 1);
185  for (auto idx : cindexes) {
186  connections.emplace(members[idx]->proTxHash);
187  }
188  }
189  if (!connections.empty()) {
190  if (!g_connman->HasMasternodeQuorumNodes(llmqType, pindexQuorum->GetBlockHash()) && LogAcceptCategory(BCLog::LLMQ)) {
191  auto mnList = deterministicMNManager->GetListAtChainTip();
192  std::string debugMsg = strprintf("CLLMQUtils::%s -- adding masternodes quorum connections for quorum %s:\n", __func__, pindexQuorum->GetBlockHash().ToString());
193  for (auto& c : connections) {
194  auto dmn = mnList.GetValidMN(c);
195  if (!dmn) {
196  debugMsg += strprintf(" %s (not in valid MN set anymore)\n", c.ToString());
197  } else {
198  debugMsg += strprintf(" %s (%s)\n", c.ToString(), dmn->pdmnState->addr.ToString(false));
199  }
200  }
201  LogPrint(BCLog::NET_NETCONN, debugMsg.c_str());
202  }
203  g_connman->SetMasternodeQuorumNodes(llmqType, pindexQuorum->GetBlockHash(), connections);
204  }
205 }
206 
207 void CLLMQUtils::AddQuorumProbeConnections(Consensus::LLMQType llmqType, const CBlockIndex *pindexQuorum, const uint256 &myProTxHash)
208 {
209  auto members = GetAllQuorumMembers(llmqType, pindexQuorum);
210  auto curTime = GetAdjustedTime();
211 
212  std::set<uint256> probeConnections;
213  for (auto& dmn : members) {
214  if (dmn->proTxHash == myProTxHash) {
215  continue;
216  }
217  auto lastOutbound = mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastOutboundSuccess();
218  // re-probe after 50 minutes so that the "good connection" check in the DKG doesn't fail just because we're on
219  // the brink of timeout
220  if (curTime - lastOutbound > 50 * 60) {
221  probeConnections.emplace(dmn->proTxHash);
222  }
223  }
224 
225  if (!probeConnections.empty()) {
227  auto mnList = deterministicMNManager->GetListAtChainTip();
228  std::string debugMsg = strprintf("CLLMQUtils::%s -- adding masternodes probes for quorum %s:\n", __func__, pindexQuorum->GetBlockHash().ToString());
229  for (auto& c : probeConnections) {
230  auto dmn = mnList.GetValidMN(c);
231  if (!dmn) {
232  debugMsg += strprintf(" %s (not in valid MN set anymore)\n", c.ToString());
233  } else {
234  debugMsg += strprintf(" %s (%s)\n", c.ToString(), dmn->pdmnState->addr.ToString(false));
235  }
236  }
237  LogPrint(BCLog::NET_NETCONN, debugMsg.c_str());
238  }
239  g_connman->AddPendingProbeConnections(probeConnections);
240  }
241 }
242 
243 bool CLLMQUtils::IsQuorumActive(Consensus::LLMQType llmqType, const uint256& quorumHash)
244 {
245  auto& params = Params().GetConsensus().llmqs.at(llmqType);
246 
247  // sig shares and recovered sigs are only accepted from recent/active quorums
248  // we allow one more active quorum as specified in consensus, as otherwise there is a small window where things could
249  // fail while we are on the brink of a new quorum
250  auto quorums = quorumManager->ScanQuorums(llmqType, (int)params.signingActiveQuorumCount + 1);
251  for (auto& q : quorums) {
252  if (q->qc.quorumHash == quorumHash) {
253  return true;
254  }
255  }
256  return false;
257 }
258 
259 
260 } // namespace llmq
static std::set< uint256 > GetQuorumConnections(Consensus::LLMQType llmqType, const CBlockIndex *pindexQuorum, const uint256 &forMember, bool onlyOutbound)
uint256 GetRandHash()
Definition: random.cpp:384
static uint256 BuildSignHash(Consensus::LLMQType llmqType, const uint256 &quorumHash, const uint256 &id, const uint256 &msgHash)
static void AddQuorumProbeConnections(Consensus::LLMQType llmqType, const CBlockIndex *pindexQuorum, const uint256 &myProTxHash)
#define strprintf
Definition: tinyformat.h:1066
static bool IsQuorumActive(Consensus::LLMQType llmqType, const uint256 &quorumHash)
std::vector< CQuorumCPtr > ScanQuorums(Consensus::LLMQType llmqType, size_t maxCount)
Definition: quorums.cpp:279
std::shared_ptr< const CDeterministicMN > CDeterministicMNCPtr
std::unique_ptr< CDeterministicMNManager > deterministicMNManager
static bool LogAcceptCategory(uint64_t category)
Definition: util.h:152
CQuorumManager * quorumManager
Definition: quorums.cpp:30
CMasternodeMetaMan mmetaman
uint256 GetBlockHash() const
Definition: chain.h:292
uint256 SerializeHash(const T &obj, int nType=SER_GETHASH, int nVersion=PROTOCOL_VERSION)
Compute the 256-bit hash of an object&#39;s serialization.
Definition: hash.h:254
LLMQType
Definition: params.h:48
static uint256 BuildCommitmentHash(Consensus::LLMQType llmqType, const uint256 &blockHash, const std::vector< bool > &validMembers, const CBLSPublicKey &pubKey, const uint256 &vvecHash)
#define LOCK(cs)
Definition: sync.h:178
int64_t GetSporkValue(SporkId nSporkID)
GetSporkValue returns the spork value given a Spork ID.
Definition: spork.cpp:217
std::string ToString() const
Definition: uint256.cpp:62
#define DYNBITSET(obj)
Definition: serialize.h:372
std::map< LLMQType, LLMQParams > llmqs
Definition: params.h:189
CSporkManager sporkManager
Definition: spork.cpp:29
#define LogPrint(category,...)
Definition: util.h:214
uint256 GetHash()
Definition: hash.h:203
256-bit opaque blob.
Definition: uint256.h:123
static uint256 DeterministicOutboundConnection(const uint256 &proTxHash1, const uint256 &proTxHash2)
The block chain is a tree shaped structure starting with the genesis block at the root...
Definition: chain.h:170
const CChainParams & Params()
Return the currently selected parameters.
int64_t GetAdjustedTime()
Definition: timedata.cpp:35
static void EnsureQuorumConnections(Consensus::LLMQType llmqType, const CBlockIndex *pindexQuorum, const uint256 &myProTxHash, bool allowWatch)
static std::set< size_t > CalcDeterministicWatchConnections(Consensus::LLMQType llmqType, const CBlockIndex *pindexQuorum, size_t memberCount, size_t connectionCount)
std::unique_ptr< CConnman > g_connman
Definition: init.cpp:97
A writer stream (for serialization) that computes a 256-bit hash.
Definition: hash.h:184
const Consensus::Params & GetConsensus() const
Definition: chainparams.h:54
static std::set< uint256 > GetQuorumRelayMembers(Consensus::LLMQType llmqType, const CBlockIndex *pindexQuorum, const uint256 &forMember, bool onlyOutbound)
uint64_t GetUint64(int pos) const
Definition: uint256.h:82
static bool IsAllMembersConnectedEnabled(Consensus::LLMQType llmqType)
CMasternodeMetaInfoPtr GetMetaInfo(const uint256 &proTxHash, bool fCreate=true)
Wrapped mutex: supports recursive locking, but no waiting TODO: We should move away from using the re...
Definition: sync.h:94
static std::vector< CDeterministicMNCPtr > GetAllQuorumMembers(Consensus::LLMQType llmqType, const CBlockIndex *pindexQuorum)
Released under the MIT license