LCOV - code coverage report
Current view: top level - src/llmq - quorums_signing.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 317 452 70.1 %
Date: 2025-04-02 01:23:23 Functions: 34 43 79.1 %

          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         475 : CRecoveredSigsDb::CRecoveredSigsDb(CDBWrapper& _db) : db(_db)
      28             : {
      29         950 :     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          12 : bool CRecoveredSigsDb::HasRecoveredSig(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash)
     116             : {
     117          24 :     auto k = std::make_tuple(std::string("rs_r"), (uint8_t)llmqType, id, msgHash);
     118          12 :     return db.Exists(k);
     119             : }
     120             : 
     121        8738 : bool CRecoveredSigsDb::HasRecoveredSigForId(Consensus::LLMQType llmqType, const uint256& id)
     122             : {
     123        8738 :     auto cacheKey = std::make_pair(llmqType, id);
     124        8738 :     bool ret;
     125        8738 :     {
     126        8738 :         LOCK(cs);
     127        8738 :         if (hasSigForIdCache.get(cacheKey, ret)) {
     128       10596 :             return ret;
     129             :         }
     130             :     }
     131             : 
     132             : 
     133       15618 :     auto k = std::make_tuple(std::string("rs_r"), (uint8_t)llmqType, id);
     134        3440 :     ret = db.Exists(k);
     135             : 
     136        6880 :     LOCK(cs);
     137        3440 :     hasSigForIdCache.insert(cacheKey, ret);
     138        3440 :     return ret;
     139             : }
     140             : 
     141         733 : bool CRecoveredSigsDb::HasRecoveredSigForSession(const uint256& signHash)
     142             : {
     143         733 :     bool ret;
     144         733 :     {
     145         733 :         LOCK(cs);
     146         733 :         if (hasSigForSessionCache.get(signHash, ret)) {
     147        1166 :             return ret;
     148             :         }
     149             :     }
     150             : 
     151        1033 :     auto k = std::make_tuple(std::string("rs_s"), signHash);
     152         150 :     ret = db.Exists(k);
     153             : 
     154         300 :     LOCK(cs);
     155         150 :     hasSigForSessionCache.insert(signHash, ret);
     156         150 :     return ret;
     157             : }
     158             : 
     159       13701 : bool CRecoveredSigsDb::HasRecoveredSigForHash(const uint256& hash)
     160             : {
     161       13701 :     bool ret;
     162       13701 :     {
     163       13701 :         LOCK(cs);
     164       13701 :         if (hasSigForHashCache.get(hash, ret)) {
     165       23304 :             return ret;
     166             :         }
     167             :     }
     168             : 
     169       17799 :     auto k = std::make_tuple(std::string("rs_h"), hash);
     170        2049 :     ret = db.Exists(k);
     171             : 
     172        2049 :     LOCK(cs);
     173        2049 :     hasSigForHashCache.insert(hash, ret);
     174        2049 :     return ret;
     175             : }
     176             : 
     177        1664 : bool CRecoveredSigsDb::ReadRecoveredSig(Consensus::LLMQType llmqType, const uint256& id, CRecoveredSig& ret)
     178             : {
     179        4992 :     auto k = std::make_tuple(std::string("rs_r"), (uint8_t)llmqType, id);
     180             : 
     181        3328 :     CDataStream ds(SER_DISK, CLIENT_VERSION);
     182        1664 :     if (!db.ReadDataStream(k, ds)) {
     183             :         return false;
     184             :     }
     185             : 
     186        1664 :     try {
     187        1664 :         ret.Unserialize(ds);
     188             :         return true;
     189           0 :     } catch (std::exception&) {
     190           0 :         return false;
     191             :     }
     192             : }
     193             : 
     194        1664 : bool CRecoveredSigsDb::GetRecoveredSigByHash(const uint256& hash, CRecoveredSig& ret)
     195             : {
     196        4992 :     auto k1 = std::make_tuple(std::string("rs_h"), hash);
     197        1664 :     std::pair<uint8_t, uint256> k2;
     198        1664 :     if (!db.Read(k1, k2)) {
     199             :         return false;
     200             :     }
     201             : 
     202        1664 :     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        2049 : void CRecoveredSigsDb::WriteRecoveredSig(const llmq::CRecoveredSig& recSig)
     211             : {
     212        2049 :     CDBBatch batch(CLIENT_VERSION | ADDRV2_FORMAT);
     213             : 
     214        2049 :     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        6147 :     auto k1 = std::make_tuple(std::string("rs_r"), recSig.llmqType, recSig.id);
     219        6147 :     auto k2 = std::make_tuple(std::string("rs_r"), recSig.llmqType, recSig.id, recSig.msgHash);
     220        2049 :     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        2049 :     batch.Write(k2, curTime);
     223             : 
     224             :     // store by object hash
     225        6147 :     auto k3 = std::make_tuple(std::string("rs_h"), recSig.GetHash());
     226        2049 :     batch.Write(k3, std::make_pair(recSig.llmqType, recSig.id));
     227             : 
     228             :     // store by signHash
     229        2049 :     auto signHash = llmq::utils::BuildSignHash(recSig);
     230        6147 :     auto k4 = std::make_tuple(std::string("rs_s"), signHash);
     231        2049 :     batch.Write(k4, (uint8_t)1);
     232             : 
     233             :     // store by current time. Allows fast cleanup of old recSigs
     234        6147 :     auto k5 = std::make_tuple(std::string("rs_t"), (uint32_t)htobe32(curTime), recSig.llmqType, recSig.id);
     235        2049 :     batch.Write(k5, (uint8_t)1);
     236             : 
     237        2049 :     db.WriteBatch(batch);
     238             : 
     239        2049 :     {
     240        2049 :         int64_t t = GetTimeMillis();
     241             : 
     242        4098 :         LOCK(cs);
     243        2049 :         hasSigForIdCache.insert(std::make_pair((Consensus::LLMQType)recSig.llmqType, recSig.id), true);
     244        2049 :         hasSigForSessionCache.insert(signHash, true);
     245        4098 :         hasSigForHashCache.insert(recSig.GetHash(), true);
     246             :     }
     247        2049 : }
     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        3400 : void CRecoveredSigsDb::CleanupOldRecoveredSigs(int64_t maxAge)
     309             : {
     310        3400 :     std::unique_ptr<CDBIterator> pcursor(db.NewIterator());
     311             : 
     312       10200 :     auto start = std::make_tuple(std::string("rs_t"), (uint32_t)0, (uint8_t)0, uint256());
     313        3400 :     uint32_t endTime = (uint32_t)(GetAdjustedTime() - maxAge);
     314        3400 :     pcursor->Seek(start);
     315             : 
     316        3400 :     std::vector<std::pair<Consensus::LLMQType, uint256>> toDelete;
     317        3400 :     std::vector<decltype(start)> toDelete2;
     318             : 
     319        3400 :     while (pcursor->Valid()) {
     320         415 :         decltype(start) k;
     321             : 
     322         415 :         if (!pcursor->GetKey(k) || std::get<0>(k) != "rs_t") {
     323             :             break;
     324             :         }
     325         221 :         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        3400 :     pcursor.reset();
     335             : 
     336        3400 :     if (toDelete.empty()) {
     337        3400 :         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        3278 : bool CRecoveredSigsDb::HasVotedOnId(Consensus::LLMQType llmqType, const uint256& id)
     363             : {
     364        6556 :     auto k = std::make_tuple(std::string("rs_v"), (uint8_t)llmqType, id);
     365        3278 :     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        3278 : void CRecoveredSigsDb::WriteVoteForId(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash)
     375             : {
     376        6556 :     auto k1 = std::make_tuple(std::string("rs_v"), (uint8_t)llmqType, id);
     377        9834 :     auto k2 = std::make_tuple(std::string("rs_vt"), (uint32_t)htobe32(GetAdjustedTime()), (uint8_t)llmqType, id);
     378             : 
     379        6556 :     CDBBatch batch(CLIENT_VERSION | ADDRV2_FORMAT);
     380        3278 :     batch.Write(k1, msgHash);
     381        3278 :     batch.Write(k2, (uint8_t)1);
     382             : 
     383        3278 :     db.WriteBatch(batch);
     384        3278 : }
     385             : 
     386        3400 : void CRecoveredSigsDb::CleanupOldVotes(int64_t maxAge)
     387             : {
     388        3400 :     std::unique_ptr<CDBIterator> pcursor(db.NewIterator());
     389             : 
     390       10200 :     auto start = std::make_tuple(std::string("rs_vt"), (uint32_t)0, (uint8_t)0, uint256());
     391        3400 :     uint32_t endTime = (uint32_t)(GetAdjustedTime() - maxAge);
     392        3400 :     pcursor->Seek(start);
     393             : 
     394        3400 :     CDBBatch batch(CLIENT_VERSION | ADDRV2_FORMAT);
     395        3400 :     size_t cnt = 0;
     396        3400 :     while (pcursor->Valid()) {
     397         415 :         decltype(start) k;
     398             : 
     399         415 :         if (!pcursor->GetKey(k) || std::get<0>(k) != "rs_vt") {
     400             :             break;
     401             :         }
     402         370 :         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        3400 :     pcursor.reset();
     417             : 
     418        3400 :     if (cnt == 0) {
     419        3400 :         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         475 : CSigningManager::CSigningManager(CDBWrapper& llmqDb, bool fMemory) : db(llmqDb)
     430             : {
     431         475 : }
     432             : 
     433        8324 : bool CSigningManager::AlreadyHave(const CInv& inv)
     434             : {
     435        8324 :     if (inv.type != MSG_QUORUM_RECOVERED_SIG) {
     436             :         return false;
     437             :     }
     438        8324 :     return db.HasRecoveredSigForHash(inv.hash);
     439             : }
     440             : 
     441        1664 : bool CSigningManager::GetRecoveredSigForGetData(const uint256& hash, CRecoveredSig& ret)
     442             : {
     443        1664 :     if (!db.GetRecoveredSigByHash(hash, ret)) {
     444             :         return false;
     445             :     }
     446        1664 :     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        1664 : void CSigningManager::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman)
     454             : {
     455        1664 :     if (strCommand == NetMsgType::QSIGREC) {
     456        3328 :         CRecoveredSig recoveredSig;
     457        1664 :         vRecv >> recoveredSig;
     458        1664 :         ProcessMessageRecoveredSig(pfrom, recoveredSig, connman);
     459             :     }
     460        1664 : }
     461             : 
     462        1664 : void CSigningManager::ProcessMessageRecoveredSig(CNode* pfrom, const CRecoveredSig& recoveredSig, CConnman& connman)
     463             : {
     464        1664 :     bool ban = false;
     465        1664 :     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        1664 :     if (db.HasRecoveredSigForHash(recoveredSig.GetHash())) {
     476             :         return;
     477             :     }
     478             : 
     479        6656 :     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        3328 :     LOCK(cs);
     483        3328 :     pendingRecoveredSigs[pfrom->GetId()].emplace_back(recoveredSig);
     484             : }
     485             : 
     486        1664 : bool CSigningManager::PreVerifyRecoveredSig(NodeId nodeId, const CRecoveredSig& recoveredSig, bool& retBan)
     487             : {
     488        1664 :     retBan = false;
     489             : 
     490        1664 :     auto llmqType = (Consensus::LLMQType)recoveredSig.llmqType;
     491        1664 :     if (!Params().GetConsensus().llmqs.count(llmqType)) {
     492           0 :         retBan = true;
     493           0 :         return false;
     494             :     }
     495             : 
     496        3328 :     CQuorumCPtr quorum = quorumManager->GetQuorum(llmqType, recoveredSig.quorumHash);
     497             : 
     498        1664 :     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        1664 :     if (!llmq::utils::IsQuorumActive(llmqType, quorum->qc.quorumHash)) {
     504           0 :         return false;
     505             :     }
     506             : 
     507             :     return true;
     508             : }
     509             : 
     510      155386 : 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      155386 :     {
     516      155386 :         LOCK(cs);
     517      155386 :         if (pendingRecoveredSigs.empty()) {
     518      154567 :             return;
     519             :         }
     520             : 
     521       13210 :         std::unordered_set<std::pair<NodeId, uint256>, StaticSaltedHasher> uniqueSignHashes;
     522       34924 :         llmq::utils::IterateNodesRandom(pendingRecoveredSigs, [&]() { return uniqueSignHashes.size() < maxUniqueSessions; }, [&](NodeId nodeId, std::list<CRecoveredSig>& ns) {
     523       34924 :             if (ns.empty()) {
     524             :                 return false;
     525             :             }
     526        1664 :             auto& recSig = *ns.begin();
     527             : 
     528        1664 :             bool alreadyHave = db.HasRecoveredSigForHash(recSig.GetHash());
     529        1664 :             if (!alreadyHave) {
     530        1664 :                 uniqueSignHashes.emplace(nodeId, llmq::utils::BuildSignHash(recSig));
     531        1664 :                 retSigShares[nodeId].emplace_back(recSig);
     532             :             }
     533        1664 :             ns.erase(ns.begin());
     534       14055 :             return !ns.empty(); }, rnd);
     535             : 
     536       12391 :         if (retSigShares.empty()) {
     537      166139 :             return;
     538             :         }
     539             :     }
     540             : 
     541        1859 :     for (auto& p : retSigShares) {
     542        1040 :         NodeId nodeId = p.first;
     543        1040 :         auto& v = p.second;
     544             : 
     545        2704 :         for (auto it = v.begin(); it != v.end();) {
     546        1664 :             auto& recSig = *it;
     547             : 
     548        1664 :             Consensus::LLMQType llmqType = (Consensus::LLMQType)recSig.llmqType;
     549        1664 :             auto quorumKey = std::make_pair((Consensus::LLMQType)recSig.llmqType, recSig.quorumHash);
     550        1664 :             if (!retQuorums.count(quorumKey)) {
     551        2050 :                 CQuorumCPtr quorum = quorumManager->GetQuorum(llmqType, recSig.quorumHash);
     552        1025 :                 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        1025 :                 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        1025 :                 retQuorums.emplace(quorumKey, quorum);
     566             :             }
     567             : 
     568        1664 :             ++it;
     569             :         }
     570             :     }
     571             : }
     572             : 
     573      155386 : bool CSigningManager::ProcessPendingRecoveredSigs(CConnman& connman)
     574             : {
     575      310772 :     std::unordered_map<NodeId, std::list<CRecoveredSig>> recSigsByNode;
     576      155386 :     std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher> quorums;
     577             : 
     578      155386 :     CollectPendingRecoveredSigsToVerify(32, recSigsByNode, quorums);
     579      155386 :     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         819 :     CBLSBatchVerifier<NodeId, uint256> batchVerifier(false, false);
     586             : 
     587         819 :     size_t verifyCount = 0;
     588        1859 :     for (auto& p : recSigsByNode) {
     589        1040 :         NodeId nodeId = p.first;
     590        1040 :         auto& v = p.second;
     591             : 
     592        2704 :         for (auto& recSig : v) {
     593             :             // we didn't verify the lazy signature until now
     594        1664 :             if (!recSig.sig.Get().IsValid()) {
     595        1040 :                 batchVerifier.badSources.emplace(nodeId);
     596             :                 break;
     597             :             }
     598             : 
     599        1664 :             const auto& quorum = quorums.at(std::make_pair((Consensus::LLMQType)recSig.llmqType, recSig.quorumHash));
     600        1664 :             batchVerifier.PushMessage(nodeId, recSig.GetHash(), llmq::utils::BuildSignHash(recSig), recSig.sig.Get(), quorum->qc.quorumPublicKey);
     601        1664 :             verifyCount++;
     602             :         }
     603             :     }
     604             : 
     605        1638 :     cxxtimer::Timer verifyTimer(true);
     606         819 :     batchVerifier.Verify();
     607         819 :     verifyTimer.stop();
     608             : 
     609         819 :     LogPrint(BCLog::LLMQ, "CSigningManager::%s -- verified recovered sig(s). count=%d, vt=%d, nodes=%d\n", __func__, verifyCount, verifyTimer.count(), recSigsByNode.size());
     610             : 
     611        1638 :     std::unordered_set<uint256, StaticSaltedHasher> processed;
     612        1859 :     for (auto& p : recSigsByNode) {
     613        1040 :         NodeId nodeId = p.first;
     614        1040 :         auto& v = p.second;
     615             : 
     616        1040 :         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        2704 :         for (auto& recSig : v) {
     624        1664 :             if (!processed.emplace(recSig.GetHash()).second) {
     625           0 :                 continue;
     626             :             }
     627             : 
     628        1664 :             const auto& quorum = quorums.at(std::make_pair((Consensus::LLMQType)recSig.llmqType, recSig.quorumHash));
     629        1664 :             ProcessRecoveredSig(nodeId, recSig, quorum, connman);
     630             :         }
     631             :     }
     632             : 
     633         819 :     return true;
     634             : }
     635             : 
     636             : // signature must be verified already
     637        2049 : void CSigningManager::ProcessRecoveredSig(NodeId nodeId, const CRecoveredSig& recoveredSig, const CQuorumCPtr& quorum, CConnman& connman)
     638             : {
     639        2049 :     auto llmqType = (Consensus::LLMQType)recoveredSig.llmqType;
     640             : 
     641        2049 :     {
     642        2049 :         LOCK(cs_main);
     643        2049 :         connman.RemoveAskFor(recoveredSig.GetHash(), MSG_QUORUM_RECOVERED_SIG);
     644             :     }
     645             : 
     646        2049 :     if (db.HasRecoveredSigForHash(recoveredSig.GetHash())) {
     647           0 :         return;
     648             :     }
     649             : 
     650        4098 :     std::vector<CRecoveredSigsListener*> listeners;
     651        2049 :     {
     652        2049 :         LOCK(cs);
     653        2049 :         listeners = recoveredSigsListeners;
     654             : 
     655        2049 :         auto signHash = llmq::utils::BuildSignHash(recoveredSig);
     656             : 
     657        8196 :         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        2049 :         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        2049 :         db.WriteRecoveredSig(recoveredSig);
     683             :     }
     684             : 
     685        2049 :     CInv inv(MSG_QUORUM_RECOVERED_SIG, recoveredSig.GetHash());
     686        2049 :     g_connman->ForEachNode([&](CNode* pnode) {
     687       21669 :         if (pnode->nVersion >= LLMQS_PROTO_VERSION && pnode->m_wants_recsigs && pnode->CanRelay()) {
     688        6658 :             pnode->PushInventory(inv);
     689             :         }
     690       21669 :     });
     691        6147 :     for (auto& l : listeners) {
     692        4098 :         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      155386 : void CSigningManager::Cleanup()
     702             : {
     703      155386 :     int64_t now = GetTimeMillis();
     704      155386 :     if (now - lastCleanupTime < 5000) {
     705             :         return;
     706             :     }
     707             : 
     708        3400 :     int64_t maxAge = DEFAULT_MAX_RECOVERED_SIGS_AGE;
     709             : 
     710        3400 :     db.CleanupOldRecoveredSigs(maxAge);
     711        3400 :     db.CleanupOldVotes(maxAge);
     712             : 
     713        3400 :     lastCleanupTime = GetTimeMillis();
     714             : }
     715             : 
     716         694 : void CSigningManager::RegisterRecoveredSigsListener(CRecoveredSigsListener* l)
     717             : {
     718         694 :     LOCK(cs);
     719         694 :     recoveredSigsListeners.emplace_back(l);
     720         694 : }
     721             : 
     722         714 : void CSigningManager::UnregisterRecoveredSigsListener(CRecoveredSigsListener* l)
     723             : {
     724         714 :     LOCK(cs);
     725         714 :     auto itRem = std::remove(recoveredSigsListeners.begin(), recoveredSigsListeners.end(), l);
     726         714 :     recoveredSigsListeners.erase(itRem, recoveredSigsListeners.end());
     727         714 : }
     728             : 
     729        3282 : bool CSigningManager::AsyncSignIfMember(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash, bool allowReSign)
     730             : {
     731        3282 :     auto& params = Params().GetConsensus().llmqs.at(llmqType);
     732             : 
     733        3282 :     if (!activeMasternodeManager) {
     734             :         return false;
     735             :     }
     736             : 
     737        3278 :     {
     738        3278 :         LOCK(cs);
     739             : 
     740        3278 :         bool hasVoted = db.HasVotedOnId(llmqType, id);
     741        3278 :         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        3278 :         if (db.HasRecoveredSigForId(llmqType, id)) {
     759             :             // no need to sign it if we already have a recovered sig
     760             :             return true;
     761             :         }
     762        3278 :         if (!hasVoted) {
     763        3278 :             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        6560 :     CQuorumCPtr quorum = SelectQuorumForSigning(llmqType, id);
     773        3278 :     if (!quorum) {
     774        2823 :         LogPrint(BCLog::LLMQ, "CSigningManager::%s -- failed to select quorum. id=%s, msgHash=%s\n", __func__, id.ToString(), msgHash.ToString());
     775         941 :         return false;
     776             :     }
     777             : 
     778        2337 :     if (!quorum->IsValidMember(activeMasternodeManager->GetProTx())) {
     779             :         return false;
     780             :     }
     781             : 
     782         968 :     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         968 :     quorumSigSharesManager->AsyncSign(quorum, id, msgHash);
     787             : 
     788             :     return true;
     789             : }
     790             : 
     791          12 : bool CSigningManager::HasRecoveredSig(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash)
     792             : {
     793          12 :     return db.HasRecoveredSig(llmqType, id, msgHash);
     794             : }
     795             : 
     796        3411 : bool CSigningManager::HasRecoveredSigForId(Consensus::LLMQType llmqType, const uint256& id)
     797             : {
     798        3411 :     return db.HasRecoveredSigForId(llmqType, id);
     799             : }
     800             : 
     801         733 : bool CSigningManager::HasRecoveredSigForSession(const uint256& signHash)
     802             : {
     803         733 :     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        4013 : CQuorumCPtr CSigningManager::SelectQuorumForSigning(Consensus::LLMQType llmqType, const uint256& selectionHash, int signHeight, int signOffset)
     833             : {
     834        4013 :     auto& llmqParams = Params().GetConsensus().llmqs.at(llmqType);
     835        4013 :     size_t poolSize = (size_t)llmqParams.signingActiveQuorumCount;
     836             : 
     837        4013 :     CBlockIndex* pindexStart;
     838        4013 :     {
     839        4013 :         LOCK(cs_main);
     840        4013 :         if (signHeight == -1) {
     841        3278 :             signHeight = chainActive.Height();
     842             :         }
     843        4013 :         int startBlockHeight = signHeight - signOffset;
     844        4013 :         if (startBlockHeight > chainActive.Height()) {
     845           0 :             return {};
     846             :         }
     847        8026 :         pindexStart = chainActive[startBlockHeight];
     848             :     }
     849             : 
     850        4013 :     auto quorums =  quorumManager->ScanQuorums(llmqType, pindexStart, poolSize);
     851        4013 :     if (quorums.empty()) {
     852         941 :         return nullptr;
     853             :     }
     854             : 
     855        7085 :     std::vector<std::pair<uint256, size_t>> scores;
     856        3072 :     scores.reserve(quorums.size());
     857        8394 :     for (size_t i = 0; i < quorums.size(); i++) {
     858        5322 :         CHashWriter h(SER_NETWORK, 0);
     859        5322 :         h << (uint8_t)llmqType;
     860        5322 :         h << quorums[i]->qc.quorumHash;
     861        5322 :         h << selectionHash;
     862       10644 :         scores.emplace_back(h.GetHash(), i);
     863             :     }
     864        3072 :     std::sort(scores.begin(), scores.end());
     865        3072 :     return quorums[scores.front().second];
     866             : }
     867             : 
     868         735 : bool CSigningManager::VerifyRecoveredSig(Consensus::LLMQType llmqType, int signedAtHeight, const uint256& id, const uint256& msgHash, const CBLSSignature& sig)
     869             : {
     870        1470 :     auto quorum = SelectQuorumForSigning(llmqType, id, signedAtHeight);
     871         735 :     if (!quorum) {
     872             :         return false;
     873             :     }
     874             : 
     875         735 :     uint256 signHash = llmq::utils::BuildSignHash(llmqType, quorum->qc.quorumHash, id, msgHash);
     876         735 :     return sig.VerifyInsecure(quorum->qc.quorumPublicKey, signHash);
     877             : }
     878             : 
     879             : } // namespace llmq

Generated by: LCOV version 1.14