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 170 : static uint256 MakeQuorumKey(const CQuorum& q)
34 : {
35 170 : CHashWriter hw(SER_NETWORK, 0);
36 170 : hw << (uint8_t)q.params.type;
37 170 : hw << q.qc.quorumHash;
38 680 : for (const auto& dmn : q.members) {
39 510 : hw << dmn->proTxHash;
40 : }
41 170 : return hw.GetHash();
42 : }
43 :
44 171 : CQuorum::~CQuorum()
45 : {
46 : // most likely the thread is already done
47 124 : 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 171 : if (cachePopulatorThread.joinable() && cachePopulatorThread.get_id() != std::this_thread::get_id()) {
51 47 : cachePopulatorThread.join();
52 : }
53 124 : }
54 :
55 124 : void CQuorum::Init(const CFinalCommitment& _qc, const CBlockIndex* _pindexQuorum, const uint256& _minedBlockHash, const std::vector<CDeterministicMNCPtr>& _members)
56 : {
57 124 : qc = _qc;
58 124 : pindexQuorum = _pindexQuorum;
59 124 : members = _members;
60 124 : minedBlockHash = _minedBlockHash;
61 124 : }
62 :
63 7023 : bool CQuorum::IsMember(const uint256& proTxHash) const
64 : {
65 20250 : for (auto& dmn : members) {
66 17160 : if (dmn->proTxHash == proTxHash) {
67 3933 : return true;
68 : }
69 : }
70 3090 : return false;
71 : }
72 :
73 3305 : bool CQuorum::IsValidMember(const uint256& proTxHash) const
74 : {
75 8795 : for (size_t i = 0; i < members.size(); i++) {
76 7595 : if (members[i]->proTxHash == proTxHash) {
77 2105 : return qc.validMembers[i];
78 : }
79 : }
80 : return false;
81 : }
82 :
83 720 : CBLSPublicKey CQuorum::GetPubKeyShare(size_t memberIdx) const
84 : {
85 720 : if (quorumVvec == nullptr || memberIdx >= members.size() || !qc.validMembers[memberIdx]) {
86 0 : return CBLSPublicKey();
87 : }
88 720 : auto& m = members[memberIdx];
89 720 : return blsCache.BuildPubKeyShare(m->proTxHash, quorumVvec, CBLSId(m->proTxHash));
90 : }
91 :
92 968 : CBLSSecretKey CQuorum::GetSkShare() const
93 : {
94 968 : return skShare;
95 : }
96 :
97 968 : int CQuorum::GetMemberIndex(const uint256& proTxHash) const
98 : {
99 1744 : for (size_t i = 0; i < members.size(); i++) {
100 1744 : if (members[i]->proTxHash == proTxHash) {
101 968 : return (int)i;
102 : }
103 : }
104 : return -1;
105 : }
106 :
107 46 : void CQuorum::WriteContributions(CEvoDB& evoDb)
108 : {
109 46 : uint256 dbKey = MakeQuorumKey(*this);
110 :
111 46 : if (quorumVvec != nullptr) {
112 92 : evoDb.GetRawDB().Write(std::make_pair(DB_QUORUM_QUORUM_VVEC, dbKey), *quorumVvec);
113 : }
114 46 : if (skShare.IsValid()) {
115 92 : evoDb.GetRawDB().Write(std::make_pair(DB_QUORUM_SK_SHARE, dbKey), skShare);
116 : }
117 46 : }
118 :
119 124 : bool CQuorum::ReadContributions(CEvoDB& evoDb)
120 : {
121 124 : uint256 dbKey = MakeQuorumKey(*this);
122 :
123 248 : BLSVerificationVector qv;
124 124 : if (evoDb.Read(std::make_pair(DB_QUORUM_QUORUM_VVEC, dbKey), qv)) {
125 1 : 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 1 : evoDb.Read(std::make_pair(DB_QUORUM_SK_SHARE, dbKey), skShare);
133 :
134 1 : return true;
135 : }
136 :
137 47 : void CQuorum::StartCachePopulatorThread(std::shared_ptr<CQuorum> _this)
138 : {
139 47 : if (_this->quorumVvec == nullptr) {
140 0 : return;
141 : }
142 :
143 94 : cxxtimer::Timer t(true);
144 47 : 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 376 : _this->cachePopulatorThread = std::thread(&TraceThread<std::function<void()> >, "quorum-cachepop", [_this, t] {
149 188 : for (size_t i = 0; i < _this->members.size() && !_this->stopCachePopulatorThread && !ShutdownRequested(); i++) {
150 141 : if (_this->qc.validMembers[i]) {
151 127 : _this->GetPubKeyShare(i);
152 : }
153 : }
154 47 : LogPrint(BCLog::LLMQ, "CQuorum::StartCachePopulatorThread -- done. time=%d\n", t.count());
155 47 : });
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 41174 : void CQuorumManager::UpdatedBlockTip(const CBlockIndex* pindexNew, bool fInitialDownload)
167 : {
168 41174 : if (!g_tiertwo_sync_state.IsBlockchainSynced() || !activeMasternodeManager) {
169 : return;
170 : }
171 :
172 8592 : for (auto& p : Params().GetConsensus().llmqs) {
173 4296 : const auto& params = Params().GetConsensus().llmqs.at(p.first);
174 :
175 4296 : auto lastQuorums = ScanQuorums(p.first, pindexNew, (size_t)params.keepOldConnections);
176 :
177 4296 : llmq::EnsureLatestQuorumConnections(p.first, pindexNew, activeMasternodeManager->GetProTx(), lastQuorums);
178 : }
179 : }
180 :
181 :
182 124 : bool CQuorumManager::BuildQuorumFromCommitment(const CFinalCommitment& qc, const CBlockIndex* pindexQuorum, const uint256& minedBlockHash, std::shared_ptr<CQuorum>& quorum) const
183 : {
184 124 : assert(pindexQuorum);
185 124 : assert(qc.quorumHash == pindexQuorum->GetBlockHash());
186 :
187 124 : auto members = deterministicMNManager->GetAllQuorumMembers((Consensus::LLMQType)qc.llmqType, pindexQuorum);
188 124 : quorum->Init(qc, pindexQuorum, minedBlockHash, members);
189 :
190 124 : bool hasValidVvec = false;
191 124 : if (quorum->ReadContributions(evoDb)) {
192 : hasValidVvec = true;
193 : } else {
194 123 : if (BuildQuorumContributions(qc, quorum)) {
195 46 : quorum->WriteContributions(evoDb);
196 : hasValidVvec = true;
197 : } else {
198 77 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- quorum.ReadContributions and BuildQuorumContributions for block %s failed\n", __func__, qc.quorumHash.ToString());
199 : }
200 : }
201 :
202 77 : 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 94 : CQuorum::StartCachePopulatorThread(quorum);
207 : }
208 :
209 124 : return true;
210 : }
211 :
212 123 : bool CQuorumManager::BuildQuorumContributions(const CFinalCommitment& fqc, std::shared_ptr<CQuorum>& quorum) const
213 : {
214 246 : std::vector<uint16_t> memberIndexes;
215 123 : std::vector<BLSVerificationVectorPtr> vvecs;
216 0 : BLSSecretKeyVector skContributions;
217 123 : if (!dkgManager.GetVerifiedContributions((Consensus::LLMQType)fqc.llmqType, quorum->pindexQuorum, fqc.validMembers, memberIndexes, vvecs, skContributions)) {
218 : return false;
219 : }
220 :
221 123 : BLSVerificationVectorPtr quorumVvec;
222 246 : CBLSSecretKey skShare;
223 :
224 246 : cxxtimer::Timer t2(true);
225 246 : quorumVvec = blsWorker.BuildQuorumVerificationVector(vvecs);
226 123 : if (quorumVvec == nullptr) {
227 77 : 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 77 : return false;
231 : }
232 92 : skShare = blsWorker.AggregateSecretKeys(skContributions);
233 46 : 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 46 : t2.stop();
239 :
240 46 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- built quorum vvec and skShare. time=%d\n", __func__, t2.count());
241 :
242 46 : quorum->quorumVvec = quorumVvec;
243 46 : quorum->skShare = skShare;
244 :
245 46 : return true;
246 : }
247 :
248 15960 : bool CQuorumManager::HasQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash)
249 : {
250 15960 : return quorumBlockProcessor->HasMinedCommitment(llmqType, quorumHash);
251 : }
252 :
253 5453 : std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType, size_t maxCount)
254 : {
255 16359 : const CBlockIndex* pindex = WITH_LOCK(cs_main, return chainActive.Tip());
256 5453 : return ScanQuorums(llmqType, pindex, maxCount);
257 : }
258 :
259 13762 : std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType, const CBlockIndex* pindexStart, size_t maxCount)
260 : {
261 13762 : if (pindexStart == nullptr || maxCount == 0) {
262 13762 : return {};
263 : }
264 :
265 13762 : bool fCacheExists{false};
266 13762 : void* pIndexScanCommitments{(void*)pindexStart};
267 13762 : size_t nScanCommitments{maxCount};
268 13762 : std::vector<CQuorumCPtr> vecResultQuorums;
269 :
270 13762 : {
271 13762 : LOCK(quorumsCacheCs);
272 13762 : auto& cache = scanQuorumsCache.at(llmqType);
273 13762 : fCacheExists = cache.get(pindexStart->GetBlockHash(), vecResultQuorums);
274 13762 : if (fCacheExists) {
275 : // We have exactly what requested so just return it
276 6010 : if (vecResultQuorums.size() == maxCount) {
277 7140 : return vecResultQuorums;
278 : }
279 : // If we have more cached than requested return only a subvector
280 2683 : if (vecResultQuorums.size() > maxCount) {
281 243 : 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 2440 : if (vecResultQuorums.size() > 0) {
286 2440 : nScanCommitments -= vecResultQuorums.size();
287 2440 : 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 11196 : 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 23954 : auto quorumIndexes = quorumBlockProcessor->GetMinedCommitmentsUntilBlock(llmqType, (const CBlockIndex*)pIndexScanCommitments, nScanCommitments);
296 10192 : vecResultQuorums.reserve(vecResultQuorums.size() + quorumIndexes.size());
297 :
298 21995 : for (auto& quorumIndex : quorumIndexes) {
299 11803 : assert(quorumIndex);
300 23606 : auto quorum = GetQuorum(llmqType, quorumIndex);
301 11803 : assert(quorum != nullptr);
302 11803 : vecResultQuorums.emplace_back(quorum);
303 : }
304 :
305 10192 : size_t nCountResult{vecResultQuorums.size()};
306 10192 : if (nCountResult > 0 && !fCacheExists) {
307 10584 : LOCK(quorumsCacheCs);
308 : // Don't cache more than cache.max_size() elements
309 5292 : auto& cache = scanQuorumsCache.at(llmqType);
310 5292 : size_t nCacheEndIndex = std::min(nCountResult, cache.max_size());
311 10584 : cache.emplace(pindexStart->GetBlockHash(), {vecResultQuorums.begin(), vecResultQuorums.begin() + nCacheEndIndex});
312 : }
313 : // Don't return more than nCountRequested elements
314 10192 : size_t nResultEndIndex = std::min(nCountResult, maxCount);
315 10192 : return {vecResultQuorums.begin(), vecResultQuorums.begin() + nResultEndIndex};
316 : }
317 :
318 4157 : CQuorumCPtr CQuorumManager::GetQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash)
319 : {
320 4157 : CBlockIndex* pindexQuorum;
321 4157 : {
322 4157 : LOCK(cs_main);
323 4157 : pindexQuorum = LookupBlockIndex(quorumHash);
324 :
325 4157 : if (!pindexQuorum) {
326 0 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- block %s not found.\n", __func__, quorumHash.ToString());
327 0 : return nullptr;
328 : }
329 : }
330 4157 : return GetQuorum(llmqType, pindexQuorum);
331 : }
332 :
333 15960 : CQuorumCPtr CQuorumManager::GetQuorum(Consensus::LLMQType llmqType, const CBlockIndex* pindexQuorum)
334 : {
335 15960 : assert(pindexQuorum);
336 :
337 15960 : 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 15960 : if (!HasQuorum(llmqType, quorumHash)) {
341 0 : return nullptr;
342 : }
343 :
344 31920 : LOCK(quorumsCacheCs);
345 15960 : CQuorumCPtr pQuorum;
346 15960 : if (mapQuorumsCache.at(llmqType).get(quorumHash, pQuorum)) {
347 15836 : return pQuorum;
348 : }
349 :
350 16084 : CFinalCommitment qc;
351 124 : uint256 retMinedBlockHash;
352 124 : if (!quorumBlockProcessor->GetMinedCommitment(llmqType, quorumHash, qc, retMinedBlockHash)) {
353 0 : return nullptr;
354 : }
355 :
356 124 : auto& params = Params().GetConsensus().llmqs.at(llmqType);
357 :
358 248 : auto quorum = std::make_shared<CQuorum>(params, blsWorker);
359 124 : if (!BuildQuorumFromCommitment(qc, pindexQuorum, retMinedBlockHash, quorum)) {
360 0 : return nullptr;
361 : }
362 :
363 248 : mapQuorumsCache.at(llmqType).emplace(quorumHash, quorum);
364 :
365 124 : return quorum;
366 : }
367 :
368 : } // namespace llmq
|