Dash Core Source Documentation (0.16.0.1)

Find detailed information regarding the Dash Core source code.

quorums.cpp
Go to the documentation of this file.
1 // Copyright (c) 2018-2019 The Dash Core developers
2 // Distributed under the MIT/X11 software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include <llmq/quorums.h>
10 #include <llmq/quorums_init.h>
11 #include <llmq/quorums_utils.h>
12 
13 #include <evo/specialtx.h>
14 
16 #include <chainparams.h>
17 #include <init.h>
19 #include <univalue.h>
20 #include <validation.h>
21 
22 #include <cxxtimer.hpp>
23 
24 namespace llmq
25 {
26 
27 static const std::string DB_QUORUM_SK_SHARE = "q_Qsk";
28 static const std::string DB_QUORUM_QUORUM_VVEC = "q_Qqvvec";
29 
31 
32 static uint256 MakeQuorumKey(const CQuorum& q)
33 {
34  CHashWriter hw(SER_NETWORK, 0);
35  hw << q.params.type;
36  hw << q.qc.quorumHash;
37  for (const auto& dmn : q.members) {
38  hw << dmn->proTxHash;
39  }
40  return hw.GetHash();
41 }
42 
44 {
45  // most likely the thread is already done
47  // watch out to not join the thread when we're called from inside the thread, which might happen on shutdown. This
48  // is because on shutdown the thread is the last owner of the shared CQuorum instance and thus the destroyer of it.
49  if (cachePopulatorThread.joinable() && cachePopulatorThread.get_id() != std::this_thread::get_id()) {
50  cachePopulatorThread.join();
51  }
52 }
53 
54 void CQuorum::Init(const CFinalCommitment& _qc, const CBlockIndex* _pindexQuorum, const uint256& _minedBlockHash, const std::vector<CDeterministicMNCPtr>& _members)
55 {
56  qc = _qc;
57  pindexQuorum = _pindexQuorum;
58  members = _members;
59  minedBlockHash = _minedBlockHash;
60 }
61 
62 bool CQuorum::IsMember(const uint256& proTxHash) const
63 {
64  for (auto& dmn : members) {
65  if (dmn->proTxHash == proTxHash) {
66  return true;
67  }
68  }
69  return false;
70 }
71 
72 bool CQuorum::IsValidMember(const uint256& proTxHash) const
73 {
74  for (size_t i = 0; i < members.size(); i++) {
75  if (members[i]->proTxHash == proTxHash) {
76  return qc.validMembers[i];
77  }
78  }
79  return false;
80 }
81 
82 CBLSPublicKey CQuorum::GetPubKeyShare(size_t memberIdx) const
83 {
84  if (quorumVvec == nullptr || memberIdx >= members.size() || !qc.validMembers[memberIdx]) {
85  return CBLSPublicKey();
86  }
87  auto& m = members[memberIdx];
88  return blsCache.BuildPubKeyShare(m->proTxHash, quorumVvec, CBLSId::FromHash(m->proTxHash));
89 }
90 
92 {
93  return skShare;
94 }
95 
96 int CQuorum::GetMemberIndex(const uint256& proTxHash) const
97 {
98  for (size_t i = 0; i < members.size(); i++) {
99  if (members[i]->proTxHash == proTxHash) {
100  return (int)i;
101  }
102  }
103  return -1;
104 }
105 
107 {
108  uint256 dbKey = MakeQuorumKey(*this);
109 
110  if (quorumVvec != nullptr) {
111  evoDb.GetRawDB().Write(std::make_pair(DB_QUORUM_QUORUM_VVEC, dbKey), *quorumVvec);
112  }
113  if (skShare.IsValid()) {
114  evoDb.GetRawDB().Write(std::make_pair(DB_QUORUM_SK_SHARE, dbKey), skShare);
115  }
116 }
117 
119 {
120  uint256 dbKey = MakeQuorumKey(*this);
121 
123  if (evoDb.Read(std::make_pair(DB_QUORUM_QUORUM_VVEC, dbKey), qv)) {
124  quorumVvec = std::make_shared<BLSVerificationVector>(std::move(qv));
125  } else {
126  return false;
127  }
128 
129  // We ignore the return value here as it is ok if this fails. If it fails, it usually means that we are not a
130  // member of the quorum but observed the whole DKG process to have the quorum verification vector.
131  evoDb.Read(std::make_pair(DB_QUORUM_SK_SHARE, dbKey), skShare);
132 
133  return true;
134 }
135 
136 void CQuorum::StartCachePopulatorThread(std::shared_ptr<CQuorum> _this)
137 {
138  if (_this->quorumVvec == nullptr) {
139  return;
140  }
141 
142  cxxtimer::Timer t(true);
143  LogPrint(BCLog::LLMQ, "CQuorum::StartCachePopulatorThread -- start\n");
144 
145  // this thread will exit after some time
146  // when then later some other thread tries to get keys, it will be much faster
147  _this->cachePopulatorThread = std::thread([_this, t]() {
148  RenameThread("dash-q-cachepop");
149  for (size_t i = 0; i < _this->members.size() && !_this->stopCachePopulatorThread && !ShutdownRequested(); i++) {
150  if (_this->qc.validMembers[i]) {
151  _this->GetPubKeyShare(i);
152  }
153  }
154  LogPrint(BCLog::LLMQ, "CQuorum::StartCachePopulatorThread -- done. time=%d\n", t.count());
155  });
156 }
157 
159  evoDb(_evoDb),
160  blsWorker(_blsWorker),
161  dkgManager(_dkgManager)
162 {
163 }
164 
165 void CQuorumManager::UpdatedBlockTip(const CBlockIndex* pindexNew, bool fInitialDownload)
166 {
168  return;
169  }
170 
171  for (auto& p : Params().GetConsensus().llmqs) {
172  EnsureQuorumConnections(p.first, pindexNew);
173  }
174 }
175 
177 {
178  const auto& params = Params().GetConsensus().llmqs.at(llmqType);
179 
180  auto myProTxHash = activeMasternodeInfo.proTxHash;
181  auto lastQuorums = ScanQuorums(llmqType, pindexNew, (size_t)params.keepOldConnections);
182 
183  auto connmanQuorumsToDelete = g_connman->GetMasternodeQuorums(llmqType);
184 
185  // don't remove connections for the currently in-progress DKG round
186  int curDkgHeight = pindexNew->nHeight - (pindexNew->nHeight % params.dkgInterval);
187  auto curDkgBlock = pindexNew->GetAncestor(curDkgHeight)->GetBlockHash();
188  connmanQuorumsToDelete.erase(curDkgBlock);
189 
190  bool allowWatch = gArgs.GetBoolArg("-watchquorums", DEFAULT_WATCH_QUORUMS);
191  for (auto& quorum : lastQuorums) {
192  if (!quorum->IsMember(myProTxHash) && !allowWatch) {
193  continue;
194  }
195 
196  CLLMQUtils::EnsureQuorumConnections(llmqType, quorum->pindexQuorum, myProTxHash, allowWatch);
197 
198  connmanQuorumsToDelete.erase(quorum->qc.quorumHash);
199  }
200 
201  for (auto& qh : connmanQuorumsToDelete) {
202  LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- removing masternodes quorum connections for quorum %s:\n", __func__, qh.ToString());
203  g_connman->RemoveMasternodeQuorumNodes(llmqType, qh);
204  }
205 }
206 
207 bool CQuorumManager::BuildQuorumFromCommitment(const CFinalCommitment& qc, const CBlockIndex* pindexQuorum, const uint256& minedBlockHash, std::shared_ptr<CQuorum>& quorum) const
208 {
209  assert(pindexQuorum);
210  assert(qc.quorumHash == pindexQuorum->GetBlockHash());
211 
212  auto members = CLLMQUtils::GetAllQuorumMembers((Consensus::LLMQType)qc.llmqType, pindexQuorum);
213 
214  quorum->Init(qc, pindexQuorum, minedBlockHash, members);
215 
216  bool hasValidVvec = false;
217  if (quorum->ReadContributions(evoDb)) {
218  hasValidVvec = true;
219  } else {
220  if (BuildQuorumContributions(qc, quorum)) {
221  quorum->WriteContributions(evoDb);
222  hasValidVvec = true;
223  } else {
224  LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- quorum.ReadContributions and BuildQuorumContributions for block %s failed\n", __func__, qc.quorumHash.ToString());
225  }
226  }
227 
228  if (hasValidVvec) {
229  // pre-populate caches in the background
230  // recovering public key shares is quite expensive and would result in serious lags for the first few signing
231  // sessions if the shares would be calculated on-demand
233  }
234 
235  return true;
236 }
237 
238 bool CQuorumManager::BuildQuorumContributions(const CFinalCommitment& fqc, std::shared_ptr<CQuorum>& quorum) const
239 {
240  std::vector<uint16_t> memberIndexes;
241  std::vector<BLSVerificationVectorPtr> vvecs;
242  BLSSecretKeyVector skContributions;
243  if (!dkgManager.GetVerifiedContributions((Consensus::LLMQType)fqc.llmqType, quorum->pindexQuorum, fqc.validMembers, memberIndexes, vvecs, skContributions)) {
244  return false;
245  }
246 
247  BLSVerificationVectorPtr quorumVvec;
248  CBLSSecretKey skShare;
249 
250  cxxtimer::Timer t2(true);
251  quorumVvec = blsWorker.BuildQuorumVerificationVector(vvecs);
252  if (quorumVvec == nullptr) {
253  LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- failed to build quorumVvec\n", __func__);
254  // without the quorum vvec, there can't be a skShare, so we fail here. Failure is not fatal here, as it still
255  // allows to use the quorum as a non-member (verification through the quorum pub key)
256  return false;
257  }
258  skShare = blsWorker.AggregateSecretKeys(skContributions);
259  if (!skShare.IsValid()) {
260  LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- failed to build skShare\n", __func__);
261  // We don't bail out here as this is not a fatal error and still allows us to recover public key shares (as we
262  // have a valid quorum vvec at this point)
263  }
264  t2.stop();
265 
266  LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- built quorum vvec and skShare. time=%d\n", __func__, t2.count());
267 
268  quorum->quorumVvec = quorumVvec;
269  quorum->skShare = skShare;
270 
271  return true;
272 }
273 
274 bool CQuorumManager::HasQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash)
275 {
276  return quorumBlockProcessor->HasMinedCommitment(llmqType, quorumHash);
277 }
278 
279 std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType, size_t maxCount)
280 {
281  const CBlockIndex* pindex;
282  {
283  LOCK(cs_main);
284  pindex = chainActive.Tip();
285  }
286  return ScanQuorums(llmqType, pindex, maxCount);
287 }
288 
289 std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType, const CBlockIndex* pindexStart, size_t maxCount)
290 {
291  auto& params = Params().GetConsensus().llmqs.at(llmqType);
292 
293  auto cacheKey = std::make_pair(llmqType, pindexStart->GetBlockHash());
294  const size_t cacheMaxSize = params.signingActiveQuorumCount + 1;
295 
296  std::vector<CQuorumCPtr> result;
297 
298  if (maxCount <= cacheMaxSize) {
300  if (scanQuorumsCache.get(cacheKey, result)) {
301  if (result.size() > maxCount) {
302  result.resize(maxCount);
303  }
304  return result;
305  }
306  }
307 
308  bool storeCache = false;
309  size_t maxCount2 = maxCount;
310  if (maxCount2 <= cacheMaxSize) {
311  maxCount2 = cacheMaxSize;
312  storeCache = true;
313  }
314 
315  auto quorumIndexes = quorumBlockProcessor->GetMinedCommitmentsUntilBlock(params.type, pindexStart, maxCount2);
316  result.reserve(quorumIndexes.size());
317 
318  for (auto& quorumIndex : quorumIndexes) {
319  assert(quorumIndex);
320  auto quorum = GetQuorum(params.type, quorumIndex);
321  assert(quorum != nullptr);
322  result.emplace_back(quorum);
323  }
324 
325  if (storeCache) {
327  scanQuorumsCache.insert(cacheKey, result);
328  }
329 
330  if (result.size() > maxCount) {
331  result.resize(maxCount);
332  }
333 
334  return result;
335 }
336 
338 {
339  CBlockIndex* pindexQuorum;
340  {
341  LOCK(cs_main);
342  auto quorumIt = mapBlockIndex.find(quorumHash);
343 
344  if (quorumIt == mapBlockIndex.end()) {
345  LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- block %s not found\n", __func__, quorumHash.ToString());
346  return nullptr;
347  }
348  pindexQuorum = quorumIt->second;
349  }
350  return GetQuorum(llmqType, pindexQuorum);
351 }
352 
354 {
355  assert(pindexQuorum);
356 
357  auto quorumHash = pindexQuorum->GetBlockHash();
358 
359  // we must check this before we look into the cache. Reorgs might have happened which would mean we might have
360  // cached quorums which are not in the active chain anymore
361  if (!HasQuorum(llmqType, quorumHash)) {
362  return nullptr;
363  }
364 
366 
367  auto it = quorumsCache.find(std::make_pair(llmqType, quorumHash));
368  if (it != quorumsCache.end()) {
369  return it->second;
370  }
371 
372  CFinalCommitment qc;
373  uint256 minedBlockHash;
374  if (!quorumBlockProcessor->GetMinedCommitment(llmqType, quorumHash, qc, minedBlockHash)) {
375  return nullptr;
376  }
377 
378  auto& params = Params().GetConsensus().llmqs.at(llmqType);
379 
380  auto quorum = std::make_shared<CQuorum>(params, blsWorker);
381 
382  if (!BuildQuorumFromCommitment(qc, pindexQuorum, minedBlockHash, quorum)) {
383  return nullptr;
384  }
385 
386  quorumsCache.emplace(std::make_pair(llmqType, quorumHash), quorum);
387 
388  return quorum;
389 }
390 
391 } // namespace llmq
CBLSWorkerCache blsCache
Definition: quorums.h:51
unordered_lru_cache< std::pair< Consensus::LLMQType, uint256 >, std::vector< CQuorumCPtr >, StaticSaltedHasher, 32 > scanQuorumsCache
Definition: quorums.h:90
CMasternodeSync masternodeSync
UniValue quorum(const JSONRPCRequest &request)
Definition: rpcquorums.cpp:492
CCriticalSection quorumsCacheCs
Definition: quorums.h:88
Definition: evodb.h:32
bool ShutdownRequested()
Definition: init.cpp:179
CQuorumBlockProcessor * quorumBlockProcessor
BlockMap & mapBlockIndex
Definition: validation.cpp:215
CCriticalSection cs_main
Definition: validation.cpp:213
CBLSWorker * blsWorker
uint256 minedBlockHash
Definition: quorums.h:41
std::atomic< bool > stopCachePopulatorThread
Definition: quorums.h:52
std::map< std::pair< Consensus::LLMQType, uint256 >, CQuorumPtr > quorumsCache
Definition: quorums.h:89
static const std::string DB_QUORUM_SK_SHARE
Definition: quorums.cpp:27
std::unique_ptr< CEvoDB > evoDb
Definition: evodb.cpp:7
An object of this class represents a quorum which was mined on-chain (through a quorum commitment) It...
Definition: quorums.h:34
std::vector< CBLSPublicKey > BLSVerificationVector
Definition: bls.h:465
static uint256 MakeQuorumKey(const CQuorum &q)
Definition: quorums.cpp:32
std::vector< CQuorumCPtr > ScanQuorums(Consensus::LLMQType llmqType, size_t maxCount)
Definition: quorums.cpp:279
CEvoDB & evoDb
Definition: quorums.h:84
Consensus::LLMQType llmqType
bool ReadContributions(CEvoDB &evoDb)
Definition: quorums.cpp:118
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
Definition: util.cpp:824
void Init(const CFinalCommitment &_qc, const CBlockIndex *_pindexQuorum, const uint256 &_minedBlockHash, const std::vector< CDeterministicMNCPtr > &_members)
Definition: quorums.cpp:54
bool BuildQuorumContributions(const CFinalCommitment &fqc, std::shared_ptr< CQuorum > &quorum) const
Definition: quorums.cpp:238
BLSVerificationVectorPtr BuildQuorumVerificationVector(const std::vector< BLSVerificationVectorPtr > &vvecs, size_t start=0, size_t count=0, bool parallel=true)
Definition: bls_worker.cpp:628
bool IsBlockchainSynced()
std::vector< CBLSSecretKey > BLSSecretKeyVector
Definition: bls.h:467
static CBLSId FromHash(const uint256 &hash)
Definition: bls.cpp:51
bool HasMinedCommitment(Consensus::LLMQType llmqType, const uint256 &quorumHash)
void RenameThread(const char *name)
Definition: util.cpp:1244
bool GetVerifiedContributions(Consensus::LLMQType llmqType, const CBlockIndex *pindexQuorum, const std::vector< bool > &validMembers, std::vector< uint16_t > &memberIndexesRet, std::vector< BLSVerificationVectorPtr > &vvecsRet, BLSSecretKeyVector &skContributionsRet)
static void StartCachePopulatorThread(std::shared_ptr< CQuorum > _this)
Definition: quorums.cpp:136
CQuorumManager * quorumManager
Definition: quorums.cpp:30
void EnsureQuorumConnections(Consensus::LLMQType llmqType, const CBlockIndex *pindexNew)
Definition: quorums.cpp:176
void insert(const Key &key, const Value &v)
uint256 GetBlockHash() const
Definition: chain.h:292
CBLSPublicKey GetPubKeyShare(size_t memberIdx) const
Definition: quorums.cpp:82
const Consensus::LLMQParams & params
Definition: quorums.h:38
LLMQType
Definition: params.h:48
std::shared_ptr< BLSVerificationVector > BLSVerificationVectorPtr
Definition: bls.h:471
int GetMemberIndex(const uint256 &proTxHash) const
Definition: quorums.cpp:96
The quorum manager maintains quorums which were mined on chain.
Definition: quorums.h:81
This class works as a stopwatch.
Definition: cxxtimer.hpp:38
CActiveMasternodeInfo activeMasternodeInfo
bool get(const Key &key, Value &value)
void UpdatedBlockTip(const CBlockIndex *pindexNew, bool fInitialDownload)
Definition: quorums.cpp:165
bool IsValidMember(const uint256 &proTxHash) const
Definition: quorums.cpp:72
#define LOCK(cs)
Definition: sync.h:178
const CBlockIndex * pindexQuorum
Definition: quorums.h:40
BLSVerificationVectorPtr quorumVvec
Definition: quorums.h:45
bool IsMember(const uint256 &proTxHash) const
Definition: quorums.cpp:62
std::string ToString() const
Definition: uint256.cpp:62
CBLSSecretKey AggregateSecretKeys(const BLSSecretKeyVector &secKeys, size_t start=0, size_t count=0, bool parallel=true)
Definition: bls_worker.cpp:670
CBLSPublicKey BuildPubKeyShare(const uint256 &cacheKey, const BLSVerificationVectorPtr &vvec, const CBLSId &id)
Definition: bls_worker.h:176
void WriteContributions(CEvoDB &evoDb)
Definition: quorums.cpp:106
std::map< LLMQType, LLMQParams > llmqs
Definition: params.h:189
CFinalCommitment qc
Definition: quorums.h:39
#define LogPrint(category,...)
Definition: util.h:214
uint256 GetHash()
Definition: hash.h:203
bool GetMinedCommitment(Consensus::LLMQType llmqType, const uint256 &quorumHash, CFinalCommitment &ret, uint256 &retMinedBlockHash)
256-bit opaque blob.
Definition: uint256.h:123
ArgsManager gArgs
Definition: util.cpp:108
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.
bool HasQuorum(Consensus::LLMQType llmqType, const uint256 &quorumHash)
Definition: quorums.cpp:274
std::shared_ptr< const CQuorum > CQuorumCPtr
Definition: quorums.h:73
static const std::string DB_QUORUM_QUORUM_VVEC
Definition: quorums.cpp:28
static void EnsureQuorumConnections(Consensus::LLMQType llmqType, const CBlockIndex *pindexQuorum, const uint256 &myProTxHash, bool allowWatch)
CBlockIndex * Tip() const
Returns the index entry for the tip of this chain, or nullptr if none.
Definition: chain.h:453
static const bool DEFAULT_WATCH_QUORUMS
Definition: quorums_init.h:15
std::unique_ptr< CConnman > g_connman
Definition: init.cpp:97
duration_t::rep count() const
Return the elapsed time.
Definition: cxxtimer.hpp:170
CQuorumCPtr GetQuorum(Consensus::LLMQType llmqType, const uint256 &quorumHash)
Definition: quorums.cpp:337
A writer stream (for serialization) that computes a 256-bit hash.
Definition: hash.h:184
void stop()
Stop/pause the timer.
Definition: cxxtimer.hpp:152
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: chain.h:183
CBLSWorker & blsWorker
Definition: quorums.h:85
const Consensus::Params & GetConsensus() const
Definition: chainparams.h:54
CDKGSessionManager & dkgManager
Definition: quorums.h:86
CBLSSecretKey skShare
Definition: quorums.h:46
CBlockIndex * GetAncestor(int height)
Efficiently find an ancestor of this block.
Definition: chain.cpp:110
std::vector< CDeterministicMNCPtr > members
Definition: quorums.h:42
CChain & chainActive
The currently-connected chain of blocks (protected by cs_main).
Definition: validation.cpp:217
bool BuildQuorumFromCommitment(const CFinalCommitment &qc, const CBlockIndex *pindexQuorum, const uint256 &minedBlockHash, std::shared_ptr< CQuorum > &quorum) const
Definition: quorums.cpp:207
std::vector< const CBlockIndex * > GetMinedCommitmentsUntilBlock(Consensus::LLMQType llmqType, const CBlockIndex *pindex, size_t maxCount)
std::vector< bool > validMembers
bool IsValid() const
Definition: bls.h:94
std::thread cachePopulatorThread
Definition: quorums.h:53
static std::vector< CDeterministicMNCPtr > GetAllQuorumMembers(Consensus::LLMQType llmqType, const CBlockIndex *pindexQuorum)
CQuorumManager(CEvoDB &_evoDb, CBLSWorker &_blsWorker, CDKGSessionManager &_dkgManager)
Definition: quorums.cpp:158
CBLSSecretKey GetSkShare() const
Definition: quorums.cpp:91
Released under the MIT license