Line data Source code
1 : // Copyright (c) 2018-2025 The Dash Core developers
2 : // Distributed under the MIT/X11 software license, see the accompanying
3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 :
5 : #include <llmq/signing.h>
6 :
7 : #include <llmq/commitment.h>
8 : #include <llmq/params.h>
9 : #include <llmq/quorumsman.h>
10 : #include <llmq/signhash.h>
11 :
12 : #include <chainparams.h>
13 : #include <dbwrapper.h>
14 : #include <streams.h>
15 : #include <util/system.h>
16 :
17 : #include <algorithm>
18 : #include <ranges>
19 : #include <unordered_map>
20 : #include <unordered_set>
21 :
22 : namespace llmq
23 : {
24 540 : CRecoveredSigsDb::CRecoveredSigsDb(const util::DbWrapperParams& db_params) :
25 180 : db{util::MakeDbWrapper({db_params.path / "llmq" / "recsigdb", db_params.memory, db_params.wipe, /*cache_size=*/8 << 20})}
26 180 : {
27 360 : }
28 :
29 360 : CRecoveredSigsDb::~CRecoveredSigsDb() = default;
30 :
31 0 : bool CRecoveredSigsDb::HasRecoveredSig(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash) const
32 : {
33 0 : auto k = std::make_tuple(std::string("rs_r"), llmqType, id, msgHash);
34 0 : return db->Exists(k);
35 0 : }
36 :
37 0 : bool CRecoveredSigsDb::HasRecoveredSigForId(Consensus::LLMQType llmqType, const uint256& id) const
38 : {
39 0 : auto cacheKey = std::make_pair(llmqType, id);
40 : bool ret;
41 : {
42 0 : LOCK(cs_cache);
43 0 : if (hasSigForIdCache.get(cacheKey, ret)) {
44 0 : return ret;
45 : }
46 0 : }
47 :
48 :
49 0 : auto k = std::make_tuple(std::string("rs_r"), llmqType, id);
50 0 : ret = db->Exists(k);
51 :
52 0 : LOCK(cs_cache);
53 0 : hasSigForIdCache.insert(cacheKey, ret);
54 0 : return ret;
55 0 : }
56 :
57 0 : bool CRecoveredSigsDb::HasRecoveredSigForSession(const uint256& signHash) const
58 : {
59 : bool ret;
60 : {
61 0 : LOCK(cs_cache);
62 0 : if (hasSigForSessionCache.get(signHash, ret)) {
63 0 : return ret;
64 : }
65 0 : }
66 :
67 0 : auto k = std::make_tuple(std::string("rs_s"), signHash);
68 0 : ret = db->Exists(k);
69 :
70 0 : LOCK(cs_cache);
71 0 : hasSigForSessionCache.insert(signHash, ret);
72 0 : return ret;
73 0 : }
74 :
75 0 : bool CRecoveredSigsDb::HasRecoveredSigForHash(const uint256& hash) const
76 : {
77 : bool ret;
78 : {
79 0 : LOCK(cs_cache);
80 0 : if (hasSigForHashCache.get(hash, ret)) {
81 0 : return ret;
82 : }
83 0 : }
84 :
85 0 : auto k = std::make_tuple(std::string("rs_h"), hash);
86 0 : ret = db->Exists(k);
87 :
88 0 : LOCK(cs_cache);
89 0 : hasSigForHashCache.insert(hash, ret);
90 0 : return ret;
91 0 : }
92 :
93 0 : bool CRecoveredSigsDb::ReadRecoveredSig(Consensus::LLMQType llmqType, const uint256& id, CRecoveredSig& ret) const
94 : {
95 0 : auto k = std::make_tuple(std::string("rs_r"), llmqType, id);
96 :
97 0 : CDataStream ds(SER_DISK, CLIENT_VERSION);
98 0 : if (!db->ReadDataStream(k, ds)) {
99 0 : return false;
100 : }
101 :
102 : try {
103 0 : ret.Unserialize(ds);
104 0 : return true;
105 0 : } catch (std::exception&) {
106 0 : return false;
107 0 : }
108 0 : }
109 :
110 0 : bool CRecoveredSigsDb::GetRecoveredSigByHash(const uint256& hash, CRecoveredSig& ret) const
111 : {
112 0 : auto k1 = std::make_tuple(std::string("rs_h"), hash);
113 0 : std::pair<Consensus::LLMQType, uint256> k2;
114 0 : if (!db->Read(k1, k2)) {
115 0 : return false;
116 : }
117 :
118 0 : return ReadRecoveredSig(k2.first, k2.second, ret);
119 0 : }
120 :
121 0 : bool CRecoveredSigsDb::GetRecoveredSigById(Consensus::LLMQType llmqType, const uint256& id, CRecoveredSig& ret) const
122 : {
123 0 : return ReadRecoveredSig(llmqType, id, ret);
124 : }
125 :
126 0 : void CRecoveredSigsDb::WriteRecoveredSig(const llmq::CRecoveredSig& recSig)
127 : {
128 0 : CDBBatch batch(*db);
129 :
130 0 : uint32_t curTime = GetTime<std::chrono::seconds>().count();
131 :
132 : // we put these close to each other to leverage leveldb's key compaction
133 : // this way, the second key can be used for fast HasRecoveredSig checks while the first key stores the recSig
134 0 : auto k1 = std::make_tuple(std::string("rs_r"), recSig.getLlmqType(), recSig.getId());
135 0 : auto k2 = std::make_tuple(std::string("rs_r"), recSig.getLlmqType(), recSig.getId(), recSig.getMsgHash());
136 0 : batch.Write(k1, recSig);
137 : // this key is also used to store the current time, so that we can easily get to the "rs_t" key when we have the id
138 0 : batch.Write(k2, curTime);
139 :
140 : // store by object hash
141 0 : auto k3 = std::make_tuple(std::string("rs_h"), recSig.GetHash());
142 0 : batch.Write(k3, std::make_pair(recSig.getLlmqType(), recSig.getId()));
143 :
144 : // store by signHash
145 0 : auto signHash = recSig.buildSignHash();
146 0 : auto k4 = std::make_tuple(std::string("rs_s"), signHash.Get());
147 0 : batch.Write(k4, (uint8_t)1);
148 :
149 : // store by current time. Allows fast cleanup of old recSigs
150 0 : auto k5 = std::make_tuple(std::string("rs_t"), (uint32_t)htobe32_internal(curTime), recSig.getLlmqType(), recSig.getId());
151 0 : batch.Write(k5, (uint8_t)1);
152 :
153 0 : db->WriteBatch(batch);
154 :
155 : {
156 0 : LOCK(cs_cache);
157 0 : hasSigForIdCache.insert(std::make_pair(recSig.getLlmqType(), recSig.getId()), true);
158 0 : hasSigForSessionCache.insert(signHash.Get(), true);
159 0 : hasSigForHashCache.insert(recSig.GetHash(), true);
160 0 : }
161 0 : }
162 :
163 0 : void CRecoveredSigsDb::RemoveRecoveredSig(CDBBatch& batch, Consensus::LLMQType llmqType, const uint256& id, bool deleteHashKey, bool deleteTimeKey)
164 : {
165 0 : CRecoveredSig recSig;
166 0 : if (!ReadRecoveredSig(llmqType, id, recSig)) {
167 0 : return;
168 : }
169 :
170 0 : auto signHash = recSig.buildSignHash();
171 :
172 0 : auto k1 = std::make_tuple(std::string("rs_r"), recSig.getLlmqType(), recSig.getId());
173 0 : auto k2 = std::make_tuple(std::string("rs_r"), recSig.getLlmqType(), recSig.getId(), recSig.getMsgHash());
174 0 : auto k3 = std::make_tuple(std::string("rs_h"), recSig.GetHash());
175 0 : auto k4 = std::make_tuple(std::string("rs_s"), signHash.Get());
176 0 : batch.Erase(k1);
177 0 : batch.Erase(k2);
178 0 : if (deleteHashKey) {
179 0 : batch.Erase(k3);
180 0 : batch.Erase(k4);
181 0 : }
182 :
183 0 : if (deleteTimeKey) {
184 0 : CDataStream writeTimeDs(SER_DISK, CLIENT_VERSION);
185 0 : if (db->ReadDataStream(k2, writeTimeDs)) {
186 : uint32_t writeTime;
187 0 : writeTimeDs >> writeTime;
188 0 : auto k5 = std::make_tuple(std::string("rs_t"), (uint32_t) htobe32_internal(writeTime), recSig.getLlmqType(), recSig.getId());
189 0 : batch.Erase(k5);
190 0 : }
191 0 : }
192 :
193 0 : LOCK(cs_cache);
194 0 : hasSigForIdCache.erase(std::make_pair(recSig.getLlmqType(), recSig.getId()));
195 0 : if (deleteHashKey) {
196 0 : hasSigForSessionCache.erase(signHash.Get());
197 0 : hasSigForHashCache.erase(recSig.GetHash());
198 0 : }
199 0 : }
200 :
201 : // Remove the recovered sig itself and all keys required to get from id -> recSig
202 : // This will leave the byHash and signHash key in-place so that HasRecoveredSigForHash /
203 : // late-share filtering still returns true
204 0 : void CRecoveredSigsDb::TruncateRecoveredSig(Consensus::LLMQType llmqType, const uint256& id)
205 : {
206 0 : CDBBatch batch(*db);
207 0 : RemoveRecoveredSig(batch, llmqType, id, false, false);
208 0 : db->WriteBatch(batch);
209 0 : }
210 :
211 0 : void CRecoveredSigsDb::CleanupOldRecoveredSigs(int64_t maxAge)
212 : {
213 0 : std::unique_ptr<CDBIterator> pcursor(db->NewIterator());
214 :
215 0 : auto start = std::make_tuple(std::string("rs_t"), (uint32_t)0, (Consensus::LLMQType)0, uint256());
216 0 : uint32_t endTime = (uint32_t)(GetTime<std::chrono::seconds>().count() - maxAge);
217 0 : pcursor->Seek(start);
218 :
219 0 : std::vector<std::pair<Consensus::LLMQType, uint256>> toDelete;
220 0 : std::vector<decltype(start)> toDelete2;
221 :
222 0 : while (pcursor->Valid()) {
223 0 : decltype(start) k;
224 :
225 0 : if (!pcursor->GetKey(k) || std::get<0>(k) != "rs_t") {
226 0 : break;
227 : }
228 0 : if (be32toh_internal(std::get<1>(k)) >= endTime) {
229 0 : break;
230 : }
231 :
232 0 : toDelete.emplace_back(std::get<2>(k), std::get<3>(k));
233 0 : toDelete2.emplace_back(k);
234 :
235 0 : pcursor->Next();
236 0 : }
237 0 : pcursor.reset();
238 :
239 0 : if (toDelete.empty()) {
240 0 : return;
241 : }
242 :
243 0 : CDBBatch batch(*db);
244 0 : for (const auto& e : toDelete) {
245 0 : RemoveRecoveredSig(batch, e.first, e.second, true, false);
246 :
247 0 : if (batch.SizeEstimate() >= (1 << 24)) {
248 0 : db->WriteBatch(batch);
249 0 : batch.Clear();
250 0 : }
251 : }
252 :
253 0 : for (const auto& e : toDelete2) {
254 0 : batch.Erase(e);
255 : }
256 :
257 0 : db->WriteBatch(batch);
258 :
259 0 : LogPrint(BCLog::LLMQ, "CRecoveredSigsDb::%d -- deleted %d entries\n", __func__, toDelete.size());
260 0 : }
261 :
262 0 : bool CRecoveredSigsDb::HasVotedOnId(Consensus::LLMQType llmqType, const uint256& id) const
263 : {
264 0 : auto k = std::make_tuple(std::string("rs_v"), llmqType, id);
265 0 : return db->Exists(k);
266 0 : }
267 :
268 0 : bool CRecoveredSigsDb::GetVoteForId(Consensus::LLMQType llmqType, const uint256& id, uint256& msgHashRet) const
269 : {
270 0 : auto k = std::make_tuple(std::string("rs_v"), llmqType, id);
271 0 : return db->Read(k, msgHashRet);
272 0 : }
273 :
274 0 : void CRecoveredSigsDb::WriteVoteForId(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash)
275 : {
276 0 : auto k1 = std::make_tuple(std::string("rs_v"), llmqType, id);
277 0 : auto k2 = std::make_tuple(std::string("rs_vt"), (uint32_t)htobe32_internal(GetTime<std::chrono::seconds>().count()), llmqType, id);
278 :
279 0 : CDBBatch batch(*db);
280 0 : batch.Write(k1, msgHash);
281 0 : batch.Write(k2, (uint8_t)1);
282 :
283 0 : db->WriteBatch(batch);
284 0 : }
285 :
286 0 : void CRecoveredSigsDb::CleanupOldVotes(int64_t maxAge)
287 : {
288 0 : std::unique_ptr<CDBIterator> pcursor(db->NewIterator());
289 :
290 0 : auto start = std::make_tuple(std::string("rs_vt"), (uint32_t)0, (Consensus::LLMQType)0, uint256());
291 0 : uint32_t endTime = (uint32_t)(GetTime<std::chrono::seconds>().count() - maxAge);
292 0 : pcursor->Seek(start);
293 :
294 0 : CDBBatch batch(*db);
295 0 : size_t cnt = 0;
296 0 : while (pcursor->Valid()) {
297 0 : decltype(start) k;
298 :
299 0 : if (!pcursor->GetKey(k) || std::get<0>(k) != "rs_vt") {
300 0 : break;
301 : }
302 0 : if (be32toh_internal(std::get<1>(k)) >= endTime) {
303 0 : break;
304 : }
305 :
306 0 : Consensus::LLMQType llmqType = std::get<2>(k);
307 0 : const uint256& id = std::get<3>(k);
308 :
309 0 : batch.Erase(k);
310 0 : batch.Erase(std::make_tuple(std::string("rs_v"), llmqType, id));
311 :
312 0 : cnt++;
313 :
314 0 : pcursor->Next();
315 0 : }
316 0 : pcursor.reset();
317 :
318 0 : if (cnt == 0) {
319 0 : return;
320 : }
321 :
322 0 : db->WriteBatch(batch);
323 :
324 0 : LogPrint(BCLog::LLMQ, "CRecoveredSigsDb::%d -- deleted %d entries\n", __func__, cnt);
325 0 : }
326 :
327 : //////////////////
328 :
329 540 : CSigningManager::CSigningManager(const CQuorumManager& _qman, const util::DbWrapperParams& db_params,
330 : int64_t max_recsigs_age) :
331 180 : db{db_params},
332 180 : qman{_qman},
333 180 : m_max_recsigs_age{max_recsigs_age}
334 180 : {
335 360 : }
336 :
337 360 : CSigningManager::~CSigningManager() = default;
338 :
339 0 : bool CSigningManager::AlreadyHave(const CInv& inv) const
340 : {
341 0 : if (inv.type != MSG_QUORUM_RECOVERED_SIG) {
342 0 : return false;
343 : }
344 : {
345 0 : LOCK(cs_pending);
346 0 : if (pendingReconstructedRecoveredSigs.count(inv.hash)) {
347 0 : return true;
348 : }
349 0 : }
350 :
351 0 : return db.HasRecoveredSigForHash(inv.hash);
352 0 : }
353 :
354 0 : bool CSigningManager::GetRecoveredSigForGetData(const uint256& hash, CRecoveredSig& ret) const
355 : {
356 0 : if (!db.GetRecoveredSigByHash(hash, ret)) {
357 0 : return false;
358 : }
359 0 : if (!IsQuorumActive(ret.getLlmqType(), qman, ret.getQuorumHash())) {
360 : // we don't want to propagate sigs from inactive quorums
361 0 : return false;
362 : }
363 0 : return true;
364 0 : }
365 :
366 0 : void CSigningManager::VerifyAndProcessRecoveredSig(NodeId from, std::shared_ptr<CRecoveredSig> recoveredSig)
367 : {
368 0 : auto llmq_type = recoveredSig->getLlmqType();
369 0 : auto quorum = qman.GetQuorum(llmq_type, recoveredSig->getQuorumHash());
370 :
371 0 : if (!quorum) {
372 0 : LogPrint(BCLog::LLMQ, "CSigningManager::%s -- quorum %s not found\n", __func__,
373 : recoveredSig->getQuorumHash().ToString());
374 0 : return;
375 : }
376 0 : if (!IsQuorumActive(llmq_type, qman, quorum->qc->quorumHash)) {
377 0 : return;
378 : }
379 :
380 : // It's important to only skip seen *valid* sig shares here. See comment for CBatchedSigShare
381 : // We don't receive recovered sigs in batches, but we do batched verification per node on these
382 0 : if (db.HasRecoveredSigForHash(recoveredSig->GetHash())) {
383 0 : return;
384 : }
385 :
386 0 : LogPrint(BCLog::LLMQ, "CSigningManager::%s -- signHash=%s, id=%s, msgHash=%s, node=%d\n", __func__,
387 : recoveredSig->buildSignHash().ToString(), recoveredSig->getId().ToString(), recoveredSig->getMsgHash().ToString(), from);
388 :
389 0 : LOCK(cs_pending);
390 0 : if (pendingReconstructedRecoveredSigs.count(recoveredSig->GetHash())) {
391 : // no need to perform full verification
392 0 : LogPrint(BCLog::LLMQ, "CSigningManager::%s -- already pending reconstructed sig, signHash=%s, id=%s, msgHash=%s, node=%d\n", __func__,
393 : recoveredSig->buildSignHash().ToString(), recoveredSig->getId().ToString(), recoveredSig->getMsgHash().ToString(), from);
394 0 : return;
395 : }
396 :
397 0 : pendingRecoveredSigs[from].emplace_back(std::move(recoveredSig));
398 0 : }
399 :
400 0 : bool CSigningManager::CollectPendingRecoveredSigsToVerify(
401 : size_t maxUniqueSessions, std::unordered_map<NodeId, std::list<std::shared_ptr<const CRecoveredSig>>>& retSigShares,
402 : std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CBLSPublicKey, StaticSaltedHasher>& ret_pubkeys)
403 : {
404 0 : bool more_work{false};
405 :
406 : {
407 0 : LOCK(cs_pending);
408 0 : if (pendingRecoveredSigs.empty()) {
409 0 : return false;
410 : }
411 :
412 : // TODO: refactor it to remove duplicated code with `CSigSharesManager::CollectPendingSigSharesToVerify`
413 0 : std::unordered_set<std::pair<NodeId, uint256>, StaticSaltedHasher> uniqueSignHashes;
414 0 : IterateNodesRandom(pendingRecoveredSigs, [&]() {
415 0 : return uniqueSignHashes.size() < maxUniqueSessions;
416 0 : }, [&](NodeId nodeId, std::list<std::shared_ptr<const CRecoveredSig>>& ns) {
417 0 : if (ns.empty()) {
418 0 : return false;
419 : }
420 0 : auto& recSig = *ns.begin();
421 :
422 0 : bool alreadyHave = db.HasRecoveredSigForHash(recSig->GetHash());
423 0 : if (!alreadyHave) {
424 0 : uniqueSignHashes.emplace(nodeId, recSig->buildSignHash().Get());
425 0 : retSigShares[nodeId].emplace_back(recSig);
426 0 : }
427 0 : ns.erase(ns.begin());
428 0 : return !ns.empty();
429 0 : }, rnd);
430 :
431 0 : if (retSigShares.empty()) {
432 0 : return false;
433 : }
434 :
435 0 : more_work = std::any_of(pendingRecoveredSigs.begin(), pendingRecoveredSigs.end(),
436 0 : [](const auto& p) { return !p.second.empty(); }) ||
437 0 : !pendingReconstructedRecoveredSigs.empty();
438 0 : }
439 :
440 0 : for (auto& [nodeId, v] : retSigShares) {
441 0 : for (auto it = v.begin(); it != v.end();) {
442 0 : const auto& recSig = *it;
443 :
444 0 : auto llmqType = recSig->getLlmqType();
445 0 : auto quorumKey = std::make_pair(recSig->getLlmqType(), recSig->getQuorumHash());
446 0 : if (!ret_pubkeys.count(quorumKey)) {
447 0 : auto quorum = qman.GetQuorum(llmqType, recSig->getQuorumHash());
448 0 : if (!quorum) {
449 0 : LogPrint(BCLog::LLMQ, "CSigningManager::%s -- quorum %s not found, node=%d\n", __func__,
450 : recSig->getQuorumHash().ToString(), nodeId);
451 0 : it = v.erase(it);
452 0 : continue;
453 : }
454 0 : if (!IsQuorumActive(llmqType, qman, quorum->qc->quorumHash)) {
455 0 : LogPrint(BCLog::LLMQ, "CSigningManager::%s -- quorum %s not active anymore, node=%d\n", __func__,
456 : recSig->getQuorumHash().ToString(), nodeId);
457 0 : it = v.erase(it);
458 0 : continue;
459 : }
460 :
461 0 : ret_pubkeys.emplace(quorumKey, quorum->qc->quorumPublicKey);
462 0 : }
463 :
464 0 : ++it;
465 : }
466 : }
467 :
468 0 : return more_work;
469 0 : }
470 :
471 0 : Uint256HashMap<std::shared_ptr<const CRecoveredSig>> CSigningManager::FetchPendingReconstructed()
472 : {
473 0 : Uint256HashMap<std::shared_ptr<const CRecoveredSig>> tmp;
474 0 : WITH_LOCK(cs_pending, swap(tmp, pendingReconstructedRecoveredSigs));
475 0 : return tmp;
476 0 : }
477 :
478 : // signature must be verified already
479 0 : bool CSigningManager::ProcessRecoveredSig(const std::shared_ptr<const CRecoveredSig>& recoveredSig)
480 : {
481 0 : auto llmqType = recoveredSig->getLlmqType();
482 :
483 0 : if (db.HasRecoveredSigForHash(recoveredSig->GetHash())) {
484 0 : return false;
485 : }
486 :
487 0 : auto signHash = recoveredSig->buildSignHash();
488 :
489 0 : LogPrint(BCLog::LLMQ, "CSigningManager::%s -- valid recSig. signHash=%s, id=%s, msgHash=%s\n", __func__,
490 : signHash.ToString(), recoveredSig->getId().ToString(), recoveredSig->getMsgHash().ToString());
491 :
492 0 : if (db.HasRecoveredSigForId(llmqType, recoveredSig->getId())) {
493 0 : CRecoveredSig otherRecoveredSig;
494 0 : if (db.GetRecoveredSigById(llmqType, recoveredSig->getId(), otherRecoveredSig)) {
495 0 : auto otherSignHash = otherRecoveredSig.buildSignHash();
496 0 : if (signHash.Get() != otherSignHash.Get()) {
497 : // this should really not happen, as each masternode is participating in only one vote,
498 : // even if it's a member of multiple quorums. so a majority is only possible on one quorum and one msgHash per id
499 0 : LogPrintf("CSigningManager::%s -- conflicting recoveredSig for signHash=%s, id=%s, msgHash=%s, otherSignHash=%s\n", __func__,
500 : signHash.ToString(), recoveredSig->getId().ToString(), recoveredSig->getMsgHash().ToString(), otherSignHash.ToString());
501 0 : } else {
502 : // Looks like we're trying to process a recSig that is already known. This might happen if the same
503 : // recSig comes in through regular QRECSIG messages and at the same time through some other message
504 : // which allowed to reconstruct a recSig (e.g. ISLOCK). In this case, just bail out.
505 : }
506 0 : return false;
507 : } else {
508 : // This case is very unlikely. It can only happen when cleanup caused this specific recSig to vanish
509 : // between the HasRecoveredSigForId and GetRecoveredSigById call. If that happens, treat it as if we
510 : // never had that recSig
511 : }
512 0 : }
513 :
514 0 : db.WriteRecoveredSig(*recoveredSig);
515 0 : WITH_LOCK(cs_pending, pendingReconstructedRecoveredSigs.erase(recoveredSig->GetHash()));
516 :
517 0 : return true;
518 0 : }
519 :
520 0 : std::vector<CRecoveredSigsListener*> CSigningManager::GetListeners() const
521 : {
522 0 : LOCK(cs_listeners);
523 0 : return recoveredSigsListeners;
524 0 : }
525 :
526 0 : void CSigningManager::PushReconstructedRecoveredSig(const std::shared_ptr<const llmq::CRecoveredSig>& recoveredSig)
527 : {
528 0 : LOCK(cs_pending);
529 0 : pendingReconstructedRecoveredSigs.emplace(std::piecewise_construct, std::forward_as_tuple(recoveredSig->GetHash()), std::forward_as_tuple(recoveredSig));
530 0 : }
531 :
532 0 : void CSigningManager::TruncateRecoveredSig(Consensus::LLMQType llmqType, const uint256& id)
533 : {
534 0 : db.TruncateRecoveredSig(llmqType, id);
535 0 : }
536 :
537 0 : void CSigningManager::Cleanup()
538 : {
539 0 : db.CleanupOldRecoveredSigs(m_max_recsigs_age);
540 0 : db.CleanupOldVotes(m_max_recsigs_age);
541 0 : }
542 :
543 0 : void CSigningManager::RegisterRecoveredSigsListener(CRecoveredSigsListener* l)
544 : {
545 0 : LOCK(cs_listeners);
546 0 : recoveredSigsListeners.emplace_back(l);
547 0 : }
548 :
549 0 : void CSigningManager::UnregisterRecoveredSigsListener(CRecoveredSigsListener* l)
550 : {
551 0 : LOCK(cs_listeners);
552 0 : auto itRem = std::remove(recoveredSigsListeners.begin(), recoveredSigsListeners.end(), l);
553 0 : recoveredSigsListeners.erase(itRem, recoveredSigsListeners.end());
554 0 : }
555 :
556 0 : bool CSigningManager::HasRecoveredSig(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash) const
557 : {
558 0 : return db.HasRecoveredSig(llmqType, id, msgHash);
559 : }
560 :
561 0 : bool CSigningManager::HasRecoveredSigForId(Consensus::LLMQType llmqType, const uint256& id) const
562 : {
563 0 : return db.HasRecoveredSigForId(llmqType, id);
564 : }
565 :
566 0 : bool CSigningManager::HasRecoveredSigForSession(const uint256& signHash) const
567 : {
568 0 : return db.HasRecoveredSigForSession(signHash);
569 : }
570 :
571 0 : bool CSigningManager::GetRecoveredSigForId(Consensus::LLMQType llmqType, const uint256& id, llmq::CRecoveredSig& retRecSig) const
572 : {
573 0 : if (!db.GetRecoveredSigById(llmqType, id, retRecSig)) {
574 0 : return false;
575 : }
576 0 : return true;
577 0 : }
578 :
579 0 : bool CSigningManager::IsConflicting(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash) const
580 : {
581 0 : if (!db.HasRecoveredSigForId(llmqType, id)) {
582 : // no recovered sig present, so no conflict
583 0 : return false;
584 : }
585 :
586 0 : if (!db.HasRecoveredSig(llmqType, id, msgHash)) {
587 : // recovered sig is present, but not for the given msgHash. That's a conflict!
588 0 : return true;
589 : }
590 :
591 : // all good
592 0 : return false;
593 0 : }
594 :
595 0 : bool CSigningManager::GetVoteForId(Consensus::LLMQType llmqType, const uint256& id, uint256& msgHashRet) const
596 : {
597 0 : return db.GetVoteForId(llmqType, id, msgHashRet);
598 : }
599 :
600 0 : SignHash CSigBase::buildSignHash() const { return SignHash(llmqType, quorumHash, id, msgHash); }
601 :
602 :
603 0 : bool IsQuorumActive(Consensus::LLMQType llmqType, const CQuorumManager& qman, const uint256& quorumHash)
604 : {
605 : // sig shares and recovered sigs are only accepted from recent/active quorums
606 : // we allow one more active quorum as specified in consensus, as otherwise there is a small window where things could
607 : // fail while we are on the brink of a new quorum
608 0 : const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
609 0 : assert(llmq_params_opt.has_value());
610 0 : auto quorums = qman.ScanQuorums(llmqType, llmq_params_opt->keepOldConnections);
611 0 : return std::ranges::any_of(quorums, [&quorumHash](const auto& q) { return q->qc->quorumHash == quorumHash; });
612 0 : }
613 :
614 : } // namespace llmq
|