Line data Source code
1 : // Copyright (c) 2018-2026 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/quorumsman.h>
6 :
7 : #include <bls/bls.h>
8 : #include <bls/bls_ies.h>
9 : #include <evo/deterministicmns.h>
10 : #include <evo/evodb.h>
11 : #include <llmq/blockprocessor.h>
12 : #include <llmq/commitment.h>
13 : #include <llmq/dkgsessionmgr.h>
14 : #include <llmq/options.h>
15 : #include <llmq/params.h>
16 : #include <llmq/signhash.h>
17 : #include <llmq/utils.h>
18 : #include <util/helpers.h>
19 : #include <util/std23.h>
20 :
21 : #include <chainparams.h>
22 : #include <dbwrapper.h>
23 : #include <logging.h>
24 : #include <util/thread.h>
25 : #include <util/time.h>
26 : #include <validation.h>
27 :
28 : #include <cxxtimer.hpp>
29 :
30 : namespace llmq {
31 6126 : CQuorumManager::CQuorumManager(CBLSWorker& _blsWorker, CDeterministicMNManager& dmnman, CEvoDB& _evoDb,
32 : CQuorumBlockProcessor& _quorumBlockProcessor, CQuorumSnapshotManager& qsnapman,
33 : const ChainstateManager& chainman, const util::DbWrapperParams& db_params) :
34 3063 : blsWorker{_blsWorker},
35 3063 : m_dmnman{dmnman},
36 3063 : quorumBlockProcessor{_quorumBlockProcessor},
37 3063 : m_qsnapman{qsnapman},
38 3063 : m_chainman{chainman},
39 : db{util::MakeDbWrapper({db_params.path / "llmq" / "quorumdb", db_params.memory, db_params.wipe, /*cache_size=*/1 << 20})}
40 3063 : {
41 : utils::InitQuorumsCache(mapQuorumsCache, m_chainman.GetConsensus(), /*limit_by_connections=*/false);
42 : m_cache_interrupt.reset();
43 3063 : m_cache_thread = std::thread(&util::TraceThread, "q-cache", [this] { CacheWarmingThreadMain(); });
44 : MigrateOldQuorumDB(_evoDb);
45 3063 : }
46 :
47 6126 : CQuorumManager::~CQuorumManager()
48 3063 : {
49 3063 : if (m_cache_thread.joinable()) {
50 3063 : m_cache_interrupt();
51 3063 : m_cache_thread.join();
52 3063 : }
53 6126 : }
54 :
55 81 : bool CQuorumManager::GetEncryptedContributions(Consensus::LLMQType llmq_type, const CBlockIndex* block_index,
56 : const std::vector<bool>& valid_members, const uint256& protx_hash,
57 : std::vector<CBLSIESEncryptedObject<CBLSSecretKey>>& vec_enc) const
58 : {
59 81 : if (m_qdkgsman) {
60 81 : return m_qdkgsman->GetEncryptedContributions(llmq_type, block_index, valid_members, protx_hash, vec_enc);
61 : }
62 0 : return false;
63 81 : }
64 :
65 5355 : CQuorumPtr CQuorumManager::BuildQuorumFromCommitment(const Consensus::LLMQType llmqType, gsl::not_null<const CBlockIndex*> pQuorumBaseBlockIndex, bool populate_cache) const
66 : {
67 5355 : const uint256& quorumHash{pQuorumBaseBlockIndex->GetBlockHash()};
68 :
69 21420 : auto [qc, minedBlockHash] = quorumBlockProcessor.GetMinedCommitment(llmqType, quorumHash);
70 5355 : if (minedBlockHash == uint256::ZERO) {
71 0 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- No mined commitment for llmqType[%d] nHeight[%d] quorumHash[%s]\n", __func__, std23::to_underlying(llmqType), pQuorumBaseBlockIndex->nHeight, pQuorumBaseBlockIndex->GetBlockHash().ToString());
72 0 : return nullptr;
73 : }
74 5355 : assert(qc.quorumHash == pQuorumBaseBlockIndex->GetBlockHash());
75 :
76 5355 : const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
77 5355 : assert(llmq_params_opt.has_value());
78 5355 : auto members = utils::GetAllQuorumMembers(qc.llmqType, {m_dmnman, m_qsnapman, m_chainman, pQuorumBaseBlockIndex});
79 :
80 5355 : auto quorum = std::make_shared<CQuorum>(llmq_params_opt.value(), blsWorker,
81 5355 : std::make_unique<CFinalCommitment>(std::move(qc)), pQuorumBaseBlockIndex, minedBlockHash, members);
82 :
83 5355 : if (populate_cache && llmq_params_opt->size == 1) {
84 174 : WITH_LOCK(m_cs_maps, mapQuorumsCache[llmqType].insert(quorumHash, quorum));
85 :
86 87 : return quorum;
87 : }
88 :
89 5268 : bool hasValidVvec = false;
90 10536 : if (WITH_LOCK(cs_db, return quorum->ReadContributions(*db))) {
91 922 : hasValidVvec = true;
92 922 : } else {
93 4346 : if (BuildQuorumContributions(quorum->qc, quorum)) {
94 3792 : WITH_LOCK(cs_db, quorum->WriteContributions(*db));
95 1896 : hasValidVvec = true;
96 1896 : } else {
97 2450 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- llmqType[%d] quorumIndex[%d] quorum.ReadContributions and BuildQuorumContributions for quorumHash[%s] failed\n", __func__, std23::to_underlying(llmqType), quorum->qc->quorumIndex, quorum->qc->quorumHash.ToString());
98 : }
99 : }
100 :
101 5268 : if (hasValidVvec && populate_cache) {
102 : // pre-populate caches in the background
103 : // recovering public key shares is quite expensive and would result in serious lags for the first few signing
104 : // sessions if the shares would be calculated on-demand
105 2818 : QueueQuorumForWarming(quorum);
106 2818 : }
107 :
108 10536 : WITH_LOCK(m_cs_maps, mapQuorumsCache[llmqType].insert(quorumHash, quorum));
109 :
110 5268 : return quorum;
111 5355 : }
112 :
113 4346 : bool CQuorumManager::BuildQuorumContributions(const CFinalCommitmentPtr& fqc, const std::shared_ptr<CQuorum>& quorum) const
114 : {
115 4346 : std::vector<uint16_t> memberIndexes;
116 4346 : std::vector<BLSVerificationVectorPtr> vvecs;
117 4346 : std::vector<CBLSSecretKey> skContributions;
118 8041 : if (!m_qdkgsman ||
119 7390 : !m_qdkgsman->GetVerifiedContributions((Consensus::LLMQType)fqc->llmqType, quorum->m_quorum_base_block_index,
120 3695 : fqc->validMembers, memberIndexes, vvecs, skContributions)) {
121 2450 : return false;
122 : }
123 :
124 1896 : cxxtimer::Timer t2(true);
125 1896 : quorum->SetVerificationVector(blsWorker.BuildQuorumVerificationVector(vvecs));
126 1896 : if (!quorum->HasVerificationVector()) {
127 0 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- failed to build quorumVvec\n", __func__);
128 : // without the quorum vvec, there can't be a skShare, so we fail here. Failure is not fatal here, as it still
129 : // allows to use the quorum as a non-member (verification through the quorum pub key)
130 0 : return false;
131 : }
132 1896 : if (!m_handler || !m_handler->SetQuorumSecretKeyShare(*quorum, skContributions)) {
133 20 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- failed to build skShare\n", __func__);
134 : // We don't bail out here as this is not a fatal error and still allows us to recover public key shares (as we
135 : // have a valid quorum vvec at this point)
136 20 : }
137 1896 : t2.stop();
138 :
139 1896 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- built quorum vvec and skShare. time=%d\n", __func__, t2.count());
140 :
141 1896 : return true;
142 4346 : }
143 :
144 200163 : bool CQuorumManager::HasQuorum(Consensus::LLMQType llmqType, const CQuorumBlockProcessor& quorum_block_processor, const uint256& quorumHash)
145 : {
146 200163 : return quorum_block_processor.HasMinedCommitment(llmqType, quorumHash);
147 : }
148 :
149 78984 : std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType, size_t nCountRequested) const
150 : {
151 157968 : const CBlockIndex* pindex = WITH_LOCK(::cs_main, return m_chainman.ActiveTip());
152 78984 : return ScanQuorums(llmqType, pindex, nCountRequested);
153 : }
154 :
155 950204 : std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType,
156 : gsl::not_null<const CBlockIndex*> pindexStart,
157 : size_t nCountRequested) const
158 : {
159 950204 : if (nCountRequested == 0 || !m_chainman.IsQuorumTypeEnabled(llmqType, pindexStart)) {
160 292690 : return {};
161 : }
162 :
163 657514 : gsl::not_null<const CBlockIndex*> pindexStore{pindexStart};
164 657514 : const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
165 657514 : assert(llmq_params_opt.has_value());
166 :
167 : // Quorum sets can only change during the mining phase of DKG.
168 : // Find the closest known block index.
169 657514 : const int quorumCycleStartHeight = pindexStart->nHeight - (pindexStart->nHeight % llmq_params_opt->dkgInterval);
170 657514 : const int quorumCycleMiningStartHeight = quorumCycleStartHeight + llmq_params_opt->dkgMiningWindowStart;
171 657514 : const int quorumCycleMiningEndHeight = quorumCycleStartHeight + llmq_params_opt->dkgMiningWindowEnd;
172 :
173 657514 : if (pindexStart->nHeight < quorumCycleMiningStartHeight) {
174 : // too early for this cycle, use the previous one
175 : // bail out if it's below genesis block
176 291605 : if (quorumCycleMiningEndHeight < llmq_params_opt->dkgInterval) return {};
177 288595 : pindexStore = pindexStart->GetAncestor(quorumCycleMiningEndHeight - llmq_params_opt->dkgInterval);
178 654504 : } else if (pindexStart->nHeight > quorumCycleMiningEndHeight) {
179 : // we are past the mining phase of this cycle, use it
180 128373 : pindexStore = pindexStart->GetAncestor(quorumCycleMiningEndHeight);
181 128373 : }
182 : // everything else is inside the mining phase of this cycle, no pindexStore adjustment needed
183 :
184 654504 : gsl::not_null<const CBlockIndex*> pIndexScanCommitments{pindexStore};
185 654504 : size_t nScanCommitments{nCountRequested};
186 654504 : std::vector<CQuorumCPtr> vecResultQuorums;
187 :
188 : {
189 654504 : LOCK(m_cs_maps);
190 654504 : if (scanQuorumsCache.empty()) {
191 4590 : for (const auto& llmq : Params().GetConsensus().llmqs) {
192 : // NOTE: We store it for each block hash in the DKG mining phase here
193 : // and not for a single quorum hash per quorum like we do for other caches.
194 : // And we only do this for max_cycles() of the most recent quorums
195 : // because signing by old quorums requires the exact quorum hash to be specified
196 : // and quorum scanning isn't needed there.
197 3825 : scanQuorumsCache.try_emplace(llmq.type, llmq.max_cycles(llmq.keepOldConnections) * (llmq.dkgMiningWindowEnd - llmq.dkgMiningWindowStart));
198 : }
199 765 : }
200 654504 : auto& cache = scanQuorumsCache[llmqType];
201 654504 : bool fCacheExists = cache.get(pindexStore->GetBlockHash(), vecResultQuorums);
202 654504 : if (fCacheExists) {
203 : // We have exactly what requested so just return it
204 282436 : if (vecResultQuorums.size() == nCountRequested) {
205 119178 : return vecResultQuorums;
206 : }
207 : // If we have more cached than requested return only a subvector
208 163258 : if (vecResultQuorums.size() > nCountRequested) {
209 27846 : return {vecResultQuorums.begin(), vecResultQuorums.begin() + nCountRequested};
210 : }
211 : // If we have cached quorums but not enough, subtract what we have from the count and the set correct index where to start
212 : // scanning for the rests
213 135412 : if (!vecResultQuorums.empty()) {
214 135412 : nScanCommitments -= vecResultQuorums.size();
215 : // bail out if it's below genesis block
216 135412 : if (vecResultQuorums.back()->m_quorum_base_block_index->pprev == nullptr) return {};
217 135412 : pIndexScanCommitments = vecResultQuorums.back()->m_quorum_base_block_index->pprev;
218 135412 : }
219 135412 : } else {
220 : // If there is nothing in cache request at least keepOldConnections because this gets cached then later
221 372068 : nScanCommitments = std::max(nCountRequested, static_cast<size_t>(llmq_params_opt->keepOldConnections));
222 : }
223 654504 : }
224 :
225 : // Get the block indexes of the mined commitments to build the required quorums from
226 1014960 : std::vector<const CBlockIndex*> pQuorumBaseBlockIndexes{ llmq_params_opt->useRotation ?
227 144190 : quorumBlockProcessor.GetMinedCommitmentsIndexedUntilBlock(llmqType, pIndexScanCommitments, nScanCommitments) :
228 363290 : quorumBlockProcessor.GetMinedCommitmentsUntilBlock(llmqType, pIndexScanCommitments, nScanCommitments)
229 : };
230 507480 : vecResultQuorums.reserve(vecResultQuorums.size() + pQuorumBaseBlockIndexes.size());
231 :
232 607372 : for (auto& pQuorumBaseBlockIndex : pQuorumBaseBlockIndexes) {
233 99892 : assert(pQuorumBaseBlockIndex);
234 : // populate cache for keepOldConnections most recent quorums only
235 99892 : bool populate_cache = vecResultQuorums.size() < static_cast<size_t>(llmq_params_opt->keepOldConnections);
236 :
237 : // We assume that every quorum asked for is available to us on hand, if this
238 : // fails then we can assume that something has gone wrong and we should stop
239 : // trying to process any further and return a blank.
240 99892 : auto quorum = GetQuorum(llmqType, pQuorumBaseBlockIndex, populate_cache);
241 99892 : if (!quorum) {
242 0 : LogPrintf("%s: ERROR! Unexpected missing quorum with llmqType=%d, blockHash=%s, populate_cache=%s\n",
243 : __func__, std23::to_underlying(llmqType), pQuorumBaseBlockIndex->GetBlockHash().ToString(),
244 : util::to_string(populate_cache));
245 0 : return {};
246 : }
247 99892 : vecResultQuorums.emplace_back(quorum);
248 99892 : }
249 :
250 507480 : const size_t nCountResult{vecResultQuorums.size()};
251 507480 : if (nCountResult > 0) {
252 177476 : LOCK(m_cs_maps);
253 : // Don't cache more than keepOldConnections elements
254 : // because signing by old quorums requires the exact quorum hash
255 : // to be specified and quorum scanning isn't needed there.
256 177476 : auto& cache = scanQuorumsCache[llmqType];
257 177476 : const size_t nCacheEndIndex = std::min(nCountResult, static_cast<size_t>(llmq_params_opt->keepOldConnections));
258 177476 : cache.emplace(pindexStore->GetBlockHash(), {vecResultQuorums.begin(), vecResultQuorums.begin() + nCacheEndIndex});
259 177476 : }
260 : // Don't return more than nCountRequested elements
261 507480 : const size_t nResultEndIndex = std::min(nCountResult, nCountRequested);
262 507480 : return {vecResultQuorums.begin(), vecResultQuorums.begin() + nResultEndIndex};
263 950208 : }
264 :
265 0 : bool CQuorumManager::IsMasternode() const
266 : {
267 0 : if (m_handler) {
268 0 : return m_handler->IsMasternode();
269 : }
270 0 : return false;
271 0 : }
272 :
273 8932 : bool CQuorumManager::IsWatching() const
274 : {
275 8932 : if (m_handler) {
276 4913 : return m_handler->IsWatching();
277 : }
278 4019 : return false;
279 8932 : }
280 :
281 223 : bool CQuorumManager::IsDataRequestPending(const uint256& proRegTx, bool we_requested, const uint256& quorumHash,
282 : Consensus::LLMQType llmqType) const
283 : {
284 223 : const CQuorumDataRequestKey key{proRegTx, we_requested, quorumHash, llmqType};
285 223 : LOCK(cs_data_requests);
286 223 : const auto it = mapQuorumDataRequests.find(key);
287 223 : return it != mapQuorumDataRequests.end() && !it->second.IsExpired(/*add_bias=*/true);
288 223 : }
289 :
290 0 : DataRequestStatus CQuorumManager::GetDataRequestStatus(const uint256& proRegTx, bool we_requested,
291 : const uint256& quorumHash, Consensus::LLMQType llmqType) const
292 : {
293 0 : const CQuorumDataRequestKey key{proRegTx, we_requested, quorumHash, llmqType};
294 0 : LOCK(cs_data_requests);
295 0 : const auto it = mapQuorumDataRequests.find(key);
296 0 : if (it == mapQuorumDataRequests.end()) {
297 0 : return DataRequestStatus::NotFound;
298 : }
299 0 : if (it->second.IsProcessed()) {
300 0 : return DataRequestStatus::Processed;
301 : }
302 0 : return DataRequestStatus::Pending;
303 0 : }
304 :
305 79784 : void CQuorumManager::CleanupExpiredDataRequests() const
306 : {
307 79784 : LOCK(cs_data_requests);
308 79784 : auto it = mapQuorumDataRequests.begin();
309 80226 : while (it != mapQuorumDataRequests.end()) {
310 442 : if (it->second.IsExpired(/*add_bias=*/true)) {
311 262 : it = mapQuorumDataRequests.erase(it);
312 262 : } else {
313 180 : ++it;
314 : }
315 : }
316 79784 : }
317 :
318 54 : void CQuorumManager::CleanupOldQuorumData(const Uint256HashSet& dbKeysToSkip) const
319 : {
320 54 : LOCK(cs_db);
321 54 : DataCleanupHelper(*db, dbKeysToSkip);
322 54 : }
323 :
324 100279 : CQuorumCPtr CQuorumManager::GetQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash) const
325 : {
326 200558 : const CBlockIndex* pQuorumBaseBlockIndex = [&]() {
327 : // Lock contention may still be high here; consider using a shared lock
328 : // We cannot hold cs_quorumBaseBlockIndexCache the whole time as that creates lock-order inversion with cs_main;
329 : // We cannot acquire cs_main if we have cs_quorumBaseBlockIndexCache held
330 : const CBlockIndex* pindex;
331 200558 : if (!WITH_LOCK(cs_quorumBaseBlockIndexCache, return quorumBaseBlockIndexCache.get(quorumHash, pindex))) {
332 4256 : pindex = WITH_LOCK(::cs_main, return m_chainman.m_blockman.LookupBlockIndex(quorumHash));
333 2128 : if (pindex) {
334 2121 : LOCK(cs_quorumBaseBlockIndexCache);
335 2121 : quorumBaseBlockIndexCache.insert(quorumHash, pindex);
336 2121 : }
337 2128 : }
338 100279 : return pindex;
339 0 : }();
340 100279 : if (!pQuorumBaseBlockIndex) {
341 7 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- block %s not found\n", __func__, quorumHash.ToString());
342 7 : return nullptr;
343 : }
344 100272 : return GetQuorum(llmqType, pQuorumBaseBlockIndex);
345 100279 : }
346 :
347 200164 : CQuorumCPtr CQuorumManager::GetQuorum(Consensus::LLMQType llmqType, gsl::not_null<const CBlockIndex*> pQuorumBaseBlockIndex, bool populate_cache) const
348 : {
349 200164 : auto quorumHash = pQuorumBaseBlockIndex->GetBlockHash();
350 :
351 : // we must check this before we look into the cache. Reorgs might have happened which would mean we might have
352 : // cached quorums which are not in the active chain anymore
353 200164 : if (!HasQuorum(llmqType, quorumBlockProcessor, quorumHash)) {
354 11 : return nullptr;
355 : }
356 :
357 200153 : CQuorumPtr pQuorum;
358 394951 : if (LOCK(m_cs_maps); mapQuorumsCache[llmqType].get(quorumHash, pQuorum)) {
359 194798 : return pQuorum;
360 : }
361 :
362 5355 : return BuildQuorumFromCommitment(llmqType, pQuorumBaseBlockIndex, populate_cache);
363 200164 : }
364 :
365 496 : bool CQuorumManager::RegisterDataRequest(const CQuorumDataRequestKey& key, const CQuorumDataRequest& request,
366 : bool add_expiry_bias) const
367 : {
368 496 : LOCK(cs_data_requests);
369 539 : auto [old_pair, inserted] = mapQuorumDataRequests.emplace(key, request);
370 496 : if (!inserted) {
371 133 : if (old_pair->second.IsExpired(add_expiry_bias)) {
372 86 : old_pair->second = request;
373 43 : return true;
374 : }
375 90 : return false;
376 : }
377 363 : return true;
378 496 : }
379 :
380 179 : CQuorumManager::DataResponseValidation CQuorumManager::ValidateDataResponse(
381 : const CQuorumDataRequestKey& key, const CQuorumDataRequest& response) const
382 : {
383 179 : LOCK(cs_data_requests);
384 179 : auto it = mapQuorumDataRequests.find(key);
385 179 : if (it == mapQuorumDataRequests.end()) {
386 6 : return DataResponseValidation::NotRequested;
387 : }
388 173 : if (it->second.IsProcessed()) {
389 6 : return DataResponseValidation::AlreadyReceived;
390 : }
391 167 : if (response != it->second) {
392 18 : return DataResponseValidation::Mismatch;
393 : }
394 149 : it->second.SetProcessed();
395 149 : return DataResponseValidation::OK;
396 179 : }
397 :
398 130 : CQuorumPtr CQuorumManager::GetCachedMutableQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash) const
399 : {
400 130 : CQuorumPtr pQuorum;
401 130 : LOCK(m_cs_maps);
402 130 : mapQuorumsCache[llmqType].get(quorumHash, pQuorum);
403 130 : return pQuorum;
404 130 : }
405 :
406 130 : void CQuorumManager::WriteContributions(const CQuorumPtr& quorum) const
407 : {
408 130 : LOCK(cs_db);
409 130 : quorum->WriteContributions(*db);
410 130 : }
411 :
412 3063 : void CQuorumManager::CacheWarmingThreadMain() const
413 : {
414 400319 : while (!m_cache_interrupt) {
415 397256 : CQuorumCPtr pQuorum;
416 : {
417 397256 : LOCK(m_cache_cs);
418 397256 : if (!m_cache_queue.empty()) {
419 2948 : pQuorum = std::move(m_cache_queue.front());
420 2948 : m_cache_queue.pop_front();
421 2948 : };
422 397256 : }
423 :
424 397256 : if (!pQuorum) {
425 394308 : m_cache_interrupt.sleep_for(std::chrono::milliseconds(100));
426 394308 : continue;
427 : }
428 :
429 2948 : cxxtimer::Timer t(true);
430 2948 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- type=%d height=%d hash=%s start\n", __func__,
431 : std23::to_underlying(pQuorum->params.type), pQuorum->m_quorum_base_block_index->nHeight,
432 : pQuorum->m_quorum_base_block_index->GetBlockHash().ToString());
433 :
434 : // when then later some other thread tries to get keys, it will be much faster
435 14009 : for (const auto i : util::irange(pQuorum->members.size())) {
436 11061 : if (m_cache_interrupt) {
437 0 : break;
438 : }
439 11061 : if (pQuorum->qc->validMembers[i]) {
440 10906 : pQuorum->GetPubKeyShare(i);
441 10906 : }
442 : }
443 :
444 2948 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- type=%d height=%d hash=%s done. time=%d\n", __func__,
445 : std23::to_underlying(pQuorum->params.type), pQuorum->m_quorum_base_block_index->nHeight,
446 : pQuorum->m_quorum_base_block_index->GetBlockHash().ToString(), t.count());
447 397256 : }
448 3063 : }
449 :
450 2948 : void CQuorumManager::QueueQuorumForWarming(CQuorumCPtr pQuorum) const
451 : {
452 2948 : if (pQuorum->HasVerificationVector()) {
453 2948 : LOCK(m_cache_cs);
454 2948 : m_cache_queue.push_back(std::move(pQuorum));
455 2948 : }
456 2948 : }
457 :
458 : // TODO: remove in v23
459 3063 : void CQuorumManager::MigrateOldQuorumDB(CEvoDB& evoDb) const
460 : {
461 3063 : LOCK(cs_db);
462 3063 : if (!db->IsEmpty()) return;
463 :
464 2868 : const auto prefixes = {DB_QUORUM_QUORUM_VVEC, DB_QUORUM_SK_SHARE};
465 :
466 2868 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- start\n", __func__);
467 :
468 2868 : CDBBatch batch(*db);
469 2868 : std::unique_ptr<CDBIterator> pcursor(evoDb.GetRawDB().NewIterator());
470 :
471 8604 : for (const auto& prefix : prefixes) {
472 5736 : auto start = std::make_tuple(prefix, uint256());
473 5736 : pcursor->Seek(start);
474 :
475 5736 : int count{0};
476 5736 : while (pcursor->Valid()) {
477 1541 : decltype(start) k;
478 1541 : CDataStream s(SER_DISK, CLIENT_VERSION);
479 1541 : CBLSSecretKey sk;
480 :
481 1541 : if (!pcursor->GetKey(k) || std::get<0>(k) != prefix) {
482 1541 : break;
483 : }
484 :
485 0 : if (prefix == DB_QUORUM_QUORUM_VVEC) {
486 0 : if (!evoDb.GetRawDB().ReadDataStream(k, s)) {
487 0 : break;
488 : }
489 0 : batch.Write(k, s);
490 0 : }
491 0 : if (prefix == DB_QUORUM_SK_SHARE) {
492 0 : if (!pcursor->GetValue(sk)) {
493 0 : break;
494 : }
495 0 : batch.Write(k, sk);
496 0 : }
497 :
498 0 : if (batch.SizeEstimate() >= (1 << 24)) {
499 0 : db->WriteBatch(batch);
500 0 : batch.Clear();
501 0 : }
502 :
503 0 : ++count;
504 0 : pcursor->Next();
505 1541 : }
506 :
507 5736 : db->WriteBatch(batch);
508 :
509 5736 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- %s moved %d\n", __func__, prefix, count);
510 5736 : }
511 :
512 2868 : pcursor.reset();
513 2868 : db->CompactFull();
514 :
515 2868 : DataCleanupHelper(evoDb.GetRawDB(), {});
516 2868 : evoDb.CommitRootTransaction();
517 :
518 2868 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- done\n", __func__);
519 3063 : }
520 :
521 82937 : CQuorumCPtr SelectQuorumForSigning(const Consensus::LLMQParams& llmq_params, const CChain& active_chain, const CQuorumManager& qman,
522 : const uint256& selectionHash, int signHeight, int signOffset)
523 : {
524 82937 : size_t poolSize = llmq_params.signingActiveQuorumCount;
525 :
526 : CBlockIndex* pindexStart;
527 : {
528 82937 : LOCK(::cs_main);
529 82937 : if (signHeight == -1) {
530 64632 : signHeight = active_chain.Height();
531 64632 : }
532 82937 : int startBlockHeight = signHeight - signOffset;
533 82937 : if (startBlockHeight > active_chain.Height() || startBlockHeight < 0) {
534 9 : return {};
535 : }
536 82928 : pindexStart = active_chain[startBlockHeight];
537 82937 : }
538 :
539 : // don't remove connections for the currently in-progress DKG round
540 82928 : if (IsQuorumRotationEnabled(llmq_params, pindexStart)) {
541 5698 : auto quorums = qman.ScanQuorums(llmq_params.type, pindexStart, poolSize);
542 5698 : if (quorums.empty()) {
543 2239 : return nullptr;
544 : }
545 : //log2 int
546 3459 : int n = std::log2(llmq_params.signingActiveQuorumCount);
547 : //Extract last 64 bits of selectionHash
548 3459 : uint64_t b = selectionHash.GetUint64(3);
549 : //Take last n bits of b
550 3459 : uint64_t signer = (((1ull << n) - 1) & (b >> (64 - n - 1)));
551 :
552 3459 : if (signer > quorums.size()) {
553 0 : return nullptr;
554 : }
555 6918 : auto itQuorum = std::find_if(quorums.begin(),
556 3459 : quorums.end(),
557 8132 : [signer](const CQuorumCPtr& obj) {
558 4673 : return uint64_t(obj->qc->quorumIndex) == signer;
559 : });
560 3459 : if (itQuorum == quorums.end()) {
561 564 : return nullptr;
562 : }
563 2895 : return *itQuorum;
564 5698 : } else {
565 77230 : auto quorums = qman.ScanQuorums(llmq_params.type, pindexStart, poolSize);
566 77230 : if (quorums.empty()) {
567 27980 : return nullptr;
568 : }
569 :
570 49250 : std::vector<std::pair<uint256, size_t>> scores;
571 49250 : scores.reserve(quorums.size());
572 136310 : for (const auto i : util::irange(quorums.size())) {
573 87060 : CHashWriter h(SER_NETWORK, 0);
574 87060 : h << llmq_params.type;
575 87060 : h << quorums[i]->qc->quorumHash;
576 87060 : h << selectionHash;
577 87060 : scores.emplace_back(h.GetHash(), i);
578 : }
579 49250 : std::sort(scores.begin(), scores.end());
580 49250 : return quorums[scores.front().second];
581 77230 : }
582 82937 : }
583 :
584 18185 : VerifyRecSigStatus VerifyRecoveredSig(Consensus::LLMQType llmqType, const CChain& active_chain, const CQuorumManager& qman,
585 : int signedAtHeight, const uint256& id, const uint256& msgHash, const CBLSSignature& sig,
586 : const int signOffset)
587 : {
588 18185 : const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
589 18185 : assert(llmq_params_opt.has_value());
590 18185 : auto quorum = SelectQuorumForSigning(llmq_params_opt.value(), active_chain, qman, id, signedAtHeight, signOffset);
591 18185 : if (!quorum) {
592 28 : return VerifyRecSigStatus::NoQuorum;
593 : }
594 :
595 18157 : SignHash signHash{llmqType, quorum->qc->quorumHash, id, msgHash};
596 18157 : const bool ret = sig.VerifyInsecure(quorum->qc->quorumPublicKey, signHash.Get());
597 18157 : return ret ? VerifyRecSigStatus::Valid : VerifyRecSigStatus::Invalid;
598 18185 : }
599 :
600 : } // namespace llmq
|