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 360 : CQuorumManager::CQuorumManager(CBLSWorker& _blsWorker, CDeterministicMNManager& dmnman, CEvoDB& _evoDb,
32 : CQuorumBlockProcessor& _quorumBlockProcessor, CQuorumSnapshotManager& qsnapman,
33 : const ChainstateManager& chainman, const util::DbWrapperParams& db_params) :
34 180 : blsWorker{_blsWorker},
35 180 : m_dmnman{dmnman},
36 180 : quorumBlockProcessor{_quorumBlockProcessor},
37 180 : m_qsnapman{qsnapman},
38 180 : m_chainman{chainman},
39 : db{util::MakeDbWrapper({db_params.path / "llmq" / "quorumdb", db_params.memory, db_params.wipe, /*cache_size=*/1 << 20})}
40 180 : {
41 : utils::InitQuorumsCache(mapQuorumsCache, m_chainman.GetConsensus(), /*limit_by_connections=*/false);
42 : m_cache_interrupt.reset();
43 180 : m_cache_thread = std::thread(&util::TraceThread, "q-cache", [this] { CacheWarmingThreadMain(); });
44 : MigrateOldQuorumDB(_evoDb);
45 180 : }
46 :
47 360 : CQuorumManager::~CQuorumManager()
48 180 : {
49 180 : if (m_cache_thread.joinable()) {
50 180 : m_cache_interrupt();
51 180 : m_cache_thread.join();
52 180 : }
53 360 : }
54 :
55 0 : 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 0 : if (m_qdkgsman) {
60 0 : return m_qdkgsman->GetEncryptedContributions(llmq_type, block_index, valid_members, protx_hash, vec_enc);
61 : }
62 0 : return false;
63 0 : }
64 :
65 0 : CQuorumPtr CQuorumManager::BuildQuorumFromCommitment(const Consensus::LLMQType llmqType, gsl::not_null<const CBlockIndex*> pQuorumBaseBlockIndex, bool populate_cache) const
66 : {
67 0 : const uint256& quorumHash{pQuorumBaseBlockIndex->GetBlockHash()};
68 :
69 0 : auto [qc, minedBlockHash] = quorumBlockProcessor.GetMinedCommitment(llmqType, quorumHash);
70 0 : 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 0 : assert(qc.quorumHash == pQuorumBaseBlockIndex->GetBlockHash());
75 :
76 0 : const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
77 0 : assert(llmq_params_opt.has_value());
78 0 : auto members = utils::GetAllQuorumMembers(qc.llmqType, {m_dmnman, m_qsnapman, m_chainman, pQuorumBaseBlockIndex});
79 :
80 0 : auto quorum = std::make_shared<CQuorum>(llmq_params_opt.value(), blsWorker,
81 0 : std::make_unique<CFinalCommitment>(std::move(qc)), pQuorumBaseBlockIndex, minedBlockHash, members);
82 :
83 0 : if (populate_cache && llmq_params_opt->size == 1) {
84 0 : WITH_LOCK(m_cs_maps, mapQuorumsCache[llmqType].insert(quorumHash, quorum));
85 :
86 0 : return quorum;
87 : }
88 :
89 0 : bool hasValidVvec = false;
90 0 : if (WITH_LOCK(cs_db, return quorum->ReadContributions(*db))) {
91 0 : hasValidVvec = true;
92 0 : } else {
93 0 : if (BuildQuorumContributions(quorum->qc, quorum)) {
94 0 : WITH_LOCK(cs_db, quorum->WriteContributions(*db));
95 0 : hasValidVvec = true;
96 0 : } else {
97 0 : 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 0 : 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 0 : QueueQuorumForWarming(quorum);
106 0 : }
107 :
108 0 : WITH_LOCK(m_cs_maps, mapQuorumsCache[llmqType].insert(quorumHash, quorum));
109 :
110 0 : return quorum;
111 0 : }
112 :
113 0 : bool CQuorumManager::BuildQuorumContributions(const CFinalCommitmentPtr& fqc, const std::shared_ptr<CQuorum>& quorum) const
114 : {
115 0 : std::vector<uint16_t> memberIndexes;
116 0 : std::vector<BLSVerificationVectorPtr> vvecs;
117 0 : std::vector<CBLSSecretKey> skContributions;
118 0 : if (!m_qdkgsman ||
119 0 : !m_qdkgsman->GetVerifiedContributions((Consensus::LLMQType)fqc->llmqType, quorum->m_quorum_base_block_index,
120 0 : fqc->validMembers, memberIndexes, vvecs, skContributions)) {
121 0 : return false;
122 : }
123 :
124 0 : cxxtimer::Timer t2(true);
125 0 : quorum->SetVerificationVector(blsWorker.BuildQuorumVerificationVector(vvecs));
126 0 : 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 0 : if (!m_handler || !m_handler->SetQuorumSecretKeyShare(*quorum, skContributions)) {
133 0 : 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 0 : }
137 0 : t2.stop();
138 :
139 0 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- built quorum vvec and skShare. time=%d\n", __func__, t2.count());
140 :
141 0 : return true;
142 0 : }
143 :
144 0 : bool CQuorumManager::HasQuorum(Consensus::LLMQType llmqType, const CQuorumBlockProcessor& quorum_block_processor, const uint256& quorumHash)
145 : {
146 0 : return quorum_block_processor.HasMinedCommitment(llmqType, quorumHash);
147 : }
148 :
149 0 : std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType, size_t nCountRequested) const
150 : {
151 0 : const CBlockIndex* pindex = WITH_LOCK(::cs_main, return m_chainman.ActiveTip());
152 0 : return ScanQuorums(llmqType, pindex, nCountRequested);
153 : }
154 :
155 0 : std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType,
156 : gsl::not_null<const CBlockIndex*> pindexStart,
157 : size_t nCountRequested) const
158 : {
159 0 : if (nCountRequested == 0 || !m_chainman.IsQuorumTypeEnabled(llmqType, pindexStart)) {
160 0 : return {};
161 : }
162 :
163 0 : gsl::not_null<const CBlockIndex*> pindexStore{pindexStart};
164 0 : const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
165 0 : 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 0 : const int quorumCycleStartHeight = pindexStart->nHeight - (pindexStart->nHeight % llmq_params_opt->dkgInterval);
170 0 : const int quorumCycleMiningStartHeight = quorumCycleStartHeight + llmq_params_opt->dkgMiningWindowStart;
171 0 : const int quorumCycleMiningEndHeight = quorumCycleStartHeight + llmq_params_opt->dkgMiningWindowEnd;
172 :
173 0 : if (pindexStart->nHeight < quorumCycleMiningStartHeight) {
174 : // too early for this cycle, use the previous one
175 : // bail out if it's below genesis block
176 0 : if (quorumCycleMiningEndHeight < llmq_params_opt->dkgInterval) return {};
177 0 : pindexStore = pindexStart->GetAncestor(quorumCycleMiningEndHeight - llmq_params_opt->dkgInterval);
178 0 : } else if (pindexStart->nHeight > quorumCycleMiningEndHeight) {
179 : // we are past the mining phase of this cycle, use it
180 0 : pindexStore = pindexStart->GetAncestor(quorumCycleMiningEndHeight);
181 0 : }
182 : // everything else is inside the mining phase of this cycle, no pindexStore adjustment needed
183 :
184 0 : gsl::not_null<const CBlockIndex*> pIndexScanCommitments{pindexStore};
185 0 : size_t nScanCommitments{nCountRequested};
186 0 : std::vector<CQuorumCPtr> vecResultQuorums;
187 :
188 : {
189 0 : LOCK(m_cs_maps);
190 0 : if (scanQuorumsCache.empty()) {
191 0 : 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 0 : scanQuorumsCache.try_emplace(llmq.type, llmq.max_cycles(llmq.keepOldConnections) * (llmq.dkgMiningWindowEnd - llmq.dkgMiningWindowStart));
198 : }
199 0 : }
200 0 : auto& cache = scanQuorumsCache[llmqType];
201 0 : bool fCacheExists = cache.get(pindexStore->GetBlockHash(), vecResultQuorums);
202 0 : if (fCacheExists) {
203 : // We have exactly what requested so just return it
204 0 : if (vecResultQuorums.size() == nCountRequested) {
205 0 : return vecResultQuorums;
206 : }
207 : // If we have more cached than requested return only a subvector
208 0 : if (vecResultQuorums.size() > nCountRequested) {
209 0 : 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 0 : if (!vecResultQuorums.empty()) {
214 0 : nScanCommitments -= vecResultQuorums.size();
215 : // bail out if it's below genesis block
216 0 : if (vecResultQuorums.back()->m_quorum_base_block_index->pprev == nullptr) return {};
217 0 : pIndexScanCommitments = vecResultQuorums.back()->m_quorum_base_block_index->pprev;
218 0 : }
219 0 : } else {
220 : // If there is nothing in cache request at least keepOldConnections because this gets cached then later
221 0 : nScanCommitments = std::max(nCountRequested, static_cast<size_t>(llmq_params_opt->keepOldConnections));
222 : }
223 0 : }
224 :
225 : // Get the block indexes of the mined commitments to build the required quorums from
226 0 : std::vector<const CBlockIndex*> pQuorumBaseBlockIndexes{ llmq_params_opt->useRotation ?
227 0 : quorumBlockProcessor.GetMinedCommitmentsIndexedUntilBlock(llmqType, pIndexScanCommitments, nScanCommitments) :
228 0 : quorumBlockProcessor.GetMinedCommitmentsUntilBlock(llmqType, pIndexScanCommitments, nScanCommitments)
229 : };
230 0 : vecResultQuorums.reserve(vecResultQuorums.size() + pQuorumBaseBlockIndexes.size());
231 :
232 0 : for (auto& pQuorumBaseBlockIndex : pQuorumBaseBlockIndexes) {
233 0 : assert(pQuorumBaseBlockIndex);
234 : // populate cache for keepOldConnections most recent quorums only
235 0 : 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 0 : auto quorum = GetQuorum(llmqType, pQuorumBaseBlockIndex, populate_cache);
241 0 : 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 0 : vecResultQuorums.emplace_back(quorum);
248 0 : }
249 :
250 0 : const size_t nCountResult{vecResultQuorums.size()};
251 0 : if (nCountResult > 0) {
252 0 : 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 0 : auto& cache = scanQuorumsCache[llmqType];
257 0 : const size_t nCacheEndIndex = std::min(nCountResult, static_cast<size_t>(llmq_params_opt->keepOldConnections));
258 0 : cache.emplace(pindexStore->GetBlockHash(), {vecResultQuorums.begin(), vecResultQuorums.begin() + nCacheEndIndex});
259 0 : }
260 : // Don't return more than nCountRequested elements
261 0 : const size_t nResultEndIndex = std::min(nCountResult, nCountRequested);
262 0 : return {vecResultQuorums.begin(), vecResultQuorums.begin() + nResultEndIndex};
263 0 : }
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 2 : bool CQuorumManager::IsWatching() const
274 : {
275 2 : if (m_handler) {
276 0 : return m_handler->IsWatching();
277 : }
278 2 : return false;
279 2 : }
280 :
281 0 : bool CQuorumManager::IsDataRequestPending(const uint256& proRegTx, bool we_requested, const uint256& quorumHash,
282 : Consensus::LLMQType llmqType) const
283 : {
284 0 : const CQuorumDataRequestKey key{proRegTx, we_requested, quorumHash, llmqType};
285 0 : LOCK(cs_data_requests);
286 0 : const auto it = mapQuorumDataRequests.find(key);
287 0 : return it != mapQuorumDataRequests.end() && !it->second.IsExpired(/*add_bias=*/true);
288 0 : }
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 0 : void CQuorumManager::CleanupExpiredDataRequests() const
306 : {
307 0 : LOCK(cs_data_requests);
308 0 : auto it = mapQuorumDataRequests.begin();
309 0 : while (it != mapQuorumDataRequests.end()) {
310 0 : if (it->second.IsExpired(/*add_bias=*/true)) {
311 0 : it = mapQuorumDataRequests.erase(it);
312 0 : } else {
313 0 : ++it;
314 : }
315 : }
316 0 : }
317 :
318 0 : void CQuorumManager::CleanupOldQuorumData(const Uint256HashSet& dbKeysToSkip) const
319 : {
320 0 : LOCK(cs_db);
321 0 : DataCleanupHelper(*db, dbKeysToSkip);
322 0 : }
323 :
324 0 : CQuorumCPtr CQuorumManager::GetQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash) const
325 : {
326 0 : 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 0 : if (!WITH_LOCK(cs_quorumBaseBlockIndexCache, return quorumBaseBlockIndexCache.get(quorumHash, pindex))) {
332 0 : pindex = WITH_LOCK(::cs_main, return m_chainman.m_blockman.LookupBlockIndex(quorumHash));
333 0 : if (pindex) {
334 0 : LOCK(cs_quorumBaseBlockIndexCache);
335 0 : quorumBaseBlockIndexCache.insert(quorumHash, pindex);
336 0 : }
337 0 : }
338 0 : return pindex;
339 0 : }();
340 0 : if (!pQuorumBaseBlockIndex) {
341 0 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- block %s not found\n", __func__, quorumHash.ToString());
342 0 : return nullptr;
343 : }
344 0 : return GetQuorum(llmqType, pQuorumBaseBlockIndex);
345 0 : }
346 :
347 0 : CQuorumCPtr CQuorumManager::GetQuorum(Consensus::LLMQType llmqType, gsl::not_null<const CBlockIndex*> pQuorumBaseBlockIndex, bool populate_cache) const
348 : {
349 0 : 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 0 : if (!HasQuorum(llmqType, quorumBlockProcessor, quorumHash)) {
354 0 : return nullptr;
355 : }
356 :
357 0 : CQuorumPtr pQuorum;
358 0 : if (LOCK(m_cs_maps); mapQuorumsCache[llmqType].get(quorumHash, pQuorum)) {
359 0 : return pQuorum;
360 : }
361 :
362 0 : return BuildQuorumFromCommitment(llmqType, pQuorumBaseBlockIndex, populate_cache);
363 0 : }
364 :
365 0 : bool CQuorumManager::RegisterDataRequest(const CQuorumDataRequestKey& key, const CQuorumDataRequest& request,
366 : bool add_expiry_bias) const
367 : {
368 0 : LOCK(cs_data_requests);
369 0 : auto [old_pair, inserted] = mapQuorumDataRequests.emplace(key, request);
370 0 : if (!inserted) {
371 0 : if (old_pair->second.IsExpired(add_expiry_bias)) {
372 0 : old_pair->second = request;
373 0 : return true;
374 : }
375 0 : return false;
376 : }
377 0 : return true;
378 0 : }
379 :
380 0 : CQuorumManager::DataResponseValidation CQuorumManager::ValidateDataResponse(
381 : const CQuorumDataRequestKey& key, const CQuorumDataRequest& response) const
382 : {
383 0 : LOCK(cs_data_requests);
384 0 : auto it = mapQuorumDataRequests.find(key);
385 0 : if (it == mapQuorumDataRequests.end()) {
386 0 : return DataResponseValidation::NotRequested;
387 : }
388 0 : if (it->second.IsProcessed()) {
389 0 : return DataResponseValidation::AlreadyReceived;
390 : }
391 0 : if (response != it->second) {
392 0 : return DataResponseValidation::Mismatch;
393 : }
394 0 : it->second.SetProcessed();
395 0 : return DataResponseValidation::OK;
396 0 : }
397 :
398 0 : CQuorumPtr CQuorumManager::GetCachedMutableQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash) const
399 : {
400 0 : CQuorumPtr pQuorum;
401 0 : LOCK(m_cs_maps);
402 0 : mapQuorumsCache[llmqType].get(quorumHash, pQuorum);
403 0 : return pQuorum;
404 0 : }
405 :
406 0 : void CQuorumManager::WriteContributions(const CQuorumPtr& quorum) const
407 : {
408 0 : LOCK(cs_db);
409 0 : quorum->WriteContributions(*db);
410 0 : }
411 :
412 180 : void CQuorumManager::CacheWarmingThreadMain() const
413 : {
414 2005 : while (!m_cache_interrupt) {
415 1825 : CQuorumCPtr pQuorum;
416 : {
417 1825 : LOCK(m_cache_cs);
418 1825 : if (!m_cache_queue.empty()) {
419 0 : pQuorum = std::move(m_cache_queue.front());
420 0 : m_cache_queue.pop_front();
421 0 : };
422 1825 : }
423 :
424 1825 : if (!pQuorum) {
425 1825 : m_cache_interrupt.sleep_for(std::chrono::milliseconds(100));
426 1825 : continue;
427 : }
428 :
429 0 : cxxtimer::Timer t(true);
430 0 : 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 0 : for (const auto i : util::irange(pQuorum->members.size())) {
436 0 : if (m_cache_interrupt) {
437 0 : break;
438 : }
439 0 : if (pQuorum->qc->validMembers[i]) {
440 0 : pQuorum->GetPubKeyShare(i);
441 0 : }
442 : }
443 :
444 0 : 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 1825 : }
448 180 : }
449 :
450 0 : void CQuorumManager::QueueQuorumForWarming(CQuorumCPtr pQuorum) const
451 : {
452 0 : if (pQuorum->HasVerificationVector()) {
453 0 : LOCK(m_cache_cs);
454 0 : m_cache_queue.push_back(std::move(pQuorum));
455 0 : }
456 0 : }
457 :
458 : // TODO: remove in v23
459 180 : void CQuorumManager::MigrateOldQuorumDB(CEvoDB& evoDb) const
460 : {
461 180 : LOCK(cs_db);
462 180 : if (!db->IsEmpty()) return;
463 :
464 180 : const auto prefixes = {DB_QUORUM_QUORUM_VVEC, DB_QUORUM_SK_SHARE};
465 :
466 180 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- start\n", __func__);
467 :
468 180 : CDBBatch batch(*db);
469 180 : std::unique_ptr<CDBIterator> pcursor(evoDb.GetRawDB().NewIterator());
470 :
471 540 : for (const auto& prefix : prefixes) {
472 360 : auto start = std::make_tuple(prefix, uint256());
473 360 : pcursor->Seek(start);
474 :
475 360 : int count{0};
476 360 : while (pcursor->Valid()) {
477 0 : decltype(start) k;
478 0 : CDataStream s(SER_DISK, CLIENT_VERSION);
479 0 : CBLSSecretKey sk;
480 :
481 0 : if (!pcursor->GetKey(k) || std::get<0>(k) != prefix) {
482 0 : 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 0 : }
506 :
507 360 : db->WriteBatch(batch);
508 :
509 360 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- %s moved %d\n", __func__, prefix, count);
510 360 : }
511 :
512 180 : pcursor.reset();
513 180 : db->CompactFull();
514 :
515 180 : DataCleanupHelper(evoDb.GetRawDB(), {});
516 180 : evoDb.CommitRootTransaction();
517 :
518 180 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- done\n", __func__);
519 180 : }
520 :
521 0 : CQuorumCPtr SelectQuorumForSigning(const Consensus::LLMQParams& llmq_params, const CChain& active_chain, const CQuorumManager& qman,
522 : const uint256& selectionHash, int signHeight, int signOffset)
523 : {
524 0 : size_t poolSize = llmq_params.signingActiveQuorumCount;
525 :
526 : CBlockIndex* pindexStart;
527 : {
528 0 : LOCK(::cs_main);
529 0 : if (signHeight == -1) {
530 0 : signHeight = active_chain.Height();
531 0 : }
532 0 : int startBlockHeight = signHeight - signOffset;
533 0 : if (startBlockHeight > active_chain.Height() || startBlockHeight < 0) {
534 0 : return {};
535 : }
536 0 : pindexStart = active_chain[startBlockHeight];
537 0 : }
538 :
539 : // don't remove connections for the currently in-progress DKG round
540 0 : if (IsQuorumRotationEnabled(llmq_params, pindexStart)) {
541 0 : auto quorums = qman.ScanQuorums(llmq_params.type, pindexStart, poolSize);
542 0 : if (quorums.empty()) {
543 0 : return nullptr;
544 : }
545 : //log2 int
546 0 : int n = std::log2(llmq_params.signingActiveQuorumCount);
547 : //Extract last 64 bits of selectionHash
548 0 : uint64_t b = selectionHash.GetUint64(3);
549 : //Take last n bits of b
550 0 : uint64_t signer = (((1ull << n) - 1) & (b >> (64 - n - 1)));
551 :
552 0 : if (signer > quorums.size()) {
553 0 : return nullptr;
554 : }
555 0 : auto itQuorum = std::find_if(quorums.begin(),
556 0 : quorums.end(),
557 0 : [signer](const CQuorumCPtr& obj) {
558 0 : return uint64_t(obj->qc->quorumIndex) == signer;
559 : });
560 0 : if (itQuorum == quorums.end()) {
561 0 : return nullptr;
562 : }
563 0 : return *itQuorum;
564 0 : } else {
565 0 : auto quorums = qman.ScanQuorums(llmq_params.type, pindexStart, poolSize);
566 0 : if (quorums.empty()) {
567 0 : return nullptr;
568 : }
569 :
570 0 : std::vector<std::pair<uint256, size_t>> scores;
571 0 : scores.reserve(quorums.size());
572 0 : for (const auto i : util::irange(quorums.size())) {
573 0 : CHashWriter h(SER_NETWORK, 0);
574 0 : h << llmq_params.type;
575 0 : h << quorums[i]->qc->quorumHash;
576 0 : h << selectionHash;
577 0 : scores.emplace_back(h.GetHash(), i);
578 : }
579 0 : std::sort(scores.begin(), scores.end());
580 0 : return quorums[scores.front().second];
581 0 : }
582 0 : }
583 :
584 0 : 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 0 : const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
589 0 : assert(llmq_params_opt.has_value());
590 0 : auto quorum = SelectQuorumForSigning(llmq_params_opt.value(), active_chain, qman, id, signedAtHeight, signOffset);
591 0 : if (!quorum) {
592 0 : return VerifyRecSigStatus::NoQuorum;
593 : }
594 :
595 0 : SignHash signHash{llmqType, quorum->qc->quorumHash, id, msgHash};
596 0 : const bool ret = sig.VerifyInsecure(quorum->qc->quorumPublicKey, signHash.Get());
597 0 : return ret ? VerifyRecSigStatus::Valid : VerifyRecSigStatus::Invalid;
598 0 : }
599 :
600 : } // namespace llmq
|