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