@@ -201,8 +201,6 @@ CQuorumManager::CQuorumManager(CBLSWorker& _blsWorker, CChainState& chainstate,
201
201
m_peerman (peerman)
202
202
{
203
203
utils::InitQuorumsCache (mapQuorumsCache, false );
204
- utils::InitQuorumsCache (scanQuorumsCache, false );
205
-
206
204
quorumThreadInterrupt.reset ();
207
205
}
208
206
@@ -503,14 +501,45 @@ std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqTyp
503
501
return {};
504
502
}
505
503
506
- const CBlockIndex* pIndexScanCommitments{pindexStart};
504
+ gsl::not_null<const CBlockIndex*> pindexStore{pindexStart};
505
+ const auto & llmq_params_opt = GetLLMQParams (llmqType);
506
+ assert (llmq_params_opt.has_value ());
507
+
508
+ // Quorum sets can only change during the mining phase of DKG.
509
+ // Find the closest known block index.
510
+ const int quorumCycleStartHeight = pindexStart->nHeight - (pindexStart->nHeight % llmq_params_opt->dkgInterval );
511
+ const int quorumCycleMiningStartHeight = quorumCycleStartHeight + llmq_params_opt->dkgMiningWindowStart ;
512
+ const int quorumCycleMiningEndHeight = quorumCycleStartHeight + llmq_params_opt->dkgMiningWindowEnd ;
513
+
514
+ if (pindexStart->nHeight < quorumCycleMiningStartHeight) {
515
+ // too early for this cycle, use the previous one
516
+ // bail out if it's below genesis block
517
+ if (quorumCycleMiningEndHeight < llmq_params_opt->dkgInterval ) return {};
518
+ pindexStore = pindexStart->GetAncestor (quorumCycleMiningEndHeight - llmq_params_opt->dkgInterval );
519
+ } else if (pindexStart->nHeight > quorumCycleMiningEndHeight) {
520
+ // we are past the mining phase of this cycle, use it
521
+ pindexStore = pindexStart->GetAncestor (quorumCycleMiningEndHeight);
522
+ }
523
+ // everything else is inside the mining phase of this cycle, no pindexStore adjustment needed
524
+
525
+ gsl::not_null<const CBlockIndex*> pIndexScanCommitments{pindexStore};
507
526
size_t nScanCommitments{nCountRequested};
508
527
std::vector<CQuorumCPtr> vecResultQuorums;
509
528
510
529
{
511
530
LOCK (cs_scan_quorums);
531
+ if (scanQuorumsCache.empty ()) {
532
+ for (const auto & llmq : Params ().GetConsensus ().llmqs ) {
533
+ // NOTE: We store it for each block hash in the DKG mining phase here
534
+ // and not for a single quorum hash per quorum like we do for other caches.
535
+ // And we only do this for max_cycles() of the most recent quorums
536
+ // because signing by old quorums requires the exact quorum hash to be specified
537
+ // and quorum scanning isn't needed there.
538
+ scanQuorumsCache.try_emplace (llmq.type , utils::max_cycles (llmq, llmq.keepOldConnections ) * (llmq.dkgMiningWindowEnd - llmq.dkgMiningWindowStart ));
539
+ }
540
+ }
512
541
auto & cache = scanQuorumsCache[llmqType];
513
- bool fCacheExists = cache.get (pindexStart ->GetBlockHash (), vecResultQuorums);
542
+ bool fCacheExists = cache.get (pindexStore ->GetBlockHash (), vecResultQuorums);
514
543
if (fCacheExists ) {
515
544
// We have exactly what requested so just return it
516
545
if (vecResultQuorums.size () == nCountRequested) {
@@ -524,17 +553,17 @@ std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqTyp
524
553
// scanning for the rests
525
554
if (!vecResultQuorums.empty ()) {
526
555
nScanCommitments -= vecResultQuorums.size ();
556
+ // bail out if it's below genesis block
557
+ if (vecResultQuorums.back ()->m_quorum_base_block_index ->pprev == nullptr ) return {};
527
558
pIndexScanCommitments = vecResultQuorums.back ()->m_quorum_base_block_index ->pprev ;
528
559
}
529
560
} else {
530
- // If there is nothing in cache request at least cache.max_size() because this gets cached then later
531
- nScanCommitments = std::max (nCountRequested, cache. max_size ( ));
561
+ // If there is nothing in cache request at least keepOldConnections because this gets cached then later
562
+ nScanCommitments = std::max (nCountRequested, static_cast < size_t >(llmq_params_opt-> keepOldConnections ));
532
563
}
533
564
}
534
565
535
566
// Get the block indexes of the mined commitments to build the required quorums from
536
- const auto & llmq_params_opt = GetLLMQParams (llmqType);
537
- assert (llmq_params_opt.has_value ());
538
567
std::vector<const CBlockIndex*> pQuorumBaseBlockIndexes{ llmq_params_opt->useRotation ?
539
568
quorumBlockProcessor.GetMinedCommitmentsIndexedUntilBlock (llmqType, pIndexScanCommitments, nScanCommitments) :
540
569
quorumBlockProcessor.GetMinedCommitmentsUntilBlock (llmqType, pIndexScanCommitments, nScanCommitments)
@@ -551,10 +580,12 @@ std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqTyp
551
580
const size_t nCountResult{vecResultQuorums.size ()};
552
581
if (nCountResult > 0 ) {
553
582
LOCK (cs_scan_quorums);
554
- // Don't cache more than cache.max_size() elements
583
+ // Don't cache more than keepOldConnections elements
584
+ // because signing by old quorums requires the exact quorum hash
585
+ // to be specified and quorum scanning isn't needed there.
555
586
auto & cache = scanQuorumsCache[llmqType];
556
- const size_t nCacheEndIndex = std::min (nCountResult, cache. max_size ( ));
557
- cache.emplace (pindexStart ->GetBlockHash (), {vecResultQuorums.begin (), vecResultQuorums.begin () + nCacheEndIndex});
587
+ const size_t nCacheEndIndex = std::min (nCountResult, static_cast < size_t >(llmq_params_opt-> keepOldConnections ));
588
+ cache.emplace (pindexStore ->GetBlockHash (), {vecResultQuorums.begin (), vecResultQuorums.begin () + nCacheEndIndex});
558
589
}
559
590
// Don't return more than nCountRequested elements
560
591
const size_t nResultEndIndex = std::min (nCountResult, nCountRequested);
@@ -1023,13 +1054,31 @@ void CQuorumManager::StartCleanupOldQuorumDataThread(const CBlockIndex* pIndex)
1023
1054
workerPool.push ([pIndex, t, this ](int threadId) {
1024
1055
std::set<uint256> dbKeysToSkip;
1025
1056
1057
+ if (LOCK (cs_cleanup); cleanupQuorumsCache.empty ()) {
1058
+ utils::InitQuorumsCache (cleanupQuorumsCache, false );
1059
+ }
1026
1060
for (const auto & params : Params ().GetConsensus ().llmqs ) {
1027
1061
if (quorumThreadInterrupt) {
1028
1062
break ;
1029
1063
}
1030
- for (const auto & pQuorum : ScanQuorums (params.type , pIndex, params.keepOldKeys )) {
1031
- dbKeysToSkip.insert (MakeQuorumKey (*pQuorum));
1064
+ LOCK (cs_cleanup);
1065
+ auto & cache = cleanupQuorumsCache[params.type ];
1066
+ const CBlockIndex* pindex_loop{pIndex};
1067
+ std::set<uint256> quorum_keys;
1068
+ while (pindex_loop != nullptr && pIndex->nHeight - pindex_loop->nHeight < utils::max_store_depth (params)) {
1069
+ uint256 quorum_key;
1070
+ if (cache.get (pindex_loop->GetBlockHash (), quorum_key)) {
1071
+ quorum_keys.insert (quorum_key);
1072
+ if (quorum_keys.size () >= params.keepOldKeys ) break ; // extra safety belt
1073
+ }
1074
+ pindex_loop = pindex_loop->pprev ;
1075
+ }
1076
+ for (const auto & pQuorum : ScanQuorums (params.type , pIndex, params.keepOldKeys - quorum_keys.size ())) {
1077
+ const uint256 quorum_key = MakeQuorumKey (*pQuorum);
1078
+ quorum_keys.insert (quorum_key);
1079
+ cache.insert (pQuorum->m_quorum_base_block_index ->GetBlockHash (), quorum_key);
1032
1080
}
1081
+ dbKeysToSkip.merge (quorum_keys);
1033
1082
}
1034
1083
1035
1084
if (!quorumThreadInterrupt) {
0 commit comments