LCOV - code coverage report
Current view: top level - src/llmq - quorums_signing.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 133 452 29.4 %
Date: 2026-08-09 10:51:41 Functions: 16 43 37.2 %

          Line data    Source code
       1             : // Copyright (c) 2018-2022 The Dash Core developers
       2             : // Copyright (c) 2023 The PIVX Core 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_signing.h"
       7             : #include "clientversion.h"
       8             : #include "netaddress.h"
       9             : #include "quorums_signing_shares.h"
      10             : #include "quorums_utils.h"
      11             : 
      12             : #include "activemasternode.h"
      13             : #include "bls/bls_batchverifier.h"
      14             : #include "cxxtimer.h"
      15             : #include "net_processing.h"
      16             : #include "validation.h"
      17             : 
      18             : #include <algorithm>
      19             : #include <limits>
      20             : #include <unordered_set>
      21             : 
      22             : namespace llmq
      23             : {
      24             : 
      25             : std::unique_ptr<CSigningManager> quorumSigningManager{nullptr};
      26             : 
      27         413 : CRecoveredSigsDb::CRecoveredSigsDb(CDBWrapper& _db) : db(_db)
      28             : {
      29         826 :     if (Params().NetworkIDString() == CBaseChainParams::TESTNET) {
      30             :         // TODO this can be completely removed after some time (when we're pretty sure the conversion has been run on most testnet MNs)
      31           1 :         if (db.Exists(std::string("rs_upgraded"))) {
      32             :             return;
      33             :         }
      34             : 
      35           1 :         ConvertInvalidTimeKeys();
      36           1 :         AddVoteTimeKeys();
      37             : 
      38           2 :         db.Write(std::string("rs_upgraded"), (uint8_t)1);
      39             :     }
      40             : }
      41             : 
      42             : // This converts time values in "rs_t" from host endianness to big endianness, which is required to have proper ordering of the keys
      43           1 : void CRecoveredSigsDb::ConvertInvalidTimeKeys()
      44             : {
      45           1 :     LogPrintf("CRecoveredSigsDb::%s -- converting invalid rs_t keys\n", __func__);
      46             : 
      47           1 :     std::unique_ptr<CDBIterator> pcursor(db.NewIterator());
      48             : 
      49           4 :     auto start = std::make_tuple(std::string("rs_t"), (uint32_t)0, (uint8_t)0, uint256());
      50           1 :     pcursor->Seek(start);
      51             : 
      52           2 :     CDBBatch batch(CLIENT_VERSION | ADDRV2_FORMAT);
      53           1 :     size_t cnt = 0;
      54           1 :     while (pcursor->Valid()) {
      55           0 :         decltype(start) k;
      56             : 
      57           0 :         if (!pcursor->GetKey(k) || std::get<0>(k) != "rs_t") {
      58             :             break;
      59             :         }
      60             : 
      61           0 :         batch.Erase(k);
      62           0 :         std::get<1>(k) = htobe32(std::get<1>(k));
      63           0 :         batch.Write(k, (uint8_t)1);
      64             : 
      65           0 :         cnt++;
      66             : 
      67           0 :         pcursor->Next();
      68             :     }
      69           1 :     pcursor.reset();
      70             : 
      71           1 :     db.WriteBatch(batch);
      72             : 
      73           1 :     LogPrintf("CRecoveredSigsDb::%s -- converted %d invalid rs_t keys\n", __func__, cnt);
      74           1 : }
      75             : 
      76             : // This adds rs_vt keys for every rs_v entry to the DB. The time in the key is set to the current time.
      77             : // This causes cleanup of all these votes a week later.
      78           1 : void CRecoveredSigsDb::AddVoteTimeKeys()
      79             : {
      80           1 :     LogPrintf("CRecoveredSigsDb::%s -- adding rs_vt keys with current time\n", __func__);
      81             : 
      82           1 :     auto curTime = GetAdjustedTime();
      83             : 
      84           1 :     std::unique_ptr<CDBIterator> pcursor(db.NewIterator());
      85             : 
      86           4 :     auto start = std::make_tuple(std::string("rs_v"), (uint8_t)0, uint256());
      87           1 :     pcursor->Seek(start);
      88             : 
      89           2 :     CDBBatch batch(CLIENT_VERSION | ADDRV2_FORMAT);
      90           1 :     size_t cnt = 0;
      91           1 :     while (pcursor->Valid()) {
      92           0 :         decltype(start) k;
      93             : 
      94           0 :         if (!pcursor->GetKey(k) || std::get<0>(k) != "rs_v") {
      95             :             break;
      96             :         }
      97             : 
      98           0 :         uint8_t llmqType = std::get<1>(k);
      99           0 :         const uint256& id = std::get<2>(k);
     100             : 
     101           0 :         auto k2 = std::make_tuple(std::string("rs_vt"), (uint32_t)htobe32(curTime), llmqType, id);
     102           0 :         batch.Write(k2, (uint8_t)1);
     103             : 
     104           0 :         cnt++;
     105             : 
     106           0 :         pcursor->Next();
     107             :     }
     108           1 :     pcursor.reset();
     109             : 
     110           1 :     db.WriteBatch(batch);
     111             : 
     112           1 :     LogPrintf("CRecoveredSigsDb::%s -- added %d rs_vt entries\n", __func__, cnt);
     113           1 : }
     114             : 
     115           0 : bool CRecoveredSigsDb::HasRecoveredSig(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash)
     116             : {
     117           0 :     auto k = std::make_tuple(std::string("rs_r"), (uint8_t)llmqType, id, msgHash);
     118           0 :     return db.Exists(k);
     119             : }
     120             : 
     121           6 : bool CRecoveredSigsDb::HasRecoveredSigForId(Consensus::LLMQType llmqType, const uint256& id)
     122             : {
     123           6 :     auto cacheKey = std::make_pair(llmqType, id);
     124           6 :     bool ret;
     125           6 :     {
     126           6 :         LOCK(cs);
     127           6 :         if (hasSigForIdCache.get(cacheKey, ret)) {
     128           0 :             return ret;
     129             :         }
     130             :     }
     131             : 
     132             : 
     133          18 :     auto k = std::make_tuple(std::string("rs_r"), (uint8_t)llmqType, id);
     134           6 :     ret = db.Exists(k);
     135             : 
     136          12 :     LOCK(cs);
     137           6 :     hasSigForIdCache.insert(cacheKey, ret);
     138           6 :     return ret;
     139             : }
     140             : 
     141           0 : bool CRecoveredSigsDb::HasRecoveredSigForSession(const uint256& signHash)
     142             : {
     143           0 :     bool ret;
     144           0 :     {
     145           0 :         LOCK(cs);
     146           0 :         if (hasSigForSessionCache.get(signHash, ret)) {
     147           0 :             return ret;
     148             :         }
     149             :     }
     150             : 
     151           0 :     auto k = std::make_tuple(std::string("rs_s"), signHash);
     152           0 :     ret = db.Exists(k);
     153             : 
     154           0 :     LOCK(cs);
     155           0 :     hasSigForSessionCache.insert(signHash, ret);
     156           0 :     return ret;
     157             : }
     158             : 
     159           0 : bool CRecoveredSigsDb::HasRecoveredSigForHash(const uint256& hash)
     160             : {
     161           0 :     bool ret;
     162           0 :     {
     163           0 :         LOCK(cs);
     164           0 :         if (hasSigForHashCache.get(hash, ret)) {
     165           0 :             return ret;
     166             :         }
     167             :     }
     168             : 
     169           0 :     auto k = std::make_tuple(std::string("rs_h"), hash);
     170           0 :     ret = db.Exists(k);
     171             : 
     172           0 :     LOCK(cs);
     173           0 :     hasSigForHashCache.insert(hash, ret);
     174           0 :     return ret;
     175             : }
     176             : 
     177           0 : bool CRecoveredSigsDb::ReadRecoveredSig(Consensus::LLMQType llmqType, const uint256& id, CRecoveredSig& ret)
     178             : {
     179           0 :     auto k = std::make_tuple(std::string("rs_r"), (uint8_t)llmqType, id);
     180             : 
     181           0 :     CDataStream ds(SER_DISK, CLIENT_VERSION);
     182           0 :     if (!db.ReadDataStream(k, ds)) {
     183             :         return false;
     184             :     }
     185             : 
     186           0 :     try {
     187           0 :         ret.Unserialize(ds);
     188             :         return true;
     189           0 :     } catch (std::exception&) {
     190           0 :         return false;
     191             :     }
     192             : }
     193             : 
     194           0 : bool CRecoveredSigsDb::GetRecoveredSigByHash(const uint256& hash, CRecoveredSig& ret)
     195             : {
     196           0 :     auto k1 = std::make_tuple(std::string("rs_h"), hash);
     197           0 :     std::pair<uint8_t, uint256> k2;
     198           0 :     if (!db.Read(k1, k2)) {
     199             :         return false;
     200             :     }
     201             : 
     202           0 :     return ReadRecoveredSig((Consensus::LLMQType)k2.first, k2.second, ret);
     203             : }
     204             : 
     205           0 : bool CRecoveredSigsDb::GetRecoveredSigById(Consensus::LLMQType llmqType, const uint256& id, CRecoveredSig& ret)
     206             : {
     207           0 :     return ReadRecoveredSig(llmqType, id, ret);
     208             : }
     209             : 
     210           0 : void CRecoveredSigsDb::WriteRecoveredSig(const llmq::CRecoveredSig& recSig)
     211             : {
     212           0 :     CDBBatch batch(CLIENT_VERSION | ADDRV2_FORMAT);
     213             : 
     214           0 :     uint32_t curTime = GetAdjustedTime();
     215             : 
     216             :     // we put these close to each other to leverage leveldb's key compaction
     217             :     // this way, the second key can be used for fast HasRecoveredSig checks while the first key stores the recSig
     218           0 :     auto k1 = std::make_tuple(std::string("rs_r"), recSig.llmqType, recSig.id);
     219           0 :     auto k2 = std::make_tuple(std::string("rs_r"), recSig.llmqType, recSig.id, recSig.msgHash);
     220           0 :     batch.Write(k1, recSig);
     221             :     // 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
     222           0 :     batch.Write(k2, curTime);
     223             : 
     224             :     // store by object hash
     225           0 :     auto k3 = std::make_tuple(std::string("rs_h"), recSig.GetHash());
     226           0 :     batch.Write(k3, std::make_pair(recSig.llmqType, recSig.id));
     227             : 
     228             :     // store by signHash
     229           0 :     auto signHash = llmq::utils::BuildSignHash(recSig);
     230           0 :     auto k4 = std::make_tuple(std::string("rs_s"), signHash);
     231           0 :     batch.Write(k4, (uint8_t)1);
     232             : 
     233             :     // store by current time. Allows fast cleanup of old recSigs
     234           0 :     auto k5 = std::make_tuple(std::string("rs_t"), (uint32_t)htobe32(curTime), recSig.llmqType, recSig.id);
     235           0 :     batch.Write(k5, (uint8_t)1);
     236             : 
     237           0 :     db.WriteBatch(batch);
     238             : 
     239           0 :     {
     240           0 :         int64_t t = GetTimeMillis();
     241             : 
     242           0 :         LOCK(cs);
     243           0 :         hasSigForIdCache.insert(std::make_pair((Consensus::LLMQType)recSig.llmqType, recSig.id), true);
     244           0 :         hasSigForSessionCache.insert(signHash, true);
     245           0 :         hasSigForHashCache.insert(recSig.GetHash(), true);
     246             :     }
     247           0 : }
     248             : 
     249           0 : void CRecoveredSigsDb::RemoveRecoveredSig(CDBBatch& batch, Consensus::LLMQType llmqType, const uint256& id, bool deleteHashKey, bool deleteTimeKey)
     250             : {
     251           0 :     AssertLockHeld(cs);
     252             : 
     253           0 :     CRecoveredSig recSig;
     254           0 :     if (!ReadRecoveredSig(llmqType, id, recSig)) {
     255           0 :         return;
     256             :     }
     257             : 
     258           0 :     auto signHash = llmq::utils::BuildSignHash(recSig);
     259             : 
     260           0 :     auto k1 = std::make_tuple(std::string("rs_r"), recSig.llmqType, recSig.id);
     261           0 :     auto k2 = std::make_tuple(std::string("rs_r"), recSig.llmqType, recSig.id, recSig.msgHash);
     262           0 :     auto k3 = std::make_tuple(std::string("rs_h"), recSig.GetHash());
     263           0 :     auto k4 = std::make_tuple(std::string("rs_s"), signHash);
     264           0 :     batch.Erase(k1);
     265           0 :     batch.Erase(k2);
     266           0 :     if (deleteHashKey) {
     267           0 :         batch.Erase(k3);
     268             :     }
     269           0 :     batch.Erase(k4);
     270             : 
     271           0 :     if (deleteTimeKey) {
     272           0 :         CDataStream writeTimeDs(SER_DISK, CLIENT_VERSION);
     273             :         // TODO remove the size() == sizeof(uint32_t) in a future version (when we stop supporting upgrades from < 0.14.1)
     274           0 :         if (db.ReadDataStream(k2, writeTimeDs) && writeTimeDs.size() == sizeof(uint32_t)) {
     275           0 :             uint32_t writeTime;
     276           0 :             writeTimeDs >> writeTime;
     277           0 :             auto k5 = std::make_tuple(std::string("rs_t"), (uint32_t)htobe32(writeTime), recSig.llmqType, recSig.id);
     278           0 :             batch.Erase(k5);
     279             :         }
     280             :     }
     281             : 
     282           0 :     hasSigForIdCache.erase(std::make_pair((Consensus::LLMQType)recSig.llmqType, recSig.id));
     283           0 :     hasSigForSessionCache.erase(signHash);
     284           0 :     if (deleteHashKey) {
     285           0 :         hasSigForHashCache.erase(recSig.GetHash());
     286             :     }
     287             : }
     288             : 
     289             : // Completely remove any traces of the recovered sig
     290           0 : void CRecoveredSigsDb::RemoveRecoveredSig(Consensus::LLMQType llmqType, const uint256& id)
     291             : {
     292           0 :     LOCK(cs);
     293           0 :     CDBBatch batch(CLIENT_VERSION | ADDRV2_FORMAT);
     294           0 :     RemoveRecoveredSig(batch, llmqType, id, true, true);
     295           0 :     db.WriteBatch(batch);
     296           0 : }
     297             : 
     298             : // Remove the recovered sig itself and all keys required to get from id -> recSig
     299             : // This will leave the byHash key in-place so that HasRecoveredSigForHash still returns true
     300           0 : void CRecoveredSigsDb::TruncateRecoveredSig(Consensus::LLMQType llmqType, const uint256& id)
     301             : {
     302           0 :     LOCK(cs);
     303           0 :     CDBBatch batch(CLIENT_VERSION | ADDRV2_FORMAT);
     304           0 :     RemoveRecoveredSig(batch, llmqType, id, false, false);
     305           0 :     db.WriteBatch(batch);
     306           0 : }
     307             : 
     308        1832 : void CRecoveredSigsDb::CleanupOldRecoveredSigs(int64_t maxAge)
     309             : {
     310        1832 :     std::unique_ptr<CDBIterator> pcursor(db.NewIterator());
     311             : 
     312        5496 :     auto start = std::make_tuple(std::string("rs_t"), (uint32_t)0, (uint8_t)0, uint256());
     313        1832 :     uint32_t endTime = (uint32_t)(GetAdjustedTime() - maxAge);
     314        1832 :     pcursor->Seek(start);
     315             : 
     316        1832 :     std::vector<std::pair<Consensus::LLMQType, uint256>> toDelete;
     317        1832 :     std::vector<decltype(start)> toDelete2;
     318             : 
     319        1832 :     while (pcursor->Valid()) {
     320           0 :         decltype(start) k;
     321             : 
     322           0 :         if (!pcursor->GetKey(k) || std::get<0>(k) != "rs_t") {
     323             :             break;
     324             :         }
     325           0 :         if (be32toh(std::get<1>(k)) >= endTime) {
     326             :             break;
     327             :         }
     328             : 
     329           0 :         toDelete.emplace_back((Consensus::LLMQType)std::get<2>(k), std::get<3>(k));
     330           0 :         toDelete2.emplace_back(k);
     331             : 
     332           0 :         pcursor->Next();
     333             :     }
     334        1832 :     pcursor.reset();
     335             : 
     336        1832 :     if (toDelete.empty()) {
     337        1832 :         return;
     338             :     }
     339             : 
     340           0 :     CDBBatch batch(CLIENT_VERSION | ADDRV2_FORMAT);
     341           0 :     {
     342           0 :         LOCK(cs);
     343           0 :         for (auto& e : toDelete) {
     344           0 :             RemoveRecoveredSig(batch, e.first, e.second, true, false);
     345             : 
     346           0 :             if (batch.SizeEstimate() >= (1 << 24)) {
     347           0 :                 db.WriteBatch(batch);
     348           0 :                 batch.Clear();
     349             :             }
     350             :         }
     351             :     }
     352             : 
     353           0 :     for (auto& e : toDelete2) {
     354           0 :         batch.Erase(e);
     355             :     }
     356             : 
     357           0 :     db.WriteBatch(batch);
     358             : 
     359           0 :     LogPrint(BCLog::LLMQ, "CRecoveredSigsDb::%d -- deleted %d entries\n", __func__, toDelete.size());
     360             : }
     361             : 
     362           6 : bool CRecoveredSigsDb::HasVotedOnId(Consensus::LLMQType llmqType, const uint256& id)
     363             : {
     364          12 :     auto k = std::make_tuple(std::string("rs_v"), (uint8_t)llmqType, id);
     365           6 :     return db.Exists(k);
     366             : }
     367             : 
     368           0 : bool CRecoveredSigsDb::GetVoteForId(Consensus::LLMQType llmqType, const uint256& id, uint256& msgHashRet)
     369             : {
     370           0 :     auto k = std::make_tuple(std::string("rs_v"), (uint8_t)llmqType, id);
     371           0 :     return db.Read(k, msgHashRet);
     372             : }
     373             : 
     374           6 : void CRecoveredSigsDb::WriteVoteForId(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash)
     375             : {
     376          12 :     auto k1 = std::make_tuple(std::string("rs_v"), (uint8_t)llmqType, id);
     377          18 :     auto k2 = std::make_tuple(std::string("rs_vt"), (uint32_t)htobe32(GetAdjustedTime()), (uint8_t)llmqType, id);
     378             : 
     379          12 :     CDBBatch batch(CLIENT_VERSION | ADDRV2_FORMAT);
     380           6 :     batch.Write(k1, msgHash);
     381           6 :     batch.Write(k2, (uint8_t)1);
     382             : 
     383           6 :     db.WriteBatch(batch);
     384           6 : }
     385             : 
     386        1832 : void CRecoveredSigsDb::CleanupOldVotes(int64_t maxAge)
     387             : {
     388        1832 :     std::unique_ptr<CDBIterator> pcursor(db.NewIterator());
     389             : 
     390        5496 :     auto start = std::make_tuple(std::string("rs_vt"), (uint32_t)0, (uint8_t)0, uint256());
     391        1832 :     uint32_t endTime = (uint32_t)(GetAdjustedTime() - maxAge);
     392        1832 :     pcursor->Seek(start);
     393             : 
     394        1832 :     CDBBatch batch(CLIENT_VERSION | ADDRV2_FORMAT);
     395        1832 :     size_t cnt = 0;
     396        1832 :     while (pcursor->Valid()) {
     397           0 :         decltype(start) k;
     398             : 
     399           0 :         if (!pcursor->GetKey(k) || std::get<0>(k) != "rs_vt") {
     400             :             break;
     401             :         }
     402           0 :         if (be32toh(std::get<1>(k)) >= endTime) {
     403             :             break;
     404             :         }
     405             : 
     406           0 :         uint8_t llmqType = std::get<2>(k);
     407           0 :         const uint256& id = std::get<3>(k);
     408             : 
     409           0 :         batch.Erase(k);
     410           0 :         batch.Erase(std::make_tuple(std::string("rs_v"), llmqType, id));
     411             : 
     412           0 :         cnt++;
     413             : 
     414           0 :         pcursor->Next();
     415             :     }
     416        1832 :     pcursor.reset();
     417             : 
     418        1832 :     if (cnt == 0) {
     419        1832 :         return;
     420             :     }
     421             : 
     422           0 :     db.WriteBatch(batch);
     423             : 
     424           0 :     LogPrint(BCLog::LLMQ, "CRecoveredSigsDb::%d -- deleted %d entries\n", __func__, cnt);
     425             : }
     426             : 
     427             : //////////////////
     428             : 
     429         413 : CSigningManager::CSigningManager(CDBWrapper& llmqDb, bool fMemory) : db(llmqDb)
     430             : {
     431         413 : }
     432             : 
     433           0 : bool CSigningManager::AlreadyHave(const CInv& inv)
     434             : {
     435           0 :     if (inv.type != MSG_QUORUM_RECOVERED_SIG) {
     436             :         return false;
     437             :     }
     438           0 :     return db.HasRecoveredSigForHash(inv.hash);
     439             : }
     440             : 
     441           0 : bool CSigningManager::GetRecoveredSigForGetData(const uint256& hash, CRecoveredSig& ret)
     442             : {
     443           0 :     if (!db.GetRecoveredSigByHash(hash, ret)) {
     444             :         return false;
     445             :     }
     446           0 :     if (!llmq::utils::IsQuorumActive((Consensus::LLMQType)(ret.llmqType), ret.quorumHash)) {
     447             :         // we don't want to propagate sigs from inactive quorums
     448           0 :         return false;
     449             :     }
     450             :     return true;
     451             : }
     452             : 
     453           0 : void CSigningManager::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman)
     454             : {
     455           0 :     if (strCommand == NetMsgType::QSIGREC) {
     456           0 :         CRecoveredSig recoveredSig;
     457           0 :         vRecv >> recoveredSig;
     458           0 :         ProcessMessageRecoveredSig(pfrom, recoveredSig, connman);
     459             :     }
     460           0 : }
     461             : 
     462           0 : void CSigningManager::ProcessMessageRecoveredSig(CNode* pfrom, const CRecoveredSig& recoveredSig, CConnman& connman)
     463             : {
     464           0 :     bool ban = false;
     465           0 :     if (!PreVerifyRecoveredSig(pfrom->GetId(), recoveredSig, ban)) {
     466           0 :         if (ban) {
     467           0 :             LOCK(cs_main);
     468           0 :             Misbehaving(pfrom->GetId(), 100);
     469             :         }
     470           0 :         return;
     471             :     }
     472             : 
     473             :     // It's important to only skip seen *valid* sig shares here. See comment for CBatchedSigShare
     474             :     // We don't receive recovered sigs in batches, but we do batched verification per node on these
     475           0 :     if (db.HasRecoveredSigForHash(recoveredSig.GetHash())) {
     476             :         return;
     477             :     }
     478             : 
     479           0 :     LogPrint(BCLog::LLMQ, "CSigningManager::%s -- signHash=%s, id=%s, msgHash=%s, node=%d\n", __func__,
     480             :         llmq::utils::BuildSignHash(recoveredSig).ToString(), recoveredSig.id.ToString(), recoveredSig.msgHash.ToString(), pfrom->GetId());
     481             : 
     482           0 :     LOCK(cs);
     483           0 :     pendingRecoveredSigs[pfrom->GetId()].emplace_back(recoveredSig);
     484             : }
     485             : 
     486           0 : bool CSigningManager::PreVerifyRecoveredSig(NodeId nodeId, const CRecoveredSig& recoveredSig, bool& retBan)
     487             : {
     488           0 :     retBan = false;
     489             : 
     490           0 :     auto llmqType = (Consensus::LLMQType)recoveredSig.llmqType;
     491           0 :     if (!Params().GetConsensus().llmqs.count(llmqType)) {
     492           0 :         retBan = true;
     493           0 :         return false;
     494             :     }
     495             : 
     496           0 :     CQuorumCPtr quorum = quorumManager->GetQuorum(llmqType, recoveredSig.quorumHash);
     497             : 
     498           0 :     if (!quorum) {
     499           0 :         LogPrint(BCLog::LLMQ, "CSigningManager::%s -- quorum %s not found, node=%d\n", __func__,
     500             :             recoveredSig.quorumHash.ToString(), nodeId);
     501           0 :         return false;
     502             :     }
     503           0 :     if (!llmq::utils::IsQuorumActive(llmqType, quorum->qc.quorumHash)) {
     504           0 :         return false;
     505             :     }
     506             : 
     507             :     return true;
     508             : }
     509             : 
     510       66233 : void CSigningManager::CollectPendingRecoveredSigsToVerify(
     511             :     size_t maxUniqueSessions,
     512             :     std::unordered_map<NodeId, std::list<CRecoveredSig>>& retSigShares,
     513             :     std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>& retQuorums)
     514             : {
     515       66233 :     {
     516       66233 :         LOCK(cs);
     517       66233 :         if (pendingRecoveredSigs.empty()) {
     518       66233 :             return;
     519             :         }
     520             : 
     521           0 :         std::unordered_set<std::pair<NodeId, uint256>, StaticSaltedHasher> uniqueSignHashes;
     522           0 :         llmq::utils::IterateNodesRandom(pendingRecoveredSigs, [&]() { return uniqueSignHashes.size() < maxUniqueSessions; }, [&](NodeId nodeId, std::list<CRecoveredSig>& ns) {
     523           0 :             if (ns.empty()) {
     524             :                 return false;
     525             :             }
     526           0 :             auto& recSig = *ns.begin();
     527             : 
     528           0 :             bool alreadyHave = db.HasRecoveredSigForHash(recSig.GetHash());
     529           0 :             if (!alreadyHave) {
     530           0 :                 uniqueSignHashes.emplace(nodeId, llmq::utils::BuildSignHash(recSig));
     531           0 :                 retSigShares[nodeId].emplace_back(recSig);
     532             :             }
     533           0 :             ns.erase(ns.begin());
     534           0 :             return !ns.empty(); }, rnd);
     535             : 
     536           0 :         if (retSigShares.empty()) {
     537       66233 :             return;
     538             :         }
     539             :     }
     540             : 
     541           0 :     for (auto& p : retSigShares) {
     542           0 :         NodeId nodeId = p.first;
     543           0 :         auto& v = p.second;
     544             : 
     545           0 :         for (auto it = v.begin(); it != v.end();) {
     546           0 :             auto& recSig = *it;
     547             : 
     548           0 :             Consensus::LLMQType llmqType = (Consensus::LLMQType)recSig.llmqType;
     549           0 :             auto quorumKey = std::make_pair((Consensus::LLMQType)recSig.llmqType, recSig.quorumHash);
     550           0 :             if (!retQuorums.count(quorumKey)) {
     551           0 :                 CQuorumCPtr quorum = quorumManager->GetQuorum(llmqType, recSig.quorumHash);
     552           0 :                 if (!quorum) {
     553           0 :                     LogPrint(BCLog::LLMQ, "CSigningManager::%s -- quorum %s not found, node=%d\n", __func__,
     554             :                         recSig.quorumHash.ToString(), nodeId);
     555           0 :                     it = v.erase(it);
     556           0 :                     continue;
     557             :                 }
     558           0 :                 if (!llmq::utils::IsQuorumActive(llmqType, quorum->qc.quorumHash)) {
     559           0 :                     LogPrint(BCLog::LLMQ, "CSigningManager::%s -- quorum %s not active anymore, node=%d\n", __func__,
     560             :                         recSig.quorumHash.ToString(), nodeId);
     561           0 :                     it = v.erase(it);
     562           0 :                     continue;
     563             :                 }
     564             : 
     565           0 :                 retQuorums.emplace(quorumKey, quorum);
     566             :             }
     567             : 
     568           0 :             ++it;
     569             :         }
     570             :     }
     571             : }
     572             : 
     573       66233 : bool CSigningManager::ProcessPendingRecoveredSigs(CConnman& connman)
     574             : {
     575      132466 :     std::unordered_map<NodeId, std::list<CRecoveredSig>> recSigsByNode;
     576       66233 :     std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher> quorums;
     577             : 
     578       66233 :     CollectPendingRecoveredSigsToVerify(32, recSigsByNode, quorums);
     579       66233 :     if (recSigsByNode.empty()) {
     580             :         return false;
     581             :     }
     582             : 
     583             :     // It's ok to perform insecure batched verification here as we verify against the quorum public keys, which are not
     584             :     // craftable by individual entities, making the rogue public key attack impossible
     585           0 :     CBLSBatchVerifier<NodeId, uint256> batchVerifier(false, false);
     586             : 
     587           0 :     size_t verifyCount = 0;
     588           0 :     for (auto& p : recSigsByNode) {
     589           0 :         NodeId nodeId = p.first;
     590           0 :         auto& v = p.second;
     591             : 
     592           0 :         for (auto& recSig : v) {
     593             :             // we didn't verify the lazy signature until now
     594           0 :             if (!recSig.sig.Get().IsValid()) {
     595           0 :                 batchVerifier.badSources.emplace(nodeId);
     596             :                 break;
     597             :             }
     598             : 
     599           0 :             const auto& quorum = quorums.at(std::make_pair((Consensus::LLMQType)recSig.llmqType, recSig.quorumHash));
     600           0 :             batchVerifier.PushMessage(nodeId, recSig.GetHash(), llmq::utils::BuildSignHash(recSig), recSig.sig.Get(), quorum->qc.quorumPublicKey);
     601           0 :             verifyCount++;
     602             :         }
     603             :     }
     604             : 
     605           0 :     cxxtimer::Timer verifyTimer(true);
     606           0 :     batchVerifier.Verify();
     607           0 :     verifyTimer.stop();
     608             : 
     609           0 :     LogPrint(BCLog::LLMQ, "CSigningManager::%s -- verified recovered sig(s). count=%d, vt=%d, nodes=%d\n", __func__, verifyCount, verifyTimer.count(), recSigsByNode.size());
     610             : 
     611           0 :     std::unordered_set<uint256, StaticSaltedHasher> processed;
     612           0 :     for (auto& p : recSigsByNode) {
     613           0 :         NodeId nodeId = p.first;
     614           0 :         auto& v = p.second;
     615             : 
     616           0 :         if (batchVerifier.badSources.count(nodeId)) {
     617           0 :             LOCK(cs_main);
     618           0 :             LogPrintf("CSigningManager::%s -- invalid recSig from other node, banning peer=%d\n", __func__, nodeId);
     619           0 :             Misbehaving(nodeId, 100);
     620           0 :             continue;
     621             :         }
     622             : 
     623           0 :         for (auto& recSig : v) {
     624           0 :             if (!processed.emplace(recSig.GetHash()).second) {
     625           0 :                 continue;
     626             :             }
     627             : 
     628           0 :             const auto& quorum = quorums.at(std::make_pair((Consensus::LLMQType)recSig.llmqType, recSig.quorumHash));
     629           0 :             ProcessRecoveredSig(nodeId, recSig, quorum, connman);
     630             :         }
     631             :     }
     632             : 
     633           0 :     return true;
     634             : }
     635             : 
     636             : // signature must be verified already
     637           0 : void CSigningManager::ProcessRecoveredSig(NodeId nodeId, const CRecoveredSig& recoveredSig, const CQuorumCPtr& quorum, CConnman& connman)
     638             : {
     639           0 :     auto llmqType = (Consensus::LLMQType)recoveredSig.llmqType;
     640             : 
     641           0 :     {
     642           0 :         LOCK(cs_main);
     643           0 :         connman.RemoveAskFor(recoveredSig.GetHash(), MSG_QUORUM_RECOVERED_SIG);
     644             :     }
     645             : 
     646           0 :     if (db.HasRecoveredSigForHash(recoveredSig.GetHash())) {
     647           0 :         return;
     648             :     }
     649             : 
     650           0 :     std::vector<CRecoveredSigsListener*> listeners;
     651           0 :     {
     652           0 :         LOCK(cs);
     653           0 :         listeners = recoveredSigsListeners;
     654             : 
     655           0 :         auto signHash = llmq::utils::BuildSignHash(recoveredSig);
     656             : 
     657           0 :         LogPrint(BCLog::LLMQ, "CSigningManager::%s -- valid recSig. signHash=%s, id=%s, msgHash=%s, node=%d\n", __func__,
     658             :             signHash.ToString(), recoveredSig.id.ToString(), recoveredSig.msgHash.ToString(), nodeId);
     659             : 
     660           0 :         if (db.HasRecoveredSigForId(llmqType, recoveredSig.id)) {
     661           0 :             CRecoveredSig otherRecoveredSig;
     662           0 :             if (db.GetRecoveredSigById(llmqType, recoveredSig.id, otherRecoveredSig)) {
     663           0 :                 auto otherSignHash = llmq::utils::BuildSignHash(recoveredSig);
     664           0 :                 if (signHash != otherSignHash) {
     665             :                     // this should really not happen, as each masternode is participating in only one vote,
     666             :                     // even if it's a member of multiple quorums. so a majority is only possible on one quorum and one msgHash per id
     667           0 :                     LogPrintf("CSigningManager::%s -- conflicting recoveredSig for signHash=%s, id=%s, msgHash=%s, otherSignHash=%s\n", __func__,
     668           0 :                         signHash.ToString(), recoveredSig.id.ToString(), recoveredSig.msgHash.ToString(), otherSignHash.ToString());
     669             :                 } else {
     670             :                     // Looks like we're trying to process a recSig that is already known. This might happen if the same
     671             :                     // recSig comes in through regular QRECSIG messages and at the same time through some other message
     672             :                     // which allowed to reconstruct a recSig (e.g. IXLOCK). In this case, just bail out.
     673             :                 }
     674           0 :                 return;
     675             :             } else {
     676             :                 // This case is very unlikely. It can only happen when cleanup caused this specific recSig to vanish
     677             :                 // between the HasRecoveredSigForId and GetRecoveredSigById call. If that happens, treat it as if we
     678             :                 // never had that recSig
     679             :             }
     680             :         }
     681             : 
     682           0 :         db.WriteRecoveredSig(recoveredSig);
     683             :     }
     684             : 
     685           0 :     CInv inv(MSG_QUORUM_RECOVERED_SIG, recoveredSig.GetHash());
     686           0 :     g_connman->ForEachNode([&](CNode* pnode) {
     687           0 :         if (pnode->nVersion >= LLMQS_PROTO_VERSION && pnode->m_wants_recsigs && pnode->CanRelay()) {
     688           0 :             pnode->PushInventory(inv);
     689             :         }
     690           0 :     });
     691           0 :     for (auto& l : listeners) {
     692           0 :         l->HandleNewRecoveredSig(recoveredSig);
     693             :     }
     694             : }
     695             : 
     696           0 : void CSigningManager::TruncateRecoveredSig(Consensus::LLMQType llmqType, const uint256& id)
     697             : {
     698           0 :     db.TruncateRecoveredSig(llmqType, id);
     699           0 : }
     700             : 
     701       66233 : void CSigningManager::Cleanup()
     702             : {
     703       66233 :     int64_t now = GetTimeMillis();
     704       66233 :     if (now - lastCleanupTime < 5000) {
     705             :         return;
     706             :     }
     707             : 
     708        1832 :     int64_t maxAge = DEFAULT_MAX_RECOVERED_SIGS_AGE;
     709             : 
     710        1832 :     db.CleanupOldRecoveredSigs(maxAge);
     711        1832 :     db.CleanupOldVotes(maxAge);
     712             : 
     713        1832 :     lastCleanupTime = GetTimeMillis();
     714             : }
     715             : 
     716         570 : void CSigningManager::RegisterRecoveredSigsListener(CRecoveredSigsListener* l)
     717             : {
     718         570 :     LOCK(cs);
     719         570 :     recoveredSigsListeners.emplace_back(l);
     720         570 : }
     721             : 
     722         590 : void CSigningManager::UnregisterRecoveredSigsListener(CRecoveredSigsListener* l)
     723             : {
     724         590 :     LOCK(cs);
     725         590 :     auto itRem = std::remove(recoveredSigsListeners.begin(), recoveredSigsListeners.end(), l);
     726         590 :     recoveredSigsListeners.erase(itRem, recoveredSigsListeners.end());
     727         590 : }
     728             : 
     729           6 : bool CSigningManager::AsyncSignIfMember(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash, bool allowReSign)
     730             : {
     731           6 :     auto& params = Params().GetConsensus().llmqs.at(llmqType);
     732             : 
     733           6 :     if (!activeMasternodeManager) {
     734             :         return false;
     735             :     }
     736             : 
     737           6 :     {
     738           6 :         LOCK(cs);
     739             : 
     740           6 :         bool hasVoted = db.HasVotedOnId(llmqType, id);
     741           6 :         if (hasVoted) {
     742           0 :             uint256 prevMsgHash;
     743           0 :             db.GetVoteForId(llmqType, id, prevMsgHash);
     744           0 :             if (msgHash != prevMsgHash) {
     745           0 :                 LogPrintf("CSigningManager::%s -- already voted for id=%s and msgHash=%s. Not voting on conflicting msgHash=%s\n", __func__,
     746           0 :                     id.ToString(), prevMsgHash.ToString(), msgHash.ToString());
     747           0 :                 return false;
     748           0 :             } else if (allowReSign) {
     749           0 :                 LogPrint(BCLog::LLMQ, "CSigningManager::%s -- already voted for id=%s and msgHash=%s. Resigning!\n", __func__,
     750             :                     id.ToString(), prevMsgHash.ToString());
     751             :             } else {
     752           0 :                 LogPrint(BCLog::LLMQ, "CSigningManager::%s -- already voted for id=%s and msgHash=%s. Not voting again.\n", __func__,
     753             :                     id.ToString(), prevMsgHash.ToString());
     754           0 :                 return false;
     755             :             }
     756             :         }
     757             : 
     758           6 :         if (db.HasRecoveredSigForId(llmqType, id)) {
     759             :             // no need to sign it if we already have a recovered sig
     760             :             return true;
     761             :         }
     762           6 :         if (!hasVoted) {
     763           6 :             db.WriteVoteForId(llmqType, id, msgHash);
     764             :         }
     765             :     }
     766             : 
     767             :     // This might end up giving different results on different members
     768             :     // This might happen when we are on the brink of confirming a new quorum
     769             :     // This gives a slight risk of not getting enough shares to recover a signature
     770             :     // But at least it shouldn't be possible to get conflicting recovered signatures
     771             :     // TODO fix this by re-signing when the next block arrives, but only when that block results in a change of the quorum list and no recovered signature has been created in the mean time
     772          12 :     CQuorumCPtr quorum = SelectQuorumForSigning(llmqType, id);
     773           6 :     if (!quorum) {
     774          18 :         LogPrint(BCLog::LLMQ, "CSigningManager::%s -- failed to select quorum. id=%s, msgHash=%s\n", __func__, id.ToString(), msgHash.ToString());
     775           6 :         return false;
     776             :     }
     777             : 
     778           0 :     if (!quorum->IsValidMember(activeMasternodeManager->GetProTx())) {
     779             :         return false;
     780             :     }
     781             : 
     782           0 :     if (allowReSign) {
     783             :         // make us re-announce all known shares (other nodes might have run into a timeout)
     784           0 :         quorumSigSharesManager->ForceReAnnouncement(quorum, llmqType, id, msgHash);
     785             :     }
     786           0 :     quorumSigSharesManager->AsyncSign(quorum, id, msgHash);
     787             : 
     788             :     return true;
     789             : }
     790             : 
     791           0 : bool CSigningManager::HasRecoveredSig(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash)
     792             : {
     793           0 :     return db.HasRecoveredSig(llmqType, id, msgHash);
     794             : }
     795             : 
     796           0 : bool CSigningManager::HasRecoveredSigForId(Consensus::LLMQType llmqType, const uint256& id)
     797             : {
     798           0 :     return db.HasRecoveredSigForId(llmqType, id);
     799             : }
     800             : 
     801           0 : bool CSigningManager::HasRecoveredSigForSession(const uint256& signHash)
     802             : {
     803           0 :     return db.HasRecoveredSigForSession(signHash);
     804             : }
     805             : 
     806           0 : bool CSigningManager::IsConflicting(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash)
     807             : {
     808           0 :     if (!db.HasRecoveredSigForId(llmqType, id)) {
     809             :         // no recovered sig present, so no conflict
     810             :         return false;
     811             :     }
     812             : 
     813           0 :     if (!db.HasRecoveredSig(llmqType, id, msgHash)) {
     814             :         // recovered sig is present, but not for the given msgHash. That's a conflict!
     815           0 :         return true;
     816             :     }
     817             : 
     818             :     // all good
     819             :     return false;
     820             : }
     821             : 
     822           0 : bool CSigningManager::HasVotedOnId(Consensus::LLMQType llmqType, const uint256& id)
     823             : {
     824           0 :     return db.HasVotedOnId(llmqType, id);
     825             : }
     826             : 
     827           0 : bool CSigningManager::GetVoteForId(Consensus::LLMQType llmqType, const uint256& id, uint256& msgHashRet)
     828             : {
     829           0 :     return db.GetVoteForId(llmqType, id, msgHashRet);
     830             : }
     831             : 
     832           6 : CQuorumCPtr CSigningManager::SelectQuorumForSigning(Consensus::LLMQType llmqType, const uint256& selectionHash, int signHeight, int signOffset)
     833             : {
     834           6 :     auto& llmqParams = Params().GetConsensus().llmqs.at(llmqType);
     835           6 :     size_t poolSize = (size_t)llmqParams.signingActiveQuorumCount;
     836             : 
     837           6 :     CBlockIndex* pindexStart;
     838           6 :     {
     839           6 :         LOCK(cs_main);
     840           6 :         if (signHeight == -1) {
     841           6 :             signHeight = chainActive.Height();
     842             :         }
     843           6 :         int startBlockHeight = signHeight - signOffset;
     844           6 :         if (startBlockHeight > chainActive.Height()) {
     845           0 :             return {};
     846             :         }
     847          12 :         pindexStart = chainActive[startBlockHeight];
     848             :     }
     849             : 
     850           6 :     auto quorums =  quorumManager->ScanQuorums(llmqType, pindexStart, poolSize);
     851           6 :     if (quorums.empty()) {
     852           6 :         return nullptr;
     853             :     }
     854             : 
     855           6 :     std::vector<std::pair<uint256, size_t>> scores;
     856           0 :     scores.reserve(quorums.size());
     857           0 :     for (size_t i = 0; i < quorums.size(); i++) {
     858           0 :         CHashWriter h(SER_NETWORK, 0);
     859           0 :         h << (uint8_t)llmqType;
     860           0 :         h << quorums[i]->qc.quorumHash;
     861           0 :         h << selectionHash;
     862           0 :         scores.emplace_back(h.GetHash(), i);
     863             :     }
     864           0 :     std::sort(scores.begin(), scores.end());
     865           0 :     return quorums[scores.front().second];
     866             : }
     867             : 
     868           0 : bool CSigningManager::VerifyRecoveredSig(Consensus::LLMQType llmqType, int signedAtHeight, const uint256& id, const uint256& msgHash, const CBLSSignature& sig)
     869             : {
     870           0 :     auto quorum = SelectQuorumForSigning(llmqType, id, signedAtHeight);
     871           0 :     if (!quorum) {
     872             :         return false;
     873             :     }
     874             : 
     875           0 :     uint256 signHash = llmq::utils::BuildSignHash(llmqType, quorum->qc.quorumHash, id, msgHash);
     876           0 :     return sig.VerifyInsecure(quorum->qc.quorumPublicKey, signHash);
     877             : }
     878             : 
     879             : } // namespace llmq

Generated by: LCOV version 1.14