Line data Source code
1 : // Copyright (c) 2018 The Dash Core developers
2 : // Copyright (c) 2023 The PIVX developers
3 : // Distributed under the MIT/X11 software license, see the accompanying
4 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 :
6 : #include "quorums.h"
7 :
8 : #include "activemasternode.h"
9 : #include "chainparams.h"
10 : #include "cxxtimer.h"
11 : #include "evo/deterministicmns.h"
12 : #include "llmq/quorums_connections.h"
13 : #include "logging.h"
14 : #include "quorums_blockprocessor.h"
15 : #include "quorums_commitment.h"
16 : #include "quorums_dkgsessionmgr.h"
17 : #include "shutdown.h"
18 : #include "tiertwo/tiertwo_sync_state.h"
19 : #include "univalue.h"
20 : #include "validation.h"
21 :
22 : #include <cstddef>
23 : #include <iostream>
24 :
25 : namespace llmq
26 : {
27 :
28 : static const std::string DB_QUORUM_SK_SHARE = "q_Qsk";
29 : static const std::string DB_QUORUM_QUORUM_VVEC = "q_Qqvvec";
30 :
31 : std::unique_ptr<CQuorumManager> quorumManager{nullptr};
32 :
33 198 : static uint256 MakeQuorumKey(const CQuorum& q)
34 : {
35 198 : CHashWriter hw(SER_NETWORK, 0);
36 198 : hw << (uint8_t)q.params.type;
37 198 : hw << q.qc.quorumHash;
38 792 : for (const auto& dmn : q.members) {
39 594 : hw << dmn->proTxHash;
40 : }
41 198 : return hw.GetHash();
42 : }
43 :
44 200 : CQuorum::~CQuorum()
45 : {
46 : // most likely the thread is already done
47 144 : stopCachePopulatorThread = true;
48 : // watch out to not join the thread when we're called from inside the thread, which might happen on shutdown. This
49 : // is because on shutdown the thread is the last owner of the shared CQuorum instance and thus the destroyer of it.
50 200 : if (cachePopulatorThread.joinable() && cachePopulatorThread.get_id() != std::this_thread::get_id()) {
51 56 : cachePopulatorThread.join();
52 : }
53 144 : }
54 :
55 144 : void CQuorum::Init(const CFinalCommitment& _qc, const CBlockIndex* _pindexQuorum, const uint256& _minedBlockHash, const std::vector<CDeterministicMNCPtr>& _members)
56 : {
57 144 : qc = _qc;
58 144 : pindexQuorum = _pindexQuorum;
59 144 : members = _members;
60 144 : minedBlockHash = _minedBlockHash;
61 144 : }
62 :
63 7997 : bool CQuorum::IsMember(const uint256& proTxHash) const
64 : {
65 23117 : for (auto& dmn : members) {
66 19510 : if (dmn->proTxHash == proTxHash) {
67 4390 : return true;
68 : }
69 : }
70 3607 : return false;
71 : }
72 :
73 3379 : bool CQuorum::IsValidMember(const uint256& proTxHash) const
74 : {
75 8996 : for (size_t i = 0; i < members.size(); i++) {
76 7779 : if (members[i]->proTxHash == proTxHash) {
77 2162 : return qc.validMembers[i];
78 : }
79 : }
80 : return false;
81 : }
82 :
83 770 : CBLSPublicKey CQuorum::GetPubKeyShare(size_t memberIdx) const
84 : {
85 770 : if (quorumVvec == nullptr || memberIdx >= members.size() || !qc.validMembers[memberIdx]) {
86 0 : return CBLSPublicKey();
87 : }
88 770 : auto& m = members[memberIdx];
89 770 : return blsCache.BuildPubKeyShare(m->proTxHash, quorumVvec, CBLSId(m->proTxHash));
90 : }
91 :
92 1002 : CBLSSecretKey CQuorum::GetSkShare() const
93 : {
94 1002 : return skShare;
95 : }
96 :
97 1002 : int CQuorum::GetMemberIndex(const uint256& proTxHash) const
98 : {
99 1827 : for (size_t i = 0; i < members.size(); i++) {
100 1827 : if (members[i]->proTxHash == proTxHash) {
101 1002 : return (int)i;
102 : }
103 : }
104 : return -1;
105 : }
106 :
107 54 : void CQuorum::WriteContributions(CEvoDB& evoDb)
108 : {
109 54 : uint256 dbKey = MakeQuorumKey(*this);
110 :
111 54 : if (quorumVvec != nullptr) {
112 108 : evoDb.GetRawDB().Write(std::make_pair(DB_QUORUM_QUORUM_VVEC, dbKey), *quorumVvec);
113 : }
114 54 : if (skShare.IsValid()) {
115 108 : evoDb.GetRawDB().Write(std::make_pair(DB_QUORUM_SK_SHARE, dbKey), skShare);
116 : }
117 54 : }
118 :
119 144 : bool CQuorum::ReadContributions(CEvoDB& evoDb)
120 : {
121 144 : uint256 dbKey = MakeQuorumKey(*this);
122 :
123 288 : BLSVerificationVector qv;
124 144 : if (evoDb.Read(std::make_pair(DB_QUORUM_QUORUM_VVEC, dbKey), qv)) {
125 2 : quorumVvec = std::make_shared<BLSVerificationVector>(std::move(qv));
126 : } else {
127 : return false;
128 : }
129 :
130 : // We ignore the return value here as it is ok if this fails. If it fails, it usually means that we are not a
131 : // member of the quorum but observed the whole DKG process to have the quorum verification vector.
132 2 : evoDb.Read(std::make_pair(DB_QUORUM_SK_SHARE, dbKey), skShare);
133 :
134 2 : return true;
135 : }
136 :
137 56 : void CQuorum::StartCachePopulatorThread(std::shared_ptr<CQuorum> _this)
138 : {
139 56 : if (_this->quorumVvec == nullptr) {
140 0 : return;
141 : }
142 :
143 112 : cxxtimer::Timer t(true);
144 56 : LogPrint(BCLog::LLMQ, "CQuorum::StartCachePopulatorThread -- start\n");
145 :
146 : // this thread will exit after some time
147 : // when then later some other thread tries to get keys, it will be much faster
148 448 : _this->cachePopulatorThread = std::thread(&TraceThread<std::function<void()> >, "quorum-cachepop", [_this, t] {
149 224 : for (size_t i = 0; i < _this->members.size() && !_this->stopCachePopulatorThread && !ShutdownRequested(); i++) {
150 168 : if (_this->qc.validMembers[i]) {
151 151 : _this->GetPubKeyShare(i);
152 : }
153 : }
154 56 : LogPrint(BCLog::LLMQ, "CQuorum::StartCachePopulatorThread -- done. time=%d\n", t.count());
155 56 : });
156 : }
157 :
158 475 : CQuorumManager::CQuorumManager(CEvoDB& _evoDb, CBLSWorker& _blsWorker, CDKGSessionManager& _dkgManager) : evoDb(_evoDb),
159 : blsWorker(_blsWorker),
160 475 : dkgManager(_dkgManager)
161 : {
162 475 : utils::InitQuorumsCache(mapQuorumsCache);
163 475 : utils::InitQuorumsCache(scanQuorumsCache);
164 475 : }
165 :
166 41471 : void CQuorumManager::UpdatedBlockTip(const CBlockIndex* pindexNew, bool fInitialDownload)
167 : {
168 41471 : if (!g_tiertwo_sync_state.IsBlockchainSynced() || !activeMasternodeManager) {
169 : return;
170 : }
171 :
172 8992 : for (auto& p : Params().GetConsensus().llmqs) {
173 4496 : const auto& params = Params().GetConsensus().llmqs.at(p.first);
174 :
175 4496 : auto lastQuorums = ScanQuorums(p.first, pindexNew, (size_t)params.keepOldConnections);
176 :
177 4496 : llmq::EnsureLatestQuorumConnections(p.first, pindexNew, activeMasternodeManager->GetProTx(), lastQuorums);
178 : }
179 : }
180 :
181 :
182 144 : bool CQuorumManager::BuildQuorumFromCommitment(const CFinalCommitment& qc, const CBlockIndex* pindexQuorum, const uint256& minedBlockHash, std::shared_ptr<CQuorum>& quorum) const
183 : {
184 144 : assert(pindexQuorum);
185 144 : assert(qc.quorumHash == pindexQuorum->GetBlockHash());
186 :
187 144 : auto members = deterministicMNManager->GetAllQuorumMembers((Consensus::LLMQType)qc.llmqType, pindexQuorum);
188 144 : quorum->Init(qc, pindexQuorum, minedBlockHash, members);
189 :
190 144 : bool hasValidVvec = false;
191 144 : if (quorum->ReadContributions(evoDb)) {
192 : hasValidVvec = true;
193 : } else {
194 142 : if (BuildQuorumContributions(qc, quorum)) {
195 54 : quorum->WriteContributions(evoDb);
196 : hasValidVvec = true;
197 : } else {
198 88 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- quorum.ReadContributions and BuildQuorumContributions for block %s failed\n", __func__, qc.quorumHash.ToString());
199 : }
200 : }
201 :
202 88 : if (hasValidVvec) {
203 : // pre-populate caches in the background
204 : // recovering public key shares is quite expensive and would result in serious lags for the first few signing
205 : // sessions if the shares would be calculated on-demand
206 112 : CQuorum::StartCachePopulatorThread(quorum);
207 : }
208 :
209 144 : return true;
210 : }
211 :
212 142 : bool CQuorumManager::BuildQuorumContributions(const CFinalCommitment& fqc, std::shared_ptr<CQuorum>& quorum) const
213 : {
214 284 : std::vector<uint16_t> memberIndexes;
215 142 : std::vector<BLSVerificationVectorPtr> vvecs;
216 0 : BLSSecretKeyVector skContributions;
217 142 : if (!dkgManager.GetVerifiedContributions((Consensus::LLMQType)fqc.llmqType, quorum->pindexQuorum, fqc.validMembers, memberIndexes, vvecs, skContributions)) {
218 : return false;
219 : }
220 :
221 142 : BLSVerificationVectorPtr quorumVvec;
222 284 : CBLSSecretKey skShare;
223 :
224 284 : cxxtimer::Timer t2(true);
225 284 : quorumVvec = blsWorker.BuildQuorumVerificationVector(vvecs);
226 142 : if (quorumVvec == nullptr) {
227 88 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- failed to build quorumVvec\n", __func__);
228 : // without the quorum vvec, there can't be a skShare, so we fail here. Failure is not fatal here, as it still
229 : // allows to use the quorum as a non-member (verification through the quorum pub key)
230 88 : return false;
231 : }
232 108 : skShare = blsWorker.AggregateSecretKeys(skContributions);
233 54 : if (!skShare.IsValid()) {
234 0 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- failed to build skShare\n", __func__);
235 : // We don't bail out here as this is not a fatal error and still allows us to recover public key shares (as we
236 : // have a valid quorum vvec at this point)
237 : }
238 54 : t2.stop();
239 :
240 54 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- built quorum vvec and skShare. time=%d\n", __func__, t2.count());
241 :
242 54 : quorum->quorumVvec = quorumVvec;
243 54 : quorum->skShare = skShare;
244 :
245 54 : return true;
246 : }
247 :
248 17329 : bool CQuorumManager::HasQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash)
249 : {
250 17329 : return quorumBlockProcessor->HasMinedCommitment(llmqType, quorumHash);
251 : }
252 :
253 5694 : std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType, size_t maxCount)
254 : {
255 17082 : const CBlockIndex* pindex = WITH_LOCK(cs_main, return chainActive.Tip());
256 5694 : return ScanQuorums(llmqType, pindex, maxCount);
257 : }
258 :
259 14293 : std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType, const CBlockIndex* pindexStart, size_t maxCount)
260 : {
261 14293 : if (pindexStart == nullptr || maxCount == 0) {
262 14293 : return {};
263 : }
264 :
265 14293 : bool fCacheExists{false};
266 14293 : void* pIndexScanCommitments{(void*)pindexStart};
267 14293 : size_t nScanCommitments{maxCount};
268 14293 : std::vector<CQuorumCPtr> vecResultQuorums;
269 :
270 14293 : {
271 14293 : LOCK(quorumsCacheCs);
272 14293 : auto& cache = scanQuorumsCache.at(llmqType);
273 14293 : fCacheExists = cache.get(pindexStart->GetBlockHash(), vecResultQuorums);
274 14293 : if (fCacheExists) {
275 : // We have exactly what requested so just return it
276 6326 : if (vecResultQuorums.size() == maxCount) {
277 7756 : return vecResultQuorums;
278 : }
279 : // If we have more cached than requested return only a subvector
280 2766 : if (vecResultQuorums.size() > maxCount) {
281 318 : return {vecResultQuorums.begin(), vecResultQuorums.begin() + maxCount};
282 : }
283 : // If we have cached quorums but not enough, subtract what we have from the count and the set correct index where to start
284 : // scanning for the rests
285 2448 : if (vecResultQuorums.size() > 0) {
286 2448 : nScanCommitments -= vecResultQuorums.size();
287 2448 : pIndexScanCommitments = (void*)vecResultQuorums.back()->pindexQuorum->pprev;
288 : }
289 : } else {
290 : // If there is nothing in cache request at least cache.max_size() because this gets cached then later
291 11419 : nScanCommitments = std::max(maxCount, cache.max_size());
292 : }
293 : }
294 : // Get the block indexes of the mined commitments to build the required quorums from
295 24708 : auto quorumIndexes = quorumBlockProcessor->GetMinedCommitmentsUntilBlock(llmqType, (const CBlockIndex*)pIndexScanCommitments, nScanCommitments);
296 10415 : vecResultQuorums.reserve(vecResultQuorums.size() + quorumIndexes.size());
297 :
298 23400 : for (auto& quorumIndex : quorumIndexes) {
299 12985 : assert(quorumIndex);
300 25970 : auto quorum = GetQuorum(llmqType, quorumIndex);
301 12985 : assert(quorum != nullptr);
302 12985 : vecResultQuorums.emplace_back(quorum);
303 : }
304 :
305 10415 : size_t nCountResult{vecResultQuorums.size()};
306 10415 : if (nCountResult > 0 && !fCacheExists) {
307 11774 : LOCK(quorumsCacheCs);
308 : // Don't cache more than cache.max_size() elements
309 5887 : auto& cache = scanQuorumsCache.at(llmqType);
310 5887 : size_t nCacheEndIndex = std::min(nCountResult, cache.max_size());
311 11774 : cache.emplace(pindexStart->GetBlockHash(), {vecResultQuorums.begin(), vecResultQuorums.begin() + nCacheEndIndex});
312 : }
313 : // Don't return more than nCountRequested elements
314 10415 : size_t nResultEndIndex = std::min(nCountResult, maxCount);
315 10415 : return {vecResultQuorums.begin(), vecResultQuorums.begin() + nResultEndIndex};
316 : }
317 :
318 4344 : CQuorumCPtr CQuorumManager::GetQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash)
319 : {
320 4344 : CBlockIndex* pindexQuorum;
321 4344 : {
322 4344 : LOCK(cs_main);
323 4344 : pindexQuorum = LookupBlockIndex(quorumHash);
324 :
325 4344 : if (!pindexQuorum) {
326 0 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- block %s not found.\n", __func__, quorumHash.ToString());
327 0 : return nullptr;
328 : }
329 : }
330 4344 : return GetQuorum(llmqType, pindexQuorum);
331 : }
332 :
333 17329 : CQuorumCPtr CQuorumManager::GetQuorum(Consensus::LLMQType llmqType, const CBlockIndex* pindexQuorum)
334 : {
335 17329 : assert(pindexQuorum);
336 :
337 17329 : auto quorumHash = pindexQuorum->GetBlockHash();
338 : // we must check this before we look into the cache. Reorgs might have happened which would mean we might have
339 : // cached quorums which are not in the active chain anymore
340 17329 : if (!HasQuorum(llmqType, quorumHash)) {
341 0 : return nullptr;
342 : }
343 :
344 34658 : LOCK(quorumsCacheCs);
345 17329 : CQuorumCPtr pQuorum;
346 17329 : if (mapQuorumsCache.at(llmqType).get(quorumHash, pQuorum)) {
347 17185 : return pQuorum;
348 : }
349 :
350 17473 : CFinalCommitment qc;
351 144 : uint256 retMinedBlockHash;
352 144 : if (!quorumBlockProcessor->GetMinedCommitment(llmqType, quorumHash, qc, retMinedBlockHash)) {
353 0 : return nullptr;
354 : }
355 :
356 144 : auto& params = Params().GetConsensus().llmqs.at(llmqType);
357 :
358 288 : auto quorum = std::make_shared<CQuorum>(params, blsWorker);
359 144 : if (!BuildQuorumFromCommitment(qc, pindexQuorum, retMinedBlockHash, quorum)) {
360 0 : return nullptr;
361 : }
362 :
363 288 : mapQuorumsCache.at(llmqType).emplace(quorumHash, quorum);
364 :
365 144 : return quorum;
366 : }
367 :
368 : } // namespace llmq
|