LCOV - code coverage report
Current view: top level - src/llmq - signing.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 12 354 3.4 %
Date: 2026-06-25 07:23:51 Functions: 8 49 16.3 %

          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

Generated by: LCOV version 1.16